mirror of
https://github.com/standardnotes/server
synced 2026-01-28 11:02:23 -05:00
Compare commits
25 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7dc4670028 | ||
|
|
dc88e2413b | ||
|
|
b7f7c3f164 | ||
|
|
f7def38e20 | ||
|
|
cf49e1ff74 | ||
|
|
38de2d6b30 | ||
|
|
4b3de264ef | ||
|
|
4bb785c7f0 | ||
|
|
2fb904d2cb | ||
|
|
ee79347e27 | ||
|
|
3477c81d37 | ||
|
|
930789316c | ||
|
|
01a08eae58 | ||
|
|
d73c9833ab | ||
|
|
1841597405 | ||
|
|
8003e5ce43 | ||
|
|
d0023a6c92 | ||
|
|
a9293f6ce1 | ||
|
|
58c5b586a9 | ||
|
|
21d224da22 | ||
|
|
43d957c8d3 | ||
|
|
917fad510a | ||
|
|
269eef7ef3 | ||
|
|
b811f4527b | ||
|
|
67378e4535 |
51
.github/dependabot.yml
vendored
51
.github/dependabot.yml
vendored
@@ -9,134 +9,83 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/analytics"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/api-gateway"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/auth"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/common"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/domain-events"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/domain-events-infra"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/event-store"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/files"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/predicates"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/scheduler"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/security"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/settings"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/sncrypto-node"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/syncing-server"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/packages/time"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
reviewers:
|
||||
- "moughxyz"
|
||||
- "karolsojko"
|
||||
|
||||
@@ -3,6 +3,36 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.18.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.18.0...@standardnotes/analytics@1.18.1) (2022-08-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **analytics:** add payment success activity ([9307893](https://github.com/standardnotes/server/commit/930789316c2eec8227f26e75d4917796168f2d08))
|
||||
|
||||
# [1.18.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.17.2...@standardnotes/analytics@1.18.0) (2022-08-15)
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** add payment failed event handler ([58c5b58](https://github.com/standardnotes/server/commit/58c5b586a904cf1fd179cc28783a6ae7da688063))
|
||||
|
||||
## [1.17.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.17.1...@standardnotes/analytics@1.17.2) (2022-08-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **analytics:** quarterly calculations over time ([43d957c](https://github.com/standardnotes/server/commit/43d957c8d382b501e8101b51e30b33f18a4dd871))
|
||||
|
||||
## [1.17.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.17.0...@standardnotes/analytics@1.17.1) (2022-08-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **analytics:** expire bitop keys ([269eef7](https://github.com/standardnotes/server/commit/269eef7ef31343390c6909350bf1bfede94c24b3))
|
||||
|
||||
# [1.17.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.16.0...@standardnotes/analytics@1.17.0) (2022-08-15)
|
||||
|
||||
### Features
|
||||
|
||||
* **api-gateway:** add quarterly analytics ([67378e4](https://github.com/standardnotes/server/commit/67378e4535ef2760cfe3fe27256ffe117ee11a71))
|
||||
|
||||
# [1.16.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@1.15.0...@standardnotes/analytics@1.16.0) (2022-08-15)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "1.16.0",
|
||||
"version": "1.18.1",
|
||||
"engines": {
|
||||
"node": ">=14.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -11,4 +11,6 @@ export enum AnalyticsActivity {
|
||||
EmailUnbackedUpData = 'email-unbacked-up-data',
|
||||
EmailBackup = 'email-backup',
|
||||
LimitedDiscountOfferPurchased = 'limited-discount-offer-purchased',
|
||||
PaymentFailed = 'payment-failed',
|
||||
PaymentSuccess = 'payment-success',
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ describe('RedisAnalyticsStore', () => {
|
||||
redisClient.setbit = jest.fn()
|
||||
redisClient.getbit = jest.fn().mockReturnValue(1)
|
||||
redisClient.bitop = jest.fn()
|
||||
redisClient.expire = jest.fn()
|
||||
|
||||
periodKeyGenerator = {} as jest.Mocked<PeriodKeyGeneratorInterface>
|
||||
periodKeyGenerator.getPeriodKey = jest.fn().mockReturnValue('period-key')
|
||||
|
||||
@@ -15,13 +15,18 @@ export class RedisAnalyticsStore implements AnalyticsStoreInterface {
|
||||
throw new Error(`Unsuporrted period: ${period}`)
|
||||
}
|
||||
|
||||
const periodKeys = this.periodKeyGenerator.getDiscretePeriodKeys(Period.Last30Days)
|
||||
const periodKeys = this.periodKeyGenerator.getDiscretePeriodKeys(period)
|
||||
await this.redisClient.bitop(
|
||||
'OR',
|
||||
`bitmap:action:${activity}:timespan:${periodKeys[0]}-${periodKeys[periodKeys.length - 1]}`,
|
||||
...periodKeys.map((p) => `bitmap:action:${activity}:timespan:${p}`),
|
||||
)
|
||||
|
||||
await this.redisClient.expire(
|
||||
`bitmap:action:${activity}:timespan:${periodKeys[0]}-${periodKeys[periodKeys.length - 1]}`,
|
||||
3600,
|
||||
)
|
||||
|
||||
return this.redisClient.bitcount(
|
||||
`bitmap:action:${activity}:timespan:${periodKeys[0]}-${periodKeys[periodKeys.length - 1]}`,
|
||||
)
|
||||
@@ -31,11 +36,13 @@ export class RedisAnalyticsStore implements AnalyticsStoreInterface {
|
||||
activity: AnalyticsActivity,
|
||||
period: Period,
|
||||
): Promise<Array<{ periodKey: string; totalCount: number }>> {
|
||||
if (period !== Period.Last30Days) {
|
||||
if (
|
||||
![Period.Last30Days, Period.Q1ThisYear, Period.Q2ThisYear, Period.Q3ThisYear, Period.Q4ThisYear].includes(period)
|
||||
) {
|
||||
throw new Error(`Unsuporrted period: ${period}`)
|
||||
}
|
||||
|
||||
const periodKeys = this.periodKeyGenerator.getDiscretePeriodKeys(Period.Last30Days)
|
||||
const periodKeys = this.periodKeyGenerator.getDiscretePeriodKeys(period)
|
||||
const counts = []
|
||||
for (const periodKey of periodKeys) {
|
||||
counts.push({
|
||||
@@ -105,6 +112,8 @@ export class RedisAnalyticsStore implements AnalyticsStoreInterface {
|
||||
`bitmap:action:${activity}:timespan:${subsequentPeriodKey}`,
|
||||
)
|
||||
|
||||
await this.redisClient.expire(diffKey, 3600)
|
||||
|
||||
const retainedTotalInActivity = await this.redisClient.bitcount(diffKey)
|
||||
|
||||
const initialTotalInActivity = await this.redisClient.bitcount(
|
||||
|
||||
@@ -3,6 +3,46 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.15.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.2...@standardnotes/api-gateway@1.15.3) (2022-08-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api-gateway:** add error logs on missing connection id for websockets ([f7def38](https://github.com/standardnotes/api-gateway/commit/f7def38e20f87ae24ebc736a41bc7cac53b0c61f))
|
||||
|
||||
## [1.15.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.1...@standardnotes/api-gateway@1.15.2) (2022-08-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api-gateway:** add payment success events to report ([ee79347](https://github.com/standardnotes/api-gateway/commit/ee79347e27f5887def2cda57091a7c0a40570d33))
|
||||
|
||||
## [1.15.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.15.0...@standardnotes/api-gateway@1.15.1) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
# [1.15.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.14.3...@standardnotes/api-gateway@1.15.0) (2022-08-15)
|
||||
|
||||
### Features
|
||||
|
||||
* **api-gateway:** add gathering analytics for failed payments ([d0023a6](https://github.com/standardnotes/api-gateway/commit/d0023a6c92756c81b8daa9089d38141b6cd4fe48))
|
||||
|
||||
## [1.14.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.14.2...@standardnotes/api-gateway@1.14.3) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.14.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.14.1...@standardnotes/api-gateway@1.14.2) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.14.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.14.0...@standardnotes/api-gateway@1.14.1) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
# [1.14.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.13.1...@standardnotes/api-gateway@1.14.0) (2022-08-15)
|
||||
|
||||
### Features
|
||||
|
||||
* **api-gateway:** add quarterly analytics ([67378e4](https://github.com/standardnotes/api-gateway/commit/67378e4535ef2760cfe3fe27256ffe117ee11a71))
|
||||
|
||||
## [1.13.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.13.0...@standardnotes/api-gateway@1.13.1) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
@@ -19,6 +19,66 @@ const requestReport = async (
|
||||
statisticsStore: StatisticsStoreInterface,
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
): Promise<void> => {
|
||||
const analyticsOverTime = []
|
||||
|
||||
const thirtyDaysAnalyticsNames = [
|
||||
AnalyticsActivity.GeneralActivity,
|
||||
AnalyticsActivity.EditingItems,
|
||||
AnalyticsActivity.SubscriptionPurchased,
|
||||
AnalyticsActivity.Register,
|
||||
AnalyticsActivity.SubscriptionRenewed,
|
||||
AnalyticsActivity.DeleteAccount,
|
||||
AnalyticsActivity.SubscriptionCancelled,
|
||||
AnalyticsActivity.SubscriptionRefunded,
|
||||
]
|
||||
|
||||
for (const analyticsName of thirtyDaysAnalyticsNames) {
|
||||
analyticsOverTime.push({
|
||||
name: analyticsName,
|
||||
period: Period.Last30Days,
|
||||
counts: await analyticsStore.calculateActivityChangesTotalCount(analyticsName, Period.Last30Days),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCountOverTime(analyticsName, Period.Last30Days),
|
||||
})
|
||||
}
|
||||
|
||||
const quarterlyAnalyticsNames = [
|
||||
AnalyticsActivity.Register,
|
||||
AnalyticsActivity.SubscriptionPurchased,
|
||||
AnalyticsActivity.SubscriptionRenewed,
|
||||
]
|
||||
|
||||
for (const analyticsName of quarterlyAnalyticsNames) {
|
||||
for (const period of [Period.Q1ThisYear, Period.Q2ThisYear, Period.Q3ThisYear, Period.Q4ThisYear]) {
|
||||
analyticsOverTime.push({
|
||||
name: analyticsName,
|
||||
period: period,
|
||||
counts: await analyticsStore.calculateActivityChangesTotalCount(analyticsName, period),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCountOverTime(analyticsName, period),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const yesterdayActivityStatistics = []
|
||||
const yesterdayActivityNames = [
|
||||
AnalyticsActivity.EditingItems,
|
||||
AnalyticsActivity.LimitedDiscountOfferPurchased,
|
||||
AnalyticsActivity.GeneralActivity,
|
||||
AnalyticsActivity.PaymentFailed,
|
||||
AnalyticsActivity.PaymentSuccess,
|
||||
]
|
||||
|
||||
for (const activityName of yesterdayActivityNames) {
|
||||
yesterdayActivityStatistics.push({
|
||||
name: activityName,
|
||||
retention: await analyticsStore.calculateActivityRetention(
|
||||
activityName,
|
||||
Period.DayBeforeYesterday,
|
||||
Period.Yesterday,
|
||||
),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCount(activityName, Period.Yesterday),
|
||||
})
|
||||
}
|
||||
|
||||
const event: DailyAnalyticsReportGeneratedEvent = {
|
||||
type: 'DAILY_ANALYTICS_REPORT_GENERATED',
|
||||
createdAt: new Date(),
|
||||
@@ -33,138 +93,8 @@ const requestReport = async (
|
||||
applicationStatistics: await statisticsStore.getYesterdayApplicationUsage(),
|
||||
snjsStatistics: await statisticsStore.getYesterdaySNJSUsage(),
|
||||
outOfSyncIncidents: await statisticsStore.getYesterdayOutOfSyncIncidents(),
|
||||
activityStatistics: [
|
||||
{
|
||||
name: AnalyticsActivity.EditingItems,
|
||||
retention: await analyticsStore.calculateActivityRetention(
|
||||
AnalyticsActivity.EditingItems,
|
||||
Period.DayBeforeYesterday,
|
||||
Period.Yesterday,
|
||||
),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCount(
|
||||
AnalyticsActivity.EditingItems,
|
||||
Period.Yesterday,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: AnalyticsActivity.LimitedDiscountOfferPurchased,
|
||||
retention: 0,
|
||||
totalCount: await analyticsStore.calculateActivityTotalCount(
|
||||
AnalyticsActivity.LimitedDiscountOfferPurchased,
|
||||
Period.Yesterday,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: AnalyticsActivity.GeneralActivity,
|
||||
retention: await analyticsStore.calculateActivityRetention(
|
||||
AnalyticsActivity.GeneralActivity,
|
||||
Period.DayBeforeYesterday,
|
||||
Period.Yesterday,
|
||||
),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCount(
|
||||
AnalyticsActivity.GeneralActivity,
|
||||
Period.Yesterday,
|
||||
),
|
||||
},
|
||||
],
|
||||
activityStatisticsOverTime: [
|
||||
{
|
||||
name: AnalyticsActivity.GeneralActivity,
|
||||
period: Period.Last30Days,
|
||||
counts: await analyticsStore.calculateActivityChangesTotalCount(
|
||||
AnalyticsActivity.GeneralActivity,
|
||||
Period.Last30Days,
|
||||
),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCountOverTime(
|
||||
AnalyticsActivity.GeneralActivity,
|
||||
Period.Last30Days,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: AnalyticsActivity.EditingItems,
|
||||
period: Period.Last30Days,
|
||||
counts: await analyticsStore.calculateActivityChangesTotalCount(
|
||||
AnalyticsActivity.EditingItems,
|
||||
Period.Last30Days,
|
||||
),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCountOverTime(
|
||||
AnalyticsActivity.EditingItems,
|
||||
Period.Last30Days,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: AnalyticsActivity.SubscriptionPurchased,
|
||||
period: Period.Last30Days,
|
||||
counts: await analyticsStore.calculateActivityChangesTotalCount(
|
||||
AnalyticsActivity.SubscriptionPurchased,
|
||||
Period.Last30Days,
|
||||
),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCountOverTime(
|
||||
AnalyticsActivity.SubscriptionPurchased,
|
||||
Period.Last30Days,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: AnalyticsActivity.Register,
|
||||
period: Period.Last30Days,
|
||||
counts: await analyticsStore.calculateActivityChangesTotalCount(
|
||||
AnalyticsActivity.Register,
|
||||
Period.Last30Days,
|
||||
),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCountOverTime(
|
||||
AnalyticsActivity.Register,
|
||||
Period.Last30Days,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: AnalyticsActivity.SubscriptionRenewed,
|
||||
period: Period.Last30Days,
|
||||
counts: await analyticsStore.calculateActivityChangesTotalCount(
|
||||
AnalyticsActivity.SubscriptionRenewed,
|
||||
Period.Last30Days,
|
||||
),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCountOverTime(
|
||||
AnalyticsActivity.SubscriptionRenewed,
|
||||
Period.Last30Days,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: AnalyticsActivity.DeleteAccount,
|
||||
period: Period.Last30Days,
|
||||
counts: await analyticsStore.calculateActivityChangesTotalCount(
|
||||
AnalyticsActivity.DeleteAccount,
|
||||
Period.Last30Days,
|
||||
),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCountOverTime(
|
||||
AnalyticsActivity.DeleteAccount,
|
||||
Period.Last30Days,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: AnalyticsActivity.SubscriptionCancelled,
|
||||
period: Period.Last30Days,
|
||||
counts: await analyticsStore.calculateActivityChangesTotalCount(
|
||||
AnalyticsActivity.SubscriptionCancelled,
|
||||
Period.Last30Days,
|
||||
),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCountOverTime(
|
||||
AnalyticsActivity.SubscriptionCancelled,
|
||||
Period.Last30Days,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: AnalyticsActivity.SubscriptionRefunded,
|
||||
period: Period.Last30Days,
|
||||
counts: await analyticsStore.calculateActivityChangesTotalCount(
|
||||
AnalyticsActivity.SubscriptionRefunded,
|
||||
Period.Last30Days,
|
||||
),
|
||||
totalCount: await analyticsStore.calculateActivityTotalCountOverTime(
|
||||
AnalyticsActivity.SubscriptionRefunded,
|
||||
Period.Last30Days,
|
||||
),
|
||||
},
|
||||
],
|
||||
activityStatistics: yesterdayActivityStatistics,
|
||||
activityStatisticsOverTime: analyticsOverTime,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.13.1",
|
||||
"version": "1.15.3",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import { BaseHttpController, controller, httpDelete, httpPost } from 'inversify-express-utils'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
|
||||
|
||||
@controller('/v1/sockets')
|
||||
export class WebSocketsController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
|
||||
constructor(
|
||||
@inject(TYPES.HTTPService) private httpService: HttpServiceInterface,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/', TYPES.AuthMiddleware)
|
||||
async createWebSocketConnection(request: Request, response: Response): Promise<void> {
|
||||
if (!request.headers.connectionid) {
|
||||
this.logger.error('Could not create a websocket connection. Missing connection id header.')
|
||||
|
||||
response.status(400).send('Missing connection id in the request')
|
||||
|
||||
return
|
||||
@@ -24,6 +31,8 @@ export class WebSocketsController extends BaseHttpController {
|
||||
@httpDelete('/')
|
||||
async deleteWebSocketConnection(request: Request, response: Response): Promise<void> {
|
||||
if (!request.headers.connectionid) {
|
||||
this.logger.error('Could not delete a websocket connection. Missing connection id header.')
|
||||
|
||||
response.status(400).send('Missing connection id in the request')
|
||||
|
||||
return
|
||||
|
||||
@@ -3,6 +3,46 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.20.1...@standardnotes/auth-server@1.21.0) (2022-08-29)
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** retain user agent, api version and received at on revoked sessions ([dc88e24](https://github.com/standardnotes/server/commit/dc88e2413b3be254b265d2874174eaac39d628ee))
|
||||
|
||||
## [1.20.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.20.0...@standardnotes/auth-server@1.20.1) (2022-08-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **scheduler:** change discount from 10% to 20% on a limited time offer ([4b3de26](https://github.com/standardnotes/server/commit/4b3de264efc4fffb2603181c158cddb25c4ed4a9))
|
||||
|
||||
# [1.20.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.19.1...@standardnotes/auth-server@1.20.0) (2022-08-15)
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** add payment success event handler ([01a08ea](https://github.com/standardnotes/server/commit/01a08eae582e070ec844f5e05f34260447b7d4c6))
|
||||
|
||||
## [1.19.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.19.0...@standardnotes/auth-server@1.19.1) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.19.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.18.4...@standardnotes/auth-server@1.19.0) (2022-08-15)
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** add payment failed event handler ([58c5b58](https://github.com/standardnotes/server/commit/58c5b586a904cf1fd179cc28783a6ae7da688063))
|
||||
|
||||
## [1.18.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.18.3...@standardnotes/auth-server@1.18.4) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.18.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.18.2...@standardnotes/auth-server@1.18.3) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.18.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.18.1...@standardnotes/auth-server@1.18.2) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.18.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.18.0...@standardnotes/auth-server@1.18.1) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class revokedSessionData1661771230400 implements MigrationInterface {
|
||||
name = 'revokedSessionData1661771230400'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `revoked_sessions` ADD `received_at` datetime NULL')
|
||||
await queryRunner.query('ALTER TABLE `revoked_sessions` ADD `user_agent` text NULL')
|
||||
await queryRunner.query('ALTER TABLE `revoked_sessions` ADD `api_version` varchar(255) NULL')
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.18.1",
|
||||
"version": "1.21.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -191,6 +191,8 @@ import { AuthController } from '../Controller/AuthController'
|
||||
import { VerifyPredicate } from '../Domain/UseCase/VerifyPredicate/VerifyPredicate'
|
||||
import { PredicateVerificationRequestedEventHandler } from '../Domain/Handler/PredicateVerificationRequestedEventHandler'
|
||||
import { MuteMarketingEmails } from '../Domain/UseCase/MuteMarketingEmails/MuteMarketingEmails'
|
||||
import { PaymentFailedEventHandler } from '../Domain/Handler/PaymentFailedEventHandler'
|
||||
import { PaymentSuccessEventHandler } from '../Domain/Handler/PaymentSuccessEventHandler'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const newrelicFormatter = require('@newrelic/winston-enricher')
|
||||
@@ -478,6 +480,8 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<PredicateVerificationRequestedEventHandler>(TYPES.PredicateVerificationRequestedEventHandler)
|
||||
.to(PredicateVerificationRequestedEventHandler)
|
||||
container.bind<PaymentFailedEventHandler>(TYPES.PaymentFailedEventHandler).to(PaymentFailedEventHandler)
|
||||
container.bind<PaymentSuccessEventHandler>(TYPES.PaymentSuccessEventHandler).to(PaymentSuccessEventHandler)
|
||||
|
||||
// Services
|
||||
container.bind<UAParser>(TYPES.DeviceDetector).toConstantValue(new UAParser())
|
||||
@@ -576,6 +580,8 @@ export class ContainerConfigLoader {
|
||||
],
|
||||
['SHARED_SUBSCRIPTION_INVITATION_CREATED', container.get(TYPES.SharedSubscriptionInvitationCreatedEventHandler)],
|
||||
['PREDICATE_VERIFICATION_REQUESTED', container.get(TYPES.PredicateVerificationRequestedEventHandler)],
|
||||
['PAYMENT_FAILED', container.get(TYPES.PaymentFailedEventHandler)],
|
||||
['PAYMENT_SUCCESS', container.get(TYPES.PaymentSuccessEventHandler)],
|
||||
])
|
||||
|
||||
if (env.get('SQS_QUEUE_URL', true)) {
|
||||
|
||||
@@ -143,6 +143,8 @@ const TYPES = {
|
||||
UserDisabledSessionUserAgentLoggingEventHandler: Symbol.for('UserDisabledSessionUserAgentLoggingEventHandler'),
|
||||
SharedSubscriptionInvitationCreatedEventHandler: Symbol.for('SharedSubscriptionInvitationCreatedEventHandler'),
|
||||
PredicateVerificationRequestedEventHandler: Symbol.for('PredicateVerificationRequestedEventHandler'),
|
||||
PaymentFailedEventHandler: Symbol.for('PaymentFailedEventHandler'),
|
||||
PaymentSuccessEventHandler: Symbol.for('PaymentSuccessEventHandler'),
|
||||
// Services
|
||||
DeviceDetector: Symbol.for('DeviceDetector'),
|
||||
SessionService: Symbol.for('SessionService'),
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { PaymentFailedEvent } from '@standardnotes/domain-events'
|
||||
import { AnalyticsStoreInterface } from '@standardnotes/analytics'
|
||||
|
||||
import { PaymentFailedEventHandler } from './PaymentFailedEventHandler'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { User } from '../User/User'
|
||||
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
|
||||
|
||||
describe('PaymentFailedEventHandler', () => {
|
||||
let userRepository: UserRepositoryInterface
|
||||
let event: PaymentFailedEvent
|
||||
let user: User
|
||||
let getUserAnalyticsId: GetUserAnalyticsId
|
||||
let analyticsStore: AnalyticsStoreInterface
|
||||
|
||||
const createHandler = () => new PaymentFailedEventHandler(userRepository, getUserAnalyticsId, analyticsStore)
|
||||
|
||||
beforeEach(() => {
|
||||
user = {} as jest.Mocked<User>
|
||||
|
||||
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
||||
userRepository.findOneByEmail = jest.fn().mockReturnValue(user)
|
||||
|
||||
getUserAnalyticsId = {} as jest.Mocked<GetUserAnalyticsId>
|
||||
getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 })
|
||||
|
||||
analyticsStore = {} as jest.Mocked<AnalyticsStoreInterface>
|
||||
analyticsStore.markActivity = jest.fn()
|
||||
|
||||
event = {} as jest.Mocked<PaymentFailedEvent>
|
||||
event.payload = {
|
||||
userEmail: 'test@test.com',
|
||||
}
|
||||
})
|
||||
|
||||
it('should mark payment failed for analytics', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(analyticsStore.markActivity).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not mark payment failed for analytics if user is not found', async () => {
|
||||
userRepository.findOneByEmail = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(analyticsStore.markActivity).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics'
|
||||
import { DomainEventHandlerInterface, PaymentFailedEvent } from '@standardnotes/domain-events'
|
||||
import { inject, injectable } from 'inversify'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
|
||||
@injectable()
|
||||
export class PaymentFailedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
@inject(TYPES.UserRepository) private userRepository: UserRepositoryInterface,
|
||||
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
|
||||
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
|
||||
) {}
|
||||
|
||||
async handle(event: PaymentFailedEvent): Promise<void> {
|
||||
const user = await this.userRepository.findOneByEmail(event.payload.userEmail)
|
||||
if (user === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid })
|
||||
await this.analyticsStore.markActivity([AnalyticsActivity.PaymentFailed], analyticsId, [
|
||||
Period.Today,
|
||||
Period.ThisWeek,
|
||||
Period.ThisMonth,
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { PaymentSuccessEvent } from '@standardnotes/domain-events'
|
||||
import { AnalyticsStoreInterface } from '@standardnotes/analytics'
|
||||
|
||||
import { PaymentSuccessEventHandler } from './PaymentSuccessEventHandler'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { User } from '../User/User'
|
||||
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
|
||||
|
||||
describe('PaymentSuccessEventHandler', () => {
|
||||
let userRepository: UserRepositoryInterface
|
||||
let event: PaymentSuccessEvent
|
||||
let user: User
|
||||
let getUserAnalyticsId: GetUserAnalyticsId
|
||||
let analyticsStore: AnalyticsStoreInterface
|
||||
|
||||
const createHandler = () => new PaymentSuccessEventHandler(userRepository, getUserAnalyticsId, analyticsStore)
|
||||
|
||||
beforeEach(() => {
|
||||
user = {} as jest.Mocked<User>
|
||||
|
||||
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
||||
userRepository.findOneByEmail = jest.fn().mockReturnValue(user)
|
||||
|
||||
getUserAnalyticsId = {} as jest.Mocked<GetUserAnalyticsId>
|
||||
getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 })
|
||||
|
||||
analyticsStore = {} as jest.Mocked<AnalyticsStoreInterface>
|
||||
analyticsStore.markActivity = jest.fn()
|
||||
|
||||
event = {} as jest.Mocked<PaymentSuccessEvent>
|
||||
event.payload = {
|
||||
userEmail: 'test@test.com',
|
||||
}
|
||||
})
|
||||
|
||||
it('should mark payment failed for analytics', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(analyticsStore.markActivity).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not mark payment failed for analytics if user is not found', async () => {
|
||||
userRepository.findOneByEmail = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(analyticsStore.markActivity).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics'
|
||||
import { DomainEventHandlerInterface, PaymentSuccessEvent } from '@standardnotes/domain-events'
|
||||
import { inject, injectable } from 'inversify'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
|
||||
@injectable()
|
||||
export class PaymentSuccessEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
@inject(TYPES.UserRepository) private userRepository: UserRepositoryInterface,
|
||||
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
|
||||
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
|
||||
) {}
|
||||
|
||||
async handle(event: PaymentSuccessEvent): Promise<void> {
|
||||
const user = await this.userRepository.findOneByEmail(event.payload.userEmail)
|
||||
if (user === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid })
|
||||
await this.analyticsStore.markActivity([AnalyticsActivity.PaymentSuccess], analyticsId, [
|
||||
Period.Today,
|
||||
Period.ThisWeek,
|
||||
Period.ThisMonth,
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
|
||||
Period.ThisMonth,
|
||||
])
|
||||
|
||||
const limitedDiscountPurchased = event.payload.discountCode === 'limited-10'
|
||||
const limitedDiscountPurchased = ['limited-10', 'limited-20'].includes(event.payload.discountCode as string)
|
||||
if (limitedDiscountPurchased) {
|
||||
await this.analyticsStore.markActivity([AnalyticsActivity.LimitedDiscountOfferPurchased], analyticsId, [
|
||||
Period.Today,
|
||||
|
||||
@@ -4,17 +4,23 @@ import { UserDisabledSessionUserAgentLoggingEvent } from '@standardnotes/domain-
|
||||
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
|
||||
|
||||
import { UserDisabledSessionUserAgentLoggingEventHandler } from './UserDisabledSessionUserAgentLoggingEventHandler'
|
||||
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
|
||||
|
||||
describe('UserDisabledSessionUserAgentLoggingEventHandler', () => {
|
||||
let sessionRepository: SessionRepositoryInterface
|
||||
let revokedSessionRepository: RevokedSessionRepositoryInterface
|
||||
let event: UserDisabledSessionUserAgentLoggingEvent
|
||||
|
||||
const createHandler = () => new UserDisabledSessionUserAgentLoggingEventHandler(sessionRepository)
|
||||
const createHandler = () =>
|
||||
new UserDisabledSessionUserAgentLoggingEventHandler(sessionRepository, revokedSessionRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
sessionRepository = {} as jest.Mocked<SessionRepositoryInterface>
|
||||
sessionRepository.clearUserAgentByUserUuid = jest.fn()
|
||||
|
||||
revokedSessionRepository = {} as jest.Mocked<RevokedSessionRepositoryInterface>
|
||||
revokedSessionRepository.clearUserAgentByUserUuid = jest.fn()
|
||||
|
||||
event = {} as jest.Mocked<UserDisabledSessionUserAgentLoggingEvent>
|
||||
event.payload = {
|
||||
userUuid: '1-2-3',
|
||||
@@ -26,5 +32,6 @@ describe('UserDisabledSessionUserAgentLoggingEventHandler', () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(sessionRepository.clearUserAgentByUserUuid).toHaveBeenCalledWith('1-2-3')
|
||||
expect(revokedSessionRepository.clearUserAgentByUserUuid).toHaveBeenCalledWith('1-2-3')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,13 +2,18 @@ import { DomainEventHandlerInterface, UserDisabledSessionUserAgentLoggingEvent }
|
||||
import { inject, injectable } from 'inversify'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
|
||||
import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
|
||||
|
||||
@injectable()
|
||||
export class UserDisabledSessionUserAgentLoggingEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(@inject(TYPES.SessionRepository) private sessionRepository: SessionRepositoryInterface) {}
|
||||
constructor(
|
||||
@inject(TYPES.SessionRepository) private sessionRepository: SessionRepositoryInterface,
|
||||
@inject(TYPES.SessionRepository) private revokedSessionRepository: RevokedSessionRepositoryInterface,
|
||||
) {}
|
||||
|
||||
async handle(event: UserDisabledSessionUserAgentLoggingEvent): Promise<void> {
|
||||
await this.sessionRepository.clearUserAgentByUserUuid(event.payload.userUuid)
|
||||
await this.revokedSessionRepository.clearUserAgentByUserUuid(event.payload.userUuid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,4 +36,25 @@ export class RevokedSession {
|
||||
)
|
||||
@JoinColumn({ name: 'user_uuid', referencedColumnName: 'uuid' })
|
||||
declare user: Promise<User>
|
||||
|
||||
@Column({
|
||||
name: 'received_at',
|
||||
type: 'datetime',
|
||||
nullable: true,
|
||||
})
|
||||
declare receivedAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'user_agent',
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare userAgent: string | null
|
||||
|
||||
@Column({
|
||||
name: 'api_version',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare apiVersion: string
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
import { RevokedSession } from './RevokedSession'
|
||||
|
||||
export interface RevokedSessionRepositoryInterface {
|
||||
@@ -5,4 +6,5 @@ export interface RevokedSessionRepositoryInterface {
|
||||
findAllByUserUuid(userUuid: string): Promise<Array<RevokedSession>>
|
||||
save(revokedSession: RevokedSession): Promise<RevokedSession>
|
||||
remove(revokedSession: RevokedSession): Promise<RevokedSession>
|
||||
clearUserAgentByUserUuid(userUuid: Uuid): Promise<void>
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'reflect-metadata'
|
||||
import * as winston from 'winston'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { ApiVersion } from '../Api/ApiVersion'
|
||||
import { Session } from './Session'
|
||||
import { SessionRepositoryInterface } from './SessionRepositoryInterface'
|
||||
import { SessionService } from './SessionService'
|
||||
@@ -47,6 +48,7 @@ describe('SessionService', () => {
|
||||
session.uuid = '2e1e43'
|
||||
session.userUuid = '1-2-3'
|
||||
session.userAgent = 'Chrome'
|
||||
session.apiVersion = ApiVersion.v20200115
|
||||
session.hashedAccessToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||
session.hashedRefreshToken = '4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce'
|
||||
|
||||
@@ -80,6 +82,7 @@ describe('SessionService', () => {
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.convertStringDateToMilliseconds = jest.fn().mockReturnValue(123)
|
||||
timer.getUTCDate = jest.fn().mockReturnValue(new Date(1))
|
||||
|
||||
deviceDetector = {} as jest.Mocked<UAParser>
|
||||
deviceDetector.setUA = jest.fn().mockReturnThis()
|
||||
@@ -111,6 +114,7 @@ describe('SessionService', () => {
|
||||
expect(revokedSessionRepository.save).toHaveBeenCalledWith({
|
||||
uuid: '2e1e43',
|
||||
received: true,
|
||||
receivedAt: new Date(1),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -476,6 +480,8 @@ describe('SessionService', () => {
|
||||
expect(revokedSessionRepository.save).toHaveBeenCalledWith({
|
||||
uuid: '2e1e43',
|
||||
userUuid: '1-2-3',
|
||||
userAgent: 'Chrome',
|
||||
apiVersion: '20200115',
|
||||
createdAt: expect.any(Date),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -203,6 +203,7 @@ export class SessionService implements SessionServiceInterface {
|
||||
|
||||
async markRevokedSessionAsReceived(revokedSession: RevokedSession): Promise<RevokedSession> {
|
||||
revokedSession.received = true
|
||||
revokedSession.receivedAt = this.timer.getUTCDate()
|
||||
|
||||
return this.revokedSessionRepository.save(revokedSession)
|
||||
}
|
||||
@@ -224,7 +225,9 @@ export class SessionService implements SessionServiceInterface {
|
||||
const revokedSession = new RevokedSession()
|
||||
revokedSession.uuid = session.uuid
|
||||
revokedSession.userUuid = session.userUuid
|
||||
revokedSession.createdAt = dayjs.utc().toDate()
|
||||
revokedSession.createdAt = this.timer.getUTCDate()
|
||||
revokedSession.apiVersion = session.apiVersion
|
||||
revokedSession.userAgent = session.userAgent
|
||||
|
||||
return this.revokedSessionRepository.save(revokedSession)
|
||||
}
|
||||
@@ -246,8 +249,8 @@ export class SessionService implements SessionServiceInterface {
|
||||
}
|
||||
session.userUuid = dto.user.uuid
|
||||
session.apiVersion = dto.apiVersion
|
||||
session.createdAt = dayjs.utc().toDate()
|
||||
session.updatedAt = dayjs.utc().toDate()
|
||||
session.createdAt = this.timer.getUTCDate()
|
||||
session.updatedAt = this.timer.getUTCDate()
|
||||
session.readonlyAccess = dto.readonlyAccess
|
||||
|
||||
return session
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { Repository, SelectQueryBuilder } from 'typeorm'
|
||||
import { Repository, SelectQueryBuilder, UpdateQueryBuilder } from 'typeorm'
|
||||
|
||||
import { RevokedSession } from '../../Domain/Session/RevokedSession'
|
||||
|
||||
@@ -9,12 +9,14 @@ import { MySQLRevokedSessionRepository } from './MySQLRevokedSessionRepository'
|
||||
describe('MySQLRevokedSessionRepository', () => {
|
||||
let ormRepository: Repository<RevokedSession>
|
||||
let queryBuilder: SelectQueryBuilder<RevokedSession>
|
||||
let updateQueryBuilder: UpdateQueryBuilder<RevokedSession>
|
||||
let session: RevokedSession
|
||||
|
||||
const createRepository = () => new MySQLRevokedSessionRepository(ormRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
queryBuilder = {} as jest.Mocked<SelectQueryBuilder<RevokedSession>>
|
||||
updateQueryBuilder = {} as jest.Mocked<UpdateQueryBuilder<RevokedSession>>
|
||||
|
||||
session = {} as jest.Mocked<RevokedSession>
|
||||
|
||||
@@ -36,6 +38,24 @@ describe('MySQLRevokedSessionRepository', () => {
|
||||
expect(ormRepository.remove).toHaveBeenCalledWith(session)
|
||||
})
|
||||
|
||||
it('should clear user agent data on all user sessions', async () => {
|
||||
ormRepository.createQueryBuilder = jest.fn().mockImplementation(() => updateQueryBuilder)
|
||||
|
||||
updateQueryBuilder.update = jest.fn().mockReturnThis()
|
||||
updateQueryBuilder.set = jest.fn().mockReturnThis()
|
||||
updateQueryBuilder.where = jest.fn().mockReturnThis()
|
||||
updateQueryBuilder.execute = jest.fn()
|
||||
|
||||
await createRepository().clearUserAgentByUserUuid('1-2-3')
|
||||
|
||||
expect(updateQueryBuilder.update).toHaveBeenCalled()
|
||||
expect(updateQueryBuilder.set).toHaveBeenCalledWith({
|
||||
userAgent: null,
|
||||
})
|
||||
expect(updateQueryBuilder.where).toHaveBeenCalledWith('user_uuid = :userUuid', { userUuid: '1-2-3' })
|
||||
expect(updateQueryBuilder.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should find one session by id', async () => {
|
||||
queryBuilder.where = jest.fn().mockReturnThis()
|
||||
queryBuilder.getOne = jest.fn().mockReturnValue(session)
|
||||
|
||||
@@ -20,6 +20,17 @@ export class MySQLRevokedSessionRepository implements RevokedSessionRepositoryIn
|
||||
return this.ormRepository.remove(revokedSession)
|
||||
}
|
||||
|
||||
async clearUserAgentByUserUuid(userUuid: string): Promise<void> {
|
||||
await this.ormRepository
|
||||
.createQueryBuilder('revoked_session')
|
||||
.update()
|
||||
.set({
|
||||
userAgent: null,
|
||||
})
|
||||
.where('user_uuid = :userUuid', { userUuid })
|
||||
.execute()
|
||||
}
|
||||
|
||||
async findAllByUserUuid(userUuid: string): Promise<RevokedSession[]> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('revoked_session')
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.7.33](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.32...@standardnotes/domain-events-infra@1.7.33) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.7.32](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.31...@standardnotes/domain-events-infra@1.7.32) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.7.31](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.7.30...@standardnotes/domain-events-infra@1.7.31) (2022-08-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.7.31",
|
||||
"version": "1.7.33",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.54.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.54.0...@standardnotes/domain-events@2.54.1) (2022-08-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **domain-events:** missing exports ([2fb904d](https://github.com/standardnotes/server/commit/2fb904d2cbff040ba9a6b2b323eab1591309b174))
|
||||
|
||||
# [2.54.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.53.0...@standardnotes/domain-events@2.54.0) (2022-08-15)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-events:** add payment success event ([1841597](https://github.com/standardnotes/server/commit/1841597405461981c7b6eb4ee8ada079c77a8fc5))
|
||||
|
||||
# [2.53.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.52.0...@standardnotes/domain-events@2.53.0) (2022-08-09)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.53.0",
|
||||
"version": "2.54.1",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
|
||||
import { PaymentSuccessEventPayload } from './PaymentSuccessEventPayload'
|
||||
|
||||
export interface PaymentSuccessEvent extends DomainEventInterface {
|
||||
type: 'PAYMENT_SUCCESS'
|
||||
payload: PaymentSuccessEventPayload
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface PaymentSuccessEventPayload {
|
||||
userEmail: string
|
||||
}
|
||||
@@ -54,6 +54,8 @@ export * from './Event/OneDriveBackupFailedEvent'
|
||||
export * from './Event/OneDriveBackupFailedEventPayload'
|
||||
export * from './Event/PaymentFailedEvent'
|
||||
export * from './Event/PaymentFailedEventPayload'
|
||||
export * from './Event/PaymentSuccessEvent'
|
||||
export * from './Event/PaymentSuccessEventPayload'
|
||||
export * from './Event/PredicateVerificationRequestedEvent'
|
||||
export * from './Event/PredicateVerificationRequestedEventPayload'
|
||||
export * from './Event/PredicateVerifiedEvent'
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.33](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.1.32...@standardnotes/event-store@1.1.33) (2022-08-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **event-store:** add payment events handling ([3477c81](https://github.com/standardnotes/server/commit/3477c81d37e6cb92e76d59b1128daac702a4a7ae))
|
||||
|
||||
## [1.1.32](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.1.31...@standardnotes/event-store@1.1.32) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.1.31](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.1.30...@standardnotes/event-store@1.1.31) (2022-08-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/event-store",
|
||||
"version": "1.1.31",
|
||||
"version": "1.1.33",
|
||||
"description": "Event Store Service",
|
||||
"private": true,
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -76,6 +76,8 @@ export class ContainerConfigLoader {
|
||||
['EMAIL_BACKUP_ATTACHMENT_CREATED', container.get(TYPES.EventHandler)],
|
||||
['EMAIL_BACKUP_REQUESTED', container.get(TYPES.EventHandler)],
|
||||
['OFFLINE_SUBSCRIPTION_TOKEN_CREATED', container.get(TYPES.EventHandler)],
|
||||
['PAYMENT_FAILED', container.get(TYPES.EventHandler)],
|
||||
['PAYMENT_SUCCESS', container.get(TYPES.EventHandler)],
|
||||
])
|
||||
|
||||
container
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.5.35](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.34...@standardnotes/files-server@1.5.35) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.5.34](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.33...@standardnotes/files-server@1.5.34) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.5.33](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.5.32...@standardnotes/files-server@1.5.33) (2022-08-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.5.33",
|
||||
"version": "1.5.35",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.10.10](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.9...@standardnotes/scheduler-server@1.10.10) (2022-08-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **scheduler:** change discount from 10% to 20% on a limited time offer ([4b3de26](https://github.com/standardnotes/server/commit/4b3de264efc4fffb2603181c158cddb25c4ed4a9))
|
||||
|
||||
## [1.10.9](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.8...@standardnotes/scheduler-server@1.10.9) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.10.8](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.7...@standardnotes/scheduler-server@1.10.8) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.10.7](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.6...@standardnotes/scheduler-server@1.10.7) (2022-08-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.10.7",
|
||||
"version": "1.10.10",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -205,7 +205,7 @@ describe('JobDoneInterpreter', () => {
|
||||
|
||||
expect(domainEventFactory.createDiscountApplyRequestedEvent).toHaveBeenCalledWith({
|
||||
userEmail: 'test@standardnotes.com',
|
||||
discountCode: 'limited-10',
|
||||
discountCode: 'limited-20',
|
||||
})
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
@@ -234,7 +234,7 @@ describe('JobDoneInterpreter', () => {
|
||||
|
||||
expect(domainEventFactory.createDiscountWithdrawRequestedEvent).toHaveBeenCalledWith({
|
||||
userEmail: 'test@standardnotes.com',
|
||||
discountCode: 'limited-10',
|
||||
discountCode: 'limited-20',
|
||||
})
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -116,7 +116,7 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createDiscountApplyRequestedEvent({
|
||||
userEmail: job.userIdentifier,
|
||||
discountCode: 'limited-10',
|
||||
discountCode: 'limited-20',
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -127,7 +127,7 @@ export class JobDoneInterpreter implements JobDoneInterpreterInterface {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createDiscountWithdrawRequestedEvent({
|
||||
userEmail: job.userIdentifier,
|
||||
discountCode: 'limited-10',
|
||||
discountCode: 'limited-20',
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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.6.50](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.49...@standardnotes/syncing-server@1.6.50) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.6.49](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.48...@standardnotes/syncing-server@1.6.49) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.6.48](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.47...@standardnotes/syncing-server@1.6.48) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.6.47](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.46...@standardnotes/syncing-server@1.6.47) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.6.46](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.45...@standardnotes/syncing-server@1.6.46) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.6.45](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.44...@standardnotes/syncing-server@1.6.45) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.6.44](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.6.43...@standardnotes/syncing-server@1.6.44) (2022-08-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/syncing-server",
|
||||
"version": "1.6.44",
|
||||
"version": "1.6.50",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user