From d0f17a0203e5fca1c9a1c50cf0e3d02110741edf Mon Sep 17 00:00:00 2001 From: Javier Date: Wed, 28 Aug 2024 18:52:00 +0200 Subject: [PATCH 1/2] [Fix #418] Adding const support Signed-off-by: Javier --- api/pom.xml | 1 + .../io/serverlessworkflow/api/ApiTest.java | 19 +++++++++ .../serverlessworkflow/api/FeaturesTest.java | 3 +- .../test/resources/features/callFunction.yaml | 18 +++++++++ .../generator/ConstAnnotator.java | 40 +++++++++++++++++++ 5 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 api/src/test/resources/features/callFunction.yaml create mode 100644 custom-generator/src/main/java/io/serverlessworkflow/generator/ConstAnnotator.java diff --git a/api/pom.xml b/api/pom.xml index 37524b37..3bf0047e 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -101,6 +101,7 @@ true true io.serverlessworkflow.generator.UnreferencedFactory + io.serverlessworkflow.generator.ConstAnnotator diff --git a/api/src/test/java/io/serverlessworkflow/api/ApiTest.java b/api/src/test/java/io/serverlessworkflow/api/ApiTest.java index ac5f7532..20248f35 100644 --- a/api/src/test/java/io/serverlessworkflow/api/ApiTest.java +++ b/api/src/test/java/io/serverlessworkflow/api/ApiTest.java @@ -18,6 +18,7 @@ import static io.serverlessworkflow.api.WorkflowReader.readWorkflowFromClasspath; import static org.assertj.core.api.Assertions.assertThat; +import io.serverlessworkflow.api.types.CallFunction; import io.serverlessworkflow.api.types.CallHTTP; import io.serverlessworkflow.api.types.CallTask; import io.serverlessworkflow.api.types.Task; @@ -44,4 +45,22 @@ void testCallHTTPAPI() throws IOException { assertThat(httpCall.getWith().getMethod()).isEqualTo("get"); } } + + @Test + void testCallFunctionAPIWithoutArguments() throws IOException { + Workflow workflow = readWorkflowFromClasspath("features/callFunction.yaml"); + assertThat(workflow.getDo()).isNotEmpty(); + assertThat(workflow.getDo().get(0).getName()).isNotNull(); + assertThat(workflow.getDo().get(0).getTask()).isNotNull(); + Task task = workflow.getDo().get(0).getTask(); + CallTask callTask = task.getCallTask(); + assertThat(callTask).isNotNull(); + assertThat(callTask.get()).isInstanceOf(CallFunction.class); + if (callTask.get() instanceof CallFunction) { + CallFunction functionCall = callTask.getCallFunction(); + assertThat(functionCall).isNotNull(); + assertThat(callTask.getCallAsyncAPI()).isNull(); + assertThat(functionCall.getWith()).isNull(); + } + } } diff --git a/api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java b/api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java index 8e77673b..af11a49b 100644 --- a/api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java +++ b/api/src/test/java/io/serverlessworkflow/api/FeaturesTest.java @@ -43,7 +43,8 @@ public class FeaturesTest { "features/set.yaml", "features/switch.yaml", "features/try.yaml", - "features/listen.yaml" + "features/listen.yaml", + "features/callFunction.yaml" }) public void testSpecFeaturesParsing(String workflowLocation) throws IOException { Workflow workflow = readWorkflowFromClasspath(workflowLocation); diff --git a/api/src/test/resources/features/callFunction.yaml b/api/src/test/resources/features/callFunction.yaml new file mode 100644 index 00000000..162ec31e --- /dev/null +++ b/api/src/test/resources/features/callFunction.yaml @@ -0,0 +1,18 @@ +document: + dsl: 1.0.0-alpha1 + namespace: default + name: http-call-with-response-output + +use: + functions: + getPet: + call: http + with: + method: get + endpoint: + uri: https://petstore.swagger.io/v2/pet/{petId} + output: response + +do: + - getPetFunctionCall: + call: getPet \ No newline at end of file diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/ConstAnnotator.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/ConstAnnotator.java new file mode 100644 index 00000000..a893cfb5 --- /dev/null +++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/ConstAnnotator.java @@ -0,0 +1,40 @@ +/* + * 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.generator; + +import com.fasterxml.jackson.databind.JsonNode; +import com.sun.codemodel.JDefinedClass; +import com.sun.codemodel.JFieldVar; +import jakarta.validation.constraints.Pattern; +import org.jsonschema2pojo.AbstractAnnotator; +import org.jsonschema2pojo.GenerationConfig; + +public class ConstAnnotator extends AbstractAnnotator { + + private static final String CONST = "const"; + + public ConstAnnotator(GenerationConfig generationConfig) { + super(generationConfig); + } + + @Override + public void propertyField( + JFieldVar field, JDefinedClass clazz, String propertyName, JsonNode propertyNode) { + if (propertyNode.has(CONST)) { + field.annotate(Pattern.class).param("regexp", propertyNode.get(CONST).asText()); + } + } +} From 3b65612d602c6cf0abfd303463a58e06b33072da Mon Sep 17 00:00:00 2001 From: Javier Date: Wed, 28 Aug 2024 19:39:11 +0200 Subject: [PATCH 2/2] [Fix #418] Adding Jsr 303 validation support Notice that this will force us to add version and other mandatory field we were not including in the examples Signed-off-by: Javier --- api/pom.xml | 14 ++--- .../api/ObjectMapperFactory.java | 7 ++- ...eanDeserializerModifierWithValidation.java | 35 +++++++++++++ .../BeanDeserializerWithValidation.java | 51 +++++++++++++++++++ .../serialization/DeserializeHelper.java | 3 +- .../test/resources/features/callFunction.yaml | 1 + api/src/test/resources/features/callHttp.yaml | 1 + .../test/resources/features/callOpenAPI.yaml | 1 + .../test/resources/features/composite.yaml | 1 + .../test/resources/features/data-flow.yaml | 1 + api/src/test/resources/features/emit.yaml | 1 + api/src/test/resources/features/flow.yaml | 1 + api/src/test/resources/features/for.yaml | 1 + api/src/test/resources/features/listen.yaml | 1 + api/src/test/resources/features/raise.yaml | 4 +- api/src/test/resources/features/set.yaml | 1 + api/src/test/resources/features/switch.yaml | 1 + api/src/test/resources/features/try.yaml | 1 + pom.xml | 14 ++++- 19 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 api/src/main/java/io/serverlessworkflow/serialization/BeanDeserializerModifierWithValidation.java create mode 100644 api/src/main/java/io/serverlessworkflow/serialization/BeanDeserializerWithValidation.java diff --git a/api/pom.xml b/api/pom.xml index 3bf0047e..d5128d57 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -33,7 +33,14 @@ jakarta.validation jakarta.validation-api - + + org.hibernate.validator + hibernate-validator + + + org.glassfish.expressly + expressly + org.junit.jupiter @@ -65,11 +72,6 @@ logback-classic test - - org.assertj - assertj-core - test - diff --git a/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java b/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java index 7bc8414e..850e7da7 100644 --- a/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java +++ b/api/src/main/java/io/serverlessworkflow/api/ObjectMapperFactory.java @@ -17,8 +17,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature; +import io.serverlessworkflow.serialization.BeanDeserializerModifierWithValidation; class ObjectMapperFactory { @@ -36,10 +38,13 @@ public static final ObjectMapper yamlMapper() { } private static ObjectMapper configure(ObjectMapper mapper) { + SimpleModule validationModule = new SimpleModule(); + validationModule.setDeserializerModifier(new BeanDeserializerModifierWithValidation()); return mapper .configure(SerializationFeature.INDENT_OUTPUT, true) .configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false) - .configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); + .configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false) + .registerModule(validationModule); } private ObjectMapperFactory() {} diff --git a/api/src/main/java/io/serverlessworkflow/serialization/BeanDeserializerModifierWithValidation.java b/api/src/main/java/io/serverlessworkflow/serialization/BeanDeserializerModifierWithValidation.java new file mode 100644 index 00000000..e4e019ac --- /dev/null +++ b/api/src/main/java/io/serverlessworkflow/serialization/BeanDeserializerModifierWithValidation.java @@ -0,0 +1,35 @@ +/* + * 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.serialization; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.deser.BeanDeserializer; +import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; + +public class BeanDeserializerModifierWithValidation extends BeanDeserializerModifier { + + private static final long serialVersionUID = 1L; + + @Override + public JsonDeserializer modifyDeserializer( + DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer deserializer) { + return deserializer instanceof BeanDeserializer + ? new BeanDeserializerWithValidation((BeanDeserializer) deserializer) + : deserializer; + } +} diff --git a/api/src/main/java/io/serverlessworkflow/serialization/BeanDeserializerWithValidation.java b/api/src/main/java/io/serverlessworkflow/serialization/BeanDeserializerWithValidation.java new file mode 100644 index 00000000..a77e117d --- /dev/null +++ b/api/src/main/java/io/serverlessworkflow/serialization/BeanDeserializerWithValidation.java @@ -0,0 +1,51 @@ +/* + * 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.serialization; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.BeanDeserializer; +import com.fasterxml.jackson.databind.deser.BeanDeserializerBase; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import java.io.IOException; +import java.util.Set; + +public class BeanDeserializerWithValidation extends BeanDeserializer { + private static final long serialVersionUID = 1L; + private static final Validator validator = + Validation.buildDefaultValidatorFactory().getValidator(); + + protected BeanDeserializerWithValidation(BeanDeserializerBase src) { + super(src); + } + + private void validate(T t) throws IOException { + Set> violations = validator.validate(t); + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + Object instance = super.deserialize(p, ctxt); + validate(instance); + return instance; + } +} diff --git a/api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java b/api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java index fc5390f1..72b4cce0 100644 --- a/api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java +++ b/api/src/main/java/io/serverlessworkflow/serialization/DeserializeHelper.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.databind.JsonMappingException; +import jakarta.validation.ConstraintViolationException; import java.io.IOException; import java.util.Collection; @@ -33,7 +34,7 @@ public static T deserializeOneOf( try { Object object = p.getCodec().treeToValue(node, unionType); return targetClass.getConstructor(unionType).newInstance(object); - } catch (IOException | ReflectiveOperationException io) { + } catch (IOException | ReflectiveOperationException | ConstraintViolationException io) { ex.addSuppressed(io); } } diff --git a/api/src/test/resources/features/callFunction.yaml b/api/src/test/resources/features/callFunction.yaml index 162ec31e..95a3a987 100644 --- a/api/src/test/resources/features/callFunction.yaml +++ b/api/src/test/resources/features/callFunction.yaml @@ -2,6 +2,7 @@ document: dsl: 1.0.0-alpha1 namespace: default name: http-call-with-response-output + version: 1.0.0 use: functions: diff --git a/api/src/test/resources/features/callHttp.yaml b/api/src/test/resources/features/callHttp.yaml index 9b48c783..4022e38a 100644 --- a/api/src/test/resources/features/callHttp.yaml +++ b/api/src/test/resources/features/callHttp.yaml @@ -2,6 +2,7 @@ document: dsl: 1.0.0-alpha1 namespace: default name: http-call-with-response-output + version: 1.0.0 do: - getPet: call: http diff --git a/api/src/test/resources/features/callOpenAPI.yaml b/api/src/test/resources/features/callOpenAPI.yaml index 46ecc921..1a1d0c56 100644 --- a/api/src/test/resources/features/callOpenAPI.yaml +++ b/api/src/test/resources/features/callOpenAPI.yaml @@ -2,6 +2,7 @@ document: dsl: 1.0.0-alpha1 namespace: default name: openapi-call-with-content-output + version: 1.0.0 do: - findPet: call: openapi diff --git a/api/src/test/resources/features/composite.yaml b/api/src/test/resources/features/composite.yaml index 71b0dea4..515cda25 100644 --- a/api/src/test/resources/features/composite.yaml +++ b/api/src/test/resources/features/composite.yaml @@ -2,6 +2,7 @@ document: dsl: 1.0.0-alpha1 namespace: default name: do + version: 1.0.0 do: - compositeExample: do: diff --git a/api/src/test/resources/features/data-flow.yaml b/api/src/test/resources/features/data-flow.yaml index d66d7848..bebb2123 100644 --- a/api/src/test/resources/features/data-flow.yaml +++ b/api/src/test/resources/features/data-flow.yaml @@ -2,6 +2,7 @@ document: dsl: 1.0.0-alpha1 namespace: default name: output-filtering + version: 1.0.0 do: - getPet: call: http diff --git a/api/src/test/resources/features/emit.yaml b/api/src/test/resources/features/emit.yaml index 488feedc..983407d9 100644 --- a/api/src/test/resources/features/emit.yaml +++ b/api/src/test/resources/features/emit.yaml @@ -2,6 +2,7 @@ document: dsl: 1.0.0-alpha1 namespace: default name: emit + version: 1.0.0 do: - emitEvent: emit: diff --git a/api/src/test/resources/features/flow.yaml b/api/src/test/resources/features/flow.yaml index 83baf04c..6bd8a6e7 100644 --- a/api/src/test/resources/features/flow.yaml +++ b/api/src/test/resources/features/flow.yaml @@ -2,6 +2,7 @@ document: dsl: 1.0.0-alpha1 namespace: default name: implicit-sequence + version: 1.0.0 do: - setRed: set: diff --git a/api/src/test/resources/features/for.yaml b/api/src/test/resources/features/for.yaml index f8ae826d..662dff91 100644 --- a/api/src/test/resources/features/for.yaml +++ b/api/src/test/resources/features/for.yaml @@ -2,6 +2,7 @@ document: dsl: 1.0.0-alpha1 namespace: default name: for + version: 1.0.0 do: - loopColors: for: diff --git a/api/src/test/resources/features/listen.yaml b/api/src/test/resources/features/listen.yaml index 393355d0..1c56c229 100644 --- a/api/src/test/resources/features/listen.yaml +++ b/api/src/test/resources/features/listen.yaml @@ -2,6 +2,7 @@ document: dsl: 1.0.0-alpha1 namespace: default name: listen-task + version: 1.0.0 do: - listenToSomething: listen: diff --git a/api/src/test/resources/features/raise.yaml b/api/src/test/resources/features/raise.yaml index 9dd6b4f3..7745162c 100644 --- a/api/src/test/resources/features/raise.yaml +++ b/api/src/test/resources/features/raise.yaml @@ -2,10 +2,12 @@ document: dsl: 1.0.0-alpha1 namespace: default name: raise-custom-error + version: 1.0.0 do: - raiseError: raise: error: status: 400 type: https://serverlessworkflow.io/errors/types/compliance - title: Compliance Error \ No newline at end of file + title: Compliance Error + instance: raiseError \ No newline at end of file diff --git a/api/src/test/resources/features/set.yaml b/api/src/test/resources/features/set.yaml index 1589792f..dfebbf2d 100644 --- a/api/src/test/resources/features/set.yaml +++ b/api/src/test/resources/features/set.yaml @@ -2,6 +2,7 @@ document: dsl: 1.0.0-alpha1 namespace: default name: set + version: 1.0.0 do: - setShape: set: diff --git a/api/src/test/resources/features/switch.yaml b/api/src/test/resources/features/switch.yaml index 74d046cb..aa073fed 100644 --- a/api/src/test/resources/features/switch.yaml +++ b/api/src/test/resources/features/switch.yaml @@ -2,6 +2,7 @@ document: dsl: 1.0.0-alpha1 namespace: default name: switch-match + version: 1.0.0 do: - switchColor: switch: diff --git a/api/src/test/resources/features/try.yaml b/api/src/test/resources/features/try.yaml index 7f9ba599..ec19194d 100644 --- a/api/src/test/resources/features/try.yaml +++ b/api/src/test/resources/features/try.yaml @@ -2,6 +2,7 @@ document: dsl: 1.0.0-alpha1 namespace: default name: try-catch-404 + version: 1.0.0 do: - tryGetPet: try: diff --git a/pom.xml b/pom.xml index 42064622..5311c098 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,8 @@ 5.11.0 5.12.0 2.0.16 - + 8.0.1.Final + 5.0.0 true @@ -124,6 +125,17 @@ json-schema-validator ${version.com.networknt} + + org.hibernate.validator + hibernate-validator + ${version.org.hibernate.validator} + + + org.glassfish.expressly + expressly + ${version.org.glassfish.expressly} + + com.fasterxml.jackson.dataformat jackson-dataformat-yaml