feat(pipes): apply EventBridge Pipe enrichment before target delivery - #1658
Conversation
PipesPoller delivered the raw source record straight to the target; pipe.getEnrichment() was stored but never invoked. Implement the AWS Pipes source->filter->ENRICHMENT->target flow: when an enrichment Lambda is configured, invoke it once with the filtered events (bare JSON array) and forward its response to the target (null/empty response skips the target, per AWS). Also resolve qualified Lambda ARNs (:$LATEST) in PipesTargetInvoker. Covered by PipesTargetInvokerTest.
|
| Filename | Overview |
|---|---|
| src/main/java/io/github/hectorvent/floci/services/pipes/PipesPoller.java | Adds deliverEnrichedBatch for SQS-sourced enrichment; correctly gates Lambda-target array-wrapping via isLambdaTarget; documents the known Kinesis/DynamoDB/Kafka bypass in both code and docs. |
| src/main/java/io/github/hectorvent/floci/services/pipes/PipesTargetInvoker.java | Adds applyEnrichment with correct null-skip and FunctionError semantics; fixes qualified-ARN resolution via lambdaFunctionName; surfaces target Lambda FunctionError so the poller routes to DLQ instead of silently consuming the record. |
| src/test/java/io/github/hectorvent/floci/services/pipes/PipesTargetInvokerTest.java | Adds 9 tests covering Lambda invocation, qualified-ARN name extraction, null/empty/object/array skip semantics, unsupported enrichment type, FunctionError handling, and target Lambda FunctionError surfacing. |
| src/test/java/io/github/hectorvent/floci/services/pipes/PipesPollerTest.java | Adds unit tests for asEventArray and two integration-style tests verifying that non-Lambda targets receive the raw enrichment response while Lambda targets receive the array-wrapped form. |
| docs/services/pipes.md | Adds Enrichment section clearly documenting Lambda-only support, null-skip semantics, FunctionError DLQ routing, and the known SQS-only limitation for enrichment. |
Reviews (7): Last reviewed commit: "docs: regenerate action tables" | Re-trigger Greptile
EventBridge Pipes delivers events to a target as a batch (JSON array). The enrichment path forwarded a single-object enrichment response verbatim, so a target expecting an array (e.g. a Step Functions state machine with InputPath "$.[0]") received an object and resolved null. Wrap a non-array enrichment response in a one-element array before target delivery. Covered by PipesPollerTest.asEventArray*. (cherry picked from commit f160f66857c2362e3f1bad3bbfdb025396b281a0)
…delivery invokeLambda (the Lambda target path) discarded the InvokeResult, so a target Lambda that returned a FunctionError (unhandled exception, throttle) was treated as a successful delivery and the source record was consumed. Surface it (throw) so the caller routes the record to the DLQ. Also switch applyEnrichment's FunctionError failure from a bare RuntimeException to AwsException, per the service-layer convention. Covered by PipesTargetInvokerTest.invoke_lambdaTargetFunctionErrorThrows.
deliverEnrichedBatch wrapped the enrichment response in a JSON batch array before invoking the target unconditionally. That is correct for a Lambda target (which expects an SQSRecord[]-style batch) but corrupts every other target — a Step Functions execution, SQS message, SNS publish or EventBridge event must receive the raw enrichment response. Gate the wrapping on isLambdaTarget, matching the non-enrichment delivery path. Adds pollSqs tests for both a non-Lambda (raw) and a Lambda (wrapped) target.
|
Applied the Greptile P1: On the summary's other note — enrichment is still only wired on the SQS poll path; the Kinesis/DynamoDB-Streams/Kafka paths don't yet run enrichment. That's a deliberate scope boundary for this PR (SQS is the common Pipes source); I'll track extending enrichment to the stream sources as a follow-up rather than widen this change. Branch is up to date with |
|
Thanks @abanna, good catch on the enrichment step being parsed but never invoked, and the qualified ARN fix plus the FunctionError to DLQ change are real improvements on their own. Checked the behavior against the AWS Pipes enrichment docs. What lines up with AWS: synchronous Two things worth addressing:
// enrichment returns {} AWS: skip the target
// this PR: forwards it, and for a Lambda target delivers [{}] (explicit invoke with empty payload)
if (resp.isEmpty() || "null".equals(resp)) { ... } // should also treat {} and [] as empty
Smaller note: for the unsupported enrichment types (API destinations, API Gateway, SFN Express are all valid on AWS) the code warns and delivers the unenriched payload anyway. Failing the batch to the DLQ might be safer than silently delivering data the pipe was supposed to enrich. Nice work overall, the test coverage on the batch shaping is thorough. |
…cument SQS-only wiring
Review feedback:
- applyEnrichment now also skips the target when the Lambda enrichment returns an
empty object {} or empty array [] (whitespace variants included), matching AWS;
a non-empty array such as [{}] still invokes the target.
- A non-Lambda enrichment type (API destination, API Gateway, SFN Express) now
fails the batch to the DLQ instead of silently delivering the unenriched payload.
- Documented that enrichment is currently applied only on the SQS source path
(Kinesis/DynamoDB Streams/Kafka bypass it) in code and docs/services/pipes.md.
- Generalized a comment that named a specific downstream consumer.
- Tests: empty {}/[] skip, [{}] invokes, unsupported type throws.
|
Thanks — addressed the enrichment points and merged current 1. Empty-response semantics now match AWS. 2. SQS-only wiring — documented explicitly rather than silently bypassing. Enrichment is applied only on the SQS source path; Kinesis/DynamoDB Streams/Kafka deliver straight to the target. Noted at the shared 3. Unsupported enrichment types now fail to the DLQ. API destinations, API Gateway and SFN Express are valid enrichment types on AWS but not emulated; The three greptile items were already handled in the current revision: Verified (maven, temurin-25): |
Summary
PipesPollerdelivered the raw source record straight to the target;pipe.getEnrichment()was parsed and stored but never invoked. This implements the AWS EventBridge Pipes source → filter → enrichment → target flow:null/empty enrichment response skips the target (per AWS behavior)....:function:NAME:$LATEST) when invoking the enrichment function fromPipesTargetInvoker.Without this, a pipe configured with an enrichment step silently bypassed it, delivering an unenriched payload to the target (Step Functions / SQS / etc.).
Type of change
feat:)AWS Compatibility
EventBridge Pipes enrichment: an enrichment resource (Lambda) is invoked between the filter and the target, receiving the filtered events as a JSON array and whose response becomes the target input; a
null/empty response drops the event. Verified against the AWS Pipes documented behavior and exercised byPipesTargetInvokerTest.Checklist
./mvnw testpasses locally (PipesTargetInvokerTest: 24 tests, 0 failures — run in aneclipse-temurin:25-jdkcontainer)PipesTargetInvokerTest)