graphql-client 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f327b5063c82073f7208c75d7a8e178694b67ce5
4
- data.tar.gz: 2fba5fd397e36ff3f3cd81cc1515a9dd2573476a
3
+ metadata.gz: 852686ecf24561871545329aeb4868baaccb99de
4
+ data.tar.gz: 2bd0845ad82f0ba555497e0a46c315a9d8a6fa0f
5
5
  SHA512:
6
- metadata.gz: 55bf804e30c3d080d899d49c2016aab6932699565a95d0eae7e90a16bfcd645077a179ac11c08aba366bb583b4ee7ddf73dafad57bea2c0851f081587a6a0191
7
- data.tar.gz: f1fe23003faefe5a176c696cfe6fb68b63cb2edc6c0c14bfb65ffb12c823943e4518bffb39b718d5f04270f3f8e8be224280a4990e3e527212b83ba83dcee97c
6
+ metadata.gz: 58701e7031146155bcf9584bf61cdbee25f6ac9bf61121eccd5e915970624d2755aef071f18c781d38fcbf3ca1878fccff4f22d33b7dbe3c537598a597658f23
7
+ data.tar.gz: 8dfe674b8d2c39233fcdfd533e8ea516c010e0cecb7be38fb01c2128f76dc466e61e7dd334e0dff36e810465d9e9f6064d7c5f9969de9e65e081fa0dba0bc9ea
@@ -1,14 +1,100 @@
1
+ require "active_support/inflector"
1
2
  require "graphql"
2
- require "graphql/client/document"
3
- require "graphql/client/fragment"
4
- require "graphql/client/node"
3
+ require "graphql/client/const_proxy"
5
4
  require "graphql/client/query_result"
6
- require "graphql/client/query"
5
+ require "graphql/language/mutator"
6
+ require "graphql/language/nodes/deep_freeze_ext"
7
+ require "graphql/language/operation_slice"
8
+ require "graphql/relay/parser"
7
9
 
8
10
  module GraphQL
9
- module Client
10
- class << self
11
- attr_accessor :schema
11
+ class Client
12
+ class Error < StandardError; end
13
+ class ValidationError < Error; end
14
+
15
+ attr_reader :schema
16
+
17
+ def initialize(schema:)
18
+ @schema = schema
19
+ @definitions = []
20
+ end
21
+
22
+ class Definition
23
+ def initialize(name:, client:, source:)
24
+ @name = name
25
+ @client = client
26
+ @_nodes = @client._parse(@name, source)
27
+ @query_result = GraphQL::Client::QueryResult.wrap(@_nodes.first, name: name)
28
+ end
29
+
30
+ attr_reader :_nodes
31
+
32
+ def operation_name
33
+ if op = @_nodes.find { |d| d.is_a?(GraphQL::Language::Nodes::OperationDefinition) }
34
+ op.name
35
+ else
36
+ nil
37
+ end
38
+ end
39
+
40
+ def document
41
+ @document ||= Language::OperationSlice.slice(@client.document, operation_name).deep_freeze
42
+ end
43
+
44
+ def new(*args)
45
+ @query_result.new(*args)
46
+ end
47
+ end
48
+
49
+ def parse(str)
50
+ definition = ConstProxy.new { |name| Definition.new(client: self, name: name, source: str) }
51
+ @definitions << definition
52
+ definition
53
+ end
54
+
55
+ def _parse(name, str)
56
+ str = str.strip
57
+
58
+ str = str.gsub(/\.\.\.([a-zA-Z0-9_]+(::[a-zA-Z0-9_]+)+)(\.([a-zA-Z0-9_]+))?/) { |m|
59
+ const_name, fragment_name = $1, $4
60
+ nodes = ActiveSupport::Inflector.constantize(const_name)._nodes
61
+
62
+ fragment_name = fragment_name ?
63
+ nodes.find { |n| n.name.end_with?(fragment_name) }.name : # XXX
64
+ nodes.first.name
65
+
66
+ "...#{fragment_name}"
67
+ }
68
+
69
+ doc = GraphQL::Relay::Parser.parse(str)
70
+
71
+ mutator = GraphQL::Language::Mutator.new(doc)
72
+
73
+ aliases = {}
74
+ doc.definitions.each do |definition|
75
+ aliases[definition.name] = (name.split("::") << definition.name).compact.join("__")
76
+ end
77
+ mutator.rename_definitions(aliases)
78
+
79
+ # TODO: Make this __typename injection optional
80
+ mutator.prepend_selection(GraphQL::Language::Nodes::Field.new(name: "__typename").deep_freeze)
81
+
82
+ doc.definitions.map(&:deep_freeze)
83
+ end
84
+
85
+ def document
86
+ GraphQL::Language::Nodes::Document.new(definitions: @definitions.flat_map(&:_nodes)).deep_freeze
87
+ end
88
+
89
+ def validate!
90
+ validator = StaticValidation::Validator.new(schema: @schema)
91
+ query = Query.new(@schema, document: document)
92
+
93
+ validator.validate(query).fetch(:errors).each do |error|
94
+ raise ValidationError, error["message"]
95
+ end
96
+
97
+ nil
12
98
  end
13
99
  end
14
100
  end
@@ -0,0 +1,29 @@
1
+ module GraphQL
2
+ class Client
3
+ # Internal: Hack to track the constant name an object is assigned to.
4
+ #
5
+ # FooConstant = ConstProxy.new { |name|
6
+ # name # "FooConstant"
7
+ # }
8
+ #
9
+ module ConstProxy
10
+ def self.new(&initializer)
11
+ raise ArgumentError, "initializer required" unless block_given?
12
+
13
+ Module.new do
14
+ extend ConstProxy
15
+ @initializer = initializer
16
+ end
17
+ end
18
+
19
+ def name
20
+ super || raise(RuntimeError, "expected object to be assigned to a constant")
21
+ end
22
+
23
+ def method_missing(*args, &block)
24
+ @target ||= @initializer.call(self.name)
25
+ @target.send(*args, &block)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -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 << ">"
@@ -0,0 +1,45 @@
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
+ def rename_definitions(definitions)
12
+ rename_node = -> (node, parent) {
13
+ node.name = definitions.fetch(node.name, node.name)
14
+ }
15
+
16
+ visitor = Visitor.new(@document)
17
+ visitor[Nodes::FragmentDefinition].leave << rename_node
18
+ visitor[Nodes::OperationDefinition].leave << rename_node
19
+ visitor[Nodes::FragmentSpread].leave << rename_node
20
+ visitor.visit
21
+
22
+ nil
23
+ end
24
+
25
+ def prepend_selection(selection)
26
+ on_selections = -> (node, parent) {
27
+ return if !node.selections.any?
28
+ # TODO: Simplify if AbstractNode#eql? is implemented
29
+ existing_selections = Set.new(node.selections.map { |s| s.respond_to?(:name) ? s.name : nil }.compact)
30
+ selections_to_prepend = [selection].reject { |s| existing_selections.include?(s.name) }
31
+ node.selections = selections_to_prepend + node.selections
32
+ }
33
+
34
+ visitor = Visitor.new(@document)
35
+ visitor[Nodes::Field].leave << on_selections
36
+ visitor[Nodes::FragmentDefinition].leave << on_selections
37
+ visitor[Nodes::InlineFragment].leave << on_selections
38
+ visitor[Nodes::OperationDefinition].leave << on_selections
39
+ visitor.visit
40
+
41
+ nil
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
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
+ definitions = []
17
+ definitions << document.definitions.find { |d| d.name == operation_name }
18
+
19
+ visitor = Visitor.new(document)
20
+ visitor[Nodes::FragmentSpread] << -> (node, parent) {
21
+ if fragment = document.definitions.find { |d| d.name == node.name }
22
+ definitions << fragment
23
+ end
24
+ }
25
+ visitor.visit
26
+
27
+ Nodes::Document.new(definitions: definitions.uniq)
28
+ end
29
+ end
30
+ end
31
+ 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.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-19 00:00:00.000000000 Z
11
+ date: 2016-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -74,20 +74,12 @@ 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
77
+ - lib/graphql/client/const_proxy.rb
81
78
  - lib/graphql/client/query_result.rb
79
+ - lib/graphql/language/mutator.rb
82
80
  - 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
81
+ - lib/graphql/language/operation_slice.rb
82
+ - lib/graphql/relay/parser.rb
91
83
  homepage:
92
84
  licenses:
93
85
  - MIT
@@ -108,9 +100,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
100
  version: '0'
109
101
  requirements: []
110
102
  rubyforge_project:
111
- rubygems_version: 2.5.1
103
+ rubygems_version: 2.4.5.1
112
104
  signing_key:
113
105
  specification_version: 4
114
106
  summary: "???"
115
107
  test_files: []
116
- has_rdoc:
@@ -1,47 +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
-
26
- document.definitions.inject({}) do |doc, definition|
27
- name = definition.name.to_sym
28
-
29
- case definition
30
- when GraphQL::Language::Nodes::OperationDefinition
31
- query = GraphQL::Client::Query.new(definition.deep_freeze, fragments.values).freeze
32
- query.node.validate!(schema: schema) if schema
33
- doc[name] = query
34
-
35
- when GraphQL::Language::Nodes::FragmentDefinition
36
- definition = GraphQL::Language::Nodes::InlineFragment.new(type: definition.type, directives: definition.directives, selections: definition.selections)
37
- fragment = GraphQL::Client::Fragment.new(definition.deep_freeze, fragments.values).freeze
38
- fragment.node.validate!(schema: schema) if schema
39
- doc[name] = fragment
40
- end
41
-
42
- doc
43
- end
44
- end
45
- end
46
- end
47
- 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