Skip to content

Commit 1587e13

Browse files
authored
Merge pull request #225 from yarinvak/release/v7.2
v7.2
2 parents 8b7716f + 91ebff7 commit 1587e13

25 files changed

+1384
-147
lines changed

README.md

Lines changed: 128 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ syntax for GraphQL schema definition.
3535

3636
```groovy
3737
dependencies {
38-
compile "io.github.graphql-java:graphql-java-annotations:7.1"
38+
compile "io.github.graphql-java:graphql-java-annotations:7.2"
3939
}
4040
```
4141

@@ -45,7 +45,7 @@ dependencies {
4545
<dependency>
4646
<groupId>io.github.graphql-java</groupId>
4747
<artifactId>graphql-java-annotations</artifactId>
48-
<version>7.1</version>
48+
<version>7.2</version>
4949
</dependency>
5050
```
5151

@@ -401,20 +401,94 @@ graphqlAnnotations.registerType(new UUIDTypeFunction())
401401
You can also specify custom type function for any field with `@GraphQLType` annotation.
402402

403403
## Directives
404-
You can wire your fields using directives with annotations.
405-
We allow both defining directives using annotations, and wiring fields.
404+
In GraphQL, you can add directives to your schema. Directive is a way of adding some logic to your schema or changing your schema.
405+
For example, we can create a `@upper` directive, that if we add it to string fields in our schema, they will be transformed to upper cases (its an example, you need to implement it).
406406

407-
### Creating/Defining a ``GraphQLDirective``
408-
In order to create a directive, you first have to create a class that the directive will be created from.
409-
For example:
407+
### Declaring a ``GraphQLDirective``
408+
There are multiple ways to declare a directive in your schema using graphql-java-annotations.
409+
410+
#### Using a Java Annotation (recommended)
411+
This is the most recommended way of creating a directive, because it is very easy to use later in your schema.
412+
In order to declare a directive using a java annotation, you first have to create the java annotation, and annotate it with special annotations.
413+
414+
For example, we wish to create a directive that adds suffix to graphql fields.
415+
416+
```java
417+
@GraphQLName("suffix")
418+
@GraphQLDescription("this directive adds suffix to a string type")
419+
@GraphQLDirectiveDefinition(wiring = SuffixWiring.class)
420+
@DirectiveLocations({Introspection.DirectiveLocation.FIELD_DEFINITION, Introspection.DirectiveLocation.INTERFACE})
421+
@Retention(RetentionPolicy.RUNTIME)
422+
@interface Suffix {
423+
@GraphQLName("suffixToAdd")
424+
@GraphQLDescription("the suffix to add to your type")
425+
boolean suffixToAdd() default true;
426+
}
427+
```
410428

429+
- must be annotated with `@GraphQLDirectiveDefinition` and to supply a wiring class to it (will be explained later)
430+
- the name of the directive will be taken from the class name (`Suffix`) or if annotated with `@GraphQLName` - from its value
431+
- the description is taken from the `@GraphQLDescription` annotation
432+
- must be annotated with `@Retention` with a `RUNTIME` policy
433+
- must be annotated with `@DirectiveLocations` in order to specify where we can put this directive on (for example - field definition, interface)
434+
435+
You can see that we also defined a ``sufixToAdd`` argument for the directive. We can also use `@GraphQLName` and `@GraphQLDescription` annotations in there.
436+
437+
In order to define a default value for the argument, use the `default` keyword like in the example.
438+
439+
After you created the class, you will be able to create the ``GraphQLDirective`` object using the following code:
411440
```java
441+
GraphQLDirective directive = graphqlAnnotations.directiveViaAnnotation(Suffix.class);
442+
```
443+
444+
#### Using a method declaration
445+
You can also declare an annotation via a method declaration inside some class.
446+
For example, we will create a class of directive declarations:
447+
448+
```java
449+
class DirectiveDeclarations{
412450
@GraphQLName("upper")
413-
@GraphQLDescription("upper")
451+
@GraphQLDescription("upper directive")
452+
@GraphQLDirectiveDefinition(wiring = UpperWiring.class)
453+
@DirectiveLocations({Introspection.DirectiveLocation.FIELD_DEFINITION, Introspection.DirectiveLocation.INTERFACE})
454+
public void upperDirective(@GraphQLName("isActive") @GraphQLDescription("is active") boolean isActive) {
455+
}
456+
457+
@GraphQLName("suffix")
458+
@GraphQLDescription("suffix directive")
459+
@GraphQLDirectiveDefinition(wiring = SuffixWiring.class)
414460
@DirectiveLocations({Introspection.DirectiveLocation.FIELD_DEFINITION, Introspection.DirectiveLocation.INTERFACE})
415-
public static class UpperDirective {
416-
private boolean isActive = true;
461+
public void suffixDirective(@GraphQLName("suffix") @GraphQLDescription("the suffix") String suffix) {
417462
}
463+
}
464+
```
465+
466+
- The methods has to be annotated with the `@GraphQLDirectiveDefinition` annotation, and to be supplied with a wiring class
467+
- The methods has to be annotated with the `@DirectiveLocations` annotation
468+
- Can be used: `@GraphQLName` and `@GraphQLDescription` - also inside method parameters (that will be transformed into arguments of the directive)
469+
470+
Notice that method params cannot have default values - so the directive arguments will not have default values.
471+
472+
In order to create the directives, you need to write:
473+
```java
474+
Set<GraphQLDirective> set = graphqlAnnotations.directives(DirectiveDeclarations.class);
475+
```
476+
477+
#### Using a class declaration
478+
479+
Another way is to declare the directive using a class.
480+
481+
For example:
482+
483+
```java
484+
@GraphQLName("upper")
485+
@GraphQLDescription("upper")
486+
@DirectiveLocations({Introspection.DirectiveLocation.FIELD_DEFINITION, Introspection.DirectiveLocation.INTERFACE})
487+
@GraphQLDirectiveDefinition(wiring = UpperWiring.class)
488+
public static class UpperDirective {
489+
@GraphQLName("isActive")
490+
private boolean isActive = true;
491+
}
418492
```
419493

420494
The name of the directive will be taken from the ``@GraphQLName`` annotation (if not specified, the name will be the class's name).
@@ -426,13 +500,14 @@ You can also use ``@GraphQLName`` and ``@GraphQLDescription`` annotations on the
426500

427501
After you created the class, you will be able to create the ``GraphQLDirective`` object using the following code:
428502
```java
429-
graphqlAnnotations.directive(UpperDirective.class);
503+
GraphQLDirective directive = graphqlAnnotations.directive(UpperDirective.class);
430504
```
431505

432-
### Wiring with directives
433-
Using directives you will be able to wire fields and more, for example, changing the data fetchers of the fields.
506+
### Wiring with the directives
507+
In order to define the wiring logic (what will be executed on top of the graphql type annotated with the directive) we have to create wiring class.
508+
509+
In order to define a wiring functionality, you have to create a Wiring class matching one of your directives. For example:
434510

435-
In order to define a wiring functionality, you have to create a Wiring class matching one of you directives. For example:
436511
```java
437512
public class UpperWiring implements AnnotationsDirectiveWiring {
438513
@Override
@@ -450,20 +525,56 @@ public class UpperWiring implements AnnotationsDirectiveWiring {
450525
}
451526
```
452527

528+
In this example we wrap the data fetcher of the field in order to make the resolved value upper case.
529+
453530
You can also use the `field.transform` method in order to change some of the field's properties.
454531

455532
This class turns your string field to upper case if the directive argument "isActive" is set to true.
456-
Now, you have to wire the field itself:
533+
534+
Put this class inside the `@GraphQLDirectiveDefinition(wiring = UpperWiring.class)` annotation where you declare your directive (see directive declaration section above).
535+
536+
### Using the directives
537+
538+
There are 2 ways of using the directives in your graphql types.
539+
540+
#### Using the directive java annotation (RECOMMENDED)
541+
This way only works if you declared your directive as a java annotation.
542+
In the example above, we created the `@Suffix` annotation as a directive.
543+
So now we can put it on top of our graphql field.
544+
545+
For example:
546+
457547
```java
458548
@GraphQLField
459-
@GraphQLDirectives(@Directive(name = "upperCase", wiringClass = UpperWiring.class, argumentsValues = {"true"}))
460-
public static String name() {
549+
@Suffix(suffixToAdd = " is cool")
550+
public String name(){
461551
return "yarin";
462552
}
463553
```
554+
555+
Now every time the field will be executed, the suffix " is cool" will be added to it.
556+
You can also use directive on field arguments, interfaces, etc.
557+
558+
#### Using `@GraphQLDirectives` annotation
559+
This way works in the 3 methods of declaring directives, but is less recommended because its more complicated and not so nice.
560+
You can annotate your graphql field with the `@GraphQLDirectives` annotation and supply it with the directives to use and the arguments values you want to supply.
561+
562+
For example:
563+
564+
```java
565+
@GraphQLField
566+
@GraphQLDirectives(@Directive(name = "upperCase", argumentsValues = {"true"}))
567+
public String name() {
568+
return "yarin";
569+
}
570+
```
571+
464572
We now wired the field "name" - so it will turn upper case when calling the field.
465573
The ``Directive`` annotations requires the name of the directive, the wiring class (the ``UpperWiring`` class defined earlier), and the values of the arguments. If an argument has a default value, you don't have to supply a value in the arguments values.
466574

575+
Notice that in any way, the directives are sequential, so the first annotated directive will happen before the second one.
576+
If put both java annotation directive and `@GraphQLDirectives` annotation directives, the java annotation directive will be applied first.
577+
467578
## Relay support
468579

469580
### Mutations

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ org.gradle.daemon=true
55
org.gradle.parallel=true
66
org.gradle.jvmargs=-Dfile.encoding=UTF-8
77

8-
version = 7.1
8+
version = 7.2

src/main/java/graphql/annotations/directives/Directive.java renamed to src/main/java/graphql/annotations/annotationTypes/directives/activation/Directive.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,21 @@
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
* See the License for the specific language governing permissions and
1414
*/
15-
package graphql.annotations.directives;
15+
package graphql.annotations.annotationTypes.directives.activation;
1616

1717
import java.lang.annotation.ElementType;
1818
import java.lang.annotation.Retention;
1919
import java.lang.annotation.RetentionPolicy;
2020
import java.lang.annotation.Target;
2121

22+
23+
/**
24+
* A way of activating a graphql directive - put this annotation on top of the graphql field/argument and determine which directive you wish to use
25+
*/
2226
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
2327
@Retention(RetentionPolicy.RUNTIME)
2428
public @interface Directive {
2529
String name();
2630

27-
Class<? extends AnnotationsDirectiveWiring> wiringClass();
28-
2931
String[] argumentsValues() default {};
3032
}

src/main/java/graphql/annotations/annotationTypes/GraphQLDirectives.java renamed to src/main/java/graphql/annotations/annotationTypes/directives/activation/GraphQLDirectives.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,17 @@
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
* See the License for the specific language governing permissions and
1414
*/
15-
package graphql.annotations.annotationTypes;
16-
17-
import graphql.annotations.directives.Directive;
15+
package graphql.annotations.annotationTypes.directives.activation;
1816

1917
import java.lang.annotation.ElementType;
2018
import java.lang.annotation.Retention;
2119
import java.lang.annotation.RetentionPolicy;
2220
import java.lang.annotation.Target;
2321

22+
/**
23+
* Put in on a graphql field / other graphql type in order to activate directives on it
24+
*/
25+
2426
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
2527
@Retention(RetentionPolicy.RUNTIME)
2628
public @interface GraphQLDirectives {

src/main/java/graphql/annotations/directives/creation/DirectiveLocations.java renamed to src/main/java/graphql/annotations/annotationTypes/directives/definition/DirectiveLocations.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
* See the License for the specific language governing permissions and
1414
*/
15-
package graphql.annotations.directives.creation;
15+
package graphql.annotations.annotationTypes.directives.definition;
1616

1717
import graphql.introspection.Introspection;
1818

@@ -21,7 +21,7 @@
2121
import java.lang.annotation.RetentionPolicy;
2222
import java.lang.annotation.Target;
2323

24-
@Target({ElementType.TYPE})
24+
@Target({ElementType.TYPE, ElementType.METHOD})
2525
@Retention(RetentionPolicy.RUNTIME)
2626
public @interface DirectiveLocations {
2727
Introspection.DirectiveLocation[] value();
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Copyright 2016 Yurii Rashkovskii
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
*/
15+
package graphql.annotations.annotationTypes.directives.definition;
16+
17+
import graphql.annotations.directives.AnnotationsDirectiveWiring;
18+
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
22+
import java.lang.annotation.Target;
23+
24+
25+
/**
26+
* You should put this annotation on top of a directive method you created, or a directive java annotation you created
27+
*/
28+
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
29+
@Retention(RetentionPolicy.RUNTIME)
30+
public @interface GraphQLDirectiveDefinition {
31+
Class<? extends AnnotationsDirectiveWiring> wiring();
32+
}

src/main/java/graphql/annotations/directives/DirectiveWiringMapRetriever.java

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
*/
1515
package graphql.annotations.directives;
1616

17-
import graphql.annotations.annotationTypes.GraphQLDirectives;
17+
import graphql.annotations.annotationTypes.directives.activation.GraphQLDirectives;
18+
import graphql.annotations.processor.DirectiveAndWiring;
1819
import graphql.annotations.processor.ProcessingElementsContainer;
1920
import graphql.annotations.processor.exceptions.GraphQLAnnotationsException;
21+
import graphql.annotations.processor.util.DirectiveJavaAnnotationUtil;
2022
import graphql.schema.GraphQLDirective;
2123

2224
import java.lang.reflect.AnnotatedElement;
@@ -26,20 +28,31 @@
2628

2729
public class DirectiveWiringMapRetriever {
2830
public HashMap<GraphQLDirective, AnnotationsDirectiveWiring> getDirectiveWiringMap(AnnotatedElement object, ProcessingElementsContainer container) {
29-
GraphQLDirectives directivesContainer = object.getAnnotation(GraphQLDirectives.class);
3031
LinkedHashMap<GraphQLDirective, AnnotationsDirectiveWiring> map = new LinkedHashMap<>();
32+
DirectiveJavaAnnotationUtil.getDirectiveAnnotations(object).sequential().forEach(x ->
33+
putInMap(container, map, DirectiveJavaAnnotationUtil.getName(x)));
34+
35+
GraphQLDirectives directivesContainer = object.getAnnotation(GraphQLDirectives.class);
3136
if (directivesContainer == null) return map;
3237
Arrays.stream(directivesContainer.value()).sequential().forEach(x -> {
33-
if (!container.getDirectiveRegistry().containsKey(x.name())) {
34-
throw new GraphQLAnnotationsException(String.format("No directive named %s is found in the directive registry", x.name()), null);
35-
}
36-
try {
37-
map.put(container.getDirectiveRegistry().get(x.name()), x.wiringClass().newInstance());
38-
} catch (InstantiationException | IllegalAccessException e) {
39-
throw new GraphQLAnnotationsException("Cannot create an instance of the wiring class " + x.wiringClass().getSimpleName(), e);
40-
}
38+
putInMap(container, map, x.name());
4139
});
40+
4241
return map;
4342
}
4443

44+
private void putInMap(ProcessingElementsContainer container, LinkedHashMap<GraphQLDirective, AnnotationsDirectiveWiring> map, String name) {
45+
if (!container.getDirectiveRegistry().containsKey(name)) {
46+
throw new GraphQLAnnotationsException(String.format("No directive named %s is found in the directive registry", name), null);
47+
}
48+
DirectiveAndWiring directiveAndWiring = container.getDirectiveRegistry().get(name);
49+
if (directiveAndWiring.getWiringClass() == null) {
50+
throw new GraphQLAnnotationsException("No wiring class was supplied to directive " + directiveAndWiring.getDirective().getName(), null);
51+
}
52+
try {
53+
map.put(directiveAndWiring.getDirective(), directiveAndWiring.getWiringClass().newInstance());
54+
} catch (InstantiationException | IllegalAccessException e) {
55+
throw new GraphQLAnnotationsException("Cannot create an instance of the wiring class " + directiveAndWiring.getWiringClass().getSimpleName(), e);
56+
}
57+
}
4558
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Copyright 2016 Yurii Rashkovskii
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
*/
15+
package graphql.annotations.processor;
16+
17+
import graphql.annotations.directives.AnnotationsDirectiveWiring;
18+
import graphql.schema.GraphQLDirective;
19+
20+
public class DirectiveAndWiring {
21+
private GraphQLDirective directive;
22+
private Class<? extends AnnotationsDirectiveWiring> wiringClass;
23+
24+
public DirectiveAndWiring(GraphQLDirective directive, Class<? extends AnnotationsDirectiveWiring> wiringClass) {
25+
this.directive = directive;
26+
this.wiringClass = wiringClass;
27+
}
28+
29+
public GraphQLDirective getDirective() {
30+
return directive;
31+
}
32+
33+
public Class<? extends AnnotationsDirectiveWiring> getWiringClass() {
34+
return wiringClass;
35+
}
36+
}

0 commit comments

Comments
 (0)