1
- import { APIGatewayProxyEvent , APIGatewayEventRequestContext , APIGatewayProxyResult } from " aws-lambda" ;
2
- import { ProcessMethod } from " ./EventProcessor" ;
1
+ import { APIGatewayEventRequestContext , APIGatewayProxyEvent , APIGatewayProxyResult } from ' aws-lambda'
2
+ import { ProcessMethod } from ' ./EventProcessor'
3
3
4
4
export type ProxyIntegrationEvent = APIGatewayProxyEvent
5
5
type ProxyIntegrationParams = {
@@ -8,12 +8,12 @@ type ProxyIntegrationParams = {
8
8
export type ProxyIntegrationEventWithParams = APIGatewayProxyEvent & ProxyIntegrationParams
9
9
10
10
export interface ProxyIntegrationRoute {
11
- path : string ;
12
- method : string ;
11
+ path : string
12
+ method : string
13
13
action : (
14
14
request : ProxyIntegrationEventWithParams ,
15
15
context : APIGatewayEventRequestContext
16
- ) => APIGatewayProxyResult | Promise < APIGatewayProxyResult > ;
16
+ ) => APIGatewayProxyResult | Promise < APIGatewayProxyResult >
17
17
}
18
18
19
19
export type ProxyIntegrationErrorMapping = {
@@ -29,40 +29,40 @@ export type ProxyIntegrationError = {
29
29
}
30
30
31
31
export interface ProxyIntegrationConfig {
32
- cors ?: boolean ;
33
- routes : ProxyIntegrationRoute [ ] ;
34
- debug ?: boolean ;
35
- errorMapping ?: ProxyIntegrationErrorMapping ;
36
- defaultHeaders ?: APIGatewayProxyResult [ 'headers' ] ;
37
- proxyPath ?: string ;
32
+ cors ?: boolean
33
+ routes : ProxyIntegrationRoute [ ]
34
+ debug ?: boolean
35
+ errorMapping ?: ProxyIntegrationErrorMapping
36
+ defaultHeaders ?: APIGatewayProxyResult [ 'headers' ]
37
+ proxyPath ?: string
38
38
}
39
39
40
40
const NO_MATCHING_ACTION = ( request : APIGatewayProxyEvent ) => {
41
41
throw {
42
42
reason : 'NO_MATCHING_ACTION' ,
43
43
message : `Could not find matching action for ${ request . path } and method ${ request . httpMethod } `
44
44
}
45
- } ;
45
+ }
46
46
47
47
const addCorsHeaders = ( toAdd : APIGatewayProxyResult [ 'headers' ] = { } ) => {
48
- toAdd [ " Access-Control-Allow-Origin" ] = "*" ;
49
- toAdd [ " Access-Control-Allow-Methods" ] = " GET,POST,PUT,DELETE,HEAD,PATCH" ;
50
- toAdd [ " Access-Control-Allow-Headers" ] = " Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token" ;
51
- return toAdd ;
48
+ toAdd [ ' Access-Control-Allow-Origin' ] = '*'
49
+ toAdd [ ' Access-Control-Allow-Methods' ] = ' GET,POST,PUT,DELETE,HEAD,PATCH'
50
+ toAdd [ ' Access-Control-Allow-Headers' ] = ' Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'
51
+ return toAdd
52
52
}
53
53
54
54
const processActionAndReturn = async ( actionConfig : Pick < ProxyIntegrationRoute , 'action' > , event : ProxyIntegrationEventWithParams ,
55
- context : APIGatewayEventRequestContext , headers : APIGatewayProxyResult [ 'headers' ] ) => {
55
+ context : APIGatewayEventRequestContext , headers : APIGatewayProxyResult [ 'headers' ] ) => {
56
56
57
57
const res = await actionConfig . action ( event , context )
58
58
if ( ! res || ! res . body ) {
59
- const consolidateBody = res && JSON . stringify ( res ) || '{}' ;
59
+ const consolidateBody = res && JSON . stringify ( res ) || '{}'
60
60
61
61
return {
62
62
statusCode : 200 ,
63
63
headers,
64
64
body : consolidateBody
65
- } ;
65
+ }
66
66
}
67
67
68
68
return {
@@ -79,91 +79,91 @@ export const process: ProcessMethod<ProxyIntegrationConfig, ProxyIntegrationEven
79
79
( proxyIntegrationConfig , event , context ) => {
80
80
81
81
if ( proxyIntegrationConfig . debug ) {
82
- console . log ( " Lambda proxyIntegrationConfig: " , proxyIntegrationConfig ) ;
83
- console . log ( " Lambda event: " , event ) ;
84
- console . log ( " Lambda context: " , context ) ;
82
+ console . log ( ' Lambda proxyIntegrationConfig: ' , proxyIntegrationConfig )
83
+ console . log ( ' Lambda event: ' , event )
84
+ console . log ( ' Lambda context: ' , context )
85
85
}
86
86
87
87
//validate config
88
88
if ( ! Array . isArray ( proxyIntegrationConfig . routes ) || proxyIntegrationConfig . routes . length < 1 ) {
89
- throw new Error ( 'proxyIntegration.routes must not be empty' ) ;
89
+ throw new Error ( 'proxyIntegration.routes must not be empty' )
90
90
}
91
91
92
92
// detect if it's an http-call at all:
93
93
if ( ! event . httpMethod || ! event . path ) {
94
- return null ;
94
+ return null
95
95
}
96
96
97
- const headers : APIGatewayProxyResult [ 'headers' ] = { } ;
97
+ const headers : APIGatewayProxyResult [ 'headers' ] = { }
98
98
if ( proxyIntegrationConfig . cors ) {
99
- addCorsHeaders ( headers ) ;
99
+ addCorsHeaders ( headers )
100
100
if ( event . httpMethod === 'OPTIONS' ) {
101
101
return Promise . resolve ( {
102
102
statusCode : 200 ,
103
103
headers,
104
104
body : ''
105
- } ) ;
105
+ } )
106
106
}
107
107
}
108
108
Object . assign ( headers , { 'Content-Type' : 'application/json' } , proxyIntegrationConfig . defaultHeaders )
109
109
110
110
// assure necessary values have sane defaults:
111
- const errorMapping = proxyIntegrationConfig . errorMapping || { } ;
112
- errorMapping [ 'NO_MATCHING_ACTION' ] = 404 ;
111
+ const errorMapping = proxyIntegrationConfig . errorMapping || { }
112
+ errorMapping [ 'NO_MATCHING_ACTION' ] = 404
113
113
114
114
if ( proxyIntegrationConfig . proxyPath ) {
115
- console . log ( " proxy path is set: " + proxyIntegrationConfig . proxyPath )
116
- event . path = ( event . pathParameters || { } ) [ proxyIntegrationConfig . proxyPath ] ;
117
- console . log ( " proxy path with event path: " + event . path )
115
+ console . log ( ' proxy path is set: ' + proxyIntegrationConfig . proxyPath )
116
+ event . path = ( event . pathParameters || { } ) [ proxyIntegrationConfig . proxyPath ]
117
+ console . log ( ' proxy path with event path: ' + event . path )
118
118
119
119
} else {
120
- event . path = normalizeRequestPath ( event ) ;
120
+ event . path = normalizeRequestPath ( event )
121
121
}
122
122
123
123
try {
124
124
const actionConfig = findMatchingActionConfig ( event . httpMethod , event . path , proxyIntegrationConfig ) || {
125
125
action : NO_MATCHING_ACTION ,
126
126
paths : undefined
127
- } ;
127
+ }
128
128
129
- event . paths = actionConfig . paths ;
129
+ event . paths = actionConfig . paths
130
130
if ( event . body ) {
131
131
try {
132
- event . body = JSON . parse ( event . body ) ;
132
+ event . body = JSON . parse ( event . body )
133
133
} catch ( parseError ) {
134
- console . log ( `Could not parse body as json: ${ event . body } ` , parseError ) ;
134
+ console . log ( `Could not parse body as json: ${ event . body } ` , parseError )
135
135
return {
136
136
statusCode : 400 ,
137
137
headers,
138
- body : JSON . stringify ( { message : " body is not a valid JSON" , error : " ParseError" } )
138
+ body : JSON . stringify ( { message : ' body is not a valid JSON' , error : ' ParseError' } )
139
139
}
140
140
}
141
141
}
142
142
return processActionAndReturn ( actionConfig , event , context , headers ) . catch ( error => {
143
143
console . log ( 'Error while handling action function.' , error )
144
- return convertError ( error , errorMapping , headers ) ;
144
+ return convertError ( error , errorMapping , headers )
145
145
} )
146
146
} catch ( error ) {
147
- console . log ( 'Error while evaluating matching action handler' , error ) ;
148
- return convertError ( error , errorMapping , headers ) ;
147
+ console . log ( 'Error while evaluating matching action handler' , error )
148
+ return convertError ( error , errorMapping , headers )
149
149
}
150
150
}
151
151
152
152
const normalizeRequestPath = ( event : APIGatewayProxyEvent ) : string => {
153
153
if ( isLocalExecution ( event ) ) {
154
- return event . path ;
154
+ return event . path
155
155
}
156
156
157
157
// ugly hack: if host is from API-Gateway 'Custom Domain Name Mapping', then event.path has the value '/basepath/resource-path/';
158
158
// if host is from amazonaws.com, then event.path is just '/resource-path':
159
- const apiId = event . requestContext ? event . requestContext . apiId : null ; // the apiId that is the first part of the amazonaws.com-host
159
+ const apiId = event . requestContext ? event . requestContext . apiId : null // the apiId that is the first part of the amazonaws.com-host
160
160
if ( ( apiId && event . headers && event . headers . Host && event . headers . Host . substring ( 0 , apiId . length ) !== apiId ) ) {
161
161
// remove first path element:
162
- const groups = / \/ [ ^ \/ ] + ( .* ) / . exec ( event . path ) || [ null , null ] ;
163
- return groups [ 1 ] || '/' ;
162
+ const groups : any = / \/ [ ^ \/ ] + ( .* ) / . exec ( event . path ) || [ null , null ]
163
+ return groups [ 1 ] || '/'
164
164
}
165
165
166
- return event . path ;
166
+ return event . path
167
167
}
168
168
169
169
const hasReason = ( error : any ) : error is { reason : string } => typeof error . reason === 'string'
@@ -175,73 +175,74 @@ const convertError = (error: ProxyIntegrationError | Error, errorMapping?: Proxy
175
175
statusCode : errorMapping [ error . reason ] ,
176
176
body : JSON . stringify ( { message : error . message , error : error . reason } ) ,
177
177
headers
178
- } ;
178
+ }
179
179
} else if ( hasStatus ( error ) ) {
180
180
return {
181
181
statusCode : error . status ,
182
182
body : JSON . stringify ( { message : error . message , error : error . status } ) ,
183
183
headers : addCorsHeaders ( { } )
184
- } ;
184
+ }
185
185
}
186
186
try {
187
187
return {
188
188
statusCode : 500 ,
189
- body : JSON . stringify ( { error : " ServerError" , message : `Generic error:${ JSON . stringify ( error ) } ` } ) ,
189
+ body : JSON . stringify ( { error : ' ServerError' , message : `Generic error:${ JSON . stringify ( error ) } ` } ) ,
190
190
headers : addCorsHeaders ( { } )
191
- } ;
192
- } catch ( stringifyError ) { }
191
+ }
192
+ } catch ( stringifyError ) {
193
+ }
193
194
194
195
return {
195
196
statusCode : 500 ,
196
- body : JSON . stringify ( { error : " ServerError" , message : `Generic error` } )
197
- } ;
197
+ body : JSON . stringify ( { error : ' ServerError' , message : `Generic error` } )
198
+ }
198
199
}
199
200
200
201
const findMatchingActionConfig = ( httpMethod : string , httpPath : string , routeConfig : ProxyIntegrationConfig ) :
201
202
Pick < ProxyIntegrationRoute , 'action' > & ProxyIntegrationParams | null => {
202
203
203
- const paths : ProxyIntegrationParams [ 'paths' ] = { } ;
204
- const matchingMethodRoutes = routeConfig . routes . filter ( route => route . method === httpMethod ) ;
204
+ const paths : ProxyIntegrationParams [ 'paths' ] = { }
205
+ const matchingMethodRoutes = routeConfig . routes . filter ( route => route . method === httpMethod )
205
206
for ( let route of matchingMethodRoutes ) {
206
207
if ( routeConfig . debug ) {
207
- console . log ( `Examining route ${ route . path } to match ${ httpPath } ` ) ;
208
+ console . log ( `Examining route ${ route . path } to match ${ httpPath } ` )
208
209
}
209
- const pathPartNames = extractPathNames ( route . path ) ;
210
- const pathValues = extractPathValues ( route . path , httpPath ) ;
210
+ const pathPartNames = extractPathNames ( route . path )
211
+ const pathValues = extractPathValues ( route . path , httpPath )
211
212
if ( pathValues && pathPartNames ) {
212
213
for ( let ii = 0 ; ii < pathValues . length ; ii ++ ) {
213
- paths [ pathPartNames [ ii ] ] = decodeURIComponent ( pathValues [ ii ] ) ;
214
+ paths [ pathPartNames [ ii ] ] = decodeURIComponent ( pathValues [ ii ] )
214
215
}
215
216
if ( routeConfig . debug ) {
216
- console . log ( `Found matching route ${ route . path } with paths` , paths ) ;
217
+ console . log ( `Found matching route ${ route . path } with paths` , paths )
217
218
}
218
219
return {
219
220
action : route . action ,
220
221
paths : paths
221
- } ;
222
+ }
222
223
}
223
224
}
224
225
if ( routeConfig . debug ) {
225
- console . log ( `No match for ${ httpPath } ` ) ;
226
+ console . log ( `No match for ${ httpPath } ` )
226
227
}
227
228
228
- return null ;
229
+ return null
229
230
}
230
231
231
232
const extractPathValues = ( pathExpression : string , httpPath : string ) => {
232
- const pathValueRegex = new RegExp ( '^' + pathExpression . replace ( / : [ \w ] + / g, " ([^/]+)" ) + '$' ) ;
233
- const pathValues = pathValueRegex . exec ( httpPath ) ;
234
- return pathValues && pathValues . length > 0 ? pathValues . slice ( 1 ) : null ;
233
+ const pathValueRegex = new RegExp ( '^' + pathExpression . replace ( / : [ \w ] + / g, ' ([^/]+)' ) + '$' )
234
+ const pathValues = pathValueRegex . exec ( httpPath )
235
+ return pathValues && pathValues . length > 0 ? pathValues . slice ( 1 ) : null
235
236
}
236
237
237
238
const extractPathNames = ( pathExpression : string ) => {
238
- const pathNameRegex = new RegExp ( '^' + pathExpression . replace ( / : [ \w . ] + / g, " :([\\w]+)" ) + '$' ) ;
239
- const pathNames = pathNameRegex . exec ( pathExpression ) ;
240
- return pathNames && pathNames . length > 0 ? pathNames . slice ( 1 ) : null ;
239
+ const pathNameRegex = new RegExp ( '^' + pathExpression . replace ( / : [ \w . ] + / g, ' :([\\w]+)' ) + '$' )
240
+ const pathNames = pathNameRegex . exec ( pathExpression )
241
+ return pathNames && pathNames . length > 0 ? pathNames . slice ( 1 ) : null
241
242
}
242
243
243
244
const isLocalExecution = ( event : ProxyIntegrationEvent ) => {
244
245
return event . headers
245
246
&& event . headers . Host
246
- && ( event . headers . Host . startsWith ( 'localhost' ) || event . headers . Host . startsWith ( '127.0.0.1' ) ) ;
247
+ && ( event . headers . Host . startsWith ( 'localhost' ) || event . headers . Host . startsWith ( '127.0.0.1' ) )
247
248
}
0 commit comments