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

fix(apigateway): cannot create an ApiKey with an imported RestApi #22368

Merged
merged 4 commits into from
Oct 11, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 17 additions & 1 deletion packages/@aws-cdk/aws-apigateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,22 @@ declare const lambdaFn: lambda.Function;
importedKey.grantRead(lambdaFn);
```

### Adding an API Key to an imported RestApi

API Keys are added to ApiGateway Stages, not to the API itself. When you import a RestApi
it does not have any information on the Stages that may be associated with it. Since adding an API
Key requires a stage, you should instead add the Api Key to the imported Stage.

```ts
declare const restApi: apigateway.IRestApi;
const importedStage = apigateway.Stage.fromStageAttributes(this, 'imported-stage', {
stageName: 'myStageName',
restApi,
});

importedStage.addApiKey('MyApiKey');
```

### ⚠️ Multiple API Keys

It is possible to specify multiple API keys for a given Usage Plan, by calling `usagePlan.addApiKey()`.
Expand Down Expand Up @@ -426,7 +442,7 @@ declare const api: apigateway.RestApi;

const key = new apigateway.RateLimitedApiKey(this, 'rate-limited-api-key', {
customerId: 'hello-customer',
resources: [api],
stages: [api.deploymentStage],
quota: {
limit: 10000,
period: apigateway.Period.MONTH
Expand Down
41 changes: 31 additions & 10 deletions packages/@aws-cdk/aws-apigateway/lib/api-key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Construct } from 'constructs';
import { CfnApiKey } from './apigateway.generated';
import { ResourceOptions } from './resource';
import { IRestApi } from './restapi';
import { IStage } from './stage';
import { QuotaSettings, ThrottleSettings, UsagePlan, UsagePlanPerApiStage } from './usage-plan';

/**
Expand Down Expand Up @@ -56,9 +57,17 @@ export interface ApiKeyProps extends ApiKeyOptions {
/**
* A list of resources this api key is associated with.
* @default none
* @deprecated - use `stages` instead
*/
readonly resources?: IRestApi[];

/**
* A list of Stages this api key is associated with.
*
* @default - the api key is not associated with any stages
*/
readonly stages?: IStage[];

/**
* An AWS Marketplace customer identifier to use when integrating with the AWS SaaS Marketplace.
* @link http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-apikey.html#cfn-apigateway-apikey-customerid
Expand Down Expand Up @@ -168,7 +177,7 @@ export class ApiKey extends ApiKeyBase {
enabled: props.enabled ?? true,
generateDistinctId: props.generateDistinctId,
name: this.physicalName,
stageKeys: this.renderStageKeys(props.resources),
stageKeys: this.renderStageKeys(props.resources, props.stages),
value: props.value,
});

Expand All @@ -182,17 +191,29 @@ export class ApiKey extends ApiKeyBase {
});
}

private renderStageKeys(resources: IRestApi[] | undefined): CfnApiKey.StageKeyProperty[] | undefined {
if (!resources) {
private renderStageKeys(resources?: IRestApi[], stages?: IStage[]): CfnApiKey.StageKeyProperty[] | undefined {
if (!resources && !stages) {
return undefined;
}

return resources.map((resource: IRestApi) => {
const restApi = resource;
const restApiId = restApi.restApiId;
const stageName = restApi.deploymentStage!.stageName.toString();
return { restApiId, stageName };
});
if (resources && stages) {
throw new Error('Only one of "resources" or "stages" should be provided');
}

return resources
? resources.map((resource: IRestApi) => {
const restApi = resource;
if (!restApi.deploymentStage) {
throw new Error('Cannot add an ApiKey to a RestApi that does not contain a "deploymentStage".\n'+
'Either set the RestApi.deploymentStage or create an ApiKey from a Stage');
}
const restApiId = restApi.restApiId;
const stageName = restApi.deploymentStage!.stageName.toString();
return { restApiId, stageName };
})
: stages ? stages.map((stage => {
return { restApiId: stage.restApi.restApiId, stageName: stage.stageName };
})) : undefined;
}
}

Expand Down Expand Up @@ -258,4 +279,4 @@ const writePermissions = [
'apigateway:PUT',
'apigateway:PATCH',
'apigateway:DELETE',
];
];
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-apigateway/lib/restapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,11 +403,11 @@ export abstract class RestApiBase extends Resource implements IRestApi {
}

/**
* Add an ApiKey
* Add an ApiKey to the deploymentStage
*/
public addApiKey(id: string, options?: ApiKeyOptions): IApiKey {
return new ApiKey(this, id, {
resources: [this],
stages: [this.deploymentStage],
...options,
});
}
Expand Down