Repository: serverlessworkflow/sdk-java
Pull request #403 (2024-08-13 12:17:42)

Add helper function to facilitate use of library with oneOf types
Any**Many thanks for submitting your Pull Request :heart:!**

**What this PR does / why we need it**:
I was using the library and it felt a by unconfortable to check every item on an oneOf if it was null or not to define it inner type so i made a minor tweak on generator and deserializers to haver a hint allowing the use of a switch expression to select the type.

Here follows a example of usage:

```java
switch (taskitem.get()) {
            case CallTask callTask -> {
                switch (callTask.get()) {
                    case CallHTTP httpCall -> {
                        yield activities.execute(HTTP_ACTION_METHOD_NAME, TaskResult.class, execution.getContext(), input);
                    }
                    case CallGRPC grpcCall -> {

                        throw new NotImplementedException();
                    }
                    case CallFunction functionCall -> {

                        throw new NotImplementedException();
                    }
                    case CallOpenAPI openApiCall -> {

                        throw new NotImplementedException();
                    }
                    case CallAsyncAPI asyncApiCall -> {

                        throw new NotImplementedException();
                    }
                    default -> throw new IllegalStateException("Unexpected value: " + callTask.get());
                }
            }
            case SwitchTask switchTask -> {
                throw new NotImplementedException();
            }
            case DoTask doTask -> {

                throw new NotImplementedException();
            }
            case ForkTask forkTask -> {

                throw new NotImplementedException();
            }
            case ForTask forTask -> {

                throw new NotImplementedException();
            }
            case RaiseTask raiseTask -> {

                throw new NotImplementedException();
            }
            case SetTask setTask -> {

                throw new NotImplementedException();
            }
            case TryTask tryTask -> {

                throw new NotImplementedException();
            }
            case WaitTask waitTask -> {

                throw new NotImplementedException();
            }
            case ListenTask listenTask -> {

                throw new NotImplementedException();
            }
            case EmitTask emitTask -> {

                throw new NotImplementedException();
            }
            default -> {
                yield null;
            }
 } 
```
**Special notes for reviewers**:

I'm open to comments and reviews and still can contribute on this PR

**Additional information (if needed):**

ricardozanini: @uasouz thank you! Can u please sign your commit?
uasouz: @fjtirado Thanks for the feedback, I will adjust the PR accordingly and send new commits soon.

@ricardozanini Sorry for many force commits, had small issue with bad config not signing with mixed git configs.
ricardozanini: @uasouz please try squashing your commits and sign it.
uasouz: The PR is updated.

I squashed the commits and looks like they are signed and verified. But it still fails the check. I have to figure out why. But if there is anything else I can change on the PR, just comment.

Patch:
diff --git a/api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java b/api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java
new file mode 100644
index 0000000..f3d2ab2
--- /dev/null
+++ b/api/src/main/java/io/serverlessworkflow/api/OneOfValueProvider.java
@@ -0,0 +1,20 @@
+/*
+ * 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;
+
+public interface OneOfValueProvider {
+  Object get();
+}
diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java
index e2b781c..1e99f0f 100644
--- a/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java
+++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/AllAnyOneOfSchemaRule.java
@@ -31,6 +31,7 @@ import com.sun.codemodel.JMethod;
 import com.sun.codemodel.JMod;
 import com.sun.codemodel.JPackage;
 import com.sun.codemodel.JType;
+import com.sun.codemodel.JVar;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URLDecoder;
@@ -102,13 +103,27 @@ class AllAnyOneOfSchemaRule extends SchemaRule {
 
   private JDefinedClass populateClass(
       JDefinedClass definedClass, Optional<JType> refType, Collection<JType> unionTypes) {
-    unionTypes.forEach(unionType -> wrapIt(definedClass, unionType));
+    JType clazzClass = definedClass.owner()._ref(Object.class);
+
+    JFieldVar valueField =
+        definedClass.field(
+            JMod.PRIVATE,
+            clazzClass,
+            ruleFactory.getNameHelper().getPropertyName("value", null),
+            null);
+
+    definedClass._implements(
+        definedClass.owner().ref(GeneratorUtils.ONE_OF_VALUE_PROVIDER_INTERFACE_NAME));
+
+    GeneratorUtils.implementInterface(definedClass, valueField);
+
+    unionTypes.forEach(unionType -> wrapIt(definedClass, valueField, unionType));
     refType.ifPresent(
         type -> {
           if (type instanceof JClass) {
             definedClass._extends((JClass) type);
           } else {
-            wrapIt(definedClass, type);
+            wrapIt(definedClass, valueField, type);
           }
         });
     if (definedClass.constructors().hasNext()
@@ -166,23 +181,25 @@ class AllAnyOneOfSchemaRule extends SchemaRule {
       definedClass
           .annotate(JsonDeserialize.class)
           .param("using", generateDeserializer(definedClass, unionTypes));
+
       return populateClass(definedClass, refType, unionTypes);
     } catch (JClassAlreadyExistsException e) {
       throw new IllegalArgumentException(e);
     }
   }
 
-  private void wrapIt(JDefinedClass definedClass, JType unionType) {
+  private void wrapIt(JDefinedClass definedClass, JFieldVar valueField, JType unionType) {
     final String name = unionType.name();
     JFieldVar instanceField =
         definedClass.field(
             JMod.PRIVATE, unionType, ruleFactory.getNameHelper().getPropertyName(name, null));
     GeneratorUtils.buildMethod(definedClass, instanceField, ruleFactory.getNameHelper(), name);
     JMethod constructor = definedClass.constructor(JMod.PUBLIC);
+    JVar instanceParam = constructor.param(unionType, instanceField.name());
     constructor
         .body()
-        .assign(
-            JExpr._this().ref(instanceField), constructor.param(unionType, instanceField.name()));
+        .assign(JExpr._this().ref(valueField), instanceParam)
+        .assign(JExpr._this().ref(instanceField), instanceParam);
   }
 
   private void unionType(
diff --git a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java
index ffc2346..ebf1c50 100644
--- a/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java
+++ b/custom-generator/src/main/java/io/serverlessworkflow/generator/GeneratorUtils.java
@@ -36,6 +36,8 @@ public class GeneratorUtils {
       "io.serverlessworkflow.serialization.SerializeHelper";
   public static final String DESERIALIZE_HELPER_NAME =
       "io.serverlessworkflow.serialization.DeserializeHelper";
+  public static final String ONE_OF_VALUE_PROVIDER_INTERFACE_NAME =
+      "io.serverlessworkflow.api.OneOfValueProvider";
 
   @FunctionalInterface
   public interface SerializerFiller {
@@ -55,6 +57,13 @@ public class GeneratorUtils {
     return createClass(relatedClass, JsonDeserializer.class, "Deserializer");
   }
 
+  public static JMethod implementInterface(JDefinedClass definedClass, JFieldVar valueField) {
+    JMethod method = definedClass.method(JMod.PUBLIC, Object.class, "get");
+    method.annotate(Override.class);
+    method.body()._return(valueField);
+    return method;
+  }
+
   public static JMethod buildMethod(
       JDefinedClass definedClass, JFieldVar instanceField, NameHelper nameHelper, String name) {
     JMethod method =


Test patch:
diff --git a/api/src/test/java/io/serverlessworkflow/api/ApiTest.java b/api/src/test/java/io/serverlessworkflow/api/ApiTest.java
index 8e4c27b..ac5f753 100644
--- a/api/src/test/java/io/serverlessworkflow/api/ApiTest.java
+++ b/api/src/test/java/io/serverlessworkflow/api/ApiTest.java
@@ -34,12 +34,14 @@ public class ApiTest {
     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(task.getDoTask()).isNull();
-    CallHTTP httpCall = callTask.getCallHTTP();
-    assertThat(httpCall).isNotNull();
-    assertThat(callTask.getCallAsyncAPI()).isNull();
-    assertThat(httpCall.getWith().getMethod()).isEqualTo("get");
+    if (task.get() instanceof CallTask) {
+      CallTask callTask = task.getCallTask();
+      assertThat(callTask).isNotNull();
+      assertThat(task.getDoTask()).isNull();
+      CallHTTP httpCall = callTask.getCallHTTP();
+      assertThat(httpCall).isNotNull();
+      assertThat(callTask.getCallAsyncAPI()).isNull();
+      assertThat(httpCall.getWith().getMethod()).isEqualTo("get");
+    }
   }
 }