diff --git a/impl/pom.xml b/impl/pom.xml
index 3907fb71..92abc7b6 100644
--- a/impl/pom.xml
+++ b/impl/pom.xml
@@ -26,6 +26,10 @@
jersey-media-json-jackson
${version.org.glassfish.jersey}
+
+ com.networknt
+ json-schema-validator
+
net.thisptr
jackson-jq
diff --git a/impl/src/main/java/io/serverlessworkflow/impl/WorkflowContext.java b/impl/src/main/java/io/serverlessworkflow/impl/WorkflowContext.java
index 6982cfd6..de88d2d0 100644
--- a/impl/src/main/java/io/serverlessworkflow/impl/WorkflowContext.java
+++ b/impl/src/main/java/io/serverlessworkflow/impl/WorkflowContext.java
@@ -19,7 +19,6 @@
import io.serverlessworkflow.impl.json.JsonUtils;
public class WorkflowContext {
-
private final WorkflowPosition position;
private JsonNode context;
private final JsonNode input;
diff --git a/impl/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java b/impl/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java
index ec39c90b..afcebeba 100644
--- a/impl/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java
+++ b/impl/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java
@@ -15,44 +15,79 @@
*/
package io.serverlessworkflow.impl;
+import static io.serverlessworkflow.impl.WorkflowUtils.*;
import static io.serverlessworkflow.impl.json.JsonUtils.*;
import com.fasterxml.jackson.databind.JsonNode;
+import io.serverlessworkflow.api.types.Input;
+import io.serverlessworkflow.api.types.Output;
import io.serverlessworkflow.api.types.TaskBase;
import io.serverlessworkflow.api.types.TaskItem;
import io.serverlessworkflow.api.types.Workflow;
import io.serverlessworkflow.impl.executors.DefaultTaskExecutorFactory;
import io.serverlessworkflow.impl.executors.TaskExecutor;
import io.serverlessworkflow.impl.executors.TaskExecutorFactory;
+import io.serverlessworkflow.impl.expressions.ExpressionFactory;
+import io.serverlessworkflow.impl.expressions.JQExpressionFactory;
import io.serverlessworkflow.impl.json.JsonUtils;
+import io.serverlessworkflow.impl.jsonschema.DefaultSchemaValidatorFactory;
+import io.serverlessworkflow.impl.jsonschema.SchemaValidator;
+import io.serverlessworkflow.impl.jsonschema.SchemaValidatorFactory;
+import io.serverlessworkflow.resources.DefaultResourceLoaderFactory;
+import io.serverlessworkflow.resources.ResourceLoaderFactory;
+import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
public class WorkflowDefinition {
private WorkflowDefinition(
Workflow workflow,
- TaskExecutorFactory taskFactory,
- Collection listeners) {
+ Collection listeners,
+ WorkflowFactories factories) {
this.workflow = workflow;
- this.taskFactory = taskFactory;
this.listeners = listeners;
+ this.factories = factories;
+ if (workflow.getInput() != null) {
+ Input input = workflow.getInput();
+ this.inputSchemaValidator =
+ getSchemaValidator(
+ factories.getValidatorFactory(), schemaToNode(factories, input.getSchema()));
+ this.inputFilter = buildWorkflowFilter(factories.getExpressionFactory(), input.getFrom());
+ }
+ if (workflow.getOutput() != null) {
+ Output output = workflow.getOutput();
+ this.outputSchemaValidator =
+ getSchemaValidator(
+ factories.getValidatorFactory(), schemaToNode(factories, output.getSchema()));
+ this.outputFilter = buildWorkflowFilter(factories.getExpressionFactory(), output.getAs());
+ }
}
private final Workflow workflow;
private final Collection listeners;
- private final TaskExecutorFactory taskFactory;
+ private final WorkflowFactories factories;
+ private Optional inputSchemaValidator = Optional.empty();
+ private Optional outputSchemaValidator = Optional.empty();
+ private Optional inputFilter = Optional.empty();
+ private Optional outputFilter = Optional.empty();
+
private final Map> taskExecutors =
new ConcurrentHashMap<>();
public static class Builder {
private final Workflow workflow;
private TaskExecutorFactory taskFactory = DefaultTaskExecutorFactory.get();
+ private ExpressionFactory exprFactory = JQExpressionFactory.get();
private Collection listeners;
+ private ResourceLoaderFactory resourceLoaderFactory = DefaultResourceLoaderFactory.get();
+ private SchemaValidatorFactory schemaValidatorFactory = DefaultSchemaValidatorFactory.get();
+ private Path path;
private Builder(Workflow workflow) {
this.workflow = workflow;
@@ -71,13 +106,39 @@ public Builder withTaskExecutorFactory(TaskExecutorFactory factory) {
return this;
}
+ public Builder withExpressionFactory(ExpressionFactory factory) {
+ this.exprFactory = factory;
+ return this;
+ }
+
+ public Builder withPath(Path path) {
+ this.path = path;
+ return this;
+ }
+
+ public Builder withResourceLoaderFactory(ResourceLoaderFactory resourceLoader) {
+ this.resourceLoaderFactory = resourceLoader;
+ return this;
+ }
+
+ public Builder withSchemaValidatorFactory(SchemaValidatorFactory factory) {
+ this.schemaValidatorFactory = factory;
+ return this;
+ }
+
public WorkflowDefinition build() {
- return new WorkflowDefinition(
- workflow,
- taskFactory,
- listeners == null
- ? Collections.emptySet()
- : Collections.unmodifiableCollection(listeners));
+ WorkflowDefinition def =
+ new WorkflowDefinition(
+ workflow,
+ listeners == null
+ ? Collections.emptySet()
+ : Collections.unmodifiableCollection(listeners),
+ new WorkflowFactories(
+ taskFactory,
+ resourceLoaderFactory.getResourceLoader(path),
+ exprFactory,
+ schemaValidatorFactory));
+ return def;
}
}
@@ -86,7 +147,7 @@ public static Builder builder(Workflow workflow) {
}
public WorkflowInstance execute(Object input) {
- return new WorkflowInstance(taskFactory, JsonUtils.fromValue(input));
+ return new WorkflowInstance(JsonUtils.fromValue(input));
}
enum State {
@@ -101,11 +162,15 @@ public class WorkflowInstance {
private State state;
private WorkflowContext context;
- private WorkflowInstance(TaskExecutorFactory factory, JsonNode input) {
+ private WorkflowInstance(JsonNode input) {
this.output = input;
- this.state = State.STARTED;
+ inputSchemaValidator.ifPresent(v -> v.validate(input));
this.context = WorkflowContext.builder(input).build();
+ inputFilter.ifPresent(f -> output = f.apply(context, Optional.empty(), output));
+ this.state = State.STARTED;
processDo(workflow.getDo());
+ outputFilter.ifPresent(f -> output = f.apply(context, Optional.empty(), output));
+ outputSchemaValidator.ifPresent(v -> v.validate(output));
}
private void processDo(List tasks) {
@@ -118,7 +183,7 @@ private void processDo(List tasks) {
taskExecutors
.computeIfAbsent(
context.position().jsonPointer(),
- k -> taskFactory.getTaskExecutor(task.getTask()))
+ k -> factories.getTaskFactory().getTaskExecutor(task.getTask(), factories))
.apply(context, output);
listeners.forEach(l -> l.onTaskEnded(context.position(), task.getTask()));
context.position().back().back();
diff --git a/impl/src/main/java/io/serverlessworkflow/impl/WorkflowFactories.java b/impl/src/main/java/io/serverlessworkflow/impl/WorkflowFactories.java
new file mode 100644
index 00000000..6b0408b5
--- /dev/null
+++ b/impl/src/main/java/io/serverlessworkflow/impl/WorkflowFactories.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+import io.serverlessworkflow.impl.executors.TaskExecutorFactory;
+import io.serverlessworkflow.impl.expressions.ExpressionFactory;
+import io.serverlessworkflow.impl.jsonschema.SchemaValidatorFactory;
+import io.serverlessworkflow.resources.ResourceLoader;
+
+public class WorkflowFactories {
+
+ private final TaskExecutorFactory taskFactory;
+ private final ResourceLoader resourceLoader;
+ private final ExpressionFactory expressionFactory;
+ private final SchemaValidatorFactory validatorFactory;
+
+ public WorkflowFactories(
+ TaskExecutorFactory taskFactory,
+ ResourceLoader resourceLoader,
+ ExpressionFactory expressionFactory,
+ SchemaValidatorFactory validatorFactory) {
+ this.taskFactory = taskFactory;
+ this.resourceLoader = resourceLoader;
+ this.expressionFactory = expressionFactory;
+ this.validatorFactory = validatorFactory;
+ }
+
+ public TaskExecutorFactory getTaskFactory() {
+ return taskFactory;
+ }
+
+ public ResourceLoader getResourceLoader() {
+ return resourceLoader;
+ }
+
+ public ExpressionFactory getExpressionFactory() {
+ return expressionFactory;
+ }
+
+ public SchemaValidatorFactory getValidatorFactory() {
+ return validatorFactory;
+ }
+}
diff --git a/impl/src/main/java/io/serverlessworkflow/impl/WorkflowFilter.java b/impl/src/main/java/io/serverlessworkflow/impl/WorkflowFilter.java
new file mode 100644
index 00000000..7fde97ba
--- /dev/null
+++ b/impl/src/main/java/io/serverlessworkflow/impl/WorkflowFilter.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import java.util.Optional;
+
+@FunctionalInterface
+public interface WorkflowFilter {
+ JsonNode apply(WorkflowContext workflow, Optional> task, JsonNode node);
+}
diff --git a/impl/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java b/impl/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java
new file mode 100644
index 00000000..787d17dc
--- /dev/null
+++ b/impl/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java
@@ -0,0 +1,103 @@
+/*
+ * 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;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.serverlessworkflow.api.WorkflowFormat;
+import io.serverlessworkflow.api.types.ExportAs;
+import io.serverlessworkflow.api.types.InputFrom;
+import io.serverlessworkflow.api.types.OutputAs;
+import io.serverlessworkflow.api.types.SchemaExternal;
+import io.serverlessworkflow.api.types.SchemaInline;
+import io.serverlessworkflow.api.types.SchemaUnion;
+import io.serverlessworkflow.impl.expressions.Expression;
+import io.serverlessworkflow.impl.expressions.ExpressionFactory;
+import io.serverlessworkflow.impl.expressions.ExpressionUtils;
+import io.serverlessworkflow.impl.json.JsonUtils;
+import io.serverlessworkflow.impl.jsonschema.SchemaValidator;
+import io.serverlessworkflow.impl.jsonschema.SchemaValidatorFactory;
+import io.serverlessworkflow.resources.StaticResource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.util.Map;
+import java.util.Optional;
+
+public class WorkflowUtils {
+
+ private WorkflowUtils() {}
+
+ public static Optional getSchemaValidator(
+ SchemaValidatorFactory validatorFactory, Optional node) {
+ return node.map(n -> validatorFactory.getValidator(n));
+ }
+
+ public static Optional schemaToNode(WorkflowFactories factories, SchemaUnion schema) {
+ if (schema != null) {
+ if (schema.getSchemaInline() != null) {
+ SchemaInline inline = schema.getSchemaInline();
+ return Optional.of(JsonUtils.mapper().convertValue(inline.getDocument(), JsonNode.class));
+ } else if (schema.getSchemaExternal() != null) {
+ SchemaExternal external = schema.getSchemaExternal();
+ StaticResource resource = factories.getResourceLoader().loadStatic(external.getResource());
+ ObjectMapper mapper = WorkflowFormat.fromFileName(resource.name()).mapper();
+ try (InputStream in = resource.open()) {
+ return Optional.of(mapper.readTree(in));
+ } catch (IOException io) {
+ throw new UncheckedIOException(io);
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ public static Optional buildWorkflowFilter(
+ ExpressionFactory exprFactory, InputFrom from) {
+ return from != null
+ ? Optional.of(buildWorkflowFilter(exprFactory, from.getString(), from.getObject()))
+ : Optional.empty();
+ }
+
+ public static Optional buildWorkflowFilter(
+ ExpressionFactory exprFactory, OutputAs as) {
+ return as != null
+ ? Optional.of(buildWorkflowFilter(exprFactory, as.getString(), as.getObject()))
+ : Optional.empty();
+ }
+
+ public static Optional buildWorkflowFilter(
+ ExpressionFactory exprFactory, ExportAs as) {
+ return as != null
+ ? Optional.of(buildWorkflowFilter(exprFactory, as.getString(), as.getObject()))
+ : Optional.empty();
+ }
+
+ private static WorkflowFilter buildWorkflowFilter(
+ ExpressionFactory exprFactory, String str, Object object) {
+ if (str != null) {
+ Expression expression = exprFactory.getExpression(str);
+ return expression::eval;
+ } else {
+ Object exprObj = ExpressionUtils.buildExpressionObject(object, exprFactory);
+ return exprObj instanceof Map
+ ? (w, t, n) ->
+ JsonUtils.fromValue(
+ ExpressionUtils.evaluateExpressionMap((Map) exprObj, w, t, n))
+ : (w, t, n) -> JsonUtils.fromValue(object);
+ }
+ }
+}
diff --git a/impl/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java b/impl/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java
index 36dbbf4f..3ed5d6e6 100644
--- a/impl/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java
+++ b/impl/src/main/java/io/serverlessworkflow/impl/executors/AbstractTaskExecutor.java
@@ -15,6 +15,8 @@
*/
package io.serverlessworkflow.impl.executors;
+import static io.serverlessworkflow.impl.WorkflowUtils.*;
+
import com.fasterxml.jackson.databind.JsonNode;
import io.serverlessworkflow.api.types.Export;
import io.serverlessworkflow.api.types.Input;
@@ -22,93 +24,79 @@
import io.serverlessworkflow.api.types.TaskBase;
import io.serverlessworkflow.impl.TaskContext;
import io.serverlessworkflow.impl.WorkflowContext;
-import io.serverlessworkflow.impl.expressions.Expression;
-import io.serverlessworkflow.impl.expressions.ExpressionFactory;
-import io.serverlessworkflow.impl.expressions.ExpressionUtils;
-import io.serverlessworkflow.impl.json.JsonUtils;
-import java.util.Map;
+import io.serverlessworkflow.impl.WorkflowFactories;
+import io.serverlessworkflow.impl.WorkflowFilter;
+import io.serverlessworkflow.impl.jsonschema.SchemaValidator;
import java.util.Optional;
public abstract class AbstractTaskExecutor implements TaskExecutor {
protected final T task;
- protected final ExpressionFactory exprFactory;
-
- private interface TaskFilter {
- JsonNode apply(WorkflowContext workflow, TaskContext task, JsonNode node);
- }
- private final Optional> inputProcessor;
- private final Optional> outputProcessor;
- private final Optional> contextProcessor;
+ private Optional inputProcessor = Optional.empty();
+ private Optional outputProcessor = Optional.empty();
+ private Optional contextProcessor = Optional.empty();
+ private Optional inputSchemaValidator = Optional.empty();
+ private Optional outputSchemaValidator = Optional.empty();
+ private Optional contextSchemaValidator = Optional.empty();
- protected AbstractTaskExecutor(T task, ExpressionFactory exprFactory) {
+ protected AbstractTaskExecutor(T task, WorkflowFactories holder) {
this.task = task;
- this.exprFactory = exprFactory;
- this.inputProcessor = Optional.ofNullable(getInputProcessor());
- this.outputProcessor = Optional.ofNullable(getOutputProcessor());
- this.contextProcessor = Optional.ofNullable(getContextProcessor());
+ buildInputProcessors(holder);
+ buildOutputProcessors(holder);
+ buildContextProcessors(holder);
}
- private TaskFilter getInputProcessor() {
+ private void buildInputProcessors(WorkflowFactories holder) {
if (task.getInput() != null) {
Input input = task.getInput();
- // TODO add schema validator
- if (input.getFrom() != null) {
- return getTaskFilter(input.getFrom().getString(), input.getFrom().getObject());
- }
+ this.inputProcessor = buildWorkflowFilter(holder.getExpressionFactory(), input.getFrom());
+ this.inputSchemaValidator =
+ getSchemaValidator(holder.getValidatorFactory(), schemaToNode(holder, input.getSchema()));
}
- return null;
}
- private TaskFilter getOutputProcessor() {
+ private void buildOutputProcessors(WorkflowFactories holder) {
if (task.getOutput() != null) {
Output output = task.getOutput();
- // TODO add schema validator
- if (output.getAs() != null) {
- return getTaskFilter(output.getAs().getString(), output.getAs().getObject());
- }
+ this.outputProcessor = buildWorkflowFilter(holder.getExpressionFactory(), output.getAs());
+ this.outputSchemaValidator =
+ getSchemaValidator(
+ holder.getValidatorFactory(), schemaToNode(holder, output.getSchema()));
}
- return null;
}
- private TaskFilter getContextProcessor() {
+ private void buildContextProcessors(WorkflowFactories holder) {
if (task.getExport() != null) {
Export export = task.getExport();
- // TODO add schema validator
if (export.getAs() != null) {
- return getTaskFilter(export.getAs().getString(), export.getAs().getObject());
+ this.contextProcessor = buildWorkflowFilter(holder.getExpressionFactory(), export.getAs());
}
- }
- return null;
- }
-
- private TaskFilter getTaskFilter(String str, Object object) {
- if (str != null) {
- Expression expression = exprFactory.getExpression(str);
- return expression::eval;
- } else {
- Object exprObj = ExpressionUtils.buildExpressionObject(object, exprFactory);
- return exprObj instanceof Map
- ? (w, t, n) ->
- JsonUtils.fromValue(
- ExpressionUtils.evaluateExpressionMap((Map) exprObj, w, t, n))
- : (w, t, n) -> JsonUtils.fromValue(object);
+ this.contextSchemaValidator =
+ getSchemaValidator(
+ holder.getValidatorFactory(), schemaToNode(holder, export.getSchema()));
}
}
@Override
public JsonNode apply(WorkflowContext workflowContext, JsonNode rawInput) {
TaskContext taskContext = new TaskContext<>(rawInput, task);
+ inputSchemaValidator.ifPresent(s -> s.validate(taskContext.rawInput()));
inputProcessor.ifPresent(
- p -> taskContext.input(p.apply(workflowContext, taskContext, taskContext.rawInput())));
+ p ->
+ taskContext.input(
+ p.apply(workflowContext, Optional.of(taskContext), taskContext.rawInput())));
taskContext.rawOutput(internalExecute(workflowContext, taskContext, taskContext.input()));
outputProcessor.ifPresent(
- p -> taskContext.output(p.apply(workflowContext, taskContext, taskContext.rawOutput())));
+ p ->
+ taskContext.output(
+ p.apply(workflowContext, Optional.of(taskContext), taskContext.rawOutput())));
+ outputSchemaValidator.ifPresent(s -> s.validate(taskContext.output()));
contextProcessor.ifPresent(
p ->
workflowContext.context(
- p.apply(workflowContext, taskContext, workflowContext.context())));
+ p.apply(workflowContext, Optional.of(taskContext), workflowContext.context())));
+ contextSchemaValidator.ifPresent(s -> s.validate(workflowContext.context()));
return taskContext.output();
}
diff --git a/impl/src/main/java/io/serverlessworkflow/impl/executors/DefaultTaskExecutorFactory.java b/impl/src/main/java/io/serverlessworkflow/impl/executors/DefaultTaskExecutorFactory.java
index cf49657e..cb76e395 100644
--- a/impl/src/main/java/io/serverlessworkflow/impl/executors/DefaultTaskExecutorFactory.java
+++ b/impl/src/main/java/io/serverlessworkflow/impl/executors/DefaultTaskExecutorFactory.java
@@ -18,33 +18,23 @@
import io.serverlessworkflow.api.types.CallTask;
import io.serverlessworkflow.api.types.Task;
import io.serverlessworkflow.api.types.TaskBase;
-import io.serverlessworkflow.impl.expressions.ExpressionFactory;
-import io.serverlessworkflow.impl.expressions.JQExpressionFactory;
+import io.serverlessworkflow.impl.WorkflowFactories;
public class DefaultTaskExecutorFactory implements TaskExecutorFactory {
- private final ExpressionFactory exprFactory;
-
- private static TaskExecutorFactory instance =
- new DefaultTaskExecutorFactory(JQExpressionFactory.get());
+ private static TaskExecutorFactory instance = new DefaultTaskExecutorFactory();
public static TaskExecutorFactory get() {
return instance;
}
- public static TaskExecutorFactory get(ExpressionFactory factory) {
- return new DefaultTaskExecutorFactory(factory);
- }
-
- protected DefaultTaskExecutorFactory(ExpressionFactory exprFactory) {
- this.exprFactory = exprFactory;
- }
+ protected DefaultTaskExecutorFactory() {}
- public TaskExecutor extends TaskBase> getTaskExecutor(Task task) {
+ public TaskExecutor extends TaskBase> getTaskExecutor(Task task, WorkflowFactories factories) {
if (task.getCallTask() != null) {
CallTask callTask = task.getCallTask();
if (callTask.getCallHTTP() != null) {
- return new HttpExecutor(callTask.getCallHTTP(), exprFactory);
+ return new HttpExecutor(callTask.getCallHTTP(), factories);
}
}
throw new UnsupportedOperationException(task.get().getClass().getName() + " not supported yet");
diff --git a/impl/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java b/impl/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java
index 60da619c..e17fd8dc 100644
--- a/impl/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java
+++ b/impl/src/main/java/io/serverlessworkflow/impl/executors/HttpExecutor.java
@@ -24,6 +24,7 @@
import io.serverlessworkflow.api.types.UriTemplate;
import io.serverlessworkflow.impl.TaskContext;
import io.serverlessworkflow.impl.WorkflowContext;
+import io.serverlessworkflow.impl.WorkflowFactories;
import io.serverlessworkflow.impl.expressions.Expression;
import io.serverlessworkflow.impl.expressions.ExpressionFactory;
import io.serverlessworkflow.impl.expressions.ExpressionUtils;
@@ -34,9 +35,9 @@
import jakarta.ws.rs.client.Entity;
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;
public class HttpExecutor extends AbstractTaskExecutor {
@@ -57,28 +58,31 @@ private interface RequestSupplier {
JsonNode apply(Builder request, WorkflowContext workflow, TaskContext> task, JsonNode node);
}
- public HttpExecutor(CallHTTP task, ExpressionFactory factory) {
- super(task, factory);
+ public HttpExecutor(CallHTTP task, WorkflowFactories holder) {
+ super(task, holder);
HTTPArguments httpArgs = task.getWith();
- this.targetSupplier = getTargetSupplier(httpArgs.getEndpoint());
+ this.targetSupplier = getTargetSupplier(httpArgs.getEndpoint(), holder.getExpressionFactory());
this.headersMap =
httpArgs.getHeaders() != null
? ExpressionUtils.buildExpressionMap(
- httpArgs.getHeaders().getAdditionalProperties(), factory)
+ httpArgs.getHeaders().getAdditionalProperties(), holder.getExpressionFactory())
: Map.of();
this.queryMap =
httpArgs.getQuery() != null
? ExpressionUtils.buildExpressionMap(
- httpArgs.getQuery().getAdditionalProperties(), factory)
+ httpArgs.getQuery().getAdditionalProperties(), holder.getExpressionFactory())
: Map.of();
switch (httpArgs.getMethod().toUpperCase()) {
case HttpMethod.POST:
- Object body = ExpressionUtils.buildExpressionObject(httpArgs.getBody(), factory);
+ Object body =
+ ExpressionUtils.buildExpressionObject(
+ httpArgs.getBody(), holder.getExpressionFactory());
this.requestFunction =
(request, workflow, context, node) ->
request.post(
Entity.json(
- ExpressionUtils.evaluateExpressionObject(body, workflow, context, node)),
+ ExpressionUtils.evaluateExpressionObject(
+ body, workflow, Optional.of(context), node)),
JsonNode.class);
break;
case HttpMethod.GET:
@@ -92,79 +96,58 @@ protected JsonNode internalExecute(
WorkflowContext workflow, TaskContext taskContext, JsonNode input) {
WebTarget target = targetSupplier.apply(workflow, taskContext, input);
for (Entry entry :
- ExpressionUtils.evaluateExpressionMap(queryMap, workflow, taskContext, input).entrySet()) {
+ ExpressionUtils.evaluateExpressionMap(queryMap, workflow, Optional.of(taskContext), input)
+ .entrySet()) {
target = target.queryParam(entry.getKey(), entry.getValue());
}
Builder request = target.request();
- ExpressionUtils.evaluateExpressionMap(headersMap, workflow, taskContext, input)
+ ExpressionUtils.evaluateExpressionMap(headersMap, workflow, Optional.of(taskContext), input)
.forEach(request::header);
return requestFunction.apply(request, workflow, taskContext, input);
}
- private TargetSupplier getTargetSupplier(Endpoint endpoint) {
+ private static TargetSupplier getTargetSupplier(
+ Endpoint endpoint, ExpressionFactory expressionFactory) {
if (endpoint.getEndpointConfiguration() != null) {
EndpointUri uri = endpoint.getEndpointConfiguration().getUri();
if (uri.getLiteralEndpointURI() != null) {
return getURISupplier(uri.getLiteralEndpointURI());
} else if (uri.getExpressionEndpointURI() != null) {
- return new ExpressionURISupplier(uri.getExpressionEndpointURI());
+ return new ExpressionURISupplier(
+ expressionFactory.getExpression(uri.getExpressionEndpointURI()));
}
} else if (endpoint.getRuntimeExpression() != null) {
- return new ExpressionURISupplier(endpoint.getRuntimeExpression());
+ return new ExpressionURISupplier(
+ expressionFactory.getExpression(endpoint.getRuntimeExpression()));
} else if (endpoint.getUriTemplate() != null) {
return getURISupplier(endpoint.getUriTemplate());
}
throw new IllegalArgumentException("Invalid endpoint definition " + endpoint);
}
- private TargetSupplier getURISupplier(UriTemplate template) {
+ private static TargetSupplier getURISupplier(UriTemplate template) {
if (template.getLiteralUri() != null) {
- return new URISupplier(template.getLiteralUri());
+ return (w, t, n) -> client.target(template.getLiteralUri());
} else if (template.getLiteralUriTemplate() != null) {
- return new URITemplateSupplier(template.getLiteralUriTemplate());
+ return (w, t, n) ->
+ client
+ .target(template.getLiteralUriTemplate())
+ .resolveTemplates(
+ JsonUtils.mapper().convertValue(n, new TypeReference