graphql_migrate_execution 0.0.2 → 1.0.1
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/bin/graphql_migrate_execution +5 -6
- data/lib/graphql_migrate_execution/action.rb +33 -24
- data/lib/graphql_migrate_execution/dataloader_all.rb +39 -14
- data/lib/graphql_migrate_execution/dataloader_batch.rb +66 -4
- data/lib/graphql_migrate_execution/dataloader_manual.rb +2 -5
- data/lib/graphql_migrate_execution/dataloader_shorthand.rb +20 -7
- data/lib/graphql_migrate_execution/do_nothing.rb +1 -1
- data/lib/graphql_migrate_execution/fallback_value.rb +39 -0
- data/lib/graphql_migrate_execution/field_definition.rb +32 -4
- data/lib/graphql_migrate_execution/hash_key.rb +12 -0
- data/lib/graphql_migrate_execution/implicit.rb +22 -6
- data/lib/graphql_migrate_execution/migration.rb +45 -0
- data/lib/graphql_migrate_execution/not_implemented.rb +1 -1
- data/lib/graphql_migrate_execution/resolve_batch.rb +16 -0
- data/lib/graphql_migrate_execution/resolve_each.rb +7 -7
- data/lib/graphql_migrate_execution/resolve_static.rb +8 -7
- data/lib/graphql_migrate_execution/resolver_method.rb +80 -5
- data/lib/graphql_migrate_execution/strategy.rb +111 -25
- data/lib/graphql_migrate_execution/type_definition.rb +14 -1
- data/lib/graphql_migrate_execution/unsupported_current_path.rb +8 -0
- data/lib/graphql_migrate_execution/unsupported_extra.rb +8 -0
- data/lib/graphql_migrate_execution/version.rb +1 -1
- data/lib/graphql_migrate_execution/visitor.rb +54 -18
- data/lib/graphql_migrate_execution.rb +7 -30
- data/readme.md +158 -4
- metadata +8 -5
- data/lib/graphql_migrate_execution/add_future.rb +0 -9
- data/lib/graphql_migrate_execution/analyze.rb +0 -30
- data/lib/graphql_migrate_execution/remove_legacy.rb +0 -9
|
@@ -15,13 +15,19 @@ module GraphqlMigrateExecution
|
|
|
15
15
|
@calls_class = false
|
|
16
16
|
@calls_dataloader = false
|
|
17
17
|
@dataloader_call = false
|
|
18
|
+
@uses_current_path = false
|
|
19
|
+
@dataload_association = nil
|
|
20
|
+
@dataload_record = nil
|
|
21
|
+
@dataload_record_using = nil
|
|
22
|
+
@dataload_record_find_by = nil
|
|
23
|
+
@return_expressions = nil
|
|
18
24
|
end
|
|
19
25
|
|
|
20
26
|
attr_reader :name, :node, :parameter_names, :self_sends
|
|
21
27
|
|
|
22
|
-
attr_reader :source_class_node, :source_arg_nodes, :load_arg_node, :dataload_association
|
|
28
|
+
attr_reader :source_class_node, :source_arg_nodes, :load_arg_node, :dataload_association, :dataload_record, :dataload_record_using, :dataload_record_find_by
|
|
23
29
|
|
|
24
|
-
attr_accessor :calls_object, :calls_context, :calls_class, :calls_dataloader
|
|
30
|
+
attr_accessor :calls_object, :calls_context, :calls_class, :calls_dataloader, :uses_current_path
|
|
25
31
|
|
|
26
32
|
attr_accessor :dataloader_call
|
|
27
33
|
|
|
@@ -42,6 +48,7 @@ module GraphqlMigrateExecution
|
|
|
42
48
|
calls_to_self.delete(:dataload_association)
|
|
43
49
|
calls_to_self.delete(:dataload_record)
|
|
44
50
|
calls_to_self.delete(:dataload)
|
|
51
|
+
calls_to_self.delete(:dataload_all)
|
|
45
52
|
|
|
46
53
|
# Global-ish methods:
|
|
47
54
|
calls_to_self.delete(:raise)
|
|
@@ -67,6 +74,22 @@ module GraphqlMigrateExecution
|
|
|
67
74
|
assoc_sym = assoc_arg.unescaped.to_sym
|
|
68
75
|
@dataload_association = assoc_sym == name ? true : assoc_sym
|
|
69
76
|
end
|
|
77
|
+
when :dataload_record
|
|
78
|
+
if (record_args = call_node.arguments.arguments) &&
|
|
79
|
+
(record_arg = record_args.first) &&
|
|
80
|
+
(record_arg.is_a?(Prism::ConstantReadNode) || record_arg.is_a?(Prism::ConstantPathNode)) &&
|
|
81
|
+
(using_arg = record_args[1]) &&
|
|
82
|
+
# Must be `object.{something}`
|
|
83
|
+
(using_arg.is_a?(Prism::CallNode)) &&
|
|
84
|
+
(using_arg.receiver.is_a?(Prism::CallNode) && using_arg.receiver.name == :object)
|
|
85
|
+
@dataload_record = record_arg.full_name
|
|
86
|
+
@dataload_record_using = using_arg.name
|
|
87
|
+
|
|
88
|
+
if (kwargs = record_args.last).is_a?(Prism::KeywordHashNode) && (find_by_kwarg = kwargs.elements.find { |el| el.key.is_a?(Prism::SymbolNode) && el.key.unescaped == "find_by" })
|
|
89
|
+
find_by_node = find_by_kwarg.value
|
|
90
|
+
@dataload_record_find_by = find_by_node.unescaped.to_sym # Assumes a SymbolNode
|
|
91
|
+
end
|
|
92
|
+
end
|
|
70
93
|
else
|
|
71
94
|
if (source_call = call_node.receiver) # eg dataloader.with(...).load(...)
|
|
72
95
|
@source_class_node = source_call.arguments.arguments.first
|
|
@@ -78,15 +101,16 @@ module GraphqlMigrateExecution
|
|
|
78
101
|
input_is_object = @load_arg_node.is_a?(Prism::CallNode) && @load_arg_node.name == :object
|
|
79
102
|
# Guess whether these args are free of runtime context:
|
|
80
103
|
shortcutable_source_args = @source_arg_nodes && (@source_arg_nodes.empty? || (@source_arg_nodes.all? { |a| Visitor.constant_node?(a) }))
|
|
81
|
-
|
|
104
|
+
source_ref_is_constant = @source_class_node.is_a?(Prism::ConstantPathNode) || @source_class_node.is_a?(Prism::ConstantReadNode)
|
|
105
|
+
if source_ref_is_constant && shortcutable_source_args && input_is_object
|
|
82
106
|
DataloaderShorthand
|
|
83
107
|
else
|
|
84
108
|
case call_node.name
|
|
85
109
|
when :load, :request, :dataload
|
|
86
110
|
DataloaderAll
|
|
87
|
-
when :load_all, :request_all, :
|
|
111
|
+
when :load_all, :request_all, :dataload_all
|
|
88
112
|
DataloaderBatch
|
|
89
|
-
when :dataload_association
|
|
113
|
+
when :dataload_association, :dataload_record
|
|
90
114
|
DataloaderShorthand
|
|
91
115
|
else
|
|
92
116
|
DataloaderManual
|
|
@@ -101,5 +125,56 @@ module GraphqlMigrateExecution
|
|
|
101
125
|
NotImplemented
|
|
102
126
|
end
|
|
103
127
|
end
|
|
128
|
+
|
|
129
|
+
def returns_hash?
|
|
130
|
+
return_expressions.all? { |exp_node| exp_node.is_a?(Prism::HashNode) || (exp_node.is_a?(Prism::CallNode) && exp_node.name == :new && exp_node.receiver.is_a?(Prism::ConstantReadNode) && exp_node.receiver.name == :Hash) }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def return_expressions
|
|
134
|
+
if @return_expressions.nil?
|
|
135
|
+
@return_expressions = []
|
|
136
|
+
find_return_expressions(@node)
|
|
137
|
+
end
|
|
138
|
+
@return_expressions
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def returns_string_hash?
|
|
142
|
+
return_expressions.any? { |exp_node| exp_node.is_a?(Prism::HashNode) && exp_node.elements.size > 0 && exp_node.elements.all? { |el| el.key.is_a?(Prism::StringNode) } }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
private
|
|
146
|
+
|
|
147
|
+
def find_return_expressions(node)
|
|
148
|
+
case node
|
|
149
|
+
when Prism::DefNode
|
|
150
|
+
find_return_expressions(node.body)
|
|
151
|
+
when Prism::StatementsNode
|
|
152
|
+
find_return_expressions(node.body.last)
|
|
153
|
+
when Prism::IfNode # TODO else, `?`, case
|
|
154
|
+
find_return_expressions(node.statements)
|
|
155
|
+
find_return_expressions(node.subsequent)
|
|
156
|
+
when Prism::ElseNode, Prism::WhenNode
|
|
157
|
+
find_return_expressions(node.statements)
|
|
158
|
+
when Prism::UnlessNode
|
|
159
|
+
find_return_expressions(node.statements)
|
|
160
|
+
find_return_expressions(node.else_clause)
|
|
161
|
+
when Prism::CaseNode
|
|
162
|
+
node.conditions.each do |cond_node|
|
|
163
|
+
find_return_expressions(cond_node)
|
|
164
|
+
end
|
|
165
|
+
when Prism::ReturnNode
|
|
166
|
+
find_return_expressions(node.arguments.first)
|
|
167
|
+
when Prism::LocalVariableReadNode
|
|
168
|
+
if (lv_write_node = @node.body.body.find { |n| n.is_a?(Prism::LocalVariableWriteNode) && n.name == node.name })
|
|
169
|
+
find_return_expressions(lv_write_node.value)
|
|
170
|
+
else
|
|
171
|
+
# Couldn't find assignment :'(
|
|
172
|
+
@return_expressions << node
|
|
173
|
+
end
|
|
174
|
+
else
|
|
175
|
+
# This is an expression that produces a return value
|
|
176
|
+
@return_expressions << node
|
|
177
|
+
end
|
|
178
|
+
end
|
|
104
179
|
end
|
|
105
180
|
end
|
|
@@ -1,66 +1,152 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require_relative "./action"
|
|
3
|
+
|
|
2
4
|
module GraphqlMigrateExecution
|
|
3
5
|
class Strategy
|
|
4
|
-
|
|
6
|
+
include Action::Colorize
|
|
7
|
+
def initialize(action, field_definitions)
|
|
8
|
+
@action = action
|
|
9
|
+
@migration = action.migration
|
|
10
|
+
@filepath = action.filepath
|
|
11
|
+
@action_method = @migration.action_method
|
|
12
|
+
@message = action.message
|
|
13
|
+
@result_source = action.result_source
|
|
14
|
+
@field_definitions = field_definitions
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def migrate(field_definition)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def cleanup(field_definition)
|
|
5
21
|
end
|
|
6
22
|
|
|
7
|
-
def
|
|
23
|
+
def run
|
|
24
|
+
case @action_method
|
|
25
|
+
when :analyze
|
|
26
|
+
@message << "\n#{colorize("#{colorize(self.class.strategy_name, self.class.color)} (#{@field_definitions.size})", :BOLD)}:\n"
|
|
27
|
+
max_path = @field_definitions.map { |f| f.path.size }.max + 2
|
|
28
|
+
@field_definitions.each do |field_defn|
|
|
29
|
+
name = field_defn.path.ljust(max_path)
|
|
30
|
+
@message << "\n - #{name} (#{field_defn.resolve_mode.inspect} -> #{field_defn.resolve_mode_key.inspect}) @ #{@filepath}:#{field_defn.source_line}"
|
|
31
|
+
end
|
|
32
|
+
@message << "\n"
|
|
33
|
+
when :migrate, :cleanup
|
|
34
|
+
indent_size = @action.strategy_name_padding + 1
|
|
35
|
+
indent = " " * indent_size
|
|
36
|
+
indent2_size = @action.field_name_padding
|
|
37
|
+
@message << "\n#{colorize(self.class.strategy_name.ljust(indent_size), self.class.color)}"
|
|
38
|
+
first = true
|
|
39
|
+
@field_definitions.each do |field_defn|
|
|
40
|
+
@message << "#{first ? "" : "#{indent}"}#{field_defn.path.ljust(indent2_size)} @ #{@filepath}:#{field_defn.source_line}\n"
|
|
41
|
+
first = false
|
|
42
|
+
public_send(@action_method, field_defn)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.strategy_name
|
|
48
|
+
name.split("::").last
|
|
8
49
|
end
|
|
9
50
|
|
|
10
51
|
class << self
|
|
11
52
|
attr_accessor :color
|
|
12
53
|
end
|
|
13
54
|
|
|
55
|
+
# These methods conflict with built-in class methods,
|
|
56
|
+
# so when migrating them, prefix them.
|
|
57
|
+
METHODS_TO_RENAME = [
|
|
58
|
+
"name",
|
|
59
|
+
"fields",
|
|
60
|
+
"arguments",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
def self.prefix_if_necessary(method_name)
|
|
64
|
+
name_s = method_name.to_s
|
|
65
|
+
if METHODS_TO_RENAME.include?(name_s)
|
|
66
|
+
"resolve_#{method_name}"
|
|
67
|
+
else
|
|
68
|
+
name_s
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
14
72
|
private
|
|
15
73
|
|
|
16
|
-
def inject_resolve_keyword(
|
|
74
|
+
def inject_resolve_keyword(field_definition, keyword)
|
|
17
75
|
value = field_definition.future_resolve_shorthand.inspect
|
|
18
|
-
inject_field_keyword(
|
|
76
|
+
inject_field_keyword(field_definition, keyword, value)
|
|
19
77
|
end
|
|
20
78
|
|
|
21
|
-
def inject_field_keyword(
|
|
79
|
+
def inject_field_keyword(field_definition, keyword, value)
|
|
22
80
|
field_definition_source = field_definition.source
|
|
23
|
-
|
|
24
|
-
|
|
81
|
+
pair = "#{keyword}: #{value}"
|
|
82
|
+
if field_definition_source.include?(pair)
|
|
83
|
+
# Pass, don't re-add it
|
|
84
|
+
elsif field_definition_source.include?("#{keyword}:")
|
|
85
|
+
warn "Can't re-inject #{keyword} because it's already present in the definition:\n\n#{field_definition_source}"
|
|
25
86
|
else
|
|
26
|
-
field_definition_source
|
|
87
|
+
new_definition_source = if field_definition_source[/ [a-z_]+:/] # Does it already have keywords?
|
|
88
|
+
field_definition_source.sub(/(field.+?)((?:,$)|(?: do)|(?: {)|$)/, "\\1, #{pair}\\2")
|
|
89
|
+
elsif (block_node = field_definition.node.block)
|
|
90
|
+
block_source = block_node.location.slice
|
|
91
|
+
field_definition_source.sub(" " + block_source, ", #{pair} #{block_source}")
|
|
92
|
+
else
|
|
93
|
+
field_definition_source + ", #{pair}"
|
|
94
|
+
end
|
|
95
|
+
@result_source.sub!(field_definition_source, new_definition_source)
|
|
27
96
|
end
|
|
28
|
-
new_source.sub!(field_definition_source, new_definition_source)
|
|
29
97
|
end
|
|
30
98
|
|
|
31
99
|
|
|
32
|
-
def remove_field_keyword(
|
|
100
|
+
def remove_field_keyword(field_definition, keyword)
|
|
33
101
|
field_definition_source = field_definition.source
|
|
34
102
|
new_definition_source = field_definition_source.sub(/, #{keyword}: \S+(,|$)/, "\\1")
|
|
35
|
-
|
|
103
|
+
@result_source.sub!(field_definition_source, new_definition_source)
|
|
36
104
|
end
|
|
37
105
|
|
|
38
|
-
def replace_resolver_method(
|
|
106
|
+
def replace_resolver_method(field_definition, new_params)
|
|
39
107
|
resolver_method = field_definition.resolver_method
|
|
40
|
-
|
|
108
|
+
old_method_name = resolver_method.name.to_s
|
|
109
|
+
new_method_name = self.class.prefix_if_necessary(old_method_name)
|
|
41
110
|
old_method = resolver_method.source
|
|
42
|
-
|
|
43
|
-
|
|
111
|
+
|
|
112
|
+
method_prefix = field_definition.type_definition.is_interface ? "def #{new_method_name}" : "def self.#{new_method_name}"
|
|
113
|
+
new_class_method = old_method.sub("def #{old_method_name}", method_prefix)
|
|
44
114
|
|
|
45
115
|
if resolver_method.parameter_names.empty?
|
|
46
|
-
new_class_method.sub!(
|
|
116
|
+
new_class_method.sub!(new_method_name, "#{new_method_name}(#{new_params})")
|
|
47
117
|
else
|
|
48
|
-
new_class_method.sub!("
|
|
118
|
+
new_class_method.sub!("#{method_prefix}(", "#{method_prefix}(#{new_params}, ")
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
if field_definition.type_definition.is_interface
|
|
122
|
+
indent = new_class_method[/\A +/]
|
|
123
|
+
new_class_method.gsub!(/^#{indent}/, "#{indent} ")
|
|
124
|
+
new_class_method = "#{indent}resolver_methods do\n#{new_class_method}#{indent}end\n"
|
|
49
125
|
end
|
|
50
126
|
|
|
51
127
|
old_lines = old_method.split("\n")
|
|
52
|
-
|
|
53
|
-
|
|
128
|
+
forward_previous_params = resolver_method.parameter_names.map { |n| ", #{n}: #{n}"}.join
|
|
129
|
+
if old_lines.size == 1
|
|
130
|
+
# TODO doesn't support `def ... =` syntax
|
|
131
|
+
previous_def = /def [^;]+;/.match(old_method)
|
|
132
|
+
new_inst_method = old_lines.first[/^ +/] + "#{previous_def} self.class.#{new_method_name}(#{new_params}#{forward_previous_params}); end"
|
|
133
|
+
else
|
|
134
|
+
new_body = old_lines.first[/^ +/] + " self.class.#{new_method_name}(#{new_params}#{forward_previous_params})"
|
|
135
|
+
new_inst_method = [old_lines.first, new_body, old_lines.last].join("\n")
|
|
136
|
+
end
|
|
54
137
|
|
|
55
138
|
new_double_definition = new_class_method + "\n" + new_inst_method + "\n"
|
|
56
|
-
|
|
139
|
+
@result_source.sub!(old_method, new_double_definition)
|
|
57
140
|
end
|
|
58
141
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
142
|
+
|
|
143
|
+
def remove_resolver_method(field_definition)
|
|
144
|
+
if field_definition.resolver_method
|
|
145
|
+
src_pattern = /(\n*)(#{Regexp.quote(field_definition.resolver_method.source)})(\n*)/
|
|
146
|
+
@result_source.sub!(src_pattern) do
|
|
147
|
+
# $2 includes a newline, too
|
|
148
|
+
"#{$1.length > 1 ? "\n" : ""}#{$3.length > 0 ? "\n" : ""}"
|
|
149
|
+
end
|
|
64
150
|
end
|
|
65
151
|
end
|
|
66
152
|
end
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
module GraphqlMigrateExecution
|
|
3
3
|
class TypeDefinition
|
|
4
|
-
def initialize(name)
|
|
4
|
+
def initialize(name, migration)
|
|
5
5
|
@name = name
|
|
6
|
+
@migration = migration
|
|
6
7
|
@field_definitions = {}
|
|
7
8
|
@resolver_methods = {}
|
|
9
|
+
@is_resolver = false
|
|
10
|
+
@is_interface
|
|
8
11
|
end
|
|
9
12
|
|
|
13
|
+
attr_accessor :is_resolver, :migration, :is_interface
|
|
14
|
+
|
|
10
15
|
attr_reader :resolver_methods, :name, :field_definitions
|
|
11
16
|
|
|
12
17
|
def field_definition(name, node)
|
|
@@ -16,5 +21,13 @@ module GraphqlMigrateExecution
|
|
|
16
21
|
def resolver_method(name, node)
|
|
17
22
|
@resolver_methods[name] = ResolverMethod.new(name, node)
|
|
18
23
|
end
|
|
24
|
+
|
|
25
|
+
def returns_hash?
|
|
26
|
+
@resolver_methods.each_value.first.returns_hash?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def returns_string_hash?
|
|
30
|
+
@resolver_methods.each_value.first.returns_string_hash?
|
|
31
|
+
end
|
|
19
32
|
end
|
|
20
33
|
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module GraphqlMigrateExecution
|
|
2
|
+
# These use `context[:current_path]` or `context.current_path` which isn't supported. Refactor these fields then try migrating again.
|
|
3
|
+
#
|
|
4
|
+
# Open an issue on GraphQL-Ruby's GitHub repo to discuss further.
|
|
5
|
+
class UnsupportedCurrentPath < Strategy
|
|
6
|
+
self.color = :RED
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module GraphqlMigrateExecution
|
|
2
|
+
# These use a field `extra` which isn't supported. Remove this configuration and refactor the field, then try migrating again.
|
|
3
|
+
#
|
|
4
|
+
# (Currently, only `:ast_node` and `:lookahead` are currently supported. Please open an issue on GraphQL-Ruby if this is a problem for you.)
|
|
5
|
+
class UnsupportedExtra < Strategy
|
|
6
|
+
self.color = :RED
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -7,6 +7,7 @@ module GraphqlMigrateExecution
|
|
|
7
7
|
@type_definition_stack = []
|
|
8
8
|
@current_field_definition = nil
|
|
9
9
|
@current_resolver_method = nil
|
|
10
|
+
@is_public = true
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def visit_class_node(node)
|
|
@@ -23,6 +24,7 @@ module GraphqlMigrateExecution
|
|
|
23
24
|
|
|
24
25
|
def visit_module_node(node)
|
|
25
26
|
td = @type_definitions[node.name]
|
|
27
|
+
td.is_interface = true
|
|
26
28
|
@type_definition_stack << td
|
|
27
29
|
super
|
|
28
30
|
ensure
|
|
@@ -37,7 +39,7 @@ module GraphqlMigrateExecution
|
|
|
37
39
|
when "hash_key"
|
|
38
40
|
@current_field_definition.resolve_mode ||= :hash_key
|
|
39
41
|
@current_field_definition.hash_key = get_keyword_value(assoc.value)
|
|
40
|
-
when "resolver"
|
|
42
|
+
when "resolver", "mutation", "subscription"
|
|
41
43
|
@current_field_definition.resolve_mode ||= :resolver
|
|
42
44
|
@current_field_definition.resolver = get_keyword_value(assoc.value)
|
|
43
45
|
when "method"
|
|
@@ -49,10 +51,15 @@ module GraphqlMigrateExecution
|
|
|
49
51
|
when "dig"
|
|
50
52
|
@current_field_definition.resolve_mode ||= :dig
|
|
51
53
|
@current_field_definition.dig = get_keyword_value(assoc.value)
|
|
52
|
-
when "resolve_each", "resolve_static", "resolve_batch"
|
|
54
|
+
when "resolve_each", "resolve_static", "resolve_batch", "resolve_legacy_instance_method"
|
|
53
55
|
# These should override any other keywords that are discovered
|
|
54
56
|
@current_field_definition.resolve_mode = :already_migrated
|
|
55
57
|
@current_field_definition.already_migrated = { assoc.key.unescaped.to_sym => get_keyword_value(assoc.value) }
|
|
58
|
+
when "fallback_value"
|
|
59
|
+
if !assoc.value.is_a?(Prism::NilNode)
|
|
60
|
+
@current_field_definition.resolve_mode ||= :fallback_value
|
|
61
|
+
@current_field_definition.fallback_value = self.class.source_for_constant_node(assoc.value)
|
|
62
|
+
end
|
|
56
63
|
else
|
|
57
64
|
# fallback_value, connection, extensions, extras, resolver, mutation, subscription
|
|
58
65
|
@current_field_definition.unknown_options << assoc.key.unescaped
|
|
@@ -64,15 +71,18 @@ module GraphqlMigrateExecution
|
|
|
64
71
|
end
|
|
65
72
|
|
|
66
73
|
def visit_call_node(node)
|
|
67
|
-
if node.receiver.nil? && node.name == :field
|
|
74
|
+
if node.receiver.nil? && node.name == :field && node.arguments
|
|
68
75
|
first_arg = node.arguments.arguments.first # rubocop:disable Development/ContextIsPassedCop
|
|
69
|
-
if first_arg.is_a?(Prism::SymbolNode)
|
|
76
|
+
if first_arg.is_a?(Prism::SymbolNode) && (td = @type_definition_stack.last)
|
|
70
77
|
field_name = first_arg.unescaped
|
|
71
|
-
td = @type_definition_stack.last
|
|
72
78
|
@current_field_definition = td.field_definition(field_name, node)
|
|
73
79
|
else
|
|
74
80
|
warn "GraphQL-Ruby warning: Skipping unrecognized field definition: #{node.inspect}"
|
|
75
81
|
end
|
|
82
|
+
elsif node.receiver.nil? && ((node.name == :private) || (node.name == :protected))
|
|
83
|
+
@is_public = false
|
|
84
|
+
elsif node.receiver.nil? && node.name == :public
|
|
85
|
+
@is_public = true
|
|
76
86
|
elsif @current_resolver_method
|
|
77
87
|
if node.receiver.nil? || node.receiver.is_a?(Prism::SelfNode)
|
|
78
88
|
@current_resolver_method.self_sends.add(node.name)
|
|
@@ -89,6 +99,15 @@ module GraphqlMigrateExecution
|
|
|
89
99
|
case node.name
|
|
90
100
|
when :dataloader, :dataload, :dataload_association, :dataload_record, :dataload_all
|
|
91
101
|
@current_resolver_method.calls_dataloader = true
|
|
102
|
+
when :current_path
|
|
103
|
+
if node.receiver.is_a?(Prism::CallNode) && node.receiver.name == :context
|
|
104
|
+
@current_resolver_method.uses_current_path = true
|
|
105
|
+
end
|
|
106
|
+
when :[]
|
|
107
|
+
if node.receiver.is_a?(Prism::CallNode) && node.receiver.name == :context &&
|
|
108
|
+
(arg = node.arguments.arguments.first).is_a?(Prism::SymbolNode) && (arg.unescaped == "current_path")
|
|
109
|
+
@current_resolver_method.uses_current_path = true
|
|
110
|
+
end
|
|
92
111
|
end
|
|
93
112
|
end
|
|
94
113
|
super
|
|
@@ -99,22 +118,39 @@ module GraphqlMigrateExecution
|
|
|
99
118
|
end
|
|
100
119
|
|
|
101
120
|
def visit_def_node(node)
|
|
102
|
-
if
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
121
|
+
if @is_public && (td = @type_definition_stack.last)
|
|
122
|
+
if node.receiver.nil?
|
|
123
|
+
@current_resolver_method = td.resolver_method(node.name, node)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
if node.name == :resolve && td.resolver_methods.size == 1
|
|
127
|
+
td.is_resolver = true
|
|
128
|
+
elsif td.is_resolver && td.resolver_methods.size > 1
|
|
129
|
+
td.is_resolver = false
|
|
130
|
+
end
|
|
106
131
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
when
|
|
111
|
-
|
|
132
|
+
body = case node.body
|
|
133
|
+
when Prism::StatementsNode
|
|
134
|
+
node.body.body
|
|
135
|
+
when Prism::BeginNode
|
|
136
|
+
node.body.statements.body
|
|
137
|
+
when nil
|
|
138
|
+
nil
|
|
139
|
+
else
|
|
140
|
+
raise ArgumentError, "Unexpected DefNode body for `def #{node.name}`: #{node.body.class}"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
if body && @current_resolver_method && body.length == 1 && (call_node = body.first).is_a?(Prism::CallNode)
|
|
144
|
+
case call_node.name
|
|
145
|
+
when :load, :request, :load_all, :request_all
|
|
146
|
+
if (call_node2 = call_node.receiver).is_a?(Prism::CallNode) && call_node2.name == :with
|
|
147
|
+
@current_resolver_method.dataloader_call = true
|
|
148
|
+
end
|
|
149
|
+
when :dataload_record, :dataload_association, :dataload, :dataload_all
|
|
112
150
|
@current_resolver_method.dataloader_call = true
|
|
151
|
+
else
|
|
152
|
+
# not a single dataloader call
|
|
113
153
|
end
|
|
114
|
-
when :dataload_record, :dataload_association, :dataload
|
|
115
|
-
@current_resolver_method.dataloader_call = true
|
|
116
|
-
else
|
|
117
|
-
# not a single dataloader call
|
|
118
154
|
end
|
|
119
155
|
end
|
|
120
156
|
super
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require "prism"
|
|
2
3
|
require "graphql_migrate_execution/action"
|
|
3
|
-
require "graphql_migrate_execution/
|
|
4
|
-
require "graphql_migrate_execution/remove_legacy"
|
|
5
|
-
require "graphql_migrate_execution/analyze"
|
|
4
|
+
require "graphql_migrate_execution/migration"
|
|
6
5
|
require "graphql_migrate_execution/field_definition"
|
|
7
6
|
require "graphql_migrate_execution/resolver_method"
|
|
8
7
|
require "graphql_migrate_execution/type_definition"
|
|
@@ -10,6 +9,8 @@ require "graphql_migrate_execution/visitor"
|
|
|
10
9
|
require "graphql_migrate_execution/strategy"
|
|
11
10
|
require "graphql_migrate_execution/implicit"
|
|
12
11
|
require "graphql_migrate_execution/do_nothing"
|
|
12
|
+
require "graphql_migrate_execution/fallback_value"
|
|
13
|
+
require "graphql_migrate_execution/resolve_batch"
|
|
13
14
|
require "graphql_migrate_execution/resolve_each"
|
|
14
15
|
require "graphql_migrate_execution/resolve_static"
|
|
15
16
|
require "graphql_migrate_execution/not_implemented"
|
|
@@ -17,35 +18,11 @@ require "graphql_migrate_execution/dataloader_all"
|
|
|
17
18
|
require "graphql_migrate_execution/dataloader_batch"
|
|
18
19
|
require "graphql_migrate_execution/dataloader_manual"
|
|
19
20
|
require "graphql_migrate_execution/dataloader_shorthand"
|
|
21
|
+
require "graphql_migrate_execution/hash_key"
|
|
22
|
+
require "graphql_migrate_execution/unsupported_extra"
|
|
23
|
+
require "graphql_migrate_execution/unsupported_current_path"
|
|
20
24
|
require "graphql_migrate_execution/not_implemented"
|
|
21
25
|
require "irb"
|
|
22
26
|
|
|
23
27
|
module GraphqlMigrateExecution
|
|
24
|
-
class Migration
|
|
25
|
-
def initialize(glob, concise: false, migrate: false, cleanup: false, only: nil, implicit: nil, colorable: IRB::Color.colorable?)
|
|
26
|
-
@glob = glob
|
|
27
|
-
@skip_description = concise
|
|
28
|
-
@colorable = colorable
|
|
29
|
-
@only = only
|
|
30
|
-
@implicit = implicit
|
|
31
|
-
@action_class = if migrate
|
|
32
|
-
AddFuture
|
|
33
|
-
elsif cleanup
|
|
34
|
-
RemoveLegacy
|
|
35
|
-
else
|
|
36
|
-
Analyze
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
attr_reader :skip_description, :colorable
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def run
|
|
44
|
-
Dir.glob(@glob).each do |filepath|
|
|
45
|
-
source = File.read(filepath)
|
|
46
|
-
file_migrate = @action_class.new(self, filepath, source)
|
|
47
|
-
puts file_migrate.run
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
28
|
end
|