Compare commits

..

70 Commits

Author SHA1 Message Date
standardci
c5fdd59eb1 chore(release): publish new version
- @standardnotes/analytics@1.24.0
 - @standardnotes/api-gateway@1.16.5
 - @standardnotes/auth-server@1.25.1
 - @standardnotes/syncing-server@1.6.64
2022-09-07 14:18:45 +00:00
Karol Sójko
7132dc3ac0 feat(analytics): add calculation retention for two activities 2022-09-07 16:16:27 +02:00
standardci
956d5be959 chore(release): publish new version
- @standardnotes/api-gateway@1.16.4
2022-09-07 13:43:04 +00:00
Karol Sójko
936591d40b fix(api-gateway): add registration-to-subscription time to analytics report 2022-09-07 15:41:02 +02:00
standardci
686e4f8ddf chore(release): publish new version
- @standardnotes/analytics@1.23.0
 - @standardnotes/api-gateway@1.16.3
 - @standardnotes/auth-server@1.25.0
 - @standardnotes/syncing-server@1.6.63
2022-09-07 12:36:46 +00:00
Karol Sójko
b61825235e feat(auth): add measuring registration to subscription time statistics 2022-09-07 14:34:45 +02:00
standardci
8157f324a0 chore(release): publish new version
- @standardnotes/auth-server@1.24.4
2022-09-07 08:37:22 +00:00
Karol Sójko
132b617aaa fix(auth): forbid users on shared subscription to send out invitations 2022-09-07 10:35:06 +02:00
standardci
25b1f3e9ea chore(release): publish new version
- @standardnotes/api-gateway@1.16.2
 - @standardnotes/auth-server@1.24.3
 - @standardnotes/domain-events-infra@1.8.3
 - @standardnotes/domain-events@2.59.1
 - @standardnotes/event-store@1.3.6
 - @standardnotes/files-server@1.5.45
 - @standardnotes/scheduler-server@1.10.22
 - @standardnotes/syncing-server@1.6.62
2022-09-06 10:30:52 +00:00
Karol Sójko
f94c8fc26e fix(api-gateway): period types on analytics report 2022-09-06 12:28:48 +02:00
Karol Sójko
d149f46cf6 fix(api-gateway): include period key in statistics measures 2022-09-06 12:16:07 +02:00
standardci
6a24ba5d56 chore(release): publish new version
- @standardnotes/api-gateway@1.16.1
 - @standardnotes/auth-server@1.24.2
 - @standardnotes/event-store@1.3.5
 - @standardnotes/files-server@1.5.44
 - @standardnotes/scheduler-server@1.10.21
 - @standardnotes/syncing-server@1.6.61
 - @standardnotes/time@1.11.0
2022-09-06 10:10:20 +00:00
Karol Sójko
db8333a858 feat(time): add converting microseconds to time structure 2022-09-06 12:08:39 +02:00
standardci
3af254d7c7 chore(release): publish new version
- @standardnotes/api-gateway@1.16.0
 - @standardnotes/auth-server@1.24.1
 - @standardnotes/domain-events-infra@1.8.2
 - @standardnotes/domain-events@2.59.0
 - @standardnotes/event-store@1.3.4
 - @standardnotes/files-server@1.5.43
 - @standardnotes/scheduler-server@1.10.20
 - @standardnotes/syncing-server@1.6.60
2022-09-06 09:28:23 +00:00
Karol Sójko
8151bb108a feat(api-gateway): add statistics measures to report generation 2022-09-06 11:26:27 +02:00
Karol Sójko
3b18769c2d chore(deps): upgrade uuid 2022-09-06 11:26:27 +02:00
standardci
2883cac6d4 chore(release): publish new version
- @standardnotes/analytics@1.22.0
 - @standardnotes/api-gateway@1.15.12
 - @standardnotes/auth-server@1.24.0
 - @standardnotes/domain-events-infra@1.8.1
 - @standardnotes/domain-events@2.58.0
 - @standardnotes/event-store@1.3.3
 - @standardnotes/files-server@1.5.42
 - @standardnotes/scheduler-server@1.10.19
 - @standardnotes/syncing-server@1.6.59
2022-09-06 08:56:45 +00:00
Karol Sójko
d7ae2f0625 feat(auth): add statistics for refunds and account deletions 2022-09-06 10:54:50 +02:00
standardci
318f6d0986 chore(release): publish new version
- @standardnotes/auth-server@1.23.2
2022-09-06 07:42:31 +00:00
Karol Sójko
2ca430f40c fix(auth): add debug logs for subscription canceling 2022-09-06 09:40:57 +02:00
standardci
fd65060a8e chore(release): publish new version
- @standardnotes/analytics@1.21.1
 - @standardnotes/api-gateway@1.15.11
 - @standardnotes/auth-server@1.23.1
 - @standardnotes/syncing-server@1.6.58
2022-09-06 07:36:07 +00:00
Karol Sójko
cb81f819ba fix(analytics): increment by float instead of integer on measures 2022-09-06 09:34:13 +02:00
standardci
61c7040e4b chore(release): publish new version
- @standardnotes/analytics@1.21.0
 - @standardnotes/api-gateway@1.15.10
 - @standardnotes/auth-server@1.23.0
 - @standardnotes/syncing-server@1.6.57
2022-09-05 19:14:47 +00:00
Karol Sójko
fa10827443 feat(auth): add measuring subscription length 2022-09-05 21:13:07 +02:00
standardci
bcee779e74 chore(release): publish new version
- @standardnotes/auth-server@1.22.1
2022-09-05 15:31:41 +00:00
Karol Sójko
34315c91d7 fix(auth): StatisticsStore binding 2022-09-05 17:29:34 +02:00
standardci
8d3bf6c4a5 chore(release): publish new version
- @standardnotes/analytics@1.20.0
 - @standardnotes/api-gateway@1.15.9
 - @standardnotes/auth-server@1.22.0
 - @standardnotes/common@1.31.0
 - @standardnotes/domain-events-infra@1.8.0
 - @standardnotes/domain-events@2.57.0
 - @standardnotes/event-store@1.3.2
 - @standardnotes/files-server@1.5.41
 - @standardnotes/predicates@1.4.0
 - @standardnotes/scheduler-server@1.10.18
 - @standardnotes/security@1.3.0
 - @standardnotes/sncrypto-node@1.11.0
 - @standardnotes/syncing-server@1.6.56
 - @standardnotes/time@1.10.0
2022-09-05 14:57:25 +00:00
Karol Sójko
0c176b70f8 feat(auth): add keeping stats on payments 2022-09-05 16:55:53 +02:00
standardci
87a5854357 chore(release): publish new version
- @standardnotes/api-gateway@1.15.8
 - @standardnotes/auth-server@1.21.5
 - @standardnotes/domain-events-infra@1.7.37
 - @standardnotes/domain-events@2.56.0
 - @standardnotes/event-store@1.3.1
 - @standardnotes/files-server@1.5.40
 - @standardnotes/scheduler-server@1.10.17
 - @standardnotes/syncing-server@1.6.55
2022-09-05 14:47:52 +00:00
Karol Sójko
9c2d51d718 feat(domain-events): add amount of dollars to payment success event 2022-09-05 16:46:20 +02:00
standardci
e618f046ea chore(release): publish new version
- @standardnotes/analytics@1.19.0
 - @standardnotes/api-gateway@1.15.7
 - @standardnotes/auth-server@1.21.4
 - @standardnotes/syncing-server@1.6.54
2022-09-05 14:41:15 +00:00
Karol Sójko
a36cb925ff feat(analytics): add statistics measurements tracking 2022-09-05 16:39:44 +02:00
standardci
9e2aea2793 chore(release): publish new version
- @standardnotes/event-store@1.3.0
2022-09-02 09:12:05 +00:00
Karol Sójko
ef1e2bb5ed feat(event-store): add listening to subscription reverts 2022-09-02 11:10:33 +02:00
standardci
6a457281ea chore(release): publish new version
- @standardnotes/api-gateway@1.15.6
 - @standardnotes/auth-server@1.21.3
 - @standardnotes/domain-events-infra@1.7.36
 - @standardnotes/domain-events@2.55.1
 - @standardnotes/event-store@1.2.3
 - @standardnotes/files-server@1.5.39
 - @standardnotes/scheduler-server@1.10.16
 - @standardnotes/syncing-server@1.6.53
2022-09-01 10:11:46 +00:00
Karol Sójko
41c512798d fix(domain-events): add admin-panel as event source option 2022-09-01 12:10:00 +02:00
standardci
ffa0f51305 chore(release): publish new version
- @standardnotes/api-gateway@1.15.5
 - @standardnotes/auth-server@1.21.2
 - @standardnotes/domain-events-infra@1.7.35
 - @standardnotes/domain-events@2.55.0
 - @standardnotes/event-store@1.2.2
 - @standardnotes/files-server@1.5.38
 - @standardnotes/scheduler-server@1.10.15
 - @standardnotes/syncing-server@1.6.52
2022-09-01 09:02:44 +00:00
Karol Sójko
e0cec9e24a feat(domain-events): add subscription revert requested event 2022-09-01 11:00:45 +02:00
standardci
f6b359a772 chore(release): publish new version
- @standardnotes/scheduler-server@1.10.14
2022-08-30 12:53:27 +00:00
Karol Sójko
648eb89c7c fix(scheduler): linter issues 2022-08-30 14:51:54 +02:00
standardci
ba22e085b8 chore(release): publish new version
- @standardnotes/scheduler-server@1.10.13
2022-08-30 12:46:35 +00:00
Karol Sójko
35373db1d3 fix(scheduler): skip sending exit survery when a subscription is replaced 2022-08-30 14:44:54 +02:00
standardci
932cfa7200 chore(release): publish new version
- @standardnotes/api-gateway@1.15.4
 - @standardnotes/auth-server@1.21.1
 - @standardnotes/domain-events-infra@1.7.34
 - @standardnotes/domain-events@2.54.2
 - @standardnotes/event-store@1.2.1
 - @standardnotes/files-server@1.5.37
 - @standardnotes/scheduler-server@1.10.12
 - @standardnotes/syncing-server@1.6.51
2022-08-30 12:35:32 +00:00
Karol Sójko
932ef933fc fix(domain-events): add replaced boolean to subscription canceled event 2022-08-30 14:33:59 +02:00
standardci
4f1293525c chore(release): publish new version
- @standardnotes/event-store@1.2.0
2022-08-30 08:02:20 +00:00
Karol Sójko
dd6bec8a0c feat(event-store): add account claim events subscription 2022-08-30 10:00:48 +02:00
standardci
1abca64765 chore(release): publish new version
- @standardnotes/scheduler-server@1.10.11
2022-08-29 12:40:41 +00:00
Karol Sójko
dbe55d89ec fix(scheduler): change logs severity to reduce noise 2022-08-29 14:39:04 +02:00
Karol Sójko
dcb3ad661c chore(setup): node version setting up on workflows 2022-08-29 14:20:39 +02:00
standardci
1e1f6cb4a3 chore(release): publish new version
- @standardnotes/files-server@1.5.36
2022-08-29 11:59:12 +00:00
Karol Sójko
83d96fd71d chore(deps): upgrade ts-node 2022-08-29 13:57:43 +02:00
standardci
7dc4670028 chore(release): publish new version
- @standardnotes/auth-server@1.21.0
2022-08-29 11:20:16 +00:00
Karol Sójko
dc88e2413b feat(auth): retain user agent, api version and received at on revoked sessions 2022-08-29 13:18:05 +02:00
standardci
b7f7c3f164 chore(release): publish new version
- @standardnotes/api-gateway@1.15.3
2022-08-22 08:37:14 +00:00
Karol Sójko
f7def38e20 fix(api-gateway): add error logs on missing connection id for websockets 2022-08-22 10:35:38 +02:00
Karol Sójko
cf49e1ff74 chore(deps): remove reviewers assignment from dependabot to avoid noise 2022-08-17 10:06:22 +02:00
standardci
38de2d6b30 chore(release): publish new version
- @standardnotes/auth-server@1.20.1
 - @standardnotes/scheduler-server@1.10.10
2022-08-17 06:53:26 +00:00
Karol Sójko
4b3de264ef fix(scheduler): change discount from 10% to 20% on a limited time offer 2022-08-17 08:51:35 +02:00
standardci
4bb785c7f0 chore(release): publish new version
- @standardnotes/analytics@1.18.1
 - @standardnotes/api-gateway@1.15.2
 - @standardnotes/auth-server@1.20.0
 - @standardnotes/domain-events-infra@1.7.33
 - @standardnotes/domain-events@2.54.1
 - @standardnotes/event-store@1.1.33
 - @standardnotes/files-server@1.5.35
 - @standardnotes/scheduler-server@1.10.9
 - @standardnotes/syncing-server@1.6.50
2022-08-15 16:37:33 +00:00
Karol Sójko
2fb904d2cb fix(domain-events): missing exports 2022-08-15 18:35:52 +02:00
Karol Sójko
ee79347e27 fix(api-gateway): add payment success events to report 2022-08-15 18:27:44 +02:00
Karol Sójko
3477c81d37 fix(event-store): add payment events handling 2022-08-15 18:26:41 +02:00
Karol Sójko
930789316c fix(analytics): add payment success activity 2022-08-15 18:24:53 +02:00
Karol Sójko
01a08eae58 feat(auth): add payment success event handler 2022-08-15 18:24:13 +02:00
standardci
d73c9833ab chore(release): publish new version
- @standardnotes/api-gateway@1.15.1
 - @standardnotes/auth-server@1.19.1
 - @standardnotes/domain-events-infra@1.7.32
 - @standardnotes/domain-events@2.54.0
 - @standardnotes/event-store@1.1.32
 - @standardnotes/files-server@1.5.34
 - @standardnotes/scheduler-server@1.10.8
 - @standardnotes/syncing-server@1.6.49
2022-08-15 15:03:28 +00:00
Karol Sójko
1841597405 feat(domain-events): add payment success event 2022-08-15 17:01:32 +02:00
standardci
8003e5ce43 chore(release): publish new version
- @standardnotes/api-gateway@1.15.0
2022-08-15 12:39:40 +00:00
Karol Sójko
d0023a6c92 feat(api-gateway): add gathering analytics for failed payments 2022-08-15 14:38:04 +02:00
standardci
a9293f6ce1 chore(release): publish new version
- @standardnotes/analytics@1.18.0
 - @standardnotes/api-gateway@1.14.3
 - @standardnotes/auth-server@1.19.0
 - @standardnotes/syncing-server@1.6.48
2022-08-15 11:12:04 +00:00
Karol Sójko
58c5b586a9 feat(auth): add payment failed event handler 2022-08-15 13:10:36 +02:00
103 changed files with 1657 additions and 156 deletions

View File

@@ -9,134 +9,83 @@ updates:
directory: "/"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/analytics"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/api-gateway"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/auth"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/common"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/domain-events"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/domain-events-infra"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/event-store"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/files"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/predicates"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/scheduler"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/security"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/settings"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/sncrypto-node"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/syncing-server"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "npm"
directory: "/packages/time"
schedule:
interval: "daily"
reviewers:
- "moughxyz"
- "karolsojko"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
reviewers:
- "moughxyz"
- "karolsojko"

View File

@@ -60,6 +60,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Build locally
run: yarn build
- name: Configure AWS credentials
@@ -90,6 +95,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Build locally
run: yarn build
- name: Login to Docker Hub

View File

@@ -63,6 +63,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Build locally
run: yarn build
- name: Configure AWS credentials
@@ -93,6 +98,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Build locally
run: yarn build
- name: Login to Docker Hub

View File

@@ -32,6 +32,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Build locally
run: yarn build
- name: Configure AWS credentials
@@ -62,6 +67,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Build locally
run: yarn build
- name: Login to Docker Hub

View File

@@ -63,6 +63,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Build locally
run: yarn build
- name: Configure AWS credentials
@@ -93,6 +98,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Build locally
run: yarn build
- name: Login to Docker Hub

View File

@@ -11,6 +11,11 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: ESLint
run: yarn lint
- name: Build

View File

@@ -32,6 +32,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Build locally
run: yarn build
- name: Configure AWS credentials
@@ -62,6 +67,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Build locally
run: yarn build
- name: Login to Docker Hub

View File

@@ -14,9 +14,11 @@ jobs:
with:
ref: main
token: ${{ secrets.CI_PAT_TOKEN }}
- uses: actions/setup-node@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Setup git config
run: |
git config --global user.name "standardci"
@@ -43,4 +45,4 @@ jobs:
commit-message: "${{ 'chore(deps): upgrade snjs' }}"
delete-branch: true
committer: standardci <ci@standardnotes.com>
author: standardci <ci@standardnotes.com>
author: standardci <ci@standardnotes.com>

View File

@@ -63,6 +63,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Build locally
run: yarn build
- name: Configure AWS credentials
@@ -93,6 +98,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version-file: '.nvmrc'
- name: Build locally
run: yarn build
- name: Login to Docker Hub

73
.pnp.cjs generated
View File

@@ -124,7 +124,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["newrelic", "npm:9.0.0"],\
["npm-check-updates", "npm:16.0.1"],\
["prettier", "npm:2.7.1"],\
["ts-node", "virtual:c0eab07e71af57f5501e97e7ca7a2a4f4965035bd2455ad124a8b09fa55780657c55fe3df41019fa6c2c44487c897668c842a0939e380b3c1f13b3756d128543#npm:10.8.2"],\
["ts-node", "virtual:8859b278716fedf3e7458b5628625f7e35678c418626878559a0b816445001b7e24c55546f4677ba4c20b521aa0cf52cc33ac07deff171e383ada6eeab69933f#npm:10.9.1"],\
["typescript", "patch:typescript@npm%3A4.7.4#~builtin<compat/typescript>::version=4.7.4&hash=7ad353"]\
],\
"linkType": "SOFT"\
@@ -2607,7 +2607,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["ts-jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.0.5"],\
["typeorm", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:0.3.7"],\
["ua-parser-js", "npm:1.0.2"],\
["uuid", "npm:8.3.2"],\
["uuid", "npm:9.0.0"],\
["winston", "npm:3.8.1"]\
],\
"linkType": "SOFT"\
@@ -2788,8 +2788,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["prettyjson", "npm:1.2.5"],\
["reflect-metadata", "npm:0.1.13"],\
["ts-jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.0.5"],\
["ts-node", "virtual:b442cf0427cc365d1c137f7340f9b81f9b204561afe791a8564ae9590c3a7fc4b5f793aaf8817b946f75a3cb64d03ef8790eb847f8b576b41e700da7b00c240c#npm:10.8.2"],\
["uuid", "npm:8.3.2"],\
["ts-node", "virtual:b442cf0427cc365d1c137f7340f9b81f9b204561afe791a8564ae9590c3a7fc4b5f793aaf8817b946f75a3cb64d03ef8790eb847f8b576b41e700da7b00c240c#npm:10.9.1"],\
["uuid", "npm:9.0.0"],\
["winston", "npm:3.8.1"]\
],\
"linkType": "SOFT"\
@@ -2866,11 +2866,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/jest", "npm:28.1.4"],\
["@types/newrelic", "npm:7.0.3"],\
["@types/node", "npm:18.0.3"],\
["@typescript-eslint/eslint-plugin", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:5.30.5"],\
["@typescript-eslint/eslint-plugin", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:5.30.5"],\
["aws-sdk", "npm:2.1168.0"],\
["dayjs", "npm:1.11.3"],\
["dotenv", "npm:16.0.1"],\
["eslint-plugin-prettier", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.2.1"],\
["eslint", "npm:8.19.0"],\
["eslint-plugin-prettier", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.2.1"],\
["inversify", "npm:6.0.1"],\
["ioredis", "npm:5.2.0"],\
["jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.1.2"],\
@@ -2924,7 +2925,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["newrelic", "npm:9.0.0"],\
["npm-check-updates", "npm:16.0.1"],\
["prettier", "npm:2.7.1"],\
["ts-node", "virtual:c0eab07e71af57f5501e97e7ca7a2a4f4965035bd2455ad124a8b09fa55780657c55fe3df41019fa6c2c44487c897668c842a0939e380b3c1f13b3756d128543#npm:10.8.2"],\
["ts-node", "virtual:8859b278716fedf3e7458b5628625f7e35678c418626878559a0b816445001b7e24c55546f4677ba4c20b521aa0cf52cc33ac07deff171e383ada6eeab69933f#npm:10.9.1"],\
["typescript", "patch:typescript@npm%3A4.7.4#~builtin<compat/typescript>::version=4.7.4&hash=7ad353"]\
],\
"linkType": "SOFT"\
@@ -3035,7 +3036,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["ts-jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.0.5"],\
["typeorm", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:0.3.7"],\
["ua-parser-js", "npm:1.0.2"],\
["uuid", "npm:8.3.2"],\
["uuid", "npm:9.0.0"],\
["winston", "npm:3.8.1"]\
],\
"linkType": "SOFT"\
@@ -12187,10 +12188,53 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
],\
"linkType": "SOFT"\
}],\
["virtual:b442cf0427cc365d1c137f7340f9b81f9b204561afe791a8564ae9590c3a7fc4b5f793aaf8817b946f75a3cb64d03ef8790eb847f8b576b41e700da7b00c240c#npm:10.8.2", {\
"packageLocation": "./.yarn/__virtual__/ts-node-virtual-8a01a45377/0/cache/ts-node-npm-10.8.2-f3c0c9eaee-1eede939be.zip/node_modules/ts-node/",\
["npm:10.9.1", {\
"packageLocation": "./.yarn/cache/ts-node-npm-10.9.1-6c268be7f4-090adff130.zip/node_modules/ts-node/",\
"packageDependencies": [\
["ts-node", "virtual:b442cf0427cc365d1c137f7340f9b81f9b204561afe791a8564ae9590c3a7fc4b5f793aaf8817b946f75a3cb64d03ef8790eb847f8b576b41e700da7b00c240c#npm:10.8.2"],\
["ts-node", "npm:10.9.1"]\
],\
"linkType": "SOFT"\
}],\
["virtual:8859b278716fedf3e7458b5628625f7e35678c418626878559a0b816445001b7e24c55546f4677ba4c20b521aa0cf52cc33ac07deff171e383ada6eeab69933f#npm:10.9.1", {\
"packageLocation": "./.yarn/__virtual__/ts-node-virtual-ac01688ebc/0/cache/ts-node-npm-10.9.1-6c268be7f4-090adff130.zip/node_modules/ts-node/",\
"packageDependencies": [\
["ts-node", "virtual:8859b278716fedf3e7458b5628625f7e35678c418626878559a0b816445001b7e24c55546f4677ba4c20b521aa0cf52cc33ac07deff171e383ada6eeab69933f#npm:10.9.1"],\
["@cspotcode/source-map-support", "npm:0.8.1"],\
["@swc/core", null],\
["@swc/wasm", null],\
["@tsconfig/node10", "npm:1.0.9"],\
["@tsconfig/node12", "npm:1.0.11"],\
["@tsconfig/node14", "npm:1.0.3"],\
["@tsconfig/node16", "npm:1.0.3"],\
["@types/node", "npm:18.0.3"],\
["@types/swc__core", null],\
["@types/swc__wasm", null],\
["@types/typescript", null],\
["acorn", "npm:8.7.1"],\
["acorn-walk", "npm:8.2.0"],\
["arg", "npm:4.1.3"],\
["create-require", "npm:1.1.1"],\
["diff", "npm:4.0.2"],\
["make-error", "npm:1.3.6"],\
["typescript", "patch:typescript@npm%3A4.7.4#~builtin<compat/typescript>::version=4.7.4&hash=7ad353"],\
["v8-compile-cache-lib", "npm:3.0.1"],\
["yn", "npm:3.1.1"]\
],\
"packagePeers": [\
"@swc/core",\
"@swc/wasm",\
"@types/node",\
"@types/swc__core",\
"@types/swc__wasm",\
"@types/typescript",\
"typescript"\
],\
"linkType": "HARD"\
}],\
["virtual:b442cf0427cc365d1c137f7340f9b81f9b204561afe791a8564ae9590c3a7fc4b5f793aaf8817b946f75a3cb64d03ef8790eb847f8b576b41e700da7b00c240c#npm:10.9.1", {\
"packageLocation": "./.yarn/__virtual__/ts-node-virtual-c4e9951caa/0/cache/ts-node-npm-10.9.1-6c268be7f4-090adff130.zip/node_modules/ts-node/",\
"packageDependencies": [\
["ts-node", "virtual:b442cf0427cc365d1c137f7340f9b81f9b204561afe791a8564ae9590c3a7fc4b5f793aaf8817b946f75a3cb64d03ef8790eb847f8b576b41e700da7b00c240c#npm:10.9.1"],\
["@cspotcode/source-map-support", "npm:0.8.1"],\
["@swc/core", null],\
["@swc/wasm", null],\
@@ -12735,6 +12779,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["uuid", "npm:8.3.2"]\
],\
"linkType": "HARD"\
}],\
["npm:9.0.0", {\
"packageLocation": "./.yarn/cache/uuid-npm-9.0.0-46c41e3e43-8dd2c83c43.zip/node_modules/uuid/",\
"packageDependencies": [\
["uuid", "npm:9.0.0"]\
],\
"linkType": "HARD"\
}]\
]],\
["v8-compile-cache", [\

Binary file not shown.

Binary file not shown.

View File

@@ -60,7 +60,7 @@
"ini": "^3.0.0",
"npm-check-updates": "^16.0.1",
"prettier": "^2.7.1",
"ts-node": "^10.8.1",
"ts-node": "^10.9.1",
"typescript": "^4.7.4"
},
"packageManager": "yarn@3.2.1",

View File

@@ -3,6 +3,60 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.24.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.23.0...@standardnotes/analytics@1.24.0) (2022-09-07)
### Features
* **analytics:** add calculation retention for two activities ([7132dc3](https://github.com/standardnotes/server/commit/7132dc3ac0cf878d2c326243747343e8a6746e2f))
# [1.23.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.22.0...@standardnotes/analytics@1.23.0) (2022-09-07)
### Features
* **auth:** add measuring registration to subscription time statistics ([b618252](https://github.com/standardnotes/server/commit/b61825235eebaf5eddb55cbda173176ca43c0099))
# [1.22.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.21.1...@standardnotes/analytics@1.22.0) (2022-09-06)
### Features
* **auth:** add statistics for refunds and account deletions ([d7ae2f0](https://github.com/standardnotes/server/commit/d7ae2f06255b19eb5d3403a4989610390064754e))
## [1.21.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.21.0...@standardnotes/analytics@1.21.1) (2022-09-06)
### Bug Fixes
* **analytics:** increment by float instead of integer on measures ([cb81f81](https://github.com/standardnotes/server/commit/cb81f819ba30a45f27ec344480b5ef22e5a0a50d))
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.20.0...@standardnotes/analytics@1.21.0) (2022-09-05)
### Features
* **auth:** add measuring subscription length ([fa10827](https://github.com/standardnotes/server/commit/fa108274430d8dff1016ddcba5bbcb2778eb781b))
# [1.20.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.19.0...@standardnotes/analytics@1.20.0) (2022-09-05)
### Features
* **auth:** add keeping stats on payments ([0c176b7](https://github.com/standardnotes/server/commit/0c176b70f8281e1e490224b9c7ab85f272a3d4e9))
# [1.19.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.18.1...@standardnotes/analytics@1.19.0) (2022-09-05)
### Features
* **analytics:** add statistics measurements tracking ([a36cb92](https://github.com/standardnotes/server/commit/a36cb925ff3bd8396a53f58c3e954549e904d694))
## [1.18.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.18.0...@standardnotes/analytics@1.18.1) (2022-08-15)
### Bug Fixes
* **analytics:** add payment success activity ([9307893](https://github.com/standardnotes/server/commit/930789316c2eec8227f26e75d4917796168f2d08))
# [1.18.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.17.2...@standardnotes/analytics@1.18.0) (2022-08-15)
### Features
* **auth:** add payment failed event handler ([58c5b58](https://github.com/standardnotes/server/commit/58c5b586a904cf1fd179cc28783a6ae7da688063))
## [1.17.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.17.1...@standardnotes/analytics@1.17.2) (2022-08-15)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "1.17.2",
"version": "1.24.0",
"engines": {
"node": ">=14.0.0 <17.0.0"
},
@@ -23,7 +23,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage"
"test": "jest spec --coverage"
},
"devDependencies": {
"@types/ioredis": "^4.28.10",

View File

@@ -11,4 +11,6 @@ export enum AnalyticsActivity {
EmailUnbackedUpData = 'email-unbacked-up-data',
EmailBackup = 'email-backup',
LimitedDiscountOfferPurchased = 'limited-discount-offer-purchased',
PaymentFailed = 'payment-failed',
PaymentSuccess = 'payment-success',
}

View File

@@ -6,6 +6,12 @@ export interface AnalyticsStoreInterface {
markActivity(activities: AnalyticsActivity[], analyticsId: number, periods: Period[]): Promise<void>
wasActivityDone(activity: AnalyticsActivity, analyticsId: number, period: Period): Promise<boolean>
calculateActivityRetention(activity: AnalyticsActivity, firstPeriod: Period, secondPeriod: Period): Promise<number>
calculateActivitiesRetention(parameters: {
firstActivity: AnalyticsActivity
firstActivityPeriodKey: string
secondActivity: AnalyticsActivity
secondActivityPeriodKey: string
}): Promise<number>
calculateActivityTotalCount(activity: AnalyticsActivity, period: Period): Promise<number>
calculateActivityChangesTotalCount(
activity: AnalyticsActivity,

View File

@@ -0,0 +1,7 @@
export enum StatisticsMeasure {
Income = 'income',
SubscriptionLength = 'subscription-length',
RegistrationLength = 'registration-length',
RegistrationToSubscriptionTime = 'registration-to-subscription-time',
Refunds = 'refunds',
}

View File

@@ -1,3 +1,6 @@
import { Period } from '../Time/Period'
import { StatisticsMeasure } from './StatisticsMeasure'
export interface StatisticsStoreInterface {
incrementSNJSVersionUsage(snjsVersion: string): Promise<void>
incrementApplicationVersionUsage(applicationVersion: string): Promise<void>
@@ -5,4 +8,7 @@ export interface StatisticsStoreInterface {
getYesterdaySNJSUsage(): Promise<Array<{ version: string; count: number }>>
getYesterdayApplicationUsage(): Promise<Array<{ version: string; count: number }>>
getYesterdayOutOfSyncIncidents(): Promise<number>
incrementMeasure(measure: StatisticsMeasure, value: number, periods: Period[]): Promise<void>
getMeasureAverage(measure: StatisticsMeasure, period: Period): Promise<number>
getMeasureTotal(measure: StatisticsMeasure, period: Period): Promise<number>
}

View File

@@ -1,5 +1,6 @@
export * from './Analytics/AnalyticsActivity'
export * from './Analytics/AnalyticsStoreInterface'
export * from './Statistics/StatisticsMeasure'
export * from './Statistics/StatisticsStoreInterface'
export * from './Time/Period'
export * from './Time/PeriodKeyGenerator'

View File

@@ -125,7 +125,7 @@ describe('RedisAnalyticsStore', () => {
expect(redisClient.bitop).toHaveBeenCalledWith(
'AND',
'bitmap:action:editing-items:timespan:period-key-period-key',
'bitmap:action:editing-items-editing-items:timespan:period-key',
'bitmap:action:editing-items:timespan:period-key',
'bitmap:action:editing-items:timespan:period-key',
)

View File

@@ -95,21 +95,19 @@ export class RedisAnalyticsStore implements AnalyticsStoreInterface {
return bitValue === 1
}
async calculateActivityRetention(
activity: AnalyticsActivity,
firstPeriod: Period,
secondPeriod: Period,
): Promise<number> {
const initialPeriodKey = this.periodKeyGenerator.getPeriodKey(firstPeriod)
const subsequentPeriodKey = this.periodKeyGenerator.getPeriodKey(secondPeriod)
const diffKey = `bitmap:action:${activity}:timespan:${initialPeriodKey}-${subsequentPeriodKey}`
async calculateActivitiesRetention(parameters: {
firstActivity: AnalyticsActivity
firstActivityPeriodKey: string
secondActivity: AnalyticsActivity
secondActivityPeriodKey: string
}): Promise<number> {
const diffKey = `bitmap:action:${parameters.firstActivity}-${parameters.secondActivity}:timespan:${parameters.secondActivityPeriodKey}`
await this.redisClient.bitop(
'AND',
diffKey,
`bitmap:action:${activity}:timespan:${initialPeriodKey}`,
`bitmap:action:${activity}:timespan:${subsequentPeriodKey}`,
`bitmap:action:${parameters.firstActivity}:timespan:${parameters.firstActivityPeriodKey}`,
`bitmap:action:${parameters.secondActivity}:timespan:${parameters.secondActivityPeriodKey}`,
)
await this.redisClient.expire(diffKey, 3600)
@@ -117,12 +115,25 @@ export class RedisAnalyticsStore implements AnalyticsStoreInterface {
const retainedTotalInActivity = await this.redisClient.bitcount(diffKey)
const initialTotalInActivity = await this.redisClient.bitcount(
`bitmap:action:${activity}:timespan:${initialPeriodKey}`,
`bitmap:action:${parameters.firstActivity}:timespan:${parameters.firstActivityPeriodKey}`,
)
return Math.ceil((retainedTotalInActivity * 100) / initialTotalInActivity)
}
async calculateActivityRetention(
activity: AnalyticsActivity,
firstPeriod: Period,
secondPeriod: Period,
): Promise<number> {
return this.calculateActivitiesRetention({
firstActivity: activity,
firstActivityPeriodKey: this.periodKeyGenerator.getPeriodKey(firstPeriod),
secondActivity: activity,
secondActivityPeriodKey: this.periodKeyGenerator.getPeriodKey(secondPeriod),
})
}
async calculateActivityTotalCount(activity: AnalyticsActivity, period: Period): Promise<number> {
return this.redisClient.bitcount(
`bitmap:action:${activity}:timespan:${this.periodKeyGenerator.getPeriodKey(period)}`,

View File

@@ -1,5 +1,7 @@
import * as IORedis from 'ioredis'
import { PeriodKeyGeneratorInterface } from '../../Domain'
import { Period, PeriodKeyGeneratorInterface } from '../../Domain'
import { StatisticsMeasure } from '../../Domain/Statistics/StatisticsMeasure'
import { RedisStatisticsStore } from './RedisStatisticsStore'
@@ -13,6 +15,7 @@ describe('RedisStatisticsStore', () => {
beforeEach(() => {
pipeline = {} as jest.Mocked<IORedis.Pipeline>
pipeline.incr = jest.fn()
pipeline.incrbyfloat = jest.fn()
pipeline.setbit = jest.fn()
pipeline.exec = jest.fn()
@@ -88,4 +91,30 @@ describe('RedisStatisticsStore', () => {
expect(pipeline.incr).toHaveBeenCalled()
expect(pipeline.exec).toHaveBeenCalled()
})
it('should increment measure by a value', async () => {
await createStore().incrementMeasure(StatisticsMeasure.Income, 2, [Period.Today, Period.ThisMonth])
expect(pipeline.incr).toHaveBeenCalledTimes(2)
expect(pipeline.incrbyfloat).toHaveBeenCalledTimes(2)
expect(pipeline.exec).toHaveBeenCalled()
})
it('should count a measurement average', async () => {
redisClient.get = jest.fn().mockReturnValueOnce('5').mockReturnValueOnce('2')
expect(await createStore().getMeasureAverage(StatisticsMeasure.Income, Period.Today)).toEqual(2 / 5)
})
it('should count a measurement average - 0 increments', async () => {
redisClient.get = jest.fn().mockReturnValueOnce(null).mockReturnValueOnce(null)
expect(await createStore().getMeasureAverage(StatisticsMeasure.Income, Period.Today)).toEqual(0)
})
it('should count a measurement average - 0 total value', async () => {
redisClient.get = jest.fn().mockReturnValueOnce(5).mockReturnValueOnce(null)
expect(await createStore().getMeasureAverage(StatisticsMeasure.Income, Period.Today)).toEqual(0)
})
})

View File

@@ -1,12 +1,49 @@
import * as IORedis from 'ioredis'
import { Period, PeriodKeyGeneratorInterface } from '../../Domain'
import { StatisticsMeasure } from '../../Domain/Statistics/StatisticsMeasure'
import { StatisticsStoreInterface } from '../../Domain/Statistics/StatisticsStoreInterface'
export class RedisStatisticsStore implements StatisticsStoreInterface {
constructor(private periodKeyGenerator: PeriodKeyGeneratorInterface, private redisClient: IORedis.Redis) {}
async getMeasureTotal(measure: StatisticsMeasure, period: Period): Promise<number> {
const totalValue = await this.redisClient.get(
`count:measure:${measure}:timespan:${this.periodKeyGenerator.getPeriodKey(period)}`,
)
if (totalValue === null) {
return 0
}
return +totalValue
}
async incrementMeasure(measure: StatisticsMeasure, value: number, periods: Period[]): Promise<void> {
const pipeline = this.redisClient.pipeline()
for (const period of periods) {
pipeline.incrbyfloat(`count:measure:${measure}:timespan:${this.periodKeyGenerator.getPeriodKey(period)}`, value)
pipeline.incr(`count:increments:${measure}:timespan:${this.periodKeyGenerator.getPeriodKey(period)}`)
}
await pipeline.exec()
}
async getMeasureAverage(measure: StatisticsMeasure, period: Period): Promise<number> {
const increments = await this.redisClient.get(
`count:increments:${measure}:timespan:${this.periodKeyGenerator.getPeriodKey(period)}`,
)
if (increments === null) {
return 0
}
const totalValue = await this.getMeasureTotal(measure, period)
return totalValue / +increments
}
async getYesterdayOutOfSyncIncidents(): Promise<number> {
const count = await this.redisClient.get(
`count:action:out-of-sync:timespan:${this.periodKeyGenerator.getPeriodKey(Period.Yesterday)}`,

View File

@@ -3,6 +3,99 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.16.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.16.4...@standardnotes/api-gateway@1.16.5) (2022-09-07)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.16.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.16.3...@standardnotes/api-gateway@1.16.4) (2022-09-07)
### Bug Fixes
* **api-gateway:** add registration-to-subscription time to analytics report ([936591d](https://github.com/standardnotes/api-gateway/commit/936591d40b5f5beb5c0a824c92cdfa20fff51c97))
## [1.16.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.16.2...@standardnotes/api-gateway@1.16.3) (2022-09-07)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.16.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.16.1...@standardnotes/api-gateway@1.16.2) (2022-09-06)
### Bug Fixes
* **api-gateway:** include period key in statistics measures ([d149f46](https://github.com/standardnotes/api-gateway/commit/d149f46cf6456201dd8690977f64ed32a75f3459))
* **api-gateway:** period types on analytics report ([f94c8fc](https://github.com/standardnotes/api-gateway/commit/f94c8fc26e684a07101cc5282ebb9cda3c8c6961))
## [1.16.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.16.0...@standardnotes/api-gateway@1.16.1) (2022-09-06)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.16.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.12...@standardnotes/api-gateway@1.16.0) (2022-09-06)
### Features
* **api-gateway:** add statistics measures to report generation ([8151bb1](https://github.com/standardnotes/api-gateway/commit/8151bb108affb2b5cfa1ab365f99a9f0170a7795))
## [1.15.12](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.11...@standardnotes/api-gateway@1.15.12) (2022-09-06)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.15.11](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.10...@standardnotes/api-gateway@1.15.11) (2022-09-06)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.15.10](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.9...@standardnotes/api-gateway@1.15.10) (2022-09-05)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.15.9](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.8...@standardnotes/api-gateway@1.15.9) (2022-09-05)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.15.8](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.7...@standardnotes/api-gateway@1.15.8) (2022-09-05)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.15.7](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.6...@standardnotes/api-gateway@1.15.7) (2022-09-05)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.15.6](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.5...@standardnotes/api-gateway@1.15.6) (2022-09-01)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.15.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.4...@standardnotes/api-gateway@1.15.5) (2022-09-01)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.15.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.3...@standardnotes/api-gateway@1.15.4) (2022-08-30)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.15.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.2...@standardnotes/api-gateway@1.15.3) (2022-08-22)
### Bug Fixes
* **api-gateway:** add error logs on missing connection id for websockets ([f7def38](https://github.com/standardnotes/api-gateway/commit/f7def38e20f87ae24ebc736a41bc7cac53b0c61f))
## [1.15.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.1...@standardnotes/api-gateway@1.15.2) (2022-08-15)
### Bug Fixes
* **api-gateway:** add payment success events to report ([ee79347](https://github.com/standardnotes/api-gateway/commit/ee79347e27f5887def2cda57091a7c0a40570d33))
## [1.15.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.0...@standardnotes/api-gateway@1.15.1) (2022-08-15)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.15.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.14.3...@standardnotes/api-gateway@1.15.0) (2022-08-15)
### Features
* **api-gateway:** add gathering analytics for failed payments ([d0023a6](https://github.com/standardnotes/api-gateway/commit/d0023a6c92756c81b8daa9089d38141b6cd4fe48))
## [1.14.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.14.2...@standardnotes/api-gateway@1.14.3) (2022-08-15)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.14.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.14.1...@standardnotes/api-gateway@1.14.2) (2022-08-15)
**Note:** Version bump only for package @standardnotes/api-gateway

View File

@@ -12,7 +12,13 @@ import {
DailyAnalyticsReportGeneratedEvent,
DomainEventService,
} from '@standardnotes/domain-events'
import { AnalyticsActivity, AnalyticsStoreInterface, Period, StatisticsStoreInterface } from '@standardnotes/analytics'
import {
AnalyticsActivity,
AnalyticsStoreInterface,
Period,
StatisticsMeasure,
StatisticsStoreInterface,
} from '@standardnotes/analytics'
const requestReport = async (
analyticsStore: AnalyticsStoreInterface,
@@ -60,9 +66,10 @@ const requestReport = async (
const yesterdayActivityStatistics = []
const yesterdayActivityNames = [
AnalyticsActivity.EditingItems,
AnalyticsActivity.LimitedDiscountOfferPurchased,
AnalyticsActivity.GeneralActivity,
AnalyticsActivity.PaymentFailed,
AnalyticsActivity.PaymentSuccess,
]
for (const activityName of yesterdayActivityNames) {
@@ -77,6 +84,25 @@ const requestReport = async (
})
}
const statisticMeasureNames = [
StatisticsMeasure.Income,
StatisticsMeasure.Refunds,
StatisticsMeasure.RegistrationLength,
StatisticsMeasure.SubscriptionLength,
StatisticsMeasure.RegistrationToSubscriptionTime,
]
const statisticMeasures = []
for (const statisticMeasureName of statisticMeasureNames) {
for (const period of [Period.Yesterday, Period.ThisMonth]) {
statisticMeasures.push({
name: statisticMeasureName,
period,
totalValue: await statisticsStore.getMeasureTotal(statisticMeasureName, period),
average: await statisticsStore.getMeasureAverage(statisticMeasureName, period),
})
}
}
const event: DailyAnalyticsReportGeneratedEvent = {
type: 'DAILY_ANALYTICS_REPORT_GENERATED',
createdAt: new Date(),
@@ -93,6 +119,7 @@ const requestReport = async (
outOfSyncIncidents: await statisticsStore.getYesterdayOutOfSyncIncidents(),
activityStatistics: yesterdayActivityStatistics,
activityStatisticsOverTime: analyticsOverTime,
statisticMeasures,
},
}

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.14.2",
"version": "1.16.5",
"engines": {
"node": ">=16.0.0 <17.0.0"
},

View File

@@ -1,18 +1,25 @@
import { Request, Response } from 'express'
import { inject } from 'inversify'
import { BaseHttpController, controller, httpDelete, httpPost } from 'inversify-express-utils'
import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types'
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
@controller('/v1/sockets')
export class WebSocketsController extends BaseHttpController {
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
constructor(
@inject(TYPES.HTTPService) private httpService: HttpServiceInterface,
@inject(TYPES.Logger) private logger: Logger,
) {
super()
}
@httpPost('/', TYPES.AuthMiddleware)
async createWebSocketConnection(request: Request, response: Response): Promise<void> {
if (!request.headers.connectionid) {
this.logger.error('Could not create a websocket connection. Missing connection id header.')
response.status(400).send('Missing connection id in the request')
return
@@ -24,6 +31,8 @@ export class WebSocketsController extends BaseHttpController {
@httpDelete('/')
async deleteWebSocketConnection(request: Request, response: Response): Promise<void> {
if (!request.headers.connectionid) {
this.logger.error('Could not delete a websocket connection. Missing connection id header.')
response.status(400).send('Missing connection id in the request')
return

View File

@@ -3,6 +3,116 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.25.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.25.0...@standardnotes/auth-server@1.25.1) (2022-09-07)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.25.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.24.4...@standardnotes/auth-server@1.25.0) (2022-09-07)
### Features
* **auth:** add measuring registration to subscription time statistics ([b618252](https://github.com/standardnotes/server/commit/b61825235eebaf5eddb55cbda173176ca43c0099))
## [1.24.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.24.3...@standardnotes/auth-server@1.24.4) (2022-09-07)
### Bug Fixes
* **auth:** forbid users on shared subscription to send out invitations ([132b617](https://github.com/standardnotes/server/commit/132b617aaa8a703877fd7e8d23711fb1ec234524))
## [1.24.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.24.2...@standardnotes/auth-server@1.24.3) (2022-09-06)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.24.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.24.1...@standardnotes/auth-server@1.24.2) (2022-09-06)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.24.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.24.0...@standardnotes/auth-server@1.24.1) (2022-09-06)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.24.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.23.2...@standardnotes/auth-server@1.24.0) (2022-09-06)
### Features
* **auth:** add statistics for refunds and account deletions ([d7ae2f0](https://github.com/standardnotes/server/commit/d7ae2f06255b19eb5d3403a4989610390064754e))
## [1.23.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.23.1...@standardnotes/auth-server@1.23.2) (2022-09-06)
### Bug Fixes
* **auth:** add debug logs for subscription canceling ([2ca430f](https://github.com/standardnotes/server/commit/2ca430f40ce6a8d56aafa27e9c2d0b0dd561c650))
## [1.23.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.23.0...@standardnotes/auth-server@1.23.1) (2022-09-06)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.23.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.22.1...@standardnotes/auth-server@1.23.0) (2022-09-05)
### Features
* **auth:** add measuring subscription length ([fa10827](https://github.com/standardnotes/server/commit/fa108274430d8dff1016ddcba5bbcb2778eb781b))
## [1.22.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.22.0...@standardnotes/auth-server@1.22.1) (2022-09-05)
### Bug Fixes
* **auth:** StatisticsStore binding ([34315c9](https://github.com/standardnotes/server/commit/34315c91d7428bbe8297e50972aa7823e2a983b2))
# [1.22.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.21.5...@standardnotes/auth-server@1.22.0) (2022-09-05)
### Features
* **auth:** add keeping stats on payments ([0c176b7](https://github.com/standardnotes/server/commit/0c176b70f8281e1e490224b9c7ab85f272a3d4e9))
## [1.21.5](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.21.4...@standardnotes/auth-server@1.21.5) (2022-09-05)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.21.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.21.3...@standardnotes/auth-server@1.21.4) (2022-09-05)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.21.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.21.2...@standardnotes/auth-server@1.21.3) (2022-09-01)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.21.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.21.1...@standardnotes/auth-server@1.21.2) (2022-09-01)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.21.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.21.0...@standardnotes/auth-server@1.21.1) (2022-08-30)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.20.1...@standardnotes/auth-server@1.21.0) (2022-08-29)
### Features
* **auth:** retain user agent, api version and received at on revoked sessions ([dc88e24](https://github.com/standardnotes/server/commit/dc88e2413b3be254b265d2874174eaac39d628ee))
## [1.20.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.20.0...@standardnotes/auth-server@1.20.1) (2022-08-17)
### Bug Fixes
* **scheduler:** change discount from 10% to 20% on a limited time offer ([4b3de26](https://github.com/standardnotes/server/commit/4b3de264efc4fffb2603181c158cddb25c4ed4a9))
# [1.20.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.19.1...@standardnotes/auth-server@1.20.0) (2022-08-15)
### Features
* **auth:** add payment success event handler ([01a08ea](https://github.com/standardnotes/server/commit/01a08eae582e070ec844f5e05f34260447b7d4c6))
## [1.19.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.19.0...@standardnotes/auth-server@1.19.1) (2022-08-15)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.19.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.18.4...@standardnotes/auth-server@1.19.0) (2022-08-15)
### Features
* **auth:** add payment failed event handler ([58c5b58](https://github.com/standardnotes/server/commit/58c5b586a904cf1fd179cc28783a6ae7da688063))
## [1.18.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.18.3...@standardnotes/auth-server@1.18.4) (2022-08-15)
**Note:** Version bump only for package @standardnotes/auth-server

View File

@@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from 'typeorm'
export class revokedSessionData1661771230400 implements MigrationInterface {
name = 'revokedSessionData1661771230400'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query('ALTER TABLE `revoked_sessions` ADD `received_at` datetime NULL')
await queryRunner.query('ALTER TABLE `revoked_sessions` ADD `user_agent` text NULL')
await queryRunner.query('ALTER TABLE `revoked_sessions` ADD `api_version` varchar(255) NULL')
}
public async down(): Promise<void> {
return
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.18.4",
"version": "1.25.1",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -63,7 +63,7 @@
"reflect-metadata": "0.1.13",
"typeorm": "^0.3.6",
"ua-parser-js": "1.0.2",
"uuid": "8.3.2",
"uuid": "^9.0.0",
"winston": "^3.8.1"
},
"devDependencies": {

View File

@@ -9,7 +9,13 @@ import {
} from '@standardnotes/domain-events'
import { TimerInterface, Timer } from '@standardnotes/time'
import { UAParser } from 'ua-parser-js'
import { AnalyticsStoreInterface, PeriodKeyGenerator, RedisAnalyticsStore } from '@standardnotes/analytics'
import {
AnalyticsStoreInterface,
PeriodKeyGenerator,
RedisAnalyticsStore,
RedisStatisticsStore,
StatisticsStoreInterface,
} from '@standardnotes/analytics'
import { Env } from './Env'
import TYPES from './Types'
@@ -191,6 +197,9 @@ import { AuthController } from '../Controller/AuthController'
import { VerifyPredicate } from '../Domain/UseCase/VerifyPredicate/VerifyPredicate'
import { PredicateVerificationRequestedEventHandler } from '../Domain/Handler/PredicateVerificationRequestedEventHandler'
import { MuteMarketingEmails } from '../Domain/UseCase/MuteMarketingEmails/MuteMarketingEmails'
import { PaymentFailedEventHandler } from '../Domain/Handler/PaymentFailedEventHandler'
import { PaymentSuccessEventHandler } from '../Domain/Handler/PaymentSuccessEventHandler'
import { RefundProcessedEventHandler } from '../Domain/Handler/RefundProcessedEventHandler'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicFormatter = require('@newrelic/winston-enricher')
@@ -478,6 +487,9 @@ export class ContainerConfigLoader {
container
.bind<PredicateVerificationRequestedEventHandler>(TYPES.PredicateVerificationRequestedEventHandler)
.to(PredicateVerificationRequestedEventHandler)
container.bind<PaymentFailedEventHandler>(TYPES.PaymentFailedEventHandler).to(PaymentFailedEventHandler)
container.bind<PaymentSuccessEventHandler>(TYPES.PaymentSuccessEventHandler).to(PaymentSuccessEventHandler)
container.bind<RefundProcessedEventHandler>(TYPES.RefundProcessedEventHandler).to(RefundProcessedEventHandler)
// Services
container.bind<UAParser>(TYPES.DeviceDetector).toConstantValue(new UAParser())
@@ -538,9 +550,13 @@ export class ContainerConfigLoader {
.bind<SelectorInterface<boolean>>(TYPES.BooleanSelector)
.toConstantValue(new DeterministicSelector<boolean>())
container.bind<UserSubscriptionServiceInterface>(TYPES.UserSubscriptionService).to(UserSubscriptionService)
const periodKeyGenerator = new PeriodKeyGenerator()
container
.bind<AnalyticsStoreInterface>(TYPES.AnalyticsStore)
.toConstantValue(new RedisAnalyticsStore(new PeriodKeyGenerator(), container.get(TYPES.Redis)))
.toConstantValue(new RedisAnalyticsStore(periodKeyGenerator, container.get(TYPES.Redis)))
container
.bind<StatisticsStoreInterface>(TYPES.StatisticsStore)
.toConstantValue(new RedisStatisticsStore(periodKeyGenerator, container.get(TYPES.Redis)))
if (env.get('SNS_TOPIC_ARN', true)) {
container
@@ -576,6 +592,9 @@ export class ContainerConfigLoader {
],
['SHARED_SUBSCRIPTION_INVITATION_CREATED', container.get(TYPES.SharedSubscriptionInvitationCreatedEventHandler)],
['PREDICATE_VERIFICATION_REQUESTED', container.get(TYPES.PredicateVerificationRequestedEventHandler)],
['PAYMENT_FAILED', container.get(TYPES.PaymentFailedEventHandler)],
['PAYMENT_SUCCESS', container.get(TYPES.PaymentSuccessEventHandler)],
['REFUND_PROCESSED', container.get(TYPES.RefundProcessedEventHandler)],
])
if (env.get('SQS_QUEUE_URL', true)) {

View File

@@ -143,6 +143,9 @@ const TYPES = {
UserDisabledSessionUserAgentLoggingEventHandler: Symbol.for('UserDisabledSessionUserAgentLoggingEventHandler'),
SharedSubscriptionInvitationCreatedEventHandler: Symbol.for('SharedSubscriptionInvitationCreatedEventHandler'),
PredicateVerificationRequestedEventHandler: Symbol.for('PredicateVerificationRequestedEventHandler'),
PaymentFailedEventHandler: Symbol.for('PaymentFailedEventHandler'),
PaymentSuccessEventHandler: Symbol.for('PaymentSuccessEventHandler'),
RefundProcessedEventHandler: Symbol.for('RefundProcessedEventHandler'),
// Services
DeviceDetector: Symbol.for('DeviceDetector'),
SessionService: Symbol.for('SessionService'),
@@ -184,6 +187,7 @@ const TYPES = {
BooleanSelector: Symbol.for('BooleanSelector'),
UserSubscriptionService: Symbol.for('UserSubscriptionService'),
AnalyticsStore: Symbol.for('AnalyticsStore'),
StatisticsStore: Symbol.for('StatisticsStore'),
}
export default TYPES

View File

@@ -12,7 +12,8 @@ import { User } from '../User/User'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
import { AnalyticsStoreInterface } from '@standardnotes/analytics'
import { AnalyticsStoreInterface, StatisticsStoreInterface } from '@standardnotes/analytics'
import { TimerInterface } from '@standardnotes/time'
describe('AccountDeletionRequestedEventHandler', () => {
let userRepository: UserRepositoryInterface
@@ -27,6 +28,8 @@ describe('AccountDeletionRequestedEventHandler', () => {
let event: AccountDeletionRequestedEvent
let getUserAnalyticsId: GetUserAnalyticsId
let analyticsStore: AnalyticsStoreInterface
let statisticsStore: StatisticsStoreInterface
let timer: TimerInterface
const createHandler = () =>
new AccountDeletionRequestedEventHandler(
@@ -36,6 +39,8 @@ describe('AccountDeletionRequestedEventHandler', () => {
revokedSessionRepository,
getUserAnalyticsId,
analyticsStore,
statisticsStore,
timer,
logger,
)
@@ -87,6 +92,13 @@ describe('AccountDeletionRequestedEventHandler', () => {
logger = {} as jest.Mocked<Logger>
logger.info = jest.fn()
logger.warn = jest.fn()
statisticsStore = {} as jest.Mocked<StatisticsStoreInterface>
statisticsStore.incrementMeasure = jest.fn()
timer = {} as jest.Mocked<TimerInterface>
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123)
timer.convertDateToMicroseconds = jest.fn().mockReturnValue(100)
})
it('should remove a user', async () => {

View File

@@ -1,5 +1,12 @@
import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics'
import {
AnalyticsActivity,
AnalyticsStoreInterface,
Period,
StatisticsMeasure,
StatisticsStoreInterface,
} from '@standardnotes/analytics'
import { AccountDeletionRequestedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events'
import { TimerInterface } from '@standardnotes/time'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types'
@@ -18,6 +25,8 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
@inject(TYPES.RevokedSessionRepository) private revokedSessionRepository: RevokedSessionRepositoryInterface,
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
@inject(TYPES.Timer) private timer: TimerInterface,
@inject(TYPES.Logger) private logger: Logger,
) {}
@@ -39,6 +48,14 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
Period.ThisMonth,
])
const registrationLength =
this.timer.getTimestampInMicroseconds() - this.timer.convertDateToMicroseconds(user.createdAt)
await this.statisticsStore.incrementMeasure(StatisticsMeasure.RegistrationLength, registrationLength, [
Period.Today,
Period.ThisWeek,
Period.ThisMonth,
])
await this.userRepository.remove(user)
this.logger.info(`Finished account cleanup for user: ${event.payload.userUuid}`)

View File

@@ -0,0 +1,51 @@
import 'reflect-metadata'
import { PaymentFailedEvent } from '@standardnotes/domain-events'
import { AnalyticsStoreInterface } from '@standardnotes/analytics'
import { PaymentFailedEventHandler } from './PaymentFailedEventHandler'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { User } from '../User/User'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
describe('PaymentFailedEventHandler', () => {
let userRepository: UserRepositoryInterface
let event: PaymentFailedEvent
let user: User
let getUserAnalyticsId: GetUserAnalyticsId
let analyticsStore: AnalyticsStoreInterface
const createHandler = () => new PaymentFailedEventHandler(userRepository, getUserAnalyticsId, analyticsStore)
beforeEach(() => {
user = {} as jest.Mocked<User>
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.findOneByEmail = jest.fn().mockReturnValue(user)
getUserAnalyticsId = {} as jest.Mocked<GetUserAnalyticsId>
getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 })
analyticsStore = {} as jest.Mocked<AnalyticsStoreInterface>
analyticsStore.markActivity = jest.fn()
event = {} as jest.Mocked<PaymentFailedEvent>
event.payload = {
userEmail: 'test@test.com',
}
})
it('should mark payment failed for analytics', async () => {
await createHandler().handle(event)
expect(analyticsStore.markActivity).toHaveBeenCalled()
})
it('should not mark payment failed for analytics if user is not found', async () => {
userRepository.findOneByEmail = jest.fn().mockReturnValue(null)
await createHandler().handle(event)
expect(analyticsStore.markActivity).not.toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,30 @@
import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics'
import { DomainEventHandlerInterface, PaymentFailedEvent } from '@standardnotes/domain-events'
import { inject, injectable } from 'inversify'
import TYPES from '../../Bootstrap/Types'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
@injectable()
export class PaymentFailedEventHandler implements DomainEventHandlerInterface {
constructor(
@inject(TYPES.UserRepository) private userRepository: UserRepositoryInterface,
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
) {}
async handle(event: PaymentFailedEvent): Promise<void> {
const user = await this.userRepository.findOneByEmail(event.payload.userEmail)
if (user === null) {
return
}
const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid })
await this.analyticsStore.markActivity([AnalyticsActivity.PaymentFailed], analyticsId, [
Period.Today,
Period.ThisWeek,
Period.ThisMonth,
])
}
}

View File

@@ -0,0 +1,58 @@
import 'reflect-metadata'
import { PaymentSuccessEvent } from '@standardnotes/domain-events'
import { AnalyticsStoreInterface, StatisticsStoreInterface } from '@standardnotes/analytics'
import { PaymentSuccessEventHandler } from './PaymentSuccessEventHandler'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { User } from '../User/User'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
describe('PaymentSuccessEventHandler', () => {
let userRepository: UserRepositoryInterface
let event: PaymentSuccessEvent
let user: User
let getUserAnalyticsId: GetUserAnalyticsId
let analyticsStore: AnalyticsStoreInterface
let statisticsStore: StatisticsStoreInterface
const createHandler = () =>
new PaymentSuccessEventHandler(userRepository, getUserAnalyticsId, analyticsStore, statisticsStore)
beforeEach(() => {
user = {} as jest.Mocked<User>
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.findOneByEmail = jest.fn().mockReturnValue(user)
getUserAnalyticsId = {} as jest.Mocked<GetUserAnalyticsId>
getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 })
analyticsStore = {} as jest.Mocked<AnalyticsStoreInterface>
analyticsStore.markActivity = jest.fn()
statisticsStore = {} as jest.Mocked<StatisticsStoreInterface>
statisticsStore.incrementMeasure = jest.fn()
event = {} as jest.Mocked<PaymentSuccessEvent>
event.payload = {
userEmail: 'test@test.com',
amount: 12.45,
}
})
it('should mark payment failed for analytics', async () => {
await createHandler().handle(event)
expect(analyticsStore.markActivity).toHaveBeenCalled()
expect(statisticsStore.incrementMeasure).toHaveBeenCalled()
})
it('should not mark payment failed for analytics if user is not found', async () => {
userRepository.findOneByEmail = jest.fn().mockReturnValue(null)
await createHandler().handle(event)
expect(analyticsStore.markActivity).not.toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,43 @@
import {
AnalyticsActivity,
AnalyticsStoreInterface,
Period,
StatisticsMeasure,
StatisticsStoreInterface,
} from '@standardnotes/analytics'
import { DomainEventHandlerInterface, PaymentSuccessEvent } from '@standardnotes/domain-events'
import { inject, injectable } from 'inversify'
import TYPES from '../../Bootstrap/Types'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
@injectable()
export class PaymentSuccessEventHandler implements DomainEventHandlerInterface {
constructor(
@inject(TYPES.UserRepository) private userRepository: UserRepositoryInterface,
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
) {}
async handle(event: PaymentSuccessEvent): Promise<void> {
const user = await this.userRepository.findOneByEmail(event.payload.userEmail)
if (user === null) {
return
}
const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid })
await this.analyticsStore.markActivity([AnalyticsActivity.PaymentSuccess], analyticsId, [
Period.Today,
Period.ThisWeek,
Period.ThisMonth,
])
await this.statisticsStore.incrementMeasure(StatisticsMeasure.Income, event.payload.amount, [
Period.Today,
Period.ThisWeek,
Period.ThisMonth,
])
}
}

View File

@@ -0,0 +1,34 @@
import 'reflect-metadata'
import { RefundProcessedEvent } from '@standardnotes/domain-events'
import { Period, StatisticsMeasure, StatisticsStoreInterface } from '@standardnotes/analytics'
import { RefundProcessedEventHandler } from './RefundProcessedEventHandler'
describe('RefundProcessedEventHandler', () => {
let event: RefundProcessedEvent
let statisticsStore: StatisticsStoreInterface
const createHandler = () => new RefundProcessedEventHandler(statisticsStore)
beforeEach(() => {
statisticsStore = {} as jest.Mocked<StatisticsStoreInterface>
statisticsStore.incrementMeasure = jest.fn()
event = {} as jest.Mocked<RefundProcessedEvent>
event.payload = {
userEmail: 'test@test.com',
amount: 12.45,
}
})
it('should mark refunds for statistics', async () => {
await createHandler().handle(event)
expect(statisticsStore.incrementMeasure).toHaveBeenCalledWith(StatisticsMeasure.Refunds, 12.45, [
Period.Today,
Period.ThisWeek,
Period.ThisMonth,
])
})
})

View File

@@ -0,0 +1,18 @@
import { Period, StatisticsMeasure, StatisticsStoreInterface } from '@standardnotes/analytics'
import { DomainEventHandlerInterface, RefundProcessedEvent } from '@standardnotes/domain-events'
import { inject, injectable } from 'inversify'
import TYPES from '../../Bootstrap/Types'
@injectable()
export class RefundProcessedEventHandler implements DomainEventHandlerInterface {
constructor(@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface) {}
async handle(event: RefundProcessedEvent): Promise<void> {
await this.statisticsStore.incrementMeasure(StatisticsMeasure.Refunds, event.payload.amount, [
Period.Today,
Period.ThisWeek,
Period.ThisMonth,
])
}
}

View File

@@ -8,10 +8,12 @@ import * as dayjs from 'dayjs'
import { SubscriptionCancelledEventHandler } from './SubscriptionCancelledEventHandler'
import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface'
import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
import { AnalyticsStoreInterface } from '@standardnotes/analytics'
import { AnalyticsStoreInterface, Period, StatisticsMeasure, StatisticsStoreInterface } from '@standardnotes/analytics'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { User } from '../User/User'
import { UserSubscription } from '../Subscription/UserSubscription'
import { Logger } from 'winston'
describe('SubscriptionCancelledEventHandler', () => {
let userSubscriptionRepository: UserSubscriptionRepositoryInterface
@@ -20,7 +22,9 @@ describe('SubscriptionCancelledEventHandler', () => {
let userRepository: UserRepositoryInterface
let getUserAnalyticsId: GetUserAnalyticsId
let analyticsStore: AnalyticsStoreInterface
let statisticsStore: StatisticsStoreInterface
let timestamp: number
let logger: Logger
const createHandler = () =>
new SubscriptionCancelledEventHandler(
@@ -29,6 +33,8 @@ describe('SubscriptionCancelledEventHandler', () => {
userRepository,
getUserAnalyticsId,
analyticsStore,
statisticsStore,
logger,
)
beforeEach(() => {
@@ -43,8 +49,16 @@ describe('SubscriptionCancelledEventHandler', () => {
analyticsStore = {} as jest.Mocked<AnalyticsStoreInterface>
analyticsStore.markActivity = jest.fn()
statisticsStore = {} as jest.Mocked<StatisticsStoreInterface>
statisticsStore.incrementMeasure = jest.fn()
const userSubscription = {
createdAt: 1642395451515000,
} as jest.Mocked<UserSubscription>
userSubscriptionRepository = {} as jest.Mocked<UserSubscriptionRepositoryInterface>
userSubscriptionRepository.updateCancelled = jest.fn()
userSubscriptionRepository.findBySubscriptionId = jest.fn().mockReturnValue([userSubscription])
offlineUserSubscriptionRepository = {} as jest.Mocked<OfflineUserSubscriptionRepositoryInterface>
offlineUserSubscriptionRepository.updateCancelled = jest.fn()
@@ -59,14 +73,24 @@ describe('SubscriptionCancelledEventHandler', () => {
subscriptionName: SubscriptionName.ProPlan,
timestamp,
offline: false,
replaced: false,
}
logger = {} as jest.Mocked<Logger>
logger.info = jest.fn()
})
it('should update subscription cancelled', async () => {
event.payload.timestamp = 1642395451516000
await createHandler().handle(event)
expect(userSubscriptionRepository.updateCancelled).toHaveBeenCalledWith(1, true, timestamp)
expect(userSubscriptionRepository.updateCancelled).toHaveBeenCalledWith(1, true, 1642395451516000)
expect(analyticsStore.markActivity).toHaveBeenCalled()
expect(statisticsStore.incrementMeasure).toHaveBeenCalledWith(StatisticsMeasure.SubscriptionLength, 1000, [
Period.Today,
Period.ThisWeek,
Period.ThisMonth,
])
})
it('should update subscription cancelled - user not found', async () => {

View File

@@ -1,12 +1,20 @@
import { DomainEventHandlerInterface, SubscriptionCancelledEvent } from '@standardnotes/domain-events'
import { inject, injectable } from 'inversify'
import {
AnalyticsActivity,
AnalyticsStoreInterface,
Period,
StatisticsMeasure,
StatisticsStoreInterface,
} from '@standardnotes/analytics'
import TYPES from '../../Bootstrap/Types'
import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface'
import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics'
import { UserSubscription } from '../Subscription/UserSubscription'
import { Logger } from 'winston'
@injectable()
export class SubscriptionCancelledEventHandler implements DomainEventHandlerInterface {
@@ -17,6 +25,8 @@ export class SubscriptionCancelledEventHandler implements DomainEventHandlerInte
@inject(TYPES.UserRepository) private userRepository: UserRepositoryInterface,
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
@inject(TYPES.Logger) private logger: Logger,
) {}
async handle(event: SubscriptionCancelledEvent): Promise<void> {
if (event.payload.offline) {
@@ -35,6 +45,20 @@ export class SubscriptionCancelledEventHandler implements DomainEventHandlerInte
Period.ThisWeek,
Period.ThisMonth,
])
const subscriptions = await this.userSubscriptionRepository.findBySubscriptionId(event.payload.subscriptionId)
if (subscriptions.length !== 0) {
const lastSubscription = subscriptions.shift() as UserSubscription
const subscriptionLength = event.payload.timestamp - lastSubscription.createdAt
this.logger.info(
`Canceling subscription ${lastSubscription.uuid} - lasted for ${subscriptionLength} microseconds`,
)
await this.statisticsStore.incrementMeasure(StatisticsMeasure.SubscriptionLength, subscriptionLength, [
Period.Today,
Period.ThisWeek,
Period.ThisMonth,
])
}
}
}

View File

@@ -16,9 +16,10 @@ import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/Offl
import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription'
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
import { AnalyticsStoreInterface, Period } from '@standardnotes/analytics'
import { AnalyticsStoreInterface, Period, StatisticsStoreInterface } from '@standardnotes/analytics'
import { AnalyticsEntity } from '../Analytics/AnalyticsEntity'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
import { TimerInterface } from '@standardnotes/time'
describe('SubscriptionPurchasedEventHandler', () => {
let userRepository: UserRepositoryInterface
@@ -35,6 +36,8 @@ describe('SubscriptionPurchasedEventHandler', () => {
let getUserAnalyticsId: GetUserAnalyticsId
let analyticsStore: AnalyticsStoreInterface
let timestamp: number
let statisticsStore: StatisticsStoreInterface
let timer: TimerInterface
const createHandler = () =>
new SubscriptionPurchasedEventHandler(
@@ -45,6 +48,8 @@ describe('SubscriptionPurchasedEventHandler', () => {
subscriptionSettingService,
getUserAnalyticsId,
analyticsStore,
statisticsStore,
timer,
logger,
)
@@ -66,7 +71,14 @@ describe('SubscriptionPurchasedEventHandler', () => {
userRepository.findOneByEmail = jest.fn().mockReturnValue(user)
userRepository.save = jest.fn().mockReturnValue(user)
statisticsStore = {} as jest.Mocked<StatisticsStoreInterface>
statisticsStore.incrementMeasure = jest.fn()
timer = {} as jest.Mocked<TimerInterface>
timer.convertDateToMicroseconds = jest.fn().mockReturnValue(1)
userSubscriptionRepository = {} as jest.Mocked<UserSubscriptionRepositoryInterface>
userSubscriptionRepository.countByUserUuid = jest.fn().mockReturnValue(0)
userSubscriptionRepository.save = jest.fn().mockReturnValue(subscription)
offlineUserSubscription = {} as jest.Mocked<OfflineUserSubscription>
@@ -146,6 +158,15 @@ describe('SubscriptionPurchasedEventHandler', () => {
updatedAt: expect.any(Number),
cancelled: false,
})
expect(statisticsStore.incrementMeasure).toHaveBeenCalled()
})
it("should not measure registration to subscription time if this is not user's first subscription", async () => {
userSubscriptionRepository.countByUserUuid = jest.fn().mockReturnValue(1)
await createHandler().handle(event)
expect(statisticsStore.incrementMeasure).not.toHaveBeenCalled()
})
it('should update analytics on limited discount offer purchasing', async () => {

View File

@@ -13,8 +13,15 @@ import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription
import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics'
import {
AnalyticsActivity,
AnalyticsStoreInterface,
Period,
StatisticsMeasure,
StatisticsStoreInterface,
} from '@standardnotes/analytics'
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
import { TimerInterface } from '@standardnotes/time'
@injectable()
export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInterface {
@@ -27,6 +34,8 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
@inject(TYPES.SubscriptionSettingService) private subscriptionSettingService: SubscriptionSettingServiceInterface,
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
@inject(TYPES.Timer) private timer: TimerInterface,
@inject(TYPES.Logger) private logger: Logger,
) {}
@@ -52,6 +61,8 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
return
}
const previousSubscriptionCount = await this.userSubscriptionRepository.countByUserUuid(user.uuid)
const userSubscription = await this.createSubscription(
event.payload.subscriptionId,
event.payload.subscriptionName,
@@ -74,12 +85,20 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
Period.ThisMonth,
])
const limitedDiscountPurchased = event.payload.discountCode === 'limited-10'
const limitedDiscountPurchased = ['limited-10', 'limited-20'].includes(event.payload.discountCode as string)
if (limitedDiscountPurchased) {
await this.analyticsStore.markActivity([AnalyticsActivity.LimitedDiscountOfferPurchased], analyticsId, [
Period.Today,
])
}
if (previousSubscriptionCount === 0) {
await this.statisticsStore.incrementMeasure(
StatisticsMeasure.RegistrationToSubscriptionTime,
event.payload.timestamp - this.timer.convertDateToMicroseconds(user.createdAt),
[Period.Today, Period.ThisWeek, Period.ThisMonth],
)
}
}
private async addUserRole(user: User, subscriptionName: SubscriptionName): Promise<void> {

View File

@@ -4,17 +4,23 @@ import { UserDisabledSessionUserAgentLoggingEvent } from '@standardnotes/domain-
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
import { UserDisabledSessionUserAgentLoggingEventHandler } from './UserDisabledSessionUserAgentLoggingEventHandler'
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
describe('UserDisabledSessionUserAgentLoggingEventHandler', () => {
let sessionRepository: SessionRepositoryInterface
let revokedSessionRepository: RevokedSessionRepositoryInterface
let event: UserDisabledSessionUserAgentLoggingEvent
const createHandler = () => new UserDisabledSessionUserAgentLoggingEventHandler(sessionRepository)
const createHandler = () =>
new UserDisabledSessionUserAgentLoggingEventHandler(sessionRepository, revokedSessionRepository)
beforeEach(() => {
sessionRepository = {} as jest.Mocked<SessionRepositoryInterface>
sessionRepository.clearUserAgentByUserUuid = jest.fn()
revokedSessionRepository = {} as jest.Mocked<RevokedSessionRepositoryInterface>
revokedSessionRepository.clearUserAgentByUserUuid = jest.fn()
event = {} as jest.Mocked<UserDisabledSessionUserAgentLoggingEvent>
event.payload = {
userUuid: '1-2-3',
@@ -26,5 +32,6 @@ describe('UserDisabledSessionUserAgentLoggingEventHandler', () => {
await createHandler().handle(event)
expect(sessionRepository.clearUserAgentByUserUuid).toHaveBeenCalledWith('1-2-3')
expect(revokedSessionRepository.clearUserAgentByUserUuid).toHaveBeenCalledWith('1-2-3')
})
})

View File

@@ -2,13 +2,18 @@ import { DomainEventHandlerInterface, UserDisabledSessionUserAgentLoggingEvent }
import { inject, injectable } from 'inversify'
import TYPES from '../../Bootstrap/Types'
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
@injectable()
export class UserDisabledSessionUserAgentLoggingEventHandler implements DomainEventHandlerInterface {
constructor(@inject(TYPES.SessionRepository) private sessionRepository: SessionRepositoryInterface) {}
constructor(
@inject(TYPES.SessionRepository) private sessionRepository: SessionRepositoryInterface,
@inject(TYPES.SessionRepository) private revokedSessionRepository: RevokedSessionRepositoryInterface,
) {}
async handle(event: UserDisabledSessionUserAgentLoggingEvent): Promise<void> {
await this.sessionRepository.clearUserAgentByUserUuid(event.payload.userUuid)
await this.revokedSessionRepository.clearUserAgentByUserUuid(event.payload.userUuid)
}
}

View File

@@ -36,4 +36,25 @@ export class RevokedSession {
)
@JoinColumn({ name: 'user_uuid', referencedColumnName: 'uuid' })
declare user: Promise<User>
@Column({
name: 'received_at',
type: 'datetime',
nullable: true,
})
declare receivedAt: Date
@Column({
name: 'user_agent',
type: 'text',
nullable: true,
})
declare userAgent: string | null
@Column({
name: 'api_version',
length: 255,
nullable: true,
})
declare apiVersion: string
}

View File

@@ -1,3 +1,4 @@
import { Uuid } from '@standardnotes/common'
import { RevokedSession } from './RevokedSession'
export interface RevokedSessionRepositoryInterface {
@@ -5,4 +6,5 @@ export interface RevokedSessionRepositoryInterface {
findAllByUserUuid(userUuid: string): Promise<Array<RevokedSession>>
save(revokedSession: RevokedSession): Promise<RevokedSession>
remove(revokedSession: RevokedSession): Promise<RevokedSession>
clearUserAgentByUserUuid(userUuid: Uuid): Promise<void>
}

View File

@@ -2,6 +2,7 @@ import 'reflect-metadata'
import * as winston from 'winston'
import { TimerInterface } from '@standardnotes/time'
import { ApiVersion } from '../Api/ApiVersion'
import { Session } from './Session'
import { SessionRepositoryInterface } from './SessionRepositoryInterface'
import { SessionService } from './SessionService'
@@ -47,6 +48,7 @@ describe('SessionService', () => {
session.uuid = '2e1e43'
session.userUuid = '1-2-3'
session.userAgent = 'Chrome'
session.apiVersion = ApiVersion.v20200115
session.hashedAccessToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
session.hashedRefreshToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
@@ -80,6 +82,7 @@ describe('SessionService', () => {
timer = {} as jest.Mocked<TimerInterface>
timer.convertStringDateToMilliseconds = jest.fn().mockReturnValue(123)
timer.getUTCDate = jest.fn().mockReturnValue(new Date(1))
deviceDetector = {} as jest.Mocked<UAParser>
deviceDetector.setUA = jest.fn().mockReturnThis()
@@ -111,6 +114,7 @@ describe('SessionService', () => {
expect(revokedSessionRepository.save).toHaveBeenCalledWith({
uuid: '2e1e43',
received: true,
receivedAt: new Date(1),
})
})
@@ -476,6 +480,8 @@ describe('SessionService', () => {
expect(revokedSessionRepository.save).toHaveBeenCalledWith({
uuid: '2e1e43',
userUuid: '1-2-3',
userAgent: 'Chrome',
apiVersion: '20200115',
createdAt: expect.any(Date),
})
})

View File

@@ -203,6 +203,7 @@ export class SessionService implements SessionServiceInterface {
async markRevokedSessionAsReceived(revokedSession: RevokedSession): Promise<RevokedSession> {
revokedSession.received = true
revokedSession.receivedAt = this.timer.getUTCDate()
return this.revokedSessionRepository.save(revokedSession)
}
@@ -224,7 +225,9 @@ export class SessionService implements SessionServiceInterface {
const revokedSession = new RevokedSession()
revokedSession.uuid = session.uuid
revokedSession.userUuid = session.userUuid
revokedSession.createdAt = dayjs.utc().toDate()
revokedSession.createdAt = this.timer.getUTCDate()
revokedSession.apiVersion = session.apiVersion
revokedSession.userAgent = session.userAgent
return this.revokedSessionRepository.save(revokedSession)
}
@@ -246,8 +249,8 @@ export class SessionService implements SessionServiceInterface {
}
session.userUuid = dto.user.uuid
session.apiVersion = dto.apiVersion
session.createdAt = dayjs.utc().toDate()
session.updatedAt = dayjs.utc().toDate()
session.createdAt = this.timer.getUTCDate()
session.updatedAt = this.timer.getUTCDate()
session.readonlyAccess = dto.readonlyAccess
return session

View File

@@ -4,6 +4,7 @@ import { UserSubscriptionType } from './UserSubscriptionType'
export interface UserSubscriptionRepositoryInterface {
findOneByUuid(uuid: Uuid): Promise<UserSubscription | null>
countByUserUuid(userUuid: Uuid): Promise<number>
findOneByUserUuid(userUuid: Uuid): Promise<UserSubscription | null>
findOneByUserUuidAndSubscriptionId(userUuid: Uuid, subscriptionId: number): Promise<UserSubscription | null>
findBySubscriptionIdAndType(subscriptionId: number, type: UserSubscriptionType): Promise<UserSubscription[]>

View File

@@ -9,6 +9,7 @@ import { InviteToSharedSubscription } from './InviteToSharedSubscription'
import { UserSubscriptionRepositoryInterface } from '../../Subscription/UserSubscriptionRepositoryInterface'
import { UserSubscription } from '../../Subscription/UserSubscription'
import { RoleName } from '@standardnotes/common'
import { UserSubscriptionType } from '../../Subscription/UserSubscriptionType'
describe('InviteToSharedSubscription', () => {
let userSubscriptionRepository: UserSubscriptionRepositoryInterface
@@ -28,9 +29,10 @@ describe('InviteToSharedSubscription', () => {
beforeEach(() => {
userSubscriptionRepository = {} as jest.Mocked<UserSubscriptionRepositoryInterface>
userSubscriptionRepository.findOneByUserUuid = jest
.fn()
.mockReturnValue({ subscriptionId: 2 } as jest.Mocked<UserSubscription>)
userSubscriptionRepository.findOneByUserUuid = jest.fn().mockReturnValue({
subscriptionId: 2,
subscriptionType: UserSubscriptionType.Regular,
} as jest.Mocked<UserSubscription>)
timer = {} as jest.Mocked<TimerInterface>
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(1)
@@ -159,4 +161,24 @@ describe('InviteToSharedSubscription', () => {
})
expect(domainEventPublisher.publish).toHaveBeenCalled()
})
it('should not create an inivitation for sharing the subscription if the inviter is on a shared subscription', async () => {
userSubscriptionRepository.findOneByUserUuid = jest.fn().mockReturnValue({
subscriptionId: 2,
subscriptionType: UserSubscriptionType.Shared,
} as jest.Mocked<UserSubscription>)
await createUseCase().execute({
inviteeIdentifier: 'invitee@test.te',
inviterUuid: '1-2-3',
inviterEmail: 'inviter@test.te',
inviterRoles: [RoleName.ProUser],
})
expect(sharedSubscriptionInvitationRepository.save).not.toHaveBeenCalled()
expect(domainEventFactory.createSharedSubscriptionInvitationCreatedEvent).not.toHaveBeenCalled()
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
})
})

View File

@@ -11,6 +11,7 @@ import { InviterIdentifierType } from '../../SharedSubscription/InviterIdentifie
import { SharedSubscriptionInvitation } from '../../SharedSubscription/SharedSubscriptionInvitation'
import { SharedSubscriptionInvitationRepositoryInterface } from '../../SharedSubscription/SharedSubscriptionInvitationRepositoryInterface'
import { UserSubscriptionRepositoryInterface } from '../../Subscription/UserSubscriptionRepositoryInterface'
import { UserSubscriptionType } from '../../Subscription/UserSubscriptionType'
import { UseCaseInterface } from '../UseCaseInterface'
import { InviteToSharedSubscriptionDTO } from './InviteToSharedSubscriptionDTO'
@@ -35,18 +36,18 @@ export class InviteToSharedSubscription implements UseCaseInterface {
}
}
const numberOfUsedInvites = await this.sharedSubscriptionInvitationRepository.countByInviterEmailAndStatus(
dto.inviterEmail,
[InvitationStatus.Sent, InvitationStatus.Accepted],
)
if (numberOfUsedInvites >= this.MAX_NUMBER_OF_INVITES) {
const inviterUserSubscription = await this.userSubscriptionRepository.findOneByUserUuid(dto.inviterUuid)
if (inviterUserSubscription === null || inviterUserSubscription.subscriptionType === UserSubscriptionType.Shared) {
return {
success: false,
}
}
const inviterUserSubscription = await this.userSubscriptionRepository.findOneByUserUuid(dto.inviterUuid)
if (inviterUserSubscription === null) {
const numberOfUsedInvites = await this.sharedSubscriptionInvitationRepository.countByInviterEmailAndStatus(
dto.inviterEmail,
[InvitationStatus.Sent, InvitationStatus.Accepted],
)
if (numberOfUsedInvites >= this.MAX_NUMBER_OF_INVITES) {
return {
success: false,
}

View File

@@ -1,6 +1,6 @@
import 'reflect-metadata'
import { Repository, SelectQueryBuilder } from 'typeorm'
import { Repository, SelectQueryBuilder, UpdateQueryBuilder } from 'typeorm'
import { RevokedSession } from '../../Domain/Session/RevokedSession'
@@ -9,12 +9,14 @@ import { MySQLRevokedSessionRepository } from './MySQLRevokedSessionRepository'
describe('MySQLRevokedSessionRepository', () => {
let ormRepository: Repository<RevokedSession>
let queryBuilder: SelectQueryBuilder<RevokedSession>
let updateQueryBuilder: UpdateQueryBuilder<RevokedSession>
let session: RevokedSession
const createRepository = () => new MySQLRevokedSessionRepository(ormRepository)
beforeEach(() => {
queryBuilder = {} as jest.Mocked<SelectQueryBuilder<RevokedSession>>
updateQueryBuilder = {} as jest.Mocked<UpdateQueryBuilder<RevokedSession>>
session = {} as jest.Mocked<RevokedSession>
@@ -36,6 +38,24 @@ describe('MySQLRevokedSessionRepository', () => {
expect(ormRepository.remove).toHaveBeenCalledWith(session)
})
it('should clear user agent data on all user sessions', async () => {
ormRepository.createQueryBuilder = jest.fn().mockImplementation(() => updateQueryBuilder)
updateQueryBuilder.update = jest.fn().mockReturnThis()
updateQueryBuilder.set = jest.fn().mockReturnThis()
updateQueryBuilder.where = jest.fn().mockReturnThis()
updateQueryBuilder.execute = jest.fn()
await createRepository().clearUserAgentByUserUuid('1-2-3')
expect(updateQueryBuilder.update).toHaveBeenCalled()
expect(updateQueryBuilder.set).toHaveBeenCalledWith({
userAgent: null,
})
expect(updateQueryBuilder.where).toHaveBeenCalledWith('user_uuid = :userUuid', { userUuid: '1-2-3' })
expect(updateQueryBuilder.execute).toHaveBeenCalled()
})
it('should find one session by id', async () => {
queryBuilder.where = jest.fn().mockReturnThis()
queryBuilder.getOne = jest.fn().mockReturnValue(session)

View File

@@ -20,6 +20,17 @@ export class MySQLRevokedSessionRepository implements RevokedSessionRepositoryIn
return this.ormRepository.remove(revokedSession)
}
async clearUserAgentByUserUuid(userUuid: string): Promise<void> {
await this.ormRepository
.createQueryBuilder('revoked_session')
.update()
.set({
userAgent: null,
})
.where('user_uuid = :userUuid', { userUuid })
.execute()
}
async findAllByUserUuid(userUuid: string): Promise<RevokedSession[]> {
return this.ormRepository
.createQueryBuilder('revoked_session')

View File

@@ -75,6 +75,21 @@ describe('MySQLUserSubscriptionRepository', () => {
expect(result).toEqual(subscription)
})
it('should count by user uuid', async () => {
ormRepository.createQueryBuilder = jest.fn().mockImplementation(() => selectQueryBuilder)
selectQueryBuilder.where = jest.fn().mockReturnThis()
selectQueryBuilder.getCount = jest.fn().mockReturnValue(2)
const result = await createRepository().countByUserUuid('123')
expect(selectQueryBuilder.where).toHaveBeenCalledWith('user_uuid = :user_uuid', {
user_uuid: '123',
})
expect(selectQueryBuilder.getCount).toHaveBeenCalled()
expect(result).toEqual(2)
})
it('should find one, longest lasting subscription by user uuid if there are no ucanceled ones', async () => {
subscription.cancelled = true
@@ -157,6 +172,7 @@ describe('MySQLUserSubscriptionRepository', () => {
ormRepository.createQueryBuilder = jest.fn().mockImplementation(() => selectQueryBuilder)
selectQueryBuilder.where = jest.fn().mockReturnThis()
selectQueryBuilder.orderBy = jest.fn().mockReturnThis()
selectQueryBuilder.getMany = jest.fn().mockReturnValue([subscription])
const result = await createRepository().findBySubscriptionId(123)
@@ -164,6 +180,7 @@ describe('MySQLUserSubscriptionRepository', () => {
expect(selectQueryBuilder.where).toHaveBeenCalledWith('subscription_id = :subscriptionId', {
subscriptionId: 123,
})
expect(selectQueryBuilder.orderBy).toHaveBeenCalledWith('created_at', 'DESC')
expect(selectQueryBuilder.getMany).toHaveBeenCalled()
expect(result).toEqual([subscription])
})

View File

@@ -14,6 +14,15 @@ export class MySQLUserSubscriptionRepository implements UserSubscriptionReposito
private ormRepository: Repository<UserSubscription>,
) {}
async countByUserUuid(userUuid: Uuid): Promise<number> {
return await this.ormRepository
.createQueryBuilder()
.where('user_uuid = :user_uuid', {
user_uuid: userUuid,
})
.getCount()
}
async save(subscription: UserSubscription): Promise<UserSubscription> {
return this.ormRepository.save(subscription)
}
@@ -44,6 +53,7 @@ export class MySQLUserSubscriptionRepository implements UserSubscriptionReposito
.where('subscription_id = :subscriptionId', {
subscriptionId,
})
.orderBy('created_at', 'DESC')
.getMany()
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.31.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.30.0...@standardnotes/common@1.31.0) (2022-09-05)
### Features
* **auth:** add keeping stats on payments ([0c176b7](https://github.com/standardnotes/server/commit/0c176b70f8281e1e490224b9c7ab85f272a3d4e9))
# [1.30.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.29.0...@standardnotes/common@1.30.0) (2022-07-14)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/common",
"version": "1.30.0",
"version": "1.31.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -23,7 +23,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage"
"test": "jest spec --coverage"
},
"devDependencies": {
"@types/jest": "^28.1.4",

View File

@@ -3,6 +3,48 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.8.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.2...@standardnotes/domain-events-infra@1.8.3) (2022-09-06)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.8.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.1...@standardnotes/domain-events-infra@1.8.2) (2022-09-06)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.8.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.0...@standardnotes/domain-events-infra@1.8.1) (2022-09-06)
**Note:** Version bump only for package @standardnotes/domain-events-infra
# [1.8.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.37...@standardnotes/domain-events-infra@1.8.0) (2022-09-05)
### Features
* **auth:** add keeping stats on payments ([0c176b7](https://github.com/standardnotes/server/commit/0c176b70f8281e1e490224b9c7ab85f272a3d4e9))
## [1.7.37](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.36...@standardnotes/domain-events-infra@1.7.37) (2022-09-05)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.7.36](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.35...@standardnotes/domain-events-infra@1.7.36) (2022-09-01)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.7.35](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.34...@standardnotes/domain-events-infra@1.7.35) (2022-09-01)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.7.34](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.33...@standardnotes/domain-events-infra@1.7.34) (2022-08-30)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.7.33](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.32...@standardnotes/domain-events-infra@1.7.33) (2022-08-15)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.7.32](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.31...@standardnotes/domain-events-infra@1.7.32) (2022-08-15)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.7.31](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.30...@standardnotes/domain-events-infra@1.7.31) (2022-08-10)
**Note:** Version bump only for package @standardnotes/domain-events-infra

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events-infra",
"version": "1.7.31",
"version": "1.8.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -21,7 +21,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage"
"test": "jest spec --coverage"
},
"dependencies": {
"@standardnotes/domain-events": "workspace:*",

View File

@@ -3,6 +3,67 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.59.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.59.0...@standardnotes/domain-events@2.59.1) (2022-09-06)
### Bug Fixes
* **api-gateway:** include period key in statistics measures ([d149f46](https://github.com/standardnotes/server/commit/d149f46cf6456201dd8690977f64ed32a75f3459))
* **api-gateway:** period types on analytics report ([f94c8fc](https://github.com/standardnotes/server/commit/f94c8fc26e684a07101cc5282ebb9cda3c8c6961))
# [2.59.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.58.0...@standardnotes/domain-events@2.59.0) (2022-09-06)
### Features
* **api-gateway:** add statistics measures to report generation ([8151bb1](https://github.com/standardnotes/server/commit/8151bb108affb2b5cfa1ab365f99a9f0170a7795))
# [2.58.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.57.0...@standardnotes/domain-events@2.58.0) (2022-09-06)
### Features
* **auth:** add statistics for refunds and account deletions ([d7ae2f0](https://github.com/standardnotes/server/commit/d7ae2f06255b19eb5d3403a4989610390064754e))
# [2.57.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.56.0...@standardnotes/domain-events@2.57.0) (2022-09-05)
### Features
* **auth:** add keeping stats on payments ([0c176b7](https://github.com/standardnotes/server/commit/0c176b70f8281e1e490224b9c7ab85f272a3d4e9))
# [2.56.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.55.1...@standardnotes/domain-events@2.56.0) (2022-09-05)
### Features
* **domain-events:** add amount of dollars to payment success event ([9c2d51d](https://github.com/standardnotes/server/commit/9c2d51d718516b550c23637b00a3edead0749425))
## [2.55.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.55.0...@standardnotes/domain-events@2.55.1) (2022-09-01)
### Bug Fixes
* **domain-events:** add admin-panel as event source option ([41c5127](https://github.com/standardnotes/server/commit/41c512798d932859b5d46c6e62fccb89fa288891))
# [2.55.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.54.2...@standardnotes/domain-events@2.55.0) (2022-09-01)
### Features
* **domain-events:** add subscription revert requested event ([e0cec9e](https://github.com/standardnotes/server/commit/e0cec9e24ab9954868fb428062c9a82d0f0f85d5))
## [2.54.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.54.1...@standardnotes/domain-events@2.54.2) (2022-08-30)
### Bug Fixes
* **domain-events:** add replaced boolean to subscription canceled event ([932ef93](https://github.com/standardnotes/server/commit/932ef933fce71aabdddca33da4eec6ce8fe686ef))
## [2.54.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.54.0...@standardnotes/domain-events@2.54.1) (2022-08-15)
### Bug Fixes
* **domain-events:** missing exports ([2fb904d](https://github.com/standardnotes/server/commit/2fb904d2cbff040ba9a6b2b323eab1591309b174))
# [2.54.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.53.0...@standardnotes/domain-events@2.54.0) (2022-08-15)
### Features
* **domain-events:** add payment success event ([1841597](https://github.com/standardnotes/server/commit/1841597405461981c7b6eb4ee8ada079c77a8fc5))
# [2.53.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.52.0...@standardnotes/domain-events@2.53.0) (2022-08-09)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events",
"version": "2.53.0",
"version": "2.59.1",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -21,7 +21,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage --passWithNoTests"
"test": "jest spec --coverage --passWithNoTests"
},
"dependencies": {
"@standardnotes/common": "workspace:*",

View File

@@ -12,6 +12,12 @@ export interface DailyAnalyticsReportGeneratedEventPayload {
retention: number
totalCount: number
}>
statisticMeasures: Array<{
name: string
totalValue: number
average: number
period: number
}>
activityStatisticsOverTime: Array<{
name: string
period: number

View File

@@ -1,4 +1,5 @@
export enum DomainEventService {
AdminPanel = 'admin-panel',
Auth = 'auth',
SyncingServer = 'syncing-server',
Payments = 'payments',

View File

@@ -0,0 +1,8 @@
import { DomainEventInterface } from './DomainEventInterface'
import { PaymentSuccessEventPayload } from './PaymentSuccessEventPayload'
export interface PaymentSuccessEvent extends DomainEventInterface {
type: 'PAYMENT_SUCCESS'
payload: PaymentSuccessEventPayload
}

View File

@@ -0,0 +1,4 @@
export interface PaymentSuccessEventPayload {
userEmail: string
amount: number
}

View File

@@ -1,3 +1,4 @@
export interface RefundProcessedEventPayload {
userEmail: string
amount: number
}

View File

@@ -6,4 +6,5 @@ export interface SubscriptionCancelledEventPayload {
subscriptionName: SubscriptionName
timestamp: number
offline: boolean
replaced: boolean
}

View File

@@ -0,0 +1,8 @@
import { DomainEventInterface } from './DomainEventInterface'
import { SubscriptionRevertRequestedEventPayload } from './SubscriptionRevertRequestedEventPayload'
export interface SubscriptionRevertRequestedEvent extends DomainEventInterface {
type: 'SUBSCRIPTION_REVERT_REQUESTED'
payload: SubscriptionRevertRequestedEventPayload
}

View File

@@ -0,0 +1,3 @@
export interface SubscriptionRevertRequestedEventPayload {
userEmail: string
}

View File

@@ -54,6 +54,8 @@ export * from './Event/OneDriveBackupFailedEvent'
export * from './Event/OneDriveBackupFailedEventPayload'
export * from './Event/PaymentFailedEvent'
export * from './Event/PaymentFailedEventPayload'
export * from './Event/PaymentSuccessEvent'
export * from './Event/PaymentSuccessEventPayload'
export * from './Event/PredicateVerificationRequestedEvent'
export * from './Event/PredicateVerificationRequestedEventPayload'
export * from './Event/PredicateVerifiedEvent'
@@ -84,6 +86,8 @@ export * from './Event/SubscriptionRenewedEvent'
export * from './Event/SubscriptionRenewedEventPayload'
export * from './Event/SubscriptionExpiredEvent'
export * from './Event/SubscriptionExpiredEventPayload'
export * from './Event/SubscriptionRevertRequestedEvent'
export * from './Event/SubscriptionRevertRequestedEventPayload'
export * from './Event/SubscriptionSyncRequestedEvent'
export * from './Event/SubscriptionSyncRequestedEventPayload'
export * from './Event/UserDisabledSessionUserAgentLoggingEvent'

View File

@@ -3,6 +3,64 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.3.6](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.3.5...@standardnotes/event-store@1.3.6) (2022-09-06)
**Note:** Version bump only for package @standardnotes/event-store
## [1.3.5](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.3.4...@standardnotes/event-store@1.3.5) (2022-09-06)
**Note:** Version bump only for package @standardnotes/event-store
## [1.3.4](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.3.3...@standardnotes/event-store@1.3.4) (2022-09-06)
**Note:** Version bump only for package @standardnotes/event-store
## [1.3.3](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.3.2...@standardnotes/event-store@1.3.3) (2022-09-06)
**Note:** Version bump only for package @standardnotes/event-store
## [1.3.2](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.3.1...@standardnotes/event-store@1.3.2) (2022-09-05)
**Note:** Version bump only for package @standardnotes/event-store
## [1.3.1](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.3.0...@standardnotes/event-store@1.3.1) (2022-09-05)
**Note:** Version bump only for package @standardnotes/event-store
# [1.3.0](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.2.3...@standardnotes/event-store@1.3.0) (2022-09-02)
### Features
* **event-store:** add listening to subscription reverts ([ef1e2bb](https://github.com/standardnotes/server/commit/ef1e2bb5edb6df191d22a676e365aed3511b3960))
## [1.2.3](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.2.2...@standardnotes/event-store@1.2.3) (2022-09-01)
**Note:** Version bump only for package @standardnotes/event-store
## [1.2.2](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.2.1...@standardnotes/event-store@1.2.2) (2022-09-01)
**Note:** Version bump only for package @standardnotes/event-store
## [1.2.1](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.2.0...@standardnotes/event-store@1.2.1) (2022-08-30)
**Note:** Version bump only for package @standardnotes/event-store
# [1.2.0](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.1.33...@standardnotes/event-store@1.2.0) (2022-08-30)
### Features
* **event-store:** add account claim events subscription ([dd6bec8](https://github.com/standardnotes/server/commit/dd6bec8a0c957b87ccf4d1fb0636fa3f56a4525e))
## [1.1.33](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.1.32...@standardnotes/event-store@1.1.33) (2022-08-15)
### Bug Fixes
* **event-store:** add payment events handling ([3477c81](https://github.com/standardnotes/server/commit/3477c81d37e6cb92e76d59b1128daac702a4a7ae))
## [1.1.32](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.1.31...@standardnotes/event-store@1.1.32) (2022-08-15)
**Note:** Version bump only for package @standardnotes/event-store
## [1.1.31](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.1.30...@standardnotes/event-store@1.1.31) (2022-08-12)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/event-store",
"version": "1.1.31",
"version": "1.3.6",
"description": "Event Store Service",
"private": true,
"main": "dist/src/index.js",

View File

@@ -76,6 +76,10 @@ export class ContainerConfigLoader {
['EMAIL_BACKUP_ATTACHMENT_CREATED', container.get(TYPES.EventHandler)],
['EMAIL_BACKUP_REQUESTED', container.get(TYPES.EventHandler)],
['OFFLINE_SUBSCRIPTION_TOKEN_CREATED', container.get(TYPES.EventHandler)],
['PAYMENT_FAILED', container.get(TYPES.EventHandler)],
['PAYMENT_SUCCESS', container.get(TYPES.EventHandler)],
['ACCOUNT_CLAIM_REQUESTED', container.get(TYPES.EventHandler)],
['SUBSCRIPTION_REVERT_REQUESTED', container.get(TYPES.EventHandler)],
])
container

View File

@@ -3,6 +3,54 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.5.45](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.44...@standardnotes/files-server@1.5.45) (2022-09-06)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.44](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.43...@standardnotes/files-server@1.5.44) (2022-09-06)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.43](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.42...@standardnotes/files-server@1.5.43) (2022-09-06)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.42](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.41...@standardnotes/files-server@1.5.42) (2022-09-06)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.41](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.40...@standardnotes/files-server@1.5.41) (2022-09-05)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.40](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.39...@standardnotes/files-server@1.5.40) (2022-09-05)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.39](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.38...@standardnotes/files-server@1.5.39) (2022-09-01)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.38](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.37...@standardnotes/files-server@1.5.38) (2022-09-01)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.37](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.36...@standardnotes/files-server@1.5.37) (2022-08-30)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.36](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.35...@standardnotes/files-server@1.5.36) (2022-08-29)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.35](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.34...@standardnotes/files-server@1.5.35) (2022-08-15)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.34](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.33...@standardnotes/files-server@1.5.34) (2022-08-15)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.33](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.32...@standardnotes/files-server@1.5.33) (2022-08-10)
**Note:** Version bump only for package @standardnotes/files-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.5.33",
"version": "1.5.45",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -50,7 +50,7 @@
"nodemon": "^2.0.19",
"prettyjson": "^1.2.5",
"reflect-metadata": "^0.1.13",
"ts-node": "^10.4.0",
"ts-node": "^10.9.1",
"winston": "^3.8.1"
},
"devDependencies": {
@@ -71,6 +71,6 @@
"nodemon": "^2.0.19",
"npm-check-updates": "^16.0.1",
"ts-jest": "^28.0.5",
"uuid": "^8.3.2"
"uuid": "^9.0.0"
}
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.4.0](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.3.0...@standardnotes/predicates@1.4.0) (2022-09-05)
### Features
* **auth:** add keeping stats on payments ([0c176b7](https://github.com/standardnotes/server/commit/0c176b70f8281e1e490224b9c7ab85f272a3d4e9))
# [1.3.0](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.2.6...@standardnotes/predicates@1.3.0) (2022-07-25)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/predicates",
"version": "1.3.0",
"version": "1.4.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -23,7 +23,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage --passWithNoTests"
"test": "jest spec --coverage --passWithNoTests"
},
"devDependencies": {
"@types/jest": "^28.1.4",

View File

@@ -3,6 +3,74 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.10.22](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.21...@standardnotes/scheduler-server@1.10.22) (2022-09-06)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.21](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.20...@standardnotes/scheduler-server@1.10.21) (2022-09-06)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.20](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.19...@standardnotes/scheduler-server@1.10.20) (2022-09-06)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.19](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.18...@standardnotes/scheduler-server@1.10.19) (2022-09-06)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.18](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.17...@standardnotes/scheduler-server@1.10.18) (2022-09-05)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.17](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.16...@standardnotes/scheduler-server@1.10.17) (2022-09-05)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.16](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.15...@standardnotes/scheduler-server@1.10.16) (2022-09-01)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.15](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.14...@standardnotes/scheduler-server@1.10.15) (2022-09-01)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.14](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.13...@standardnotes/scheduler-server@1.10.14) (2022-08-30)
### Bug Fixes
* **scheduler:** linter issues ([648eb89](https://github.com/standardnotes/server/commit/648eb89c7c7b95cfb6235e38180958c3d332825d))
## [1.10.13](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.12...@standardnotes/scheduler-server@1.10.13) (2022-08-30)
### Bug Fixes
* **scheduler:** skip sending exit survery when a subscription is replaced ([35373db](https://github.com/standardnotes/server/commit/35373db1d3d25ae5878ef7f7cfc8594a6623da8a))
## [1.10.12](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.11...@standardnotes/scheduler-server@1.10.12) (2022-08-30)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.11](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.10...@standardnotes/scheduler-server@1.10.11) (2022-08-29)
### Bug Fixes
* **scheduler:** change logs severity to reduce noise ([dbe55d8](https://github.com/standardnotes/server/commit/dbe55d89ec33a4c69cae350793bc4c5dd6589a2b))
## [1.10.10](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.9...@standardnotes/scheduler-server@1.10.10) (2022-08-17)
### Bug Fixes
* **scheduler:** change discount from 10% to 20% on a limited time offer ([4b3de26](https://github.com/standardnotes/server/commit/4b3de264efc4fffb2603181c158cddb25c4ed4a9))
## [1.10.9](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.8...@standardnotes/scheduler-server@1.10.9) (2022-08-15)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.8](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.7...@standardnotes/scheduler-server@1.10.8) (2022-08-15)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.7](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.6...@standardnotes/scheduler-server@1.10.7) (2022-08-10)
**Note:** Version bump only for package @standardnotes/scheduler-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/scheduler-server",
"version": "1.10.7",
"version": "1.10.22",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -15,6 +15,7 @@
"prebuild": "yarn clean",
"build": "tsc --rootDir ./",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"pretest": "yarn lint && yarn build",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"worker": "yarn node dist/bin/worker.js",
@@ -48,6 +49,7 @@
"@types/newrelic": "^7.0.3",
"@types/node": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.29.0",
"eslint": "^8.14.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^28.1.2",
"npm-check-updates": "^16.0.1",

View File

@@ -18,7 +18,7 @@ describe('PredicateVerifiedEventHandler', () => {
updatePredicateStatus.execute = jest.fn()
logger = {} as jest.Mocked<Logger>
logger.info = jest.fn()
logger.debug = jest.fn()
event = {} as jest.Mocked<PredicateVerifiedEvent>
event.payload = {

View File

@@ -13,7 +13,7 @@ export class PredicateVerifiedEventHandler implements DomainEventHandlerInterfac
) {}
async handle(event: PredicateVerifiedEvent): Promise<void> {
this.logger.info(
this.logger.debug(
`Updating predicate ${event.payload.predicate.name} for job ${event.payload.predicate.jobUuid} with status ${event.payload.predicateVerificationResult}`,
)

View File

@@ -24,7 +24,9 @@ describe('SubscriptionCancelledEventHandler', () => {
})
it('should schedule a job to do an exit interview', async () => {
await createHandler().handle({ payload: { userEmail: 'test@test.te' } } as jest.Mocked<SubscriptionCancelledEvent>)
await createHandler().handle({
payload: { userEmail: 'test@test.te', replaced: false },
} as jest.Mocked<SubscriptionCancelledEvent>)
expect(jobRepository.save).toHaveBeenNthCalledWith(1, {
createdAt: 1,
@@ -35,4 +37,12 @@ describe('SubscriptionCancelledEventHandler', () => {
userIdentifierType: 'email',
})
})
it('should not schedule a job to do an exit interview if the subscription is replaced', async () => {
await createHandler().handle({
payload: { userEmail: 'test@test.te', replaced: true },
} as jest.Mocked<SubscriptionCancelledEvent>)
expect(jobRepository.save).not.toHaveBeenCalled()
})
})

View File

@@ -16,7 +16,9 @@ export class SubscriptionCancelledEventHandler implements DomainEventHandlerInte
) {}
async handle(event: SubscriptionCancelledEvent): Promise<void> {
await this.scheduleExitInterview(event)
if (!event.payload.replaced) {
await this.scheduleExitInterview(event)
}
}
private async scheduleExitInterview(event: SubscriptionCancelledEvent): Promise<void> {

View File

@@ -54,6 +54,7 @@ describe('JobDoneInterpreter', () => {
logger = {} as jest.Mocked<Logger>
logger.info = jest.fn()
logger.debug = jest.fn()
logger.warn = jest.fn()
})
@@ -205,7 +206,7 @@ describe('JobDoneInterpreter', () => {
expect(domainEventFactory.createDiscountApplyRequestedEvent).toHaveBeenCalledWith({
userEmail: 'test@standardnotes.com',
discountCode: 'limited-10',
discountCode: 'limited-20',
})
expect(domainEventPublisher.publish).toHaveBeenCalled()
})
@@ -234,7 +235,7 @@ describe('JobDoneInterpreter', () => {
expect(domainEventFactory.createDiscountWithdrawRequestedEvent).toHaveBeenCalledWith({
userEmail: 'test@standardnotes.com',
discountCode: 'limited-10',
discountCode: 'limited-20',
})
expect(domainEventPublisher.publish).toHaveBeenCalled()
})

View File

@@ -31,10 +31,10 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
return
}
this.logger.info(`[${jobUuid}]${job.name}: Interpreting job as done.`)
this.logger.debug(`[${jobUuid}]${job.name}: Interpreting job as done.`)
if (!(await this.predicatesAreFulfilled(job))) {
this.logger.info(`[${jobUuid}]${job.name}: predicates are not fulfilled.`)
this.logger.debug(`[${jobUuid}]${job.name}: predicates are not fulfilled.`)
return
}
@@ -73,7 +73,7 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
}
private async requestEmailBackupEncouragementEmail(job: Job): Promise<void> {
this.logger.info(`[${job.uuid}]${job.name}: requesting email backup encouragement email.`)
this.logger.debug(`[${job.uuid}]${job.name}: requesting email backup encouragement email.`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createEmailMessageRequestedEvent({
@@ -85,7 +85,7 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
}
private async requestSubscriptionPurchaseEncouragementEmail(job: Job): Promise<void> {
this.logger.info(`[${job.uuid}]${job.name}: requesting subscription purchase encouragement email.`)
this.logger.debug(`[${job.uuid}]${job.name}: requesting subscription purchase encouragement email.`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createEmailMessageRequestedEvent({
@@ -99,7 +99,7 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
}
private async requestExitInterviewEmail(job: Job): Promise<void> {
this.logger.info(`[${job.uuid}]${job.name}: requesting exit interview email.`)
this.logger.debug(`[${job.uuid}]${job.name}: requesting exit interview email.`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createEmailMessageRequestedEvent({
@@ -111,23 +111,23 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
}
private async requestDiscountApply(job: Job): Promise<void> {
this.logger.info(`[${job.uuid}]${job.name}: requesting discount applying.`)
this.logger.debug(`[${job.uuid}]${job.name}: requesting discount applying.`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createDiscountApplyRequestedEvent({
userEmail: job.userIdentifier,
discountCode: 'limited-10',
discountCode: 'limited-20',
}),
)
}
private async requestDiscountWithdraw(job: Job): Promise<void> {
this.logger.info(`[${job.uuid}]${job.name}: requesting discount withdraw.`)
this.logger.debug(`[${job.uuid}]${job.name}: requesting discount withdraw.`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createDiscountWithdrawRequestedEvent({
userEmail: job.userIdentifier,
discountCode: 'limited-10',
discountCode: 'limited-20',
}),
)
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.3.0](https://github.com/standardnotes/server/compare/@standardnotes/security@1.2.6...@standardnotes/security@1.3.0) (2022-09-05)
### Features
* **auth:** add keeping stats on payments ([0c176b7](https://github.com/standardnotes/server/commit/0c176b70f8281e1e490224b9c7ab85f272a3d4e9))
## [1.2.6](https://github.com/standardnotes/server/compare/@standardnotes/security@1.2.5...@standardnotes/security@1.2.6) (2022-07-14)
**Note:** Version bump only for package @standardnotes/security

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/security",
"version": "1.2.6",
"version": "1.3.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -23,7 +23,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage"
"test": "jest spec --coverage"
},
"dependencies": {
"@standardnotes/common": "workspace:*",

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.11.0](https://github.com/standardnotes/server/compare/@standardnotes/sncrypto-node@1.10.1...@standardnotes/sncrypto-node@1.11.0) (2022-09-05)
### Features
* **auth:** add keeping stats on payments ([0c176b7](https://github.com/standardnotes/server/commit/0c176b70f8281e1e490224b9c7ab85f272a3d4e9))
## [1.10.1](https://github.com/standardnotes/server/compare/@standardnotes/sncrypto-node@1.10.0...@standardnotes/sncrypto-node@1.10.1) (2022-07-12)
**Note:** Version bump only for package @standardnotes/sncrypto-node

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/sncrypto-node",
"version": "1.10.1",
"version": "1.11.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -21,7 +21,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec"
"test": "jest spec"
},
"dependencies": {
"@standardnotes/sncrypto-common": "^1.9.0",

View File

@@ -3,6 +3,74 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.6.64](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.63...@standardnotes/syncing-server@1.6.64) (2022-09-07)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.63](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.62...@standardnotes/syncing-server@1.6.63) (2022-09-07)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.62](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.61...@standardnotes/syncing-server@1.6.62) (2022-09-06)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.61](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.60...@standardnotes/syncing-server@1.6.61) (2022-09-06)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.60](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.59...@standardnotes/syncing-server@1.6.60) (2022-09-06)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.59](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.58...@standardnotes/syncing-server@1.6.59) (2022-09-06)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.58](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.57...@standardnotes/syncing-server@1.6.58) (2022-09-06)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.57](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.56...@standardnotes/syncing-server@1.6.57) (2022-09-05)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.56](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.55...@standardnotes/syncing-server@1.6.56) (2022-09-05)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.55](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.54...@standardnotes/syncing-server@1.6.55) (2022-09-05)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.54](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.53...@standardnotes/syncing-server@1.6.54) (2022-09-05)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.53](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.52...@standardnotes/syncing-server@1.6.53) (2022-09-01)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.52](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.51...@standardnotes/syncing-server@1.6.52) (2022-09-01)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.51](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.50...@standardnotes/syncing-server@1.6.51) (2022-08-30)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.50](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.49...@standardnotes/syncing-server@1.6.50) (2022-08-15)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.49](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.48...@standardnotes/syncing-server@1.6.49) (2022-08-15)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.48](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.47...@standardnotes/syncing-server@1.6.48) (2022-08-15)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.47](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.46...@standardnotes/syncing-server@1.6.47) (2022-08-15)
**Note:** Version bump only for package @standardnotes/syncing-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.6.47",
"version": "1.6.64",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -52,7 +52,7 @@
"reflect-metadata": "0.1.13",
"typeorm": "^0.3.6",
"ua-parser-js": "1.0.2",
"uuid": "8.3.2",
"uuid": "^9.0.0",
"winston": "^3.8.1"
},
"devDependencies": {

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.11.0](https://github.com/standardnotes/server/compare/@standardnotes/time@1.10.0...@standardnotes/time@1.11.0) (2022-09-06)
### Features
* **time:** add converting microseconds to time structure ([db8333a](https://github.com/standardnotes/server/commit/db8333a858ba0fa6750116967355d26e72b8c16d))
# [1.10.0](https://github.com/standardnotes/server/compare/@standardnotes/time@1.9.1...@standardnotes/time@1.10.0) (2022-09-05)
### Features
* **auth:** add keeping stats on payments ([0c176b7](https://github.com/standardnotes/server/commit/0c176b70f8281e1e490224b9c7ab85f272a3d4e9))
## [1.9.1](https://github.com/standardnotes/server/compare/@standardnotes/time@1.9.0...@standardnotes/time@1.9.1) (2022-07-12)
**Note:** Version bump only for package @standardnotes/time

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/time",
"version": "1.9.1",
"version": "1.11.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -21,7 +21,7 @@
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage"
"test": "jest spec --coverage"
},
"dependencies": {
"dayjs": "^1.10.8",

View File

@@ -1,4 +1,7 @@
export enum Time {
MicrosecondsInADay = 86_400_000_000,
MicrosecondsInAnHour = 3_600_000_000,
MicrosecondsInAMinute = 60_000_000,
MicrosecondsInASecond = 1_000_000,
MicrosecondsInAMillisecond = 1_000,
}

View File

@@ -0,0 +1,6 @@
export type TimeStructure = {
days: number
hours: number
minutes: number
seconds: number
}

View File

@@ -108,4 +108,13 @@ describe('Timer', () => {
it('should format date', () => {
expect(createTimer().formatDate(new Date('2021-03-29T08:00:05.233123Z'), 'YYYY-MM-DD')).toEqual('2021-03-29')
})
it('should convert microseconds to time structure', () => {
expect(createTimer().convertMicrosecondsToTimeStructure(1821050982073)).toEqual({
days: 21,
hours: 1,
minutes: 50,
seconds: 50,
})
})
})

View File

@@ -3,12 +3,33 @@ import * as utc from 'dayjs/plugin/utc'
import * as microtime from 'microtime'
import { Time } from './Time'
import { TimerInterface } from './TimerInterface'
import { TimeStructure } from './TimeStructure'
export class Timer implements TimerInterface {
constructor() {
dayjs.extend(utc)
}
convertMicrosecondsToTimeStructure(microseconds: number): TimeStructure {
const days = Math.floor(microseconds / Time.MicrosecondsInADay)
const hoursLeftOver = microseconds % Time.MicrosecondsInADay
const hours = Math.floor(hoursLeftOver / Time.MicrosecondsInAnHour)
const minutesLeftOver = microseconds % Time.MicrosecondsInAnHour
const minutes = Math.floor(minutesLeftOver / Time.MicrosecondsInAMinute)
const secondsLeftOver = microseconds % Time.MicrosecondsInAMinute
const seconds = Math.floor(secondsLeftOver / Time.MicrosecondsInASecond)
return {
days,
hours,
minutes,
seconds,
}
}
formatDate(date: Date, format: string): string {
return dayjs.utc(date).format(format)
}

Some files were not shown because too many files have changed in this diff Show More