rails-graphql 1.0.0.beta → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/gql_parser.c +1 -16
- data/ext/gql_parser.h +21 -0
- data/ext/shared.c +0 -5
- data/ext/shared.h +6 -6
- data/lib/generators/graphql/channel_generator.rb +27 -0
- data/lib/generators/graphql/controller_generator.rb +9 -4
- data/lib/generators/graphql/install_generator.rb +49 -0
- data/lib/generators/graphql/schema_generator.rb +9 -4
- data/lib/generators/graphql/templates/channel.erb +7 -0
- data/lib/generators/graphql/templates/config.rb +97 -0
- data/lib/generators/graphql/templates/controller.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +5 -3
- data/lib/gql_parser.so +0 -0
- data/lib/rails/graphql/alternative/field_set.rb +12 -0
- data/lib/rails/graphql/alternative/query.rb +9 -4
- data/lib/rails/graphql/alternative/subscription.rb +2 -1
- data/lib/rails/graphql/argument.rb +5 -3
- data/lib/rails/graphql/callback.rb +8 -7
- data/lib/rails/graphql/collectors/hash_collector.rb +12 -1
- data/lib/rails/graphql/collectors/json_collector.rb +21 -0
- data/lib/rails/graphql/config.rb +73 -57
- data/lib/rails/graphql/directive/include_directive.rb +0 -1
- data/lib/rails/graphql/directive/skip_directive.rb +0 -1
- data/lib/rails/graphql/directive/specified_by_directive.rb +24 -0
- data/lib/rails/graphql/directive.rb +30 -24
- data/lib/rails/graphql/event.rb +7 -6
- data/lib/rails/graphql/field/authorized_field.rb +0 -5
- data/lib/rails/graphql/field/input_field.rb +0 -5
- data/lib/rails/graphql/field/mutation_field.rb +5 -6
- data/lib/rails/graphql/field/output_field.rb +13 -2
- data/lib/rails/graphql/field/proxied_field.rb +5 -5
- data/lib/rails/graphql/field/resolved_field.rb +1 -1
- data/lib/rails/graphql/field/subscription_field.rb +35 -52
- data/lib/rails/graphql/field/typed_field.rb +26 -2
- data/lib/rails/graphql/field.rb +20 -19
- data/lib/rails/graphql/global_id.rb +5 -1
- data/lib/rails/graphql/helpers/inherited_collection/array.rb +1 -0
- data/lib/rails/graphql/helpers/inherited_collection/base.rb +2 -0
- data/lib/rails/graphql/helpers/inherited_collection/hash.rb +2 -1
- data/lib/rails/graphql/helpers/registerable.rb +1 -1
- data/lib/rails/graphql/helpers/with_arguments.rb +3 -2
- data/lib/rails/graphql/helpers/with_callbacks.rb +3 -3
- data/lib/rails/graphql/helpers/with_description.rb +10 -8
- data/lib/rails/graphql/helpers/with_directives.rb +5 -1
- data/lib/rails/graphql/helpers/with_events.rb +1 -0
- data/lib/rails/graphql/helpers/with_fields.rb +28 -22
- data/lib/rails/graphql/helpers/with_name.rb +3 -2
- data/lib/rails/graphql/helpers/with_schema_fields.rb +72 -48
- data/lib/rails/graphql/introspection.rb +1 -1
- data/lib/rails/graphql/railtie.rb +3 -2
- data/lib/rails/graphql/railties/app/base_channel.rb +10 -0
- data/lib/rails/graphql/railties/app/base_controller.rb +12 -0
- data/lib/rails/graphql/railties/app/views/_cable.js.erb +56 -0
- data/lib/rails/graphql/railties/app/views/_fetch.js.erb +20 -0
- data/lib/rails/graphql/railties/app/views/graphiql.html.erb +101 -0
- data/lib/rails/graphql/railties/base_generator.rb +3 -9
- data/lib/rails/graphql/railties/channel.rb +8 -8
- data/lib/rails/graphql/railties/controller.rb +45 -24
- data/lib/rails/graphql/request/arguments.rb +2 -1
- data/lib/rails/graphql/request/backtrace.rb +31 -10
- data/lib/rails/graphql/request/component/field.rb +15 -8
- data/lib/rails/graphql/request/component/fragment.rb +13 -7
- data/lib/rails/graphql/request/component/operation/subscription.rb +4 -6
- data/lib/rails/graphql/request/component/operation.rb +11 -4
- data/lib/rails/graphql/request/component/spread.rb +13 -4
- data/lib/rails/graphql/request/component/typename.rb +1 -1
- data/lib/rails/graphql/request/component.rb +2 -0
- data/lib/rails/graphql/request/context.rb +1 -1
- data/lib/rails/graphql/request/event.rb +6 -2
- data/lib/rails/graphql/request/helpers/directives.rb +1 -0
- data/lib/rails/graphql/request/helpers/selection_set.rb +10 -4
- data/lib/rails/graphql/request/helpers/value_writers.rb +8 -5
- data/lib/rails/graphql/request/prepared_data.rb +3 -1
- data/lib/rails/graphql/request/steps/organizable.rb +1 -1
- data/lib/rails/graphql/request/steps/preparable.rb +1 -1
- data/lib/rails/graphql/request/steps/resolvable.rb +1 -1
- data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +3 -3
- data/lib/rails/graphql/request/strategy.rb +18 -4
- data/lib/rails/graphql/request/subscription.rb +18 -16
- data/lib/rails/graphql/request.rb +67 -37
- data/lib/rails/graphql/schema.rb +39 -86
- data/lib/rails/graphql/shortcuts.rb +11 -5
- data/lib/rails/graphql/source/active_record/builders.rb +20 -21
- data/lib/rails/graphql/source/active_record_source.rb +93 -33
- data/lib/rails/graphql/source/base.rb +11 -39
- data/lib/rails/graphql/source/builder.rb +9 -22
- data/lib/rails/graphql/source/scoped_arguments.rb +10 -4
- data/lib/rails/graphql/source.rb +23 -37
- data/lib/rails/graphql/subscription/provider/action_cable.rb +10 -9
- data/lib/rails/graphql/subscription/provider/base.rb +6 -5
- data/lib/rails/graphql/subscription/store/base.rb +5 -9
- data/lib/rails/graphql/subscription/store/memory.rb +18 -9
- data/lib/rails/graphql/type/creator.rb +196 -0
- data/lib/rails/graphql/type/enum.rb +17 -9
- data/lib/rails/graphql/type/input.rb +20 -4
- data/lib/rails/graphql/type/interface.rb +15 -4
- data/lib/rails/graphql/type/object/directive_object.rb +6 -5
- data/lib/rails/graphql/type/object/input_value_object.rb +3 -4
- data/lib/rails/graphql/type/object/type_object.rb +40 -13
- data/lib/rails/graphql/type/object.rb +10 -5
- data/lib/rails/graphql/type/scalar/binary_scalar.rb +2 -0
- data/lib/rails/graphql/type/scalar/date_scalar.rb +2 -0
- data/lib/rails/graphql/type/scalar/date_time_scalar.rb +2 -0
- data/lib/rails/graphql/type/scalar/decimal_scalar.rb +2 -0
- data/lib/rails/graphql/type/scalar/json_scalar.rb +3 -1
- data/lib/rails/graphql/type/scalar/time_scalar.rb +3 -1
- data/lib/rails/graphql/type/scalar.rb +1 -1
- data/lib/rails/graphql/type/union.rb +7 -2
- data/lib/rails/graphql/type.rb +10 -2
- data/lib/rails/graphql/type_map.rb +18 -7
- data/lib/rails/graphql/uri.rb +5 -4
- data/lib/rails/graphql/version.rb +6 -2
- data/lib/rails/graphql.rb +9 -7
- data/test/assets/introspection-mem.txt +1 -1
- data/test/assets/introspection.gql +2 -0
- data/test/assets/mem.gql +74 -60
- data/test/assets/mysql.gql +69 -55
- data/test/assets/sqlite.gql +78 -64
- data/test/assets/translate.gql +50 -39
- data/test/config.rb +2 -1
- data/test/graphql/schema_test.rb +2 -31
- data/test/graphql/source_test.rb +0 -10
- data/test/graphql/type/interface_test.rb +8 -5
- data/test/graphql/type/object_test.rb +8 -2
- data/test/graphql/type_map_test.rb +13 -16
- data/test/integration/global_id_test.rb +4 -4
- data/test/integration/memory/star_wars_validation_test.rb +2 -2
- data/test/integration/mysql/star_wars_introspection_test.rb +1 -1
- data/test/integration/resolver_precedence_test.rb +1 -1
- data/test/integration/schemas/memory.rb +3 -4
- data/test/integration/sqlite/star_wars_global_id_test.rb +27 -21
- data/test/integration/sqlite/star_wars_introspection_test.rb +1 -1
- data/test/integration/translate_test.rb +26 -14
- metadata +20 -7
@@ -17,21 +17,24 @@ module Rails
|
|
17
17
|
def inherited(subclass)
|
18
18
|
super if defined? super
|
19
19
|
|
20
|
-
TYPE_FIELD_CLASS.each_key do |
|
21
|
-
fields = instance_variable_defined?("@#{
|
22
|
-
fields = fields ? instance_variable_get("@#{
|
23
|
-
fields.each_value { |field| subclass.add_proxy_field(
|
20
|
+
TYPE_FIELD_CLASS.each_key do |type|
|
21
|
+
fields = instance_variable_defined?("@#{type}_fields")
|
22
|
+
fields = fields ? instance_variable_get("@#{type}_fields") : EMPTY_HASH
|
23
|
+
fields.each_value { |field| subclass.add_proxy_field(type, field) }
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
# Helper class to be used as the +self+ in configuration blocks
|
29
29
|
ScopedConfig = Struct.new(:source, :type) do
|
30
|
-
def
|
30
|
+
def argument(*args, **xargs, &block)
|
31
31
|
xargs[:owner] ||= source
|
32
32
|
GraphQL::Argument.new(*args, **xargs, &block)
|
33
33
|
end
|
34
34
|
|
35
|
+
alias arg argument
|
36
|
+
alias kind type
|
37
|
+
|
35
38
|
private
|
36
39
|
|
37
40
|
def respond_to_missing?(method_name, include_private = false)
|
@@ -55,7 +58,6 @@ module Rails
|
|
55
58
|
safe_field: :safe_add_field,
|
56
59
|
field: :add_field,
|
57
60
|
proxy_field: :add_proxy_field,
|
58
|
-
field?: :has_field?,
|
59
61
|
import: :import_into,
|
60
62
|
import_all: :import_all_into,
|
61
63
|
)
|
@@ -76,13 +78,16 @@ module Rails
|
|
76
78
|
end
|
77
79
|
end
|
78
80
|
|
81
|
+
# Allow hash access with the type or the type and the name
|
82
|
+
def [](type, name = nil)
|
83
|
+
name.nil? ? fields_for(type) : find_field(type, name)
|
84
|
+
end
|
85
|
+
|
79
86
|
# Check if there are fields set fot he given type
|
80
87
|
def fields_for?(type)
|
81
88
|
public_send("#{type}_fields?")
|
82
89
|
end
|
83
90
|
|
84
|
-
alias [] :fields_for
|
85
|
-
|
86
91
|
# Return the object name for a given +type+ of list of fields
|
87
92
|
def type_name_for(type)
|
88
93
|
method_name = :"#{type}_type_name"
|
@@ -116,6 +121,7 @@ module Rails
|
|
116
121
|
# Add a new field to the list but use a proxy instead of a hard copy of
|
117
122
|
# a given +field+
|
118
123
|
def add_proxy_field(type, field, *args, **xargs, &block)
|
124
|
+
field = field.field if field.is_a?(Module) && field <= Alternative::Query
|
119
125
|
raise ArgumentError, (+<<~MSG).squish if field.schema_type != type
|
120
126
|
A #{field.schema_type} field cannot be added as a #{type} field.
|
121
127
|
MSG
|
@@ -180,12 +186,15 @@ module Rails
|
|
180
186
|
MSG
|
181
187
|
end
|
182
188
|
|
183
|
-
# Get the list of GraphQL names of all the fields
|
189
|
+
# Get the list of GraphQL names of all the fields defined
|
184
190
|
def field_names_for(type, enabled_only = true)
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
191
|
+
source = (enabled_only ? enabled_fields_from(type) : lazy_each_field_from(type))
|
192
|
+
source&.map(&:gql_name)&.eager
|
193
|
+
end
|
194
|
+
|
195
|
+
# Return a lazy enumerator for enabled fields
|
196
|
+
def enabled_fields_from(type)
|
197
|
+
lazy_each_field_from(type)&.select(&:enabled?)
|
189
198
|
end
|
190
199
|
|
191
200
|
# Run a configuration block for the given +type+
|
@@ -195,8 +204,6 @@ module Rails
|
|
195
204
|
|
196
205
|
# Import a class of fields into the given section of schema fields
|
197
206
|
def import_into(type, source)
|
198
|
-
return if source.try(:abstract?)
|
199
|
-
|
200
207
|
# Import an alternative declaration of a field
|
201
208
|
if source.is_a?(Module) && source <= Alternative::Query
|
202
209
|
return add_proxy_field(type, source.field)
|
@@ -232,7 +239,7 @@ module Rails
|
|
232
239
|
object = mod.const_get(const_name)
|
233
240
|
|
234
241
|
import_into(type, object, **xargs) if object.is_a?(Class)
|
235
|
-
import_all_into(type, object, recursive: recursive, **xargs) if recursive
|
242
|
+
import_all_into(type, object, recursive: recursive, **xargs) if recursive && object.is_a?(Module)
|
236
243
|
end
|
237
244
|
end
|
238
245
|
|
@@ -253,9 +260,9 @@ module Rails
|
|
253
260
|
def validate!(*)
|
254
261
|
super if defined? super
|
255
262
|
|
256
|
-
TYPE_FIELD_CLASS.each_key do |
|
257
|
-
next unless public_send("#{
|
258
|
-
fields_for(
|
263
|
+
TYPE_FIELD_CLASS.each_key do |type|
|
264
|
+
next unless public_send("#{type}_fields?")
|
265
|
+
fields_for(type).each_value(&:validate!)
|
259
266
|
end
|
260
267
|
end
|
261
268
|
|
@@ -264,52 +271,69 @@ module Rails
|
|
264
271
|
find_field!(gid.scope, gid.name)
|
265
272
|
end
|
266
273
|
|
267
|
-
TYPE_FIELD_CLASS.each_key do |
|
274
|
+
TYPE_FIELD_CLASS.each_key do |type|
|
268
275
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
269
|
-
def #{
|
270
|
-
|
276
|
+
def #{type}_fields?
|
277
|
+
defined?(@#{type}_fields) && @#{type}_fields.present?
|
271
278
|
end
|
272
279
|
|
273
|
-
def #{
|
274
|
-
|
280
|
+
def #{type}_fields(&block)
|
281
|
+
configure_fields(:#{type}, &block) if block.present?
|
282
|
+
@#{type}_fields if defined?(@#{type}_fields)
|
275
283
|
end
|
276
284
|
|
277
|
-
def add_#{
|
278
|
-
add_field(:#{
|
285
|
+
def add_#{type}_field(*args, **xargs, &block)
|
286
|
+
add_field(:#{type}, *args, **xargs, &block)
|
279
287
|
end
|
280
288
|
|
281
|
-
def #{
|
282
|
-
|
289
|
+
def #{type}_field?(name)
|
290
|
+
has_field?(:#{type}, name)
|
283
291
|
end
|
284
292
|
|
285
|
-
def #{
|
286
|
-
|
287
|
-
@#{kind}_fields if defined?(@#{kind}_fields)
|
293
|
+
def #{type}_field(name)
|
294
|
+
find_field(:#{type}, name)
|
288
295
|
end
|
289
296
|
|
290
|
-
def #{
|
297
|
+
def #{type}_type_name
|
291
298
|
source = (respond_to?(:config) ? config : GraphQL.config)
|
292
|
-
source.schema_type_names[:#{
|
299
|
+
source.schema_type_names[:#{type}]
|
293
300
|
end
|
294
301
|
|
295
|
-
def #{
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
302
|
+
def #{type}_type
|
303
|
+
return unless #{type}_fields?
|
304
|
+
|
305
|
+
OpenStruct.new(
|
306
|
+
name: "\#{name}[:#{type}]",
|
307
|
+
kind: :object,
|
308
|
+
object?: true,
|
309
|
+
kind_enum: 'OBJECT',
|
310
|
+
fields: @#{type}_fields,
|
311
|
+
gql_name: #{type}_type_name,
|
312
|
+
description: nil,
|
313
|
+
output_type?: true,
|
314
|
+
operational?: true,
|
315
|
+
interfaces?: false,
|
316
|
+
internal?: false,
|
317
|
+
).freeze
|
310
318
|
end
|
311
319
|
RUBY
|
312
320
|
end
|
321
|
+
|
322
|
+
protected
|
323
|
+
|
324
|
+
# A little helper to define arguments using the :arguments key
|
325
|
+
def argument(*args, **xargs, &block)
|
326
|
+
xargs[:owner] = self
|
327
|
+
GraphQL::Argument.new(*args, **xargs, &block)
|
328
|
+
end
|
329
|
+
|
330
|
+
alias arg argument
|
331
|
+
|
332
|
+
private
|
333
|
+
|
334
|
+
def lazy_each_field_from(type)
|
335
|
+
fields_for(type).each_pair.lazy.each_entry.map(&:last) if fields_for?(type)
|
336
|
+
end
|
313
337
|
end
|
314
338
|
end
|
315
339
|
end
|
@@ -123,8 +123,9 @@ module Rails
|
|
123
123
|
initializer 'graphql.reloader', before: :load_config_initializers do |app|
|
124
124
|
next unless (path = app.root.join('app', 'graphql')).exist?
|
125
125
|
|
126
|
-
children = config.graphql.paths.join(',')
|
127
|
-
autoloader = app.autoloaders.
|
126
|
+
children = config.graphql.paths.to_a.join(',')
|
127
|
+
autoloader = app.respond_to?(:autoloaders) ? app.autoloaders : Rails.autoloaders
|
128
|
+
autoloader = autoloader.main
|
128
129
|
|
129
130
|
ActiveSupport::Dependencies.autoload_paths.delete(path.to_s)
|
130
131
|
autoloader.collapse(path.glob("**/{#{children}}").select(&:directory?))
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
base = ActionController::Base
|
5
|
+
base = ApplicationController if defined?(ApplicationController)
|
6
|
+
|
7
|
+
BaseController = Class.new(base) do
|
8
|
+
include ::Rails::GraphQL::Controller
|
9
|
+
|
10
|
+
skip_before_action :verify_authenticity_token
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
var queue = [];
|
2
|
+
var current = null;
|
3
|
+
var identifier = JSON.stringify({ channel: '<%= channel %>' });
|
4
|
+
var socket = new WebSocket("ws://" + window.location.hostname + "/<%= url %>");
|
5
|
+
|
6
|
+
// TOOD: This is a temporary implementation
|
7
|
+
socket.onopen = function(event) {
|
8
|
+
const msg = { command: 'subscribe', identifier: identifier };
|
9
|
+
socket.send(JSON.stringify(msg));
|
10
|
+
};
|
11
|
+
|
12
|
+
socket.onmessage = function(event) {
|
13
|
+
const msg = JSON.parse(event.data);
|
14
|
+
if (msg.type === "ping") {
|
15
|
+
return;
|
16
|
+
}
|
17
|
+
|
18
|
+
if (msg.type === "confirm_subscription") {
|
19
|
+
execute_next();
|
20
|
+
return;
|
21
|
+
}
|
22
|
+
|
23
|
+
if (msg.message && current) {
|
24
|
+
current.resolve(msg.message.result);
|
25
|
+
current = null;
|
26
|
+
execute_next();
|
27
|
+
} else {
|
28
|
+
console.dir(msg);
|
29
|
+
}
|
30
|
+
};
|
31
|
+
|
32
|
+
function execute_next() {
|
33
|
+
if (socket.readyState != '1' || queue.length === 0 || current) {
|
34
|
+
return;
|
35
|
+
}
|
36
|
+
|
37
|
+
current = queue.shift();
|
38
|
+
socket.send(JSON.stringify({
|
39
|
+
command: 'message',
|
40
|
+
identifier: identifier,
|
41
|
+
data: JSON.stringify({ action: 'execute', ...current.data }),
|
42
|
+
}));
|
43
|
+
}
|
44
|
+
|
45
|
+
function graphQLFetcher(graphQLParams) {
|
46
|
+
var resolve;
|
47
|
+
var promise = new Promise((success) => {
|
48
|
+
resolve = success;
|
49
|
+
});
|
50
|
+
|
51
|
+
var item = { data: graphQLParams, promise, resolve };
|
52
|
+
|
53
|
+
queue.push(item);
|
54
|
+
execute_next();
|
55
|
+
return promise;
|
56
|
+
};
|
@@ -0,0 +1,20 @@
|
|
1
|
+
function graphQLFetcher(graphQLParams) {
|
2
|
+
// This example expects a GraphQL server at the path /graphql.
|
3
|
+
// Change this to point wherever you host your GraphQL server.
|
4
|
+
return fetch('<%= url %>', {
|
5
|
+
method: 'post',
|
6
|
+
headers: {
|
7
|
+
'Accept': 'application/json',
|
8
|
+
'Content-Type': 'application/json'
|
9
|
+
},
|
10
|
+
body: JSON.stringify(graphQLParams),
|
11
|
+
}).then(function (response) {
|
12
|
+
return response.text();
|
13
|
+
}).then(function (responseBody) {
|
14
|
+
try {
|
15
|
+
return JSON.parse(responseBody);
|
16
|
+
} catch (error) {
|
17
|
+
return responseBody;
|
18
|
+
}
|
19
|
+
});
|
20
|
+
}
|
@@ -0,0 +1,101 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<meta name="robots" content="noindex" />
|
6
|
+
<meta name="referrer" content="origin" />
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
8
|
+
<title>SWAPI GraphQL API</title>
|
9
|
+
<style>
|
10
|
+
body {
|
11
|
+
height: 100vh;
|
12
|
+
margin: 0;
|
13
|
+
overflow: hidden;
|
14
|
+
}
|
15
|
+
#splash {
|
16
|
+
color: #333;
|
17
|
+
display: flex;
|
18
|
+
flex-direction: column;
|
19
|
+
font-family: system, -apple-system, "San Francisco", ".SFNSDisplay-Regular", "Segoe UI", Segoe, "Segoe WP", "Helvetica Neue", helvetica, "Lucida Grande", arial, sans-serif;
|
20
|
+
height: 100vh;
|
21
|
+
justify-content: center;
|
22
|
+
text-align: center;
|
23
|
+
}
|
24
|
+
</style>
|
25
|
+
<link rel="icon" href="favicon.ico">
|
26
|
+
<link type="text/css" href="//unpkg.com/graphiql/graphiql.min.css" rel="stylesheet" />
|
27
|
+
</head>
|
28
|
+
<body>
|
29
|
+
<div id="splash">
|
30
|
+
Loading…
|
31
|
+
</div>
|
32
|
+
<script src="//cdn.jsdelivr.net/es6-promise/4.0.5/es6-promise.auto.min.js"></script>
|
33
|
+
<script src="https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js"></script>
|
34
|
+
<script src="https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js"></script>
|
35
|
+
<script src="//unpkg.com/graphiql/graphiql.min.js"></script>
|
36
|
+
<script>
|
37
|
+
// Parse the search string to get url parameters.
|
38
|
+
var search = window.location.search;
|
39
|
+
var parameters = {};
|
40
|
+
search.substr(1).split('&').forEach(function (entry) {
|
41
|
+
var eq = entry.indexOf('=');
|
42
|
+
if (eq >= 0) {
|
43
|
+
parameters[decodeURIComponent(entry.slice(0, eq))] =
|
44
|
+
decodeURIComponent(entry.slice(eq + 1));
|
45
|
+
}
|
46
|
+
});
|
47
|
+
|
48
|
+
// if variables was provided, try to format it.
|
49
|
+
if (parameters.variables) {
|
50
|
+
try {
|
51
|
+
parameters.variables =
|
52
|
+
JSON.stringify(JSON.parse(parameters.variables), null, 2);
|
53
|
+
} catch (e) {
|
54
|
+
// Do nothing, we want to display the invalid JSON as a string, rather
|
55
|
+
// than present an error.
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
// When the query and variables string is edited, update the URL bar so
|
60
|
+
// that it can be easily shared
|
61
|
+
function onEditQuery(newQuery) {
|
62
|
+
parameters.query = newQuery;
|
63
|
+
updateURL();
|
64
|
+
}
|
65
|
+
function onEditVariables(newVariables) {
|
66
|
+
parameters.variables = newVariables;
|
67
|
+
updateURL();
|
68
|
+
}
|
69
|
+
function onEditOperationName(newOperationName) {
|
70
|
+
parameters.operationName = newOperationName;
|
71
|
+
updateURL();
|
72
|
+
}
|
73
|
+
function updateURL() {
|
74
|
+
var newSearch = '?' + Object.keys(parameters).filter(function (key) {
|
75
|
+
return Boolean(parameters[key]);
|
76
|
+
}).map(function (key) {
|
77
|
+
return encodeURIComponent(key) + '=' +
|
78
|
+
encodeURIComponent(parameters[key]);
|
79
|
+
}).join('&');
|
80
|
+
history.replaceState(null, null, newSearch);
|
81
|
+
}
|
82
|
+
|
83
|
+
<%= render partial: "/#{settings[:mode]}", formats: :js, locals: settings %>
|
84
|
+
|
85
|
+
// Render <GraphiQL /> into the body.
|
86
|
+
ReactDOM.render(
|
87
|
+
React.createElement(GraphiQL, {
|
88
|
+
fetcher: graphQLFetcher,
|
89
|
+
query: parameters.query,
|
90
|
+
variables: parameters.variables,
|
91
|
+
operationName: parameters.operationName,
|
92
|
+
onEditQuery: onEditQuery,
|
93
|
+
onEditVariables: onEditVariables,
|
94
|
+
onEditOperationName: onEditOperationName
|
95
|
+
}),
|
96
|
+
document.body,
|
97
|
+
);
|
98
|
+
</script>
|
99
|
+
</body>
|
100
|
+
</html>
|
101
|
+
|
@@ -7,23 +7,17 @@ module Rails
|
|
7
7
|
# A module to help generators to operate
|
8
8
|
module BaseGenerator
|
9
9
|
TEMPALTES_PATH = '../../../generators/graphql/templates'
|
10
|
+
APP_MODULE_NAME = Rails.application.class.name.chomp('::Application')
|
10
11
|
|
11
12
|
def self.included(base)
|
13
|
+
base.const_set(:APP_MODULE_NAME, APP_MODULE_NAME)
|
12
14
|
base.send(:namespace, "graphql:#{base.name.demodulize.underscore[0..-11]}")
|
13
15
|
base.send(:source_root, File.expand_path(TEMPALTES_PATH, __dir__))
|
14
|
-
base.send(:class_option, :directory,
|
15
|
-
type: :string,
|
16
|
+
base.send(:class_option, :directory, type: :string,
|
16
17
|
default: 'app/graphql',
|
17
18
|
desc: 'Directory where generated files should be saved',
|
18
19
|
)
|
19
20
|
end
|
20
|
-
|
21
|
-
protected
|
22
|
-
|
23
|
-
def app_module_name
|
24
|
-
require File.expand_path('config/application', destination_root)
|
25
|
-
Rails.application.class.name.chomp('::Application')
|
26
|
-
end
|
27
21
|
end
|
28
22
|
end
|
29
23
|
end
|
@@ -79,12 +79,6 @@ module Rails
|
|
79
79
|
}
|
80
80
|
end
|
81
81
|
|
82
|
-
# The list of ids of subscription and to which field they are
|
83
|
-
# associated with
|
84
|
-
def gql_subscriptions
|
85
|
-
@gql_subscriptions ||= {}
|
86
|
-
end
|
87
|
-
|
88
82
|
# The instance of a GraphQL request. It can't simply perform using
|
89
83
|
# +execute+, because it is important to check if any subscription was
|
90
84
|
# generated
|
@@ -117,8 +111,8 @@ module Rails
|
|
117
111
|
end
|
118
112
|
|
119
113
|
# Get the GraphQL variables for a request
|
120
|
-
def gql_variables(data)
|
121
|
-
variables
|
114
|
+
def gql_variables(data, variables = nil)
|
115
|
+
variables ||= data['variables']
|
122
116
|
|
123
117
|
case variables
|
124
118
|
when ::ActionController::Parameters then variables.permit!.to_h
|
@@ -128,6 +122,12 @@ module Rails
|
|
128
122
|
end
|
129
123
|
end
|
130
124
|
|
125
|
+
# The list of ids of subscription and to which field they are
|
126
|
+
# associated with
|
127
|
+
def gql_subscriptions
|
128
|
+
@gql_subscriptions ||= {}
|
129
|
+
end
|
130
|
+
|
131
131
|
# Remove all subscriptions
|
132
132
|
def gql_clear_subscriptions
|
133
133
|
gql_remove_subscription(*gql_subscriptions.keys) unless gql_subscriptions.empty?
|
@@ -22,6 +22,9 @@ module Rails
|
|
22
22
|
# Each controller is assigned to a GraphQL schema on which the requests
|
23
23
|
# will be performed from. It can be a string or the class
|
24
24
|
class_attribute :gql_schema, instance_accessor: false
|
25
|
+
|
26
|
+
# Add the internal views directory
|
27
|
+
prepend_view_path("#{__dir__}/app/views")
|
25
28
|
end
|
26
29
|
|
27
30
|
# POST /execute
|
@@ -34,39 +37,35 @@ module Rails
|
|
34
37
|
render plain: gql_schema_header + gql_describe_schema + gql_schema_footer
|
35
38
|
end
|
36
39
|
|
40
|
+
# GET /graphiql
|
41
|
+
def graphiql
|
42
|
+
render '/graphiql', layout: false, locals: { settings: graphiql_settings }
|
43
|
+
end
|
44
|
+
|
37
45
|
protected
|
38
46
|
|
47
|
+
# Identifies if the request should be threated as a compiled request
|
48
|
+
def gql_compiled_request?(*)
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
39
52
|
# Render a response as a GraphQL request
|
40
53
|
def gql_request_response(*args, **xargs)
|
41
54
|
render json: gql_request(*args, **xargs)
|
42
55
|
end
|
43
56
|
|
44
|
-
# Shows a text representation of the schema
|
45
|
-
def gql_describe_schema(schema = gql_schema)
|
46
|
-
schema.to_gql(
|
47
|
-
with_descriptions: !params.key?(:without_descriptions),
|
48
|
-
with_spec: !params.key?(:without_spec),
|
49
|
-
)
|
50
|
-
end
|
51
|
-
|
52
57
|
# Execute a GraphQL request
|
53
|
-
def gql_request(
|
58
|
+
def gql_request(document, **xargs)
|
54
59
|
request_xargs = REQUEST_XARGS.each_with_object({}) do |setting, result|
|
55
60
|
result[setting] ||= (xargs[setting] || send(:"gql_#{setting}"))
|
56
61
|
end
|
57
62
|
|
58
63
|
request_xargs[:hash] ||= gql_query_cache_key
|
59
64
|
request_xargs[:origin] ||= self
|
65
|
+
request_xargs[:compiled] ||= gql_compiled_request?(document)
|
60
66
|
|
61
67
|
request_xargs = request_xargs.except(*%i[query_cache_key query_cache_version])
|
62
|
-
::Rails::GraphQL::Request.execute(
|
63
|
-
end
|
64
|
-
|
65
|
-
# Print a header of the current schema for the description process
|
66
|
-
# TODO: Maybe add a way to detect from which file the schema is being loaded
|
67
|
-
def gql_schema_header
|
68
|
-
ns = +" [#{gql_schema.namespace}]" if gql_schema.namespace != :base
|
69
|
-
+"#{DESCRIBE_HEADER}# Schema #{gql_schema.name}#{ns}\n"
|
68
|
+
::Rails::GraphQL::Request.execute(document, **request_xargs)
|
70
69
|
end
|
71
70
|
|
72
71
|
# The schema on which the requests will be performed from
|
@@ -87,10 +86,12 @@ module Rails
|
|
87
86
|
end
|
88
87
|
|
89
88
|
# Get the GraphQL query to execute
|
90
|
-
def
|
89
|
+
def gql_document
|
91
90
|
params[:query]
|
92
91
|
end
|
93
92
|
|
93
|
+
alias gql_query gql_document
|
94
|
+
|
94
95
|
# Get the cache key of the query for persisted queries
|
95
96
|
def gql_query_cache_key(key = nil, version = nil)
|
96
97
|
return unless (key ||= params[:query_cache_key]).present?
|
@@ -117,6 +118,30 @@ module Rails
|
|
117
118
|
end
|
118
119
|
end
|
119
120
|
|
121
|
+
# Return the settings for the GraphiQL view
|
122
|
+
def graphiql_settings(mode = nil)
|
123
|
+
if mode == :cable
|
124
|
+
{ mode: :cable, url: '/cable', channel: 'GraphQL::BaseChannel' }
|
125
|
+
else
|
126
|
+
{ mode: :fetch, url: '/graphql' }
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Shows a text representation of the schema
|
131
|
+
def gql_describe_schema(schema = gql_schema)
|
132
|
+
schema.to_gql(
|
133
|
+
with_descriptions: !params.key?(:without_descriptions),
|
134
|
+
with_spec: !params.key?(:without_spec),
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Print a header of the current schema for the description process
|
139
|
+
# TODO: Maybe add a way to detect from which file the schema is being loaded
|
140
|
+
def gql_schema_header
|
141
|
+
ns = +" [#{gql_schema.namespace}]" if gql_schema.namespace != :base
|
142
|
+
+"#{DESCRIBE_HEADER}# Schema #{gql_schema.name}#{ns}\n"
|
143
|
+
end
|
144
|
+
|
120
145
|
# Show the footer of the describe page
|
121
146
|
def gql_schema_footer
|
122
147
|
$/ + $/ + '# Version: ' + gql_version + $/ +
|
@@ -133,12 +158,8 @@ module Rails
|
|
133
158
|
|
134
159
|
# Find the default application schema
|
135
160
|
def gql_application_default_schema
|
136
|
-
app_class = Rails.application.class
|
137
|
-
|
138
|
-
? :module_parent_name \
|
139
|
-
: :parent_name
|
140
|
-
|
141
|
-
klass = "::GraphQL::#{app_class.public_send(source_name)}Schema".constantize
|
161
|
+
app_class = Rails.application.class.name.chomp('::Application')
|
162
|
+
klass = "::GraphQL::#{app_class}Schema".safe_constantize
|
142
163
|
self.class.gql_schema = klass
|
143
164
|
end
|
144
165
|
end
|