From f4f80481aadb28e765317a59aa15fc2c28a050ae Mon Sep 17 00:00:00 2001 From: Julian Michel Date: Mon, 26 Jul 2021 19:43:41 +0000 Subject: [PATCH] feat(aws-apigateway): import existing usage plan --- packages/@aws-cdk/aws-apigateway/README.md | 6 ++ .../@aws-cdk/aws-apigateway/lib/usage-plan.ts | 96 ++++++++++++++----- .../integ.usage-plan.sharing.expected.json | 41 ++++++++ .../test/integ.usage-plan.sharing.ts | 37 +++++++ .../aws-apigateway/test/usage-plan.test.ts | 20 ++++ 5 files changed, 176 insertions(+), 24 deletions(-) create mode 100644 packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.expected.json create mode 100644 packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.ts diff --git a/packages/@aws-cdk/aws-apigateway/README.md b/packages/@aws-cdk/aws-apigateway/README.md index 9e33ef2125810..620b728b91435 100644 --- a/packages/@aws-cdk/aws-apigateway/README.md +++ b/packages/@aws-cdk/aws-apigateway/README.md @@ -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', ''); +``` + 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. diff --git a/packages/@aws-cdk/aws-apigateway/lib/usage-plan.ts b/packages/@aws-cdk/aws-apigateway/lib/usage-plan.ts index 82f45bc538db6..4db28eb936e80 100644 --- a/packages/@aws-cdk/aws-apigateway/lib/usage-plan.ts +++ b/packages/@aws-cdk/aws-apigateway/lib/usage-plan.ts @@ -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'; @@ -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(); + 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. @@ -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(); + + 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 diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.expected.json b/packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.expected.json new file mode 100644 index 0000000000000..9e663c4a1f2c8 --- /dev/null +++ b/packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.expected.json @@ -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 + } + } + } + } +] \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.ts b/packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.ts new file mode 100644 index 0000000000000..58ffe84234367 --- /dev/null +++ b/packages/@aws-cdk/aws-apigateway/test/integ.usage-plan.sharing.ts @@ -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(); diff --git a/packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts b/packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts index 88df6f2a04506..2cb6e4ed48ed2 100644 --- a/packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts +++ b/packages/@aws-cdk/aws-apigateway/test/usage-plan.test.ts @@ -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();