@@ -5,6 +5,11 @@ import { Method, MethodOptions } from './method';
5
5
import { RestApi } from './restapi' ;
6
6
7
7
export interface IRestApiResource extends cdk . IConstruct {
8
+ /**
9
+ * The parent of this resource or undefined for the root resource.
10
+ */
11
+ readonly parentResource ?: IRestApiResource ;
12
+
8
13
/**
9
14
* The rest API that this resource is part of.
10
15
*
@@ -37,6 +42,17 @@ export interface IRestApiResource extends cdk.IConstruct {
37
42
*/
38
43
readonly defaultMethodOptions ?: MethodOptions ;
39
44
45
+ /**
46
+ * Gets or create all resources leading up to the specified path.
47
+ *
48
+ * - Path may only start with "/" if this method is called on the root resource.
49
+ * - All resources are created using default options.
50
+ *
51
+ * @param path The relative path
52
+ * @returns a new or existing resource.
53
+ */
54
+ resourceForPath ( path : string ) : Resource ;
55
+
40
56
/**
41
57
* Defines a new child resource where this resource is the parent.
42
58
* @param pathPart The path part for the child resource
@@ -45,6 +61,14 @@ export interface IRestApiResource extends cdk.IConstruct {
45
61
*/
46
62
addResource ( pathPart : string , options ?: ResourceOptions ) : Resource ;
47
63
64
+ /**
65
+ * Retrieves a child resource by path part.
66
+ *
67
+ * @param pathPart The path part of the child resource
68
+ * @returns the child resource or undefined if not found
69
+ */
70
+ getResource ( pathPart : string ) : IRestApiResource | undefined ;
71
+
48
72
/**
49
73
* Adds a greedy proxy resource ("{proxy+}") and an ANY method to this route.
50
74
* @param options Default integration and method options.
@@ -89,7 +113,71 @@ export interface ResourceProps extends ResourceOptions {
89
113
pathPart : string ;
90
114
}
91
115
92
- export class Resource extends cdk . Construct implements IRestApiResource {
116
+ export abstract class ResourceBase extends cdk . Construct implements IRestApiResource {
117
+ public abstract readonly parentResource ?: IRestApiResource ;
118
+ public abstract readonly resourceApi : RestApi ;
119
+ public abstract readonly resourceId : string ;
120
+ public abstract readonly resourcePath : string ;
121
+ public abstract readonly defaultIntegration ?: Integration ;
122
+ public abstract readonly defaultMethodOptions ?: MethodOptions ;
123
+
124
+ private readonly children : { [ pathPart : string ] : Resource } = { } ;
125
+
126
+ constructor ( scope : cdk . Construct , id : string ) {
127
+ super ( scope , id ) ;
128
+ }
129
+
130
+ public addResource ( pathPart : string , options ?: ResourceOptions ) : Resource {
131
+ return new Resource ( this , pathPart , { parent : this , pathPart, ...options } ) ;
132
+ }
133
+
134
+ public addMethod ( httpMethod : string , integration ?: Integration , options ?: MethodOptions ) : Method {
135
+ return new Method ( this , httpMethod , { resource : this , httpMethod, integration, options } ) ;
136
+ }
137
+
138
+ public addProxy ( options ?: ResourceOptions ) : ProxyResource {
139
+ return new ProxyResource ( this , '{proxy+}' , { parent : this , ...options } ) ;
140
+ }
141
+
142
+ public getResource ( pathPart : string ) : IRestApiResource | undefined {
143
+ return this . children [ pathPart ] ;
144
+ }
145
+
146
+ public trackChild ( pathPart : string , resource : Resource ) {
147
+ this . children [ pathPart ] = resource ;
148
+ }
149
+
150
+ public resourceForPath ( path : string ) : Resource {
151
+ if ( ! path ) {
152
+ return this ;
153
+ }
154
+
155
+ 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 } ` ) ;
158
+ }
159
+
160
+ // trim trailing "/"
161
+ return this . resourceForPath ( path . substr ( 1 ) ) ;
162
+ }
163
+
164
+ const parts = path . split ( '/' ) ;
165
+ const next = parts . shift ( ) ;
166
+ if ( ! next || next === '' ) {
167
+ throw new Error ( `resourceForPath cannot be called with an empty path` ) ;
168
+ }
169
+
170
+ let resource = this . getResource ( next ) ;
171
+ if ( ! resource ) {
172
+ resource = this . addResource ( next ) ;
173
+ }
174
+
175
+ return resource . resourceForPath ( parts . join ( '/' ) ) ;
176
+ }
177
+ }
178
+
179
+ export class Resource extends ResourceBase {
180
+ public readonly parentResource ?: IRestApiResource ;
93
181
public readonly resourceApi : RestApi ;
94
182
public readonly resourceId : string ;
95
183
public readonly resourcePath : string ;
@@ -101,6 +189,12 @@ export class Resource extends cdk.Construct implements IRestApiResource {
101
189
102
190
validateResourcePathPart ( props . pathPart ) ;
103
191
192
+ this . parentResource = props . parent ;
193
+
194
+ if ( props . parent instanceof ResourceBase ) {
195
+ props . parent . trackChild ( props . pathPart , this ) ;
196
+ }
197
+
104
198
const resourceProps : CfnResourceProps = {
105
199
restApiId : props . parent . resourceApi . restApiId ,
106
200
parentId : props . parent . resourceId ,
@@ -130,18 +224,6 @@ export class Resource extends cdk.Construct implements IRestApiResource {
130
224
...props . defaultMethodOptions
131
225
} ;
132
226
}
133
-
134
- public addResource ( pathPart : string , options ?: ResourceOptions ) : Resource {
135
- return new Resource ( this , pathPart , { parent : this , pathPart, ...options } ) ;
136
- }
137
-
138
- public addMethod ( httpMethod : string , integration ?: Integration , options ?: MethodOptions ) : Method {
139
- return new Method ( this , httpMethod , { resource : this , httpMethod, integration, options } ) ;
140
- }
141
-
142
- public addProxy ( options ?: ResourceOptions ) : ProxyResource {
143
- return new ProxyResource ( this , '{proxy+}' , { parent : this , ...options } ) ;
144
- }
145
227
}
146
228
147
229
export interface ProxyResourceProps extends ResourceOptions {
@@ -171,8 +253,6 @@ export class ProxyResource extends Resource {
171
253
*/
172
254
public readonly anyMethod ?: Method ;
173
255
174
- private readonly parentResource : IRestApiResource ;
175
-
176
256
constructor ( scope : cdk . Construct , id : string , props : ProxyResourceProps ) {
177
257
super ( scope , id , {
178
258
parent : props . parent ,
@@ -181,8 +261,6 @@ export class ProxyResource extends Resource {
181
261
defaultMethodOptions : props . defaultMethodOptions ,
182
262
} ) ;
183
263
184
- this . parentResource = props . parent ;
185
-
186
264
const anyMethod = props . anyMethod !== undefined ? props . anyMethod : true ;
187
265
if ( anyMethod ) {
188
266
this . anyMethod = this . addMethod ( 'ANY' ) ;
@@ -192,7 +270,7 @@ export class ProxyResource extends Resource {
192
270
public addMethod ( httpMethod : string , integration ?: Integration , options ?: MethodOptions ) : Method {
193
271
// In case this proxy is mounted under the root, also add this method to
194
272
// the root so that empty paths are proxied as well.
195
- if ( this . parentResource . resourcePath === '/' ) {
273
+ if ( this . parentResource && this . parentResource . resourcePath === '/' ) {
196
274
this . parentResource . addMethod ( httpMethod ) ;
197
275
}
198
276
return super . addMethod ( httpMethod , integration , options ) ;
0 commit comments