diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/AuthProvider.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/AuthProvider.java index 638d0428..273d9820 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/AuthProvider.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/AuthProvider.java @@ -18,10 +18,12 @@ import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowModel; +import java.net.URI; public interface AuthProvider { - String authScheme(); + String scheme(); - String authParameter(WorkflowContext workflow, TaskContext task, WorkflowModel model); + String content( + WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri, String method); } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/AuthProviderFactory.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/AuthProviderFactory.java index fb28f43b..da350153 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/AuthProviderFactory.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/AuthProviderFactory.java @@ -70,8 +70,10 @@ private static Optional buildFromPolicy( new BearerAuthProvider( app, workflow, authenticationPolicy.getBearerAuthenticationPolicy())); } else if (authenticationPolicy.getDigestAuthenticationPolicy() != null) { - // TODO implement digest authentication - return Optional.empty(); + // + return Optional.of( + new DigestAuthProvider( + app, workflow, authenticationPolicy.getDigestAuthenticationPolicy())); } else if (authenticationPolicy.getOAuth2AuthenticationPolicy() != null) { return Optional.of( new OAuth2AuthProvider( diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/BasicAuthProvider.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/BasicAuthProvider.java index b866b181..efae0ded 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/BasicAuthProvider.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/BasicAuthProvider.java @@ -28,6 +28,7 @@ import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowUtils; import io.serverlessworkflow.impl.WorkflowValueResolver; +import java.net.URI; import java.util.Base64; class BasicAuthProvider implements AuthProvider { @@ -57,7 +58,8 @@ public BasicAuthProvider( } @Override - public String authParameter(WorkflowContext workflow, TaskContext task, WorkflowModel model) { + public String content( + WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri, String method) { return new String( Base64.getEncoder() .encode( @@ -69,7 +71,7 @@ public String authParameter(WorkflowContext workflow, TaskContext task, Workflow } @Override - public String authScheme() { + public String scheme() { return "Basic"; } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/BearerAuthProvider.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/BearerAuthProvider.java index c77cf63d..bab7c942 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/BearerAuthProvider.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/BearerAuthProvider.java @@ -28,6 +28,7 @@ import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowUtils; import io.serverlessworkflow.impl.WorkflowValueResolver; +import java.net.URI; class BearerAuthProvider implements AuthProvider { @@ -48,12 +49,13 @@ public BearerAuthProvider( } @Override - public String authParameter(WorkflowContext workflow, TaskContext task, WorkflowModel model) { + public String content( + WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri, String method) { return tokenFilter.apply(workflow, task, model); } @Override - public String authScheme() { + public String scheme() { return "Bearer"; } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/CommonOAuthProvider.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/CommonOAuthProvider.java index bb97c8df..624f99d9 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/CommonOAuthProvider.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/CommonOAuthProvider.java @@ -25,6 +25,7 @@ import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowValueResolver; +import java.net.URI; import java.util.Arrays; import java.util.Map; import java.util.ServiceLoader; @@ -48,12 +49,13 @@ protected CommonOAuthProvider(WorkflowValueResolver tokenPr } @Override - public String authParameter(WorkflowContext workflow, TaskContext task, WorkflowModel model) { + public String content( + WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri, String method) { return tokenProvider.apply(workflow, task, model).validateAndGet(workflow, task, model).token(); } @Override - public String authScheme() { + public String scheme() { return "Bearer"; } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/auth/DigestAuthProvider.java b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/DigestAuthProvider.java new file mode 100644 index 00000000..3701ff94 --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/auth/DigestAuthProvider.java @@ -0,0 +1,231 @@ +/* + * 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.auth; + +import static io.serverlessworkflow.impl.WorkflowUtils.checkSecret; +import static io.serverlessworkflow.impl.WorkflowUtils.secretProp; +import static io.serverlessworkflow.impl.auth.AuthUtils.PASSWORD; +import static io.serverlessworkflow.impl.auth.AuthUtils.USER; + +import io.serverlessworkflow.api.types.DigestAuthenticationPolicy; +import io.serverlessworkflow.api.types.DigestAuthenticationProperties; +import io.serverlessworkflow.api.types.Workflow; +import io.serverlessworkflow.impl.TaskContext; +import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowContext; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowUtils; +import io.serverlessworkflow.impl.WorkflowValueResolver; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.StringTokenizer; +import java.util.concurrent.atomic.AtomicInteger; + +class DigestAuthProvider implements AuthProvider { + + private static final String NONCE = "nonce"; + private static final String REALM = "realm"; + private static final String QOP_KEY = "qop"; + private static final String OPAQUE = "opaque"; + + private static class DigestServerInfo { + + private Algorithm algorithm = Algorithm.MD5; + private String nonce; + private String opaque; + private String realm; + private QOP qop = null; + + public static DigestServerInfo from(String header) { + DigestServerInfo serverInfo = new DigestServerInfo(); + StringTokenizer tokenizer = new StringTokenizer(header); + while (tokenizer.hasMoreElements()) { + String token = tokenizer.nextToken(); + + int indexOf = token.indexOf("="); + if (indexOf != -1) { + String key = token.substring(0, indexOf).trim().toLowerCase(); + String value = token.substring(indexOf + 1).trim(); + switch (key) { + case "algorithm": + serverInfo.algorithm = Algorithm.valueOf(value.toUpperCase()); + break; + case NONCE: + serverInfo.nonce = value; + break; + case OPAQUE: + serverInfo.opaque = value; + break; + case REALM: + serverInfo.realm = value; + break; + case QOP_KEY: + serverInfo.qop = QOP.valueOf(value.toUpperCase()); + break; + } + } + } + return serverInfo; + } + } + + private static enum Algorithm { + MD5, + MD5SESSS + }; + + private static enum QOP { + AUTH, + AUTH_INT, + }; + + private final WorkflowValueResolver userFilter; + private final WorkflowValueResolver passwordFilter; + + public DigestAuthProvider( + WorkflowApplication app, Workflow workflow, DigestAuthenticationPolicy authPolicy) { + DigestAuthenticationProperties properties = + authPolicy.getDigest().getDigestAuthenticationProperties(); + if (properties != null) { + userFilter = WorkflowUtils.buildStringFilter(app, properties.getUsername()); + passwordFilter = WorkflowUtils.buildStringFilter(app, properties.getPassword()); + } else if (authPolicy.getDigest().getDigestAuthenticationPolicySecret() != null) { + String secretName = + checkSecret(workflow, authPolicy.getDigest().getDigestAuthenticationPolicySecret()); + userFilter = (w, t, m) -> secretProp(w, secretName, USER); + passwordFilter = (w, t, m) -> secretProp(w, secretName, PASSWORD); + } else { + throw new IllegalStateException( + "Both secret and properties are null for digest authorization"); + } + } + + @Override + public String scheme() { + return "Digest"; + } + + @Override + public String content( + WorkflowContext workflow, TaskContext task, WorkflowModel model, URI uri, String method) { + try { + HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection(); + connection.setRequestMethod(method); + int responseCode = connection.getResponseCode(); + if (responseCode == 401) { + DigestServerInfo serverInfo = + DigestServerInfo.from(connection.getHeaderField("WWW-Authenticate")); + String userName = userFilter.apply(workflow, task, model); + String path = uri.getPath(); + String ha1 = + calculateHash(userName, serverInfo.realm, passwordFilter.apply(workflow, task, model)); + + String nonceCount; + String clientNonce; + if (serverInfo.qop == QOP.AUTH + || serverInfo.qop == QOP.AUTH_INT + || serverInfo.algorithm == Algorithm.MD5SESSS) { + nonceCount = Integer.toString(nc.getAndIncrement()); + clientNonce = getClientNonce(nonceCount); + } else { + nonceCount = null; + clientNonce = null; + } + String response; + if (serverInfo.algorithm == Algorithm.MD5SESSS) { + ha1 = calculateHash(ha1, serverInfo.nonce, clientNonce); + } + String ha2 = calculateHash(String.format("%s:%s", method, uri)); + if (serverInfo.qop == QOP.AUTH || serverInfo.qop == QOP.AUTH_INT) { + response = + calculateHash( + ha1, + serverInfo.nonce, + nonceCount, + clientNonce, + serverInfo.qop.toString().toLowerCase(), + ha2); + } else { + response = calculateHash(ha1, serverInfo.nonce, ha2); + } + + return buildResponseInfo(serverInfo, userName, path, clientNonce, nonceCount, response); + } else { + throw new IllegalStateException( + "URI " + + uri + + " is not digest protected, it returned code " + + responseCode + + " when invoked without authentication header, but it should have returned 401 as per RFC 2617"); + } + } catch (IOException io) { + throw new UncheckedIOException(io); + } + } + + private String buildResponseInfo( + DigestServerInfo digestInfo, + String userName, + String uri, + String clientNonce, + String nonceCount, + String response) { + StringBuilder sb = new StringBuilder("username=" + userName); + addHeader(sb, "uri", uri); + addHeader(sb, "response", response); + addHeader(sb, NONCE, digestInfo.nonce); + addHeader(sb, REALM, digestInfo.realm); + if (digestInfo.opaque != null) { + addHeader(sb, OPAQUE, digestInfo.opaque); + } + if (digestInfo.qop != null) { + addHeader(sb, QOP_KEY, digestInfo.qop.toString()); + } + if (clientNonce != null) { + addHeader(sb, "cnonce", clientNonce); + addHeader(sb, "nc", nonceCount); + } + return sb.toString(); + } + + private StringBuilder addHeader(StringBuilder sb, String key, String value) { + return sb.append(',').append(key).append('=').append(value); + } + + private static AtomicInteger nc = new AtomicInteger(1); + + private static String getClientNonce(String nonceCount) { + return "impl-" + nonceCount; + } + + private String calculateHash(String firstOne, String... strs) { + try { + + MessageDigest md = MessageDigest.getInstance("MD5"); + StringBuilder sb = new StringBuilder(firstOne); + for (String str : strs) { + sb.append(':').append(str); + } + return new String(md.digest(sb.toString().getBytes())); + } catch (NoSuchAlgorithmException ex) { + throw new UnsupportedOperationException("System is not supporting MD5!!!!", ex); + } + } +} diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/resources/ResourceLoader.java b/impl/core/src/main/java/io/serverlessworkflow/impl/resources/ResourceLoader.java index e3854e12..b1e688d6 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/resources/ResourceLoader.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/resources/ResourceLoader.java @@ -99,20 +99,22 @@ public T load( WorkflowContext workflowContext, TaskContext taskContext, WorkflowModel model) { - return loadURI( + URI uri = uriSupplier(endPoint) .apply( workflowContext, taskContext, - model == null ? application.modelFactory().fromNull() : model), + model == null ? application.modelFactory().fromNull() : model); + return loadURI( + uri, function, AuthProviderFactory.getAuth( workflowContext.definition(), endPoint.getEndpointConfiguration()) .map( auth -> AuthUtils.authHeaderValue( - auth.authScheme(), - auth.authParameter(workflowContext, taskContext, model)))); + auth.scheme(), + auth.content(workflowContext, taskContext, model, uri, "GET")))); } public T loadURI(URI uri, Function function) { diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/AbstractRequestSupplier.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/AbstractRequestSupplier.java index cbcbc867..b7a6baff 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/AbstractRequestSupplier.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/AbstractRequestSupplier.java @@ -23,23 +23,31 @@ import io.serverlessworkflow.impl.WorkflowError; import io.serverlessworkflow.impl.WorkflowException; import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.auth.AuthProvider; +import io.serverlessworkflow.impl.auth.AuthUtils; import jakarta.ws.rs.client.Invocation.Builder; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status.Family; +import java.net.URI; +import java.util.Optional; abstract class AbstractRequestSupplier implements RequestSupplier { private final boolean redirect; + private final Optional authProvider; + protected final String method; - public AbstractRequestSupplier(boolean redirect) { + public AbstractRequestSupplier(String method, boolean redirect, Optional auth) { this.redirect = redirect; + this.method = method; + this.authProvider = auth; } @Override public WorkflowModel apply( - Builder request, WorkflowContext workflow, TaskContext task, WorkflowModel model) { + Builder request, URI uri, WorkflowContext workflow, TaskContext task, WorkflowModel model) { HttpModelConverter converter = HttpConverterResolver.converter(workflow, task); - + authProvider.ifPresent(auth -> addAuthHeader(auth, uri, request, workflow, task, model)); Response response = invokeRequest(request, converter, workflow, task, model); validateStatus(task, response, converter); return workflow @@ -66,4 +74,17 @@ protected abstract Response invokeRequest( WorkflowContext workflow, TaskContext task, WorkflowModel model); + + private void addAuthHeader( + AuthProvider auth, + URI uri, + Builder request, + WorkflowContext workflow, + TaskContext task, + WorkflowModel model) { + String scheme = auth.scheme(); + String parameter = auth.content(workflow, task, model, uri, method); + task.authorization(scheme, parameter); + request.header(AuthUtils.AUTH_HEADER_NAME, AuthUtils.authHeaderValue(scheme, parameter)); + } } diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/CallableTaskHttpExecutorBuilder.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/CallableTaskHttpExecutorBuilder.java index 848bfe93..a5b3eb04 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/CallableTaskHttpExecutorBuilder.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/CallableTaskHttpExecutorBuilder.java @@ -23,7 +23,6 @@ import io.serverlessworkflow.api.types.TaskBase; import io.serverlessworkflow.impl.WorkflowDefinition; import io.serverlessworkflow.impl.WorkflowMutablePosition; -import io.serverlessworkflow.impl.WorkflowUtils; import io.serverlessworkflow.impl.WorkflowValueResolver; import io.serverlessworkflow.impl.executors.CallableTask; import io.serverlessworkflow.impl.executors.CallableTaskBuilder; @@ -70,8 +69,6 @@ public void init(CallHTTP task, WorkflowDefinition definition, WorkflowMutablePo builder.withBody(httpArgs.getBody()); builder.withMethod(httpArgs.getMethod().toUpperCase()); builder.redirect(httpArgs.isRedirect()); - builder.timeout( - WorkflowUtils.getTaskTimeout(definition.application(), definition.workflow(), task)); } @Override diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpClientResolver.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpClientResolver.java index e161008d..fc40b257 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpClientResolver.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpClientResolver.java @@ -20,8 +20,6 @@ import io.serverlessworkflow.impl.WorkflowContext; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; -import java.time.Duration; -import java.util.Optional; public class HttpClientResolver { @@ -32,14 +30,6 @@ private static class DefaultHolder { } public static Client client(WorkflowContext workflowContext, TaskContext taskContext) { - return client(workflowContext, taskContext, false, Optional.empty()); - } - - public static Client client( - WorkflowContext workflowContext, - TaskContext taskContext, - boolean redirect, - Optional timeout) { WorkflowApplication appl = workflowContext.definition().application(); return appl.additionalObject(HTTP_CLIENT_PROVIDER, workflowContext, taskContext) .orElseGet(() -> DefaultHolder.client); 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 825a61f7..ba2ec8ef 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 @@ -18,82 +18,59 @@ import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowUtils; import io.serverlessworkflow.impl.WorkflowValueResolver; -import io.serverlessworkflow.impl.auth.AuthProvider; -import io.serverlessworkflow.impl.auth.AuthUtils; import io.serverlessworkflow.impl.executors.CallableTask; import jakarta.ws.rs.client.Invocation.Builder; import jakarta.ws.rs.client.WebTarget; +import java.net.URI; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.function.Supplier; public class HttpExecutor implements CallableTask { - private final WorkflowValueResolver targetSupplier; + private final WorkflowValueResolver uriSupplier; + private final Optional> pathSupplier; private final Optional>> headersMap; private final Optional>> queryMap; - private final Optional authProvider; private final RequestSupplier requestFunction; HttpExecutor( - WorkflowValueResolver targetSupplier, + WorkflowValueResolver uriSupplier, Optional>> headersMap, Optional>> queryMap, - Optional authProvider, - RequestSupplier requestFunction) { - this.targetSupplier = targetSupplier; + RequestSupplier requestFunction, + Optional> pathSupplier) { + this.uriSupplier = uriSupplier; this.headersMap = headersMap; this.queryMap = queryMap; - this.authProvider = authProvider; this.requestFunction = requestFunction; - } - - private static class TargetQuerySupplier implements Supplier { - - private WebTarget target; - - public TargetQuerySupplier(WebTarget original) { - this.target = original; - } - - public void addQuery(String key, Object value) { - target = target.queryParam(key, value); - } - - public WebTarget get() { - return target; - } + this.pathSupplier = pathSupplier; } public CompletableFuture apply( WorkflowContext workflow, TaskContext taskContext, WorkflowModel input) { - TargetQuerySupplier supplier = - new TargetQuerySupplier(targetSupplier.apply(workflow, taskContext, input)); - queryMap.ifPresent( - q -> q.apply(workflow, taskContext, input).forEach((k, v) -> supplier.addQuery(k, v))); - Builder request = supplier.get().request(); + URI uri = + pathSupplier + .map( + p -> + WorkflowUtils.concatURI( + uriSupplier.apply(workflow, taskContext, input), + p.apply(workflow, taskContext, input))) + .orElse(uriSupplier.apply(workflow, taskContext, input)); + + WebTarget target = HttpClientResolver.client(workflow, taskContext).target(uri); + for (Entry entry : + queryMap.map(q -> q.apply(workflow, taskContext, input)).orElse(Map.of()).entrySet()) { + target = target.queryParam(entry.getKey(), entry.getValue()); + } + Builder request = target.request(); headersMap.ifPresent( h -> h.apply(workflow, taskContext, input).forEach((k, v) -> request.header(k, v))); return CompletableFuture.supplyAsync( - () -> { - authProvider.ifPresent( - auth -> addAuthHeader(auth, request, workflow, taskContext, input)); - return requestFunction.apply(request, workflow, taskContext, input); - }, + () -> requestFunction.apply(request, uri, workflow, taskContext, input), workflow.definition().application().executorService()); } - - private void addAuthHeader( - AuthProvider auth, - Builder request, - WorkflowContext workflow, - TaskContext task, - WorkflowModel model) { - String scheme = auth.authScheme(); - String parameter = auth.authParameter(workflow, task, model); - task.authorization(scheme, parameter); - request.header(AuthUtils.AUTH_HEADER_NAME, AuthUtils.authHeaderValue(scheme, parameter)); - } } diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutorBuilder.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutorBuilder.java index 05b5898a..bdecc36d 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutorBuilder.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutorBuilder.java @@ -22,10 +22,7 @@ import io.serverlessworkflow.impl.auth.AuthProvider; import io.serverlessworkflow.impl.auth.AuthProviderFactory; import jakarta.ws.rs.HttpMethod; -import jakarta.ws.rs.client.Invocation; -import jakarta.ws.rs.client.WebTarget; import java.net.URI; -import java.time.Duration; import java.util.Map; import java.util.Optional; @@ -36,7 +33,6 @@ public class HttpExecutorBuilder { private Object body; private String method = HttpMethod.GET; private boolean redirect; - private Optional> timeout = Optional.empty(); private WorkflowValueResolver> headersMap; private WorkflowValueResolver> queryMap; private Optional authProvider = Optional.empty(); @@ -88,36 +84,17 @@ public HttpExecutorBuilder redirect(boolean redirect) { return this; } - public HttpExecutorBuilder timeout(Optional> timeout) { - this.timeout = timeout; - return this; - } - public HttpExecutor build(String uri) { return build((w, f, n) -> URI.create(uri)); } public HttpExecutor build(WorkflowValueResolver uriSupplier) { - return new HttpExecutor( - getTargetSupplier(uriSupplier), + uriSupplier, Optional.ofNullable(headersMap), Optional.ofNullable(queryMap), - authProvider, - buildRequestSupplier()); - } - - private WorkflowValueResolver getTargetSupplier( - WorkflowValueResolver uriSupplier) { - return pathSupplier == null - ? (w, t, n) -> - HttpClientResolver.client(w, t, redirect, timeout.map(v -> v.apply(w, t, n))) - .target(uriSupplier.apply(w, t, n)) - : (w, t, n) -> - HttpClientResolver.client(w, t, redirect, timeout.map(v -> v.apply(w, t, n))) - .target( - WorkflowUtils.concatURI( - uriSupplier.apply(w, t, n), pathSupplier.apply(w, t, n))); + buildRequestSupplier(), + Optional.ofNullable(pathSupplier)); } public static HttpExecutorBuilder builder(WorkflowDefinition definition) { @@ -125,32 +102,19 @@ public static HttpExecutorBuilder builder(WorkflowDefinition definition) { } private RequestSupplier buildRequestSupplier() { - switch (method.toUpperCase()) { + String theMethod = method.toUpperCase(); + switch (theMethod) { case HttpMethod.POST: - return new WithBodyRequestSupplier( - Invocation.Builder::post, definition.application(), body, redirect); case HttpMethod.PUT: + case HttpMethod.PATCH: return new WithBodyRequestSupplier( - Invocation.Builder::put, definition.application(), body, redirect); + theMethod, redirect, authProvider, definition.application(), body); case HttpMethod.DELETE: - return new WithoutBodyRequestSupplier( - Invocation.Builder::delete, definition.application(), redirect); case HttpMethod.HEAD: - return new WithoutBodyRequestSupplier( - Invocation.Builder::head, definition.application(), redirect); - case HttpMethod.PATCH: - return new WithBodyRequestSupplier( - (request, entity) -> request.method("PATCH", entity), - definition.application(), - body, - redirect); case HttpMethod.OPTIONS: - return new WithoutBodyRequestSupplier( - Invocation.Builder::options, definition.application(), redirect); case HttpMethod.GET: default: - return new WithoutBodyRequestSupplier( - Invocation.Builder::get, definition.application(), redirect); + return new WithoutBodyRequestSupplier(theMethod, redirect, authProvider); } } } diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/RequestSupplier.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/RequestSupplier.java index 50155fff..2961b253 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/RequestSupplier.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/RequestSupplier.java @@ -19,9 +19,10 @@ import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowModel; import jakarta.ws.rs.client.Invocation.Builder; +import java.net.URI; @FunctionalInterface interface RequestSupplier { WorkflowModel apply( - Builder request, WorkflowContext workflow, TaskContext task, WorkflowModel model); + Builder request, URI uri, WorkflowContext workflow, TaskContext task, WorkflowModel model); } diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/WithBodyRequestSupplier.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/WithBodyRequestSupplier.java index 949abf79..6a6d247e 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/WithBodyRequestSupplier.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/WithBodyRequestSupplier.java @@ -21,22 +21,21 @@ import io.serverlessworkflow.impl.WorkflowFilter; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowUtils; -import jakarta.ws.rs.client.Entity; +import io.serverlessworkflow.impl.auth.AuthProvider; import jakarta.ws.rs.client.Invocation.Builder; import jakarta.ws.rs.core.Response; -import java.util.function.BiFunction; +import java.util.Optional; class WithBodyRequestSupplier extends AbstractRequestSupplier { private final WorkflowFilter bodyFilter; - private final BiFunction, Response> requestFunction; public WithBodyRequestSupplier( - BiFunction, Response> requestFunction, + String method, + boolean redirect, + Optional auth, WorkflowApplication application, - Object body, - boolean redirect) { - super(redirect); - this.requestFunction = requestFunction; + Object body) { + super(method, redirect, auth); bodyFilter = WorkflowUtils.buildWorkflowFilter(application, body); } @@ -47,7 +46,6 @@ protected Response invokeRequest( WorkflowContext workflow, TaskContext task, WorkflowModel model) { - return requestFunction.apply( - request, converter.toEntity(bodyFilter.apply(workflow, task, model))); + return request.method(method, converter.toEntity(bodyFilter.apply(workflow, task, model))); } } diff --git a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/WithoutBodyRequestSupplier.java b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/WithoutBodyRequestSupplier.java index a9604005..55d068d6 100644 --- a/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/WithoutBodyRequestSupplier.java +++ b/impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/WithoutBodyRequestSupplier.java @@ -16,22 +16,17 @@ package io.serverlessworkflow.impl.executors.http; import io.serverlessworkflow.impl.TaskContext; -import io.serverlessworkflow.impl.WorkflowApplication; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.auth.AuthProvider; import jakarta.ws.rs.client.Invocation.Builder; import jakarta.ws.rs.core.Response; -import java.util.function.Function; +import java.util.Optional; class WithoutBodyRequestSupplier extends AbstractRequestSupplier { - private final Function requestFunction; - public WithoutBodyRequestSupplier( - Function requestFunction, - WorkflowApplication application, - boolean redirect) { - super(redirect); - this.requestFunction = requestFunction; + public WithoutBodyRequestSupplier(String method, boolean redirect, Optional auth) { + super(method, redirect, auth); } @Override @@ -41,6 +36,6 @@ protected Response invokeRequest( WorkflowContext workflow, TaskContext task, WorkflowModel model) { - return requestFunction.apply(request); + return request.method(method); } }