diff --git a/README.md b/README.md index e827d737..a062d74e 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,30 @@ plugins: - serverless-step-functions ``` +### Adding a custom logical id for a stateMachine +You can use a custom logical id that is only unique within the stack as opposed to the name that needs to be unique globally. This can make referencing the state machine easier/simpler because you don't have to duplicate the interpolation logic everywhere you reference the state machine. + +```yml +service: messager + +functions: + sendMessage: + handler: handler.sendMessage + +stepFunctions: + stateMachines: + sendMessageFunc: + id: SendMessageStateMachine + name: sendMessageFunc-${self:custom.service}-${opt:stage} + definition: + + +plugins: + - serverless-step-functions +``` + +You can then `Ref: SendMessageStateMachine` in various parts of CloudFormation or serverless.yml + #### Current Gotcha Please keep this gotcha in mind if you want to reference the `name` from the `resources` section. To generate Logical ID for CloudFormation, the plugin transforms the specified name in serverless.yml based on the following scheme. diff --git a/lib/deploy/events/apiGateway/methods.js b/lib/deploy/events/apiGateway/methods.js index 8c761b45..f16b7c12 100644 --- a/lib/deploy/events/apiGateway/methods.js +++ b/lib/deploy/events/apiGateway/methods.js @@ -25,7 +25,7 @@ module.exports = { }; _.merge(template, - this.getMethodIntegration(event.stateMachineName, stateMachineObj.name, event.http), + this.getMethodIntegration(event.stateMachineName, stateMachineObj, event.http), this.getMethodResponses(event.http) ); @@ -42,7 +42,7 @@ module.exports = { return BbPromise.resolve(); }, - getMethodIntegration(stateMachineName, customName, http) { + getMethodIntegration(stateMachineName, stateMachineObj, http) { const apiToStepFunctionsIamRoleLogicalId = this.getApiToStepFunctionsIamRoleLogicalId(); const integration = { IntegrationHttpMethod: 'POST', @@ -66,7 +66,11 @@ module.exports = { ], }, PassthroughBehavior: 'NEVER', - RequestTemplates: this.getIntegrationRequestTemplates(stateMachineName, customName, http), + RequestTemplates: this.getIntegrationRequestTemplates( + stateMachineName, + stateMachineObj, + http + ), }; const integrationResponse = { @@ -108,16 +112,19 @@ module.exports = { }; }, - getIntegrationRequestTemplates(stateMachineName, customName, http) { - const defaultRequestTemplates = this.getDefaultRequestTemplates(stateMachineName, customName); + getIntegrationRequestTemplates(stateMachineName, stateMachineObj, http) { + const defaultRequestTemplates = this.getDefaultRequestTemplates( + stateMachineName, + stateMachineObj + ); return Object.assign( defaultRequestTemplates, _.get(http, ['request', 'template']) ); }, - getDefaultRequestTemplates(stateMachineName, customName) { - const stateMachineLogicalId = this.getStateMachineLogicalId(stateMachineName, customName); + getDefaultRequestTemplates(stateMachineName, stateMachineObj) { + const stateMachineLogicalId = this.getStateMachineLogicalId(stateMachineName, stateMachineObj); return { 'application/json': this.buildDefaultRequestTemplate(stateMachineLogicalId), 'application/x-www-form-urlencoded': this.buildDefaultRequestTemplate(stateMachineLogicalId), diff --git a/lib/deploy/events/apiGateway/methods.test.js b/lib/deploy/events/apiGateway/methods.test.js index 8170afab..329aed09 100644 --- a/lib/deploy/events/apiGateway/methods.test.js +++ b/lib/deploy/events/apiGateway/methods.test.js @@ -82,6 +82,19 @@ 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', { name: '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', { @@ -113,7 +126,7 @@ describe('#methods()', () => { it('should set custom stateMachinelogical ID in default templates when customName is set', () => { const requestTemplates = serverlessStepFunctions - .getIntegrationRequestTemplates('stateMachine', 'custom'); + .getIntegrationRequestTemplates('stateMachine', { name: 'custom' }); expect(requestTemplates['application/json']['Fn::Join'][1][2].Ref) .to.be.equal('Custom'); }); diff --git a/lib/deploy/events/schedule/compileScheduledEvents.js b/lib/deploy/events/schedule/compileScheduledEvents.js index 07acda31..41a02508 100644 --- a/lib/deploy/events/schedule/compileScheduledEvents.js +++ b/lib/deploy/events/schedule/compileScheduledEvents.js @@ -74,7 +74,7 @@ module.exports = { } const stateMachineLogicalId = this - .getStateMachineLogicalId(stateMachineName, stateMachineObj.name); + .getStateMachineLogicalId(stateMachineName, stateMachineObj); const scheduleLogicalId = this .getScheduleLogicalId(stateMachineName, scheduleNumberInFunction); const scheduleIamRoleLogicalId = this diff --git a/lib/deploy/stepFunctions/compileStateMachines.js b/lib/deploy/stepFunctions/compileStateMachines.js index 8eddc208..165f5daa 100644 --- a/lib/deploy/stepFunctions/compileStateMachines.js +++ b/lib/deploy/stepFunctions/compileStateMachines.js @@ -10,11 +10,6 @@ module.exports = { let DefinitionString; let RoleArn; let DependsOn; - let Name; - - if (stateMachineObj.name) { - Name = stateMachineObj.name; - } if (stateMachineObj.definition) { DefinitionString = JSON.stringify(stateMachineObj.definition); @@ -52,9 +47,10 @@ module.exports = { DependsOn = 'IamRoleStateMachineExecution'; } - const stateMachineLogicalId = this.getStateMachineLogicalId(stateMachineName, Name); + const stateMachineLogicalId = this.getStateMachineLogicalId(stateMachineName, + stateMachineObj); const stateMachineOutputLogicalId = this - .getStateMachineOutputLogicalId(stateMachineName, Name); + .getStateMachineOutputLogicalId(stateMachineName, stateMachineObj); const stateMachineTemplate = ` { @@ -72,8 +68,9 @@ module.exports = { [stateMachineLogicalId]: JSON.parse(stateMachineTemplate), }; - if (Name) { - newStateMachineObject[stateMachineLogicalId].Properties.StateMachineName = Name; + if (stateMachineObj.name) { + newStateMachineObject[stateMachineLogicalId].Properties.StateMachineName + = stateMachineObj.name; } _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, diff --git a/lib/invoke/invoke.js b/lib/invoke/invoke.js index 7f0493dc..df2ddb45 100644 --- a/lib/invoke/invoke.js +++ b/lib/invoke/invoke.js @@ -5,15 +5,9 @@ const path = require('path'); module.exports = { getStateMachineArn() { - let stateMachineOutputKey; - if (this.serverless.service.stepFunctions.stateMachines[this.options.name].name) { - stateMachineOutputKey = - this.getStateMachineOutputLogicalId(this.options.name - , this.serverless.service.stepFunctions.stateMachines[this.options.name].name); - } else { - stateMachineOutputKey = - this.getStateMachineOutputLogicalId(this.options.name); - } + const stateMachineOutputKey = + this.getStateMachineOutputLogicalId(this.options.name, + this.serverless.service.stepFunctions.stateMachines[this.options.name]); const stackName = this.provider.naming.getStackName(this.options.stage); diff --git a/lib/naming.js b/lib/naming.js index 578074bf..418b693d 100644 --- a/lib/naming.js +++ b/lib/naming.js @@ -1,18 +1,24 @@ 'use strict'; module.exports = { - getStateMachineLogicalId(stateMachineName, customName) { - if (customName) { - return `${this.provider.naming.getNormalizedFunctionName(customName)}`; + getStateMachineLogicalId(stateMachineName, stateMachine) { + const custom = stateMachine ? stateMachine.id || stateMachine.name : null; + + if (custom) { + return `${this.provider.naming.getNormalizedFunctionName(custom)}`; } + return `${this.provider.naming .getNormalizedFunctionName(stateMachineName)}StepFunctionsStateMachine`; }, - getStateMachineOutputLogicalId(stateMachineName, customName) { - if (customName) { - return `${this.provider.naming.getNormalizedFunctionName(customName)}Arn`; + getStateMachineOutputLogicalId(stateMachineName, stateMachine) { + const custom = stateMachine ? stateMachine.id || stateMachine.name : null; + + if (custom) { + return `${this.provider.naming.getNormalizedFunctionName(custom)}Arn`; } + return `${this.provider.naming .getNormalizedFunctionName(stateMachineName)}StepFunctionsStateMachineArn`; }, diff --git a/lib/naming.test.js b/lib/naming.test.js index 55abae0f..de19a33b 100644 --- a/lib/naming.test.js +++ b/lib/naming.test.js @@ -34,18 +34,50 @@ describe('#naming', () => { describe('#getStateMachineLogicalId() -- Named', () => { it('should normalize the stateMachine name and add the standard suffix', () => { - expect(serverlessStepFunctions.getStateMachineLogicalId('stateMachine', 'alphaNumeric')).to - .equal('AlphaNumeric'); + expect(serverlessStepFunctions.getStateMachineLogicalId('stateMachine', + { name: 'alphaNumeric' })) + .to.equal('AlphaNumeric'); }); }); describe('#getStateMachineOutputLogicalId() -- Named', () => { it('should normalize the stateMachine output name and add the standard suffix', () => { - expect(serverlessStepFunctions.getStateMachineOutputLogicalId('stateMachine', 'alphaNumeric')) + expect(serverlessStepFunctions.getStateMachineOutputLogicalId('stateMachine', + { name: 'alphaNumeric' })) .to.equal('AlphaNumericArn'); }); }); + describe('#getStateMachineLogicalId() -- With Id', () => { + it('should normalize the stateMachine name and add the standard suffix', () => { + expect(serverlessStepFunctions.getStateMachineLogicalId('stateMachine', + { id: 'foo', name: 'alphaNumeric' })) + .to.equal('Foo'); + }); + }); + + describe('#getStateMachineOutputLogicalId() -- With Id', () => { + it('should normalize the stateMachine output name and add the standard suffix', () => { + expect(serverlessStepFunctions.getStateMachineOutputLogicalId('stateMachine', + { id: 'foo', name: 'alphaNumeric' })) + .to.equal('FooArn'); + }); + }); + + describe('#getStateMachineLogicalId() -- With Only Id', () => { + it('should normalize the stateMachine name and add the standard suffix', () => { + expect(serverlessStepFunctions.getStateMachineLogicalId('stateMachine', { id: 'foo' })).to + .equal('Foo'); + }); + }); + + describe('#getStateMachineOutputLogicalId() -- With Only Id', () => { + it('should normalize the stateMachine output name and add the standard suffix', () => { + expect(serverlessStepFunctions.getStateMachineOutputLogicalId('stateMachine', { id: 'foo' })) + .to.equal('FooArn'); + }); + }); + describe('#getActivityLogicalId()', () => { it('should normalize the activity name and add the standard suffix', () => { expect(serverlessStepFunctions.getActivityLogicalId('activity')).to