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,21 +1,21 @@
|
|
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
|
# A set of helper methods to write a value to the response
|
7
7
|
module ValueWriters
|
8
8
|
# TODO: Maybe move this to a setting so it allow extensions
|
9
9
|
KIND_WRITERS = {
|
10
|
-
union:
|
11
|
-
interface:
|
12
|
-
object:
|
10
|
+
union: :write_union,
|
11
|
+
interface: :write_interface,
|
12
|
+
object: :write_object,
|
13
13
|
}.freeze
|
14
14
|
|
15
15
|
# Write a value to the response
|
16
16
|
def write_value(value)
|
17
17
|
return write_leaf(value) if value.nil?
|
18
|
-
send(KIND_WRITERS[field.kind] ||
|
18
|
+
send(KIND_WRITERS[field.kind] || :write_leaf, value)
|
19
19
|
end
|
20
20
|
|
21
21
|
# Resolve a given value when it is an array
|
@@ -31,7 +31,7 @@ module Rails # :nodoc:
|
|
31
31
|
response.next
|
32
32
|
|
33
33
|
format_array_execption(error, idx)
|
34
|
-
request.exception_to_error(error,
|
34
|
+
request.exception_to_error(error, self)
|
35
35
|
end
|
36
36
|
rescue StandardError => error
|
37
37
|
format_array_execption(error, idx)
|
@@ -41,13 +41,13 @@ module Rails # :nodoc:
|
|
41
41
|
|
42
42
|
# Helper to start writing as array
|
43
43
|
def write_array!(value, &block)
|
44
|
-
raise InvalidValueError,
|
44
|
+
raise InvalidValueError, (+<<~MSG).squish unless value.respond_to?(:each)
|
45
45
|
The #{gql_name} field is excepting an array
|
46
46
|
but got an "#{value.class.name}" instead.
|
47
47
|
MSG
|
48
48
|
|
49
49
|
@writing_array = true
|
50
|
-
response.with_stack(
|
50
|
+
response.with_stack(gql_name, array: true, plain: leaf_type?) do
|
51
51
|
value.each(&block)
|
52
52
|
end
|
53
53
|
ensure
|
@@ -56,9 +56,11 @@ module Rails # :nodoc:
|
|
56
56
|
|
57
57
|
# Add the item index to the exception message
|
58
58
|
def format_array_execption(error, idx)
|
59
|
-
real_error =
|
60
|
-
|
61
|
-
|
59
|
+
real_error = (+<<~MSG).squish
|
60
|
+
The #{ActiveSupport::Inflector.ordinalize(idx + 1)} value of the #{gql_name} field
|
61
|
+
MSG
|
62
|
+
|
63
|
+
source_error = +"The #{gql_name} field value"
|
62
64
|
|
63
65
|
message = error.message.gsub(source_error, real_error)
|
64
66
|
error.define_singleton_method(:message) { message }
|
@@ -68,13 +70,13 @@ module Rails # :nodoc:
|
|
68
70
|
|
69
71
|
# Write a value based on a Union type
|
70
72
|
def write_union(value)
|
71
|
-
object = type_klass.all_members
|
73
|
+
object = type_klass.all_members&.reverse_each&.find { |t| t.valid_member?(value) }
|
72
74
|
object.nil? ? raise_invalid_member! : resolve_fields(object)
|
73
75
|
end
|
74
76
|
|
75
77
|
# Write a value based on a Interface type
|
76
78
|
def write_interface(value)
|
77
|
-
object = type_klass.all_types
|
79
|
+
object = type_klass.all_types&.reverse_each&.find { |t| t.valid_member?(value) }
|
78
80
|
object.nil? ? raise_invalid_member! : resolve_fields(object)
|
79
81
|
end
|
80
82
|
|
@@ -105,7 +107,7 @@ module Rails # :nodoc:
|
|
105
107
|
# A problem when an object-based value is not a valid member of the
|
106
108
|
# +type_klass+ of this field
|
107
109
|
def raise_invalid_member!
|
108
|
-
raise
|
110
|
+
raise FieldError, (+<<~MSG).squish
|
109
111
|
The #{gql_name} field result is not a member of #{type_klass.gql_name}.
|
110
112
|
MSG
|
111
113
|
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
class Request
|
6
|
+
# = GraphQL Request Bypass Data
|
7
|
+
#
|
8
|
+
# This class works in collaboration with the prepare stage of a request
|
9
|
+
# execution. In that stage, the strategy must check if the request already
|
10
|
+
# have a prepared data for the field. The field can result in a instance
|
11
|
+
# of this class, which than bypasses the prepare stage by grabbing the
|
12
|
+
# next value from here
|
13
|
+
class PreparedData
|
14
|
+
SCHEMA_BASED = Helpers::WithSchemaFields::TYPE_FIELD_CLASS.keys.freeze
|
15
|
+
NULL = Object.new.freeze
|
16
|
+
|
17
|
+
REPEAT_OPTIONS = {
|
18
|
+
true => true,
|
19
|
+
false => 1,
|
20
|
+
cycle: true,
|
21
|
+
always: true,
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
# Look up the given +field+ using the request as a reference. It accepts
|
25
|
+
# any +Rails::GraphQL::Field+ or a string where +"query.create_user"+
|
26
|
+
# means a query field on the request schema with +create_user+ as name,
|
27
|
+
# or +create_user+ as gql_name, and "User.devices" will use the schema
|
28
|
+
# of the request to lookup the type +"User"+ and then pick the field
|
29
|
+
# +devices+
|
30
|
+
def self.lookup(request, field)
|
31
|
+
return field if field.is_a?(GraphQL::Field)
|
32
|
+
|
33
|
+
source, name = field.to_s.split('.')
|
34
|
+
return if source.nil? || name.nil?
|
35
|
+
|
36
|
+
if SCHEMA_BASED.any? { |item| item.to_s == source }
|
37
|
+
request.schema.find_field!(source.to_sym, name)
|
38
|
+
elsif (type = request.schema.find_type!(source)).is_a?(Helpers::WithFields)
|
39
|
+
type.find_field!(name)
|
40
|
+
else
|
41
|
+
field
|
42
|
+
end
|
43
|
+
rescue NotFoundError
|
44
|
+
# Return the original value, maybe it will be resolved somewhere else
|
45
|
+
field
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(field, value, repeat: 1)
|
49
|
+
# Check if it has a valid field
|
50
|
+
raise ::ArgumentError, (+<<~MSG).squish unless field.is_a?(GraphQL::Field)
|
51
|
+
Unable to setup a prepared data for "#{field.inspect}".
|
52
|
+
You must provide a valid field.
|
53
|
+
MSG
|
54
|
+
|
55
|
+
@field = field
|
56
|
+
@value = value
|
57
|
+
@array = value.is_a?(Array) && !field.array?
|
58
|
+
@repeat =
|
59
|
+
case repeat
|
60
|
+
when Numeric then repeat
|
61
|
+
when Enumerator then repeat.size
|
62
|
+
else REPEAT_OPTIONS[repeat]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Add one more item to the list of data
|
67
|
+
def push(*values)
|
68
|
+
return @value += values if @array
|
69
|
+
@value = [@value, *values]
|
70
|
+
end
|
71
|
+
|
72
|
+
# The the whole value, because the prepare stage always deal with all
|
73
|
+
# the information available
|
74
|
+
def all
|
75
|
+
@field.array? ? Array.wrap(@value) : @value
|
76
|
+
end
|
77
|
+
|
78
|
+
# Get the enumerable of the whole value
|
79
|
+
def enum
|
80
|
+
@enum ||= (@array ? @value.to_enum : @value.then)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get the next value, take into consideration the value on repeat
|
84
|
+
def next
|
85
|
+
value = enum.next
|
86
|
+
@field.array? ? Array.wrap(value) : value
|
87
|
+
rescue StopIteration
|
88
|
+
if @repeat == true || (@repeat != false && (@repeat -= 1) > 0)
|
89
|
+
enum.rewind
|
90
|
+
self.next
|
91
|
+
else
|
92
|
+
NULL
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -1,27 +1,34 @@
|
|
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
|
# Helper methods for the authorize step of a request
|
7
7
|
module Authorizable
|
8
8
|
# Event used to perform an authorization step
|
9
9
|
class Event < GraphQL::Event
|
10
|
+
# Same behavior as the request event
|
11
|
+
def same_source?(other)
|
12
|
+
super || (source.try(:kind) == :field && source.field == other)
|
13
|
+
end
|
14
|
+
|
10
15
|
# Similar to trigger for object, but with an extra extension for
|
11
16
|
# instance methods defined on the given object
|
12
17
|
def authorize_using(object, send_args, events = nil)
|
13
|
-
|
18
|
+
@object = object
|
19
|
+
|
20
|
+
cache = data[:request].nested_cache(:authorize, object) { [] }
|
14
21
|
return false if cache.present? && cache.none?
|
15
22
|
args, xargs = send_args
|
16
23
|
|
17
24
|
# Authorize through instance method
|
18
25
|
using_object = cache[0] ||= authorize_on_object(object)
|
19
26
|
set_on(using_object) do |instance|
|
20
|
-
instance.public_send(
|
27
|
+
instance.public_send(:authorize!, *args, **xargs)
|
21
28
|
end if using_object
|
22
29
|
|
23
30
|
# Authorize through events
|
24
|
-
using_events = cache[1] ||= (events || object.all_events[
|
31
|
+
using_events = cache[1] ||= (events || object.all_events.try(:[], :authorize))
|
25
32
|
using_events&.each { |block| block.call(self, *args, **xargs) }
|
26
33
|
|
27
34
|
# Does any authorize process ran
|
@@ -29,14 +36,14 @@ module Rails # :nodoc:
|
|
29
36
|
end
|
30
37
|
|
31
38
|
# Simply unauthorize the operation
|
32
|
-
def
|
33
|
-
raise UnauthorizedFieldError, message ||
|
39
|
+
def unauthorized!(*, message: nil, **)
|
40
|
+
raise UnauthorizedFieldError, message || (+<<~MSG).squish
|
34
41
|
Unauthorized access to "#{field.gql_name}" field.
|
35
42
|
MSG
|
36
43
|
end
|
37
44
|
|
38
45
|
# Simply authorize the operation
|
39
|
-
def
|
46
|
+
def authorized!(*)
|
40
47
|
throw :authorized
|
41
48
|
end
|
42
49
|
|
@@ -54,16 +61,18 @@ module Rails # :nodoc:
|
|
54
61
|
end
|
55
62
|
|
56
63
|
# Check if the field is correctly authorized to be executed
|
64
|
+
# TODO: Implement reverse order of authorization
|
57
65
|
def check_authorization!
|
58
66
|
return unless field.authorizable?
|
59
67
|
*args, block = field.authorizer
|
60
68
|
|
61
69
|
catch(:authorized) do
|
62
70
|
event = authorization_event
|
63
|
-
schema_events = request.all_events[:authorize
|
71
|
+
schema_events = request.all_events.try(:[], :authorize)
|
64
72
|
executed = event.authorize_using(schema, args, schema_events)
|
73
|
+
executed = event.authorize_using(self, args) || executed
|
65
74
|
|
66
|
-
element = field
|
75
|
+
element = field&.owner
|
67
76
|
while element && element != schema
|
68
77
|
executed = event.authorize_using(element, args) || executed
|
69
78
|
element = element.try(:owner)
|
@@ -74,14 +83,14 @@ module Rails # :nodoc:
|
|
74
83
|
executed = true
|
75
84
|
end
|
76
85
|
|
77
|
-
event.
|
86
|
+
event.unauthorized!(message: (+<<~MSG).squish) unless executed
|
78
87
|
Authorization required but unable to be executed
|
79
88
|
MSG
|
80
89
|
end
|
81
90
|
rescue UnauthorizedFieldError => error
|
82
91
|
request.rescue_with_handler(error)
|
83
|
-
request.exception_to_error(error,
|
84
|
-
invalidate!
|
92
|
+
request.exception_to_error(error, self, stage: :authorization)
|
93
|
+
invalidate!(:authorization)
|
85
94
|
end
|
86
95
|
|
87
96
|
private
|
@@ -89,6 +98,7 @@ module Rails # :nodoc:
|
|
89
98
|
# Build and store the authorization event
|
90
99
|
def authorization_event
|
91
100
|
Event.new(:authorize, self,
|
101
|
+
context: request.context,
|
92
102
|
request: request,
|
93
103
|
schema: schema,
|
94
104
|
field: field,
|
@@ -1,18 +1,56 @@
|
|
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
|
# Helper methods for the organize step of a request
|
7
7
|
module Organizable
|
8
|
-
# Check if it is already organized
|
9
|
-
def organized?
|
10
|
-
data.nil?
|
11
|
-
end
|
12
|
-
|
13
8
|
# Organize the object if it is not already organized
|
14
9
|
def organize!
|
15
|
-
|
10
|
+
organize unless defined?(@organized)
|
11
|
+
rescue => error
|
12
|
+
invalidate!
|
13
|
+
report_exception(error)
|
14
|
+
ensure
|
15
|
+
@organized = true
|
16
|
+
end
|
17
|
+
|
18
|
+
# Build the cache object
|
19
|
+
def cache_dump
|
20
|
+
arguments =
|
21
|
+
if @arguments === EMPTY_HASH
|
22
|
+
nil
|
23
|
+
elsif is_a?(Component::Operation)
|
24
|
+
all_to_gid(@arguments.transform_values)
|
25
|
+
else
|
26
|
+
@arguments.to_h
|
27
|
+
end
|
28
|
+
|
29
|
+
{ arguments: arguments }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Organize from cache data
|
33
|
+
def cache_load(data)
|
34
|
+
if (args = data[:arguments]).blank?
|
35
|
+
@arguments = EMPTY_HASH
|
36
|
+
elsif is_a?(Component::Operation)
|
37
|
+
@arguments = all_from_gid(args.transform_values)
|
38
|
+
|
39
|
+
unless defined?(@variables)
|
40
|
+
@variables = collect_arguments(self, request.args, var_access: false).freeze
|
41
|
+
end
|
42
|
+
else
|
43
|
+
@arguments = request.build(Request::Arguments, args).freeze
|
44
|
+
end
|
45
|
+
|
46
|
+
# Always trigger the organized event
|
47
|
+
unless unresolvable?
|
48
|
+
strategy.add_listeners_from(self)
|
49
|
+
trigger_event(:organized)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Mark component as organized
|
53
|
+
@organized = true
|
16
54
|
end
|
17
55
|
|
18
56
|
protected
|
@@ -31,53 +69,67 @@ module Rails # :nodoc:
|
|
31
69
|
def organize_then(after_block, &block)
|
32
70
|
stacked do
|
33
71
|
block.call
|
34
|
-
strategy.
|
72
|
+
strategy.add_listeners_from(self)
|
35
73
|
trigger_event(:organized)
|
36
74
|
after_block.call if after_block.present?
|
37
75
|
end
|
38
|
-
ensure
|
39
|
-
@data = nil
|
40
76
|
end
|
41
77
|
|
42
78
|
# Helper parser for request arguments (operation variables) that
|
43
79
|
# collect necessary arguments from the request
|
44
|
-
|
80
|
+
# Default values forces this method to run even without nodes
|
81
|
+
def parse_variables(nodes)
|
82
|
+
if nodes.blank?
|
83
|
+
@variables = EMPTY_HASH
|
84
|
+
@arguments = EMPTY_HASH
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
45
88
|
@arguments = {}
|
46
89
|
|
47
|
-
|
48
|
-
arg_name =
|
49
|
-
raise ExecutionError,
|
90
|
+
nodes&.each do |node|
|
91
|
+
arg_name, type, value, _directives = node
|
92
|
+
raise ExecutionError, (+<<~MSG).squish if arguments.key?(arg_name)
|
50
93
|
The "#{arg_name}" argument is already defined for this #{kind}.
|
51
94
|
MSG
|
52
95
|
|
53
|
-
|
54
|
-
|
96
|
+
# TODO: Move this to a better builder of the type
|
97
|
+
type_name, dimensions, nullability = type
|
98
|
+
xargs = { owner: schema, default: value, array: dimensions > 0 }
|
99
|
+
xargs[:nullable] = (nullability & 0b10) == 0
|
100
|
+
xargs[:null] = (nullability & 0b01) == 0
|
101
|
+
|
102
|
+
# TODO: Follow up with the support for directives
|
103
|
+
item = arguments[arg_name.to_s] = Argument.new(arg_name, type_name, **xargs)
|
55
104
|
item.node = node
|
56
105
|
item.validate!
|
57
|
-
end unless data[:variables].blank?
|
58
|
-
|
59
|
-
args = request.sanitized_arguments
|
60
|
-
args = collect_arguments(self, args, var_access: false) do |errors|
|
61
|
-
"Invalid arguments for #{log_source}: #{errors}."
|
62
106
|
end
|
63
107
|
|
108
|
+
args = collect_arguments(self, request.args, var_access: false)
|
109
|
+
|
64
110
|
@variables = args.freeze
|
65
111
|
@arguments.freeze
|
112
|
+
rescue ArgumentsError => error
|
113
|
+
raise ArgumentsError, (+<<~MSG).squish
|
114
|
+
Invalid arguments for #{log_source}: #{error.message}.
|
115
|
+
MSG
|
66
116
|
end
|
67
117
|
|
68
118
|
# Helper parser for arguments that also collect necessary variables
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
args = collect_arguments(self, args) do |errors|
|
77
|
-
"Invalid arguments for #{gql_name} #{kind}: #{errors}."
|
119
|
+
# Default values forces this method to run even without nodes
|
120
|
+
def parse_arguments(nodes)
|
121
|
+
return @arguments = EMPTY_HASH if nodes.blank?
|
122
|
+
|
123
|
+
args = nodes.each.with_object({}) do |(name, value, var_name), hash|
|
124
|
+
hash[name.to_s] = var_name.nil? ? value : var_name
|
78
125
|
end
|
79
126
|
|
127
|
+
args = collect_arguments(self, args)
|
80
128
|
@arguments = request.build(Request::Arguments, args).freeze
|
129
|
+
rescue ArgumentsError => error
|
130
|
+
raise ArgumentsError, (+<<~MSG).squish
|
131
|
+
Invalid arguments for #{gql_name} #{kind}: #{error.message}.
|
132
|
+
MSG
|
81
133
|
end
|
82
134
|
|
83
135
|
# Build a hash that collect validated values for a set of arguments.
|
@@ -89,41 +141,51 @@ module Rails # :nodoc:
|
|
89
141
|
|
90
142
|
errors = []
|
91
143
|
source = source.all_arguments if source.respond_to?(:all_arguments)
|
144
|
+
return unless source.present?
|
145
|
+
|
92
146
|
result = source.each_pair.each_with_object({}) do |(key, argument), hash|
|
93
|
-
|
94
|
-
value = values[key]
|
147
|
+
value = values && values[key]
|
95
148
|
|
96
|
-
#
|
97
|
-
if value.is_a?(::
|
98
|
-
var_name =
|
99
|
-
raise ArgumentError,
|
149
|
+
# A token means the name of a variable
|
150
|
+
if value.is_a?(::GQLParser::Token) && value.of_type?(:variable)
|
151
|
+
var_name = value.to_s
|
152
|
+
raise ArgumentError, (+<<~MSG).squish unless var_access
|
100
153
|
Unable to use variable "$#{var_name}" in the current scope
|
101
154
|
MSG
|
102
155
|
|
103
|
-
op_vars ||= operation.all_arguments
|
104
|
-
raise ArgumentError,
|
156
|
+
op_var = (op_vars ||= operation.all_arguments).try(:[], var_name)
|
157
|
+
raise ArgumentError, (+<<~MSG).squish unless op_var.present?
|
105
158
|
The #{operation.log_source} does not define the $#{var_name} variable
|
106
159
|
MSG
|
107
160
|
|
108
161
|
# When arguments are not equivalent, they can ended up with
|
109
162
|
# invalid values, so this already ensures that whatever the
|
110
163
|
# variable value ended up being, it will be valid due to this
|
111
|
-
raise ArgumentError,
|
164
|
+
raise ArgumentError, (+<<~MSG).squish unless op_var =~ argument
|
112
165
|
The $#{var_name} variable on #{operation.log_source} is not compatible
|
113
166
|
with "#{key}" argument
|
114
167
|
MSG
|
115
168
|
|
169
|
+
# Mark the variable as used and grab the value
|
116
170
|
operation.used_variables << var_name
|
117
|
-
next unless variables.key?(op_var.name)
|
118
171
|
value = variables[op_var.name]
|
119
|
-
|
172
|
+
elsif !value.nil?
|
120
173
|
# Only when the given value is an actual value that we check if
|
121
174
|
# it is valid
|
122
|
-
raise ArgumentError,
|
123
|
-
Invalid value
|
175
|
+
raise ArgumentError, (+<<~MSG).squish unless argument.valid?(value)
|
176
|
+
Invalid value "#{value.to_s}" provided to
|
177
|
+
#{argument.node ? "$#{argument.name} variable" : "#{key} argument"}
|
178
|
+
on #{argument.node ? operation.log_source : gql_name}
|
124
179
|
MSG
|
125
180
|
|
126
181
|
value = argument.deserialize(value)
|
182
|
+
elsif argument.default_value?
|
183
|
+
# Ensure to always import arguments that have default values but
|
184
|
+
# were not included in the field
|
185
|
+
value = argument.deserialize
|
186
|
+
else
|
187
|
+
# Otherwise, simply just skip the argument
|
188
|
+
next
|
127
189
|
end
|
128
190
|
|
129
191
|
hash[argument.name] = value
|
@@ -131,14 +193,14 @@ module Rails # :nodoc:
|
|
131
193
|
errors << error.message
|
132
194
|
end
|
133
195
|
|
134
|
-
# Checks for any required
|
196
|
+
# Checks for any required argument that was not provided
|
135
197
|
source.each_value do |argument|
|
136
198
|
next if result.key?(argument.name) || argument.null?
|
137
|
-
errors << "
|
199
|
+
errors << +"the \"#{argument.gql_name}\" argument can not be null"
|
138
200
|
end
|
139
201
|
|
140
202
|
return result if errors.blank?
|
141
|
-
raise
|
203
|
+
raise ArgumentsError, errors.to_sentence
|
142
204
|
end
|
143
205
|
end
|
144
206
|
end
|
@@ -1,26 +1,39 @@
|
|
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
|
# Helper methods for the prepare step of a request
|
7
|
-
module
|
7
|
+
module Preparable
|
8
8
|
# Prepare the object
|
9
9
|
def prepare!
|
10
|
-
|
10
|
+
prepare
|
11
|
+
rescue => error
|
12
|
+
report_exception(error)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Marks that the field has prepared data that came outside from the
|
16
|
+
# request
|
17
|
+
def prepared_data!
|
18
|
+
@prepared_data = true
|
19
|
+
end
|
20
|
+
|
21
|
+
# Checks if the field has prepared data
|
22
|
+
def prepared_data?
|
23
|
+
defined?(@prepared_data) && @prepared_data
|
11
24
|
end
|
12
25
|
|
13
26
|
protected
|
14
27
|
|
15
28
|
# Normal mode of the prepare step
|
16
29
|
def prepare
|
17
|
-
return if invalid?
|
18
30
|
prepare_then { prepare_fields }
|
19
31
|
end
|
20
32
|
|
21
33
|
# The actual process that prepare the object
|
22
34
|
def prepare_then(after_block = nil, &block)
|
23
|
-
return if
|
35
|
+
return if unresolvable?
|
36
|
+
|
24
37
|
stacked do
|
25
38
|
block.call if block.present?
|
26
39
|
trigger_event(:prepared)
|
@@ -1,25 +1,34 @@
|
|
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
|
# Helper methods for the resolve step of a request
|
7
|
-
module
|
7
|
+
module Resolvable
|
8
8
|
# Resolve the object
|
9
9
|
def resolve!
|
10
|
-
|
10
|
+
resolve
|
11
|
+
rescue => error
|
12
|
+
report_exception(error)
|
11
13
|
end
|
12
14
|
|
13
15
|
protected
|
14
16
|
|
15
17
|
# Normal mode of the resolve step
|
18
|
+
# TODO: Field a way to cache the resolved result, mostly for
|
19
|
+
# performance improvement in recursion
|
16
20
|
def resolve
|
21
|
+
return if skipped?
|
17
22
|
invalid? ? try(:resolve_invalid) : resolve_then
|
23
|
+
rescue
|
24
|
+
try(:resolve_invalid)
|
25
|
+
raise
|
18
26
|
end
|
19
27
|
|
20
28
|
# The actual process that resolve the object
|
21
29
|
def resolve_then(after_block = nil, &block)
|
22
|
-
return if
|
30
|
+
return if unresolvable?
|
31
|
+
|
23
32
|
stacked do
|
24
33
|
block.call if block.present?
|
25
34
|
trigger_event(:finalize)
|