diff --git a/impl/pom.xml b/impl/pom.xml index 32ee86a0..3907fb71 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -8,6 +8,7 @@ serverlessworkflow-impl 3.1.9 + 1.0.1 @@ -25,6 +26,11 @@ jersey-media-json-jackson ${version.org.glassfish.jersey} + + net.thisptr + jackson-jq + ${version.net.thisptr} + org.junit.jupiter junit-jupiter-api diff --git a/impl/src/main/java/io/serverlessworkflow/impl/AbstractTaskExecutor.java b/impl/src/main/java/io/serverlessworkflow/impl/AbstractTaskExecutor.java index f377b3f5..13181603 100644 --- a/impl/src/main/java/io/serverlessworkflow/impl/AbstractTaskExecutor.java +++ b/impl/src/main/java/io/serverlessworkflow/impl/AbstractTaskExecutor.java @@ -21,9 +21,11 @@ public abstract class AbstractTaskExecutor implements TaskExecutor { protected final T task; + protected final ExpressionFactory exprFactory; - protected AbstractTaskExecutor(T task) { + protected AbstractTaskExecutor(T task, ExpressionFactory exprFactory) { this.task = task; + this.exprFactory = exprFactory; } @Override diff --git a/impl/src/main/java/io/serverlessworkflow/impl/DefaultTaskExecutorFactory.java b/impl/src/main/java/io/serverlessworkflow/impl/DefaultTaskExecutorFactory.java index 806a3208..fab07d8c 100644 --- a/impl/src/main/java/io/serverlessworkflow/impl/DefaultTaskExecutorFactory.java +++ b/impl/src/main/java/io/serverlessworkflow/impl/DefaultTaskExecutorFactory.java @@ -18,23 +18,32 @@ import io.serverlessworkflow.api.types.CallTask; import io.serverlessworkflow.api.types.Task; import io.serverlessworkflow.api.types.TaskBase; +import io.serverlessworkflow.impl.jq.JQExpressionFactory; public class DefaultTaskExecutorFactory implements TaskExecutorFactory { - protected DefaultTaskExecutorFactory() {} + private final ExpressionFactory exprFactory; - private static TaskExecutorFactory instance = new DefaultTaskExecutorFactory(); + private static TaskExecutorFactory instance = + new DefaultTaskExecutorFactory(JQExpressionFactory.get()); public static TaskExecutorFactory get() { return instance; } - public TaskExecutor getTaskExecutor(Task task) { + public static TaskExecutorFactory get(ExpressionFactory factory) { + return new DefaultTaskExecutorFactory(factory); + } + protected DefaultTaskExecutorFactory(ExpressionFactory exprFactory) { + this.exprFactory = exprFactory; + } + + public TaskExecutor getTaskExecutor(Task task) { if (task.getCallTask() != null) { CallTask callTask = task.getCallTask(); if (callTask.getCallHTTP() != null) { - return new HttpExecutor(callTask.getCallHTTP()); + return new HttpExecutor(callTask.getCallHTTP(), exprFactory); } } throw new UnsupportedOperationException(task.get().getClass().getName() + " not supported yet"); diff --git a/impl/src/main/java/io/serverlessworkflow/impl/Expression.java b/impl/src/main/java/io/serverlessworkflow/impl/Expression.java new file mode 100644 index 00000000..b5bbfc0b --- /dev/null +++ b/impl/src/main/java/io/serverlessworkflow/impl/Expression.java @@ -0,0 +1,22 @@ +/* + * 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; + +public interface Expression { + JsonNode eval(JsonNode input); +} diff --git a/impl/src/main/java/io/serverlessworkflow/impl/ExpressionFactory.java b/impl/src/main/java/io/serverlessworkflow/impl/ExpressionFactory.java new file mode 100644 index 00000000..8f9c1dd1 --- /dev/null +++ b/impl/src/main/java/io/serverlessworkflow/impl/ExpressionFactory.java @@ -0,0 +1,21 @@ +/* + * 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; + +public interface ExpressionFactory { + + Expression getExpression(String expression); +} diff --git a/impl/src/main/java/io/serverlessworkflow/impl/ExpressionUtils.java b/impl/src/main/java/io/serverlessworkflow/impl/ExpressionUtils.java new file mode 100644 index 00000000..45000931 --- /dev/null +++ b/impl/src/main/java/io/serverlessworkflow/impl/ExpressionUtils.java @@ -0,0 +1,40 @@ +/* + * 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; + +public class ExpressionUtils { + + private static final String EXPR_PREFIX = "${"; + private static final String EXPR_SUFFIX = "}"; + + private ExpressionUtils() {} + + public static String trimExpr(String expr) { + expr = expr.trim(); + if (expr.startsWith(EXPR_PREFIX)) { + expr = trimExpr(expr, EXPR_PREFIX, EXPR_SUFFIX); + } + return expr.trim(); + } + + private static String trimExpr(String expr, String prefix, String suffix) { + expr = expr.substring(prefix.length()); + if (expr.endsWith(suffix)) { + expr = expr.substring(0, expr.length() - suffix.length()); + } + return expr; + } +} diff --git a/impl/src/main/java/io/serverlessworkflow/impl/HttpExecutor.java b/impl/src/main/java/io/serverlessworkflow/impl/HttpExecutor.java index 203ca15d..e2c2c42f 100644 --- a/impl/src/main/java/io/serverlessworkflow/impl/HttpExecutor.java +++ b/impl/src/main/java/io/serverlessworkflow/impl/HttpExecutor.java @@ -18,7 +18,10 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.api.types.CallHTTP; +import io.serverlessworkflow.api.types.Endpoint; +import io.serverlessworkflow.api.types.EndpointUri; import io.serverlessworkflow.api.types.HTTPArguments; +import io.serverlessworkflow.api.types.UriTemplate; import io.serverlessworkflow.api.types.WithHTTPHeaders; import io.serverlessworkflow.api.types.WithHTTPQuery; import jakarta.ws.rs.HttpMethod; @@ -27,40 +30,33 @@ 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.function.Function; public class HttpExecutor extends AbstractTaskExecutor { private static final Client client = ClientBuilder.newClient(); - public HttpExecutor(CallHTTP task) { - super(task); + private final Function targetSupplier; + + public HttpExecutor(CallHTTP task, ExpressionFactory factory) { + super(task, factory); + this.targetSupplier = getTargetSupplier(task.getWith().getEndpoint()); } @Override protected JsonNode internalExecute(JsonNode node) { HTTPArguments httpArgs = task.getWith(); - // missing checks - String uri = - httpArgs - .getEndpoint() - .getEndpointConfiguration() - .getUri() - .getLiteralEndpointURI() - .getLiteralUriTemplate(); - WebTarget target = client.target(uri); WithHTTPQuery query = httpArgs.getQuery(); + WebTarget target = targetSupplier.apply(node); if (query != null) { for (Entry entry : query.getAdditionalProperties().entrySet()) { target = target.queryParam(entry.getKey(), entry.getValue()); } } - Builder request = - target - .resolveTemplates( - JsonUtils.mapper().convertValue(node, new TypeReference>() {})) - .request(); + Builder request = target.request(); WithHTTPHeaders headers = httpArgs.getHeaders(); if (headers != null) { headers.getAdditionalProperties().forEach(request::header); @@ -73,4 +69,71 @@ protected JsonNode internalExecute(JsonNode node) { return request.post(Entity.json(httpArgs.getBody()), JsonNode.class); } } + + private Function getTargetSupplier(Endpoint endpoint) { + 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()); + } + } else if (endpoint.getRuntimeExpression() != null) { + return new ExpressionURISupplier(endpoint.getRuntimeExpression()); + } else if (endpoint.getUriTemplate() != null) { + return getURISupplier(endpoint.getUriTemplate()); + } + throw new IllegalArgumentException("Invalid endpoint definition " + endpoint); + } + + private Function getURISupplier(UriTemplate template) { + if (template.getLiteralUri() != null) { + return new URISupplier(template.getLiteralUri()); + } else if (template.getLiteralUriTemplate() != null) { + return new URITemplateSupplier(template.getLiteralUriTemplate()); + } + throw new IllegalArgumentException("Invalid uritemplate definition " + template); + } + + private class URISupplier implements Function { + private final URI uri; + + public URISupplier(URI uri) { + this.uri = uri; + } + + @Override + public WebTarget apply(JsonNode input) { + return client.target(uri); + } + } + + private class URITemplateSupplier implements Function { + private final String uri; + + public URITemplateSupplier(String uri) { + this.uri = uri; + } + + @Override + public WebTarget apply(JsonNode input) { + return client + .target(uri) + .resolveTemplates( + JsonUtils.mapper().convertValue(input, new TypeReference>() {})); + } + } + + private class ExpressionURISupplier implements Function { + private Expression expr; + + public ExpressionURISupplier(String expr) { + this.expr = exprFactory.getExpression(expr); + } + + @Override + public WebTarget apply(JsonNode input) { + return client.target(expr.eval(input).asText()); + } + } } diff --git a/impl/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java b/impl/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java index 8c8dad4f..f926a755 100644 --- a/impl/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java +++ b/impl/src/main/java/io/serverlessworkflow/impl/WorkflowDefinition.java @@ -33,22 +33,22 @@ public class WorkflowDefinition { private WorkflowDefinition( Workflow workflow, - TaskExecutorFactory factory, + TaskExecutorFactory taskFactory, Collection listeners) { this.workflow = workflow; - this.factory = factory; + this.taskFactory = taskFactory; this.listeners = listeners; } private final Workflow workflow; private final Collection listeners; - private final TaskExecutorFactory factory; + private final TaskExecutorFactory taskFactory; private final Map> taskExecutors = new ConcurrentHashMap<>(); public static class Builder { private final Workflow workflow; - private TaskExecutorFactory factory = DefaultTaskExecutorFactory.get(); + private TaskExecutorFactory taskFactory = DefaultTaskExecutorFactory.get(); private Collection listeners; private Builder(Workflow workflow) { @@ -64,14 +64,14 @@ public Builder withListener(WorkflowExecutionListener listener) { } public Builder withTaskExecutorFactory(TaskExecutorFactory factory) { - this.factory = factory; + this.taskFactory = factory; return this; } public WorkflowDefinition build() { return new WorkflowDefinition( workflow, - factory, + taskFactory, listeners == null ? Collections.emptySet() : Collections.unmodifiableCollection(listeners)); @@ -83,7 +83,7 @@ public static Builder builder(Workflow workflow) { } public WorkflowInstance execute(Object input) { - return new WorkflowInstance(factory, JsonUtils.fromValue(input)); + return new WorkflowInstance(taskFactory, JsonUtils.fromValue(input)); } enum State { @@ -97,7 +97,6 @@ public class WorkflowInstance { private final JsonNode input; private JsonNode output; private State state; - private TaskExecutorFactory factory; private JsonPointer currentPos; @@ -106,7 +105,6 @@ private WorkflowInstance(TaskExecutorFactory factory, JsonNode input) { this.output = object(); this.state = State.STARTED; this.currentPos = JsonPointer.compile("/"); - this.factory = factory; processDo(workflow.getDo()); } @@ -119,7 +117,7 @@ private void processDo(List tasks) { this.output = MergeUtils.merge( taskExecutors - .computeIfAbsent(currentPos, k -> factory.getTaskExecutor(task.getTask())) + .computeIfAbsent(currentPos, k -> taskFactory.getTaskExecutor(task.getTask())) .apply(input), output); listeners.forEach(l -> l.onTaskEnded(currentPos, task.getTask())); diff --git a/impl/src/main/java/io/serverlessworkflow/impl/jq/JQExpression.java b/impl/src/main/java/io/serverlessworkflow/impl/jq/JQExpression.java new file mode 100644 index 00000000..b77f34a2 --- /dev/null +++ b/impl/src/main/java/io/serverlessworkflow/impl/jq/JQExpression.java @@ -0,0 +1,251 @@ +/* + * 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.jq; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import io.serverlessworkflow.impl.Expression; +import io.serverlessworkflow.impl.JsonUtils; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; +import net.thisptr.jackson.jq.Output; +import net.thisptr.jackson.jq.Scope; +import net.thisptr.jackson.jq.Version; +import net.thisptr.jackson.jq.exception.JsonQueryException; +import net.thisptr.jackson.jq.internal.javacc.ExpressionParser; +import net.thisptr.jackson.jq.internal.tree.FunctionCall; +import net.thisptr.jackson.jq.internal.tree.StringInterpolation; +import net.thisptr.jackson.jq.internal.tree.binaryop.BinaryOperatorExpression; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JQExpression implements Expression { + + private static final Logger logger = LoggerFactory.getLogger(JQExpression.class); + private final Map, Collection> + declaredFieldsMap = new ConcurrentHashMap<>(); + private final Map, Collection> + allFieldsMap = new ConcurrentHashMap<>(); + + private final Supplier scope; + private final String expr; + + private net.thisptr.jackson.jq.Expression internalExpr; + private static Field rhsField; + + static { + try { + rhsField = BinaryOperatorExpression.class.getDeclaredField("rhs"); + rhsField.setAccessible(true); + } catch (ReflectiveOperationException e) { + logger.warn("Unexpected exception while resolving rhs field", e); + } + } + + public JQExpression(Supplier scope, String expr, Version version) + throws JsonQueryException { + this.expr = expr; + this.scope = scope; + this.internalExpr = compile(version); + checkFunctionCall(internalExpr); + } + + private net.thisptr.jackson.jq.Expression compile(Version version) throws JsonQueryException { + net.thisptr.jackson.jq.Expression expression; + try { + expression = ExpressionParser.compile(expr, version); + } catch (JsonQueryException ex) { + expression = handleStringInterpolation(version).orElseThrow(() -> ex); + } + checkFunctionCall(expression); + return expression; + } + + private Optional handleStringInterpolation(Version version) { + if (!expr.startsWith("\"")) { + try { + net.thisptr.jackson.jq.Expression expression = + ExpressionParser.compile("\"" + expr + "\"", version); + if (expression instanceof StringInterpolation) { + return Optional.of(expression); + } + } catch (JsonQueryException ex) { + // ignoring it + } + } + return Optional.empty(); + } + + private interface TypedOutput extends Output { + T getResult(); + } + + @SuppressWarnings("unchecked") + private TypedOutput output(Class returnClass) { + TypedOutput out; + if (String.class.isAssignableFrom(returnClass)) { + out = (TypedOutput) new StringOutput(); + } else if (Collection.class.isAssignableFrom(returnClass)) { + out = (TypedOutput) new CollectionOutput(); + } else { + out = (TypedOutput) new JsonNodeOutput(); + } + return out; + } + + private static class StringOutput implements TypedOutput { + StringBuilder sb = new StringBuilder(); + + @Override + public void emit(JsonNode out) throws JsonQueryException { + if (sb.length() > 0) { + sb.append(' '); + } + if (!out.isNull() && out.asText() != null) { + sb.append(out.asText()); + } + } + + @Override + public String getResult() { + return sb.toString(); + } + } + + private static class CollectionOutput implements TypedOutput> { + Collection result = new ArrayList<>(); + + @SuppressWarnings("unchecked") + @Override + public void emit(JsonNode out) throws JsonQueryException { + Object obj = JsonUtils.toJavaValue(out); + if (obj instanceof Collection) result.addAll((Collection) obj); + else { + result.add(obj); + } + } + + @Override + public Collection getResult() { + return result; + } + } + + private static class JsonNodeOutput implements TypedOutput { + + private JsonNode result; + private boolean arrayCreated; + + @Override + public void emit(JsonNode out) throws JsonQueryException { + if (this.result == null) { + this.result = out; + } else if (!arrayCreated) { + ArrayNode newNode = JsonUtils.mapper().createArrayNode(); + newNode.add(this.result).add(out); + this.result = newNode; + arrayCreated = true; + } else { + ((ArrayNode) this.result).add(out); + } + } + + @Override + public JsonNode getResult() { + return result; + } + } + + @Override + public JsonNode eval(JsonNode context) { + TypedOutput output = output(JsonNode.class); + try { + internalExpr.apply(this.scope.get(), context, output); + return output.getResult(); + } catch (JsonQueryException e) { + throw new IllegalArgumentException( + "Unable to evaluate content " + context + " using expr " + expr, e); + } + } + + private void checkFunctionCall(net.thisptr.jackson.jq.Expression toCheck) + throws JsonQueryException { + if (toCheck instanceof FunctionCall) { + toCheck.apply(scope.get(), JsonUtils.mapper().createObjectNode(), out -> {}); + } else if (toCheck instanceof BinaryOperatorExpression) { + if (rhsField != null) { + try { + checkFunctionCall((net.thisptr.jackson.jq.Expression) rhsField.get(toCheck)); + } catch (ReflectiveOperationException e) { + logger.warn( + "Ignoring unexpected error {} while accesing field {} for class{} and expression {}", + e.getMessage(), + rhsField.getName(), + toCheck.getClass(), + expr); + } + } + } else if (toCheck != null) { + for (Field f : getAllExprFields(toCheck)) + try { + checkFunctionCall((net.thisptr.jackson.jq.Expression) f.get(toCheck)); + } catch (ReflectiveOperationException e) { + logger.warn( + "Ignoring unexpected error {} while accesing field {} for class{} and expression {}", + e.getMessage(), + f.getName(), + toCheck.getClass(), + expr); + } + } + } + + private Collection getAllExprFields(net.thisptr.jackson.jq.Expression toCheck) { + return allFieldsMap.computeIfAbsent(toCheck.getClass(), this::getAllExprFields); + } + + private Collection getAllExprFields( + Class clazz) { + Collection fields = new HashSet<>(); + Class currentClass = clazz; + do { + fields.addAll( + declaredFieldsMap.computeIfAbsent( + currentClass.asSubclass(net.thisptr.jackson.jq.Expression.class), + this::getDeclaredExprFields)); + currentClass = currentClass.getSuperclass(); + } while (net.thisptr.jackson.jq.Expression.class.isAssignableFrom(currentClass)); + return fields; + } + + private Collection getDeclaredExprFields( + Class clazz) { + Collection fields = new HashSet<>(); + for (Field f : clazz.getDeclaredFields()) { + if (net.thisptr.jackson.jq.Expression.class.isAssignableFrom(f.getType())) { + f.setAccessible(true); + fields.add(f); + } + } + return fields; + } +} diff --git a/impl/src/main/java/io/serverlessworkflow/impl/jq/JQExpressionFactory.java b/impl/src/main/java/io/serverlessworkflow/impl/jq/JQExpressionFactory.java new file mode 100644 index 00000000..787842d6 --- /dev/null +++ b/impl/src/main/java/io/serverlessworkflow/impl/jq/JQExpressionFactory.java @@ -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.jq; + +import io.serverlessworkflow.impl.Expression; +import io.serverlessworkflow.impl.ExpressionFactory; +import io.serverlessworkflow.impl.ExpressionUtils; +import java.util.function.Supplier; +import net.thisptr.jackson.jq.BuiltinFunctionLoader; +import net.thisptr.jackson.jq.Scope; +import net.thisptr.jackson.jq.Versions; +import net.thisptr.jackson.jq.exception.JsonQueryException; + +public class JQExpressionFactory implements ExpressionFactory { + + private JQExpressionFactory() {} + + private static final JQExpressionFactory instance = new JQExpressionFactory(); + + public static JQExpressionFactory get() { + return instance; + } + + private static Supplier scopeSupplier = new DefaultScopeSupplier(); + + private static class DefaultScopeSupplier implements Supplier { + private static class DefaultScope { + private static Scope scope; + + static { + scope = Scope.newEmptyScope(); + BuiltinFunctionLoader.getInstance().loadFunctions(Versions.JQ_1_6, scope); + } + } + + @Override + public Scope get() { + return DefaultScope.scope; + } + } + + @Override + public Expression getExpression(String expression) { + try { + return new JQExpression(scopeSupplier, ExpressionUtils.trimExpr(expression), Versions.JQ_1_6); + } catch (JsonQueryException e) { + throw new IllegalArgumentException(e); + } + } +} diff --git a/impl/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java b/impl/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java index f5feb513..66ef5d86 100644 --- a/impl/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java +++ b/impl/src/test/java/io/serverlessworkflow/impl/WorkflowDefinitionTest.java @@ -41,11 +41,12 @@ void testWorkflowExecution(String fileName, Object input, Condition cond } private static Stream provideParameters() { + Map petInput = Map.of("petId", 10); + Condition petCondition = + new Condition<>( + o -> ((Map) o).containsKey("photoUrls"), "callHttpCondition"); return Stream.of( - Arguments.of( - "callHttp.yaml", - Map.of("petId", 1), - new Condition<>( - o -> ((Map) o).containsKey("photoUrls"), "callHttpCondition"))); + Arguments.of("callHttp.yaml", petInput, petCondition), + Arguments.of("call-http-endpoint-interpolation.yaml", petInput, petCondition)); } } diff --git a/impl/src/test/resources/call-http-endpoint-interpolation.yaml b/impl/src/test/resources/call-http-endpoint-interpolation.yaml new file mode 100644 index 00000000..8380a9aa --- /dev/null +++ b/impl/src/test/resources/call-http-endpoint-interpolation.yaml @@ -0,0 +1,13 @@ +document: + dsl: '1.0.0-alpha5' + namespace: examples + name: call-http-shorthand-endpoint + version: '0.1.0' +do: + - getPet: + call: http + with: + headers: + content-type: application/json + method: get + endpoint: ${ "https://petstore.swagger.io/v2/pet/\(.petId)" } \ No newline at end of file diff --git a/impl/src/main/resources/callHttp.yaml b/impl/src/test/resources/callHttp.yaml similarity index 100% rename from impl/src/main/resources/callHttp.yaml rename to impl/src/test/resources/callHttp.yaml pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy