@@ -30,19 +30,28 @@ class ServerlessStepFunctionsLocal {
3030 this . config . path = './.step-functions-local' ;
3131 }
3232
33+ if ( this . config . startStepFunctionsLocalApp === undefined ) {
34+ this . config . startStepFunctionsLocalApp = true
35+ }
36+
3337 this . stepfunctionsServer = new StepFunctionsLocal ( this . config ) ;
3438
3539 this . stepfunctionsAPI = new AWS . StepFunctions ( { endpoint : 'http://localhost:8083' , region : this . config . region } ) ;
3640
3741 this . hooks = {
3842 'offline:start:init' : async ( ) => {
39- await this . installStepFunctions ( ) ;
43+ if ( this . config . startStepFunctionsLocalApp ) {
44+ await this . installStepFunctions ( ) ;
45+ }
46+
4047 await this . startStepFunctions ( ) ;
4148 await this . getStepFunctionsFromConfig ( ) ;
4249 await this . createEndpoints ( ) ;
4350 } ,
4451 'before:offline:start:end' : async ( ) => {
45- await this . stopStepFunctions ( ) ;
52+ if ( this . config . startStepFunctionsLocalApp ) {
53+ await this . stopStepFunctions ( ) ;
54+ }
4655 }
4756 } ;
4857 }
@@ -52,15 +61,20 @@ class ServerlessStepFunctionsLocal {
5261 }
5362
5463 async startStepFunctions ( ) {
55- this . stepfunctionsServer . start ( {
56- account : this . config . accountId . toString ( ) ,
57- lambdaEndpoint : this . config . lambdaEndpoint
58- } ) . on ( 'data' , data => {
59- console . log ( chalk . blue ( '[Serverless Step Functions Local]' ) , data . toString ( ) ) ;
60- } ) ;
64+ if ( this . config . startStepFunctionsLocalApp ) {
65+ this . stepfunctionsServer . start ( {
66+ account : this . config . accountId . toString ( ) ,
67+ lambdaEndpoint : this . config . lambdaEndpoint
68+ } ) . on ( 'data' , data => {
69+ console . log ( chalk . blue ( '[Serverless Step Functions Local]' ) , data . toString ( ) ) ;
70+ } ) ;
71+ } else {
72+ console . log ( chalk . blue ( '[Serverless Step Functions Local]' ) , 'Waiting for AWS Step Functions emulator on port 8083' ) ;
73+ }
6174
6275 // Wait for server to start
6376 await tcpPortUsed . waitUntilUsed ( 8083 , 200 , 10000 ) ;
77+ console . log ( chalk . blue ( '[Serverless Step Functions Local]' ) , 'AWS Step Functions emulator detected on 8083' ) ;
6478 }
6579
6680 stopStepFunctions ( ) {
@@ -80,6 +94,19 @@ class ServerlessStepFunctionsLocal {
8094
8195 this . stateMachines = parsed . stepFunctions . stateMachines ;
8296
97+ // replace Fn::GetAtt
98+ Object . keys ( this . stateMachines ) . map ( stateMachineName => {
99+ const machine = this . stateMachines [ stateMachineName ]
100+ Object . keys ( machine . definition . States ) . map ( stateName => {
101+ const state = machine . definition . States [ stateName ]
102+ if ( state . Type === 'Task' ) {
103+ if ( state . Resource && state . Resource [ 'Fn::GetAtt' ] && Array . isArray ( state . Resource [ 'Fn::GetAtt' ] ) ) {
104+ state . Resource = `arn:aws:lambda:${ this . config . region } :${ this . config . accountId } :function:${ this . service . service } -${ this . serverless . stage ? this . serverless . stage : 'dev' } -${ state . Resource [ 'Fn::GetAtt' ] [ 0 ] } `
105+ }
106+ }
107+ } )
108+ } )
109+
83110 if ( parsed . custom
84111 && parsed . custom . stepFunctionsLocal
85112 && parsed . custom . stepFunctionsLocal . TaskResourceMapping ) {
@@ -104,6 +131,51 @@ class ServerlessStepFunctionsLocal {
104131 }
105132
106133 async createEndpoints ( ) {
134+ // Delete existing state machines
135+ const EMPTY = Symbol ( 'empty' )
136+ let nextToken = EMPTY
137+ const knownStateMachines = Object . keys ( this . stateMachines )
138+ // A state machine is eventually deleted.
139+ // We need to wait until it's actually deleted because otherwise
140+ // the new state machine created later is deleted as well and is not
141+ // available.
142+ while ( true ) {
143+ let hasRunningMachine = false
144+ while ( nextToken ) {
145+ const data = await this . stepfunctionsAPI . listStateMachines ( {
146+ nextToken : ( nextToken === EMPTY ? undefined : nextToken )
147+ } ) . promise ( )
148+
149+ nextToken = data . nextToken
150+ for ( const machine of data . stateMachines ) {
151+ if ( ! knownStateMachines . includes ( machine . name ) ) {
152+ continue
153+ }
154+ hasRunningMachine = true
155+
156+ await this . stepfunctionsAPI . deleteStateMachine ( {
157+ stateMachineArn : machine . stateMachineArn
158+ } )
159+ . promise ( )
160+ . catch ( err => {
161+ // state machine was not found
162+ if ( err && err . code === 400 ) {
163+ return
164+ }
165+
166+ throw err
167+ } )
168+ }
169+ }
170+
171+ if ( ! hasRunningMachine ) {
172+ break
173+ }
174+
175+ await new Promise ( resolve => setTimeout ( resolve , 1000 ) )
176+ console . log ( chalk . blue ( '[Serverless Step Functions Local]' ) , 'Retrying old state machine removal' ) ;
177+ }
178+
107179 const endpoints = await Promise . all ( Object . keys ( this . stateMachines ) . map ( stateMachineName => this . stepfunctionsAPI . createStateMachine ( {
108180 definition : JSON . stringify ( this . stateMachines [ stateMachineName ] . definition ) ,
109181 name : stateMachineName ,
0 commit comments