From 6ecf4fb3b64922abec4371d5483324e3bc69dd4f Mon Sep 17 00:00:00 2001 From: Helber Belmiro Date: Fri, 4 Aug 2023 13:54:01 -0300 Subject: [PATCH 1/4] KOGITO-8410 Added support to GET method to Knative custom function Signed-off-by: Helber Belmiro --- .../customfunctions/KnativeTypeHandler.java | 21 +++-- .../main/resources/getKnativeFunction.sw.json | 31 ++++++++ .../it/KnativeServingAddonIT.java | 27 +++++++ .../GetRequestKnativeParamsDecorator.java | 39 +++++++++ .../serving/customfunctions/Operation.java | 54 ++++++++++++- .../GetRequestKnativeParamsDecoratorTest.java | 79 +++++++++++++++++++ .../customfunctions/OperationTest.java | 38 +++++++-- .../customfunctions/WebClientProducer.java | 34 ++++++++ 8 files changed, 307 insertions(+), 16 deletions(-) create mode 100644 quarkus/addons/knative/serving/integration-tests/src/main/resources/getKnativeFunction.sw.json create mode 100644 quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecorator.java create mode 100644 quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecoratorTest.java create mode 100644 quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/WebClientProducer.java diff --git a/quarkus/addons/knative/serving/deployment/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/deployment/customfunctions/KnativeTypeHandler.java b/quarkus/addons/knative/serving/deployment/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/deployment/customfunctions/KnativeTypeHandler.java index 0319463bc40..9ba69dab93b 100644 --- a/quarkus/addons/knative/serving/deployment/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/deployment/customfunctions/KnativeTypeHandler.java +++ b/quarkus/addons/knative/serving/deployment/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/deployment/customfunctions/KnativeTypeHandler.java @@ -24,6 +24,7 @@ import org.jbpm.ruleflow.core.factory.NodeFactory; import org.jbpm.ruleflow.core.factory.WorkItemNodeFactory; import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.CloudEventKnativeParamsDecorator; +import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.GetRequestKnativeParamsDecorator; import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler; import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.Operation; import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.PlainJsonKnativeParamsDecorator; @@ -32,6 +33,7 @@ import org.kie.kogito.serverless.workflow.parser.types.WorkItemTypeHandler; import org.kie.kogito.serverless.workflow.suppliers.ParamsRestBodyBuilderSupplier; import org.kogito.workitem.rest.RestWorkItemHandler; +import org.kogito.workitem.rest.decorators.ParamsDecorator; import com.github.javaparser.ast.expr.Expression; @@ -79,24 +81,29 @@ public class KnativeTypeHandler extends WorkItemTypeHandler { Operation operation = Operation.parse(trimCustomOperation(functionDef)); - if (operation.isCloudEvent()) { - node.workParameter(RestWorkItemHandler.PARAMS_DECORATOR, CloudEventKnativeParamsDecorator.class.getName()); - } else { - node.workParameter(RestWorkItemHandler.PARAMS_DECORATOR, PlainJsonKnativeParamsDecorator.class.getName()); - } - Supplier requestTimeout = runtimeRestApi(functionDef, "timeout", context.getContext(), String.class, DEFAULT_REQUEST_TIMEOUT_VALUE); return node.workParameter(KnativeWorkItemHandler.SERVICE_PROPERTY_NAME, operation.getService()) .workParameter(KnativeWorkItemHandler.PATH_PROPERTY_NAME, operation.getPath()) .workParameter(RestWorkItemHandler.BODY_BUILDER, new ParamsRestBodyBuilderSupplier()) - .workParameter(RestWorkItemHandler.METHOD, HttpMethod.POST) + .workParameter(RestWorkItemHandler.PARAMS_DECORATOR, getParamsDecorator(operation).getName()) + .workParameter(RestWorkItemHandler.METHOD, operation.getHttpMethod()) .workParameter(RestWorkItemHandler.REQUEST_TIMEOUT_IN_MILLIS, requestTimeout) .metaData(TaskDescriptor.KEY_WORKITEM_TYPE, RestWorkItemHandler.REST_TASK_TYPE) .workName(KnativeWorkItemHandler.NAME); } + private static Class getParamsDecorator(Operation operation) { + if (operation.isCloudEvent()) { + return CloudEventKnativeParamsDecorator.class; + } else if (HttpMethod.GET.equals(operation.getHttpMethod())) { + return GetRequestKnativeParamsDecorator.class; + } else { + return PlainJsonKnativeParamsDecorator.class; + } + } + @Override public String type() { return KnativeWorkItemHandler.NAME; diff --git a/quarkus/addons/knative/serving/integration-tests/src/main/resources/getKnativeFunction.sw.json b/quarkus/addons/knative/serving/integration-tests/src/main/resources/getKnativeFunction.sw.json new file mode 100644 index 00000000000..1802240e120 --- /dev/null +++ b/quarkus/addons/knative/serving/integration-tests/src/main/resources/getKnativeFunction.sw.json @@ -0,0 +1,31 @@ +{ + "id": "getKnativeFunction", + "version": "1.0", + "name": "Test Knative function", + "description": "This workflow tests a Knative function", + "start": "invokeFunction", + "functions": [ + { + "name": "greet", + "type": "custom", + "operation": "knative:services.v1.serving.knative.dev/default/serverless-workflow-greeting-quarkus?path=/plainJsonFunction&method=GET" + } + ], + "states": [ + { + "name": "invokeFunction", + "type": "operation", + "actions": [ + { + "functionRef": { + "refName": "greet", + "arguments": { + "name": ".name" + } + } + } + ], + "end": true + } + ] +} diff --git a/quarkus/addons/knative/serving/integration-tests/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/it/KnativeServingAddonIT.java b/quarkus/addons/knative/serving/integration-tests/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/it/KnativeServingAddonIT.java index 60b92485d7d..d360391c6e5 100644 --- a/quarkus/addons/knative/serving/integration-tests/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/it/KnativeServingAddonIT.java +++ b/quarkus/addons/knative/serving/integration-tests/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/it/KnativeServingAddonIT.java @@ -37,6 +37,8 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; @@ -85,6 +87,22 @@ static void afterAll() { } } + @Test + void executeHttpGet() { + mockExecuteHttpGetEndpoint(); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"name\": \"hbelmiro\" }").when() + .post("/getKnativeFunction") + .then() + .statusCode(HttpURLConnection.HTTP_CREATED) + .body("workflowdata.message", is("Hello")); + + wireMockServer.verify(getRequestedFor(urlEqualTo("/plainJsonFunction?name=hbelmiro"))); + } + @Test void executeWithEmptyParameters() { mockExecuteWithEmptyParametersEndpoint(); @@ -306,6 +324,15 @@ private void mockExecuteWithParametersEndpoint() { .put("message", "Hello")))); } + private void mockExecuteHttpGetEndpoint() { + wireMockServer.stubFor(get(urlEqualTo("/plainJsonFunction?name=hbelmiro")) + .willReturn(aResponse() + .withStatus(HttpURLConnection.HTTP_OK) + .withHeader("Content-Type", "application/json") + .withJsonBody(JsonNodeFactory.instance.objectNode() + .put("message", "Hello")))); + } + private void mockExecuteWithArrayEndpoint() { wireMockServer.stubFor(post(urlEqualTo("/arrayFunction")) .withRequestBody(equalToJson(JsonNodeFactory.instance.objectNode() diff --git a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecorator.java b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecorator.java new file mode 100644 index 00000000000..42c456db07f --- /dev/null +++ b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecorator.java @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * 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 org.kie.kogito.addons.quarkus.knative.serving.customfunctions; + +import java.text.MessageFormat; +import java.util.Map; + +import org.kie.kogito.internal.process.runtime.KogitoWorkItem; +import org.kogito.workitem.rest.decorators.ParamsDecorator; + +import io.vertx.mutiny.ext.web.client.HttpRequest; + +public final class GetRequestKnativeParamsDecorator implements ParamsDecorator { + + @Override + public void decorate(KogitoWorkItem workItem, Map parameters, HttpRequest request) { + KnativeFunctionPayloadSupplier.getPayload(parameters).forEach((key, value) -> { + if (value instanceof String) { + request.addQueryParam(key, (String) value); + } else { + String message = "Knative functions support only GET requests with String attributes. {0} has a {1} value."; + throw new IllegalArgumentException(MessageFormat.format(message, key, value.getClass())); + } + }); + } +} diff --git a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/Operation.java b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/Operation.java index 95b1c995382..acbfc13fb12 100644 --- a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/Operation.java +++ b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/Operation.java @@ -15,26 +15,51 @@ */ package org.kie.kogito.addons.quarkus.knative.serving.customfunctions; +import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.Set; + +import io.vertx.core.http.HttpMethod; public final class Operation { + private static final Set SUPPORTED_METHODS = Set.of(HttpMethod.POST, HttpMethod.GET); + + private static final HttpMethod DEFAULT_HTTP_METHOD = HttpMethod.POST; + static final String CLOUD_EVENT_PARAMETER_NAME = "asCloudEvent"; static final String PATH_PARAMETER_NAME = "path"; + static final String METHOD_PARAMETER_NAME = "method"; + private final String service; private final String path; private final boolean isCloudEvent; + private final HttpMethod httpMethod; + private Operation(Builder builder) { this.service = Objects.requireNonNull(builder.service); this.path = builder.path != null ? builder.path : "/"; this.isCloudEvent = builder.isCloudEvent; + this.httpMethod = builder.httpMethod; + validate(this); + } + + private static void validate(Operation operation) { + if (!SUPPORTED_METHODS.contains(operation.getHttpMethod())) { + throw new UnsupportedOperationException( + MessageFormat.format("Knative custom function doesn''t support the {0} HTTP method. Supported methods are: {1}.", operation.getHttpMethod(), SUPPORTED_METHODS)); + } + + if (operation.isCloudEvent() && !operation.getHttpMethod().equals(HttpMethod.POST)) { + throw new UnsupportedOperationException(MessageFormat.format("Knative custom function can only send CloudEvents through POST method. Method used: {0}", operation.getHttpMethod())); + } } public String getService() { @@ -49,6 +74,10 @@ public boolean isCloudEvent() { return isCloudEvent; } + public HttpMethod getHttpMethod() { + return httpMethod; + } + public static Operation parse(String value) { String[] parts = value.split("\\?", 2); @@ -61,8 +90,9 @@ public static Operation parse(String value) { return builder() .withService(parts[0]) - .withPath(params.get("path")) + .withPath(params.get(PATH_PARAMETER_NAME)) .withIsCloudEvent(Boolean.parseBoolean(params.get(CLOUD_EVENT_PARAMETER_NAME))) + .withMethod(HttpMethod.valueOf(params.getOrDefault(METHOD_PARAMETER_NAME, DEFAULT_HTTP_METHOD.name()).toUpperCase())) .build(); } @@ -81,12 +111,23 @@ public boolean equals(Object o) { Operation operation = (Operation) o; return isCloudEvent == operation.isCloudEvent && Objects.equals(service, operation.service) - && Objects.equals(path, operation.path); + && Objects.equals(path, operation.path) + && Objects.equals(httpMethod, operation.httpMethod); + } + + @Override + public String toString() { + return "Operation{" + + "service='" + service + '\'' + + ", path='" + path + '\'' + + ", isCloudEvent=" + isCloudEvent + + ", httpMethod=" + httpMethod + + '}'; } @Override public int hashCode() { - return Objects.hash(service, path, isCloudEvent); + return Objects.hash(service, path, isCloudEvent, httpMethod); } public static class Builder { @@ -97,6 +138,8 @@ public static class Builder { private boolean isCloudEvent; + private HttpMethod httpMethod = DEFAULT_HTTP_METHOD; + private Builder() { } @@ -115,6 +158,11 @@ public Builder withIsCloudEvent(boolean isCloudEvent) { return this; } + public Builder withMethod(HttpMethod httpMethod) { + this.httpMethod = httpMethod; + return this; + } + public Operation build() { return new Operation(this); } diff --git a/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecoratorTest.java b/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecoratorTest.java new file mode 100644 index 00000000000..342f19d6296 --- /dev/null +++ b/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecoratorTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * 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 org.kie.kogito.addons.quarkus.knative.serving.customfunctions; + +import java.util.HashMap; +import java.util.Map; + +import javax.inject.Inject; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.vertx.core.http.HttpMethod; +import io.vertx.mutiny.core.buffer.Buffer; +import io.vertx.mutiny.ext.web.client.HttpRequest; +import io.vertx.mutiny.ext.web.client.WebClient; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler.PAYLOAD_FIELDS_DELIMITER; +import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler.PAYLOAD_FIELDS_PROPERTY_NAME; + +@QuarkusTest +class GetRequestKnativeParamsDecoratorTest { + + @Inject + WebClient webClient; + + final GetRequestKnativeParamsDecorator decorator = new GetRequestKnativeParamsDecorator(); + + @Test + void decorate() { + Map expectedParams = Map.of( + "key1", "value1", + "key2", "value2"); + + HttpRequest request = createRequest(); + + HashMap parameters = new HashMap<>(expectedParams); + parameters.put(PAYLOAD_FIELDS_PROPERTY_NAME, String.join(PAYLOAD_FIELDS_DELIMITER, expectedParams.keySet())); + + decorator.decorate(null, parameters, request); + + assertThat(request.queryParams()).hasSize(2); + expectedParams.forEach((k, v) -> assertThat(request.queryParams().get(k)).isEqualTo(v)); + } + + @Test + void decorateNonStringValuesShouldThrowException() { + Map expectedParams = Map.of( + "key1", "value1", + "key2", new Object()); + + HttpRequest request = createRequest(); + + HashMap parameters = new HashMap<>(expectedParams); + parameters.put(PAYLOAD_FIELDS_PROPERTY_NAME, String.join(PAYLOAD_FIELDS_DELIMITER, expectedParams.keySet())); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> decorator.decorate(null, parameters, request)); + } + + private HttpRequest createRequest() { + return webClient.request(HttpMethod.GET, 8080, "localhost", "/path"); + } +} diff --git a/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/OperationTest.java b/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/OperationTest.java index e39da433f3a..6ac1dc5a5c7 100644 --- a/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/OperationTest.java +++ b/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/OperationTest.java @@ -21,24 +21,43 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import io.vertx.core.http.HttpMethod; + import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.Operation.CLOUD_EVENT_PARAMETER_NAME; +import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.Operation.METHOD_PARAMETER_NAME; import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.Operation.PATH_PARAMETER_NAME; class OperationTest { + public static final String SERVICE = "service"; + public static Stream parseSource() { return Stream.of( - Arguments.of("service", Operation.builder().withService("service").build()), + Arguments.of(SERVICE, Operation.builder().withService(SERVICE).build()), + + Arguments.of("service?", Operation.builder().withService(SERVICE).build()), - Arguments.of("service?", Operation.builder().withService("service").build()), + Arguments.of("service?" + PATH_PARAMETER_NAME + "=/my_path", Operation.builder().withService(SERVICE).withPath("/my_path").build()), - Arguments.of("service?" + PATH_PARAMETER_NAME + "=/my_path", Operation.builder().withService("service").withPath("/my_path").build()), + Arguments.of("service?" + CLOUD_EVENT_PARAMETER_NAME + "=true", Operation.builder().withService(SERVICE).withIsCloudEvent(true).build()), - Arguments.of("service?" + CLOUD_EVENT_PARAMETER_NAME + "=true", Operation.builder().withService("service").withIsCloudEvent(true).build()), + Arguments.of("service?" + METHOD_PARAMETER_NAME + "=GET", Operation.builder().withService(SERVICE).withMethod(HttpMethod.GET).build()), + + Arguments.of("service?" + METHOD_PARAMETER_NAME + "=get", Operation.builder().withService(SERVICE).withMethod(HttpMethod.GET).build()), Arguments.of("service?" + PATH_PARAMETER_NAME + "=/my_path&" + CLOUD_EVENT_PARAMETER_NAME + "=true", - Operation.builder().withService("service").withPath("/my_path").withIsCloudEvent(true).build())); + Operation.builder().withService(SERVICE).withPath("/my_path").withIsCloudEvent(true).build()), + + Arguments.of("service?" + PATH_PARAMETER_NAME + "=/my_path&" + CLOUD_EVENT_PARAMETER_NAME + "=false&" + METHOD_PARAMETER_NAME + "=GET", + Operation.builder().withService(SERVICE).withPath("/my_path").withIsCloudEvent(false).withMethod(HttpMethod.GET).build())); + } + + public static Stream invalidOperationSource() { + return Stream.of( + Arguments.of(Operation.builder().withService(SERVICE).withMethod(HttpMethod.DELETE)), + Arguments.of(Operation.builder().withService(SERVICE).withIsCloudEvent(true).withMethod(HttpMethod.GET))); } @ParameterizedTest @@ -46,4 +65,11 @@ public static Stream parseSource() { void parse(String operationValue, Operation expectedOperation) { assertThat(Operation.parse(operationValue)).isEqualTo(expectedOperation); } -} \ No newline at end of file + + @ParameterizedTest + @MethodSource("invalidOperationSource") + void invalidOperation(Operation.Builder operationBuilder) { + assertThatExceptionOfType(UnsupportedOperationException.class) + .isThrownBy(operationBuilder::build); + } +} diff --git a/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/WebClientProducer.java b/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/WebClientProducer.java new file mode 100644 index 00000000000..71f9225df12 --- /dev/null +++ b/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/WebClientProducer.java @@ -0,0 +1,34 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * 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 org.kie.kogito.addons.quarkus.knative.serving.customfunctions; + +import javax.enterprise.inject.Produces; +import javax.inject.Inject; + +import io.vertx.ext.web.client.WebClientOptions; +import io.vertx.mutiny.core.Vertx; +import io.vertx.mutiny.ext.web.client.WebClient; + +public class WebClientProducer { + + @Inject + Vertx vertx; + + @Produces + WebClient webClient() { + return WebClient.create(vertx, new WebClientOptions()); + } +} From 41c56711f543a6950ef3604b05efce0c4dd872dc Mon Sep 17 00:00:00 2001 From: Helber Belmiro Date: Mon, 14 Aug 2023 12:06:52 -0300 Subject: [PATCH 2/4] KOGITO-8410 Added support for Number and Boolean to GetRequestKnativeParamsDecorator Signed-off-by: Helber Belmiro --- .../GetRequestKnativeParamsDecorator.java | 4 +++- .../GetRequestKnativeParamsDecoratorTest.java | 12 +++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecorator.java b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecorator.java index 42c456db07f..7937e03a03a 100644 --- a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecorator.java +++ b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecorator.java @@ -30,8 +30,10 @@ public void decorate(KogitoWorkItem workItem, Map parameters, Ht KnativeFunctionPayloadSupplier.getPayload(parameters).forEach((key, value) -> { if (value instanceof String) { request.addQueryParam(key, (String) value); + } else if (value instanceof Number || value instanceof Boolean) { + request.addQueryParam(key, String.valueOf(value)); } else { - String message = "Knative functions support only GET requests with String attributes. {0} has a {1} value."; + String message = "Knative functions support only GET requests with String, Number or Boolean attributes. {0} has a {1} value."; throw new IllegalArgumentException(MessageFormat.format(message, key, value.getClass())); } }); diff --git a/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecoratorTest.java b/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecoratorTest.java index 342f19d6296..1b3248be032 100644 --- a/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecoratorTest.java +++ b/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecoratorTest.java @@ -43,9 +43,11 @@ class GetRequestKnativeParamsDecoratorTest { @Test void decorate() { - Map expectedParams = Map.of( + Map expectedParams = Map.of( "key1", "value1", - "key2", "value2"); + "key2", "value2", + "booleanParam", true, + "numberParam", 42); HttpRequest request = createRequest(); @@ -54,12 +56,12 @@ void decorate() { decorator.decorate(null, parameters, request); - assertThat(request.queryParams()).hasSize(2); - expectedParams.forEach((k, v) -> assertThat(request.queryParams().get(k)).isEqualTo(v)); + assertThat(request.queryParams()).hasSize(4); + expectedParams.forEach((k, v) -> assertThat(request.queryParams().get(k)).isEqualTo(String.valueOf(v))); } @Test - void decorateNonStringValuesShouldThrowException() { + void decorateInvalidTypeShouldThrowException() { Map expectedParams = Map.of( "key1", "value1", "key2", new Object()); From 7cdf2973fdf12e5013ef329fe7b28d0c8c6e483c Mon Sep 17 00:00:00 2001 From: Helber Belmiro Date: Wed, 16 Aug 2023 16:08:33 -0300 Subject: [PATCH 3/4] KOGITO-8410 Removed GetRequestKnativeParamsDecorator Signed-off-by: Helber Belmiro --- .../decorators/PrefixParamsDecorator.java | 2 +- .../customfunctions/KnativeTypeHandler.java | 7 +- .../GetRequestKnativeParamsDecorator.java | 41 ---------- .../KnativeWorkItemHandler.java | 16 ++++ .../GetRequestKnativeParamsDecoratorTest.java | 81 ------------------- 5 files changed, 21 insertions(+), 126 deletions(-) delete mode 100644 quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecorator.java delete mode 100644 quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecoratorTest.java diff --git a/kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/PrefixParamsDecorator.java b/kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/PrefixParamsDecorator.java index 6bc6c447c17..3f449962ce9 100644 --- a/kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/PrefixParamsDecorator.java +++ b/kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/PrefixParamsDecorator.java @@ -18,7 +18,7 @@ public class PrefixParamsDecorator extends AbstractParamsDecorator { private static final String HEADER_PREFIX = "HEADER_"; - private static final String QUERY_PREFIX = "QUERY_"; + public static final String QUERY_PREFIX = "QUERY_"; @Override protected boolean isHeaderParameter(String key) { diff --git a/quarkus/addons/knative/serving/deployment/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/deployment/customfunctions/KnativeTypeHandler.java b/quarkus/addons/knative/serving/deployment/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/deployment/customfunctions/KnativeTypeHandler.java index 9ba69dab93b..0d0600b8567 100644 --- a/quarkus/addons/knative/serving/deployment/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/deployment/customfunctions/KnativeTypeHandler.java +++ b/quarkus/addons/knative/serving/deployment/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/deployment/customfunctions/KnativeTypeHandler.java @@ -24,7 +24,6 @@ import org.jbpm.ruleflow.core.factory.NodeFactory; import org.jbpm.ruleflow.core.factory.WorkItemNodeFactory; import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.CloudEventKnativeParamsDecorator; -import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.GetRequestKnativeParamsDecorator; import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler; import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.Operation; import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.PlainJsonKnativeParamsDecorator; @@ -34,6 +33,7 @@ import org.kie.kogito.serverless.workflow.suppliers.ParamsRestBodyBuilderSupplier; import org.kogito.workitem.rest.RestWorkItemHandler; import org.kogito.workitem.rest.decorators.ParamsDecorator; +import org.kogito.workitem.rest.decorators.PrefixParamsDecorator; import com.github.javaparser.ast.expr.Expression; @@ -42,6 +42,7 @@ import io.serverlessworkflow.api.functions.FunctionRef; import io.vertx.core.http.HttpMethod; +import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler.PAYLOAD_FIELDS_DELIMITER; import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler.PAYLOAD_FIELDS_PROPERTY_NAME; import static org.kie.kogito.serverless.workflow.parser.FunctionTypeHandlerFactory.trimCustomOperation; import static org.kie.kogito.serverless.workflow.utils.ServerlessWorkflowUtils.runtimeRestApi; @@ -63,7 +64,7 @@ public class KnativeTypeHandler extends WorkItemTypeHandler { List payloadFields = new ArrayList<>(); functionRef.getArguments().fieldNames().forEachRemaining(payloadFields::add); if (!payloadFields.isEmpty()) { - node.workParameter(PAYLOAD_FIELDS_PROPERTY_NAME, String.join(";", payloadFields)); + node.workParameter(PAYLOAD_FIELDS_PROPERTY_NAME, String.join(PAYLOAD_FIELDS_DELIMITER, payloadFields)); } } @@ -98,7 +99,7 @@ private static Class getParamsDecorator(Operation ope if (operation.isCloudEvent()) { return CloudEventKnativeParamsDecorator.class; } else if (HttpMethod.GET.equals(operation.getHttpMethod())) { - return GetRequestKnativeParamsDecorator.class; + return PrefixParamsDecorator.class; } else { return PlainJsonKnativeParamsDecorator.class; } diff --git a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecorator.java b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecorator.java deleted file mode 100644 index 7937e03a03a..00000000000 --- a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecorator.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2023 Red Hat, Inc. and/or its affiliates. - * - * 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 org.kie.kogito.addons.quarkus.knative.serving.customfunctions; - -import java.text.MessageFormat; -import java.util.Map; - -import org.kie.kogito.internal.process.runtime.KogitoWorkItem; -import org.kogito.workitem.rest.decorators.ParamsDecorator; - -import io.vertx.mutiny.ext.web.client.HttpRequest; - -public final class GetRequestKnativeParamsDecorator implements ParamsDecorator { - - @Override - public void decorate(KogitoWorkItem workItem, Map parameters, HttpRequest request) { - KnativeFunctionPayloadSupplier.getPayload(parameters).forEach((key, value) -> { - if (value instanceof String) { - request.addQueryParam(key, (String) value); - } else if (value instanceof Number || value instanceof Boolean) { - request.addQueryParam(key, String.valueOf(value)); - } else { - String message = "Knative functions support only GET requests with String, Number or Boolean attributes. {0} has a {1} value."; - throw new IllegalArgumentException(MessageFormat.format(message, key, value.getClass())); - } - }); - } -} diff --git a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/KnativeWorkItemHandler.java b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/KnativeWorkItemHandler.java index 696c2685359..b7b50c41bc4 100644 --- a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/KnativeWorkItemHandler.java +++ b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/KnativeWorkItemHandler.java @@ -16,6 +16,7 @@ package org.kie.kogito.addons.quarkus.knative.serving.customfunctions; import java.net.URI; +import java.util.Arrays; import java.util.Map; import org.kie.kogito.addons.k8s.resource.catalog.KubernetesServiceCatalog; @@ -25,9 +26,11 @@ import org.kie.kogito.process.workitem.WorkItemExecutionException; import org.kogito.workitem.rest.RestWorkItemHandler; +import io.vertx.core.http.HttpMethod; import io.vertx.mutiny.ext.web.client.WebClient; import static org.kie.kogito.addons.k8s.resource.catalog.KubernetesProtocol.KNATIVE; +import static org.kogito.workitem.rest.decorators.PrefixParamsDecorator.QUERY_PREFIX; public final class KnativeWorkItemHandler extends RestWorkItemHandler { @@ -58,9 +61,22 @@ public KnativeWorkItemHandler(WebClient httpClient, WebClient httpsClient, Kuber public void executeWorkItem(KogitoWorkItem workItem, KogitoWorkItemManager manager) { Map parameters = workItem.getParameters(); parameters.put(RestWorkItemHandler.URL, getUrl(parameters)); + + if (HttpMethod.GET.name().equals(workItem.getParameters().get(RestWorkItemHandler.METHOD))) { + addQueryParamsPrefix(workItem); + } + super.executeWorkItem(workItem, manager); } + private static void addQueryParamsPrefix(KogitoWorkItem workItem) { + String payloadFields = workItem.getParameters().remove(PAYLOAD_FIELDS_PROPERTY_NAME).toString(); + if (payloadFields != null) { + Arrays.stream(payloadFields.split(PAYLOAD_FIELDS_DELIMITER)) + .forEach(field -> workItem.getParameters().put(QUERY_PREFIX + field, workItem.getParameters().remove(field))); + } + } + private String getUrl(Map parameters) { return getServiceAddress(parameters) + parameters.remove(PATH_PROPERTY_NAME); } diff --git a/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecoratorTest.java b/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecoratorTest.java deleted file mode 100644 index 1b3248be032..00000000000 --- a/quarkus/addons/knative/serving/runtime/src/test/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/GetRequestKnativeParamsDecoratorTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2023 Red Hat, Inc. and/or its affiliates. - * - * 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 org.kie.kogito.addons.quarkus.knative.serving.customfunctions; - -import java.util.HashMap; -import java.util.Map; - -import javax.inject.Inject; - -import org.junit.jupiter.api.Test; - -import io.quarkus.test.junit.QuarkusTest; -import io.vertx.core.http.HttpMethod; -import io.vertx.mutiny.core.buffer.Buffer; -import io.vertx.mutiny.ext.web.client.HttpRequest; -import io.vertx.mutiny.ext.web.client.WebClient; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler.PAYLOAD_FIELDS_DELIMITER; -import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler.PAYLOAD_FIELDS_PROPERTY_NAME; - -@QuarkusTest -class GetRequestKnativeParamsDecoratorTest { - - @Inject - WebClient webClient; - - final GetRequestKnativeParamsDecorator decorator = new GetRequestKnativeParamsDecorator(); - - @Test - void decorate() { - Map expectedParams = Map.of( - "key1", "value1", - "key2", "value2", - "booleanParam", true, - "numberParam", 42); - - HttpRequest request = createRequest(); - - HashMap parameters = new HashMap<>(expectedParams); - parameters.put(PAYLOAD_FIELDS_PROPERTY_NAME, String.join(PAYLOAD_FIELDS_DELIMITER, expectedParams.keySet())); - - decorator.decorate(null, parameters, request); - - assertThat(request.queryParams()).hasSize(4); - expectedParams.forEach((k, v) -> assertThat(request.queryParams().get(k)).isEqualTo(String.valueOf(v))); - } - - @Test - void decorateInvalidTypeShouldThrowException() { - Map expectedParams = Map.of( - "key1", "value1", - "key2", new Object()); - - HttpRequest request = createRequest(); - - HashMap parameters = new HashMap<>(expectedParams); - parameters.put(PAYLOAD_FIELDS_PROPERTY_NAME, String.join(PAYLOAD_FIELDS_DELIMITER, expectedParams.keySet())); - - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> decorator.decorate(null, parameters, request)); - } - - private HttpRequest createRequest() { - return webClient.request(HttpMethod.GET, 8080, "localhost", "/path"); - } -} From 24e5d9d7c197e0d595c618674deb1a109549a749 Mon Sep 17 00:00:00 2001 From: Helber Belmiro Date: Tue, 22 Aug 2023 11:41:52 -0300 Subject: [PATCH 4/4] KOGITO-8410 Replaced PrefixParamsDecorator with CollectionParamsDecorator Signed-off-by: Helber Belmiro --- .../CollectionParamsDecoratorSupplier.java | 0 .../decorators/PrefixParamsDecorator.java | 2 +- .../customfunctions/KnativeTypeHandler.java | 54 ++++++++++--------- .../KnativeFunctionPayloadSupplier.java | 12 ++--- .../KnativeWorkItemHandler.java | 18 ------- 5 files changed, 35 insertions(+), 51 deletions(-) rename kogito-serverless-workflow/{kogito-serverless-workflow-openapi-parser => kogito-serverless-workflow-rest-parser}/src/main/java/org/kie/kogito/serverless/workflow/suppliers/CollectionParamsDecoratorSupplier.java (100%) diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-openapi-parser/src/main/java/org/kie/kogito/serverless/workflow/suppliers/CollectionParamsDecoratorSupplier.java b/kogito-serverless-workflow/kogito-serverless-workflow-rest-parser/src/main/java/org/kie/kogito/serverless/workflow/suppliers/CollectionParamsDecoratorSupplier.java similarity index 100% rename from kogito-serverless-workflow/kogito-serverless-workflow-openapi-parser/src/main/java/org/kie/kogito/serverless/workflow/suppliers/CollectionParamsDecoratorSupplier.java rename to kogito-serverless-workflow/kogito-serverless-workflow-rest-parser/src/main/java/org/kie/kogito/serverless/workflow/suppliers/CollectionParamsDecoratorSupplier.java diff --git a/kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/PrefixParamsDecorator.java b/kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/PrefixParamsDecorator.java index 3f449962ce9..6bc6c447c17 100644 --- a/kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/PrefixParamsDecorator.java +++ b/kogito-workitems/kogito-rest-workitem/src/main/java/org/kogito/workitem/rest/decorators/PrefixParamsDecorator.java @@ -18,7 +18,7 @@ public class PrefixParamsDecorator extends AbstractParamsDecorator { private static final String HEADER_PREFIX = "HEADER_"; - public static final String QUERY_PREFIX = "QUERY_"; + private static final String QUERY_PREFIX = "QUERY_"; @Override protected boolean isHeaderParameter(String key) { diff --git a/quarkus/addons/knative/serving/deployment/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/deployment/customfunctions/KnativeTypeHandler.java b/quarkus/addons/knative/serving/deployment/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/deployment/customfunctions/KnativeTypeHandler.java index 0d0600b8567..b46269a3871 100644 --- a/quarkus/addons/knative/serving/deployment/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/deployment/customfunctions/KnativeTypeHandler.java +++ b/quarkus/addons/knative/serving/deployment/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/deployment/customfunctions/KnativeTypeHandler.java @@ -23,6 +23,7 @@ import org.jbpm.ruleflow.core.RuleFlowNodeContainerFactory; import org.jbpm.ruleflow.core.factory.NodeFactory; import org.jbpm.ruleflow.core.factory.WorkItemNodeFactory; +import org.jetbrains.annotations.NotNull; import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.CloudEventKnativeParamsDecorator; import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler; import org.kie.kogito.addons.quarkus.knative.serving.customfunctions.Operation; @@ -30,10 +31,9 @@ import org.kie.kogito.serverless.workflow.parser.ParserContext; import org.kie.kogito.serverless.workflow.parser.VariableInfo; import org.kie.kogito.serverless.workflow.parser.types.WorkItemTypeHandler; +import org.kie.kogito.serverless.workflow.suppliers.CollectionParamsDecoratorSupplier; import org.kie.kogito.serverless.workflow.suppliers.ParamsRestBodyBuilderSupplier; import org.kogito.workitem.rest.RestWorkItemHandler; -import org.kogito.workitem.rest.decorators.ParamsDecorator; -import org.kogito.workitem.rest.decorators.PrefixParamsDecorator; import com.github.javaparser.ast.expr.Expression; @@ -42,7 +42,6 @@ import io.serverlessworkflow.api.functions.FunctionRef; import io.vertx.core.http.HttpMethod; -import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler.PAYLOAD_FIELDS_DELIMITER; import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler.PAYLOAD_FIELDS_PROPERTY_NAME; import static org.kie.kogito.serverless.workflow.parser.FunctionTypeHandlerFactory.trimCustomOperation; import static org.kie.kogito.serverless.workflow.utils.ServerlessWorkflowUtils.runtimeRestApi; @@ -60,19 +59,42 @@ public class KnativeTypeHandler extends WorkItemTypeHandler { WorkItemNodeFactory node = buildWorkItem(embeddedSubProcess, context, varInfo.getInputVar(), varInfo.getOutputVar()) .name(functionDef.getName()); - if (functionRef.getArguments() != null && !functionRef.getArguments().isEmpty()) { - List payloadFields = new ArrayList<>(); - functionRef.getArguments().fieldNames().forEachRemaining(payloadFields::add); + List payloadFields = getPayloadFields(functionRef); + + Operation operation = Operation.parse(trimCustomOperation(functionDef)); + + if (HttpMethod.GET.equals(operation.getHttpMethod())) { + node.workParameter(RestWorkItemHandler.PARAMS_DECORATOR, new CollectionParamsDecoratorSupplier(List.of(), payloadFields)); + } else { if (!payloadFields.isEmpty()) { - node.workParameter(PAYLOAD_FIELDS_PROPERTY_NAME, String.join(PAYLOAD_FIELDS_DELIMITER, payloadFields)); + node.workParameter(PAYLOAD_FIELDS_PROPERTY_NAME, payloadFields); + } + if (operation.isCloudEvent()) { + node.workParameter(RestWorkItemHandler.PARAMS_DECORATOR, CloudEventKnativeParamsDecorator.class.getName()); + } else { + node.workParameter(RestWorkItemHandler.PARAMS_DECORATOR, PlainJsonKnativeParamsDecorator.class.getName()); } } + node.workParameter(KnativeWorkItemHandler.SERVICE_PROPERTY_NAME, operation.getService()) + .workParameter(KnativeWorkItemHandler.PATH_PROPERTY_NAME, operation.getPath()) + .workParameter(RestWorkItemHandler.METHOD, operation.getHttpMethod()); + return addFunctionArgs(workflow, fillWorkItemHandler(workflow, context, node, functionDef), functionRef); } + @NotNull + private static List getPayloadFields(FunctionRef functionRef) { + List payloadFields = new ArrayList<>(); + + if (functionRef.getArguments() != null && !functionRef.getArguments().isEmpty()) { + functionRef.getArguments().fieldNames().forEachRemaining(payloadFields::add); + } + return payloadFields; + } + @Override protected > WorkItemNodeFactory fillWorkItemHandler( Workflow workflow, ParserContext context, WorkItemNodeFactory node, FunctionDefinition functionDef) { @@ -80,31 +102,15 @@ public class KnativeTypeHandler extends WorkItemTypeHandler { functionDef.getMetadata().forEach(node::metaData); } - Operation operation = Operation.parse(trimCustomOperation(functionDef)); - Supplier requestTimeout = runtimeRestApi(functionDef, "timeout", context.getContext(), String.class, DEFAULT_REQUEST_TIMEOUT_VALUE); - return node.workParameter(KnativeWorkItemHandler.SERVICE_PROPERTY_NAME, operation.getService()) - .workParameter(KnativeWorkItemHandler.PATH_PROPERTY_NAME, operation.getPath()) - .workParameter(RestWorkItemHandler.BODY_BUILDER, new ParamsRestBodyBuilderSupplier()) - .workParameter(RestWorkItemHandler.PARAMS_DECORATOR, getParamsDecorator(operation).getName()) - .workParameter(RestWorkItemHandler.METHOD, operation.getHttpMethod()) + return node.workParameter(RestWorkItemHandler.BODY_BUILDER, new ParamsRestBodyBuilderSupplier()) .workParameter(RestWorkItemHandler.REQUEST_TIMEOUT_IN_MILLIS, requestTimeout) .metaData(TaskDescriptor.KEY_WORKITEM_TYPE, RestWorkItemHandler.REST_TASK_TYPE) .workName(KnativeWorkItemHandler.NAME); } - private static Class getParamsDecorator(Operation operation) { - if (operation.isCloudEvent()) { - return CloudEventKnativeParamsDecorator.class; - } else if (HttpMethod.GET.equals(operation.getHttpMethod())) { - return PrefixParamsDecorator.class; - } else { - return PlainJsonKnativeParamsDecorator.class; - } - } - @Override public String type() { return KnativeWorkItemHandler.NAME; diff --git a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/KnativeFunctionPayloadSupplier.java b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/KnativeFunctionPayloadSupplier.java index a5f2bd5fa9a..bad93914de8 100644 --- a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/KnativeFunctionPayloadSupplier.java +++ b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/KnativeFunctionPayloadSupplier.java @@ -15,13 +15,12 @@ */ package org.kie.kogito.addons.quarkus.knative.serving.customfunctions; -import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Function; import static java.util.stream.Collectors.toMap; -import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler.PAYLOAD_FIELDS_DELIMITER; import static org.kie.kogito.addons.quarkus.knative.serving.customfunctions.KnativeWorkItemHandler.PAYLOAD_FIELDS_PROPERTY_NAME; final class KnativeFunctionPayloadSupplier { @@ -35,11 +34,8 @@ static Map getPayload(Map parameters) { } private static List getPayloadFields(Map parameters) { - String payloadFields = (String) parameters.remove(PAYLOAD_FIELDS_PROPERTY_NAME); - if (payloadFields != null) { - return Arrays.asList(payloadFields.split(PAYLOAD_FIELDS_DELIMITER)); - } else { - return List.of(); - } + @SuppressWarnings("unchecked") + List payloadFields = (List) parameters.remove(PAYLOAD_FIELDS_PROPERTY_NAME); + return Objects.requireNonNullElseGet(payloadFields, List::of); } } diff --git a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/KnativeWorkItemHandler.java b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/KnativeWorkItemHandler.java index b7b50c41bc4..8ae77622bf8 100644 --- a/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/KnativeWorkItemHandler.java +++ b/quarkus/addons/knative/serving/runtime/src/main/java/org/kie/kogito/addons/quarkus/knative/serving/customfunctions/KnativeWorkItemHandler.java @@ -16,7 +16,6 @@ package org.kie.kogito.addons.quarkus.knative.serving.customfunctions; import java.net.URI; -import java.util.Arrays; import java.util.Map; import org.kie.kogito.addons.k8s.resource.catalog.KubernetesServiceCatalog; @@ -26,11 +25,9 @@ import org.kie.kogito.process.workitem.WorkItemExecutionException; import org.kogito.workitem.rest.RestWorkItemHandler; -import io.vertx.core.http.HttpMethod; import io.vertx.mutiny.ext.web.client.WebClient; import static org.kie.kogito.addons.k8s.resource.catalog.KubernetesProtocol.KNATIVE; -import static org.kogito.workitem.rest.decorators.PrefixParamsDecorator.QUERY_PREFIX; public final class KnativeWorkItemHandler extends RestWorkItemHandler { @@ -46,8 +43,6 @@ public final class KnativeWorkItemHandler extends RestWorkItemHandler { public static final String PAYLOAD_FIELDS_PROPERTY_NAME = "knative_function_payload_fields"; - public static final String PAYLOAD_FIELDS_DELIMITER = ";"; - public static final String CLOUDEVENT_SENT_AS_PLAIN_JSON_ERROR_MESSAGE = "A Knative custom function argument cannot be a CloudEvent when the 'asCloudEvent' property are not set to 'true'"; private final KubernetesServiceCatalog kubernetesServiceCatalog; @@ -61,22 +56,9 @@ public KnativeWorkItemHandler(WebClient httpClient, WebClient httpsClient, Kuber public void executeWorkItem(KogitoWorkItem workItem, KogitoWorkItemManager manager) { Map parameters = workItem.getParameters(); parameters.put(RestWorkItemHandler.URL, getUrl(parameters)); - - if (HttpMethod.GET.name().equals(workItem.getParameters().get(RestWorkItemHandler.METHOD))) { - addQueryParamsPrefix(workItem); - } - super.executeWorkItem(workItem, manager); } - private static void addQueryParamsPrefix(KogitoWorkItem workItem) { - String payloadFields = workItem.getParameters().remove(PAYLOAD_FIELDS_PROPERTY_NAME).toString(); - if (payloadFields != null) { - Arrays.stream(payloadFields.split(PAYLOAD_FIELDS_DELIMITER)) - .forEach(field -> workItem.getParameters().put(QUERY_PREFIX + field, workItem.getParameters().remove(field))); - } - } - private String getUrl(Map parameters) { return getServiceAddress(parameters) + parameters.remove(PATH_PROPERTY_NAME); }