Skip to content

Commit b92e6d3

Browse files
authored
Merge branch 'master' into ecs_alb_close_sg
2 parents 4f0aca5 + 3a53242 commit b92e6d3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2691
-272
lines changed

packages/@aws-cdk/aws-apigateway/README.md

+26-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ running on AWS Lambda, or any web application.
3838
- [Private Integrations](#private-integrations)
3939
- [Gateway Response](#gateway-response)
4040
- [OpenAPI Definition](#openapi-definition)
41+
- [Endpoint configuration](#endpoint-configuration)
4142
- [APIGateway v2](#apigateway-v2)
4243

4344
## Defining APIs
@@ -200,6 +201,12 @@ const key = api.addApiKey('ApiKey', {
200201
});
201202
```
202203

204+
Existing API keys can also be imported into a CDK app using its id.
205+
206+
```ts
207+
const importedKey = ApiKey.fromApiKeyId(this, 'imported-key', '<api-key-id>');
208+
```
209+
203210
In scenarios where you need to create a single api key and configure rate limiting for it, you can use `RateLimitedApiKey`.
204211
This construct lets you specify rate limiting properties which should be applied only to the api key being created.
205212
The API key created has the specified rate limits, such as quota and throttles, applied.
@@ -986,11 +993,29 @@ to configure these.
986993
There are a number of limitations in using OpenAPI definitions in API Gateway. Read the [Amazon API Gateway important
987994
notes for REST APIs](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-known-issues.html#api-gateway-known-issues-rest-apis)
988995
for more details.
989-
996+
990997
**Note:** When starting off with an OpenAPI definition using `SpecRestApi`, it is not possible to configure some
991998
properties that can be configured directly in the OpenAPI specification file. This is to prevent people duplication
992999
of these properties and potential confusion.
9931000
1001+
### Endpoint configuration
1002+
1003+
By default, `SpecRestApi` will create an edge optimized endpoint.
1004+
1005+
This can be modified as shown below:
1006+
1007+
```ts
1008+
const api = new apigateway.SpecRestApi(this, 'ExampleRestApi', {
1009+
// ...
1010+
endpointTypes: [apigateway.EndpointType.PRIVATE]
1011+
});
1012+
```
1013+
1014+
**Note:** For private endpoints you will still need to provide the
1015+
[`x-amazon-apigateway-policy`](https://docs.aws.amazon.com/apigateway/latest/developerguide/openapi-extensions-policy.html) and
1016+
[`x-amazon-apigateway-endpoint-configuration`](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions-endpoint-configuration.html)
1017+
in your openApi file.
1018+
9941019
## APIGateway v2
9951020
9961021
APIGateway v2 APIs are now moved to its own package named `aws-apigatewayv2`. For backwards compatibility, existing

packages/@aws-cdk/aws-apigateway/lib/api-key.ts

+12
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@ export interface ApiKeyProps extends ApiKeyOptions {
8181
* for Method resources that require an Api Key.
8282
*/
8383
export class ApiKey extends Resource implements IApiKey {
84+
85+
/**
86+
* Import an ApiKey by its Id
87+
*/
88+
public static fromApiKeyId(scope: Construct, id: string, apiKeyId: string): IApiKey {
89+
class Import extends Resource implements IApiKey {
90+
public keyId = apiKeyId;
91+
}
92+
93+
return new Import(scope, id);
94+
}
95+
8496
public readonly keyId: string;
8597

8698
constructor(scope: Construct, id: string, props: ApiKeyProps = { }) {

packages/@aws-cdk/aws-apigateway/lib/deployment.ts

+43-37
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { CfnResource, Construct, Lazy, RemovalPolicy, Resource, Stack } from '@aws-cdk/core';
21
import * as crypto from 'crypto';
2+
import { Construct, Lazy, RemovalPolicy, Resource, CfnResource } from '@aws-cdk/core';
33
import { CfnDeployment } from './apigateway.generated';
4-
import { IRestApi, RestApi, SpecRestApi } from './restapi';
4+
import { IRestApi, RestApi, SpecRestApi, RestApiBase } from './restapi';
5+
import { Method } from './method';
56

67
export interface DeploymentProps {
78
/**
@@ -77,6 +78,10 @@ export class Deployment extends Resource {
7778

7879
this.api = props.api;
7980
this.deploymentId = Lazy.stringValue({ produce: () => this.resource.ref });
81+
82+
if (props.api instanceof RestApiBase) {
83+
props.api._attachDeployment(this);
84+
}
8085
}
8186

8287
/**
@@ -92,27 +97,28 @@ export class Deployment extends Resource {
9297
}
9398

9499
/**
95-
* Hook into synthesis before it occurs and make any final adjustments.
100+
* Quoting from CloudFormation's docs:
101+
*
102+
* If you create an AWS::ApiGateway::RestApi resource and its methods (using
103+
* AWS::ApiGateway::Method) in the same template as your deployment, the
104+
* deployment must depend on the RestApi's methods. To create a dependency,
105+
* add a DependsOn attribute to the deployment. If you don't, AWS
106+
* CloudFormation creates the deployment right after it creates the RestApi
107+
* resource that doesn't contain any methods, and AWS CloudFormation
108+
* encounters the following error: The REST API doesn't contain any methods.
109+
*
110+
* @param method The method to add as a dependency of the deployment
111+
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-deployment.html
112+
* @see https://github.com/aws/aws-cdk/pull/6165
113+
* @internal
96114
*/
97-
protected prepare() {
98-
if (this.api instanceof RestApi) {
99-
// Ignore IRestApi that are imported
100-
101-
/*
102-
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-deployment.html
103-
* Quoting from CloudFormation's docs - "If you create an AWS::ApiGateway::RestApi resource and its methods (using AWS::ApiGateway::Method) in
104-
* the same template as your deployment, the deployment must depend on the RestApi's methods. To create a dependency, add a DependsOn attribute
105-
* to the deployment. If you don't, AWS CloudFormation creates the deployment right after it creates the RestApi resource that doesn't contain
106-
* any methods, and AWS CloudFormation encounters the following error: The REST API doesn't contain any methods."
107-
*/
108-
109-
/*
110-
* Adding a dependency between LatestDeployment and Method construct, using ConstructNode.addDependencies(), creates additional dependencies
111-
* between AWS::ApiGateway::Deployment and the AWS::Lambda::Permission nodes (children under Method), causing cyclic dependency errors. Hence,
112-
* falling back to declaring dependencies between the underlying CfnResources.
113-
*/
114-
this.api.methods.map(m => m.node.defaultChild as CfnResource).forEach(m => this.resource.addDependsOn(m));
115-
}
115+
public _addMethodDependency(method: Method) {
116+
// adding a dependency between the constructs using `node.addDependency()`
117+
// will create additional dependencies between `AWS::ApiGateway::Deployment`
118+
// and the `AWS::Lambda::Permission` resources (children under Method),
119+
// causing cyclic dependency errors. Hence, falling back to declaring
120+
// dependencies between the underlying CfnResources.
121+
this.node.addDependency(method.node.defaultChild as CfnResource);
116122
}
117123
}
118124

@@ -122,9 +128,9 @@ interface LatestDeploymentResourceProps {
122128
}
123129

124130
class LatestDeploymentResource extends CfnDeployment {
125-
private hashComponents = new Array<any>();
126-
private originalLogicalId: string;
127-
private api: IRestApi;
131+
private readonly hashComponents = new Array<any>();
132+
private readonly originalLogicalId: string;
133+
private readonly api: IRestApi;
128134

129135
constructor(scope: Construct, id: string, props: LatestDeploymentResourceProps) {
130136
super(scope, id, {
@@ -133,7 +139,8 @@ class LatestDeploymentResource extends CfnDeployment {
133139
});
134140

135141
this.api = props.restApi;
136-
this.originalLogicalId = Stack.of(this).getLogicalId(this);
142+
this.originalLogicalId = this.stack.getLogicalId(this);
143+
this.overrideLogicalId(Lazy.stringValue({ produce: () => this.calculateLogicalId() }));
137144
}
138145

139146
/**
@@ -150,27 +157,26 @@ class LatestDeploymentResource extends CfnDeployment {
150157
this.hashComponents.push(data);
151158
}
152159

153-
/**
154-
* Hooks into synthesis to calculate a logical ID that hashes all the components
155-
* add via `addToLogicalId`.
156-
*/
157-
protected prepare() {
160+
private calculateLogicalId() {
161+
const hash = [ ...this.hashComponents ];
162+
158163
if (this.api instanceof RestApi || this.api instanceof SpecRestApi) { // Ignore IRestApi that are imported
159164

160165
// Add CfnRestApi to the logical id so a new deployment is triggered when any of its properties change.
161166
const cfnRestApiCF = (this.api.node.defaultChild as any)._toCloudFormation();
162-
this.addToLogicalId(Stack.of(this).resolve(cfnRestApiCF));
167+
hash.push(this.stack.resolve(cfnRestApiCF));
163168
}
164169

165-
const stack = Stack.of(this);
170+
let lid = this.originalLogicalId;
166171

167172
// if hash components were added to the deployment, we use them to calculate
168173
// a logical ID for the deployment resource.
169-
if (this.hashComponents.length > 0) {
174+
if (hash.length > 0) {
170175
const md5 = crypto.createHash('md5');
171-
this.hashComponents.map(x => stack.resolve(x)).forEach(c => md5.update(JSON.stringify(c)));
172-
this.overrideLogicalId(this.originalLogicalId + md5.digest('hex'));
176+
hash.map(x => this.stack.resolve(x)).forEach(c => md5.update(JSON.stringify(c)));
177+
lid += md5.digest('hex');
173178
}
174-
super.prepare();
179+
180+
return lid;
175181
}
176182
}

packages/@aws-cdk/aws-apigateway/lib/restapi.ts

+69-30
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ export interface RestApiBaseProps {
159159
* @default - when no export name is given, output will be created without export
160160
*/
161161
readonly endpointExportName?: string;
162+
163+
/**
164+
* A list of the endpoint types of the API. Use this property when creating
165+
* an API.
166+
*
167+
* @default EndpointType.EDGE
168+
*/
169+
readonly endpointTypes?: EndpointType[];
162170
}
163171

164172
/**
@@ -218,18 +226,9 @@ export interface RestApiProps extends RestApiOptions {
218226
* The EndpointConfiguration property type specifies the endpoint types of a REST API
219227
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-apigateway-restapi-endpointconfiguration.html
220228
*
221-
* @default - No endpoint configuration
229+
* @default EndpointType.EDGE
222230
*/
223231
readonly endpointConfiguration?: EndpointConfiguration;
224-
225-
/**
226-
* A list of the endpoint types of the API. Use this property when creating
227-
* an API.
228-
*
229-
* @default - No endpoint types.
230-
* @deprecated this property is deprecated, use endpointConfiguration instead
231-
*/
232-
readonly endpointTypes?: EndpointType[];
233232
}
234233

235234
/**
@@ -248,7 +247,6 @@ export interface SpecRestApiProps extends RestApiBaseProps {
248247
* Base implementation that are common to various implementations of IRestApi
249248
*/
250249
export abstract class RestApiBase extends Resource implements IRestApi {
251-
252250
/**
253251
* Checks if the given object is an instance of RestApiBase.
254252
* @internal
@@ -384,6 +382,15 @@ export abstract class RestApiBase extends Resource implements IRestApi {
384382
ignore(method);
385383
}
386384

385+
/**
386+
* Associates a Deployment resource with this REST API.
387+
*
388+
* @internal
389+
*/
390+
public _attachDeployment(deployment: Deployment) {
391+
ignore(deployment);
392+
}
393+
387394
protected configureCloudWatchRole(apiResource: CfnRestApi) {
388395
const role = new iam.Role(this, 'CloudWatchRole', {
389396
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'),
@@ -423,6 +430,25 @@ export abstract class RestApiBase extends Resource implements IRestApi {
423430
}
424431
}
425432
}
433+
434+
/**
435+
* @internal
436+
*/
437+
protected _configureEndpoints(props: RestApiProps): CfnRestApi.EndpointConfigurationProperty | undefined {
438+
if (props.endpointTypes && props.endpointConfiguration) {
439+
throw new Error('Only one of the RestApi props, endpointTypes or endpointConfiguration, is allowed');
440+
}
441+
if (props.endpointConfiguration) {
442+
return {
443+
types: props.endpointConfiguration.types,
444+
vpcEndpointIds: props.endpointConfiguration?.vpcEndpoints?.map(vpcEndpoint => vpcEndpoint.vpcEndpointId),
445+
};
446+
}
447+
if (props.endpointTypes) {
448+
return { types: props.endpointTypes };
449+
}
450+
return undefined;
451+
}
426452
}
427453

428454
/**
@@ -463,6 +489,7 @@ export class SpecRestApi extends RestApiBase {
463489
failOnWarnings: props.failOnWarnings,
464490
body: apiDefConfig.inlineDefinition ? apiDefConfig.inlineDefinition : undefined,
465491
bodyS3Location: apiDefConfig.inlineDefinition ? undefined : apiDefConfig.s3Location,
492+
endpointConfiguration: this._configureEndpoints(props),
466493
parameters: props.parameters,
467494
});
468495
this.node.defaultChild = resource;
@@ -550,6 +577,11 @@ export class RestApi extends RestApiBase {
550577
*/
551578
public readonly methods = new Array<Method>();
552579

580+
/**
581+
* This list of deployments bound to this RestApi
582+
*/
583+
private readonly deployments = new Array<Deployment>();
584+
553585
constructor(scope: Construct, id: string, props: RestApiProps = { }) {
554586
super(scope, id, props);
555587

@@ -560,7 +592,7 @@ export class RestApi extends RestApiBase {
560592
failOnWarnings: props.failOnWarnings,
561593
minimumCompressionSize: props.minimumCompressionSize,
562594
binaryMediaTypes: props.binaryMediaTypes,
563-
endpointConfiguration: this.configureEndpoints(props),
595+
endpointConfiguration: this._configureEndpoints(props),
564596
apiKeySourceType: props.apiKeySourceType,
565597
cloneFrom: props.cloneFrom ? props.cloneFrom.restApiId : undefined,
566598
parameters: props.parameters,
@@ -627,6 +659,29 @@ export class RestApi extends RestApiBase {
627659
*/
628660
public _attachMethod(method: Method) {
629661
this.methods.push(method);
662+
663+
// add this method as a dependency to all deployments defined for this api
664+
// when additional deployments are added, _attachDeployment is called and
665+
// this method will be added there.
666+
for (const dep of this.deployments) {
667+
dep._addMethodDependency(method);
668+
}
669+
}
670+
671+
/**
672+
* Attaches a deployment to this REST API.
673+
*
674+
* @internal
675+
*/
676+
public _attachDeployment(deployment: Deployment) {
677+
this.deployments.push(deployment);
678+
679+
// add all methods that were already defined as dependencies of this
680+
// deployment when additional methods are added, _attachMethod is called and
681+
// it will be added as a dependency to this deployment.
682+
for (const method of this.methods) {
683+
deployment._addMethodDependency(method);
684+
}
630685
}
631686

632687
/**
@@ -639,22 +694,6 @@ export class RestApi extends RestApiBase {
639694

640695
return [];
641696
}
642-
643-
private configureEndpoints(props: RestApiProps): CfnRestApi.EndpointConfigurationProperty | undefined {
644-
if (props.endpointTypes && props.endpointConfiguration) {
645-
throw new Error('Only one of the RestApi props, endpointTypes or endpointConfiguration, is allowed');
646-
}
647-
if (props.endpointConfiguration) {
648-
return {
649-
types: props.endpointConfiguration.types,
650-
vpcEndpointIds: props.endpointConfiguration?.vpcEndpoints?.map(vpcEndpoint => vpcEndpoint.vpcEndpointId),
651-
};
652-
}
653-
if (props.endpointTypes) {
654-
return { types: props.endpointTypes };
655-
}
656-
return undefined;
657-
}
658697
}
659698

660699
/**
@@ -666,7 +705,7 @@ export interface EndpointConfiguration {
666705
/**
667706
* A list of endpoint types of an API or its custom domain name.
668707
*
669-
* @default - no endpoint types.
708+
* @default EndpointType.EDGE
670709
*/
671710
readonly types: EndpointType[];
672711

@@ -752,4 +791,4 @@ class RootResource extends ResourceBase {
752791

753792
function ignore(_x: any) {
754793
return;
755-
}
794+
}

packages/@aws-cdk/aws-apigateway/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@
115115
"from-method:@aws-cdk/aws-apigateway.Resource",
116116
"duration-prop-type:@aws-cdk/aws-apigateway.QuotaSettings.period",
117117
"duration-prop-type:@aws-cdk/aws-apigateway.ResponseType.INTEGRATION_TIMEOUT",
118-
"from-method:@aws-cdk/aws-apigateway.ApiKey",
119118
"ref-via-interface:@aws-cdk/aws-apigateway.ApiKeyProps.resources",
120119
"props-physical-name:@aws-cdk/aws-apigateway.DeploymentProps",
121120
"props-physical-name:@aws-cdk/aws-apigateway.MethodProps",

0 commit comments

Comments
 (0)