From af3dada391663f3fa58666c97f381d36f941b354 Mon Sep 17 00:00:00 2001 From: Smriti Vashisth Date: Mon, 15 Nov 2021 09:13:03 -0800 Subject: [PATCH 1/6] feat(apigatwewayv2): add support for domain name configurations --- packages/@aws-cdk/aws-apigatewayv2/README.md | 10 +++ .../lib/common/domain-name.ts | 88 +++++++++++++++++-- .../test/http/domain-name.test.ts | 80 ++++++++++++++++- 3 files changed, 168 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index cc6c6f48c5827..4d7b26bad5843 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -191,6 +191,8 @@ const domainName = 'example.com'; const dn = new apigwv2.DomainName(this, 'DN', { domainName, certificate: acm.Certificate.fromCertificateArn(this, 'cert', certArn), + endpointType: EndpointType.REGIONAL, + securityPolicy: SecurityPolicy.TLS_1_2, }); declare const handler: lambda.Function; @@ -204,6 +206,14 @@ const api = new apigwv2.HttpApi(this, 'HttpProxyProdApi', { }); ``` +The properties certificate, endpointType, securityPolicy, certificateName, and ownershipVerificationCertificate are mapped in CFN to a DomainNameConfiguration. +See [DomainNameConfiguration](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigatewayv2-domainname-domainnameconfiguration.html) +for more details. +You need only one DomainNameConfiguration, unless you're migrating from one endpoint type to another. To migrate to a new endpoint type, +you add a new domain name configuration via `addDomainNameConfiguration` and then configure DNS records to route traffic to the new endpoint. +After that, you can remove the previous DomainNameConfiguration. +Learn more at [Migrating a custom domain name](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-regional-api-custom-domain-migrate.html) + To associate a specific `Stage` to a custom domain mapping - ```ts diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts index e550a21f915f3..4e6036540301e 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts @@ -1,9 +1,34 @@ import { ICertificate } from '@aws-cdk/aws-certificatemanager'; import { IBucket } from '@aws-cdk/aws-s3'; -import { IResource, Resource, Token } from '@aws-cdk/core'; +import { IResource, Lazy, Resource, Token } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnDomainName, CfnDomainNameProps } from '../apigatewayv2.generated'; +/** + * The minimum version of the SSL protocol that you want API Gateway to use for HTTPS connections. + */ +export enum SecurityPolicy { + /** Cipher suite TLS 1.0 */ + TLS_1_0 = 'TLS_1_0', + + /** Cipher suite TLS 1.2 */ + TLS_1_2 = 'TLS_1_2', +} + +/** + * Endpoint type for a domain name. + */ +export enum EndpointType { + /** + * For an edge-optimized custom domain name. + */ + EDGE = 'EDGE', + /** + * For a regional custom domain name. + */ + REGIONAL = 'REGIONAL', +} + /** * Represents an APIGatewayV2 DomainName * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigatewayv2-domainname.html @@ -57,14 +82,42 @@ export interface DomainNameProps { */ readonly domainName: string; /** - * The ACM certificate for this domain name + * The ACM certificate for this domain name. + * Certificate can be both ACM issued or imported. */ readonly certificate: ICertificate; /** * The mutual TLS authentication configuration for a custom domain name. * @default - mTLS is not configured. */ - readonly mtls?: MTLSConfig + readonly mtls?: MTLSConfig; + /** + * The user-friendly name of the certificate that will be used by the endpoint for this domain name. + * This property is optional and is helpful if you have too many certificates and it is easier to remember + * certificates by some name rather that the domain they are valid for. + * @default - No friendly certificate name + */ + readonly certificateName?: string; + + /** + * The type of endpoint for this DomainName. + * @default EndpointType.REGIONAL + */ + readonly endpointType?: EndpointType; + + /** + * The Transport Layer Security (TLS) version + cipher suite for this domain name. + * @default SecurityPolicy.TLS_1_2 + */ + readonly securityPolicy?: SecurityPolicy; + + /** + * A public certificate issued by ACM to validate that you own a custom domain. This parameter is required + * only when you configure mutual TLS authentication and you specify an ACM imported or private CA certificate + * for `certificate`. The ownership verification certificate validates that you have permissions to use the domain name. + * @default - only required when configuring mTLS + */ + readonly ownershipVerificationCertificate?: ICertificate; } /** @@ -107,6 +160,7 @@ export class DomainName extends Resource implements IDomainName { public readonly name: string; public readonly regionalDomainName: string; public readonly regionalHostedZoneId: string; + private readonly domainNameConfigurations: CfnDomainName.DomainNameConfigurationProperty[] = []; constructor(scope: Construct, id: string, props: DomainNameProps) { super(scope, id); @@ -118,18 +172,17 @@ export class DomainName extends Resource implements IDomainName { const mtlsConfig = this.configureMTLS(props.mtls); const domainNameProps: CfnDomainNameProps = { domainName: props.domainName, - domainNameConfigurations: [ - { - certificateArn: props.certificate.certificateArn, - endpointType: 'REGIONAL', - }, - ], + domainNameConfigurations: Lazy.any({ produce: () => this.domainNameConfigurations }), mutualTlsAuthentication: mtlsConfig, }; const resource = new CfnDomainName(this, 'Resource', domainNameProps); this.name = resource.ref; this.regionalDomainName = Token.asString(resource.getAtt('RegionalDomainName')); this.regionalHostedZoneId = Token.asString(resource.getAtt('RegionalHostedZoneId')); + + if (props.certificate) { + this.addDomainNameConfiguration(props); + } } private configureMTLS(mtlsConfig?: MTLSConfig): CfnDomainName.MutualTlsAuthenticationProperty | undefined { @@ -139,4 +192,21 @@ export class DomainName extends Resource implements IDomainName { truststoreVersion: mtlsConfig.version, }; } + + /** + * Adds a configuration to a domain name. Properties like certificate, endpoint type and security policy can be set using this method. + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigatewayv2-domainname-domainnameconfiguration.html + * @param props - domain name properties to be set + */ + public addDomainNameConfiguration(props: DomainNameProps) : void { + const domainNameConfig: CfnDomainName.DomainNameConfigurationProperty = { + certificateArn: props.certificate.certificateArn, + certificateName: props.certificateName, + endpointType: props.endpointType?.toString(), + ownershipVerificationCertificateArn: props.ownershipVerificationCertificate?.certificateArn, + securityPolicy: props.securityPolicy?.toString(), + }; + + this.domainNameConfigurations.push(domainNameConfig); + } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts index 9c60f7b5196e3..a00e9ca11aade 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts @@ -2,10 +2,12 @@ import { Template } from '@aws-cdk/assertions'; import { Certificate } from '@aws-cdk/aws-certificatemanager'; import { Bucket } from '@aws-cdk/aws-s3'; import { Stack } from '@aws-cdk/core'; -import { DomainName, HttpApi } from '../../lib'; +import { DomainName, EndpointType, HttpApi, SecurityPolicy } from '../../lib'; const domainName = 'example.com'; const certArn = 'arn:aws:acm:us-east-1:111111111111:certificate'; +const certArn2 = 'arn:aws:acm:us-east-1:111111111111:certificate2'; +const ownershipCertArn = 'arn:aws:acm:us-east-1:111111111111:ownershipcertificate'; describe('DomainName', () => { test('create domain name correctly', () => { @@ -16,6 +18,7 @@ describe('DomainName', () => { new DomainName(stack, 'DomainName', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), + endpointType: EndpointType.REGIONAL, }); // THEN @@ -53,6 +56,7 @@ describe('DomainName', () => { const dn = new DomainName(stack, 'DomainName', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), + endpointType: EndpointType.REGIONAL, }); // WHEN @@ -79,6 +83,7 @@ describe('DomainName', () => { const dn = new DomainName(stack, 'DN', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), + endpointType: EndpointType.REGIONAL, }); api.addStage('beta', { @@ -117,6 +122,7 @@ describe('DomainName', () => { const dn = new DomainName(stack, 'DN', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), + endpointType: EndpointType.REGIONAL, }); // WHEN @@ -155,6 +161,7 @@ describe('DomainName', () => { const dn = new DomainName(stack, 'DN', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), + endpointType: EndpointType.REGIONAL, }); const t = () => { new HttpApi(stack, 'Api', { @@ -179,6 +186,7 @@ describe('DomainName', () => { new DomainName(stack, 'DomainName', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), + endpointType: EndpointType.REGIONAL, mtls: { bucket, key: 'someca.pem', @@ -209,6 +217,7 @@ describe('DomainName', () => { new DomainName(stack, 'DomainName', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), + endpointType: EndpointType.REGIONAL, mtls: { bucket, key: 'someca.pem', @@ -231,4 +240,73 @@ describe('DomainName', () => { }, }); }); + + test('domain with mutual tls configuration and ownership cert', () => { + // GIVEN + const stack = new Stack(); + const bucket = Bucket.fromBucketName(stack, 'testBucket', 'example-bucket'); + + // WHEN + new DomainName(stack, 'DomainName', { + domainName, + certificate: Certificate.fromCertificateArn(stack, 'cert2', certArn2), + ownershipVerificationCertificate: Certificate.fromCertificateArn(stack, 'ownershipCert', ownershipCertArn), + endpointType: EndpointType.REGIONAL, + securityPolicy: SecurityPolicy.TLS_1_2, + mtls: { + bucket, + key: 'someca.pem', + version: 'version', + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::DomainName', { + DomainName: 'example.com', + DomainNameConfigurations: [ + { + CertificateArn: 'arn:aws:acm:us-east-1:111111111111:certificate2', + EndpointType: 'REGIONAL', + SecurityPolicy: 'TLS_1_2', + OwnershipVerificationCertificateArn: 'arn:aws:acm:us-east-1:111111111111:ownershipcertificate', + }, + ], + MutualTlsAuthentication: { + TruststoreUri: 's3://example-bucket/someca.pem', + TruststoreVersion: 'version', + }, + }); + }); + + test('add new configuration to a domain name for migration', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const dn = new DomainName(stack, 'DomainName', { + domainName, + certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), + endpointType: EndpointType.REGIONAL, + }); + dn.addDomainNameConfiguration({ + domainName, + certificate: Certificate.fromCertificateArn(stack, 'cert2', certArn2), + endpointType: EndpointType.EDGE, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::DomainName', { + DomainName: 'example.com', + DomainNameConfigurations: [ + { + CertificateArn: 'arn:aws:acm:us-east-1:111111111111:certificate', + EndpointType: 'REGIONAL', + }, + { + CertificateArn: 'arn:aws:acm:us-east-1:111111111111:certificate2', + EndpointType: 'EDGE', + }, + ], + }); + }); }); From 2df967d4757199ac4a7b29704f02d44fc6fd8d6e Mon Sep 17 00:00:00 2001 From: Smriti Vashisth Date: Tue, 16 Nov 2021 13:03:33 -0800 Subject: [PATCH 2/6] update domain name configuration code --- packages/@aws-cdk/aws-apigatewayv2/README.md | 10 ++-------- .../aws-apigatewayv2/lib/common/domain-name.ts | 10 ++++------ .../aws-apigatewayv2/test/http/domain-name.test.ts | 8 +------- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index 4d7b26bad5843..e231140ebe32c 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -191,8 +191,6 @@ const domainName = 'example.com'; const dn = new apigwv2.DomainName(this, 'DN', { domainName, certificate: acm.Certificate.fromCertificateArn(this, 'cert', certArn), - endpointType: EndpointType.REGIONAL, - securityPolicy: SecurityPolicy.TLS_1_2, }); declare const handler: lambda.Function; @@ -206,12 +204,8 @@ const api = new apigwv2.HttpApi(this, 'HttpProxyProdApi', { }); ``` -The properties certificate, endpointType, securityPolicy, certificateName, and ownershipVerificationCertificate are mapped in CFN to a DomainNameConfiguration. -See [DomainNameConfiguration](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigatewayv2-domainname-domainnameconfiguration.html) -for more details. -You need only one DomainNameConfiguration, unless you're migrating from one endpoint type to another. To migrate to a new endpoint type, -you add a new domain name configuration via `addDomainNameConfiguration` and then configure DNS records to route traffic to the new endpoint. -After that, you can remove the previous DomainNameConfiguration. +To migrate a domain endpoint from one type to another, you can add a new domain name configuration via `addDomainNameConfiguration` +and then configure DNS records to route traffic to the new endpoint. After that, you can remove the previous endpoint configuration. Learn more at [Migrating a custom domain name](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-regional-api-custom-domain-migrate.html) To associate a specific `Stage` to a custom domain mapping - diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts index 4e6036540301e..7ae26609bb2e6 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts @@ -93,8 +93,6 @@ export interface DomainNameProps { readonly mtls?: MTLSConfig; /** * The user-friendly name of the certificate that will be used by the endpoint for this domain name. - * This property is optional and is helpful if you have too many certificates and it is easier to remember - * certificates by some name rather that the domain they are valid for. * @default - No friendly certificate name */ readonly certificateName?: string; @@ -114,10 +112,10 @@ export interface DomainNameProps { /** * A public certificate issued by ACM to validate that you own a custom domain. This parameter is required * only when you configure mutual TLS authentication and you specify an ACM imported or private CA certificate - * for `certificate`. The ownership verification certificate validates that you have permissions to use the domain name. + * for `certificate`. The ownership certificate validates that you have permissions to use the domain name. * @default - only required when configuring mTLS */ - readonly ownershipVerificationCertificate?: ICertificate; + readonly ownershipCertificate?: ICertificate; } /** @@ -202,8 +200,8 @@ export class DomainName extends Resource implements IDomainName { const domainNameConfig: CfnDomainName.DomainNameConfigurationProperty = { certificateArn: props.certificate.certificateArn, certificateName: props.certificateName, - endpointType: props.endpointType?.toString(), - ownershipVerificationCertificateArn: props.ownershipVerificationCertificate?.certificateArn, + endpointType: props.endpointType ? props.endpointType?.toString() : 'REGIONAL', + ownershipVerificationCertificateArn: props.ownershipCertificate?.certificateArn, securityPolicy: props.securityPolicy?.toString(), }; diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts index a00e9ca11aade..dc058e5cdb6c7 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts @@ -18,7 +18,6 @@ describe('DomainName', () => { new DomainName(stack, 'DomainName', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), - endpointType: EndpointType.REGIONAL, }); // THEN @@ -56,7 +55,6 @@ describe('DomainName', () => { const dn = new DomainName(stack, 'DomainName', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), - endpointType: EndpointType.REGIONAL, }); // WHEN @@ -83,7 +81,6 @@ describe('DomainName', () => { const dn = new DomainName(stack, 'DN', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), - endpointType: EndpointType.REGIONAL, }); api.addStage('beta', { @@ -122,7 +119,6 @@ describe('DomainName', () => { const dn = new DomainName(stack, 'DN', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), - endpointType: EndpointType.REGIONAL, }); // WHEN @@ -161,7 +157,6 @@ describe('DomainName', () => { const dn = new DomainName(stack, 'DN', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), - endpointType: EndpointType.REGIONAL, }); const t = () => { new HttpApi(stack, 'Api', { @@ -217,7 +212,6 @@ describe('DomainName', () => { new DomainName(stack, 'DomainName', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), - endpointType: EndpointType.REGIONAL, mtls: { bucket, key: 'someca.pem', @@ -250,7 +244,7 @@ describe('DomainName', () => { new DomainName(stack, 'DomainName', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert2', certArn2), - ownershipVerificationCertificate: Certificate.fromCertificateArn(stack, 'ownershipCert', ownershipCertArn), + ownershipCertificate: Certificate.fromCertificateArn(stack, 'ownershipCert', ownershipCertArn), endpointType: EndpointType.REGIONAL, securityPolicy: SecurityPolicy.TLS_1_2, mtls: { From c698f5eec96c91d364897091476e2fe4b73fea13 Mon Sep 17 00:00:00 2001 From: Smriti Vashisth Date: Tue, 16 Nov 2021 13:08:58 -0800 Subject: [PATCH 3/6] removing endpoint type from an existing test --- packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts index dc058e5cdb6c7..e339efe7b9853 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts @@ -181,7 +181,6 @@ describe('DomainName', () => { new DomainName(stack, 'DomainName', { domainName, certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), - endpointType: EndpointType.REGIONAL, mtls: { bucket, key: 'someca.pem', From bba9fc101ae54ef596f28d936d57c1261360c9fc Mon Sep 17 00:00:00 2001 From: Smriti Vashisth Date: Wed, 17 Nov 2021 09:38:49 -0800 Subject: [PATCH 4/6] add validations for domain name properties --- packages/@aws-cdk/aws-apigatewayv2/README.md | 2 +- .../lib/common/domain-name.ts | 25 ++++++++++-- .../test/http/domain-name.test.ts | 40 ++++++++++++++++++- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index e231140ebe32c..89dcb79f6409d 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -204,7 +204,7 @@ const api = new apigwv2.HttpApi(this, 'HttpProxyProdApi', { }); ``` -To migrate a domain endpoint from one type to another, you can add a new domain name configuration via `addDomainNameConfiguration` +To migrate a domain endpoint from one type to another, you can add a new endpoint configuration via `addEndpoint()` and then configure DNS records to route traffic to the new endpoint. After that, you can remove the previous endpoint configuration. Learn more at [Migrating a custom domain name](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-regional-api-custom-domain-migrate.html) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts index 7ae26609bb2e6..26aaaa389c278 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts @@ -167,6 +167,11 @@ export class DomainName extends Resource implements IDomainName { throw new Error('empty string for domainName not allowed'); } + // validation for ownership certificate + if (props.ownershipCertificate && !props.mtls) { + throw new Error('ownership certificate can only be used with mtls domains'); + } + const mtlsConfig = this.configureMTLS(props.mtls); const domainNameProps: CfnDomainNameProps = { domainName: props.domainName, @@ -179,7 +184,7 @@ export class DomainName extends Resource implements IDomainName { this.regionalHostedZoneId = Token.asString(resource.getAtt('RegionalHostedZoneId')); if (props.certificate) { - this.addDomainNameConfiguration(props); + this.addEndpoint(props); } } @@ -194,9 +199,9 @@ export class DomainName extends Resource implements IDomainName { /** * Adds a configuration to a domain name. Properties like certificate, endpoint type and security policy can be set using this method. * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigatewayv2-domainname-domainnameconfiguration.html - * @param props - domain name properties to be set + * @param props domain name properties to be set */ - public addDomainNameConfiguration(props: DomainNameProps) : void { + public addEndpoint(props: DomainNameProps) : void { const domainNameConfig: CfnDomainName.DomainNameConfigurationProperty = { certificateArn: props.certificate.certificateArn, certificateName: props.certificateName, @@ -205,6 +210,20 @@ export class DomainName extends Resource implements IDomainName { securityPolicy: props.securityPolicy?.toString(), }; + if (this.isDuplicateEndpointType(domainNameConfig.endpointType)) { + throw new Error('no two domain name configurations should have the same endpointType'); + } + this.domainNameConfigurations.push(domainNameConfig); } + + // validates whether the new domain name configuration has a unique endpoint or not + private isDuplicateEndpointType(endpointType: string | undefined) : boolean { + for (let config of this.domainNameConfigurations) { + if (endpointType && endpointType == config.endpointType) { + return true; + } + } + return false; + } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts index e339efe7b9853..3da272a7701f1 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts @@ -271,6 +271,23 @@ describe('DomainName', () => { }); }); + test('throws when ownerhsip cert is used for non-mtls domain', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const t = () => { + new DomainName(stack, 'DomainName', { + domainName, + certificate: Certificate.fromCertificateArn(stack, 'cert2', certArn2), + ownershipCertificate: Certificate.fromCertificateArn(stack, 'ownershipCert', ownershipCertArn), + }); + }; + + // THEN + expect(t).toThrow(/ownership certificate can only be used with mtls domains/); + }); + test('add new configuration to a domain name for migration', () => { // GIVEN const stack = new Stack(); @@ -281,7 +298,7 @@ describe('DomainName', () => { certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), endpointType: EndpointType.REGIONAL, }); - dn.addDomainNameConfiguration({ + dn.addEndpoint({ domainName, certificate: Certificate.fromCertificateArn(stack, 'cert2', certArn2), endpointType: EndpointType.EDGE, @@ -302,4 +319,25 @@ describe('DomainName', () => { ], }); }); + + test('throws when endpoint types for two domain name configurations are the same', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const t = () => { + const dn = new DomainName(stack, 'DomainName', { + domainName, + certificate: Certificate.fromCertificateArn(stack, 'cert', certArn), + endpointType: EndpointType.REGIONAL, + }); + dn.addEndpoint({ + domainName, + certificate: Certificate.fromCertificateArn(stack, 'cert2', certArn2), + }); + }; + + // THEN + expect(t).toThrow(/no two domain name configurations should have the same endpointType/); + }); }); From c8a1f0ce5dadbe10ec284b2f9aa4eaac9fa2f619 Mon Sep 17 00:00:00 2001 From: Smriti Vashisth Date: Fri, 19 Nov 2021 10:03:55 -0800 Subject: [PATCH 5/6] refactor code to add new endpoint --- .../lib/common/domain-name.ts | 49 ++++++++++--------- .../test/http/domain-name.test.ts | 4 +- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts index 26aaaa389c278..bd965d17479d3 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts @@ -76,21 +76,29 @@ export interface DomainNameAttributes { /** * properties used for creating the DomainName */ -export interface DomainNameProps { +export interface DomainNameProps extends EndpointOptions { /** * The custom domain name */ readonly domainName: string; - /** - * The ACM certificate for this domain name. - * Certificate can be both ACM issued or imported. - */ - readonly certificate: ICertificate; + /** * The mutual TLS authentication configuration for a custom domain name. * @default - mTLS is not configured. */ readonly mtls?: MTLSConfig; +} + +/** + * properties for creating a domain name endpoint + */ +export interface EndpointOptions { + /** + * The ACM certificate for this domain name. + * Certificate can be both ACM issued or imported. + */ + readonly certificate: ICertificate; + /** * The user-friendly name of the certificate that will be used by the endpoint for this domain name. * @default - No friendly certificate name @@ -197,33 +205,28 @@ export class DomainName extends Resource implements IDomainName { } /** - * Adds a configuration to a domain name. Properties like certificate, endpoint type and security policy can be set using this method. - * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigatewayv2-domainname-domainnameconfiguration.html - * @param props domain name properties to be set + * Adds an endpoint to a domain name. + * @param options domain name endpoint properties to be set */ - public addEndpoint(props: DomainNameProps) : void { + public addEndpoint(options: EndpointOptions) : void { const domainNameConfig: CfnDomainName.DomainNameConfigurationProperty = { - certificateArn: props.certificate.certificateArn, - certificateName: props.certificateName, - endpointType: props.endpointType ? props.endpointType?.toString() : 'REGIONAL', - ownershipVerificationCertificateArn: props.ownershipCertificate?.certificateArn, - securityPolicy: props.securityPolicy?.toString(), + certificateArn: options.certificate.certificateArn, + certificateName: options.certificateName, + endpointType: options.endpointType ? options.endpointType?.toString() : 'REGIONAL', + ownershipVerificationCertificateArn: options.ownershipCertificate?.certificateArn, + securityPolicy: options.securityPolicy?.toString(), }; - if (this.isDuplicateEndpointType(domainNameConfig.endpointType)) { - throw new Error('no two domain name configurations should have the same endpointType'); - } - + this.validateDuplicateEndpointType(domainNameConfig.endpointType); this.domainNameConfigurations.push(domainNameConfig); } - // validates whether the new domain name configuration has a unique endpoint or not - private isDuplicateEndpointType(endpointType: string | undefined) : boolean { + // validates that the new domain name configuration has a unique endpoint + private validateDuplicateEndpointType(endpointType: string | undefined) : void { for (let config of this.domainNameConfigurations) { if (endpointType && endpointType == config.endpointType) { - return true; + throw new Error(`an endpoint with type ${endpointType} already exists`); } } - return false; } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts index 3da272a7701f1..b1c5e0b52e35f 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/domain-name.test.ts @@ -299,7 +299,6 @@ describe('DomainName', () => { endpointType: EndpointType.REGIONAL, }); dn.addEndpoint({ - domainName, certificate: Certificate.fromCertificateArn(stack, 'cert2', certArn2), endpointType: EndpointType.EDGE, }); @@ -332,12 +331,11 @@ describe('DomainName', () => { endpointType: EndpointType.REGIONAL, }); dn.addEndpoint({ - domainName, certificate: Certificate.fromCertificateArn(stack, 'cert2', certArn2), }); }; // THEN - expect(t).toThrow(/no two domain name configurations should have the same endpointType/); + expect(t).toThrow(/an endpoint with type REGIONAL already exists/); }); }); From 1e215310b2c12c5864de39fdefc8cd9279e187c4 Mon Sep 17 00:00:00 2001 From: Smriti Vashisth Date: Fri, 19 Nov 2021 10:09:05 -0800 Subject: [PATCH 6/6] update method name to validate endpoint --- packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts index bd965d17479d3..70bc42e67b64e 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/domain-name.ts @@ -217,12 +217,12 @@ export class DomainName extends Resource implements IDomainName { securityPolicy: options.securityPolicy?.toString(), }; - this.validateDuplicateEndpointType(domainNameConfig.endpointType); + this.validateEndpointType(domainNameConfig.endpointType); this.domainNameConfigurations.push(domainNameConfig); } // validates that the new domain name configuration has a unique endpoint - private validateDuplicateEndpointType(endpointType: string | undefined) : void { + private validateEndpointType(endpointType: string | undefined) : void { for (let config of this.domainNameConfigurations) { if (endpointType && endpointType == config.endpointType) { throw new Error(`an endpoint with type ${endpointType} already exists`);