From c3f34ebc2ade336ab6c7ca8fd95e368f3a0a57be Mon Sep 17 00:00:00 2001 From: fjtirado Date: Mon, 17 Nov 2025 23:30:31 +0100 Subject: [PATCH] [Fix #984] Fixing node response type Signed-off-by: fjtirado --- .../impl/WorkflowApplication.java | 15 +++--- .../NamedWorkflowAdditionalObject.java | 20 ++++++++ .../executors/http/HttpConverterResolver.java | 45 +++++++++++++++++ .../impl/executors/http/HttpExecutor.java | 34 ++++++------- .../executors/http/HttpModelConverter.java | 9 ++-- .../jackson/JacksonModelConverterFactory.java | 48 +++++++++++++++++++ ...l.additional.NamedWorkflowAdditionalObject | 1 + .../impl/test/HTTPWorkflowDefinitionTest.java | 4 ++ .../callFindByStatusHttp.yaml | 21 ++++++++ 9 files changed, 166 insertions(+), 31 deletions(-) create mode 100644 impl/core/src/main/java/io/serverlessworkflow/impl/additional/NamedWorkflowAdditionalObject.java create mode 100644 impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpConverterResolver.java create mode 100644 impl/jwt-impl/src/main/java/io/serverlessworkflow/impl/executors/http/jackson/JacksonModelConverterFactory.java create mode 100644 impl/jwt-impl/src/main/resources/META-INF/services/io.serverlessworkflow.impl.additional.NamedWorkflowAdditionalObject create mode 100644 impl/test/src/test/resources/workflows-samples/callFindByStatusHttp.yaml diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java index 2cbca0283..c354325c6 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowApplication.java @@ -19,6 +19,7 @@ import io.serverlessworkflow.api.types.SchemaInline; import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.impl.additional.NamedWorkflowAdditionalObject; import io.serverlessworkflow.impl.additional.WorkflowAdditionalObject; import io.serverlessworkflow.impl.config.ConfigManager; import io.serverlessworkflow.impl.config.ConfigSecretManager; @@ -172,13 +173,16 @@ public SchemaValidator getValidator(SchemaInline inline) { () -> new RuntimeDescriptor("reference impl", "1.0.0_alpha", Collections.emptyMap()); private boolean lifeCycleCEPublishingEnabled = true; private WorkflowModelFactory modelFactory; - private Map> additionalObjects; + private Map> additionalObjects = new HashMap<>(); private SecretManager secretManager; private ConfigManager configManager; private SchedulerListener schedulerListener; private Optional templateResolver; - private Builder() {} + private Builder() { + ServiceLoader.load(NamedWorkflowAdditionalObject.class) + .forEach(a -> additionalObjects.put(a.name(), a)); + } public Builder withListener(WorkflowExecutionListener listener) { listeners.add(listener); @@ -257,9 +261,6 @@ public Builder withConfigManager(ConfigManager configManager) { public Builder withAdditionalObject( String name, WorkflowAdditionalObject additionalObject) { - if (additionalObjects == null) { - additionalObjects = new HashMap<>(); - } additionalObjects.put(name, additionalObject); return this; } @@ -314,9 +315,7 @@ public WorkflowApplication build() { } schedulerListener = new SchedulerListener(scheduler); listeners.add(schedulerListener); - if (additionalObjects == null) { - additionalObjects = Collections.emptyMap(); - } + if (configManager == null) { configManager = ServiceLoader.load(ConfigManager.class) diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/additional/NamedWorkflowAdditionalObject.java b/impl/core/src/main/java/io/serverlessworkflow/impl/additional/NamedWorkflowAdditionalObject.java new file mode 100644 index 000000000..7991bade5 --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/additional/NamedWorkflowAdditionalObject.java @@ -0,0 +1,20 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.impl.additional; + +public interface NamedWorkflowAdditionalObject extends WorkflowAdditionalObject { + String name(); +} diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpConverterResolver.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpConverterResolver.java new file mode 100644 index 000000000..df80d122f --- /dev/null +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpConverterResolver.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.impl.executors.http; + +import io.serverlessworkflow.impl.TaskContext; +import io.serverlessworkflow.impl.WorkflowContext; + +public class HttpConverterResolver { + + public static final String HTTP_MODEL_CONVERTER = "httpModelConverter"; + + private static class DefaultHolder { + private static final HttpModelConverter converter = + new HttpModelConverter() { + @Override + public Class responseType() { + return Object.class; + } + }; + } + + public static HttpModelConverter converter( + WorkflowContext workflowContext, TaskContext taskContext) { + return workflowContext + .definition() + .application() + .additionalObject(HTTP_MODEL_CONVERTER, workflowContext, taskContext) + .orElseGet(() -> DefaultHolder.converter); + } + + private HttpConverterResolver() {} +} diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutor.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutor.java index 8cbd23c7b..3a9c81448 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutor.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutor.java @@ -45,9 +45,6 @@ public class HttpExecutor implements CallableTask { - // TODO allow changing default converter - private static final HttpModelConverter defaultConverter = new HttpModelConverter() {}; - private WorkflowValueResolver targetSupplier; private Optional>> headersMap; private Optional>> queryMap; @@ -124,8 +121,7 @@ public HttpExecutor build(WorkflowValueResolver uriSupplier) { ? getTargetSupplier(uriSupplier) : getTargetSupplier(uriSupplier, pathSupplier); executor.authProvider = AuthProviderFactory.getAuth(definition, authPolicy); - executor.requestFunction = - buildRequestSupplier(method, body, definition.application(), defaultConverter); + executor.requestFunction = buildRequestSupplier(method, body, definition.application()); executor.headersMap = Optional.ofNullable(headersMap); executor.queryMap = Optional.ofNullable(queryMap); return executor; @@ -176,27 +172,31 @@ public void init(CallHTTP task, WorkflowDefinition definition) { : Optional.empty(); this.requestFunction = buildRequestSupplier( - httpArgs.getMethod().toUpperCase(), - httpArgs.getBody(), - definition.application(), - defaultConverter); + httpArgs.getMethod().toUpperCase(), httpArgs.getBody(), definition.application()); } private static RequestSupplier buildRequestSupplier( - String method, Object body, WorkflowApplication application, HttpModelConverter converter) { + String method, Object body, WorkflowApplication application) { + switch (method.toUpperCase()) { case HttpMethod.POST: WorkflowFilter bodyFilter = WorkflowUtils.buildWorkflowFilter(application, body); - return (request, w, context, node) -> - converter.toModel( - application.modelFactory(), - node, - request.post( - converter.toEntity(bodyFilter.apply(w, context, node)), node.objectClass())); + return (request, w, t, node) -> { + HttpModelConverter converter = HttpConverterResolver.converter(w, t); + return w.definition() + .application() + .modelFactory() + .fromAny( + request.post( + converter.toEntity(bodyFilter.apply(w, t, node)), converter.responseType())); + }; case HttpMethod.GET: default: return (request, w, t, n) -> - converter.toModel(application.modelFactory(), n, request.get(n.objectClass())); + w.definition() + .application() + .modelFactory() + .fromAny(request.get(HttpConverterResolver.converter(w, t).responseType())); } } diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpModelConverter.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpModelConverter.java index 42155ac1e..d5bdded7a 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpModelConverter.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpModelConverter.java @@ -16,16 +16,13 @@ package io.serverlessworkflow.impl.executors.http; import io.serverlessworkflow.impl.WorkflowModel; -import io.serverlessworkflow.impl.WorkflowModelFactory; import jakarta.ws.rs.client.Entity; public interface HttpModelConverter { - default WorkflowModel toModel(WorkflowModelFactory factory, WorkflowModel model, Object entity) { - return factory.fromAny(model, entity); - } - - default Entity toEntity(WorkflowModel model) { + default Entity toEntity(WorkflowModel model) { return Entity.json(model.as(model.objectClass()).orElseThrow()); } + + Class responseType(); } diff --git a/impl/jwt-impl/src/main/java/io/serverlessworkflow/impl/executors/http/jackson/JacksonModelConverterFactory.java b/impl/jwt-impl/src/main/java/io/serverlessworkflow/impl/executors/http/jackson/JacksonModelConverterFactory.java new file mode 100644 index 000000000..e5909d2f9 --- /dev/null +++ b/impl/jwt-impl/src/main/java/io/serverlessworkflow/impl/executors/http/jackson/JacksonModelConverterFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright 2020-Present The Serverless Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.serverlessworkflow.impl.executors.http.jackson; + +import com.fasterxml.jackson.databind.JsonNode; +import io.serverlessworkflow.impl.TaskContextData; +import io.serverlessworkflow.impl.WorkflowContextData; +import io.serverlessworkflow.impl.additional.NamedWorkflowAdditionalObject; +import io.serverlessworkflow.impl.executors.http.HttpConverterResolver; +import io.serverlessworkflow.impl.executors.http.HttpModelConverter; + +public class JacksonModelConverterFactory + implements NamedWorkflowAdditionalObject { + + private static class JacksonModelConverterHolder { + + private static HttpModelConverter converter = + new HttpModelConverter() { + @Override + public Class responseType() { + return JsonNode.class; + } + }; + } + + @Override + public HttpModelConverter apply(WorkflowContextData t, TaskContextData u) { + return JacksonModelConverterHolder.converter; + } + + @Override + public String name() { + return HttpConverterResolver.HTTP_MODEL_CONVERTER; + } +} diff --git a/impl/jwt-impl/src/main/resources/META-INF/services/io.serverlessworkflow.impl.additional.NamedWorkflowAdditionalObject b/impl/jwt-impl/src/main/resources/META-INF/services/io.serverlessworkflow.impl.additional.NamedWorkflowAdditionalObject new file mode 100644 index 000000000..9f3e874a8 --- /dev/null +++ b/impl/jwt-impl/src/main/resources/META-INF/services/io.serverlessworkflow.impl.additional.NamedWorkflowAdditionalObject @@ -0,0 +1 @@ +io.serverlessworkflow.impl.executors.http.jackson.JacksonModelConverterFactory \ No newline at end of file diff --git a/impl/test/src/test/java/io/serverlessworkflow/impl/test/HTTPWorkflowDefinitionTest.java b/impl/test/src/test/java/io/serverlessworkflow/impl/test/HTTPWorkflowDefinitionTest.java index ab95b236f..360f691c1 100644 --- a/impl/test/src/test/java/io/serverlessworkflow/impl/test/HTTPWorkflowDefinitionTest.java +++ b/impl/test/src/test/java/io/serverlessworkflow/impl/test/HTTPWorkflowDefinitionTest.java @@ -106,6 +106,10 @@ private static Stream provideParameters() { "workflows-samples/call-http-endpoint-interpolation.yaml", petInput, petCondition), Arguments.of( "workflows-samples/call-http-query-parameters.yaml", starTrekInput, starTrekCondition), + Arguments.of( + "workflows-samples/callFindByStatusHttp.yaml", + Map.of(), + new Condition(o -> !o.asCollection().isEmpty(), "HasElementCondition")), Arguments.of( "workflows-samples/call-http-query-parameters-external-schema.yaml", starTrekInput, diff --git a/impl/test/src/test/resources/workflows-samples/callFindByStatusHttp.yaml b/impl/test/src/test/resources/workflows-samples/callFindByStatusHttp.yaml new file mode 100644 index 000000000..6adeed8c7 --- /dev/null +++ b/impl/test/src/test/resources/workflows-samples/callFindByStatusHttp.yaml @@ -0,0 +1,21 @@ +document: + dsl: 1.0.0-alpha1 + namespace: test + name: http-call-find-by-status + version: 1.0.0 +do: + - tryGetPet: + try: + - getPet: + call: http + with: + headers: + content-type: application/json + method: get + endpoint: + uri: https://petstore.swagger.io/v2/pet/findByStatus?status=sold + catch: + errors: + with: + type: https://serverlessworkflow.io/spec/1.0.0/errors/communication + status: 404 \ No newline at end of file