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
| @@ -0,0 +1,64 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Rails
         | 
| 4 | 
            +
              module GraphQL
         | 
| 5 | 
            +
                class Request
         | 
| 6 | 
            +
                  # = GraphQl Cached Strategy
         | 
| 7 | 
            +
                  #
         | 
| 8 | 
            +
                  # This strategy will process hard cached operations. Soft cached
         | 
| 9 | 
            +
                  # operations are those that only the document is cached in the server and
         | 
| 10 | 
            +
                  # processed via its unique identifier (UUID). Whereas, hard cached
         | 
| 11 | 
            +
                  # operations pretty muchy skips the organize step since that is what is
         | 
| 12 | 
            +
                  # cached.
         | 
| 13 | 
            +
                  #
         | 
| 14 | 
            +
                  # Beware, if the version in the cache is different from the version in the
         | 
| 15 | 
            +
                  # type map, it won't be able to process it.
         | 
| 16 | 
            +
                  class Strategy::CachedStrategy < Strategy
         | 
| 17 | 
            +
                    self.priority = 100
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    class << self
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                      # Resolve whenever it has a cache directive on any of the operations
         | 
| 22 | 
            +
                      def can_resolve?(request)
         | 
| 23 | 
            +
                        false
         | 
| 24 | 
            +
                        # request.operations.each_value.any? do |op|
         | 
| 25 | 
            +
                        #   op.data&.directives&.any? { |dir| directive_name(dir) == 'cached' }
         | 
| 26 | 
            +
                        # end
         | 
| 27 | 
            +
                      end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                      private
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                        def directive_name(obj)
         | 
| 32 | 
            +
                          Native.node_name(Native.directive_name(obj))
         | 
| 33 | 
            +
                        end
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    # Executes the strategy in the normal mode
         | 
| 37 | 
            +
                    def resolve!
         | 
| 38 | 
            +
                      response.with_stack('data') do
         | 
| 39 | 
            +
                        for_each_operation { |op| collect_listeners { op.organize! } }
         | 
| 40 | 
            +
                        for_each_operation { |op| collect_data      { op.prepare!  } }
         | 
| 41 | 
            +
                        for_each_operation { |op| collect_response  { op.resolve!  } }
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                        # collect_data(true) { op.prepare! }
         | 
| 44 | 
            +
                        # collect_response   { op.resolve! }
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                        # operations.each_value do |op|
         | 
| 47 | 
            +
                        #   collect_listeners  { op.organize! }
         | 
| 48 | 
            +
                        #   collect_data(true) { op.prepare! }
         | 
| 49 | 
            +
                        #   collect_response   { op.resolve! }
         | 
| 50 | 
            +
                        # end
         | 
| 51 | 
            +
                      end
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    private
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                      # Execute a given block for each defined operation
         | 
| 57 | 
            +
                      def for_each_operation(&block)
         | 
| 58 | 
            +
                        operations.each_value(&block)
         | 
| 59 | 
            +
                      end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
            end
         | 
| @@ -1,8 +1,8 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            module Rails | 
| 4 | 
            -
              module GraphQL | 
| 5 | 
            -
                class Request | 
| 3 | 
            +
            module Rails
         | 
| 4 | 
            +
              module GraphQL
         | 
| 5 | 
            +
                class Request
         | 
| 6 6 | 
             
                  # = GraphQl Strategy Dynamic Instance
         | 
| 7 7 | 
             
                  #
         | 
| 8 8 | 
             
                  # When an event is call on non-object types, this class allows both
         | 
| @@ -16,18 +16,18 @@ module Rails # :nodoc: | |
| 16 16 |  | 
| 17 17 | 
             
                    private
         | 
| 18 18 |  | 
| 19 | 
            -
                      def respond_to_missing?(method_name, include_private = false) | 
| 19 | 
            +
                      def respond_to_missing?(method_name, include_private = false)
         | 
| 20 20 | 
             
                        __current_object__&.respond_to?(method_name, include_private) || super
         | 
| 21 21 | 
             
                      end
         | 
| 22 22 |  | 
| 23 | 
            -
                      def method_missing(method_name, *args, **xargs, &block) | 
| 23 | 
            +
                      def method_missing(method_name, *args, **xargs, &block)
         | 
| 24 24 | 
             
                        object = __current_object__
         | 
| 25 25 |  | 
| 26 26 | 
             
                        return super unless object&.respond_to?(method_name)
         | 
| 27 27 | 
             
                        object.public_send(method_name, *args, **xargs, &block)
         | 
| 28 28 | 
             
                      end
         | 
| 29 29 |  | 
| 30 | 
            -
                      def __current_object__ | 
| 30 | 
            +
                      def __current_object__
         | 
| 31 31 | 
             
                        return unless __getobj__.instance_variable_defined?(:@event)
         | 
| 32 32 |  | 
| 33 33 | 
             
                        event = __getobj__.instance_variable_get(:@event)
         | 
| @@ -1,8 +1,8 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            module Rails | 
| 4 | 
            -
              module GraphQL | 
| 5 | 
            -
                class Request | 
| 3 | 
            +
            module Rails
         | 
| 4 | 
            +
              module GraphQL
         | 
| 5 | 
            +
                class Request
         | 
| 6 6 | 
             
                  # = GraphQl Multi Query Strategy
         | 
| 7 7 | 
             
                  #
         | 
| 8 8 | 
             
                  # This is a resolution strategy to solve requests that only contain
         | 
| @@ -11,25 +11,18 @@ module Rails # :nodoc: | |
| 11 11 | 
             
                  class Strategy::MultiQueryStrategy < Strategy
         | 
| 12 12 | 
             
                    self.priority = 10
         | 
| 13 13 |  | 
| 14 | 
            -
                    def self.can_resolve?(request) | 
| 15 | 
            -
                      request.operations. | 
| 14 | 
            +
                    def self.can_resolve?(request)
         | 
| 15 | 
            +
                      request.operations.each_value.all? { |op| op.of_type?(:query) }
         | 
| 16 16 | 
             
                    end
         | 
| 17 17 |  | 
| 18 18 | 
             
                    # Executes the strategy in the normal mode
         | 
| 19 19 | 
             
                    def resolve!
         | 
| 20 | 
            -
                      response.with_stack( | 
| 20 | 
            +
                      response.with_stack('data') do
         | 
| 21 21 | 
             
                        for_each_operation { |op| collect_listeners { op.organize! } }
         | 
| 22 22 | 
             
                        for_each_operation { |op| collect_data      { op.prepare!  } }
         | 
| 23 23 | 
             
                        for_each_operation { |op| collect_response  { op.resolve!  } }
         | 
| 24 24 | 
             
                      end
         | 
| 25 25 | 
             
                    end
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                    private
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                      # Execute a given block for each defined operation
         | 
| 30 | 
            -
                      def for_each_operation
         | 
| 31 | 
            -
                        operations.each_value { |op| yield op }
         | 
| 32 | 
            -
                      end
         | 
| 33 26 | 
             
                  end
         | 
| 34 27 | 
             
                end
         | 
| 35 28 | 
             
              end
         | 
| @@ -1,24 +1,24 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            module Rails | 
| 4 | 
            -
              module GraphQL | 
| 5 | 
            -
                class Request | 
| 3 | 
            +
            module Rails
         | 
| 4 | 
            +
              module GraphQL
         | 
| 5 | 
            +
                class Request
         | 
| 6 6 | 
             
                  # = GraphQl Sequenced Strategy
         | 
| 7 7 | 
             
                  #
         | 
| 8 8 | 
             
                  # This is the default resolution strategy, where each operation is
         | 
| 9 9 | 
             
                  # performed in sequece, and they don't relate to each other in any way.
         | 
| 10 10 | 
             
                  class Strategy::SequencedStrategy < Strategy
         | 
| 11 | 
            -
                    def self.can_resolve?(_) | 
| 11 | 
            +
                    def self.can_resolve?(_)
         | 
| 12 12 | 
             
                      true
         | 
| 13 13 | 
             
                    end
         | 
| 14 14 |  | 
| 15 15 | 
             
                    # Executes the strategy in the normal mode
         | 
| 16 16 | 
             
                    def resolve!
         | 
| 17 | 
            -
                      response.with_stack( | 
| 18 | 
            -
                         | 
| 19 | 
            -
                          collect_listeners | 
| 20 | 
            -
                          collect_data | 
| 21 | 
            -
                          collect_response | 
| 17 | 
            +
                      response.with_stack('data') do
         | 
| 18 | 
            +
                        for_each_operation do |op|
         | 
| 19 | 
            +
                          collect_listeners  { op.organize! }
         | 
| 20 | 
            +
                          collect_data(true) { op.prepare! }
         | 
| 21 | 
            +
                          collect_response   { op.resolve! }
         | 
| 22 22 | 
             
                        end
         | 
| 23 23 | 
             
                      end
         | 
| 24 24 | 
             
                    end
         | 
| @@ -1,8 +1,8 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            module Rails | 
| 4 | 
            -
              module GraphQL | 
| 5 | 
            -
                class Request | 
| 3 | 
            +
            module Rails
         | 
| 4 | 
            +
              module GraphQL
         | 
| 5 | 
            +
                class Request
         | 
| 6 6 | 
             
                  # = GraphQL Request Strategy
         | 
| 7 7 | 
             
                  #
         | 
| 8 8 | 
             
                  # This is the base class for the strategies of resolving a request.
         | 
| @@ -11,10 +11,9 @@ module Rails # :nodoc: | |
| 11 11 |  | 
| 12 12 | 
             
                    autoload :DynamicInstance
         | 
| 13 13 |  | 
| 14 | 
            -
                     | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
                    end
         | 
| 14 | 
            +
                    autoload :SequencedStrategy
         | 
| 15 | 
            +
                    autoload :MultiQueryStrategy
         | 
| 16 | 
            +
                    autoload :CachedStrategy
         | 
| 18 17 |  | 
| 19 18 | 
             
                    # Configurations for the prepare step
         | 
| 20 19 | 
             
                    PREPARE_XARGS = { object?: true, reverse?: true }.freeze
         | 
| @@ -24,7 +23,7 @@ module Rails # :nodoc: | |
| 24 23 |  | 
| 25 24 | 
             
                    delegate :operations, :errors, :response, :schema, to: :request
         | 
| 26 25 |  | 
| 27 | 
            -
                    attr_reader :listeners, :request, :context
         | 
| 26 | 
            +
                    attr_reader :listeners, :request, :context, :stage
         | 
| 28 27 |  | 
| 29 28 | 
             
                    class << self
         | 
| 30 29 | 
             
                      # Check if the strategy can resolve the given +request+. By default,
         | 
| @@ -38,7 +37,15 @@ module Rails # :nodoc: | |
| 38 37 | 
             
                    def initialize(request)
         | 
| 39 38 | 
             
                      @request = request
         | 
| 40 39 | 
             
                      @objects_pool = {}
         | 
| 41 | 
            -
                       | 
| 40 | 
            +
                      @listeners = Hash.new { |h, k| h[k] = Set.new }
         | 
| 41 | 
            +
                      add_listeners_from(request)
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    # Clear all strategy information
         | 
| 45 | 
            +
                    def clear
         | 
| 46 | 
            +
                      @listeners.clear
         | 
| 47 | 
            +
                      @objects_pool.clear
         | 
| 48 | 
            +
                      @stage = @context = @objects_pool = @data_pool = @listeners = nil
         | 
| 42 49 | 
             
                    end
         | 
| 43 50 |  | 
| 44 51 | 
             
                    # Executes the strategy in the normal mode
         | 
| @@ -48,17 +55,12 @@ module Rails # :nodoc: | |
| 48 55 |  | 
| 49 56 | 
             
                    # Find a given +type+ and store it on request cache
         | 
| 50 57 | 
             
                    def find_type!(type)
         | 
| 51 | 
            -
                      request. | 
| 58 | 
            +
                      request.nested_cache(:types, type) { schema.find_type!(type) }
         | 
| 52 59 | 
             
                    end
         | 
| 53 60 |  | 
| 54 61 | 
             
                    # Find a given +directive+ and store it on request cache
         | 
| 55 62 | 
             
                    def find_directive!(directive)
         | 
| 56 | 
            -
                      request. | 
| 57 | 
            -
                    end
         | 
| 58 | 
            -
             | 
| 59 | 
            -
                    # Check if it's enabled to collect listeners
         | 
| 60 | 
            -
                    def add_listeners?
         | 
| 61 | 
            -
                      !listeners.frozen?
         | 
| 63 | 
            +
                      request.nested_cache(:directives, directive) { schema.find_directive!(directive) }
         | 
| 62 64 | 
             
                    end
         | 
| 63 65 |  | 
| 64 66 | 
             
                    # Check if any listener were actually added
         | 
| @@ -79,8 +81,8 @@ module Rails # :nodoc: | |
| 79 81 |  | 
| 80 82 | 
             
                    # When a +field+ has a perform step, run it under the context of the
         | 
| 81 83 | 
             
                    # prepared value from the data pool
         | 
| 82 | 
            -
                    def perform(field)
         | 
| 83 | 
            -
                      context.stacked(@data_pool[field]) do
         | 
| 84 | 
            +
                    def perform(field, data = nil)
         | 
| 85 | 
            +
                      context.stacked(data || @data_pool[field]) do
         | 
| 84 86 | 
             
                        safe_store_data(field) do
         | 
| 85 87 | 
             
                          Event.trigger(:perform, field, self, &field.performer)
         | 
| 86 88 | 
             
                        end
         | 
| @@ -91,27 +93,26 @@ module Rails # :nodoc: | |
| 91 93 | 
             
                    # block using context stack
         | 
| 92 94 | 
             
                    def prepare(field, &block)
         | 
| 93 95 | 
             
                      value = safe_store_data(field) do
         | 
| 94 | 
            -
                         | 
| 96 | 
            +
                        prepared = request.prepared_data_for(field)
         | 
| 97 | 
            +
                        if prepared.is_a?(PreparedData)
         | 
| 98 | 
            +
                          field.prepared_data!
         | 
| 99 | 
            +
                          prepared.all
         | 
| 100 | 
            +
                        else
         | 
| 101 | 
            +
                          Event.trigger(:prepare, field, self, **PREPARE_XARGS)
         | 
| 102 | 
            +
                        end
         | 
| 95 103 | 
             
                      end
         | 
| 96 104 |  | 
| 97 | 
            -
                       | 
| 105 | 
            +
                      perform(field, value) if field.mutation?
         | 
| 98 106 |  | 
| 99 | 
            -
                       | 
| 100 | 
            -
                       | 
| 101 | 
            -
                        It is expected to get a result from prepare events
         | 
| 102 | 
            -
                      MSG
         | 
| 107 | 
            +
                      value = @data_pool[field]
         | 
| 108 | 
            +
                      context.stacked(value, &block) unless value.nil?
         | 
| 103 109 | 
             
                    end
         | 
| 104 110 |  | 
| 105 111 | 
             
                    # Resolve a value for a given object, It uses the +args+ to prevent
         | 
| 106 112 | 
             
                    # problems with nil values.
         | 
| 107 113 | 
             
                    def resolve(field, *args, array: false, decorate: false, &block)
         | 
| 108 | 
            -
                       | 
| 109 | 
            -
                        prepared = data_for(args, field)&.last
         | 
| 110 | 
            -
                        args << Event.trigger(:resolve, field, self, prepared: prepared,
         | 
| 111 | 
            -
                          &field.resolver) if field.try(:dynamic_resolver?)
         | 
| 112 | 
            -
                      end if args.size.zero?
         | 
| 114 | 
            +
                      resolve_data_for(field, args)
         | 
| 113 115 |  | 
| 114 | 
            -
                      # Now we have a value to set on the context
         | 
| 115 116 | 
             
                      value = args.last
         | 
| 116 117 | 
             
                      value = field.decorate(value) if decorate
         | 
| 117 118 | 
             
                      context.stacked(value) do |current|
         | 
| @@ -124,12 +125,18 @@ module Rails # :nodoc: | |
| 124 125 | 
             
                      end
         | 
| 125 126 | 
             
                    end
         | 
| 126 127 |  | 
| 127 | 
            -
                    #  | 
| 128 | 
            -
                     | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 128 | 
            +
                    # Get the resolved data for a given field
         | 
| 129 | 
            +
                    def resolve_data_for(field, args)
         | 
| 130 | 
            +
                      return unless args.size.zero?
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                      if field.try(:dynamic_resolver?)
         | 
| 133 | 
            +
                        prepared = prepared_data_for(field)
         | 
| 134 | 
            +
                        args << Event.trigger(:resolve, field, self, prepared: prepared, &field.resolver)
         | 
| 135 | 
            +
                      elsif field.prepared_data?
         | 
| 136 | 
            +
                        args << prepared_data_for(field)
         | 
| 137 | 
            +
                      else
         | 
| 138 | 
            +
                        data_for(args, field)
         | 
| 139 | 
            +
                      end
         | 
| 133 140 | 
             
                    end
         | 
| 134 141 |  | 
| 135 142 | 
             
                    # Check if the given class is in the pool, or add a new instance to the
         | 
| @@ -154,20 +161,35 @@ module Rails # :nodoc: | |
| 154 161 | 
             
                    def trigger_event(event_name, **xargs)
         | 
| 155 162 | 
             
                      return unless listening_to?(event_name)
         | 
| 156 163 |  | 
| 157 | 
            -
                      objects  | 
| 158 | 
            -
                       | 
| 164 | 
            +
                      # A simpler attempt to remove select less objects (or even none) by
         | 
| 165 | 
            +
                      # assuming that the first item will work as exclusive and
         | 
| 166 | 
            +
                      # non-exclusive, and the others, only non-exclusive or anything
         | 
| 167 | 
            +
                      # different than a +Callback+
         | 
| 168 | 
            +
                      event_name = event_name.to_sym
         | 
| 169 | 
            +
                      list = listeners[event_name]
         | 
| 170 | 
            +
                      objects = request.stack.select.with_index do |obj, idx|
         | 
| 171 | 
            +
                        next unless list.include?(obj)
         | 
| 172 | 
            +
                        next true if idx == 0
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                        obj.all_events.try(:[], event_name)&.any? do |ev|
         | 
| 175 | 
            +
                          !(ev.is_a?(Callback) && ev.exclusive?)
         | 
| 176 | 
            +
                        end
         | 
| 177 | 
            +
                      end
         | 
| 159 178 |  | 
| 160 | 
            -
                       | 
| 179 | 
            +
                      # Now trigger with more for all the selected objects
         | 
| 180 | 
            +
                      Event.trigger(event_name, objects, self, **xargs) if objects.present?
         | 
| 161 181 | 
             
                    end
         | 
| 162 182 |  | 
| 163 183 | 
             
                    # Check what kind of event listeners the object have, in order to speed
         | 
| 164 | 
            -
                    # up processing by avoiding  | 
| 165 | 
            -
                    def  | 
| 166 | 
            -
                       | 
| 167 | 
            -
             | 
| 168 | 
            -
                      object.all_listeners.each do |event_name|
         | 
| 184 | 
            +
                    # up processing by avoiding unnecessary event instances
         | 
| 185 | 
            +
                    def add_listeners_from(object)
         | 
| 186 | 
            +
                      object.all_listeners&.each do |event_name|
         | 
| 169 187 | 
             
                        listeners[event_name] << object
         | 
| 170 188 | 
             
                      end
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                      if request.prepared_data_for?(object)
         | 
| 191 | 
            +
                        listeners[:prepare] << object
         | 
| 192 | 
            +
                      end
         | 
| 171 193 | 
             
                    end
         | 
| 172 194 |  | 
| 173 195 | 
             
                    # Store a given resolve +value+ for a given +field+
         | 
| @@ -177,48 +199,93 @@ module Rails # :nodoc: | |
| 177 199 |  | 
| 178 200 | 
             
                    # Only store a given +value+ for a given +field+ if it is not set yet
         | 
| 179 201 | 
             
                    def safe_store_data(field, value = nil)
         | 
| 180 | 
            -
                       | 
| 181 | 
            -
             | 
| 182 | 
            -
             | 
| 202 | 
            +
                      value ||= yield if block_given?
         | 
| 203 | 
            +
                      @data_pool[field] ||= value unless value.nil?
         | 
| 204 | 
            +
                    end
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                    # Get the prepared data for the given +field+, getting ready for
         | 
| 207 | 
            +
                    # resolve, while ensuring to check prepared data on request
         | 
| 208 | 
            +
                    def prepared_data_for(field)
         | 
| 209 | 
            +
                      return @data_pool[field] unless field.prepared_data?
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                      prepared = request.prepared_data_for(field).next
         | 
| 212 | 
            +
                      prepared unless prepared === PreparedData::NULL
         | 
| 213 | 
            +
                    end
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                    # Simply run the organize step for compilation
         | 
| 216 | 
            +
                    def compile
         | 
| 217 | 
            +
                      for_each_operation { |op| collect_listeners { op.organize! } }
         | 
| 218 | 
            +
                    end
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                    # Build the cache object
         | 
| 221 | 
            +
                    def cache_dump
         | 
| 222 | 
            +
                      { class: self.class }
         | 
| 223 | 
            +
                    end
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                    # Organize from cache data
         | 
| 226 | 
            +
                    def cache_load(data)
         | 
| 227 | 
            +
                      data, operations, fragments = data.values_at(:strategy, :operations, :fragments)
         | 
| 228 | 
            +
             | 
| 229 | 
            +
                      collect_listeners do
         | 
| 230 | 
            +
                        # Load all operations
         | 
| 231 | 
            +
                        operations = operations.transform_values do |operation|
         | 
| 232 | 
            +
                          request.build_from_cache(operation.delete(:type)).tap do |instance|
         | 
| 233 | 
            +
                            instance.instance_variable_set(:@request, request)
         | 
| 234 | 
            +
                            instance.cache_load(operation)
         | 
| 235 | 
            +
                          end
         | 
| 236 | 
            +
                        end
         | 
| 237 | 
            +
             | 
| 238 | 
            +
                        # Load all fragments
         | 
| 239 | 
            +
                        fragments = fragments&.transform_values do |fragment|
         | 
| 240 | 
            +
                          request.build_from_cache(Component::Fragment).tap do |instance|
         | 
| 241 | 
            +
                            instance.instance_variable_set(:@request, request)
         | 
| 242 | 
            +
                            instance.cache_load(fragment)
         | 
| 243 | 
            +
                          end
         | 
| 244 | 
            +
                        end
         | 
| 183 245 | 
             
                      end
         | 
| 246 | 
            +
             | 
| 247 | 
            +
                      # Mark itself as already organized
         | 
| 248 | 
            +
                      @organized = true
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                      # Save operations and fragments into the request
         | 
| 251 | 
            +
                      request.instance_variable_set(:@operations, operations)
         | 
| 252 | 
            +
                      request.instance_variable_set(:@fragments, fragments)
         | 
| 184 253 | 
             
                    end
         | 
| 185 254 |  | 
| 186 255 | 
             
                    protected
         | 
| 187 256 |  | 
| 188 | 
            -
                      #  | 
| 189 | 
            -
                      def  | 
| 190 | 
            -
                         | 
| 191 | 
            -
             | 
| 257 | 
            +
                      # Execute a given block for each defined operation
         | 
| 258 | 
            +
                      def for_each_operation
         | 
| 259 | 
            +
                        operations.each do |key, value|
         | 
| 260 | 
            +
                          operations[key] = Component::Operation.build(request, value) \
         | 
| 261 | 
            +
                            if value.is_a?(::GQLParser::Token)
         | 
| 192 262 |  | 
| 193 | 
            -
             | 
| 194 | 
            -
             | 
| 195 | 
            -
                        listeners.freeze
         | 
| 263 | 
            +
                          yield(operations[key])
         | 
| 264 | 
            +
                        end
         | 
| 196 265 | 
             
                      end
         | 
| 197 266 |  | 
| 198 | 
            -
                      # A  | 
| 267 | 
            +
                      # A start of the organize step
         | 
| 199 268 | 
             
                      def collect_listeners
         | 
| 200 | 
            -
                         | 
| 269 | 
            +
                        return if defined?(@organized)
         | 
| 270 | 
            +
                        @stage = :organize
         | 
| 201 271 | 
             
                        yield
         | 
| 202 | 
            -
                        lock_listeners!
         | 
| 203 272 | 
             
                      end
         | 
| 204 273 |  | 
| 205 274 | 
             
                      # This is where the strategy is most effective. By preparing the tree,
         | 
| 206 275 | 
             
                      # it can load data in a pretty smart way
         | 
| 207 | 
            -
                      def collect_data
         | 
| 276 | 
            +
                      def collect_data(force = false)
         | 
| 277 | 
            +
                        @stage = :prepare
         | 
| 208 278 | 
             
                        @data_pool = {}
         | 
| 209 279 | 
             
                        @context = request.build(Request::Context)
         | 
| 210 280 |  | 
| 211 281 | 
             
                        # TODO: Create an orchestrator to allow cross query loading
         | 
| 212 | 
            -
                        yield if listening_to?(:prepare)
         | 
| 282 | 
            +
                        yield if force || listening_to?(:prepare)
         | 
| 213 283 | 
             
                      end
         | 
| 214 284 |  | 
| 215 | 
            -
                      #  | 
| 216 | 
            -
                      # ({Request::Context}[rdoc-ref:Rails::GraphQL::Request::Context])
         | 
| 217 | 
            -
                      # and start collecting results
         | 
| 285 | 
            +
                      # Start collecting results
         | 
| 218 286 | 
             
                      def collect_response
         | 
| 287 | 
            +
                        @stage = :resolve
         | 
| 219 288 | 
             
                        yield
         | 
| 220 | 
            -
                      ensure
         | 
| 221 | 
            -
                        @context = @objects_pool = @data_pool = @listeners = nil
         | 
| 222 289 | 
             
                      end
         | 
| 223 290 |  | 
| 224 291 | 
             
                      # Fetch the data for a given field and set as the first element
         | 
| @@ -232,17 +299,6 @@ module Rails # :nodoc: | |
| 232 299 |  | 
| 233 300 | 
             
                        result << current[key] if current.respond_to?(:key?) && current.key?(key)
         | 
| 234 301 | 
             
                      end
         | 
| 235 | 
            -
             | 
| 236 | 
            -
                    private
         | 
| 237 | 
            -
             | 
| 238 | 
            -
                      # Collect the base listeners from the request
         | 
| 239 | 
            -
                      def collect_request_listeners
         | 
| 240 | 
            -
                        @listeners = Hash.new { |h, k| h[k] = [] }
         | 
| 241 | 
            -
                        add_listener(request)
         | 
| 242 | 
            -
             | 
| 243 | 
            -
                        lock_listeners!
         | 
| 244 | 
            -
                        @base_listeners = @listeners
         | 
| 245 | 
            -
                      end
         | 
| 246 302 | 
             
                  end
         | 
| 247 303 | 
             
                end
         | 
| 248 304 | 
             
              end
         | 
| @@ -0,0 +1,80 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Rails
         | 
| 4 | 
            +
              module GraphQL
         | 
| 5 | 
            +
                class Request
         | 
| 6 | 
            +
                  # = GraphQL Request Subscription
         | 
| 7 | 
            +
                  #
         | 
| 8 | 
            +
                  # A simple object to store information about a generated subscription
         | 
| 9 | 
            +
                  # TODO: Add a callback for the schema allowing it to prepare the context
         | 
| 10 | 
            +
                  # before saving it into a subscription
         | 
| 11 | 
            +
                  class Subscription
         | 
| 12 | 
            +
                    NULL_SCOPE = Object.new.freeze
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    attr_reader :sid, :schema, :args, :field, :scope, :context, :broadcastable,
         | 
| 15 | 
            +
                      :origin, :last_updated_at, :operation_id
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    alias broadcastable? broadcastable
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    def initialize(request, operation)
         | 
| 20 | 
            +
                      entrypoint = operation.selection.each_value.first
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                      @schema = request.schema.namespace
         | 
| 23 | 
            +
                      @origin = request.origin
         | 
| 24 | 
            +
                      @operation_id = operation.hash
         | 
| 25 | 
            +
                      @args = entrypoint.arguments.to_h
         | 
| 26 | 
            +
                      @field = entrypoint.field
         | 
| 27 | 
            +
                      @context = request.context.to_h
         | 
| 28 | 
            +
                      @broadcastable = operation.broadcastable?
         | 
| 29 | 
            +
                      updated!
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                      @scope = parse_scope(field.full_scope, request, operation)
         | 
| 32 | 
            +
                      @sid = request.schema.subscription_id_for(self)
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    def updated!
         | 
| 36 | 
            +
                      @last_updated_at = Time.current
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    def marshal_dump
         | 
| 40 | 
            +
                      raise ::TypeError, +"Request subscriptions must not be used as payload."
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    def inspect
         | 
| 44 | 
            +
                      (+<<~INFO).squish << '>'
         | 
| 45 | 
            +
                        #<#{self.class.name}
         | 
| 46 | 
            +
                        #{schema}@#{sid}
         | 
| 47 | 
            +
                        [#{scope.nil? ? scope.inspect : scope.hash}, #{args.hash}]
         | 
| 48 | 
            +
                        #{field.inspect}
         | 
| 49 | 
            +
                      INFO
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    protected
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                      def parse_scope(list, request, operation)
         | 
| 55 | 
            +
                        enum = GraphQL.enumerate(list)
         | 
| 56 | 
            +
                        return NULL_SCOPE if enum.empty?
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                        ext_self = nil
         | 
| 59 | 
            +
                        enum.map do |item|
         | 
| 60 | 
            +
                          case item
         | 
| 61 | 
            +
                          when Symbol
         | 
| 62 | 
            +
                            context[item]
         | 
| 63 | 
            +
                          when Proc
         | 
| 64 | 
            +
                            next item.call if item.arity == 0
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                            ext_self ||= SimpleDelegator.new(self).tap do |object|
         | 
| 67 | 
            +
                              object.define_singleton_method(:request) { request }
         | 
| 68 | 
            +
                              object.define_singleton_method(:operation) { operation }
         | 
| 69 | 
            +
                            end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                            item.call(ext_self)
         | 
| 72 | 
            +
                          else
         | 
| 73 | 
            +
                            item
         | 
| 74 | 
            +
                          end
         | 
| 75 | 
            +
                        end.compact
         | 
| 76 | 
            +
                      end
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
            end
         |