Skip to content

Commit 3745db6

Browse files
committed
tests
Signed-off-by: Dmitrii Tikhomirov <chani.liet@gmail.com>
1 parent 50aedb5 commit 3745db6

File tree

11 files changed

+677
-39
lines changed

11 files changed

+677
-39
lines changed

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
import io.serverlessworkflow.api.types.Headers;
2525
import io.serverlessworkflow.api.types.Query;
2626
import io.serverlessworkflow.api.types.ReferenceableAuthenticationPolicy;
27+
import io.serverlessworkflow.api.types.TaskTimeout;
28+
import io.serverlessworkflow.api.types.Timeout;
29+
import io.serverlessworkflow.api.types.TimeoutAfter;
2730
import io.serverlessworkflow.api.types.UriTemplate;
2831
import io.swagger.v3.oas.models.media.Schema;
2932
import io.swagger.v3.oas.models.parameters.Parameter;
@@ -82,6 +85,14 @@ public CallHTTP build() {
8285

8386
addTarget(endpoint);
8487

88+
TaskTimeout taskTimeout = new TaskTimeout();
89+
Timeout timeout = new Timeout();
90+
taskTimeout.withTaskTimeoutDefinition(timeout);
91+
TimeoutAfter timeoutAfter = new TimeoutAfter();
92+
timeout.setAfter(timeoutAfter);
93+
timeoutAfter.withDurationExpression("PT30S");
94+
callHTTP.setTimeout(taskTimeout);
95+
8596
return callHTTP;
8697
}
8798

@@ -176,8 +187,17 @@ private void addQueryParams(HTTPArguments httpArgs) {
176187
Object value = workflowParams.get(name);
177188
if (value instanceof String asString) {
178189
httpQuery.setAdditionalProperty(name, asString);
190+
} else if (value instanceof Number asNumber) {
191+
httpQuery.setAdditionalProperty(name, asNumber.toString());
192+
} else if (value instanceof Boolean asBoolean) {
193+
httpQuery.setAdditionalProperty(name, asBoolean.toString());
194+
} else if (value instanceof Character asCharacter) {
195+
httpQuery.setAdditionalProperty(name, asCharacter.toString());
179196
} else {
180-
throw new IllegalArgumentException("Query parameter " + name + " must be a String");
197+
throw new IllegalArgumentException(
198+
"Query parameter "
199+
+ name
200+
+ " must be a type of String, Number, Boolean or Character");
181201
}
182202
}
183203
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import java.net.URI;
3838
import java.util.Objects;
3939
import java.util.concurrent.CompletableFuture;
40-
import java.util.concurrent.ExecutionException;
4140
import java.util.stream.Collectors;
4241

4342
public class OpenAPIExecutor implements CallableTask<CallOpenAPI> {
@@ -129,7 +128,9 @@ public CompletableFuture<WorkflowModel> apply(
129128

130129
try {
131130
return executor.apply(workflowContext, taskContext, input).get();
132-
} catch (InterruptedException | RuntimeException | ExecutionException e) {
131+
} catch (Exception e) {
132+
133+
System.out.println("Call to " + server + " failed: " + e.getMessage());
133134
ex = new RuntimeException(e);
134135
}
135136
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public OperationDefinition parse() {
4747

4848
public OperationDefinition getOperation(OpenAPI openAPI) {
4949
if (openAPI == null || openAPI.getPaths() == null) {
50-
return null;
50+
throw new IllegalArgumentException("Invalid OpenAPI document");
5151
}
5252

5353
Set<String> paths = openAPI.getPaths().keySet();

impl/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@
6262
<artifactId>serverlessworkflow-impl-jq</artifactId>
6363
<version>${project.version}</version>
6464
</dependency>
65+
<dependency>
66+
<groupId>io.serverlessworkflow</groupId>
67+
<artifactId>serverlessworkflow-impl-openapi</artifactId>
68+
<version>${project.version}</version>
69+
</dependency>
6570
<dependency>
6671
<groupId>net.thisptr</groupId>
6772
<artifactId>jackson-jq</artifactId>

impl/test/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
<groupId>org.glassfish.jersey.media</groupId>
4242
<artifactId>jersey-media-json-jackson</artifactId>
4343
</dependency>
44+
<dependency>
45+
<groupId>io.serverlessworkflow</groupId>
46+
<artifactId>serverlessworkflow-impl-openapi</artifactId>
47+
</dependency>
4448
<dependency>
4549
<groupId>org.glassfish.jersey.core</groupId>
4650
<artifactId>jersey-client</artifactId>
@@ -75,6 +79,14 @@
7579
</dependency>
7680
</dependencies>
7781
<build>
82+
<resources>
83+
<resource>
84+
<directory>src/test/resources</directory>
85+
<includes>
86+
<include>**/*</include>
87+
</includes>
88+
</resource>
89+
</resources>
7890
<plugins>
7991
<plugin>
8092
<artifactId>maven-jar-plugin</artifactId>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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.test;
17+
18+
import com.fasterxml.jackson.databind.ObjectMapper;
19+
import java.nio.charset.StandardCharsets;
20+
import java.time.Instant;
21+
import java.util.Base64;
22+
import java.util.Map;
23+
24+
public class AccessTokenProvider {
25+
26+
private static final ObjectMapper MAPPER = new ObjectMapper();
27+
28+
public static String fakeAccessToken() throws Exception {
29+
long now = Instant.now().getEpochSecond();
30+
return fakeJwt(
31+
Map.of(
32+
"iss", "http://localhost:8888/realms/test-realm",
33+
"aud", "account",
34+
"sub", "test-subject",
35+
"azp", "serverless-workflow",
36+
"scope", "profile email",
37+
"exp", now + 3600,
38+
"iat", now));
39+
}
40+
41+
public static String fakeJwt(Map<String, Object> payload) throws Exception {
42+
String headerJson =
43+
MAPPER.writeValueAsString(
44+
Map.of(
45+
"alg", "RS256",
46+
"typ", "Bearer",
47+
"kid", "test"));
48+
String payloadJson = MAPPER.writeValueAsString(payload);
49+
return b64Url(headerJson) + "." + b64Url(payloadJson) + ".sig";
50+
}
51+
52+
private static String b64Url(String s) {
53+
return Base64.getUrlEncoder()
54+
.withoutPadding()
55+
.encodeToString(s.getBytes(StandardCharsets.UTF_8));
56+
}
57+
}

impl/test/src/test/java/io/serverlessworkflow/impl/test/OAuthHTTPWorkflowDefinitionTest.java

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,14 @@
1616
package io.serverlessworkflow.impl.test;
1717

1818
import static io.serverlessworkflow.api.WorkflowReader.readWorkflowFromClasspath;
19+
import static io.serverlessworkflow.impl.test.AccessTokenProvider.fakeAccessToken;
1920
import static org.junit.jupiter.api.Assertions.assertEquals;
2021
import static org.junit.jupiter.api.Assertions.assertTrue;
2122

2223
import com.fasterxml.jackson.databind.ObjectMapper;
2324
import io.serverlessworkflow.api.types.Workflow;
2425
import io.serverlessworkflow.impl.WorkflowApplication;
2526
import java.io.IOException;
26-
import java.nio.charset.StandardCharsets;
27-
import java.time.Instant;
28-
import java.util.Base64;
2927
import java.util.Map;
3028
import okhttp3.mockwebserver.MockResponse;
3129
import okhttp3.mockwebserver.MockWebServer;
@@ -818,34 +816,4 @@ public void testOAuthJSONClientCredentialsParamsNoEndpointWorkflowExecution() th
818816
assertEquals("/hello", petRequest.getPath());
819817
assertEquals("Bearer " + jwt, petRequest.getHeader("Authorization"));
820818
}
821-
822-
public static String fakeJwt(Map<String, Object> payload) throws Exception {
823-
String headerJson =
824-
MAPPER.writeValueAsString(
825-
Map.of(
826-
"alg", "RS256",
827-
"typ", "Bearer",
828-
"kid", "test"));
829-
String payloadJson = MAPPER.writeValueAsString(payload);
830-
return b64Url(headerJson) + "." + b64Url(payloadJson) + ".sig";
831-
}
832-
833-
private static String b64Url(String s) {
834-
return Base64.getUrlEncoder()
835-
.withoutPadding()
836-
.encodeToString(s.getBytes(StandardCharsets.UTF_8));
837-
}
838-
839-
public static String fakeAccessToken() throws Exception {
840-
long now = Instant.now().getEpochSecond();
841-
return fakeJwt(
842-
Map.of(
843-
"iss", "http://localhost:8888/realms/test-realm",
844-
"aud", "account",
845-
"sub", "test-subject",
846-
"azp", "serverless-workflow",
847-
"scope", "profile email",
848-
"exp", now + 3600,
849-
"iat", now));
850-
}
851819
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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.test;
17+
18+
import static io.serverlessworkflow.api.WorkflowReader.readWorkflowFromClasspath;
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertTrue;
21+
22+
import com.fasterxml.jackson.databind.ObjectMapper;
23+
import io.serverlessworkflow.api.types.Workflow;
24+
import io.serverlessworkflow.impl.WorkflowApplication;
25+
import java.io.IOException;
26+
import java.net.URL;
27+
import java.nio.charset.StandardCharsets;
28+
import java.nio.file.Files;
29+
import java.nio.file.Path;
30+
import java.util.Map;
31+
import okhttp3.OkHttpClient;
32+
import okhttp3.mockwebserver.MockResponse;
33+
import okhttp3.mockwebserver.MockWebServer;
34+
import okhttp3.mockwebserver.RecordedRequest;
35+
import org.junit.jupiter.api.AfterEach;
36+
import org.junit.jupiter.api.BeforeEach;
37+
import org.junit.jupiter.api.Test;
38+
39+
public class OpenAPITest {
40+
41+
private static final ObjectMapper MAPPER = new ObjectMapper();
42+
43+
private MockWebServer authServer;
44+
private MockWebServer openApiServer;
45+
private MockWebServer restServer;
46+
47+
private OkHttpClient httpClient;
48+
49+
private static String PROJECT_JSON_SUCCESS =
50+
"""
51+
{
52+
"success": true,
53+
"data": {
54+
"id": 55504,
55+
"name": "CRM",
56+
"code": "crm-20251111",
57+
"ownerId": 12345,
58+
"members": [
59+
12345,
60+
67890
61+
],
62+
"created_at": "2025-09-20T00:58:50.170784Z"
63+
}
64+
}
65+
""";
66+
67+
@BeforeEach
68+
void setUp() throws IOException {
69+
authServer = new MockWebServer();
70+
authServer.start(8888);
71+
72+
openApiServer = new MockWebServer();
73+
openApiServer.start(8887);
74+
75+
restServer = new MockWebServer();
76+
restServer.start(8886);
77+
78+
httpClient = new OkHttpClient();
79+
}
80+
81+
@AfterEach
82+
void tearDown() throws IOException {
83+
authServer.shutdown();
84+
openApiServer.shutdown();
85+
restServer.shutdown();
86+
}
87+
88+
@Test
89+
public void testOpenAPIBearerQueryInlinedBodyWithPositiveResponce() throws Exception {
90+
Workflow workflow =
91+
readWorkflowFromClasspath("workflows-samples/openapi/project-post-positive.yaml");
92+
93+
URL url = this.getClass().getResource("/workflows-samples/openapi/schema.yaml");
94+
95+
Path workflowPath = Path.of(url.getPath());
96+
String yaml = Files.readString(workflowPath, StandardCharsets.UTF_8);
97+
98+
openApiServer.enqueue(
99+
new MockResponse()
100+
.setBody(yaml)
101+
.setHeader("Content-Type", "application/yaml")
102+
.setResponseCode(200));
103+
104+
restServer.enqueue(
105+
new MockResponse()
106+
.setBody(PROJECT_JSON_SUCCESS)
107+
.setHeader("Content-Type", "application/json")
108+
.setResponseCode(200));
109+
110+
Map<String, Object> result;
111+
112+
try (WorkflowApplication app = WorkflowApplication.builder().build()) {
113+
result =
114+
app.workflowDefinition(workflow).instance(Map.of()).start().get().asMap().orElseThrow();
115+
} catch (Exception e) {
116+
throw new RuntimeException("Workflow execution failed", e);
117+
}
118+
119+
RecordedRequest restRequest = restServer.takeRequest();
120+
assertEquals("POST", restRequest.getMethod());
121+
assertTrue(restRequest.getPath().startsWith("/projects?"));
122+
assertTrue(restRequest.getPath().contains("notifyMembers=true"));
123+
assertTrue(restRequest.getPath().contains("validateOnly=false"));
124+
assertTrue(restRequest.getPath().contains("lang=en"));
125+
assertEquals("application/json", restRequest.getHeader("Content-Type"));
126+
assertEquals("Bearer eyJhbnNpc2l0b3IuYm9sdXMubWFnbnVz", restRequest.getHeader("Authorization"));
127+
}
128+
}

impl/test/src/test/java/io/serverlessworkflow/impl/test/OpenIDCHTTPWorkflowDefinitionTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
package io.serverlessworkflow.impl.test;
1717

1818
import static io.serverlessworkflow.api.WorkflowReader.readWorkflowFromClasspath;
19-
import static io.serverlessworkflow.impl.test.OAuthHTTPWorkflowDefinitionTest.fakeAccessToken;
20-
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
import static io.serverlessworkflow.impl.test.AccessTokenProvider.fakeAccessToken;
20+
import static org.junit.Assert.assertEquals;
2121
import static org.junit.jupiter.api.Assertions.assertTrue;
2222

2323
import com.fasterxml.jackson.databind.ObjectMapper;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
document:
2+
dsl: '1.0.1'
3+
namespace: test
4+
name: openapi-example
5+
version: '0.1.0'
6+
do:
7+
- findPet:
8+
call: openapi
9+
with:
10+
document:
11+
endpoint: http://127.0.0.1:8887/schema.yaml
12+
operationId: addProject
13+
parameters:
14+
name: "New CRM"
15+
code: "crm-20251111"
16+
description: "Internal CRM for sales department"
17+
ownerId: 12345
18+
members:
19+
- 12345
20+
- 67890
21+
validateOnly: false
22+
notifyMembers: true
23+
lang: en
24+
Authorization: "Bearer eyJhbnNpc2l0b3IuYm9sdXMubWFnbnVz"

0 commit comments

Comments
 (0)