Skip to content

Commit ee44c19

Browse files
authored
Add OpenAPI task implementation (#804)
* Added OpenAPI call task support Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com> * tests Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com> * tests and refactoring Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com> * post review fix Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com> * post review Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com> * remove timeouts in HttpCallAdapter improve ExpressionURISupplier Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com> --------- Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com>
1 parent 1131a3a commit ee44c19

File tree

22 files changed

+1637
-57
lines changed

22 files changed

+1637
-57
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.impl.executors.http;
17+
18+
import io.serverlessworkflow.api.types.UriTemplate;
19+
import io.serverlessworkflow.impl.TaskContext;
20+
import io.serverlessworkflow.impl.WorkflowContext;
21+
import io.serverlessworkflow.impl.WorkflowModel;
22+
import io.serverlessworkflow.impl.WorkflowValueResolver;
23+
import jakarta.ws.rs.client.WebTarget;
24+
25+
public class ExpressionURISupplier implements TargetSupplier {
26+
private WorkflowValueResolver<String> expr;
27+
28+
public ExpressionURISupplier(WorkflowValueResolver<String> expr) {
29+
this.expr = expr;
30+
}
31+
32+
@Override
33+
public WebTarget apply(WorkflowContext workflow, TaskContext task, WorkflowModel node) {
34+
return HttpExecutor.getURISupplier(
35+
new UriTemplate().withLiteralUriTemplate(expr.apply(workflow, task, node)))
36+
.apply(workflow, task, node);
37+
}
38+
}

impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpExecutor.java

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,6 @@ public class HttpExecutor implements CallableTask<CallHTTP> {
5656
private RequestSupplier requestFunction;
5757
private HttpModelConverter converter = new HttpModelConverter() {};
5858

59-
@FunctionalInterface
60-
private interface TargetSupplier {
61-
WebTarget apply(WorkflowContext workflow, TaskContext task, WorkflowModel node);
62-
}
63-
6459
@FunctionalInterface
6560
private interface RequestSupplier {
6661
WorkflowModel apply(
@@ -172,7 +167,7 @@ public boolean accept(Class<? extends TaskBase> clazz) {
172167
return clazz.equals(CallHTTP.class);
173168
}
174169

175-
private static TargetSupplier getTargetSupplier(
170+
public static TargetSupplier getTargetSupplier(
176171
Endpoint endpoint, ExpressionFactory expressionFactory) {
177172
if (endpoint.getEndpointConfiguration() != null) {
178173
EndpointUri uri = endpoint.getEndpointConfiguration().getUri();
@@ -193,7 +188,7 @@ private static TargetSupplier getTargetSupplier(
193188
throw new IllegalArgumentException("Invalid endpoint definition " + endpoint);
194189
}
195190

196-
private static TargetSupplier getURISupplier(UriTemplate template) {
191+
public static TargetSupplier getURISupplier(UriTemplate template) {
197192
if (template.getLiteralUri() != null) {
198193
return (w, t, n) -> client.target(template.getLiteralUri());
199194
} else if (template.getLiteralUriTemplate() != null) {
@@ -202,17 +197,4 @@ private static TargetSupplier getURISupplier(UriTemplate template) {
202197
}
203198
throw new IllegalArgumentException("Invalid uritemplate definition " + template);
204199
}
205-
206-
private static class ExpressionURISupplier implements TargetSupplier {
207-
private WorkflowValueResolver<String> expr;
208-
209-
public ExpressionURISupplier(WorkflowValueResolver<String> expr) {
210-
this.expr = expr;
211-
}
212-
213-
@Override
214-
public WebTarget apply(WorkflowContext workflow, TaskContext task, WorkflowModel node) {
215-
return client.target(expr.apply(workflow, task, node));
216-
}
217-
}
218200
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.impl.executors.http;
17+
18+
import io.serverlessworkflow.impl.TaskContext;
19+
import io.serverlessworkflow.impl.WorkflowContext;
20+
import io.serverlessworkflow.impl.WorkflowModel;
21+
import jakarta.ws.rs.client.WebTarget;
22+
23+
public interface TargetSupplier {
24+
WebTarget apply(WorkflowContext workflow, TaskContext task, WorkflowModel node);
25+
}

impl/openapi/pom.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<groupId>io.serverlessworkflow</groupId>
6+
<artifactId>serverlessworkflow-impl</artifactId>
7+
<version>8.0.0-SNAPSHOT</version>
8+
</parent>
9+
<artifactId>serverlessworkflow-impl-openapi</artifactId>
10+
<name>Serverless Workflow :: Impl :: OpenAPI</name>
11+
<dependencies>
12+
<dependency>
13+
<groupId>jakarta.ws.rs</groupId>
14+
<artifactId>jakarta.ws.rs-api</artifactId>
15+
</dependency>
16+
<dependency>
17+
<groupId>io.serverlessworkflow</groupId>
18+
<artifactId>serverlessworkflow-impl-core</artifactId>
19+
</dependency>
20+
<dependency>
21+
<groupId>io.serverlessworkflow</groupId>
22+
<artifactId>serverlessworkflow-impl-http</artifactId>
23+
</dependency>
24+
<dependency>
25+
<groupId>io.swagger.parser.v3</groupId>
26+
<artifactId>swagger-parser</artifactId>
27+
<version>${version.io.swagger.parser.v3}</version>
28+
</dependency>
29+
</dependencies>
30+
</project>
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.impl.executors.openapi;
17+
18+
import io.serverlessworkflow.api.types.CallHTTP;
19+
import io.serverlessworkflow.api.types.Endpoint;
20+
import io.serverlessworkflow.api.types.EndpointConfiguration;
21+
import io.serverlessworkflow.api.types.HTTPArguments;
22+
import io.serverlessworkflow.api.types.HTTPHeaders;
23+
import io.serverlessworkflow.api.types.HTTPQuery;
24+
import io.serverlessworkflow.api.types.Headers;
25+
import io.serverlessworkflow.api.types.Query;
26+
import io.serverlessworkflow.api.types.ReferenceableAuthenticationPolicy;
27+
import io.serverlessworkflow.api.types.UriTemplate;
28+
import io.swagger.v3.oas.models.media.Schema;
29+
import io.swagger.v3.oas.models.parameters.Parameter;
30+
import java.net.URI;
31+
import java.util.Collection;
32+
import java.util.LinkedHashMap;
33+
import java.util.Map;
34+
35+
@SuppressWarnings("rawtypes")
36+
class HttpCallAdapter {
37+
38+
private ReferenceableAuthenticationPolicy auth;
39+
private Map<String, Schema> body;
40+
private String contentType;
41+
private Collection<Parameter> headers;
42+
private String method;
43+
private Collection<Parameter> query;
44+
private boolean redirect;
45+
private URI server;
46+
private URI target;
47+
private Map<String, Object> workflowParams;
48+
49+
HttpCallAdapter auth(ReferenceableAuthenticationPolicy policy) {
50+
if (policy != null) {
51+
this.auth = policy;
52+
}
53+
return this;
54+
}
55+
56+
HttpCallAdapter body(Map<String, Schema> body) {
57+
this.body = body;
58+
return this;
59+
}
60+
61+
CallHTTP build() {
62+
CallHTTP callHTTP = new CallHTTP();
63+
64+
HTTPArguments httpArgs = new HTTPArguments();
65+
callHTTP.withWith(httpArgs);
66+
67+
Endpoint endpoint = new Endpoint();
68+
httpArgs.withEndpoint(endpoint);
69+
70+
if (this.auth != null) {
71+
EndpointConfiguration endPointConfig = new EndpointConfiguration();
72+
endPointConfig.setAuthentication(this.auth);
73+
endpoint.setEndpointConfiguration(endPointConfig);
74+
}
75+
76+
httpArgs.setRedirect(this.redirect);
77+
httpArgs.setMethod(this.method);
78+
79+
addHttpHeaders(httpArgs);
80+
addQueryParams(httpArgs);
81+
addBody(httpArgs);
82+
83+
addTarget(endpoint);
84+
85+
return callHTTP;
86+
}
87+
88+
HttpCallAdapter contentType(String contentType) {
89+
this.contentType = contentType;
90+
return this;
91+
}
92+
93+
HttpCallAdapter headers(Collection<io.swagger.v3.oas.models.parameters.Parameter> headers) {
94+
this.headers = headers;
95+
return this;
96+
}
97+
98+
HttpCallAdapter method(String method) {
99+
this.method = method;
100+
return this;
101+
}
102+
103+
HttpCallAdapter query(Collection<io.swagger.v3.oas.models.parameters.Parameter> query) {
104+
this.query = query;
105+
return this;
106+
}
107+
108+
HttpCallAdapter redirect(boolean redirect) {
109+
this.redirect = redirect;
110+
return this;
111+
}
112+
113+
HttpCallAdapter server(String server) {
114+
this.server = URI.create(server);
115+
return this;
116+
}
117+
118+
HttpCallAdapter target(URI target) {
119+
this.target = target;
120+
return this;
121+
}
122+
123+
HttpCallAdapter workflowParams(Map<String, Object> workflowParams) {
124+
this.workflowParams = workflowParams;
125+
return this;
126+
}
127+
128+
private void addBody(HTTPArguments httpArgs) {
129+
Map<String, Object> bodyContent = new LinkedHashMap<>();
130+
if (!(body == null || body.isEmpty())) {
131+
for (Map.Entry<String, Schema> entry : body.entrySet()) {
132+
String name = entry.getKey();
133+
if (workflowParams.containsKey(name)) {
134+
Object value = workflowParams.get(name);
135+
bodyContent.put(name, value);
136+
}
137+
}
138+
if (!bodyContent.isEmpty()) {
139+
httpArgs.setBody(bodyContent);
140+
}
141+
}
142+
}
143+
144+
private void addHttpHeaders(HTTPArguments httpArgs) {
145+
if (!(headers == null || headers.isEmpty())) {
146+
Headers hdrs = new Headers();
147+
HTTPHeaders httpHeaders = new HTTPHeaders();
148+
hdrs.setHTTPHeaders(httpHeaders);
149+
httpArgs.setHeaders(hdrs);
150+
151+
for (Parameter p : headers) {
152+
String name = p.getName();
153+
if (workflowParams.containsKey(name)) {
154+
Object value = workflowParams.get(name);
155+
if (value instanceof String asString) {
156+
httpHeaders.setAdditionalProperty(name, asString);
157+
} else {
158+
throw new IllegalArgumentException("Header parameter " + name + " must be a String");
159+
}
160+
}
161+
}
162+
}
163+
}
164+
165+
private void addQueryParams(HTTPArguments httpArgs) {
166+
if (!(query == null || query.isEmpty())) {
167+
Query queryParams = new Query();
168+
httpArgs.setQuery(queryParams);
169+
HTTPQuery httpQuery = new HTTPQuery();
170+
queryParams.setHTTPQuery(httpQuery);
171+
172+
for (Parameter p : query) {
173+
String name = p.getName();
174+
if (workflowParams.containsKey(name)) {
175+
Object value = workflowParams.get(name);
176+
if (value instanceof String asString) {
177+
httpQuery.setAdditionalProperty(name, asString);
178+
} else if (value instanceof Number asNumber) {
179+
httpQuery.setAdditionalProperty(name, asNumber.toString());
180+
} else if (value instanceof Boolean asBoolean) {
181+
httpQuery.setAdditionalProperty(name, asBoolean.toString());
182+
} else if (value instanceof Character asCharacter) {
183+
httpQuery.setAdditionalProperty(name, asCharacter.toString());
184+
} else {
185+
httpQuery.setAdditionalProperty(name, value.toString());
186+
}
187+
}
188+
}
189+
}
190+
}
191+
192+
private void addTarget(Endpoint endpoint) {
193+
if (this.target == null) {
194+
throw new IllegalArgumentException("No Server defined for the OpenAPI operation");
195+
}
196+
UriTemplate uriTemplate = new UriTemplate();
197+
uriTemplate.withLiteralUri(this.server.resolve(this.target.getPath()));
198+
endpoint.setUriTemplate(uriTemplate);
199+
}
200+
}

0 commit comments

Comments
 (0)