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,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
|