Skip to content

Commit 244f9a5

Browse files
committed
Merge remote-tracking branch 'upstream/master' into kaqqao-122TypeResolver
# Conflicts: # src/test/groovy/readme/ReadmeExamples.java
2 parents cad4613 + 902d7db commit 244f9a5

File tree

59 files changed

+5167
-101
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+5167
-101
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
.idea
33
.gradle
44
build
5+
classes
56
_site
67
generated-src/

README.md

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,105 @@ Example: [GraphQL Test](src/test/groovy/graphql/GraphQLTest.groovy)
344344
More complex examples: [StarWars query tests](src/test/groovy/graphql/StarWarsQueryTest.groovy)
345345

346346

347+
#### Causing mutation during execution
348+
349+
A good starting point to learn more about mutating data in graphql is [http://graphql.org/learn/queries/#mutations](http://graphql.org/learn/queries/#mutations)
350+
351+
In essence you need to define a `GraphQLObjectType` that takes arguments as input. Those arguments are what you can use to mutate your data store
352+
via the data fetcher invoked.
353+
354+
The mutation is invoked via a query like :
355+
356+
```graphql
357+
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
358+
createReview(episode: $ep, review: $review) {
359+
stars
360+
commentary
361+
}
362+
}
363+
```
364+
365+
You need to send in arguments during that mutation operation, in this case for the variables for `$ep` and `$review`
366+
367+
You would create types like this to handle this mutation :
368+
369+
```java
370+
GraphQLInputObjectType episodeType = GraphQLInputObjectType.newInputObject()
371+
.name("Episode")
372+
.field(newInputObjectField()
373+
.name("episodeNumber")
374+
.type(Scalars.GraphQLInt))
375+
.build();
376+
377+
GraphQLInputObjectType reviewInputType = GraphQLInputObjectType.newInputObject()
378+
.name("ReviewInput")
379+
.field(newInputObjectField()
380+
.name("stars")
381+
.type(Scalars.GraphQLString)
382+
.name("commentary")
383+
.type(Scalars.GraphQLString))
384+
.build();
385+
386+
GraphQLObjectType reviewType = newObject()
387+
.name("Review")
388+
.field(newFieldDefinition()
389+
.name("stars")
390+
.type(GraphQLString))
391+
.field(newFieldDefinition()
392+
.name("commentary")
393+
.type(GraphQLString))
394+
.build();
395+
396+
GraphQLObjectType createReviewForEpisodeMutation = newObject()
397+
.name("CreateReviewForEpisodeMutation")
398+
.field(newFieldDefinition()
399+
.name("createReview")
400+
.type(reviewType)
401+
.argument(newArgument()
402+
.name("episode")
403+
.type(episodeType)
404+
)
405+
.argument(newArgument()
406+
.name("review")
407+
.type(reviewInputType)
408+
)
409+
.dataFetcher(mutationDataFetcher())
410+
)
411+
.build();
412+
413+
GraphQLSchema schema = GraphQLSchema.newSchema()
414+
.query(queryType)
415+
.mutation(createReviewForEpisodeMutation)
416+
.build();
417+
418+
```
419+
420+
Notice that the input arguments are of type `GraphQLInputObjectType`. This is important. Input arguments can ONLY be of that type
421+
and you cannot use output types such as `GraphQLObjectType`. Scalars types are consider both input and output types.
422+
423+
The data fetcher here is responsible for executing the mutation and returning some sensible output values.
424+
425+
```java
426+
private DataFetcher mutationDataFetcher() {
427+
return new DataFetcher() {
428+
@Override
429+
public Review get(DataFetchingEnvironment environment) {
430+
Episode episode = environment.getArgument("episode");
431+
ReviewInput review = environment.getArgument("review");
432+
433+
// make a call to your store to mutate your database
434+
Review updatedReview = reviewStore().update(episode, review);
435+
436+
// this returns a new view of the data
437+
return updatedReview;
438+
}
439+
};
440+
}
441+
```
442+
443+
Notice how it calls a data store to mutate the backing database and then returns a `Review` object that can be used as the output values
444+
to the caller.
445+
347446
#### Execution strategies
348447

349448
All fields in a SelectionSet are executed serially per default.
@@ -420,6 +519,117 @@ public Object executeOperation(@RequestBody Map body) {
420519
}
421520
```
422521
522+
### Schema IDL support
523+
524+
This library allows for "schema driven" development of graphql applications.
525+
526+
It allows you to compile a set of schema files into a executable `GraphqlSchema`.
527+
528+
529+
So given a graphql schema input file like :
530+
531+
```graphql
532+
533+
schema {
534+
query: QueryType
535+
}
536+
537+
type QueryType {
538+
hero(episode: Episode): Character
539+
human(id : String) : Human
540+
droid(id: ID!): Droid
541+
}
542+
543+
544+
enum Episode {
545+
NEWHOPE
546+
EMPIRE
547+
JEDI
548+
}
549+
550+
interface Character {
551+
id: ID!
552+
name: String!
553+
friends: [Character]
554+
appearsIn: [Episode]!
555+
}
556+
557+
type Human implements Character {
558+
id: ID!
559+
name: String!
560+
friends: [Character]
561+
appearsIn: [Episode]!
562+
homePlanet: String
563+
}
564+
565+
type Droid implements Character {
566+
id: ID!
567+
name: String!
568+
friends: [Character]
569+
appearsIn: [Episode]!
570+
primaryFunction: String
571+
}
572+
573+
574+
```
575+
576+
You could compile and generate an executable schema via
577+
578+
```java
579+
SchemaCompiler schemaCompiler = new SchemaCompiler();
580+
SchemaGenerator schemaGenerator = new SchemaGenerator();
581+
582+
File schemaFile = loadSchema("starWarsSchema.graphqls");
583+
584+
TypeDefinitionRegistry typeRegistry = schemaCompiler.compile(schemaFile);
585+
RuntimeWiring wiring = buildRuntimeWiring();
586+
GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, wiring);
587+
588+
```
589+
590+
The static schema definition file has the field and type definitions but you need a runtime wiring to make
591+
it a truly executable schema.
592+
593+
The runtime wiring contains `DataFetchers`, `TypeResolvers` and custom `Scalars` that are needed to make a fully
594+
executable schema.
595+
596+
You wire this together using this builder pattern
597+
598+
```java
599+
600+
RuntimeWiring buildRuntimeWiring() {
601+
return RuntimeWiring.newRuntimeWiring()
602+
.scalar(CustomScalar)
603+
// this uses builder function lambda syntax
604+
.type(typeWiring -> typeWiring.typeName("QueryType")
605+
.dataFetcher("hero", new StaticDataFetcher(StarWarsData.getArtoo()))
606+
.dataFetcher("human", StarWarsData.getHumanDataFetcher())
607+
.dataFetcher("droid", StarWarsData.getDroidDataFetcher())
608+
)
609+
.type(typeWiring -> typeWiring.typeName("Human")
610+
.dataFetcher("friends", StarWarsData.getFriendsDataFetcher())
611+
)
612+
// you can use builder syntax if you don't like the lambda syntax
613+
.type(typeWiring -> typeWiring.typeName("Droid")
614+
.dataFetcher("friends", StarWarsData.getFriendsDataFetcher())
615+
)
616+
// or full builder syntax if that takes your fancy
617+
.type(
618+
newTypeWiring("Character")
619+
.typeResolver(StarWarsData.getCharacterTypeResolver())
620+
.build()
621+
)
622+
.build();
623+
}
624+
625+
626+
```
627+
628+
NOTE: IDL is not currently part of the [formal graphql spec](https://facebook.github.io/graphql/#sec-Appendix-Grammar-Summary.Query-Document).
629+
The implementation in this library is based off the [reference implementation](https://github.com/graphql/graphql-js). However plenty of
630+
code out there is based on this IDL syntax and hence you can be fairly confident that you are building on solid technology ground.
631+
632+
423633
#### Contributions
424634

425635
Every contribution to make this project better is welcome: Thank you!

src/main/java/graphql/execution/ExecutionStrategy.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ public abstract class ExecutionStrategy {
4848
* Handle exceptions which occur during data fetching. By default, add all exceptions to the execution context's
4949
* error's. Subclasses may specify custom handling, e.g. of different behavior with different exception types (e.g.
5050
* re-throwing certain exceptions).
51-
* @param executionContext
52-
* @param fieldDef
53-
* @param argumentValues
54-
* @param e
51+
*
52+
* @param executionContext the execution context in play
53+
* @param fieldDef the field definition
54+
* @param argumentValues the map of arguments
55+
* @param e the exception that occurred
5556
*/
5657
protected void handleDataFetchingException(
5758
ExecutionContext executionContext,
@@ -70,11 +71,10 @@ protected ExecutionResult resolveField(ExecutionContext executionContext, Execut
7071
DataFetchingEnvironment environment = new DataFetchingEnvironmentImpl(
7172
parameters.source(),
7273
argumentValues,
73-
executionContext.getRoot(),
7474
fields,
7575
fieldDef.getType(),
7676
type,
77-
executionContext.getGraphQLSchema()
77+
executionContext
7878
);
7979

8080
Instrumentation instrumentation = executionContext.getInstrumentation();

src/main/java/graphql/execution/batched/BatchedExecutionStrategy.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,10 @@ private List<GraphQLExecutionNodeValue> fetchData(ExecutionContext executionCont
270270
DataFetchingEnvironment environment = new DataFetchingEnvironmentImpl(
271271
sources,
272272
argumentValues,
273-
executionContext.getRoot(),
274273
fields,
275274
fieldDef.getType(),
276275
parentType,
277-
executionContext.getGraphQLSchema()
276+
executionContext
278277
);
279278

280279
List<Object> values;

src/main/java/graphql/execution/batched/UnbatchedDataFetcher.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ public Object get(DataFetchingEnvironment environment) {
3535
environment.getFields(),
3636
environment.getFieldType(),
3737
environment.getParentType(),
38-
environment.getGraphQLSchema());
38+
environment.getGraphQLSchema(),
39+
environment.getFragmentsByName(),
40+
environment.getExecutionId());
3941
results.add(delegate.get(singleEnv));
4042
}
4143
return results;

src/main/java/graphql/introspection/Introspection.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package graphql.introspection;
22

33

4+
import graphql.language.AstValueHelper;
5+
import graphql.language.AstPrinter;
46
import graphql.schema.DataFetcher;
57
import graphql.schema.DataFetchingEnvironment;
68
import graphql.schema.GraphQLArgument;
@@ -11,6 +13,7 @@
1113
import graphql.schema.GraphQLFieldsContainer;
1214
import graphql.schema.GraphQLInputObjectField;
1315
import graphql.schema.GraphQLInputObjectType;
16+
import graphql.schema.GraphQLInputType;
1417
import graphql.schema.GraphQLInterfaceType;
1518
import graphql.schema.GraphQLList;
1619
import graphql.schema.GraphQLNonNull;
@@ -98,15 +101,19 @@ public enum TypeKind {
98101
.dataFetcher(environment -> {
99102
if (environment.getSource() instanceof GraphQLArgument) {
100103
GraphQLArgument inputField = environment.getSource();
101-
return inputField.getDefaultValue() != null ? inputField.getDefaultValue().toString() : null;
104+
return inputField.getDefaultValue() != null ? print(inputField.getDefaultValue(), inputField.getType()) : null;
102105
} else if (environment.getSource() instanceof GraphQLInputObjectField) {
103106
GraphQLInputObjectField inputField = environment.getSource();
104-
return inputField.getDefaultValue() != null ? inputField.getDefaultValue().toString() : null;
107+
return inputField.getDefaultValue() != null ? print(inputField.getDefaultValue(), inputField.getType()) : null;
105108
}
106109
return null;
107110
}))
108111
.build();
109112

113+
private static String print(Object value, GraphQLInputType type) {
114+
return AstPrinter.printAst(AstValueHelper.astFromValue(value, type));
115+
}
116+
110117

111118
public static GraphQLObjectType __Field = newObject()
112119
.name("__Field")

0 commit comments

Comments
 (0)