Skip to content

Commit

Permalink
Merge branch 'master' into sagemaker-l2
Browse files Browse the repository at this point in the history
  • Loading branch information
petermeansrock authored Mar 11, 2020
2 parents b3678d2 + 040906f commit d16127e
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 0 deletions.
30 changes: 30 additions & 0 deletions packages/@aws-cdk/aws-apigateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,36 @@ plan.addApiStage({
});
```

In scenarios where you need to create a single api key and configure rate limiting for it, you can use `RateLimitedApiKey`.
This construct lets you specify rate limiting properties which should be applied only to the api key being created.
The API key created has the specified rate limits, such as quota and throttles, applied.

The following example shows how to use a rate limited api key :
```ts
const hello = new lambda.Function(this, 'hello', {
runtime: lambda.Runtime.NODEJS_10_X,
handler: 'hello.handler',
code: lambda.Code.fromAsset('lambda')
});

const api = new apigateway.RestApi(this, 'hello-api', { });
const integration = new apigateway.LambdaIntegration(hello);

const v1 = api.root.addResource('v1');
const echo = v1.addResource('echo');
const echoMethod = echo.addMethod('GET', integration, { apiKeyRequired: true });

const key = new apigateway.RateLimitedApiKey(this, 'rate-limited-api-key', {
customerId: 'hello-customer',
resources: [api],
quota: {
limit: 10000,
period: apigateway.Period.MONTH
}
});

```

### Working with models

When you work with Lambda integrations that are not Proxy integrations, you
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-apigateway/lib/api-key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface IApiKey extends IResourceBase {
*/
export interface ApiKeyProps extends ResourceOptions {
/**
* [disable-awslint:ref-via-interface]
* A list of resources this api key is associated with.
* @default none
*/
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-apigateway/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './stage';
export * from './integrations';
export * from './lambda-api';
export * from './api-key';
export * from './rate-limited-api-key';
export * from './usage-plan';
export * from './vpc-link';
export * from './methodresponse';
Expand Down
54 changes: 54 additions & 0 deletions packages/@aws-cdk/aws-apigateway/lib/rate-limited-api-key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Construct, Resource } from '@aws-cdk/core';
import { ApiKey, ApiKeyProps, IApiKey } from './api-key';
import { QuotaSettings, ThrottleSettings, UsagePlan, UsagePlanPerApiStage } from './usage-plan';

/**
* RateLimitedApiKey properties.
*/
export interface RateLimitedApiKeyProps extends ApiKeyProps {
/**
* API Stages to be associated with the RateLimitedApiKey.
* @default none
*/
readonly apiStages?: UsagePlanPerApiStage[];

/**
* Number of requests clients can make in a given time period.
* @default none
*/
readonly quota?: QuotaSettings;

/**
* Overall throttle settings for the API.
* @default none
*/
readonly throttle?: ThrottleSettings;
}

/**
* An API Gateway ApiKey, for which a rate limiting configuration can be specified.
*
* @resource AWS::ApiGateway::ApiKey
*/
export class RateLimitedApiKey extends Resource implements IApiKey {
public readonly keyId: string;

constructor(scope: Construct, id: string, props: RateLimitedApiKeyProps = { }) {
super(scope, id, {
physicalName: props.apiKeyName,
});

const resource = new ApiKey(this, 'Resource', props);

if (props.apiStages || props.quota || props.throttle) {
new UsagePlan(this, 'UsagePlanResource', {
apiKey: resource,
apiStages: props.apiStages,
quota: props.quota,
throttle: props.throttle
});
}

this.keyId = resource.keyId;
}
}
108 changes: 108 additions & 0 deletions packages/@aws-cdk/aws-apigateway/test/test.rate-limited-api-key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { expect, haveResource, ResourcePart } from '@aws-cdk/assert';
import * as cdk from '@aws-cdk/core';
import { Test } from "nodeunit";
import * as apigateway from '../lib';

const API_KEY_RESOURCE_TYPE = 'AWS::ApiGateway::ApiKey';
const USAGE_PLAN_RESOURCE_TYPE = 'AWS::ApiGateway::UsagePlan';
const USAGE_PLAN_KEY_RESOURCE_TYPE = 'AWS::ApiGateway::UsagePlanKey';

export = {
'default setup'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const api = new apigateway.RestApi(stack, 'my-api', { cloudWatchRole: false, deploy: false });
api.root.addMethod('GET'); // Need at least one method on the api

// WHEN
new apigateway.RateLimitedApiKey(stack, 'my-api-key');

// THEN
// should have an api key with no props defined.
expect(stack).to(haveResource(API_KEY_RESOURCE_TYPE, undefined, ResourcePart.CompleteDefinition));
// should not have a usage plan.
expect(stack).notTo(haveResource(USAGE_PLAN_RESOURCE_TYPE));
// should not have a usage plan key.
expect(stack).notTo(haveResource(USAGE_PLAN_KEY_RESOURCE_TYPE));

test.done();
},

'only api key is created when rate limiting properties are not provided'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const api = new apigateway.RestApi(stack, 'test-api', { cloudWatchRole: false, deploy: true, deployOptions: { stageName: 'test' } });
api.root.addMethod('GET'); // api must have atleast one method.

// WHEN
new apigateway.RateLimitedApiKey(stack, 'test-api-key', {
customerId: 'test-customer',
resources: [api]
});

// THEN
expect(stack).to(haveResource('AWS::ApiGateway::ApiKey', {
CustomerId: 'test-customer',
StageKeys: [
{
RestApiId: { Ref: "testapiD6451F70" },
StageName: { Ref: "testapiDeploymentStagetest5869DF71" }
}
]
}));
// should not have a usage plan.
expect(stack).notTo(haveResource(USAGE_PLAN_RESOURCE_TYPE));
// should not have a usage plan key.
expect(stack).notTo(haveResource(USAGE_PLAN_KEY_RESOURCE_TYPE));

test.done();
},

'api key and usage plan are created and linked when rate limiting properties are provided'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const api = new apigateway.RestApi(stack, 'test-api', { cloudWatchRole: false, deploy: true, deployOptions: { stageName: 'test' } });
api.root.addMethod('GET'); // api must have atleast one method.

// WHEN
new apigateway.RateLimitedApiKey(stack, 'test-api-key', {
customerId: 'test-customer',
resources: [api],
quota: {
limit: 10000,
period: apigateway.Period.MONTH
}
});

// THEN
// should have an api key
expect(stack).to(haveResource('AWS::ApiGateway::ApiKey', {
CustomerId: 'test-customer',
StageKeys: [
{
RestApiId: { Ref: "testapiD6451F70" },
StageName: { Ref: "testapiDeploymentStagetest5869DF71" }
}
]
}));
// should have a usage plan with specified quota.
expect(stack).to(haveResource(USAGE_PLAN_RESOURCE_TYPE, {
Quota: {
Limit: 10000,
Period: 'MONTH'
}
}, ResourcePart.Properties));
// should have a usage plan key linking the api key and usage plan
expect(stack).to(haveResource(USAGE_PLAN_KEY_RESOURCE_TYPE, {
KeyId: {
Ref: 'testapikey998028B6'
},
KeyType: 'API_KEY',
UsagePlanId: {
Ref: 'testapikeyUsagePlanResource66DB63D6'
}
}, ResourcePart.Properties));

test.done();
}
};

0 comments on commit d16127e

Please sign in to comment.