diff --git a/package-lock.json b/package-lock.json index 7678a7bb..0debd573 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "serverless-domain-manager", - "version": "7.1.0", + "version": "7.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "serverless-domain-manager", - "version": "7.1.0", + "version": "7.1.1", "license": "MIT", "dependencies": { "@aws-sdk/client-acm": "^3.370.0", diff --git a/src/aws/acm-wrapper.ts b/src/aws/acm-wrapper.ts index 7af6fdbb..12b11a28 100644 --- a/src/aws/acm-wrapper.ts +++ b/src/aws/acm-wrapper.ts @@ -1,10 +1,13 @@ import { - ACMClient, - ListCertificatesCommand, - ListCertificatesCommandOutput, + ACMClient, + CertificateSummary, + ListCertificatesCommand, + ListCertificatesCommandInput, + ListCertificatesCommandOutput } from "@aws-sdk/client-acm"; import Globals from "../globals"; import DomainConfig = require("../models/domain-config"); +import { getAWSPagedResults } from "../utils"; const certStatuses = ["PENDING_VALIDATION", "ISSUED", "INACTIVE"]; @@ -15,7 +18,8 @@ class ACMWrapper { const isEdge = endpointType === Globals.endpointTypes.edge; this.acm = new ACMClient({ credentials, - region: isEdge ? Globals.defaultRegion : Globals.getRegion() + region: isEdge ? Globals.defaultRegion : Globals.getRegion(), + retryStrategy: Globals.getRetryStrategy() }); } @@ -24,16 +28,20 @@ class ACMWrapper { let certificateName = domain.certificateName; // The certificate name try { - const response: ListCertificatesCommandOutput = await this.acm.send( - new ListCertificatesCommand({CertificateStatuses: certStatuses}) + const certificates = await getAWSPagedResults( + this.acm, + "CertificateSummaryList", + "NextToken", + "NextToken", + new ListCertificatesCommand({ CertificateStatuses: certStatuses }) ); // enhancement idea: weight the choice of cert so longer expires // and RenewalEligibility = ELIGIBLE is more preferable if (certificateName) { - certificateArn = this.getCertArnByCertName(response.CertificateSummaryList, certificateName); + certificateArn = this.getCertArnByCertName(certificates, certificateName); } else { certificateName = domain.givenDomainName; - certificateArn = this.getCertArnByDomainName(response.CertificateSummaryList, certificateName); + certificateArn = this.getCertArnByDomainName(certificates, certificateName); } } catch (err) { throw Error(`Could not search certificates in Certificate Manager.\n${err.message}`); diff --git a/src/aws/api-gateway-v1-wrapper.ts b/src/aws/api-gateway-v1-wrapper.ts index 59f62809..df83d151 100644 --- a/src/aws/api-gateway-v1-wrapper.ts +++ b/src/aws/api-gateway-v1-wrapper.ts @@ -5,24 +5,27 @@ import DomainConfig = require("../models/domain-config"); import DomainInfo = require("../models/domain-info"); import Globals from "../globals"; import { - APIGatewayClient, - CreateBasePathMappingCommand, - CreateDomainNameCommand, - CreateDomainNameCommandOutput, - DeleteBasePathMappingCommand, - DeleteDomainNameCommand, - GetBasePathMappingsCommand, - GetBasePathMappingsCommandOutput, - GetDomainNameCommand, - GetDomainNameCommandOutput, - UpdateBasePathMappingCommand, + APIGatewayClient, + BasePathMapping, + CreateBasePathMappingCommand, + CreateDomainNameCommand, + CreateDomainNameCommandOutput, + DeleteBasePathMappingCommand, + DeleteDomainNameCommand, + GetBasePathMappingsCommand, + GetBasePathMappingsCommandInput, + GetBasePathMappingsCommandOutput, + GetDomainNameCommand, + GetDomainNameCommandOutput, + UpdateBasePathMappingCommand } from "@aws-sdk/client-api-gateway"; import ApiGatewayMap = require("../models/api-gateway-map"); import APIGatewayBase = require("../models/apigateway-base"); import Logging from "../logging"; +import { getAWSPagedResults } from "../utils"; class APIGatewayV1Wrapper extends APIGatewayBase { - constructor(credentials?: any,) { + constructor(credentials?: any) { super(); this.apiGateway = new APIGatewayClient({ credentials, @@ -124,12 +127,15 @@ class APIGatewayV1Wrapper extends APIGatewayBase { public async getBasePathMappings(domain: DomainConfig): Promise { try { - const response: GetBasePathMappingsCommandOutput = await this.apiGateway.send( + const items = await getAWSPagedResults( + this.apiGateway, + "items", + "position", + "position", new GetBasePathMappingsCommand({ - domainName: domain.givenDomainName + domainName: domain.givenDomainName, }) ); - const items = response.items || []; return items.map((item) => { return new ApiGatewayMap(item.restApiId, item.basePath, item.stage, null); } diff --git a/src/aws/api-gateway-v2-wrapper.ts b/src/aws/api-gateway-v2-wrapper.ts index 18ea71d3..f3bd0eb2 100644 --- a/src/aws/api-gateway-v2-wrapper.ts +++ b/src/aws/api-gateway-v2-wrapper.ts @@ -7,22 +7,24 @@ import Globals from "../globals"; import ApiGatewayMap = require("../models/api-gateway-map"); import APIGatewayBase = require("../models/apigateway-base"); import { - ApiGatewayV2Client, - CreateApiMappingCommand, - CreateDomainNameCommand, - CreateDomainNameCommandOutput, - DeleteApiMappingCommand, - DeleteDomainNameCommand, - GetApiMappingsCommand, - GetApiMappingsCommandOutput, - GetDomainNameCommand, - GetDomainNameCommandOutput, - UpdateApiMappingCommand, + ApiGatewayV2Client, + ApiMapping, + CreateApiMappingCommand, + CreateDomainNameCommand, + CreateDomainNameCommandOutput, + DeleteApiMappingCommand, + DeleteDomainNameCommand, + GetApiMappingsCommand, + GetApiMappingsCommandInput, + GetApiMappingsCommandOutput, + GetDomainNameCommand, + GetDomainNameCommandOutput, + UpdateApiMappingCommand } from "@aws-sdk/client-apigatewayv2"; import Logging from "../logging"; +import { getAWSPagedResults } from "../utils"; class APIGatewayV2Wrapper extends APIGatewayBase { - constructor(credentials?: any) { super(); this.apiGateway = new ApiGatewayV2Client({ @@ -153,12 +155,15 @@ class APIGatewayV2Wrapper extends APIGatewayBase { */ public async getBasePathMappings(domain: DomainConfig): Promise { try { - const response: GetApiMappingsCommandOutput = await this.apiGateway.send( - new GetApiMappingsCommand({ - DomainName: domain.givenDomainName - }) + const items = await getAWSPagedResults( + this.apiGateway, + "Items", + "NextToken", + "NextToken", + new GetApiMappingsCommand({ + DomainName: domain.givenDomainName + }) ); - const items = response.Items || []; return items.map( (item) => new ApiGatewayMap(item.ApiId, item.ApiMappingKey, item.Stage, item.ApiMappingId) ); diff --git a/src/aws/cloud-formation-wrapper.ts b/src/aws/cloud-formation-wrapper.ts index 4f298a12..180d0113 100644 --- a/src/aws/cloud-formation-wrapper.ts +++ b/src/aws/cloud-formation-wrapper.ts @@ -2,17 +2,22 @@ * Wrapper class for AWS CloudFormation provider */ -import Globals from "../globals"; -import Logging from "../logging"; import { - CloudFormationClient, - DescribeStackResourceCommand, - DescribeStackResourceCommandOutput, - DescribeStacksCommand, - DescribeStacksCommandOutput, - ListExportsCommand, - ListExportsCommandOutput + CloudFormationClient, + DescribeStackResourceCommand, + DescribeStackResourceCommandOutput, + DescribeStacksCommand, + DescribeStacksCommandInput, + DescribeStacksCommandOutput, + Export, + ListExportsCommand, + ListExportsCommandInput, + ListExportsCommandOutput, + Stack } from "@aws-sdk/client-cloudformation"; +import Globals from "../globals"; +import Logging from "../logging"; +import { getAWSPagedResults } from "../utils"; class CloudFormationWrapper { public cloudFormation: CloudFormationClient; @@ -24,7 +29,8 @@ class CloudFormationWrapper { this.stackName = Globals.serverless.service.provider.stackName || defaultStackName; this.cloudFormation = new CloudFormationClient({ credentials, - region: Globals.getRegion() + region: Globals.getRegion(), + retryStrategy: Globals.getRetryStrategy() }); } @@ -120,10 +126,13 @@ class CloudFormationWrapper { * Gets values by names from cloudformation exports */ public async getImportValues(names: string[]): Promise { - const response: ListExportsCommandOutput = await this.cloudFormation.send( - new ListExportsCommand({}) + const exports = await getAWSPagedResults( + this.cloudFormation, + "Exports", + "NextToken", + "NextToken", + new ListExportsCommand({}) ); - const exports = response.Exports || []; // filter Exports by names which we need const filteredExports = exports.filter((item) => names.indexOf(item.Name) !== -1); // converting a list of unique values to dict @@ -152,10 +161,13 @@ class CloudFormationWrapper { */ public async getNestedStack(logicalResourceId: string, stackName: string) { // get all stacks from the CloudFormation - const response: DescribeStacksCommandOutput = await this.cloudFormation.send( - new DescribeStacksCommand({}) + const stacks = await getAWSPagedResults( + this.cloudFormation, + "Stacks", + "NextToken", + "NextToken", + new DescribeStacksCommand({}) ); - const stacks = response.Stacks || []; // filter stacks by given stackName and check by nested stack RootId const regex = new RegExp("/" + stackName + "/"); diff --git a/src/aws/route53-wrapper.ts b/src/aws/route53-wrapper.ts index 37b67d4d..b8cb19be 100644 --- a/src/aws/route53-wrapper.ts +++ b/src/aws/route53-wrapper.ts @@ -2,11 +2,14 @@ import Globals from "../globals"; import DomainConfig = require("../models/domain-config"); import Logging from "../logging"; import { - ChangeResourceRecordSetsCommand, - ListHostedZonesCommand, - ListHostedZonesCommandOutput, - Route53Client + ChangeResourceRecordSetsCommand, + HostedZone, + ListHostedZonesCommand, + ListHostedZonesCommandInput, + ListHostedZonesCommandOutput, + Route53Client } from "@aws-sdk/client-route-53"; +import { getAWSPagedResults } from "../utils"; class Route53Wrapper { public route53: Route53Client; @@ -16,10 +19,14 @@ class Route53Wrapper { if (credentials) { this.route53 = new Route53Client({ credentials, - region: region || Globals.getRegion() + region: region || Globals.getRegion(), + retryStrategy: Globals.getRetryStrategy() }); } else { - this.route53 = new Route53Client({region: Globals.getRegion()}); + this.route53 = new Route53Client({ + region: Globals.getRegion(), + retryStrategy: Globals.getRetryStrategy() + }); } } @@ -40,10 +47,13 @@ class Route53Wrapper { let hostedZones = []; try { - const response: ListHostedZonesCommandOutput = await this.route53.send( - new ListHostedZonesCommand({}) + hostedZones = await getAWSPagedResults( + this.route53, + "HostedZones", + "Marker", + "NextMarker", + new ListHostedZonesCommand({}) ); - hostedZones = response.HostedZones || hostedZones; } catch (err) { throw new Error(`Unable to list hosted zones in Route53.\n${err.message}`); } diff --git a/src/utils.ts b/src/utils.ts index f1c762e7..97ace62b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,5 @@ +import { Client, Command } from "@smithy/smithy-client"; +import { MetadataBearer } from "@smithy/types"; import Globals from "./globals"; /** @@ -5,7 +7,7 @@ import Globals from "./globals"; * @param seconds * @returns {Promise} Resolves after given number of seconds. */ -async function sleep(seconds) { +async function sleep(seconds: number) { return new Promise((resolve) => setTimeout(resolve, 1000 * seconds)); } @@ -37,7 +39,34 @@ function evaluateBoolean(value: any, defaultValue: boolean): boolean { throw new Error(`${Globals.pluginName}: Ambiguous boolean config: "${value}"`); } -export { - evaluateBoolean, - sleep, -}; +/** + * Iterate through the pages of a AWS SDK response and collect them into a single array + * + * @param client - The AWS service instance to use to make the calls + * @param resultsKey - The key name in the response that contains the items to return + * @param nextTokenKey - The request key name to append to the request that has the paging token value + * @param nextRequestTokenKey - The response key name that has the next paging token value + * @param params - Parameters to send in the request + */ +async function getAWSPagedResults( + client: Client, + resultsKey: keyof ClientOutputCommand, + nextTokenKey: keyof ClientInputCommand, + nextRequestTokenKey: keyof ClientOutputCommand, + params: Command +): Promise { + let results = []; + let response = await client.send(params); + results = results.concat(response[resultsKey] || results); + while ( + response.hasOwnProperty(nextRequestTokenKey) && + response[nextRequestTokenKey] + ) { + params.input[nextTokenKey] = response[nextRequestTokenKey]; + response = await client.send(params); + results = results.concat(response[resultsKey]); + } + return results; +} + +export { evaluateBoolean, sleep, getAWSPagedResults }; diff --git a/test/unit-tests/aws/acm-wrapper.test.ts b/test/unit-tests/aws/acm-wrapper.test.ts index a9e39ed0..27e08e06 100644 --- a/test/unit-tests/aws/acm-wrapper.test.ts +++ b/test/unit-tests/aws/acm-wrapper.test.ts @@ -60,6 +60,39 @@ describe("ACM Wrapper checks", () => { const actualResult = await acmWrapper.getCertArn(dc); expect(actualResult).to.equal(testCertificateArnByDomain); }); + + it("getCertArn by domain name by getting all paginated certificates from AWS", async () => { + const ACMCMock = mockClient(ACMClient); + ACMCMock.on(ListCertificatesCommand) + .resolvesOnce({ + CertificateSummaryList: [ + { + CertificateArn: "test_domain_arn", + DomainName: "test_domain", + Status: "ISSUED", + }, + ], + NextToken: 'NextToken', + }) + .resolves({ + CertificateSummaryList: [ + { + CertificateArn: "test_domain_arn2", + DomainName: "test_domain2", + Status: "ISSUED", + }, + ], + }); + + const acmWrapper = new ACMWrapper(null, Globals.endpointTypes.regional); + const dc = new DomainConfig(getDomainConfig({ + domainName: "test_domain", + })); + + const actualResult = await acmWrapper.getCertArn(dc); + expect(actualResult).to.equal(testCertificateArnByDomain); + expect(ACMCMock.calls().length).to.equal(2); + }); it("empty getCertArn by certificate name", async () => { const ACMCMock = mockClient(ACMClient); diff --git a/test/unit-tests/aws/api-gateway-v1-wrapper.test.ts b/test/unit-tests/aws/api-gateway-v1-wrapper.test.ts index d5f85b41..22635f81 100644 --- a/test/unit-tests/aws/api-gateway-v1-wrapper.test.ts +++ b/test/unit-tests/aws/api-gateway-v1-wrapper.test.ts @@ -361,6 +361,44 @@ describe("API Gateway V1 wrapper checks", () => { expect(commandCalls.length).to.equal(1); }); + + it("get all base path mappings", async () => { + const APIGatewayCMock = mockClient(APIGatewayClient); + APIGatewayCMock.on(GetBasePathMappingsCommand) + .resolvesOnce({ + items: [ + { + restApiId: "test_rest_api_id", + basePath: "test", + stage: "test" + }, + ], + position: "position", + }) + .resolves({ + items: [ + { + restApiId: "test_rest_api_id2", + basePath: "test2", + stage: "test", + }, + ], + }); + + const apiGatewayV1Wrapper = new APIGatewayV1Wrapper(); + const dc = new DomainConfig(getDomainConfig({ + domainName: "test_domain" + })); + + const actualResult = await apiGatewayV1Wrapper.getBasePathMappings(dc); + const expectedResult = [ + new ApiGatewayMap("test_rest_api_id", "test", "test", null), + new ApiGatewayMap("test_rest_api_id2", "test2", "test", null), + ] + + expect(actualResult).to.eql(expectedResult); + expect(APIGatewayCMock.calls().length).to.equal(2); + }); it("get base path mapping failure", async () => { const APIGatewayMock = mockClient(APIGatewayClient); diff --git a/test/unit-tests/aws/api-gateway-v2-wrapper.test.ts b/test/unit-tests/aws/api-gateway-v2-wrapper.test.ts index 5932e13d..45af2675 100644 --- a/test/unit-tests/aws/api-gateway-v2-wrapper.test.ts +++ b/test/unit-tests/aws/api-gateway-v2-wrapper.test.ts @@ -395,6 +395,41 @@ describe("API Gateway V2 wrapper checks", () => { expect(commandCalls.length).to.equal(1); }); + + it("get all base path mappings", async () => { + const APIGatewayMock = mockClient(ApiGatewayV2Client); + APIGatewayMock.on(GetApiMappingsCommand).resolvesOnce({ + Items: [{ + ApiId: "test_rest_api_id", + ApiMappingKey: "test", + Stage: "test", + ApiMappingId: "test_id" + }], + NextToken: "NextToken" + }) + .resolves({ + Items: [{ + ApiId: "test_rest_api_id2", + ApiMappingKey: "test2", + Stage: "test", + ApiMappingId: "test_id2" + }] + }); + + const apiGatewayV2Wrapper = new APIGatewayV2Wrapper(); + const dc = new DomainConfig(getDomainConfig({ + domainName: "test_domain" + })); + + const actualResult = await apiGatewayV2Wrapper.getBasePathMappings(dc); + const expectedResult = [ + new ApiGatewayMap("test_rest_api_id", "test", "test", "test_id"), + new ApiGatewayMap("test_rest_api_id2", "test2", "test", "test_id2") + ] + + expect(actualResult).to.eql(expectedResult); + expect(APIGatewayMock.calls().length).to.equal(2); + }); it("get base path mapping failure", async () => { const APIGatewayMock = mockClient(ApiGatewayV2Client); diff --git a/test/unit-tests/aws/cloud-formation-wrapper.test.ts b/test/unit-tests/aws/cloud-formation-wrapper.test.ts index a3a18882..d08330bd 100644 --- a/test/unit-tests/aws/cloud-formation-wrapper.test.ts +++ b/test/unit-tests/aws/cloud-formation-wrapper.test.ts @@ -56,6 +56,38 @@ describe("Cloud Formation wrapper checks", () => { const commandCalls = CloudFormationMock.commandCalls(ListExportsCommand, expectedParams, true); expect(commandCalls.length).to.equal(1); }); + + it("findApiId for the rest api type via Fn::ImportValue paginated", async () => { + const fnImportValue = "test-value"; + const CloudFormationMock = mockClient(CloudFormationClient); + CloudFormationMock.on(ListExportsCommand).resolvesOnce({ + Exports: [ + {Name: "test-name", Value: fnImportValue}, + {Name: "dummy-name", Value: "dummy-value"}, + ], + NextToken: "NextToken" + }) + .resolves({ + Exports: [ + {Name: "test-name2", Value: "test-name2"}, + {Name: "dummy-name2", Value: "dummy-value2"}, + ] + }); + + const cloudFormationWrapper = new CloudFormationWrapper(); + Globals.serverless.service.provider.apiGateway.restApiId = { + [Globals.CFFuncNames.fnImport]: "test-name" + }; + + const actualResult = await cloudFormationWrapper.findApiId(Globals.apiTypes.rest) + expect(actualResult).to.equal(fnImportValue); + + const expectedParams = { + NextToken: "NextToken" + }; + const commandCalls = CloudFormationMock.commandCalls(ListExportsCommand, expectedParams, true); + expect(commandCalls.length).to.equal(2); + }); it("findApiId for the rest api type via Fn::ImportValue not found", async () => { const fnImportValue = "test-value"; @@ -265,6 +297,72 @@ describe("Cloud Formation wrapper checks", () => { const allCommandCalls = CloudFormationMock.commandCalls(DescribeStackResourceCommand); expect(allCommandCalls.length).to.equal(2); }); + + it("findApiId for the rest api type with paginated nested stacks", async () => { + const physicalResourceId = "test_rest_api_id"; + const nestedStackName = "custom-stage-name-NestedStackTwo-U89W84TQIHJK"; + const CloudFormationMock = mockClient(CloudFormationClient); + CloudFormationMock.on(DescribeStackResourceCommand).rejectsOnce() + .resolves({ + StackResourceDetail: { + LogicalResourceId: Globals.CFResourceIds[Globals.apiTypes.rest], + PhysicalResourceId: physicalResourceId, + ResourceType: "", + LastUpdatedTimestamp: undefined, + ResourceStatus: ResourceStatus.CREATE_COMPLETE, + }, + }); + CloudFormationMock.on(DescribeStacksCommand).resolvesOnce({ + Stacks: [ + { + StackName: "custom-stage-name-NestedStackOne-U89W84TQIHJK", + RootId: "arn:aws:cloudformation:us-east-1:000000000000:stack/dummy-name/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + CreationTime: null, + StackStatus: StackStatus.CREATE_COMPLETE + }, + { + StackName: nestedStackName, + RootId: `arn:aws:cloudformation:us-east-1:000000000000:stack/${Globals.serverless.service.provider.stackName}/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, + CreationTime: null, + StackStatus: StackStatus.CREATE_COMPLETE + }, + { + StackName: "outside-stack-NestedStackZERO-U89W84TQIHJK", + RootId: null, + CreationTime: null, + StackStatus: StackStatus.CREATE_COMPLETE + }, + ], + NextToken: "NextToken" + }) + .resolves({ + Stacks: [ + { + StackName: "custom-stage-name-NestedStackOne-U89W84TQ1235", + RootId: "arn:aws:cloudformation:us-east-1:000000000000:stack/dummy-name2/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", + CreationTime: null, + StackStatus: StackStatus.CREATE_COMPLETE + } + ], + }); + + const actualResult = await new CloudFormationWrapper().findApiId(Globals.apiTypes.rest) + expect(actualResult).to.equal(physicalResourceId); + + const expectedParams = { + LogicalResourceId: Globals.CFResourceIds[Globals.apiTypes.rest], + StackName: nestedStackName, + }; + + const commandCalls = CloudFormationMock.commandCalls(DescribeStackResourceCommand, expectedParams, true); + expect(commandCalls.length).to.equal(1); + + const allCommandCalls = CloudFormationMock.commandCalls(DescribeStackResourceCommand); + expect(allCommandCalls.length).to.equal(2); + + const describeStacksCommandCalls = CloudFormationMock.commandCalls(DescribeStacksCommand); + expect(describeStacksCommandCalls.length).to.equal(2); + }); it("findApiId for the rest api type with nested stacks failure", async () => { const nestedStackName = "custom-stage-name-NestedStackTwo-U89W84TQIHJK"; diff --git a/test/unit-tests/aws/route53-wrapper.test.ts b/test/unit-tests/aws/route53-wrapper.test.ts index ca4a4f18..576892a7 100644 --- a/test/unit-tests/aws/route53-wrapper.test.ts +++ b/test/unit-tests/aws/route53-wrapper.test.ts @@ -70,6 +70,87 @@ describe("Route53 wrapper checks", () => { actualId = await new Route53Wrapper().getRoute53HostedZoneId(dc); expect(actualId).to.equal(dc.hostedZoneId); }); + + it("get route53 hosted zone id paginated", async () => { + const testId = "test_host_id" + const Route53Mock = mockClient(Route53Client); + Route53Mock.on(ListHostedZonesCommand).resolvesOnce({ + HostedZones: [ + { + CallerReference: "1", + Config: {PrivateZone: false}, + Id: testId, + Name: "test_domain", + }, { + CallerReference: "2", + Config: {PrivateZone: false}, + Id: testId, + Name: "dummy_test_domain", + }, { + CallerReference: "3", + Config: {PrivateZone: false}, + Id: testId, + Name: "domain", + } + ], + NextMarker: "NextMarker" + }) + .resolvesOnce({ + HostedZones: [ + { + CallerReference: "4", + Config: {PrivateZone: false}, + Id: testId, + Name: "test_domain2", + }, { + CallerReference: "5", + Config: {PrivateZone: false}, + Id: testId, + Name: "dummy_test_domain2", + }, { + CallerReference: "6", + Config: {PrivateZone: false}, + Id: testId, + Name: "domain2", + } + ], + NextMarker: "NextMarker" + }) + .resolves({ + HostedZones: [ + { + CallerReference: "7", + Config: {PrivateZone: false}, + Id: testId, + Name: "test_domain3", + }, { + CallerReference: "8", + Config: {PrivateZone: false}, + Id: testId, + Name: "dummy_test_domain3", + }, { + CallerReference: "9", + Config: {PrivateZone: false}, + Id: testId, + Name: "domain3", + } + ] + }); + + const dc = new DomainConfig(getDomainConfig({ + domainName: "test_domain" + })); + + let actualId = await new Route53Wrapper().getRoute53HostedZoneId(dc); + expect(actualId).to.equal(testId); + + const commandCalls = Route53Mock.commandCalls(ListHostedZonesCommand, {}); + expect(commandCalls.length).to.equal(3); + + dc.hostedZoneId = "test_id" + actualId = await new Route53Wrapper().getRoute53HostedZoneId(dc); + expect(actualId).to.equal(dc.hostedZoneId); + }); it("get route53 hosted zone id public", async () => { const testId = "test_host_id" @@ -101,7 +182,7 @@ describe("Route53 wrapper checks", () => { expect(commandCalls.length).to.equal(1); }); - it("get route53 hosted zone id privet", async () => { + it("get route53 hosted zone id private", async () => { const testId = "test_host_id" const Route53Mock = mockClient(Route53Client); Route53Mock.on(ListHostedZonesCommand).resolves({