From 441db63c7c6805c1a9a9c7d1f6d12473ae922596 Mon Sep 17 00:00:00 2001 From: Kishore <42832651+kishore03109@users.noreply.github.com> Date: Thu, 13 Apr 2023 13:53:16 +0800 Subject: [PATCH 01/12] feat(DomainAssociation): mock calls --- src/services/identity/LaunchClient.ts | 132 +++++++++++++++++++++-- src/services/identity/LaunchesService.ts | 6 +- 2 files changed, 127 insertions(+), 11 deletions(-) diff --git a/src/services/identity/LaunchClient.ts b/src/services/identity/LaunchClient.ts index 406dfad65..ec445443f 100644 --- a/src/services/identity/LaunchClient.ts +++ b/src/services/identity/LaunchClient.ts @@ -8,10 +8,15 @@ import { GetDomainAssociationCommandOutput, SubDomainSetting, } from "@aws-sdk/client-amplify" -import { options } from "joi" +import { SubDomain } from "aws-sdk/clients/amplify" -import logger from "@root/logger/logger" +import config from "@config/config" +// create a new interface that extends GetDomainAssociationCommandInput +interface MockGetDomainAssociationCommandInput + extends GetDomainAssociationCommandInput { + subDomainSettings: SubDomainSetting[] +} class LaunchClient { private readonly amplifyClient: InstanceType @@ -35,21 +40,130 @@ class LaunchClient { sendCreateDomainAssociation = ( input: CreateDomainAssociationCommandInput - ): Promise => - this.amplifyClient.send(new CreateDomainAssociationCommand(input)) + ): Promise => { + if (this.isTestEnv()) { + return this.mockCreateDomainAssociationOutput(input) + } + const output = this.amplifyClient.send( + new CreateDomainAssociationCommand(input) + ) + return output + } createGetDomainAssociationCommandInput = ( appId: string, - domainName: string - ): GetDomainAssociationCommandInput => ({ + domainName: string, + subDomainSettings: SubDomainSetting[] + ): + | GetDomainAssociationCommandInput + | MockGetDomainAssociationCommandInput => ({ appId, domainName, + ...(this.isTestEnv() && { subDomainSettings }), }) sendGetDomainAssociationCommand = ( - input: GetDomainAssociationCommandInput - ): Promise => - this.amplifyClient.send(new GetDomainAssociationCommand(input)) + input: + | GetDomainAssociationCommandInput + | MockGetDomainAssociationCommandInput + ): Promise => { + const isMockInput = "subDomainSettings" in input + if (isMockInput) { + // handle mock input + return this.mockGetDomainAssociationOutput(input) + } + return this.amplifyClient.send(new GetDomainAssociationCommand(input)) + } + + /** + * The rate limit for Create Domain Association is 10 per hour. + * We want to limit interference with operations, as such we mock this call during development. + * @returns Mocked output for CreateDomainAssociationCommand + */ + mockCreateDomainAssociationOutput = ( + input: CreateDomainAssociationCommandInput + ): Promise => { + const mockResponse: CreateDomainAssociationCommandOutput = { + $metadata: { + httpStatusCode: 200, + }, + domainAssociation: { + autoSubDomainCreationPatterns: [], + autoSubDomainIAMRole: undefined, + certificateVerificationDNSRecord: undefined, + domainAssociationArn: `arn:aws:amplify:ap-southeast-1:11111:apps/${input.appId}/domains/${input.domainName}`, + domainName: input.domainName, + domainStatus: "CREATING", + enableAutoSubDomain: false, + statusReason: undefined, + subDomains: undefined, + }, + } + + const subDomainSettingsList = input.subDomainSettings + if (!subDomainSettingsList || subDomainSettingsList.length === 0) { + return Promise.resolve(mockResponse) + } + + const subDomains: SubDomain[] = this.getSubDomains(subDomainSettingsList) + + // We know that domainAssociation is not undefined, so we can use the non-null assertion operator + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + mockResponse.domainAssociation!.subDomains = subDomains + return Promise.resolve(mockResponse) + } + + private isTestEnv() { + return config.get("env") === "test" || config.get("env") === "dev" + } + + private getSubDomains(subDomainList: SubDomainSetting[], hasCreated = false) { + const subDomainPrefixList = subDomainList + .map((subDomain) => subDomain.prefix) + .filter((prefix) => prefix !== undefined) as string[] + + const subDomains: SubDomain[] = [] + subDomainPrefixList.forEach((subDomainPrefix) => { + subDomains.push({ + dnsRecord: `${subDomainPrefix} CNAME ${ + hasCreated ? "test.cloudfront.net" : "" + }`, + subDomainSetting: { + branchName: "master", + prefix: subDomainPrefix, + }, + verified: false, + }) + }) + return subDomains + } + + mockGetDomainAssociationOutput( + input: MockGetDomainAssociationCommandInput + ): Promise { + const mockResponse: GetDomainAssociationCommandOutput = { + $metadata: { + httpStatusCode: 200, + }, + domainAssociation: { + certificateVerificationDNSRecord: `testcert.${input.domainName}. CNAME testcert.acm-validations.aws.`, + domainAssociationArn: `arn:aws:amplify:ap-southeast-1:11111:apps/${input.appId}/domains/${input.domainName}`, + domainName: input.domainName, + domainStatus: "PENDING_VERIFICATION", + enableAutoSubDomain: false, + subDomains: undefined, + statusReason: undefined, + }, + } + const hasCreated = true // this is a get call, assume domain has already been created + const subDomains = this.getSubDomains(input.subDomainSettings, hasCreated) + + // We know that domainAssociation is not undefined, so we can use the non-null assertion operator + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + mockResponse.domainAssociation!.subDomains = subDomains + + return Promise.resolve(mockResponse) + } } export default LaunchClient diff --git a/src/services/identity/LaunchesService.ts b/src/services/identity/LaunchesService.ts index 85c56a3f5..8ef6c0c09 100644 --- a/src/services/identity/LaunchesService.ts +++ b/src/services/identity/LaunchesService.ts @@ -192,11 +192,13 @@ export class LaunchesService { getDomainAssociationRecord = async ( domainName: string, - appId: string + appId: string, + subDomainSettings: SubDomainSetting[] ): Promise => { const getDomainAssociationOptions = this.launchClient.createGetDomainAssociationCommandInput( appId, - domainName + domainName, + subDomainSettings ) return this.launchClient.sendGetDomainAssociationCommand( getDomainAssociationOptions From efd424503689a93a46a60b40e361fc4de32a573e Mon Sep 17 00:00:00 2001 From: Kishore <42832651+kishore03109@users.noreply.github.com> Date: Thu, 13 Apr 2023 13:54:55 +0800 Subject: [PATCH 02/12] feat(timeout): dont have to useTimeout during dev since we are... mocking calls to amplify now --- src/services/identity/LaunchClient.ts | 2 +- src/services/infra/InfraService.ts | 35 ++++++++++++++++----------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/services/identity/LaunchClient.ts b/src/services/identity/LaunchClient.ts index ec445443f..99d155845 100644 --- a/src/services/identity/LaunchClient.ts +++ b/src/services/identity/LaunchClient.ts @@ -10,7 +10,7 @@ import { } from "@aws-sdk/client-amplify" import { SubDomain } from "aws-sdk/clients/amplify" -import config from "@config/config" +import { config } from "@config/config" // create a new interface that extends GetDomainAssociationCommandInput interface MockGetDomainAssociationCommandInput diff --git a/src/services/infra/InfraService.ts b/src/services/infra/InfraService.ts index f23ea75c9..5c5715b2e 100644 --- a/src/services/infra/InfraService.ts +++ b/src/services/infra/InfraService.ts @@ -2,6 +2,8 @@ import { SubDomainSettings } from "aws-sdk/clients/amplify" import Joi from "joi" import { Err, err, errAsync, Ok, ok, okAsync, Result } from "neverthrow" +import { config } from "@config/config" + import { Site } from "@database/models" import { User } from "@database/models/User" import { @@ -226,23 +228,28 @@ export default class InfraService { ) // Get DNS records from Amplify - /** - * note: we wait for ard 90 sec as there is a time taken - * for amplify to generate the certification manager in the first place - * This is a dirty workaround for now, and will cause issues when we integrate - * this directly within the Isomer CMS. - * todo: push this check into a queue-like system when integrating this with cms - */ - await new Promise((resolve) => setTimeout(resolve, 90000)) - - /** - * todo: add some level of retry logic if get domain association command - * does not contain the DNS redirections info. - */ + const isTestEnv = + config.get("env") === "test" || config.get("env") === "dev" + // since we mock values during development, we don't have to await for the dns records + if (!isTestEnv) { + /** + * note: we wait for ard 90 sec as there is a time taken + * for amplify to generate the certification manager in the first place + * This is a dirty workaround for now, and will cause issues when we integrate + * this directly within the Isomer CMS. + * todo: push this check into a queue-like system when integrating this with cms + */ + await new Promise((resolve) => setTimeout(resolve, 90000)) + /** + * todo: add some level of retry logic if get domain association command + * does not contain the DNS redirections info. + */ + } const dnsInfo = await this.launchesService.getDomainAssociationRecord( primaryDomain, - appId + appId, + subDomainSettings ) const certificationRecord = this.parseDNSRecords( From 84125dfc0cbf97e3e1bcd4703eb3e4ba25d9da4b Mon Sep 17 00:00:00 2001 From: Kishore <42832651+kishore03109@users.noreply.github.com> Date: Fri, 14 Apr 2023 12:00:46 +0800 Subject: [PATCH 03/12] style(var name): rename var --- src/services/identity/LaunchClient.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/identity/LaunchClient.ts b/src/services/identity/LaunchClient.ts index 99d155845..2f951a786 100644 --- a/src/services/identity/LaunchClient.ts +++ b/src/services/identity/LaunchClient.ts @@ -155,8 +155,8 @@ class LaunchClient { statusReason: undefined, }, } - const hasCreated = true // this is a get call, assume domain has already been created - const subDomains = this.getSubDomains(input.subDomainSettings, hasCreated) + const isCreated = true // this is a get call, assume domain has already been created + const subDomains = this.getSubDomains(input.subDomainSettings, isCreated) // We know that domainAssociation is not undefined, so we can use the non-null assertion operator // eslint-disable-next-line @typescript-eslint/no-non-null-assertion From b99eef4cc52e86095c3f03dde2d4b646ac41b90f Mon Sep 17 00:00:00 2001 From: Kishore <42832651+kishore03109@users.noreply.github.com> Date: Fri, 14 Apr 2023 12:14:13 +0800 Subject: [PATCH 04/12] fix(launch client): use generice util function --- src/services/identity/LaunchClient.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/identity/LaunchClient.ts b/src/services/identity/LaunchClient.ts index 2f951a786..5128c05b9 100644 --- a/src/services/identity/LaunchClient.ts +++ b/src/services/identity/LaunchClient.ts @@ -10,7 +10,7 @@ import { } from "@aws-sdk/client-amplify" import { SubDomain } from "aws-sdk/clients/amplify" -import { config } from "@config/config" +import { isSecure } from "@root/utils/auth-utils" // create a new interface that extends GetDomainAssociationCommandInput interface MockGetDomainAssociationCommandInput @@ -114,7 +114,7 @@ class LaunchClient { } private isTestEnv() { - return config.get("env") === "test" || config.get("env") === "dev" + return isSecure } private getSubDomains(subDomainList: SubDomainSetting[], hasCreated = false) { From e0ce6091dcc0612139711d7231c43a278b9fea74 Mon Sep 17 00:00:00 2001 From: Kishore <42832651+kishore03109@users.noreply.github.com> Date: Fri, 14 Apr 2023 12:25:49 +0800 Subject: [PATCH 05/12] style(launch client): input return type for function calls --- src/services/identity/LaunchClient.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/services/identity/LaunchClient.ts b/src/services/identity/LaunchClient.ts index 5128c05b9..4ca3ade32 100644 --- a/src/services/identity/LaunchClient.ts +++ b/src/services/identity/LaunchClient.ts @@ -113,11 +113,14 @@ class LaunchClient { return Promise.resolve(mockResponse) } - private isTestEnv() { + private isTestEnv(): boolean { return isSecure } - private getSubDomains(subDomainList: SubDomainSetting[], hasCreated = false) { + private getSubDomains( + subDomainList: SubDomainSetting[], + hasCreated = false + ): SubDomain[] { const subDomainPrefixList = subDomainList .map((subDomain) => subDomain.prefix) .filter((prefix) => prefix !== undefined) as string[] From d796b4b445002d849382531bce3fdd61682966d6 Mon Sep 17 00:00:00 2001 From: Kishore <42832651+kishore03109@users.noreply.github.com> Date: Fri, 14 Apr 2023 14:01:32 +0800 Subject: [PATCH 06/12] feat(env var ): use env var to denote mocking amplify calls --- .env-example | 1 + src/config/config.ts | 6 ++++++ src/services/identity/LaunchClient.ts | 10 +++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.env-example b/.env-example index 3c23c0dd6..da44309d5 100644 --- a/.env-example +++ b/.env-example @@ -26,6 +26,7 @@ export SITE_CREATE_FORM_KEY="" # generate your own access key and secret access key from AWS export AWS_ACCESS_KEY_ID="" export AWS_SECRET_ACCESS_KEY="" +export MOCK_AMPLIFY_CREATE_DOMAIN_CALLS="true" # Required to run end-to-end tests export E2E_TEST_REPO="e2e-test-repo" diff --git a/src/config/config.ts b/src/config/config.ts index be5bd885f..6c7ccaa1a 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -172,6 +172,12 @@ const config = convict({ format: String, default: "", }, + mockAmplifyCreateDomainAssociationCalls: { + doc: "Mock createDomainAssociation calls to Amplify ", + env: "MOCK_AMPLIFY_CREATE_DOMAIN_ASSOCIATION_CALLS", + format: "required-boolean", + default: true, + }, }, sqs: { incomingQueueUrl: { diff --git a/src/services/identity/LaunchClient.ts b/src/services/identity/LaunchClient.ts index 4ca3ade32..f1b587f72 100644 --- a/src/services/identity/LaunchClient.ts +++ b/src/services/identity/LaunchClient.ts @@ -10,7 +10,7 @@ import { } from "@aws-sdk/client-amplify" import { SubDomain } from "aws-sdk/clients/amplify" -import { isSecure } from "@root/utils/auth-utils" +import { config } from "@config/config" // create a new interface that extends GetDomainAssociationCommandInput interface MockGetDomainAssociationCommandInput @@ -41,7 +41,7 @@ class LaunchClient { sendCreateDomainAssociation = ( input: CreateDomainAssociationCommandInput ): Promise => { - if (this.isTestEnv()) { + if (this.shouldMockAmplifyCreateDomainCalls()) { return this.mockCreateDomainAssociationOutput(input) } const output = this.amplifyClient.send( @@ -59,7 +59,7 @@ class LaunchClient { | MockGetDomainAssociationCommandInput => ({ appId, domainName, - ...(this.isTestEnv() && { subDomainSettings }), + ...(this.shouldMockAmplifyCreateDomainCalls() && { subDomainSettings }), }) sendGetDomainAssociationCommand = ( @@ -113,8 +113,8 @@ class LaunchClient { return Promise.resolve(mockResponse) } - private isTestEnv(): boolean { - return isSecure + private shouldMockAmplifyCreateDomainCalls(): boolean { + return config.get("aws.amplify.mockAmplifyCreateDomainAssociationCalls") } private getSubDomains( From 9f6870cf956f41a56a9af503b704cd8d2203e4dc Mon Sep 17 00:00:00 2001 From: Kishore <42832651+kishore03109@users.noreply.github.com> Date: Mon, 17 Apr 2023 14:03:50 +0800 Subject: [PATCH 07/12] style(launch client): improve code structure for clarity --- src/services/identity/LaunchClient.ts | 30 ++++++++++++--------------- src/services/infra/InfraService.ts | 2 +- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/services/identity/LaunchClient.ts b/src/services/identity/LaunchClient.ts index f1b587f72..b1d249b26 100644 --- a/src/services/identity/LaunchClient.ts +++ b/src/services/identity/LaunchClient.ts @@ -119,31 +119,33 @@ class LaunchClient { private getSubDomains( subDomainList: SubDomainSetting[], - hasCreated = false + isCreated = false ): SubDomain[] { - const subDomainPrefixList = subDomainList + const subDomains = subDomainList .map((subDomain) => subDomain.prefix) - .filter((prefix) => prefix !== undefined) as string[] - - const subDomains: SubDomain[] = [] - subDomainPrefixList.forEach((subDomainPrefix) => { - subDomains.push({ + .filter((prefix) => prefix !== undefined && prefix !== null) + .map((subDomainPrefix) => ({ dnsRecord: `${subDomainPrefix} CNAME ${ - hasCreated ? "test.cloudfront.net" : "" + isCreated ? "test.cloudfront.net" : "" }`, subDomainSetting: { branchName: "master", prefix: subDomainPrefix, }, verified: false, - }) - }) + })) as SubDomain[] return subDomains } mockGetDomainAssociationOutput( input: MockGetDomainAssociationCommandInput ): Promise { + const isSubDomainCreated = true // this is a `get` call, assume domain has already been created + const subDomains = this.getSubDomains( + input.subDomainSettings, + isSubDomainCreated + ) + const mockResponse: GetDomainAssociationCommandOutput = { $metadata: { httpStatusCode: 200, @@ -154,16 +156,10 @@ class LaunchClient { domainName: input.domainName, domainStatus: "PENDING_VERIFICATION", enableAutoSubDomain: false, - subDomains: undefined, + subDomains, statusReason: undefined, }, } - const isCreated = true // this is a get call, assume domain has already been created - const subDomains = this.getSubDomains(input.subDomainSettings, isCreated) - - // We know that domainAssociation is not undefined, so we can use the non-null assertion operator - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - mockResponse.domainAssociation!.subDomains = subDomains return Promise.resolve(mockResponse) } diff --git a/src/services/infra/InfraService.ts b/src/services/infra/InfraService.ts index 5c5715b2e..92c94819b 100644 --- a/src/services/infra/InfraService.ts +++ b/src/services/infra/InfraService.ts @@ -375,7 +375,7 @@ export default class InfraService { await Promise.all( messages.map(async (message) => { const site = await this.sitesService.getBySiteName(message.repoName) - if (!site || site.isErr()) { + if (site.isErr()) { return } const isSuccess = message.status === SiteLaunchLambdaStatus.SUCCESS From 1260b7e201f3f77e8b10cb6ca74fd903564567bf Mon Sep 17 00:00:00 2001 From: Kishore <42832651+kishore03109@users.noreply.github.com> Date: Wed, 19 Apr 2023 11:12:06 +0800 Subject: [PATCH 08/12] perf(form sg site launch): non-blocking calls --- src/routes/formsgSiteLaunch.ts | 54 +++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/routes/formsgSiteLaunch.ts b/src/routes/formsgSiteLaunch.ts index 12a32a07b..60fa3c3c8 100644 --- a/src/routes/formsgSiteLaunch.ts +++ b/src/routes/formsgSiteLaunch.ts @@ -208,7 +208,7 @@ export class FormsgSiteLaunchRouter { ) || [] res.sendStatus(200) // we have received the form and obtained relevant field - await this.handleSiteLaunchResults(formResponses, submissionId) + this.handleSiteLaunchResults(formResponses, submissionId) } sendLaunchError = async ( @@ -301,33 +301,39 @@ export class FormsgSiteLaunchRouter { formResponses: FormResponsesProps[], submissionId: string ) { - const launchResults = await Promise.all( - formResponses.map(this.launchSiteFromForm) - ) - const successResults: DnsRecordsEmailProps[] = [] - launchResults.forEach((launchResult) => { - if (launchResult.isOk()) { - successResults.push(launchResult.value) - } - }) + try { + const launchResults = await Promise.all( + formResponses.map(this.launchSiteFromForm) + ) + const successResults: DnsRecordsEmailProps[] = [] + launchResults.forEach((launchResult) => { + if (launchResult.isOk()) { + successResults.push(launchResult.value) + } + }) - await this.sendDNSRecords(submissionId, successResults) + await this.sendDNSRecords(submissionId, successResults) - const failureResults: LaunchFailureEmailProps[] = [] + const failureResults: LaunchFailureEmailProps[] = [] - launchResults.forEach((launchResult) => { - if (launchResult.isErr() && launchResult.error) { - failureResults.push(launchResult.error) - return - } - if (launchResult.isErr()) { - failureResults.push({ - error: "Unknown error", - }) - } - }) + launchResults.forEach((launchResult) => { + if (launchResult.isErr() && launchResult.error) { + failureResults.push(launchResult.error) + return + } + if (launchResult.isErr()) { + failureResults.push({ + error: "Unknown error", + }) + } + }) - await this.sendLaunchError(submissionId, failureResults) + await this.sendLaunchError(submissionId, failureResults) + } catch (e) { + logger.error( + `Something unexpected went wrong when launching sites from form submission ${submissionId}. Error: ${e}` + ) + } } getRouter() { From cd97d22f5e748cdc8fcee66d95f3fe16a5bebeed Mon Sep 17 00:00:00 2001 From: Kishore <42832651+kishore03109@users.noreply.github.com> Date: Wed, 19 Apr 2023 11:13:02 +0800 Subject: [PATCH 09/12] feat(launches Service): change API contract to original values during ... mocking --- src/services/identity/LaunchClient.ts | 56 ++++++++++++++---------- src/services/identity/LaunchesService.ts | 6 +-- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/services/identity/LaunchClient.ts b/src/services/identity/LaunchClient.ts index b1d249b26..9d14720c1 100644 --- a/src/services/identity/LaunchClient.ts +++ b/src/services/identity/LaunchClient.ts @@ -1,10 +1,10 @@ import { AmplifyClient, CreateDomainAssociationCommand, - CreateDomainAssociationCommandInput, + CreateDomainAssociationCommandInput as AmplifySDKCreateDomainAssociationCommandInput, CreateDomainAssociationCommandOutput, GetDomainAssociationCommand, - GetDomainAssociationCommandInput, + GetDomainAssociationCommandInput as AmplifySDKGetDomainAssociationCommandInput, GetDomainAssociationCommandOutput, SubDomainSetting, } from "@aws-sdk/client-amplify" @@ -12,20 +12,30 @@ import { SubDomain } from "aws-sdk/clients/amplify" import { config } from "@config/config" -// create a new interface that extends GetDomainAssociationCommandInput -interface MockGetDomainAssociationCommandInput - extends GetDomainAssociationCommandInput { - subDomainSettings: SubDomainSetting[] +// stricter typing to interact with Amplify SDK +type CreateDomainAssociationCommandInput = { + [K in keyof AmplifySDKCreateDomainAssociationCommandInput]: NonNullable< + AmplifySDKCreateDomainAssociationCommandInput[K] + > +} + +type GetDomainAssociationCommandInput = { + [K in keyof AmplifySDKGetDomainAssociationCommandInput]: NonNullable< + AmplifySDKGetDomainAssociationCommandInput[K] + > } class LaunchClient { private readonly amplifyClient: InstanceType + private readonly mockDomainAssociations: Map + constructor() { const AWS_REGION = "ap-southeast-1" this.amplifyClient = new AmplifyClient({ region: AWS_REGION, }) + this.mockDomainAssociations = new Map() } createDomainAssociationCommandInput = ( @@ -41,7 +51,7 @@ class LaunchClient { sendCreateDomainAssociation = ( input: CreateDomainAssociationCommandInput ): Promise => { - if (this.shouldMockAmplifyCreateDomainCalls()) { + if (this.shouldMockAmplifyDomainCalls()) { return this.mockCreateDomainAssociationOutput(input) } const output = this.amplifyClient.send( @@ -52,23 +62,16 @@ class LaunchClient { createGetDomainAssociationCommandInput = ( appId: string, - domainName: string, - subDomainSettings: SubDomainSetting[] - ): - | GetDomainAssociationCommandInput - | MockGetDomainAssociationCommandInput => ({ + domainName: string + ): GetDomainAssociationCommandInput => ({ appId, domainName, - ...(this.shouldMockAmplifyCreateDomainCalls() && { subDomainSettings }), }) sendGetDomainAssociationCommand = ( - input: - | GetDomainAssociationCommandInput - | MockGetDomainAssociationCommandInput + input: GetDomainAssociationCommandInput ): Promise => { - const isMockInput = "subDomainSettings" in input - if (isMockInput) { + if (this.shouldMockAmplifyDomainCalls()) { // handle mock input return this.mockGetDomainAssociationOutput(input) } @@ -83,6 +86,8 @@ class LaunchClient { mockCreateDomainAssociationOutput = ( input: CreateDomainAssociationCommandInput ): Promise => { + // We are mocking the response from the Amplify API, so we need to store the input + this.mockDomainAssociations.set(input.domainName, input.subDomainSettings) const mockResponse: CreateDomainAssociationCommandOutput = { $metadata: { httpStatusCode: 200, @@ -113,7 +118,7 @@ class LaunchClient { return Promise.resolve(mockResponse) } - private shouldMockAmplifyCreateDomainCalls(): boolean { + private shouldMockAmplifyDomainCalls(): boolean { return config.get("aws.amplify.mockAmplifyCreateDomainAssociationCalls") } @@ -138,13 +143,16 @@ class LaunchClient { } mockGetDomainAssociationOutput( - input: MockGetDomainAssociationCommandInput + input: GetDomainAssociationCommandInput ): Promise { const isSubDomainCreated = true // this is a `get` call, assume domain has already been created - const subDomains = this.getSubDomains( - input.subDomainSettings, - isSubDomainCreated - ) + const subDomainSettings = this.mockDomainAssociations.get(input.domainName) + if (!subDomainSettings) { + throw new Error( + `NotFoundException: Domain association ${input.domainName} not found.` + ) + } + const subDomains = this.getSubDomains(subDomainSettings, isSubDomainCreated) const mockResponse: GetDomainAssociationCommandOutput = { $metadata: { diff --git a/src/services/identity/LaunchesService.ts b/src/services/identity/LaunchesService.ts index 8ef6c0c09..85c56a3f5 100644 --- a/src/services/identity/LaunchesService.ts +++ b/src/services/identity/LaunchesService.ts @@ -192,13 +192,11 @@ export class LaunchesService { getDomainAssociationRecord = async ( domainName: string, - appId: string, - subDomainSettings: SubDomainSetting[] + appId: string ): Promise => { const getDomainAssociationOptions = this.launchClient.createGetDomainAssociationCommandInput( appId, - domainName, - subDomainSettings + domainName ) return this.launchClient.sendGetDomainAssociationCommand( getDomainAssociationOptions From 602b6f8bc03138ee091d291b2d54864d262f466c Mon Sep 17 00:00:00 2001 From: Kishore <42832651+kishore03109@users.noreply.github.com> Date: Wed, 19 Apr 2023 14:24:25 +0800 Subject: [PATCH 10/12] style(launch client): clearer style --- src/services/identity/LaunchClient.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/identity/LaunchClient.ts b/src/services/identity/LaunchClient.ts index 9d14720c1..08854344e 100644 --- a/src/services/identity/LaunchClient.ts +++ b/src/services/identity/LaunchClient.ts @@ -135,10 +135,10 @@ class LaunchClient { }`, subDomainSetting: { branchName: "master", - prefix: subDomainPrefix, + prefix: subDomainPrefix as string, }, verified: false, - })) as SubDomain[] + })) return subDomains } From 10e26d10504ec23d9dc83d8bdea006a0cede5305 Mon Sep 17 00:00:00 2001 From: Kishore <42832651+kishore03109@users.noreply.github.com> Date: Wed, 19 Apr 2023 14:26:28 +0800 Subject: [PATCH 11/12] feat(launches client): make mocking functions private --- src/services/identity/LaunchClient.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/identity/LaunchClient.ts b/src/services/identity/LaunchClient.ts index 08854344e..c8d1101be 100644 --- a/src/services/identity/LaunchClient.ts +++ b/src/services/identity/LaunchClient.ts @@ -83,7 +83,7 @@ class LaunchClient { * We want to limit interference with operations, as such we mock this call during development. * @returns Mocked output for CreateDomainAssociationCommand */ - mockCreateDomainAssociationOutput = ( + private mockCreateDomainAssociationOutput = ( input: CreateDomainAssociationCommandInput ): Promise => { // We are mocking the response from the Amplify API, so we need to store the input @@ -142,7 +142,7 @@ class LaunchClient { return subDomains } - mockGetDomainAssociationOutput( + private mockGetDomainAssociationOutput( input: GetDomainAssociationCommandInput ): Promise { const isSubDomainCreated = true // this is a `get` call, assume domain has already been created From 9e661017fc3a4e6e97a3d2165ce12073c4d9403c Mon Sep 17 00:00:00 2001 From: Kishore <42832651+kishore03109@users.noreply.github.com> Date: Wed, 19 Apr 2023 14:28:56 +0800 Subject: [PATCH 12/12] fix(infra service): wrong arguments passed --- src/services/infra/InfraService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/infra/InfraService.ts b/src/services/infra/InfraService.ts index 92c94819b..588796517 100644 --- a/src/services/infra/InfraService.ts +++ b/src/services/infra/InfraService.ts @@ -248,8 +248,7 @@ export default class InfraService { const dnsInfo = await this.launchesService.getDomainAssociationRecord( primaryDomain, - appId, - subDomainSettings + appId ) const certificationRecord = this.parseDNSRecords(