diff --git a/experimental/agentic/src/main/java/io/serverlessworkflow/impl/model/agentic/AgenticModel.java b/experimental/agentic/src/main/java/io/serverlessworkflow/impl/model/agentic/AgenticModel.java index 2270fe0b..3046047a 100644 --- a/experimental/agentic/src/main/java/io/serverlessworkflow/impl/model/agentic/AgenticModel.java +++ b/experimental/agentic/src/main/java/io/serverlessworkflow/impl/model/agentic/AgenticModel.java @@ -46,18 +46,14 @@ public Optional> asMap() { } @Override - public Optional as(Class clazz) { - if (AgenticScope.class.isAssignableFrom(clazz)) { - return Optional.of(clazz.cast(this.agenticScope)); - } else if (Map.class.isAssignableFrom(clazz)) { - return asMap().map(clazz::cast); - } else { - return super.as(clazz); - } + public Object asJavaObject() { + return agenticScope; } @Override - public Object asJavaObject() { - return agenticScope; + protected Optional convert(Class clazz) { + return AgenticScope.class.isAssignableFrom(clazz) + ? Optional.of(clazz.cast(this.agenticScope)) + : super.convert(clazz); } } diff --git a/experimental/model/src/main/java/io/serverlessworkflow/impl/model/func/JavaModel.java b/experimental/model/src/main/java/io/serverlessworkflow/impl/model/func/JavaModel.java index b4cec73e..da3fa9b8 100644 --- a/experimental/model/src/main/java/io/serverlessworkflow/impl/model/func/JavaModel.java +++ b/experimental/model/src/main/java/io/serverlessworkflow/impl/model/func/JavaModel.java @@ -15,6 +15,7 @@ */ package io.serverlessworkflow.impl.model.func; +import io.serverlessworkflow.impl.AbstractWorkflowModel; import io.serverlessworkflow.impl.WorkflowModel; import java.time.OffsetDateTime; import java.util.Collection; @@ -24,7 +25,7 @@ import java.util.Optional; import java.util.stream.Collectors; -public class JavaModel implements WorkflowModel { +public class JavaModel extends AbstractWorkflowModel { protected Object object; @@ -65,7 +66,6 @@ public Optional asNumber() { @Override public Optional> asMap() { - return object instanceof Map ? Optional.of((Map) object) : Optional.empty(); } @@ -94,10 +94,7 @@ public Class objectClass() { } @Override - public Optional as(Class clazz) { - if (WorkflowModel.class.isAssignableFrom(clazz)) { - return Optional.of(clazz.cast(this)); - } + protected Optional convert(Class clazz) { return object != null && clazz.isAssignableFrom(object.getClass()) ? Optional.of(clazz.cast(object)) : Optional.empty(); diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/AbstractWorkflowModel.java b/impl/core/src/main/java/io/serverlessworkflow/impl/AbstractWorkflowModel.java new file mode 100644 index 00000000..93ad46ba --- /dev/null +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/AbstractWorkflowModel.java @@ -0,0 +1,48 @@ +/* + * 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.impl; + +import java.time.OffsetDateTime; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; + +public abstract class AbstractWorkflowModel implements WorkflowModel { + + protected abstract Optional convert(Class clazz); + + @Override + public Optional as(Class clazz) { + if (WorkflowModel.class.isAssignableFrom(clazz)) { + return (Optional) Optional.of(this); + } else if (String.class.isAssignableFrom(clazz)) { + return (Optional) asText(); + } else if (Boolean.class.isAssignableFrom(clazz)) { + return (Optional) asBoolean(); + } else if (OffsetDateTime.class.isAssignableFrom(clazz)) { + return (Optional) asDate(); + } else if (Number.class.isAssignableFrom(clazz)) { + return (Optional) asNumber(); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = asCollection(); + return collection.isEmpty() ? Optional.empty() : (Optional) Optional.of(collection); + } else if (Map.class.isAssignableFrom(clazz)) { + return (Optional) asMap(); + } else { + return convert(clazz); + } + } +} diff --git a/impl/model/pom.xml b/impl/model/pom.xml index c7377971..fad5716b 100644 --- a/impl/model/pom.xml +++ b/impl/model/pom.xml @@ -16,5 +16,17 @@ io.serverlessworkflow serverlessworkflow-impl-core + + org.junit.jupiter + junit-jupiter-engine + + + org.assertj + assertj-core + + + ch.qos.logback + logback-classic + \ No newline at end of file diff --git a/impl/model/src/main/java/io/serverlessworkflow/impl/model/jackson/JacksonModel.java b/impl/model/src/main/java/io/serverlessworkflow/impl/model/jackson/JacksonModel.java index 91e59116..254c4eda 100644 --- a/impl/model/src/main/java/io/serverlessworkflow/impl/model/jackson/JacksonModel.java +++ b/impl/model/src/main/java/io/serverlessworkflow/impl/model/jackson/JacksonModel.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.BooleanNode; import com.fasterxml.jackson.databind.node.NullNode; +import io.serverlessworkflow.impl.AbstractWorkflowModel; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.jackson.JsonUtils; import java.time.OffsetDateTime; @@ -31,7 +32,7 @@ @JsonSerialize(using = JacksonModelSerializer.class) @JsonDeserialize(using = JacksonModelDeserializer.class) -public class JacksonModel implements WorkflowModel { +public class JacksonModel extends AbstractWorkflowModel { protected JsonNode node; @@ -68,13 +69,6 @@ public Optional asNumber() { return node.isNumber() ? Optional.of(node.asLong()) : Optional.empty(); } - @Override - public Optional as(Class clazz) { - return clazz.isAssignableFrom(node.getClass()) - ? Optional.of(clazz.cast(node)) - : Optional.of(JsonUtils.convertValue(node, clazz)); - } - @Override public String toString() { return node.toPrettyString(); @@ -84,7 +78,7 @@ public String toString() { public Optional> asMap() { // TODO use generic to avoid warning return node.isObject() - ? Optional.of(JsonUtils.convertValue(node, Map.class)) + ? Optional.of(JsonUtils.mapper().convertValue(node, Map.class)) : Optional.empty(); } @@ -97,4 +91,11 @@ public Object asJavaObject() { public Class objectClass() { return node.getClass(); } + + @Override + protected Optional convert(Class clazz) { + return clazz.isAssignableFrom(node.getClass()) + ? Optional.of(clazz.cast(node)) + : Optional.of(JsonUtils.mapper().convertValue(node, clazz)); + } } diff --git a/impl/model/src/test/java/io/serverlessworkflow/impl/model/jackson/JacksonModelTest.java b/impl/model/src/test/java/io/serverlessworkflow/impl/model/jackson/JacksonModelTest.java new file mode 100644 index 00000000..2f26ba9f --- /dev/null +++ b/impl/model/src/test/java/io/serverlessworkflow/impl/model/jackson/JacksonModelTest.java @@ -0,0 +1,95 @@ +/* + * 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.impl.model.jackson; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.databind.JsonNode; +import io.serverlessworkflow.impl.WorkflowModel; +import io.serverlessworkflow.impl.WorkflowModelFactory; +import io.serverlessworkflow.impl.jackson.JsonUtils; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class JacksonModelTest { + + private static WorkflowModelFactory factory; + + @BeforeAll + static void init() { + factory = new JacksonModelFactory(); + } + + @Test + void testObjectFromNode() { + testObjectNode( + factory.fromAny( + JsonUtils.mapper() + .createObjectNode() + .put("name", "Javierito") + .put("jobs", 3) + .put("male", true))); + } + + @Test + void testObjectFromString() { + testObjectNode(factory.fromAny("{\"name\":\"Javierito\",\"jobs\":3,\"male\":true}")); + } + + @Test + void testObjectFromMap() { + Map map = new LinkedHashMap(); + map.put("name", "Javierito"); + map.put("jobs", 3); + map.put("male", true); + testObjectNode(factory.fromAny(map)); + } + + private void testObjectNode(WorkflowModel model) { + assertThat(model.as(JsonNode.class).orElseThrow()) + .isEqualTo( + JsonUtils.mapper() + .createObjectNode() + .put("name", "Javierito") + .put("jobs", 3) + .put("male", true)); + assertThat(model.as(String.class).orElseThrow()) + .isEqualTo("{\"name\":\"Javierito\",\"jobs\":3,\"male\":true}"); + assertThat(model.as(String.class)).isEqualTo(model.asText()); + assertThat(model.as(Map.class)).isEqualTo(model.asMap()); + assertThat(model.as(Map.class).orElseThrow()) + .isEqualTo(Map.of("name", "Javierito", "jobs", 3, "male", true)); + } + + @Test + void testCollection() { + WorkflowModel model = + factory.fromAny( + JsonUtils.mapper() + .createArrayNode() + .add( + JsonUtils.mapper() + .createObjectNode() + .put("name", "Javierito") + .put("jobs", 3) + .put("male", true))); + testObjectNode(factory.fromAny(model.asCollection().iterator().next())); + testObjectNode(factory.fromAny(model.as(Collection.class).orElseThrow().iterator().next())); + } +}