Repository: smallrye/smallrye-graphql
Issue: Typesafe Client: send Booleans not as String

E.g. mutations containing a `Boolean` non-primitive results in a String `true` or `false` but not in the proper literals.

Changed files:
client/implementation/src/test/java/test/unit/MutationBehavior.java
client/implementation/src/test/java/test/unit/ParametersBehavior.java
client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/RequestBuilder.java
client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/reflection/TypeInfo.java
client/implementation/src/test/java/test/unit/NestedBehavior.java

diff --git a/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/RequestBuilder.java b/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/RequestBuilder.java
index 3b6b5efb..4b01beee 100644
--- a/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/RequestBuilder.java
+++ b/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/RequestBuilder.java
@@ -61,6 +61,7 @@ class RequestBuilder {
 
     public boolean unquoted(TypeInfo type) {
         return type.isPrimitive()
+                || Boolean.class.isAssignableFrom(type.getRawType())
                 || Number.class.isAssignableFrom(type.getRawType())
                 || type.isEnum();
     }
diff --git a/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/reflection/TypeInfo.java b/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/reflection/TypeInfo.java
index b9cc7915..127e7247 100644
--- a/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/reflection/TypeInfo.java
+++ b/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/reflection/TypeInfo.java
@@ -126,6 +126,7 @@ public class TypeInfo {
     public boolean isScalar() {
         return isPrimitive()
                 || Number.class.isAssignableFrom(getRawType())
+                || Boolean.class.isAssignableFrom(getRawType())
                 || isEnum()
                 || CharSequence.class.isAssignableFrom(getRawType())
                 || Character.class.equals(getRawType()) // has a valueOf(char), not valueOf(String)
diff --git a/client/implementation/src/test/java/test/unit/MutationBehavior.java b/client/implementation/src/test/java/test/unit/MutationBehavior.java
index 026cd11c..532da821 100644
--- a/client/implementation/src/test/java/test/unit/MutationBehavior.java
+++ b/client/implementation/src/test/java/test/unit/MutationBehavior.java
@@ -239,4 +239,106 @@ public class MutationBehavior {
         then(fixture.mutation()).isEqualTo("say(greeting: {text: 'one', someEnum: ONE}) {text count}");
         then(greeting).isEqualTo(new Greeting("ho", 3));
     }
+
+    private static class PrimitiveTypesClass {
+        boolean b = true;
+        char c = 'a';
+        byte y = 0x7;
+        short s = 0xff;
+        int i = 123456;
+        long l = 987654321L;
+        float f = 12.34f;
+        double d = 56.78d;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o)
+                return true;
+            if (o == null || getClass() != o.getClass())
+                return false;
+            PrimitiveTypesClass that = (PrimitiveTypesClass) o;
+            return this.b == that.b
+                    && this.c == that.c
+                    && this.y == that.y
+                    && this.s == that.s
+                    && this.i == that.i
+                    && this.l == that.l
+                    && this.f == that.f
+                    && this.d == that.d;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(b, c, y, s, i, l, f, d);
+        }
+    }
+
+    @GraphQlClientApi
+    interface MutationWithPrimitivesApi {
+        @Mutation
+        String run(PrimitiveTypesClass primitives);
+    }
+
+    @Test
+    public void shouldCallMutationWithPrimitives() {
+        fixture.returnsData("'run':'okay'");
+        MutationWithPrimitivesApi api = fixture.builder().build(MutationWithPrimitivesApi.class);
+
+        String result = api.run(new PrimitiveTypesClass());
+
+        then(fixture.mutation())
+                .isEqualTo("run(primitives: {b: true, c: a, y: 7, s: 255, i: 123456, l: 987654321, f: 12.34, d: 56.78})");
+        then(result).isEqualTo("okay");
+    }
+
+    private static class PrimitiveWrapperTypesClass {
+        Boolean b = true;
+        Character c = 'a';
+        Byte y = 0x7;
+        Short s = 0xff;
+        Integer i = 123456;
+        Long l = 987654321L;
+        Float f = 12.34f;
+        Double d = 56.78d;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o)
+                return true;
+            if (o == null || getClass() != o.getClass())
+                return false;
+            PrimitiveWrapperTypesClass that = (PrimitiveWrapperTypesClass) o;
+            return this.b.equals(that.b)
+                    && this.c.equals(that.c)
+                    && this.y.equals(that.y)
+                    && this.s.equals(that.s)
+                    && this.i.equals(that.i)
+                    && this.l.equals(that.l)
+                    && this.f.equals(that.f)
+                    && this.d.equals(that.d);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(b, c, y, s, i, l, f, d);
+        }
+    }
+
+    @GraphQlClientApi
+    interface MutationWithPrimitiveWrappersApi {
+        @Mutation
+        String run(PrimitiveWrapperTypesClass primitives);
+    }
+
+    @Test
+    public void shouldCallMutationWithPrimitiveWrappers() {
+        fixture.returnsData("'run':'okay'");
+        MutationWithPrimitiveWrappersApi api = fixture.builder().build(MutationWithPrimitiveWrappersApi.class);
+
+        String result = api.run(new PrimitiveWrapperTypesClass());
+
+        then(fixture.mutation())
+                .isEqualTo("run(primitives: {b: true, c: 'a', y: 7, s: 255, i: 123456, l: 987654321, f: 12.34, d: 56.78})");
+        then(result).isEqualTo("okay");
+    }
 }
diff --git a/client/implementation/src/test/java/test/unit/NestedBehavior.java b/client/implementation/src/test/java/test/unit/NestedBehavior.java
index ef6867bd..e072d238 100644
--- a/client/implementation/src/test/java/test/unit/NestedBehavior.java
+++ b/client/implementation/src/test/java/test/unit/NestedBehavior.java
@@ -145,14 +145,16 @@ public class NestedBehavior {
     private static class Greeting {
         String text;
         int code;
+        Boolean successful;
 
         @SuppressWarnings("unused")
         Greeting() {
         }
 
-        Greeting(String text, int code) {
+        Greeting(String text, int code, Boolean successful) {
             this.text = text;
             this.code = code;
+            this.successful = successful;
         }
 
         @Override
@@ -162,24 +164,24 @@ public class NestedBehavior {
             if (o == null || getClass() != o.getClass())
                 return false;
             Greeting greeting = (Greeting) o;
-            return code == greeting.code && text.equals(greeting.text);
+            return code == greeting.code && text.equals(greeting.text) && successful == greeting.successful;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(text, code);
+            return Objects.hash(text, code, successful);
         }
     }
 
     @Test
     public void shouldCallObjectQuery() {
-        fixture.returnsData("'greeting':{'text':'foo','code':5}");
+        fixture.returnsData("'greeting':{'text':'foo','code':5, 'successful':true}");
         ObjectApi api = fixture.builder().build(ObjectApi.class);
 
         Greeting greeting = api.greeting();
 
-        then(fixture.query()).isEqualTo("greeting {text code}");
-        then(greeting).isEqualTo(new Greeting("foo", 5));
+        then(fixture.query()).isEqualTo("greeting {text code successful}");
+        then(greeting).isEqualTo(new Greeting("foo", 5, true));
     }
 
     @Test
@@ -222,15 +224,19 @@ public class NestedBehavior {
 
     @Test
     public void shouldCallObjectListQuery() {
-        fixture.returnsData("'greetings':[{'text':'a','code':1},{'text':'b','code':2}]");
+        fixture.returnsData("'greetings':["
+                + "{'text':'a','code':1},"
+                + "{'text':'b','code':2,'successful':true},"
+                + "{'text':'c','code':3,'successful':false}]");
         ObjectListApi api = fixture.builder().build(ObjectListApi.class);
 
         List<Greeting> greeting = api.greetings();
 
-        then(fixture.query()).isEqualTo("greetings {text code}");
+        then(fixture.query()).isEqualTo("greetings {text code successful}");
         then(greeting).containsExactly(
-                new Greeting("a", 1),
-                new Greeting("b", 2));
+                new Greeting("a", 1, null),
+                new Greeting("b", 2, true),
+                new Greeting("c", 3, false));
     }
 
     @Test
@@ -318,9 +324,9 @@ public class NestedBehavior {
 
         GreetingContainer container = api.container();
 
-        then(fixture.query()).isEqualTo("container {greeting {text code} count}");
+        then(fixture.query()).isEqualTo("container {greeting {text code successful} count}");
         then(container).isEqualTo(new GreetingContainer(
-                new Greeting("a", 1), 3));
+                new Greeting("a", 1, null), 3));
     }
 
     @GraphQlClientApi
@@ -366,9 +372,9 @@ public class NestedBehavior {
 
         GreetingsContainer container = api.container();
 
-        then(fixture.query()).isEqualTo("container {greetings {text code} count}");
+        then(fixture.query()).isEqualTo("container {greetings {text code successful} count}");
         then(container).isEqualTo(new GreetingsContainer(
-                asList(new Greeting("a", 1), new Greeting("b", 2)), 3));
+                asList(new Greeting("a", 1, null), new Greeting("b", 2, null)), 3));
     }
 
     @GraphQlClientApi
@@ -442,9 +448,9 @@ public class NestedBehavior {
 
         WrappedGreetingContainer container = api.container();
 
-        then(fixture.query()).isEqualTo("container {greeting {value {text code}} count}");
+        then(fixture.query()).isEqualTo("container {greeting {value {text code successful}} count}");
         then(container).isEqualTo(new WrappedGreetingContainer(
-                new Wrapper<>(new Greeting("a", 1)), 3));
+                new Wrapper<>(new Greeting("a", 1, null)), 3));
     }
 
     @GraphQlClientApi
@@ -611,8 +617,8 @@ public class NestedBehavior {
 
         Sub sub = api.call();
 
-        then(fixture.query()).isEqualTo("call {greeting {text code} count}");
-        then(sub.greeting).isEqualTo(new Greeting("a", 1));
+        then(fixture.query()).isEqualTo("call {greeting {text code successful} count}");
+        then(sub.greeting).isEqualTo(new Greeting("a", 1, null));
         then(sub.count).isEqualTo(3);
     }
 
diff --git a/client/implementation/src/test/java/test/unit/ParametersBehavior.java b/client/implementation/src/test/java/test/unit/ParametersBehavior.java
index 064c83f4..5035171b 100644
--- a/client/implementation/src/test/java/test/unit/ParametersBehavior.java
+++ b/client/implementation/src/test/java/test/unit/ParametersBehavior.java
@@ -57,10 +57,26 @@ public class ParametersBehavior {
     }
 
     @GraphQlClientApi
-    interface BooleanParamApi {
+    interface BoolParamApi {
         String greeting(boolean really);
     }
 
+    @Test
+    public void shouldCallBoolParamQuery() {
+        fixture.returnsData("'greeting':'ho'");
+        BoolParamApi api = fixture.builder().build(BoolParamApi.class);
+
+        String greeting = api.greeting(true);
+
+        then(fixture.query()).isEqualTo("greeting(really: true)");
+        then(greeting).isEqualTo("ho");
+    }
+
+    @GraphQlClientApi
+    interface BooleanParamApi {
+        String greeting(Boolean really);
+    }
+
     @Test
     public void shouldCallBooleanParamQuery() {
         fixture.returnsData("'greeting':'ho'");