rails-graphql 0.2.1 → 1.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- 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,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Rails
|
4
|
-
module GraphQL
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
5
|
# = GraphQL Request
|
6
6
|
#
|
7
7
|
# This class is responsible for processing a GraphQL response. It will
|
@@ -11,19 +11,38 @@ module Rails # :nodoc:
|
|
11
11
|
#
|
12
12
|
# ==== Options
|
13
13
|
#
|
14
|
+
# * <tt>:args</tt> - The arguments of the request, same as variables
|
15
|
+
# * <tt>:as</tt> - The format of the output of the request, supports both
|
16
|
+
# +:hash+ and +:string+ (defaults to :string)
|
17
|
+
# * <tt>:context</tt> - The context of the request, which can be accessed in
|
18
|
+
# fields, resolvers and so as a way to customize the result
|
19
|
+
# * <tt>:controller</tt> - From which controller this operation is running
|
20
|
+
# from, which provides view-like access to helpers and methods through the
|
21
|
+
# request
|
14
22
|
# * <tt>:namespace</tt> - Set what is the namespace used for the request
|
15
|
-
# (defaults to :base)
|
23
|
+
# (defaults to :base)
|
24
|
+
# * <tt>:operation_name</tt> - The name of the operation as a sort of label,
|
25
|
+
# it can also be collected by the name of the single operation in the
|
26
|
+
# request
|
27
|
+
# * <tt>:schema</tt> - The schema on which the request should run on. It
|
28
|
+
# has higher precedence than the namespace
|
29
|
+
# * <tt>:variables</tt> - The variables of the request
|
16
30
|
class Request
|
17
31
|
extend ActiveSupport::Autoload
|
18
32
|
|
19
|
-
RESPONSE_FORMATS = {
|
33
|
+
RESPONSE_FORMATS = {
|
34
|
+
string: :to_json,
|
35
|
+
object: :as_json,
|
36
|
+
json: :as_json,
|
37
|
+
hash: :as_json,
|
38
|
+
}.freeze
|
20
39
|
|
21
40
|
eager_autoload do
|
22
41
|
autoload_under :steps do
|
23
42
|
autoload :Authorizable
|
24
43
|
autoload :Organizable
|
25
|
-
autoload :
|
26
|
-
autoload :
|
44
|
+
autoload :Preparable
|
45
|
+
autoload :Resolvable
|
27
46
|
end
|
28
47
|
|
29
48
|
autoload_under :helpers do
|
@@ -33,15 +52,25 @@ module Rails # :nodoc:
|
|
33
52
|
end
|
34
53
|
|
35
54
|
autoload :Arguments
|
55
|
+
autoload :Backtrace
|
36
56
|
autoload :Component
|
37
57
|
autoload :Context
|
38
58
|
autoload :Errors
|
39
59
|
autoload :Event
|
60
|
+
autoload :PreparedData
|
40
61
|
autoload :Strategy
|
62
|
+
autoload :Subscription
|
41
63
|
end
|
42
64
|
|
43
|
-
attr_reader :
|
44
|
-
:
|
65
|
+
attr_reader :args, :origin, :errors, :fragments, :operations, :response, :schema,
|
66
|
+
:stack, :strategy, :document, :operation_name, :subscriptions
|
67
|
+
|
68
|
+
alias arguments args
|
69
|
+
alias controller origin
|
70
|
+
alias channel origin
|
71
|
+
|
72
|
+
delegate :action_name, to: :controller, allow_nil: true
|
73
|
+
delegate :all_listeners, :all_events, to: :schema
|
45
74
|
|
46
75
|
class << self
|
47
76
|
# Shortcut for initialize, set context, and execute
|
@@ -51,6 +80,16 @@ module Rails # :nodoc:
|
|
51
80
|
result.execute(*args, **xargs)
|
52
81
|
end
|
53
82
|
|
83
|
+
# Shortcut for initialize and compile
|
84
|
+
def compile(*args, schema: nil, namespace: :base, **xargs)
|
85
|
+
new(schema, namespace: namespace).compile(*args, **xargs)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Shortcut for initialize and validate
|
89
|
+
def valid?(*args, schema: nil, namespace: :base, **xargs)
|
90
|
+
new(schema, namespace: namespace).valid?(*args, **xargs)
|
91
|
+
end
|
92
|
+
|
54
93
|
# Allow accessing component-based objects through the request
|
55
94
|
def const_defined?(name, *)
|
56
95
|
Component.const_defined?(name) || super
|
@@ -60,32 +99,21 @@ module Rails # :nodoc:
|
|
60
99
|
def const_missing(name)
|
61
100
|
Component.const_defined?(name) ? Component.const_get(name) : super
|
62
101
|
end
|
63
|
-
|
64
|
-
def eager_load! # :nodoc:
|
65
|
-
super
|
66
|
-
|
67
|
-
Request::Component.eager_load!
|
68
|
-
Request::Strategy.eager_load!
|
69
|
-
end
|
70
102
|
end
|
71
103
|
|
72
104
|
# Forces the schema to be registered on type map before moving forward
|
73
105
|
def initialize(schema = nil, namespace: :base)
|
74
106
|
@namespace = schema&.namespace || namespace
|
75
107
|
@schema = GraphQL::Schema.find!(@namespace)
|
108
|
+
@prepared_data = {}
|
76
109
|
@extensions = {}
|
77
110
|
|
78
111
|
ensure_schema!
|
79
112
|
end
|
80
113
|
|
81
|
-
#
|
82
|
-
def
|
83
|
-
@
|
84
|
-
end
|
85
|
-
|
86
|
-
# Cache all the schema events for this current request
|
87
|
-
def all_events
|
88
|
-
@all_events ||= schema.all_events
|
114
|
+
# Check if any new subscription was added
|
115
|
+
def subscriptions?
|
116
|
+
defined?(@subscriptions) && @subscriptions.any?
|
89
117
|
end
|
90
118
|
|
91
119
|
# Get the context of the request
|
@@ -99,18 +127,84 @@ module Rails # :nodoc:
|
|
99
127
|
end
|
100
128
|
|
101
129
|
# Execute a given document with the given arguments
|
102
|
-
def execute(document,
|
103
|
-
|
130
|
+
def execute(document, **xargs)
|
131
|
+
output = xargs.delete(:as) || schema.config.default_response_format
|
132
|
+
cache = xargs.delete(:hash)
|
133
|
+
formatter = RESPONSE_FORMATS[output]
|
134
|
+
|
135
|
+
document, cache = nil, document if xargs.delete(:compiled)
|
136
|
+
prepared_data = xargs.delete(:data_for)
|
104
137
|
|
105
|
-
|
106
|
-
|
138
|
+
reset!(**xargs)
|
139
|
+
prepared_data&.each { |key, value| prepare_data_for(key, value) }
|
140
|
+
@response = initialize_response(output, formatter)
|
141
|
+
execute!(document, cache)
|
107
142
|
|
108
|
-
|
109
|
-
|
143
|
+
response.public_send(formatter)
|
144
|
+
rescue StaticResponse
|
145
|
+
response.public_send(formatter)
|
110
146
|
end
|
111
147
|
|
112
148
|
alias perform execute
|
113
149
|
|
150
|
+
# Compile a given document
|
151
|
+
def compile(document, compress: true)
|
152
|
+
reset!
|
153
|
+
|
154
|
+
log_execution(document, event: 'compile.graphql') do
|
155
|
+
@document = initialize_document(document)
|
156
|
+
run_document(with: :compile)
|
157
|
+
|
158
|
+
result = Marshal.dump(cache_dump)
|
159
|
+
result = Zlib.deflate(result) if compress
|
160
|
+
|
161
|
+
@log_extra[:total] = result.bytesize
|
162
|
+
result
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Check if the given document is valid by piggybacking on the compile
|
167
|
+
# process
|
168
|
+
def valid?(document)
|
169
|
+
reset!
|
170
|
+
|
171
|
+
log_execution(document, event: 'validate.graphql') do
|
172
|
+
@document = initialize_document(document)
|
173
|
+
run_document(with: :compile)
|
174
|
+
@log_extra[:result] = @errors.empty?
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# This is used by cache and static responses to jump from executing to
|
179
|
+
# delivery a response right away
|
180
|
+
def force_response(response, error = StaticResponse)
|
181
|
+
@response = response
|
182
|
+
raise error
|
183
|
+
end
|
184
|
+
|
185
|
+
# Add a new prepared data from +value+ to the given +field+
|
186
|
+
def prepare_data_for(field, value, **options)
|
187
|
+
field = PreparedData.lookup(self, field)
|
188
|
+
|
189
|
+
if @prepared_data.key?(field)
|
190
|
+
@prepared_data[field].push(value)
|
191
|
+
else
|
192
|
+
@prepared_data[field] = PreparedData.new(field, value, **options)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Recover the next prepared data for the given field
|
197
|
+
def prepared_data_for(field)
|
198
|
+
field = field.field if field.is_a?(Component::Field)
|
199
|
+
@prepared_data[field]
|
200
|
+
end
|
201
|
+
|
202
|
+
# Check if the given field has prepared data
|
203
|
+
def prepared_data_for?(field)
|
204
|
+
field = field.field if field.is_a?(Component::Field)
|
205
|
+
defined?(@prepared_data) && @prepared_data.key?(field)
|
206
|
+
end
|
207
|
+
|
114
208
|
# Build a easy-to-access object representing the current information of
|
115
209
|
# the execution to be used on +rescue_with_handler+
|
116
210
|
def build_rescue_object(**extra)
|
@@ -125,7 +219,8 @@ module Rails # :nodoc:
|
|
125
219
|
|
126
220
|
# Use schema handlers for exceptions caught during the execution process
|
127
221
|
def rescue_with_handler(exception, **extra)
|
128
|
-
|
222
|
+
ExtendedError.extend(exception, build_rescue_object(**extra))
|
223
|
+
schema.rescue_with_handler(exception)
|
129
224
|
end
|
130
225
|
|
131
226
|
# Add the given +exception+ to the errors using the +node+ location
|
@@ -136,17 +231,28 @@ module Rails # :nodoc:
|
|
136
231
|
|
137
232
|
# A little helper to report an error on a given node
|
138
233
|
def report_node_error(message, node, **xargs)
|
234
|
+
xargs[:locations] ||= location_of(node) unless xargs.key?(:line)
|
235
|
+
report_error(message, **xargs)
|
236
|
+
end
|
237
|
+
|
238
|
+
# Get the location object of a given node
|
239
|
+
def location_of(node)
|
139
240
|
node = node.instance_variable_get(:@node) if node.is_a?(Request::Component)
|
140
|
-
|
241
|
+
return unless node.is_a?(GQLParser::Token)
|
141
242
|
|
142
|
-
|
143
|
-
|
243
|
+
[
|
244
|
+
{ 'line' => node.begin_line, 'column' => node.begin_column },
|
245
|
+
{ 'line' => node.end_line, 'column' => node.end_column },
|
246
|
+
]
|
144
247
|
end
|
145
248
|
|
146
249
|
# The final helper that facilitates how errors are reported
|
147
250
|
def report_error(message, **xargs)
|
148
251
|
xargs[:path] ||= stack_to_path
|
149
252
|
errors.add(message, **xargs)
|
253
|
+
|
254
|
+
# Return nil for easier usage
|
255
|
+
nil
|
150
256
|
end
|
151
257
|
|
152
258
|
# Add the given +object+ into the execution +stack+ and execute the given
|
@@ -154,8 +260,6 @@ module Rails # :nodoc:
|
|
154
260
|
def stacked(object, &block)
|
155
261
|
stack.unshift(object)
|
156
262
|
block.call
|
157
|
-
rescue => exception
|
158
|
-
rescue_with_handler(exception) || raise
|
159
263
|
ensure
|
160
264
|
stack.shift
|
161
265
|
end
|
@@ -183,12 +287,12 @@ module Rails # :nodoc:
|
|
183
287
|
obj
|
184
288
|
end
|
185
289
|
|
186
|
-
#
|
187
|
-
def
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
290
|
+
# This allocates a new object which is aware of extensions
|
291
|
+
def build_from_cache(klass)
|
292
|
+
ext_module = extensions[klass]
|
293
|
+
obj = klass.allocate
|
294
|
+
obj.extend(ext_module) if ext_module
|
295
|
+
obj
|
192
296
|
end
|
193
297
|
|
194
298
|
# A shared way to cache information across the execution of an request
|
@@ -196,59 +300,136 @@ module Rails # :nodoc:
|
|
196
300
|
@cache[key] ||= (init_value || block&.call || {})
|
197
301
|
end
|
198
302
|
|
303
|
+
# A better way to ensure that nil values in a hash cache won't be
|
304
|
+
# reinitialized
|
305
|
+
def nested_cache(key, sub_key)
|
306
|
+
(source = cache(key)).key?(sub_key) ? source[sub_key] : source[sub_key] = yield
|
307
|
+
end
|
308
|
+
|
309
|
+
# Show if the current cached operation is still valid
|
310
|
+
def valid_cache?
|
311
|
+
defined?(@valid_cache) && @valid_cache
|
312
|
+
end
|
313
|
+
|
314
|
+
# Write the request into the cache so it can run again faster
|
315
|
+
def write_cache_request(hash, data = cache_dump)
|
316
|
+
schema.write_on_cache(hash, Marshal.dump(data))
|
317
|
+
end
|
318
|
+
|
319
|
+
# Read the request from the cache to run it faster
|
320
|
+
def read_cache_request(data = @document)
|
321
|
+
begin
|
322
|
+
data = Zlib.inflate(data) if data[0] == 'x'
|
323
|
+
data = Marshal.load(data)
|
324
|
+
rescue Zlib::BufError, ArgumentError
|
325
|
+
raise ::ArgumentError, +'Unable to recover the cached request.'
|
326
|
+
end
|
327
|
+
|
328
|
+
cache_load(data)
|
329
|
+
end
|
330
|
+
|
331
|
+
# Build the object that represent the request in the cache format
|
332
|
+
def cache_dump
|
333
|
+
{
|
334
|
+
strategy: @strategy.cache_dump,
|
335
|
+
operation_name: @operation_name,
|
336
|
+
type_map_version: schema.version,
|
337
|
+
document: @document,
|
338
|
+
errors: @errors.cache_dump,
|
339
|
+
operations: @operations.transform_values(&:cache_dump),
|
340
|
+
fragments: @fragments&.transform_values { |f| f.try(:cache_dump) }&.compact,
|
341
|
+
}
|
342
|
+
end
|
343
|
+
|
344
|
+
# Read the request from the cache to run it faster
|
345
|
+
def cache_load(data)
|
346
|
+
version = data[:type_map_version]
|
347
|
+
@document = data[:document]
|
348
|
+
@operation_name = data[:operation_name]
|
349
|
+
resolve_from_cache = (version == schema.version)
|
350
|
+
|
351
|
+
# Run the document from scratch if TypeMap has changed
|
352
|
+
return run_document unless resolve_from_cache
|
353
|
+
@valid_cache = true unless defined?(@valid_cache)
|
354
|
+
|
355
|
+
# Run the document as a cached operation
|
356
|
+
errors.cache_load(data[:errors])
|
357
|
+
@strategy = build(data[:strategy][:class], self)
|
358
|
+
@strategy.trigger_event(:request)
|
359
|
+
@strategy.cache_load(data)
|
360
|
+
@strategy.resolve!
|
361
|
+
end
|
362
|
+
|
199
363
|
private
|
200
364
|
|
201
365
|
attr_reader :extensions
|
202
366
|
|
203
367
|
# Reset principal variables and set the given +args+
|
204
|
-
def reset!(args: nil, variables: {}, operation_name: nil)
|
205
|
-
@
|
206
|
-
|
207
|
-
@
|
368
|
+
def reset!(args: nil, variables: {}, operation_name: nil, origin: nil)
|
369
|
+
@arg_names = {}
|
370
|
+
|
371
|
+
@args = (args || variables || {}).transform_keys do |key|
|
372
|
+
key.to_s.camelize(:lower).tap do |sanitized_key|
|
373
|
+
@arg_names[sanitized_key] = key
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
@args = build_ostruct(@args).freeze
|
378
|
+
@errors = Request::Errors.new(self)
|
208
379
|
@operation_name = operation_name
|
380
|
+
@origin = origin
|
209
381
|
|
210
382
|
@stack = [schema]
|
211
383
|
@cache = {}
|
384
|
+
@log_extra = {}
|
212
385
|
@fragments = {}
|
213
386
|
@operations = {}
|
387
|
+
@subscriptions = {}
|
388
|
+
@used_variables = Set.new
|
389
|
+
|
390
|
+
@strategy = nil
|
391
|
+
schema.validate
|
214
392
|
end
|
215
393
|
|
216
394
|
# This executes the whole process capturing any exceptions and handling
|
217
395
|
# them as defined by the schema
|
218
|
-
def execute!(document)
|
219
|
-
log_execution(document) do
|
220
|
-
@document =
|
221
|
-
|
222
|
-
|
223
|
-
@strategy = find_strategy!
|
224
|
-
@strategy.trigger_event(:request)
|
225
|
-
@strategy.resolve!
|
396
|
+
def execute!(document, cache = nil)
|
397
|
+
log_execution(document, cache) do
|
398
|
+
@document = initialize_document(document, cache)
|
399
|
+
@document.is_a?(String) ? read_cache_request : run_document
|
226
400
|
end
|
227
|
-
rescue ParseError => err
|
228
|
-
parts = err.message.match(/\A(\d+)\.(\d+)(?:-\d+)?: (.*)\z/)
|
229
|
-
errors.add(parts[3], line: parts[1], col: parts[2])
|
230
401
|
ensure
|
402
|
+
report_unused_variables
|
403
|
+
write_cache_request(cache) if cache.present? && !valid_cache?
|
404
|
+
|
231
405
|
@cache.clear
|
232
|
-
@
|
406
|
+
@strategy&.clear
|
407
|
+
@fragments&.clear
|
233
408
|
@operations.clear
|
409
|
+
@prepared_data.clear
|
234
410
|
|
235
|
-
@visitor.terminate
|
236
|
-
@visitor = nil
|
237
|
-
|
238
|
-
GraphQL::Native.free_node(@document) if defined?(@document)
|
239
411
|
@response.try(:append_errors, errors)
|
240
412
|
end
|
241
413
|
|
242
|
-
#
|
414
|
+
# Prepare the definitions, find the strategy and resolve
|
415
|
+
def run_document(with: :resolve!)
|
416
|
+
return if @document.nil?
|
417
|
+
|
418
|
+
collect_definitions!
|
419
|
+
|
420
|
+
@strategy ||= find_strategy!
|
421
|
+
@strategy.trigger_event(:request)
|
422
|
+
@strategy.public_send(with)
|
423
|
+
end
|
424
|
+
|
425
|
+
# Organize the list of definitions from the document
|
243
426
|
def collect_definitions!
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
end
|
251
|
-
end
|
427
|
+
@operations = @document[0]&.index_by { |node| node[1] }
|
428
|
+
@fragments = @document[1]&.index_by { |node| node[0] }
|
429
|
+
|
430
|
+
raise ::ArgumentError, (+<<~MSG).squish if operations.blank?
|
431
|
+
The document does not contains operations.
|
432
|
+
MSG
|
252
433
|
end
|
253
434
|
|
254
435
|
# Find the best strategy to resolve the request
|
@@ -284,38 +465,66 @@ module Rails # :nodoc:
|
|
284
465
|
end
|
285
466
|
|
286
467
|
# Log the execution of a GraphQL document
|
287
|
-
def log_execution(document)
|
288
|
-
|
289
|
-
|
468
|
+
def log_execution(document, hash = nil, event: 'request.graphql')
|
469
|
+
return yield if event.nil?
|
470
|
+
|
471
|
+
data = { document: document, hash: hash }
|
472
|
+
ActiveSupport::Notifications.instrument(event, **data) do |payload|
|
473
|
+
yield.tap { log_payload(payload) }
|
290
474
|
end
|
291
475
|
end
|
292
476
|
|
293
477
|
# Build the payload to be sent to the log
|
294
|
-
def log_payload(
|
478
|
+
def log_payload(data)
|
295
479
|
name = @operation_name.presence
|
296
480
|
name ||= operations.keys.first if operations.size.eql?(1)
|
297
|
-
map_variables = args.to_h
|
481
|
+
map_variables = args.to_h.transform_keys do |key|
|
482
|
+
@arg_names[key.to_s]
|
483
|
+
end
|
298
484
|
|
485
|
+
data.merge!(@log_extra)
|
299
486
|
data.merge!(
|
300
487
|
name: name,
|
301
488
|
cached: false,
|
302
|
-
|
303
|
-
variables: map_variables,
|
489
|
+
variables: map_variables.presence,
|
304
490
|
)
|
305
491
|
end
|
306
492
|
|
307
|
-
#
|
493
|
+
# When document is empty and the hash has been provided, then
|
494
|
+
def initialize_document(document, cache = nil)
|
495
|
+
if document.present?
|
496
|
+
::GQLParser.parse_execution(document)
|
497
|
+
elsif cache.nil?
|
498
|
+
raise ::ArgumentError, +'Unable to execute an empty document.'
|
499
|
+
elsif schema.cached?(cache)
|
500
|
+
schema.read_from_cache(cache)
|
501
|
+
else
|
502
|
+
@valid_cache = true
|
503
|
+
cache
|
504
|
+
end
|
505
|
+
rescue ::GQLParser::ParserError => err
|
506
|
+
parts = err.message.match(/\A(Parser error: .*) at \[(\d+), (\d+)\]\z/m)
|
507
|
+
errors.add(parts[1], line: parts[2].to_i, col: parts[3].to_i)
|
508
|
+
nil
|
509
|
+
end
|
510
|
+
|
511
|
+
# Initialize the class that responsible for storing the response
|
308
512
|
def initialize_response(as_format, to)
|
309
|
-
raise ::ArgumentError,
|
310
|
-
The given format #{as_format.inspect} is not a valid
|
513
|
+
raise ::ArgumentError, (+<<~MSG).squish if to.nil?
|
514
|
+
The given format #{as_format.inspect} is not a valid response format.
|
311
515
|
MSG
|
312
516
|
|
313
|
-
klass =
|
314
|
-
|
315
|
-
|
517
|
+
klass =
|
518
|
+
if schema.config.enable_string_collector && as_format == :string
|
519
|
+
Collectors::JsonCollector
|
520
|
+
elsif RESPONSE_FORMATS.key?(as_format)
|
521
|
+
Collectors::HashCollector
|
522
|
+
else
|
523
|
+
as_format
|
524
|
+
end
|
316
525
|
|
317
526
|
obj = klass.new(self)
|
318
|
-
raise ::ArgumentError,
|
527
|
+
raise ::ArgumentError, (+<<~MSG).squish unless obj.respond_to?(to)
|
319
528
|
Unable to use "#{klass.name}" as response collector since it does
|
320
529
|
not implement a #{to.inspect} method.
|
321
530
|
MSG
|
@@ -326,7 +535,7 @@ module Rails # :nodoc:
|
|
326
535
|
# Little helper to build an +OpenStruct+ ensure the given +value+ is a
|
327
536
|
# +Hash+. It can also +transform_keys+ with the given block
|
328
537
|
def build_ostruct(value, &block)
|
329
|
-
raise ::ArgumentError,
|
538
|
+
raise ::ArgumentError, (+<<~MSG).squish unless value.is_a?(::Hash)
|
330
539
|
The "#{value.class.name}" is not a valid hash.
|
331
540
|
MSG
|
332
541
|
|
@@ -337,11 +546,21 @@ module Rails # :nodoc:
|
|
337
546
|
# Make sure that a schema was assigned by find the corresponding one for
|
338
547
|
# the namespace of the request
|
339
548
|
def ensure_schema!
|
340
|
-
raise ::ArgumentError,
|
549
|
+
raise ::ArgumentError, (+<<~MSG).squish if schema.nil?
|
341
550
|
Unable to perform a request under the #{@namespace.inspect} namespace,
|
342
551
|
because there are no schema assigned to it.
|
343
552
|
MSG
|
344
553
|
end
|
554
|
+
|
555
|
+
# Check all the operations and report any provided variable that was not
|
556
|
+
# used
|
557
|
+
def report_unused_variables
|
558
|
+
(@arg_names.keys - @used_variables.to_a).each do |key|
|
559
|
+
errors.add((+<<~MSG).squish)
|
560
|
+
Variable $#{@arg_names[key]} was provided but not used.
|
561
|
+
MSG
|
562
|
+
end
|
563
|
+
end
|
345
564
|
end
|
346
565
|
end
|
347
566
|
end
|