Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aws-apigateway): import existing usage plan #15771

Merged
merged 2 commits into from
Aug 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/@aws-cdk/aws-apigateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ plan.addApiStage({
});
```

Existing usage plans can be imported into a CDK app using its id.

```ts
const importedUsagePlan = UsagePlan.fromUsagePlanId(stack, 'imported-usage-plan', '<usage-plan-key-id>');
```

The name and value of the API Key can be specified at creation; if not
provided, a name and value will be automatically generated by API Gateway.

Expand Down
96 changes: 72 additions & 24 deletions packages/@aws-cdk/aws-apigateway/lib/usage-plan.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FeatureFlags, Lazy, Names, Resource, Token } from '@aws-cdk/core';
import { FeatureFlags, IResource, Lazy, Names, Resource, Token } from '@aws-cdk/core';
import { APIGATEWAY_USAGEPLANKEY_ORDERINSENSITIVE_ID } from '@aws-cdk/cx-api';
import { Construct } from 'constructs';
import { IApiKey } from './api-key';
Expand Down Expand Up @@ -156,35 +156,32 @@ export interface AddApiKeyOptions {
readonly overrideLogicalId?: string;
}

export class UsagePlan extends Resource {
/**
* A UsagePlan, either managed by this CDK app, or imported.
*/
export interface IUsagePlan extends IResource {
/**
* Id of the usage plan
* @attribute
*/
public readonly usagePlanId: string;

private readonly apiStages = new Array<UsagePlanPerApiStage>();
readonly usagePlanId: string;

constructor(scope: Construct, id: string, props: UsagePlanProps = { }) {
super(scope, id);
let resource: CfnUsagePlan;

resource = new CfnUsagePlan(this, 'Resource', {
apiStages: Lazy.any({ produce: () => this.renderApiStages(this.apiStages) }),
description: props.description,
quota: this.renderQuota(props),
throttle: this.renderThrottle(props.throttle),
usagePlanName: props.name,
});

this.apiStages.push(...(props.apiStages || []));
/**
* Adds an ApiKey.
*
* @param apiKey the api key to associate with this usage plan
* @param options options that control the behaviour of this method
*/
addApiKey(apiKey: IApiKey, options?: AddApiKeyOptions): void;

this.usagePlanId = resource.ref;
}

// Add ApiKey when
if (props.apiKey) {
this.addApiKey(props.apiKey);
}
}
abstract class UsagePlanBase extends Resource implements IUsagePlan {
/**
* Id of the usage plan
* @attribute
*/
public abstract readonly usagePlanId: string;

/**
* Adds an ApiKey.
Expand Down Expand Up @@ -213,6 +210,57 @@ export class UsagePlan extends Resource {
}
}

}

export class UsagePlan extends UsagePlanBase {

/**
* Import an externally defined usage plan using its ARN.
*
* @param scope the construct that will "own" the imported usage plan.
* @param id the id of the imported usage plan in the construct tree.
* @param usagePlanId the id of an existing usage plan.
*/
public static fromUsagePlanId(scope: Construct, id: string, usagePlanId: string): IUsagePlan {
class Import extends UsagePlanBase {
public readonly usagePlanId = usagePlanId;

constructor() {
super(scope, id);
}
}
return new Import();
}

/**
* @attribute
*/
public readonly usagePlanId: string;

private readonly apiStages = new Array<UsagePlanPerApiStage>();

constructor(scope: Construct, id: string, props: UsagePlanProps = { }) {
super(scope, id);
let resource: CfnUsagePlan;

resource = new CfnUsagePlan(this, 'Resource', {
apiStages: Lazy.any({ produce: () => this.renderApiStages(this.apiStages) }),
description: props.description,
quota: this.renderQuota(props),
throttle: this.renderThrottle(props.throttle),
usagePlanName: props.name,
});

this.apiStages.push(...(props.apiStages || []));

this.usagePlanId = resource.ref;

// Add ApiKey when
if (props.apiKey) {
this.addApiKey(props.apiKey);
}
}

/**
* Adds an apiStage.
* @param apiStage
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[
{
"Resources": {
"myusageplan4B391740": {
"Type": "AWS::ApiGateway::UsagePlan"
}
},
"Outputs": {
"ExportsOutputRefmyusageplan4B391740F6B819BA": {
"Value": {
"Ref": "myusageplan4B391740"
},
"Export": {
"Name": "test-apigateway-usageplan-create:ExportsOutputRefmyusageplan4B391740F6B819BA"
}
}
}
},
{
"Resources": {
"myusageplanUsagePlanKeyResourcetestapigatewayusageplanimportmyapikey14CF31667CCB4183": {
"Type": "AWS::ApiGateway::UsagePlanKey",
"Properties": {
"KeyId": {
"Ref": "myapikey5C116C09"
},
"KeyType": "API_KEY",
"UsagePlanId": {
"Fn::ImportValue": "test-apigateway-usageplan-create:ExportsOutputRefmyusageplan4B391740F6B819BA"
}
}
},
"myapikey5C116C09": {
"Type": "AWS::ApiGateway::ApiKey",
"Properties": {
"Enabled": true
}
}
}
}
]
37 changes: 37 additions & 0 deletions packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/// !cdk-integ *
import * as cdk from '@aws-cdk/core';
import * as apigateway from '../lib';
import { IUsagePlan } from '../lib';

class Create extends cdk.Stack {
public usagePlan: IUsagePlan;

constructor(scope: cdk.App, id: string) {
super(scope, id);

this.usagePlan = new apigateway.UsagePlan(this, 'myusageplan');
}
}

interface ImportStackProps extends cdk.StackProps {
usagePlan: apigateway.IUsagePlan;
}

class Import extends cdk.Stack {
constructor(scope: cdk.App, id: string, props: ImportStackProps) {
super(scope, id);

const usageplan = apigateway.UsagePlan.fromUsagePlanId(this, 'myusageplan', props.usagePlan.usagePlanId);
const apikey = new apigateway.ApiKey(this, 'myapikey');
usageplan.addApiKey(apikey);
}
}

const app = new cdk.App();

const test = new Create(app, 'test-apigateway-usageplan-create');
new Import(app, 'test-apigateway-usageplan-import', {
usagePlan: test.usagePlan,
});

app.synth();
20 changes: 20 additions & 0 deletions packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,26 @@ describe('usage plan', () => {
}, ResourcePart.Properties);
});


test('imported', () => {
// GIVEN
const stack = new cdk.Stack();
const usagePlan: apigateway.IUsagePlan = apigateway.UsagePlan.fromUsagePlanId(stack, 'my-usage-plan', 'imported-id');
const apiKey: apigateway.ApiKey = new apigateway.ApiKey(stack, 'my-api-key');

// WHEN
usagePlan.addApiKey(apiKey);

// THEN
expect(stack).toHaveResource('AWS::ApiGateway::UsagePlanKey', {
KeyId: {
Ref: 'myapikey1B052F70',
},
KeyType: 'API_KEY',
UsagePlanId: 'imported-id',
}, ResourcePart.Properties);
});

test('multiple keys', () => {
// GIVEN
const stack = new cdk.Stack();
Expand Down