Skip to content

Commit 203f114

Browse files
orangewiserix0rrr
authored andcommitted
feat(apigateway): support for UsagePlan, ApiKey, UsagePlanKey (#2564)
Add support for UsagePlan, ApiKey, UsagePlanKey. Fixes #723.
1 parent 1a7d4db commit 203f114

File tree

12 files changed

+704
-5
lines changed

12 files changed

+704
-5
lines changed

install.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ npm ci --global-style
1616
export PATH=node_modules/.bin:$PATH
1717

1818
echo "============================================================================================="
19-
echo "bootstrapping..."
19+
echo "cleanup and start bootstrapping..."
20+
lerna clean --yes
2021
lerna bootstrap --reject-cycles --ci

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,42 @@ book.addMethod('GET', getBookIntegration, {
104104
});
105105
```
106106

107+
The following example shows how to use an API Key with a usage plan:
108+
109+
```ts
110+
const hello = new lambda.Function(this, 'hello', {
111+
runtime: lambda.Runtime.NodeJS810,
112+
handler: 'hello.handler',
113+
code: lambda.Code.asset('lambda')
114+
});
115+
116+
const api = new apigateway.RestApi(this, 'hello-api', { });
117+
const integration = new apigateway.LambdaIntegration(hello);
118+
119+
const v1 = api.root.addResource('v1');
120+
const echo = v1.addResource('echo');
121+
const echoMethod = echo.addMethod('GET', integration, { apiKeyRequired: true });
122+
const key = api.addApiKey('ApiKey');
123+
124+
const plan = api.addUsagePlan('UsagePlan', {
125+
name: 'Easy',
126+
apiKey: key
127+
});
128+
129+
plan.addApiStage({
130+
stage: api.deploymentStage,
131+
throttle: [
132+
{
133+
method: echoMethod,
134+
throttle: {
135+
rateLimit: 10,
136+
burstLimit: 2
137+
}
138+
}
139+
]
140+
});
141+
```
142+
107143
#### Default Integration and Method Options
108144

109145
The `defaultIntegration` and `defaultMethodOptions` properties can be used to
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { Construct, IResource as IResourceBase, Resource } from '@aws-cdk/cdk';
2+
import { CfnApiKey } from './apigateway.generated';
3+
import { ResourceOptions } from "./resource";
4+
import { RestApi } from './restapi';
5+
6+
/**
7+
* API keys are alphanumeric string values that you distribute to
8+
* app developer customers to grant access to your API
9+
*/
10+
export interface ApiKeyAttributes {
11+
/**
12+
* The API key ID.
13+
* @attribute
14+
*/
15+
readonly keyId: string;
16+
}
17+
18+
/**
19+
* API keys are alphanumeric string values that you distribute to
20+
* app developer customers to grant access to your API
21+
*/
22+
export interface IApiKey extends IResourceBase {
23+
/**
24+
* The API key ID.
25+
* @attribute
26+
*/
27+
readonly keyId: string;
28+
}
29+
30+
/**
31+
* ApiKey Properties.
32+
*/
33+
export interface ApiKeyProps extends ResourceOptions {
34+
/**
35+
* A list of resources this api key is associated with.
36+
* @default none
37+
*/
38+
readonly resources?: RestApi[];
39+
40+
/**
41+
* An AWS Marketplace customer identifier to use when integrating with the AWS SaaS Marketplace.
42+
* @link http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-apikey.html#cfn-apigateway-apikey-customerid
43+
* @default none
44+
*/
45+
readonly customerId?: string;
46+
47+
/**
48+
* A description of the purpose of the API key.
49+
* @link http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-apikey.html#cfn-apigateway-apikey-description
50+
* @default none
51+
*/
52+
readonly description?: string;
53+
54+
/**
55+
* Indicates whether the API key can be used by clients.
56+
* @link http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-apikey.html#cfn-apigateway-apikey-enabled
57+
* @default true
58+
*/
59+
readonly enabled?: boolean;
60+
61+
/**
62+
* Specifies whether the key identifier is distinct from the created API key value.
63+
* @link http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-apikey.html#cfn-apigateway-apikey-generatedistinctid
64+
* @default false
65+
*/
66+
readonly generateDistinctId?: boolean;
67+
68+
/**
69+
* A name for the API key. If you don't specify a name, AWS CloudFormation generates a unique physical ID and uses that ID for the API key name.
70+
* @link http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-apikey.html#cfn-apigateway-apikey-name
71+
* @default automically generated name
72+
*/
73+
readonly name?: string;
74+
}
75+
76+
/**
77+
* An API Gateway ApiKey.
78+
*
79+
* An ApiKey can be distributed to API clients that are executing requests
80+
* for Method resources that require an Api Key.
81+
*/
82+
export class ApiKey extends Resource implements IApiKey {
83+
public readonly keyId: string;
84+
85+
constructor(scope: Construct, id: string, props: ApiKeyProps = { }) {
86+
super(scope, id);
87+
88+
const resource = new CfnApiKey(this, 'Resource', {
89+
customerId: props.customerId,
90+
description: props.description,
91+
enabled: props.enabled || true,
92+
generateDistinctId: props.generateDistinctId,
93+
name: props.name,
94+
stageKeys: this.renderStageKeys(props.resources)
95+
});
96+
97+
this.keyId = resource.ref;
98+
}
99+
100+
private renderStageKeys(resources: RestApi[] | undefined): CfnApiKey.StageKeyProperty[] | undefined {
101+
if (!resources) {
102+
return undefined;
103+
}
104+
105+
return resources.map((resource: RestApi) => {
106+
const restApi = resource;
107+
const restApiId = restApi.restApiId;
108+
const stageName = restApi.deploymentStage!.stageName.toString();
109+
return { restApiId, stageName };
110+
});
111+
}
112+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export * from './deployment';
66
export * from './stage';
77
export * from './integrations';
88
export * from './lambda-api';
9+
export * from './api-key';
10+
export * from './usage-plan';
911
export * from './vpc-link';
1012
export * from './methodresponse';
1113
export * from './model';

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import iam = require('@aws-cdk/aws-iam');
22
import { CfnOutput, Construct, IResource as IResourceBase, Resource } from '@aws-cdk/cdk';
3+
import { ApiKey, IApiKey } from './api-key';
34
import { CfnAccount, CfnRestApi } from './apigateway.generated';
45
import { Deployment } from './deployment';
56
import { Integration } from './integration';
67
import { Method, MethodOptions } from './method';
78
import { IResource, ResourceBase, ResourceOptions } from './resource';
89
import { Stage, StageOptions } from './stage';
10+
import { UsagePlan, UsagePlanProps } from './usage-plan';
911

1012
export interface IRestApi extends IResourceBase {
1113
/**
@@ -249,6 +251,22 @@ export class RestApi extends Resource implements IRestApi {
249251
return this.deploymentStage.urlForPath(path);
250252
}
251253

254+
/**
255+
* Adds a usage plan.
256+
*/
257+
public addUsagePlan(id: string, props: UsagePlanProps): UsagePlan {
258+
return new UsagePlan(this, id, props);
259+
}
260+
261+
/**
262+
* Add an ApiKey
263+
*/
264+
public addApiKey(id: string): IApiKey {
265+
return new ApiKey(this, id, {
266+
resources: [this]
267+
});
268+
}
269+
252270
/**
253271
* @returns The "execute-api" ARN.
254272
* @default "*" returns the execute API ARN for all methods/resources in

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ export class Stage extends Resource {
163163
*/
164164
public readonly stageName: string;
165165

166-
private readonly restApi: IRestApi;
166+
public readonly restApi: IRestApi;
167167
private enableCacheCluster?: boolean;
168168

169169
constructor(scope: Construct, id: string, props: StageProps) {

0 commit comments

Comments
 (0)