Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ public static Builder communication(int status, TaskContext context, String titl
.title(title);
}

public static Builder communication(int status, TaskContext context) {
return new Builder(Errors.COMMUNICATION.toString(), status)
.instance(context.position().jsonPointer());
}

public static Builder communication(TaskContext context, String title) {
return communication(Errors.COMMUNICATION.status(), context, title);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@
package io.serverlessworkflow.impl.executors.http;

import io.serverlessworkflow.impl.WorkflowApplication;
import io.serverlessworkflow.impl.WorkflowFilter;
import io.serverlessworkflow.impl.WorkflowUtils;
import io.serverlessworkflow.impl.WorkflowValueResolver;
import jakarta.ws.rs.HttpMethod;
import jakarta.ws.rs.client.Invocation;
import jakarta.ws.rs.client.WebTarget;
import java.net.URI;
import java.util.Map;
Expand All @@ -32,30 +31,27 @@ abstract class AbstractHttpExecutorBuilder {
protected WorkflowValueResolver<Map<String, Object>> queryMap;
protected Optional<AuthProvider> authProvider = Optional.empty();
protected RequestSupplier requestFunction;
protected boolean redirect;

protected static RequestSupplier buildRequestSupplier(
String method, Object body, WorkflowApplication application) {
String method, Object body, boolean redirect, WorkflowApplication application) {

switch (method.toUpperCase()) {
case HttpMethod.POST:
WorkflowFilter bodyFilter = WorkflowUtils.buildWorkflowFilter(application, body);
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()));
};
return new WithBodyRequestSupplier(Invocation.Builder::post, application, body, redirect);
case HttpMethod.PUT:
return new WithBodyRequestSupplier(Invocation.Builder::put, application, body, redirect);
case HttpMethod.DELETE:
return new WithoutBodyRequestSupplier(Invocation.Builder::delete, application, redirect);
case HttpMethod.HEAD:
return new WithoutBodyRequestSupplier(Invocation.Builder::head, application, redirect);
case HttpMethod.OPTIONS:
return new WithoutBodyRequestSupplier(Invocation.Builder::options, application, redirect);
case HttpMethod.PATCH:
return new WithBodyRequestSupplier(
(request, entity) -> request.method("patch", entity), application, body, redirect);
case HttpMethod.GET:
default:
return (request, w, t, n) ->
w.definition()
.application()
.modelFactory()
.fromAny(request.get(HttpConverterResolver.converter(w, t).responseType()));
return new WithoutBodyRequestSupplier(Invocation.Builder::get, application, redirect);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* 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;
import io.serverlessworkflow.impl.WorkflowError;
import io.serverlessworkflow.impl.WorkflowException;
import io.serverlessworkflow.impl.WorkflowModel;
import jakarta.ws.rs.client.Invocation.Builder;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status.Family;

abstract class AbstractRequestSupplier implements RequestSupplier {

private final boolean redirect;

public AbstractRequestSupplier(boolean redirect) {
this.redirect = redirect;
}

@Override
public WorkflowModel apply(
Builder request, WorkflowContext workflow, TaskContext task, WorkflowModel model) {
HttpModelConverter converter = HttpConverterResolver.converter(workflow, task);
Response response = invokeRequest(request, converter, workflow, task, model);
validateStatus(task, response, converter);
return workflow
.definition()
.application()
.modelFactory()
.fromAny(response.readEntity(converter.responseType()));
}

private void validateStatus(TaskContext task, Response response, HttpModelConverter converter) {
if (response.getStatusInfo().getFamily() != Family.SUCCESSFUL) {
throw new WorkflowException(
converter
.errorFromResponse(WorkflowError.communication(response.getStatus(), task), response)
.build());
}
}

protected abstract Response invokeRequest(
Builder request,
HttpModelConverter converter,
WorkflowContext workflow,
TaskContext task,
WorkflowModel model);
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ public void init(CallHTTP task, WorkflowDefinition definition, WorkflowMutablePo
}
this.requestFunction =
buildRequestSupplier(
httpArgs.getMethod().toUpperCase(), httpArgs.getBody(), definition.application());
httpArgs.getMethod().toUpperCase(),
httpArgs.getBody(),
httpArgs.isRedirect(),
definition.application());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@

import io.serverlessworkflow.impl.TaskContext;
import io.serverlessworkflow.impl.WorkflowContext;
import io.serverlessworkflow.impl.WorkflowError;
import io.serverlessworkflow.impl.WorkflowException;
import io.serverlessworkflow.impl.WorkflowModel;
import io.serverlessworkflow.impl.WorkflowValueResolver;
import io.serverlessworkflow.impl.executors.CallableTask;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.client.Invocation.Builder;
import jakarta.ws.rs.client.WebTarget;
import java.util.Map;
Expand Down Expand Up @@ -79,15 +76,8 @@ public CompletableFuture<WorkflowModel> apply(
h -> h.apply(workflow, taskContext, input).forEach((k, v) -> request.header(k, v)));
return CompletableFuture.supplyAsync(
() -> {
try {
authProvider.ifPresent(auth -> auth.build(request, workflow, taskContext, input));
return requestFunction.apply(request, workflow, taskContext, input);
} catch (WebApplicationException exception) {
throw new WorkflowException(
WorkflowError.communication(
exception.getResponse().getStatus(), taskContext, exception)
.build());
}
authProvider.ifPresent(auth -> auth.build(request, workflow, taskContext, input));
return requestFunction.apply(request, workflow, taskContext, input);
},
workflow.definition().application().executorService());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class HttpExecutorBuilder extends AbstractHttpExecutorBuilder {
private WorkflowValueResolver<URI> pathSupplier;
private Object body;
private String method = HttpMethod.GET;
private boolean redirect;

private HttpExecutorBuilder(WorkflowDefinition definition) {
this.definition = definition;
Expand Down Expand Up @@ -83,7 +84,7 @@ public HttpExecutor build(String uri) {
}

public HttpExecutor build(WorkflowValueResolver<URI> uriSupplier) {
this.requestFunction = buildRequestSupplier(method, body, definition.application());
this.requestFunction = buildRequestSupplier(method, body, redirect, definition.application());
this.targetSupplier =
pathSupplier == null
? getTargetSupplier(uriSupplier)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
*/
package io.serverlessworkflow.impl.executors.http;

import io.serverlessworkflow.impl.WorkflowError;
import io.serverlessworkflow.impl.WorkflowModel;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.core.Response;
import org.slf4j.LoggerFactory;

public interface HttpModelConverter {

Expand All @@ -25,4 +28,19 @@ default Entity<?> toEntity(WorkflowModel model) {
}

Class<?> responseType();

default WorkflowError.Builder errorFromResponse(
WorkflowError.Builder errorBuilder, Response response) {
try {
Object title = response.readEntity(responseType());
if (title != null) {
errorBuilder.title(title.toString());
}
} catch (Exception ex) {
LoggerFactory.getLogger(HttpModelConverter.class)
.warn("Problem extracting error from http response", ex);
}

return errorBuilder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@
@FunctionalInterface
interface RequestSupplier {
WorkflowModel apply(
Builder request, WorkflowContext workflow, TaskContext task, WorkflowModel node);
Builder request, WorkflowContext workflow, TaskContext task, WorkflowModel model);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.WorkflowApplication;
import io.serverlessworkflow.impl.WorkflowContext;
import io.serverlessworkflow.impl.WorkflowFilter;
import io.serverlessworkflow.impl.WorkflowModel;
import io.serverlessworkflow.impl.WorkflowUtils;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.Invocation.Builder;
import jakarta.ws.rs.core.Response;
import java.util.function.BiFunction;

class WithBodyRequestSupplier extends AbstractRequestSupplier {
private final WorkflowFilter bodyFilter;
private final BiFunction<Builder, Entity<?>, Response> requestFunction;

public WithBodyRequestSupplier(
BiFunction<Builder, Entity<?>, Response> requestFunction,
WorkflowApplication application,
Object body,
boolean redirect) {
super(redirect);
this.requestFunction = requestFunction;
bodyFilter = WorkflowUtils.buildWorkflowFilter(application, body);
}

@Override
protected Response invokeRequest(
Builder request,
HttpModelConverter converter,
WorkflowContext workflow,
TaskContext task,
WorkflowModel model) {
return requestFunction.apply(
request, converter.toEntity(bodyFilter.apply(workflow, task, model)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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.WorkflowApplication;
import io.serverlessworkflow.impl.WorkflowContext;
import io.serverlessworkflow.impl.WorkflowModel;
import jakarta.ws.rs.client.Invocation.Builder;
import jakarta.ws.rs.core.Response;
import java.util.function.Function;

class WithoutBodyRequestSupplier extends AbstractRequestSupplier {
private final Function<Builder, Response> requestFunction;

public WithoutBodyRequestSupplier(
Function<Builder, Response> requestFunction,
WorkflowApplication application,
boolean redirect) {
super(redirect);
this.requestFunction = requestFunction;
}

@Override
protected Response invokeRequest(
Builder request,
HttpModelConverter converter,
WorkflowContext workflow,
TaskContext task,
WorkflowModel model) {
return requestFunction.apply(request);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,6 @@ public void testOpenAPIBearerQueryInlinedBodyWithNegativeResponse() throws Excep
.orElseThrow());
assertInstanceOf(WorkflowException.class, exception.getCause());
assertTrue(exception.getMessage().contains("status=409"));
assertTrue(exception.getMessage().contains("title=HTTP 409 Client Error"));

RecordedRequest restRequest = restServer.takeRequest();
assertEquals("POST", restRequest.getMethod());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class RetryTimeoutTest {

@BeforeAll
static void init() {
app = WorkflowApplication.builder().withListener(new TraceExecutionListener()).build();
app = WorkflowApplication.builder().build();
}

@AfterAll
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ do:
- getPet:
call: getPetById
with:
petId: 69
petId: -1