Skip to content

Commit 82f7624

Browse files
committed
Add support for query and mutation directives.
1 parent c6fc618 commit 82f7624

6 files changed

Lines changed: 131 additions & 23 deletions

File tree

codegen/lib/graphql_java_gen.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,14 @@ def java_arg_expresions_with_empty_optional_args(field)
301301
expressions.join(', ')
302302
end
303303

304+
def java_directive_required_arg_defs(directive)
305+
defs = []
306+
directive.required_args.each do |arg|
307+
defs << "#{java_input_type(arg.type)} #{escape_reserved_word(arg.camelize_name)}"
308+
end
309+
defs.join(', ')
310+
end
311+
304312
def java_implements(type)
305313
return "implements #{type.name} " unless type.object?
306314
interfaces = abstract_types.fetch(type.name)
@@ -352,7 +360,7 @@ def java_doc(element)
352360
doc << "\n*"
353361
doc << "\n*"
354362
else
355-
doc << '*'
363+
doc << '*'
356364
end
357365
doc << ' @deprecated '
358366
doc << element.deprecation_reason

codegen/lib/graphql_java_gen/templates/APISchema.java.erb

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.google.gson.JsonElement;
88
import com.google.gson.JsonObject;
99
import com.shopify.graphql.support.AbstractResponse;
1010
import com.shopify.graphql.support.Arguments;
11+
import com.shopify.graphql.support.Directive;
1112
import com.shopify.graphql.support.Error;
1213
import com.shopify.graphql.support.Query;
1314
import com.shopify.graphql.support.SchemaViolationError;
@@ -18,21 +19,27 @@ import com.shopify.graphql.support.Input;
1819
<% end %>
1920

2021
import java.io.Serializable;
21-
import java.util.ArrayList;
22-
import java.util.List;
23-
import java.util.Map;
22+
import java.util.*;
2423

2524
public class <%= schema_name %> {
2625
public static final String API_VERSION = "<%= version %>";
2726

2827
<% [[:query, schema.query_root_name], [:mutation, schema.mutation_root_name]].each do |operation_type, root_name| %>
2928
<% next unless root_name %>
3029
public static <%= root_name %>Query <%= operation_type %>(<%= root_name %>QueryDefinition queryDef) {
31-
StringBuilder queryString = new StringBuilder("<%= operation_type unless operation_type == :query %>{");
32-
<%= root_name %>Query query = new <%= root_name %>Query(queryString);
33-
queryDef.define(query);
34-
queryString.append('}');
35-
return query;
30+
return <%= operation_type %>(Collections.emptyList(), queryDef);
31+
}
32+
33+
public static <%= root_name %>Query <%= operation_type %>(List<Directive> directives, <%= root_name %>QueryDefinition queryDef) {
34+
StringBuilder queryString = new StringBuilder("<%= operation_type %>");
35+
for (Directive directive : directives) {
36+
queryString.append(" " + directive.toString());
37+
}
38+
queryString.append(" {");
39+
<%= root_name %>Query query = new <%= root_name %>Query(queryString);
40+
queryDef.define(query);
41+
queryString.append('}');
42+
return query;
3643
}
3744

3845
public static class <%= operation_type.capitalize %>Response {
@@ -68,6 +75,38 @@ public class <%= schema_name %> {
6875
}
6976
<% end %>
7077

78+
<% schema.directives.select { |directive| !(directive.locations & %w{QUERY MUTATION}).empty? }.each do |directive| %>
79+
<%= java_doc(directive) %>
80+
public static class <%= directive.classify_name %>Directive extends Directive {
81+
<% directive.args.each do |arg| %>
82+
public <%= java_input_type(arg.type) %> <%= escape_reserved_word(arg.camelize_name) %>;
83+
<% end %>
84+
85+
public <%= directive.classify_name %>Directive(<%= java_directive_required_arg_defs(directive) %>) {
86+
super("<%= directive.name %>");
87+
}
88+
89+
<% unless directive.args.empty? %>
90+
@Override
91+
public String toString() {
92+
StringBuilder _queryBuilder = new StringBuilder(super.toString());
93+
_queryBuilder.append("(");
94+
<% first_arg = true %>
95+
<% directive.args.each do |arg| %>
96+
if (<%= escape_reserved_word(arg.camelize_name) %> != null) {
97+
<% unless first_arg %>_queryBuilder.append(", ");<% end %>
98+
_queryBuilder.append("<%= arg.name %>:");
99+
<%= generate_build_input_code(escape_reserved_word(arg.camelize_name), arg.type) %>
100+
}
101+
<% first_arg = false %>
102+
<% end %>
103+
_queryBuilder.append(")");
104+
return _queryBuilder.toString();
105+
}
106+
<% end %>
107+
}
108+
<% end %>
109+
71110
<% schema.types.reject{ |type| type.name.start_with?('__') || type.scalar? }.each do |type| %>
72111
<% case type.kind when 'OBJECT', 'INTERFACE', 'UNION' %>
73112
<% fields = type.fields(include_deprecated: include_deprecated) || [] %>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.shopify.graphql.support;
2+
3+
public abstract class Directive {
4+
private final String name;
5+
6+
protected Directive(String name) {
7+
this.name = name;
8+
}
9+
10+
@Override
11+
public String toString() {
12+
return "@" + name;
13+
}
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.shopify.graphql.support;
2+
3+
import org.junit.Test;
4+
5+
import static junit.framework.Assert.assertEquals;
6+
7+
public class DirectiveTest {
8+
@Test
9+
public void testName() {
10+
Generated.SampleDirective directive = new Generated.SampleDirective();
11+
assertEquals("@sample", directive.toString());
12+
}
13+
}

support/src/test/java/com/shopify/graphql/support/Generated.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,35 @@
2222

2323
import java.io.Serializable;
2424
import java.util.ArrayList;
25+
import java.util.Collections;
2526
import java.util.List;
2627
import java.util.Map;
2728

2829
public class Generated {
2930
public static final String API_VERSION = "2020-01";
3031

3132
public static QueryRootQuery query(QueryRootQueryDefinition queryDef) {
32-
StringBuilder queryString = new StringBuilder("{");
33+
return query(Collections.emptyList(), queryDef);
34+
}
35+
36+
public static QueryRootQuery query(List<Directive> directives, QueryRootQueryDefinition queryDef) {
37+
StringBuilder queryString = new StringBuilder("query");
38+
for (Directive directive : directives) {
39+
queryString.append(" " + directive.toString());
40+
}
41+
queryString.append(" {");
3342
QueryRootQuery query = new QueryRootQuery(queryString);
3443
queryDef.define(query);
3544
queryString.append('}');
3645
return query;
3746
}
3847

48+
public static class SampleDirective extends Directive {
49+
SampleDirective() {
50+
super("sample");
51+
}
52+
}
53+
3954
public static class QueryResponse {
4055
private TopLevelResponse response;
4156
private QueryRoot data;
@@ -69,7 +84,15 @@ public static QueryResponse fromJson(String json) throws SchemaViolationError {
6984
}
7085

7186
public static MutationQuery mutation(MutationQueryDefinition queryDef) {
72-
StringBuilder queryString = new StringBuilder("mutation{");
87+
return mutation(Collections.emptyList(), queryDef);
88+
}
89+
90+
public static MutationQuery mutation(List<Directive> directives, MutationQueryDefinition queryDef) {
91+
StringBuilder queryString = new StringBuilder("mutation");
92+
for (Directive directive : directives) {
93+
queryString.append(" " + directive.toString());
94+
}
95+
queryString.append(" {");
7396
MutationQuery query = new MutationQuery(queryString);
7497
queryDef.define(query);
7598
queryString.append('}');

support/src/test/java/com/shopify/graphql/support/IntegrationTest.java

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package com.shopify.graphql.support;
22

3+
import junit.framework.Assert;
4+
35
import java.time.LocalDate;
46
import java.time.LocalDateTime;
7+
import java.util.ArrayList;
58
import java.util.Arrays;
69
import java.util.List;
710

@@ -20,13 +23,13 @@ public void testStringFieldQuery() throws Exception {
2023
@Test
2124
public void testRequiredArgQuery() throws Exception {
2225
String queryString = Generated.query(query -> query.string("user:1:name")).toString();
23-
assertEquals("{string(key:\"user:1:name\")}", queryString);
26+
assertEquals("query {string(key:\"user:1:name\")}", queryString);
2427
}
2528

2629
@Test
2730
public void testOptionalArgQuery() throws Exception {
2831
String queryString = Generated.query(query -> query.keys(10, args -> args.after("cursor"))).toString();
29-
assertEquals("{keys(first:10,after:\"cursor\")}", queryString);
32+
assertEquals("query {keys(first:10,after:\"cursor\")}", queryString);
3033
}
3134

3235
@Test
@@ -37,7 +40,7 @@ public void testInterfaceQuery() throws Exception {
3740
.onStringEntry(strEntry -> strEntry.value())
3841
)
3942
).toString();
40-
assertEquals("{entry(key:\"user:1\"){__typename,ttl,... on StringEntry{value}}}", queryString);
43+
assertEquals("query {entry(key:\"user:1\"){__typename,ttl,... on StringEntry{value}}}", queryString);
4144
}
4245

4346
@Test
@@ -47,29 +50,29 @@ public void testUnionQuery() throws Exception {
4750
.onStringEntry(strEntry -> strEntry.value())
4851
)
4952
).toString();
50-
assertEquals("{entry_union(key:\"user:1\"){__typename,... on StringEntry{value}}}", queryString);
53+
assertEquals("query {entry_union(key:\"user:1\"){__typename,... on StringEntry{value}}}", queryString);
5154
}
5255

5356
@Test
5457
public void testEnumInput() throws Exception {
5558
String queryString = Generated.query(query -> query
5659
.keys(10, args -> args.type(Generated.KeyType.INTEGER))
5760
).toString();
58-
assertEquals("{keys(first:10,type:INTEGER)}", queryString);
61+
assertEquals("query {keys(first:10,type:INTEGER)}", queryString);
5962
}
6063

6164
@Test
6265
public void testMutation() throws Exception {
6366
String queryString = Generated.mutation(mutation -> mutation.setString("foo", "bar")).toString();
64-
assertEquals("mutation{set_string(key:\"foo\",value:\"bar\")}", queryString);
67+
assertEquals("mutation {set_string(key:\"foo\",value:\"bar\")}", queryString);
6568
}
6669

6770
@Test
6871
public void testInputObject() throws Exception {
6972
String queryString = Generated.mutation(mutation -> mutation
7073
.setInteger(new Generated.SetIntegerInput("answer", 42).setNegate(true))
7174
).toString();
72-
assertEquals("mutation{set_integer(input:{key:\"answer\",value:42,negate:true})}", queryString);
75+
assertEquals("mutation {set_integer(input:{key:\"answer\",value:42,negate:true})}", queryString);
7376
}
7477

7578
@Test
@@ -78,7 +81,7 @@ public void testScalarInput() throws Exception {
7881
String queryString = Generated.mutation(mutation -> mutation
7982
.setInteger(new Generated.SetIntegerInput("answer", 42).setTtl(ttl))
8083
).toString();
81-
assertEquals("mutation{set_integer(input:{key:\"answer\",value:42,ttl:\"2017-01-31T10:09:48\"})}", queryString);
84+
assertEquals("mutation {set_integer(input:{key:\"answer\",value:42,ttl:\"2017-01-31T10:09:48\"})}", queryString);
8285
}
8386

8487
@Test
@@ -163,30 +166,38 @@ public void testOptionalFieldOnInput() throws Exception {
163166
String queryString = Generated.mutation(mutation -> mutation
164167
.setInteger(new Generated.SetIntegerInput("answer", 42).setTtl(null))
165168
).toString();
166-
assertEquals("mutation{set_integer(input:{key:\"answer\",value:42})}", queryString);
169+
assertEquals("mutation {set_integer(input:{key:\"answer\",value:42})}", queryString);
167170
}
168171

169172
@Test
170173
public void testOptionalFieldOnInputAsUndefined() throws Exception {
171174
String queryString = Generated.mutation(mutation -> mutation
172175
.setInteger(new Generated.SetIntegerInput("answer", 42).setTtlInput(Input.<LocalDateTime>undefined()))
173176
).toString();
174-
assertEquals("mutation{set_integer(input:{key:\"answer\",value:42})}", queryString);
177+
assertEquals("mutation {set_integer(input:{key:\"answer\",value:42})}", queryString);
175178
}
176179

177180
@Test
178181
public void testOptionalFieldOnInputAsExplicitNull() throws Exception {
179182
String queryString = Generated.mutation(mutation -> mutation
180183
.setInteger(new Generated.SetIntegerInput("answer", 42).setTtlInput(Input.<LocalDateTime>value(null)))
181184
).toString();
182-
assertEquals("mutation{set_integer(input:{key:\"answer\",value:42,ttl:null})}", queryString);
185+
assertEquals("mutation {set_integer(input:{key:\"answer\",value:42,ttl:null})}", queryString);
183186
}
184187

185188
@Test
186189
public void testOptionalFieldOnInputAsInputValue() throws Exception {
187190
String queryString = Generated.mutation(mutation -> mutation
188191
.setInteger(new Generated.SetIntegerInput("answer", 42).setTtlInput(Input.<LocalDateTime>value(LocalDateTime.of(2017, 1, 31, 10, 9, 48))))
189192
).toString();
190-
assertEquals("mutation{set_integer(input:{key:\"answer\",value:42,ttl:\"2017-01-31T10:09:48\"})}", queryString);
193+
assertEquals("mutation {set_integer(input:{key:\"answer\",value:42,ttl:\"2017-01-31T10:09:48\"})}", queryString);
194+
}
195+
196+
@Test
197+
public void testQueryWithDirectives() {
198+
List<Directive> directives = new ArrayList<>();
199+
directives.add(new Generated.SampleDirective());
200+
Generated.QueryRootQuery query = Generated.query(directives, root -> root.integer("productCount"));
201+
Assert.assertEquals("query @sample {integer(key:\"productCount\")}", query.toString());
191202
}
192203
}

0 commit comments

Comments
 (0)