graphql 1.8.0.pre3 → 1.8.0.pre4
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 +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
|