Repository: smallrye/smallrye-graphql
Issue: Support java.util.Date in queries from smallrye-graphql-client

I am using this library to fetch data from GitHub and I noticed that some types are not assembled correctly. This is what I have so far:  

```java 
@GraphQlClientApi(endpoint = "https://api.github.com/graphql")
@AuthorizationHeader(confPrefix = "github", type = AuthorizationHeader.Type.BEARER)
public interface GitHub {

    @Query("repository")
    Repository repository(String owner, String name);
}
```

This is the `Repository` class: 

```java
public class Repository {
    public String id;
    public java.util.Date createdAt;
}
```


This is my test: 

```java
    @Test
    void test() {
        final GitHub github = GraphQlClientBuilder.newBuilder().build(GitHub.class);
        System.out.println(github.repository("gastaldi", "backport-repo"));
    }
```


And this is the error I get: 

```java
io.smallrye.graphql.client.typesafe.api.GraphQlClientException: errors from service: [{"message":"Parse error on \\"}\\" (RCURLY) at [1, 77]","locations":[{"line":1,"column":77}]}]:
  {"query":"query { repository(owner: \\"gastaldi\\", name: \\"backport-repo\\") {id createdAt {}} }"}

	at io.smallrye.graphql.client.typesafe.impl.GraphQlClientProxy.readResponse(GraphQlClientProxy.java:128)
	at io.smallrye.graphql.client.typesafe.impl.GraphQlClientProxy.fromJson(GraphQlClientProxy.java:120)
	at io.smallrye.graphql.client.typesafe.impl.GraphQlClientProxy.invoke(GraphQlClientProxy.java:52)
	at io.smallrye.graphql.client.typesafe.impl.GraphQlClientBuilderImpl.lambda$build$0(GraphQlClientBuilderImpl.java:45)
	at com.sun.proxy.$Proxy18.repository(Unknown Source)
	at io.quarkus.backports.GraphQLTest.test(GraphQLTest.java:12)
```



Changed files:
client/implementation/src/test/java/test/unit/ScalarBehavior.java
client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/GraphQlClientBuilderImpl.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/json/JsonStringReader.java
client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/reflection/TypeInfo.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/GraphQlClientBuilderImpl.java b/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/GraphQlClientBuilderImpl.java
index a4f150bf..41f30933 100644
--- a/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/GraphQlClientBuilderImpl.java
+++ b/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/GraphQlClientBuilderImpl.java
@@ -48,10 +48,9 @@ public class GraphQlClientBuilderImpl implements GraphQlClientBuilder {
     }
 
     private ClassLoader getClassLoader(Class<?> apiClass) {
-        if (System.getSecurityManager() == null) {
+        if (System.getSecurityManager() == null)
             return apiClass.getClassLoader();
-        }
-        return AccessController.doPrivileged((PrivilegedAction<ClassLoader>) () -> apiClass.getClassLoader());
+        return AccessController.doPrivileged((PrivilegedAction<ClassLoader>) apiClass::getClassLoader);
     }
 
     private void readConfig(GraphQlClientApi annotation) {
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 bd200d63..93fd8578 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
@@ -83,15 +83,13 @@ class GraphQlClientProxy {
         while (type.isOptional())
             type = type.getItemType();
 
-        if (type.isScalar()) {
+        if (type.isScalar())
             return "";
-        } else if (type.isCollection()) {
+        if (type.isCollection())
             return fields(type.getItemType());
-        } else {
-            return type.fields()
-                    .map(this::field)
-                    .collect(joining(" ", " {", "}"));
-        }
+        return type.fields()
+                .map(this::field)
+                .collect(joining(" ", " {", "}"));
     }
 
     private String field(FieldInfo field) {
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 be3be778..3b6b5efb 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
@@ -42,23 +42,27 @@ class RequestBuilder {
     private void buildParam(TypeInfo type, Object value) {
         if (value == null)
             request.append("null");
-        else if (value instanceof Boolean || value instanceof Number || type.isEnum())
-            request.append(value);
         else if (type.isScalar())
-            buildScalarParam(value);
+            buildScalarParam(type, value);
         else if (type.isCollection())
             buildArrayParam(type.getItemType(), (List<?>) value);
         else
             buildObjectParam(type, value);
     }
 
-    private void buildScalarParam(Object value) {
-        request
-                .append("\\"")
-                .append(value.toString()
-                        .replace("\\"", "\\\\\\"")
-                        .replace("\\n", "\\\\n"))
-                .append("\\"");
+    private void buildScalarParam(TypeInfo type, Object value) {
+        boolean quoted = !unquoted(type);
+        if (quoted)
+            request.append("\\"");
+        request.append(type.stringValue(value));
+        if (quoted)
+            request.append("\\"");
+    }
+
+    public boolean unquoted(TypeInfo type) {
+        return type.isPrimitive()
+                || Number.class.isAssignableFrom(type.getRawType())
+                || type.isEnum();
     }
 
     private void buildArrayParam(TypeInfo itemType, List<?> values) {
diff --git a/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/json/JsonStringReader.java b/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/json/JsonStringReader.java
index 7d90e80a..66f12fed 100644
--- a/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/json/JsonStringReader.java
+++ b/client/implementation/src/main/java/io/smallrye/graphql/client/typesafe/impl/json/JsonStringReader.java
@@ -1,5 +1,7 @@
 package io.smallrye.graphql.client.typesafe.impl.json;
 
+import java.time.Instant;
+
 import javax.json.JsonString;
 
 import io.smallrye.graphql.client.typesafe.api.GraphQlClientException;
@@ -24,6 +26,9 @@ class JsonStringReader extends Reader<JsonString> {
             //noinspection rawtypes,unchecked
             return Enum.valueOf((Class) type.getRawType(), value.getString());
 
+        if (java.util.Date.class.equals(this.type.getRawType()))
+            return java.util.Date.from(Instant.parse(value.getString()));
+
         ConstructionInfo constructor = type.scalarConstructor()
                 .orElseThrow(() -> new GraphQlClientValueException(location, value));
         try {
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 bb0a1cd7..b9cc7915 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
@@ -4,7 +4,6 @@ import static java.lang.reflect.Modifier.isStatic;
 import static java.lang.reflect.Modifier.isTransient;
 import static java.util.Objects.requireNonNull;
 
-import java.lang.annotation.Annotation;
 import java.lang.reflect.AnnotatedType;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Executable;
@@ -111,10 +110,9 @@ public class TypeInfo {
     }
 
     private Field[] getDeclaredFields(Class<?> type) {
-        if (System.getSecurityManager() == null) {
+        if (System.getSecurityManager() == null)
             return type.getDeclaredFields();
-        }
-        return AccessController.doPrivileged((PrivilegedAction<Field[]>) () -> type.getDeclaredFields());
+        return AccessController.doPrivileged((PrivilegedAction<Field[]>) type::getDeclaredFields);
     }
 
     private boolean isGraphQlField(Field field) {
@@ -127,10 +125,11 @@ public class TypeInfo {
 
     public boolean isScalar() {
         return isPrimitive()
-                || Character.class.equals(getRawType()) // has a valueOf(char), not valueOf(String)
                 || Number.class.isAssignableFrom(getRawType())
-                || CharSequence.class.isAssignableFrom(getRawType())
                 || isEnum()
+                || CharSequence.class.isAssignableFrom(getRawType())
+                || Character.class.equals(getRawType()) // has a valueOf(char), not valueOf(String)
+                || java.util.Date.class.equals(getRawType())
                 || scalarConstructor().isPresent();
     }
 
@@ -143,15 +142,12 @@ public class TypeInfo {
     }
 
     public Optional<ConstructionInfo> scalarConstructor() {
-        return Stream.of(getRawType().getMethods()).filter(this::isStaticStringConstructor)
+        return Stream.of(getRawType().getMethods())
+                .filter(this::isStaticStringConstructor)
                 .findFirst()
                 .map(ConstructionInfo::new);
     }
 
-    private boolean hasOneStringParameter(Executable executable) {
-        return executable.getParameterCount() == 1 && CharSequence.class.isAssignableFrom(executable.getParameterTypes()[0]);
-    }
-
     private boolean isStaticStringConstructor(Method method) {
         return isStaticConstructorMethodNamed(method, "of")
                 || isStaticConstructorMethodNamed(method, "valueOf")
@@ -165,6 +161,18 @@ public class TypeInfo {
                 && hasOneStringParameter(method);
     }
 
+    private boolean hasOneStringParameter(Executable executable) {
+        return executable.getParameterCount() == 1 && CharSequence.class.isAssignableFrom(executable.getParameterTypes()[0]);
+    }
+
+    public String stringValue(Object value) {
+        if (java.util.Date.class.equals(getRawType()))
+            return ((java.util.Date) value).toInstant().toString();
+        return value.toString()
+                .replace("\\"", "\\\\\\"")
+                .replace("\\n", "\\\\n");
+    }
+
     public Object newInstance() {
         try {
             Constructor<?> noArgsConstructor = getDeclaredConstructor(getRawType());
@@ -180,8 +188,7 @@ public class TypeInfo {
             return type.getDeclaredConstructor();
         }
         try {
-            return AccessController
-                    .doPrivileged((PrivilegedExceptionAction<Constructor<?>>) () -> type.getDeclaredConstructor());
+            return AccessController.doPrivileged((PrivilegedExceptionAction<Constructor<?>>) type::getDeclaredConstructor);
         } catch (PrivilegedActionException pae) {
             if (pae.getCause() instanceof NoSuchMethodException) {
                 throw (NoSuchMethodException) pae.getCause();
@@ -229,28 +236,22 @@ public class TypeInfo {
         throw new GraphQlClientException("unsupported reflection type " + type.getClass());
     }
 
-    public <A extends Annotation> Stream<A> getAnnotations(Class<A> type) {
-        return Stream.of(((Class<?>) this.type).getAnnotationsByType(type));
+    public Optional<MethodInfo> getMethod(String name, Class<?>... args) {
+        return getDeclaredMethod((Class<?>) this.type, name, args)
+                .map(MethodInfo::of);
     }
 
-    public Optional<MethodInfo> getMethod(String name) {
+    private Optional<Method> getDeclaredMethod(Class<?> type, String name, Class<?>... args) {
         try {
-            return Optional.of(MethodInfo.of(getDeclaredMethod((Class<?>) this.type, name)));
+            if (System.getSecurityManager() == null)
+                return Optional.of(type.getDeclaredMethod(name, args));
+            return Optional.of(AccessController
+                    .doPrivileged((PrivilegedExceptionAction<Method>) () -> type.getDeclaredMethod(name, args)));
         } catch (NoSuchMethodException e) {
             return Optional.empty();
-        }
-    }
-
-    private Method getDeclaredMethod(Class<?> type, String name) throws NoSuchMethodException {
-        if (System.getSecurityManager() == null) {
-            return type.getDeclaredMethod(name);
-        }
-        try {
-            return AccessController.doPrivileged((PrivilegedExceptionAction<Method>) () -> type.getDeclaredMethod(name));
         } catch (PrivilegedActionException pae) {
-            if (pae.getCause() instanceof NoSuchMethodException) {
-                throw (NoSuchMethodException) pae.getCause();
-            }
+            if (pae.getCause() instanceof NoSuchMethodException)
+                return Optional.empty();
             throw new RuntimeException(pae.getCause());
         }
     }
diff --git a/client/implementation/src/test/java/test/unit/ScalarBehavior.java b/client/implementation/src/test/java/test/unit/ScalarBehavior.java
index 887adfe6..790ba3a9 100644
--- a/client/implementation/src/test/java/test/unit/ScalarBehavior.java
+++ b/client/implementation/src/test/java/test/unit/ScalarBehavior.java
@@ -6,7 +6,9 @@ import static org.assertj.core.api.BDDAssertions.then;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.time.Instant;
 import java.time.LocalDate;
+import java.util.Date;
 
 import javax.ws.rs.core.Response;
 
@@ -845,4 +847,22 @@ class ScalarBehavior {
         then(fixture.query()).isEqualTo("getting");
         then(value).isEqualTo("foo");
     }
+
+    @GraphQlClientApi
+    interface DateApi {
+        Date foo(Date date);
+    }
+
+    @Test
+    void shouldCallDateQuery() {
+        Instant in = Instant.ofEpochMilli(123456789);
+        Instant out = Instant.ofEpochMilli(987654321);
+        fixture.returnsData("'foo':'" + out + "'");
+        DateApi api = fixture.builder().build(DateApi.class);
+
+        Date value = api.foo(Date.from(in));
+
+        then(fixture.query()).isEqualTo("foo(date: '" + in + "')");
+        then(value).isEqualTo(Date.from(out));
+    }
 }