Skip to content

Commit

Permalink
feat: support lambda_proxy req template
Browse files Browse the repository at this point in the history
Closes #162
  • Loading branch information
theburningmonk committed May 4, 2019
1 parent 206529e commit dbd44fd
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 25 deletions.
135 changes: 121 additions & 14 deletions lib/deploy/events/apiGateway/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,79 @@ const BbPromise = require('bluebird');
const _ = require('lodash');
const awsArnRegExs = require('../../../utils/arnRegularExpressions');

const LAMBDA_PROXY_REQUEST_TEMPLATE = `
#define( $loop )
{
#foreach($key in $map.keySet())
#set( $k = $util.escapeJavaScript($key) )
#set( $v = $util.escapeJavaScript($map.get($key)).replaceAll("\\\\'", "'") )
"$k":
"$v"
#if( $foreach.hasNext ) , #end
#end
}
#end
#define( $smInput )
{
"body": $body,
"httpMethod": "$context.httpMethod",
"path": "$context.path",
"resource": "$context.resourcePath",
#set( $map = $input.params().header )
"headers": $loop,
#set( $map = $input.params().querystring )
"queryStringParameters": $loop,
#set( $map = $input.params().path )
"pathParameters": $loop,
"requestContext": {
"accountId": "\${AccountId}",
"resourceId": "$context.resourceId",
"stage": "$context.stage",
"requestId": "$context.requestId",
"requestTime": "$context.requestTime",
"requestTimeEpoch": "$context.requestTimeEpoch",
#set( $map = $context.identity )
"identity": $loop,
"path": "$context.path",
"resourcePath": "$context.resourcePath",
"httpMethod": "$context.httpMethod",
"protocol": "$context.protocol",
"apiId": "$context.apiId"
},
#set( $map = $stageVariables )
"stageVariables": $loop
}
#end
{
"input": "$util.escapeJavaScript("$smInput")",
"name":"$context.requestId",
"stateMachineArn":"\${StateMachineArn}"
}`;

const LAMBDA_PROXY_JSON_REQUEST_TEMPLATE = `
#set( $body = $input.json('$') )
${LAMBDA_PROXY_REQUEST_TEMPLATE}`;

const LAMBDA_PROXY_FORM_URL_ENCODED_REQUEST_TEMPLATE = `
#define( $body )
{
#foreach( $token in $input.path('$').split('&') )
#set( $keyVal = $token.split('=') )
#set( $keyValSize = $keyVal.size() )
#if( $keyValSize >= 1 )
#set( $key = $util.escapeJavaScript($util.urlDecode($keyVal[0])) )
#if( $keyValSize >= 2 )
#set($val = $util.escapeJavaScript($util.urlDecode($keyVal[1])).replaceAll("\\\\'","'"))
#else
#set( $val = '' )
#end
"$key": "$val"#if($foreach.hasNext),#end
#end
#end
}
#end
${LAMBDA_PROXY_REQUEST_TEMPLATE}`;

module.exports = {

compileMethods() {
Expand Down Expand Up @@ -115,14 +188,52 @@ module.exports = {
},

getIntegrationRequestTemplates(stateMachineName, stateMachineObj, http) {
const defaultRequestTemplates = this.getDefaultRequestTemplates(
const defaultTemplate = this.getDefaultRequestTemplates(
stateMachineName,
stateMachineObj
);
return Object.assign(
defaultRequestTemplates,
_.get(http, ['request', 'template'])
);

if (_.has(http, 'request.template')) {
// lambda_proxy template
if (http.request.template === 'lambda_proxy') {
return this.getLambdaProxyRequestTemplates(
stateMachineName,
stateMachineObj
);
}
// custom template (Object.assign is because the custom template might cover both
// JSON and form-url content-types)
return Object.assign(
defaultTemplate,
http.request.template
);
}
// default template
return defaultTemplate;
},

getLambdaProxyRequestTemplates(stateMachineName, stateMachineObj) {
const stateMachineLogicalId = this.getStateMachineLogicalId(stateMachineName, stateMachineObj);
return {
'application/json': this.buildLambdaProxyReqTemplate(
LAMBDA_PROXY_JSON_REQUEST_TEMPLATE,
stateMachineLogicalId),
'application/x-www-form-urlencoded': this.buildLambdaProxyReqTemplate(
LAMBDA_PROXY_FORM_URL_ENCODED_REQUEST_TEMPLATE,
stateMachineLogicalId),
};
},

buildLambdaProxyReqTemplate(template, stateMachineLogicalId) {
return {
'Fn::Sub': [
template,
{
StateMachineArn: { Ref: stateMachineLogicalId },
AccountId: { Ref: 'AWS::AccountId' },
},
],
};
},

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

buildDefaultRequestTemplate(stateMachineLogicalId) {
return {
'Fn::Join': [
'', [
"#set( $body = $util.escapeJavaScript($input.json('$')) ) \n\n",
'{"input": "$body","name": "$context.requestId","stateMachineArn":"',
{
Ref: `${stateMachineLogicalId}`,
},
'"}',
],
'Fn::Sub': [
`
#set( $body = $util.escapeJavaScript($input.json('$')) )
{"input": "$body", "name": "$context.requestId", "stateMachineArn":"\${StateMachineArn}"}`,
{ StateMachineArn: { Ref: stateMachineLogicalId } },
],
};
},
Expand Down
60 changes: 49 additions & 11 deletions lib/deploy/events/apiGateway/methods.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,17 @@ describe('#methods()', () => {

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

it('should set custom stateMachinelogical ID to RequestTemplates when customName is set',
() => {
expect(serverlessStepFunctions.getMethodIntegration('stateMachine', { name: 'custom' })
.Properties.Integration.RequestTemplates['application/json']['Fn::Join'][1][2].Ref)
const integration = serverlessStepFunctions
.getMethodIntegration('stateMachine', { name: 'custom' })
.Properties
.Integration;
expect(integration.RequestTemplates['application/json']['Fn::Sub'][1].StateMachineArn.Ref)
.to.be.equal('Custom');
});

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

it('should set custom stateMachinelogical ID in default templates when customName is set',
() => {
const requestTemplates = serverlessStepFunctions
.getIntegrationRequestTemplates('stateMachine', { name: 'custom' });
expect(requestTemplates['application/json']['Fn::Join'][1][2].Ref)
.to.be.equal('Custom');
expect(requestTemplates['application/json']['Fn::Sub'][1])
.to.be.deep.equal({
StateMachineArn: { Ref: 'Custom' },
});
});

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

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

it('should return a custom template for application/x-www-form-urlencoded when one is given',
Expand All @@ -215,6 +226,33 @@ describe('#methods()', () => {
expect(requestTemplates['application/x-www-form-urlencoded'])
.to.be.equal('custom template');
});

it('should return the LAMBDA_PROXY template if http.request.template is "lambda_proxy"', () => {
const httpWithRequestTemplate = {
path: 'foo/bar1',
method: 'post',
request: {
template: 'lambda_proxy',
},
};
const requestTemplates = serverlessStepFunctions
.getMethodIntegration('stateMachine', undefined, httpWithRequestTemplate)
.Properties.Integration.RequestTemplates;
expect(requestTemplates['application/json']['Fn::Sub'][0])
.to.contain("#set( $body = $input.json('$') )");
expect(requestTemplates['application/json']['Fn::Sub'][1])
.to.be.deep.equal({
StateMachineArn: { Ref: 'StateMachineStepFunctionsStateMachine' },
AccountId: { Ref: 'AWS::AccountId' },
});
expect(requestTemplates['application/x-www-form-urlencoded']['Fn::Sub'][0])
.to.contain('#define( $body )');
expect(requestTemplates['application/x-www-form-urlencoded']['Fn::Sub'][1])
.to.be.deep.equal({
StateMachineArn: { Ref: 'StateMachineStepFunctionsStateMachine' },
AccountId: { Ref: 'AWS::AccountId' },
});
});
});

describe('#getMethodResponses()', () => {
Expand Down

0 comments on commit dbd44fd

Please sign in to comment.