Compare commits

..

2 Commits

36 changed files with 313 additions and 15 deletions

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.25.21](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.20...@standardnotes/analytics@2.25.21) (2023-08-31)
**Note:** Version bump only for package @standardnotes/analytics
## [2.25.20](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.25.19...@standardnotes/analytics@2.25.20) (2023-08-31)
**Note:** Version bump only for package @standardnotes/analytics

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "2.25.20",
"version": "2.25.21",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.73.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.73.2...@standardnotes/api-gateway@1.73.3) (2023-08-31)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.73.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.73.1...@standardnotes/api-gateway@1.73.2) (2023-08-31)
**Note:** Version bump only for package @standardnotes/api-gateway

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.73.2",
"version": "1.73.3",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.137.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.137.1...@standardnotes/auth-server@1.137.2) (2023-08-31)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.137.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.137.0...@standardnotes/auth-server@1.137.1) (2023-08-31)
**Note:** Version bump only for package @standardnotes/auth-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.137.1",
"version": "1.137.2",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.12.17](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.16...@standardnotes/domain-events-infra@1.12.17) (2023-08-31)
**Note:** Version bump only for package @standardnotes/domain-events-infra
## [1.12.16](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.15...@standardnotes/domain-events-infra@1.12.16) (2023-08-30)
**Note:** Version bump only for package @standardnotes/domain-events-infra

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events-infra",
"version": "1.12.16",
"version": "1.12.17",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.120.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.119.0...@standardnotes/domain-events@2.120.0) (2023-08-31)
### Features
* add sending notifications to user via websockets ([#799](https://github.com/standardnotes/server/issues/799)) ([c0722b1](https://github.com/standardnotes/server/commit/c0722b173b71d696568d8e8c5095a22fd219bef6))
# [2.119.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.118.0...@standardnotes/domain-events@2.119.0) (2023-08-30)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events",
"version": "2.119.0",
"version": "2.120.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -0,0 +1,8 @@
import { DomainEventInterface } from './DomainEventInterface'
import { NotificationAddedForUserEventPayload } from './NotificationAddedForUserEventPayload'
export interface NotificationAddedForUserEvent extends DomainEventInterface {
type: 'NOTIFICATION_ADDED_FOR_USER'
payload: NotificationAddedForUserEventPayload
}

View File

@@ -0,0 +1,10 @@
export interface NotificationAddedForUserEventPayload {
notification: {
uuid: string
user_uuid: string
type: string
payload: string
created_at_timestamp: number
updated_at_timestamp: number
}
}

View File

@@ -42,6 +42,8 @@ export * from './Event/ListedAccountRequestedEvent'
export * from './Event/ListedAccountRequestedEventPayload'
export * from './Event/MuteEmailsSettingChangedEvent'
export * from './Event/MuteEmailsSettingChangedEventPayload'
export * from './Event/NotificationAddedForUserEvent'
export * from './Event/NotificationAddedForUserEventPayload'
export * from './Event/PaymentFailedEvent'
export * from './Event/PaymentFailedEventPayload'
export * from './Event/PaymentsAccountDeletedEvent'

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.11.27](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.26...@standardnotes/event-store@1.11.27) (2023-08-31)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.26](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.25...@standardnotes/event-store@1.11.26) (2023-08-31)
**Note:** Version bump only for package @standardnotes/event-store

View File

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

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.22.6](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.5...@standardnotes/files-server@1.22.6) (2023-08-31)
**Note:** Version bump only for package @standardnotes/files-server
## [1.22.5](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.22.4...@standardnotes/files-server@1.22.5) (2023-08-31)
**Note:** Version bump only for package @standardnotes/files-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.22.5",
"version": "1.22.6",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.15.16](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.15...@standardnotes/home-server@1.15.16) (2023-08-31)
**Note:** Version bump only for package @standardnotes/home-server
## [1.15.15](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.15.14...@standardnotes/home-server@1.15.15) (2023-08-31)
**Note:** Version bump only for package @standardnotes/home-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/home-server",
"version": "1.15.15",
"version": "1.15.16",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.30.6](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.30.5...@standardnotes/revisions-server@1.30.6) (2023-08-31)
**Note:** Version bump only for package @standardnotes/revisions-server
## [1.30.5](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.30.4...@standardnotes/revisions-server@1.30.5) (2023-08-31)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/revisions-server",
"version": "1.30.5",
"version": "1.30.6",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.20.31](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.30...@standardnotes/scheduler-server@1.20.31) (2023-08-31)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.20.30](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.29...@standardnotes/scheduler-server@1.20.30) (2023-08-31)
**Note:** Version bump only for package @standardnotes/scheduler-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/scheduler-server",
"version": "1.20.30",
"version": "1.20.31",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.89.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.88.3...@standardnotes/syncing-server@1.89.0) (2023-08-31)
### Features
* add sending notifications to user via websockets ([#799](https://github.com/standardnotes/syncing-server-js/issues/799)) ([c0722b1](https://github.com/standardnotes/syncing-server-js/commit/c0722b173b71d696568d8e8c5095a22fd219bef6))
## [1.88.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.88.2...@standardnotes/syncing-server@1.88.3) (2023-08-31)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.88.3",
"version": "1.89.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -161,6 +161,7 @@ import { TriggerTransitionFromPrimaryToSecondaryDatabaseForUser } from '../Domai
import { SQLItem } from '../Infra/TypeORM/SQLItem'
import { SQLItemPersistenceMapper } from '../Mapping/Persistence/SQLItemPersistenceMapper'
import { SQLItemRepository } from '../Infra/TypeORM/SQLItemRepository'
import { SendEventToClient } from '../Domain/UseCase/Syncing/SendEventToClient/SendEventToClient'
export class ContainerConfigLoader {
private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
@@ -580,10 +581,24 @@ export class ContainerConfigLoader {
container.get(TYPES.Sync_DomainEventFactory),
),
)
container
.bind<SendEventToClient>(TYPES.Sync_SendEventToClient)
.toConstantValue(
new SendEventToClient(
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
),
)
container
.bind<AddNotificationForUser>(TYPES.Sync_AddNotificationForUser)
.toConstantValue(
new AddNotificationForUser(container.get(TYPES.Sync_NotificationRepository), container.get(TYPES.Sync_Timer)),
new AddNotificationForUser(
container.get<NotificationRepositoryInterface>(TYPES.Sync_NotificationRepository),
container.get<TimerInterface>(TYPES.Sync_Timer),
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
container.get<SendEventToClient>(TYPES.Sync_SendEventToClient),
container.get<Logger>(TYPES.Sync_Logger),
),
)
container
.bind<AddNotificationsForUsers>(TYPES.Sync_AddNotificationsForUsers)

View File

@@ -87,6 +87,7 @@ const TYPES = {
Sync_TriggerTransitionFromPrimaryToSecondaryDatabaseForUser: Symbol.for(
'Sync_TriggerTransitionFromPrimaryToSecondaryDatabaseForUser',
),
Sync_SendEventToClient: Symbol.for('Sync_SendEventToClient'),
// Handlers
Sync_AccountDeletionRequestedEventHandler: Symbol.for('Sync_AccountDeletionRequestedEventHandler'),
Sync_DuplicateItemSyncedEventHandler: Symbol.for('Sync_DuplicateItemSyncedEventHandler'),

View File

@@ -5,8 +5,10 @@ import {
EmailRequestedEvent,
ItemDumpedEvent,
ItemRevisionCreationRequestedEvent,
NotificationAddedForUserEvent,
RevisionsCopyRequestedEvent,
TransitionStatusUpdatedEvent,
WebSocketMessageRequestedEvent,
} from '@standardnotes/domain-events'
import { TimerInterface } from '@standardnotes/time'
import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
@@ -14,6 +16,45 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
export class DomainEventFactory implements DomainEventFactoryInterface {
constructor(private timer: TimerInterface) {}
createNotificationAddedForUserEvent(dto: {
notification: {
uuid: string
user_uuid: string
type: string
payload: string
created_at_timestamp: number
updated_at_timestamp: number
}
}): NotificationAddedForUserEvent {
return {
type: 'NOTIFICATION_ADDED_FOR_USER',
createdAt: this.timer.getUTCDate(),
meta: {
correlation: {
userIdentifier: dto.notification.user_uuid,
userIdentifierType: 'uuid',
},
origin: DomainEventService.SyncingServer,
},
payload: dto,
}
}
createWebSocketMessageRequestedEvent(dto: { userUuid: string; message: string }): WebSocketMessageRequestedEvent {
return {
type: 'WEB_SOCKET_MESSAGE_REQUESTED',
createdAt: this.timer.getUTCDate(),
meta: {
correlation: {
userIdentifier: dto.userUuid,
userIdentifierType: 'uuid',
},
origin: DomainEventService.SyncingServer,
},
payload: dto,
}
}
createTransitionStatusUpdatedEvent(dto: {
userUuid: string
transitionType: 'items' | 'revisions'

View File

@@ -3,11 +3,24 @@ import {
EmailRequestedEvent,
ItemDumpedEvent,
ItemRevisionCreationRequestedEvent,
NotificationAddedForUserEvent,
RevisionsCopyRequestedEvent,
TransitionStatusUpdatedEvent,
WebSocketMessageRequestedEvent,
} from '@standardnotes/domain-events'
export interface DomainEventFactoryInterface {
createWebSocketMessageRequestedEvent(dto: { userUuid: string; message: string }): WebSocketMessageRequestedEvent
createNotificationAddedForUserEvent(dto: {
notification: {
uuid: string
user_uuid: string
type: string
payload: string
created_at_timestamp: number
updated_at_timestamp: number
}
}): NotificationAddedForUserEvent
createTransitionStatusUpdatedEvent(dto: {
userUuid: string
transitionType: 'items' | 'revisions'

View File

@@ -4,13 +4,21 @@ import { NotificationPayload, NotificationType, Result, Uuid } from '@standardno
import { NotificationRepositoryInterface } from '../../../Notifications/NotificationRepositoryInterface'
import { Notification } from '../../../Notifications/Notification'
import { AddNotificationForUser } from './AddNotificationForUser'
import { DomainEventFactoryInterface } from '../../../Event/DomainEventFactoryInterface'
import { SendEventToClient } from '../../Syncing/SendEventToClient/SendEventToClient'
import { NotificationAddedForUserEvent } from '@standardnotes/domain-events'
import { Logger } from 'winston'
describe('AddNotificationForUser', () => {
let notificationRepository: NotificationRepositoryInterface
let timer: TimerInterface
let payload: NotificationPayload
let domainEventFactory: DomainEventFactoryInterface
let sendEventToClientUseCase: SendEventToClient
let logger: Logger
const createUseCase = () => new AddNotificationForUser(notificationRepository, timer)
const createUseCase = () =>
new AddNotificationForUser(notificationRepository, timer, domainEventFactory, sendEventToClientUseCase, logger)
beforeEach(() => {
notificationRepository = {} as jest.Mocked<NotificationRepositoryInterface>
@@ -24,6 +32,17 @@ describe('AddNotificationForUser', () => {
type: NotificationType.create(NotificationType.TYPES.RemovedFromSharedVault).getValue(),
version: '1.0',
}).getValue()
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
domainEventFactory.createNotificationAddedForUserEvent = jest.fn().mockReturnValue({
type: 'NOTIFICATION_ADDED_FOR_USER',
} as jest.Mocked<NotificationAddedForUserEvent>)
sendEventToClientUseCase = {} as jest.Mocked<SendEventToClient>
sendEventToClientUseCase.execute = jest.fn().mockReturnValue(Result.ok())
logger = {} as jest.Mocked<Logger>
logger.error = jest.fn()
})
it('should save notification', async () => {
@@ -84,4 +103,20 @@ describe('AddNotificationForUser', () => {
mock.mockRestore()
})
it('should log error if event could not be sent to client', async () => {
sendEventToClientUseCase.execute = jest.fn().mockReturnValue(Result.fail('Oops'))
const useCase = createUseCase()
const result = await useCase.execute({
userUuid: '0e8c3c7e-3f1a-4f7a-9b5a-5b2b0a7d4b1e',
type: NotificationType.TYPES.RemovedFromSharedVault,
payload,
version: '1.0',
})
expect(result.isFailed()).toBeFalsy()
expect(logger.error).toHaveBeenCalled()
})
})

View File

@@ -4,9 +4,18 @@ import { TimerInterface } from '@standardnotes/time'
import { AddNotificationForUserDTO } from './AddNotificationForUserDTO'
import { NotificationRepositoryInterface } from '../../../Notifications/NotificationRepositoryInterface'
import { Notification } from '../../../Notifications/Notification'
import { SendEventToClient } from '../../Syncing/SendEventToClient/SendEventToClient'
import { DomainEventFactoryInterface } from '../../../Event/DomainEventFactoryInterface'
import { Logger } from 'winston'
export class AddNotificationForUser implements UseCaseInterface<Notification> {
constructor(private notificationRepository: NotificationRepositoryInterface, private timer: TimerInterface) {}
constructor(
private notificationRepository: NotificationRepositoryInterface,
private timer: TimerInterface,
private domainEventFactory: DomainEventFactoryInterface,
private sendEventToClientUseCase: SendEventToClient,
private logger: Logger,
) {}
async execute(dto: AddNotificationForUserDTO): Promise<Result<Notification>> {
const userUuidOrError = Uuid.create(dto.userUuid)
@@ -37,6 +46,27 @@ export class AddNotificationForUser implements UseCaseInterface<Notification> {
await this.notificationRepository.save(notification)
const event = this.domainEventFactory.createNotificationAddedForUserEvent({
notification: {
uuid: notification.id.toString(),
user_uuid: notification.props.userUuid.value,
type: notification.props.type.value,
payload: notification.props.payload.toString(),
created_at_timestamp: notification.props.timestamps.createdAt,
updated_at_timestamp: notification.props.timestamps.updatedAt,
},
})
const result = await this.sendEventToClientUseCase.execute({
userUuid: userUuid.value,
event,
})
if (result.isFailed()) {
this.logger.error(
`Failed to send notification added event to client for user ${userUuid.value}: ${result.getError()}`,
)
}
return Result.ok(notification)
}
}

View File

@@ -0,0 +1,56 @@
import {
DomainEventInterface,
DomainEventPublisherInterface,
WebSocketMessageRequestedEvent,
} from '@standardnotes/domain-events'
import { DomainEventFactoryInterface } from '../../../Event/DomainEventFactoryInterface'
import { SendEventToClient } from './SendEventToClient'
describe('SendEventToClient', () => {
let domainEventFactory: DomainEventFactoryInterface
let domainEventPublisher: DomainEventPublisherInterface
const createUseCase = () => new SendEventToClient(domainEventFactory, domainEventPublisher)
beforeEach(() => {
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
domainEventFactory.createWebSocketMessageRequestedEvent = jest
.fn()
.mockReturnValue({} as jest.Mocked<WebSocketMessageRequestedEvent>)
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
domainEventPublisher.publish = jest.fn()
})
it('should publish a WebSocketMessageRequestedEvent', async () => {
const useCase = createUseCase()
await useCase.execute({
userUuid: '00000000-0000-0000-0000-000000000000',
event: {
type: 'test',
} as jest.Mocked<DomainEventInterface>,
})
expect(domainEventFactory.createWebSocketMessageRequestedEvent).toHaveBeenCalledWith({
userUuid: '00000000-0000-0000-0000-000000000000',
message: JSON.stringify({
type: 'test',
}),
})
expect(domainEventPublisher.publish).toHaveBeenCalledWith({} as jest.Mocked<WebSocketMessageRequestedEvent>)
})
it('should return a failed result if user uuid is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
userUuid: 'invalid',
event: {
type: 'test',
} as jest.Mocked<DomainEventInterface>,
})
expect(result.isFailed()).toBe(true)
})
})

View File

@@ -0,0 +1,29 @@
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
import { SendEventToClientDTO } from './SendEventToClientDTO'
import { DomainEventFactoryInterface } from '../../../Event/DomainEventFactoryInterface'
export class SendEventToClient implements UseCaseInterface<void> {
constructor(
private domainEventFactory: DomainEventFactoryInterface,
private domainEventPublisher: DomainEventPublisherInterface,
) {}
async execute(dto: SendEventToClientDTO): Promise<Result<void>> {
const userUuidOrError = Uuid.create(dto.userUuid)
if (userUuidOrError.isFailed()) {
return Result.fail(userUuidOrError.getError())
}
const userUuid = userUuidOrError.getValue()
const event = this.domainEventFactory.createWebSocketMessageRequestedEvent({
userUuid: userUuid.value,
message: JSON.stringify(dto.event),
})
await this.domainEventPublisher.publish(event)
return Result.ok()
}
}

View File

@@ -0,0 +1,6 @@
import { DomainEventInterface } from '@standardnotes/domain-events'
export interface SendEventToClientDTO {
userUuid: string
event: DomainEventInterface
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.10.24](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.10.23...@standardnotes/websockets-server@1.10.24) (2023-08-31)
**Note:** Version bump only for package @standardnotes/websockets-server
## [1.10.23](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.10.22...@standardnotes/websockets-server@1.10.23) (2023-08-30)
**Note:** Version bump only for package @standardnotes/websockets-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/websockets-server",
"version": "1.10.23",
"version": "1.10.24",
"engines": {
"node": ">=18.0.0 <21.0.0"
},