Compare commits

..

47 Commits

Author SHA1 Message Date
standardci a04ab5b0e8 chore(release): publish new version
- @standardnotes/analytics@1.11.0
 - @standardnotes/api-gateway@1.9.0
 - @standardnotes/auth-server@1.13.3
 - @standardnotes/domain-events-infra@1.7.29
 - @standardnotes/domain-events@2.52.0
 - @standardnotes/event-store@1.1.28
 - @standardnotes/files-server@1.5.31
 - @standardnotes/scheduler-server@1.10.5
 - @standardnotes/syncing-server@1.6.36
2022-08-08 19:07:09 +00:00
Karol Sójko 845f08b060 feat(api-gateway): add analytics over time to daily report event 2022-08-08 21:05:31 +02:00
Karol Sójko 31c849cc2d fix(analytics): format of changes over time to total count 2022-08-08 21:04:54 +02:00
Karol Sójko 977757d346 feat(analytics): add calculating analytics over time 2022-08-08 20:48:18 +02:00
standardci f2c549158d chore(release): publish new version
- @standardnotes/analytics@1.10.0
 - @standardnotes/api-gateway@1.8.0
 - @standardnotes/auth-server@1.13.2
 - @standardnotes/syncing-server@1.6.35
2022-08-08 18:16:43 +00:00
Karol Sójko a36764f1b0 feat(api-gateway): add marking server interaction as general activity in analytics 2022-08-08 20:14:55 +02:00
standardci 033bc25d8f chore(release): publish new version
- @standardnotes/api-gateway@1.7.4
 - @standardnotes/auth-server@1.13.1
 - @standardnotes/scheduler-server@1.10.4
 - @standardnotes/syncing-server@1.6.34
2022-07-29 09:40:04 +00:00
Karol Sójko 31bd253a73 chore(deps): fix newrelic winston enricher initialization 2022-07-29 11:38:03 +02:00
standardci 2f4977be63 chore(release): publish new version
- @standardnotes/auth-server@1.13.0
2022-07-29 07:09:52 +00:00
Karol Sójko 35f931a708 feat(auth): remove crypto-random-string in favour of @standardnotes/sncrypto-node 2022-07-29 09:08:22 +02:00
standardci 3e23d8d5d5 chore(release): publish new version
- @standardnotes/api-gateway@1.7.3
 - @standardnotes/auth-server@1.12.2
 - @standardnotes/scheduler-server@1.10.3
 - @standardnotes/syncing-server@1.6.33
2022-07-29 06:35:21 +00:00
Karol Sójko 004de0a655 chore(deps): upgrade @newrelic/winston-enricher 2022-07-29 08:33:29 +02:00
standardci 47a1af4be1 chore(release): publish new version
- @standardnotes/scheduler-server@1.10.2
2022-07-29 06:30:08 +00:00
Karol Sójko 7b17c4caa8 chore(deps): upgrade inversify 2022-07-29 08:27:57 +02:00
standardci fa29885b3f chore(release): publish new version
- @standardnotes/api-gateway@1.7.2
 - @standardnotes/files-server@1.5.30
 - @standardnotes/syncing-server@1.6.32
2022-07-27 11:40:47 +00:00
Karol Sójko beece69f9e chore(deps): upgrade helmet 2022-07-27 13:39:19 +02:00
standardci 600ff1d62b chore(release): publish new version
- @standardnotes/api-gateway@1.7.1
 - @standardnotes/auth-server@1.12.1
 - @standardnotes/files-server@1.5.29
 - @standardnotes/scheduler-server@1.10.1
 - @standardnotes/syncing-server@1.6.31
2022-07-27 11:14:05 +00:00
Karol Sójko 094dc192a9 chore(deps): upgrade npm-check-updates 2022-07-27 13:12:25 +02:00
standardci 939bf30138 chore(release): publish new version
- @standardnotes/api-gateway@1.7.0
2022-07-26 13:00:56 +00:00
Karol Sójko d203ce188a feat(api-gateway): add limited discount offer purchased to analytics report 2022-07-26 14:59:23 +02:00
standardci 268fed19f9 chore(release): publish new version
- @standardnotes/analytics@1.9.0
 - @standardnotes/api-gateway@1.6.30
 - @standardnotes/auth-server@1.12.0
 - @standardnotes/domain-events-infra@1.7.28
 - @standardnotes/domain-events@2.51.0
 - @standardnotes/event-store@1.1.27
 - @standardnotes/files-server@1.5.28
 - @standardnotes/scheduler-server@1.10.0
 - @standardnotes/syncing-server@1.6.30
2022-07-26 11:54:06 +00:00
Karol Sójko 04bf414de4 feat(scheduler): enable discount applying and withdraw for everyone 2022-07-26 13:52:34 +02:00
Karol Sójko 28e1c65631 feat(domain-events): add discount code to subscription purchased event 2022-07-26 13:52:34 +02:00
Karol Sójko e936ac4ce1 feat(auth): add analytics for purchased subscription with a limited discount offer 2022-07-26 13:52:34 +02:00
Karol Sójko 13201e7a9e feat(analytics): add limited discount offer purchased activity 2022-07-26 13:52:34 +02:00
standardci 9740b28764 chore(release): publish new version
- @standardnotes/scheduler-server@1.9.2
2022-07-26 10:37:12 +00:00
Karol Sójko 1fa94efa02 fix(scheduler): change the discount code to an absolute discount 2022-07-26 12:35:16 +02:00
standardci 44172e1a8e chore(release): publish new version
- @standardnotes/scheduler-server@1.9.1
2022-07-26 07:25:30 +00:00
Karol Sójko 4ab0d24d24 fix(scheduler): eliminate read/write concurrency hazzard while updating predicate status 2022-07-26 09:23:45 +02:00
standardci 049e66770a chore(release): publish new version
- @standardnotes/scheduler-server@1.9.0
2022-07-25 18:45:09 +00:00
Karol Sójko bf12687f63 feat(scheduler): add job interpreting logs 2022-07-25 20:43:18 +02:00
standardci 10389d9029 chore(release): publish new version
- @standardnotes/auth-server@1.11.31
2022-07-25 18:34:13 +00:00
Karol Sójko 40996f9d48 fix(auth): marking predicate verification result if user is not existing 2022-07-25 20:32:40 +02:00
standardci 3d284461f3 chore(release): publish new version
- @standardnotes/api-gateway@1.6.29
 - @standardnotes/auth-server@1.11.30
 - @standardnotes/domain-events-infra@1.7.27
 - @standardnotes/domain-events@2.50.2
 - @standardnotes/event-store@1.1.26
 - @standardnotes/files-server@1.5.27
 - @standardnotes/predicates@1.3.0
 - @standardnotes/scheduler-server@1.8.2
 - @standardnotes/syncing-server@1.6.29
2022-07-25 18:22:30 +00:00
Karol Sójko 6642641c11 feat(predicates): add could-not-be-determined predicate verification result 2022-07-25 20:20:35 +02:00
standardci 3e637a482e chore(release): publish new version
- @standardnotes/scheduler-server@1.8.1
2022-07-25 11:02:23 +00:00
Karol Sójko 6374248132 fix(scheduler): checking for predicates fullfillment on applying discount 2022-07-25 13:00:46 +02:00
standardci b9661d74ee chore(release): publish new version
- @standardnotes/scheduler-server@1.8.0
2022-07-25 07:45:53 +00:00
Karol Sójko 0a5b956cb9 feat(scheduler): add feature flag behind applying and withdrawing discounts 2022-07-25 09:44:06 +02:00
standardci be88fd941d chore(release): publish new version
- @standardnotes/scheduler-server@1.7.0
2022-07-25 05:32:13 +00:00
Karol Sójko 48af9e7c1c feat(scheduler): add publishing discount apply/withdraw events 2022-07-25 07:30:32 +02:00
Karol Sójko 71684350e9 feat(scheduler): add creating discount apply/withdraw events 2022-07-25 07:20:50 +02:00
standardci 9a1924b7c6 chore(release): publish new version
- @standardnotes/scheduler-server@1.6.0
2022-07-22 11:38:37 +00:00
Karol Sójko fc20697d81 feat(scheduler): schedule apply and withdraw subscription discounts upon registration 2022-07-22 13:37:00 +02:00
standardci e7dda207fa chore(release): publish new version
- @standardnotes/analytics@1.8.3
 - @standardnotes/api-gateway@1.6.28
 - @standardnotes/auth-server@1.11.29
 - @standardnotes/domain-events-infra@1.7.26
 - @standardnotes/event-store@1.1.25
 - @standardnotes/files-server@1.5.26
 - @standardnotes/scheduler-server@1.5.23
 - @standardnotes/syncing-server@1.6.28
2022-07-15 11:02:23 +00:00
Karol Sójko 7fd97fa194 fix(analytics): change send_command to bitop function 2022-07-15 12:58:44 +02:00
Karol Sójko ccbadfbd69 chore(deps): upgrade ioredis 2022-07-15 12:55:21 +02:00
76 changed files with 953 additions and 388 deletions
Generated
+41 -125
View File
@@ -122,7 +122,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["eslint-config-prettier", "virtual:8859b278716fedf3e7458b5628625f7e35678c418626878559a0b816445001b7e24c55546f4677ba4c20b521aa0cf52cc33ac07deff171e383ada6eeab69933f#npm:8.5.0"],\
["ini", "npm:3.0.0"],\
["newrelic", "npm:8.14.1"],\
["npm-check-updates", "npm:15.3.3"],\
["npm-check-updates", "npm:16.0.1"],\
["prettier", "npm:2.7.1"],\
["ts-node", "virtual:c0eab07e71af57f5501e97e7ca7a2a4f4965035bd2455ad124a8b09fa55780657c55fe3df41019fa6c2c44487c897668c842a0939e380b3c1f13b3756d128543#npm:10.8.2"],\
["typescript", "patch:typescript@npm%3A4.7.4#~builtin<compat/typescript>::version=4.7.4&hash=7ad353"]\
@@ -1868,27 +1868,23 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["@newrelic/winston-enricher", [\
["npm:2.1.2", {\
"packageLocation": "./.yarn/cache/@newrelic-winston-enricher-npm-2.1.2-732878a1b2-d001c13166.zip/node_modules/@newrelic/winston-enricher/",\
["npm:4.0.0", {\
"packageLocation": "./.yarn/cache/@newrelic-winston-enricher-npm-4.0.0-ebaf2d0d28-f737e7d952.zip/node_modules/@newrelic/winston-enricher/",\
"packageDependencies": [\
["@newrelic/winston-enricher", "npm:2.1.2"]\
["@newrelic/winston-enricher", "npm:4.0.0"]\
],\
"linkType": "SOFT"\
}],\
["virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:2.1.2", {\
"packageLocation": "./.yarn/__virtual__/@newrelic-winston-enricher-virtual-193127fbcd/0/cache/@newrelic-winston-enricher-npm-2.1.2-732878a1b2-d001c13166.zip/node_modules/@newrelic/winston-enricher/",\
["virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.0.0", {\
"packageLocation": "./.yarn/__virtual__/@newrelic-winston-enricher-virtual-766a341e22/0/cache/@newrelic-winston-enricher-npm-4.0.0-ebaf2d0d28-f737e7d952.zip/node_modules/@newrelic/winston-enricher/",\
"packageDependencies": [\
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:2.1.2"],\
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.0.0"],\
["@types/newrelic", "npm:7.0.3"],\
["@types/winston", null],\
["newrelic", "npm:8.14.1"],\
["winston", "npm:3.8.1"]\
["newrelic", "npm:8.14.1"]\
],\
"packagePeers": [\
"@types/newrelic",\
"@types/winston",\
"newrelic",\
"winston"\
"newrelic"\
],\
"linkType": "HARD"\
}]\
@@ -2480,7 +2476,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/jest", "npm:28.1.4"],\
["@typescript-eslint/eslint-plugin", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:5.30.5"],\
["eslint-plugin-prettier", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.2.1"],\
["ioredis", "npm:4.28.5"],\
["ioredis", "npm:5.2.0"],\
["jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.1.2"],\
["reflect-metadata", "npm:0.1.13"],\
["ts-jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.0.5"]\
@@ -2508,7 +2504,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageLocation": "./packages/api-gateway/",\
"packageDependencies": [\
["@standardnotes/api-gateway", "workspace:packages/api-gateway"],\
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:2.1.2"],\
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.0.0"],\
["@sentry/node", "npm:7.5.0"],\
["@standardnotes/analytics", "workspace:packages/analytics"],\
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
@@ -2530,15 +2526,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["eslint", "npm:8.19.0"],\
["eslint-plugin-prettier", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.2.1"],\
["express", "npm:4.18.1"],\
["helmet", "npm:4.4.1"],\
["helmet", "npm:5.1.1"],\
["inversify", "npm:6.0.1"],\
["inversify-express-utils", "npm:6.4.3"],\
["ioredis", "npm:5.1.0"],\
["ioredis", "npm:5.2.0"],\
["jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.1.2"],\
["jsonwebtoken", "npm:8.5.1"],\
["newrelic", "npm:8.14.1"],\
["nodemon", "npm:2.0.19"],\
["npm-check-updates", "npm:15.3.3"],\
["npm-check-updates", "npm:16.0.1"],\
["prettyjson", "npm:1.2.5"],\
["reflect-metadata", "npm:0.1.13"],\
["ts-jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.0.5"],\
@@ -2563,7 +2559,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageLocation": "./packages/auth/",\
"packageDependencies": [\
["@standardnotes/auth-server", "workspace:packages/auth"],\
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:2.1.2"],\
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.0.0"],\
["@sentry/node", "npm:7.5.0"],\
["@standardnotes/analytics", "workspace:packages/analytics"],\
["@standardnotes/api", "npm:1.1.19"],\
@@ -2593,7 +2589,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["axios", "npm:0.27.2"],\
["bcryptjs", "npm:2.4.3"],\
["cors", "npm:2.8.5"],\
["crypto-random-string", "npm:3.3.0"],\
["dayjs", "npm:1.11.3"],\
["dotenv", "npm:16.0.1"],\
["eslint", "npm:8.19.0"],\
@@ -2601,12 +2596,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["express", "npm:4.18.1"],\
["inversify", "npm:6.0.1"],\
["inversify-express-utils", "npm:6.4.3"],\
["ioredis", "npm:5.1.0"],\
["ioredis", "npm:5.2.0"],\
["jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.1.2"],\
["mysql2", "npm:2.3.3"],\
["newrelic", "npm:8.14.1"],\
["nodemon", "npm:2.0.19"],\
["npm-check-updates", "npm:15.3.3"],\
["npm-check-updates", "npm:16.0.1"],\
["otplib", "npm:12.0.1"],\
["prettyjson", "npm:1.2.5"],\
["reflect-metadata", "npm:0.1.13"],\
@@ -2680,7 +2675,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@typescript-eslint/eslint-plugin", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:5.30.5"],\
["aws-sdk", "npm:2.1168.0"],\
["eslint-plugin-prettier", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.2.1"],\
["ioredis", "npm:4.28.5"],\
["ioredis", "npm:5.2.0"],\
["jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.1.2"],\
["newrelic", "npm:8.14.1"],\
["reflect-metadata", "npm:0.1.13"],\
@@ -2725,7 +2720,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["eslint", "npm:8.19.0"],\
["eslint-plugin-prettier", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.2.1"],\
["inversify", "npm:6.0.1"],\
["ioredis", "npm:5.1.0"],\
["ioredis", "npm:5.2.0"],\
["jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.1.2"],\
["mysql2", "npm:2.3.3"],\
["newrelic", "npm:8.14.1"],\
@@ -2782,15 +2777,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["eslint-plugin-prettier", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.2.1"],\
["express", "npm:4.18.1"],\
["express-winston", "virtual:b442cf0427cc365d1c137f7340f9b81f9b204561afe791a8564ae9590c3a7fc4b5f793aaf8817b946f75a3cb64d03ef8790eb847f8b576b41e700da7b00c240c#npm:4.2.0"],\
["helmet", "npm:4.6.0"],\
["helmet", "npm:5.1.1"],\
["inversify", "npm:6.0.1"],\
["inversify-express-utils", "npm:6.4.3"],\
["ioredis", "npm:5.1.0"],\
["ioredis", "npm:5.2.0"],\
["jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.1.2"],\
["jsonwebtoken", "npm:8.5.1"],\
["newrelic", "npm:8.14.1"],\
["nodemon", "npm:2.0.19"],\
["npm-check-updates", "npm:15.3.3"],\
["npm-check-updates", "npm:16.0.1"],\
["prettyjson", "npm:1.2.5"],\
["reflect-metadata", "npm:0.1.13"],\
["ts-jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.0.5"],\
@@ -2861,7 +2856,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageLocation": "./packages/scheduler/",\
"packageDependencies": [\
["@standardnotes/scheduler-server", "workspace:packages/scheduler"],\
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:2.1.2"],\
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.0.0"],\
["@sentry/node", "npm:7.5.0"],\
["@standardnotes/common", "workspace:packages/common"],\
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
@@ -2877,12 +2872,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["dayjs", "npm:1.11.3"],\
["dotenv", "npm:16.0.1"],\
["eslint-plugin-prettier", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.2.1"],\
["inversify", "npm:5.0.5"],\
["ioredis", "npm:5.1.0"],\
["inversify", "npm:6.0.1"],\
["ioredis", "npm:5.2.0"],\
["jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.1.2"],\
["mysql2", "npm:2.3.3"],\
["newrelic", "npm:8.14.1"],\
["npm-check-updates", "npm:15.3.3"],\
["npm-check-updates", "npm:16.0.1"],\
["reflect-metadata", "npm:0.1.13"],\
["ts-jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.0.5"],\
["typeorm", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:0.3.7"],\
@@ -2928,7 +2923,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["eslint-config-prettier", "virtual:8859b278716fedf3e7458b5628625f7e35678c418626878559a0b816445001b7e24c55546f4677ba4c20b521aa0cf52cc33ac07deff171e383ada6eeab69933f#npm:8.5.0"],\
["ini", "npm:3.0.0"],\
["newrelic", "npm:8.14.1"],\
["npm-check-updates", "npm:15.3.3"],\
["npm-check-updates", "npm:16.0.1"],\
["prettier", "npm:2.7.1"],\
["ts-node", "virtual:c0eab07e71af57f5501e97e7ca7a2a4f4965035bd2455ad124a8b09fa55780657c55fe3df41019fa6c2c44487c897668c842a0939e380b3c1f13b3756d128543#npm:10.8.2"],\
["typescript", "patch:typescript@npm%3A4.7.4#~builtin<compat/typescript>::version=4.7.4&hash=7ad353"]\
@@ -2996,7 +2991,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"packageLocation": "./packages/syncing-server/",\
"packageDependencies": [\
["@standardnotes/syncing-server", "workspace:packages/syncing-server"],\
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:2.1.2"],\
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.0.0"],\
["@sentry/node", "npm:7.5.0"],\
["@standardnotes/analytics", "workspace:packages/analytics"],\
["@standardnotes/common", "workspace:packages/common"],\
@@ -3026,16 +3021,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["eslint", "npm:8.19.0"],\
["eslint-plugin-prettier", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.2.1"],\
["express", "npm:4.18.1"],\
["helmet", "npm:4.3.1"],\
["helmet", "npm:5.1.1"],\
["inversify", "npm:6.0.1"],\
["inversify-express-utils", "npm:6.4.3"],\
["ioredis", "npm:5.1.0"],\
["ioredis", "npm:5.2.0"],\
["jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.1.2"],\
["jsonwebtoken", "npm:8.5.1"],\
["mysql2", "npm:2.3.3"],\
["newrelic", "npm:8.14.1"],\
["nodemon", "npm:2.0.19"],\
["npm-check-updates", "npm:15.3.3"],\
["npm-check-updates", "npm:16.0.1"],\
["prettyjson", "npm:1.2.5"],\
["reflect-metadata", "npm:0.1.13"],\
["ts-jest", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:28.0.5"],\
@@ -5513,14 +5508,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["crypto-random-string", [\
["npm:3.3.0", {\
"packageLocation": "./.yarn/cache/crypto-random-string-npm-3.3.0-4f73472f10-deff986631.zip/node_modules/crypto-random-string/",\
"packageDependencies": [\
["crypto-random-string", "npm:3.3.0"],\
["type-fest", "npm:0.8.1"]\
],\
"linkType": "HARD"\
}],\
["npm:4.0.0", {\
"packageLocation": "./.yarn/cache/crypto-random-string-npm-4.0.0-b9f0f76168-91f148f27b.zip/node_modules/crypto-random-string/",\
"packageDependencies": [\
@@ -5744,13 +5731,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["denque", [\
["npm:1.5.1", {\
"packageLocation": "./.yarn/cache/denque-npm-1.5.1-2dd42d2dcb-4375ad19d5.zip/node_modules/denque/",\
"packageDependencies": [\
["denque", "npm:1.5.1"]\
],\
"linkType": "HARD"\
}],\
["npm:2.0.1", {\
"packageLocation": "./.yarn/cache/denque-npm-2.0.1-4ba00e404b-ec398d1e3c.zip/node_modules/denque/",\
"packageDependencies": [\
@@ -7240,24 +7220,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["helmet", [\
["npm:4.3.1", {\
"packageLocation": "./.yarn/cache/helmet-npm-4.3.1-22cd4b53d2-47f59d8b99.zip/node_modules/helmet/",\
["npm:5.1.1", {\
"packageLocation": "./.yarn/cache/helmet-npm-5.1.1-d12265628c-b72ba26cc4.zip/node_modules/helmet/",\
"packageDependencies": [\
["helmet", "npm:4.3.1"]\
],\
"linkType": "HARD"\
}],\
["npm:4.4.1", {\
"packageLocation": "./.yarn/cache/helmet-npm-4.4.1-286ac392ee-cfe385e185.zip/node_modules/helmet/",\
"packageDependencies": [\
["helmet", "npm:4.4.1"]\
],\
"linkType": "HARD"\
}],\
["npm:4.6.0", {\
"packageLocation": "./.yarn/cache/helmet-npm-4.6.0-f244fd965c-139ad678d1.zip/node_modules/helmet/",\
"packageDependencies": [\
["helmet", "npm:4.6.0"]\
["helmet", "npm:5.1.1"]\
],\
"linkType": "HARD"\
}]\
@@ -7585,13 +7551,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["inversify", [\
["npm:5.0.5", {\
"packageLocation": "./.yarn/cache/inversify-npm-5.0.5-4d1f1420a9-a4bf1a6a62.zip/node_modules/inversify/",\
"packageDependencies": [\
["inversify", "npm:5.0.5"]\
],\
"linkType": "HARD"\
}],\
["npm:6.0.1", {\
"packageLocation": "./.yarn/cache/inversify-npm-6.0.1-39ef6784da-b6c9b56ef7.zip/node_modules/inversify/",\
"packageDependencies": [\
@@ -7613,28 +7572,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["ioredis", [\
["npm:4.28.5", {\
"packageLocation": "./.yarn/cache/ioredis-npm-4.28.5-74671f2fa3-a8793c3324.zip/node_modules/ioredis/",\
["npm:5.2.0", {\
"packageLocation": "./.yarn/cache/ioredis-npm-5.2.0-e2dd53ed39-37189fcd4b.zip/node_modules/ioredis/",\
"packageDependencies": [\
["ioredis", "npm:4.28.5"],\
["cluster-key-slot", "npm:1.1.0"],\
["debug", "virtual:b86a9fb34323a98c6519528ed55faa0d9b44ca8879307c0b29aa384bde47ff59a7d0c9051b31246f14521dfb71ba3c5d6d0b35c29fffc17bf875aa6ad977d9e8#npm:4.3.4"],\
["denque", "npm:1.5.1"],\
["lodash.defaults", "npm:4.2.0"],\
["lodash.flatten", "npm:4.4.0"],\
["lodash.isarguments", "npm:3.1.0"],\
["p-map", "npm:2.1.0"],\
["redis-commands", "npm:1.7.0"],\
["redis-errors", "npm:1.2.0"],\
["redis-parser", "npm:3.0.0"],\
["standard-as-callback", "npm:2.1.0"]\
],\
"linkType": "HARD"\
}],\
["npm:5.1.0", {\
"packageLocation": "./.yarn/cache/ioredis-npm-5.1.0-cc56f45bbf-7b1c137836.zip/node_modules/ioredis/",\
"packageDependencies": [\
["ioredis", "npm:5.1.0"],\
["ioredis", "npm:5.2.0"],\
["@ioredis/commands", "npm:1.2.0"],\
["cluster-key-slot", "npm:1.1.0"],\
["debug", "virtual:b86a9fb34323a98c6519528ed55faa0d9b44ca8879307c0b29aa384bde47ff59a7d0c9051b31246f14521dfb71ba3c5d6d0b35c29fffc17bf875aa6ad977d9e8#npm:4.3.4"],\
@@ -8921,15 +8862,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD"\
}]\
]],\
["lodash.flatten", [\
["npm:4.4.0", {\
"packageLocation": "./.yarn/cache/lodash.flatten-npm-4.4.0-495935e617-0ac34a393d.zip/node_modules/lodash.flatten/",\
"packageDependencies": [\
["lodash.flatten", "npm:4.4.0"]\
],\
"linkType": "HARD"\
}]\
]],\
["lodash.includes", [\
["npm:4.3.0", {\
"packageLocation": "./.yarn/cache/lodash.includes-npm-4.3.0-3a2f6fa22c-71092c1305.zip/node_modules/lodash.includes/",\
@@ -9810,10 +9742,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["npm-check-updates", [\
["npm:15.3.3", {\
"packageLocation": "./.yarn/cache/npm-check-updates-npm-15.3.3-146d2ff74b-3ee33fe369.zip/node_modules/npm-check-updates/",\
["npm:16.0.1", {\
"packageLocation": "./.yarn/cache/npm-check-updates-npm-16.0.1-fd143fcd27-1e747421f3.zip/node_modules/npm-check-updates/",\
"packageDependencies": [\
["npm-check-updates", "npm:15.3.3"],\
["npm-check-updates", "npm:16.0.1"],\
["chalk", "npm:5.0.1"],\
["cli-table", "npm:0.3.11"],\
["commander", "npm:9.3.0"],\
@@ -10134,13 +10066,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]\
]],\
["p-map", [\
["npm:2.1.0", {\
"packageLocation": "./.yarn/cache/p-map-npm-2.1.0-d9e865dc7c-9e3ad3c9f6.zip/node_modules/p-map/",\
"packageDependencies": [\
["p-map", "npm:2.1.0"]\
],\
"linkType": "HARD"\
}],\
["npm:4.0.0", {\
"packageLocation": "./.yarn/cache/p-map-npm-4.0.0-4677ae07c7-cb0ab21ec0.zip/node_modules/p-map/",\
"packageDependencies": [\
@@ -10999,15 +10924,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD"\
}]\
]],\
["redis-commands", [\
["npm:1.7.0", {\
"packageLocation": "./.yarn/cache/redis-commands-npm-1.7.0-528f61f9d8-d1ff7fbcb5.zip/node_modules/redis-commands/",\
"packageDependencies": [\
["redis-commands", "npm:1.7.0"]\
],\
"linkType": "HARD"\
}]\
]],\
["redis-errors", [\
["npm:1.2.0", {\
"packageLocation": "./.yarn/cache/redis-errors-npm-1.2.0-a81fd9b0f1-f28ac26921.zip/node_modules/redis-errors/",\
@@ -12547,7 +12463,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["dotenv", "npm:16.0.1"],\
["glob", "npm:7.2.3"],\
["hdb-pool", null],\
["ioredis", "npm:5.1.0"],\
["ioredis", "npm:5.2.0"],\
["js-yaml", "npm:4.1.0"],\
["mkdirp", "npm:1.0.4"],\
["mongodb", null],\
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -58,7 +58,7 @@
"eslint": "^8.17.0",
"eslint-config-prettier": "^8.5.0",
"ini": "^3.0.0",
"npm-check-updates": "^15.3.3",
"npm-check-updates": "^16.0.1",
"prettier": "^2.7.1",
"ts-node": "^10.8.1",
"typescript": "^4.7.4"
+28
View File
@@ -3,6 +3,34 @@
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/analytics@1.10.0...@standardnotes/analytics@1.11.0) (2022-08-08)
### Bug Fixes
* **analytics:** format of changes over time to total count ([31c849c](https://github.com/standardnotes/server/commit/31c849cc2d13bc97690da1cbec3d1868ddb733dc))
### Features
* **analytics:** add calculating analytics over time ([977757d](https://github.com/standardnotes/server/commit/977757d346ba94794cc090f13d4dfdf489eb9d3a))
# [1.10.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.9.0...@standardnotes/analytics@1.10.0) (2022-08-08)
### Features
* **api-gateway:** add marking server interaction as general activity in analytics ([a36764f](https://github.com/standardnotes/server/commit/a36764f1b058bed014b815fa2818370849053b18))
# [1.9.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.8.3...@standardnotes/analytics@1.9.0) (2022-07-26)
### Features
* **analytics:** add limited discount offer purchased activity ([13201e7](https://github.com/standardnotes/server/commit/13201e7a9ec875796f527b2c500cf631345c36dd))
## [1.8.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.8.2...@standardnotes/analytics@1.8.3) (2022-07-15)
### Bug Fixes
* **analytics:** change send_command to bitop function ([7fd97fa](https://github.com/standardnotes/server/commit/7fd97fa1940b6aab6b235a1ae2cb6ed2aa6ae917))
## [1.8.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.8.1...@standardnotes/analytics@1.8.2) (2022-07-12)
**Note:** Version bump only for package @standardnotes/analytics
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "1.8.2",
"version": "1.11.0",
"engines": {
"node": ">=14.0.0 <17.0.0"
},
@@ -26,11 +26,11 @@
"test:unit": "jest spec --coverage"
},
"devDependencies": {
"@types/ioredis": "^4.28.8",
"@types/ioredis": "^4.28.10",
"@types/jest": "^28.1.4",
"@typescript-eslint/eslint-plugin": "^5.30.0",
"eslint-plugin-prettier": "^4.2.1",
"ioredis": "^4.28.5",
"ioredis": "^5.2.0",
"jest": "^28.1.2",
"ts-jest": "^28.0.5"
},
@@ -1,6 +1,8 @@
export enum AnalyticsActivity {
GeneralActivity = 'general-activity',
EditingItems = 'editing-items',
Login = 'login',
EmailUnbackedUpData = 'email-unbacked-up-data',
EmailBackup = 'email-backup',
LimitedDiscountOfferPurchased = 'limited-discount-offer-purchased',
}
@@ -7,4 +7,8 @@ export interface AnalyticsStoreInterface {
wasActivityDone(activity: AnalyticsActivity, analyticsId: number, period: Period): Promise<boolean>
calculateActivityRetention(activity: AnalyticsActivity, firstPeriod: Period, secondPeriod: Period): Promise<number>
calculateActivityTotalCount(activity: AnalyticsActivity, period: Period): Promise<number>
calculateActivityChangesTotalCount(
activity: AnalyticsActivity,
period: Period,
): Promise<Array<{ periodKey: string; totalCount: number }>>
}
@@ -7,4 +7,5 @@ export enum Period {
WeekBeforeLastWeek,
ThisMonth,
LastMonth,
Last30Days,
}
@@ -5,7 +5,7 @@ describe('PeriodKeyGenerator', () => {
const createGenerator = () => new PeriodKeyGenerator()
beforeEach(() => {
jest.useFakeTimers('modern')
jest.useFakeTimers()
jest.setSystemTime(1653395155000)
})
@@ -13,6 +13,41 @@ describe('PeriodKeyGenerator', () => {
jest.useRealTimers()
})
it('should generate period keys for last 30 days', () => {
expect(createGenerator().getDiscretePeriodKeys(Period.Last30Days)).toEqual([
'2022-4-24',
'2022-4-25',
'2022-4-26',
'2022-4-27',
'2022-4-28',
'2022-4-29',
'2022-4-30',
'2022-5-1',
'2022-5-2',
'2022-5-3',
'2022-5-4',
'2022-5-5',
'2022-5-6',
'2022-5-7',
'2022-5-8',
'2022-5-9',
'2022-5-10',
'2022-5-11',
'2022-5-12',
'2022-5-13',
'2022-5-14',
'2022-5-15',
'2022-5-16',
'2022-5-17',
'2022-5-18',
'2022-5-19',
'2022-5-20',
'2022-5-21',
'2022-5-22',
'2022-5-23',
])
})
it('should generate a period key for today', () => {
expect(createGenerator().getPeriodKey(Period.Today)).toEqual('2022-5-24')
})
@@ -55,4 +90,15 @@ describe('PeriodKeyGenerator', () => {
expect(error).not.toBeNull()
})
it('should throw error on unsupported period for discrete generation', () => {
let error = null
try {
createGenerator().getDiscretePeriodKeys(Period.Today)
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
})
@@ -2,6 +2,21 @@ import { Period } from './Period'
import { PeriodKeyGeneratorInterface } from './PeriodKeyGeneratorInterface'
export class PeriodKeyGenerator implements PeriodKeyGeneratorInterface {
getDiscretePeriodKeys(period: Period): string[] {
const periodKeys = []
switch (period) {
case Period.Last30Days:
for (let i = 1; i <= 30; i++) {
periodKeys.unshift(this.getDailyKey(this.getDateNDaysBefore(i)))
}
return periodKeys
default:
throw new Error(`Unsuporrted period: ${period}`)
}
}
getPeriodKey(period: Period): string {
switch (period) {
case Period.Today:
@@ -61,11 +76,15 @@ export class PeriodKeyGenerator implements PeriodKeyGeneratorInterface {
return date.getDate().toString()
}
private getYesterdayDate(): Date {
const yesterday = new Date()
yesterday.setDate(new Date().getDate() - 1)
private getDateNDaysBefore(n: number) {
const date = new Date()
date.setDate(new Date().getDate() - n)
return yesterday
return date
}
private getYesterdayDate(): Date {
return this.getDateNDaysBefore(1)
}
private getDayBeforeYesterdayDate(): Date {
@@ -2,4 +2,5 @@ import { Period } from './Period'
export interface PeriodKeyGeneratorInterface {
getPeriodKey(period: Period): string
getDiscretePeriodKeys(period: Period): string[]
}
@@ -23,12 +23,54 @@ describe('RedisAnalyticsStore', () => {
redisClient.incr = jest.fn()
redisClient.setbit = jest.fn()
redisClient.getbit = jest.fn().mockReturnValue(1)
redisClient.send_command = jest.fn()
redisClient.bitop = jest.fn()
periodKeyGenerator = {} as jest.Mocked<PeriodKeyGeneratorInterface>
periodKeyGenerator.getPeriodKey = jest.fn().mockReturnValue('period-key')
})
it('should calculate total count changes of activities', async () => {
periodKeyGenerator.getDiscretePeriodKeys = jest.fn().mockReturnValue(['2022-4-24', '2022-4-25', '2022-4-26'])
redisClient.bitcount = jest.fn().mockReturnValueOnce(70).mockReturnValueOnce(71).mockReturnValueOnce(72)
expect(
await createStore().calculateActivityChangesTotalCount(AnalyticsActivity.EditingItems, Period.Last30Days),
).toEqual([
{
periodKey: '2022-4-24',
totalCount: 70,
},
{
periodKey: '2022-4-25',
totalCount: 71,
},
{
periodKey: '2022-4-26',
totalCount: 72,
},
])
expect(redisClient.bitcount).toHaveBeenNthCalledWith(1, 'bitmap:action:editing-items:timespan:2022-4-24')
expect(redisClient.bitcount).toHaveBeenNthCalledWith(2, 'bitmap:action:editing-items:timespan:2022-4-25')
expect(redisClient.bitcount).toHaveBeenNthCalledWith(3, 'bitmap:action:editing-items:timespan:2022-4-26')
})
it('should throw error on calculating total count changes of activities on unsupported period', async () => {
periodKeyGenerator.getDiscretePeriodKeys = jest.fn().mockReturnValue(['2022-4-24', '2022-4-25', '2022-4-26'])
redisClient.bitcount = jest.fn().mockReturnValueOnce(70).mockReturnValueOnce(71).mockReturnValueOnce(72)
let caughtError = null
try {
await createStore().calculateActivityChangesTotalCount(AnalyticsActivity.EditingItems, Period.LastWeek)
} catch (error) {
caughtError = error
}
expect(caughtError).not.toBeNull()
})
it('should calculate total count of activities', async () => {
redisClient.bitcount = jest.fn().mockReturnValue(70)
@@ -50,8 +92,7 @@ describe('RedisAnalyticsStore', () => {
),
).toEqual(70)
expect(redisClient.send_command).toHaveBeenCalledWith(
'BITOP',
expect(redisClient.bitop).toHaveBeenCalledWith(
'AND',
'bitmap:action:editing-items:timespan:period-key-period-key',
'bitmap:action:editing-items:timespan:period-key',
@@ -8,6 +8,26 @@ import { AnalyticsStoreInterface } from '../../Domain/Analytics/AnalyticsStoreIn
export class RedisAnalyticsStore implements AnalyticsStoreInterface {
constructor(private periodKeyGenerator: PeriodKeyGeneratorInterface, private redisClient: IORedis.Redis) {}
async calculateActivityChangesTotalCount(
activity: AnalyticsActivity,
period: Period,
): Promise<Array<{ periodKey: string; totalCount: number }>> {
if (period !== Period.Last30Days) {
throw new Error(`Unsuporrted period: ${period}`)
}
const periodKeys = this.periodKeyGenerator.getDiscretePeriodKeys(Period.Last30Days)
const counts = []
for (const periodKey of periodKeys) {
counts.push({
periodKey,
totalCount: await this.redisClient.bitcount(`bitmap:action:${activity}:timespan:${periodKey}`),
})
}
return counts
}
async markActivity(activities: AnalyticsActivity[], analyticsId: number, periods: Period[]): Promise<void> {
const pipeline = this.redisClient.pipeline()
@@ -59,8 +79,7 @@ export class RedisAnalyticsStore implements AnalyticsStoreInterface {
const diffKey = `bitmap:action:${activity}:timespan:${initialPeriodKey}-${subsequentPeriodKey}`
await this.redisClient.send_command(
'BITOP',
await this.redisClient.bitop(
'AND',
diffKey,
`bitmap:action:${activity}:timespan:${initialPeriodKey}`,
@@ -21,7 +21,6 @@ describe('RedisStatisticsStore', () => {
redisClient.incr = jest.fn()
redisClient.setbit = jest.fn()
redisClient.getbit = jest.fn().mockReturnValue(1)
redisClient.send_command = jest.fn()
periodKeyGenerator = {} as jest.Mocked<PeriodKeyGeneratorInterface>
periodKeyGenerator.getPeriodKey = jest.fn().mockReturnValue('period-key')
+46
View File
@@ -3,6 +3,52 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.9.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.8.0...@standardnotes/api-gateway@1.9.0) (2022-08-08)
### Features
* **api-gateway:** add analytics over time to daily report event ([845f08b](https://github.com/standardnotes/api-gateway/commit/845f08b060beda5dea69e16fbda132150de7d5f2))
# [1.8.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.7.4...@standardnotes/api-gateway@1.8.0) (2022-08-08)
### Features
* **api-gateway:** add marking server interaction as general activity in analytics ([a36764f](https://github.com/standardnotes/api-gateway/commit/a36764f1b058bed014b815fa2818370849053b18))
## [1.7.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.7.3...@standardnotes/api-gateway@1.7.4) (2022-07-29)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.7.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.7.2...@standardnotes/api-gateway@1.7.3) (2022-07-29)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.7.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.7.1...@standardnotes/api-gateway@1.7.2) (2022-07-27)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.7.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.7.0...@standardnotes/api-gateway@1.7.1) (2022-07-27)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.7.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.6.30...@standardnotes/api-gateway@1.7.0) (2022-07-26)
### Features
* **api-gateway:** add limited discount offer purchased to analytics report ([d203ce1](https://github.com/standardnotes/api-gateway/commit/d203ce188af4f775e01bc1752d4c6d84fc5f1675))
## [1.6.30](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.6.29...@standardnotes/api-gateway@1.6.30) (2022-07-26)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.6.29](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.6.28...@standardnotes/api-gateway@1.6.29) (2022-07-25)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.6.28](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.6.27...@standardnotes/api-gateway@1.6.28) (2022-07-15)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.6.27](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.6.26...@standardnotes/api-gateway@1.6.27) (2022-07-15)
**Note:** Version bump only for package @standardnotes/api-gateway
+18
View File
@@ -46,6 +46,24 @@ const requestReport = async (
Period.Yesterday,
),
},
{
name: AnalyticsActivity.LimitedDiscountOfferPurchased,
retention: 0,
totalCount: await analyticsStore.calculateActivityTotalCount(
AnalyticsActivity.LimitedDiscountOfferPurchased,
Period.Yesterday,
),
},
],
activityStatisticsOverTime: [
{
name: AnalyticsActivity.GeneralActivity,
period: Period.Last30Days,
counts: await analyticsStore.calculateActivityChangesTotalCount(
AnalyticsActivity.GeneralActivity,
Period.Last30Days,
),
},
],
},
}
+1 -1
View File
@@ -23,7 +23,7 @@ import '../src/Controller/v1/SubscriptionInvitesController'
import '../src/Controller/v2/PaymentsControllerV2'
import '../src/Controller/v2/ActionsControllerV2'
import * as helmet from 'helmet'
import helmet from 'helmet'
import * as cors from 'cors'
import { text, json, Request, Response, NextFunction, RequestHandler, ErrorRequestHandler } from 'express'
import * as winston from 'winston'
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.6.27",
"version": "1.9.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -22,7 +22,7 @@
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
},
"dependencies": {
"@newrelic/winston-enricher": "^2.1.0",
"@newrelic/winston-enricher": "^4.0.0",
"@sentry/node": "^7.3.0",
"@standardnotes/analytics": "workspace:*",
"@standardnotes/domain-events": "workspace:*",
@@ -34,10 +34,10 @@
"cors": "2.8.5",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"helmet": "4.4.1",
"helmet": "^5.1.1",
"inversify": "^6.0.1",
"inversify-express-utils": "^6.4.3",
"ioredis": "^5.0.6",
"ioredis": "^5.2.0",
"jsonwebtoken": "8.5.1",
"newrelic": "^8.14.1",
"prettyjson": "^1.2.5",
@@ -57,7 +57,7 @@
"eslint-plugin-prettier": "^4.0.0",
"jest": "^28.1.2",
"nodemon": "^2.0.19",
"npm-check-updates": "^15.3.3",
"npm-check-updates": "^16.0.1",
"ts-jest": "^28.0.5"
}
}
@@ -24,7 +24,7 @@ import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTo
import { RedisCrossServiceTokenCache } from '../Infra/Redis/RedisCrossServiceTokenCache'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicWinstonEnricher = require('@newrelic/winston-enricher')
const newrelicFormatter = require('@newrelic/winston-enricher')
export class ContainerConfigLoader {
async load(): Promise<Container> {
@@ -33,9 +33,10 @@ export class ContainerConfigLoader {
const container = new Container()
const newrelicWinstonFormatter = newrelicFormatter(winston)
const winstonFormatters = [winston.format.splat(), winston.format.json()]
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
winstonFormatters.push(newrelicWinstonEnricher())
winstonFormatters.push(newrelicWinstonFormatter())
}
const logger = winston.createLogger({
@@ -1,4 +1,5 @@
import { CrossServiceTokenData } from '@standardnotes/security'
import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics'
import { TimerInterface } from '@standardnotes/time'
import { NextFunction, Request, Response } from 'express'
import { inject, injectable } from 'inversify'
@@ -19,6 +20,7 @@ export class AuthMiddleware extends BaseMiddleware {
@inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) private crossServiceTokenCacheTTL: number,
@inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
@inject(TYPES.Timer) private timer: TimerInterface,
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
@inject(TYPES.Logger) private logger: Logger,
) {
super()
@@ -73,6 +75,10 @@ export class AuthMiddleware extends BaseMiddleware {
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
await this.analyticsStore.markActivity([AnalyticsActivity.GeneralActivity], decodedToken.analyticsId as number, [
Period.Today,
])
if (this.crossServiceTokenCacheTTL && !crossServiceTokenFetchedFromCache) {
await this.crossServiceTokenCache.set({
authorizationHeaderValue: authHeaderValue,
+46
View File
@@ -3,6 +3,52 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.13.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.13.2...@standardnotes/auth-server@1.13.3) (2022-08-08)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.13.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.13.1...@standardnotes/auth-server@1.13.2) (2022-08-08)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.13.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.13.0...@standardnotes/auth-server@1.13.1) (2022-07-29)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.13.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.12.2...@standardnotes/auth-server@1.13.0) (2022-07-29)
### Features
* **auth:** remove crypto-random-string in favour of @standardnotes/sncrypto-node ([35f931a](https://github.com/standardnotes/server/commit/35f931a708c7fead65fd40a9b4214a223831a5dc))
## [1.12.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.12.1...@standardnotes/auth-server@1.12.2) (2022-07-29)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.12.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.12.0...@standardnotes/auth-server@1.12.1) (2022-07-27)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.12.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.11.31...@standardnotes/auth-server@1.12.0) (2022-07-26)
### Features
* **auth:** add analytics for purchased subscription with a limited discount offer ([e936ac4](https://github.com/standardnotes/server/commit/e936ac4ce18fa43e47a50462c44f63a9ba1c1aa4))
## [1.11.31](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.11.30...@standardnotes/auth-server@1.11.31) (2022-07-25)
### Bug Fixes
* **auth:** marking predicate verification result if user is not existing ([40996f9](https://github.com/standardnotes/server/commit/40996f9d485686f92ee57fe6337102d94378b39b))
## [1.11.30](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.11.29...@standardnotes/auth-server@1.11.30) (2022-07-25)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.11.29](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.11.28...@standardnotes/auth-server@1.11.29) (2022-07-15)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.11.28](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.11.27...@standardnotes/auth-server@1.11.28) (2022-07-15)
**Note:** Version bump only for package @standardnotes/auth-server
+4 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.11.28",
"version": "1.13.3",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -30,7 +30,7 @@
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
},
"dependencies": {
"@newrelic/winston-enricher": "^2.1.0",
"@newrelic/winston-enricher": "^4.0.0",
"@sentry/node": "^7.3.0",
"@standardnotes/analytics": "workspace:*",
"@standardnotes/api": "^1.1.19",
@@ -49,13 +49,12 @@
"axios": "^0.27.2",
"bcryptjs": "2.4.3",
"cors": "2.8.5",
"crypto-random-string": "3.3.0",
"dayjs": "^1.11.3",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"inversify": "^6.0.1",
"inversify-express-utils": "^6.4.3",
"ioredis": "^5.0.6",
"ioredis": "^5.2.0",
"mysql2": "^2.3.3",
"newrelic": "^8.14.1",
"otplib": "12.0.1",
@@ -82,7 +81,7 @@
"eslint-plugin-prettier": "^4.0.0",
"jest": "^28.1.2",
"nodemon": "^2.0.19",
"npm-check-updates": "^15.3.3",
"npm-check-updates": "^16.0.1",
"ts-jest": "^28.0.5"
}
}
+3 -2
View File
@@ -193,7 +193,7 @@ import { PredicateVerificationRequestedEventHandler } from '../Domain/Handler/Pr
import { MuteMarketingEmails } from '../Domain/UseCase/MuteMarketingEmails/MuteMarketingEmails'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicWinstonEnricher = require('@newrelic/winston-enricher')
const newrelicFormatter = require('@newrelic/winston-enricher')
export class ContainerConfigLoader {
async load(): Promise<Container> {
@@ -215,9 +215,10 @@ export class ContainerConfigLoader {
container.bind(TYPES.Redis).toConstantValue(redis)
const newrelicWinstonFormatter = newrelicFormatter(winston)
const winstonFormatters = [winston.format.splat(), winston.format.json()]
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
winstonFormatters.push(newrelicWinstonEnricher())
winstonFormatters.push(newrelicWinstonFormatter())
}
const logger = winston.createLogger({
@@ -96,7 +96,7 @@ describe('PredicateVerificationRequestedEventHandler', () => {
expect(domainEventPublisher.publish).toHaveBeenCalled()
})
it('should not verify a predicate if user is missing', async () => {
it('should mark a predicate verification with undetermined result if user is missing', async () => {
event.meta = {
correlation: {
userIdentifier: 'test@test.te',
@@ -110,6 +110,6 @@ describe('PredicateVerificationRequestedEventHandler', () => {
await createHandler().handle(event)
expect(verifyPredicate.execute).not.toHaveBeenCalled()
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
expect(domainEventPublisher.publish).toHaveBeenCalled()
})
})
@@ -3,6 +3,7 @@ import {
DomainEventPublisherInterface,
PredicateVerificationRequestedEvent,
} from '@standardnotes/domain-events'
import { PredicateVerificationResult } from '@standardnotes/predicates'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
@@ -28,7 +29,13 @@ export class PredicateVerificationRequestedEventHandler implements DomainEventHa
if (event.meta.correlation.userIdentifierType === 'email') {
const user = await this.userRepository.findOneByEmail(event.meta.correlation.userIdentifier)
if (user === null) {
this.logger.warn(`Could not find user ${event.meta.correlation.userIdentifier} for predicate verification`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createPredicateVerifiedEvent({
predicate: event.payload.predicate,
predicateVerificationResult: PredicateVerificationResult.CouldNotBeDetermined,
userUuid,
}),
)
return
}
@@ -16,6 +16,8 @@ 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 { AnalyticsEntity } from '../Analytics/AnalyticsEntity'
describe('SubscriptionPurchasedEventHandler', () => {
let userRepository: UserRepositoryInterface
@@ -29,6 +31,7 @@ describe('SubscriptionPurchasedEventHandler', () => {
let event: SubscriptionPurchasedEvent
let subscriptionExpiresAt: number
let subscriptionSettingService: SubscriptionSettingServiceInterface
let analyticsStore: AnalyticsStoreInterface
let timestamp: number
const createHandler = () =>
@@ -38,6 +41,7 @@ describe('SubscriptionPurchasedEventHandler', () => {
offlineUserSubscriptionRepository,
roleService,
subscriptionSettingService,
analyticsStore,
logger,
)
@@ -83,11 +87,15 @@ describe('SubscriptionPurchasedEventHandler', () => {
subscriptionExpiresAt,
timestamp: dayjs.utc().valueOf(),
offline: false,
discountCode: null,
}
subscriptionSettingService = {} as jest.Mocked<SubscriptionSettingServiceInterface>
subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription = jest.fn()
analyticsStore = {} as jest.Mocked<AnalyticsStoreInterface>
analyticsStore.markActivity = jest.fn()
logger = {} as jest.Mocked<Logger>
logger.info = jest.fn()
logger.warn = jest.fn()
@@ -134,6 +142,28 @@ describe('SubscriptionPurchasedEventHandler', () => {
})
})
it('should update analytics on limited discount offer purchasing', async () => {
const analyticsEntity = { id: 3 } as jest.Mocked<AnalyticsEntity>
user = {
uuid: '123',
email: 'test@test.com',
roles: Promise.resolve([
{
name: RoleName.CoreUser,
},
]),
analyticsEntity: Promise.resolve(analyticsEntity),
} as jest.Mocked<User>
userRepository.findOneByEmail = jest.fn().mockReturnValue(user)
event.payload.discountCode = 'limited-10'
await createHandler().handle(event)
expect(analyticsStore.markActivity).toHaveBeenCalledWith(['limited-discount-offer-purchased'], 3, [Period.Today])
})
it('should create an offline subscription', async () => {
event.payload.offline = true
@@ -13,6 +13,7 @@ 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'
@injectable()
export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInterface {
@@ -23,6 +24,7 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
private offlineUserSubscriptionRepository: OfflineUserSubscriptionRepositoryInterface,
@inject(TYPES.RoleService) private roleService: RoleServiceInterface,
@inject(TYPES.SubscriptionSettingService) private subscriptionSettingService: SubscriptionSettingServiceInterface,
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
@inject(TYPES.Logger) private logger: Logger,
) {}
@@ -62,6 +64,16 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
userSubscription,
event.payload.subscriptionName,
)
const limitedDiscountPurchased = event.payload.discountCode === 'limited-10'
if (limitedDiscountPurchased) {
const analyticsEntity = await user.analyticsEntity
if (analyticsEntity) {
await this.analyticsStore.markActivity([AnalyticsActivity.LimitedDiscountOfferPurchased], analyticsEntity.id, [
Period.Today,
])
}
}
}
private async addUserRole(user: User, subscriptionName: SubscriptionName): Promise<void> {
@@ -13,6 +13,7 @@ import { RevokedSession } from './RevokedSession'
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
import { LogSessionUserAgentOption } from '@standardnotes/settings'
import { Setting } from '../Setting/Setting'
import { CryptoNode } from '@standardnotes/sncrypto-node'
describe('SessionService', () => {
let sessionRepository: SessionRepositoryInterface
@@ -25,6 +26,7 @@ describe('SessionService', () => {
let deviceDetector: UAParser
let timer: TimerInterface
let logger: winston.Logger
let cryptoNode: CryptoNode
const createService = () =>
new SessionService(
@@ -37,6 +39,7 @@ describe('SessionService', () => {
123,
234,
settingService,
cryptoNode,
)
beforeEach(() => {
@@ -96,6 +99,10 @@ describe('SessionService', () => {
logger.warn = jest.fn()
logger.error = jest.fn()
logger.debug = jest.fn()
cryptoNode = {} as jest.Mocked<CryptoNode>
cryptoNode.generateRandomKey = jest.fn().mockReturnValue('foo bar')
cryptoNode.base64URLEncode = jest.fn().mockReturnValue('foobar')
})
it('should mark a revoked session as received', async () => {
@@ -1,7 +1,6 @@
import * as crypto from 'crypto'
import * as winston from 'winston'
import * as dayjs from 'dayjs'
import * as cryptoRandomString from 'crypto-random-string'
import { UAParser } from 'ua-parser-js'
import { inject, injectable } from 'inversify'
import { v4 as uuidv4 } from 'uuid'
@@ -20,6 +19,7 @@ import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
import { LogSessionUserAgentOption, SettingName } from '@standardnotes/settings'
import { SessionBody } from '@standardnotes/responses'
import { Uuid } from '@standardnotes/common'
import { CryptoNode } from '@standardnotes/sncrypto-node'
@injectable()
export class SessionService implements SessionServiceInterface {
@@ -35,6 +35,7 @@ export class SessionService implements SessionServiceInterface {
@inject(TYPES.ACCESS_TOKEN_AGE) private accessTokenAge: number,
@inject(TYPES.REFRESH_TOKEN_AGE) private refreshTokenAge: number,
@inject(TYPES.SettingService) private settingService: SettingServiceInterface,
@inject(TYPES.CryptoNode) private cryptoNode: CryptoNode,
) {}
async createNewSessionForUser(dto: {
@@ -263,8 +264,8 @@ export class SessionService implements SessionServiceInterface {
}
private async createTokens(session: Session): Promise<SessionBody> {
const accessToken = cryptoRandomString({ length: 16, type: 'url-safe' })
const refreshToken = cryptoRandomString({ length: 16, type: 'url-safe' })
const accessToken = this.cryptoNode.base64URLEncode(await this.cryptoNode.generateRandomKey(48))
const refreshToken = this.cryptoNode.base64URLEncode(await this.cryptoNode.generateRandomKey(48))
const hashedAccessToken = crypto.createHash('sha256').update(accessToken).digest('hex')
const hashedRefreshToken = crypto.createHash('sha256').update(refreshToken).digest('hex')
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.7.29](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.28...@standardnotes/domain-events-infra@1.7.29) (2022-08-08)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.7.28](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.27...@standardnotes/domain-events-infra@1.7.28) (2022-07-26)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.7.27](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.26...@standardnotes/domain-events-infra@1.7.27) (2022-07-25)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.7.26](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.25...@standardnotes/domain-events-infra@1.7.26) (2022-07-15)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.7.25](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.24...@standardnotes/domain-events-infra@1.7.25) (2022-07-15)
**Note:** Version bump only for package @standardnotes/domain-events-infra
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events-infra",
"version": "1.7.25",
"version": "1.7.29",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -26,14 +26,14 @@
"dependencies": {
"@standardnotes/domain-events": "workspace:*",
"aws-sdk": "^2.1082.0",
"ioredis": "^4.28.5",
"ioredis": "^5.2.0",
"newrelic": "^8.8.0",
"reflect-metadata": "^0.1.13",
"sqs-consumer": "^5.6.0",
"winston": "^3.8.1"
},
"devDependencies": {
"@types/ioredis": "^4.28.8",
"@types/ioredis": "^4.28.10",
"@types/jest": "^28.1.4",
"@types/newrelic": "^7.0.3",
"@typescript-eslint/eslint-plugin": "^5.30.0",
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.52.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.51.0...@standardnotes/domain-events@2.52.0) (2022-08-08)
### Features
* **api-gateway:** add analytics over time to daily report event ([845f08b](https://github.com/standardnotes/server/commit/845f08b060beda5dea69e16fbda132150de7d5f2))
# [2.51.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.50.2...@standardnotes/domain-events@2.51.0) (2022-07-26)
### Features
* **domain-events:** add discount code to subscription purchased event ([28e1c65](https://github.com/standardnotes/server/commit/28e1c656312ae9a7c4afec1aa65bb104f788b8b6))
## [2.50.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.50.1...@standardnotes/domain-events@2.50.2) (2022-07-25)
**Note:** Version bump only for package @standardnotes/domain-events
## [2.50.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.50.0...@standardnotes/domain-events@2.50.1) (2022-07-15)
### Bug Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events",
"version": "2.50.1",
"version": "2.52.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -12,5 +12,13 @@ export interface DailyAnalyticsReportGeneratedEventPayload {
retention: number
totalCount: number
}>
activityStatisticsOverTime: Array<{
name: string
period: number
counts: Array<{
periodKey: string
totalCount: number
}>
}>
outOfSyncIncidents: number
}
@@ -7,4 +7,5 @@ export interface SubscriptionPurchasedEventPayload {
subscriptionExpiresAt: number
timestamp: number
offline: boolean
discountCode: string | null
}
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.28](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.1.27...@standardnotes/event-store@1.1.28) (2022-08-08)
**Note:** Version bump only for package @standardnotes/event-store
## [1.1.27](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.1.26...@standardnotes/event-store@1.1.27) (2022-07-26)
**Note:** Version bump only for package @standardnotes/event-store
## [1.1.26](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.1.25...@standardnotes/event-store@1.1.26) (2022-07-25)
**Note:** Version bump only for package @standardnotes/event-store
## [1.1.25](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.1.24...@standardnotes/event-store@1.1.25) (2022-07-15)
**Note:** Version bump only for package @standardnotes/event-store
## [1.1.24](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.1.23...@standardnotes/event-store@1.1.24) (2022-07-15)
**Note:** Version bump only for package @standardnotes/event-store
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/event-store",
"version": "1.1.24",
"version": "1.1.28",
"description": "Event Store Service",
"private": true,
"main": "dist/src/index.js",
@@ -37,7 +37,7 @@
"aws-sdk": "^2.1159.0",
"dotenv": "^16.0.1",
"inversify": "^6.0.1",
"ioredis": "^5.0.6",
"ioredis": "^5.2.0",
"mysql2": "^2.3.3",
"newrelic": "^8.14.1",
"reflect-metadata": "0.1.13",
+24
View File
@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.5.31](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.30...@standardnotes/files-server@1.5.31) (2022-08-08)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.30](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.29...@standardnotes/files-server@1.5.30) (2022-07-27)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.29](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.28...@standardnotes/files-server@1.5.29) (2022-07-27)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.28](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.27...@standardnotes/files-server@1.5.28) (2022-07-26)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.27](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.26...@standardnotes/files-server@1.5.27) (2022-07-25)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.26](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.25...@standardnotes/files-server@1.5.26) (2022-07-15)
**Note:** Version bump only for package @standardnotes/files-server
## [1.5.25](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.24...@standardnotes/files-server@1.5.25) (2022-07-15)
**Note:** Version bump only for package @standardnotes/files-server
+1 -1
View File
@@ -8,7 +8,7 @@ import * as busboy from 'connect-busboy'
import '../src/Controller/HealthCheckController'
import '../src/Controller/FilesController'
import * as helmet from 'helmet'
import helmet from 'helmet'
import * as cors from 'cors'
import { urlencoded, json, raw, Request, Response, NextFunction, RequestHandler, ErrorRequestHandler } from 'express'
import * as winston from 'winston'
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.5.25",
"version": "1.5.31",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -41,10 +41,10 @@
"dotenv": "^16.0.1",
"express": "^4.18.1",
"express-winston": "^4.0.5",
"helmet": "^4.3.1",
"helmet": "^5.1.1",
"inversify": "^6.0.1",
"inversify-express-utils": "^6.4.3",
"ioredis": "^5.0.6",
"ioredis": "^5.2.0",
"jsonwebtoken": "^8.5.1",
"newrelic": "^8.14.1",
"nodemon": "^2.0.19",
@@ -69,7 +69,7 @@
"eslint-plugin-prettier": "^4.0.0",
"jest": "^28.1.2",
"nodemon": "^2.0.19",
"npm-check-updates": "^15.3.3",
"npm-check-updates": "^16.0.1",
"ts-jest": "^28.0.5",
"uuid": "^8.3.2"
}
+6
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/predicates@1.2.6...@standardnotes/predicates@1.3.0) (2022-07-25)
### Features
* **predicates:** add could-not-be-determined predicate verification result ([6642641](https://github.com/standardnotes/server/commit/6642641c1161986a1c1186698f6b8151ce3aee87))
## [1.2.6](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.2.5...@standardnotes/predicates@1.2.6) (2022-07-14)
**Note:** Version bump only for package @standardnotes/predicates
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/predicates",
"version": "1.2.6",
"version": "1.3.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -1,4 +1,5 @@
export enum PredicateVerificationResult {
Affirmed = 'affirmed',
Denied = 'denied',
CouldNotBeDetermined = 'could-not-be-determined',
}
+77
View File
@@ -3,6 +3,83 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.10.5](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.4...@standardnotes/scheduler-server@1.10.5) (2022-08-08)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.4](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.3...@standardnotes/scheduler-server@1.10.4) (2022-07-29)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.3](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.2...@standardnotes/scheduler-server@1.10.3) (2022-07-29)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.2](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.1...@standardnotes/scheduler-server@1.10.2) (2022-07-29)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.10.1](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.0...@standardnotes/scheduler-server@1.10.1) (2022-07-27)
**Note:** Version bump only for package @standardnotes/scheduler-server
# [1.10.0](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.9.2...@standardnotes/scheduler-server@1.10.0) (2022-07-26)
### Features
* **scheduler:** enable discount applying and withdraw for everyone ([04bf414](https://github.com/standardnotes/server/commit/04bf414de41ecba255b068fd8e72bc569ace9ed1))
## [1.9.2](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.9.1...@standardnotes/scheduler-server@1.9.2) (2022-07-26)
### Bug Fixes
* **scheduler:** change the discount code to an absolute discount ([1fa94ef](https://github.com/standardnotes/server/commit/1fa94efa02f169ee25d11e9403ab3368b696cc33))
## [1.9.1](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.9.0...@standardnotes/scheduler-server@1.9.1) (2022-07-26)
### Bug Fixes
* **scheduler:** eliminate read/write concurrency hazzard while updating predicate status ([4ab0d24](https://github.com/standardnotes/server/commit/4ab0d24d24b62babf5d0e36fbcb3a6364abb71bc))
# [1.9.0](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.8.2...@standardnotes/scheduler-server@1.9.0) (2022-07-25)
### Features
* **scheduler:** add job interpreting logs ([bf12687](https://github.com/standardnotes/server/commit/bf12687f63738a6eac46ab1778826de5d076e4ab))
## [1.8.2](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.8.1...@standardnotes/scheduler-server@1.8.2) (2022-07-25)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.8.1](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.8.0...@standardnotes/scheduler-server@1.8.1) (2022-07-25)
### Bug Fixes
* **scheduler:** checking for predicates fullfillment on applying discount ([6374248](https://github.com/standardnotes/server/commit/637424813278b7dd81969e1783cbc38d1a916cab))
# [1.8.0](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.7.0...@standardnotes/scheduler-server@1.8.0) (2022-07-25)
### Features
* **scheduler:** add feature flag behind applying and withdrawing discounts ([0a5b956](https://github.com/standardnotes/server/commit/0a5b956cb9586d353ac68c79e8473d74f8d9796a))
# [1.7.0](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.6.0...@standardnotes/scheduler-server@1.7.0) (2022-07-25)
### Features
* **scheduler:** add creating discount apply/withdraw events ([7168435](https://github.com/standardnotes/server/commit/71684350e94053d884ae907e5d3deba4bc027f1b))
* **scheduler:** add publishing discount apply/withdraw events ([48af9e7](https://github.com/standardnotes/server/commit/48af9e7c1cfb582389af83e15977b930bf067f8d))
# [1.6.0](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.5.23...@standardnotes/scheduler-server@1.6.0) (2022-07-22)
### Features
* **scheduler:** schedule apply and withdraw subscription discounts upon registration ([fc20697](https://github.com/standardnotes/server/commit/fc20697d81419827c4f51c5c80804dd98804f33f))
## [1.5.23](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.5.22...@standardnotes/scheduler-server@1.5.23) (2022-07-15)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.5.22](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.5.21...@standardnotes/scheduler-server@1.5.22) (2022-07-15)
**Note:** Version bump only for package @standardnotes/scheduler-server
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/scheduler-server",
"version": "1.5.22",
"version": "1.10.5",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -24,7 +24,7 @@
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
},
"dependencies": {
"@newrelic/winston-enricher": "^2.1.0",
"@newrelic/winston-enricher": "^4.0.0",
"@sentry/node": "^7.3.0",
"@standardnotes/common": "workspace:*",
"@standardnotes/domain-events": "workspace:*",
@@ -34,8 +34,8 @@
"aws-sdk": "^2.1158.0",
"dayjs": "^1.11.3",
"dotenv": "^16.0.1",
"inversify": "5.0.5",
"ioredis": "^5.0.6",
"inversify": "^6.0.1",
"ioredis": "^5.2.0",
"mysql2": "^2.3.3",
"newrelic": "^8.14.1",
"reflect-metadata": "^0.1.13",
@@ -50,7 +50,7 @@
"@typescript-eslint/eslint-plugin": "^5.29.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^28.1.2",
"npm-check-updates": "^15.3.3",
"npm-check-updates": "^16.0.1",
"ts-jest": "^28.0.5"
}
}
@@ -38,7 +38,7 @@ import { UserRegisteredEventHandler } from '../Domain/Handler/UserRegisteredEven
import { SubscriptionCancelledEventHandler } from '../Domain/Handler/SubscriptionCancelledEventHandler'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicWinstonEnricher = require('@newrelic/winston-enricher')
const newrelicFormatter = require('@newrelic/winston-enricher')
export class ContainerConfigLoader {
async load(): Promise<Container> {
@@ -60,9 +60,10 @@ export class ContainerConfigLoader {
container.bind(TYPES.Redis).toConstantValue(redis)
const newrelicWinstonFormatter = newrelicFormatter(winston)
const winstonFormatters = [winston.format.splat(), winston.format.json()]
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
winstonFormatters.push(newrelicWinstonEnricher())
winstonFormatters.push(newrelicWinstonFormatter())
}
const logger = winston.createLogger({
@@ -19,6 +19,52 @@ describe('DomainEventFactory', () => {
timer.getUTCDate = jest.fn().mockReturnValue(new Date(1))
})
it('should create a DISCOUNT_APPLY_REQUESTED event', () => {
expect(
createFactory().createDiscountApplyRequestedEvent({
userEmail: 'test@test.te',
discountCode: 'off-10',
}),
).toEqual({
createdAt: expect.any(Date),
meta: {
correlation: {
userIdentifier: 'test@test.te',
userIdentifierType: 'email',
},
origin: 'scheduler',
},
payload: {
userEmail: 'test@test.te',
discountCode: 'off-10',
},
type: 'DISCOUNT_APPLY_REQUESTED',
})
})
it('should create a DISCOUNT_WITHDRAW_REQUESTED event', () => {
expect(
createFactory().createDiscountWithdrawRequestedEvent({
userEmail: 'test@test.te',
discountCode: 'off-10',
}),
).toEqual({
createdAt: expect.any(Date),
meta: {
correlation: {
userIdentifier: 'test@test.te',
userIdentifierType: 'email',
},
origin: 'scheduler',
},
payload: {
userEmail: 'test@test.te',
discountCode: 'off-10',
},
type: 'DISCOUNT_WITHDRAW_REQUESTED',
})
})
it('should create a EMAIL_MESSAGE_REQUESTED event', () => {
expect(
createFactory().createEmailMessageRequestedEvent({
@@ -1,5 +1,7 @@
import { EmailMessageIdentifier } from '@standardnotes/common'
import {
DiscountApplyRequestedEvent,
DiscountWithdrawRequestedEvent,
DomainEventService,
EmailMessageRequestedEvent,
PredicateVerificationRequestedEvent,
@@ -16,6 +18,39 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
export class DomainEventFactory implements DomainEventFactoryInterface {
constructor(@inject(TYPES.Timer) private timer: TimerInterface) {}
createDiscountApplyRequestedEvent(dto: { userEmail: string; discountCode: string }): DiscountApplyRequestedEvent {
return {
type: 'DISCOUNT_APPLY_REQUESTED',
createdAt: this.timer.getUTCDate(),
meta: {
correlation: {
userIdentifier: dto.userEmail,
userIdentifierType: 'email',
},
origin: DomainEventService.Scheduler,
},
payload: dto,
}
}
createDiscountWithdrawRequestedEvent(dto: {
userEmail: string
discountCode: string
}): DiscountWithdrawRequestedEvent {
return {
type: 'DISCOUNT_WITHDRAW_REQUESTED',
createdAt: this.timer.getUTCDate(),
meta: {
correlation: {
userIdentifier: dto.userEmail,
userIdentifierType: 'email',
},
origin: DomainEventService.Scheduler,
},
payload: dto,
}
}
createEmailMessageRequestedEvent(dto: {
userEmail: string
messageIdentifier: EmailMessageIdentifier
@@ -1,5 +1,10 @@
import { EmailMessageIdentifier } from '@standardnotes/common'
import { EmailMessageRequestedEvent, PredicateVerificationRequestedEvent } from '@standardnotes/domain-events'
import {
DiscountApplyRequestedEvent,
DiscountWithdrawRequestedEvent,
EmailMessageRequestedEvent,
PredicateVerificationRequestedEvent,
} from '@standardnotes/domain-events'
import { Job } from '../Job/Job'
import { Predicate } from '../Predicate/Predicate'
@@ -11,4 +16,6 @@ export interface DomainEventFactoryInterface {
messageIdentifier: EmailMessageIdentifier
context: Record<string, unknown>
}): EmailMessageRequestedEvent
createDiscountApplyRequestedEvent(dto: { userEmail: string; discountCode: string }): DiscountApplyRequestedEvent
createDiscountWithdrawRequestedEvent(dto: { userEmail: string; discountCode: string }): DiscountWithdrawRequestedEvent
}
@@ -24,6 +24,10 @@ export class UserRegisteredEventHandler implements DomainEventHandlerInterface {
await this.scheduleEncourageEmailBackupsJob(event)
await this.scheduleEncourageSubscriptionPurchasing(event)
await this.scheduleSubscriptionDiscountApplying(event)
await this.scheduleSubscriptionDiscountWithdraw(event)
}
private async scheduleEncourageEmailBackupsJob(event: UserRegisteredEvent): Promise<void> {
@@ -65,4 +69,36 @@ export class UserRegisteredEventHandler implements DomainEventHandlerInterface {
await this.predicateRepository.save(predicate)
}
private async scheduleSubscriptionDiscountApplying(event: UserRegisteredEvent): Promise<void> {
const job = new Job()
job.name = JobName.APPLY_SUBSCRIPTION_DISCOUNT
job.scheduledAt = this.timer.convertDateToMicroseconds(this.timer.getUTCDateNDaysAhead(7))
job.createdAt = this.timer.getTimestampInMicroseconds()
job.status = JobStatus.Pending
job.userIdentifier = event.payload.email
job.userIdentifierType = 'email'
await this.jobRepository.save(job)
const predicate = new Predicate()
predicate.name = PredicateName.SubscriptionPurchased
predicate.status = PredicateStatus.Pending
predicate.authority = PredicateAuthority.Auth
predicate.job = Promise.resolve(job)
await this.predicateRepository.save(predicate)
}
private async scheduleSubscriptionDiscountWithdraw(event: UserRegisteredEvent): Promise<void> {
const job = new Job()
job.name = JobName.WITHDRAW_SUBSCRIPTION_DISCOUNT
job.scheduledAt = this.timer.convertDateToMicroseconds(this.timer.getUTCDateNDaysAhead(12))
job.createdAt = this.timer.getTimestampInMicroseconds()
job.status = JobStatus.Pending
job.userIdentifier = event.payload.email
job.userIdentifierType = 'email'
await this.jobRepository.save(job)
}
}
@@ -1,6 +1,12 @@
import { DomainEventPublisherInterface, EmailMessageRequestedEvent } from '@standardnotes/domain-events'
import {
DiscountApplyRequestedEvent,
DiscountWithdrawRequestedEvent,
DomainEventPublisherInterface,
EmailMessageRequestedEvent,
} from '@standardnotes/domain-events'
import { PredicateName } from '@standardnotes/predicates'
import 'reflect-metadata'
import { Logger } from 'winston'
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
import { Predicate } from '../Predicate/Predicate'
import { PredicateRepositoryInterface } from '../Predicate/PredicateRepositoryInterface'
@@ -18,9 +24,10 @@ describe('JobDoneInterpreter', () => {
let domainEventFactory: DomainEventFactoryInterface
let domainEventPublisher: DomainEventPublisherInterface
let job: Job
let logger: Logger
const createInterpreter = () =>
new JobDoneInterpreter(jobRepository, predicateRepository, domainEventFactory, domainEventPublisher)
new JobDoneInterpreter(jobRepository, predicateRepository, domainEventFactory, domainEventPublisher, logger)
beforeEach(() => {
job = {} as jest.Mocked<Job>
@@ -35,9 +42,19 @@ describe('JobDoneInterpreter', () => {
domainEventFactory.createEmailMessageRequestedEvent = jest
.fn()
.mockReturnValue({} as jest.Mocked<EmailMessageRequestedEvent>)
domainEventFactory.createDiscountApplyRequestedEvent = jest
.fn()
.mockReturnValue({} as jest.Mocked<DiscountApplyRequestedEvent>)
domainEventFactory.createDiscountWithdrawRequestedEvent = jest
.fn()
.mockReturnValue({} as jest.Mocked<DiscountWithdrawRequestedEvent>)
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
domainEventPublisher.publish = jest.fn()
logger = {} as jest.Mocked<Logger>
logger.info = jest.fn()
logger.warn = jest.fn()
})
it('should do nothing if job is not found', async () => {
@@ -172,6 +189,69 @@ describe('JobDoneInterpreter', () => {
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
})
it('should request discount apply', async () => {
jobRepository.findOneByUuid = jest.fn().mockReturnValue({
name: JobName.APPLY_SUBSCRIPTION_DISCOUNT,
userIdentifier: 'test@standardnotes.com',
userIdentifierType: 'email',
} as jest.Mocked<Job>)
predicateRepository.findByJobUuid = jest
.fn()
.mockReturnValue([
{ name: PredicateName.SubscriptionPurchased, status: PredicateStatus.Denied } as jest.Mocked<Predicate>,
])
await createInterpreter().interpret('1-2-3')
expect(domainEventFactory.createDiscountApplyRequestedEvent).toHaveBeenCalledWith({
userEmail: 'test@standardnotes.com',
discountCode: 'limited-10',
})
expect(domainEventPublisher.publish).toHaveBeenCalled()
})
it('should not request discount apply if email is missing', async () => {
jobRepository.findOneByUuid = jest.fn().mockReturnValue({
name: JobName.APPLY_SUBSCRIPTION_DISCOUNT,
userIdentifier: '2-3-4',
userIdentifierType: 'uuid',
} as jest.Mocked<Job>)
await createInterpreter().interpret('1-2-3')
expect(domainEventFactory.createDiscountApplyRequestedEvent).not.toHaveBeenCalled()
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
})
it('should request discount withdraw', async () => {
jobRepository.findOneByUuid = jest.fn().mockReturnValue({
name: JobName.WITHDRAW_SUBSCRIPTION_DISCOUNT,
userIdentifier: 'test@standardnotes.com',
userIdentifierType: 'email',
} as jest.Mocked<Job>)
await createInterpreter().interpret('1-2-3')
expect(domainEventFactory.createDiscountWithdrawRequestedEvent).toHaveBeenCalledWith({
userEmail: 'test@standardnotes.com',
discountCode: 'limited-10',
})
expect(domainEventPublisher.publish).toHaveBeenCalled()
})
it('should not request discount withdraw if email is missing', async () => {
jobRepository.findOneByUuid = jest.fn().mockReturnValue({
name: JobName.WITHDRAW_SUBSCRIPTION_DISCOUNT,
userIdentifier: '2-3-4',
userIdentifierType: 'uuid',
} as jest.Mocked<Job>)
await createInterpreter().interpret('1-2-3')
expect(domainEventFactory.createDiscountWithdrawRequestedEvent).not.toHaveBeenCalled()
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
})
it('should do nothing if there is no interpretation for a given job', async () => {
jobRepository.findOneByUuid = jest.fn().mockReturnValue({
name: 'foobar' as JobName,
@@ -2,6 +2,7 @@ import { EmailMessageIdentifier } from '@standardnotes/common'
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
import { PredicateName } from '@standardnotes/predicates'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
import TYPES from '../../Bootstrap/Types'
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
@@ -20,6 +21,7 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
@inject(TYPES.PredicateRepository) private predicateRepository: PredicateRepositoryInterface,
@inject(TYPES.DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface,
@inject(TYPES.DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface,
@inject(TYPES.Logger) private logger: Logger,
) {}
async interpret(jobUuid: string): Promise<void> {
@@ -29,14 +31,18 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
return
}
this.logger.info(`[${jobUuid}]${job.name}: Interpreting job as done.`)
if (!(await this.predicatesAreFulfilled(job))) {
this.logger.info(`[${jobUuid}]${job.name}: predicates are not fulfilled.`)
return
}
switch (job.name) {
case JobName.ENCOURAGE_EMAIL_BACKUPS:
if (job.userIdentifierType === 'email') {
await this.requestEmailBackupEncouragementEmail(job.userIdentifier)
await this.requestEmailBackupEncouragementEmail(job)
}
return
case JobName.ENCOURAGE_SUBSCRIPTION_PURCHASING:
@@ -46,18 +52,32 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
return
case JobName.EXIT_INTERVIEW:
if (job.userIdentifierType === 'email') {
await this.requestExitInterviewEmail(job.userIdentifier)
await this.requestExitInterviewEmail(job)
}
return
case JobName.APPLY_SUBSCRIPTION_DISCOUNT:
if (job.userIdentifierType === 'email') {
await this.requestDiscountApply(job)
}
return
case JobName.WITHDRAW_SUBSCRIPTION_DISCOUNT:
if (job.userIdentifierType === 'email') {
await this.requestDiscountWithdraw(job)
}
return
default:
this.logger.warn(`[${jobUuid}]${job.name}: job is not interpretable.`)
return
}
}
private async requestEmailBackupEncouragementEmail(userEmail: string): Promise<void> {
private async requestEmailBackupEncouragementEmail(job: Job): Promise<void> {
this.logger.info(`[${job.uuid}]${job.name}: requesting email backup encouragement email.`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createEmailMessageRequestedEvent({
userEmail,
userEmail: job.userIdentifier,
messageIdentifier: EmailMessageIdentifier.ENCOURAGE_EMAIL_BACKUPS,
context: {},
}),
@@ -65,6 +85,8 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
}
private async requestSubscriptionPurchaseEncouragementEmail(job: Job): Promise<void> {
this.logger.info(`[${job.uuid}]${job.name}: requesting subscription purchase encouragement email.`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createEmailMessageRequestedEvent({
userEmail: job.userIdentifier,
@@ -76,16 +98,40 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
)
}
private async requestExitInterviewEmail(userEmail: string): Promise<void> {
private async requestExitInterviewEmail(job: Job): Promise<void> {
this.logger.info(`[${job.uuid}]${job.name}: requesting exit interview email.`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createEmailMessageRequestedEvent({
userEmail,
userEmail: job.userIdentifier,
messageIdentifier: EmailMessageIdentifier.EXIT_INTERVIEW,
context: {},
}),
)
}
private async requestDiscountApply(job: Job): Promise<void> {
this.logger.info(`[${job.uuid}]${job.name}: requesting discount applying.`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createDiscountApplyRequestedEvent({
userEmail: job.userIdentifier,
discountCode: 'limited-10',
}),
)
}
private async requestDiscountWithdraw(job: Job): Promise<void> {
this.logger.info(`[${job.uuid}]${job.name}: requesting discount withdraw.`)
await this.domainEventPublisher.publish(
this.domainEventFactory.createDiscountWithdrawRequestedEvent({
userEmail: job.userIdentifier,
discountCode: 'limited-10',
}),
)
}
private async predicatesAreFulfilled(job: Job): Promise<boolean> {
const predicates = await this.predicateRepository.findByJobUuid(job.uuid)
@@ -96,6 +142,7 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
PredicateStatus.Denied
)
case JobName.ENCOURAGE_SUBSCRIPTION_PURCHASING:
case JobName.APPLY_SUBSCRIPTION_DISCOUNT:
return (
predicates.find((predicate) => predicate.name === PredicateName.SubscriptionPurchased)?.status ===
PredicateStatus.Denied
@@ -3,4 +3,6 @@ export enum JobName {
ENCOURAGE_EMAIL_BACKUPS = 'encourage-email-backups',
ENCOURAGE_SUBSCRIPTION_PURCHASING = 'encourage-subscription-purchasing',
EXIT_INTERVIEW = 'exit-interview',
APPLY_SUBSCRIPTION_DISCOUNT = 'apply-subscription-discount',
WITHDRAW_SUBSCRIPTION_DISCOUNT = 'withdraw-subscription-discount',
}
@@ -1,8 +1,7 @@
import { PredicateAuthority, PredicateName, PredicateVerificationResult } from '@standardnotes/predicates'
import 'reflect-metadata'
import { JobDoneInterpreterInterface } from '../../Job/JobDoneInterpreterInterface'
import { JobRepositoryInterface } from '../../Job/JobRepositoryInterface'
import { PredicateAuthority, PredicateName, PredicateVerificationResult } from '@standardnotes/predicates'
import { Predicate } from '../../Predicate/Predicate'
import { PredicateRepositoryInterface } from '../../Predicate/PredicateRepositoryInterface'
import { PredicateStatus } from '../../Predicate/PredicateStatus'
@@ -11,12 +10,10 @@ import { UpdatePredicateStatus } from './UpdatePredicateStatus'
describe('UpdatePredicateStatus', () => {
let predicateRepository: PredicateRepositoryInterface
let jobRepository: JobRepositoryInterface
let jobDoneInterpreter: JobDoneInterpreterInterface
let predicateComplete: Predicate
let predicateIncomplete: Predicate
const createUseCase = () => new UpdatePredicateStatus(predicateRepository, jobRepository, jobDoneInterpreter)
const createUseCase = () => new UpdatePredicateStatus(predicateRepository)
beforeEach(() => {
predicateComplete = {
@@ -33,15 +30,9 @@ describe('UpdatePredicateStatus', () => {
predicateRepository = {} as jest.Mocked<PredicateRepositoryInterface>
predicateRepository.findByJobUuid = jest.fn().mockReturnValue([predicateComplete, predicateIncomplete])
predicateRepository.save = jest.fn()
jobRepository = {} as jest.Mocked<JobRepositoryInterface>
jobRepository.markJobAsDone = jest.fn()
jobDoneInterpreter = {} as jest.Mocked<JobDoneInterpreterInterface>
jobDoneInterpreter.interpret = jest.fn()
})
it('should mark a predicate as complete and update job as done', async () => {
it('should mark a predicate as complete', async () => {
expect(
await createUseCase().execute({
predicate: { name: PredicateName.EmailBackupsEnabled, jobUuid: '1-2-3', authority: PredicateAuthority.Auth },
@@ -49,7 +40,6 @@ describe('UpdatePredicateStatus', () => {
}),
).toEqual({
success: true,
allPredicatesChecked: true,
})
expect(predicateRepository.save).toHaveBeenCalledWith({
@@ -57,37 +47,5 @@ describe('UpdatePredicateStatus', () => {
name: 'email-backups-enabled',
status: 'denied',
})
expect(jobRepository.markJobAsDone).toHaveBeenCalled()
expect(jobDoneInterpreter.interpret).toHaveBeenCalled()
})
it('should mark a predicate as complete and not update job as done if there are still incomplete predicates', async () => {
predicateRepository.findByJobUuid = jest
.fn()
.mockReturnValue([
predicateComplete,
predicateIncomplete,
{ uuid: '3-4-5', status: PredicateStatus.Pending } as jest.Mocked<Predicate>,
])
expect(
await createUseCase().execute({
predicate: { name: PredicateName.EmailBackupsEnabled, jobUuid: '1-2-3', authority: PredicateAuthority.Auth },
predicateVerificationResult: PredicateVerificationResult.Denied,
}),
).toEqual({
success: true,
allPredicatesChecked: false,
})
expect(predicateRepository.save).toHaveBeenCalledWith({
uuid: '2-3-4',
name: 'email-backups-enabled',
status: 'denied',
})
expect(jobRepository.markJobAsDone).not.toHaveBeenCalled()
expect(jobDoneInterpreter.interpret).not.toHaveBeenCalled()
})
})
@@ -1,8 +1,6 @@
import { inject, injectable } from 'inversify'
import TYPES from '../../../Bootstrap/Types'
import { JobDoneInterpreterInterface } from '../../Job/JobDoneInterpreterInterface'
import { JobRepositoryInterface } from '../../Job/JobRepositoryInterface'
import { PredicateRepositoryInterface } from '../../Predicate/PredicateRepositoryInterface'
import { PredicateStatus } from '../../Predicate/PredicateStatus'
import { UseCaseInterface } from '../UseCaseInterface'
@@ -12,35 +10,20 @@ import { UpdatePredicateStatusResponse } from './UpdatePredicateStatusResponse'
@injectable()
export class UpdatePredicateStatus implements UseCaseInterface {
constructor(
@inject(TYPES.PredicateRepository) private predicateRepository: PredicateRepositoryInterface,
@inject(TYPES.JobRepository) private jobRepository: JobRepositoryInterface,
@inject(TYPES.JobDoneInterpreter) private jobDoneInterpreter: JobDoneInterpreterInterface,
) {}
constructor(@inject(TYPES.PredicateRepository) private predicateRepository: PredicateRepositoryInterface) {}
async execute(dto: UpdatePredicateStatusDTO): Promise<UpdatePredicateStatusResponse> {
const predicates = await this.predicateRepository.findByJobUuid(dto.predicate.jobUuid)
let allPredicatesChecked = true
for (const predicate of predicates) {
if (predicate.name === dto.predicate.name) {
predicate.status = dto.predicateVerificationResult as unknown as PredicateStatus
await this.predicateRepository.save(predicate)
}
if (predicate.status === PredicateStatus.Pending) {
allPredicatesChecked = false
}
}
if (allPredicatesChecked) {
await this.jobDoneInterpreter.interpret(dto.predicate.jobUuid)
await this.jobRepository.markJobAsDone(dto.predicate.jobUuid)
}
return {
success: true,
allPredicatesChecked,
}
}
}
@@ -1,6 +1,3 @@
export type UpdatePredicateStatusResponse =
| {
success: true
allPredicatesChecked: boolean
}
| { success: false }
export type UpdatePredicateStatusResponse = {
success: boolean
}
+36
View File
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.6.36](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.35...@standardnotes/syncing-server@1.6.36) (2022-08-08)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.35](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.34...@standardnotes/syncing-server@1.6.35) (2022-08-08)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.34](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.33...@standardnotes/syncing-server@1.6.34) (2022-07-29)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.33](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.32...@standardnotes/syncing-server@1.6.33) (2022-07-29)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.32](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.31...@standardnotes/syncing-server@1.6.32) (2022-07-27)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.31](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.30...@standardnotes/syncing-server@1.6.31) (2022-07-27)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.30](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.29...@standardnotes/syncing-server@1.6.30) (2022-07-26)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.29](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.28...@standardnotes/syncing-server@1.6.29) (2022-07-25)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.28](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.27...@standardnotes/syncing-server@1.6.28) (2022-07-15)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.6.27](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.26...@standardnotes/syncing-server@1.6.27) (2022-07-15)
**Note:** Version bump only for package @standardnotes/syncing-server
+1 -1
View File
@@ -8,7 +8,7 @@ import '../src/Controller/HealthCheckController'
import '../src/Controller/RevisionsController'
import '../src/Controller/ItemsController'
import * as helmet from 'helmet'
import helmet from 'helmet'
import * as cors from 'cors'
import { urlencoded, json, Request, Response, NextFunction, RequestHandler, ErrorRequestHandler } from 'express'
import * as winston from 'winston'
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.6.27",
"version": "1.6.36",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -24,7 +24,7 @@
"upgrade:snjs": "yarn ncu -u '@standardnotes/*'"
},
"dependencies": {
"@newrelic/winston-enricher": "^2.1.0",
"@newrelic/winston-enricher": "^4.0.0",
"@sentry/node": "^7.3.0",
"@standardnotes/analytics": "workspace:*",
"@standardnotes/common": "workspace:*",
@@ -40,10 +40,10 @@
"cors": "2.8.5",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"helmet": "4.3.1",
"helmet": "^5.1.1",
"inversify": "^6.0.1",
"inversify-express-utils": "^6.4.3",
"ioredis": "^5.0.6",
"ioredis": "^5.2.0",
"jsonwebtoken": "8.5.1",
"mysql2": "^2.3.3",
"newrelic": "^8.14.1",
@@ -71,7 +71,7 @@
"eslint": "^8.14.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^28.1.2",
"npm-check-updates": "^15.3.3",
"npm-check-updates": "^16.0.1",
"ts-jest": "^28.0.5"
}
}
@@ -86,7 +86,7 @@ import { ItemRepositoryInterface } from '../Domain/Item/ItemRepositoryInterface'
import { Repository } from 'typeorm'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicWinstonEnricher = require('@newrelic/winston-enricher')
const newrelicFormatter = require('@newrelic/winston-enricher')
export class ContainerConfigLoader {
private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
@@ -110,9 +110,10 @@ export class ContainerConfigLoader {
container.bind(TYPES.Redis).toConstantValue(redis)
const newrelicWinstonFormatter = newrelicFormatter(winston)
const winstonFormatters = [winston.format.splat(), winston.format.json()]
if (env.get('NEW_RELIC_ENABLED', true) === 'true') {
winstonFormatters.push(newrelicWinstonEnricher())
winstonFormatters.push(newrelicWinstonFormatter())
}
const logger = winston.createLogger({
+42 -121
View File
@@ -1260,13 +1260,12 @@ __metadata:
languageName: node
linkType: hard
"@newrelic/winston-enricher@npm:^2.1.0":
version: 2.1.2
resolution: "@newrelic/winston-enricher@npm:2.1.2"
"@newrelic/winston-enricher@npm:^4.0.0":
version: 4.0.0
resolution: "@newrelic/winston-enricher@npm:4.0.0"
peerDependencies:
newrelic: ">=6.2.0"
winston: ^3.0.0
checksum: d001c131665470be698912accf3782b1b2871f5f2f0711527d423b47e967912ebe873da99f3cdfb0d367c62ac90aab87f448ac5ce89bae9a91a36d049b024003
newrelic: ">=8.13.0"
checksum: f737e7d9528b69ab771d0dd7d2262400291de60791767e09d477f700f1221df734a3f6a13b8907b909b4c190ccf20d4d3380d691f378422e789a2b448ab4fc7c
languageName: node
linkType: hard
@@ -1752,11 +1751,11 @@ __metadata:
version: 0.0.0-use.local
resolution: "@standardnotes/analytics@workspace:packages/analytics"
dependencies:
"@types/ioredis": ^4.28.8
"@types/ioredis": ^4.28.10
"@types/jest": ^28.1.4
"@typescript-eslint/eslint-plugin": ^5.30.0
eslint-plugin-prettier: ^4.2.1
ioredis: ^4.28.5
ioredis: ^5.2.0
jest: ^28.1.2
reflect-metadata: ^0.1.13
ts-jest: ^28.0.5
@@ -1767,7 +1766,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@standardnotes/api-gateway@workspace:packages/api-gateway"
dependencies:
"@newrelic/winston-enricher": ^2.1.0
"@newrelic/winston-enricher": ^4.0.0
"@sentry/node": ^7.3.0
"@standardnotes/analytics": "workspace:*"
"@standardnotes/domain-events": "workspace:*"
@@ -1789,15 +1788,15 @@ __metadata:
eslint: ^8.14.0
eslint-plugin-prettier: ^4.0.0
express: ^4.18.1
helmet: 4.4.1
helmet: ^5.1.1
inversify: ^6.0.1
inversify-express-utils: ^6.4.3
ioredis: ^5.0.6
ioredis: ^5.2.0
jest: ^28.1.2
jsonwebtoken: 8.5.1
newrelic: ^8.14.1
nodemon: ^2.0.19
npm-check-updates: ^15.3.3
npm-check-updates: ^16.0.1
prettyjson: ^1.2.5
reflect-metadata: 0.1.13
ts-jest: ^28.0.5
@@ -1823,7 +1822,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@standardnotes/auth-server@workspace:packages/auth"
dependencies:
"@newrelic/winston-enricher": ^2.1.0
"@newrelic/winston-enricher": ^4.0.0
"@sentry/node": ^7.3.0
"@standardnotes/analytics": "workspace:*"
"@standardnotes/api": ^1.1.19
@@ -1853,7 +1852,6 @@ __metadata:
axios: ^0.27.2
bcryptjs: 2.4.3
cors: 2.8.5
crypto-random-string: 3.3.0
dayjs: ^1.11.3
dotenv: ^16.0.1
eslint: ^8.14.0
@@ -1861,12 +1859,12 @@ __metadata:
express: ^4.18.1
inversify: ^6.0.1
inversify-express-utils: ^6.4.3
ioredis: ^5.0.6
ioredis: ^5.2.0
jest: ^28.1.2
mysql2: ^2.3.3
newrelic: ^8.14.1
nodemon: ^2.0.19
npm-check-updates: ^15.3.3
npm-check-updates: ^16.0.1
otplib: 12.0.1
prettyjson: ^1.2.5
reflect-metadata: 0.1.13
@@ -1920,13 +1918,13 @@ __metadata:
resolution: "@standardnotes/domain-events-infra@workspace:packages/domain-events-infra"
dependencies:
"@standardnotes/domain-events": "workspace:*"
"@types/ioredis": ^4.28.8
"@types/ioredis": ^4.28.10
"@types/jest": ^28.1.4
"@types/newrelic": ^7.0.3
"@typescript-eslint/eslint-plugin": ^5.30.0
aws-sdk: ^2.1082.0
eslint-plugin-prettier: ^4.2.1
ioredis: ^4.28.5
ioredis: ^5.2.0
jest: ^28.1.2
newrelic: ^8.8.0
reflect-metadata: ^0.1.13
@@ -1985,7 +1983,7 @@ __metadata:
eslint: ^8.14.0
eslint-plugin-prettier: ^4.2.1
inversify: ^6.0.1
ioredis: ^5.0.6
ioredis: ^5.2.0
jest: ^28.1.2
mysql2: ^2.3.3
newrelic: ^8.14.1
@@ -2039,15 +2037,15 @@ __metadata:
eslint-plugin-prettier: ^4.0.0
express: ^4.18.1
express-winston: ^4.0.5
helmet: ^4.3.1
helmet: ^5.1.1
inversify: ^6.0.1
inversify-express-utils: ^6.4.3
ioredis: ^5.0.6
ioredis: ^5.2.0
jest: ^28.1.2
jsonwebtoken: ^8.5.1
newrelic: ^8.14.1
nodemon: ^2.0.19
npm-check-updates: ^15.3.3
npm-check-updates: ^16.0.1
prettyjson: ^1.2.5
reflect-metadata: ^0.1.13
ts-jest: ^28.0.5
@@ -2111,7 +2109,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@standardnotes/scheduler-server@workspace:packages/scheduler"
dependencies:
"@newrelic/winston-enricher": ^2.1.0
"@newrelic/winston-enricher": ^4.0.0
"@sentry/node": ^7.3.0
"@standardnotes/common": "workspace:*"
"@standardnotes/domain-events": "workspace:*"
@@ -2127,12 +2125,12 @@ __metadata:
dayjs: ^1.11.3
dotenv: ^16.0.1
eslint-plugin-prettier: ^4.0.0
inversify: 5.0.5
ioredis: ^5.0.6
inversify: ^6.0.1
ioredis: ^5.2.0
jest: ^28.1.2
mysql2: ^2.3.3
newrelic: ^8.14.1
npm-check-updates: ^15.3.3
npm-check-updates: ^16.0.1
reflect-metadata: ^0.1.13
ts-jest: ^28.0.5
typeorm: ^0.3.6
@@ -2174,7 +2172,7 @@ __metadata:
eslint-config-prettier: ^8.5.0
ini: ^3.0.0
newrelic: ^8.14.1
npm-check-updates: ^15.3.3
npm-check-updates: ^16.0.1
prettier: ^2.7.1
ts-node: ^10.8.1
typescript: ^4.7.4
@@ -2233,7 +2231,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@standardnotes/syncing-server@workspace:packages/syncing-server"
dependencies:
"@newrelic/winston-enricher": ^2.1.0
"@newrelic/winston-enricher": ^4.0.0
"@sentry/node": ^7.3.0
"@standardnotes/analytics": "workspace:*"
"@standardnotes/common": "workspace:*"
@@ -2263,16 +2261,16 @@ __metadata:
eslint: ^8.14.0
eslint-plugin-prettier: ^4.0.0
express: ^4.18.1
helmet: 4.3.1
helmet: ^5.1.1
inversify: ^6.0.1
inversify-express-utils: ^6.4.3
ioredis: ^5.0.6
ioredis: ^5.2.0
jest: ^28.1.2
jsonwebtoken: 8.5.1
mysql2: ^2.3.3
newrelic: ^8.14.1
nodemon: ^2.0.19
npm-check-updates: ^15.3.3
npm-check-updates: ^16.0.1
prettyjson: ^1.2.5
reflect-metadata: 0.1.13
ts-jest: ^28.0.5
@@ -2517,7 +2515,7 @@ __metadata:
languageName: node
linkType: hard
"@types/ioredis@npm:^4.28.10, @types/ioredis@npm:^4.28.8":
"@types/ioredis@npm:^4.28.10":
version: 4.28.10
resolution: "@types/ioredis@npm:4.28.10"
dependencies:
@@ -4220,15 +4218,6 @@ __metadata:
languageName: node
linkType: hard
"crypto-random-string@npm:3.3.0":
version: 3.3.0
resolution: "crypto-random-string@npm:3.3.0"
dependencies:
type-fest: ^0.8.1
checksum: deff9866311a3a17ffd26ecdcebbbe9e1e12cf2fca5dd6e89993c9a03342d6da83f9f82cb0bfd7b31265d45eea710f376bc2af37bf3b053ef0cade920b8b04ba
languageName: node
linkType: hard
"crypto-random-string@npm:^4.0.0":
version: 4.0.0
resolution: "crypto-random-string@npm:4.0.0"
@@ -4387,13 +4376,6 @@ __metadata:
languageName: node
linkType: hard
"denque@npm:^1.1.0":
version: 1.5.1
resolution: "denque@npm:1.5.1"
checksum: 4375ad19d5cea99f90effa82a8cecdaa10f4eb261fbcd7e47cd753ff2737f037aac8f7f4e031cc77f3966314c491c86a0d3b20c128aeee57f791b4662c45108e
languageName: node
linkType: hard
"denque@npm:^2.0.1":
version: 2.0.1
resolution: "denque@npm:2.0.1"
@@ -5617,24 +5599,10 @@ __metadata:
languageName: node
linkType: hard
"helmet@npm:4.3.1":
version: 4.3.1
resolution: "helmet@npm:4.3.1"
checksum: 47f59d8b9935eec24dadc5ce83bb7c149fc72f9355d3655babf0518740326b97fb83dc5f566cdcaf97753988003973848eb336ad3c784803a13e5056bca92bf7
languageName: node
linkType: hard
"helmet@npm:4.4.1":
version: 4.4.1
resolution: "helmet@npm:4.4.1"
checksum: cfe385e185e1ef6e4cd2ade4c54e160b05dd0454f270a663c528a8666402cbcad14e0ff0df09567fa62b0b4ac3371bbd1c8a253f6e7af37656a22339fe98c869
languageName: node
linkType: hard
"helmet@npm:^4.3.1":
version: 4.6.0
resolution: "helmet@npm:4.6.0"
checksum: 139ad678d1cab207b043c206f50f6744eff2ef1f463e4626d36718b45b337485c77d10260ef9d89d292fa678da5153d86b08172b3b365cc8e680241015ed3a49
"helmet@npm:^5.1.1":
version: 5.1.1
resolution: "helmet@npm:5.1.1"
checksum: b72ba26cc431804ad3b8ecdc18db95409a492cbb7a7e825efc27fc502b9433fec39fc083f2aad4fe7ed1a89a4287560b59f4435f9689eebbae6a2b61a1ec1b7d
languageName: node
linkType: hard
@@ -5935,13 +5903,6 @@ __metadata:
languageName: node
linkType: hard
"inversify@npm:5.0.5":
version: 5.0.5
resolution: "inversify@npm:5.0.5"
checksum: a4bf1a6a62cacfeb7f82f85c4c00eea2326cd6e30d5d47582a6323ef8aacc5adc72f9aa3d86c373e27387b4ed37fcab7cfac728cdd5bdb252c6d27f849a151f1
languageName: node
linkType: hard
"inversify@npm:^6.0.1":
version: 6.0.1
resolution: "inversify@npm:6.0.1"
@@ -5949,28 +5910,9 @@ __metadata:
languageName: node
linkType: hard
"ioredis@npm:^4.28.5":
version: 4.28.5
resolution: "ioredis@npm:4.28.5"
dependencies:
cluster-key-slot: ^1.1.0
debug: ^4.3.1
denque: ^1.1.0
lodash.defaults: ^4.2.0
lodash.flatten: ^4.4.0
lodash.isarguments: ^3.1.0
p-map: ^2.1.0
redis-commands: 1.7.0
redis-errors: ^1.2.0
redis-parser: ^3.0.0
standard-as-callback: ^2.1.0
checksum: a8793c3324cd69fa55b4baacbda118ce6724e574260157761276b31411dd3e168c75490f7155c6ce34d79e01488efa98e0cdb162991970fd56da7cbcdafb8fb8
languageName: node
linkType: hard
"ioredis@npm:^5.0.6":
version: 5.1.0
resolution: "ioredis@npm:5.1.0"
"ioredis@npm:^5.2.0":
version: 5.2.0
resolution: "ioredis@npm:5.2.0"
dependencies:
"@ioredis/commands": ^1.1.1
cluster-key-slot: ^1.1.0
@@ -5981,7 +5923,7 @@ __metadata:
redis-errors: ^1.2.0
redis-parser: ^3.0.0
standard-as-callback: ^2.1.0
checksum: 7b1c137836ee136a634926df4ec68cc2393772a32ecda8e30dd305d7b9182af02d600456e661f96a6ceada17560ce7b6458948fd09b8adc277a1d287325777dd
checksum: 37189fcd4bd834d66aabb28a754d97da9147a6162c16bd971647750dbb81cbe7d93f4c07a623efdc0fdcb61bca243678b864229ddbcc2b8692755d7be40a2227
languageName: node
linkType: hard
@@ -7067,13 +7009,6 @@ __metadata:
languageName: node
linkType: hard
"lodash.flatten@npm:^4.4.0":
version: 4.4.0
resolution: "lodash.flatten@npm:4.4.0"
checksum: 0ac34a393d4b795d4b7421153d27c13ae67e08786c9cbb60ff5b732210d46f833598eee3fb3844bb10070e8488efe390ea53bb567377e0cb47e9e630bf0811cb
languageName: node
linkType: hard
"lodash.includes@npm:^4.3.0":
version: 4.3.0
resolution: "lodash.includes@npm:4.3.0"
@@ -7871,9 +7806,9 @@ __metadata:
languageName: node
linkType: hard
"npm-check-updates@npm:^15.3.3":
version: 15.3.3
resolution: "npm-check-updates@npm:15.3.3"
"npm-check-updates@npm:^16.0.1":
version: 16.0.1
resolution: "npm-check-updates@npm:16.0.1"
dependencies:
chalk: ^5.0.1
cli-table: ^0.3.11
@@ -7905,7 +7840,7 @@ __metadata:
bin:
ncu: build/src/bin/cli.js
npm-check-updates: build/src/bin/cli.js
checksum: 3ee33fe369961d9548cdfde50085540e432a45a1166df4c1e6bacd5c25d36761efd156f6f358d1a51dd10db09d030106f87edec4d4a16637e35c09549a919cd5
checksum: 1e747421f3d34055183037335437db91cd300448dccaad305925499719d4d34e6c1adfa3ce35a178f209106a40dcd57da088d9125dab2bd125ae54f5ae1c131c
languageName: node
linkType: hard
@@ -8173,13 +8108,6 @@ __metadata:
languageName: node
linkType: hard
"p-map@npm:^2.1.0":
version: 2.1.0
resolution: "p-map@npm:2.1.0"
checksum: 9e3ad3c9f6d75a5b5661bcad78c91f3a63849189737cd75e4f1225bf9ac205194e5c44aac2ef6f09562b1facdb9bd1425584d7ac375bfaa17b3f1a142dab936d
languageName: node
linkType: hard
"p-map@npm:^4.0.0":
version: 4.0.0
resolution: "p-map@npm:4.0.0"
@@ -8943,13 +8871,6 @@ __metadata:
languageName: node
linkType: hard
"redis-commands@npm:1.7.0":
version: 1.7.0
resolution: "redis-commands@npm:1.7.0"
checksum: d1ff7fbcb5e54768c77f731f1d49679d2a62c3899522c28addb4e2e5813aea8bcac3f22519d71d330224c3f2937f935dfc3d8dc65e90db0f5fe22dc2c1515aa7
languageName: node
linkType: hard
"redis-errors@npm:^1.0.0, redis-errors@npm:^1.2.0":
version: 1.2.0
resolution: "redis-errors@npm:1.2.0"