Skip to content

Commit dbd44fd

Browse files
feat: support lambda_proxy req template
Closes #162
1 parent 206529e commit dbd44fd

File tree

2 files changed

+170
-25
lines changed

2 files changed

+170
-25
lines changed

lib/deploy/events/apiGateway/methods.js

+121-14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,79 @@ const BbPromise = require('bluebird');
44
const _ = require('lodash');
55
const awsArnRegExs = require('../../../utils/arnRegularExpressions');
66

7+
const LAMBDA_PROXY_REQUEST_TEMPLATE = `
8+
#define( $loop )
9+
{
10+
#foreach($key in $map.keySet())
11+
#set( $k = $util.escapeJavaScript($key) )
12+
#set( $v = $util.escapeJavaScript($map.get($key)).replaceAll("\\\\'", "'") )
13+
"$k":
14+
"$v"
15+
#if( $foreach.hasNext ) , #end
16+
#end
17+
}
18+
#end
19+
#define( $smInput )
20+
{
21+
"body": $body,
22+
"httpMethod": "$context.httpMethod",
23+
"path": "$context.path",
24+
"resource": "$context.resourcePath",
25+
#set( $map = $input.params().header )
26+
"headers": $loop,
27+
#set( $map = $input.params().querystring )
28+
"queryStringParameters": $loop,
29+
#set( $map = $input.params().path )
30+
"pathParameters": $loop,
31+
"requestContext": {
32+
"accountId": "\${AccountId}",
33+
"resourceId": "$context.resourceId",
34+
"stage": "$context.stage",
35+
"requestId": "$context.requestId",
36+
"requestTime": "$context.requestTime",
37+
"requestTimeEpoch": "$context.requestTimeEpoch",
38+
#set( $map = $context.identity )
39+
"identity": $loop,
40+
"path": "$context.path",
41+
"resourcePath": "$context.resourcePath",
42+
"httpMethod": "$context.httpMethod",
43+
"protocol": "$context.protocol",
44+
"apiId": "$context.apiId"
45+
},
46+
#set( $map = $stageVariables )
47+
"stageVariables": $loop
48+
}
49+
#end
50+
{
51+
"input": "$util.escapeJavaScript("$smInput")",
52+
"name":"$context.requestId",
53+
"stateMachineArn":"\${StateMachineArn}"
54+
}`;
55+
56+
const LAMBDA_PROXY_JSON_REQUEST_TEMPLATE = `
57+
#set( $body = $input.json('$') )
58+
${LAMBDA_PROXY_REQUEST_TEMPLATE}`;
59+
60+
const LAMBDA_PROXY_FORM_URL_ENCODED_REQUEST_TEMPLATE = `
61+
#define( $body )
62+
{
63+
#foreach( $token in $input.path('$').split('&') )
64+
#set( $keyVal = $token.split('=') )
65+
#set( $keyValSize = $keyVal.size() )
66+
#if( $keyValSize >= 1 )
67+
#set( $key = $util.escapeJavaScript($util.urlDecode($keyVal[0])) )
68+
#if( $keyValSize >= 2 )
69+
#set($val = $util.escapeJavaScript($util.urlDecode($keyVal[1])).replaceAll("\\\\'","'"))
70+
#else
71+
#set( $val = '' )
72+
#end
73+
"$key": "$val"#if($foreach.hasNext),#end
74+
#end
75+
#end
76+
}
77+
#end
78+
${LAMBDA_PROXY_REQUEST_TEMPLATE}`;
79+
780
module.exports = {
881

982
compileMethods() {
@@ -115,14 +188,52 @@ module.exports = {
115188
},
116189

117190
getIntegrationRequestTemplates(stateMachineName, stateMachineObj, http) {
118-
const defaultRequestTemplates = this.getDefaultRequestTemplates(
191+
const defaultTemplate = this.getDefaultRequestTemplates(
119192
stateMachineName,
120193
stateMachineObj
121194
);
122-
return Object.assign(
123-
defaultRequestTemplates,
124-
_.get(http, ['request', 'template'])
125-
);
195+
196+
if (_.has(http, 'request.template')) {
197+
// lambda_proxy template
198+
if (http.request.template === 'lambda_proxy') {
199+
return this.getLambdaProxyRequestTemplates(
200+
stateMachineName,
201+
stateMachineObj
202+
);
203+
}
204+
// custom template (Object.assign is because the custom template might cover both
205+
// JSON and form-url content-types)
206+
return Object.assign(
207+
defaultTemplate,
208+
http.request.template
209+
);
210+
}
211+
// default template
212+
return defaultTemplate;
213+
},
214+
215+
getLambdaProxyRequestTemplates(stateMachineName, stateMachineObj) {
216+
const stateMachineLogicalId = this.getStateMachineLogicalId(stateMachineName, stateMachineObj);
217+
return {
218+
'application/json': this.buildLambdaProxyReqTemplate(
219+
LAMBDA_PROXY_JSON_REQUEST_TEMPLATE,
220+
stateMachineLogicalId),
221+
'application/x-www-form-urlencoded': this.buildLambdaProxyReqTemplate(
222+
LAMBDA_PROXY_FORM_URL_ENCODED_REQUEST_TEMPLATE,
223+
stateMachineLogicalId),
224+
};
225+
},
226+
227+
buildLambdaProxyReqTemplate(template, stateMachineLogicalId) {
228+
return {
229+
'Fn::Sub': [
230+
template,
231+
{
232+
StateMachineArn: { Ref: stateMachineLogicalId },
233+
AccountId: { Ref: 'AWS::AccountId' },
234+
},
235+
],
236+
};
126237
},
127238

128239
getDefaultRequestTemplates(stateMachineName, stateMachineObj) {
@@ -135,15 +246,11 @@ module.exports = {
135246

136247
buildDefaultRequestTemplate(stateMachineLogicalId) {
137248
return {
138-
'Fn::Join': [
139-
'', [
140-
"#set( $body = $util.escapeJavaScript($input.json('$')) ) \n\n",
141-
'{"input": "$body","name": "$context.requestId","stateMachineArn":"',
142-
{
143-
Ref: `${stateMachineLogicalId}`,
144-
},
145-
'"}',
146-
],
249+
'Fn::Sub': [
250+
`
251+
#set( $body = $util.escapeJavaScript($input.json('$')) )
252+
{"input": "$body", "name": "$context.requestId", "stateMachineArn":"\${StateMachineArn}"}`,
253+
{ StateMachineArn: { Ref: stateMachineLogicalId } },
147254
],
148255
};
149256
},

lib/deploy/events/apiGateway/methods.test.js

+49-11
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,17 @@ describe('#methods()', () => {
9999

100100
it('should set stateMachinelogical ID to RequestTemplates when customName is not set', () => {
101101
expect(serverlessStepFunctions.getMethodIntegration('stateMachine').Properties
102-
.Integration.RequestTemplates['application/json']['Fn::Join'][1][2].Ref)
102+
.Integration.RequestTemplates['application/json']['Fn::Sub'][1].StateMachineArn.Ref)
103103
.to.be.equal('StateMachineStepFunctionsStateMachine');
104104
});
105105

106106
it('should set custom stateMachinelogical ID to RequestTemplates when customName is set',
107107
() => {
108-
expect(serverlessStepFunctions.getMethodIntegration('stateMachine', { name: 'custom' })
109-
.Properties.Integration.RequestTemplates['application/json']['Fn::Join'][1][2].Ref)
108+
const integration = serverlessStepFunctions
109+
.getMethodIntegration('stateMachine', { name: 'custom' })
110+
.Properties
111+
.Integration;
112+
expect(integration.RequestTemplates['application/json']['Fn::Sub'][1].StateMachineArn.Ref)
110113
.to.be.equal('Custom');
111114
});
112115

@@ -134,16 +137,20 @@ describe('#methods()', () => {
134137
it('should set stateMachinelogical ID in default templates when customName is not set', () => {
135138
const requestTemplates = serverlessStepFunctions
136139
.getIntegrationRequestTemplates('stateMachine');
137-
expect(requestTemplates['application/json']['Fn::Join'][1][2].Ref)
138-
.to.be.equal('StateMachineStepFunctionsStateMachine');
140+
expect(requestTemplates['application/json']['Fn::Sub'][1])
141+
.to.be.deep.equal({
142+
StateMachineArn: { Ref: 'StateMachineStepFunctionsStateMachine' },
143+
});
139144
});
140145

141146
it('should set custom stateMachinelogical ID in default templates when customName is set',
142147
() => {
143148
const requestTemplates = serverlessStepFunctions
144149
.getIntegrationRequestTemplates('stateMachine', { name: 'custom' });
145-
expect(requestTemplates['application/json']['Fn::Join'][1][2].Ref)
146-
.to.be.equal('Custom');
150+
expect(requestTemplates['application/json']['Fn::Sub'][1])
151+
.to.be.deep.equal({
152+
StateMachineArn: { Ref: 'Custom' },
153+
});
147154
});
148155

149156
it('should return the default template for application/json when one is not given', () => {
@@ -159,8 +166,10 @@ describe('#methods()', () => {
159166
const requestTemplates = serverlessStepFunctions
160167
.getMethodIntegration('stateMachine', undefined, httpWithoutRequestTemplate)
161168
.Properties.Integration.RequestTemplates;
162-
expect(requestTemplates['application/json']['Fn::Join'][1][2].Ref)
163-
.to.be.equal('StateMachineStepFunctionsStateMachine');
169+
expect(requestTemplates['application/json']['Fn::Sub'][1])
170+
.to.be.deep.equal({
171+
StateMachineArn: { Ref: 'StateMachineStepFunctionsStateMachine' },
172+
});
164173
});
165174

166175
it('should return a custom template for application/json when one is given', () => {
@@ -194,8 +203,10 @@ describe('#methods()', () => {
194203
const requestTemplates = serverlessStepFunctions
195204
.getMethodIntegration('stateMachine', undefined, httpWithoutRequestTemplate)
196205
.Properties.Integration.RequestTemplates;
197-
expect(requestTemplates['application/x-www-form-urlencoded']['Fn::Join'][1][2].Ref)
198-
.to.be.equal('StateMachineStepFunctionsStateMachine');
206+
expect(requestTemplates['application/x-www-form-urlencoded']['Fn::Sub'][1])
207+
.to.be.deep.equal({
208+
StateMachineArn: { Ref: 'StateMachineStepFunctionsStateMachine' },
209+
});
199210
});
200211

201212
it('should return a custom template for application/x-www-form-urlencoded when one is given',
@@ -215,6 +226,33 @@ describe('#methods()', () => {
215226
expect(requestTemplates['application/x-www-form-urlencoded'])
216227
.to.be.equal('custom template');
217228
});
229+
230+
it('should return the LAMBDA_PROXY template if http.request.template is "lambda_proxy"', () => {
231+
const httpWithRequestTemplate = {
232+
path: 'foo/bar1',
233+
method: 'post',
234+
request: {
235+
template: 'lambda_proxy',
236+
},
237+
};
238+
const requestTemplates = serverlessStepFunctions
239+
.getMethodIntegration('stateMachine', undefined, httpWithRequestTemplate)
240+
.Properties.Integration.RequestTemplates;
241+
expect(requestTemplates['application/json']['Fn::Sub'][0])
242+
.to.contain("#set( $body = $input.json('$') )");
243+
expect(requestTemplates['application/json']['Fn::Sub'][1])
244+
.to.be.deep.equal({
245+
StateMachineArn: { Ref: 'StateMachineStepFunctionsStateMachine' },
246+
AccountId: { Ref: 'AWS::AccountId' },
247+
});
248+
expect(requestTemplates['application/x-www-form-urlencoded']['Fn::Sub'][0])
249+
.to.contain('#define( $body )');
250+
expect(requestTemplates['application/x-www-form-urlencoded']['Fn::Sub'][1])
251+
.to.be.deep.equal({
252+
StateMachineArn: { Ref: 'StateMachineStepFunctionsStateMachine' },
253+
AccountId: { Ref: 'AWS::AccountId' },
254+
});
255+
});
218256
});
219257

220258
describe('#getMethodResponses()', () => {

0 commit comments

Comments
 (0)