Skip to content

Commit ddd12c4

Browse files
committed
Start reading OpenAPI from classpath
Signed-off-by: Matheus Cruz <[email protected]>
1 parent 03543dd commit ddd12c4

File tree

5 files changed

+1213
-12
lines changed

5 files changed

+1213
-12
lines changed

impl/core/src/main/java/io/serverlessworkflow/impl/WorkflowUtils.java

+14
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import io.serverlessworkflow.impl.json.JsonUtils;
3434
import io.serverlessworkflow.impl.jsonschema.SchemaValidator;
3535
import io.serverlessworkflow.impl.jsonschema.SchemaValidatorFactory;
36+
import io.serverlessworkflow.impl.resources.ClasspathResource;
3637
import io.serverlessworkflow.impl.resources.ResourceLoader;
3738
import io.serverlessworkflow.impl.resources.StaticResource;
3839

@@ -72,6 +73,19 @@ public static Optional<JsonNode> schemaToNode(ResourceLoader resourceLoader, Sch
7273
return Optional.empty();
7374
}
7475

76+
public static Optional<JsonNode> classpathResourceToNode(String resource) {
77+
if (resource != null && !resource.isEmpty()) {
78+
ClasspathResource classpathResource = new ClasspathResource(resource);
79+
ObjectMapper mapper = WorkflowFormat.fromFileName(resource).mapper();
80+
try (InputStream in = classpathResource.open()) {
81+
return Optional.of(mapper.readTree(in));
82+
} catch (IOException io) {
83+
throw new UncheckedIOException(io);
84+
}
85+
}
86+
return Optional.empty();
87+
}
88+
7589
public static Optional<WorkflowFilter> buildWorkflowFilter(
7690
ExpressionFactory exprFactory, InputFrom from) {
7791
return from != null

impl/openapi/src/main/java/io/serverlessworkflow/impl/executors/OpenAPIExecutor.java

+103-11
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,118 @@
1515
*/
1616
package io.serverlessworkflow.impl.executors;
1717

18+
import com.fasterxml.jackson.core.type.TypeReference;
1819
import com.fasterxml.jackson.databind.JsonNode;
1920
import io.serverlessworkflow.api.types.CallOpenAPI;
21+
import io.serverlessworkflow.api.types.Endpoint;
22+
import io.serverlessworkflow.api.types.EndpointUri;
23+
import io.serverlessworkflow.api.types.OpenAPIArguments;
2024
import io.serverlessworkflow.api.types.TaskBase;
25+
import io.serverlessworkflow.api.types.UriTemplate;
2126
import io.serverlessworkflow.impl.TaskContext;
2227
import io.serverlessworkflow.impl.WorkflowContext;
2328
import io.serverlessworkflow.impl.WorkflowDefinition;
29+
import io.serverlessworkflow.impl.WorkflowUtils;
30+
import io.serverlessworkflow.impl.expressions.Expression;
31+
import io.serverlessworkflow.impl.expressions.ExpressionFactory;
32+
import io.serverlessworkflow.impl.json.JsonUtils;
33+
import jakarta.ws.rs.client.Client;
34+
import jakarta.ws.rs.client.ClientBuilder;
35+
import jakarta.ws.rs.client.WebTarget;
36+
37+
import java.util.Map;
38+
import java.util.Optional;
2439

2540
public class OpenAPIExecutor implements CallableTask<CallOpenAPI> {
41+
private static final Client client = ClientBuilder.newClient();
42+
private TargetSupplier targetSupplier;
43+
44+
@FunctionalInterface
45+
private interface TargetSupplier {
46+
WebTarget apply(WorkflowContext workflow, TaskContext<?> task, JsonNode node);
47+
}
48+
49+
@Override
50+
public void init(CallOpenAPI task, WorkflowDefinition definition) {
51+
OpenAPIArguments args = task.getWith();
52+
this.targetSupplier = getTargetSupplier(args.getDocument().getEndpoint(), definition.expressionFactory(), args.getOperationId());
53+
}
54+
55+
@Override
56+
public JsonNode apply(
57+
WorkflowContext workflowContext, TaskContext<CallOpenAPI> taskContext, JsonNode input) {
58+
59+
WebTarget target = this.targetSupplier.apply(workflowContext, taskContext, input);
60+
61+
62+
return input;
63+
}
64+
65+
@Override
66+
public boolean accept(Class<? extends TaskBase> clazz) {
67+
return clazz.isAssignableFrom(CallOpenAPI.class);
68+
}
69+
70+
private static TargetSupplier getURISupplier(UriTemplate template, String operationId) {
71+
if (template.getLiteralUri() != null) {
72+
73+
Optional<JsonNode> jsonNode = WorkflowUtils.classpathResourceToNode(template.getLiteralUri().toString());
74+
75+
if (jsonNode.isEmpty()) {
76+
throw new IllegalArgumentException("Invalid OpenAPI specification " + template.getLiteralUri().toString());
77+
}
78+
79+
String host = OpenAPIReader.getHost(jsonNode.get());
80+
81+
JsonNode operation = OpenAPIReader.readOperation(jsonNode.get(), operationId);
82+
83+
if (operation == null) {
84+
throw new IllegalArgumentException("Invalid OpenAPI specification. There is no operation ID " + operationId);
85+
}
86+
87+
88+
89+
return (w, t, n) -> client.target(host);
90+
} else if (template.getLiteralUriTemplate() != null) {
91+
return (w, t, n) ->
92+
client
93+
.target(template.getLiteralUriTemplate())
94+
.resolveTemplates(
95+
JsonUtils.mapper().convertValue(n, new TypeReference<Map<String, Object>>() {
96+
}));
97+
}
98+
throw new IllegalArgumentException("Invalid uritemplate definition " + template);
99+
}
100+
101+
private static class ExpressionURISupplier implements TargetSupplier {
102+
private Expression expr;
26103

27-
@Override
28-
public void init(CallOpenAPI task, WorkflowDefinition definition) {}
104+
public ExpressionURISupplier(Expression expr) {
105+
this.expr = expr;
106+
}
29107

30-
@Override
31-
public JsonNode apply(
32-
WorkflowContext workflowContext, TaskContext<CallOpenAPI> taskContext, JsonNode input) {
33-
return input;
34-
}
108+
@Override
109+
public WebTarget apply(WorkflowContext workflow, TaskContext<?> task, JsonNode node) {
110+
return client.target(expr.eval(workflow, Optional.of(task), node).asText());
111+
}
112+
}
35113

36-
@Override
37-
public boolean accept(Class<? extends TaskBase> clazz) {
38-
return clazz.isAssignableFrom(CallOpenAPI.class);
39-
}
114+
private static TargetSupplier getTargetSupplier(
115+
Endpoint endpoint, ExpressionFactory expressionFactory, String operationId) {
116+
if (endpoint.getEndpointConfiguration() != null) {
117+
EndpointUri uri = endpoint.getEndpointConfiguration().getUri();
118+
if (uri.getLiteralEndpointURI() != null) {
119+
return getURISupplier(uri.getLiteralEndpointURI(), operationId);
120+
} else if (uri.getExpressionEndpointURI() != null) {
121+
return new ExpressionURISupplier(
122+
expressionFactory.getExpression(uri.getExpressionEndpointURI()));
123+
}
124+
} else if (endpoint.getRuntimeExpression() != null) {
125+
return new ExpressionURISupplier(
126+
expressionFactory.getExpression(endpoint.getRuntimeExpression()));
127+
} else if (endpoint.getUriTemplate() != null) {
128+
return getURISupplier(endpoint.getUriTemplate(), operationId);
129+
}
130+
throw new IllegalArgumentException("Invalid endpoint definition " + endpoint);
131+
}
40132
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.serverlessworkflow.impl.executors;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.fasterxml.jackson.databind.node.ArrayNode;
5+
6+
import java.util.Map;
7+
8+
public class OpenAPIReader {
9+
10+
public static String getHost(JsonNode jsonNode) {
11+
JsonNode host = jsonNode.get("host");
12+
if (host == null) {
13+
return null;
14+
}
15+
String scheme = getScheme(jsonNode);
16+
return scheme + "://" + host.asText();
17+
}
18+
19+
private static String getScheme(JsonNode jsonNode) {
20+
ArrayNode array = jsonNode.withArrayProperty("schemes");
21+
if (array != null && !array.isEmpty()) {
22+
// TODO: should get the first scheme?
23+
return array.get(0).asText();
24+
}
25+
// TODO: should the http be the default scheme?
26+
return "http";
27+
}
28+
29+
public static JsonNode readOperation(JsonNode jsonNode, String operationId) {
30+
JsonNode paths = jsonNode.get("paths");
31+
for (Map.Entry<String, JsonNode> entry : paths.properties()) {
32+
for (Map.Entry<String, JsonNode> httpMethod : entry.getValue().properties()) {
33+
if (httpMethod.getValue().get("operationId").asText().equals(operationId)) {
34+
return httpMethod.getValue();
35+
}
36+
}
37+
}
38+
39+
return null;
40+
}
41+
}

impl/openapi/src/test/resources/findPetsByStatus.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ do:
88
call: openapi
99
with:
1010
document:
11-
endpoint: https://petstore.swagger.io/v2/swagger.json
11+
endpoint: pets.json
1212
operationId: findPetsByStatus
1313
parameters:
1414
status: available

0 commit comments

Comments
 (0)