Skip to content

Add support for custom body mapping templates #122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,32 @@ stepFunctions:

Configuring the cors property sets Access-Control-Allow-Origin, Access-Control-Allow-Headers, Access-Control-Allow-Methods,Access-Control-Allow-Credentials headers in the CORS preflight response.

#### Customizing request body mapping templates

The plugin generates default body mapping templates for `application/json` and `application/x-www-form-urlencoded` content types. If you'd like to add more content types or customize the default ones, you can do so by including them in `serverless.yml`:

```yml
stepFunctions:
stateMachines:
hello:
events:
- http:
path: posts/create
method: POST
request:
template:
application/json: |
#set( $body = $util.escapeJavaScript($input.json('$')) )
#set( $name = $util.escapeJavaScript($input.json('$.data.attributes.order_id')) )
{
"input": "$body",
"name": "$name",
"stateMachineArn":"arn:aws:states:#{AWS::Region}:#{AWS::AccountId}:stateMachine:processOrderFlow-${opt:stage}"
}
name: processOrderFlow-${opt:stage}
definition:
```

#### Send request to an API
You can input an value as json in request body, the value is passed as the input value of your statemachine

Expand Down
59 changes: 32 additions & 27 deletions lib/deploy/events/apiGateway/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ module.exports = {
},

getMethodIntegration(stateMachineName, customName, http) {
const stateMachineLogicalId = this.getStateMachineLogicalId(stateMachineName, customName);
const apiToStepFunctionsIamRoleLogicalId = this.getApiToStepFunctionsIamRoleLogicalId();
const integration = {
IntegrationHttpMethod: 'POST',
Expand All @@ -67,32 +66,7 @@ module.exports = {
],
},
PassthroughBehavior: 'NEVER',
RequestTemplates: {
'application/json': {
'Fn::Join': [
'', [
"#set( $body = $util.escapeJavaScript($input.json('$')) ) \n\n",
'{"input": "$body","name": "$context.requestId","stateMachineArn":"',
{
Ref: `${stateMachineLogicalId}`,
},
'"}',
],
],
},
'application/x-www-form-urlencoded': {
'Fn::Join': [
'', [
"#set( $body = $util.escapeJavaScript($input.json('$')) ) \n\n",
'{"input": "$body","name": "$context.requestId","stateMachineArn":"',
{
Ref: `${stateMachineLogicalId}`,
},
'"}',
],
],
},
},
RequestTemplates: this.getIntegrationRequestTemplates(stateMachineName, customName, http),
};

const integrationResponse = {
Expand Down Expand Up @@ -134,6 +108,37 @@ module.exports = {
};
},

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

getDefaultRequestTemplates(stateMachineName, customName) {
const stateMachineLogicalId = this.getStateMachineLogicalId(stateMachineName, customName);
return {
'application/json': this.buildDefaultRequestTemplate(stateMachineLogicalId),
'application/x-www-form-urlencoded': this.buildDefaultRequestTemplate(stateMachineLogicalId),
};
},

buildDefaultRequestTemplate(stateMachineLogicalId) {
return {
'Fn::Join': [
'', [
"#set( $body = $util.escapeJavaScript($input.json('$')) ) \n\n",
'{"input": "$body","name": "$context.requestId","stateMachineArn":"',
{
Ref: `${stateMachineLogicalId}`,
},
'"}',
],
],
};
},

getMethodResponses(http) {
const methodResponse = {
Properties: {
Expand Down
100 changes: 87 additions & 13 deletions lib/deploy/events/apiGateway/methods.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,6 @@ describe('#methods()', () => {
.to.have.property('Integration');
});

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)
.to.be.equal('StateMachineStepFunctionsStateMachine');
});

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

it('should set Access-Control-Allow-Origin header when cors is true',
() => {
expect(serverlessStepFunctions.getMethodIntegration('stateMachine', 'custom', {
Expand All @@ -115,6 +102,93 @@ describe('#methods()', () => {
});
});

describe('#getIntegrationRequestTemplates()', () => {
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');
});

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

it('should return the default template for application/json when one is not given', () => {
const httpWithoutRequestTemplate = {
path: 'foo/bar1',
method: 'post',
request: {
template: {
'application/x-www-form-urlencoded': 'custom template',
},
},
};
const requestTemplates = serverlessStepFunctions
.getMethodIntegration('stateMachine', undefined, httpWithoutRequestTemplate)
.Properties.Integration.RequestTemplates;
expect(requestTemplates['application/json']['Fn::Join'][1][2].Ref)
.to.be.equal('StateMachineStepFunctionsStateMachine');
});

it('should return a custom template for application/json when one is given', () => {
const httpWithRequestTemplate = {
path: 'foo/bar1',
method: 'post',
request: {
template: {
'application/json': 'custom template',
},
},
};
const requestTemplates = serverlessStepFunctions
.getMethodIntegration('stateMachine', undefined, httpWithRequestTemplate)
.Properties.Integration.RequestTemplates;
expect(requestTemplates['application/json'])
.to.be.equal('custom template');
});

it('should return the default for application/x-www-form-urlencoded when one is not given',
() => {
const httpWithoutRequestTemplate = {
path: 'foo/bar1',
method: 'post',
request: {
template: {
'application/json': 'custom template',
},
},
};
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');
});

it('should return a custom template for application/x-www-form-urlencoded when one is given',
() => {
const httpWithRequestTemplate = {
path: 'foo/bar1',
method: 'post',
request: {
template: {
'application/x-www-form-urlencoded': 'custom template',
},
},
};
const requestTemplates = serverlessStepFunctions
.getMethodIntegration('stateMachine', undefined, httpWithRequestTemplate)
.Properties.Integration.RequestTemplates;
expect(requestTemplates['application/x-www-form-urlencoded'])
.to.be.equal('custom template');
});
});

describe('#getMethodResponses()', () => {
it('should return a corresponding methodResponses resource', () => {
expect(serverlessStepFunctions.getMethodResponses().Properties)
Expand Down