graphql_java_gen 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +119 -0
- data/codegen/lib/graphql_java_gen.rb +263 -0
- data/codegen/lib/graphql_java_gen/annotation.rb +15 -0
- data/codegen/lib/graphql_java_gen/reformatter.rb +43 -0
- data/codegen/lib/graphql_java_gen/scalar.rb +33 -0
- data/codegen/lib/graphql_java_gen/templates/APISchema.java.erb +369 -0
- data/codegen/lib/graphql_java_gen/version.rb +3 -0
- metadata +123 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a3088c3fd787e2d815792f8bb8f6a0b3fe414829
|
4
|
+
data.tar.gz: 1058513015d8fe05d201f7e72ef5f3a007a94b8a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a3d82e27f2ca1cfbf3a7b7c082a5a75d631607f32f1d6bc2e7fdb54840ed023814ce81c8fe4d6e65bf4576a0c28bb1eadbb988aecb8593362935fa2603648e18
|
7
|
+
data.tar.gz: 1c040d36778c54054772af31b651ac30db8aef05f77812c158e51cbcf33ab98b171d942d1aaf1837439a84de2dcbeefab0063a6e43eb2455e40b666d5ff86d79
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Shopify
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
# GraphQLJavaGen
|
2
|
+
|
3
|
+
Generate code for a specific GraphQL schema that provides query
|
4
|
+
builders and response classes.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
The code generator requires ruby version 2.1 or later.
|
9
|
+
|
10
|
+
It is recommended to use [bundler](http://bundler.io/) to install
|
11
|
+
the code generators ruby package.
|
12
|
+
|
13
|
+
Until this project is released, it is recommended to include it into
|
14
|
+
a project as a git submodule.
|
15
|
+
|
16
|
+
$ git submodule https://github.com/Shopify/graphql_java_gen.git
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'graphql_java_gen', path: 'graphql_java_gen'
|
22
|
+
```
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
The generated code depends on the com.shopify.graphql.support package
|
29
|
+
which is in the support directory of this repo. Create a symlink to
|
30
|
+
include that project in a [gradle multi-project build
|
31
|
+
](https://docs.gradle.org/current/userguide/multi_project_builds.html)
|
32
|
+
|
33
|
+
$ ln -s graphql_java_gen/support GraphQLSupport
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
Create a script that generates the code for a GraphQL API
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
require 'graphql_java_gen'
|
41
|
+
require 'graphql_schema'
|
42
|
+
require 'json'
|
43
|
+
|
44
|
+
introspection_result = File.read("graphql_schema.json")
|
45
|
+
schema = GraphQLSchema.new(JSON.parse(introspection_result))
|
46
|
+
|
47
|
+
GraphQLJavaGen.new(schema,
|
48
|
+
package_name: "com.example.MyApp",
|
49
|
+
nest_under: 'ExampleSchema',
|
50
|
+
custom_scalars: [
|
51
|
+
GraphQLJavaGen::Scalar.new(
|
52
|
+
type_name: 'Decimal',
|
53
|
+
java_type: 'BigDecimal',
|
54
|
+
deserialize_expr: ->(expr) { "new BigDecimal(jsonAsString(#{expr}, key))" },
|
55
|
+
imports: ['java.math.BigDecimal'],
|
56
|
+
),
|
57
|
+
]
|
58
|
+
).save("#{Dir.pwd}/../MyApp/src/main/java/com/example/MyApp/ExampleSchema.java")
|
59
|
+
```
|
60
|
+
|
61
|
+
That generated code depends on the com.shopify.graphql.support package.
|
62
|
+
|
63
|
+
The generated code includes a query builder that can be used to create a GraphQL
|
64
|
+
query in a type-safe manner.
|
65
|
+
|
66
|
+
```java
|
67
|
+
String queryString = ExampleSchema.query(query -> query
|
68
|
+
.user(user -> user
|
69
|
+
.firstName()
|
70
|
+
.lastName()
|
71
|
+
)
|
72
|
+
).toString();
|
73
|
+
```
|
74
|
+
|
75
|
+
The generated code also includes response classes that will deserialize the response
|
76
|
+
and provide methods for accessing the field data with it already coerced to the
|
77
|
+
correct type.
|
78
|
+
|
79
|
+
```java
|
80
|
+
ExampleSchema.QueryResponse response = ExampleSchema.QueryResponse.fromJson(responseJson);
|
81
|
+
if (!response.getErrors().isEmpty()) {
|
82
|
+
for (Error error : response.getErrors()) {
|
83
|
+
System.err.println("GraphQL error: " + error.message());
|
84
|
+
}
|
85
|
+
}
|
86
|
+
if (data != null) {
|
87
|
+
ExampleSchema.User user = data.getUser();
|
88
|
+
System.out.println("user.firstName() + " " + user.lastName());
|
89
|
+
}
|
90
|
+
```
|
91
|
+
|
92
|
+
## Lambda Expressions
|
93
|
+
|
94
|
+
The java 8 lambda expressions feature is essential for having a
|
95
|
+
nice syntax for the query builder. This feature is also available
|
96
|
+
for Android apps by using the
|
97
|
+
[Jack toolchain](https://source.android.com/source/jack.html).
|
98
|
+
|
99
|
+
If an older version of java must be used, then
|
100
|
+
[retrolambda](https://github.com/orfjackal/retrolambda) can be used
|
101
|
+
to backport this feature to java 5-7.
|
102
|
+
|
103
|
+
## Development
|
104
|
+
|
105
|
+
After checking out the repo, run `bundle` to install ruby dependencies.
|
106
|
+
Then, run `rake test` to run the tests.
|
107
|
+
|
108
|
+
To install this gem onto your local machine, run `bundle exec rake
|
109
|
+
install` or reference it from a Gemfile using the path option
|
110
|
+
(e.g. `gem 'graphql_java_gen', path: '~/src/graphql_java_gen'`).
|
111
|
+
|
112
|
+
## Contributing
|
113
|
+
|
114
|
+
See our [contributing guidelines](CONTRIBUTING.md) for more information.
|
115
|
+
|
116
|
+
## License
|
117
|
+
|
118
|
+
The gem is available as open source under the terms of the
|
119
|
+
[MIT License](http://opensource.org/licenses/MIT).
|
@@ -0,0 +1,263 @@
|
|
1
|
+
require 'graphql_java_gen/version'
|
2
|
+
require 'graphql_java_gen/reformatter'
|
3
|
+
require 'graphql_java_gen/scalar'
|
4
|
+
require 'graphql_java_gen/annotation'
|
5
|
+
|
6
|
+
require 'erb'
|
7
|
+
require 'set'
|
8
|
+
|
9
|
+
class GraphQLJavaGen
|
10
|
+
attr_reader :schema, :package_name, :scalars, :imports, :script_name, :schema_name
|
11
|
+
|
12
|
+
def initialize(schema,
|
13
|
+
package_name:, nest_under:, script_name: 'graphql_java_gen gem',
|
14
|
+
custom_scalars: [], custom_annotations: []
|
15
|
+
)
|
16
|
+
@schema = schema
|
17
|
+
@schema_name = nest_under
|
18
|
+
@script_name = script_name
|
19
|
+
@package_name = package_name
|
20
|
+
@scalars = (BUILTIN_SCALARS + custom_scalars).reduce({}) { |hash, scalar| hash[scalar.type_name] = scalar; hash }
|
21
|
+
@scalars.default_proc = ->(hash, key) { DEFAULT_SCALAR }
|
22
|
+
@annotations = custom_annotations
|
23
|
+
@imports = (@scalars.values.map(&:imports) + @annotations.map(&:imports)).flatten.sort.uniq
|
24
|
+
end
|
25
|
+
|
26
|
+
def save(path)
|
27
|
+
File.write(path, generate)
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate
|
31
|
+
reformat(TEMPLATE_ERB.result(binding))
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
class << self
|
37
|
+
private
|
38
|
+
|
39
|
+
def erb_for(template_filename)
|
40
|
+
erb = ERB.new(File.read(template_filename))
|
41
|
+
erb.filename = template_filename
|
42
|
+
erb
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
TEMPLATE_ERB = erb_for(File.expand_path("../graphql_java_gen/templates/APISchema.java.erb", __FILE__))
|
47
|
+
private_constant :TEMPLATE_ERB
|
48
|
+
|
49
|
+
DEFAULT_SCALAR = Scalar.new(
|
50
|
+
type_name: nil,
|
51
|
+
java_type: 'String',
|
52
|
+
deserialize_expr: ->(expr) { "jsonAsString(#{expr}, key)" },
|
53
|
+
)
|
54
|
+
private_constant :DEFAULT_SCALAR
|
55
|
+
|
56
|
+
BUILTIN_SCALARS = [
|
57
|
+
Scalar.new(
|
58
|
+
type_name: 'Int',
|
59
|
+
java_type: 'int',
|
60
|
+
deserialize_expr: ->(expr) { "jsonAsInteger(#{expr}, key)" },
|
61
|
+
),
|
62
|
+
Scalar.new(
|
63
|
+
type_name: 'Float',
|
64
|
+
java_type: 'double',
|
65
|
+
deserialize_expr: ->(expr) { "jsonAsDouble(#{expr}, key)" },
|
66
|
+
),
|
67
|
+
Scalar.new(
|
68
|
+
type_name: 'String',
|
69
|
+
java_type: 'String',
|
70
|
+
deserialize_expr: ->(expr) { "jsonAsString(#{expr}, key)" },
|
71
|
+
),
|
72
|
+
Scalar.new(
|
73
|
+
type_name: 'Boolean',
|
74
|
+
java_type: 'boolean',
|
75
|
+
deserialize_expr: ->(expr) { "jsonAsBoolean(#{expr}, key)" },
|
76
|
+
),
|
77
|
+
Scalar.new(
|
78
|
+
type_name: 'ID',
|
79
|
+
java_type: 'ID',
|
80
|
+
deserialize_expr: ->(expr) { "new ID(jsonAsString(#{expr}, key))" },
|
81
|
+
imports: ['com.shopify.graphql.support.ID'],
|
82
|
+
),
|
83
|
+
]
|
84
|
+
private_constant :BUILTIN_SCALARS
|
85
|
+
|
86
|
+
# From: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html
|
87
|
+
RESERVED_WORDS = [
|
88
|
+
"abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "final", "finally", "float",
|
89
|
+
"for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super",
|
90
|
+
"switch", "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while"
|
91
|
+
]
|
92
|
+
private_constant :RESERVED_WORDS
|
93
|
+
|
94
|
+
def escape_reserved_word(word)
|
95
|
+
return word unless RESERVED_WORDS.include?(word)
|
96
|
+
"#{word}Value"
|
97
|
+
end
|
98
|
+
|
99
|
+
def reformat(code)
|
100
|
+
Reformatter.new(indent: " " * 4).reformat(code)
|
101
|
+
end
|
102
|
+
|
103
|
+
def java_input_type(type, non_null: false)
|
104
|
+
case type.kind
|
105
|
+
when "NON_NULL"
|
106
|
+
java_input_type(type.of_type, non_null: true)
|
107
|
+
when "SCALAR"
|
108
|
+
non_null ? scalars[type.name].non_nullable_type : scalars[type.name].nullable_type
|
109
|
+
when 'LIST'
|
110
|
+
"List<#{java_input_type(type.of_type.unwrap_non_null)}>"
|
111
|
+
when 'INPUT_OBJECT', 'ENUM'
|
112
|
+
type.name
|
113
|
+
else
|
114
|
+
raise NotImplementedError, "Unhandled #{type.kind} input type"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def java_output_type(type)
|
119
|
+
type = type.unwrap_non_null
|
120
|
+
case type.kind
|
121
|
+
when "SCALAR"
|
122
|
+
scalars[type.name].nullable_type
|
123
|
+
when 'LIST'
|
124
|
+
"List<#{java_output_type(type.of_type)}>"
|
125
|
+
when 'ENUM', 'OBJECT', 'INTERFACE', 'UNION'
|
126
|
+
type.name
|
127
|
+
else
|
128
|
+
raise NotImplementedError, "Unhandled #{type.kind} response type"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def generate_build_input_code(expr, type, depth: 1)
|
133
|
+
type = type.unwrap_non_null
|
134
|
+
case type.kind
|
135
|
+
when 'SCALAR'
|
136
|
+
if ['Int', 'Float', 'Boolean'].include?(type.name)
|
137
|
+
"_queryBuilder.append(#{expr});"
|
138
|
+
else
|
139
|
+
"Query.appendQuotedString(_queryBuilder, #{expr}.toString());"
|
140
|
+
end
|
141
|
+
when 'ENUM'
|
142
|
+
"_queryBuilder.append(#{expr}.toString());"
|
143
|
+
when 'LIST'
|
144
|
+
item_type = type.of_type
|
145
|
+
<<-JAVA
|
146
|
+
_queryBuilder.append('[');
|
147
|
+
|
148
|
+
String listSeperator#{depth} = "";
|
149
|
+
for (#{java_input_type(item_type)} item#{depth} : #{expr}) {
|
150
|
+
_queryBuilder.append(listSeperator#{depth});
|
151
|
+
listSeperator#{depth} = ",";
|
152
|
+
#{generate_build_input_code("item#{depth}", item_type, depth: depth + 1)}
|
153
|
+
}
|
154
|
+
_queryBuilder.append(']');
|
155
|
+
JAVA
|
156
|
+
when 'INPUT_OBJECT'
|
157
|
+
"#{expr}.appendTo(_queryBuilder);"
|
158
|
+
else
|
159
|
+
raise NotImplementedError, "Unexpected #{type.kind} argument type"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def generate_build_output_code(expr, type, depth: 1, non_null: false, &block)
|
164
|
+
if type.non_null?
|
165
|
+
return generate_build_output_code(expr, type.of_type, depth: depth, non_null: true, &block)
|
166
|
+
end
|
167
|
+
|
168
|
+
statements = ""
|
169
|
+
unless non_null
|
170
|
+
optional_name = "optional#{depth}"
|
171
|
+
generate_build_output_code(expr, type, depth: depth, non_null: true) do |item_statements, item_expr|
|
172
|
+
statements = <<-JAVA
|
173
|
+
#{java_output_type(type)} #{optional_name} = null;
|
174
|
+
if (!#{expr}.isJsonNull()) {
|
175
|
+
#{item_statements}
|
176
|
+
#{optional_name} = #{item_expr};
|
177
|
+
}
|
178
|
+
JAVA
|
179
|
+
end
|
180
|
+
return yield statements, optional_name
|
181
|
+
end
|
182
|
+
|
183
|
+
expr = case type.kind
|
184
|
+
when 'SCALAR'
|
185
|
+
scalars[type.name].deserialize(expr)
|
186
|
+
when 'LIST'
|
187
|
+
list_name = "list#{depth}"
|
188
|
+
element_name = "element#{depth}"
|
189
|
+
generate_build_output_code(element_name, type.of_type, depth: depth + 1) do |item_statements, item_expr|
|
190
|
+
statements = <<-JAVA
|
191
|
+
#{java_output_type(type)} #{list_name} = new ArrayList<>();
|
192
|
+
for (JsonElement #{element_name} : jsonAsArray(#{expr}, key)) {
|
193
|
+
#{item_statements}
|
194
|
+
#{list_name}.add(#{item_expr});
|
195
|
+
}
|
196
|
+
JAVA
|
197
|
+
end
|
198
|
+
list_name
|
199
|
+
when 'OBJECT'
|
200
|
+
"new #{type.name}(jsonAsObject(#{expr}, key))"
|
201
|
+
when 'INTERFACE', 'UNION'
|
202
|
+
"Unknown#{type.name}.create(jsonAsObject(#{expr}, key))"
|
203
|
+
when 'ENUM'
|
204
|
+
"#{type.name}.fromGraphQl(jsonAsString(#{expr}, key))"
|
205
|
+
else
|
206
|
+
raise NotImplementedError, "Unexpected #{type.kind} argument type"
|
207
|
+
end
|
208
|
+
yield statements, expr
|
209
|
+
end
|
210
|
+
|
211
|
+
def java_arg_defs(field, skip_optional: false)
|
212
|
+
defs = []
|
213
|
+
field.required_args.each do |arg|
|
214
|
+
defs << "#{java_input_type(arg.type)} #{escape_reserved_word(arg.camelize_name)}"
|
215
|
+
end
|
216
|
+
unless field.optional_args.empty? || skip_optional
|
217
|
+
defs << "#{field.classify_name}ArgumentsDefinition argsDef"
|
218
|
+
end
|
219
|
+
if field.subfields?
|
220
|
+
defs << "#{field.type.unwrap.name}QueryDefinition queryDef"
|
221
|
+
end
|
222
|
+
defs.join(', ')
|
223
|
+
end
|
224
|
+
|
225
|
+
def java_required_arg_defs(field)
|
226
|
+
defs = []
|
227
|
+
field.required_args.each do |arg|
|
228
|
+
defs << "#{java_input_type(arg.type)} #{escape_reserved_word(arg.camelize_name)}"
|
229
|
+
end
|
230
|
+
unless field.optional_args.empty?
|
231
|
+
defs << "#{field.classify_name}ArgumentsDefinition argsDef"
|
232
|
+
end
|
233
|
+
if field.subfields?
|
234
|
+
defs << "#{field.type.unwrap.classify_name}QueryDefinition queryDef"
|
235
|
+
end
|
236
|
+
defs.join(', ')
|
237
|
+
end
|
238
|
+
|
239
|
+
def java_arg_expresions_with_empty_optional_args(field)
|
240
|
+
expressions = field.required_args.map { |arg| escape_reserved_word(arg.camelize_name) }
|
241
|
+
expressions << "args -> {}"
|
242
|
+
if field.subfields?
|
243
|
+
expressions << "queryDef"
|
244
|
+
end
|
245
|
+
expressions.join(', ')
|
246
|
+
end
|
247
|
+
|
248
|
+
def java_implements(type)
|
249
|
+
return "implements #{type.name} " unless type.object?
|
250
|
+
return "" if type.interfaces.empty?
|
251
|
+
"implements #{type.interfaces.map{ |interface| interface.name }.join(', ')} "
|
252
|
+
end
|
253
|
+
|
254
|
+
def java_annotations(field)
|
255
|
+
@annotations.map do |annotation|
|
256
|
+
"@#{annotation.name}" if annotation.annotate?(field)
|
257
|
+
end.compact.join("\n")
|
258
|
+
end
|
259
|
+
|
260
|
+
def type_names_set
|
261
|
+
@type_names_set ||= schema.types.map(&:name).to_set
|
262
|
+
end
|
263
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class GraphQLJavaGen
|
2
|
+
class Annotation
|
3
|
+
attr_reader :name, :imports
|
4
|
+
|
5
|
+
def initialize(name, imports: [], &block)
|
6
|
+
@name = name
|
7
|
+
@imports = imports
|
8
|
+
@condition = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def annotate?(field)
|
12
|
+
@condition.call(field)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class GraphQLJavaGen
|
2
|
+
# Reformat code that uses curly brace blocks
|
3
|
+
class Reformatter
|
4
|
+
INDENT_START_CHARS = ["{", "("]
|
5
|
+
INDENT_END_CHARS = ["}", ")"]
|
6
|
+
|
7
|
+
def initialize(indent: "\t")
|
8
|
+
@indent = indent
|
9
|
+
end
|
10
|
+
|
11
|
+
def reformat(code)
|
12
|
+
output = ""
|
13
|
+
indent_level = 0
|
14
|
+
squeeze_newlines = true
|
15
|
+
|
16
|
+
code.lines.each do |line|
|
17
|
+
stripped_line = line.strip
|
18
|
+
|
19
|
+
if INDENT_END_CHARS.include?(stripped_line[0])
|
20
|
+
indent_level -= 1
|
21
|
+
# no blank lines immediately preceding end of block
|
22
|
+
output.rstrip!
|
23
|
+
output << "\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
if stripped_line.empty?
|
27
|
+
output << "\n" unless squeeze_newlines
|
28
|
+
squeeze_newlines = true
|
29
|
+
else
|
30
|
+
output << @indent * indent_level << line.lstrip
|
31
|
+
squeeze_newlines = false
|
32
|
+
end
|
33
|
+
|
34
|
+
if INDENT_START_CHARS.include?(stripped_line[-1])
|
35
|
+
indent_level += 1
|
36
|
+
# no blank lines following start of block
|
37
|
+
squeeze_newlines = true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
output
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class GraphQLJavaGen
|
2
|
+
class Scalar
|
3
|
+
attr_reader :type_name, :imports
|
4
|
+
|
5
|
+
WRAPPER_OBJECT = {
|
6
|
+
'int' => 'Integer',
|
7
|
+
'long' => 'Long',
|
8
|
+
'double' => 'Double',
|
9
|
+
'boolean' => 'Boolean',
|
10
|
+
}
|
11
|
+
WRAPPER_OBJECT.default_proc = ->(_, key) { key }
|
12
|
+
private_constant :WRAPPER_OBJECT
|
13
|
+
|
14
|
+
def initialize(type_name:, java_type:, deserialize_expr:, imports: [])
|
15
|
+
@type_name = type_name
|
16
|
+
@java_type = java_type
|
17
|
+
@deserialize_expr = deserialize_expr
|
18
|
+
@imports = imports
|
19
|
+
end
|
20
|
+
|
21
|
+
def nullable_type
|
22
|
+
WRAPPER_OBJECT[@java_type]
|
23
|
+
end
|
24
|
+
|
25
|
+
def non_nullable_type
|
26
|
+
@java_type
|
27
|
+
end
|
28
|
+
|
29
|
+
def deserialize(expr)
|
30
|
+
@deserialize_expr.call(expr)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,369 @@
|
|
1
|
+
// Generated from <%= script_name %>
|
2
|
+
|
3
|
+
package <%= package_name %>;
|
4
|
+
|
5
|
+
import com.google.gson.Gson;
|
6
|
+
import com.google.gson.GsonBuilder;
|
7
|
+
import com.google.gson.JsonElement;
|
8
|
+
import com.google.gson.JsonObject;
|
9
|
+
import com.shopify.graphql.support.AbstractResponse;
|
10
|
+
import com.shopify.graphql.support.Arguments;
|
11
|
+
import com.shopify.graphql.support.Error;
|
12
|
+
import com.shopify.graphql.support.Query;
|
13
|
+
import com.shopify.graphql.support.SchemaViolationError;
|
14
|
+
import com.shopify.graphql.support.TopLevelResponse;
|
15
|
+
<% imports.each do |import| %>
|
16
|
+
import <%= import %>;
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
import java.io.Serializable;
|
20
|
+
import java.util.ArrayList;
|
21
|
+
import java.util.List;
|
22
|
+
import java.util.Map;
|
23
|
+
|
24
|
+
public class <%= schema_name %> {
|
25
|
+
<% [[:query, schema.query_root_name], [:mutation, schema.mutation_root_name]].each do |operation_type, root_name| %>
|
26
|
+
<% next unless schema.mutation_root_name %>
|
27
|
+
public static <%= root_name %>Query <%= operation_type %>(<%= root_name %>QueryDefinition queryDef) {
|
28
|
+
StringBuilder queryString = new StringBuilder("<%= operation_type unless operation_type == :query %>{");
|
29
|
+
<%= root_name %>Query query = new <%= root_name %>Query(queryString);
|
30
|
+
queryDef.define(query);
|
31
|
+
queryString.append('}');
|
32
|
+
return query;
|
33
|
+
}
|
34
|
+
|
35
|
+
public static class <%= operation_type.capitalize %>Response {
|
36
|
+
private TopLevelResponse response;
|
37
|
+
private <%= root_name %> data;
|
38
|
+
|
39
|
+
public <%= operation_type.capitalize %>Response(TopLevelResponse response) throws SchemaViolationError {
|
40
|
+
this.response = response;
|
41
|
+
this.data = response.getData() != null ? new <%= root_name %>(response.getData()) : null;
|
42
|
+
}
|
43
|
+
|
44
|
+
public <%= root_name %> getData() {
|
45
|
+
return data;
|
46
|
+
}
|
47
|
+
|
48
|
+
public List<Error> getErrors() {
|
49
|
+
return response.getErrors();
|
50
|
+
}
|
51
|
+
|
52
|
+
public String toJson() {
|
53
|
+
return new Gson().toJson(response);
|
54
|
+
}
|
55
|
+
|
56
|
+
public String prettyPrintJson() {
|
57
|
+
final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
58
|
+
return gson.toJson(response);
|
59
|
+
}
|
60
|
+
|
61
|
+
public static <%= operation_type.capitalize %>Response fromJson(String json) throws SchemaViolationError {
|
62
|
+
final TopLevelResponse response = new Gson().fromJson(json, TopLevelResponse.class);
|
63
|
+
return new <%= operation_type.capitalize %>Response(response);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
<% end %>
|
67
|
+
|
68
|
+
<% schema.types.reject{ |type| type.name.start_with?('__') || type.scalar? }.each do |type| %>
|
69
|
+
<% case type.kind when 'OBJECT', 'INTERFACE', 'UNION' %>
|
70
|
+
public interface <%= type.name %>QueryDefinition {
|
71
|
+
void define(<%= type.name %>Query _queryBuilder);
|
72
|
+
}
|
73
|
+
|
74
|
+
public static class <%= type.name %>Query extends Query<<%= type.name %>Query> {
|
75
|
+
<%= type.name %>Query(StringBuilder _queryBuilder) {
|
76
|
+
super(_queryBuilder);
|
77
|
+
<% if type.object? && type.implement?("Node") %>
|
78
|
+
startField("id");
|
79
|
+
<% end %>
|
80
|
+
<% unless type.object? %>
|
81
|
+
startField("__typename");
|
82
|
+
<% end %>
|
83
|
+
}
|
84
|
+
|
85
|
+
<% type.fields.each do |field| %>
|
86
|
+
<% next if field.name == "id" && type.object? && type.implement?("Node") %>
|
87
|
+
<% unless field.optional_args.empty? %>
|
88
|
+
public class <%= field.classify_name %>Arguments extends Arguments {
|
89
|
+
<%= field.classify_name %>Arguments(StringBuilder _queryBuilder) {
|
90
|
+
super(_queryBuilder, <%= !!field.required_args.empty? %>);
|
91
|
+
}
|
92
|
+
|
93
|
+
<% field.optional_args.each do |arg| %>
|
94
|
+
public <%= field.classify_name %>Arguments <%= escape_reserved_word(arg.camelize_name) %>(<%= java_input_type(arg.type) %> value) {
|
95
|
+
if (value != null) {
|
96
|
+
startArgument("<%= arg.name %>");
|
97
|
+
<%= generate_build_input_code('value', arg.type) %>
|
98
|
+
}
|
99
|
+
return this;
|
100
|
+
}
|
101
|
+
<% end %>
|
102
|
+
}
|
103
|
+
|
104
|
+
public interface <%= field.classify_name %>ArgumentsDefinition {
|
105
|
+
void define(<%= field.classify_name %>Arguments args);
|
106
|
+
}
|
107
|
+
|
108
|
+
public <%= type.name %>Query <%= escape_reserved_word(field.camelize_name) %>(<%= java_arg_defs(field, skip_optional: true) %>) {
|
109
|
+
return <%= escape_reserved_word(field.camelize_name) %>(<%= java_arg_expresions_with_empty_optional_args(field) %>);
|
110
|
+
}
|
111
|
+
<% end %>
|
112
|
+
|
113
|
+
public <%= type.name %>Query <%= escape_reserved_word(field.camelize_name) %>(<%= java_arg_defs(field) %>) {
|
114
|
+
startField("<%= field.name %>");
|
115
|
+
<% unless field.args.empty? %>
|
116
|
+
<% if field.required_args.empty? %>
|
117
|
+
<%= field.classify_name %>Arguments args = new <%= field.classify_name %>Arguments(_queryBuilder);
|
118
|
+
argsDef.define(args);
|
119
|
+
<%= field.classify_name %>Arguments.end(args);
|
120
|
+
<% else %>
|
121
|
+
<% field.required_args.each_with_index do |arg, i| %>
|
122
|
+
_queryBuilder.append("<%= i == 0 ? "(" : "," %><%= arg.name %>:");
|
123
|
+
<%= generate_build_input_code(escape_reserved_word(arg.camelize_name), arg.type) %>
|
124
|
+
<% end %>
|
125
|
+
<% unless field.optional_args.empty? %>
|
126
|
+
argsDef.define(new <%= field.classify_name %>Arguments(_queryBuilder));
|
127
|
+
<% end %>
|
128
|
+
_queryBuilder.append(')');
|
129
|
+
<% end %>
|
130
|
+
<% end %>
|
131
|
+
<% if field.subfields? %>
|
132
|
+
_queryBuilder.append('{');
|
133
|
+
queryDef.define(new <%= field.type.unwrap.name %>Query(_queryBuilder));
|
134
|
+
_queryBuilder.append('}');
|
135
|
+
<% end %>
|
136
|
+
return this;
|
137
|
+
}
|
138
|
+
<% end %>
|
139
|
+
<% unless type.object? %>
|
140
|
+
<% type.possible_types.each do |possible_type| %>
|
141
|
+
public <%= type.name %>Query on<%= possible_type.name %>(<%= possible_type.name %>QueryDefinition queryDef) {
|
142
|
+
startInlineFragment("<%= possible_type.name %>");
|
143
|
+
queryDef.define(new <%= possible_type.name %>Query(_queryBuilder));
|
144
|
+
_queryBuilder.append('}');
|
145
|
+
return this;
|
146
|
+
}
|
147
|
+
<% end %>
|
148
|
+
<% end %>
|
149
|
+
|
150
|
+
<% if schema.root_name?(type.name) %>
|
151
|
+
public String toString() {
|
152
|
+
return _queryBuilder.toString();
|
153
|
+
}
|
154
|
+
<% end %>
|
155
|
+
}
|
156
|
+
|
157
|
+
<% unless type.object? %>
|
158
|
+
public interface <%= type.name %> {
|
159
|
+
String getGraphQlTypeName();
|
160
|
+
<% type.fields.each do |field| %>
|
161
|
+
<%= java_output_type(field.type) %> get<%= field.classify_name %>();
|
162
|
+
<% end %>
|
163
|
+
}
|
164
|
+
<% end %>
|
165
|
+
|
166
|
+
<% class_name = type.object? ? type.name : "Unknown#{type.name}" %>
|
167
|
+
public static class <%= class_name %> extends AbstractResponse<<%= class_name %>> <%= java_implements(type) %>{
|
168
|
+
public <%= class_name %>() {
|
169
|
+
}
|
170
|
+
|
171
|
+
public <%= class_name %>(JsonObject fields) throws SchemaViolationError {
|
172
|
+
for (Map.Entry<String, JsonElement> field : fields.entrySet()) {
|
173
|
+
String key = field.getKey();
|
174
|
+
String fieldName = getFieldName(key);
|
175
|
+
switch (fieldName) {
|
176
|
+
<% type.fields.each do |field| %>
|
177
|
+
case "<%= field.name %>": {
|
178
|
+
<% generate_build_output_code("field.getValue()", field.type) do |statements, expr| %>
|
179
|
+
<%= statements %>
|
180
|
+
responseData.put(key, <%= expr %>);
|
181
|
+
<% end %>
|
182
|
+
break;
|
183
|
+
}
|
184
|
+
<% end %>
|
185
|
+
case "__typename": {
|
186
|
+
responseData.put(key, jsonAsString(field.getValue(), key));
|
187
|
+
break;
|
188
|
+
}
|
189
|
+
default: {
|
190
|
+
throw new SchemaViolationError(this, key, field.getValue());
|
191
|
+
}
|
192
|
+
}
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
<% if type.object? && type.implement?("Node") %>
|
197
|
+
public <%= class_name %>(<%= scalars['ID'].non_nullable_type %> id) {
|
198
|
+
this();
|
199
|
+
optimisticData.put("id", id);
|
200
|
+
}
|
201
|
+
<% end %>
|
202
|
+
|
203
|
+
<% if type_names_set.include?('Node') %>
|
204
|
+
public List<Node> getNodes() {
|
205
|
+
List<Node> children = new ArrayList<>();
|
206
|
+
<% if type.object? && type.implement?("Node") %>
|
207
|
+
children.add(this);
|
208
|
+
<% end %>
|
209
|
+
<% type.fields.each do |field| %>
|
210
|
+
<% next unless field.type.unwrap.object? %>
|
211
|
+
if (get<%= field.classify_name %>() != null) {
|
212
|
+
<% if field.type.unwrap_non_null.kind == 'LIST' %>
|
213
|
+
for (<%= java_output_type(field.type.unwrap) %> elem: get<%= field.classify_name %>()) {
|
214
|
+
children.addAll(elem.getNodes());
|
215
|
+
}
|
216
|
+
<% else %>
|
217
|
+
children.addAll(get<%= field.classify_name %>().getNodes());
|
218
|
+
<% end %>
|
219
|
+
}
|
220
|
+
<% end %>
|
221
|
+
return children;
|
222
|
+
}
|
223
|
+
<% end %>
|
224
|
+
|
225
|
+
<% if type.object? %>
|
226
|
+
public String getGraphQlTypeName() {
|
227
|
+
return "<%= type.name %>";
|
228
|
+
}
|
229
|
+
<% else %>
|
230
|
+
public static <%= type.name %> create(JsonObject fields) throws SchemaViolationError {
|
231
|
+
String typeName = fields.getAsJsonPrimitive("__typename").getAsString();
|
232
|
+
switch (typeName) {
|
233
|
+
<% type.possible_types.each do |possible_type| %>
|
234
|
+
case "<%= possible_type.name %>": {
|
235
|
+
return new <%= possible_type.name %>(fields);
|
236
|
+
}
|
237
|
+
<% end %>
|
238
|
+
default: {
|
239
|
+
return new <%= class_name %>(fields);
|
240
|
+
}
|
241
|
+
}
|
242
|
+
}
|
243
|
+
|
244
|
+
public String getGraphQlTypeName() {
|
245
|
+
return (String) get("__typename");
|
246
|
+
}
|
247
|
+
<% end %>
|
248
|
+
|
249
|
+
<% type.fields.each do |field| %>
|
250
|
+
<%= java_annotations(field) %>
|
251
|
+
public <%= java_output_type(field.type) %> get<%= field.classify_name %>() {
|
252
|
+
return (<%= java_output_type(field.type) %>) get("<%= field.name %>");
|
253
|
+
}
|
254
|
+
|
255
|
+
<% next if field.name == "id" && type.object? && type.implement?("Node") %>
|
256
|
+
public <%= class_name %> set<%= field.classify_name %>(<%= java_output_type(field.type) %> arg) {
|
257
|
+
optimisticData.put("<%= field.name %>", arg);
|
258
|
+
return this;
|
259
|
+
}
|
260
|
+
<% end %>
|
261
|
+
|
262
|
+
public boolean unwrapsToObject(String key) {
|
263
|
+
switch (key) {
|
264
|
+
<% type.fields.each do |field| %>
|
265
|
+
case "<%= field.name %>": return <%= field.type.unwrap.object? %>;
|
266
|
+
<% end %>
|
267
|
+
default: return false;
|
268
|
+
}
|
269
|
+
}
|
270
|
+
}
|
271
|
+
<% when 'INPUT_OBJECT' %>
|
272
|
+
public static class <%= type.name %> implements Serializable {
|
273
|
+
<% type.required_input_fields.each do |field| %>
|
274
|
+
private <%= java_input_type(field.type) %> <%= escape_reserved_word(field.camelize_name) %>;
|
275
|
+
<% end %>
|
276
|
+
<% type.optional_input_fields.each do |field| %>
|
277
|
+
private <%= java_input_type(field.type) %> <%= escape_reserved_word(field.camelize_name) %>;
|
278
|
+
<% end %>
|
279
|
+
|
280
|
+
<% unless type.required_input_fields.empty? %>
|
281
|
+
public <%= type.name %>(<%= type.required_input_fields.map{ |field| "#{java_input_type(field.type)} #{escape_reserved_word(field.camelize_name)}" }.join(', ') %>) {
|
282
|
+
<% type.required_input_fields.each do |field| %>
|
283
|
+
this.<%= escape_reserved_word(field.camelize_name) %> = <%= escape_reserved_word(field.camelize_name) %>;
|
284
|
+
<% end %>
|
285
|
+
}
|
286
|
+
<% end %>
|
287
|
+
|
288
|
+
<% type.required_input_fields.each do |field| %>
|
289
|
+
public <%= java_input_type(field.type) %> get<%= field.classify_name %>() {
|
290
|
+
return <%= escape_reserved_word(field.camelize_name) %>;
|
291
|
+
}
|
292
|
+
|
293
|
+
public <%= type.name %> set<%= field.classify_name %>(<%= java_input_type(field.type) %> <%= escape_reserved_word(field.camelize_name) %>) {
|
294
|
+
this.<%= escape_reserved_word(field.camelize_name) %> = <%= escape_reserved_word(field.camelize_name) %>;
|
295
|
+
return this;
|
296
|
+
}
|
297
|
+
<% end %>
|
298
|
+
<% type.optional_input_fields.each do |field| %>
|
299
|
+
public <%= java_input_type(field.type) %> get<%= field.classify_name %>() {
|
300
|
+
return <%= escape_reserved_word(field.camelize_name) %>;
|
301
|
+
}
|
302
|
+
|
303
|
+
public <%= type.name %> set<%= field.classify_name %>(<%= java_input_type(field.type) %> <%= escape_reserved_word(field.camelize_name) %>) {
|
304
|
+
this.<%= escape_reserved_word(field.camelize_name) %> = <%= escape_reserved_word(field.camelize_name) %>;
|
305
|
+
return this;
|
306
|
+
}
|
307
|
+
<% end %>
|
308
|
+
|
309
|
+
public void appendTo(StringBuilder _queryBuilder) {
|
310
|
+
String separator = "";
|
311
|
+
_queryBuilder.append('{');
|
312
|
+
<% type.required_input_fields.each do |field| %>
|
313
|
+
_queryBuilder.append(separator);
|
314
|
+
separator = ",";
|
315
|
+
_queryBuilder.append("<%= field.name %>:");
|
316
|
+
<%= generate_build_input_code(escape_reserved_word(field.camelize_name), field.type) %>
|
317
|
+
<% end %>
|
318
|
+
<% type.optional_input_fields.each do |field| %>
|
319
|
+
if (<%= escape_reserved_word(field.camelize_name) %> != null) {
|
320
|
+
_queryBuilder.append(separator);
|
321
|
+
separator = ",";
|
322
|
+
_queryBuilder.append("<%= field.name %>:");
|
323
|
+
<%= generate_build_input_code(escape_reserved_word(field.camelize_name), field.type) %>
|
324
|
+
}
|
325
|
+
<% end %>
|
326
|
+
_queryBuilder.append('}');
|
327
|
+
}
|
328
|
+
}
|
329
|
+
<% when 'ENUM' %>
|
330
|
+
public enum <%= type.name %> {
|
331
|
+
<% type.enum_values.each do |value| %>
|
332
|
+
<%= value.upcase_name %>,
|
333
|
+
<% end %>
|
334
|
+
UNKNOWN_VALUE;
|
335
|
+
|
336
|
+
public static <%= type.name %> fromGraphQl(String value) {
|
337
|
+
if (value == null) {
|
338
|
+
return null;
|
339
|
+
}
|
340
|
+
|
341
|
+
switch (value) {
|
342
|
+
<% type.enum_values.each do |value| %>
|
343
|
+
case "<%= value.name %>": {
|
344
|
+
return <%= value.upcase_name %>;
|
345
|
+
}
|
346
|
+
<% end %>
|
347
|
+
default: {
|
348
|
+
return UNKNOWN_VALUE;
|
349
|
+
}
|
350
|
+
}
|
351
|
+
}
|
352
|
+
public String toString() {
|
353
|
+
switch (this) {
|
354
|
+
<% type.enum_values.each do |value| %>
|
355
|
+
case <%= value.upcase_name %>: {
|
356
|
+
return "<%= value.name %>";
|
357
|
+
}
|
358
|
+
<% end %>
|
359
|
+
default: {
|
360
|
+
return "";
|
361
|
+
}
|
362
|
+
}
|
363
|
+
}
|
364
|
+
}
|
365
|
+
<% else %>
|
366
|
+
<% raise NotImplementedError, "unhandled #{type.kind} type #{type.name}" %>
|
367
|
+
<% end %>
|
368
|
+
<% end %>
|
369
|
+
}
|
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: graphql_java_gen
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dylan Thacker-Smith
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: graphql_schema
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.1.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.1.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.14'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.14'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '12.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '12.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.10'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.10'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: graphql
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.3'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.3'
|
83
|
+
description: Generates java code based on the GraphQL schema to provide type-safe
|
84
|
+
API for building GraphQL queries and using their responses.
|
85
|
+
email:
|
86
|
+
- gems@shopify.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- LICENSE.txt
|
92
|
+
- README.md
|
93
|
+
- codegen/lib/graphql_java_gen.rb
|
94
|
+
- codegen/lib/graphql_java_gen/annotation.rb
|
95
|
+
- codegen/lib/graphql_java_gen/reformatter.rb
|
96
|
+
- codegen/lib/graphql_java_gen/scalar.rb
|
97
|
+
- codegen/lib/graphql_java_gen/templates/APISchema.java.erb
|
98
|
+
- codegen/lib/graphql_java_gen/version.rb
|
99
|
+
homepage: https://github.com/Shopify/graphql_java_gen
|
100
|
+
licenses:
|
101
|
+
- MIT
|
102
|
+
metadata: {}
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- codegen/lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 2.1.0
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 2.6.10
|
120
|
+
signing_key:
|
121
|
+
specification_version: 4
|
122
|
+
summary: GraphQL java client code generator
|
123
|
+
test_files: []
|