Skip to content

Commit c5e309c

Browse files
authored
Merge pull request #464 from fjtirado/Fix_#461
[Fix #461] oneOf options must inherit union class common part
2 parents 46961c6 + 4eae5ab commit c5e309c

17 files changed

+435
-117
lines changed

api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
*/
1616
package io.serverlessworkflow.api;
1717

18-
public interface OneOfValueProvider {
19-
Object get();
18+
public interface OneOfValueProvider<T> {
19+
T get();
2020
}

api/src/main/java/io/serverlessworkflow/api/WorkflowReader.java

+15
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
package io.serverlessworkflow.api;
1717

1818
import io.serverlessworkflow.api.types.Workflow;
19+
import java.io.ByteArrayInputStream;
1920
import java.io.FileNotFoundException;
2021
import java.io.IOException;
2122
import java.io.InputStream;
2223
import java.io.Reader;
24+
import java.io.StringReader;
2325
import java.nio.file.Files;
2426
import java.nio.file.Path;
2527

@@ -37,6 +39,19 @@ public static Workflow readWorkflow(Path path, WorkflowFormat format) throws IOE
3739
return format.mapper().readValue(Files.readAllBytes(path), Workflow.class);
3840
}
3941

42+
public static Workflow readWorkflow(byte[] content, WorkflowFormat format) throws IOException {
43+
try (InputStream input = new ByteArrayInputStream(content)) {
44+
return readWorkflow(input, format);
45+
}
46+
}
47+
48+
public static Workflow readWorkflowFromString(String content, WorkflowFormat format)
49+
throws IOException {
50+
try (Reader reader = new StringReader(content)) {
51+
return readWorkflow(reader, format);
52+
}
53+
}
54+
4055
public static Workflow readWorkflowFromClasspath(String classpath) throws IOException {
4156
return readWorkflowFromClasspath(
4257
classpath,

api/src/main/java/io/serverlessworkflow/api/WorkflowWriter.java

+18
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
package io.serverlessworkflow.api;
1717

1818
import io.serverlessworkflow.api.types.Workflow;
19+
import java.io.ByteArrayOutputStream;
1920
import java.io.IOException;
2021
import java.io.OutputStream;
22+
import java.io.StringWriter;
2123
import java.io.Writer;
2224
import java.nio.file.Files;
2325
import java.nio.file.Path;
@@ -45,5 +47,21 @@ public static void writeWorkflow(Path output, Workflow workflow, WorkflowFormat
4547
}
4648
}
4749

50+
public static String workflowAsString(Workflow workflow, WorkflowFormat format)
51+
throws IOException {
52+
try (Writer writer = new StringWriter()) {
53+
writeWorkflow(writer, workflow, format);
54+
return writer.toString();
55+
}
56+
}
57+
58+
public static byte[] workflowAsBytes(Workflow workflow, WorkflowFormat format)
59+
throws IOException {
60+
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
61+
writeWorkflow(out, workflow, format);
62+
return out.toByteArray();
63+
}
64+
}
65+
4866
private WorkflowWriter() {}
4967
}

api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java

+43-10
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,57 @@
2121
import com.fasterxml.jackson.databind.JsonMappingException;
2222
import jakarta.validation.ConstraintViolationException;
2323
import java.io.IOException;
24+
import java.lang.reflect.InvocationTargetException;
25+
import java.lang.reflect.Method;
26+
import java.util.ArrayList;
2427
import java.util.Collection;
2528

2629
public class DeserializeHelper {
2730

2831
public static <T> T deserializeOneOf(
29-
JsonParser p, Class<T> targetClass, Collection<Class<?>> unionTypes) throws IOException {
32+
JsonParser p, Class<T> targetClass, Collection<Class<?>> oneOfTypes) throws IOException {
3033
TreeNode node = p.readValueAsTree();
31-
JsonProcessingException ex =
32-
new JsonMappingException(p, "Problem deserializing " + targetClass);
33-
for (Class<?> unionType : unionTypes) {
34-
try {
35-
Object object = p.getCodec().treeToValue(node, unionType);
36-
return targetClass.getConstructor(unionType).newInstance(object);
37-
} catch (IOException | ReflectiveOperationException | ConstraintViolationException io) {
38-
ex.addSuppressed(io);
34+
try {
35+
T result = targetClass.getDeclaredConstructor().newInstance();
36+
Collection<Exception> exceptions = new ArrayList<>();
37+
for (Class<?> oneOfType : oneOfTypes) {
38+
try {
39+
assingIt(p, result, node, targetClass, oneOfType);
40+
break;
41+
} catch (IOException | ConstraintViolationException | InvocationTargetException ex) {
42+
exceptions.add(ex);
43+
}
44+
}
45+
if (exceptions.size() == oneOfTypes.size()) {
46+
JsonMappingException ex =
47+
new JsonMappingException(
48+
p,
49+
String.format(
50+
"Error deserializing class %s, all oneOf alternatives %s has failed ",
51+
targetClass, oneOfTypes));
52+
exceptions.forEach(ex::addSuppressed);
53+
throw ex;
54+
}
55+
return result;
56+
} catch (ReflectiveOperationException ex) {
57+
throw new IllegalStateException(ex);
58+
}
59+
}
60+
61+
private static <T> void assingIt(
62+
JsonParser p, T result, TreeNode node, Class<T> targetClass, Class<?> type)
63+
throws JsonProcessingException, ReflectiveOperationException {
64+
findSetMethod(targetClass, type).invoke(result, p.getCodec().treeToValue(node, type));
65+
}
66+
67+
private static Method findSetMethod(Class<?> targetClass, Class<?> type) {
68+
for (Method method : targetClass.getMethods()) {
69+
OneOfSetter oneOfSetter = method.getAnnotation(OneOfSetter.class);
70+
if (oneOfSetter != null && type.equals(oneOfSetter.value())) {
71+
return method;
3972
}
4073
}
41-
throw ex;
74+
throw new IllegalStateException("Cannot find a setter for type " + type);
4275
}
4376

4477
public static <T> T deserializeItem(JsonParser p, Class<T> targetClass, Class<?> valueClass)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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.serialization;
17+
18+
import static java.lang.annotation.ElementType.METHOD;
19+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
20+
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.Target;
23+
24+
@Retention(RUNTIME)
25+
@Target(METHOD)
26+
public @interface OneOfSetter {
27+
Class<?> value();
28+
}

api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java

+20-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717

1818
import static io.serverlessworkflow.api.WorkflowReader.readWorkflow;
1919
import static io.serverlessworkflow.api.WorkflowReader.readWorkflowFromClasspath;
20+
import static io.serverlessworkflow.api.WorkflowWriter.workflowAsBytes;
21+
import static io.serverlessworkflow.api.WorkflowWriter.workflowAsString;
2022
import static io.serverlessworkflow.api.WorkflowWriter.writeWorkflow;
23+
import static org.assertj.core.api.Assertions.assertThat;
2124
import static org.junit.jupiter.api.Assertions.assertNotNull;
2225

2326
import io.serverlessworkflow.api.types.Workflow;
@@ -32,6 +35,13 @@ public class FeaturesTest {
3235
@ParameterizedTest
3336
@ValueSource(
3437
strings = {
38+
"features/authentication-bearer.yaml",
39+
"features/authentication-bearer-uri-format.yaml",
40+
"features/authentication-oauth2.yaml",
41+
"features/authentication-oauth2-secret.yaml",
42+
"features/authentication-oidc.yaml",
43+
"features/authentication-oidc-secret.yaml",
44+
"features/authentication-reusable.yaml",
3545
"features/callHttp.yaml",
3646
"features/callOpenAPI.yaml",
3747
"features/composite.yaml",
@@ -45,12 +55,13 @@ public class FeaturesTest {
4555
"features/try.yaml",
4656
"features/listen.yaml",
4757
"features/callFunction.yaml",
48-
"features/callCustomFunction.yaml"
58+
"features/callCustomFunction.yaml",
59+
"features/call-http-query-parameters.yaml"
4960
})
5061
public void testSpecFeaturesParsing(String workflowLocation) throws IOException {
5162
Workflow workflow = readWorkflowFromClasspath(workflowLocation);
5263
assertWorkflow(workflow);
53-
assertWorkflow(writeAndReadInMemory(workflow));
64+
assertWorkflowEquals(workflow, writeAndReadInMemory(workflow));
5465
}
5566

5667
private static Workflow writeAndReadInMemory(Workflow workflow) throws IOException {
@@ -69,4 +80,11 @@ private static void assertWorkflow(Workflow workflow) {
6980
assertNotNull(workflow.getDocument());
7081
assertNotNull(workflow.getDo());
7182
}
83+
84+
private static void assertWorkflowEquals(Workflow workflow, Workflow other) throws IOException {
85+
assertThat(workflowAsString(workflow, WorkflowFormat.YAML))
86+
.isEqualTo(workflowAsString(other, WorkflowFormat.YAML));
87+
assertThat(workflowAsBytes(workflow, WorkflowFormat.JSON))
88+
.isEqualTo(workflowAsBytes(other, WorkflowFormat.JSON));
89+
}
7290
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
document:
2+
dsl: '1.0.0-alpha5'
3+
namespace: examples
4+
name: bearer-auth
5+
version: '0.1.0'
6+
do:
7+
- getPet:
8+
call: http
9+
with:
10+
method: get
11+
endpoint:
12+
uri: https://petstore.swagger.io/v2/pet/{petId}
13+
authentication:
14+
bearer:
15+
token: ${ .token }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
document:
2+
dsl: '1.0.0-alpha5'
3+
namespace: examples
4+
name: bearer-auth-uri-format
5+
version: '0.1.0'
6+
do:
7+
- getPet:
8+
call: http
9+
with:
10+
method: get
11+
endpoint:
12+
uri: https://petstore.swagger.io/v2/pet/1
13+
authentication:
14+
bearer:
15+
token: ${ .token }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
document:
2+
dsl: 1.0.0-alpha1
3+
namespace: examples
4+
name: oauth2-authentication
5+
version: 1.0.0-alpha1
6+
use:
7+
secrets:
8+
- mySecret
9+
do:
10+
- getPet:
11+
call: http
12+
with:
13+
method: get
14+
endpoint:
15+
uri: https://petstore.swagger.io/v2/pet/{petId}
16+
authentication:
17+
oauth2:
18+
use: mySecret
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
document:
2+
dsl: '1.0.0-alpha5'
3+
namespace: examples
4+
name: oauth2-authentication
5+
version: '0.1.0'
6+
do:
7+
- getPet:
8+
call: http
9+
with:
10+
method: get
11+
endpoint:
12+
uri: https://petstore.swagger.io/v2/pet/{petId}
13+
authentication:
14+
oauth2:
15+
authority: http://keycloak/realms/fake-authority
16+
endpoints: #optional
17+
token: /auth/token #defaults to /oauth2/token
18+
introspection: /auth/introspect #defaults to /oauth2/introspect
19+
grant: client_credentials
20+
client:
21+
id: workflow-runtime-id
22+
secret: workflow-runtime-secret
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
document:
2+
dsl: 1.0.0-alpha1
3+
namespace: examples
4+
name: oidc-authentication
5+
version: 1.0.0-alpha1
6+
use:
7+
secrets:
8+
- mySecret
9+
do:
10+
- getPet:
11+
call: http
12+
with:
13+
method: get
14+
endpoint:
15+
uri: https://petstore.swagger.io/v2/pet/{petId}
16+
authentication:
17+
oidc:
18+
use: mySecret
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
document:
2+
dsl: '1.0.0-alpha5'
3+
namespace: examples
4+
name: oidc-authentication
5+
version: '0.1.0'
6+
do:
7+
- getPet:
8+
call: http
9+
with:
10+
method: get
11+
endpoint:
12+
uri: https://petstore.swagger.io/v2/pet/{petId}
13+
authentication:
14+
oidc:
15+
authority: http://keycloak/realms/fake-authority #endpoints are resolved using the OIDC configuration located at '/.well-known/openid-configuration'
16+
grant: client_credentials
17+
client:
18+
id: workflow-runtime-id
19+
secret: workflow-runtime-secret
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
document:
2+
dsl: '1.0.0-alpha5'
3+
namespace: examples
4+
name: bearer-auth
5+
version: '0.1.0'
6+
use:
7+
authentications:
8+
petStoreAuth:
9+
bearer:
10+
token: ${ .token }
11+
do:
12+
- getPet:
13+
call: http
14+
with:
15+
method: get
16+
endpoint:
17+
uri: https://petstore.swagger.io/v2/pet/{petId}
18+
authentication:
19+
use: petStoreAuth
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
document:
2+
dsl: 1.0.0-alpha2
3+
namespace: examples
4+
name: http-query-params
5+
version: 1.0.0-alpha2
6+
input:
7+
schema:
8+
format: json
9+
document:
10+
type: object
11+
required:
12+
- searchQuery
13+
properties:
14+
searchQuery:
15+
type: string
16+
do:
17+
- searchStarWarsCharacters:
18+
call: http
19+
with:
20+
method: get
21+
endpoint: https://swapi.dev/api/people/
22+
query:
23+
search: ${.searchQuery}
24+

0 commit comments

Comments
 (0)