Skip to content

Commit 3f98898

Browse files
authored
Merge branch 'master' into bugfix/383_find_camelCaseGetter_from_snake_case_field
2 parents 1130a0e + 446cc0b commit 3f98898

File tree

7 files changed

+128
-20
lines changed

7 files changed

+128
-20
lines changed

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
blank_issues_enabled: false
22
contact_links:
33
- name: Question
4-
url: https://spectrum.chat/graphql-java-kick
5-
about: Anything you are not sure about? Ask the community on Spectrum!
4+
url: https://github.com/graphql-java-kickstart/graphql-java-tools/discussions
5+
about: Anything you are not sure about? Ask the community!

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![TravisCI Build](https://travis-ci.org/graphql-java-kickstart/graphql-java-tools.svg?branch=master)](https://travis-ci.org/graphql-java-kickstart/graphql-java-tools)
44
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.graphql-java-kickstart/graphql-java-tools/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.graphql-java-kickstart/graphql-java-tools)
5-
[![Chat on Spectrum](https://img.shields.io/badge/spectrum-join%20the%20community-%23800080)](https://spectrum.chat/graphql-java-kick)
5+
[![Discuss on GitHub](https://img.shields.io/badge/GitHub-discuss-orange)](https://github.com/graphql-java-kickstart/graphql-java-tools/discussions)
66

77
This library allows you to use the GraphQL schema language to build your [graphql-java](https://github.com/graphql-java/graphql-java) schema.
88
Inspired by [graphql-tools](https://github.com/apollographql/graphql-tools), it parses the given GraphQL schema and allows you to BYOO (bring your own object) to fill in the implementations.
@@ -11,9 +11,10 @@ GraphQL Java Tools works well if you already have domain POJOs that hold your da
1111
GraphQL Java Tools aims for seamless integration with Java, but works for any JVM language. Try it with Kotlin!
1212

1313
## We are looking for contributors!
14+
1415
Are you interested in improving our documentation, working on the codebase, reviewing PRs?
1516

16-
[Reach out to us on Spectrum](https://spectrum.chat/graphql-java-kick) and join the team!
17+
[Reach out to us on GitHub](https://github.com/graphql-java-kickstart/graphql-java-tools/discussions) and join the team!
1718

1819
## Quick start
1920

@@ -25,7 +26,7 @@ kotlin.version=1.3.70
2526

2627
Add the dependency:
2728
```groovy
28-
compile 'com.graphql-java-kickstart:graphql-java-tools:6.2.0'
29+
compile 'com.graphql-java-kickstart:graphql-java-tools:6.3.0'
2930
```
3031

3132
### Using Maven
@@ -41,7 +42,7 @@ Add the dependency:
4142
<dependency>
4243
<groupId>com.graphql-java-kickstart</groupId>
4344
<artifactId>graphql-java-tools</artifactId>
44-
<version>6.2.0</version>
45+
<version>6.3.0</version>
4546
</dependency>
4647
```
4748

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>com.graphql-java-kickstart</groupId>
66
<artifactId>graphql-java-tools</artifactId>
7-
<version>6.2.1-SNAPSHOT</version>
7+
<version>6.3.1-SNAPSHOT</version>
88
<packaging>jar</packaging>
99

1010
<name>GraphQL Java Tools</name>

src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class SchemaParser internal constructor(
7676
val inputObjects: MutableList<GraphQLInputObjectType> = mutableListOf()
7777
inputObjectDefinitions.forEach {
7878
if (inputObjects.none { io -> io.name == it.name }) {
79-
inputObjects.add(createInputObject(it, inputObjects))
79+
inputObjects.add(createInputObject(it, inputObjects, mutableSetOf()))
8080
}
8181
}
8282
val interfaces = interfaceDefinitions.map { createInterfaceObject(it, inputObjects) }
@@ -155,7 +155,8 @@ class SchemaParser internal constructor(
155155
return schemaGeneratorDirectiveHelper.onObject(objectType, directiveHelperParameters)
156156
}
157157

158-
private fun createInputObject(definition: InputObjectTypeDefinition, inputObjects: List<GraphQLInputObjectType>): GraphQLInputObjectType {
158+
private fun createInputObject(definition: InputObjectTypeDefinition, inputObjects: List<GraphQLInputObjectType>,
159+
referencingInputObjects: MutableSet<String>): GraphQLInputObjectType {
159160
val extensionDefinitions = inputExtensionDefinitions.filter { it.name == definition.name }
160161

161162
val builder = GraphQLInputObjectType.newInputObject()
@@ -166,14 +167,16 @@ class SchemaParser internal constructor(
166167

167168
builder.withDirectives(*buildDirectives(definition.directives, Introspection.DirectiveLocation.INPUT_OBJECT))
168169

170+
referencingInputObjects.add(definition.name)
171+
169172
(extensionDefinitions + definition).forEach {
170173
it.inputValueDefinitions.forEach { inputDefinition ->
171174
val fieldBuilder = GraphQLInputObjectField.newInputObjectField()
172175
.name(inputDefinition.name)
173176
.definition(inputDefinition)
174177
.description(if (inputDefinition.description != null) inputDefinition.description.content else getDocumentation(inputDefinition))
175178
.defaultValue(buildDefaultValue(inputDefinition.defaultValue))
176-
.type(determineInputType(inputDefinition.type, inputObjects))
179+
.type(determineInputType(inputDefinition.type, inputObjects, referencingInputObjects))
177180
.withDirectives(*buildDirectives(inputDefinition.directives, Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION))
178181
builder.field(fieldBuilder.build())
179182
}
@@ -280,7 +283,7 @@ class SchemaParser internal constructor(
280283
.name(argumentDefinition.name)
281284
.definition(argumentDefinition)
282285
.description(if (argumentDefinition.description != null) argumentDefinition.description.content else getDocumentation(argumentDefinition))
283-
.type(determineInputType(argumentDefinition.type, inputObjects))
286+
.type(determineInputType(argumentDefinition.type, inputObjects, setOf()))
284287
.apply { buildDefaultValue(argumentDefinition.defaultValue)?.let { defaultValue(it) } }
285288
.withDirectives(*buildDirectives(argumentDefinition.directives, Introspection.DirectiveLocation.ARGUMENT_DEFINITION))
286289

@@ -380,7 +383,7 @@ class SchemaParser internal constructor(
380383
is NonNullType -> GraphQLNonNull(determineType(expectedType, typeDefinition.type, allowedTypeReferences, inputObjects))
381384
is InputObjectTypeDefinition -> {
382385
log.info("Create input object")
383-
createInputObject(typeDefinition, inputObjects)
386+
createInputObject(typeDefinition, inputObjects, mutableSetOf())
384387
}
385388
is TypeName -> {
386389
val scalarType = customScalars[typeDefinition.name]
@@ -398,16 +401,19 @@ class SchemaParser internal constructor(
398401
else -> throw SchemaError("Unknown type: $typeDefinition")
399402
}
400403

401-
private fun determineInputType(typeDefinition: Type<*>, inputObjects: List<GraphQLInputObjectType>) =
402-
determineInputType(GraphQLInputType::class, typeDefinition, permittedTypesForInputObject, inputObjects) as GraphQLInputType
404+
private fun determineInputType(typeDefinition: Type<*>, inputObjects: List<GraphQLInputObjectType>, referencingInputObjects: Set<String>) =
405+
determineInputType(GraphQLInputType::class, typeDefinition, permittedTypesForInputObject, inputObjects, referencingInputObjects) as GraphQLInputType
403406

404-
private fun <T : Any> determineInputType(expectedType: KClass<T>, typeDefinition: Type<*>, allowedTypeReferences: Set<String>, inputObjects: List<GraphQLInputObjectType>): GraphQLType =
407+
private fun <T : Any> determineInputType(expectedType: KClass<T>,
408+
typeDefinition: Type<*>, allowedTypeReferences: Set<String>,
409+
inputObjects: List<GraphQLInputObjectType>,
410+
referencingInputObjects: Set<String>): GraphQLType =
405411
when (typeDefinition) {
406412
is ListType -> GraphQLList(determineType(expectedType, typeDefinition.type, allowedTypeReferences, inputObjects))
407413
is NonNullType -> GraphQLNonNull(determineType(expectedType, typeDefinition.type, allowedTypeReferences, inputObjects))
408414
is InputObjectTypeDefinition -> {
409415
log.info("Create input object")
410-
createInputObject(typeDefinition, inputObjects)
416+
createInputObject(typeDefinition, inputObjects, referencingInputObjects as MutableSet<String>)
411417
}
412418
is TypeName -> {
413419
val scalarType = customScalars[typeDefinition.name]
@@ -425,9 +431,14 @@ class SchemaParser internal constructor(
425431
} else {
426432
val filteredDefinitions = inputObjectDefinitions.filter { it.name == typeDefinition.name }
427433
if (filteredDefinitions.isNotEmpty()) {
428-
val inputObject = createInputObject(filteredDefinitions[0], inputObjects)
429-
(inputObjects as MutableList).add(inputObject)
430-
inputObject
434+
val referencingInputObject = referencingInputObjects.find { it == typeDefinition.name }
435+
if (referencingInputObject != null) {
436+
GraphQLTypeReference(referencingInputObject)
437+
} else {
438+
val inputObject = createInputObject(filteredDefinitions[0], inputObjects, referencingInputObjects as MutableSet<String>)
439+
(inputObjects as MutableList).add(inputObject)
440+
inputObject
441+
}
431442
} else {
432443
// todo: handle enum type
433444
GraphQLTypeReference(typeDefinition.name)

src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ internal class MethodFieldResolver(
8080
return@add Optional.empty<Any>()
8181
}
8282

83-
if (value != null && shouldValueBeConverted(value, definition, parameterType, environment)) {
83+
if (value == null || shouldValueBeConverted(value, definition, parameterType, environment)) {
8484
return@add mapper.convertValue(value, object : TypeReference<Any>() {
8585
override fun getType() = parameterType
8686
})

src/test/groovy/graphql/kickstart/tools/SchemaParserSpec.groovy

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,50 @@ class SchemaParserSpec extends Specification {
368368
noExceptionThrown()
369369
}
370370

371+
def "allow circular relations in input objects"() {
372+
when:
373+
SchemaParser.newParser().schemaString('''\
374+
input A {
375+
id: ID!
376+
b: B
377+
}
378+
input B {
379+
id: ID!
380+
a: A
381+
}
382+
input C {
383+
id: ID!
384+
c: C
385+
}
386+
type Query {}
387+
type Mutation {
388+
test(input: A!): Boolean
389+
testC(input: C!): Boolean
390+
}
391+
'''.stripIndent())
392+
.resolvers(new GraphQLMutationResolver() {
393+
static class A {
394+
String id;
395+
B b;
396+
}
397+
static class B {
398+
String id;
399+
A a;
400+
}
401+
static class C {
402+
String id;
403+
C c;
404+
}
405+
boolean test(A a) { return true }
406+
boolean testC(C c) { return true }
407+
}, new GraphQLQueryResolver() {})
408+
.build()
409+
.makeExecutableSchema()
410+
411+
then:
412+
noExceptionThrown()
413+
}
414+
371415
enum EnumType {
372416
TEST
373417
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package graphql.kickstart.tools;
2+
3+
import graphql.ExecutionInput;
4+
import graphql.ExecutionResult;
5+
import graphql.GraphQL;
6+
import graphql.schema.GraphQLSchema;
7+
import org.junit.Test;
8+
9+
import java.util.Map;
10+
11+
import static org.junit.Assert.assertEquals;
12+
import static org.junit.Assert.assertTrue;
13+
14+
public class ResolverMethodsTest {
15+
// Note: don't convert this code to Kotlin or Groovy, since it's quite important that the
16+
// resolver method is defined with an argument of primitive type, like 'boolean', not 'Boolean':
17+
// String testOmittedBoolean(boolean value1, Boolean value2)
18+
@Test
19+
public void testOmittedBooleanArgument() {
20+
// In this schema, the 'value1' argument is optional, but the Java resolver defines it as 'boolean'
21+
// Instead of failing with an error, we expect the argument to be set to the Java default (i.e. false for booleans)
22+
GraphQLSchema schema = SchemaParser.newParser()
23+
.schemaString("" +
24+
"type Query {" +
25+
" testOmittedBoolean(value1: Boolean, value2: Boolean): String" +
26+
"}")
27+
.resolvers(new Resolver())
28+
.build()
29+
.makeExecutableSchema();
30+
31+
GraphQL gql = GraphQL.newGraphQL(schema).build();
32+
33+
ExecutionResult result = gql
34+
.execute(ExecutionInput.newExecutionInput()
35+
.query("" +
36+
"query { " +
37+
" testOmittedBoolean" +
38+
"}")
39+
.context(new Object())
40+
.root(new Object()));
41+
42+
assertTrue(result.getErrors().isEmpty());
43+
assertEquals("false,null", ((Map<?, ?>) result.getData()).get("testOmittedBoolean"));
44+
}
45+
46+
static class Resolver implements GraphQLQueryResolver {
47+
@SuppressWarnings("unused")
48+
public String testOmittedBoolean(boolean value1, Boolean value2) {
49+
return value1 + "," + value2;
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)