Repository: smallrye/smallrye-graphql
Issue: ClassCastException with Set as parameter

Hello, 

as already discussed with @t1, there is an issue with GraphQl client.

We try to create a GraphQl mutation (GraphQl version 1.0.11) which receives a Set as parameter. 
`void add(Set<AnyObjectType> values);`
It seems the RequestBuilder#buildParam() method should not use the specific List type but a more general Collection type? This leads to the following stack:

```
java.lang.ClassCastException: class java.util.ImmutableCollections$SetN cannot be cast to class java.util.List (java.util.ImmutableCollections$SetN and java.util.List are in module java.base of loader 'bootstrap')

at io.smallrye.graphql.client.typesafe.impl.RequestBuilder.buildParam(RequestBuilder.java:48)
at io.smallrye.graphql.client.typesafe.impl.RequestBuilder.appendParam(RequestBuilder.java:39)
at io.smallrye.graphql.client.typesafe.impl.RequestBuilder.build(RequestBuilder.java:30)
at io.smallrye.graphql.client.typesafe.impl.GraphQlClientProxy.request(GraphQlClientProxy.java:60)
at io.smallrye.graphql.client.typesafe.impl.GraphQlClientProxy.invoke(GraphQlClientProxy.java:46)
at io.smallrye.graphql.client.typesafe.impl.GraphQlClientBuilderImpl.lambda$build$0(GraphQlClientBuilderImpl.java:47)
at test.integration.customer.$Proxy26.addMarketingPermissions(Unknown Source)
at test.integration.customer.GraphQlCustomerAT$WhenMarketingPermissions.shouldAddPermissionsForAuthorizedUser(GraphQlCustomerAT.java:117)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
```



Changed files:
client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/RequestBuilder.java
client/implementation/src/test/java/test/unit/ParametersBehavior.java
client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/GraphQlClientProxy.java

diff --git a/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/GraphQlClientProxy.java b/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/GraphQlClientProxy.java
index bdbfe1cf..df002a75 100644
--- a/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/GraphQlClientProxy.java
+++ b/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/GraphQlClientProxy.java
@@ -1,6 +1,7 @@
 package io.smallrye.graphql.client.typesafe.impl;
 
 import static java.util.stream.Collectors.joining;
+import static javax.json.JsonValue.ValueType.ARRAY;
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
 import static javax.ws.rs.core.Response.Status.Family.SUCCESSFUL;
 
@@ -8,7 +9,6 @@ import java.io.StringReader;
 import java.util.Stack;
 
 import javax.json.Json;
-import javax.json.JsonArray;
 import javax.json.JsonBuilderFactory;
 import javax.json.JsonObject;
 import javax.json.JsonObjectBuilder;
@@ -123,13 +123,15 @@ class GraphQlClientProxy {
 
     private JsonObject readResponse(String request, String response) {
         JsonObject responseJson = jsonReaderFactory.createReader(new StringReader(response)).readObject();
-        if (responseJson.containsKey("errors") && !isEmpty(responseJson.getJsonArray("errors")))
+        if (hasErrors(responseJson))
             throw new GraphQlClientException("errors from service: " + responseJson.getJsonArray("errors") + ":\\n  " + request);
         return responseJson;
     }
 
-    private boolean isEmpty(JsonArray array) {
-        return array == null || array.isEmpty();
+    private boolean hasErrors(JsonObject responseJson) {
+        return responseJson.containsKey("errors")
+                && responseJson.get("errors").getValueType() == ARRAY
+                && !responseJson.getJsonArray("errors").isEmpty();
     }
 
     private JsonValue getData(MethodInfo method, JsonObject responseJson) {
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 4b01beee..338a8732 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
@@ -2,6 +2,9 @@ package io.smallrye.graphql.client.typesafe.impl;
 
 import static java.util.stream.Collectors.toList;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 
 import io.smallrye.graphql.client.typesafe.api.Header;
@@ -45,11 +48,19 @@ class RequestBuilder {
         else if (type.isScalar())
             buildScalarParam(type, value);
         else if (type.isCollection())
-            buildArrayParam(type.getItemType(), (List<?>) value);
+            buildArrayParam(type.getItemType(), asList(value));
         else
             buildObjectParam(type, value);
     }
 
+    private List<?> asList(Object value) {
+        if (value instanceof List)
+            return (List<?>) value;
+        if (value.getClass().isArray())
+            return Arrays.asList((Object[]) value);
+        return new ArrayList<>((Collection<?>) value);
+    }
+
     private void buildScalarParam(TypeInfo type, Object value) {
         boolean quoted = !unquoted(type);
         if (quoted)
diff --git a/client/implementation/src/test/java/test/unit/ParametersBehavior.java b/client/implementation/src/test/java/test/unit/ParametersBehavior.java
index 5035171b..e5047772 100644
--- a/client/implementation/src/test/java/test/unit/ParametersBehavior.java
+++ b/client/implementation/src/test/java/test/unit/ParametersBehavior.java
@@ -3,8 +3,13 @@ package test.unit;
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.BDDAssertions.then;
 
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Queue;
+import java.util.Set;
 
 import org.junit.jupiter.api.Test;
 
@@ -135,7 +140,7 @@ public class ParametersBehavior {
 
     @GraphQlClientApi
     interface ArrayParamApi {
-        boolean greetings(List<String> greets);
+        boolean greetings(String[] greets);
     }
 
     @Test
@@ -143,6 +148,70 @@ public class ParametersBehavior {
         fixture.returnsData("'greetings':true");
         ArrayParamApi api = fixture.builder().build(ArrayParamApi.class);
 
+        boolean success = api.greetings(new String[] { "hi", "ho" });
+
+        then(fixture.query()).isEqualTo("greetings(greets: ['hi', 'ho'])");
+        then(success).isTrue();
+    }
+
+    @GraphQlClientApi
+    interface ListParamApi {
+        boolean greetings(List<String> greets);
+    }
+
+    @Test
+    public void shouldCallListParamQuery() {
+        fixture.returnsData("'greetings':true");
+        ListParamApi api = fixture.builder().build(ListParamApi.class);
+
+        boolean success = api.greetings(asList("hi", "ho"));
+
+        then(fixture.query()).isEqualTo("greetings(greets: ['hi', 'ho'])");
+        then(success).isTrue();
+    }
+
+    @GraphQlClientApi
+    interface SetParamApi {
+        boolean greetings(Set<String> greets);
+    }
+
+    @Test
+    public void shouldCallSetParamQuery() {
+        fixture.returnsData("'greetings':true");
+        SetParamApi api = fixture.builder().build(SetParamApi.class);
+
+        boolean success = api.greetings(new HashSet<>(asList("hi", "ho")));
+
+        then(fixture.query()).isEqualTo("greetings(greets: ['hi', 'ho'])");
+        then(success).isTrue();
+    }
+
+    @GraphQlClientApi
+    interface QueueParamApi {
+        boolean greetings(Queue<String> greets);
+    }
+
+    @Test
+    public void shouldCallQueueParamQuery() {
+        fixture.returnsData("'greetings':true");
+        QueueParamApi api = fixture.builder().build(QueueParamApi.class);
+
+        boolean success = api.greetings(new ArrayDeque<>(asList("hi", "ho")));
+
+        then(fixture.query()).isEqualTo("greetings(greets: ['hi', 'ho'])");
+        then(success).isTrue();
+    }
+
+    @GraphQlClientApi
+    interface CollectionParamApi {
+        boolean greetings(Collection<String> greets);
+    }
+
+    @Test
+    public void shouldCallCollectionParamQuery() {
+        fixture.returnsData("'greetings':true");
+        CollectionParamApi api = fixture.builder().build(CollectionParamApi.class);
+
         boolean success = api.greetings(asList("hi", "ho"));
 
         then(fixture.query()).isEqualTo("greetings(greets: ['hi', 'ho'])");
@@ -150,14 +219,14 @@ public class ParametersBehavior {
     }
 
     @GraphQlClientApi
-    interface ObjectArrayParamApi {
+    interface ObjectListParamApi {
         boolean greetings(List<Greeting> greets);
     }
 
     @Test
-    public void shouldCallObjectArrayParamQuery() {
+    public void shouldCallObjectListParamQuery() {
         fixture.returnsData("'greetings':true");
-        ObjectArrayParamApi api = fixture.builder().build(ObjectArrayParamApi.class);
+        ObjectListParamApi api = fixture.builder().build(ObjectListParamApi.class);
 
         boolean success = api.greetings(asList(new Greeting("hi", 5), new Greeting("ho", 3)));
 
@@ -166,26 +235,26 @@ public class ParametersBehavior {
     }
 
     @GraphQlClientApi
-    interface ArrayObjectParamApi {
-        boolean foo(ArrayObject bar);
+    interface ListObjectParamApi {
+        boolean foo(ListObject bar);
     }
 
-    private static class ArrayObject {
+    private static class ListObject {
         List<String> texts;
         int count;
 
-        ArrayObject(List<String> texts, int count) {
+        ListObject(List<String> texts, int count) {
             this.texts = texts;
             this.count = count;
         }
     }
 
     @Test
-    public void shouldCallArrayObjectParamQuery() {
+    public void shouldCallListObjectParamQuery() {
         fixture.returnsData("'foo':true");
-        ArrayObjectParamApi api = fixture.builder().build(ArrayObjectParamApi.class);
+        ListObjectParamApi api = fixture.builder().build(ListObjectParamApi.class);
 
-        boolean success = api.foo(new ArrayObject(asList("hi", "ho"), 3));
+        boolean success = api.foo(new ListObject(asList("hi", "ho"), 3));
 
         then(fixture.query()).isEqualTo("foo(bar: {texts: ['hi', 'ho'], count: 3})");
         then(success).isTrue();