graphql-client 0.16.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
  require "active_support/inflector"
3
3
  require "active_support/notifications"
4
4
  require "graphql"
5
+ require "graphql/client/type_stack"
5
6
  require "graphql/client/collocated_enforcement"
6
7
  require "graphql/client/definition_variables"
7
8
  require "graphql/client/definition"
@@ -49,12 +50,12 @@ module GraphQL
49
50
  when GraphQL::Schema, Class
50
51
  schema
51
52
  when Hash
52
- GraphQL::Schema::Loader.load(schema)
53
+ GraphQL::Schema.from_introspection(schema)
53
54
  when String
54
55
  if schema.end_with?(".json") && File.exist?(schema)
55
56
  load_schema(File.read(schema))
56
57
  elsif schema =~ /\A\s*{/
57
- load_schema(JSON.parse(schema))
58
+ load_schema(JSON.parse(schema, freeze: true))
58
59
  end
59
60
  else
60
61
  if schema.respond_to?(:execute)
@@ -90,15 +91,41 @@ module GraphQL
90
91
  result
91
92
  end
92
93
 
93
- def initialize(schema:, execute: nil, enforce_collocated_callers: false)
94
+ def initialize(schema:, execute: nil, enforce_collocated_callers: false, raise_on_unknown_enum_value: true)
94
95
  @schema = self.class.load_schema(schema)
95
96
  @execute = execute
96
97
  @document = GraphQL::Language::Nodes::Document.new(definitions: [])
97
98
  @document_tracking_enabled = false
98
99
  @allow_dynamic_queries = false
99
100
  @enforce_collocated_callers = enforce_collocated_callers
101
+ if schema.is_a?(Class)
102
+ @possible_types = schema.possible_types
103
+ key, _types = @possible_types.first
104
+ # GraphQL-Ruby 2.3.5+ has classes here instead of strings
105
+ if key.is_a?(Module)
106
+ @possible_types = @possible_types.transform_keys(&:graphql_name)
107
+ end
108
+ end
109
+ @types = Schema.generate(@schema, raise_on_unknown_enum_value: raise_on_unknown_enum_value)
110
+ end
100
111
 
101
- @types = Schema.generate(@schema)
112
+ # A cache of the schema's merged possible types
113
+ # @param type_condition [Class, String] a type definition or type name
114
+ def possible_types(type_condition = nil)
115
+ if type_condition
116
+ if defined?(@possible_types)
117
+ if type_condition.respond_to?(:graphql_name)
118
+ type_condition = type_condition.graphql_name
119
+ end
120
+ @possible_types[type_condition]
121
+ else
122
+ @schema.possible_types(type_condition)
123
+ end
124
+ elsif defined?(@possible_types)
125
+ @possible_types
126
+ else
127
+ @schema.possible_types(type_condition)
128
+ end
102
129
  end
103
130
 
104
131
  def parse(str, filename = nil, lineno = nil)
@@ -135,11 +162,7 @@ module GraphQL
135
162
  # which corresponds to the spread.
136
163
  # We depend on ActiveSupport to either find the already-loaded
137
164
  # constant, or to load the constant by name
138
- begin
139
- fragment = ActiveSupport::Inflector.constantize(const_name)
140
- rescue NameError
141
- fragment = nil
142
- end
165
+ fragment = ActiveSupport::Inflector.safe_constantize(const_name)
143
166
 
144
167
  case fragment
145
168
  when FragmentDefinition
@@ -173,12 +196,8 @@ module GraphQL
173
196
 
174
197
  doc.definitions.each do |node|
175
198
  if node.name.nil?
176
- if node.respond_to?(:merge) # GraphQL 1.9 +
177
- node_with_name = node.merge(name: "__anonymous__")
178
- doc = doc.replace_child(node, node_with_name)
179
- else
180
- node.name = "__anonymous__"
181
- end
199
+ node_with_name = node.merge(name: "__anonymous__")
200
+ doc = doc.replace_child(node, node_with_name)
182
201
  end
183
202
  end
184
203
 
@@ -200,37 +219,13 @@ module GraphQL
200
219
  raise error
201
220
  end
202
221
 
203
- definitions = {}
204
- doc.definitions.each do |node|
205
- sliced_document = Language::DefinitionSlice.slice(document_dependencies, node.name)
206
- definition = Definition.for(
207
- client: self,
208
- ast_node: node,
209
- document: sliced_document,
210
- source_document: doc,
211
- source_location: source_location
212
- )
213
- definitions[node.name] = definition
214
- end
222
+ definitions = sliced_definitions(document_dependencies, doc, source_location: source_location)
215
223
 
216
- if @document.respond_to?(:merge) # GraphQL 1.9+
217
- visitor = RenameNodeVisitor.new(document_dependencies, definitions: definitions)
218
- visitor.visit
219
- else
220
- name_hook = RenameNodeHook.new(definitions)
221
- visitor = Language::Visitor.new(document_dependencies)
222
- visitor[Language::Nodes::FragmentDefinition].leave << name_hook.method(:rename_node)
223
- visitor[Language::Nodes::OperationDefinition].leave << name_hook.method(:rename_node)
224
- visitor[Language::Nodes::FragmentSpread].leave << name_hook.method(:rename_node)
225
- visitor.visit
226
- end
224
+ visitor = RenameNodeVisitor.new(document_dependencies, definitions: definitions)
225
+ visitor.visit
227
226
 
228
227
  if document_tracking_enabled
229
- if @document.respond_to?(:merge) # GraphQL 1.9+
230
- @document = @document.merge(definitions: @document.definitions + doc.definitions)
231
- else
232
- @document.definitions.concat(doc.definitions)
233
- end
228
+ @document = @document.merge(definitions: @document.definitions + doc.definitions)
234
229
  end
235
230
 
236
231
  if definitions["__anonymous__"]
@@ -276,27 +271,9 @@ module GraphQL
276
271
  end
277
272
  end
278
273
 
279
- class RenameNodeHook
280
- def initialize(definitions)
281
- @definitions = definitions
282
- end
283
-
284
- def rename_node(node, _parent)
285
- definition = @definitions[node.name]
286
- if definition
287
- node.extend(LazyName)
288
- node._definition = definition
289
- end
290
- end
291
- end
292
-
293
274
  # Public: A wrapper to use the more-efficient `.get_type` when it's available from GraphQL-Ruby (1.10+)
294
275
  def get_type(type_name)
295
- if @schema.respond_to?(:get_type)
296
- @schema.get_type(type_name)
297
- else
298
- @schema.types[type_name]
299
- end
276
+ @schema.get_type(type_name)
300
277
  end
301
278
 
302
279
  # Public: Create operation definition from a fragment definition.
@@ -366,7 +343,7 @@ module GraphQL
366
343
  end
367
344
 
368
345
  if allow_dynamic_queries == false && definition.name.nil?
369
- raise DynamicQueryError, "expected definition to be assigned to a static constant https://git.io/vXXSE"
346
+ raise DynamicQueryError, "expected definition to be assigned to a static constant https://github.com/github-community-projects/graphql-client/blob/master/guides/dynamic-query-error.md"
370
347
  end
371
348
 
372
349
  variables = deep_stringify_keys(variables)
@@ -403,12 +380,12 @@ module GraphQL
403
380
  error_payload = payload.merge(message: error["message"], error: error)
404
381
  ActiveSupport::Notifications.instrument("error.graphql", error_payload)
405
382
  end
406
-
407
383
  Response.new(
408
384
  result,
409
385
  data: definition.new(data, Errors.new(errors, ["data"])),
410
386
  errors: Errors.new(errors),
411
- extensions: extensions
387
+ extensions: extensions,
388
+ full_response: execute.respond_to?("last_response") ? execute.last_response : nil
412
389
  )
413
390
  end
414
391
 
@@ -424,6 +401,57 @@ module GraphQL
424
401
 
425
402
  private
426
403
 
404
+ def sliced_definitions(document_dependencies, doc, source_location:)
405
+ dependencies = document_dependencies.definitions.map do |node|
406
+ [node.name, find_definition_dependencies(node)]
407
+ end.to_h
408
+
409
+ doc.definitions.map do |node|
410
+ deps = Set.new
411
+
412
+ queue = [node.name]
413
+ while name = queue.shift
414
+ next if deps.include?(name)
415
+ deps.add(name)
416
+ queue.concat dependencies[name]
417
+ end
418
+
419
+ definitions = document_dependencies.definitions.select { |x| deps.include?(x.name) }
420
+ sliced_document = Language::Nodes::Document.new(definitions: definitions)
421
+ definition = Definition.for(
422
+ client: self,
423
+ ast_node: node,
424
+ document: sliced_document,
425
+ source_document: doc,
426
+ source_location: source_location
427
+ )
428
+
429
+ [node.name, definition]
430
+ end.to_h
431
+ end
432
+
433
+ class GatherNamesVisitor < GraphQL::Language::Visitor
434
+ def initialize(node)
435
+ @names = []
436
+ super
437
+ end
438
+
439
+ attr_reader :names
440
+
441
+ def on_fragment_spread(node, parent)
442
+ @names << node.name
443
+ super
444
+ end
445
+ end
446
+
447
+ def find_definition_dependencies(node)
448
+ visitor = GatherNamesVisitor.new(node)
449
+ visitor.visit
450
+ names = visitor.names
451
+ names.uniq!
452
+ names
453
+ end
454
+
427
455
  def deep_freeze_json_object(obj)
428
456
  case obj
429
457
  when String
@@ -5,7 +5,7 @@ module RuboCop
5
5
  module Cop
6
6
  module GraphQL
7
7
  # Public: Cop for enforcing non-interpolated GRAPHQL heredocs.
8
- class Heredoc < Cop
8
+ class Heredoc < Base
9
9
  def on_dstr(node)
10
10
  check_str(node)
11
11
  end
@@ -19,11 +19,11 @@ module RuboCop
19
19
  return unless node.location.expression.source =~ /^<<(-|~)?GRAPHQL/
20
20
 
21
21
  node.each_child_node(:begin) do |begin_node|
22
- add_offense(begin_node, location: :expression, message: "Do not interpolate variables into GraphQL queries, " \
22
+ add_offense(begin_node, message: "Do not interpolate variables into GraphQL queries, " \
23
23
  "used variables instead.")
24
24
  end
25
25
 
26
- add_offense(node, location: :expression, message: "GraphQL heredocs should be quoted. <<-'GRAPHQL'")
26
+ add_offense(node, message: "GraphQL heredocs should be quoted. <<-'GRAPHQL'")
27
27
  end
28
28
 
29
29
  def autocorrect(node)
@@ -8,7 +8,7 @@ module RuboCop
8
8
  module Cop
9
9
  module GraphQL
10
10
  # Public: Rubocop for catching overfetched fields in ERB templates.
11
- class Overfetch < Cop
11
+ class Overfetch < Base
12
12
  if defined?(RangeHelp)
13
13
  # rubocop 0.53 moved the #source_range method into this module
14
14
  include RangeHelp
@@ -21,19 +21,12 @@ module RuboCop
21
21
  query, = ::GraphQL::Client::ViewModule.extract_graphql_section(erb)
22
22
  return unless query
23
23
 
24
- aliases = {}
25
- fields = {}
26
- ranges = {}
27
-
28
24
  # TODO: Use GraphQL client parser
29
25
  document = ::GraphQL.parse(query.gsub(/::/, "__"))
30
-
31
- visitor = ::GraphQL::Language::Visitor.new(document)
32
- visitor[::GraphQL::Language::Nodes::Field] << ->(node, _parent) do
33
- name = node.alias || node.name
34
- fields[name] ||= 0
35
- field_aliases(name).each { |n| (aliases[n] ||= []) << name }
36
- ranges[name] ||= source_range(processed_source.buffer, node.line, 0)
26
+ visitor = OverfetchVisitor.new(document) do |line_num|
27
+ # `source_range` is private to this object,
28
+ # so yield back out to it to get this info:
29
+ source_range(processed_source.buffer, line_num, 0)
37
30
  end
38
31
  visitor.visit
39
32
 
@@ -41,30 +34,53 @@ module RuboCop
41
34
  method_names = method_names_for(*node)
42
35
 
43
36
  method_names.each do |method_name|
44
- aliases.fetch(method_name, []).each do |field_name|
45
- fields[field_name] += 1
37
+ visitor.aliases.fetch(method_name, []).each do |field_name|
38
+ visitor.fields[field_name] += 1
46
39
  end
47
40
  end
48
41
  end
49
42
 
50
- fields.each do |field, count|
43
+ visitor.fields.each do |field, count|
51
44
  next if count > 0
52
- add_offense(nil, location: ranges[field], message: "GraphQL field '#{field}' query but was not used in template.")
45
+ add_offense(visitor.ranges[field], message: "GraphQL field '#{field}' query but was not used in template.")
53
46
  end
54
47
  end
55
48
 
56
- def field_aliases(name)
57
- names = Set.new
49
+ class OverfetchVisitor < ::GraphQL::Language::Visitor
50
+ def initialize(doc, &range_for_line)
51
+ super(doc)
52
+ @range_for_line = range_for_line
53
+ @fields = {}
54
+ @aliases = {}
55
+ @ranges = {}
56
+ end
57
+
58
+ attr_reader :fields, :aliases, :ranges
59
+
60
+ def on_field(node, parent)
61
+ name = node.alias || node.name
62
+ fields[name] ||= 0
63
+ field_aliases(name).each { |n| (aliases[n] ||= []) << name }
64
+ ranges[name] ||= @range_for_line.call(node.line)
65
+ super
66
+ end
58
67
 
59
- names << name
60
- names << "#{name}?"
68
+ private
61
69
 
62
- names << underscore_name = ActiveSupport::Inflector.underscore(name)
63
- names << "#{underscore_name}?"
70
+ def field_aliases(name)
71
+ names = Set.new
64
72
 
65
- names
73
+ names << name
74
+ names << "#{name}?"
75
+
76
+ names << underscore_name = ActiveSupport::Inflector.underscore(name)
77
+ names << "#{underscore_name}?"
78
+
79
+ names
80
+ end
66
81
  end
67
82
 
83
+
68
84
  def method_names_for(*node)
69
85
  receiver, method_name, *_args = node
70
86
  method_names = []
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.0
4
+ version: 0.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2019-10-09 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -28,16 +27,16 @@ dependencies:
28
27
  name: graphql
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
- - - "~>"
30
+ - - ">="
32
31
  - !ruby/object:Gem::Version
33
- version: '1.8'
32
+ version: 1.13.0
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
- - - "~>"
37
+ - - ">="
39
38
  - !ruby/object:Gem::Version
40
- version: '1.8'
39
+ version: 1.13.0
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: actionpack
43
42
  requirement: !ruby/object:Gem::Requirement
@@ -100,42 +99,42 @@ dependencies:
100
99
  requirements:
101
100
  - - "~>"
102
101
  - !ruby/object:Gem::Version
103
- version: '11.2'
102
+ version: 13.2.1
104
103
  type: :development
105
104
  prerelease: false
106
105
  version_requirements: !ruby/object:Gem::Requirement
107
106
  requirements:
108
107
  - - "~>"
109
108
  - !ruby/object:Gem::Version
110
- version: '11.2'
109
+ version: 13.2.1
111
110
  - !ruby/object:Gem::Dependency
112
111
  name: rubocop-github
113
112
  requirement: !ruby/object:Gem::Requirement
114
113
  requirements:
115
- - - "~>"
114
+ - - ">="
116
115
  - !ruby/object:Gem::Version
117
- version: '0.10'
116
+ version: '0'
118
117
  type: :development
119
118
  prerelease: false
120
119
  version_requirements: !ruby/object:Gem::Requirement
121
120
  requirements:
122
- - - "~>"
121
+ - - ">="
123
122
  - !ruby/object:Gem::Version
124
- version: '0.10'
123
+ version: '0'
125
124
  - !ruby/object:Gem::Dependency
126
125
  name: rubocop
127
126
  requirement: !ruby/object:Gem::Requirement
128
127
  requirements:
129
128
  - - "~>"
130
129
  - !ruby/object:Gem::Version
131
- version: '0.55'
130
+ version: 1.75.8
132
131
  type: :development
133
132
  prerelease: false
134
133
  version_requirements: !ruby/object:Gem::Requirement
135
134
  requirements:
136
135
  - - "~>"
137
136
  - !ruby/object:Gem::Version
138
- version: '0.55'
137
+ version: 1.75.8
139
138
  description: A Ruby library for declaring, composing and executing GraphQL queries
140
139
  email: engineering@github.com
141
140
  executables: []
@@ -176,14 +175,15 @@ files:
176
175
  - lib/graphql/client/schema/scalar_type.rb
177
176
  - lib/graphql/client/schema/skip_directive.rb
178
177
  - lib/graphql/client/schema/union_type.rb
178
+ - lib/graphql/client/type_stack.rb
179
179
  - lib/graphql/client/view_module.rb
180
180
  - lib/rubocop/cop/graphql/heredoc.rb
181
181
  - lib/rubocop/cop/graphql/overfetch.rb
182
- homepage: https://github.com/github/graphql-client
182
+ homepage: https://github.com/github-community-projects/graphql-client
183
183
  licenses:
184
184
  - MIT
185
- metadata: {}
186
- post_install_message:
185
+ metadata:
186
+ rubygems_mfa_required: 'true'
187
187
  rdoc_options: []
188
188
  require_paths:
189
189
  - lib
@@ -198,8 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
198
  - !ruby/object:Gem::Version
199
199
  version: '0'
200
200
  requirements: []
201
- rubygems_version: 3.0.3
202
- signing_key:
201
+ rubygems_version: 3.6.7
203
202
  specification_version: 4
204
203
  summary: GraphQL Client
205
204
  test_files: []