rails-graphql 0.2.1 → 1.0.0.beta
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/ext/console.rb +18 -0
- data/ext/extconf.h +3 -0
- data/ext/extconf.rb +1 -54
- data/ext/gql_parser.c +646 -0
- data/ext/shared.c +482 -0
- data/ext/shared.h +177 -0
- data/lib/gql_parser.so +0 -0
- data/lib/rails/graphql/adapters/mysql_adapter.rb +59 -0
- data/lib/rails/graphql/adapters/pg_adapter.rb +25 -22
- data/lib/rails/graphql/adapters/sqlite_adapter.rb +17 -14
- data/lib/rails/graphql/alternative/field_set.rb +36 -0
- data/lib/rails/graphql/alternative/mutation.rb +17 -0
- data/lib/rails/graphql/alternative/query.rb +93 -0
- data/lib/rails/graphql/alternative/subscription.rb +17 -0
- data/lib/rails/graphql/alternative.rb +20 -0
- data/lib/rails/graphql/argument.rb +21 -24
- data/lib/rails/graphql/callback.rb +24 -9
- data/lib/rails/graphql/collectors/hash_collector.rb +14 -6
- data/lib/rails/graphql/collectors/idented_collector.rb +10 -7
- data/lib/rails/graphql/collectors/json_collector.rb +22 -17
- data/lib/rails/graphql/collectors.rb +4 -4
- data/lib/rails/graphql/config.rb +130 -15
- data/lib/rails/graphql/directive/cached_directive.rb +33 -0
- data/lib/rails/graphql/directive/deprecated_directive.rb +10 -10
- data/lib/rails/graphql/directive/include_directive.rb +5 -4
- data/lib/rails/graphql/directive/skip_directive.rb +5 -4
- data/lib/rails/graphql/directive.rb +118 -63
- data/lib/rails/graphql/errors.rb +33 -4
- data/lib/rails/graphql/event.rb +16 -5
- data/lib/rails/graphql/field/authorized_field.rb +19 -3
- data/lib/rails/graphql/field/input_field.rb +11 -10
- data/lib/rails/graphql/field/mutation_field.rb +42 -7
- data/lib/rails/graphql/field/output_field.rb +102 -13
- data/lib/rails/graphql/field/proxied_field.rb +31 -22
- data/lib/rails/graphql/field/resolved_field.rb +26 -24
- data/lib/rails/graphql/field/scoped_config.rb +10 -4
- data/lib/rails/graphql/field/subscription_field.rb +140 -0
- data/lib/rails/graphql/field/typed_field.rb +43 -22
- data/lib/rails/graphql/field.rb +70 -56
- data/lib/rails/graphql/global_id.rb +85 -0
- data/lib/rails/graphql/helpers/attribute_delegator.rb +5 -5
- data/lib/rails/graphql/helpers/inherited_collection/array.rb +50 -0
- data/lib/rails/graphql/helpers/inherited_collection/base.rb +43 -0
- data/lib/rails/graphql/helpers/inherited_collection/hash.rb +87 -0
- data/lib/rails/graphql/helpers/inherited_collection.rb +25 -76
- data/lib/rails/graphql/helpers/instantiable.rb +15 -0
- data/lib/rails/graphql/helpers/leaf_from_ar.rb +7 -7
- data/lib/rails/graphql/helpers/registerable.rb +44 -62
- data/lib/rails/graphql/helpers/unregisterable.rb +16 -0
- data/lib/rails/graphql/helpers/with_arguments.rb +31 -27
- data/lib/rails/graphql/helpers/with_assignment.rb +6 -6
- data/lib/rails/graphql/helpers/with_callbacks.rb +25 -8
- data/lib/rails/graphql/helpers/with_description.rb +71 -0
- data/lib/rails/graphql/helpers/with_directives.rb +54 -30
- data/lib/rails/graphql/helpers/with_events.rb +21 -23
- data/lib/rails/graphql/helpers/with_fields.rb +76 -22
- data/lib/rails/graphql/helpers/with_global_id.rb +22 -0
- data/lib/rails/graphql/helpers/with_name.rb +43 -0
- data/lib/rails/graphql/helpers/with_namespace.rb +7 -4
- data/lib/rails/graphql/helpers/with_owner.rb +8 -7
- data/lib/rails/graphql/helpers/with_schema_fields.rb +137 -55
- data/lib/rails/graphql/helpers/with_validator.rb +9 -9
- data/lib/rails/graphql/helpers.rb +10 -3
- data/lib/rails/graphql/introspection.rb +43 -36
- data/lib/rails/graphql/railtie.rb +88 -33
- data/lib/rails/graphql/railties/base_generator.rb +3 -9
- data/lib/rails/graphql/railties/channel.rb +157 -0
- data/lib/rails/graphql/railties/controller.rb +62 -17
- data/lib/rails/graphql/railties/controller_runtime.rb +5 -5
- data/lib/rails/graphql/railties/log_subscriber.rb +81 -14
- data/lib/rails/graphql/request/arguments.rb +24 -49
- data/lib/rails/graphql/request/backtrace.rb +191 -0
- data/lib/rails/graphql/request/component/field.rb +86 -65
- data/lib/rails/graphql/request/component/fragment.rb +72 -24
- data/lib/rails/graphql/request/component/operation/subscription.rb +164 -4
- data/lib/rails/graphql/request/component/operation.rb +63 -31
- data/lib/rails/graphql/request/component/spread.rb +68 -25
- data/lib/rails/graphql/request/component/typename.rb +27 -12
- data/lib/rails/graphql/request/component.rb +75 -36
- data/lib/rails/graphql/request/context.rb +18 -8
- data/lib/rails/graphql/request/errors.rb +16 -6
- data/lib/rails/graphql/request/event.rb +19 -8
- data/lib/rails/graphql/request/helpers/directives.rb +68 -27
- data/lib/rails/graphql/request/helpers/selection_set.rb +51 -25
- data/lib/rails/graphql/request/helpers/value_writers.rb +18 -16
- data/lib/rails/graphql/request/prepared_data.rb +98 -0
- data/lib/rails/graphql/request/steps/authorizable.rb +24 -14
- data/lib/rails/graphql/request/steps/organizable.rb +110 -48
- data/lib/rails/graphql/request/steps/{prepareable.rb → preparable.rb} +20 -7
- data/lib/rails/graphql/request/steps/{resolveable.rb → resolvable.rb} +15 -6
- data/lib/rails/graphql/request/strategy/cached_strategy.rb +64 -0
- data/lib/rails/graphql/request/strategy/dynamic_instance.rb +6 -6
- data/lib/rails/graphql/request/strategy/multi_query_strategy.rb +6 -13
- data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +9 -9
- data/lib/rails/graphql/request/strategy.rb +131 -75
- data/lib/rails/graphql/request/subscription.rb +80 -0
- data/lib/rails/graphql/request.rb +305 -86
- data/lib/rails/graphql/schema.rb +240 -48
- data/lib/rails/graphql/shortcuts.rb +22 -3
- data/lib/rails/graphql/source/active_record/builders.rb +49 -35
- data/lib/rails/graphql/source/active_record_source.rb +70 -54
- data/lib/rails/graphql/source/base.rb +111 -0
- data/lib/rails/graphql/source/builder.rb +128 -0
- data/lib/rails/graphql/source/scoped_arguments.rb +31 -19
- data/lib/rails/graphql/source.rb +89 -213
- data/lib/rails/graphql/subscription/provider/action_cable.rb +112 -0
- data/lib/rails/graphql/subscription/provider/base.rb +191 -0
- data/lib/rails/graphql/subscription/provider.rb +18 -0
- data/lib/rails/graphql/subscription/store/base.rb +145 -0
- data/lib/rails/graphql/subscription/store/memory.rb +127 -0
- data/lib/rails/graphql/subscription/store.rb +19 -0
- data/lib/rails/graphql/subscription.rb +17 -0
- data/lib/rails/graphql/to_gql.rb +29 -32
- data/lib/rails/graphql/type/enum/directive_location_enum.rb +11 -11
- data/lib/rails/graphql/type/enum/type_kind_enum.rb +3 -3
- data/lib/rails/graphql/type/enum.rb +34 -48
- data/lib/rails/graphql/type/input.rb +74 -23
- data/lib/rails/graphql/type/interface.rb +16 -26
- data/lib/rails/graphql/type/object/directive_object.rb +4 -4
- data/lib/rails/graphql/type/object/enum_value_object.rb +3 -3
- data/lib/rails/graphql/type/object/field_object.rb +24 -6
- data/lib/rails/graphql/type/object/input_value_object.rb +3 -3
- data/lib/rails/graphql/type/object/schema_object.rb +5 -8
- data/lib/rails/graphql/type/object/type_object.rb +29 -19
- data/lib/rails/graphql/type/object.rb +26 -23
- data/lib/rails/graphql/type/scalar/any_scalar.rb +30 -0
- data/lib/rails/graphql/type/scalar/bigint_scalar.rb +5 -5
- data/lib/rails/graphql/type/scalar/binary_scalar.rb +3 -3
- data/lib/rails/graphql/type/scalar/boolean_scalar.rb +8 -8
- data/lib/rails/graphql/type/scalar/date_scalar.rb +3 -3
- data/lib/rails/graphql/type/scalar/date_time_scalar.rb +3 -3
- data/lib/rails/graphql/type/scalar/decimal_scalar.rb +3 -3
- data/lib/rails/graphql/type/scalar/float_scalar.rb +5 -5
- data/lib/rails/graphql/type/scalar/id_scalar.rb +6 -5
- data/lib/rails/graphql/type/scalar/int_scalar.rb +6 -5
- data/lib/rails/graphql/type/scalar/json_scalar.rb +39 -0
- data/lib/rails/graphql/type/scalar/string_scalar.rb +18 -4
- data/lib/rails/graphql/type/scalar/time_scalar.rb +5 -5
- data/lib/rails/graphql/type/scalar.rb +25 -22
- data/lib/rails/graphql/type/union.rb +14 -16
- data/lib/rails/graphql/type.rb +34 -25
- data/lib/rails/graphql/type_map.rb +256 -164
- data/lib/rails/graphql/uri.rb +166 -0
- data/lib/rails/graphql/version.rb +15 -3
- data/lib/rails/graphql.rake +3 -0
- data/lib/rails/graphql.rb +85 -52
- data/lib/rails-graphql.rb +1 -1
- data/test/assets/en.yml +29 -0
- data/test/assets/introspection-mem.txt +1 -1
- data/test/assets/mem.gql +18 -45
- data/test/assets/mysql.gql +392 -0
- data/test/assets/sqlite.gql +21 -12
- data/test/assets/translate.gql +335 -0
- data/test/config.rb +18 -8
- data/test/graphql/schema_test.rb +12 -19
- data/test/graphql/source_test.rb +8 -75
- data/test/graphql/type/enum_test.rb +207 -203
- data/test/graphql/type/input_test.rb +14 -9
- data/test/graphql/type/interface_test.rb +4 -4
- data/test/graphql/type/scalar/any_scalar_test.rb +38 -0
- data/test/graphql/type/scalar/boolean_scalar_test.rb +6 -3
- data/test/graphql/type/scalar/json_scalar_test.rb +23 -0
- data/test/graphql/type_map_test.rb +51 -66
- data/test/graphql/type_test.rb +0 -19
- data/test/graphql_test.rb +1 -1
- data/test/integration/{authorization/authorization_test.rb → authorization_test.rb} +40 -14
- data/test/integration/config.rb +36 -3
- data/test/integration/customization_test.rb +39 -0
- data/test/integration/global_id_test.rb +99 -0
- data/test/integration/memory/star_wars_introspection_test.rb +24 -16
- data/test/integration/memory/star_wars_query_test.rb +54 -3
- data/test/integration/memory/star_wars_validation_test.rb +1 -1
- data/test/integration/mysql/star_wars_introspection_test.rb +25 -0
- data/test/integration/persisted_query_test.rb +87 -0
- data/test/integration/resolver_precedence_test.rb +154 -0
- data/test/integration/schemas/memory.rb +22 -7
- data/test/integration/schemas/mysql.rb +62 -0
- data/test/integration/schemas/sqlite.rb +21 -12
- data/test/integration/sqlite/star_wars_global_id_test.rb +83 -0
- data/test/integration/sqlite/star_wars_introspection_test.rb +10 -0
- data/test/integration/sqlite/star_wars_query_test.rb +14 -1
- data/test/integration/translate_test.rb +61 -0
- data/test/test_ext.rb +16 -13
- metadata +108 -157
- data/ext/depend +0 -3
- data/ext/graphqlparser/Ast.cpp +0 -346
- data/ext/graphqlparser/Ast.h +0 -1214
- data/ext/graphqlparser/AstNode.h +0 -36
- data/ext/graphqlparser/AstVisitor.h +0 -137
- data/ext/graphqlparser/GraphQLParser.cpp +0 -76
- data/ext/graphqlparser/GraphQLParser.h +0 -55
- data/ext/graphqlparser/JsonVisitor.cpp +0 -161
- data/ext/graphqlparser/JsonVisitor.cpp.inc +0 -456
- data/ext/graphqlparser/JsonVisitor.h +0 -121
- data/ext/graphqlparser/JsonVisitor.h.inc +0 -110
- data/ext/graphqlparser/VERSION +0 -1
- data/ext/graphqlparser/c/GraphQLAst.cpp +0 -324
- data/ext/graphqlparser/c/GraphQLAst.h +0 -180
- data/ext/graphqlparser/c/GraphQLAstForEachConcreteType.h +0 -44
- data/ext/graphqlparser/c/GraphQLAstNode.cpp +0 -25
- data/ext/graphqlparser/c/GraphQLAstNode.h +0 -33
- data/ext/graphqlparser/c/GraphQLAstToJSON.cpp +0 -21
- data/ext/graphqlparser/c/GraphQLAstToJSON.h +0 -24
- data/ext/graphqlparser/c/GraphQLAstVisitor.cpp +0 -55
- data/ext/graphqlparser/c/GraphQLAstVisitor.h +0 -53
- data/ext/graphqlparser/c/GraphQLParser.cpp +0 -35
- data/ext/graphqlparser/c/GraphQLParser.h +0 -54
- data/ext/graphqlparser/dump_json_ast.cpp +0 -48
- data/ext/graphqlparser/lexer.lpp +0 -324
- data/ext/graphqlparser/parser.ypp +0 -693
- data/ext/graphqlparser/parsergen/lexer.cpp +0 -2633
- data/ext/graphqlparser/parsergen/lexer.h +0 -528
- data/ext/graphqlparser/parsergen/location.hh +0 -189
- data/ext/graphqlparser/parsergen/parser.tab.cpp +0 -3300
- data/ext/graphqlparser/parsergen/parser.tab.hpp +0 -646
- data/ext/graphqlparser/parsergen/position.hh +0 -179
- data/ext/graphqlparser/parsergen/stack.hh +0 -156
- data/ext/graphqlparser/syntaxdefs.h +0 -19
- data/ext/libgraphqlparser/AstNode.h +0 -36
- data/ext/libgraphqlparser/CMakeLists.txt +0 -148
- data/ext/libgraphqlparser/CONTRIBUTING.md +0 -23
- data/ext/libgraphqlparser/GraphQLParser.cpp +0 -76
- data/ext/libgraphqlparser/GraphQLParser.h +0 -55
- data/ext/libgraphqlparser/JsonVisitor.cpp +0 -161
- data/ext/libgraphqlparser/JsonVisitor.h +0 -121
- data/ext/libgraphqlparser/LICENSE +0 -22
- data/ext/libgraphqlparser/README.clang-tidy +0 -7
- data/ext/libgraphqlparser/README.md +0 -84
- data/ext/libgraphqlparser/ast/ast.ast +0 -203
- data/ext/libgraphqlparser/ast/ast.py +0 -61
- data/ext/libgraphqlparser/ast/c.py +0 -100
- data/ext/libgraphqlparser/ast/c.pyc +0 -0
- data/ext/libgraphqlparser/ast/c_impl.py +0 -61
- data/ext/libgraphqlparser/ast/c_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/c_visitor_impl.py +0 -39
- data/ext/libgraphqlparser/ast/c_visitor_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/casing.py +0 -26
- data/ext/libgraphqlparser/ast/casing.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx.py +0 -197
- data/ext/libgraphqlparser/ast/cxx.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_impl.py +0 -61
- data/ext/libgraphqlparser/ast/cxx_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_header.py +0 -42
- data/ext/libgraphqlparser/ast/cxx_json_visitor_header.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.py +0 -80
- data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_visitor.py +0 -64
- data/ext/libgraphqlparser/ast/cxx_visitor.pyc +0 -0
- data/ext/libgraphqlparser/ast/js.py +0 -65
- data/ext/libgraphqlparser/ast/license.py +0 -10
- data/ext/libgraphqlparser/ast/license.pyc +0 -0
- data/ext/libgraphqlparser/c/GraphQLAstNode.cpp +0 -25
- data/ext/libgraphqlparser/c/GraphQLAstNode.h +0 -33
- data/ext/libgraphqlparser/c/GraphQLAstToJSON.cpp +0 -21
- data/ext/libgraphqlparser/c/GraphQLAstToJSON.h +0 -24
- data/ext/libgraphqlparser/c/GraphQLAstVisitor.cpp +0 -55
- data/ext/libgraphqlparser/c/GraphQLAstVisitor.h +0 -53
- data/ext/libgraphqlparser/c/GraphQLParser.cpp +0 -35
- data/ext/libgraphqlparser/c/GraphQLParser.h +0 -54
- data/ext/libgraphqlparser/clang-tidy-all.sh +0 -3
- data/ext/libgraphqlparser/cmake/version.cmake +0 -16
- data/ext/libgraphqlparser/dump_json_ast.cpp +0 -48
- data/ext/libgraphqlparser/go/README.md +0 -20
- data/ext/libgraphqlparser/go/callbacks.go +0 -18
- data/ext/libgraphqlparser/go/gotest.go +0 -64
- data/ext/libgraphqlparser/lexer.lpp +0 -324
- data/ext/libgraphqlparser/libgraphqlparser.pc.in +0 -11
- data/ext/libgraphqlparser/parser.ypp +0 -693
- data/ext/libgraphqlparser/parsergen/lexer.cpp +0 -2633
- data/ext/libgraphqlparser/parsergen/lexer.h +0 -528
- data/ext/libgraphqlparser/parsergen/location.hh +0 -189
- data/ext/libgraphqlparser/parsergen/parser.tab.cpp +0 -3300
- data/ext/libgraphqlparser/parsergen/parser.tab.hpp +0 -646
- data/ext/libgraphqlparser/parsergen/position.hh +0 -179
- data/ext/libgraphqlparser/parsergen/stack.hh +0 -156
- data/ext/libgraphqlparser/python/CMakeLists.txt +0 -14
- data/ext/libgraphqlparser/python/README.md +0 -5
- data/ext/libgraphqlparser/python/example.py +0 -31
- data/ext/libgraphqlparser/syntaxdefs.h +0 -19
- data/ext/libgraphqlparser/test/BuildCAPI.c +0 -5
- data/ext/libgraphqlparser/test/CMakeLists.txt +0 -25
- data/ext/libgraphqlparser/test/JsonVisitorTests.cpp +0 -28
- data/ext/libgraphqlparser/test/ParserTests.cpp +0 -352
- data/ext/libgraphqlparser/test/kitchen-sink.graphql +0 -59
- data/ext/libgraphqlparser/test/kitchen-sink.json +0 -1
- data/ext/libgraphqlparser/test/schema-kitchen-sink.graphql +0 -78
- data/ext/libgraphqlparser/test/schema-kitchen-sink.json +0 -1
- data/ext/libgraphqlparser/test/valgrind.supp +0 -33
- data/ext/version.cpp +0 -21
- data/lib/graphqlparser.so +0 -0
- data/lib/rails/graphql/native/functions.rb +0 -38
- data/lib/rails/graphql/native/location.rb +0 -41
- data/lib/rails/graphql/native/pointers.rb +0 -23
- data/lib/rails/graphql/native/visitor.rb +0 -349
- data/lib/rails/graphql/native.rb +0 -56
- data/test/integration/schemas/authorization.rb +0 -12
@@ -1,14 +1,174 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Rails
|
4
|
-
module GraphQL
|
5
|
-
class Request
|
6
|
-
class Component
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
class Request
|
6
|
+
class Component
|
7
7
|
# = GraphQL Request Component Subscription Operation
|
8
8
|
#
|
9
9
|
# Handles a subscription operation inside a request.
|
10
10
|
class Operation::Subscription < Operation
|
11
|
+
UNSUBSCRIBED_PAYLOAD = { 'more' => false }.freeze
|
12
|
+
UNSUBSCRIBED_RESULT = Object.new
|
13
|
+
|
11
14
|
redefine_singleton_method(:subscription?) { true }
|
15
|
+
|
16
|
+
def initialize(*)
|
17
|
+
@initial = true
|
18
|
+
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
# Fetch the subscription only when necessary
|
23
|
+
def subscription
|
24
|
+
return unless defined?(@subscription)
|
25
|
+
return @subscription if @subscription.is_a?(Request::Subscription)
|
26
|
+
@subscription = schema.subscription_provider.fetch(@subscription)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Check if the operation is running in its first iteration, which will
|
30
|
+
# produce a subscription to the field
|
31
|
+
def subscribing?
|
32
|
+
@initial
|
33
|
+
end
|
34
|
+
|
35
|
+
# Check if the current operation is running under broadcasting mode
|
36
|
+
def broadcasting?
|
37
|
+
request.context.broadcasting == true
|
38
|
+
end
|
39
|
+
|
40
|
+
# Check if the operation can be broadcasted
|
41
|
+
def broadcastable?
|
42
|
+
return @broadcastable if defined?(@broadcastable)
|
43
|
+
@broadcastable = selection.each_value.all?(&:broadcastable?)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Either throw back an empty result or skip the operation
|
47
|
+
def no_update!
|
48
|
+
subscribing? ? skip! : throw(:skip_subscription_update, EMPTY_HASH)
|
49
|
+
end
|
50
|
+
|
51
|
+
# If unsubscribe is called during an update, skip and return a proper
|
52
|
+
# result
|
53
|
+
def unsubscribe!
|
54
|
+
if subscribing?
|
55
|
+
schema.remove_subscriptions(subscription.sid) if subscription.present?
|
56
|
+
else
|
57
|
+
throw(:skip_subscription_update, UNSUBSCRIBED_RESULT)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Build the cache object
|
62
|
+
def cache_dump(initial = true)
|
63
|
+
hash = super()
|
64
|
+
hash[:initial] = initial
|
65
|
+
hash[:broadcastable] = broadcastable?
|
66
|
+
|
67
|
+
unless initial
|
68
|
+
hash[:sid] = @subscription&.sid
|
69
|
+
hash[:variables] = @variables
|
70
|
+
end
|
71
|
+
|
72
|
+
hash
|
73
|
+
end
|
74
|
+
|
75
|
+
# Organize from cache data
|
76
|
+
def cache_load(data)
|
77
|
+
@initial = data[:initial]
|
78
|
+
@broadcastable = data[:broadcastable]
|
79
|
+
|
80
|
+
unless subscribing?
|
81
|
+
@variables = data[:variables]
|
82
|
+
@subscription = data[:sid]
|
83
|
+
end
|
84
|
+
|
85
|
+
super
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
# Rewrite this method so that the subscription can be generated in
|
91
|
+
# the right place
|
92
|
+
def resolve_then(after_block = nil, &block)
|
93
|
+
subscribe_block = -> do
|
94
|
+
save_subscription
|
95
|
+
trigger_event(:subscribed)
|
96
|
+
after_block.call if after_block.present?
|
97
|
+
end
|
98
|
+
|
99
|
+
super(subscribe_block, &block)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Save the subscription using the schema subscription provider
|
103
|
+
def save_subscription
|
104
|
+
return unless subscribing? && !(invalid? ||skipped?)
|
105
|
+
check_invalid_subscription!
|
106
|
+
|
107
|
+
@subscription = Request::Subscription.new(request, self)
|
108
|
+
request.write_cache_request(subscription.sid, build_subscription_cache)
|
109
|
+
schema.add_subscriptions(subscription)
|
110
|
+
|
111
|
+
request.subscriptions[subscription.sid] = subscription
|
112
|
+
rescue => e
|
113
|
+
if subscription.present?
|
114
|
+
schema.delete_from_cache(subscription.sid)
|
115
|
+
schema.remove_subscriptions(subscription.sid)
|
116
|
+
end
|
117
|
+
|
118
|
+
raise SubscriptionError, (+<<~MSG).squish
|
119
|
+
Unable to proper setup a subscription on #{display_name}: #{e.message}
|
120
|
+
MSG
|
121
|
+
end
|
122
|
+
|
123
|
+
# A subscription is invalid if more than one field was requested or
|
124
|
+
# if the only thing in the selection is a spread
|
125
|
+
def check_invalid_subscription!
|
126
|
+
raise ValidationError, (+<<~MSG).squish if @selection.size != 1
|
127
|
+
It has #{@selection.size} and must have 1 single field on it.
|
128
|
+
MSG
|
129
|
+
|
130
|
+
element = @selection.each_value.first
|
131
|
+
raise ValidationError, (+<<~MSG).squish unless element.kind == :field
|
132
|
+
The only element inside of it must be a field, #{element.kind} found.
|
133
|
+
MSG
|
134
|
+
|
135
|
+
raise ValidationError, (+<<~MSG).squish if element.is_a?(Component::Typename)
|
136
|
+
Unable to subscribe to a __typeName field.
|
137
|
+
MSG
|
138
|
+
|
139
|
+
raise ValidationError, (+<<~MSG).squish if element.unresolvable?
|
140
|
+
Field is unresolvable.
|
141
|
+
MSG
|
142
|
+
|
143
|
+
raise ValidationError, (+<<~MSG).squish unless schema.accepts_subscription?(self)
|
144
|
+
Operation unaccepted.
|
145
|
+
MSG
|
146
|
+
end
|
147
|
+
|
148
|
+
# Very similar to the +cache_dump+ from the request, however this
|
149
|
+
# will chop the parts exclusively for the subscription
|
150
|
+
def build_subscription_cache
|
151
|
+
# Prepare each used fragment to be set on cache and on the document
|
152
|
+
frag_nodes = []
|
153
|
+
fragments = used_fragments.empty? ? nil : begin
|
154
|
+
request.fragments.slice(*used_fragments).transform_values do |frag|
|
155
|
+
frag_nodes << frag.instance_variable_get(:@node)
|
156
|
+
frag.try(:cache_dump)
|
157
|
+
end.compact
|
158
|
+
end
|
159
|
+
|
160
|
+
# Return the cache result
|
161
|
+
{
|
162
|
+
strategy: { class: request.strategy.class },
|
163
|
+
operation_name: request.operation_name,
|
164
|
+
type_map_version: request.schema.version,
|
165
|
+
document: [[@node], frag_nodes.presence],
|
166
|
+
errors: request.errors.cache_dump,
|
167
|
+
operations: { name => cache_dump(false) },
|
168
|
+
fragments: fragments,
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
12
172
|
end
|
13
173
|
end
|
14
174
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Rails
|
4
|
-
module GraphQL
|
5
|
-
class Request
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
class Request
|
6
6
|
# = GraphQL Request Component Operation
|
7
7
|
#
|
8
8
|
# This class holds information about a given operation. This will guide
|
@@ -16,9 +16,9 @@ module Rails # :nodoc:
|
|
16
16
|
class << self
|
17
17
|
alias type kind
|
18
18
|
|
19
|
-
# Helper method to initialize an operation given the
|
20
|
-
def build(request, node
|
21
|
-
request.build(const_get(
|
19
|
+
# Helper method to initialize an operation given the node
|
20
|
+
def build(request, node)
|
21
|
+
request.build(const_get(node.type.to_s.classify), request, node)
|
22
22
|
end
|
23
23
|
|
24
24
|
# Rewrite the kind to always return +:operation+
|
@@ -49,6 +49,7 @@ module Rails # :nodoc:
|
|
49
49
|
autoload :Subscription
|
50
50
|
|
51
51
|
delegate :type, :query?, :mutation?, :subscription?, to: :class
|
52
|
+
delegate :schema, to: :strategy
|
52
53
|
|
53
54
|
attr_reader :name, :variables, :arguments, :request
|
54
55
|
|
@@ -57,11 +58,11 @@ module Rails # :nodoc:
|
|
57
58
|
alias vars variables
|
58
59
|
alias all_arguments arguments
|
59
60
|
|
60
|
-
def initialize(request, node
|
61
|
-
@name =
|
61
|
+
def initialize(request, node)
|
62
|
+
@name = node[1]
|
62
63
|
@request = request
|
63
64
|
|
64
|
-
super(node
|
65
|
+
super(node)
|
65
66
|
|
66
67
|
check_invalid_operation!
|
67
68
|
end
|
@@ -88,14 +89,40 @@ module Rails # :nodoc:
|
|
88
89
|
response.safe_add(name, nil) if stacked_selection?
|
89
90
|
end
|
90
91
|
|
91
|
-
# Stores all the used
|
92
|
+
# Stores all the used variables to report not used ones
|
92
93
|
def used_variables
|
93
94
|
@used_variables ||= Set.new
|
94
95
|
end
|
95
96
|
|
97
|
+
# Stores all the used fragments
|
98
|
+
def used_fragments
|
99
|
+
@used_fragments ||= Set.new
|
100
|
+
end
|
101
|
+
|
96
102
|
# A fast way to access the correct display name for log or errors
|
97
103
|
def log_source
|
98
|
-
@log_source ||= name.blank? ? type : "#{name} #{type}"
|
104
|
+
@log_source ||= name.blank? ? type : +"#{name} #{type}"
|
105
|
+
end
|
106
|
+
|
107
|
+
# The hash of operations must take into consideration the used fragments
|
108
|
+
def hash
|
109
|
+
return super unless defined?(@used_fragments)
|
110
|
+
|
111
|
+
super ^ used_fragments.reduce(0) do |value, fragment|
|
112
|
+
request.fragments[fragment].hash
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Build the cache object
|
117
|
+
def cache_dump
|
118
|
+
super.merge(type: self.class)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Organize from cache data
|
122
|
+
def cache_load(data)
|
123
|
+
@name = data[:node][1]
|
124
|
+
|
125
|
+
super
|
99
126
|
end
|
100
127
|
|
101
128
|
protected
|
@@ -113,9 +140,9 @@ module Rails # :nodoc:
|
|
113
140
|
# Perform the organization step
|
114
141
|
def organize_then(&block)
|
115
142
|
super(block) do
|
116
|
-
parse_variables
|
117
|
-
parse_directives(type)
|
118
|
-
parse_selection
|
143
|
+
parse_variables(@node[2])
|
144
|
+
parse_directives(@node[3], type)
|
145
|
+
parse_selection(@node[4])
|
119
146
|
end
|
120
147
|
end
|
121
148
|
|
@@ -133,42 +160,47 @@ module Rails # :nodoc:
|
|
133
160
|
|
134
161
|
# Name used for debug purposes
|
135
162
|
def display_name
|
136
|
-
@display_name ||= "#{type.to_s.titlecase} #{name.presence || '__default__'}"
|
163
|
+
@display_name ||= +"#{type.to_s.titlecase} #{name.presence || '__default__'}"
|
137
164
|
end
|
138
165
|
|
139
|
-
# Add an error for each not used variable
|
166
|
+
# Add an error for each not used variable
|
140
167
|
def report_unused_variables
|
168
|
+
return if arguments.nil?
|
169
|
+
|
141
170
|
(arguments.keys - used_variables.to_a).each do |key|
|
142
171
|
argument = arguments[key]
|
143
|
-
request.report_node_error(
|
144
|
-
|
172
|
+
request.report_node_error((+<<~MSG).squish, argument.node)
|
173
|
+
Variable $#{argument.gql_name} was provided to #{log_source} but not used.
|
145
174
|
MSG
|
146
175
|
end
|
147
176
|
|
148
|
-
|
177
|
+
# Report all used variables to the request for greater scope
|
178
|
+
request.instance_variable_get(:@used_variables).merge(arguments.keys)
|
179
|
+
|
180
|
+
# Clear anything that is not necessary anymore
|
149
181
|
@used_variables = nil
|
150
182
|
end
|
151
183
|
|
152
184
|
# If there is another operation with the same name already defined,
|
153
185
|
# raise an error. If an anonymous was started, then any other
|
154
|
-
#
|
186
|
+
# operations is invalid.
|
155
187
|
def check_invalid_operation!
|
156
|
-
|
157
|
-
|
188
|
+
other = request.document[0].find do |other|
|
189
|
+
other != @node && name == other[1]
|
190
|
+
end
|
191
|
+
|
192
|
+
return if other.nil?
|
193
|
+
invalidate!
|
158
194
|
|
159
|
-
|
195
|
+
if name.nil?
|
196
|
+
request.report_node_error((+<<~MSG).squish, self)
|
160
197
|
Unable to process the operation #{display_name} when the document
|
161
198
|
contain multiple anonymous operations.
|
162
199
|
MSG
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
other_node = request.operations[name].instance_variable_get(:@node)
|
167
|
-
location = GraphQL::Native.get_location(other_node)
|
168
|
-
|
169
|
-
request.report_node_error(<<~MSG.squish, @node)
|
200
|
+
else
|
201
|
+
request.report_node_error((+<<~MSG).squish, self)
|
170
202
|
Duplicated operation named "#{name}" defined on
|
171
|
-
line #{
|
203
|
+
line #{other.begin_line}:#{other.begin_column}.
|
172
204
|
MSG
|
173
205
|
end
|
174
206
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Rails
|
4
|
-
module GraphQL
|
5
|
-
class Request
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
class Request
|
6
6
|
# = GraphQL Request Component Spread
|
7
7
|
#
|
8
8
|
# This class holds information about a given spread that should be
|
@@ -11,20 +11,18 @@ module Rails # :nodoc:
|
|
11
11
|
include SelectionSet
|
12
12
|
include Directives
|
13
13
|
|
14
|
-
delegate :operation, :typename, to: :parent
|
14
|
+
delegate :operation, :typename, :request, to: :parent
|
15
15
|
delegate :variables, to: :operation
|
16
16
|
|
17
|
-
|
17
|
+
attr_reader :name, :parent, :fragment, :current_object, :type_klass
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
def initialize(parent, node, data)
|
19
|
+
def initialize(parent, node)
|
22
20
|
@parent = parent
|
23
21
|
|
24
|
-
@name =
|
25
|
-
@inline =
|
22
|
+
@name = node[0]
|
23
|
+
@inline = name.nil?
|
26
24
|
|
27
|
-
super(node
|
25
|
+
super(node)
|
28
26
|
end
|
29
27
|
|
30
28
|
# Check if the object is an inline spread
|
@@ -32,9 +30,14 @@ module Rails # :nodoc:
|
|
32
30
|
@inline.present?
|
33
31
|
end
|
34
32
|
|
33
|
+
# Check if all the sub fields or the fragment is broadcastable
|
34
|
+
def broadcastable?
|
35
|
+
inline? ? selection.each_value.all?(&:broadcastable?) : fragment.broadcastable?
|
36
|
+
end
|
37
|
+
|
35
38
|
# Redirect to the fragment or check the inline type before resolving
|
36
39
|
def resolve_with!(object)
|
37
|
-
return if
|
40
|
+
return if unresolvable?
|
38
41
|
|
39
42
|
@current_object = object
|
40
43
|
resolve!
|
@@ -42,6 +45,25 @@ module Rails # :nodoc:
|
|
42
45
|
@current_object = nil
|
43
46
|
end
|
44
47
|
|
48
|
+
# Build the cache object
|
49
|
+
def cache_dump
|
50
|
+
inline? ? super.merge(type_klass: all_to_gid(type_klass)) : super
|
51
|
+
end
|
52
|
+
|
53
|
+
# Organize from cache data
|
54
|
+
def cache_load(data)
|
55
|
+
@name = data[:node][0]
|
56
|
+
@inline = name.nil?
|
57
|
+
|
58
|
+
if inline?
|
59
|
+
@type_klass = all_from_gid(data[:type_klass])
|
60
|
+
else
|
61
|
+
collect_fragment
|
62
|
+
end
|
63
|
+
|
64
|
+
super
|
65
|
+
end
|
66
|
+
|
45
67
|
protected
|
46
68
|
|
47
69
|
# Spread always resolve inline selection unstacked on response,
|
@@ -51,13 +73,13 @@ module Rails # :nodoc:
|
|
51
73
|
end
|
52
74
|
|
53
75
|
# Just provide the correct location for directives
|
54
|
-
def parse_directives
|
55
|
-
super(inline? ? :inline_fragment : :fragment_spread)
|
76
|
+
def parse_directives(nodes)
|
77
|
+
super(nodes, (inline? ? :inline_fragment : :fragment_spread))
|
56
78
|
end
|
57
79
|
|
58
80
|
# Scope the arguments whenever stacked within a spread
|
59
81
|
def stacked(*)
|
60
|
-
|
82
|
+
Arguments.scoped(operation) { super }
|
61
83
|
end
|
62
84
|
|
63
85
|
# Normal mode of the organize step
|
@@ -66,7 +88,6 @@ module Rails # :nodoc:
|
|
66
88
|
def organize
|
67
89
|
inline? ? organize_then { organize_fields } : organize_then do
|
68
90
|
run_on_fragment(:organize!)
|
69
|
-
# Ensure necessary variables
|
70
91
|
end
|
71
92
|
end
|
72
93
|
|
@@ -74,28 +95,28 @@ module Rails # :nodoc:
|
|
74
95
|
def organize_then(&block)
|
75
96
|
super(block) do
|
76
97
|
if inline?
|
77
|
-
@type_klass = find_type!(
|
78
|
-
|
98
|
+
@type_klass = find_type!(@node[1])
|
99
|
+
parse_directives(@node[2])
|
100
|
+
parse_selection(@node[3])
|
79
101
|
else
|
80
|
-
@
|
81
|
-
|
102
|
+
parse_directives(@node[2])
|
103
|
+
@fragment = collect_fragment
|
104
|
+
raise ArgumentError, (+<<~MSG).squish if @fragment.nil?
|
82
105
|
The "#{name}" fragment is not defined in this request.
|
83
106
|
MSG
|
84
107
|
end
|
85
|
-
|
86
|
-
parse_directives
|
87
108
|
end
|
88
109
|
end
|
89
110
|
|
90
111
|
# Spread has a special behavior when using a fragment
|
91
112
|
def prepare
|
92
113
|
return super if inline?
|
93
|
-
raise
|
114
|
+
raise(+'Prepare with fragment not implemented yet')
|
94
115
|
end
|
95
116
|
|
96
117
|
# Resolve the spread operation
|
97
118
|
def resolve
|
98
|
-
return if
|
119
|
+
return if unresolvable?
|
99
120
|
|
100
121
|
object = (defined?(@current_object) && @current_object) || parent.type_klass
|
101
122
|
return run_on_fragment(:resolve_with!, object) unless inline?
|
@@ -111,7 +132,29 @@ module Rails # :nodoc:
|
|
111
132
|
# Most of the things that are redirected to the fragment needs to run
|
112
133
|
# inside a arguments scoped
|
113
134
|
def run_on_fragment(method_name, *args)
|
114
|
-
|
135
|
+
Arguments.scoped(operation) { fragment.public_send(method_name, *args) }
|
136
|
+
end
|
137
|
+
|
138
|
+
# Only initialize the fragment once and only when requested for the
|
139
|
+
# first time. It also reports to the operation the used variables
|
140
|
+
# within the fragment
|
141
|
+
def collect_fragment
|
142
|
+
node = request.fragments[name]
|
143
|
+
|
144
|
+
if node.is_a?(Component::Fragment)
|
145
|
+
unless (used_variables = node.used_variables).nil?
|
146
|
+
operation.used_variables.merge(used_variables)
|
147
|
+
end
|
148
|
+
|
149
|
+
unless (used_fragments = node.used_fragments).nil?
|
150
|
+
operation.used_fragments.merge(used_fragments)
|
151
|
+
end
|
152
|
+
|
153
|
+
node
|
154
|
+
else
|
155
|
+
operation.used_fragments << name
|
156
|
+
request.fragments[name] = request.build(Component::Fragment, request, node)
|
157
|
+
end
|
115
158
|
end
|
116
159
|
end
|
117
160
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Rails
|
4
|
-
module GraphQL
|
5
|
-
class Request
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
class Request
|
6
6
|
# = GraphQL Request Component Typename
|
7
7
|
#
|
8
8
|
# Extra component which simulates a field that its only purpose is to
|
@@ -11,11 +11,9 @@ module Rails # :nodoc:
|
|
11
11
|
include ValueWriters
|
12
12
|
include Directives
|
13
13
|
|
14
|
-
delegate :operation, to: :parent
|
14
|
+
delegate :operation, :request, to: :parent
|
15
15
|
delegate :variables, to: :operation
|
16
16
|
|
17
|
-
parent_memoize :request
|
18
|
-
|
19
17
|
attr_reader :name, :alias_name, :parent
|
20
18
|
|
21
19
|
# Rewrite the kind to always return +:field+
|
@@ -23,13 +21,13 @@ module Rails # :nodoc:
|
|
23
21
|
:field
|
24
22
|
end
|
25
23
|
|
26
|
-
def initialize(parent, node
|
24
|
+
def initialize(parent, node)
|
27
25
|
@parent = parent
|
28
26
|
|
29
|
-
@name =
|
30
|
-
@alias_name =
|
27
|
+
@name = node[0]
|
28
|
+
@alias_name = node[1]
|
31
29
|
|
32
|
-
super(node
|
30
|
+
super(node)
|
33
31
|
end
|
34
32
|
|
35
33
|
# Set the value that the field will be resolved as
|
@@ -50,6 +48,23 @@ module Rails # :nodoc:
|
|
50
48
|
response.serialize(Type::Scalar::StringScalar, gql_name, value)
|
51
49
|
end
|
52
50
|
|
51
|
+
# Typename is always broadcastable
|
52
|
+
def broadcastable?
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
# Prepare is not necessary for this field
|
57
|
+
def prepare!
|
58
|
+
end
|
59
|
+
|
60
|
+
# Organize from cache data
|
61
|
+
def cache_load(data)
|
62
|
+
@name = data[:node][0]
|
63
|
+
@alias_name = data[:node][1]
|
64
|
+
|
65
|
+
super
|
66
|
+
end
|
67
|
+
|
53
68
|
protected
|
54
69
|
|
55
70
|
# Normal mode of the organize step
|
@@ -59,14 +74,14 @@ module Rails # :nodoc:
|
|
59
74
|
|
60
75
|
# Perform the organization step
|
61
76
|
def organize_then(&block)
|
62
|
-
super(block) { parse_directives(:field) }
|
77
|
+
super(block) { parse_directives(@node[3], :field) }
|
63
78
|
end
|
64
79
|
|
65
80
|
# Go through the right flow to write the value
|
66
81
|
def resolve_then
|
67
82
|
super do
|
68
83
|
typename = @typename || parent.typename
|
69
|
-
raise InvalidValueError,
|
84
|
+
raise InvalidValueError, (+<<~MSG).squish if typename.blank?
|
70
85
|
The #{gql_name} field value cannot be null.
|
71
86
|
MSG
|
72
87
|
|