Skip to content

Commit 22a1c68

Browse files
authored
Merge pull request #499 from fjtirado/Fix_#498
[Fix #498] Adding possibility to validate before parsing
2 parents 0939916 + c128d88 commit 22a1c68

File tree

12 files changed

+258
-63
lines changed

12 files changed

+258
-63
lines changed

api/pom.xml

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
<groupId>com.fasterxml.jackson.core</groupId>
2222
<artifactId>jackson-core</artifactId>
2323
</dependency>
24+
<dependency>
25+
<groupId>com.networknt</groupId>
26+
<artifactId>json-schema-validator</artifactId>
27+
</dependency>
2428
<dependency>
2529
<groupId>com.fasterxml.jackson.core</groupId>
2630
<artifactId>jackson-databind</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.api;
17+
18+
import io.serverlessworkflow.api.types.Workflow;
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
import java.io.Reader;
22+
23+
class DirectReader implements WorkflowReaderOperations {
24+
25+
@Override
26+
public Workflow read(InputStream input, WorkflowFormat format) throws IOException {
27+
return format.mapper().readValue(input, Workflow.class);
28+
}
29+
30+
@Override
31+
public Workflow read(Reader input, WorkflowFormat format) throws IOException {
32+
return format.mapper().readValue(input, Workflow.class);
33+
}
34+
35+
@Override
36+
public Workflow read(byte[] input, WorkflowFormat format) throws IOException {
37+
return format.mapper().readValue(input, Workflow.class);
38+
}
39+
40+
@Override
41+
public Workflow read(String input, WorkflowFormat format) throws IOException {
42+
return format.mapper().readValue(input, Workflow.class);
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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.api;
17+
18+
import com.fasterxml.jackson.databind.JsonNode;
19+
import com.networknt.schema.InputFormat;
20+
import com.networknt.schema.JsonSchema;
21+
import com.networknt.schema.JsonSchemaFactory;
22+
import com.networknt.schema.SchemaValidatorsConfig;
23+
import com.networknt.schema.SpecVersion.VersionFlag;
24+
import com.networknt.schema.ValidationMessage;
25+
import io.serverlessworkflow.api.types.Workflow;
26+
import java.io.IOException;
27+
import java.io.InputStream;
28+
import java.io.Reader;
29+
import java.io.UncheckedIOException;
30+
import java.util.Set;
31+
import java.util.stream.Collectors;
32+
33+
class ValidationReader implements WorkflowReaderOperations {
34+
private final JsonSchema schemaObject;
35+
36+
ValidationReader() {
37+
try (InputStream input =
38+
Thread.currentThread()
39+
.getContextClassLoader()
40+
.getResourceAsStream("schema/workflow.yaml")) {
41+
this.schemaObject =
42+
JsonSchemaFactory.getInstance(VersionFlag.V7)
43+
.getSchema(input, InputFormat.YAML, SchemaValidatorsConfig.builder().build());
44+
} catch (IOException e) {
45+
throw new UncheckedIOException(e);
46+
}
47+
}
48+
49+
@Override
50+
public Workflow read(InputStream input, WorkflowFormat format) throws IOException {
51+
return validate(format.mapper().readValue(input, JsonNode.class), format);
52+
}
53+
54+
@Override
55+
public Workflow read(Reader input, WorkflowFormat format) throws IOException {
56+
return validate(format.mapper().readValue(input, JsonNode.class), format);
57+
}
58+
59+
@Override
60+
public Workflow read(byte[] input, WorkflowFormat format) throws IOException {
61+
return validate(format.mapper().readValue(input, JsonNode.class), format);
62+
}
63+
64+
@Override
65+
public Workflow read(String input, WorkflowFormat format) throws IOException {
66+
return validate(format.mapper().readValue(input, JsonNode.class), format);
67+
}
68+
69+
private Workflow validate(JsonNode value, WorkflowFormat format) {
70+
Set<ValidationMessage> validationErrors = schemaObject.validate(value);
71+
if (!validationErrors.isEmpty()) {
72+
throw new IllegalArgumentException(
73+
validationErrors.stream()
74+
.map(ValidationMessage::toString)
75+
.collect(Collectors.joining("\n")));
76+
}
77+
return format.mapper().convertValue(value, Workflow.class);
78+
}
79+
}

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

+56-16
Original file line numberDiff line numberDiff line change
@@ -16,58 +16,98 @@
1616
package io.serverlessworkflow.api;
1717

1818
import io.serverlessworkflow.api.types.Workflow;
19-
import java.io.ByteArrayInputStream;
2019
import java.io.FileNotFoundException;
2120
import java.io.IOException;
2221
import java.io.InputStream;
2322
import java.io.Reader;
24-
import java.io.StringReader;
2523
import java.nio.file.Files;
2624
import java.nio.file.Path;
2725

2826
public class WorkflowReader {
2927

3028
public static Workflow readWorkflow(InputStream input, WorkflowFormat format) throws IOException {
31-
return format.mapper().readValue(input, Workflow.class);
29+
return defaultReader().read(input, format);
3230
}
3331

3432
public static Workflow readWorkflow(Reader input, WorkflowFormat format) throws IOException {
35-
return format.mapper().readValue(input, Workflow.class);
33+
return defaultReader().read(input, format);
3634
}
3735

38-
public static Workflow readWorkflow(Path path, WorkflowFormat format) throws IOException {
39-
return format.mapper().readValue(Files.readAllBytes(path), Workflow.class);
36+
public static Workflow readWorkflow(byte[] input, WorkflowFormat format) throws IOException {
37+
return defaultReader().read(input, format);
4038
}
4139

42-
public static Workflow readWorkflow(byte[] content, WorkflowFormat format) throws IOException {
43-
try (InputStream input = new ByteArrayInputStream(content)) {
44-
return readWorkflow(input, format);
45-
}
40+
public static Workflow readWorkflow(Path path) throws IOException {
41+
return readWorkflow(defaultReader(), path, WorkflowFormat.fromPath(path));
42+
}
43+
44+
public static Workflow readWorkflow(Path path, WorkflowFormat format) throws IOException {
45+
return readWorkflow(defaultReader(), path, format);
4646
}
4747

48-
public static Workflow readWorkflowFromString(String content, WorkflowFormat format)
48+
public static Workflow readWorkflowFromString(String input, WorkflowFormat format)
4949
throws IOException {
50-
try (Reader reader = new StringReader(content)) {
51-
return readWorkflow(reader, format);
52-
}
50+
return defaultReader().read(input, format);
5351
}
5452

5553
public static Workflow readWorkflowFromClasspath(String classpath) throws IOException {
54+
return readWorkflowFromClasspath(defaultReader(), classpath);
55+
}
56+
57+
public static Workflow readWorkflowFromClasspath(
58+
String classpath, ClassLoader cl, WorkflowFormat format) throws IOException {
59+
return readWorkflowFromClasspath(defaultReader(), classpath);
60+
}
61+
62+
public static Workflow readWorkflow(WorkflowReaderOperations reader, Path path)
63+
throws IOException {
64+
return readWorkflow(reader, path, WorkflowFormat.fromPath(path));
65+
}
66+
67+
public static Workflow readWorkflow(
68+
WorkflowReaderOperations reader, Path path, WorkflowFormat format) throws IOException {
69+
return reader.read(Files.readAllBytes(path), format);
70+
}
71+
72+
public static Workflow readWorkflowFromClasspath(
73+
WorkflowReaderOperations reader, String classpath) throws IOException {
5674
return readWorkflowFromClasspath(
75+
reader,
5776
classpath,
5877
Thread.currentThread().getContextClassLoader(),
5978
WorkflowFormat.fromFileName(classpath));
6079
}
6180

6281
public static Workflow readWorkflowFromClasspath(
63-
String classpath, ClassLoader cl, WorkflowFormat format) throws IOException {
82+
WorkflowReaderOperations reader, String classpath, ClassLoader cl, WorkflowFormat format)
83+
throws IOException {
6484
try (InputStream in = cl.getResourceAsStream(classpath)) {
6585
if (in == null) {
6686
throw new FileNotFoundException(classpath);
6787
}
68-
return readWorkflow(in, format);
88+
return reader.read(in, format);
6989
}
7090
}
7191

92+
public static WorkflowReaderOperations noValidation() {
93+
return NoValidationHolder.instance;
94+
}
95+
96+
public static WorkflowReaderOperations validation() {
97+
return ValidationHolder.instance;
98+
}
99+
100+
private static class NoValidationHolder {
101+
private static final WorkflowReaderOperations instance = new DirectReader();
102+
}
103+
104+
private static class ValidationHolder {
105+
private static final WorkflowReaderOperations instance = new ValidationReader();
106+
}
107+
108+
private static WorkflowReaderOperations defaultReader() {
109+
return NoValidationHolder.instance;
110+
}
111+
72112
private WorkflowReader() {}
73113
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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.api;
17+
18+
import io.serverlessworkflow.api.types.Workflow;
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
import java.io.Reader;
22+
23+
public interface WorkflowReaderOperations {
24+
Workflow read(InputStream input, WorkflowFormat format) throws IOException;
25+
26+
Workflow read(Reader input, WorkflowFormat format) throws IOException;
27+
28+
Workflow read(byte[] input, WorkflowFormat format) throws IOException;
29+
30+
Workflow read(String input, WorkflowFormat format) throws IOException;
31+
}

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

+1-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.io.ByteArrayOutputStream;
2020
import java.io.IOException;
2121
import java.io.OutputStream;
22-
import java.io.StringWriter;
2322
import java.io.Writer;
2423
import java.nio.file.Files;
2524
import java.nio.file.Path;
@@ -49,10 +48,7 @@ public static void writeWorkflow(Path output, Workflow workflow, WorkflowFormat
4948

5049
public static String workflowAsString(Workflow workflow, WorkflowFormat format)
5150
throws IOException {
52-
try (Writer writer = new StringWriter()) {
53-
writeWorkflow(writer, workflow, format);
54-
return writer.toString();
55-
}
51+
return format.mapper().writeValueAsString(workflow);
5652
}
5753

5854
public static byte[] workflowAsBytes(Workflow workflow, WorkflowFormat format)

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import static io.serverlessworkflow.api.WorkflowReader.readWorkflow;
1919
import static io.serverlessworkflow.api.WorkflowReader.readWorkflowFromClasspath;
20+
import static io.serverlessworkflow.api.WorkflowReader.validation;
2021
import static io.serverlessworkflow.api.WorkflowWriter.workflowAsBytes;
2122
import static io.serverlessworkflow.api.WorkflowWriter.workflowAsString;
2223
import static io.serverlessworkflow.api.WorkflowWriter.writeWorkflow;
@@ -53,13 +54,13 @@ public class FeaturesTest {
5354
"features/set.yaml",
5455
"features/switch.yaml",
5556
"features/try.yaml",
56-
"features/listen.yaml",
57+
"features/listen-to-any.yaml",
5758
"features/callFunction.yaml",
5859
"features/callCustomFunction.yaml",
5960
"features/call-http-query-parameters.yaml"
6061
})
6162
public void testSpecFeaturesParsing(String workflowLocation) throws IOException {
62-
Workflow workflow = readWorkflowFromClasspath(workflowLocation);
63+
Workflow workflow = readWorkflowFromClasspath(validation(), workflowLocation);
6364
assertWorkflow(workflow);
6465
assertWorkflowEquals(workflow, writeAndReadInMemory(workflow));
6566
}
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
11
document:
2-
dsl: 1.0.0-alpha5
3-
namespace: test
4-
name: call-example
5-
version: 0.1.0
6-
schedule:
7-
cron: 0 8 * * *
2+
dsl: '1.0.0-alpha5'
3+
namespace: samples
4+
name: call-custom-function-inline
5+
version: '0.1.0'
6+
use:
7+
functions:
8+
getPetById:
9+
input:
10+
schema:
11+
document:
12+
type: object
13+
properties:
14+
petId:
15+
type: string
16+
required: [ petId ]
17+
call: http
18+
with:
19+
method: get
20+
endpoint: https://petstore.swagger.io/v2/pet/{petId}
821
do:
9-
- getData:
10-
call: http
11-
with:
12-
method: get
13-
endpoint: https://api.agify.io?name=meelad
14-
output:
15-
as: ".data.reading"
16-
- filterData:
17-
for:
18-
in: ".data.reading"
19-
each: reading
20-
do:
21-
- log:
22-
call: https://raw.githubusercontent.com/serverlessworkflow/catalog/main/functions/log/1.0.0/function.yaml
23-
with:
24-
level: information
25-
format: "{TIMESTAMP} [{LEVEL}] ({CONTEXT}): {MESSAGE}"
26-
message: Hello, world!
27-
timestamp: true
22+
- getPet:
23+
call: getPetById
24+
with:
25+
petId: 69

api/src/test/resources/features/callOpenAPI.yaml

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

0 commit comments

Comments
 (0)