From aead81d71fcf61d185953801504b9b1b55953de6 Mon Sep 17 00:00:00 2001 From: Alex Wang Date: Fri, 31 Jul 2026 21:37:57 +0000 Subject: [PATCH 1/2] Add plugin terminal-payloads case for requirement 10-14 --- .../java/plugin/PluginTerminalPayloads.java | 74 +++++++++++++++++++ conformance-tests/template_plugin.yaml | 16 ++++ 2 files changed, 90 insertions(+) create mode 100644 conformance-tests/src/main/java/plugin/PluginTerminalPayloads.java diff --git a/conformance-tests/src/main/java/plugin/PluginTerminalPayloads.java b/conformance-tests/src/main/java/plugin/PluginTerminalPayloads.java new file mode 100644 index 000000000..02ad5beb9 --- /dev/null +++ b/conformance-tests/src/main/java/plugin/PluginTerminalPayloads.java @@ -0,0 +1,74 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package plugin; + +import software.amazon.lambda.durable.DurableConfig; +import software.amazon.lambda.durable.DurableContext; +import software.amazon.lambda.durable.DurableHandler; +import software.amazon.lambda.durable.config.StepConfig; +import software.amazon.lambda.durable.plugin.DurableExecutionPlugin; +import software.amazon.lambda.durable.plugin.InvocationInfo; +import software.amazon.lambda.durable.plugin.OperationEndInfo; +import software.amazon.lambda.durable.retry.RetryStrategies; + +/** + * 10-14: Operation-end info carries the checkpointed result on success and the error on failure. + * + *

Step A returns the constant "task-a" and succeeds; step B always throws "boom" with no retries, so the execution + * fails. The plugin, filtering to step-type operations, logs operation-end with the operation's status, its + * checkpointed serialized result, and its error message. + * + *

SDK CAPABILITY GAP (Java): {@link OperationEndInfo} exposes no serialized-result field — its record is + * {@code (id, name, type, subType, parentId, startTimestamp, endTimestamp, status, attempt, isReplay, error)}. The + * result of a successful operation is therefore not available to the hook, so this handler honestly logs + * {@code result: NONE} for step A. The requirement expects {@code result: "task-a"}; that assertion will fail, which + * is the correct signal that the Java plugin API does not surface operation results at the operation-end boundary. + * The error path (step B, {@code error: boom}) is fully supported via {@link OperationEndInfo#error()}. + */ +@SuppressWarnings("deprecation") +public class PluginTerminalPayloads extends DurableHandler { + + @Override + protected DurableConfig createConfiguration() { + return DurableConfig.builder().withPlugins(new PayloadPlugin()).build(); + } + + @Override + public String handleRequest(Object input, DurableContext context) { + context.step("step-a", String.class, stepCtx -> "task-a"); + return context.step( + "step-b", + String.class, + stepCtx -> { + throw new RuntimeException("boom"); + }, + StepConfig.builder() + .retryStrategy(RetryStrategies.Presets.NO_RETRY) + .build()); + } + + private static final class PayloadPlugin implements DurableExecutionPlugin { + private volatile String executionArn; + + @Override + public void onInvocationStart(InvocationInfo info) { + this.executionArn = info.durableExecutionArn(); + } + + @Override + public void onOperationEnd(OperationEndInfo info) { + if (!PluginSupport.isStep(info.type())) { + return; + } + // OperationEndInfo has no serialized-result accessor in the Java SDK; honestly report NONE. + String result = "NONE"; + String error = info.error() != null && info.error().getMessage() != null + ? info.error().getMessage() + : "NONE"; + System.out.println(String.format( + "{\"plugin\": \"CONFPLUGIN\", \"hook\": \"operation-end\", \"op\": \"%s\", \"status\": \"%s\", " + + "\"result\": \"%s\", \"error\": \"%s\"%s}", + info.id(), info.status(), result, error, PluginSupport.arnField(executionArn))); + } + } +} diff --git a/conformance-tests/template_plugin.yaml b/conformance-tests/template_plugin.yaml index ecc8cac17..a2d689b00 100644 --- a/conformance-tests/template_plugin.yaml +++ b/conformance-tests/template_plugin.yaml @@ -304,3 +304,19 @@ Resources: DurableConfig: RetentionPeriodInDays: 7 ExecutionTimeout: 300 + + PluginTerminalPayloads: + Type: AWS::Serverless::Function + TestingMetadata: + TestDescription: ["10-14"] + Properties: + CodeUri: . + Handler: plugin.PluginTerminalPayloads + Description: Operation-end info carries the checkpointed result on success and the error on failure + Role: + Fn::GetAtt: + - DurableFunctionRole + - Arn + DurableConfig: + RetentionPeriodInDays: 7 + ExecutionTimeout: 300 From 4368ef3a76839c994f8af13ad64a9e77df8b3d90 Mon Sep 17 00:00:00 2001 From: Alex Wang Date: Wed, 5 Aug 2026 18:10:23 +0000 Subject: [PATCH 2/2] Apply spotless formatting --- .../src/main/java/plugin/PluginTerminalPayloads.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/conformance-tests/src/main/java/plugin/PluginTerminalPayloads.java b/conformance-tests/src/main/java/plugin/PluginTerminalPayloads.java index 02ad5beb9..b9ce9b1f3 100644 --- a/conformance-tests/src/main/java/plugin/PluginTerminalPayloads.java +++ b/conformance-tests/src/main/java/plugin/PluginTerminalPayloads.java @@ -18,12 +18,12 @@ * fails. The plugin, filtering to step-type operations, logs operation-end with the operation's status, its * checkpointed serialized result, and its error message. * - *

SDK CAPABILITY GAP (Java): {@link OperationEndInfo} exposes no serialized-result field — its record is - * {@code (id, name, type, subType, parentId, startTimestamp, endTimestamp, status, attempt, isReplay, error)}. The - * result of a successful operation is therefore not available to the hook, so this handler honestly logs - * {@code result: NONE} for step A. The requirement expects {@code result: "task-a"}; that assertion will fail, which - * is the correct signal that the Java plugin API does not surface operation results at the operation-end boundary. - * The error path (step B, {@code error: boom}) is fully supported via {@link OperationEndInfo#error()}. + *

SDK CAPABILITY GAP (Java): {@link OperationEndInfo} exposes no serialized-result field — its record is {@code (id, + * name, type, subType, parentId, startTimestamp, endTimestamp, status, attempt, isReplay, error)}. The result of a + * successful operation is therefore not available to the hook, so this handler honestly logs {@code result: NONE} for + * step A. The requirement expects {@code result: "task-a"}; that assertion will fail, which is the correct signal that + * the Java plugin API does not surface operation results at the operation-end boundary. The error path (step B, + * {@code error: boom}) is fully supported via {@link OperationEndInfo#error()}. */ @SuppressWarnings("deprecation") public class PluginTerminalPayloads extends DurableHandler {