Skip to content

bug: SFN parallel with resultSelector from 1 branch doesn't wrap result into array #13009

@MartynasRim

Description

@MartynasRim

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

StateMachine definition:

DefinitionBody.fromChainable(
          new sfn.Parallel(this, 'ParallelState', {
            resultSelector: {
              'messages.$': '$[*].message',
            },
          })
            .branch(
              new sfn.Pass(this, 'Branch1', {
                result: sfn.Result.fromObject({ message: 'Hello from Branch 1' }),
              }),
            )
            .branch(
              new sfn.Pass(
                this,
                'Branch2',
                noResultFrom2ndBranch
                  ? {}
                  : {
                      result: sfn.Result.fromObject({ message: 'Hello from Branch 2' }),
                    },
              ),
            )
            .next(new sfn.Pass(this, 'Finish')),
        )

outputs {"messages": "Hello from Branch 1"}.

On AWS it outputs {"messages": ["Hello from Branch 1"]}

Expected Behavior

Should output {"messages": ["Hello from Branch 1"]}

How are you starting LocalStack?

With the localstack script

Steps To Reproduce

import { ExecutionStatus } from '@aws-sdk/client-sfn';
import { LocalstackCdkToolkit, SfnClientToolkit } from '@stage-tech/distribution-util-localstack-aws-tests';
import { App, aws_stepfunctions as sfn, Stack } from 'aws-cdk-lib';
import { DefinitionBody, StateMachine, StateMachineType } from 'aws-cdk-lib/aws-stepfunctions';

function getApp(noResultFrom2ndBranch = false): App {
  const app = new App();

  class SimpleStack extends Stack {
    public stateMachine: StateMachine;

    constructor(scope: App, id: string) {
      super(scope, id);

      this.stateMachine = new StateMachine(this, 'StateMachineExample', {
        stateMachineType: StateMachineType.STANDARD,
        definitionBody: DefinitionBody.fromChainable(
          new sfn.Parallel(this, 'ParallelState', {
            resultSelector: {
              'messages.$': '$[*].message',
            },
          })
            .branch(
              new sfn.Pass(this, 'Branch1', {
                result: sfn.Result.fromObject({ message: 'Hello from Branch 1' }),
              }),
            )
            .branch(
              new sfn.Pass(
                this,
                'Branch2',
                noResultFrom2ndBranch
                  ? {}
                  : {
                      result: sfn.Result.fromObject({ message: 'Hello from Branch 2' }),
                    },
              ),
            )
            .next(new sfn.Pass(this, 'Finish')),
        ),
      });
    }
  }

  new SimpleStack(app, 'LocalstackTestStack');
  return app;
}

describe.skip('localstack', () => {
  it('should return array of 2 messages', async () => {
    const app = getApp();
    const cdkToolkit = new LocalstackCdkToolkit(app);
    await cdkToolkit.deploy();
    const awsConfig = await cdkToolkit?.localstackContainer?.getAwsConfig();
    const sfnClientToolkit = new SfnClientToolkit(awsConfig);
    const sfnExecution = await sfnClientToolkit.startExecution({
      input: {},
      stateMachineName: 'StateMachineExample',
    });
    expect(sfnExecution?.status).toBe(ExecutionStatus.SUCCEEDED);
    expect(JSON.parse(sfnExecution?.output || '')).toEqual({
      messages: ['Hello from Branch 1', 'Hello from Branch 2'],
    });
  });

  // this one fails. Result {"messages": "Hello from Branch 1"} instead of {"messages": ["Hello from Branch 1"]}
  it('should return array of 1 message', async () => {
    const app = getApp(true);
    const cdkToolkit = new LocalstackCdkToolkit(app);
    await cdkToolkit.deploy();
    const awsConfig = await cdkToolkit?.localstackContainer?.getAwsConfig();
    const sfnClientToolkit = new SfnClientToolkit(awsConfig);
    const sfnExecution = await sfnClientToolkit.startExecution({
      input: {},
      stateMachineName: 'StateMachineExample',
    });
    expect(sfnExecution?.status).toBe(ExecutionStatus.SUCCEEDED);
    expect(JSON.parse(sfnExecution?.output || '')).toEqual({
      messages: ['Hello from Branch 1'],
    });
  });
});

Environment

- OS: macos
- LocalStack:
  LocalStack version: 11.0.0
  LocalStack Docker image sha: sha256:ad4f76a02108f52479a33bbe0de40690d63ef51713971731f21f1de1e4eedb85
  LocalStack build date: 
  LocalStack build git hash:

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions