Skip to content

Commit

Permalink
Support for records as input on the server side
Browse files Browse the repository at this point in the history
  • Loading branch information
jmartisk committed Sep 16, 2021
1 parent c452c4e commit 23aa62a
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.Optional;

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
import org.jboss.jandex.MethodInfo;
import org.jboss.logging.Logger;
Expand Down Expand Up @@ -39,6 +40,8 @@ public class InputTypeCreator implements Creator<InputType> {
private final FieldCreator fieldCreator;
private final TypeAutoNameStrategy autoNameStrategy;

private final DotName RECORD = DotName.createSimple("java.lang.Record");

public InputTypeCreator(FieldCreator fieldCreator, TypeAutoNameStrategy autoNameStrategy) {
this.fieldCreator = fieldCreator;
this.autoNameStrategy = autoNameStrategy;
Expand Down Expand Up @@ -84,6 +87,18 @@ public boolean hasUseableConstructor(ClassInfo classInfo) {
* @return the creator, null, if no public constructor or factory method is found
*/
public MethodInfo findCreator(ClassInfo classInfo) {
if (classInfo.superName().equals(RECORD)) {
// records should always have a canonical constructor
// the creator will be picked by the JSONB impl at runtime anyway, so
// just make sure we can find a public constructor and move on
for (MethodInfo constructor : classInfo.constructors()) {
if (!Modifier.isPublic(constructor.flags()))
continue;
return constructor;
}
return null;
}

for (final MethodInfo constructor : classInfo.constructors()) {
if (!Modifier.isPublic(constructor.flags()))
continue;
Expand Down
3 changes: 3 additions & 0 deletions server/integration-tests-jdk16/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>16</release>
<!-- parameters needed for RecordTest#testSimpleRecordWithFactory because otherwise Yasson
doesn't properly use the static factory method -->
<parameters>true</parameters>
</configuration>
</plugin>
</plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package io.smallrye.graphql.tests.records;

import io.smallrye.graphql.client.Response;
import io.smallrye.graphql.client.core.Document;
import io.smallrye.graphql.client.core.InputObject;
import io.smallrye.graphql.client.core.InputObjectField;
import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClient;
import io.smallrye.graphql.client.dynamic.vertx.VertxDynamicGraphQLClientBuilder;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Query;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

import javax.json.bind.annotation.JsonbCreator;
import java.net.URL;

import static io.smallrye.graphql.client.core.Argument.arg;
import static io.smallrye.graphql.client.core.Argument.args;
import static io.smallrye.graphql.client.core.Document.document;
import static io.smallrye.graphql.client.core.Field.field;
import static io.smallrye.graphql.client.core.InputObject.inputObject;
import static io.smallrye.graphql.client.core.InputObjectField.prop;
import static io.smallrye.graphql.client.core.Operation.operation;
import static org.junit.Assert.assertEquals;

/**
* This test verifies that the server side can handle Java records in GraphQL apis, both as input and as output types.
*/
@RunWith(Arquillian.class)
@RunAsClient
public class RecordTest {

@Deployment
public static WebArchive deployment() {
return ShrinkWrap.create(WebArchive.class)
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
.addClasses(SimpleRecord.class);
}

@ArquillianResource
URL testingURL;

@Test
public void testSimpleRecord() throws Exception {
try (DynamicGraphQLClient client = new VertxDynamicGraphQLClientBuilder()
.url(testingURL.toString() + "graphql").build()) {
Document query = document(operation(
field("simple",
args(arg("input",
inputObject(prop("a", "a"), prop("b", "b")))),
field("a"),
field("b"))));
Response response = client.executeSync(query);
assertEquals("a", response.getData().getJsonObject("simple").getString("a"));
assertEquals("b", response.getData().getJsonObject("simple").getString("b"));
}
}

@Test
public void testSimpleRecordWithFactory() throws Exception {
try (DynamicGraphQLClient client = new VertxDynamicGraphQLClientBuilder()
.url(testingURL.toString() + "graphql").build()) {
Document query = document(operation(
field("simpleWithFactory",
args(arg("input",
inputObject(prop("a", "a"), prop("b", "b")))),
field("a"),
field("b"))));
Response response = client.executeSync(query);
System.out.println(response);
System.out.println("query.build() = " + query.build());
assertEquals("a", response.getData().getJsonObject("simpleWithFactory").getString("a"));
assertEquals("b", response.getData().getJsonObject("simpleWithFactory").getString("b"));
}
}

@GraphQLApi
public static class Api {

@Query
public SimpleRecord simple(SimpleRecord input) {
return input;
}

@Query
public SimpleRecordWithFactory simpleWithFactory(SimpleRecordWithFactory input) {
return input;
}

}

public record SimpleRecord(String a, String b) {

// FIXME: until Yasson receives proper support for records, we have
// to use this hack to tell Yasson to use the canonical constructor when
// deserializing from JSON. Otherwise it will try to find and use a no-arg constructor, and
// that does not exist.
@JsonbCreator
public SimpleRecord {
}

}

public record SimpleRecordWithFactory(String a, String b) {

@JsonbCreator
public static SimpleRecordWithFactory build(String a, String b) {
return new SimpleRecordWithFactory(a, b);
}

}

}

0 comments on commit 23aa62a

Please sign in to comment.