graphql-client 0.0.18 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|