fix(stepfunctions): wrap optimized lambda:invoke result in the Invoke envelope - #2165
fix(stepfunctions): wrap optimized lambda:invoke result in the Invoke envelope#2165soreavis wants to merge 1 commit into
Conversation
… envelope A Task using the arn:aws:states:::lambda:invoke integration returned the Lambda function's return value directly as the task result, with no ExecutedVersion, Payload or StatusCode key. Real Step Functions nests the function output inside the Invoke response metadata for this integration, which is why $.Payload and the JSONata $states.result.Payload are the documented ways to read a Lambda result. Against the unwrapped result those references resolved to nothing, the state still succeeded, and the execution finished SUCCEEDED carrying silently-empty data — a pipeline accumulating results across stages looked healthy while producing an empty result. Both the direct-ARN branch and the optimized branch shared one block in invokeResource(), so the parsed function output was returned raw for both. Gate the wrap to the optimized branch: it now returns ExecutedVersion (from the resolved function's version, matching what LambdaService.invoke already reports), Payload (the parsed function output) and StatusCode (from the invoke result). A directly specified function ARN keeps returning only the function output, which is the documented behavior for that form. SdkHttpMetadata and SdkResponseMetadata are deliberately not emitted. Their contents are artifacts of a real HTTPS call to the Lambda service — Date, X-Amzn-Trace-Id, RequestId, Content-Length — and floci invokes in process, so any value it put there would be invented. The .waitForTaskToken flow is unaffected: executeTaskState overwrites the task result with the callback payload, so the envelope is discarded for that pattern, as it should be. Closes floci-io#2110
|
🎉 Thanks for your first pull request to Floci! Your CI checks need a maintainer to approve them before they run. That is GitHub's standard gate on first-time contributors, not a problem with your PR — so if the checks look like they are doing nothing, that is why. Once a maintainer approves, CI and the compatibility suite start automatically. Nothing is needed from you in the meantime. While you wait, a couple of things that make review faster:
Come join us in Slack — it is the fastest way to reach maintainers if you get stuck, or want feedback on an approach before investing more time in it. |
|
| Filename | Overview |
|---|---|
| src/main/java/io/github/hectorvent/floci/services/stepfunctions/AslExecutor.java | Correctly distinguishes optimized Lambda invocation results from direct-ARN results and constructs the expected response envelope. |
| src/test/java/io/github/hectorvent/floci/services/stepfunctions/AslExecutorLambdaInvokeResultTest.java | Provides focused coverage of both result shapes and their JSONPath and JSONata consumers. |
Reviews (1): Last reviewed commit: "fix(stepfunctions): wrap optimized lambd..." | Re-trigger Greptile
Summary
Closes #2110.
A Task using the optimized
arn:aws:states:::lambda:invokeintegration returned the Lambda function's return value directly as the task result, with noExecutedVersion/Payload/StatusCodekeys. Real Step Functions nests the function output inside the Invoke response metadata for this integration, which is why the AWS examples read a Lambda result through$.Payload(JSONPath) or$states.result.Payload(JSONata). Against the unwrapped result those references resolve to nothing, the state still succeeds, and the execution endsSUCCEEDEDcarrying empty data.In
invokeResource()the direct-ARN branch and the optimized branch shared oneif (functionName != null)block, so the output was returned raw for both. This gates the wrap to the optimized branch. A directly specified function ARN still returns only the function output — the documented behavior for that form, and now covered by a regression test.Old task result for
Resource: "arn:aws:states:::lambda:invoke", then new:{"marker":"RET","echo":{"in":1}}{"ExecutedVersion":"$LATEST","Payload":{"marker":"RET","echo":{"in":1}},"StatusCode":200}Note this changes the task result shape for existing users. State machines that adapted to the unwrapped form — reading
$.markerwhere AWS needs$.Payload.marker, or dropping theOutputPath: "$.Payload"that Workflow Studio sets by default — were already incompatible with real AWS and will now behave as they do there. Direct-ARN tasks,.waitForTaskTokentasks and every non-Lambda integration are unchanged.Type of change
fix:)feat:)feat!:orfix!:)AWS Compatibility
Invoke Lambda with Step Functions: "When the
Taskresult is returned, the function output is nested inside a dictionary of metadata", with an example carryingExecutedVersion,Payload,SdkHttpMetadata,SdkResponseMetadataandStatusCode: 200. Same page: "ThePayloadfield of the response is parsed from escaped Json to Json" (soPayloadstays a parsed node, matching the existingreadTree), and for the direct-ARN form, "the task result contains only the function output". The Task state reference reads the optimized integration's result as"Output": "{% $states.result.Payload %}", and Workflow Studio's docs state the defaultOutputPathfor Lambda Invoke states is$.Payload, which "removes the additional metadata" — both only work if the envelope is there. The new tests cover both shapes.Values come from real emulator state:
ExecutedVersionfrom the resolved function's version (the samefn.getVersion()thatLambdaService.invoke()reports since #2026),StatusCodefromInvokeResult,Payloadfrom the existing parse.SdkHttpMetadataandSdkResponseMetadataare left out on purpose. Their contents are artifacts of a real HTTPS call —Date,X-Amzn-Trace-Id,x-amzn-RequestId,Content-Length— and floci invokes in process, so anything it put there would be invented. The documented read paths only touch the three keys emitted here, and a test pins the key set at exactly those three, so adding the blocks later is a deliberate change rather than a drift. Say the word if you want them and I'll add them.Two pre-existing gaps this surfaces rather than fixes:
extractLambdaFunctionNamedrops the qualifier (#1660), so a task usingQualifieror a qualifiedFunctionNamestill invokes$LATESTand will now reportExecutedVersion: "$LATEST"for it; andInvocationTypeis hardcoded toRequestResponse, so anInvocationType: "Event"task reports200where AWS reports202. Both want their own issue — I can file them if you'd like them tracked.Checklist
./mvnw testpasses locally (with pre-existing Docker-dependent failures unrelated to this change — see the test note)New Docker-free
AslExecutorLambdaInvokeResultTest, modeled onAslExecutorCatchTest(mockedLambdaExecutorService+LambdaFunctionStore, driven throughexecuteSync): the envelope and its exact key set, the direct-ARN raw-output guard,ResultSelectorover$.Payload.marker,OutputPath: "$.Payload", and JSONata$states.result.Payload. Reverting theAslExecutorchange with the tests in place fails 4 of the 5, with the direct-ARN guard green on both sides.make docs-checkpasses; no handler action changed.Test note: in the full
./mvnw test(8393 tests) all 27services/stepfunctionsclasses are green. The failures on this machine sit in container-backed subsystems (DocumentDB, Neptune, WebSockets, ECR, API Gateway authorizers) — there is no Docker daemon here, and the same classes fail identically on a cleanmaintree.