graphql 1.8.0.pre3 → 1.8.0.pre4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/railtie.rb +8 -4
- data/lib/graphql/schema/enum.rb +7 -12
- data/lib/graphql/schema/field.rb +34 -36
- data/lib/graphql/schema/field/dynamic_resolve.rb +4 -2
- data/lib/graphql/schema/input_object.rb +7 -11
- data/lib/graphql/schema/interface.rb +1 -1
- data/lib/graphql/schema/member/build_type.rb +16 -5
- data/lib/graphql/schema/member/has_fields.rb +6 -13
- data/lib/graphql/schema/object.rb +2 -2
- data/lib/graphql/upgrader/member.rb +94 -25
- data/lib/graphql/version.rb +1 -1
- data/spec/fixtures/upgrader/blame_range.original.rb +1 -1
- data/spec/fixtures/upgrader/blame_range.transformed.rb +0 -1
- data/spec/fixtures/upgrader/type_x.original.rb +19 -1
- data/spec/fixtures/upgrader/type_x.transformed.rb +11 -2
- data/spec/graphql/query/context_spec.rb +2 -1
- data/spec/graphql/schema/enum_spec.rb +1 -1
- data/spec/graphql/schema/field_spec.rb +11 -11
- data/spec/graphql/schema/object_spec.rb +19 -2
- data/spec/graphql/upgrader/member_spec.rb +57 -17
- data/spec/support/jazz.rb +16 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef7a7ef6e77dbf150ac668f99f293298e3764e9d
|
4
|
+
data.tar.gz: 702a3a337b3c53a813bd699637a93c985f7f5bf0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9e377b6787dda4fe97be9687545adb6f0fd9625df8f3233ee069ff70d96e660b3768bb58ef5b7ddbb330df3322dbe1aba23b13af53cd661dc69fe8e5d61a9e4
|
7
|
+
data.tar.gz: 628e7b0c786292b2009ce08b799c8c9db340a289ca3d3ac59b1cc9f454639cd321a327cefb9e77954311a1bd5994ffdd4ed5bd5f0715bbd38b5118345b861dfa
|
data/lib/graphql/railtie.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative './upgrader/member'
|
4
|
-
require_relative './upgrader/schema'
|
5
3
|
|
6
4
|
module GraphQL
|
7
5
|
class Railtie < Rails::Railtie
|
8
6
|
rake_tasks do
|
7
|
+
# Defer this so that you only need the `parser` gem when you _run_ the upgrader
|
8
|
+
def load_upgraders
|
9
|
+
require_relative './upgrader/member'
|
10
|
+
require_relative './upgrader/schema'
|
11
|
+
end
|
12
|
+
|
9
13
|
namespace :graphql do
|
10
14
|
task :upgrade, [:dir] do |t, args|
|
11
15
|
unless (dir = args[:dir])
|
@@ -82,7 +86,7 @@ module GraphQL
|
|
82
86
|
|
83
87
|
task :schema, [:schema_file] do |t, args|
|
84
88
|
schema_file = args.schema_file
|
85
|
-
|
89
|
+
load_upgraders
|
86
90
|
upgrader = GraphQL::Upgrader::Schema.new File.read(schema_file)
|
87
91
|
|
88
92
|
puts "- Transforming schema #{schema_file}"
|
@@ -91,7 +95,7 @@ module GraphQL
|
|
91
95
|
|
92
96
|
task :member, [:member_file] do |t, args|
|
93
97
|
member_file = args.member_file
|
94
|
-
|
98
|
+
load_upgraders
|
95
99
|
upgrader = GraphQL::Upgrader::Member.new File.read(member_file)
|
96
100
|
next unless upgrader.upgradeable?
|
97
101
|
|
data/lib/graphql/schema/enum.rb
CHANGED
@@ -33,20 +33,15 @@ module GraphQL
|
|
33
33
|
def value(graphql_name, description = nil, value: nil, deprecation_reason: nil)
|
34
34
|
graphql_name = graphql_name.to_s
|
35
35
|
value ||= graphql_name
|
36
|
-
own_values
|
36
|
+
own_values[graphql_name] = Value.new(graphql_name, description, value, deprecation_reason)
|
37
37
|
nil
|
38
38
|
end
|
39
39
|
|
40
|
-
# @return [
|
40
|
+
# @return [Hash<String => GraphQL::Schema::Enum::Value>] Possible values of this enum, keyed by name
|
41
41
|
def values
|
42
|
-
|
43
|
-
|
44
|
-
inherited_values.
|
45
|
-
if all_values.none? { |v| v.name == inherited_v.name }
|
46
|
-
all_values << inherited_v
|
47
|
-
end
|
48
|
-
end
|
49
|
-
all_values
|
42
|
+
inherited_values = superclass <= GraphQL::Schema::Enum ? superclass.values : {}
|
43
|
+
# Local values take precedence over inherited ones
|
44
|
+
inherited_values.merge(own_values)
|
50
45
|
end
|
51
46
|
|
52
47
|
# @return [GraphQL::EnumType]
|
@@ -55,7 +50,7 @@ module GraphQL
|
|
55
50
|
enum_type.name = graphql_name
|
56
51
|
enum_type.description = description
|
57
52
|
enum_type.introspection = introspection
|
58
|
-
values.each do |val|
|
53
|
+
values.each do |name, val|
|
59
54
|
enum_value = GraphQL::EnumType::EnumValue.new
|
60
55
|
enum_value.name = val.name
|
61
56
|
enum_value.description = val.description
|
@@ -69,7 +64,7 @@ module GraphQL
|
|
69
64
|
private
|
70
65
|
|
71
66
|
def own_values
|
72
|
-
@own_values ||=
|
67
|
+
@own_values ||= {}
|
73
68
|
end
|
74
69
|
end
|
75
70
|
end
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -11,9 +11,12 @@ module GraphQL
|
|
11
11
|
attr_reader :name
|
12
12
|
|
13
13
|
# @return [String]
|
14
|
-
|
14
|
+
attr_accessor :description
|
15
15
|
|
16
|
-
|
16
|
+
# @return [Hash{String => GraphQL::Schema::Argument}]
|
17
|
+
attr_reader :arguments
|
18
|
+
|
19
|
+
def initialize(name, return_type_expr = nil, desc = nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, connection: nil, max_page_size: nil, resolve: nil, introspection: false, extras: [], &definition_block)
|
17
20
|
if !(field || function)
|
18
21
|
if return_type_expr.nil?
|
19
22
|
raise ArgumentError, "missing positional argument `type`"
|
@@ -37,11 +40,29 @@ module GraphQL
|
|
37
40
|
@method = method
|
38
41
|
@return_type_expr = return_type_expr
|
39
42
|
@return_type_null = null
|
40
|
-
@args_block = args_block
|
41
43
|
@connection = connection
|
42
44
|
@max_page_size = max_page_size
|
43
45
|
@introspection = introspection
|
44
46
|
@extras = extras
|
47
|
+
@arguments = {}
|
48
|
+
|
49
|
+
if definition_block
|
50
|
+
instance_eval(&definition_block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# This is the `argument(...)` DSL for class-based field definitons
|
55
|
+
def argument(*args)
|
56
|
+
arg_defn = self.class.argument_class.new(*args)
|
57
|
+
arguments[arg_defn.name] = arg_defn
|
58
|
+
end
|
59
|
+
|
60
|
+
def description(text = nil)
|
61
|
+
if text
|
62
|
+
@description = text
|
63
|
+
else
|
64
|
+
@description
|
65
|
+
end
|
45
66
|
end
|
46
67
|
|
47
68
|
# @return [GraphQL::Field]
|
@@ -93,17 +114,20 @@ module GraphQL
|
|
93
114
|
field_defn.connection_max_page_size = @max_page_size
|
94
115
|
field_defn.introspection = @introspection
|
95
116
|
|
96
|
-
field_proxy = FieldProxy.new(field_defn, argument_class: self.class.argument_class)
|
97
117
|
# apply this first, so it can be overriden below
|
98
118
|
if connection
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
119
|
+
# TODO: this could be a bit weird, because these fields won't be present
|
120
|
+
# after initialization, only in the `to_graphql` response.
|
121
|
+
# This calculation _could_ be moved up if need be.
|
122
|
+
argument :after, "String", "Returns the elements in the list that come after the specified global ID.", required: false
|
123
|
+
argument :before, "String", "Returns the elements in the list that come before the specified global ID.", required: false
|
124
|
+
argument :first, "Int", "Returns the first _n_ elements from the list.", required: false
|
125
|
+
argument :last, "Int", "Returns the last _n_ elements from the list.", required: false
|
103
126
|
end
|
104
127
|
|
105
|
-
|
106
|
-
|
128
|
+
@arguments.each do |name, defn|
|
129
|
+
arg_graphql = defn.to_graphql
|
130
|
+
field_defn.arguments[arg_graphql.name] = arg_graphql
|
107
131
|
end
|
108
132
|
|
109
133
|
field_defn
|
@@ -120,32 +144,6 @@ module GraphQL
|
|
120
144
|
end
|
121
145
|
end
|
122
146
|
end
|
123
|
-
|
124
|
-
|
125
|
-
# This object exists only to be `instance_eval`'d
|
126
|
-
# when the `field(...)` method is called with a block.
|
127
|
-
# This object receives that block.
|
128
|
-
class FieldProxy
|
129
|
-
def initialize(field, argument_class:)
|
130
|
-
@field = field
|
131
|
-
@argument_class = argument_class
|
132
|
-
end
|
133
|
-
|
134
|
-
# This is the `argument(...)` DSL for class-based field definitons
|
135
|
-
def argument(*args)
|
136
|
-
arg = @argument_class.new(*args)
|
137
|
-
graphql_arg = arg.graphql_definition
|
138
|
-
@field.arguments[graphql_arg.name] = graphql_arg
|
139
|
-
end
|
140
|
-
|
141
|
-
def description(text)
|
142
|
-
if @field.description
|
143
|
-
fail "You're overriding the description of #{@field.name} in the provided block!"
|
144
|
-
else
|
145
|
-
@field.description = text
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
147
|
end
|
150
148
|
end
|
151
149
|
end
|
@@ -6,6 +6,7 @@ module GraphQL
|
|
6
6
|
class DynamicResolve
|
7
7
|
def initialize(method_name:, connection:, extras:)
|
8
8
|
@method_name = method_name
|
9
|
+
@method_sym = method_name.to_sym
|
9
10
|
@connection = connection
|
10
11
|
@extras = extras
|
11
12
|
end
|
@@ -15,8 +16,9 @@ module GraphQL
|
|
15
16
|
public_send_field(obj, @method_name, args, ctx)
|
16
17
|
elsif obj.object.respond_to?(@method_name)
|
17
18
|
public_send_field(obj.object, @method_name, args, ctx)
|
18
|
-
elsif obj.is_a?(Hash)
|
19
|
-
obj
|
19
|
+
elsif obj.object.is_a?(Hash)
|
20
|
+
inner_object = obj.object
|
21
|
+
inner_object[@method_name] || inner_object[@method_sym]
|
20
22
|
else
|
21
23
|
raise <<-ERR
|
22
24
|
Failed to implement #{ctx.irep_node.owner_type.name}.#{ctx.field.name}, tried:
|
@@ -19,34 +19,30 @@ module GraphQL
|
|
19
19
|
|
20
20
|
def argument(*args)
|
21
21
|
argument = GraphQL::Schema::Argument.new(*args)
|
22
|
-
own_arguments << argument
|
23
22
|
arg_name = argument.graphql_definition.name
|
23
|
+
own_arguments[arg_name] = argument
|
24
24
|
# Add a method access
|
25
25
|
define_method(Member::BuildType.underscore(arg_name)) do
|
26
26
|
@arguments.public_send(arg_name)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
# @return [Hash<String => GraphQL::Schema::Argument] Input fields on this input object, keyed by name.
|
30
31
|
def arguments
|
31
|
-
|
32
|
-
|
33
|
-
inherited_arguments.
|
34
|
-
if all_arguments.none? { |a| a.name == inherited_a.name }
|
35
|
-
all_arguments << inherited_a
|
36
|
-
end
|
37
|
-
end
|
38
|
-
all_arguments
|
32
|
+
inherited_arguments = (superclass <= GraphQL::Schema::InputObject ? superclass.arguments : {})
|
33
|
+
# Local definitions override inherited ones
|
34
|
+
inherited_arguments.merge(own_arguments)
|
39
35
|
end
|
40
36
|
|
41
37
|
def own_arguments
|
42
|
-
@own_arguments ||=
|
38
|
+
@own_arguments ||= {}
|
43
39
|
end
|
44
40
|
|
45
41
|
def to_graphql
|
46
42
|
type_defn = GraphQL::InputObjectType.new
|
47
43
|
type_defn.name = graphql_name
|
48
44
|
type_defn.description = description
|
49
|
-
arguments.each do |arg|
|
45
|
+
arguments.each do |name, arg|
|
50
46
|
type_defn.arguments[arg.graphql_definition.name] = arg.graphql_definition
|
51
47
|
end
|
52
48
|
# Make a reference to a classic-style Arguments class
|
@@ -20,7 +20,7 @@ module GraphQL
|
|
20
20
|
type_defn = GraphQL::InterfaceType.new
|
21
21
|
type_defn.name = graphql_name
|
22
22
|
type_defn.description = description
|
23
|
-
fields.each do |field_inst|
|
23
|
+
fields.each do |field_name, field_inst|
|
24
24
|
field_defn = field_inst.graphql_definition
|
25
25
|
type_defn.fields[field_defn.name] = field_defn
|
26
26
|
end
|
@@ -4,6 +4,8 @@ module GraphQL
|
|
4
4
|
class Member
|
5
5
|
# @api private
|
6
6
|
module BuildType
|
7
|
+
LIST_TYPE_ERROR = "Use an array of [T] or [T, null: true] for list types; other arrays are not supported"
|
8
|
+
|
7
9
|
module_function
|
8
10
|
# @param type_expr [String, Class, GraphQL::BaseType]
|
9
11
|
# @return [GraphQL::BaseType]
|
@@ -46,12 +48,21 @@ module GraphQL
|
|
46
48
|
when GraphQL::BaseType, GraphQL::Schema::LateBoundType
|
47
49
|
type_expr
|
48
50
|
when Array
|
49
|
-
|
50
|
-
|
51
|
+
case type_expr.length
|
52
|
+
when 1
|
53
|
+
list_type = true
|
54
|
+
# List members are required by default
|
55
|
+
parse_type(type_expr.first, null: false)
|
56
|
+
when 2
|
57
|
+
inner_type, nullable_option = type_expr
|
58
|
+
if nullable_option.keys != [:null] || nullable_option.values != [true]
|
59
|
+
raise ArgumentError, LIST_TYPE_ERROR
|
60
|
+
end
|
61
|
+
list_type = true
|
62
|
+
parse_type(inner_type, null: true)
|
63
|
+
else
|
64
|
+
raise ArgumentError, LIST_TYPE_ERROR
|
51
65
|
end
|
52
|
-
list_type = true
|
53
|
-
# List members are required by default
|
54
|
-
parse_type(type_expr.first, null: false)
|
55
66
|
when Class
|
56
67
|
if Class < GraphQL::Schema::Member
|
57
68
|
type_expr.graphql_definition
|
@@ -13,25 +13,18 @@ module GraphQL
|
|
13
13
|
nil
|
14
14
|
end
|
15
15
|
|
16
|
-
# @return [
|
16
|
+
# @return [Hash<String => GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields
|
17
17
|
def fields
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
inherited_fields.each do |inherited_f|
|
22
|
-
if all_fields.none? {|f| f.name == inherited_f.name}
|
23
|
-
all_fields << inherited_f
|
24
|
-
end
|
25
|
-
end
|
26
|
-
all_fields
|
18
|
+
inherited_fields = (superclass.is_a?(HasFields) ? superclass.fields : {})
|
19
|
+
# Local overrides take precedence over inherited fields
|
20
|
+
inherited_fields.merge(own_fields)
|
27
21
|
end
|
28
22
|
|
29
23
|
# Register this field with the class, overriding a previous one if needed
|
30
24
|
# @param field_defn [GraphQL::Schema::Field]
|
31
25
|
# @return [void]
|
32
26
|
def add_field(field_defn)
|
33
|
-
own_fields.
|
34
|
-
own_fields << field_defn
|
27
|
+
own_fields[field_defn.name] = field_defn
|
35
28
|
nil
|
36
29
|
end
|
37
30
|
|
@@ -48,7 +41,7 @@ module GraphQL
|
|
48
41
|
|
49
42
|
# @return [Array<GraphQL::Schema::Field>] Fields defined on this class _specifically_, not parent classes
|
50
43
|
def own_fields
|
51
|
-
@own_fields ||=
|
44
|
+
@own_fields ||= {}
|
52
45
|
end
|
53
46
|
end
|
54
47
|
end
|
@@ -17,7 +17,7 @@ module GraphQL
|
|
17
17
|
new_interfaces.each do |int|
|
18
18
|
if int.is_a?(Class) && int < GraphQL::Schema::Interface
|
19
19
|
# Add the graphql field defns
|
20
|
-
int.fields.each do |field|
|
20
|
+
int.fields.each do |_name, field|
|
21
21
|
add_field(field)
|
22
22
|
end
|
23
23
|
# And call the implemented hook
|
@@ -47,7 +47,7 @@ module GraphQL
|
|
47
47
|
obj_type.interfaces = interfaces
|
48
48
|
obj_type.introspection = introspection
|
49
49
|
|
50
|
-
fields.each do |field_inst|
|
50
|
+
fields.each do |field_name, field_inst|
|
51
51
|
field_defn = field_inst.to_graphql
|
52
52
|
obj_type.fields[field_defn.name] = field_defn
|
53
53
|
end
|
@@ -95,12 +95,18 @@ module GraphQL
|
|
95
95
|
# Remove newlines -- normalize the text for processing
|
96
96
|
class RemoveNewlinesTransform
|
97
97
|
def apply(input_text)
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
98
|
+
keep_looking = true
|
99
|
+
while keep_looking do
|
100
|
+
keep_looking = false
|
101
|
+
input_text = input_text.gsub(/(?<field>(?:field|connection|argument).*?,)\n(\s*)(?<next_line>.*)/) do
|
102
|
+
keep_looking = true
|
103
|
+
field = $~[:field].chomp
|
104
|
+
next_line = $~[:next_line]
|
105
|
+
|
106
|
+
"#{field} #{next_line}"
|
107
|
+
end
|
103
108
|
end
|
109
|
+
input_text
|
104
110
|
end
|
105
111
|
end
|
106
112
|
|
@@ -141,7 +147,7 @@ module GraphQL
|
|
141
147
|
) do
|
142
148
|
field = $~[:field]
|
143
149
|
block_contents = $~[:block_contents]
|
144
|
-
kwarg_value = $~[:kwarg_value]
|
150
|
+
kwarg_value = $~[:kwarg_value].strip
|
145
151
|
|
146
152
|
"#{field}, #{@kwarg}: #{kwarg_value} do#{block_contents}"
|
147
153
|
end
|
@@ -155,6 +161,28 @@ module GraphQL
|
|
155
161
|
end
|
156
162
|
end
|
157
163
|
|
164
|
+
# Find a keyword whose value is a string or symbol,
|
165
|
+
# and if the value is equivalent to the field name,
|
166
|
+
# remove the keyword altogether.
|
167
|
+
class RemoveRedundantKwargTransform < Transform
|
168
|
+
def initialize(kwarg:)
|
169
|
+
@kwarg = kwarg
|
170
|
+
@finder_pattern = /(field|connection|argument) :(?<name>[a-zA-Z_0-9]*).*#{@kwarg}: ['":](?<kwarg_value>[a-zA-Z_0-9]+)['"]?/
|
171
|
+
end
|
172
|
+
|
173
|
+
def apply(input_text)
|
174
|
+
if input_text =~ @finder_pattern
|
175
|
+
field_name = $~[:name]
|
176
|
+
kwarg_value = $~[:kwarg_value]
|
177
|
+
if field_name == kwarg_value
|
178
|
+
# It's redundant, remove it
|
179
|
+
input_text = input_text.sub(/, #{@kwarg}: ['":]#{kwarg_value}['"]?/, "")
|
180
|
+
end
|
181
|
+
end
|
182
|
+
input_text
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
158
186
|
# Take camelized field names and convert them to underscore case.
|
159
187
|
# (They'll be automatically camelized later.)
|
160
188
|
class UnderscoreizeFieldNameTransform < Transform
|
@@ -196,7 +224,13 @@ module GraphQL
|
|
196
224
|
|
197
225
|
input_text.match(/(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)/)
|
198
226
|
field_name = $~[:name]
|
199
|
-
|
227
|
+
begin
|
228
|
+
field_ast = Parser::CurrentRuby.parse(input_text)
|
229
|
+
rescue Parser::SyntaxError
|
230
|
+
puts "Error text:"
|
231
|
+
puts input_text
|
232
|
+
raise
|
233
|
+
end
|
200
234
|
processor = ResolveProcProcessor.new
|
201
235
|
processor.process(field_ast)
|
202
236
|
proc_body = input_text[processor.proc_start..processor.proc_end]
|
@@ -288,9 +322,14 @@ module GraphQL
|
|
288
322
|
) do
|
289
323
|
indent = $~[:indent]
|
290
324
|
interfaces = $~[:interfaces].split(',').map(&:strip).reject(&:empty?)
|
291
|
-
interfaces
|
292
|
-
|
293
|
-
|
325
|
+
# Preserve leading newlines before the `interfaces ...`
|
326
|
+
# call, but don't re-insert them between `implements` calls.
|
327
|
+
extra_leading_newlines = "\n" * (indent[/^\n*/].length - 1)
|
328
|
+
indent = indent.sub(/^\n*/m, "")
|
329
|
+
interfaces_calls = interfaces
|
330
|
+
.map { |interface| "\n#{indent}implements #{interface}" }
|
331
|
+
.join
|
332
|
+
extra_leading_newlines + interfaces_calls
|
294
333
|
end
|
295
334
|
end
|
296
335
|
end
|
@@ -298,23 +337,27 @@ module GraphQL
|
|
298
337
|
class UpdateMethodSignatureTransform < Transform
|
299
338
|
def apply(input_text)
|
300
339
|
input_text.scan(/(?:input_field|field|connection|argument) .*$/).each do |field|
|
301
|
-
matches = /(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)?, (?<return_type>[
|
340
|
+
matches = /(?<field_type>input_field|field|connection|argument) :(?<name>[a-zA-Z_0-9_]*)?, (?<return_type>([A-Za-z\[\]\.\!_0-9]|::|-> ?\{ ?| ?\})+)(?<remainder>( |,|$).*)/.match(field)
|
302
341
|
if matches
|
303
342
|
name = matches[:name]
|
304
343
|
return_type = matches[:return_type]
|
305
344
|
remainder = matches[:remainder]
|
306
345
|
field_type = matches[:field_type]
|
307
|
-
|
308
|
-
# This is a small bug in the regex. Ideally the `do` part would only be in the remainder.
|
309
|
-
with_block = remainder.gsub!(/\ do$/, '') || return_type.gsub!(/\ do$/, '')
|
346
|
+
with_block = remainder.gsub!(/\ do$/, '')
|
310
347
|
|
311
348
|
remainder.gsub! /,$/, ''
|
312
349
|
remainder.gsub! /^,/, ''
|
313
350
|
remainder.chomp!
|
314
351
|
|
315
|
-
|
316
|
-
|
317
|
-
|
352
|
+
if return_type
|
353
|
+
non_nullable = return_type.gsub! '!', ''
|
354
|
+
nullable = !non_nullable
|
355
|
+
return_type = normalize_type_expression(return_type)
|
356
|
+
return_type = return_type.gsub ',', ''
|
357
|
+
else
|
358
|
+
non_nullable = nil
|
359
|
+
nullable = nil
|
360
|
+
end
|
318
361
|
|
319
362
|
input_text.sub!(field) do
|
320
363
|
is_argument = ['argument', 'input_field'].include?(field_type)
|
@@ -325,15 +368,15 @@ module GraphQL
|
|
325
368
|
end
|
326
369
|
|
327
370
|
if is_argument
|
328
|
-
if
|
371
|
+
if nullable
|
329
372
|
f += ', required: false'
|
330
|
-
|
373
|
+
elsif non_nullable
|
331
374
|
f += ', required: true'
|
332
375
|
end
|
333
376
|
else
|
334
|
-
if
|
377
|
+
if nullable
|
335
378
|
f += ', null: true'
|
336
|
-
|
379
|
+
elsif non_nullable
|
337
380
|
f += ', null: false'
|
338
381
|
end
|
339
382
|
end
|
@@ -363,17 +406,29 @@ module GraphQL
|
|
363
406
|
|
364
407
|
# Remove redundant newlines, which may have trailing spaces
|
365
408
|
# Remove double newline after `do`
|
409
|
+
# Remove double newline before `end`
|
366
410
|
class RemoveExcessWhitespaceTransform < Transform
|
367
411
|
def apply(input_text)
|
368
412
|
input_text
|
369
|
-
.gsub(/\n{3,}/m, "\n")
|
370
|
-
.gsub(/do\n
|
413
|
+
.gsub(/\n{3,}/m, "\n\n")
|
414
|
+
.gsub(/do\n{2,}/m, "do\n")
|
415
|
+
.gsub(/\n{2,}(\s*)end/m, "\n\\1end")
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
# Skip this file if you see any `field`
|
420
|
+
# helpers with `null: true` or `null: false` keywords,
|
421
|
+
# because it's already been transformed
|
422
|
+
class SkipOnNullKeyword
|
423
|
+
def skip?(input_text)
|
424
|
+
input_text =~ /field.*null: (true|false)/
|
371
425
|
end
|
372
426
|
end
|
373
427
|
|
374
428
|
class Member
|
375
|
-
def initialize(member, type_transforms: DEFAULT_TYPE_TRANSFORMS, field_transforms: DEFAULT_FIELD_TRANSFORMS, clean_up_transforms: DEFAULT_CLEAN_UP_TRANSFORMS)
|
429
|
+
def initialize(member, skip: SkipOnNullKeyword, type_transforms: DEFAULT_TYPE_TRANSFORMS, field_transforms: DEFAULT_FIELD_TRANSFORMS, clean_up_transforms: DEFAULT_CLEAN_UP_TRANSFORMS)
|
376
430
|
@member = member
|
431
|
+
@skip = skip
|
377
432
|
@type_transforms = type_transforms
|
378
433
|
@field_transforms = field_transforms
|
379
434
|
@clean_up_transforms = clean_up_transforms
|
@@ -390,7 +445,11 @@ module GraphQL
|
|
390
445
|
PositionalTypeArgTransform,
|
391
446
|
ConfigurationToKwargTransform.new(kwarg: "property"),
|
392
447
|
ConfigurationToKwargTransform.new(kwarg: "description"),
|
448
|
+
ConfigurationToKwargTransform.new(kwarg: "deprecation_reason"),
|
449
|
+
ConfigurationToKwargTransform.new(kwarg: "hash_key"),
|
393
450
|
PropertyToMethodTransform,
|
451
|
+
RemoveRedundantKwargTransform.new(kwarg: "hash_key"),
|
452
|
+
RemoveRedundantKwargTransform.new(kwarg: "method"),
|
394
453
|
UnderscoreizeFieldNameTransform,
|
395
454
|
ResolveProcToMethodTransform,
|
396
455
|
UpdateMethodSignatureTransform,
|
@@ -402,8 +461,14 @@ module GraphQL
|
|
402
461
|
]
|
403
462
|
|
404
463
|
def upgrade
|
464
|
+
type_source = @member.dup
|
465
|
+
should_skip = @skip.new.skip?(type_source)
|
466
|
+
# return the unmodified code
|
467
|
+
if should_skip
|
468
|
+
return type_source
|
469
|
+
end
|
405
470
|
# Transforms on type defn code:
|
406
|
-
type_source = apply_transforms(
|
471
|
+
type_source = apply_transforms(type_source, @type_transforms)
|
407
472
|
# Transforms on each field:
|
408
473
|
field_sources = find_fields(type_source)
|
409
474
|
field_sources.each do |field_source|
|
@@ -464,6 +529,10 @@ module GraphQL
|
|
464
529
|
# - use `gsub` after performing the transformation.
|
465
530
|
field_sources.uniq!
|
466
531
|
field_sources
|
532
|
+
rescue Parser::SyntaxError
|
533
|
+
puts "Error Source:"
|
534
|
+
puts type_source
|
535
|
+
raise
|
467
536
|
end
|
468
537
|
|
469
538
|
class FieldFinder < Parser::AST::Processor
|
data/lib/graphql/version.rb
CHANGED
@@ -12,11 +12,14 @@ module Platform
|
|
12
12
|
interfaces [GraphQL::Relay::Node.interface]
|
13
13
|
|
14
14
|
field :f1, !Objects::O1, "The x being y."
|
15
|
-
field :f2, !Enums::E1, "x for the y.",
|
15
|
+
field :f2, !Enums::E1, "x for the y.",
|
16
|
+
property: :field_2
|
16
17
|
field :f3, Enums::E2, "x for y."
|
17
18
|
field :details, types.String, "Details."
|
18
19
|
|
19
20
|
field :f4, !Objects::O2, "x as a y inside the z." do
|
21
|
+
argument :a1, !Inputs::I1
|
22
|
+
|
20
23
|
resolve ->(obj_x, arguments, context) do
|
21
24
|
Class1.new(
|
22
25
|
a: Class2.new(
|
@@ -30,6 +33,21 @@ module Platform
|
|
30
33
|
)
|
31
34
|
end
|
32
35
|
end
|
36
|
+
|
37
|
+
field :f5, -> { !types.String } do
|
38
|
+
description "The thing"
|
39
|
+
property :custom_property
|
40
|
+
visibility :custom_value
|
41
|
+
end
|
42
|
+
|
43
|
+
field :f6, -> { !types.String } do
|
44
|
+
description "The thing"
|
45
|
+
property :custom_property
|
46
|
+
visibility :custom_value
|
47
|
+
end
|
48
|
+
|
49
|
+
field :f7, field: SomeField
|
50
|
+
field :f8, function: SomeFunction
|
33
51
|
end
|
34
52
|
end
|
35
53
|
end
|
@@ -16,9 +16,11 @@ module Platform
|
|
16
16
|
field :f3, Enums::E2, "x for y.", null: true
|
17
17
|
field :details, String, "Details.", null: true
|
18
18
|
|
19
|
-
field :f4, Objects::O2, "x as a y inside the z.", null: false
|
19
|
+
field :f4, Objects::O2, "x as a y inside the z.", null: false do
|
20
|
+
argument :a1, Inputs::I1, required: true
|
21
|
+
end
|
20
22
|
|
21
|
-
def f4
|
23
|
+
def f4(**arguments)
|
22
24
|
Class1.new(
|
23
25
|
a: Class2.new(
|
24
26
|
b: @object.b_1,
|
@@ -30,6 +32,13 @@ module Platform
|
|
30
32
|
)
|
31
33
|
)
|
32
34
|
end
|
35
|
+
|
36
|
+
field :f5, String, visibility: :custom_value, method: :custom_property, description: "The thing", null: false
|
37
|
+
|
38
|
+
field :f6, String, visibility: :custom_value, method: :custom_property, description: "The thing", null: false
|
39
|
+
|
40
|
+
field :f7, field: SomeField
|
41
|
+
field :f8, function: SomeFunction
|
33
42
|
end
|
34
43
|
end
|
35
44
|
end
|
@@ -256,8 +256,9 @@ TABLE
|
|
256
256
|
}'
|
257
257
|
res = Jazz::Schema.execute(query_str, context: { magic_key: :ignored, normal_key: "normal_value" })
|
258
258
|
expected_values = ["custom_method", "magic_value", "normal_value"]
|
259
|
+
expected_values_with_nil = expected_values + [nil]
|
259
260
|
assert_equal expected_values, res["data"]["inspectContext"]
|
260
|
-
assert_equal
|
261
|
+
assert_equal expected_values_with_nil, res["data"]["find"]["inspectContext"]
|
261
262
|
end
|
262
263
|
end
|
263
264
|
end
|
@@ -21,7 +21,7 @@ describe GraphQL::Schema::Enum do
|
|
21
21
|
# values were inherited without modifying the parent
|
22
22
|
assert_equal 6, enum.values.size
|
23
23
|
assert_equal 7, new_enum.values.size
|
24
|
-
perc_value = new_enum.values
|
24
|
+
perc_value = new_enum.values["PERCUSSION"]
|
25
25
|
assert_equal "new description", perc_value.description
|
26
26
|
end
|
27
27
|
end
|
@@ -4,7 +4,7 @@ require "spec_helper"
|
|
4
4
|
describe GraphQL::Schema::Field do
|
5
5
|
describe "graphql definition" do
|
6
6
|
let(:object_class) { Jazz::Query }
|
7
|
-
let(:field) { object_class.fields
|
7
|
+
let(:field) { object_class.fields["inspect_input"] }
|
8
8
|
|
9
9
|
it "uses the argument class" do
|
10
10
|
arg_defn = field.graphql_definition.arguments.values.first
|
@@ -15,18 +15,18 @@ describe GraphQL::Schema::Field do
|
|
15
15
|
assert_equal 'inspectInput', field.graphql_definition.name
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
Class.new(Jazz::BaseObject) do
|
22
|
-
graphql_name "JustAName"
|
18
|
+
it "accepts a block for definition" do
|
19
|
+
object = Class.new(Jazz::BaseObject) do
|
20
|
+
graphql_name "JustAName"
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end.to_graphql
|
22
|
+
field :test, String, null: true do
|
23
|
+
argument :test, String, required: true
|
24
|
+
description "A Description."
|
28
25
|
end
|
29
|
-
end
|
26
|
+
end.to_graphql
|
27
|
+
|
28
|
+
assert_equal "test", object.fields["test"].arguments["test"].name
|
29
|
+
assert_equal "A Description.", object.fields["test"].description
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -24,9 +24,9 @@ describe GraphQL::Schema::Object do
|
|
24
24
|
# inherited interfaces are present
|
25
25
|
assert_equal 2, new_object_class.interfaces.size
|
26
26
|
# The new field is present
|
27
|
-
assert new_object_class.fields.
|
27
|
+
assert new_object_class.fields.key?("newField")
|
28
28
|
# The overridden field is present:
|
29
|
-
name_field = new_object_class.fields
|
29
|
+
name_field = new_object_class.fields["name"]
|
30
30
|
assert_equal "The new description", name_field.description
|
31
31
|
end
|
32
32
|
|
@@ -42,6 +42,23 @@ describe GraphQL::Schema::Object do
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
describe "wrapping a Hash" do
|
46
|
+
it "automatically looks up symbol and string keys" do
|
47
|
+
query_str = <<-GRAPHQL
|
48
|
+
{
|
49
|
+
hashyEnsemble {
|
50
|
+
name
|
51
|
+
musicians { name }
|
52
|
+
}
|
53
|
+
}
|
54
|
+
GRAPHQL
|
55
|
+
res = Jazz::Schema.execute(query_str)
|
56
|
+
ensemble = res["data"]["hashyEnsemble"]
|
57
|
+
assert_equal "The Grateful Dead", ensemble["name"]
|
58
|
+
assert_equal ["Jerry Garcia"], ensemble["musicians"].map { |m| m["name"] }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
45
62
|
describe ".to_graphql_type" do
|
46
63
|
let(:obj_type) { Jazz::Ensemble.to_graphql }
|
47
64
|
it "returns a matching GraphQL::ObjectType" do
|
@@ -17,17 +17,52 @@ describe GraphQL::Upgrader::Member do
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
describe "property / method upgrade" do
|
21
|
+
it 'upgrades the property definition to method' do
|
22
|
+
old = %{field :name, String, property: :full_name}
|
23
|
+
new = %{field :name, String, method: :full_name, null: true}
|
23
24
|
|
24
|
-
|
25
|
+
assert_equal new, upgrade(old)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'upgrades the property definition in a block to method' do
|
29
|
+
old = %{field :name, String do\n property :full_name\nend}
|
30
|
+
new = %{field :name, String, method: :full_name, null: true}
|
31
|
+
assert_equal new, upgrade(old)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "removes property when redundant" do
|
35
|
+
old = %{field :name, String do\n property "name" \nend}
|
36
|
+
new = %{field :name, String, null: true}
|
37
|
+
assert_equal new, upgrade(old)
|
38
|
+
|
39
|
+
old = %{field :name, String, property: :name}
|
40
|
+
new = %{field :name, String, null: true}
|
41
|
+
assert_equal new, upgrade(old)
|
42
|
+
|
43
|
+
end
|
25
44
|
end
|
26
45
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
46
|
+
describe "hash_key" do
|
47
|
+
it "it moves configuration to kwarg" do
|
48
|
+
old = %{field :name, String do\n hash_key :full_name\nend}
|
49
|
+
new = %{field :name, String, hash_key: :full_name, null: true}
|
50
|
+
assert_equal new, upgrade(old)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "removes it if it's redundant" do
|
54
|
+
old = %{field :name, String do\n hash_key :name\nend}
|
55
|
+
new = %{field :name, String, null: true}
|
56
|
+
assert_equal new, upgrade(old)
|
57
|
+
|
58
|
+
old = %{field :name, String, hash_key: :name}
|
59
|
+
new = %{field :name, String, null: true}
|
60
|
+
assert_equal new, upgrade(old)
|
61
|
+
|
62
|
+
old = %{field :name, String do\n hash_key "name"\nend}
|
63
|
+
new = %{field :name, String, null: true}
|
64
|
+
assert_equal new, upgrade(old)
|
65
|
+
end
|
31
66
|
end
|
32
67
|
|
33
68
|
describe 'name' do
|
@@ -224,8 +259,8 @@ describe GraphQL::Upgrader::Member do
|
|
224
259
|
new = %{field :name, String, null: false}
|
225
260
|
assert_equal new, upgrade(old)
|
226
261
|
|
227
|
-
old = %{field :name, !types.String, "description", method: :
|
228
|
-
new = %{field :name, String, "description", method: :
|
262
|
+
old = %{field :name, !types.String, "description", method: :name_full}
|
263
|
+
new = %{field :name, String, "description", method: :name_full, null: false}
|
229
264
|
assert_equal new, upgrade(old)
|
230
265
|
|
231
266
|
old = %{field :name, -> { !types.String }}
|
@@ -347,8 +382,7 @@ describe GraphQL::Upgrader::Member do
|
|
347
382
|
property: :example_field?
|
348
383
|
}
|
349
384
|
new = %{
|
350
|
-
field :is_example_field, Boolean, null: true
|
351
|
-
method: :example_field?
|
385
|
+
field :is_example_field, Boolean, method: :example_field?, null: true
|
352
386
|
}
|
353
387
|
|
354
388
|
assert_equal new, upgrade(old)
|
@@ -362,8 +396,7 @@ describe GraphQL::Upgrader::Member do
|
|
362
396
|
property: :example_connections
|
363
397
|
}
|
364
398
|
new = %{
|
365
|
-
field :example_connection, ExampleConnectionType, null: true, connection: true
|
366
|
-
method: :example_connections
|
399
|
+
field :example_connection, ExampleConnectionType, method: :example_connections, null: true, connection: true
|
367
400
|
}
|
368
401
|
|
369
402
|
assert_equal new, upgrade(old)
|
@@ -416,19 +449,26 @@ describe GraphQL::Upgrader::Member do
|
|
416
449
|
}
|
417
450
|
|
418
451
|
type_transforms.unshift(ActiveRecordTypeToClassTransform)
|
419
|
-
|
452
|
+
field_transforms = GraphQL::Upgrader::Member::DEFAULT_FIELD_TRANSFORMS
|
453
|
+
field_transforms.unshift(GraphQL::Upgrader::ConfigurationToKwargTransform.new(kwarg: "visibility"))
|
454
|
+
upgrader = GraphQL::Upgrader::Member.new(original_text, type_transforms: type_transforms, field_transforms: field_transforms)
|
420
455
|
upgrader.upgrade
|
421
456
|
end
|
422
457
|
|
423
458
|
original_files = Dir.glob("spec/fixtures/upgrader/*.original.rb")
|
424
459
|
original_files.each do |original_file|
|
425
460
|
transformed_file = original_file.sub(".original.", ".transformed.")
|
461
|
+
original_text = File.read(original_file)
|
462
|
+
expected_text = File.read(transformed_file)
|
426
463
|
it "transforms #{original_file} -> #{transformed_file}" do
|
427
|
-
original_text = File.read(original_file)
|
428
|
-
expected_text = File.read(transformed_file)
|
429
464
|
transformed_text = custom_upgrade(original_text)
|
430
465
|
assert_equal(expected_text, transformed_text)
|
431
466
|
end
|
467
|
+
|
468
|
+
it "is idempotent on #{transformed_file}" do
|
469
|
+
retransformed_text = custom_upgrade(expected_text)
|
470
|
+
assert_equal(expected_text, retransformed_text)
|
471
|
+
end
|
432
472
|
end
|
433
473
|
end
|
434
474
|
end
|
data/spec/support/jazz.rb
CHANGED
@@ -201,13 +201,15 @@ module Jazz
|
|
201
201
|
description "An object played in order to produce music"
|
202
202
|
end
|
203
203
|
field :favorite_key, Key, null: true
|
204
|
-
|
204
|
+
# Test lists with nullable members:
|
205
|
+
field :inspect_context, [String, null: true], null: false
|
205
206
|
|
206
207
|
def inspect_context
|
207
208
|
[
|
208
209
|
@context.custom_method,
|
209
210
|
@context[:magic_key],
|
210
|
-
@context[:normal_key]
|
211
|
+
@context[:normal_key],
|
212
|
+
nil,
|
211
213
|
]
|
212
214
|
end
|
213
215
|
end
|
@@ -272,6 +274,7 @@ module Jazz
|
|
272
274
|
# For asserting that the object is initialized once:
|
273
275
|
field :object_id, Integer, null: false
|
274
276
|
field :inspect_context, [String], null: false
|
277
|
+
field :hashyEnsemble, Ensemble, null: false
|
275
278
|
|
276
279
|
def ensembles
|
277
280
|
Models.data["Ensemble"]
|
@@ -317,6 +320,17 @@ module Jazz
|
|
317
320
|
@context[:normal_key]
|
318
321
|
]
|
319
322
|
end
|
323
|
+
|
324
|
+
def hashy_ensemble
|
325
|
+
# Both string and symbol keys are supported:
|
326
|
+
|
327
|
+
{
|
328
|
+
name: "The Grateful Dead",
|
329
|
+
"musicians" => [
|
330
|
+
OpenStruct.new(name: "Jerry Garcia"),
|
331
|
+
],
|
332
|
+
}
|
333
|
+
end
|
320
334
|
end
|
321
335
|
|
322
336
|
class EnsembleInput < GraphQL::Schema::InputObject
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.8.0.
|
4
|
+
version: 1.8.0.pre4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|