From 8dcb887c10df2191da1b208bf7f6ffc5e8d79f76 Mon Sep 17 00:00:00 2001 From: Matthew Heroux Date: Wed, 8 Nov 2023 06:16:16 -0600 Subject: [PATCH] feat: add message factory (#509) Signed-off-by: hxtree --- common/config/rush/pnpm-lock.yaml | 24 ---------- common/config/rush/repo-state.json | 2 +- libraries/messaging-schemas/README.md | 9 ---- libraries/messaging-schemas/package.json | 12 +---- .../events/character-joined-party.event.ts | 10 ++++ .../dto/events/character-left-party.event.ts | 10 ++++ .../dto/events/character-level-up.event.ts | 7 ++- .../src/dto/messages/base-message.dto.ts | 13 ++++- .../dto/queries/character-level-reply.dto.ts | 13 +++++ .../queries/character-level-request.dto.ts | 10 ++++ .../src/dto/responses/base-response.dto.ts | 3 -- .../src/dto/responses/index.ts | 1 - libraries/messaging-schemas/src/index.ts | 2 +- .../src/message-factory.test.ts | 47 +++++++++++++++++++ .../messaging-schemas/src/message-factory.ts | 28 +++++++++++ libraries/nestjs-modules/README.md | 4 ++ .../nestjs-modules/src/sns/sns.service.ts | 3 ++ 17 files changed, 146 insertions(+), 52 deletions(-) create mode 100644 libraries/messaging-schemas/src/dto/events/character-joined-party.event.ts create mode 100644 libraries/messaging-schemas/src/dto/events/character-left-party.event.ts create mode 100644 libraries/messaging-schemas/src/dto/queries/character-level-reply.dto.ts create mode 100644 libraries/messaging-schemas/src/dto/queries/character-level-request.dto.ts delete mode 100644 libraries/messaging-schemas/src/dto/responses/base-response.dto.ts delete mode 100644 libraries/messaging-schemas/src/dto/responses/index.ts create mode 100644 libraries/messaging-schemas/src/message-factory.test.ts create mode 100644 libraries/messaging-schemas/src/message-factory.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 50b0b5817..b0fece67e 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -149,9 +149,6 @@ importers: '@cats-cradle/validation-schemas': specifier: workspace:* version: link:../validation-schemas - '@faker-js/faker': - specifier: ~7.6.0 - version: 7.6.0 '@types/lodash': specifier: ~4.14.200 version: 4.14.200 @@ -161,30 +158,9 @@ importers: class-transformer: specifier: 0.5.1 version: 0.5.1 - class-validator: - specifier: 0.13.2 - version: 0.13.2 - class-validator-jsonschema: - specifier: 3.1.1 - version: 3.1.1(class-transformer@0.5.1)(class-validator@0.13.2) - currency.js: - specifier: ~2.0.4 - version: 2.0.4 - json-schema: - specifier: ~0.4.0 - version: 0.4.0 - json-schema-faker: - specifier: ~0.5.0-rcv.46 - version: 0.5.0-rcv.46 lodash: specifier: ~4.17.21 version: 4.17.21 - openapi3-ts: - specifier: ~3.1.2 - version: 3.1.2 - reflect-metadata: - specifier: 0.1.13 - version: 0.1.13 uuid: specifier: ~9.0.1 version: 9.0.1 diff --git a/common/config/rush/repo-state.json b/common/config/rush/repo-state.json index d4f967a2c..1b1eea48a 100644 --- a/common/config/rush/repo-state.json +++ b/common/config/rush/repo-state.json @@ -1,5 +1,5 @@ // DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. { - "pnpmShrinkwrapHash": "ce36f6df36614b70a396c456d4d1b8847dbed82b", + "pnpmShrinkwrapHash": "35dba17277aaf467f308553d1ab16371c6243f4b", "preferredVersionsHash": "8ae0ba5bd02ec9c5763773a15e27aee08a6567f6" } diff --git a/libraries/messaging-schemas/README.md b/libraries/messaging-schemas/README.md index 10b046395..82a173b10 100644 --- a/libraries/messaging-schemas/README.md +++ b/libraries/messaging-schemas/README.md @@ -44,15 +44,6 @@ request. > (Publisher -> SQS) -> Consumer -> SQS -> Publisher -## TODO - -- Add a feature to make it easy to subscribe to events. -- Provision S3 bucket and place all history of all events into. -- Save all messages received to bucket. -- Indicate whether message was sent only for testing / debugging purposes and - should not contaminate history. -- Hydration that ties into api-client query might be nifty. - ## References - [Pub/Sub Messaging Schema](https://cloud.google.com/pubsub/docs/schemas) diff --git a/libraries/messaging-schemas/package.json b/libraries/messaging-schemas/package.json index 1b668683c..5601eafb7 100644 --- a/libraries/messaging-schemas/package.json +++ b/libraries/messaging-schemas/package.json @@ -20,22 +20,14 @@ "depcheck": "npx depcheck" }, "dependencies": { - "class-transformer": "0.5.1", - "class-validator": "0.13.2", "uuid": "~9.0.1", "lodash": "~4.17.21", "@types/lodash": "~4.14.200", "@cats-cradle/base-nodejs": "workspace:*", - "@faker-js/faker": "~7.6.0", - "reflect-metadata": "0.1.13", "@types/validator": "~13.7.10", - "currency.js": "~2.0.4", - "class-validator-jsonschema": "3.1.1", - "json-schema-faker": "~0.5.0-rcv.46", - "json-schema": "~0.4.0", - "openapi3-ts": "~3.1.2", "@cats-cradle/faker-factory": "workspace:*", - "@cats-cradle/validation-schemas": "workspace:*" + "@cats-cradle/validation-schemas": "workspace:*", + "class-transformer": "0.5.1" }, "devDependencies": { "@types/jest": "29.5.5", diff --git a/libraries/messaging-schemas/src/dto/events/character-joined-party.event.ts b/libraries/messaging-schemas/src/dto/events/character-joined-party.event.ts new file mode 100644 index 000000000..12d30c629 --- /dev/null +++ b/libraries/messaging-schemas/src/dto/events/character-joined-party.event.ts @@ -0,0 +1,10 @@ +import { IsUuidV4 } from '@cats-cradle/validation-schemas'; +import { BaseEventDto } from './base-event.dto'; + +export class CharacterJoinedParty extends BaseEventDto { + @IsUuidV4() + public instanceId: string; + + @IsUuidV4() + public characterId: string; +} diff --git a/libraries/messaging-schemas/src/dto/events/character-left-party.event.ts b/libraries/messaging-schemas/src/dto/events/character-left-party.event.ts new file mode 100644 index 000000000..a0f78f206 --- /dev/null +++ b/libraries/messaging-schemas/src/dto/events/character-left-party.event.ts @@ -0,0 +1,10 @@ +import { IsUuidV4 } from '@cats-cradle/validation-schemas'; +import { BaseEventDto } from './base-event.dto'; + +export class CharacterLeftPartyEvent extends BaseEventDto { + @IsUuidV4() + public instanceId: string; + + @IsUuidV4() + public characterId: string; +} diff --git a/libraries/messaging-schemas/src/dto/events/character-level-up.event.ts b/libraries/messaging-schemas/src/dto/events/character-level-up.event.ts index 7ac3e10a7..3db3bc09b 100644 --- a/libraries/messaging-schemas/src/dto/events/character-level-up.event.ts +++ b/libraries/messaging-schemas/src/dto/events/character-level-up.event.ts @@ -1,8 +1,11 @@ -import { Min, IsInt, IsUUID } from '@cats-cradle/validation-schemas'; +import { Min, IsInt, IsUuidV4 } from '@cats-cradle/validation-schemas'; import { BaseEventDto } from './base-event.dto'; export class CharacterLevelUpEvent extends BaseEventDto { - @IsUUID() + @IsUuidV4() + public instanceId: string; + + @IsUuidV4() public characterId: string; @IsInt() diff --git a/libraries/messaging-schemas/src/dto/messages/base-message.dto.ts b/libraries/messaging-schemas/src/dto/messages/base-message.dto.ts index bb28402c4..5193db3ea 100644 --- a/libraries/messaging-schemas/src/dto/messages/base-message.dto.ts +++ b/libraries/messaging-schemas/src/dto/messages/base-message.dto.ts @@ -1,5 +1,9 @@ import { kebabCase, truncate } from 'lodash'; -import { IsUuidV4, IsDateString } from '@cats-cradle/validation-schemas'; +import { + IsUuidV4, + IsDateString, + IsBoolean, +} from '@cats-cradle/validation-schemas'; import { pascalCase } from './pascalCase'; export abstract class BaseMessageDto { @@ -9,6 +13,13 @@ export abstract class BaseMessageDto { @IsDateString() public timestamp: string; + /** + * Whether sent only for testing/debugging purposes + * tests messages should not contaminate history long term + */ + @IsBoolean() + public test: boolean; + /** * Get autogenerated SNS topic name * The name is prefixed by STAGE_NAME and contains the class name. diff --git a/libraries/messaging-schemas/src/dto/queries/character-level-reply.dto.ts b/libraries/messaging-schemas/src/dto/queries/character-level-reply.dto.ts new file mode 100644 index 000000000..73d05540f --- /dev/null +++ b/libraries/messaging-schemas/src/dto/queries/character-level-reply.dto.ts @@ -0,0 +1,13 @@ +import { IsInt, IsUuidV4 } from '@cats-cradle/validation-schemas'; +import { BaseRequestDto } from './base-request.dto'; + +export class CharacterLevelReply extends BaseRequestDto { + @IsUuidV4() + public instanceId: string; + + @IsUuidV4() + public characterId: string; + + @IsInt() + public level: string; +} diff --git a/libraries/messaging-schemas/src/dto/queries/character-level-request.dto.ts b/libraries/messaging-schemas/src/dto/queries/character-level-request.dto.ts new file mode 100644 index 000000000..5a3ac85b4 --- /dev/null +++ b/libraries/messaging-schemas/src/dto/queries/character-level-request.dto.ts @@ -0,0 +1,10 @@ +import { IsUuidV4 } from '@cats-cradle/validation-schemas'; +import { BaseRequestDto } from './base-request.dto'; + +export class CharacterLevelRequest extends BaseRequestDto { + @IsUuidV4() + public instanceId: string; + + @IsUuidV4() + public characterId: string; +} diff --git a/libraries/messaging-schemas/src/dto/responses/base-response.dto.ts b/libraries/messaging-schemas/src/dto/responses/base-response.dto.ts deleted file mode 100644 index d312f2809..000000000 --- a/libraries/messaging-schemas/src/dto/responses/base-response.dto.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { BaseMessageDto } from '../messages/base-message.dto'; - -export abstract class BaseResponseDto extends BaseMessageDto {} diff --git a/libraries/messaging-schemas/src/dto/responses/index.ts b/libraries/messaging-schemas/src/dto/responses/index.ts deleted file mode 100644 index 56d503cd7..000000000 --- a/libraries/messaging-schemas/src/dto/responses/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './base-response.dto'; diff --git a/libraries/messaging-schemas/src/index.ts b/libraries/messaging-schemas/src/index.ts index e958faa22..876d97e3b 100644 --- a/libraries/messaging-schemas/src/index.ts +++ b/libraries/messaging-schemas/src/index.ts @@ -1,5 +1,5 @@ export * from './dto/messages'; export * from './dto/commands'; export * from './dto/events'; -export * from './dto/responses'; export { messageRegistry } from './registry/message-registry'; +export { MessageFactory } from './message-factory'; diff --git a/libraries/messaging-schemas/src/message-factory.test.ts b/libraries/messaging-schemas/src/message-factory.test.ts new file mode 100644 index 000000000..5fffa6203 --- /dev/null +++ b/libraries/messaging-schemas/src/message-factory.test.ts @@ -0,0 +1,47 @@ +import { BaseMessageDto } from './dto/messages'; +import { MessageFactory } from './message-factory'; +import { CharacterLevelUpEvent } from './dto/events/character-level-up.event'; + +describe('MessageFactory', () => { + let message: CharacterLevelUpEvent; + + beforeAll(async () => { + process.env.AWS_ACCOUNT_ID = '123456789012'; + process.env.AWS_REGION = 'us-east-2'; + process.env.STAGE_NAME = 'default'; + process.env.APP_NAME = 'message-broker'; + + message = await MessageFactory.create( + CharacterLevelUpEvent, + { level: 3 }, + ); + }); + + it('should populate messageId with uuid', async () => { + expect(message.messageId).toHaveLength(36); + }); + + it('should populate time', async () => { + expect(message.timestamp).toHaveLength(24); + }); + + it('should populate partial info', async () => { + expect(message.level).toBe(3); + }); + + it('should populate queueName', async () => { + expect(message.queueName).toBe( + 'default-message-broker-character-level-up-event-queue', + ); + }); + + it('should populate topicArn', async () => { + expect(message.topicArn).toBe( + 'arn:aws:sns:us-east-2:123456789012:default-character-level-up-event-topic', + ); + }); + + it('should not leave fields undefined if not defined', async () => { + expect(message.characterId).toBeUndefined(); + }); +}); diff --git a/libraries/messaging-schemas/src/message-factory.ts b/libraries/messaging-schemas/src/message-factory.ts new file mode 100644 index 000000000..23087bfc1 --- /dev/null +++ b/libraries/messaging-schemas/src/message-factory.ts @@ -0,0 +1,28 @@ +import { ClassConstructor, plainToInstance } from 'class-transformer'; +import { v4 } from 'uuid'; + +export class MessageFactory { + public static create( + constructor: ClassConstructor, + partial: Partial = {}, + ): Promise { + return new MessageFactory().create(constructor, partial); + } + + public async create( + constructor: ClassConstructor, + partial: Partial = {}, + ): Promise { + const now = new Date(); + const auto = { + messageId: v4(), + timestamp: now.toISOString(), + }; + const objectInstance = plainToInstance(constructor, { + ...partial, + ...auto, + }); + + return objectInstance as T; + } +} diff --git a/libraries/nestjs-modules/README.md b/libraries/nestjs-modules/README.md index 34c258fc0..2e6095fd4 100644 --- a/libraries/nestjs-modules/README.md +++ b/libraries/nestjs-modules/README.md @@ -20,6 +20,10 @@ are as follows: each test or after all test within a file depending on need to clear test data. +## TODO + +- Add a feature to make it easy to subscribe to events. + ## Documentation - [Jest Using with MongoDB](https://jestjs.io/docs/mongodb) diff --git a/libraries/nestjs-modules/src/sns/sns.service.ts b/libraries/nestjs-modules/src/sns/sns.service.ts index 3ca057821..90a70c6f1 100644 --- a/libraries/nestjs-modules/src/sns/sns.service.ts +++ b/libraries/nestjs-modules/src/sns/sns.service.ts @@ -6,6 +6,9 @@ import { SnsClientService } from './sns-client.service'; export class SnsService { constructor(private snsClientService: SnsClientService) {} + // TODO use message-schema instead and MessageFactory + // async publish(message: ClassConstructor, partial: any) {} + async publish(message: string, topicArn: string): Promise { try { /**