From 86ae4a59a3ac7915ad96ed5176b545f4d005e837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Thu, 13 Oct 2022 12:23:08 +0200 Subject: [PATCH] feat(auth): remove websocket handling in favor of websockets service --- packages/api-gateway/.env.sample | 1 + .../api-gateway/src/Bootstrap/Container.ts | 1 + packages/api-gateway/src/Bootstrap/Types.ts | 1 + .../src/Controller/v1/WebSocketsController.ts | 6 +- .../src/Service/Http/HttpService.ts | 10 ++++ .../src/Service/Http/HttpServiceInterface.ts | 6 ++ packages/auth/.env.sample | 1 - packages/auth/src/Bootstrap/Container.ts | 25 --------- packages/auth/src/Bootstrap/Types.ts | 7 --- .../Controller/WebSocketsController.spec.ts | 28 ---------- .../src/Controller/WebSocketsController.ts | 29 ---------- .../Domain/Event/DomainEventFactory.spec.ts | 23 ++++++++ .../src/Domain/Event/DomainEventFactory.ts | 18 +++++- .../Event/DomainEventFactoryInterface.ts | 4 +- .../AddWebSocketsConnection.spec.ts | 26 --------- .../AddWebSocketsConnection.ts | 26 --------- .../AddWebSocketsConnectionDTO.ts | 4 -- .../AddWebSocketsConnectionResponse.ts | 3 - .../CreateWebSocketConnection.spec.ts | 25 --------- .../CreateWebSocketConnectionDTO.ts | 3 - .../CreateWebSocketConnectionResponse.ts | 3 - .../CreateWebSocketConnectionToken.ts | 26 --------- .../RemoveWebSocketsConnection.spec.ts | 26 --------- .../RemoveWebSocketsConnection.ts | 26 --------- .../RemoveWebSocketsConnectionDTO.ts | 3 - .../RemoveWebSocketsConnectionResponse.ts | 3 - .../InversifyExpressWebSocketsController.ts | 40 +------------- ...edisWebSocketsConnectionRepository.spec.ts | 44 --------------- .../RedisWebSocketsConnectionRepository.ts | 28 ---------- .../WebSocketsClientService.spec.ts | 55 +++---------------- .../WebSockets/WebSocketsClientService.ts | 37 +++---------- 31 files changed, 81 insertions(+), 457 deletions(-) delete mode 100644 packages/auth/src/Controller/WebSocketsController.spec.ts delete mode 100644 packages/auth/src/Controller/WebSocketsController.ts delete mode 100644 packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection.spec.ts delete mode 100644 packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection.ts delete mode 100644 packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnectionDTO.ts delete mode 100644 packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnectionResponse.ts delete mode 100644 packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnection.spec.ts delete mode 100644 packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionDTO.ts delete mode 100644 packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionResponse.ts delete mode 100644 packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionToken.ts delete mode 100644 packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection.spec.ts delete mode 100644 packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection.ts delete mode 100644 packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnectionDTO.ts delete mode 100644 packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnectionResponse.ts delete mode 100644 packages/auth/src/Infra/Redis/RedisWebSocketsConnectionRepository.spec.ts delete mode 100644 packages/auth/src/Infra/Redis/RedisWebSocketsConnectionRepository.ts diff --git a/packages/api-gateway/.env.sample b/packages/api-gateway/.env.sample index 5ec94e82d..5bfb5514c 100644 --- a/packages/api-gateway/.env.sample +++ b/packages/api-gateway/.env.sample @@ -7,6 +7,7 @@ PORT=3000 SYNCING_SERVER_JS_URL=http://syncing_server_js:3000 AUTH_SERVER_URL=http://auth:3000 WORKSPACE_SERVER_URL=http://workspace:3000 +WEB_SOCKET_SERVER_URL=http://websockets:3000 PAYMENTS_SERVER_URL=http://payments:3000 FILES_SERVER_URL=http://files:3000 diff --git a/packages/api-gateway/src/Bootstrap/Container.ts b/packages/api-gateway/src/Bootstrap/Container.ts index 4e46f7885..215b5f2b7 100644 --- a/packages/api-gateway/src/Bootstrap/Container.ts +++ b/packages/api-gateway/src/Bootstrap/Container.ts @@ -76,6 +76,7 @@ export class ContainerConfigLoader { container.bind(TYPES.FILES_SERVER_URL).toConstantValue(env.get('FILES_SERVER_URL', true)) container.bind(TYPES.AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET')) container.bind(TYPES.WORKSPACE_SERVER_URL).toConstantValue(env.get('WORKSPACE_SERVER_URL')) + container.bind(TYPES.WEB_SOCKET_SERVER_URL).toConstantValue(env.get('WEB_SOCKET_SERVER_URL')) container .bind(TYPES.HTTP_CALL_TIMEOUT) .toConstantValue(env.get('HTTP_CALL_TIMEOUT', true) ? +env.get('HTTP_CALL_TIMEOUT', true) : 60_000) diff --git a/packages/api-gateway/src/Bootstrap/Types.ts b/packages/api-gateway/src/Bootstrap/Types.ts index 0d4d4a7cd..ef8aa81ee 100644 --- a/packages/api-gateway/src/Bootstrap/Types.ts +++ b/packages/api-gateway/src/Bootstrap/Types.ts @@ -9,6 +9,7 @@ const TYPES = { PAYMENTS_SERVER_URL: Symbol.for('PAYMENTS_SERVER_URL'), FILES_SERVER_URL: Symbol.for('FILES_SERVER_URL'), WORKSPACE_SERVER_URL: Symbol.for('WORKSPACE_SERVER_URL'), + WEB_SOCKET_SERVER_URL: Symbol.for('WEB_SOCKET_SERVER_URL'), AUTH_JWT_SECRET: Symbol.for('AUTH_JWT_SECRET'), HTTP_CALL_TIMEOUT: Symbol.for('HTTP_CALL_TIMEOUT'), VERSION: Symbol.for('VERSION'), diff --git a/packages/api-gateway/src/Controller/v1/WebSocketsController.ts b/packages/api-gateway/src/Controller/v1/WebSocketsController.ts index 447bebe30..0a058f24b 100644 --- a/packages/api-gateway/src/Controller/v1/WebSocketsController.ts +++ b/packages/api-gateway/src/Controller/v1/WebSocketsController.ts @@ -17,7 +17,7 @@ export class WebSocketsController extends BaseHttpController { @httpPost('/tokens', TYPES.AuthMiddleware) async createWebSocketConnectionToken(request: Request, response: Response): Promise { - await this.httpService.callAuthServer(request, response, 'sockets/tokens', request.body) + await this.httpService.callWebSocketServer(request, response, 'sockets/tokens', request.body) } @httpPost('/connections', TYPES.WebSocketAuthMiddleware) @@ -30,7 +30,7 @@ export class WebSocketsController extends BaseHttpController { return } - await this.httpService.callAuthServer( + await this.httpService.callWebSocketServer( request, response, `sockets/connections/${request.headers.connectionid}`, @@ -48,7 +48,7 @@ export class WebSocketsController extends BaseHttpController { return } - await this.httpService.callAuthServer( + await this.httpService.callWebSocketServer( request, response, `sockets/connections/${request.headers.connectionid}`, diff --git a/packages/api-gateway/src/Service/Http/HttpService.ts b/packages/api-gateway/src/Service/Http/HttpService.ts index 7da503a89..e2cae9608 100644 --- a/packages/api-gateway/src/Service/Http/HttpService.ts +++ b/packages/api-gateway/src/Service/Http/HttpService.ts @@ -17,6 +17,7 @@ export class HttpService implements HttpServiceInterface { @inject(TYPES.PAYMENTS_SERVER_URL) private paymentsServerUrl: string, @inject(TYPES.FILES_SERVER_URL) private filesServerUrl: string, @inject(TYPES.WORKSPACE_SERVER_URL) private workspaceServerUrl: string, + @inject(TYPES.WEB_SOCKET_SERVER_URL) private webSocketServerUrl: string, @inject(TYPES.HTTP_CALL_TIMEOUT) private httpCallTimeout: number, @inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface, @inject(TYPES.Logger) private logger: Logger, @@ -58,6 +59,15 @@ export class HttpService implements HttpServiceInterface { await this.callServer(this.workspaceServerUrl, request, response, endpoint, payload) } + async callWebSocketServer( + request: Request, + response: Response, + endpoint: string, + payload?: Record | string, + ): Promise { + await this.callServer(this.webSocketServerUrl, request, response, endpoint, payload) + } + async callPaymentsServer( request: Request, response: Response, diff --git a/packages/api-gateway/src/Service/Http/HttpServiceInterface.ts b/packages/api-gateway/src/Service/Http/HttpServiceInterface.ts index 75c0e8c87..15397d670 100644 --- a/packages/api-gateway/src/Service/Http/HttpServiceInterface.ts +++ b/packages/api-gateway/src/Service/Http/HttpServiceInterface.ts @@ -37,4 +37,10 @@ export interface HttpServiceInterface { endpoint: string, payload?: Record | string, ): Promise + callWebSocketServer( + request: Request, + response: Response, + endpoint: string, + payload?: Record | string, + ): Promise } diff --git a/packages/auth/.env.sample b/packages/auth/.env.sample index 05d564ca8..1549bed9b 100644 --- a/packages/auth/.env.sample +++ b/packages/auth/.env.sample @@ -67,7 +67,6 @@ VALET_TOKEN_SECRET= VALET_TOKEN_TTL= WEB_SOCKET_CONNECTION_TOKEN_SECRET= -WEB_SOCKET_CONNECTION_TOKEN_TTL= # (Optional) Analytics ANALYTICS_ENABLED=false diff --git a/packages/auth/src/Bootstrap/Container.ts b/packages/auth/src/Bootstrap/Container.ts index 3bffa3a13..3c7261543 100644 --- a/packages/auth/src/Bootstrap/Container.ts +++ b/packages/auth/src/Bootstrap/Container.ts @@ -79,10 +79,6 @@ import { DeleteAccount } from '../Domain/UseCase/DeleteAccount/DeleteAccount' import { DeleteSetting } from '../Domain/UseCase/DeleteSetting/DeleteSetting' import { SettingFactory } from '../Domain/Setting/SettingFactory' import { SettingService } from '../Domain/Setting/SettingService' -import { WebSocketsConnectionRepositoryInterface } from '../Domain/WebSockets/WebSocketsConnectionRepositoryInterface' -import { RedisWebSocketsConnectionRepository } from '../Infra/Redis/RedisWebSocketsConnectionRepository' -import { AddWebSocketsConnection } from '../Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection' -import { RemoveWebSocketsConnection } from '../Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection' import axios, { AxiosInstance } from 'axios' import { UserSubscription } from '../Domain/Subscription/UserSubscription' import { MySQLUserSubscriptionRepository } from '../Infra/MySQL/MySQLUserSubscriptionRepository' @@ -206,9 +202,6 @@ import { PaymentFailedEventHandler } from '../Domain/Handler/PaymentFailedEventH import { PaymentSuccessEventHandler } from '../Domain/Handler/PaymentSuccessEventHandler' import { RefundProcessedEventHandler } from '../Domain/Handler/RefundProcessedEventHandler' import { SubscriptionInvitesController } from '../Controller/SubscriptionInvitesController' -import { CreateWebSocketConnectionToken } from '../Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionToken' -import { WebSocketsController } from '../Controller/WebSocketsController' -import { WebSocketServerInterface } from '@standardnotes/api' import { CreateCrossServiceToken } from '../Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken' // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -273,7 +266,6 @@ export class ContainerConfigLoader { // Controller container.bind(TYPES.AuthController).to(AuthController) container.bind(TYPES.SubscriptionInvitesController).to(SubscriptionInvitesController) - container.bind(TYPES.WebSocketsController).to(WebSocketsController) // Repositories container.bind(TYPES.SessionRepository).to(MySQLSessionRepository) @@ -295,9 +287,6 @@ export class ContainerConfigLoader { .bind(TYPES.EphemeralSessionRepository) .to(RedisEphemeralSessionRepository) container.bind(TYPES.LockRepository).to(LockRepository) - container - .bind(TYPES.WebSocketsConnectionRepository) - .to(RedisWebSocketsConnectionRepository) container .bind(TYPES.SubscriptionTokenRepository) .to(RedisSubscriptionTokenRepository) @@ -375,9 +364,6 @@ export class ContainerConfigLoader { container .bind(TYPES.WEB_SOCKET_CONNECTION_TOKEN_SECRET) .toConstantValue(env.get('WEB_SOCKET_CONNECTION_TOKEN_SECRET', true)) - container - .bind(TYPES.WEB_SOCKET_CONNECTION_TOKEN_TTL) - .toConstantValue(+env.get('WEB_SOCKET_CONNECTION_TOKEN_TTL', true)) container.bind(TYPES.ENCRYPTION_SERVER_KEY).toConstantValue(env.get('ENCRYPTION_SERVER_KEY')) container.bind(TYPES.ACCESS_TOKEN_AGE).toConstantValue(env.get('ACCESS_TOKEN_AGE')) container.bind(TYPES.REFRESH_TOKEN_AGE).toConstantValue(env.get('REFRESH_TOKEN_AGE')) @@ -399,7 +385,6 @@ export class ContainerConfigLoader { container.bind(TYPES.REDIS_EVENTS_CHANNEL).toConstantValue(env.get('REDIS_EVENTS_CHANNEL')) container.bind(TYPES.NEW_RELIC_ENABLED).toConstantValue(env.get('NEW_RELIC_ENABLED', true)) container.bind(TYPES.SYNCING_SERVER_URL).toConstantValue(env.get('SYNCING_SERVER_URL')) - container.bind(TYPES.WEBSOCKETS_API_URL).toConstantValue(env.get('WEBSOCKETS_API_URL', true)) container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION')) container.bind(TYPES.PAYMENTS_SERVER_URL).toConstantValue(env.get('PAYMENTS_SERVER_URL', true)) @@ -424,8 +409,6 @@ export class ContainerConfigLoader { container.bind(TYPES.UpdateSetting).to(UpdateSetting) container.bind(TYPES.DeleteSetting).to(DeleteSetting) container.bind(TYPES.DeleteAccount).to(DeleteAccount) - container.bind(TYPES.AddWebSocketsConnection).to(AddWebSocketsConnection) - container.bind(TYPES.RemoveWebSocketsConnection).to(RemoveWebSocketsConnection) container.bind(TYPES.GetUserSubscription).to(GetUserSubscription) container.bind(TYPES.GetUserOfflineSubscription).to(GetUserOfflineSubscription) container.bind(TYPES.CreateSubscriptionToken).to(CreateSubscriptionToken) @@ -454,9 +437,6 @@ export class ContainerConfigLoader { container.bind(TYPES.GetSubscriptionSetting).to(GetSubscriptionSetting) container.bind(TYPES.GetUserAnalyticsId).to(GetUserAnalyticsId) container.bind(TYPES.VerifyPredicate).to(VerifyPredicate) - container - .bind(TYPES.CreateWebSocketConnectionToken) - .to(CreateWebSocketConnectionToken) container.bind(TYPES.CreateCrossServiceToken).to(CreateCrossServiceToken) // Handlers @@ -547,11 +527,6 @@ export class ContainerConfigLoader { container .bind>(TYPES.ValetTokenEncoder) .toConstantValue(new TokenEncoder(container.get(TYPES.VALET_TOKEN_SECRET))) - container - .bind>(TYPES.WebSocketConnectionTokenEncoder) - .toConstantValue( - new TokenEncoder(container.get(TYPES.WEB_SOCKET_CONNECTION_TOKEN_SECRET)), - ) container.bind(TYPES.AuthenticationMethodResolver).to(AuthenticationMethodResolver) container.bind(TYPES.DomainEventFactory).to(DomainEventFactory) container.bind(TYPES.HTTPClient).toConstantValue(axios.create()) diff --git a/packages/auth/src/Bootstrap/Types.ts b/packages/auth/src/Bootstrap/Types.ts index 21d011d48..00c295aef 100644 --- a/packages/auth/src/Bootstrap/Types.ts +++ b/packages/auth/src/Bootstrap/Types.ts @@ -6,7 +6,6 @@ const TYPES = { // Controller AuthController: Symbol.for('AuthController'), SubscriptionInvitesController: Symbol.for('SubscriptionInvitesController'), - WebSocketsController: Symbol.for('WebSocketsController'), // Repositories UserRepository: Symbol.for('UserRepository'), SessionRepository: Symbol.for('SessionRepository'), @@ -17,7 +16,6 @@ const TYPES = { OfflineSettingRepository: Symbol.for('OfflineSettingRepository'), LockRepository: Symbol.for('LockRepository'), RoleRepository: Symbol.for('RoleRepository'), - WebSocketsConnectionRepository: Symbol.for('WebSocketsConnectionRepository'), UserSubscriptionRepository: Symbol.for('UserSubscriptionRepository'), OfflineUserSubscriptionRepository: Symbol.for('OfflineUserSubscriptionRepository'), SubscriptionTokenRepository: Symbol.for('SubscriptionTokenRepository'), @@ -83,7 +81,6 @@ const TYPES = { REDIS_EVENTS_CHANNEL: Symbol.for('REDIS_EVENTS_CHANNEL'), NEW_RELIC_ENABLED: Symbol.for('NEW_RELIC_ENABLED'), SYNCING_SERVER_URL: Symbol.for('SYNCING_SERVER_URL'), - WEBSOCKETS_API_URL: Symbol.for('WEBSOCKETS_API_URL'), VERSION: Symbol.for('VERSION'), PAYMENTS_SERVER_URL: Symbol.for('PAYMENTS_SERVER_URL'), // use cases @@ -107,8 +104,6 @@ const TYPES = { UpdateSetting: Symbol.for('UpdateSetting'), DeleteSetting: Symbol.for('DeleteSetting'), DeleteAccount: Symbol.for('DeleteAccount'), - AddWebSocketsConnection: Symbol.for('AddWebSocketsConnection'), - RemoveWebSocketsConnection: Symbol.for('RemoveWebSocketsConnection'), GetUserSubscription: Symbol.for('GetUserSubscription'), GetUserOfflineSubscription: Symbol.for('GetUserOfflineSubscription'), CreateSubscriptionToken: Symbol.for('CreateSubscriptionToken'), @@ -125,7 +120,6 @@ const TYPES = { GetSubscriptionSetting: Symbol.for('GetSubscriptionSetting'), GetUserAnalyticsId: Symbol.for('GetUserAnalyticsId'), VerifyPredicate: Symbol.for('VerifyPredicate'), - CreateWebSocketConnectionToken: Symbol.for('CreateWebSocketConnectionToken'), CreateCrossServiceToken: Symbol.for('CreateCrossServiceToken'), // Handlers UserRegisteredEventHandler: Symbol.for('UserRegisteredEventHandler'), @@ -168,7 +162,6 @@ const TYPES = { CrossServiceTokenEncoder: Symbol.for('CrossServiceTokenEncoder'), SessionTokenEncoder: Symbol.for('SessionTokenEncoder'), ValetTokenEncoder: Symbol.for('ValetTokenEncoder'), - WebSocketConnectionTokenEncoder: Symbol.for('WebSocketConnectionTokenEncoder'), WebSocketConnectionTokenDecoder: Symbol.for('WebSocketConnectionTokenDecoder'), AuthenticationMethodResolver: Symbol.for('AuthenticationMethodResolver'), DomainEventPublisher: Symbol.for('DomainEventPublisher'), diff --git a/packages/auth/src/Controller/WebSocketsController.spec.ts b/packages/auth/src/Controller/WebSocketsController.spec.ts deleted file mode 100644 index 26ff9b223..000000000 --- a/packages/auth/src/Controller/WebSocketsController.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import 'reflect-metadata' - -import { WebSocketsController } from './WebSocketsController' -import { CreateWebSocketConnectionToken } from '../Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionToken' - -describe('WebSocketsController', () => { - let createWebSocketConnectionToken: CreateWebSocketConnectionToken - - const createController = () => new WebSocketsController(createWebSocketConnectionToken) - - beforeEach(() => { - createWebSocketConnectionToken = {} as jest.Mocked - createWebSocketConnectionToken.execute = jest.fn().mockReturnValue({ token: 'foobar' }) - }) - - it('should create a web sockets connection token', async () => { - const response = await createController().createConnectionToken({ userUuid: '1-2-3' }) - - expect(response).toEqual({ - status: 200, - data: { token: 'foobar' }, - }) - - expect(createWebSocketConnectionToken.execute).toHaveBeenCalledWith({ - userUuid: '1-2-3', - }) - }) -}) diff --git a/packages/auth/src/Controller/WebSocketsController.ts b/packages/auth/src/Controller/WebSocketsController.ts deleted file mode 100644 index e4ebb0f46..000000000 --- a/packages/auth/src/Controller/WebSocketsController.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - HttpStatusCode, - WebSocketConnectionTokenRequestParams, - WebSocketConnectionTokenResponse, - WebSocketServerInterface, -} from '@standardnotes/api' -import { inject, injectable } from 'inversify' - -import TYPES from '../Bootstrap/Types' -import { CreateWebSocketConnectionToken } from '../Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionToken' - -@injectable() -export class WebSocketsController implements WebSocketServerInterface { - constructor( - @inject(TYPES.CreateWebSocketConnectionToken) - private createWebSocketConnectionToken: CreateWebSocketConnectionToken, - ) {} - - async createConnectionToken( - params: WebSocketConnectionTokenRequestParams, - ): Promise { - const result = await this.createWebSocketConnectionToken.execute({ userUuid: params.userUuid as string }) - - return { - status: HttpStatusCode.Success, - data: result, - } - } -} diff --git a/packages/auth/src/Domain/Event/DomainEventFactory.spec.ts b/packages/auth/src/Domain/Event/DomainEventFactory.spec.ts index bce04e42d..9c895be86 100644 --- a/packages/auth/src/Domain/Event/DomainEventFactory.spec.ts +++ b/packages/auth/src/Domain/Event/DomainEventFactory.spec.ts @@ -18,6 +18,29 @@ describe('DomainEventFactory', () => { timer.getUTCDate = jest.fn().mockReturnValue(new Date(1)) }) + it('should create a WEB_SOCKET_MESSAGE_REQUESTED event', () => { + expect( + createFactory().createWebSocketMessageRequestedEvent({ + userUuid: '1-2-3', + message: 'foobar', + }), + ).toEqual({ + createdAt: expect.any(Date), + meta: { + correlation: { + userIdentifier: '1-2-3', + userIdentifierType: 'uuid', + }, + origin: 'auth', + }, + payload: { + userUuid: '1-2-3', + message: 'foobar', + }, + type: 'WEB_SOCKET_MESSAGE_REQUESTED', + }) + }) + it('should create a EMAIL_MESSAGE_REQUESTED event', () => { expect( createFactory().createEmailMessageRequestedEvent({ diff --git a/packages/auth/src/Domain/Event/DomainEventFactory.ts b/packages/auth/src/Domain/Event/DomainEventFactory.ts index b13e40231..b18e68ebf 100644 --- a/packages/auth/src/Domain/Event/DomainEventFactory.ts +++ b/packages/auth/src/Domain/Event/DomainEventFactory.ts @@ -1,4 +1,4 @@ -import { EmailMessageIdentifier, ProtocolVersion, RoleName, Uuid } from '@standardnotes/common' +import { EmailMessageIdentifier, JSONString, ProtocolVersion, RoleName, Uuid } from '@standardnotes/common' import { AccountDeletionRequestedEvent, UserEmailChangedEvent, @@ -15,6 +15,7 @@ import { PredicateVerifiedEvent, DomainEventService, EmailMessageRequestedEvent, + WebSocketMessageRequestedEvent, } from '@standardnotes/domain-events' import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates' import { TimerInterface } from '@standardnotes/time' @@ -27,6 +28,21 @@ import { DomainEventFactoryInterface } from './DomainEventFactoryInterface' export class DomainEventFactory implements DomainEventFactoryInterface { constructor(@inject(TYPES.Timer) private timer: TimerInterface) {} + createWebSocketMessageRequestedEvent(dto: { userUuid: Uuid; message: JSONString }): WebSocketMessageRequestedEvent { + return { + type: 'WEB_SOCKET_MESSAGE_REQUESTED', + createdAt: this.timer.getUTCDate(), + meta: { + correlation: { + userIdentifier: dto.userUuid, + userIdentifierType: 'uuid', + }, + origin: DomainEventService.Auth, + }, + payload: dto, + } + } + createEmailMessageRequestedEvent(dto: { userEmail: string messageIdentifier: EmailMessageIdentifier diff --git a/packages/auth/src/Domain/Event/DomainEventFactoryInterface.ts b/packages/auth/src/Domain/Event/DomainEventFactoryInterface.ts index 6f51a81b1..b103ecc0d 100644 --- a/packages/auth/src/Domain/Event/DomainEventFactoryInterface.ts +++ b/packages/auth/src/Domain/Event/DomainEventFactoryInterface.ts @@ -1,4 +1,4 @@ -import { Uuid, RoleName, EmailMessageIdentifier, ProtocolVersion } from '@standardnotes/common' +import { Uuid, RoleName, EmailMessageIdentifier, ProtocolVersion, JSONString } from '@standardnotes/common' import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates' import { AccountDeletionRequestedEvent, @@ -15,10 +15,12 @@ import { SharedSubscriptionInvitationCanceledEvent, PredicateVerifiedEvent, EmailMessageRequestedEvent, + WebSocketMessageRequestedEvent, } from '@standardnotes/domain-events' import { InviteeIdentifierType } from '../SharedSubscription/InviteeIdentifierType' export interface DomainEventFactoryInterface { + createWebSocketMessageRequestedEvent(dto: { userUuid: Uuid; message: JSONString }): WebSocketMessageRequestedEvent createEmailMessageRequestedEvent(dto: { userEmail: string messageIdentifier: EmailMessageIdentifier diff --git a/packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection.spec.ts b/packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection.spec.ts deleted file mode 100644 index 4686984d6..000000000 --- a/packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import 'reflect-metadata' -import { Logger } from 'winston' -import { WebSocketsConnectionRepositoryInterface } from '../../WebSockets/WebSocketsConnectionRepositoryInterface' - -import { AddWebSocketsConnection } from './AddWebSocketsConnection' - -describe('AddWebSocketsConnection', () => { - let webSocketsConnectionRepository: WebSocketsConnectionRepositoryInterface - let logger: Logger - - const createUseCase = () => new AddWebSocketsConnection(webSocketsConnectionRepository, logger) - - beforeEach(() => { - webSocketsConnectionRepository = {} as jest.Mocked - webSocketsConnectionRepository.saveConnection = jest.fn() - - logger = {} as jest.Mocked - logger.debug = jest.fn() - }) - - it('should save a web sockets connection for a user for further communication', async () => { - await createUseCase().execute({ userUuid: '1-2-3', connectionId: '2-3-4' }) - - expect(webSocketsConnectionRepository.saveConnection).toHaveBeenCalledWith('1-2-3', '2-3-4') - }) -}) diff --git a/packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection.ts b/packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection.ts deleted file mode 100644 index cfa2953ad..000000000 --- a/packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { inject, injectable } from 'inversify' -import { Logger } from 'winston' -import TYPES from '../../../Bootstrap/Types' -import { WebSocketsConnectionRepositoryInterface } from '../../WebSockets/WebSocketsConnectionRepositoryInterface' -import { UseCaseInterface } from '../UseCaseInterface' -import { AddWebSocketsConnectionDTO } from './AddWebSocketsConnectionDTO' -import { AddWebSocketsConnectionResponse } from './AddWebSocketsConnectionResponse' - -@injectable() -export class AddWebSocketsConnection implements UseCaseInterface { - constructor( - @inject(TYPES.WebSocketsConnectionRepository) - private webSocketsConnectionRepository: WebSocketsConnectionRepositoryInterface, - @inject(TYPES.Logger) private logger: Logger, - ) {} - - async execute(dto: AddWebSocketsConnectionDTO): Promise { - this.logger.debug(`Persisting connection ${dto.connectionId} for user ${dto.userUuid}`) - - await this.webSocketsConnectionRepository.saveConnection(dto.userUuid, dto.connectionId) - - return { - success: true, - } - } -} diff --git a/packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnectionDTO.ts b/packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnectionDTO.ts deleted file mode 100644 index 6a9208d31..000000000 --- a/packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnectionDTO.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type AddWebSocketsConnectionDTO = { - userUuid: string - connectionId: string -} diff --git a/packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnectionResponse.ts b/packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnectionResponse.ts deleted file mode 100644 index dca6eda92..000000000 --- a/packages/auth/src/Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnectionResponse.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type AddWebSocketsConnectionResponse = { - success: boolean -} diff --git a/packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnection.spec.ts b/packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnection.spec.ts deleted file mode 100644 index f896c2478..000000000 --- a/packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnection.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import 'reflect-metadata' - -import { TokenEncoderInterface, WebSocketConnectionTokenData } from '@standardnotes/security' - -import { CreateWebSocketConnectionToken } from './CreateWebSocketConnectionToken' - -describe('CreateWebSocketConnection', () => { - let tokenEncoder: TokenEncoderInterface - const tokenTTL = 30 - - const createUseCase = () => new CreateWebSocketConnectionToken(tokenEncoder, tokenTTL) - - beforeEach(() => { - tokenEncoder = {} as jest.Mocked> - tokenEncoder.encodeExpirableToken = jest.fn().mockReturnValue('foobar') - }) - - it('should create a web socket connection token', async () => { - const result = await createUseCase().execute({ userUuid: '1-2-3' }) - - expect(result.token).toEqual('foobar') - - expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith({ userUuid: '1-2-3' }, 30) - }) -}) diff --git a/packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionDTO.ts b/packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionDTO.ts deleted file mode 100644 index 6731d7faf..000000000 --- a/packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionDTO.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type CreateWebSocketConnectionDTO = { - userUuid: string -} diff --git a/packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionResponse.ts b/packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionResponse.ts deleted file mode 100644 index aa7b8f374..000000000 --- a/packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionResponse.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type CreateWebSocketConnectionResponse = { - token: string -} diff --git a/packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionToken.ts b/packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionToken.ts deleted file mode 100644 index f2923d55b..000000000 --- a/packages/auth/src/Domain/UseCase/CreateWebSocketConnectionToken/CreateWebSocketConnectionToken.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { TokenEncoderInterface, WebSocketConnectionTokenData } from '@standardnotes/security' -import { inject, injectable } from 'inversify' - -import TYPES from '../../../Bootstrap/Types' -import { UseCaseInterface } from '../UseCaseInterface' -import { CreateWebSocketConnectionDTO } from './CreateWebSocketConnectionDTO' -import { CreateWebSocketConnectionResponse } from './CreateWebSocketConnectionResponse' - -@injectable() -export class CreateWebSocketConnectionToken implements UseCaseInterface { - constructor( - @inject(TYPES.WebSocketConnectionTokenEncoder) - private tokenEncoder: TokenEncoderInterface, - @inject(TYPES.WEB_SOCKET_CONNECTION_TOKEN_TTL) private tokenTTL: number, - ) {} - - async execute(dto: CreateWebSocketConnectionDTO): Promise { - const data: WebSocketConnectionTokenData = { - userUuid: dto.userUuid, - } - - return { - token: this.tokenEncoder.encodeExpirableToken(data, this.tokenTTL), - } - } -} diff --git a/packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection.spec.ts b/packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection.spec.ts deleted file mode 100644 index 4316009b6..000000000 --- a/packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import 'reflect-metadata' -import { Logger } from 'winston' -import { WebSocketsConnectionRepositoryInterface } from '../../WebSockets/WebSocketsConnectionRepositoryInterface' - -import { RemoveWebSocketsConnection } from './RemoveWebSocketsConnection' - -describe('RemoveWebSocketsConnection', () => { - let webSocketsConnectionRepository: WebSocketsConnectionRepositoryInterface - let logger: Logger - - const createUseCase = () => new RemoveWebSocketsConnection(webSocketsConnectionRepository, logger) - - beforeEach(() => { - webSocketsConnectionRepository = {} as jest.Mocked - webSocketsConnectionRepository.removeConnection = jest.fn() - - logger = {} as jest.Mocked - logger.debug = jest.fn() - }) - - it('should remove a web sockets connection', async () => { - await createUseCase().execute({ connectionId: '2-3-4' }) - - expect(webSocketsConnectionRepository.removeConnection).toHaveBeenCalledWith('2-3-4') - }) -}) diff --git a/packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection.ts b/packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection.ts deleted file mode 100644 index 3592d3845..000000000 --- a/packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { inject, injectable } from 'inversify' -import { Logger } from 'winston' -import TYPES from '../../../Bootstrap/Types' -import { WebSocketsConnectionRepositoryInterface } from '../../WebSockets/WebSocketsConnectionRepositoryInterface' -import { UseCaseInterface } from '../UseCaseInterface' -import { RemoveWebSocketsConnectionDTO } from './RemoveWebSocketsConnectionDTO' -import { RemoveWebSocketsConnectionResponse } from './RemoveWebSocketsConnectionResponse' - -@injectable() -export class RemoveWebSocketsConnection implements UseCaseInterface { - constructor( - @inject(TYPES.WebSocketsConnectionRepository) - private webSocketsConnectionRepository: WebSocketsConnectionRepositoryInterface, - @inject(TYPES.Logger) private logger: Logger, - ) {} - - async execute(dto: RemoveWebSocketsConnectionDTO): Promise { - this.logger.debug(`Removing connection ${dto.connectionId}`) - - await this.webSocketsConnectionRepository.removeConnection(dto.connectionId) - - return { - success: true, - } - } -} diff --git a/packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnectionDTO.ts b/packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnectionDTO.ts deleted file mode 100644 index ac77ebf56..000000000 --- a/packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnectionDTO.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type RemoveWebSocketsConnectionDTO = { - connectionId: string -} diff --git a/packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnectionResponse.ts b/packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnectionResponse.ts deleted file mode 100644 index 2340e8e02..000000000 --- a/packages/auth/src/Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnectionResponse.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type RemoveWebSocketsConnectionResponse = { - success: boolean -} diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressWebSocketsController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressWebSocketsController.ts index be64cfc1a..3d3e75930 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressWebSocketsController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressWebSocketsController.ts @@ -1,43 +1,27 @@ -import { WebSocketServerInterface } from '@standardnotes/api' import { ErrorTag } from '@standardnotes/common' import { TokenDecoderInterface, WebSocketConnectionTokenData } from '@standardnotes/security' -import { Request, Response } from 'express' +import { Request } from 'express' import { inject } from 'inversify' import { BaseHttpController, controller, - httpDelete, httpPost, // eslint-disable-next-line @typescript-eslint/no-unused-vars results, } from 'inversify-express-utils' import TYPES from '../../Bootstrap/Types' -import { AddWebSocketsConnection } from '../../Domain/UseCase/AddWebSocketsConnection/AddWebSocketsConnection' import { CreateCrossServiceToken } from '../../Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken' -import { RemoveWebSocketsConnection } from '../../Domain/UseCase/RemoveWebSocketsConnection/RemoveWebSocketsConnection' @controller('/sockets') export class InversifyExpressWebSocketsController extends BaseHttpController { constructor( - @inject(TYPES.AddWebSocketsConnection) private addWebSocketsConnection: AddWebSocketsConnection, - @inject(TYPES.RemoveWebSocketsConnection) private removeWebSocketsConnection: RemoveWebSocketsConnection, @inject(TYPES.CreateCrossServiceToken) private createCrossServiceToken: CreateCrossServiceToken, - @inject(TYPES.WebSocketsController) private webSocketsController: WebSocketServerInterface, @inject(TYPES.WebSocketConnectionTokenDecoder) private tokenDecoder: TokenDecoderInterface, ) { super() } - @httpPost('/tokens', TYPES.ApiGatewayAuthMiddleware) - async createConnectionToken(_request: Request, response: Response): Promise { - const result = await this.webSocketsController.createConnectionToken({ - userUuid: response.locals.user.uuid, - }) - - return this.json(result.data, result.status) - } - @httpPost('/tokens/validate') async validateToken(request: Request): Promise { if (!request.headers.authorization) { @@ -72,26 +56,4 @@ export class InversifyExpressWebSocketsController extends BaseHttpController { return this.json({ authToken: result.token }) } - - @httpPost('/connections/:connectionId', TYPES.ApiGatewayAuthMiddleware) - async storeWebSocketsConnection( - request: Request, - response: Response, - ): Promise { - await this.addWebSocketsConnection.execute({ - userUuid: response.locals.user.uuid, - connectionId: request.params.connectionId, - }) - - return this.json({ success: true }) - } - - @httpDelete('/connections/:connectionId') - async deleteWebSocketsConnection( - request: Request, - ): Promise { - await this.removeWebSocketsConnection.execute({ connectionId: request.params.connectionId }) - - return this.json({ success: true }) - } } diff --git a/packages/auth/src/Infra/Redis/RedisWebSocketsConnectionRepository.spec.ts b/packages/auth/src/Infra/Redis/RedisWebSocketsConnectionRepository.spec.ts deleted file mode 100644 index 4db514670..000000000 --- a/packages/auth/src/Infra/Redis/RedisWebSocketsConnectionRepository.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import 'reflect-metadata' - -import * as IORedis from 'ioredis' - -import { RedisWebSocketsConnectionRepository } from './RedisWebSocketsConnectionRepository' - -describe('RedisWebSocketsConnectionRepository', () => { - let redisClient: IORedis.Redis - - const createRepository = () => new RedisWebSocketsConnectionRepository(redisClient) - - beforeEach(() => { - redisClient = {} as jest.Mocked - redisClient.sadd = jest.fn() - redisClient.set = jest.fn() - redisClient.get = jest.fn() - redisClient.srem = jest.fn() - redisClient.del = jest.fn() - redisClient.smembers = jest.fn() - }) - - it('should save a connection to set of user connections', async () => { - await createRepository().saveConnection('1-2-3', '2-3-4') - - expect(redisClient.sadd).toHaveBeenCalledWith('ws_user_connections:1-2-3', '2-3-4') - expect(redisClient.set).toHaveBeenCalledWith('ws_connection:2-3-4', '1-2-3') - }) - - it('should remove a connection from the set of user connections', async () => { - redisClient.get = jest.fn().mockReturnValue('1-2-3') - - await createRepository().removeConnection('2-3-4') - - expect(redisClient.srem).toHaveBeenCalledWith('ws_user_connections:1-2-3', '2-3-4') - expect(redisClient.del).toHaveBeenCalledWith('ws_connection:2-3-4') - }) - - it('should return all connections for a user uuid', async () => { - const userUuid = '1-2-3' - - await createRepository().findAllByUserUuid(userUuid) - expect(redisClient.smembers).toHaveBeenCalledWith(`ws_user_connections:${userUuid}`) - }) -}) diff --git a/packages/auth/src/Infra/Redis/RedisWebSocketsConnectionRepository.ts b/packages/auth/src/Infra/Redis/RedisWebSocketsConnectionRepository.ts deleted file mode 100644 index 5e080475d..000000000 --- a/packages/auth/src/Infra/Redis/RedisWebSocketsConnectionRepository.ts +++ /dev/null @@ -1,28 +0,0 @@ -import * as IORedis from 'ioredis' -import { inject, injectable } from 'inversify' -import TYPES from '../../Bootstrap/Types' -import { WebSocketsConnectionRepositoryInterface } from '../../Domain/WebSockets/WebSocketsConnectionRepositoryInterface' - -@injectable() -export class RedisWebSocketsConnectionRepository implements WebSocketsConnectionRepositoryInterface { - private readonly WEB_SOCKETS_USER_CONNECTIONS_PREFIX = 'ws_user_connections' - private readonly WEB_SOCKETS_CONNETION_PREFIX = 'ws_connection' - - constructor(@inject(TYPES.Redis) private redisClient: IORedis.Redis) {} - - async findAllByUserUuid(userUuid: string): Promise { - return await this.redisClient.smembers(`${this.WEB_SOCKETS_USER_CONNECTIONS_PREFIX}:${userUuid}`) - } - - async removeConnection(connectionId: string): Promise { - const userUuid = await this.redisClient.get(`${this.WEB_SOCKETS_CONNETION_PREFIX}:${connectionId}`) - - await this.redisClient.srem(`${this.WEB_SOCKETS_USER_CONNECTIONS_PREFIX}:${userUuid}`, connectionId) - await this.redisClient.del(`${this.WEB_SOCKETS_CONNETION_PREFIX}:${connectionId}`) - } - - async saveConnection(userUuid: string, connectionId: string): Promise { - await this.redisClient.set(`${this.WEB_SOCKETS_CONNETION_PREFIX}:${connectionId}`, userUuid) - await this.redisClient.sadd(`${this.WEB_SOCKETS_USER_CONNECTIONS_PREFIX}:${userUuid}`, connectionId) - } -} diff --git a/packages/auth/src/Infra/WebSockets/WebSocketsClientService.spec.ts b/packages/auth/src/Infra/WebSockets/WebSocketsClientService.spec.ts index 66f525d13..c50006890 100644 --- a/packages/auth/src/Infra/WebSockets/WebSocketsClientService.spec.ts +++ b/packages/auth/src/Infra/WebSockets/WebSocketsClientService.spec.ts @@ -1,38 +1,21 @@ import 'reflect-metadata' -import { UserRolesChangedEvent } from '@standardnotes/domain-events' +import { DomainEventPublisherInterface, UserRolesChangedEvent } from '@standardnotes/domain-events' import { RoleName } from '@standardnotes/common' import { User } from '../../Domain/User/User' import { WebSocketsClientService } from './WebSocketsClientService' -import { WebSocketsConnectionRepositoryInterface } from '../../Domain/WebSockets/WebSocketsConnectionRepositoryInterface' import { DomainEventFactoryInterface } from '../../Domain/Event/DomainEventFactoryInterface' -import { AxiosInstance } from 'axios' -import { Logger } from 'winston' describe('WebSocketsClientService', () => { - let connectionIds: string[] let user: User let event: UserRolesChangedEvent - let webSocketsConnectionRepository: WebSocketsConnectionRepositoryInterface let domainEventFactory: DomainEventFactoryInterface - let httpClient: AxiosInstance - let logger: Logger + let domainEventPublisher: DomainEventPublisherInterface - let webSocketsApiUrl = 'http://test-websockets' - - const createService = () => - new WebSocketsClientService( - webSocketsConnectionRepository, - domainEventFactory, - httpClient, - webSocketsApiUrl, - logger, - ) + const createService = () => new WebSocketsClientService(domainEventFactory, domainEventPublisher) beforeEach(() => { - connectionIds = ['1', '2'] - user = { uuid: '123', email: 'test@test.com', @@ -45,43 +28,19 @@ describe('WebSocketsClientService', () => { event = {} as jest.Mocked - webSocketsConnectionRepository = {} as jest.Mocked - webSocketsConnectionRepository.findAllByUserUuid = jest.fn().mockReturnValue(connectionIds) - domainEventFactory = {} as jest.Mocked domainEventFactory.createUserRolesChangedEvent = jest.fn().mockReturnValue(event) - httpClient = {} as jest.Mocked - httpClient.request = jest.fn() - - logger = {} as jest.Mocked - logger.debug = jest.fn() + domainEventPublisher = {} as jest.Mocked + domainEventPublisher.publish = jest.fn() }) - it('should send a user role changed event to all user connections', async () => { + it('should request a message about a user role changed', async () => { await createService().sendUserRolesChangedEvent(user) expect(domainEventFactory.createUserRolesChangedEvent).toHaveBeenCalledWith('123', 'test@test.com', [ RoleName.ProUser, ]) - expect(httpClient.request).toHaveBeenCalledTimes(connectionIds.length) - connectionIds.map((id, index) => { - expect(httpClient.request).toHaveBeenNthCalledWith( - index + 1, - expect.objectContaining({ - method: 'POST', - url: `${webSocketsApiUrl}/${id}`, - data: JSON.stringify(event), - }), - ) - }) - }) - - it('should not send a user role changed event if web sockets api url not defined', async () => { - webSocketsApiUrl = '' - - await createService().sendUserRolesChangedEvent(user) - - expect(httpClient.request).not.toHaveBeenCalled() + expect(domainEventPublisher.publish).toHaveBeenCalled() }) }) diff --git a/packages/auth/src/Infra/WebSockets/WebSocketsClientService.ts b/packages/auth/src/Infra/WebSockets/WebSocketsClientService.ts index dd65cc7e1..f753c773a 100644 --- a/packages/auth/src/Infra/WebSockets/WebSocketsClientService.ts +++ b/packages/auth/src/Infra/WebSockets/WebSocketsClientService.ts @@ -1,52 +1,31 @@ -import { AxiosInstance } from 'axios' import { RoleName } from '@standardnotes/common' import { inject, injectable } from 'inversify' -import { Logger } from 'winston' import TYPES from '../../Bootstrap/Types' import { DomainEventFactoryInterface } from '../../Domain/Event/DomainEventFactoryInterface' import { User } from '../../Domain/User/User' -import { WebSocketsConnectionRepositoryInterface } from '../../Domain/WebSockets/WebSocketsConnectionRepositoryInterface' import { ClientServiceInterface } from '../../Domain/Client/ClientServiceInterface' +import { DomainEventPublisherInterface } from '@standardnotes/domain-events' @injectable() export class WebSocketsClientService implements ClientServiceInterface { constructor( - @inject(TYPES.WebSocketsConnectionRepository) - private webSocketsConnectionRepository: WebSocketsConnectionRepositoryInterface, @inject(TYPES.DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface, - @inject(TYPES.HTTPClient) private httpClient: AxiosInstance, - @inject(TYPES.WEBSOCKETS_API_URL) private webSocketsApiUrl: string, - @inject(TYPES.Logger) private logger: Logger, + @inject(TYPES.DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface, ) {} async sendUserRolesChangedEvent(user: User): Promise { - if (!this.webSocketsApiUrl) { - this.logger.debug('Web Sockets API url not defined. Skipped sending user role changed event.') - - return - } - - const userConnections = await this.webSocketsConnectionRepository.findAllByUserUuid(user.uuid) const event = this.domainEventFactory.createUserRolesChangedEvent( user.uuid, user.email, (await user.roles).map((role) => role.name) as RoleName[], ) - for (const connectionUuid of userConnections) { - await this.httpClient.request({ - method: 'POST', - url: `${this.webSocketsApiUrl}/${connectionUuid}`, - headers: { - Accept: 'text/plain', - 'Content-Type': 'text/plain', - }, - data: JSON.stringify(event), - validateStatus: - /* istanbul ignore next */ - (status: number) => status >= 200 && status < 500, - }) - } + await this.domainEventPublisher.publish( + this.domainEventFactory.createWebSocketMessageRequestedEvent({ + userUuid: user.uuid, + message: JSON.stringify(event), + }), + ) } }