Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(apigatewayv2): http api - domain url for a stage #15973

Merged
merged 8 commits into from
Aug 12, 2021
6 changes: 6 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2/lib/common/api-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ export class ApiMapping extends Resource implements IApiMapping {
*/
public readonly mappingKey?: string;

/**
* API domain name
*/
public readonly domainName?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be optional.

Why not just set the type to IDomainName instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, I've updated the type here


constructor(scope: Construct, id: string, props: ApiMappingProps) {
super(scope, id);

Expand Down Expand Up @@ -121,5 +126,6 @@ export class ApiMapping extends Resource implements IApiMapping {

this.apiMappingId = resource.ref;
this.mappingKey = props.apiMappingKey;
this.domainName = props.domainName.name;
}
}
7 changes: 6 additions & 1 deletion packages/@aws-cdk/aws-apigatewayv2/lib/common/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export abstract class StageBase extends Resource implements IStage {
public abstract readonly stageName: string;
protected abstract readonly baseApi: IApi;

/**
* The created ApiMapping if domain mapping has been added
*/
protected apiMapping?: ApiMapping
samtwil marked this conversation as resolved.
Show resolved Hide resolved

/**
* The URL to this stage.
*/
Expand All @@ -45,7 +50,7 @@ export abstract class StageBase extends Resource implements IStage {
* @internal
*/
protected _addDomainMapping(domainMapping: DomainMappingOptions) {
new ApiMapping(this, `${domainMapping.domainName}${domainMapping.mappingKey}`, {
this.apiMapping = new ApiMapping(this, `${domainMapping.domainName}${domainMapping.mappingKey}`, {
nija-at marked this conversation as resolved.
Show resolved Hide resolved
api: this.baseApi,
domainName: domainMapping.domainName,
stage: this,
Expand Down
14 changes: 14 additions & 0 deletions packages/@aws-cdk/aws-apigatewayv2/lib/http/stage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,18 @@ export class HttpStage extends HttpStageBase {
const urlPath = this.stageName === DEFAULT_STAGE_NAME ? '' : this.stageName;
return `https://${this.api.apiId}.execute-api.${s.region}.${s.urlSuffix}/${urlPath}`;
}

/**
* The custom domain URL to this stage
*/
public get customDomainUrl(): string {
nija-at marked this conversation as resolved.
Show resolved Hide resolved
if (!this.apiMapping) {
throw new Error('customDomainUrl is not available when domain mapping has not been added');
}
if (!this.apiMapping.domainName) {
throw new Error('Unable to build customDomainUrl due to invalid domainName');
}

return `https://${this.apiMapping.domainName}/${this.apiMapping.mappingKey ?? ''}`;
}
}
68 changes: 67 additions & 1 deletion packages/@aws-cdk/aws-apigatewayv2/test/http/stage.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Template } from '@aws-cdk/assertions';
import { Certificate } from '@aws-cdk/aws-certificatemanager';
import { Metric } from '@aws-cdk/aws-cloudwatch';
import { Stack } from '@aws-cdk/core';
import { HttpApi, HttpStage } from '../../lib';
import { DomainName, HttpApi, HttpStage } from '../../lib';

const domainName = 'example.com';
const certArn = 'arn:aws:acm:us-east-1:111111111111:certificate';
nija-at marked this conversation as resolved.
Show resolved Hide resolved

describe('HttpStage', () => {
test('default', () => {
Expand Down Expand Up @@ -58,6 +61,69 @@ describe('HttpStage', () => {
expect(betaStage.url.endsWith('/')).toBe(false);
});

test('customDomainUrl returns the correct path', () => {
const stack = new Stack();
const api = new HttpApi(stack, 'Api', {
createDefaultStage: false,
});

const dn = new DomainName(stack, 'DN', {
domainName,
certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
});

const stage = new HttpStage(stack, 'DefaultStage', {
httpApi: api,
domainMapping: {
domainName: dn,
},
});

expect(stage.customDomainUrl.endsWith(`${domainName}/`)).toBe(true);
});

test('customDomainUrl throws error if domainMapping is not configured', () => {
const stack = new Stack();
const api = new HttpApi(stack, 'Api', {
createDefaultStage: false,
});

const stage = new HttpStage(stack, 'DefaultStage', {
httpApi: api,
});

const t = () => {
stage.customDomainUrl;
};

expect(t).toThrow('customDomainUrl is not available when domain mapping has not been added');
});

test('customDomainUrl throws error if domainName is invalid', () => {
const stack = new Stack();
const api = new HttpApi(stack, 'Api', {
createDefaultStage: false,
});

const dn = new DomainName(stack, 'DN', {
domainName: '',
certificate: Certificate.fromCertificateArn(stack, 'cert', certArn),
});

const stage = new HttpStage(stack, 'DefaultStage', {
httpApi: api,
domainMapping: {
domainName: dn,
},
});

const t = () => {
stage.customDomainUrl;
};

expect(t).toThrow('Unable to build customDomainUrl due to invalid domainName');
});

test('get metric', () => {
// GIVEN
const stack = new Stack();
Expand Down