diff --git a/.vscode/settings.json b/.vscode/settings.json index 5fcb5cf4c..771540158 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -91,6 +91,7 @@ "Vachel", "Vallon", "VALLONS", + "vendia", "virtuals" ], "[dotenv]": { diff --git a/common/git-hooks/post-merge b/common/git-hooks/post-merge index 808f93daa..ca4162bf5 100755 --- a/common/git-hooks/post-merge +++ b/common/git-hooks/post-merge @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # Check if the merge was successful if [ "$1" -eq 0 ]; then diff --git a/services/email-service/.env.dist b/services/email-service/.env.dist index 569c3b57f..abfcb9124 100644 --- a/services/email-service/.env.dist +++ b/services/email-service/.env.dist @@ -6,6 +6,7 @@ BASE_URL= APP_NAME=email-service ENVIROMENT=local +EMAIL_ADDRESS= MONGO_DATABASE_URI= MONGO_DATABASE_USER= MONGO_DATABASE_PASSWORD= diff --git a/services/email-service/bin/app.ts b/services/email-service/bin/app.ts index 681a149db..982d6bdae 100755 --- a/services/email-service/bin/app.ts +++ b/services/email-service/bin/app.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node import * as cdk from 'aws-cdk-lib'; -import { EmailServiceStack } from '../stacks/email-service.stack'; +import { MainStack } from '../stacks/main.stack'; const app = new cdk.App(); -new EmailServiceStack(app, 'EmailServiceStack'); +new MainStack(app, 'EmailServiceMainStack'); diff --git a/services/email-service/openapi-spec.json b/services/email-service/openapi-spec.json index fcc2a2a46..7219575ea 100644 --- a/services/email-service/openapi-spec.json +++ b/services/email-service/openapi-spec.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "paths": { - "/v1/health": { + "/health": { "get": { "operationId": "HealthController_check", "parameters": [], @@ -104,7 +104,7 @@ } } }, - "/v1/email-message/user-account-created": { + "/email-message/user-account-created": { "post": { "operationId": "EmailMessageController_convert", "parameters": [ @@ -135,7 +135,7 @@ "responses": { "201": { "description": "" } } } }, - "/v1/email-message/user-forgotten-password-reset": { + "/email-message/user-forgotten-password-reset": { "post": { "operationId": "EmailMessageController_convertUserForgottenPasswordReset", "parameters": [ @@ -168,7 +168,7 @@ "responses": { "201": { "description": "" } } } }, - "/v1/email-message/stats": { + "/email-message/stats": { "get": { "operationId": "EmailMessageController_stats", "parameters": [], @@ -183,7 +183,13 @@ "contact": {} }, "tags": [], - "servers": [], + "servers": [ + { "url": "http://localhost:3000", "description": "Local" }, + { + "url": "https://nx7uv2rfy4.execute-api.us-east-2.amazonaws.com/default/v1/email-message/", + "description": "Sandbox" + } + ], "components": { "schemas": { "UserAccountCreatedDto": { diff --git a/services/email-service/src/index.ts b/services/email-service/src/index.ts index 11506665b..79e39a903 100644 --- a/services/email-service/src/index.ts +++ b/services/email-service/src/index.ts @@ -1,7 +1,7 @@ import { NestFactory } from '@nestjs/core'; import { ExpressAdapter } from '@nestjs/platform-express'; import serverlessExpress from '@vendia/serverless-express'; -import { ValidationPipe } from '@nestjs/common'; +import { ValidationPipe, VersioningType } from '@nestjs/common'; import { Context, Handler } from 'aws-lambda'; import express from 'express'; @@ -17,6 +17,11 @@ async function bootstrap() { new ExpressAdapter(expressApp), ); app.enableCors(); + app.enableVersioning({ + type: VersioningType.HEADER, + header: 'Accept-Version', + defaultVersion: '1', + }); app.useGlobalPipes( new ValidationPipe({ transform: true, diff --git a/services/email-service/src/main.ts b/services/email-service/src/main.ts index 750954ff4..80d0eb32b 100644 --- a/services/email-service/src/main.ts +++ b/services/email-service/src/main.ts @@ -9,14 +9,27 @@ async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableVersioning({ - type: VersioningType.URI, + type: VersioningType.HEADER, + header: 'Accept-Version', defaultVersion: '1', }); + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + transformOptions: { enableImplicitConversion: true }, + }), + ); + const config = new DocumentBuilder() .setTitle('@cats-cradles/email-service') .setDescription('An API for the email service') .setVersion('1.0') + .addServer('http://localhost:3000', 'Local') + .addServer( + 'https://nx7uv2rfy4.execute-api.us-east-2.amazonaws.com/default/v1/email-message/', + 'Sandbox', + ) .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, document); @@ -24,13 +37,6 @@ async function bootstrap() { // update the openapi-spec writeFileSync('./openapi-spec.json', JSON.stringify(document)); - app.useGlobalPipes( - new ValidationPipe({ - transform: true, - transformOptions: { enableImplicitConversion: true }, - }), - ); - await app.listen(3000); } bootstrap(); diff --git a/services/email-service/src/models/email-message/email-message.schema.ts b/services/email-service/src/models/email-message/email-message.schema.ts index e61978023..52d3d0c6d 100644 --- a/services/email-service/src/models/email-message/email-message.schema.ts +++ b/services/email-service/src/models/email-message/email-message.schema.ts @@ -23,7 +23,11 @@ export class EmailMessage { public data?: string; @IsEnum(StatusType) - @Prop() + @Prop({ + type: String, + enum: Object.values(StatusType), + default: StatusType.OPEN, + }) // Use the enum public status: StatusType; @IsDateString() diff --git a/services/email-service/src/modules/email-message/dto/template.dto.ts b/services/email-service/src/modules/email-message/dto/template.dto.ts index 079eb84f1..64c53b9ae 100644 --- a/services/email-service/src/modules/email-message/dto/template.dto.ts +++ b/services/email-service/src/modules/email-message/dto/template.dto.ts @@ -4,10 +4,6 @@ import { Expose } from 'class-transformer'; import { kebabCase } from 'lodash'; export abstract class TemplateDto { - abstract subject: string; - - abstract fromAddress: string; - @IsEmail() @ApiProperty({ description: 'The email recipient', diff --git a/services/email-service/src/modules/email-message/dto/user-account-created.dto.ts b/services/email-service/src/modules/email-message/dto/user-account-created.dto.ts index 7c26ffa4b..dce121e3c 100644 --- a/services/email-service/src/modules/email-message/dto/user-account-created.dto.ts +++ b/services/email-service/src/modules/email-message/dto/user-account-created.dto.ts @@ -1,13 +1,9 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsString } from 'class-validator'; +import { IsEmail, IsString } from 'class-validator'; import { Expose } from 'class-transformer'; import { TemplateDto } from './template.dto'; export class UserAccountCreatedDto extends TemplateDto { - readonly subject: 'User Account Created'; - - readonly fromAddress = 'contact@ouxsoft.com'; - @IsString() @ApiProperty({ description: 'The first name of the recipient', diff --git a/services/email-service/src/modules/email-message/dto/user-forgotten-password-reset.dto.ts b/services/email-service/src/modules/email-message/dto/user-forgotten-password-reset.dto.ts index 086c1275f..774af6de0 100644 --- a/services/email-service/src/modules/email-message/dto/user-forgotten-password-reset.dto.ts +++ b/services/email-service/src/modules/email-message/dto/user-forgotten-password-reset.dto.ts @@ -1,13 +1,9 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsString, IsFQDN } from 'class-validator'; +import { IsString, IsFQDN, IsEmail } from 'class-validator'; import { Expose } from 'class-transformer'; import { TemplateDto } from './template.dto'; export class UserForgottenPasswordResetDto extends TemplateDto { - readonly subject: 'Forgotten Password Reset'; - - readonly fromAddress = 'contact@ouxsoft.com'; - @IsString() @ApiProperty({ description: 'The username of the recipient', diff --git a/services/email-service/src/modules/email-message/email-message.controller.ts b/services/email-service/src/modules/email-message/email-message.controller.ts index de89956fe..e15f74218 100644 --- a/services/email-service/src/modules/email-message/email-message.controller.ts +++ b/services/email-service/src/modules/email-message/email-message.controller.ts @@ -1,5 +1,10 @@ import { - Get, Controller, Post, Body, Query, + Get, + Controller, + Post, + Body, + Query, + VERSION_NEUTRAL, } from '@nestjs/common'; import { ApiBody, ApiQuery } from '@nestjs/swagger'; import { UserAccountCreatedDto, UserForgottenPasswordResetDto } from './dto'; @@ -11,7 +16,7 @@ import { ActionType } from '../../models/email-message/action.type'; import { EngineService } from './engine.service'; import { QueueService } from './queue.service'; -@Controller({ path: 'email-message', version: ['1'] }) +@Controller({ path: 'email-message', version: ['1', VERSION_NEUTRAL] }) export class EmailMessageController { constructor( private _engineService: EngineService, @@ -25,11 +30,16 @@ export class EmailMessageController { @Body() data: UserAccountCreatedDto, @Query('action') action: ActionType, ): Promise { + const defaultData = { + subject: 'Forgotten Password Reset', + fromAddress: 'PUT_EMAIL_HERE@gmail.com', + }; + return this._engineService.process( action, UserAccountCreatedDto.slug, UserAccountCreatedTemplate, - data, + { ...data, ...defaultData }, ); } @@ -40,11 +50,16 @@ export class EmailMessageController { @Body() data: UserForgottenPasswordResetDto, @Query('action') action: ActionType, ): Promise { + const defaultData = { + subject: 'User Account Created', + fromAddress: 'PUT_EMAIL_HERE@gmail.com', + }; + return this._engineService.process( action, UserForgottenPasswordResetDto.slug, UserForgottenPasswordResetTemplate, - data, + { ...data, ...defaultData }, ); } diff --git a/services/email-service/src/modules/health/health.controller.ts b/services/email-service/src/modules/health/health.controller.ts index 273ccf3ca..ff249ae4d 100644 --- a/services/email-service/src/modules/health/health.controller.ts +++ b/services/email-service/src/modules/health/health.controller.ts @@ -1,7 +1,7 @@ -import { Controller, Get } from '@nestjs/common'; +import { Controller, Get, VERSION_NEUTRAL } from '@nestjs/common'; import { HealthCheckService, HealthCheck } from '@nestjs/terminus'; -@Controller('health') +@Controller({ path: 'health', version: ['1', VERSION_NEUTRAL] }) export class HealthController { constructor(private health: HealthCheckService) {} diff --git a/services/email-service/stacks/__snapshots__/email-service.stack.test.ts.snap b/services/email-service/stacks/__snapshots__/email-service.stack.test.ts.snap deleted file mode 100644 index 7a8f084f0..000000000 --- a/services/email-service/stacks/__snapshots__/email-service.stack.test.ts.snap +++ /dev/null @@ -1,477 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`EmailServiceStack should match snapshot test 1`] = ` -{ - Outputs: { - LocalhostAPIExample: { - Value: { - Fn::Join: [ - , - [ - https://, - { - Ref: emailservicestackemailservicestackapiendpointrestapiidssmParameter2AA3C49E, - }, - .execute-api., - { - Ref: AWS::Region, - }, - .amazonaws.com/default/v1/email-message/, - ], - ], - }, - }, - }, - Parameters: { - BootstrapVersion: { - Default: /cdk-bootstrap/hnb659fds/version, - Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip], - Type: AWS::SSM::Parameter::Value, - }, - emailservicestackemailservicestackapiendpointrestapiidssmParameter2AA3C49E: { - Default: web-api-gateway-rest-api-id, - Type: AWS::SSM::Parameter::Value, - }, - emailservicestackemailservicestackapiendpointrootresourceidssmParameterC9D307E0: { - Default: web-api-gateway-root-resource-id, - Type: AWS::SSM::Parameter::Value, - }, - emailservicestackemailservicestackapiendpointv1resourceidssmParameter26206A24: { - Default: web-api-gateway-v1-resource-id, - Type: AWS::SSM::Parameter::Value, - }, - lambdalayernestjslatestversionssmParameter: { - Default: lambda-layer-nestjs-latest-version, - Type: AWS::SSM::Parameter::Value, - }, - mongodatabasepasswordssmParameter: { - Default: MONGO_DATABASE_PASSWORD, - Type: AWS::SSM::Parameter::Value, - }, - mongodatabaseurissmParameter: { - Default: MONGO_DATABASE_URI, - Type: AWS::SSM::Parameter::Value, - }, - mongodatabaseuserssmParameter: { - Default: MONGO_DATABASE_USER, - Type: AWS::SSM::Parameter::Value, - }, - restapiidssmParameter: { - Default: web-api-gateway-rest-api-id, - Type: AWS::SSM::Parameter::Value, - }, - }, - Resources: { - LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A: { - DependsOn: [ - LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB, - LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB, - ], - Properties: { - Code: { - S3Bucket: { - Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, - }, - S3Key: UNIQUE_HASH.zip, - }, - Handler: index.handler, - Role: { - Fn::GetAtt: [ - LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB, - Arn, - ], - }, - Runtime: nodejs18.x, - Timeout: 900, - }, - Type: AWS::Lambda::Function, - }, - LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB: { - Properties: { - AssumeRolePolicyDocument: { - Statement: [ - { - Action: sts:AssumeRole, - Effect: Allow, - Principal: { - Service: lambda.amazonaws.com, - }, - }, - ], - Version: 2012-10-17, - }, - ManagedPolicyArns: [ - { - Fn::Join: [ - , - [ - arn:, - { - Ref: AWS::Partition, - }, - :iam::aws:policy/service-role/AWSLambdaBasicExecutionRole, - ], - ], - }, - ], - }, - Type: AWS::IAM::Role, - }, - LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB: { - Properties: { - PolicyDocument: { - Statement: [ - { - Action: [ - logs:PutRetentionPolicy, - logs:DeleteRetentionPolicy, - ], - Effect: Allow, - Resource: *, - }, - ], - Version: 2012-10-17, - }, - PolicyName: LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB, - Roles: [ - { - Ref: LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB, - }, - ], - }, - Type: AWS::IAM::Policy, - }, - emailservicestackemailservicestackapiendpointemailservicestackapiendpointapigwresource91001160: { - Properties: { - ParentId: { - Ref: emailservicestackemailservicestackapiendpointv1resourceidssmParameter26206A24, - }, - PathPart: email-message, - RestApiId: { - Ref: emailservicestackemailservicestackapiendpointrestapiidssmParameter2AA3C49E, - }, - }, - Type: AWS::ApiGateway::Resource, - }, - emailservicestackemailservicestackapiendpointemailservicestackapiendpointapigwresourceproxy710BB26B: { - Properties: { - ParentId: { - Ref: emailservicestackemailservicestackapiendpointemailservicestackapiendpointapigwresource91001160, - }, - PathPart: {proxy+}, - RestApiId: { - Ref: emailservicestackemailservicestackapiendpointrestapiidssmParameter2AA3C49E, - }, - }, - Type: AWS::ApiGateway::Resource, - }, - emailservicestackemailservicestackapiendpointemailservicestackapiendpointapigwresourceproxyANYApiPermissionMyTestStackemailservicestackemailservicestackapiendpointemailservicestackapiendpointmainapi87D71987ANYv1emailmessageproxy52342AFC: { - Properties: { - Action: lambda:InvokeFunction, - FunctionName: { - Fn::GetAtt: [ - emailservicestacknestjsNodeJsLambda2E7BD2F0, - Arn, - ], - }, - Principal: apigateway.amazonaws.com, - SourceArn: { - Fn::Join: [ - , - [ - arn:, - { - Ref: AWS::Partition, - }, - :execute-api:, - { - Ref: AWS::Region, - }, - :, - { - Ref: AWS::AccountId, - }, - :, - { - Ref: emailservicestackemailservicestackapiendpointrestapiidssmParameter2AA3C49E, - }, - /*/*/v1/email-message/*, - ], - ], - }, - }, - Type: AWS::Lambda::Permission, - }, - emailservicestackemailservicestackapiendpointemailservicestackapiendpointapigwresourceproxyANYApiPermissionTestMyTestStackemailservicestackemailservicestackapiendpointemailservicestackapiendpointmainapi87D71987ANYv1emailmessageproxy3DD9E49E: { - Properties: { - Action: lambda:InvokeFunction, - FunctionName: { - Fn::GetAtt: [ - emailservicestacknestjsNodeJsLambda2E7BD2F0, - Arn, - ], - }, - Principal: apigateway.amazonaws.com, - SourceArn: { - Fn::Join: [ - , - [ - arn:, - { - Ref: AWS::Partition, - }, - :execute-api:, - { - Ref: AWS::Region, - }, - :, - { - Ref: AWS::AccountId, - }, - :, - { - Ref: emailservicestackemailservicestackapiendpointrestapiidssmParameter2AA3C49E, - }, - /test-invoke-stage/*/v1/email-message/*, - ], - ], - }, - }, - Type: AWS::Lambda::Permission, - }, - emailservicestackemailservicestackapiendpointemailservicestackapiendpointapigwresourceproxyANYD46D497E: { - Properties: { - AuthorizationType: NONE, - HttpMethod: ANY, - Integration: { - IntegrationHttpMethod: POST, - Type: AWS_PROXY, - Uri: { - Fn::Join: [ - , - [ - arn:, - { - Ref: AWS::Partition, - }, - :apigateway:, - { - Ref: AWS::Region, - }, - :lambda:path/2015-03-31/functions/, - { - Fn::GetAtt: [ - emailservicestacknestjsNodeJsLambda2E7BD2F0, - Arn, - ], - }, - /invocations, - ], - ], - }, - }, - ResourceId: { - Ref: emailservicestackemailservicestackapiendpointemailservicestackapiendpointapigwresourceproxy710BB26B, - }, - RestApiId: { - Ref: emailservicestackemailservicestackapiendpointrestapiidssmParameter2AA3C49E, - }, - }, - Type: AWS::ApiGateway::Method, - }, - emailservicestackemailservicestackapiendpointemailservicestackapiendpointdeployment_UNIQUE_HASH: { - Properties: { - RestApiId: { - Ref: emailservicestackemailservicestackapiendpointrestapiidssmParameter2AA3C49E, - }, - StageName: default, - }, - Type: AWS::ApiGateway::Deployment, - }, - emailservicestacknestjsNodeJsLambda2E7BD2F0: { - DependsOn: [ - emailservicestacknestjsNodeJsLambdaServiceRoleDefaultPolicyBB097E87, - emailservicestacknestjsNodeJsLambdaServiceRole5649574F, - ], - Properties: { - Code: { - S3Bucket: { - Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, - }, - S3Key: UNIQUE_HASH.zip, - }, - Environment: { - Variables: { - AWS_ACCOUNT_ID: { - Ref: AWS::AccountId, - }, - BASE_URL: { - Fn::Join: [ - , - [ - https://, - { - Ref: restapiidssmParameter, - }, - .execute-api., - { - Ref: AWS::Region, - }, - .amazonaws.com/default/v1, - ], - ], - }, - MONGO_DATABASE_PASSWORD: { - Ref: mongodatabasepasswordssmParameter, - }, - MONGO_DATABASE_URI: { - Ref: mongodatabaseurissmParameter, - }, - MONGO_DATABASE_USER: { - Ref: mongodatabaseuserssmParameter, - }, - STAGE_NAME: default, - }, - }, - Handler: index.handler, - Layers: [ - { - Ref: lambdalayernestjslatestversionssmParameter, - }, - ], - MemorySize: 1024, - Role: { - Fn::GetAtt: [ - emailservicestacknestjsNodeJsLambdaServiceRole5649574F, - Arn, - ], - }, - Runtime: nodejs18.x, - Timeout: 30, - }, - Type: AWS::Lambda::Function, - }, - emailservicestacknestjsNodeJsLambdaLogRetentionB4D4A106: { - Properties: { - LogGroupName: { - Fn::Join: [ - , - [ - /aws/lambda/, - { - Ref: emailservicestacknestjsNodeJsLambda2E7BD2F0, - }, - ], - ], - }, - RetentionInDays: 1, - ServiceToken: { - Fn::GetAtt: [ - LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A, - Arn, - ], - }, - }, - Type: Custom::LogRetention, - }, - emailservicestacknestjsNodeJsLambdaServiceRole5649574F: { - Properties: { - AssumeRolePolicyDocument: { - Statement: [ - { - Action: sts:AssumeRole, - Effect: Allow, - Principal: { - Service: lambda.amazonaws.com, - }, - }, - ], - Version: 2012-10-17, - }, - ManagedPolicyArns: [ - { - Fn::Join: [ - , - [ - arn:, - { - Ref: AWS::Partition, - }, - :iam::aws:policy/service-role/AWSLambdaBasicExecutionRole, - ], - ], - }, - ], - }, - Type: AWS::IAM::Role, - }, - emailservicestacknestjsNodeJsLambdaServiceRoleDefaultPolicyBB097E87: { - Properties: { - PolicyDocument: { - Statement: [ - { - Action: [ - ses:SendEmail, - ses:SendRawEmail, - ses:SendTemplatedEmail, - ], - Effect: Allow, - Resource: { - Fn::Join: [ - , - [ - arn:aws:ses:, - { - Ref: AWS::Region, - }, - :, - { - Ref: AWS::AccountId, - }, - :identity/contact@ouxsoft.com, - ], - ], - }, - }, - ], - Version: 2012-10-17, - }, - PolicyName: emailservicestacknestjsNodeJsLambdaServiceRoleDefaultPolicyBB097E87, - Roles: [ - { - Ref: emailservicestacknestjsNodeJsLambdaServiceRole5649574F, - }, - ], - }, - Type: AWS::IAM::Policy, - }, - }, - Rules: { - CheckBootstrapVersion: { - Assertions: [ - { - Assert: { - Fn::Not: [ - { - Fn::Contains: [ - [ - 1, - 2, - 3, - 4, - 5, - ], - { - Ref: BootstrapVersion, - }, - ], - }, - ], - }, - AssertDescription: CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI., - }, - ], - }, - }, -} -`; diff --git a/services/email-service/stacks/email-service.stack.test.ts b/services/email-service/stacks/email-service.stack.test.ts deleted file mode 100644 index be55a5058..000000000 --- a/services/email-service/stacks/email-service.stack.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as cdk from 'aws-cdk-lib'; -import { Template } from 'aws-cdk-lib/assertions'; -import { EmailServiceStack } from './email-service.stack'; - -describe('EmailServiceStack', () => { - it('should match snapshot test', () => { - const app = new cdk.App(); - const stack = new EmailServiceStack(app, 'MyTestStack'); - const template = Template.fromStack(stack); - - // TODO consider reworking to use DNS instead of incrementing and deploying each time - const customSnapshotSerializer = { - test: (val: any) => typeof val === 'string', - print: (val: any) => val - .replace(/deployment\d{8}T\d{9}Z[^:]+/, 'deployment_UNIQUE_HASH') - .replace(/[a-fA-F0-9]+\.zip/, 'UNIQUE_HASH.zip'), - }; - expect.addSnapshotSerializer(customSnapshotSerializer); - - expect(template.toJSON()).toMatchSnapshot(); - }); -}); diff --git a/services/email-service/stacks/email-service.stack.ts b/services/email-service/stacks/email-service.stack.ts index 919cc0b0d..eefa58f91 100644 --- a/services/email-service/stacks/email-service.stack.ts +++ b/services/email-service/stacks/email-service.stack.ts @@ -1,20 +1,19 @@ import { Microservice } from '@cats-cradle/constructs'; import { Construct } from 'constructs'; import * as cdk from 'aws-cdk-lib'; -import { StackProps } from 'aws-cdk-lib'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as sns from 'aws-cdk-lib/aws-sns'; import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions'; import { EmailSendCommand } from '@cats-cradle/messaging-schemas'; import * as path from 'path'; -export class EmailServiceStack extends cdk.Stack { - constructor(scope: Construct, id: string, props?: StackProps) { +export class EmailServiceStack extends cdk.NestedStack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // setup ses const SES_ACCOUNT = this.account; - const SES_EMAIL_FROM = 'contact@ouxsoft.com'; + const SES_RECIPIENTS_ALLOWED = '*'; const SES_REGION = this.region; // deploy lambda @@ -34,31 +33,31 @@ export class EmailServiceStack extends cdk.Stack { 'ses:SendTemplatedEmail', ], resources: [ - `arn:aws:ses:${SES_REGION}:${SES_ACCOUNT}:identity/${SES_EMAIL_FROM}`, + `arn:aws:ses:${SES_REGION}:${SES_ACCOUNT}:identity/${SES_RECIPIENTS_ALLOWED}`, ], }), ); - // TODO should be a queue to already existing topic - // SendEmailCommandTopic - // const sendEmailCommandTopic = new sns.Topic( - // this, - // 'email-send-command-topic', - // { - // topicName: EmailSendCommand.topicName(), - // displayName: EmailSendCommand.topicName(), - // }, - // ); - // sendEmailCommandTopic.addSubscription( - // new subscriptions.LambdaSubscription(nodeJsFunction),cd . - // ); - - // new cdk.CfnOutput(this, 'endEmailCommandTopicARN', { - // value: sendEmailCommandTopic.topicArn, - // }); - new cdk.CfnOutput(this, 'Localhost API Example', { value: `${microservice.getBaseUrl()}/`, }); } } + +// TODO should be a queue to already existing topic +// SendEmailCommandTopic +// const sendEmailCommandTopic = new sns.Topic( +// this, +// 'email-send-command-topic', +// { +// topicName: EmailSendCommand.topicName(), +// displayName: EmailSendCommand.topicName(), +// }, +// ); +// sendEmailCommandTopic.addSubscription( +// new subscriptions.LambdaSubscription(nodeJsFunction),cd . +// ); + +// new cdk.CfnOutput(this, 'endEmailCommandTopicARN', { +// value: sendEmailCommandTopic.topicArn, +// }); diff --git a/services/email-service/stacks/email-verification.stack.ts b/services/email-service/stacks/email-verification.stack.ts new file mode 100644 index 000000000..1c6b887b2 --- /dev/null +++ b/services/email-service/stacks/email-verification.stack.ts @@ -0,0 +1,31 @@ +import * as cdk from 'aws-cdk-lib'; +import * as ses from 'aws-cdk-lib/aws-ses'; +import { Construct } from 'constructs'; +import * as ssm from 'aws-cdk-lib/aws-ssm'; + +export class EmailVerificationStack extends cdk.NestedStack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // TODO: Currently this is manually set in SSM + + // Create an Amazon SES Identity (email address) and verify it + const senderEmailAddress = ssm.StringParameter.fromStringParameterAttributes( + scope, + 'sender-email-address', + { + parameterName: 'SENDER_EMAIL_ADDRESS', + }, + ).stringValue; + + const sesIdentity = new ses.CfnEmailIdentity(this, 'SesIdentity', { + emailIdentity: senderEmailAddress, + }); + + new cdk.CfnOutput(this, 'EmailAddressOutput', { + value: senderEmailAddress, + }); + + // Need to check email the verification status of email + } +} diff --git a/services/email-service/stacks/main.stack.ts b/services/email-service/stacks/main.stack.ts new file mode 100644 index 000000000..45de1e1f1 --- /dev/null +++ b/services/email-service/stacks/main.stack.ts @@ -0,0 +1,14 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import { EmailVerificationStack } from './email-verification.stack'; +import { EmailServiceStack } from './email-service.stack'; + +export class MainStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + new EmailVerificationStack(this, 'EmailVerificationStack'); + + new EmailServiceStack(this, 'EmailServiceStack'); + } +} diff --git a/services/luck-by-dice/openapi-spec.json b/services/luck-by-dice/openapi-spec.json index f2ae58bf4..21633cd19 100644 --- a/services/luck-by-dice/openapi-spec.json +++ b/services/luck-by-dice/openapi-spec.json @@ -128,11 +128,17 @@ "info": { "title": "@cats-cradle/luck-by-dice", "description": "An API Simulating dice rolls that can be effected by luck from dice notation", - "version": "2.0.8", + "version": "2.0.9", "contact": {} }, "tags": [], - "servers": [], + "servers": [ + { "url": "http://localhost:3000", "description": "Local" }, + { + "url": "https://nx7uv2rfy4.execute-api.us-east-2.amazonaws.com/default/v1/luck-by-dice/", + "description": "Sandbox" + } + ], "components": { "securitySchemes": { "lambda": { diff --git a/services/luck-by-dice/src/main.ts b/services/luck-by-dice/src/main.ts index c937f04e5..cd8013a61 100644 --- a/services/luck-by-dice/src/main.ts +++ b/services/luck-by-dice/src/main.ts @@ -19,6 +19,11 @@ async function bootstrap() { .setTitle(pkg.name) .setVersion(pkg.version) .setDescription(pkg.description) + .addServer('http://localhost:3000', 'Local') + .addServer( + 'https://nx7uv2rfy4.execute-api.us-east-2.amazonaws.com/default/v1/luck-by-dice/', + 'Sandbox', + ) .addApiKey( { type: 'apiKey',