Skip to content

Commit 862ed7b

Browse files
author
Elad Ben-Israel
authored
refactor: convert "import" to "from" methods (#2456)
Implement and apply the following awslint rules: - `awslint:from-method`: resources should have at least one static "from" method - `awslint:from-signature`: enforce method signature - `awslint:from-attributes`: a `fromAttributes` static method can be used to import from more than a single attribute - `awslint:from-attributes-struct`: `fromFooAttributes` should accept a `FooAttributes` struct as input - `awslint:no-static-import`: forbids a static `import` (deprecation helper rule) - `awslint:attribute-tag`: all resource attributes should have an "@Attribute" doc tag - `awslint:attribute-readonly`: all attributes must be readonly properties Many resources now have an additional `fromFooArn` or `fromFooName` for importing from the intrinsic attribute. Misc: - Deprecate `Token.unresolved` in favor of `Token.isToken` (more idiomatic). - Added eager resolution of `Fn.select` and `Fn.split` in case they receive concrete values - Refactoring of awslint (decouple "resource" from "cfn-resource"). - Added `iam.Grant.drop` which allows "dropping" a grant on the floor for imported resources NOTE: many of the new methods do not have inline documentation. We will address this in a subsequent pass that will be focused on docs. Fixes #2450 Fixes #2428 Fixes #2424 Fixes #2429 Fixes #2425 Fixes #2422 Fixes #2423 Fixes #89 BREAKING CHANGE: all `Foo.import` static methods are now `Foo.fromFooAttributes` * All `FooImportProps` structs are now called `FooAttributes` * `stepfunctions.StateMachine.export` has been removed. * `ses.ReceiptRule.name` is now `ses.ReceiptRule.receiptRuleName` * `ses.ReceiptRuleSet.name` is now `ses.ReceiptRuleSet.receiptRuleSetName` * `secretsmanager.AttachedSecret` is now called `secretsmanager.SecretTargetAttachment` to match service semantics * `ecr.Repository.export` has been removed * `s3.Bucket.bucketUrl` is now called `s3.Bucket.bucketWebsiteUrl` * `lambda.Version.functionVersion` is now called `lambda.Version.version` * `ec2.SecurityGroup.groupName` is now `ec2.SecurityGroup.securityGroupName` * `cognito.UserPoolClient.clientId` is now `cognito.UserPoolClient.userPoolClientId` * `apigateway.IRestApiResource` is now `apigateway.IResource` * `apigateway.IResource.resourcePath` is now `apigateway.IResource.path` * `apigateway.IResource.resourceApi` is now `apigateway.IResource.restApi`
1 parent e4ec30a commit 862ed7b

File tree

148 files changed

+2807
-2130
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

148 files changed

+2807
-2130
lines changed

packages/@aws-cdk/app-delivery/test/test.pipeline-deploy-stack-action.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ function createSelfUpdatingStack(pipelineStack: cdk.Stack): SelfUpdatingPipeline
327327
});
328328

329329
// simple source
330-
const bucket = s3.Bucket.import( pipeline, 'PatternBucket', { bucketArn: 'arn:aws:s3:::totally-fake-bucket' });
330+
const bucket = s3.Bucket.fromBucketArn( pipeline, 'PatternBucket', 'arn:aws:s3:::totally-fake-bucket');
331331
const sourceOutput = new codepipeline.Artifact('SourceOutput');
332332
const sourceAction = new cpactions.S3SourceAction({
333333
actionName: 'S3Source',

packages/@aws-cdk/assets/lib/asset.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,7 @@ export class Asset extends cdk.Construct {
122122
const s3Filename = cdk.Fn.select(1, cdk.Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, keyParam.stringValue)).toString();
123123
this.s3ObjectKey = `${this.s3Prefix}${s3Filename}`;
124124

125-
this.bucket = s3.Bucket.import(this, 'AssetBucket', {
126-
bucketName: this.s3BucketName
127-
});
125+
this.bucket = s3.Bucket.fromBucketName(this, 'AssetBucket', this.s3BucketName);
128126

129127
// form the s3 URL of the object key
130128
this.s3Url = this.bucket.urlForObject(this.s3ObjectKey);

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

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export interface DeploymentProps {
5555
* automatically for the `restApi.latestDeployment` deployment.
5656
*/
5757
export class Deployment extends Resource {
58+
/** @attribute */
5859
public readonly deploymentId: string;
5960
public readonly api: IRestApi;
6061

packages/@aws-cdk/aws-apigateway/lib/integrations/lambda.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export class LambdaIntegration extends AwsIntegration {
5555
super.bind(method);
5656
const principal = new iam.ServicePrincipal('apigateway.amazonaws.com');
5757

58-
const desc = `${method.httpMethod}.${method.resource.resourcePath.replace(/\//g, '.')}`;
58+
const desc = `${method.httpMethod}.${method.resource.path.replace(/\//g, '.')}`;
5959

6060
this.handler.addPermission(`ApiPermission.${desc}`, {
6161
principal,

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

+12-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CfnMethod, CfnMethodProps } from './apigateway.generated';
33
import { ConnectionType, Integration } from './integration';
44
import { MockIntegration } from './integrations/mock';
55
import { MethodResponse } from './methodresponse';
6-
import { IRestApiResource } from './resource';
6+
import { IResource } from './resource';
77
import { RestApi } from './restapi';
88
import { validateHttpMethod } from './util';
99

@@ -66,7 +66,7 @@ export interface MethodProps {
6666
* The resource this method is associated with. For root resource methods,
6767
* specify the `RestApi` object.
6868
*/
69-
readonly resource: IRestApiResource;
69+
readonly resource: IResource;
7070

7171
/**
7272
* The HTTP method ("GET", "POST", "PUT", ...) that clients use to call this method.
@@ -85,16 +85,18 @@ export interface MethodProps {
8585
}
8686

8787
export class Method extends Resource {
88+
/** @attribute */
8889
public readonly methodId: string;
90+
8991
public readonly httpMethod: string;
90-
public readonly resource: IRestApiResource;
92+
public readonly resource: IResource;
9193
public readonly restApi: RestApi;
9294

9395
constructor(scope: Construct, id: string, props: MethodProps) {
9496
super(scope, id);
9597

9698
this.resource = props.resource;
97-
this.restApi = props.resource.resourceApi;
99+
this.restApi = props.resource.restApi;
98100
this.httpMethod = props.httpMethod.toUpperCase();
99101

100102
validateHttpMethod(this.httpMethod);
@@ -120,9 +122,9 @@ export class Method extends Resource {
120122

121123
this.methodId = resource.ref;
122124

123-
props.resource.resourceApi._attachMethod(this);
125+
props.resource.restApi._attachMethod(this);
124126

125-
const deployment = props.resource.resourceApi.latestDeployment;
127+
const deployment = props.resource.restApi.latestDeployment;
126128
if (deployment) {
127129
deployment.node.addDependency(resource);
128130
deployment.addToLogicalId({ method: methodProps });
@@ -136,6 +138,8 @@ export class Method extends Resource {
136138
*
137139
* NOTE: {stage} will refer to the `restApi.deploymentStage`, which will
138140
* automatically set if auto-deploy is enabled.
141+
*
142+
* @attribute
139143
*/
140144
public get methodArn(): string {
141145
if (!this.restApi.deploymentStage) {
@@ -145,15 +149,15 @@ export class Method extends Resource {
145149
}
146150

147151
const stage = this.restApi.deploymentStage.stageName.toString();
148-
return this.restApi.executeApiArn(this.httpMethod, this.resource.resourcePath, stage);
152+
return this.restApi.executeApiArn(this.httpMethod, this.resource.path, stage);
149153
}
150154

151155
/**
152156
* Returns an execute-api ARN for this method's "test-invoke-stage" stage.
153157
* This stage is used by the AWS Console UI when testing the method.
154158
*/
155159
public get testMethodArn(): string {
156-
return this.restApi.executeApiArn(this.httpMethod, this.resource.resourcePath, 'test-invoke-stage');
160+
return this.restApi.executeApiArn(this.httpMethod, this.resource.path, 'test-invoke-stage');
157161
}
158162

159163
private renderIntegration(integration?: Integration): CfnMethod.IntegrationProperty {

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

+27-25
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { Construct, IResource, Resource as ResourceConstruct } from '@aws-cdk/cdk';
1+
import { Construct, IResource as IResourceBase, Resource as ResourceConstruct } from '@aws-cdk/cdk';
22
import { CfnResource, CfnResourceProps } from './apigateway.generated';
33
import { Integration } from './integration';
44
import { Method, MethodOptions } from './method';
55
import { RestApi } from './restapi';
66

7-
export interface IRestApiResource extends IResource {
7+
export interface IResource extends IResourceBase {
88
/**
99
* The parent of this resource or undefined for the root resource.
1010
*/
11-
readonly parentResource?: IRestApiResource;
11+
readonly parentResource?: IResource;
1212

1313
/**
1414
* The rest API that this resource is part of.
@@ -18,17 +18,18 @@ export interface IRestApiResource extends IResource {
1818
* hash to determine the ID of the deployment. This allows us to automatically update
1919
* the deployment when the model of the REST API changes.
2020
*/
21-
readonly resourceApi: RestApi;
21+
readonly restApi: RestApi;
2222

2323
/**
2424
* The ID of the resource.
25+
* @attribute
2526
*/
2627
readonly resourceId: string;
2728

2829
/**
2930
* The full path of this resuorce.
3031
*/
31-
readonly resourcePath: string;
32+
readonly path: string;
3233

3334
/**
3435
* An integration to use as a default for all methods created within this
@@ -67,7 +68,7 @@ export interface IRestApiResource extends IResource {
6768
* @param pathPart The path part of the child resource
6869
* @returns the child resource or undefined if not found
6970
*/
70-
getResource(pathPart: string): IRestApiResource | undefined;
71+
getResource(pathPart: string): IResource | undefined;
7172

7273
/**
7374
* Adds a greedy proxy resource ("{proxy+}") and an ANY method to this route.
@@ -105,19 +106,19 @@ export interface ResourceProps extends ResourceOptions {
105106
* The parent resource of this resource. You can either pass another
106107
* `Resource` object or a `RestApi` object here.
107108
*/
108-
readonly parent: IRestApiResource;
109+
readonly parent: IResource;
109110

110111
/**
111112
* A path name for the resource.
112113
*/
113114
readonly pathPart: string;
114115
}
115116

116-
export abstract class ResourceBase extends ResourceConstruct implements IRestApiResource {
117-
public abstract readonly parentResource?: IRestApiResource;
118-
public abstract readonly resourceApi: RestApi;
117+
export abstract class ResourceBase extends ResourceConstruct implements IResource {
118+
public abstract readonly parentResource?: IResource;
119+
public abstract readonly restApi: RestApi;
119120
public abstract readonly resourceId: string;
120-
public abstract readonly resourcePath: string;
121+
public abstract readonly path: string;
121122
public abstract readonly defaultIntegration?: Integration;
122123
public abstract readonly defaultMethodOptions?: MethodOptions;
123124

@@ -139,7 +140,7 @@ export abstract class ResourceBase extends ResourceConstruct implements IRestApi
139140
return new ProxyResource(this, '{proxy+}', { parent: this, ...options });
140141
}
141142

142-
public getResource(pathPart: string): IRestApiResource | undefined {
143+
public getResource(pathPart: string): IResource | undefined {
143144
return this.children[pathPart];
144145
}
145146

@@ -153,8 +154,8 @@ export abstract class ResourceBase extends ResourceConstruct implements IRestApi
153154
}
154155

155156
if (path.startsWith('/')) {
156-
if (this.resourcePath !== '/') {
157-
throw new Error(`Path may start with "/" only for the resource, but we are at: ${this.resourcePath}`);
157+
if (this.path !== '/') {
158+
throw new Error(`Path may start with "/" only for the resource, but we are at: ${this.path}`);
158159
}
159160

160161
// trim trailing "/"
@@ -177,10 +178,11 @@ export abstract class ResourceBase extends ResourceConstruct implements IRestApi
177178
}
178179

179180
export class Resource extends ResourceBase {
180-
public readonly parentResource?: IRestApiResource;
181-
public readonly resourceApi: RestApi;
181+
public readonly parentResource?: IResource;
182+
public readonly restApi: RestApi;
182183
public readonly resourceId: string;
183-
public readonly resourcePath: string;
184+
public readonly path: string;
185+
184186
public readonly defaultIntegration?: Integration;
185187
public readonly defaultMethodOptions?: MethodOptions;
186188

@@ -196,21 +198,21 @@ export class Resource extends ResourceBase {
196198
}
197199

198200
const resourceProps: CfnResourceProps = {
199-
restApiId: props.parent.resourceApi.restApiId,
201+
restApiId: props.parent.restApi.restApiId,
200202
parentId: props.parent.resourceId,
201203
pathPart: props.pathPart
202204
};
203205
const resource = new CfnResource(this, 'Resource', resourceProps);
204206

205207
this.resourceId = resource.resourceId;
206-
this.resourceApi = props.parent.resourceApi;
208+
this.restApi = props.parent.restApi;
207209

208210
// render resource path (special case for root)
209-
this.resourcePath = props.parent.resourcePath;
210-
if (!this.resourcePath.endsWith('/')) { this.resourcePath += '/'; }
211-
this.resourcePath += props.pathPart;
211+
this.path = props.parent.path;
212+
if (!this.path.endsWith('/')) { this.path += '/'; }
213+
this.path += props.pathPart;
212214

213-
const deployment = props.parent.resourceApi.latestDeployment;
215+
const deployment = props.parent.restApi.latestDeployment;
214216
if (deployment) {
215217
deployment.node.addDependency(resource);
216218
deployment.addToLogicalId({ resource: resourceProps });
@@ -231,7 +233,7 @@ export interface ProxyResourceProps extends ResourceOptions {
231233
* The parent resource of this resource. You can either pass another
232234
* `Resource` object or a `RestApi` object here.
233235
*/
234-
readonly parent: IRestApiResource;
236+
readonly parent: IResource;
235237

236238
/**
237239
* Adds an "ANY" method to this resource. If set to `false`, you will have to explicitly
@@ -270,7 +272,7 @@ export class ProxyResource extends Resource {
270272
public addMethod(httpMethod: string, integration?: Integration, options?: MethodOptions): Method {
271273
// In case this proxy is mounted under the root, also add this method to
272274
// the root so that empty paths are proxied as well.
273-
if (this.parentResource && this.parentResource.resourcePath === '/') {
275+
if (this.parentResource && this.parentResource.path === '/') {
274276
this.parentResource.addMethod(httpMethod);
275277
}
276278
return super.addMethod(httpMethod, integration, options);

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

+20-31
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import iam = require('@aws-cdk/aws-iam');
2-
import { CfnOutput, Construct, IResource, Resource } from '@aws-cdk/cdk';
2+
import { CfnOutput, Construct, IResource as IResourceBase, Resource } from '@aws-cdk/cdk';
33
import { CfnAccount, CfnRestApi } from './apigateway.generated';
44
import { Deployment } from './deployment';
55
import { Integration } from './integration';
66
import { Method, MethodOptions } from './method';
7-
import { IRestApiResource, ResourceBase, ResourceOptions } from './resource';
7+
import { IResource, ResourceBase, ResourceOptions } from './resource';
88
import { Stage, StageOptions } from './stage';
99

10-
export interface RestApiImportProps {
10+
export interface RestApiAttributes {
1111
/**
1212
* The REST API ID of an existing REST API resource.
1313
*/
@@ -19,22 +19,18 @@ export interface RestApiImportProps {
1919
readonly restApiRootResourceId?: string;
2020
}
2121

22-
export interface IRestApi extends IResource {
22+
export interface IRestApi extends IResourceBase {
2323
/**
2424
* The ID of this API Gateway RestApi.
25+
* @attribute
2526
*/
2627
readonly restApiId: string;
2728

28-
/**
29-
* The resource ID of the root resource.
30-
*/
31-
readonly restApiRootResourceId: string;
32-
3329
/**
3430
* Exports a REST API resource from this stack.
3531
* @returns REST API props that can be imported to another stack.
3632
*/
37-
export(): RestApiImportProps;
33+
export(): RestApiAttributes;
3834
}
3935

4036
export interface RestApiProps extends ResourceOptions {
@@ -165,20 +161,11 @@ export interface RestApiProps extends ResourceOptions {
165161
* public endpoint.
166162
*/
167163
export class RestApi extends Resource implements IRestApi {
168-
/**
169-
* Imports an existing REST API resource.
170-
* @param scope Parent construct
171-
* @param id Construct ID
172-
* @param props Imported rest API properties
173-
*/
174-
public static import(scope: Construct, id: string, props: RestApiImportProps): IRestApi {
175-
class Import extends Construct implements IRestApi {
176-
public restApiId = props.restApiId;
177-
public get restApiRootResourceId() {
178-
if (!props.restApiRootResourceId) { throw new Error(`Imported REST API does not have "restApiRootResourceId"`); }
179-
return props.restApiRootResourceId;
180-
}
181-
public export() { return props; }
164+
165+
public static fromRestApiId(scope: Construct, id: string, restApiId: string): IRestApi {
166+
class Import extends Resource implements IRestApi {
167+
public readonly restApiId = restApiId;
168+
public export(): RestApiAttributes { return { restApiId }; }
182169
}
183170

184171
return new Import(scope, id);
@@ -191,6 +178,8 @@ export class RestApi extends Resource implements IRestApi {
191178

192179
/**
193180
* The resource ID of the root resource.
181+
*
182+
* @attribute
194183
*/
195184
public readonly restApiRootResourceId: string;
196185

@@ -216,7 +205,7 @@ export class RestApi extends Resource implements IRestApi {
216205
* api.root.addResource('friends').addMethod('GET', getFriendsHandler); // "GET /friends"
217206
*
218207
*/
219-
public readonly root: IRestApiResource;
208+
public readonly root: IResource;
220209

221210
private readonly methods = new Array<Method>();
222211

@@ -252,7 +241,7 @@ export class RestApi extends Resource implements IRestApi {
252241
* Exports a REST API resource from this stack.
253242
* @returns REST API props that can be imported to another stack.
254243
*/
255-
public export(): RestApiImportProps {
244+
public export(): RestApiAttributes {
256245
return {
257246
restApiId: new CfnOutput(this, 'RestApiId', { value: this.restApiId }).makeImportValue().toString()
258247
};
@@ -402,10 +391,10 @@ export enum EndpointType {
402391
}
403392

404393
class RootResource extends ResourceBase {
405-
public readonly parentResource?: IRestApiResource;
406-
public readonly resourceApi: RestApi;
394+
public readonly parentResource?: IResource;
395+
public readonly restApi: RestApi;
407396
public readonly resourceId: string;
408-
public readonly resourcePath: string;
397+
public readonly path: string;
409398
public readonly defaultIntegration?: Integration | undefined;
410399
public readonly defaultMethodOptions?: MethodOptions | undefined;
411400

@@ -415,8 +404,8 @@ class RootResource extends ResourceBase {
415404
this.parentResource = undefined;
416405
this.defaultIntegration = props.defaultIntegration;
417406
this.defaultMethodOptions = props.defaultMethodOptions;
418-
this.resourceApi = api;
407+
this.restApi = api;
419408
this.resourceId = resourceId;
420-
this.resourcePath = '/';
409+
this.path = '/';
421410
}
422411
}

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

+3
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ export interface MethodDeploymentOptions {
133133
}
134134

135135
export class Stage extends Resource {
136+
/**
137+
* @attribute
138+
*/
136139
public readonly stageName: string;
137140

138141
private readonly restApi: IRestApi;

0 commit comments

Comments
 (0)