Skip to content

Commit

Permalink
[Fix #498] Adding possibility to validate before parsing
Browse files Browse the repository at this point in the history
Fix #498

Signed-off-by: Francisco Javier Tirado Sarti <[email protected]>
  • Loading branch information
fjtirado committed Dec 19, 2024
1 parent 0939916 commit 53c0c5a
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 65 deletions.
4 changes: 4 additions & 0 deletions api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
Expand Down
44 changes: 44 additions & 0 deletions api/src/main/java/io/serverlessworkflow/api/DirectReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2020-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.serverlessworkflow.api;

import io.serverlessworkflow.api.types.Workflow;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;

class DirectReader implements WorkflowReaderOperations {

@Override
public Workflow read(InputStream input, WorkflowFormat format) throws IOException {
return format.mapper().readValue(input, Workflow.class);
}

@Override
public Workflow read(Reader input, WorkflowFormat format) throws IOException {
return format.mapper().readValue(input, Workflow.class);
}

@Override
public Workflow read(byte[] input, WorkflowFormat format) throws IOException {
return format.mapper().readValue(input, Workflow.class);
}

@Override
public Workflow read(String input, WorkflowFormat format) throws IOException {
return format.mapper().readValue(input, Workflow.class);
}
}
76 changes: 76 additions & 0 deletions api/src/main/java/io/serverlessworkflow/api/ValidationReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2020-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.serverlessworkflow.api;

import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.InputFormat;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.SchemaValidatorsConfig;
import com.networknt.schema.SpecVersion.VersionFlag;
import com.networknt.schema.ValidationMessage;
import io.serverlessworkflow.api.types.Workflow;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Set;
import java.util.stream.Collectors;

class ValidationReader implements WorkflowReaderOperations {
private final JsonSchema schemaObject;

ValidationReader() {
this.schemaObject =
JsonSchemaFactory.getInstance(VersionFlag.V7)
.getSchema(
Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream("schema/workflow.yaml"),
InputFormat.YAML,
SchemaValidatorsConfig.builder().build());
}

@Override
public Workflow read(InputStream input, WorkflowFormat format) throws IOException {
return validate(format.mapper().readValue(input, JsonNode.class), format);
}

@Override
public Workflow read(Reader input, WorkflowFormat format) throws IOException {
return validate(format.mapper().readValue(input, JsonNode.class), format);
}

@Override
public Workflow read(byte[] input, WorkflowFormat format) throws IOException {
return validate(format.mapper().readValue(input, JsonNode.class), format);
}

@Override
public Workflow read(String input, WorkflowFormat format) throws IOException {
return validate(format.mapper().readValue(input, JsonNode.class), format);
}

private Workflow validate(JsonNode value, WorkflowFormat format) {
Set<ValidationMessage> validationErrors = schemaObject.validate(value);
if (!validationErrors.isEmpty()) {
throw new IllegalArgumentException(
validationErrors.stream()
.map(ValidationMessage::toString)
.collect(Collectors.joining("\n")));
}
return format.mapper().convertValue(value, Workflow.class);
}
}
63 changes: 47 additions & 16 deletions api/src/main/java/io/serverlessworkflow/api/WorkflowReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,58 +16,89 @@
package io.serverlessworkflow.api;

import io.serverlessworkflow.api.types.Workflow;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Path;

public class WorkflowReader {

private static class NoValidationHolder {
private static final WorkflowReaderOperations instance = new DirectReader();
}

private static class ValidationHolder {
private static final WorkflowReaderOperations instance = new ValidationReader();
}

public static WorkflowReaderOperations noValidation() {
return NoValidationHolder.instance;
}

public static WorkflowReaderOperations validation() {
return ValidationHolder.instance;
}

public static Workflow readWorkflow(InputStream input, WorkflowFormat format) throws IOException {
return format.mapper().readValue(input, Workflow.class);
return defaultReader().read(input, format);
}

public static Workflow readWorkflow(Reader input, WorkflowFormat format) throws IOException {
return format.mapper().readValue(input, Workflow.class);
return defaultReader().read(input, format);
}

public static Workflow readWorkflow(Path path, WorkflowFormat format) throws IOException {
return format.mapper().readValue(Files.readAllBytes(path), Workflow.class);
public static Workflow readWorkflow(byte[] input, WorkflowFormat format) throws IOException {
return defaultReader().read(input, format);
}

public static Workflow readWorkflow(byte[] content, WorkflowFormat format) throws IOException {
try (InputStream input = new ByteArrayInputStream(content)) {
return readWorkflow(input, format);
}
public static Workflow readWorkflow(Path path, WorkflowFormat format) throws IOException {
return readWorkflow(defaultReader(), path, format);
}

public static Workflow readWorkflowFromString(String content, WorkflowFormat format)
public static Workflow readWorkflowFromString(String input, WorkflowFormat format)
throws IOException {
try (Reader reader = new StringReader(content)) {
return readWorkflow(reader, format);
}
return defaultReader().read(input, format);
}

public static Workflow readWorkflowFromClasspath(String classpath) throws IOException {
return readWorkflowFromClasspath(defaultReader(), classpath);
}

public static Workflow readWorkflowFromClasspath(
String classpath, ClassLoader cl, WorkflowFormat format) throws IOException {
return readWorkflowFromClasspath(defaultReader(), classpath);
}

public static Workflow readWorkflow(
WorkflowReaderOperations reader, Path path, WorkflowFormat format) throws IOException {
return reader.read(Files.readAllBytes(path), format);
}

public static Workflow readWorkflowFromClasspath(
WorkflowReaderOperations reader, String classpath) throws IOException {
return readWorkflowFromClasspath(
reader,
classpath,
Thread.currentThread().getContextClassLoader(),
WorkflowFormat.fromFileName(classpath));
}

public static Workflow readWorkflowFromClasspath(
String classpath, ClassLoader cl, WorkflowFormat format) throws IOException {
WorkflowReaderOperations reader, String classpath, ClassLoader cl, WorkflowFormat format)
throws IOException {
try (InputStream in = cl.getResourceAsStream(classpath)) {
if (in == null) {
throw new FileNotFoundException(classpath);
}
return readWorkflow(in, format);
return reader.read(in, format);
}
}

private static WorkflowReaderOperations defaultReader() {
return NoValidationHolder.instance;
}

private WorkflowReader() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2020-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.serverlessworkflow.api;

import io.serverlessworkflow.api.types.Workflow;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;

public interface WorkflowReaderOperations {
Workflow read(InputStream input, WorkflowFormat format) throws IOException;

Workflow read(Reader input, WorkflowFormat format) throws IOException;

Workflow read(byte[] input, WorkflowFormat format) throws IOException;

Workflow read(String input, WorkflowFormat format) throws IOException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
Expand Down Expand Up @@ -49,10 +48,7 @@ public static void writeWorkflow(Path output, Workflow workflow, WorkflowFormat

public static String workflowAsString(Workflow workflow, WorkflowFormat format)
throws IOException {
try (Writer writer = new StringWriter()) {
writeWorkflow(writer, workflow, format);
return writer.toString();
}
return format.mapper().writeValueAsString(workflow);
}

public static byte[] workflowAsBytes(Workflow workflow, WorkflowFormat format)
Expand Down
9 changes: 5 additions & 4 deletions api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
*/
package io.serverlessworkflow.api;

import static io.serverlessworkflow.api.WorkflowReader.readWorkflow;
import static io.serverlessworkflow.api.WorkflowReader.noValidation;
import static io.serverlessworkflow.api.WorkflowReader.readWorkflowFromClasspath;
import static io.serverlessworkflow.api.WorkflowReader.validation;
import static io.serverlessworkflow.api.WorkflowWriter.workflowAsBytes;
import static io.serverlessworkflow.api.WorkflowWriter.workflowAsString;
import static io.serverlessworkflow.api.WorkflowWriter.writeWorkflow;
Expand Down Expand Up @@ -53,13 +54,13 @@ public class FeaturesTest {
"features/set.yaml",
"features/switch.yaml",
"features/try.yaml",
"features/listen.yaml",
"features/listen-to-any.yaml",
"features/callFunction.yaml",
"features/callCustomFunction.yaml",
"features/call-http-query-parameters.yaml"
})
public void testSpecFeaturesParsing(String workflowLocation) throws IOException {
Workflow workflow = readWorkflowFromClasspath(workflowLocation);
Workflow workflow = readWorkflowFromClasspath(validation(), workflowLocation);
assertWorkflow(workflow);
assertWorkflowEquals(workflow, writeAndReadInMemory(workflow));
}
Expand All @@ -71,7 +72,7 @@ private static Workflow writeAndReadInMemory(Workflow workflow) throws IOExcepti
bytes = out.toByteArray();
}
try (ByteArrayInputStream in = new ByteArrayInputStream(bytes)) {
return readWorkflow(in, WorkflowFormat.JSON);
return noValidation().read(in, WorkflowFormat.JSON);
}
}

Expand Down
48 changes: 23 additions & 25 deletions api/src/test/resources/features/callCustomFunction.yaml
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
document:
dsl: 1.0.0-alpha5
namespace: test
name: call-example
version: 0.1.0
schedule:
cron: 0 8 * * *
dsl: '1.0.0-alpha5'
namespace: samples
name: call-custom-function-inline
version: '0.1.0'
use:
functions:
getPetById:
input:
schema:
document:
type: object
properties:
petId:
type: string
required: [ petId ]
call: http
with:
method: get
endpoint: https://petstore.swagger.io/v2/pet/{petId}
do:
- getData:
call: http
with:
method: get
endpoint: https://api.agify.io?name=meelad
output:
as: ".data.reading"
- filterData:
for:
in: ".data.reading"
each: reading
do:
- log:
call: https://raw.githubusercontent.com/serverlessworkflow/catalog/main/functions/log/1.0.0/function.yaml
with:
level: information
format: "{TIMESTAMP} [{LEVEL}] ({CONTEXT}): {MESSAGE}"
message: Hello, world!
timestamp: true
- getPet:
call: getPetById
with:
petId: 69
2 changes: 1 addition & 1 deletion api/src/test/resources/features/callOpenAPI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ do:
call: openapi
with:
document:
uri: "https://petstore.swagger.io/v2/swagger.json"
endpoint: "https://petstore.swagger.io/v2/swagger.json"
operationId: findPetsByStatus
parameters:
status: ${ .status }
Expand Down
Loading

0 comments on commit 53c0c5a

Please sign in to comment.