-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(apigateway): support custom domain names
Adds support for custom domain names in API Gateway and using route53 aliases. Fixes #3103 Misc: change the awslint rule that verifies resource attributes to use CloudFormation attribute names.
- Loading branch information
Elad Ben-Israel
committed
Jun 30, 2019
1 parent
12e6380
commit 8c42a2b
Showing
23 changed files
with
1,223 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { Construct, Resource, Token } from '@aws-cdk/core'; | ||
import { CfnBasePathMapping } from './apigateway.generated'; | ||
import { IDomainName } from './domain-name'; | ||
import { IRestApi, RestApi } from './restapi'; | ||
|
||
export interface BasePathMappingOptions { | ||
/** | ||
* The base path name that callers of the API must provide in the URL after | ||
* the domain name (e.g. `example.com/base-path`). If you specify this | ||
* property, it can't be an empty string. | ||
* | ||
* @default - map requests from the domain root (e.g. `example.com`). If this | ||
* is undefined, no additional mappings will be allowed on this domain name. | ||
*/ | ||
readonly basePath?: string; | ||
} | ||
|
||
export interface BasePathMappingProps extends BasePathMappingOptions { | ||
/** | ||
* The DomainName to associate with this base path mapping. | ||
*/ | ||
readonly domainName: IDomainName; | ||
|
||
/** | ||
* The RestApi resource to target. | ||
*/ | ||
readonly restApi: IRestApi; | ||
} | ||
|
||
/** | ||
* This resource creates a base path that clients who call your API must use in | ||
* the invocation URL. | ||
* | ||
* In most cases, you will probably want to use | ||
* `DomainName.addBasePathMapping()` to define mappings. | ||
*/ | ||
export class BasePathMapping extends Resource { | ||
constructor(scope: Construct, id: string, props: BasePathMappingProps) { | ||
super(scope, id); | ||
|
||
if (props.basePath && !Token.isUnresolved(props.basePath)) { | ||
if (!props.basePath.match(/^[a-z0-9$_.+!*'()-]+$/)) { | ||
throw new Error(`A base path may only contain letters, numbers, and one of "$-_.+!*'()", received: ${props.basePath}`); | ||
} | ||
} | ||
|
||
// if this is an owned API and it has a deployment stage, map all requests | ||
// to that stage. otherwise, the stage will have to be specified in the URL. | ||
const stage = props.restApi instanceof RestApi | ||
? props.restApi.deploymentStage | ||
: undefined; | ||
|
||
new CfnBasePathMapping(this, 'Resource', { | ||
basePath: props.basePath, | ||
domainName: props.domainName.domainName, | ||
restApiId: props.restApi.restApiId, | ||
stage: stage && stage.stageName, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import acm = require('@aws-cdk/aws-certificatemanager'); | ||
import { Construct, IResource, Resource } from '@aws-cdk/core'; | ||
import { CfnDomainName } from './apigateway.generated'; | ||
import { BasePathMapping, BasePathMappingOptions } from './base-path-mapping'; | ||
import { EndpointType, IRestApi} from './restapi'; | ||
|
||
export interface DomainNameOptions { | ||
/** | ||
* The custom domain name for your API. Uppercase letters are not supported. | ||
*/ | ||
readonly domainName: string; | ||
|
||
/** | ||
* The reference to an AWS-managed certificate for use by the edge-optimized | ||
* endpoint for the domain name. For "EDGE" domain names, the certificate | ||
* needs to be in the US East (N. Virginia) region. | ||
*/ | ||
readonly certificate: acm.ICertificate; | ||
|
||
/** | ||
* The type of endpoint for this DomainName. | ||
* @default REGIONAL | ||
*/ | ||
readonly endpointType?: EndpointType; | ||
} | ||
|
||
export interface DomainNameProps extends DomainNameOptions { | ||
/** | ||
* If specified, all requests to this domain will be mapped to the production | ||
* deployment of this API. If you wish to map this domain to multiple APIs | ||
* with different base paths, don't specify this option and use | ||
* `addBasePathMapping`. | ||
* | ||
* @default - you will have to call `addBasePathMapping` to map this domain to | ||
* API endpoints. | ||
*/ | ||
readonly mapping?: IRestApi; | ||
} | ||
|
||
export interface IDomainName extends IResource { | ||
/** | ||
* The domain name (e.g. `example.com`) | ||
* | ||
* @attribute DomainName | ||
*/ | ||
readonly domainName: string; | ||
|
||
/** | ||
* The Route53 alias target to use in order to connect a record set to this domain through an alias. | ||
* | ||
* @attribute DistributionDomainName,RegionalDomainName | ||
*/ | ||
readonly domainNameAliasDomainName: string; | ||
|
||
/** | ||
* Thje Route53 hosted zone ID to use in order to connect a record set to this domain through an alias. | ||
* | ||
* @attribute DistributionHostedZoneId,RegionalHostedZoneId | ||
*/ | ||
readonly domainNameAliasHostedZoneId: string; | ||
} | ||
|
||
export class DomainName extends Resource implements IDomainName { | ||
|
||
/** | ||
* Imports an existing domain name. | ||
*/ | ||
public static fromDomainNameAttributes(scope: Construct, id: string, attrs: DomainNameAttributes): IDomainName { | ||
class Import extends Resource implements IDomainName { | ||
public readonly domainName = attrs.domainName; | ||
public readonly domainNameAliasDomainName = attrs.domainNameAliasTarget; | ||
public readonly domainNameAliasHostedZoneId = attrs.domainNameAliasHostedZoneId; | ||
} | ||
|
||
return new Import(scope, id); | ||
} | ||
|
||
public readonly domainName: string; | ||
public readonly domainNameAliasDomainName: string; | ||
public readonly domainNameAliasHostedZoneId: string; | ||
|
||
constructor(scope: Construct, id: string, props: DomainNameProps) { | ||
super(scope, id); | ||
|
||
const endpointType = props.endpointType || EndpointType.REGIONAL; | ||
const edge = endpointType === EndpointType.EDGE; | ||
|
||
const resource = new CfnDomainName(this, 'Resource', { | ||
domainName: props.domainName, | ||
certificateArn: edge ? props.certificate.certificateArn : undefined, | ||
regionalCertificateArn: edge ? undefined : props.certificate.certificateArn, | ||
endpointConfiguration: { types: [endpointType] }, | ||
}); | ||
|
||
this.domainName = resource.ref; | ||
|
||
this.domainNameAliasDomainName = edge | ||
? resource.attrDistributionDomainName | ||
: resource.attrRegionalDomainName; | ||
|
||
this.domainNameAliasHostedZoneId = edge | ||
? resource.attrDistributionHostedZoneId | ||
: resource.attrRegionalHostedZoneId; | ||
|
||
if (props.mapping) { | ||
this.addBasePathMapping(props.mapping); | ||
} | ||
} | ||
|
||
/** | ||
* Maps this domain to an API endpoint. | ||
* @param targetApi That target API endpoint, requests will be mapped to the deployment stage. | ||
* @param options Options for mapping to base path with or without a stage | ||
*/ | ||
public addBasePathMapping(targetApi: IRestApi, options: BasePathMappingOptions = { }) { | ||
const basePath = options.basePath || '/'; | ||
const id = `Map:${basePath}=>${targetApi.node.uniqueId}`; | ||
return new BasePathMapping(this, id, { | ||
domainName: this, | ||
restApi: targetApi, | ||
...options | ||
}); | ||
} | ||
} | ||
|
||
export interface DomainNameAttributes { | ||
/** | ||
* The domain name (e.g. `example.com`) | ||
*/ | ||
readonly domainName: string; | ||
|
||
/** | ||
* The Route53 alias target to use in order to connect a record set to this domain through an alias. | ||
*/ | ||
readonly domainNameAliasTarget: string; | ||
|
||
/** | ||
* Thje Route53 hosted zone ID to use in order to connect a record set to this domain through an alias. | ||
*/ | ||
readonly domainNameAliasHostedZoneId: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.