Compare commits

...

15 Commits

Author SHA1 Message Date
standardci 7e1fcebdd8 chore(release): publish new version
- @standardnotes/api-gateway@1.6.1
 - @standardnotes/auth-server@1.8.0
 - @standardnotes/common@1.25.0
 - @standardnotes/domain-events-infra@1.7.5
 - @standardnotes/domain-events@2.39.0
 - @standardnotes/files-server@1.4.0
 - @standardnotes/predicates@1.2.0
 - @standardnotes/scheduler-server@1.5.0
 - @standardnotes/security@1.2.0
 - @standardnotes/syncing-server@1.5.0
2022-07-06 10:04:10 +00:00
Karol Sójko fd4ee2123d feat: add common package 2022-07-06 12:02:39 +02:00
standardci 62caa840ef chore(release): publish new version
- @standardnotes/api-gateway@1.6.0
 - @standardnotes/auth-server@1.7.0
 - @standardnotes/files-server@1.3.0
 - @standardnotes/scheduler-server@1.4.0
 - @standardnotes/syncing-server@1.4.0
 - @standardnotes/time@1.9.0
2022-07-06 09:47:24 +00:00
Karol Sójko 565e890973 feat: add time package 2022-07-06 11:46:04 +02:00
standardci 84c4642ced chore(release): publish new version
- @standardnotes/api-gateway@1.5.0
 - @standardnotes/auth-server@1.6.0
 - @standardnotes/domain-events-infra@1.7.4
 - @standardnotes/domain-events@2.38.0
 - @standardnotes/files-server@1.2.0
 - @standardnotes/scheduler-server@1.3.2
 - @standardnotes/security@1.1.0
 - @standardnotes/syncing-server@1.3.0
2022-07-06 09:28:38 +00:00
Karol Sójko 699164eba5 fix: deps to @standarnotes/security 2022-07-06 11:27:19 +02:00
Karol Sójko d86928f1b4 feat: add security package 2022-07-06 11:24:32 +02:00
standardci 06fc077f1b chore(release): publish new version
- @standardnotes/analytics@1.8.1
 - @standardnotes/api-gateway@1.4.2
 - @standardnotes/auth-server@1.5.1
 - @standardnotes/domain-events-infra@1.7.3
 - @standardnotes/domain-events@2.37.1
 - @standardnotes/files-server@1.1.14
 - @standardnotes/predicates@1.1.1
 - @standardnotes/scheduler-server@1.3.1
 - @standardnotes/syncing-server@1.2.2
2022-07-06 09:20:41 +00:00
Karol Sójko 97ba31f345 fix: files included in distributable packages 2022-07-06 11:19:05 +02:00
standardci a8795defc1 chore(release): publish new version
- @standardnotes/api-gateway@1.4.1
 - @standardnotes/auth-server@1.5.0
 - @standardnotes/domain-events-infra@1.7.2
 - @standardnotes/domain-events@2.37.0
 - @standardnotes/files-server@1.1.13
 - @standardnotes/predicates@1.1.0
 - @standardnotes/scheduler-server@1.3.0
 - @standardnotes/syncing-server@1.2.1
2022-07-06 08:52:19 +00:00
Karol Sójko 1b35cf7a39 fix: scheduler to predicates imports 2022-07-06 10:51:02 +02:00
Karol Sójko ed62ed516f feat: add predicates package 2022-07-06 10:49:22 +02:00
standardci b4f1c6f7f8 chore(release): publish new version
- @standardnotes/analytics@1.8.0
 - @standardnotes/api-gateway@1.4.0
 - @standardnotes/auth-server@1.4.0
 - @standardnotes/domain-events-infra@1.7.1
 - @standardnotes/domain-events@2.36.0
 - @standardnotes/files-server@1.1.12
 - @standardnotes/scheduler-server@1.2.7
 - @standardnotes/syncing-server@1.2.0
2022-07-06 08:37:46 +00:00
Karol Sójko 14e4ca70b4 feat: add analytics package 2022-07-06 10:36:32 +02:00
Karol Sójko 12fa94539b fix: build process due to composite packages 2022-07-06 10:29:06 +02:00
183 changed files with 3593 additions and 478 deletions
+2 -2
View File
@@ -32,7 +32,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:api-gateway
run: yarn build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
@@ -62,7 +62,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:api-gateway
run: yarn build
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
+2 -2
View File
@@ -33,7 +33,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:auth
run: yarn build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
@@ -63,7 +63,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:auth
run: yarn build
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
+2 -2
View File
@@ -33,7 +33,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:files
run: yarn build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
@@ -63,7 +63,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:files
run: yarn build
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
+2 -2
View File
@@ -33,7 +33,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:scheduler
run: yarn build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
@@ -63,7 +63,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:scheduler
run: yarn build
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
+2 -2
View File
@@ -33,7 +33,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:syncing-server
run: yarn build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
@@ -63,7 +63,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build locally
run: yarn build:syncing-server
run: yarn build
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
Generated
+410 -331
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
dist
+6
View File
@@ -0,0 +1,6 @@
{
"extends": "../../.eslintrc",
"parserOptions": {
"project": "./linter.tsconfig.json"
}
}
+98
View File
@@ -0,0 +1,98 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.8.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.8.0...@standardnotes/analytics@1.8.1) (2022-07-06)
### Bug Fixes
* files included in distributable packages ([97ba31f](https://github.com/standardnotes/server/commit/97ba31f345fc95df0c15a348f0461fb9e5bcb923))
# 1.8.0 (2022-07-06)
### Features
* add analytics package ([14e4ca7](https://github.com/standardnotes/server/commit/14e4ca70b438dd3eaaa404bc0ca31d22a62b45be))
## [1.6.1](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.6.0...@standardnotes/analytics@1.6.1) (2022-07-04)
### Bug Fixes
* add missing reflect-metadata package to all packages ([ce3a5bb](https://github.com/standardnotes/snjs/commit/ce3a5bbf3f1d2276ac4abc3eec3c6a44c8c3ba9b))
# [1.6.0](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.5.0...@standardnotes/analytics@1.6.0) (2022-06-02)
### Features
* refactor analytics store to handle different periods of time ([00d4f3f](https://github.com/standardnotes/snjs/commit/00d4f3f2f742b0deb5ef4cd415c672574cb3a911))
# [1.5.0](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.4.1...@standardnotes/analytics@1.5.0) (2022-06-01)
### Features
* add unmarking activities ([09cea1d](https://github.com/standardnotes/snjs/commit/09cea1d8e97dd83f2eaafaef5ff680aef8c5c3ff))
## [1.4.1](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.4.0...@standardnotes/analytics@1.4.1) (2022-06-01)
### Bug Fixes
* rename analytics activity backup to email backup ([30d2db6](https://github.com/standardnotes/snjs/commit/30d2db63e5dec05b3f0976211c661d8aa0e04139))
# [1.4.0](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.3.1...@standardnotes/analytics@1.4.0) (2022-06-01)
### Features
* add calculating total counts of activites in analytics ([6ed659a](https://github.com/standardnotes/snjs/commit/6ed659a7c4411ce2555e4af96dc5473c3d03fd41))
## [1.3.1](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.3.0...@standardnotes/analytics@1.3.1) (2022-05-26)
### Bug Fixes
* add backup analytics activity ([cf7ae68](https://github.com/standardnotes/snjs/commit/cf7ae68e13aa9403702340da8a3bca35e9273784))
# [1.3.0](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.2.0...@standardnotes/analytics@1.3.0) (2022-05-26)
### Features
* add calculating activity retention in analytics ([bc2f26d](https://github.com/standardnotes/snjs/commit/bc2f26d63a8ff0e2750b37bc1ee56297f6a8c98d))
# [1.2.0](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.1.0...@standardnotes/analytics@1.2.0) (2022-05-26)
### Features
* add activity indicators in analytics ([e6f5b5a](https://github.com/standardnotes/snjs/commit/e6f5b5afbff1f5f96adfcba42f4708fa74ac7f80))
# [1.1.0](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.0.7...@standardnotes/analytics@1.1.0) (2022-05-24)
### Features
* add marking activity in analytics ([#750](https://github.com/standardnotes/snjs/issues/750)) ([2a68fa6](https://github.com/standardnotes/snjs/commit/2a68fa6636c24e79443359d31a8427d50ca87cca))
## [1.0.7](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.0.5...@standardnotes/analytics@1.0.7) (2022-05-04)
**Note:** Version bump only for package @standardnotes/analytics
## [1.0.6](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.0.5...@standardnotes/analytics@1.0.6) (2022-05-04)
**Note:** Version bump only for package @standardnotes/analytics
## [1.0.5](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.0.4...@standardnotes/analytics@1.0.5) (2022-04-22)
**Note:** Version bump only for package @standardnotes/analytics
## [1.0.4](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.0.3...@standardnotes/analytics@1.0.4) (2022-04-15)
**Note:** Version bump only for package @standardnotes/analytics
## [1.0.3](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.0.2...@standardnotes/analytics@1.0.3) (2022-04-11)
**Note:** Version bump only for package @standardnotes/analytics
## [1.0.2](https://github.com/standardnotes/snjs/compare/@standardnotes/analytics@1.0.1...@standardnotes/analytics@1.0.2) (2022-03-31)
**Note:** Version bump only for package @standardnotes/analytics
## 1.0.1 (2022-03-10)
**Note:** Version bump only for package @standardnotes/analytics
+11
View File
@@ -0,0 +1,11 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const base = require('../../jest.config');
module.exports = {
...base,
globals: {
'ts-jest': {
tsconfig: 'tsconfig.json',
},
},
};
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["dist"]
}
+40
View File
@@ -0,0 +1,40 @@
{
"name": "@standardnotes/analytics",
"version": "1.8.1",
"engines": {
"node": ">=14.0.0 <17.0.0"
},
"description": "Analytics tools for Standard Notes projects",
"main": "dist/src/index.js",
"author": "Standard Notes",
"types": "dist/src/index.d.ts",
"files": [
"dist/src/**/*.js",
"dist/src/**/*.d.ts"
],
"publishConfig": {
"access": "public"
},
"license": "AGPL-3.0-or-later",
"scripts": {
"clean": "rm -fr dist",
"prestart": "yarn clean",
"start": "tsc -p tsconfig.json --watch",
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage"
},
"devDependencies": {
"@types/ioredis": "^4.28.8",
"@types/jest": "^27.4.1",
"@typescript-eslint/eslint-plugin": "^5.30.0",
"eslint-plugin-prettier": "^4.2.1",
"ioredis": "^4.28.5",
"jest": "^27.5.1",
"ts-jest": "^27.1.3"
},
"dependencies": {
"reflect-metadata": "^0.1.13"
}
}
@@ -0,0 +1,6 @@
export enum AnalyticsActivity {
EditingItems = 'editing-items',
Login = 'login',
EmailUnbackedUpData = 'email-unbacked-up-data',
EmailBackup = 'email-backup',
}
@@ -0,0 +1,10 @@
import { Period } from '../Time/Period'
import { AnalyticsActivity } from './AnalyticsActivity'
export interface AnalyticsStoreInterface {
unmarkActivity(activities: AnalyticsActivity[], analyticsId: number, periods: Period[]): Promise<void>
markActivity(activities: AnalyticsActivity[], analyticsId: number, periods: Period[]): Promise<void>
wasActivityDone(activity: AnalyticsActivity, analyticsId: number, period: Period): Promise<boolean>
calculateActivityRetention(activity: AnalyticsActivity, firstPeriod: Period, secondPeriod: Period): Promise<number>
calculateActivityTotalCount(activity: AnalyticsActivity, period: Period): Promise<number>
}
@@ -0,0 +1,8 @@
export interface StatisticsStoreInterface {
incrementSNJSVersionUsage(snjsVersion: string): Promise<void>
incrementApplicationVersionUsage(applicationVersion: string): Promise<void>
incrementOutOfSyncIncidents(): Promise<void>
getYesterdaySNJSUsage(): Promise<Array<{ version: string; count: number }>>
getYesterdayApplicationUsage(): Promise<Array<{ version: string; count: number }>>
getYesterdayOutOfSyncIncidents(): Promise<number>
}
@@ -0,0 +1,10 @@
export enum Period {
Today,
Yesterday,
DayBeforeYesterday,
ThisWeek,
LastWeek,
WeekBeforeLastWeek,
ThisMonth,
LastMonth,
}
@@ -0,0 +1,58 @@
import { Period } from './Period'
import { PeriodKeyGenerator } from './PeriodKeyGenerator'
describe('PeriodKeyGenerator', () => {
const createGenerator = () => new PeriodKeyGenerator()
beforeEach(() => {
jest.useFakeTimers('modern')
jest.setSystemTime(1653395155000)
})
afterEach(() => {
jest.useRealTimers()
})
it('should generate a period key for today', () => {
expect(createGenerator().getPeriodKey(Period.Today)).toEqual('2022-5-24')
})
it('should generate a period key for yesterday', () => {
expect(createGenerator().getPeriodKey(Period.Yesterday)).toEqual('2022-5-23')
})
it('should generate a period key for the day before yesterday', () => {
expect(createGenerator().getPeriodKey(Period.DayBeforeYesterday)).toEqual('2022-5-22')
})
it('should generate a period key for this week', () => {
expect(createGenerator().getPeriodKey(Period.ThisWeek)).toEqual('2022-week-21')
})
it('should generate a period key for last week', () => {
expect(createGenerator().getPeriodKey(Period.LastWeek)).toEqual('2022-week-20')
})
it('should generate a period key for the week before last week', () => {
expect(createGenerator().getPeriodKey(Period.WeekBeforeLastWeek)).toEqual('2022-week-19')
})
it('should generate a period key for this month', () => {
expect(createGenerator().getPeriodKey(Period.ThisMonth)).toEqual('2022-5')
})
it('should generate a period key for last month', () => {
expect(createGenerator().getPeriodKey(Period.LastMonth)).toEqual('2022-4')
})
it('should throw error on unsupported period', () => {
let error = null
try {
createGenerator().getPeriodKey(42 as Period)
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
})
@@ -0,0 +1,99 @@
import { Period } from './Period'
import { PeriodKeyGeneratorInterface } from './PeriodKeyGeneratorInterface'
export class PeriodKeyGenerator implements PeriodKeyGeneratorInterface {
getPeriodKey(period: Period): string {
switch (period) {
case Period.Today:
return this.getDailyKey()
case Period.Yesterday:
return this.getDailyKey(this.getYesterdayDate())
case Period.DayBeforeYesterday:
return this.getDailyKey(this.getDayBeforeYesterdayDate())
case Period.ThisWeek:
return this.getWeeklyKey()
case Period.LastWeek:
return this.getWeeklyKey(this.getLastWeekDate())
case Period.WeekBeforeLastWeek:
return this.getWeeklyKey(this.getWeekBeforeLastWeekDate())
case Period.ThisMonth:
return this.getMonthlyKey()
case Period.LastMonth:
return this.getMonthlyKey(this.getLastMonthDate())
default:
throw new Error(`Unsuporrted period: ${period}`)
}
}
private getMonthlyKey(date?: Date): string {
date = date ?? new Date()
return `${this.getYear(date)}-${this.getMonth(date)}`
}
private getDailyKey(date?: Date): string {
date = date ?? new Date()
return `${this.getYear(date)}-${this.getMonth(date)}-${this.getDayOfTheMonth(date)}`
}
private getWeeklyKey(date?: Date): string {
date = date ?? new Date()
const firstJanuary = new Date(date.getFullYear(), 0, 1)
const numberOfDaysPassed = Math.floor((date.getTime() - firstJanuary.getTime()) / (24 * 60 * 60 * 1000))
const weekNumber = Math.ceil((date.getDay() + 1 + numberOfDaysPassed) / 7)
return `${this.getYear(date)}-week-${weekNumber}`
}
private getYear(date: Date): string {
return date.getFullYear().toString()
}
private getMonth(date: Date): string {
return (date.getMonth() + 1).toString()
}
private getDayOfTheMonth(date: Date): string {
return date.getDate().toString()
}
private getYesterdayDate(): Date {
const yesterday = new Date()
yesterday.setDate(new Date().getDate() - 1)
return yesterday
}
private getDayBeforeYesterdayDate(): Date {
const dayBeforeYesterday = new Date()
dayBeforeYesterday.setDate(new Date().getDate() - 2)
return dayBeforeYesterday
}
private getLastWeekDate(): Date {
const yesterday = new Date()
yesterday.setDate(new Date().getDate() - 7)
return yesterday
}
private getLastMonthDate(): Date {
const lastMonth = new Date()
lastMonth.setDate(1)
lastMonth.setMonth(lastMonth.getMonth() - 1)
return lastMonth
}
private getWeekBeforeLastWeekDate(): Date {
const yesterday = new Date()
yesterday.setDate(new Date().getDate() - 14)
return yesterday
}
}
@@ -0,0 +1,5 @@
import { Period } from './Period'
export interface PeriodKeyGeneratorInterface {
getPeriodKey(period: Period): string
}
+6
View File
@@ -0,0 +1,6 @@
export * from './Analytics/AnalyticsActivity'
export * from './Analytics/AnalyticsStoreInterface'
export * from './Statistics/StatisticsStoreInterface'
export * from './Time/Period'
export * from './Time/PeriodKeyGenerator'
export * from './Time/PeriodKeyGeneratorInterface'
@@ -0,0 +1,131 @@
import * as IORedis from 'ioredis'
import { Period } from '../../Domain'
import { AnalyticsActivity } from '../../Domain/Analytics/AnalyticsActivity'
import { PeriodKeyGeneratorInterface } from '../../Domain/Time/PeriodKeyGeneratorInterface'
import { RedisAnalyticsStore } from './RedisAnalyticsStore'
describe('RedisAnalyticsStore', () => {
let redisClient: IORedis.Redis
let pipeline: IORedis.Pipeline
let periodKeyGenerator: PeriodKeyGeneratorInterface
const createStore = () => new RedisAnalyticsStore(periodKeyGenerator, redisClient)
beforeEach(() => {
pipeline = {} as jest.Mocked<IORedis.Pipeline>
pipeline.incr = jest.fn()
pipeline.setbit = jest.fn()
pipeline.exec = jest.fn()
redisClient = {} as jest.Mocked<IORedis.Redis>
redisClient.pipeline = jest.fn().mockReturnValue(pipeline)
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')
})
it('should calculate total count of activities', async () => {
redisClient.bitcount = jest.fn().mockReturnValue(70)
expect(await createStore().calculateActivityTotalCount(AnalyticsActivity.EditingItems, Period.Yesterday)).toEqual(
70,
)
expect(redisClient.bitcount).toHaveBeenCalledWith('bitmap:action:editing-items:timespan:period-key')
})
it('should calculate activity retention', async () => {
redisClient.bitcount = jest.fn().mockReturnValueOnce(7).mockReturnValueOnce(10)
expect(
await createStore().calculateActivityRetention(
AnalyticsActivity.EditingItems,
Period.DayBeforeYesterday,
Period.Yesterday,
),
).toEqual(70)
expect(redisClient.send_command).toHaveBeenCalledWith(
'BITOP',
'AND',
'bitmap:action:editing-items:timespan:period-key-period-key',
'bitmap:action:editing-items:timespan:period-key',
'bitmap:action:editing-items:timespan:period-key',
)
})
it('shoud tell if activity was done', async () => {
await createStore().wasActivityDone(AnalyticsActivity.EditingItems, 123, Period.Yesterday)
expect(redisClient.getbit).toHaveBeenCalledWith('bitmap:action:editing-items:timespan:period-key', 123)
})
it('should mark activity as done', async () => {
await createStore().markActivity([AnalyticsActivity.EditingItems], 123, [Period.Today])
expect(pipeline.setbit).toBeCalledTimes(1)
expect(pipeline.setbit).toHaveBeenNthCalledWith(1, 'bitmap:action:editing-items:timespan:period-key', 123, 1)
expect(pipeline.exec).toHaveBeenCalled()
})
it('should mark activities as done', async () => {
await createStore().markActivity([AnalyticsActivity.EditingItems, AnalyticsActivity.EmailUnbackedUpData], 123, [
Period.Today,
Period.ThisWeek,
])
expect(pipeline.setbit).toBeCalledTimes(4)
expect(pipeline.setbit).toHaveBeenNthCalledWith(1, 'bitmap:action:editing-items:timespan:period-key', 123, 1)
expect(pipeline.setbit).toHaveBeenNthCalledWith(2, 'bitmap:action:editing-items:timespan:period-key', 123, 1)
expect(pipeline.setbit).toHaveBeenNthCalledWith(
3,
'bitmap:action:email-unbacked-up-data:timespan:period-key',
123,
1,
)
expect(pipeline.setbit).toHaveBeenNthCalledWith(
4,
'bitmap:action:email-unbacked-up-data:timespan:period-key',
123,
1,
)
expect(pipeline.exec).toHaveBeenCalled()
})
it('should unmark activity as done', async () => {
await createStore().unmarkActivity([AnalyticsActivity.EditingItems], 123, [Period.Today])
expect(pipeline.setbit).toBeCalledTimes(1)
expect(pipeline.setbit).toHaveBeenNthCalledWith(1, 'bitmap:action:editing-items:timespan:period-key', 123, 0)
expect(pipeline.exec).toHaveBeenCalled()
})
it('should unmark activities as done', async () => {
await createStore().unmarkActivity([AnalyticsActivity.EditingItems, AnalyticsActivity.EmailUnbackedUpData], 123, [
Period.Today,
Period.ThisWeek,
])
expect(pipeline.setbit).toBeCalledTimes(4)
expect(pipeline.setbit).toHaveBeenNthCalledWith(1, 'bitmap:action:editing-items:timespan:period-key', 123, 0)
expect(pipeline.setbit).toHaveBeenNthCalledWith(2, 'bitmap:action:editing-items:timespan:period-key', 123, 0)
expect(pipeline.setbit).toHaveBeenNthCalledWith(
3,
'bitmap:action:email-unbacked-up-data:timespan:period-key',
123,
0,
)
expect(pipeline.setbit).toHaveBeenNthCalledWith(
4,
'bitmap:action:email-unbacked-up-data:timespan:period-key',
123,
0,
)
expect(pipeline.exec).toHaveBeenCalled()
})
})
@@ -0,0 +1,84 @@
import * as IORedis from 'ioredis'
import { Period } from '../../Domain/Time/Period'
import { PeriodKeyGeneratorInterface } from '../../Domain/Time/PeriodKeyGeneratorInterface'
import { AnalyticsActivity } from '../../Domain/Analytics/AnalyticsActivity'
import { AnalyticsStoreInterface } from '../../Domain/Analytics/AnalyticsStoreInterface'
export class RedisAnalyticsStore implements AnalyticsStoreInterface {
constructor(private periodKeyGenerator: PeriodKeyGeneratorInterface, private redisClient: IORedis.Redis) {}
async markActivity(activities: AnalyticsActivity[], analyticsId: number, periods: Period[]): Promise<void> {
const pipeline = this.redisClient.pipeline()
for (const activity of activities) {
for (const period of periods) {
pipeline.setbit(
`bitmap:action:${activity}:timespan:${this.periodKeyGenerator.getPeriodKey(period)}`,
analyticsId,
1,
)
}
}
await pipeline.exec()
}
async unmarkActivity(activities: AnalyticsActivity[], analyticsId: number, periods: Period[]): Promise<void> {
const pipeline = this.redisClient.pipeline()
for (const activity of activities) {
for (const period of periods) {
pipeline.setbit(
`bitmap:action:${activity}:timespan:${this.periodKeyGenerator.getPeriodKey(period)}`,
analyticsId,
0,
)
}
}
await pipeline.exec()
}
async wasActivityDone(activity: AnalyticsActivity, analyticsId: number, period: Period): Promise<boolean> {
const bitValue = await this.redisClient.getbit(
`bitmap:action:${activity}:timespan:${this.periodKeyGenerator.getPeriodKey(period)}`,
analyticsId,
)
return bitValue === 1
}
async calculateActivityRetention(
activity: AnalyticsActivity,
firstPeriod: Period,
secondPeriod: Period,
): Promise<number> {
const initialPeriodKey = this.periodKeyGenerator.getPeriodKey(firstPeriod)
const subsequentPeriodKey = this.periodKeyGenerator.getPeriodKey(secondPeriod)
const diffKey = `bitmap:action:${activity}:timespan:${initialPeriodKey}-${subsequentPeriodKey}`
await this.redisClient.send_command(
'BITOP',
'AND',
diffKey,
`bitmap:action:${activity}:timespan:${initialPeriodKey}`,
`bitmap:action:${activity}:timespan:${subsequentPeriodKey}`,
)
const retainedTotalInActivity = await this.redisClient.bitcount(diffKey)
const initialTotalInActivity = await this.redisClient.bitcount(
`bitmap:action:${activity}:timespan:${initialPeriodKey}`,
)
return Math.ceil((retainedTotalInActivity * 100) / initialTotalInActivity)
}
async calculateActivityTotalCount(activity: AnalyticsActivity, period: Period): Promise<number> {
return this.redisClient.bitcount(
`bitmap:action:${activity}:timespan:${this.periodKeyGenerator.getPeriodKey(period)}`,
)
}
}
@@ -0,0 +1,92 @@
import * as IORedis from 'ioredis'
import { PeriodKeyGeneratorInterface } from '../../Domain'
import { RedisStatisticsStore } from './RedisStatisticsStore'
describe('RedisStatisticsStore', () => {
let redisClient: IORedis.Redis
let periodKeyGenerator: PeriodKeyGeneratorInterface
let pipeline: IORedis.Pipeline
const createStore = () => new RedisStatisticsStore(periodKeyGenerator, redisClient)
beforeEach(() => {
pipeline = {} as jest.Mocked<IORedis.Pipeline>
pipeline.incr = jest.fn()
pipeline.setbit = jest.fn()
pipeline.exec = jest.fn()
redisClient = {} as jest.Mocked<IORedis.Redis>
redisClient.pipeline = jest.fn().mockReturnValue(pipeline)
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')
})
it('should get yesterday out of sync incidents', async () => {
redisClient.get = jest.fn().mockReturnValue(1)
expect(await createStore().getYesterdayOutOfSyncIncidents()).toEqual(1)
})
it('should default to 0 yesterday out of sync incidents', async () => {
redisClient.get = jest.fn().mockReturnValue(null)
expect(await createStore().getYesterdayOutOfSyncIncidents()).toEqual(0)
})
it('should get yesterday application version usage', async () => {
redisClient.keys = jest
.fn()
.mockReturnValue([
'count:action:application-request:1.2.3:timespan:2022-3-10',
'count:action:application-request:2.3.4:timespan:2022-3-10',
])
redisClient.get = jest.fn().mockReturnValueOnce(3).mockReturnValueOnce(4)
expect(await createStore().getYesterdayApplicationUsage()).toEqual([
{ count: 3, version: '1.2.3' },
{ count: 4, version: '2.3.4' },
])
})
it('should get yesterday snjs version usage', async () => {
redisClient.keys = jest
.fn()
.mockReturnValue([
'count:action:snjs-request:1.2.3:timespan:2022-3-10',
'count:action:snjs-request:2.3.4:timespan:2022-3-10',
])
redisClient.get = jest.fn().mockReturnValueOnce(3).mockReturnValueOnce(4)
expect(await createStore().getYesterdaySNJSUsage()).toEqual([
{ count: 3, version: '1.2.3' },
{ count: 4, version: '2.3.4' },
])
})
it('should increment application version usage', async () => {
await createStore().incrementApplicationVersionUsage('1.2.3')
expect(pipeline.incr).toHaveBeenCalled()
expect(pipeline.exec).toHaveBeenCalled()
})
it('should increment snjs version usage', async () => {
await createStore().incrementSNJSVersionUsage('1.2.3')
expect(pipeline.incr).toHaveBeenCalled()
expect(pipeline.exec).toHaveBeenCalled()
})
it('should increment out of sync incedent count', async () => {
await createStore().incrementOutOfSyncIncidents()
expect(pipeline.incr).toHaveBeenCalled()
expect(pipeline.exec).toHaveBeenCalled()
})
})
@@ -0,0 +1,99 @@
import * as IORedis from 'ioredis'
import { Period, PeriodKeyGeneratorInterface } from '../../Domain'
import { StatisticsStoreInterface } from '../../Domain/Statistics/StatisticsStoreInterface'
export class RedisStatisticsStore implements StatisticsStoreInterface {
constructor(private periodKeyGenerator: PeriodKeyGeneratorInterface, private redisClient: IORedis.Redis) {}
async getYesterdayOutOfSyncIncidents(): Promise<number> {
const count = await this.redisClient.get(
`count:action:out-of-sync:timespan:${this.periodKeyGenerator.getPeriodKey(Period.Yesterday)}`,
)
if (count === null) {
return 0
}
return +count
}
async incrementOutOfSyncIncidents(): Promise<void> {
const pipeline = this.redisClient.pipeline()
pipeline.incr(`count:action:out-of-sync:timespan:${this.periodKeyGenerator.getPeriodKey(Period.Today)}`)
pipeline.incr(`count:action:out-of-sync:timespan:${this.periodKeyGenerator.getPeriodKey(Period.ThisWeek)}`)
pipeline.incr(`count:action:out-of-sync:timespan:${this.periodKeyGenerator.getPeriodKey(Period.ThisMonth)}`)
await pipeline.exec()
}
async getYesterdaySNJSUsage(): Promise<{ version: string; count: number }[]> {
const keys = await this.redisClient.keys(
`count:action:snjs-request:*:timespan:${this.periodKeyGenerator.getPeriodKey(Period.Yesterday)}`,
)
return this.getRequestCountPerVersion(keys)
}
async getYesterdayApplicationUsage(): Promise<{ version: string; count: number }[]> {
const keys = await this.redisClient.keys(
`count:action:application-request:*:timespan:${this.periodKeyGenerator.getPeriodKey(Period.Yesterday)}`,
)
return this.getRequestCountPerVersion(keys)
}
async incrementApplicationVersionUsage(applicationVersion: string): Promise<void> {
const pipeline = this.redisClient.pipeline()
pipeline.incr(
`count:action:application-request:${applicationVersion}:timespan:${this.periodKeyGenerator.getPeriodKey(
Period.Today,
)}`,
)
pipeline.incr(
`count:action:application-request:${applicationVersion}:timespan:${this.periodKeyGenerator.getPeriodKey(
Period.ThisWeek,
)}`,
)
pipeline.incr(
`count:action:application-request:${applicationVersion}:timespan:${this.periodKeyGenerator.getPeriodKey(
Period.ThisMonth,
)}`,
)
await pipeline.exec()
}
async incrementSNJSVersionUsage(snjsVersion: string): Promise<void> {
const pipeline = this.redisClient.pipeline()
pipeline.incr(
`count:action:snjs-request:${snjsVersion}:timespan:${this.periodKeyGenerator.getPeriodKey(Period.Today)}`,
)
pipeline.incr(
`count:action:snjs-request:${snjsVersion}:timespan:${this.periodKeyGenerator.getPeriodKey(Period.ThisWeek)}`,
)
pipeline.incr(
`count:action:snjs-request:${snjsVersion}:timespan:${this.periodKeyGenerator.getPeriodKey(Period.ThisMonth)}`,
)
await pipeline.exec()
}
private async getRequestCountPerVersion(keys: string[]): Promise<{ version: string; count: number }[]> {
const statistics = []
for (const key of keys) {
const count = await this.redisClient.get(key)
const version = key.split(':')[3]
statistics.push({
version,
count: +(count as string),
})
}
return statistics
}
}
+2
View File
@@ -0,0 +1,2 @@
export * from './Redis/RedisAnalyticsStore'
export * from './Redis/RedisStatisticsStore'
+2
View File
@@ -0,0 +1,2 @@
export * from './Domain'
export * from './Infra'
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"composite": true,
"outDir": "./dist",
},
"include": [
"src/**/*"
],
"references": []
}
+34
View File
@@ -3,6 +3,40 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.6.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.6.0...@standardnotes/api-gateway@1.6.1) (2022-07-06)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.6.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.5.0...@standardnotes/api-gateway@1.6.0) (2022-07-06)
### Features
* add time package ([565e890](https://github.com/standardnotes/api-gateway/commit/565e890973b1d96544bb750fdd700d58f8dad088))
# [1.5.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.4.2...@standardnotes/api-gateway@1.5.0) (2022-07-06)
### Bug Fixes
* deps to @standarnotes/security ([699164e](https://github.com/standardnotes/api-gateway/commit/699164eba553cd07fb50f7a06ae8991028167603))
### Features
* add security package ([d86928f](https://github.com/standardnotes/api-gateway/commit/d86928f1b4b5feda8c330ed8ee0bf9de0fc12ae7))
## [1.4.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.4.1...@standardnotes/api-gateway@1.4.2) (2022-07-06)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.4.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.4.0...@standardnotes/api-gateway@1.4.1) (2022-07-06)
**Note:** Version bump only for package @standardnotes/api-gateway
# [1.4.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.3.5...@standardnotes/api-gateway@1.4.0) (2022-07-06)
### Features
* add analytics package ([14e4ca7](https://github.com/standardnotes/api-gateway/commit/14e4ca70b438dd3eaaa404bc0ca31d22a62b45be))
## [1.3.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.3.4...@standardnotes/api-gateway@1.3.5) (2022-07-06)
### Bug Fixes
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.3.5",
"version": "1.6.1",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -24,11 +24,11 @@
"dependencies": {
"@newrelic/winston-enricher": "^2.1.0",
"@sentry/node": "^7.3.0",
"@standardnotes/analytics": "^1.6.0",
"@standardnotes/auth": "3.19.4",
"@standardnotes/analytics": "workspace:*",
"@standardnotes/domain-events": "workspace:*",
"@standardnotes/domain-events-infra": "workspace:*",
"@standardnotes/time": "^1.7.1",
"@standardnotes/security": "workspace:*",
"@standardnotes/time": "workspace:*",
"aws-sdk": "^2.1160.0",
"axios": "0.24.0",
"cors": "2.8.5",
@@ -1,4 +1,4 @@
import { CrossServiceTokenData } from '@standardnotes/auth'
import { CrossServiceTokenData } from '@standardnotes/security'
import { TimerInterface } from '@standardnotes/time'
import { NextFunction, Request, Response } from 'express'
import { inject, injectable } from 'inversify'
@@ -1,4 +1,4 @@
import { OfflineUserTokenData, CrossServiceTokenData } from '@standardnotes/auth'
import { OfflineUserTokenData, CrossServiceTokenData } from '@standardnotes/security'
import { NextFunction, Request, Response } from 'express'
import { inject, injectable } from 'inversify'
import { BaseMiddleware } from 'inversify-express-utils'
+42
View File
@@ -3,6 +3,48 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.8.0](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.7.0...@standardnotes/auth-server@1.8.0) (2022-07-06)
### Features
* add common package ([fd4ee21](https://github.com/standardnotes/auth/commit/fd4ee2123dc72b4d8755504d57bced608c1ab928))
# [1.7.0](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.6.0...@standardnotes/auth-server@1.7.0) (2022-07-06)
### Features
* add time package ([565e890](https://github.com/standardnotes/auth/commit/565e890973b1d96544bb750fdd700d58f8dad088))
# [1.6.0](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.5.1...@standardnotes/auth-server@1.6.0) (2022-07-06)
### Bug Fixes
* deps to @standarnotes/security ([699164e](https://github.com/standardnotes/auth/commit/699164eba553cd07fb50f7a06ae8991028167603))
### Features
* add security package ([d86928f](https://github.com/standardnotes/auth/commit/d86928f1b4b5feda8c330ed8ee0bf9de0fc12ae7))
## [1.5.1](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.5.0...@standardnotes/auth-server@1.5.1) (2022-07-06)
**Note:** Version bump only for package @standardnotes/auth-server
# [1.5.0](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.4.0...@standardnotes/auth-server@1.5.0) (2022-07-06)
### Bug Fixes
* scheduler to predicates imports ([1b35cf7](https://github.com/standardnotes/auth/commit/1b35cf7a392fbe6c4b49889f92ffab6ddd7bb181))
### Features
* add predicates package ([ed62ed5](https://github.com/standardnotes/auth/commit/ed62ed516f5cf8784975f41680366bf0518ce491))
# [1.4.0](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.3.14...@standardnotes/auth-server@1.4.0) (2022-07-06)
### Features
* add analytics package ([14e4ca7](https://github.com/standardnotes/auth/commit/14e4ca70b438dd3eaaa404bc0ca31d22a62b45be))
## [1.3.14](https://github.com/standardnotes/auth/compare/@standardnotes/auth-server@1.3.13...@standardnotes/auth-server@1.3.14) (2022-07-06)
### Bug Fixes
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.3.14",
"version": "1.8.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -33,19 +33,19 @@
"dependencies": {
"@newrelic/winston-enricher": "^2.1.0",
"@sentry/node": "^7.3.0",
"@standardnotes/analytics": "^1.6.0",
"@standardnotes/analytics": "workspace:*",
"@standardnotes/api": "^1.1.19",
"@standardnotes/auth": "^3.19.4",
"@standardnotes/common": "^1.23.1",
"@standardnotes/common": "workspace:*",
"@standardnotes/domain-events": "workspace:*",
"@standardnotes/domain-events-infra": "workspace:*",
"@standardnotes/features": "^1.47.0",
"@standardnotes/predicates": "workspace:*",
"@standardnotes/responses": "^1.6.39",
"@standardnotes/scheduler": "^1.1.2",
"@standardnotes/security": "workspace:*",
"@standardnotes/settings": "^1.15.0",
"@standardnotes/sncrypto-common": "^1.9.0",
"@standardnotes/sncrypto-node": "^1.8.3",
"@standardnotes/time": "^1.7.1",
"@standardnotes/time": "workspace:*",
"aws-sdk": "^2.1159.0",
"axios": "0.24.0",
"bcryptjs": "2.4.3",
+1 -1
View File
@@ -143,7 +143,7 @@ import {
TokenEncoder,
TokenEncoderInterface,
ValetTokenData,
} from '@standardnotes/auth'
} from '@standardnotes/security'
import { FileUploadedEventHandler } from '../Domain/Handler/FileUploadedEventHandler'
import { CreateValetToken } from '../Domain/UseCase/CreateValetToken/CreateValetToken'
import { CreateListedAccount } from '../Domain/UseCase/CreateListedAccount/CreateListedAccount'
@@ -3,7 +3,7 @@ import 'reflect-metadata'
import { ApiGatewayAuthMiddleware } from './ApiGatewayAuthMiddleware'
import { NextFunction, Request, Response } from 'express'
import { Logger } from 'winston'
import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/auth'
import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/security'
import { RoleName } from '@standardnotes/common'
describe('ApiGatewayAuthMiddleware', () => {
@@ -1,4 +1,4 @@
import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/auth'
import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/security'
import { NextFunction, Request, Response } from 'express'
import { inject, injectable } from 'inversify'
import { BaseMiddleware } from 'inversify-express-utils'
@@ -3,7 +3,7 @@ import 'reflect-metadata'
import { ApiGatewayOfflineAuthMiddleware } from './ApiGatewayOfflineAuthMiddleware'
import { NextFunction, Request, Response } from 'express'
import { Logger } from 'winston'
import { OfflineUserTokenData, TokenDecoderInterface } from '@standardnotes/auth'
import { OfflineUserTokenData, TokenDecoderInterface } from '@standardnotes/security'
describe('ApiGatewayOfflineAuthMiddleware', () => {
let tokenDecoder: TokenDecoderInterface<OfflineUserTokenData>
@@ -1,4 +1,4 @@
import { OfflineUserTokenData, TokenDecoderInterface } from '@standardnotes/auth'
import { OfflineUserTokenData, TokenDecoderInterface } from '@standardnotes/security'
import { NextFunction, Request, Response } from 'express'
import { inject, injectable } from 'inversify'
import { BaseMiddleware } from 'inversify-express-utils'
@@ -11,7 +11,7 @@ import { CreateOfflineSubscriptionTokenResponse } from '../Domain/UseCase/Create
import { AuthenticateOfflineSubscriptionToken } from '../Domain/UseCase/AuthenticateOfflineSubscriptionToken/AuthenticateOfflineSubscriptionToken'
import { OfflineUserSubscription } from '../Domain/Subscription/OfflineUserSubscription'
import { GetUserOfflineSubscription } from '../Domain/UseCase/GetUserOfflineSubscription/GetUserOfflineSubscription'
import { OfflineUserTokenData, TokenEncoderInterface } from '@standardnotes/auth'
import { OfflineUserTokenData, TokenEncoderInterface } from '@standardnotes/security'
import { SubscriptionName } from '@standardnotes/common'
import { Logger } from 'winston'
@@ -14,7 +14,7 @@ import { AuthenticateOfflineSubscriptionToken } from '../Domain/UseCase/Authenti
import { CreateOfflineSubscriptionToken } from '../Domain/UseCase/CreateOfflineSubscriptionToken/CreateOfflineSubscriptionToken'
import { GetUserOfflineSubscription } from '../Domain/UseCase/GetUserOfflineSubscription/GetUserOfflineSubscription'
import { Logger } from 'winston'
import { OfflineUserTokenData, TokenEncoderInterface } from '@standardnotes/auth'
import { OfflineUserTokenData, TokenEncoderInterface } from '@standardnotes/security'
@controller('/offline')
export class OfflineController extends BaseHttpController {
@@ -10,7 +10,7 @@ import { GetActiveSessionsForUser } from '../Domain/UseCase/GetActiveSessionsFor
import { AuthenticateRequest } from '../Domain/UseCase/AuthenticateRequest'
import { User } from '../Domain/User/User'
import { Role } from '../Domain/Role/Role'
import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/auth'
import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/security'
import { GetUserAnalyticsId } from '../Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
describe('SessionsController', () => {
@@ -16,7 +16,7 @@ import { Role } from '../Domain/Role/Role'
import { User } from '../Domain/User/User'
import { ProjectorInterface } from '../Projection/ProjectorInterface'
import { SessionProjector } from '../Projection/SessionProjector'
import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/auth'
import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/security'
import { RoleName } from '@standardnotes/common'
import { GetUserAnalyticsId } from '../Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
@@ -1,4 +1,4 @@
import { Role } from '@standardnotes/auth'
import { Role } from '@standardnotes/security'
import { Request, Response } from 'express'
import { inject } from 'inversify'
import {
@@ -12,7 +12,7 @@ import { ProjectorInterface } from '../Projection/ProjectorInterface'
import { Role } from '../Domain/Role/Role'
import { SettingServiceInterface } from '../Domain/Setting/SettingServiceInterface'
import { Setting } from '../Domain/Setting/Setting'
import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/auth'
import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/security'
import { GetUserAnalyticsId } from '../Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
describe('SubscriptionTokensController', () => {
@@ -1,4 +1,4 @@
import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/auth'
import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/security'
import { ErrorTag, RoleName } from '@standardnotes/common'
import { SettingName } from '@standardnotes/settings'
import { Request, Response } from 'express'
@@ -1,6 +1,6 @@
import 'reflect-metadata'
import { SessionTokenData, TokenEncoderInterface } from '@standardnotes/auth'
import { SessionTokenData, TokenEncoderInterface } from '@standardnotes/security'
import { Logger } from 'winston'
import { ProjectorInterface } from '../../Projection/ProjectorInterface'
@@ -1,4 +1,4 @@
import { SessionTokenData, TokenEncoderInterface } from '@standardnotes/auth'
import { SessionTokenData, TokenEncoderInterface } from '@standardnotes/security'
import { Uuid } from '@standardnotes/common'
import * as crypto from 'crypto'
@@ -1,4 +1,4 @@
import { SessionTokenData, TokenEncoderInterface } from '@standardnotes/auth'
import { SessionTokenData, TokenEncoderInterface } from '@standardnotes/security'
import 'reflect-metadata'
import { Logger } from 'winston'
@@ -1,6 +1,6 @@
import 'reflect-metadata'
import { SessionTokenData, TokenEncoderInterface } from '@standardnotes/auth'
import { SessionTokenData, TokenEncoderInterface } from '@standardnotes/security'
import { SessionBody } from '@standardnotes/responses'
import { Logger } from 'winston'
@@ -2,7 +2,7 @@ import {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
SessionTokenData,
TokenEncoderInterface,
} from '@standardnotes/auth'
} from '@standardnotes/security'
import { Uuid } from '@standardnotes/common'
import { SessionBody } from '@standardnotes/responses'
import { inject, injectable } from 'inversify'
@@ -1,6 +1,6 @@
import 'reflect-metadata'
import { SessionTokenData, TokenDecoderInterface } from '@standardnotes/auth'
import { SessionTokenData, TokenDecoderInterface } from '@standardnotes/security'
import { RevokedSession } from '../Session/RevokedSession'
import { Session } from '../Session/Session'
@@ -1,4 +1,4 @@
import { SessionTokenData, TokenDecoderInterface } from '@standardnotes/auth'
import { SessionTokenData, TokenDecoderInterface } from '@standardnotes/security'
import { inject, injectable } from 'inversify'
import TYPES from '../../Bootstrap/Types'
import { SessionServiceInterface } from '../Session/SessionServiceInterface'
@@ -1,7 +1,7 @@
import 'reflect-metadata'
import { EmailMessageIdentifier, RoleName } from '@standardnotes/common'
import { PredicateName, PredicateAuthority, PredicateVerificationResult } from '@standardnotes/scheduler'
import { PredicateName, PredicateAuthority, PredicateVerificationResult } from '@standardnotes/predicates'
import { TimerInterface } from '@standardnotes/time'
import { DomainEventFactory } from './DomainEventFactory'
@@ -16,7 +16,7 @@ import {
DomainEventService,
EmailMessageRequestedEvent,
} from '@standardnotes/domain-events'
import { Predicate, PredicateVerificationResult } from '@standardnotes/scheduler'
import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates'
import { TimerInterface } from '@standardnotes/time'
import { inject, injectable } from 'inversify'
import TYPES from '../../Bootstrap/Types'
@@ -1,5 +1,5 @@
import { Uuid, RoleName, EmailMessageIdentifier } from '@standardnotes/common'
import { Predicate, PredicateVerificationResult } from '@standardnotes/scheduler'
import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates'
import {
AccountDeletionRequestedEvent,
CloudBackupRequestedEvent,
@@ -1,6 +1,6 @@
import 'reflect-metadata'
import { Role } from '@standardnotes/auth'
import { Role } from '@standardnotes/security'
import { RoleName, SubscriptionName } from '@standardnotes/common'
import { RoleToSubscriptionMapInterface } from '../Role/RoleToSubscriptionMapInterface'
@@ -1,6 +1,6 @@
import { DomainEventHandlerInterface, ExtensionKeyGrantedEvent } from '@standardnotes/domain-events'
import { SettingName } from '@standardnotes/settings'
import { OfflineFeaturesTokenData } from '@standardnotes/auth'
import { OfflineFeaturesTokenData } from '@standardnotes/security'
import { ContentDecoderInterface } from '@standardnotes/common'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
@@ -7,7 +7,7 @@ import {
PredicateVerificationRequestedEventPayload,
PredicateVerifiedEvent,
} from '@standardnotes/domain-events'
import { Predicate, PredicateVerificationResult } from '@standardnotes/scheduler'
import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates'
import { Logger } from 'winston'
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
@@ -1,4 +1,4 @@
import { OfflineFeaturesTokenData } from '@standardnotes/auth'
import { OfflineFeaturesTokenData } from '@standardnotes/security'
import { DomainEventHandlerInterface, SubscriptionSyncRequestedEvent } from '@standardnotes/domain-events'
import { inject, injectable } from 'inversify'
import { Logger } from 'winston'
@@ -1,6 +1,6 @@
import 'reflect-metadata'
import { TokenEncoderInterface, ValetTokenData } from '@standardnotes/auth'
import { TokenEncoderInterface, ValetTokenData } from '@standardnotes/security'
import { CreateValetToken } from './CreateValetToken'
import { TimerInterface } from '@standardnotes/time'
import { UserSubscription } from '../../Subscription/UserSubscription'
@@ -1,7 +1,7 @@
import { inject, injectable } from 'inversify'
import { SubscriptionName } from '@standardnotes/common'
import { TimerInterface } from '@standardnotes/time'
import { TokenEncoderInterface, ValetTokenData } from '@standardnotes/auth'
import { TokenEncoderInterface, ValetTokenData } from '@standardnotes/security'
import { CreateValetTokenPayload, CreateValetTokenResponseData } from '@standardnotes/responses'
import { SubscriptionSettingName } from '@standardnotes/settings'
@@ -7,7 +7,7 @@ import { VerifyMFA } from './VerifyMFA'
import { Setting } from '../Setting/Setting'
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
import { SettingName } from '@standardnotes/settings'
import { SelectorInterface } from '@standardnotes/auth'
import { SelectorInterface } from '@standardnotes/security'
import { LockRepositoryInterface } from '../User/LockRepositoryInterface'
describe('VerifyMFA', () => {
@@ -12,7 +12,7 @@ import { UseCaseInterface } from './UseCaseInterface'
import { VerifyMFADTO } from './VerifyMFADTO'
import { VerifyMFAResponse } from './VerifyMFAResponse'
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
import { SelectorInterface } from '@standardnotes/auth'
import { SelectorInterface } from '@standardnotes/security'
import { LockRepositoryInterface } from '../User/LockRepositoryInterface'
@injectable()
@@ -1,6 +1,6 @@
import 'reflect-metadata'
import { PredicateName, PredicateVerificationResult, PredicateAuthority } from '@standardnotes/scheduler'
import { PredicateName, PredicateVerificationResult, PredicateAuthority } from '@standardnotes/predicates'
import { Setting } from '../../Setting/Setting'
import { SettingRepositoryInterface } from '../../Setting/SettingRepositoryInterface'
@@ -1,5 +1,5 @@
import { Uuid } from '@standardnotes/common'
import { PredicateName, PredicateVerificationResult } from '@standardnotes/scheduler'
import { PredicateName, PredicateVerificationResult } from '@standardnotes/predicates'
import { EmailBackupFrequency, SettingName } from '@standardnotes/settings'
import { inject, injectable } from 'inversify'
@@ -1,5 +1,5 @@
import { Uuid } from '@standardnotes/common'
import { Predicate } from '@standardnotes/scheduler'
import { Predicate } from '@standardnotes/predicates'
export type VerifyPredicateDTO = {
predicate: Predicate
@@ -1,4 +1,4 @@
import { PredicateVerificationResult } from '@standardnotes/scheduler'
import { PredicateVerificationResult } from '@standardnotes/predicates'
export type VerifyPredicateResponse = {
predicateVerificationResult: PredicateVerificationResult
@@ -1,6 +1,6 @@
import 'reflect-metadata'
import { SelectorInterface } from '@standardnotes/auth'
import { SelectorInterface } from '@standardnotes/security'
import { ProtocolVersion } from '@standardnotes/common'
import { KeyParamsFactory } from './KeyParamsFactory'
@@ -6,7 +6,7 @@ import { inject, injectable } from 'inversify'
import TYPES from '../../Bootstrap/Types'
import { KeyParamsFactoryInterface } from './KeyParamsFactoryInterface'
import { User } from './User'
import { SelectorInterface } from '@standardnotes/auth'
import { SelectorInterface } from '@standardnotes/security'
@injectable()
export class KeyParamsFactory implements KeyParamsFactoryInterface {
+1
View File
@@ -0,0 +1 @@
dist
+6
View File
@@ -0,0 +1,6 @@
{
"extends": "../../.eslintrc",
"parserOptions": {
"project": "./linter.tsconfig.json"
}
}
+264
View File
@@ -0,0 +1,264 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# 1.25.0 (2022-07-06)
### Features
* add common package ([fd4ee21](https://github.com/standardnotes/server/commit/fd4ee2123dc72b4d8755504d57bced608c1ab928))
## [1.23.2](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.23.1...@standardnotes/common@1.23.2) (2022-07-04)
### Bug Fixes
* add missing reflect-metadata package ([70aa494](https://github.com/standardnotes/snjs/commit/70aa4943a3fde70d4360055db22cb5388fc3f76e))
## [1.23.1](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.23.0...@standardnotes/common@1.23.1) (2022-06-27)
### Bug Fixes
* add email message identifier for files email campaign ([affa300](https://github.com/standardnotes/snjs/commit/affa3005b6933165e262227282198310f0a65cb3))
# [1.23.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.22.0...@standardnotes/common@1.23.0) (2022-06-15)
### Features
* add email requested events ([4d501fa](https://github.com/standardnotes/snjs/commit/4d501faedad44ff23782db1704956a6c19365fb6))
# [1.22.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.21.0...@standardnotes/common@1.22.0) (2022-05-22)
### Features
* optional files navigation ([#745](https://github.com/standardnotes/snjs/issues/745)) ([8512166](https://github.com/standardnotes/snjs/commit/851216615478b57b11a570173f94ee598bec31c0))
# [1.21.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.20.1...@standardnotes/common@1.21.0) (2022-05-17)
### Features
* remove basic user role and core subscription plan ([#741](https://github.com/standardnotes/snjs/issues/741)) ([7800ecd](https://github.com/standardnotes/snjs/commit/7800ecd119e7bbb5872d48bd7806b5d0f5522c0e))
## [1.20.1](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.20.0...@standardnotes/common@1.20.1) (2022-05-16)
**Note:** Version bump only for package @standardnotes/common
# [1.20.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.19.8...@standardnotes/common@1.20.0) (2022-05-16)
### Features
* remove basic user role and core subscription plan names ([304e232](https://github.com/standardnotes/snjs/commit/304e232e738456a93374de869117b5579e8a8f57))
## [1.19.8](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.19.6...@standardnotes/common@1.19.8) (2022-05-04)
**Note:** Version bump only for package @standardnotes/common
## [1.19.7](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.19.6...@standardnotes/common@1.19.7) (2022-05-04)
**Note:** Version bump only for package @standardnotes/common
## [1.19.6](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.19.5...@standardnotes/common@1.19.6) (2022-04-22)
**Note:** Version bump only for package @standardnotes/common
## [1.19.5](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.19.4...@standardnotes/common@1.19.5) (2022-04-21)
### Bug Fixes
* abort key recovery after aborted challenge ([#703](https://github.com/standardnotes/snjs/issues/703)) ([a67fb7e](https://github.com/standardnotes/snjs/commit/a67fb7e8cde41a5c9fadf545933e35d525faeaf0))
## [1.19.4](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.19.3...@standardnotes/common@1.19.4) (2022-04-15)
**Note:** Version bump only for package @standardnotes/common
## [1.19.3](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.19.2...@standardnotes/common@1.19.3) (2022-04-11)
**Note:** Version bump only for package @standardnotes/common
## [1.19.2](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.19.1...@standardnotes/common@1.19.2) (2022-04-01)
**Note:** Version bump only for package @standardnotes/common
## [1.19.1](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.19.0...@standardnotes/common@1.19.1) (2022-03-31)
**Note:** Version bump only for package @standardnotes/common
# [1.19.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.18.0...@standardnotes/common@1.19.0) (2022-03-31)
### Features
* encryption and models packages ([#679](https://github.com/standardnotes/snjs/issues/679)) ([5e03d48](https://github.com/standardnotes/snjs/commit/5e03d48aba7e3dd266117201139ab869b1f70cc9))
# [1.18.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.17.0...@standardnotes/common@1.18.0) (2022-03-30)
### Features
* files-beta role ([#678](https://github.com/standardnotes/snjs/issues/678)) ([f1ae62f](https://github.com/standardnotes/snjs/commit/f1ae62ff05e361dc551f1a0d047feabd129d0f76))
# [1.17.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.16.2...@standardnotes/common@1.17.0) (2022-03-22)
### Features
* add read only access error tag ([c3c7b4c](https://github.com/standardnotes/snjs/commit/c3c7b4c12f9b23dfc8e4bf1d4af43f6307f64190))
## [1.16.2](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.16.1...@standardnotes/common@1.16.2) (2022-03-21)
**Note:** Version bump only for package @standardnotes/common
## [1.16.1](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.16.0...@standardnotes/common@1.16.1) (2022-03-18)
### Bug Fixes
* add demo user role ([aa7ebc8](https://github.com/standardnotes/snjs/commit/aa7ebc84ef1160688d2767b86a32719ce68a257b))
# [1.16.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.15.3...@standardnotes/common@1.16.0) (2022-03-16)
### Features
* delete file functionality ([#657](https://github.com/standardnotes/snjs/issues/657)) ([edec4f7](https://github.com/standardnotes/snjs/commit/edec4f7a65ef557ed5f47be4dddcf2b659ee28b4))
## [1.15.4](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.15.3...@standardnotes/common@1.15.4) (2022-03-16)
**Note:** Version bump only for package @standardnotes/common
## [1.15.3](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.15.1...@standardnotes/common@1.15.3) (2022-02-28)
### Bug Fixes
* add pseudo change to get lerna to trigger ([41e6817](https://github.com/standardnotes/snjs/commit/41e6817bbf726b0932cdf16f58622328b9e42803))
## [1.15.2](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.15.1...@standardnotes/common@1.15.2) (2022-02-28)
### Bug Fixes
* add pseudo change to get lerna to trigger ([41e6817](https://github.com/standardnotes/snjs/commit/41e6817bbf726b0932cdf16f58622328b9e42803))
## [1.15.1](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.15.0...@standardnotes/common@1.15.1) (2022-02-27)
**Note:** Version bump only for package @standardnotes/common
# [1.15.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.14.1...@standardnotes/common@1.15.0) (2022-02-25)
### Features
* extract core functionalities to separate packages ([#610](https://github.com/standardnotes/snjs/issues/610)) ([801547a](https://github.com/standardnotes/snjs/commit/801547a71614ad51a92fb249eaa184ed46a44aac))
## [1.14.1](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.14.0...@standardnotes/common@1.14.1) (2022-02-24)
**Note:** Version bump only for package @standardnotes/common
# [1.14.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.13.0...@standardnotes/common@1.14.0) (2022-02-22)
### Features
* extract services package ([#605](https://github.com/standardnotes/snjs/issues/605)) ([3966b10](https://github.com/standardnotes/snjs/commit/3966b10745c10ef5bb92871abb13ceb4ea631362))
# [1.13.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.12.0...@standardnotes/common@1.13.0) (2022-02-22)
### Features
* extract SNJS utils as a separate package ([#604](https://github.com/standardnotes/snjs/issues/604)) ([b28195c](https://github.com/standardnotes/snjs/commit/b28195c20be788eec8dabc44c5aff518f074cdd9))
# [1.12.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.11.0...@standardnotes/common@1.12.0) (2022-02-18)
### Features
* add item integrity hash model ([975474a](https://github.com/standardnotes/snjs/commit/975474a04c4b11edea381235fe38273db59fa770))
# [1.11.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.10.0...@standardnotes/common@1.11.0) (2022-02-16)
### Features
* add paid roles definition ([3432608](https://github.com/standardnotes/snjs/commit/34326086c16000397d994054e807dd3589b536db))
# [1.10.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.9.0...@standardnotes/common@1.10.0) (2022-02-10)
### Features
* move role names and suscription names from auth to common package ([5358c03](https://github.com/standardnotes/snjs/commit/5358c03ef113597bcdcf7b0f3e730c8014885a0f))
# [1.9.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.8.0...@standardnotes/common@1.9.0) (2022-02-07)
### Features
* dynamic features based on runtime env ([#590](https://github.com/standardnotes/snjs/issues/590)) ([060861a](https://github.com/standardnotes/snjs/commit/060861a6c5bc179e3e1987c2b63490888e153bbb))
# [1.8.0](https://github.com/standardnotes/snjs/compare/@standardnotes/common@1.7.0...@standardnotes/common@1.8.0) (2022-01-15)
### Bug Fixes
* correct gitignore paths ([cefc0cf](https://github.com/standardnotes/snjs/commit/cefc0cfcf98e3e5378e055b8c46931b53b23195e))
* include dist in static components ([d17ce0f](https://github.com/standardnotes/snjs/commit/d17ce0f67045c6e4c97bf4577709aa58794e72e6))
### Features
* remove server extension type ([#552](https://github.com/standardnotes/snjs/issues/552)) ([aa542f3](https://github.com/standardnotes/snjs/commit/aa542f3124c60fa81a0b271030b3c35415c54a62))
# 1.7.0 (2021-12-23)
### Features
* rename email backup setting to email backup frequency ([25e7b46](https://github.com/standardnotes/snjs/commit/25e7b4620834711ac7f513ae893898c5eab1af53))
## 1.6.3 (2021-12-23)
### Bug Fixes
* lock package versions ([8aa2ce6](https://github.com/standardnotes/snjs/commit/8aa2ce676b57598ab72840adf851869d8e769022))
## 1.6.2 (2021-12-23)
### Bug Fixes
* add publishing from package version by lerna ([80433d0](https://github.com/standardnotes/snjs/commit/80433d044f258095753482b8322d73aba3d9a9e4))
## 1.6.1 (2021-12-23)
### Bug Fixes
* remove the ammend commit from lerna versioning ([f0400d9](https://github.com/standardnotes/snjs/commit/f0400d9a2f5a04eaece2e4c16da71166a2ddb251))
# 1.6.0 (2021-12-23)
### Features
* add one drive backup frequency setting ([#522](https://github.com/standardnotes/snjs/issues/522)) ([c27827f](https://github.com/standardnotes/snjs/commit/c27827f8c7969dd32511c9c75122ece372132c83))
## 1.5.4 (2021-12-23)
### Bug Fixes
* remove running tests upon deployment - ensured on PR status checks ([#523](https://github.com/standardnotes/snjs/issues/523)) ([5c795d1](https://github.com/standardnotes/snjs/commit/5c795d17b583d02955773576384e622c3ef7f418))
## 1.5.3 (2021-12-23)
### Bug Fixes
* pr template ([#518](https://github.com/standardnotes/snjs/issues/518)) ([b445bb6](https://github.com/standardnotes/snjs/commit/b445bb64841217ae27c2514887629235be95d2a3))
## 1.5.2 (2021-12-23)
### Bug Fixes
* checkout with personal access token ([773c1ef](https://github.com/standardnotes/snjs/commit/773c1ef91c4452ad411e928342060dcb59428e3c))
## 1.5.1 (2021-12-22)
### Bug Fixes
* gpg signing with CI StandardNotes user ([d72f61c](https://github.com/standardnotes/snjs/commit/d72f61c23cd15b31d37340cc756d16526634b9ee))
# 1.5.0 (2021-12-22)
### Bug Fixes
* versioning and package dependencies ([#509](https://github.com/standardnotes/snjs/issues/509)) ([fe1df94](https://github.com/standardnotes/snjs/commit/fe1df94eff3e90bcf9ba0cf45bdc44ac49204c71))
### Features
* add content decoder to common package ([504cf10](https://github.com/standardnotes/snjs/commit/504cf10d83c9cba6e8ee79ce138847a293a2f9e0))
* add ContentType to common package ([#401](https://github.com/standardnotes/snjs/issues/401)) ([1152c02](https://github.com/standardnotes/snjs/commit/1152c020e30e60996b4830b66e07ec4183bbac24))
* add SN|Privileges to content types ([#444](https://github.com/standardnotes/snjs/issues/444)) ([0eee358](https://github.com/standardnotes/snjs/commit/0eee3581e5f9f41f227c824adc92a0e15b8fa4b4))
* extract settings and common package ([#372](https://github.com/standardnotes/snjs/issues/372)) ([4f89688](https://github.com/standardnotes/snjs/commit/4f89688054cdae88c001287c9fb3431debd0136c))
* remove legacy mfa ([#495](https://github.com/standardnotes/snjs/issues/495)) ([b0498f4](https://github.com/standardnotes/snjs/commit/b0498f4fad85367e1b57c6deacb5d313331bf8db))
* upgrade node engine versions to latest active LTS ([#462](https://github.com/standardnotes/snjs/issues/462)) ([686fc15](https://github.com/standardnotes/snjs/commit/686fc15030d302b474ebb7ef1cd4dcc48ec42359))
+19
View File
@@ -0,0 +1,19 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const base = require('../../jest.config');
module.exports = {
...base,
globals: {
'ts-jest': {
tsconfig: 'tsconfig.json',
},
},
coverageThreshold: {
global: {
branches: 14,
functions: 13,
lines: 14,
statements: 14
}
}
};
+4
View File
@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["dist"]
}
+39
View File
@@ -0,0 +1,39 @@
{
"name": "@standardnotes/common",
"version": "1.25.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
"description": "Common types and utilities for Standard Notes projects",
"main": "dist/src/index.js",
"author": "Standard Notes",
"types": "dist/src/index.d.ts",
"files": [
"dist/src/**/*.js",
"dist/src/**/*.d.ts"
],
"publishConfig": {
"access": "public"
},
"license": "AGPL-3.0-or-later",
"scripts": {
"clean": "rm -fr dist",
"prestart": "yarn clean",
"start": "tsc -p tsconfig.json --watch",
"prebuild": "yarn clean",
"build": "tsc -p tsconfig.json",
"lint": "eslint . --ext .ts",
"test:unit": "jest spec --coverage"
},
"devDependencies": {
"@types/jest": "^27.4.1",
"@types/node": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.30.0",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^27.5.1",
"ts-jest": "^27.1.3"
},
"dependencies": {
"reflect-metadata": "^0.1.13"
}
}
@@ -0,0 +1,37 @@
import 'reflect-metadata'
import { ContentDecoder } from './ContentDecoder'
describe('ContentDecoder', () => {
const createDecoder = () => new ContentDecoder()
it('should decode content', () => {
const content = '000eyJmb28iOiJiYXIifQ=='
expect(createDecoder().decode(content)).toEqual({
foo: 'bar',
})
})
it('should decode content without padding', () => {
const content = 'eyJmb28iOiJiYXIifQ=='
expect(createDecoder().decode(content, 0)).toEqual({
foo: 'bar',
})
})
it('should encode content', () => {
expect(
createDecoder().encode({
foo: 'bar',
}),
).toEqual('000eyJmb28iOiJiYXIifQ==')
})
it('should return empty object on decoding failure', () => {
const content = '032400eyJmb28iOiJiYXIifQ=='
expect(createDecoder().decode(content)).toEqual({})
})
})
@@ -0,0 +1,23 @@
import { ContentDecoderInterface } from './ContentDecoderInterface'
export class ContentDecoder implements ContentDecoderInterface {
decode(content: string, leftPaddingLength = 3): Record<string, unknown> {
try {
const contentToDecode = leftPaddingLength > 0 ? content.substring(leftPaddingLength) : content
const contentBuffer = Buffer.from(contentToDecode, 'base64')
const decodedContent = contentBuffer.toString()
return JSON.parse(decodedContent)
} catch (error) {
return {}
}
}
encode(content: Record<string, unknown>, leftPaddingLength = 3): string | undefined {
const stringifiedContent = JSON.stringify(content)
const encodedContent = Buffer.from(stringifiedContent).toString('base64')
return encodedContent.padStart(encodedContent.length + leftPaddingLength, '0')
}
}
@@ -0,0 +1,4 @@
export interface ContentDecoderInterface {
decode(content: string, leftPaddingLength?: number): Record<string, unknown>
encode(content: Record<string, unknown>, leftPaddingLength?: number): string | undefined
}
@@ -0,0 +1,44 @@
/* istanbul ignore file */
export enum ContentType {
Any = '*',
Item = 'SF|Item',
RootKey = 'SN|RootKey|NoSync',
ItemsKey = 'SN|ItemsKey',
EncryptedStorage = 'SN|EncryptedStorage',
Privileges = 'SN|Privileges',
Note = 'Note',
Tag = 'Tag',
SmartView = 'SN|SmartTag',
Component = 'SN|Component',
Editor = 'SN|Editor',
ActionsExtension = 'Extension',
UserPrefs = 'SN|UserPreferences',
HistorySession = 'SN|HistorySession',
Theme = 'SN|Theme',
File = 'SN|File',
FilesafeCredentials = 'SN|FileSafe|Credentials',
FilesafeFileMetadata = 'SN|FileSafe|FileMetadata',
FilesafeIntegration = 'SN|FileSafe|Integration',
ExtensionRepo = 'SN|ExtensionRepo',
Unknown = 'Unknown',
}
export function DisplayStringForContentType(contentType: ContentType): string | undefined {
const map: Partial<Record<ContentType, string>> = {
[ContentType.ActionsExtension]: 'action-based extension',
[ContentType.Component]: 'component',
[ContentType.Editor]: 'editor',
[ContentType.File]: 'file',
[ContentType.FilesafeCredentials]: 'FileSafe credential',
[ContentType.FilesafeFileMetadata]: 'FileSafe file',
[ContentType.FilesafeIntegration]: 'FileSafe integration',
[ContentType.ItemsKey]: 'encryption key',
[ContentType.Note]: 'note',
[ContentType.SmartView]: 'smart view',
[ContentType.Tag]: 'tag',
[ContentType.Theme]: 'theme',
[ContentType.UserPrefs]: 'user preferences',
}
return map[contentType]
}
@@ -0,0 +1,2 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type AnyRecord = Partial<Record<string, any>>
@@ -0,0 +1 @@
export type ApplicationIdentifier = string
@@ -0,0 +1 @@
export type MicrosecondsTimestamp = number
@@ -0,0 +1 @@
export type Uuid = string
@@ -0,0 +1,18 @@
export enum EmailMessageIdentifier {
WELCOME_EMAIL = 'WELCOME_EMAIL',
ACCOUNT_CLAIM = 'ACCOUNT_CLAIM',
ACTIVATION_CODE = 'ACTIVATION_CODE',
VERSION_ADOPTION_REPORT = 'VERSION_ADOPTION_REPORT',
FAILED_DROPBOX_BACKUP = 'FAILED_DROPBOX_BACKUP',
FAILED_GOOGLE_DRIVE_BACKUP = 'FAILED_GOOGLE_DRIVE_BACKUP',
FAILED_ONE_DRIVE_BACKUP = 'FAILED_ONE_DRIVE_BACKUP',
DATA_BACKUP = 'DATA_BACKUP',
FAILED_BACKUP_ATTACHMENT_TOO_BIG = 'FAILED_BACKUP_ATTACHMENT_TOO_BIG',
OFFLINE_SUBSCRIPTION_ACCESS = 'OFFLINE_SUBSCRIPTION_ACCESS',
SIGN_IN = 'SIGN_IN',
SHARED_SUBSCRIPTION_INVITATION = 'SHARED_SUBSCRIPTION_INVITATION',
ENCOURAGE_EMAIL_BACKUPS = 'ENCOURAGE_EMAIL_BACKUPS',
ENCOURAGE_SUBSCRIPTION_PURCHASING = 'ENCOURAGE_SUBSCRIPTION_PURCHASING',
EXIT_INTERVIEW = 'EXIT_INTERVIEW',
MARKETING_CAMPAIGN_FILES = 'MARKETING_CAMPAIGN_FILES',
}
@@ -0,0 +1,12 @@
/* istanbul ignore file */
export enum ErrorTag {
MfaInvalid = 'mfa-invalid',
MfaRequired = 'mfa-required',
RefreshTokenInvalid = 'invalid-refresh-token',
RefreshTokenExpired = 'expired-refresh-token',
AccessTokenExpired = 'expired-access-token',
ParametersInvalid = 'invalid-parameters',
RevokedSession = 'revoked-session',
AuthInvalid = 'invalid-auth',
ReadOnlyAccess = 'read-only-access',
}
@@ -0,0 +1,6 @@
import { KeyParamsContent001 } from './KeyParamsContent001'
import { KeyParamsContent002 } from './KeyParamsContent002'
import { KeyParamsContent003 } from './KeyParamsContent003'
import { KeyParamsContent004 } from './KeyParamsContent004'
export type AnyKeyParamsContent = KeyParamsContent001 | KeyParamsContent002 | KeyParamsContent003 | KeyParamsContent004
@@ -0,0 +1,10 @@
import { ProtocolVersion } from '../Protocol/ProtocolVersion'
import { KeyParamsOrigination } from './KeyParamsOrigination'
export type BaseKeyParams = {
/** Seconds since creation date */
created?: string
/** The event that lead to the creation of these params */
origination?: KeyParamsOrigination
version: ProtocolVersion
}
@@ -0,0 +1,8 @@
import { BaseKeyParams } from './BaseKeyParams'
export type KeyParamsContent001 = BaseKeyParams & {
email: string
pw_cost: number
pw_salt: string
pw_nonce: string
}
@@ -0,0 +1,8 @@
import { BaseKeyParams } from './BaseKeyParams'
export type KeyParamsContent002 = BaseKeyParams & {
email: string
pw_cost: number
pw_salt: string
pw_nonce: string
}
@@ -0,0 +1,6 @@
import { BaseKeyParams } from './BaseKeyParams'
export type KeyParamsContent003 = BaseKeyParams & {
identifier: string
pw_nonce: string
}
@@ -0,0 +1,6 @@
import { BaseKeyParams } from './BaseKeyParams'
export type KeyParamsContent004 = Required<BaseKeyParams> & {
identifier: string
pw_nonce: string
}
@@ -0,0 +1,8 @@
export enum KeyParamsOrigination {
Registration = 'registration',
EmailChange = 'email-change',
PasswordChange = 'password-change',
ProtocolUpgrade = 'protocol-upgrade',
PasscodeCreate = 'passcode-create',
PasscodeChange = 'passcode-change',
}
@@ -0,0 +1,56 @@
export enum ProtocolVersion {
V001 = '001',
V002 = '002',
V003 = '003',
V004 = '004',
}
export const ProtocolVersionLatest = ProtocolVersion.V004
/** The last protocol version to not use root-key based items keys */
export const ProtocolVersionLastNonrootItemsKey = ProtocolVersion.V003
export const ProtocolExpirationDates: Partial<Record<ProtocolVersion, number>> = Object.freeze({
[ProtocolVersion.V001]: Date.parse('2018-01-01'),
[ProtocolVersion.V002]: Date.parse('2020-01-01'),
})
export function isProtocolVersionExpired(version: ProtocolVersion) {
const expireDate = ProtocolExpirationDates[version]
if (!expireDate) {
return false
}
const expired = new Date().getTime() > expireDate
return expired
}
export const ProtocolVersionLength = 3
export function protocolVersionFromEncryptedString(string: string): ProtocolVersion {
const version = string.substring(0, ProtocolVersionLength) as ProtocolVersion
if (Object.values(ProtocolVersion).includes(version)) {
return version
}
throw Error(`Unrecognized protocol version ${version}`)
}
/**
* -1 if a < b
* 0 if a == b
* 1 if a > b
*/
export function compareVersions(a: ProtocolVersion, b: ProtocolVersion): number {
const aNum = Number(a)
const bNum = Number(b)
return aNum - bNum
}
export function leftVersionGreaterThanOrEqualToRight(a: ProtocolVersion, b: ProtocolVersion): boolean {
return compareVersions(a, b) >= 0
}
export function isVersionLessThanOrEqualTo(input: ProtocolVersion, compareTo: ProtocolVersion): boolean {
return compareVersions(input, compareTo) <= 0
}
@@ -0,0 +1,4 @@
/* istanbul ignore file */
import { RoleName } from './RoleName'
export const PaidRoles = [RoleName.CoreUser, RoleName.PlusUser, RoleName.ProUser]
@@ -0,0 +1,7 @@
/* istanbul ignore file */
export enum RoleName {
CoreUser = 'CORE_USER',
PlusUser = 'PLUS_USER',
ProUser = 'PRO_USER',
FilesBetaUser = 'FILES_BETA_USER',
}

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