Skip to content

Commit 53e1191

Browse files
author
Elad Ben-Israel
authored
refactor(apigateway): API cleanups (#2903)
* Fixed formatting * Add `vcpLink.addTargets` and made `targets` optional, with a validation error. BREAKING CHANGE: `MethodOptions.authorizerId` is now called `authorizer` and accepts an `IAuthorizer` which is a placeholder interface for the authorizer resource. * **apigateway:** `restapi.executeApiArn` renamed to `arnForExecuteApi`. * **apigateway:** `restapi.latestDeployment` and `deploymentStage` are now read-only.
1 parent 3ae7511 commit 53e1191

File tree

8 files changed

+126
-48
lines changed

8 files changed

+126
-48
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Represents an API Gateway authorizer.
3+
*/
4+
export interface IAuthorizer {
5+
/**
6+
* The authorizer ID.
7+
*/
8+
readonly authorizerId: string;
9+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export * from './usage-plan';
1111
export * from './vpc-link';
1212
export * from './methodresponse';
1313
export * from './model';
14+
export * from './authorizer';
1415

1516
// AWS::ApiGateway CloudFormation Resources:
1617
export * from './apigateway.generated';

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Construct, Resource, Stack } from '@aws-cdk/cdk';
22
import { CfnMethod, CfnMethodProps } from './apigateway.generated';
3+
import { IAuthorizer } from './authorizer';
34
import { ConnectionType, Integration } from './integration';
45
import { MockIntegration } from './integrations/mock';
56
import { MethodResponse } from './methodresponse';
@@ -23,11 +24,8 @@ export interface MethodOptions {
2324
/**
2425
* If `authorizationType` is `Custom`, this specifies the ID of the method
2526
* authorizer resource.
26-
*
27-
* NOTE: in the future this will be replaced with an `IAuthorizer`
28-
* construct.
2927
*/
30-
readonly authorizerId?: string;
28+
readonly authorizer?: IAuthorizer;
3129

3230
/**
3331
* Indicates whether the method requires clients to submit a valid API key.
@@ -108,6 +106,7 @@ export class Method extends Resource {
108106
const options = props.options || {};
109107

110108
const defaultMethodOptions = props.resource.defaultMethodOptions || {};
109+
const authorizer = options.authorizer || defaultMethodOptions.authorizer;
111110

112111
const methodProps: CfnMethodProps = {
113112
resourceId: props.resource.resourceId,
@@ -116,7 +115,7 @@ export class Method extends Resource {
116115
operationName: options.operationName || defaultMethodOptions.operationName,
117116
apiKeyRequired: options.apiKeyRequired || defaultMethodOptions.apiKeyRequired,
118117
authorizationType: options.authorizationType || defaultMethodOptions.authorizationType || AuthorizationType.None,
119-
authorizerId: options.authorizerId || defaultMethodOptions.authorizerId,
118+
authorizerId: authorizer && authorizer.authorizerId,
120119
requestParameters: options.requestParameters,
121120
integration: this.renderIntegration(props.integration),
122121
methodResponses: this.renderMethodResponses(options.methodResponses),
@@ -153,15 +152,15 @@ export class Method extends Resource {
153152
}
154153

155154
const stage = this.restApi.deploymentStage.stageName.toString();
156-
return this.restApi.executeApiArn(this.httpMethod, this.resource.path, stage);
155+
return this.restApi.arnForExecuteApi(this.httpMethod, this.resource.path, stage);
157156
}
158157

159158
/**
160159
* Returns an execute-api ARN for this method's "test-invoke-stage" stage.
161160
* This stage is used by the AWS Console UI when testing the method.
162161
*/
163162
public get testMethodArn(): string {
164-
return this.restApi.executeApiArn(this.httpMethod, this.resource.path, 'test-invoke-stage');
163+
return this.restApi.arnForExecuteApi(this.httpMethod, this.resource.path, 'test-invoke-stage');
165164
}
166165

167166
private renderIntegration(integration?: Integration): CfnMethod.IntegrationProperty {

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

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -178,30 +178,24 @@ export class RestApi extends Resource implements IRestApi {
178178
public readonly restApiRootResourceId: string;
179179

180180
/**
181-
* API Gateway deployment that represents the latest changes of the API.
182-
* This resource will be automatically updated every time the REST API model changes.
183-
* This will be undefined if `deploy` is false.
181+
* Represents the root resource ("/") of this API. Use it to define the API model:
182+
*
183+
* api.root.addMethod('ANY', redirectToHomePage); // "ANY /"
184+
* api.root.addResource('friends').addMethod('GET', getFriendsHandler); // "GET /friends"
185+
*
184186
*/
185-
public latestDeployment?: Deployment;
187+
public readonly root: IResource;
186188

187189
/**
188190
* API Gateway stage that points to the latest deployment (if defined).
189191
*
190192
* If `deploy` is disabled, you will need to explicitly assign this value in order to
191193
* set up integrations.
192194
*/
193-
public deploymentStage?: Stage;
194-
195-
/**
196-
* Represents the root resource ("/") of this API. Use it to define the API model:
197-
*
198-
* api.root.addMethod('ANY', redirectToHomePage); // "ANY /"
199-
* api.root.addResource('friends').addMethod('GET', getFriendsHandler); // "GET /friends"
200-
*
201-
*/
202-
public readonly root: IResource;
195+
public deploymentStage: Stage;
203196

204197
private readonly methods = new Array<Method>();
198+
private _latestDeployment: Deployment | undefined;
205199

206200
constructor(scope: Construct, id: string, props: RestApiProps = { }) {
207201
super(scope, id);
@@ -231,6 +225,15 @@ export class RestApi extends Resource implements IRestApi {
231225
this.root = new RootResource(this, props, resource.attrRootResourceId);
232226
}
233227

228+
/**
229+
* API Gateway deployment that represents the latest changes of the API.
230+
* This resource will be automatically updated every time the REST API model changes.
231+
* This will be undefined if `deploy` is false.
232+
*/
233+
public get latestDeployment() {
234+
return this._latestDeployment;
235+
}
236+
234237
/**
235238
* The deployed root URL of this REST API.
236239
*/
@@ -275,7 +278,7 @@ export class RestApi extends Resource implements IRestApi {
275278
* @param path The resource path. Must start with '/' (default `*`)
276279
* @param stage The stage (default `*`)
277280
*/
278-
public executeApiArn(method: string = '*', path: string = '/*', stage: string = '*') {
281+
public arnForExecuteApi(method: string = '*', path: string = '/*', stage: string = '*') {
279282
if (!path.startsWith('/')) {
280283
throw new Error(`"path" must begin with a "/": '${path}'`);
281284
}
@@ -317,7 +320,7 @@ export class RestApi extends Resource implements IRestApi {
317320
const deploy = props.deploy === undefined ? true : props.deploy;
318321
if (deploy) {
319322

320-
this.latestDeployment = new Deployment(this, 'Deployment', {
323+
this._latestDeployment = new Deployment(this, 'Deployment', {
321324
description: 'Automatically created by the RestApi construct',
322325
api: this,
323326
retainDeployments: props.retainDeployments
@@ -328,7 +331,7 @@ export class RestApi extends Resource implements IRestApi {
328331
const stageName = (props.deployOptions && props.deployOptions.stageName) || 'prod';
329332

330333
this.deploymentStage = new Stage(this, `DeploymentStage.${stageName}`, {
331-
deployment: this.latestDeployment,
334+
deployment: this._latestDeployment,
332335
...props.deployOptions
333336
});
334337

packages/@aws-cdk/aws-apigateway/lib/usage-plan.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,13 @@ export interface ThrottlingPerMethod {
6666
* The method for which you specify the throttling settings.
6767
* @default none
6868
*/
69-
readonly method: Method,
69+
readonly method: Method;
7070

7171
/**
7272
* Specifies the overall request rate (average requests per second) and burst capacity.
7373
* @default none
7474
*/
75-
readonly throttle: ThrottleSettings
75+
readonly throttle: ThrottleSettings;
7676
}
7777

7878
/**
@@ -90,57 +90,57 @@ export interface UsagePlanPerApiStage {
9090
/**
9191
* @default none
9292
*/
93-
readonly api?: IRestApi,
93+
readonly api?: IRestApi;
9494

9595
/**
9696
*
9797
* [disable-awslint:ref-via-interface]
9898
* @default none
9999
*/
100-
readonly stage?: Stage,
100+
readonly stage?: Stage;
101101

102102
/**
103103
* @default none
104104
*/
105-
readonly throttle?: ThrottlingPerMethod[]
105+
readonly throttle?: ThrottlingPerMethod[];
106106
}
107107

108108
export interface UsagePlanProps {
109109
/**
110110
* API Stages to be associated which the usage plan.
111111
* @default none
112112
*/
113-
readonly apiStages?: UsagePlanPerApiStage[],
113+
readonly apiStages?: UsagePlanPerApiStage[];
114114

115115
/**
116116
* Represents usage plan purpose.
117117
* @default none
118118
*/
119-
readonly description?: string,
119+
readonly description?: string;
120120

121121
/**
122122
* Number of requests clients can make in a given time period.
123123
* @default none
124124
*/
125-
readonly quota?: QuotaSettings
125+
readonly quota?: QuotaSettings;
126126

127127
/**
128128
* Overall throttle settings for the API.
129129
* @default none
130130
*/
131-
readonly throttle?: ThrottleSettings,
131+
readonly throttle?: ThrottleSettings;
132132

133133
/**
134134
* Name for this usage plan.
135135
* @default none
136136
*/
137-
readonly name?: string
137+
readonly name?: string;
138138

139139
/**
140140
* ApiKey to be associated with the usage plan.
141141
* @default none
142142
*/
143-
readonly apiKey?: IApiKey
143+
readonly apiKey?: IApiKey;
144144
}
145145

146146
export class UsagePlan extends Resource {

packages/@aws-cdk/aws-apigateway/lib/vpc-link.ts

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import elbv2 = require('@aws-cdk/aws-elasticloadbalancingv2');
2-
import { Construct, Resource } from '@aws-cdk/cdk';
2+
import { Construct, Lazy, Resource } from '@aws-cdk/cdk';
33
import { CfnVpcLink } from './apigateway.generated';
44

55
/**
@@ -8,9 +8,9 @@ import { CfnVpcLink } from './apigateway.generated';
88
export interface VpcLinkProps {
99
/**
1010
* The name used to label and identify the VPC link.
11-
* @default automatically generated name
11+
* @default - automatically generated name
1212
*/
13-
readonly name?: string;
13+
readonly vpcLinkName?: string;
1414

1515
/**
1616
* The description of the VPC link.
@@ -21,8 +21,10 @@ export interface VpcLinkProps {
2121
/**
2222
* The network load balancers of the VPC targeted by the VPC link.
2323
* The network load balancers must be owned by the same AWS account of the API owner.
24+
*
25+
* @default - no targets. Use `addTargets` to add targets
2426
*/
25-
readonly targets: elbv2.INetworkLoadBalancer[];
27+
readonly targets?: elbv2.INetworkLoadBalancer[];
2628
}
2729

2830
/**
@@ -36,15 +38,36 @@ export class VpcLink extends Resource {
3638
*/
3739
public readonly vpcLinkId: string;
3840

39-
constructor(scope: Construct, id: string, props: VpcLinkProps) {
41+
private readonly targets = new Array<elbv2.INetworkLoadBalancer>();
42+
43+
constructor(scope: Construct, id: string, props: VpcLinkProps = {}) {
4044
super(scope, id);
4145

4246
const cfnResource = new CfnVpcLink(this, 'Resource', {
43-
name: props.name || this.node.uniqueId,
47+
name: props.vpcLinkName || this.node.uniqueId,
4448
description: props.description,
45-
targetArns: props.targets.map(nlb => nlb.loadBalancerArn)
49+
targetArns: Lazy.listValue({ produce: () => this.renderTargets() })
4650
});
4751

4852
this.vpcLinkId = cfnResource.refAsString;
53+
54+
if (props.targets) {
55+
this.addTargets(...props.targets);
56+
}
57+
}
58+
59+
public addTargets(...targets: elbv2.INetworkLoadBalancer[]) {
60+
this.targets.push(...targets);
61+
}
62+
63+
protected validate(): string[] {
64+
if (this.targets.length === 0) {
65+
return [ `No targets added to vpc link` ];
66+
}
67+
return [];
68+
}
69+
70+
private renderTargets() {
71+
return this.targets.map(nlb => nlb.loadBalancerArn);
4972
}
5073
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ export = {
402402
api.root.addMethod('GET');
403403

404404
// WHEN
405-
const arn = api.executeApiArn('method', '/path', 'stage');
405+
const arn = api.arnForExecuteApi('method', '/path', 'stage');
406406

407407
// THEN
408408
test.deepEqual(stack.resolve(arn), { 'Fn::Join':
@@ -426,7 +426,7 @@ export = {
426426
api.root.addMethod('GET');
427427

428428
// THEN
429-
test.throws(() => api.executeApiArn('method', 'hey-path', 'stage'), /"path" must begin with a "\/": 'hey-path'/);
429+
test.throws(() => api.arnForExecuteApi('method', 'hey-path', 'stage'), /"path" must begin with a "\/": 'hey-path'/);
430430
test.done();
431431
},
432432

@@ -534,7 +534,7 @@ export = {
534534
const api = new apigateway.RestApi(stack, 'myapi', {
535535
defaultIntegration: rootInteg,
536536
defaultMethodOptions: {
537-
authorizerId: 'AUTHID',
537+
authorizer: { authorizerId: 'AUTHID' },
538538
authorizationType: apigateway.AuthorizationType.IAM,
539539
}
540540
});
@@ -553,7 +553,7 @@ export = {
553553
const child2 = api.root.addResource('child2', {
554554
defaultIntegration: new apigateway.MockIntegration(),
555555
defaultMethodOptions: {
556-
authorizerId: 'AUTHID2',
556+
authorizer: { authorizerId: 'AUTHID2' },
557557
}
558558
});
559559

0 commit comments

Comments
 (0)