graphql-client 0.0.18 → 0.0.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/client.rb +48 -129
- data/lib/graphql/client/error.rb +5 -0
- data/lib/graphql/client/erubis.rb +27 -3
- data/lib/graphql/client/http.rb +41 -5
- data/lib/graphql/client/log_subscriber.rb +24 -4
- data/lib/graphql/client/query.rb +65 -0
- data/lib/graphql/client/query_result.rb +21 -20
- data/lib/graphql/client/railtie.rb +11 -5
- data/lib/graphql/client/response.rb +87 -0
- data/lib/graphql/client/view_module.rb +9 -11
- data/lib/graphql/language/nodes/deep_freeze_ext.rb +1 -3
- data/lib/graphql/language/operation_slice.rb +4 -4
- metadata +32 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c087161bf28d989bf274ba68bf76223ff4cc7b84
|
4
|
+
data.tar.gz: 3054f8552e6e6a532ebd09a15e7f5914b8dfa994
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4044f2bf520a94bcaf5f6a918baf4daa8eb72479f7fd9de12bacfe58f40a362f57d675207ef0a25d9508113ccc3a0985c2095fa4534385ab4d33b08db2592815
|
7
|
+
data.tar.gz: 8d19a14603c36df798b2280432d54e5dceb98d31a1d949c10bd2ac349b82dcf559359253e10b51a3d655f3e3cf22505f1b4ea97ff4a9acdf8e1992e91c462f9b
|
data/lib/graphql/client.rb
CHANGED
@@ -1,39 +1,17 @@
|
|
1
1
|
require "active_support/inflector"
|
2
2
|
require "active_support/notifications"
|
3
3
|
require "graphql"
|
4
|
+
require "graphql/client/error"
|
4
5
|
require "graphql/client/query_result"
|
6
|
+
require "graphql/client/query"
|
7
|
+
require "graphql/client/response"
|
5
8
|
require "graphql/language/nodes/deep_freeze_ext"
|
6
9
|
require "graphql/language/operation_slice"
|
7
10
|
|
8
11
|
module GraphQL
|
9
12
|
class Client
|
10
|
-
class Error < StandardError; end
|
11
13
|
class ValidationError < Error; end
|
12
14
|
|
13
|
-
class ResponseError < Error
|
14
|
-
def initialize(definition, error)
|
15
|
-
@request_definition = definition
|
16
|
-
@locations = error["locations"]
|
17
|
-
super error["message"]
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
class ResponseErrors < Error
|
22
|
-
include Enumerable
|
23
|
-
|
24
|
-
attr_reader :errors
|
25
|
-
|
26
|
-
def initialize(definition, errors)
|
27
|
-
@request_definition = definition
|
28
|
-
@errors = errors.map { |error| ResponseError.new(definition, error) }
|
29
|
-
super @errors.map(&:message).join(", ")
|
30
|
-
end
|
31
|
-
|
32
|
-
def each(&block)
|
33
|
-
errors.each(&block)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
15
|
attr_reader :schema, :fetch
|
38
16
|
|
39
17
|
attr_accessor :document_tracking_enabled
|
@@ -50,8 +28,6 @@ module GraphQL
|
|
50
28
|
else
|
51
29
|
load_schema(JSON.parse(schema))
|
52
30
|
end
|
53
|
-
else
|
54
|
-
nil
|
55
31
|
end
|
56
32
|
end
|
57
33
|
|
@@ -114,34 +90,49 @@ module GraphQL
|
|
114
90
|
end
|
115
91
|
|
116
92
|
private
|
117
|
-
|
118
|
-
|
119
|
-
|
93
|
+
|
94
|
+
def query_result_class
|
95
|
+
@query_result_class ||= GraphQL::Client::QueryResult.wrap(definition_node, name: name)
|
96
|
+
end
|
120
97
|
end
|
121
98
|
|
122
99
|
class OperationDefinition < Definition
|
123
100
|
# Public: Alias for definition name.
|
124
|
-
|
101
|
+
alias operation_name definition_name
|
125
102
|
end
|
126
103
|
|
127
104
|
class FragmentDefinition < Definition
|
128
105
|
end
|
129
106
|
|
130
|
-
def parse(str)
|
107
|
+
def parse(str, filename = nil, lineno = nil)
|
108
|
+
if filename.nil? || lineno.nil?
|
109
|
+
filename, lineno, = caller(1, 1).first.split(":", 3)
|
110
|
+
lineno = lineno.to_i
|
111
|
+
end
|
112
|
+
|
131
113
|
definition_dependencies = Set.new
|
132
114
|
|
133
|
-
str = str.gsub(/\.\.\.([a-zA-Z0-9_]+(::[a-zA-Z0-9_]+)+)/)
|
134
|
-
|
115
|
+
str = str.gsub(/\.\.\.([a-zA-Z0-9_]+(::[a-zA-Z0-9_]+)+)/) do
|
116
|
+
match = Regexp.last_match
|
117
|
+
const_name = match[1]
|
135
118
|
case fragment = ActiveSupport::Inflector.safe_constantize(const_name)
|
136
119
|
when FragmentDefinition
|
137
120
|
definition_dependencies.merge(fragment.document.definitions)
|
138
121
|
"...#{fragment.definition_name}"
|
139
|
-
when nil
|
140
|
-
raise NameError, "uninitialized constant #{const_name}\n#{str}"
|
141
122
|
else
|
142
|
-
|
123
|
+
if fragment
|
124
|
+
error = TypeError.new("expected #{const_name} to be a #{FragmentDefinition}, but was a #{fragment.class}")
|
125
|
+
else
|
126
|
+
error = NameError.new("uninitialized constant #{const_name}")
|
127
|
+
end
|
128
|
+
|
129
|
+
if filename && lineno
|
130
|
+
error.set_backtrace(["#{filename}:#{lineno + match.pre_match.count("\n") + 1}"] + caller)
|
131
|
+
end
|
132
|
+
|
133
|
+
raise error
|
143
134
|
end
|
144
|
-
|
135
|
+
end
|
145
136
|
|
146
137
|
doc = GraphQL.parse(str)
|
147
138
|
|
@@ -149,8 +140,7 @@ module GraphQL
|
|
149
140
|
node.name ||= "__anonymous__"
|
150
141
|
end
|
151
142
|
|
152
|
-
|
153
|
-
document_dependencies = Language::Nodes::Document.new(definitions: definition_dependencies.to_a)
|
143
|
+
document_dependencies = Language::Nodes::Document.new(definitions: doc.definitions + definition_dependencies.to_a)
|
154
144
|
|
155
145
|
if @schema
|
156
146
|
rules = GraphQL::StaticValidation::ALL_RULES - [GraphQL::StaticValidation::FragmentsAreUsed]
|
@@ -159,7 +149,10 @@ module GraphQL
|
|
159
149
|
|
160
150
|
errors = validator.validate(query)
|
161
151
|
errors.fetch(:errors).each do |error|
|
162
|
-
|
152
|
+
validation_line = error["locations"][0]["line"]
|
153
|
+
error = ValidationError.new(error["message"])
|
154
|
+
error.set_backtrace(["#{filename}:#{lineno + validation_line}"] + caller) if filename && lineno
|
155
|
+
raise error
|
163
156
|
end
|
164
157
|
end
|
165
158
|
|
@@ -171,12 +164,13 @@ module GraphQL
|
|
171
164
|
definitions[node.name] = definition
|
172
165
|
end
|
173
166
|
|
174
|
-
rename_node = ->
|
175
|
-
|
167
|
+
rename_node = ->(node, _parent) do
|
168
|
+
definition = definitions[node.name]
|
169
|
+
if definition
|
176
170
|
node.extend(LazyName)
|
177
171
|
node.name = -> { definition.definition_name }
|
178
172
|
end
|
179
|
-
|
173
|
+
end
|
180
174
|
visitor = Language::Visitor.new(doc)
|
181
175
|
visitor[Language::Nodes::FragmentDefinition].leave << rename_node
|
182
176
|
visitor[Language::Nodes::OperationDefinition].leave << rename_node
|
@@ -185,9 +179,7 @@ module GraphQL
|
|
185
179
|
|
186
180
|
doc.deep_freeze
|
187
181
|
|
188
|
-
if document_tracking_enabled
|
189
|
-
self.document.definitions.concat(doc.definitions)
|
190
|
-
end
|
182
|
+
document.definitions.concat(doc.definitions) if document_tracking_enabled
|
191
183
|
|
192
184
|
if definitions[nil]
|
193
185
|
definitions[nil]
|
@@ -200,87 +192,15 @@ module GraphQL
|
|
200
192
|
end
|
201
193
|
end
|
202
194
|
|
203
|
-
|
204
|
-
@document
|
205
|
-
end
|
206
|
-
|
207
|
-
class Response
|
208
|
-
attr_reader :extensions
|
209
|
-
|
210
|
-
def initialize(extensions: nil)
|
211
|
-
@extensions = extensions || {}
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
class SuccessfulResponse < Response
|
216
|
-
attr_reader :data
|
217
|
-
|
218
|
-
def initialize(data:, **kargs)
|
219
|
-
@data = data
|
220
|
-
super(**kargs)
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
class PartialResponse < SuccessfulResponse
|
225
|
-
attr_reader :errors
|
226
|
-
|
227
|
-
def initialize(errors:, **kargs)
|
228
|
-
@errors = errors
|
229
|
-
super(**kargs)
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
class FailedResponse < Response
|
234
|
-
attr_reader :errors
|
235
|
-
|
236
|
-
def initialize(errors:, **kargs)
|
237
|
-
@errors = errors
|
238
|
-
super(**kargs)
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
class Query
|
243
|
-
attr_reader :document, :operation_name, :variables, :context
|
244
|
-
|
245
|
-
def initialize(document, operation_name: nil, variables: {}, context: {})
|
246
|
-
@document = document
|
247
|
-
@operation_name = operation_name
|
248
|
-
@variables = variables
|
249
|
-
@context = context
|
250
|
-
end
|
251
|
-
|
252
|
-
def to_s
|
253
|
-
document.to_query_string
|
254
|
-
end
|
255
|
-
|
256
|
-
def operation
|
257
|
-
document.definitions.find { |node| node.name == operation_name }
|
258
|
-
end
|
259
|
-
|
260
|
-
def operation_type
|
261
|
-
operation.operation_type
|
262
|
-
end
|
263
|
-
|
264
|
-
def payload
|
265
|
-
{
|
266
|
-
document: document,
|
267
|
-
operation_name: operation_name,
|
268
|
-
operation_type: operation_type,
|
269
|
-
variables: variables
|
270
|
-
}
|
271
|
-
end
|
272
|
-
end
|
195
|
+
attr_reader :document
|
273
196
|
|
274
197
|
def query(definition, variables: {}, context: {})
|
275
|
-
unless fetch
|
276
|
-
raise Error, "client network fetching not configured"
|
277
|
-
end
|
198
|
+
raise Error, "client network fetching not configured" unless fetch
|
278
199
|
|
279
200
|
query = Query.new(definition.document,
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
)
|
201
|
+
operation_name: definition.operation_name,
|
202
|
+
variables: variables,
|
203
|
+
context: context)
|
284
204
|
|
285
205
|
result = ActiveSupport::Notifications.instrument("query.graphql", query.payload) do
|
286
206
|
fetch.call(query)
|
@@ -316,11 +236,10 @@ module GraphQL
|
|
316
236
|
fetch.call(IntrospectionQuery)
|
317
237
|
end
|
318
238
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
@name.call
|
323
|
-
end
|
239
|
+
module LazyName
|
240
|
+
def name
|
241
|
+
@name.call
|
324
242
|
end
|
243
|
+
end
|
325
244
|
end
|
326
245
|
end
|
@@ -2,12 +2,36 @@ require "action_view"
|
|
2
2
|
|
3
3
|
module GraphQL
|
4
4
|
class Client
|
5
|
+
# Public: Extended Erubis implementation that supports GraphQL static
|
6
|
+
# query sections.
|
7
|
+
#
|
8
|
+
# <%graphql
|
9
|
+
# query GetVerison {
|
10
|
+
# version
|
11
|
+
# }
|
12
|
+
# %>
|
13
|
+
# <%= data.version %>
|
14
|
+
#
|
15
|
+
# Configure ActionView's default ERB implementation to use this class.
|
16
|
+
#
|
17
|
+
# ActionView::Template::Handlers::ERB.erb_implementation = GraphQL::Client::Erubis
|
18
|
+
#
|
5
19
|
class Erubis < ActionView::Template::Handlers::Erubis
|
6
|
-
|
7
|
-
|
20
|
+
# Public: Extract GraphQL section from ERB template.
|
21
|
+
#
|
22
|
+
# src - String ERB text
|
23
|
+
#
|
24
|
+
# Returns String GraphQL query and line number or nil or no section was
|
25
|
+
# defined.
|
26
|
+
def self.extract_graphql_section(src)
|
27
|
+
query_string = src.scan(/<%graphql([^%]+)%>/).flatten.first
|
28
|
+
if query_string
|
29
|
+
return query_string, Regexp.last_match.pre_match.count("\n") + 1
|
30
|
+
end
|
8
31
|
end
|
9
32
|
|
10
|
-
#
|
33
|
+
# Internal: Extend Rails' Erubis handler to simply ignore <%graphql
|
34
|
+
# sections.
|
11
35
|
def convert_input(src, input)
|
12
36
|
input = input.gsub(/<%graphql/, "<%#")
|
13
37
|
super(src, input)
|
data/lib/graphql/client/http.rb
CHANGED
@@ -4,19 +4,55 @@ require "uri"
|
|
4
4
|
|
5
5
|
module GraphQL
|
6
6
|
class Client
|
7
|
+
# Public: Basic HTTP network adapter.
|
8
|
+
#
|
9
|
+
# GraphQL::Client::Client.new(
|
10
|
+
# fetch: GraphQL::Client::HTTP.new("http://graphql-swapi.parseapp.com/")
|
11
|
+
# )
|
12
|
+
#
|
13
|
+
# Assumes GraphQL endpoint follows the express-graphql endpoint conventions.
|
14
|
+
# https://github.com/graphql/express-graphql#http-usage
|
15
|
+
#
|
16
|
+
# Production applications should consider implementing there own network
|
17
|
+
# adapter. This class exists for trivial stock usage and allows for minimal
|
18
|
+
# request header configuration.
|
7
19
|
class HTTP
|
8
|
-
|
9
|
-
|
10
|
-
#
|
20
|
+
# Public: Create HTTP adapter instance for a single GraphQL endpoint.
|
21
|
+
#
|
22
|
+
# GraphQL::Client::HTTP.new("http://graphql-swapi.parseapp.com/") do
|
23
|
+
# def headers(query)
|
24
|
+
# { "User-Agent": "My Client" }
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# uri - String endpoint URI
|
29
|
+
# block - Optional block to configure class
|
11
30
|
def initialize(uri, &block)
|
12
|
-
@uri =
|
31
|
+
@uri = URI.parse(uri)
|
13
32
|
class_eval(&block) if block_given?
|
14
33
|
end
|
15
34
|
|
16
|
-
|
35
|
+
# Public: Parsed endpoint URI
|
36
|
+
#
|
37
|
+
# Returns URI.
|
38
|
+
attr_reader :uri
|
39
|
+
|
40
|
+
# Public: Extension point for subclasses to set custom request headers.
|
41
|
+
#
|
42
|
+
# query - The GraphQL::Client::Query being sent
|
43
|
+
#
|
44
|
+
# Returns Hash of String header names and values.
|
45
|
+
def headers(_)
|
17
46
|
{}
|
18
47
|
end
|
19
48
|
|
49
|
+
# Public: Make an HTTP request for GraphQL query.
|
50
|
+
#
|
51
|
+
# Implements Client's "fetch" adapter interface.
|
52
|
+
#
|
53
|
+
# query - The GraphQL::Client::Query being sent
|
54
|
+
#
|
55
|
+
# Returns { "data" => ... , "errors" => ... } Hash.
|
20
56
|
def call(query)
|
21
57
|
http = Net::HTTP.new(uri.host, uri.port)
|
22
58
|
http.use_ssl = uri.scheme == "https"
|
@@ -2,13 +2,33 @@ require "active_support/log_subscriber"
|
|
2
2
|
|
3
3
|
module GraphQL
|
4
4
|
class Client
|
5
|
+
# Public: Logger for "*.graphql" notification events.
|
6
|
+
#
|
7
|
+
# Logs GraphQL queries to Rails logger.
|
8
|
+
#
|
9
|
+
# QUERY (123ms) UsersController::ShowQuery
|
10
|
+
# MUTATION (456ms) UsersController::UpdateMutation
|
11
|
+
#
|
12
|
+
# Enable GraphQL Client query logging.
|
13
|
+
#
|
14
|
+
# require "graphql/client/log_subscriber"
|
15
|
+
# GraphQL::Client::LogSubscriber.attach_to :graphql
|
16
|
+
#
|
5
17
|
class LogSubscriber < ActiveSupport::LogSubscriber
|
6
18
|
def query(event)
|
7
|
-
|
8
|
-
|
19
|
+
# TODO: Colorize output
|
20
|
+
info do
|
21
|
+
[
|
22
|
+
event.payload[:operation_type].upcase,
|
23
|
+
"(#{event.duration.round(1)}ms)",
|
24
|
+
event.payload[:operation_name].gsub("__", "::")
|
25
|
+
].join(" ")
|
26
|
+
end
|
27
|
+
|
28
|
+
debug do
|
29
|
+
event.payload[:document].to_query_string
|
30
|
+
end
|
9
31
|
end
|
10
32
|
end
|
11
33
|
end
|
12
34
|
end
|
13
|
-
|
14
|
-
GraphQL::Client::LogSubscriber.attach_to :graphql
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module GraphQL
|
2
|
+
class Client
|
3
|
+
class Query
|
4
|
+
# Internal: Construct Query.
|
5
|
+
#
|
6
|
+
# Avoid creating queries with this constructor, perfer using Client#query.
|
7
|
+
#
|
8
|
+
# document - A parsed GraphQL::Language::Nodes::Document of the query
|
9
|
+
# operation_name - String operation to execute
|
10
|
+
# variables - Hash of variables to execute with the operation
|
11
|
+
# context - Hash of metadata to pass to network adapter
|
12
|
+
def initialize(document, operation_name: nil, variables: {}, context: {})
|
13
|
+
@document = document
|
14
|
+
@operation_name = operation_name
|
15
|
+
@variables = variables
|
16
|
+
@context = context
|
17
|
+
end
|
18
|
+
|
19
|
+
# Public: A parsed GraphQL::Language::Nodes::Document of the query.
|
20
|
+
attr_reader :document
|
21
|
+
|
22
|
+
# Public: String name of operation to execute.
|
23
|
+
attr_reader :operation_name
|
24
|
+
|
25
|
+
# Public: Hash of variables to execute with the operation.
|
26
|
+
attr_reader :variables
|
27
|
+
|
28
|
+
# Public: Hash of contextual metadata.
|
29
|
+
attr_reader :context
|
30
|
+
|
31
|
+
# Public: Serialized query string
|
32
|
+
#
|
33
|
+
# Returns String.
|
34
|
+
def to_s
|
35
|
+
document.to_query_string
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public: Get operation definition node.
|
39
|
+
#
|
40
|
+
# Returns GraphQL::Language::Nodes::OperationDefinition.
|
41
|
+
def operation
|
42
|
+
document.definitions.find { |node| node.name == operation_name }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: Query operation type
|
46
|
+
#
|
47
|
+
# Returns "query", "mutation" or "subscription".
|
48
|
+
def operation_type
|
49
|
+
operation.operation_type
|
50
|
+
end
|
51
|
+
|
52
|
+
# Internal: Payload object to pass to ActiveSupport::Notifications.
|
53
|
+
#
|
54
|
+
# Returns Hash.
|
55
|
+
def payload
|
56
|
+
{
|
57
|
+
document: document,
|
58
|
+
operation_name: operation_name,
|
59
|
+
operation_type: operation_type,
|
60
|
+
variables: variables
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -42,9 +42,7 @@ module GraphQL
|
|
42
42
|
|
43
43
|
# Convert GraphQL camelcase to snake case: commitComments -> commit_comments
|
44
44
|
field_alias = ActiveSupport::Inflector.underscore(field)
|
45
|
-
if field != field_alias
|
46
|
-
send :alias_method, field_alias, field
|
47
|
-
end
|
45
|
+
send :alias_method, field_alias, field if field != field_alias
|
48
46
|
|
49
47
|
class_eval <<-RUBY, __FILE__, __LINE__
|
50
48
|
def #{field_alias}?
|
@@ -52,15 +50,14 @@ module GraphQL
|
|
52
50
|
end
|
53
51
|
RUBY
|
54
52
|
|
55
|
-
|
56
|
-
|
53
|
+
next unless field == "edges"
|
54
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
57
55
|
def each_node
|
58
56
|
return enum_for(:each_node) unless block_given?
|
59
57
|
edges.each { |edge| yield edge.node }
|
60
58
|
self
|
61
59
|
end
|
62
|
-
|
63
|
-
end
|
60
|
+
RUBY
|
64
61
|
end
|
65
62
|
|
66
63
|
assigns = fields.map do |field, type|
|
@@ -85,12 +82,12 @@ module GraphQL
|
|
85
82
|
end
|
86
83
|
end
|
87
84
|
|
88
|
-
|
89
|
-
|
85
|
+
class << self
|
86
|
+
attr_reader :source_node
|
90
87
|
end
|
91
88
|
|
92
|
-
|
93
|
-
|
89
|
+
class << self
|
90
|
+
attr_reader :fields
|
94
91
|
end
|
95
92
|
|
96
93
|
def self.name
|
@@ -98,7 +95,7 @@ module GraphQL
|
|
98
95
|
end
|
99
96
|
|
100
97
|
def self.inspect
|
101
|
-
"#<#{
|
98
|
+
"#<#{name} fields=#{@fields.keys.inspect}>"
|
102
99
|
end
|
103
100
|
|
104
101
|
def self.cast(obj)
|
@@ -110,9 +107,9 @@ module GraphQL
|
|
110
107
|
when QueryResult
|
111
108
|
spreads = Set.new(self.spreads(obj.class.source_node).map(&:name))
|
112
109
|
|
113
|
-
|
114
|
-
message = "couldn't cast #{obj.inspect} to #{
|
115
|
-
suggestion = "\n ...#{name ||
|
110
|
+
unless spreads.include?(source_node.name)
|
111
|
+
message = "couldn't cast #{obj.inspect} to #{inspect}\n\n"
|
112
|
+
suggestion = "\n ...#{name || 'YourFragment'} # SUGGESTION"
|
116
113
|
message << GraphQL::Language::Generation.generate(obj.class.source_node).sub(/\n}$/, "#{suggestion}\n}")
|
117
114
|
raise TypeError, message
|
118
115
|
end
|
@@ -122,7 +119,7 @@ module GraphQL
|
|
122
119
|
when NilClass
|
123
120
|
nil
|
124
121
|
else
|
125
|
-
raise TypeError,
|
122
|
+
raise TypeError, obj.class.to_s
|
126
123
|
end
|
127
124
|
end
|
128
125
|
|
@@ -150,7 +147,7 @@ module GraphQL
|
|
150
147
|
end
|
151
148
|
|
152
149
|
def self.|(other)
|
153
|
-
new_fields =
|
150
|
+
new_fields = fields.dup
|
154
151
|
other.fields.each do |name, value|
|
155
152
|
if new_fields[name]
|
156
153
|
new_fields[name] |= value
|
@@ -159,14 +156,14 @@ module GraphQL
|
|
159
156
|
end
|
160
157
|
end
|
161
158
|
# TODO: Picking first source node seems error prone
|
162
|
-
define(name: self.name, source_node:
|
159
|
+
define(name: self.name, source_node: source_node, fields: new_fields)
|
163
160
|
end
|
164
161
|
|
165
162
|
attr_reader :data
|
166
|
-
|
163
|
+
alias to_h data
|
167
164
|
|
168
165
|
def inspect
|
169
|
-
ivars =
|
166
|
+
ivars = self.class.fields.keys.map { |sym| "#{sym}=#{instance_variable_get("@#{sym}").inspect}" }
|
170
167
|
buf = "#<#{self.class.name}"
|
171
168
|
buf << " " << ivars.join(" ") if ivars.any?
|
172
169
|
buf << ">"
|
@@ -178,6 +175,10 @@ module GraphQL
|
|
178
175
|
rescue NoMethodError => e
|
179
176
|
raise NoMethodError, "undefined method `#{e.name}' for #{inspect}"
|
180
177
|
end
|
178
|
+
|
179
|
+
def respond_to_missing?(*args)
|
180
|
+
super
|
181
|
+
end
|
181
182
|
end
|
182
183
|
end
|
183
184
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require "graphql"
|
2
2
|
require "graphql/client"
|
3
|
-
require "graphql/client/log_subscriber"
|
4
3
|
require "rails/railtie"
|
5
4
|
|
6
5
|
module GraphQL
|
@@ -11,12 +10,17 @@ module GraphQL
|
|
11
10
|
|
12
11
|
# Eager load leaky dependency to workaround AS::Dependencies unloading issues
|
13
12
|
# https://github.com/rmosolgo/graphql-ruby/pull/240
|
14
|
-
initializer "graphql.eager_load_hack" do |
|
13
|
+
initializer "graphql.eager_load_hack" do |_app|
|
15
14
|
require "graphql"
|
16
15
|
GraphQL::BOOLEAN_TYPE.name
|
17
16
|
end
|
18
17
|
|
19
|
-
initializer "graphql.
|
18
|
+
initializer "graphql.configure_log_subscriber" do |_app|
|
19
|
+
require "graphql/client/log_subscriber"
|
20
|
+
GraphQL::Client::LogSubscriber.attach_to :graphql
|
21
|
+
end
|
22
|
+
|
23
|
+
initializer "graphql.configure_erb_implementation" do |_app|
|
20
24
|
require "graphql/client/erubis"
|
21
25
|
ActionView::Template::Handlers::ERB.erb_implementation = GraphQL::Client::Erubis
|
22
26
|
end
|
@@ -27,11 +31,13 @@ module GraphQL
|
|
27
31
|
path = app.paths["app/views"].first
|
28
32
|
client = config.graphql.client
|
29
33
|
|
30
|
-
|
34
|
+
config.watchable_dirs[path] = [:erb]
|
35
|
+
|
36
|
+
Object.const_set(:Views, Module.new do
|
31
37
|
extend GraphQL::Client::ViewModule
|
32
38
|
self.path = path
|
33
39
|
self.client = client
|
34
|
-
|
40
|
+
end)
|
35
41
|
end
|
36
42
|
end
|
37
43
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "graphql/client/error"
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Client
|
5
|
+
# Public: Abstract base class for GraphQL responses.
|
6
|
+
#
|
7
|
+
# https://facebook.github.io/graphql/#sec-Response-Format
|
8
|
+
class Response
|
9
|
+
# Public: Hash of server specific extension metadata.
|
10
|
+
attr_reader :extensions
|
11
|
+
|
12
|
+
# Internal: Initialize base class.
|
13
|
+
def initialize(extensions: nil)
|
14
|
+
@extensions = extensions || {}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class SuccessfulResponse < Response
|
19
|
+
# Public: Wrapped QueryResult of data returned from the server.
|
20
|
+
#
|
21
|
+
# https://facebook.github.io/graphql/#sec-Data
|
22
|
+
#
|
23
|
+
# Returns instance of QueryResult subclass.
|
24
|
+
attr_reader :data
|
25
|
+
|
26
|
+
# Internal: Initialize SuccessfulResponse.
|
27
|
+
def initialize(data:, **kargs)
|
28
|
+
@data = data
|
29
|
+
super(**kargs)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class PartialResponse < SuccessfulResponse
|
34
|
+
# Public: Get partial failures from response.
|
35
|
+
#
|
36
|
+
# https://facebook.github.io/graphql/#sec-Errors
|
37
|
+
#
|
38
|
+
# Returns ResponseErrors collection object.
|
39
|
+
attr_reader :errors
|
40
|
+
|
41
|
+
# Internal: Initialize PartialResponse.
|
42
|
+
def initialize(errors:, **kargs)
|
43
|
+
@errors = errors
|
44
|
+
super(**kargs)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class FailedResponse < Response
|
49
|
+
# Public: Get errors from response.
|
50
|
+
#
|
51
|
+
# https://facebook.github.io/graphql/#sec-Errors
|
52
|
+
#
|
53
|
+
# Returns ResponseErrors collection object.
|
54
|
+
attr_reader :errors
|
55
|
+
|
56
|
+
# Internal: Initialize FailedResponse.
|
57
|
+
def initialize(errors:, **kargs)
|
58
|
+
@errors = errors
|
59
|
+
super(**kargs)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class ResponseError < Error
|
64
|
+
def initialize(definition, error)
|
65
|
+
@request_definition = definition
|
66
|
+
@locations = error["locations"]
|
67
|
+
super error["message"]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class ResponseErrors < Error
|
72
|
+
include Enumerable
|
73
|
+
|
74
|
+
attr_reader :errors
|
75
|
+
|
76
|
+
def initialize(definition, errors)
|
77
|
+
@request_definition = definition
|
78
|
+
@errors = errors.map { |error| ResponseError.new(definition, error) }
|
79
|
+
super @errors.map(&:message).join(", ")
|
80
|
+
end
|
81
|
+
|
82
|
+
def each(&block)
|
83
|
+
errors.each(&block)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -20,17 +20,14 @@ module GraphQL
|
|
20
20
|
#
|
21
21
|
# Returns nothing.
|
22
22
|
def eager_load!
|
23
|
-
return unless File.directory?(
|
23
|
+
return unless File.directory?(path)
|
24
24
|
|
25
|
-
Dir.entries(
|
25
|
+
Dir.entries(path).each do |entry|
|
26
26
|
next if entry == "." || entry == ".."
|
27
27
|
name = entry.sub(/(\.\w+)+$/, "").camelize.to_sym
|
28
28
|
if ViewModule.valid_constant_name?(name) && loadable_const_defined?(name)
|
29
29
|
mod = const_get(name, false)
|
30
30
|
mod.eager_load!
|
31
|
-
else
|
32
|
-
# Ignore template names that don't camelize to safe constants:
|
33
|
-
# 404, foo-dash
|
34
31
|
end
|
35
32
|
end
|
36
33
|
|
@@ -97,7 +94,7 @@ module GraphQL
|
|
97
94
|
# Returns String absolute path to file, otherwise nil.
|
98
95
|
def const_path(name)
|
99
96
|
pathname = ActiveSupport::Inflector.underscore(name.to_s)
|
100
|
-
Dir[File.join(
|
97
|
+
Dir[File.join(path, "{#{pathname},_#{pathname}}{/,.*}")].map { |fn| File.expand_path(fn) }.first
|
101
98
|
end
|
102
99
|
|
103
100
|
# Internal: Initialize new module for constant name and load ERB statics.
|
@@ -115,13 +112,12 @@ module GraphQL
|
|
115
112
|
|
116
113
|
if File.extname(path) == ".erb"
|
117
114
|
contents = File.read(path)
|
118
|
-
query = GraphQL::Client::Erubis.
|
119
|
-
|
120
|
-
mod = client.parse(query) if query
|
115
|
+
query, lineno = GraphQL::Client::Erubis.extract_graphql_section(contents)
|
116
|
+
mod = client.parse(query, path, lineno) if query
|
121
117
|
end
|
122
118
|
|
123
119
|
mod.extend(ViewModule)
|
124
|
-
mod.client =
|
120
|
+
mod.client = client
|
125
121
|
mod.path = path
|
126
122
|
mod
|
127
123
|
end
|
@@ -132,7 +128,9 @@ module GraphQL
|
|
132
128
|
#
|
133
129
|
# Returns module or raises NameError if missing.
|
134
130
|
def const_missing(name)
|
135
|
-
|
131
|
+
path = const_path(name)
|
132
|
+
|
133
|
+
if path
|
136
134
|
mod = load_module(path)
|
137
135
|
const_set(name, mod)
|
138
136
|
mod.unloadable
|
@@ -9,9 +9,7 @@ module GraphQL
|
|
9
9
|
# Returns self Node.
|
10
10
|
def deep_freeze
|
11
11
|
self.class.child_attributes.each do |attr_name|
|
12
|
-
public_send(attr_name).freeze.each
|
13
|
-
node.deep_freeze
|
14
|
-
end
|
12
|
+
public_send(attr_name).freeze.each(&:deep_freeze)
|
15
13
|
end
|
16
14
|
freeze
|
17
15
|
end
|
@@ -16,7 +16,7 @@ module GraphQL
|
|
16
16
|
seen = Set.new([operation_name])
|
17
17
|
stack = [operation_name]
|
18
18
|
|
19
|
-
|
19
|
+
until stack.empty?
|
20
20
|
name = stack.pop
|
21
21
|
names = find_definition_fragment_spreads(document, name)
|
22
22
|
seen.merge(names)
|
@@ -28,12 +28,12 @@ module GraphQL
|
|
28
28
|
|
29
29
|
def self.find_definition_fragment_spreads(document, definition_name)
|
30
30
|
definition = document.definitions.find { |node| node.name == definition_name }
|
31
|
-
raise "missing definition: #{definition_name}"
|
31
|
+
raise "missing definition: #{definition_name}" unless definition
|
32
32
|
spreads = Set.new
|
33
33
|
visitor = Visitor.new(definition)
|
34
|
-
visitor[Nodes::FragmentSpread].enter << -> (node,
|
34
|
+
visitor[Nodes::FragmentSpread].enter << -> (node, _parent) do
|
35
35
|
spreads << node.name
|
36
|
-
|
36
|
+
end
|
37
37
|
visitor.visit
|
38
38
|
spreads
|
39
39
|
end
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.19
|
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-
|
11
|
+
date: 2016-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -31,7 +31,21 @@ dependencies:
|
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: '6.0'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: graphql
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.18'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.18'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: actionpack
|
35
49
|
requirement: !ruby/object:Gem::Requirement
|
36
50
|
requirements:
|
37
51
|
- - ">="
|
@@ -40,7 +54,7 @@ dependencies:
|
|
40
54
|
- - "<"
|
41
55
|
- !ruby/object:Gem::Version
|
42
56
|
version: '6.0'
|
43
|
-
type: :
|
57
|
+
type: :development
|
44
58
|
prerelease: false
|
45
59
|
version_requirements: !ruby/object:Gem::Requirement
|
46
60
|
requirements:
|
@@ -51,47 +65,47 @@ dependencies:
|
|
51
65
|
- !ruby/object:Gem::Version
|
52
66
|
version: '6.0'
|
53
67
|
- !ruby/object:Gem::Dependency
|
54
|
-
name:
|
68
|
+
name: minitest
|
55
69
|
requirement: !ruby/object:Gem::Requirement
|
56
70
|
requirements:
|
57
71
|
- - "~>"
|
58
72
|
- !ruby/object:Gem::Version
|
59
|
-
version: '
|
60
|
-
type: :
|
73
|
+
version: '5.9'
|
74
|
+
type: :development
|
61
75
|
prerelease: false
|
62
76
|
version_requirements: !ruby/object:Gem::Requirement
|
63
77
|
requirements:
|
64
78
|
- - "~>"
|
65
79
|
- !ruby/object:Gem::Version
|
66
|
-
version: '
|
80
|
+
version: '5.9'
|
67
81
|
- !ruby/object:Gem::Dependency
|
68
|
-
name:
|
82
|
+
name: rake
|
69
83
|
requirement: !ruby/object:Gem::Requirement
|
70
84
|
requirements:
|
71
85
|
- - "~>"
|
72
86
|
- !ruby/object:Gem::Version
|
73
|
-
version: '
|
87
|
+
version: '11.2'
|
74
88
|
type: :development
|
75
89
|
prerelease: false
|
76
90
|
version_requirements: !ruby/object:Gem::Requirement
|
77
91
|
requirements:
|
78
92
|
- - "~>"
|
79
93
|
- !ruby/object:Gem::Version
|
80
|
-
version: '
|
94
|
+
version: '11.2'
|
81
95
|
- !ruby/object:Gem::Dependency
|
82
|
-
name:
|
96
|
+
name: rubocop
|
83
97
|
requirement: !ruby/object:Gem::Requirement
|
84
98
|
requirements:
|
85
99
|
- - "~>"
|
86
100
|
- !ruby/object:Gem::Version
|
87
|
-
version: '
|
101
|
+
version: '0.42'
|
88
102
|
type: :development
|
89
103
|
prerelease: false
|
90
104
|
version_requirements: !ruby/object:Gem::Requirement
|
91
105
|
requirements:
|
92
106
|
- - "~>"
|
93
107
|
- !ruby/object:Gem::Version
|
94
|
-
version: '
|
108
|
+
version: '0.42'
|
95
109
|
description: "???"
|
96
110
|
email: engineering@github.com
|
97
111
|
executables: []
|
@@ -100,11 +114,14 @@ extra_rdoc_files: []
|
|
100
114
|
files:
|
101
115
|
- LICENSE
|
102
116
|
- lib/graphql/client.rb
|
117
|
+
- lib/graphql/client/error.rb
|
103
118
|
- lib/graphql/client/erubis.rb
|
104
119
|
- lib/graphql/client/http.rb
|
105
120
|
- lib/graphql/client/log_subscriber.rb
|
121
|
+
- lib/graphql/client/query.rb
|
106
122
|
- lib/graphql/client/query_result.rb
|
107
123
|
- lib/graphql/client/railtie.rb
|
124
|
+
- lib/graphql/client/response.rb
|
108
125
|
- lib/graphql/client/view_module.rb
|
109
126
|
- lib/graphql/language/nodes/deep_freeze_ext.rb
|
110
127
|
- lib/graphql/language/operation_slice.rb
|