graphql-client 0.0.7 → 0.0.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8414ab402cf8454eb7d34081edc3c17fff0611de
4
- data.tar.gz: 2a1351fe377f4e9999b9a0c5cfedd04081240753
3
+ metadata.gz: cee45489051065d2c8d47c6f8c68c6b3c98d75ca
4
+ data.tar.gz: e462bb5c2fb1997f8679bb0fea315f8abd751539
5
5
  SHA512:
6
- metadata.gz: b1b30fc5a6c9f06f03a8c3ed9e69614bb1f7d3a0cbd449bb72a2077d34a154a6b2b48bd3d0b988997b2d59716941a3121fe2958f5948dff6afcce17c779c3cdd
7
- data.tar.gz: dfbe13c52c3bd8d9df76129b8de2ec7f4fe2bb430c1788f9d77966c8a75f57d8fcbdc973e280239f763fba1d6976be8babaffdb86590b0da35819b40e1c0fb26
6
+ metadata.gz: 9f335b9446f04fe5e684218b1f0f377b16412ac4918065ac610b2abff61d00792a461131b1ab615cd6512ba611a8dde6929af9c1b229f4a36c713e7e0983a22d
7
+ data.tar.gz: 428487913828790e402b3a3c51daceafe8c338ebabfd234d0570424c8af426bc54bfa6f8fbf306d365fd674174a5708936e8a350cce5fcdf5c6426f5d3f1bd37
@@ -1,15 +1,38 @@
1
- require "graphql"
2
1
  require "active_support/inflector"
2
+ require "graphql"
3
+ require "set"
3
4
 
4
5
  module GraphQL
5
- module Client
6
+ class Client
6
7
  class QueryResult
7
- def self.fields
8
- @fields
8
+ # Internal: Get QueryResult class for result of query.
9
+ #
10
+ # Returns subclass of QueryResult or nil.
11
+ def self.wrap(node, name: nil)
12
+ fields = {}
13
+
14
+ node.selections.each do |selection|
15
+ case selection
16
+ when Language::Nodes::FragmentSpread
17
+ when Language::Nodes::Field
18
+ field_name = selection.alias || selection.name
19
+ field_klass = selection.selections.any? ? wrap(selection, name: "#{name}.#{field_name}") : nil
20
+ fields[field_name] ? fields[field_name] |= field_klass : fields[field_name] = field_klass
21
+ when Language::Nodes::InlineFragment
22
+ wrap(selection, name: name).fields.each do |fragment_name, klass|
23
+ fields[fragment_name.to_s] ? fields[fragment_name.to_s] |= klass : fields[fragment_name.to_s] = klass
24
+ end
25
+ end
26
+ end
27
+
28
+ define(name: name, source_node: node, fields: fields)
9
29
  end
10
30
 
11
- def self.define(fields: {})
31
+ # Internal
32
+ def self.define(name:, source_node:, fields: {})
12
33
  Class.new(self) do
34
+ @name = name
35
+ @source_node = source_node
13
36
  @fields = {}
14
37
 
15
38
  fields.each do |field, type|
@@ -62,8 +85,20 @@ module GraphQL
62
85
  end
63
86
  end
64
87
 
88
+ def self.source_node
89
+ @source_node
90
+ end
91
+
92
+ def self.fields
93
+ @fields
94
+ end
95
+
96
+ def self.name
97
+ @name || super || GraphQL::Client::QueryResult.name
98
+ end
99
+
65
100
  def self.inspect
66
- "#<GraphQL::Client::QueryResult fields=#{@fields.keys.inspect}>"
101
+ "#<#{self.name} fields=#{@fields.keys.inspect}>"
67
102
  end
68
103
 
69
104
  def self.cast(obj)
@@ -71,6 +106,14 @@ module GraphQL
71
106
  when Hash
72
107
  new(obj)
73
108
  when QueryResult
109
+ spreads = Set.new(obj.class.source_node.selections.select { |s| s.is_a?(GraphQL::Language::Nodes::FragmentSpread) }.map(&:name))
110
+
111
+ if !spreads.include?(self.source_node.name)
112
+ message = "couldn't cast #{obj.inspect} to #{self.inspect}\n\n"
113
+ suggestion = "\n ...#{name || "YourFragment"} # SUGGESTION"
114
+ message << GraphQL::Language::Generation.generate(obj.class.source_node).sub(/\n}$/, "#{suggestion}\n}")
115
+ raise TypeError, message
116
+ end
74
117
  cast(obj.to_h)
75
118
  when Array
76
119
  obj.map { |e| cast(e) }
@@ -99,7 +142,8 @@ module GraphQL
99
142
  new_fields[name] = value
100
143
  end
101
144
  end
102
- define(fields: new_fields)
145
+ # TODO: Picking first source node seems error prone
146
+ define(name: self.name, source_node: self.source_node, fields: new_fields)
103
147
  end
104
148
 
105
149
  attr_reader :data
@@ -107,7 +151,7 @@ module GraphQL
107
151
 
108
152
  def inspect
109
153
  ivars = (self.class.fields.keys - [:__typename]).map { |sym| "#{sym}=#{instance_variable_get("@#{sym}").inspect}" }
110
- buf = "#<GraphQL::Client::QueryResult"
154
+ buf = "#<#{self.class.name}"
111
155
  buf << " " << @__typename if @__typename
112
156
  buf << " " << ivars.join(" ") if ivars.any?
113
157
  buf << ">"
@@ -1,14 +1,138 @@
1
+ require "active_support/inflector"
1
2
  require "graphql"
2
- require "graphql/client/document"
3
- require "graphql/client/fragment"
4
- require "graphql/client/node"
5
3
  require "graphql/client/query_result"
6
- require "graphql/client/query"
4
+ require "graphql/language/mutator"
5
+ require "graphql/language/nodes/deep_freeze_ext"
6
+ require "graphql/language/operation_slice"
7
+ require "graphql/relay/parser"
7
8
 
8
9
  module GraphQL
9
- module Client
10
- class << self
11
- attr_accessor :schema
10
+ class Client
11
+ class Error < StandardError; end
12
+ class ValidationError < Error; end
13
+
14
+ attr_reader :schema
15
+
16
+ def initialize(schema:)
17
+ @schema = schema
18
+ @definitions = []
19
+ @document = GraphQL::Language::Nodes::Document.new(definitions: @definitions)
20
+ @document_slices = {}
21
+ end
22
+
23
+ class Definition < Module
24
+ def initialize(node:)
25
+ @definition_node = node
26
+ end
27
+
28
+ # Internal: Get underlying operation or fragment defintion AST node for
29
+ # definition.
30
+ #
31
+ # Returns OperationDefinition or FragmentDefinition object.
32
+ attr_reader :definition_node
33
+
34
+ # Public: Ruby constant name of definition.
35
+ #
36
+ # Returns String or errors if definition was not assigned to a constant.
37
+ def name
38
+ @name ||= super || raise(RuntimeError, "definition must be assigned to a constant")
39
+ end
40
+
41
+ # Public: Global name of definition in client document.
42
+ #
43
+ # Returns a GraphQL safe name of the Ruby constant String.
44
+ #
45
+ # "Users::UserQuery" #=> "Users__UserQuery"
46
+ #
47
+ # Returns String.
48
+ def definition_name
49
+ @definition_name ||= name.gsub("::", "__").freeze
50
+ end
51
+
52
+ def new(*args)
53
+ query_result_class.new(*args)
54
+ end
55
+
56
+ private
57
+ def query_result_class
58
+ @query_result_class ||= GraphQL::Client::QueryResult.wrap(definition_node, name: name)
59
+ end
60
+ end
61
+
62
+ class OperationDefinition < Definition
63
+ # Public: Alias for definition name.
64
+ alias_method :operation_name, :definition_name
65
+ end
66
+
67
+ class FragmentDefinition < Definition
68
+ end
69
+
70
+ def parse(str)
71
+ str = str.gsub(/\.\.\.([a-zA-Z0-9_]+(::[a-zA-Z0-9_]+)+)/) { |m|
72
+ const_name = $1
73
+ case fragment = ActiveSupport::Inflector.safe_constantize(const_name)
74
+ when FragmentDefinition
75
+ "...#{fragment.definition_name}"
76
+ when nil
77
+ raise NameError, "uninitialized constant #{const_name}\n#{str}"
78
+ else
79
+ raise TypeError, "expected #{const_name} to be a #{FragmentDefinition}, but was a #{fragment.class}"
80
+ end
81
+ }
82
+
83
+ doc = GraphQL::Relay::Parser.parse(str)
84
+
85
+ mutator = GraphQL::Language::Mutator.new(doc)
86
+
87
+ # TODO: Make this __typename injection optional
88
+ mutator.prepend_selection(GraphQL::Language::Nodes::Field.new(name: "__typename").deep_freeze)
89
+
90
+ definitions, renames = {}, {}
91
+ doc.definitions.each do |node|
92
+ local_name = node.name
93
+ definition = case node
94
+ when Language::Nodes::OperationDefinition
95
+ OperationDefinition.new(node: node)
96
+ when Language::Nodes::FragmentDefinition
97
+ FragmentDefinition.new(node: node)
98
+ end
99
+ definitions[local_name] = definition
100
+ renames[local_name] = -> { definition.definition_name }
101
+ end
102
+ mutator.rename_definitions(renames)
103
+
104
+ doc.deep_freeze
105
+
106
+ self.document.definitions.concat(doc.definitions)
107
+
108
+ if definitions[nil]
109
+ definitions[nil]
110
+ else
111
+ Module.new do
112
+ definitions.each do |name, definition|
113
+ const_set(name, definition)
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ def document
120
+ @document
121
+ end
122
+
123
+ def document_slice(operation_name)
124
+ @document_slices[operation_name] ||= Language::OperationSlice.slice(document, operation_name).deep_freeze
125
+ end
126
+
127
+ def validate!
128
+ validator = StaticValidation::Validator.new(schema: @schema)
129
+ query = Query.new(@schema, document: document)
130
+
131
+ validator.validate(query).fetch(:errors).each do |error|
132
+ raise ValidationError, error["message"]
133
+ end
134
+
135
+ nil
12
136
  end
13
137
  end
14
138
  end
@@ -0,0 +1,54 @@
1
+ require "graphql"
2
+ require "set"
3
+
4
+ module GraphQL
5
+ module Language
6
+ class Mutator
7
+ def initialize(document)
8
+ @document = document
9
+ end
10
+
11
+ module LazyName
12
+ def name
13
+ @name.call
14
+ end
15
+ end
16
+
17
+ def rename_definitions(definitions)
18
+ rename_node = -> (node, parent) {
19
+ if name = definitions[node.name]
20
+ node.extend(LazyName) if name.is_a?(Proc)
21
+ node.name = name
22
+ end
23
+ }
24
+
25
+ visitor = Visitor.new(@document)
26
+ visitor[Nodes::FragmentDefinition].leave << rename_node
27
+ visitor[Nodes::OperationDefinition].leave << rename_node
28
+ visitor[Nodes::FragmentSpread].leave << rename_node
29
+ visitor.visit
30
+
31
+ nil
32
+ end
33
+
34
+ def prepend_selection(selection)
35
+ on_selections = -> (node, parent) {
36
+ return if !node.selections.any?
37
+ # TODO: Simplify if AbstractNode#eql? is implemented
38
+ existing_selections = Set.new(node.selections.map { |s| s.respond_to?(:name) ? s.name : nil }.compact)
39
+ selections_to_prepend = [selection].reject { |s| existing_selections.include?(s.name) }
40
+ node.selections = selections_to_prepend + node.selections
41
+ }
42
+
43
+ visitor = Visitor.new(@document)
44
+ visitor[Nodes::Field].leave << on_selections
45
+ visitor[Nodes::FragmentDefinition].leave << on_selections
46
+ visitor[Nodes::InlineFragment].leave << on_selections
47
+ visitor[Nodes::OperationDefinition].leave << on_selections
48
+ visitor.visit
49
+
50
+ nil
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,40 @@
1
+ require "graphql"
2
+
3
+ module GraphQL
4
+ module Language
5
+ module OperationSlice
6
+ # Public: Return's minimal document to represent operation.
7
+ #
8
+ # Find's target operation and any fragment dependencies and returns a
9
+ # new document with just those definitions.
10
+ #
11
+ # document - The Nodes::Document to find definitions.
12
+ # operation_name - The String name of Nodes::OperationDefinition
13
+ #
14
+ # Returns new Nodes::Document.
15
+ def self.slice(document, operation_name)
16
+ seen = Set.new([operation_name])
17
+ stack = [operation_name]
18
+
19
+ while name = stack.pop
20
+ names = find_definition_fragment_spreads(document, name)
21
+ seen.merge(names)
22
+ stack.concat(names.to_a)
23
+ end
24
+
25
+ Nodes::Document.new(definitions: document.definitions.select { |node| seen.include?(node.name) })
26
+ end
27
+
28
+ def self.find_definition_fragment_spreads(document, definition_name)
29
+ definition = document.definitions.find { |node| node.name == definition_name }
30
+ spreads = Set.new
31
+ visitor = Visitor.new(definition)
32
+ visitor[Nodes::FragmentSpread].enter << -> (node, parent) {
33
+ spreads << node.name
34
+ }
35
+ visitor.visit
36
+ spreads
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,27 @@
1
+ require "graphql"
2
+
3
+ module GraphQL
4
+ module Relay
5
+ module Parser
6
+ ANONYMOUS_SENTINEL = "__anonymous__".freeze
7
+
8
+ # Public: Extended GraphQL.parse that supports Relay style anonymous
9
+ # fragments.
10
+ #
11
+ # TODO: See about getting support for this upstreamed to the graphql-ruby
12
+ # gem.
13
+ #
14
+ # str - A GraphQL String
15
+ #
16
+ # Returns a GraphQL::Language::Nodes::Document.
17
+ def self.parse(str)
18
+ str = str.sub(/fragment on /, "fragment #{ANONYMOUS_SENTINEL} on ")
19
+ document = GraphQL.parse(str)
20
+ document.definitions.each do |node|
21
+ node.name = nil if node.name == ANONYMOUS_SENTINEL
22
+ end
23
+ document
24
+ end
25
+ end
26
+ end
27
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-05 00:00:00.000000000 Z
11
+ date: 2016-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -74,20 +74,11 @@ extra_rdoc_files: []
74
74
  files:
75
75
  - LICENSE
76
76
  - lib/graphql/client.rb
77
- - lib/graphql/client/document.rb
78
- - lib/graphql/client/fragment.rb
79
- - lib/graphql/client/node.rb
80
- - lib/graphql/client/query.rb
81
77
  - lib/graphql/client/query_result.rb
78
+ - lib/graphql/language/mutator.rb
82
79
  - lib/graphql/language/nodes/deep_freeze_ext.rb
83
- - lib/graphql/language/nodes/inject_selection_ext.rb
84
- - lib/graphql/language/nodes/query_result_class_ext.rb
85
- - lib/graphql/language/nodes/replace_fragment_spread_ext.rb
86
- - lib/graphql/language/nodes/selection_ext.rb
87
- - lib/graphql/language/nodes/validate_ext.rb
88
- - lib/graphql/relay/node_query.rb
89
- - lib/graphql/schema/json_loader.rb
90
- - lib/graphql/schema_load_json.rb
80
+ - lib/graphql/language/operation_slice.rb
81
+ - lib/graphql/relay/parser.rb
91
82
  homepage:
92
83
  licenses:
93
84
  - MIT
@@ -1,48 +0,0 @@
1
- require "graphql"
2
- require "graphql/client/fragment"
3
- require "graphql/client/node"
4
- require "graphql/client/query"
5
- require "graphql/language/nodes/deep_freeze_ext"
6
- require "graphql/language/nodes/inject_selection_ext"
7
- require "graphql/language/nodes/replace_fragment_spread_ext"
8
- require "graphql/language/nodes/validate_ext"
9
-
10
- module GraphQL
11
- module Client
12
- class Document < Node
13
- def self.parse(str, schema: GraphQL::Client.schema)
14
- str = str.strip
15
- str, fragments = scan_interpolated_fragments(str)
16
-
17
- document = GraphQL.parse(str)
18
- document = document.inject_selection(GraphQL::Language::Nodes::Field.new(name: "__typename"))
19
-
20
- document.definitions.each do |definition|
21
- fragments[definition.name.to_sym] = definition if definition.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
22
- end
23
-
24
- document = document.replace_fragment_spread(fragments)
25
- document = document.replace_fragment_spread(fragments) # XXX: Multiple pass
26
-
27
- document.definitions.inject({}) do |doc, definition|
28
- name = definition.name.to_sym
29
-
30
- case definition
31
- when GraphQL::Language::Nodes::OperationDefinition
32
- query = GraphQL::Client::Query.new(definition.deep_freeze, fragments.values).freeze
33
- query.node.validate!(schema: schema) if schema
34
- doc[name] = query
35
-
36
- when GraphQL::Language::Nodes::FragmentDefinition
37
- definition = GraphQL::Language::Nodes::InlineFragment.new(type: definition.type, directives: definition.directives, selections: definition.selections)
38
- fragment = GraphQL::Client::Fragment.new(definition.deep_freeze, fragments.values).freeze
39
- fragment.node.validate!(schema: schema) if schema
40
- doc[name] = fragment
41
- end
42
-
43
- doc
44
- end
45
- end
46
- end
47
- end
48
- end
@@ -1,32 +0,0 @@
1
- require "graphql"
2
- require "graphql/client/node"
3
- require "graphql/language/nodes/deep_freeze_ext"
4
- require "graphql/language/nodes/inject_selection_ext"
5
- require "graphql/language/nodes/replace_fragment_spread_ext"
6
- require "graphql/language/nodes/validate_ext"
7
-
8
- module GraphQL
9
- module Client
10
- class Fragment < Node
11
- def self.parse(str, schema: GraphQL::Client.schema)
12
- str = str.strip
13
- str, fragments = scan_interpolated_fragments(str)
14
-
15
- if str.start_with?("fragment")
16
- str = str.sub(/^fragment on /, "fragment __anonymous__ on ")
17
- doc = GraphQL.parse(str)
18
- doc = doc.inject_selection(GraphQL::Language::Nodes::Field.new(name: "__typename"))
19
- doc = doc.replace_fragment_spread(fragments)
20
- fragment = doc.definitions.first
21
- node = GraphQL::Language::Nodes::InlineFragment.new(type: fragment.type, directives: fragment.directives, selections: fragment.selections)
22
- else
23
- raise ArgumentError, "expected string to be a fragment:\n#{str}"
24
- end
25
-
26
- fragment = new(node.deep_freeze, fragments.values).freeze
27
- fragment.node.validate!(schema: schema) if schema
28
- fragment
29
- end
30
- end
31
- end
32
- end
@@ -1,36 +0,0 @@
1
- require "active_support/inflector"
2
- require "graphql"
3
- require "graphql/language/nodes/query_result_class_ext"
4
-
5
- module GraphQL
6
- module Client
7
- class Node
8
- def self.scan_interpolated_fragments(str)
9
- fragments, index = {}, 1
10
- str = str.gsub(/\.\.\.([a-zA-Z0-9_]+(::[a-zA-Z0-9_]+)+)/) { |m|
11
- index += 1
12
- name = "__fragment#{index}__"
13
- fragments[name.to_sym] = ActiveSupport::Inflector.constantize($1).node
14
- "...#{name}"
15
- }
16
- return str, fragments
17
- end
18
-
19
- attr_reader :node
20
-
21
- attr_reader :fragments
22
-
23
- attr_reader :type
24
-
25
- def initialize(node, fragments)
26
- @node = node
27
- @fragments = fragments
28
- @type = node.query_result_class(shadow: fragments)
29
- end
30
-
31
- def new(*args)
32
- type.new(*args)
33
- end
34
- end
35
- end
36
- end
@@ -1,30 +0,0 @@
1
- require "graphql"
2
- require "graphql/client/node"
3
- require "graphql/language/nodes/deep_freeze_ext"
4
- require "graphql/language/nodes/inject_selection_ext"
5
- require "graphql/language/nodes/replace_fragment_spread_ext"
6
- require "graphql/language/nodes/validate_ext"
7
-
8
- module GraphQL
9
- module Client
10
- class Query < Node
11
- def self.parse(str, schema: GraphQL::Client.schema)
12
- str = str.strip
13
- str, fragments = scan_interpolated_fragments(str)
14
-
15
- if str.start_with?("query")
16
- doc = GraphQL.parse(str)
17
- doc = doc.inject_selection(GraphQL::Language::Nodes::Field.new(name: "__typename"))
18
- doc = doc.replace_fragment_spread(fragments)
19
- node = doc.definitions.first
20
- else
21
- raise ArgumentError, "expected string to be a query:\n#{str}"
22
- end
23
-
24
- query = new(node.deep_freeze, fragments.values).freeze
25
- query.node.validate!(schema: schema) if schema
26
- query
27
- end
28
- end
29
- end
30
- end
@@ -1,44 +0,0 @@
1
- require "graphql"
2
- require "graphql/language/nodes/selection_ext"
3
-
4
- module GraphQL
5
- module Language
6
- module Nodes
7
- module Selections
8
- def inject_selection(*selections)
9
- other = self.dup
10
- other.selections = selections + self.selections.map do |selection|
11
- case selection
12
- when Selections
13
- selection.inject_selection(*selections)
14
- else
15
- selection
16
- end
17
- end
18
- other
19
- end
20
- end
21
-
22
- class Document < AbstractNode
23
- def inject_selection(*args)
24
- other = self.dup
25
- other.definitions = self.definitions.map do |definition|
26
- case definition
27
- when Selections
28
- definition.inject_selection(*args)
29
- else
30
- definition
31
- end
32
- end
33
- other
34
- end
35
- end
36
-
37
- class Field < AbstractNode
38
- def inject_selection(*selections)
39
- self.selections.any? ? super : self
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,70 +0,0 @@
1
- require "graphql"
2
- require "graphql/language/nodes/selection_ext"
3
- require "graphql/client/query_result"
4
- require "set"
5
-
6
- module GraphQL
7
- module Language
8
- module Nodes
9
- module Selections
10
- # Public: Get GraphQL::QueryResult class for result of query.
11
- #
12
- # Returns subclass of QueryResult or nil.
13
- def query_result_class(**kargs)
14
- GraphQL::Client::QueryResult.define(fields: selections_query_result_classes(**kargs))
15
- end
16
-
17
- def selection_query_result_classes(**kargs)
18
- selections_query_result_classes(**kargs)
19
- end
20
-
21
- # Internal: Gather QueryResult classes for each selection.
22
- #
23
- # Returns a Hash[String => (QueryResult|nil)].
24
- def selections_query_result_classes(shadow: Set.new, **kargs)
25
- self.selections.inject({}) do |h, selection|
26
- case selection
27
- when Selection
28
- if !shadow.include?(selection)
29
- selection.selection_query_result_classes(shadow: shadow, **kargs).each do |name, klass|
30
- h[name] ? h[name] |= klass : h[name] = klass
31
- end
32
- end
33
- h
34
- else
35
- raise TypeError, "expected selection to be of type Selection, but was #{selection.class}"
36
- end
37
- end
38
- end
39
- end
40
-
41
- class Field < AbstractNode
42
- # Public: Get GraphQL::QueryResult class for result of query.
43
- #
44
- # Returns subclass of QueryResult or nil.
45
- def query_result_class(**kargs)
46
- if self.selections.any?
47
- super
48
- else
49
- nil
50
- end
51
- end
52
-
53
- def selection_query_result_classes(**kargs)
54
- name = self.alias || self.name
55
- { name => query_result_class(**kargs) }
56
- end
57
- end
58
-
59
- class FragmentSpread < AbstractNode
60
- def selection_query_result_classes(fragments: {}, shadow: Set.new, **kargs)
61
- unless fragment = fragments[name.to_sym]
62
- raise ArgumentError, "missing fragment '#{name}'"
63
- end
64
- return {} if shadow.include?(fragment)
65
- fragment.selection_query_result_classes(fragments: fragments, shadow: shadow, **kargs)
66
- end
67
- end
68
- end
69
- end
70
- end
@@ -1,44 +0,0 @@
1
- require "graphql"
2
- require "graphql/language/nodes/selection_ext"
3
-
4
- module GraphQL
5
- module Language
6
- module Nodes
7
- module Selections
8
- def replace_fragment_spread(fragments)
9
- other = self.dup
10
- other.selections = self.selections.map do |selection|
11
- case selection
12
- when FragmentSpread
13
- if fragment = fragments[selection.name.to_sym]
14
- InlineFragment.new(type: fragment.type, directives: fragment.directives, selections: fragment.selections)
15
- else
16
- selection
17
- end
18
- when Selections
19
- selection.replace_fragment_spread(fragments)
20
- else
21
- selection
22
- end
23
- end
24
- other
25
- end
26
- end
27
-
28
- class Document < AbstractNode
29
- def replace_fragment_spread(fragments)
30
- other = self.dup
31
- other.definitions = self.definitions.map do |definition|
32
- case definition
33
- when Selections
34
- definition.replace_fragment_spread(fragments)
35
- else
36
- definition
37
- end
38
- end
39
- other
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,37 +0,0 @@
1
- require "graphql"
2
-
3
- module GraphQL
4
- module Language
5
- module Nodes
6
- # Public: Define shared trait for Nodes that have a "selections" collection.
7
- module Selections
8
- end
9
-
10
- # Public: Define shared trait for Nodes that may be in a "selections" collection.
11
- module Selection
12
- end
13
-
14
- class Field < AbstractNode
15
- include Selection
16
- include Selections
17
- end
18
-
19
- class FragmentDefinition < AbstractNode
20
- include Selections
21
- end
22
-
23
- class FragmentSpread < AbstractNode
24
- include Selection
25
- end
26
-
27
- class InlineFragment < AbstractNode
28
- include Selection
29
- include Selections
30
- end
31
-
32
- class OperationDefinition < AbstractNode
33
- include Selections
34
- end
35
- end
36
- end
37
- end
@@ -1,45 +0,0 @@
1
- require "graphql"
2
-
3
- module GraphQL
4
- class ValidationError < GraphQL::ExecutionError
5
- end
6
-
7
- module Language
8
- module Nodes
9
- class Document < AbstractNode
10
- def validate!(schema:, rules: StaticValidation::ALL_RULES)
11
- validator = StaticValidation::Validator.new(schema: schema, rules: rules)
12
- query = Query.new(schema, document: self)
13
-
14
- validator.validate(query).fetch(:errors).each do |error|
15
- raise ValidationError, error["message"]
16
- end
17
-
18
- nil
19
- end
20
- end
21
-
22
- class FragmentDefinition < AbstractNode
23
- def validate!(schema:, **kargs)
24
- document = Document.new(definitions: [self])
25
- rules = StaticValidation::ALL_RULES - [StaticValidation::FragmentsAreUsed]
26
- document.validate!(schema: schema, rules: rules, **kargs)
27
- end
28
- end
29
-
30
- class OperationDefinition < AbstractNode
31
- def validate!(schema:, **kargs)
32
- document = Document.new(definitions: [self])
33
- document.validate!(schema: schema, **kargs)
34
- end
35
- end
36
-
37
- class InlineFragment < AbstractNode
38
- def validate!(schema:, **kargs)
39
- fragment = FragmentDefinition.new(name: "FooFragment", type: self.type, directives: self.directives, selections: self.selections)
40
- fragment.validate!(schema: schema, **kargs)
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,19 +0,0 @@
1
- require "graphql"
2
- require "graphql/language/nodes/deep_freeze_ext"
3
-
4
- module GraphQL
5
- module Relay
6
- NODE_QUERY = GraphQL.parse(<<-'GRAPHQL').definitions.first.deep_freeze
7
- query($id: ID!) {
8
- node(id: $id) {
9
- ...NodeFragment
10
- }
11
- }
12
- GRAPHQL
13
-
14
- def self.NodeQuery(fragment)
15
- fragment = GraphQL::Language::Nodes::FragmentDefinition.new(name: "NodeFragment", type: fragment.type, selections: fragment.selections)
16
- GraphQL::Language::Nodes::Document.new(definitions: [NODE_QUERY, fragment])
17
- end
18
- end
19
- end
@@ -1,101 +0,0 @@
1
- require "graphql"
2
- require "json"
3
-
4
- module GraphQL
5
- class Schema
6
- module JSONLoader
7
- def self.define_schema(json)
8
- schema = JSON.load(json).fetch("data").fetch("__schema")
9
- types = Schema::JSONLoader.define_types(schema)
10
- # TODO: handle schema["mutationType"]
11
- # TODO: handle schema["subscriptionType"]
12
- query = types.fetch(schema.fetch("queryType").fetch("name"))
13
- Schema.new(query: query, types: types.values)
14
- end
15
-
16
- def self.define_types(schema)
17
- schema.fetch("types").inject({}) do |types, type|
18
- type_kind, type_name = type.fetch("kind"), type.fetch("name")
19
-
20
- if !type_name.start_with?("__")
21
- case type_kind
22
- when "INTERFACE"
23
- types[type_name] = define_interface(types, type)
24
- when "OBJECT"
25
- types[type_name] = define_object(types, type)
26
- when "SCALAR"
27
- types[type_name] = define_scalar(types, type)
28
- else
29
- # TODO: handle other type kinds
30
- fail NotImplementedError, type_kind + " not implemented"
31
- end
32
- end
33
-
34
- types
35
- end
36
- end
37
-
38
- def self.resolve_type(types, type)
39
- case kind = type.fetch("kind")
40
- when "INTERFACE"
41
- types.fetch(type.fetch("name"))
42
- when "LIST"
43
- ListType.new(of_type: resolve_type(types, type.fetch("ofType")))
44
- when "NON_NULL"
45
- NonNullType.new(of_type: resolve_type(types, type.fetch("ofType")))
46
- when "OBJECT"
47
- types.fetch(type.fetch("name"))
48
- when "SCALAR"
49
- types.fetch(type.fetch("name"))
50
- else
51
- # TODO: handle other type kinds
52
- fail NotImplementedError, kind + " not implemented"
53
- end
54
- end
55
-
56
- def self.define_interface(types, type)
57
- InterfaceType.define do
58
- name type.fetch("name")
59
- end
60
- end
61
-
62
- def self.define_object(types, type)
63
- ObjectType.define do
64
- name type.fetch("name")
65
- description type["description"]
66
-
67
- Array(type["fields"]).each do |field_data|
68
- field field_data["name"] do
69
- type JSONLoader.resolve_type(types, field_data["type"])
70
- description field_data["description"]
71
- field_data["args"].each do |arg|
72
- argument arg["name"] do
73
- type JSONLoader.resolve_type(types, arg["type"])
74
- description arg["description"]
75
- end
76
- end
77
- end
78
- end
79
- end
80
- end
81
-
82
- def self.define_scalar(types, type)
83
- case name = type.fetch("name")
84
- when "Int"
85
- INT_TYPE
86
- when "String"
87
- STRING_TYPE
88
- when "Float"
89
- FLOAT_TYPE
90
- when "Boolean"
91
- BOOLEAN_TYPE
92
- when "ID"
93
- ID_TYPE
94
- else
95
- # TODO: handle other scalar names
96
- fail NotImplementedError, name + " scalar not implemented"
97
- end
98
- end
99
- end
100
- end
101
- end
@@ -1,10 +0,0 @@
1
- require "graphql"
2
- require "graphql/schema/json_loader"
3
-
4
- module GraphQL
5
- class Schema
6
- def self.load_json(json)
7
- Schema::JSONLoader.define_schema(json)
8
- end
9
- end
10
- end