Search code examples
amazon-web-servicesdocker-composeaws-api-gatewayaws-step-functionslocalstack

Error Invoking Step Function from API Gateway in LocalStack


I am currently trying to set up the invocation of a Step Function via an API Gateway endpoint in LocalStack. I have both the Step Function and the API Gateway creating successfully however when I try to cUrl the API endpoint I get an error:

{"Type": "User", "message": "Error invoking integration for API Gateway ID '5zpknu8ld7': Expecting value: line 1 column 1 (char 0)", "__type": "InvalidRequest"}

The following is the series of commands in my init script which can be used to recreate this issue (hopefully):

#!/bin/bash
set -x

apt-get install jq -y
source ~/.bashrc

export STEP_FUNCTION_DEFINITION=$(</etc/localstack/init/functions/step-func-def.json)

awslocal lambda create-function \
    --function-name lambda-function \
    --runtime python3.10 \
    --zip-file fileb:///etc/localstack/init/functions/function.zip \
    --handler handler.lambda_handler \
    --role arn:aws:iam::000000000000:role/lambda-role
awslocal stepfunctions create-state-machine \
    --name step-function \
    --definition "$STEP_FUNCTION_DEFINITION" \
    --role-arn arn:aws:iam::000000000000:role/step-function-role

export gateway_repsonse=$(awslocal apigateway create-rest-api --name api-gateway)
export gateway_id=$(echo $gateway_repsonse | jq -r '.id')
export root_resources_response=$(awslocal apigateway get-resources --rest-api-id $gateway_id)
export root_resource_id=$(echo $root_resources_response | jq -r '.items[0].id')
export session_resource_response=$(awslocal apigateway create-resource \
    --rest-api-id $gateway_id \
    --parent-id $root_resource_id \
    --path-part "{sessionId}")
export session_resource_id=$(echo $session_resource_response | jq -r '.id')

awslocal apigateway put-method \
    --rest-api-id $gateway_id \
    --resource-id $session_resource_id \
    --http-method POST \
    --request-parameters "method.request.path.sessionId=true" \
    --authorization-type "NONE"
awslocal apigateway put-integration \
    --rest-api-id $gateway_id \
    --resource-id $session_resource_id \
    --http-method POST \
    --type AWS \
    --integration-http-method POST \
    --uri arn:aws:apigateway:us-east-1:states:action/StartExecution \
    --request-parameters '{"input": "{}","name": "MyExecution","stateMachineArn": "arn:aws:states:us-east-1:000000000000:stateMachine:step-function"}' \
    --passthrough-behavior WHEN_NO_MATCH
awslocal apigateway create-deployment \
    --rest-api-id $gateway_id \
    --stage-name test

set +x

The step function definition is as follows:

{
    "StartAt": "QueryBMM",
    "States": {
        "QueryBMM": {
            "Type": "Task",
            "Resource": "arn:aws:states:::lambda:invoke",
            "Parameters": {
                "Payload.$": "$",
                "FunctionName": "arn:aws:lambda:us-east-1:000000000000:function:step-function-handler"
            },
            "Retry": [
                {
                  "ErrorEquals": [
                    "Lambda.ServiceException",
                    "Lambda.AWSLambdaException",
                    "Lambda.SdkClientException",
                    "Lambda.TooManyRequestsException"
                  ],
                  "IntervalSeconds": 2,
                  "MaxAttempts": 6,
                  "BackoffRate": 2
                }
            ],
            "End": true
        }
    }
}

I tried running:

curl -X POST http://localhost:4566/restapis/<gateway_id>/test/_user_request_/test

and expected that the step function and thus the lambda would be invoked. Instead I get the error listed earlier. Is anybody able to see any issues with this setup?


Solution

  • When you are stuck with issues like this one, I strongly suggest you to look at the tests that Localstack certifies as working. For example, in this case, you could search in the StartExecution tests, there is the test_apigateway_with_step_function_integration test. Then search the test name in the Github repository and you will find its code.


    I had a similar problem in the past, so I have translated that Python test as shell code. Maybe you are missing the --request-templates parameter (?):

    REQUEST_TEMPLATE="$(echo "
         {
             \"application/json\": \"{
                 \\\"input\\\": \\\"\$util.escapeJavaScript(\$input.json('$'))\\\",
                 \\\"stateMachineArn\\\": \\\"$STATE_MACHINE_ARN\\\"
             }\"
         }" | sed -z 's/\n//g' )"
    awslocal apigateway put-integration \
        --rest-api-id "$REST_API_ID" \
        --resource-id "$RESOURCE_ID" \
        --http-method POST \
        --type AWS \
        --integration-http-method POST \
        --uri "$INTEGRATION_URI" \
        --passthrough-behavior WHEN_NO_MATCH \
        --request-templates "$REQUEST_TEMPLATE"