graphql 1.11.3 → 1.12.0
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.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +8 -0
- data/lib/generators/graphql/install_generator.rb +5 -5
- data/lib/generators/graphql/object_generator.rb +2 -0
- data/lib/generators/graphql/relay_generator.rb +63 -0
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +8 -0
- data/lib/generators/graphql/templates/base_edge.erb +8 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/enum.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/interface.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +9 -0
- data/lib/generators/graphql/templates/object.erb +3 -1
- data/lib/generators/graphql/templates/query_type.erb +3 -3
- data/lib/generators/graphql/templates/scalar.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +10 -35
- data/lib/generators/graphql/templates/union.erb +3 -1
- data/lib/graphql.rb +55 -4
- data/lib/graphql/analysis/analyze_query.rb +7 -0
- data/lib/graphql/analysis/ast.rb +11 -2
- data/lib/graphql/analysis/ast/visitor.rb +9 -1
- data/lib/graphql/argument.rb +3 -3
- data/lib/graphql/backtrace.rb +28 -19
- data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
- data/lib/graphql/backtrace/table.rb +22 -2
- data/lib/graphql/backtrace/tracer.rb +40 -8
- data/lib/graphql/backwards_compatibility.rb +1 -0
- data/lib/graphql/compatibility/execution_specification.rb +1 -0
- data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
- data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
- data/lib/graphql/dataloader.rb +197 -0
- data/lib/graphql/dataloader/null_dataloader.rb +21 -0
- data/lib/graphql/dataloader/request.rb +24 -0
- data/lib/graphql/dataloader/request_all.rb +22 -0
- data/lib/graphql/dataloader/source.rb +93 -0
- data/lib/graphql/define/assign_global_id_field.rb +2 -2
- data/lib/graphql/define/instance_definable.rb +32 -2
- data/lib/graphql/define/type_definer.rb +5 -5
- data/lib/graphql/deprecated_dsl.rb +5 -0
- data/lib/graphql/enum_type.rb +2 -0
- data/lib/graphql/execution/errors.rb +4 -0
- data/lib/graphql/execution/execute.rb +7 -0
- data/lib/graphql/execution/interpreter.rb +20 -6
- data/lib/graphql/execution/interpreter/arguments.rb +57 -5
- data/lib/graphql/execution/interpreter/arguments_cache.rb +8 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
- data/lib/graphql/execution/interpreter/runtime.rb +251 -138
- data/lib/graphql/execution/multiplex.rb +20 -6
- data/lib/graphql/function.rb +4 -0
- data/lib/graphql/input_object_type.rb +2 -0
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/interface_type.rb +3 -1
- data/lib/graphql/introspection.rb +96 -0
- data/lib/graphql/introspection/field_type.rb +7 -3
- data/lib/graphql/introspection/input_value_type.rb +6 -0
- data/lib/graphql/introspection/introspection_query.rb +6 -92
- data/lib/graphql/introspection/type_type.rb +7 -3
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/language/block_string.rb +24 -5
- data/lib/graphql/language/document_from_schema_definition.rb +50 -23
- data/lib/graphql/language/lexer.rb +7 -3
- data/lib/graphql/language/lexer.rl +7 -3
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/parser.rb +107 -103
- data/lib/graphql/language/parser.y +4 -0
- data/lib/graphql/language/sanitized_printer.rb +59 -26
- data/lib/graphql/name_validator.rb +6 -7
- data/lib/graphql/object_type.rb +2 -0
- data/lib/graphql/pagination/connection.rb +5 -1
- data/lib/graphql/pagination/connections.rb +15 -17
- data/lib/graphql/query.rb +8 -3
- data/lib/graphql/query/context.rb +38 -4
- data/lib/graphql/query/fingerprint.rb +2 -0
- data/lib/graphql/query/serial_execution.rb +1 -0
- data/lib/graphql/query/validation_pipeline.rb +4 -1
- data/lib/graphql/relay/array_connection.rb +2 -2
- data/lib/graphql/relay/base_connection.rb +7 -0
- data/lib/graphql/relay/connection_instrumentation.rb +4 -4
- data/lib/graphql/relay/connection_type.rb +1 -1
- data/lib/graphql/relay/mutation.rb +1 -0
- data/lib/graphql/relay/node.rb +3 -0
- data/lib/graphql/relay/range_add.rb +14 -5
- data/lib/graphql/relay/type_extensions.rb +2 -0
- data/lib/graphql/scalar_type.rb +2 -0
- data/lib/graphql/schema.rb +107 -38
- data/lib/graphql/schema/argument.rb +74 -5
- data/lib/graphql/schema/build_from_definition.rb +203 -86
- data/lib/graphql/schema/default_type_error.rb +2 -0
- data/lib/graphql/schema/directive.rb +76 -0
- data/lib/graphql/schema/directive/deprecated.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +57 -0
- data/lib/graphql/schema/enum.rb +3 -0
- data/lib/graphql/schema/enum_value.rb +12 -6
- data/lib/graphql/schema/field.rb +59 -24
- data/lib/graphql/schema/field/connection_extension.rb +11 -9
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/input_object.rb +38 -25
- data/lib/graphql/schema/interface.rb +2 -1
- data/lib/graphql/schema/late_bound_type.rb +2 -2
- data/lib/graphql/schema/loader.rb +1 -0
- data/lib/graphql/schema/member.rb +4 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
- data/lib/graphql/schema/member/build_type.rb +17 -7
- data/lib/graphql/schema/member/has_arguments.rb +70 -51
- data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
- data/lib/graphql/schema/member/has_directives.rb +98 -0
- data/lib/graphql/schema/member/has_fields.rb +2 -2
- data/lib/graphql/schema/member/has_validators.rb +31 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +3 -3
- data/lib/graphql/schema/object.rb +11 -0
- data/lib/graphql/schema/printer.rb +5 -4
- data/lib/graphql/schema/relay_classic_mutation.rb +4 -2
- data/lib/graphql/schema/resolver.rb +7 -0
- data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
- data/lib/graphql/schema/subscription.rb +20 -12
- data/lib/graphql/schema/timeout.rb +29 -15
- data/lib/graphql/schema/timeout_middleware.rb +2 -0
- data/lib/graphql/schema/unique_within_type.rb +1 -2
- data/lib/graphql/schema/validation.rb +10 -0
- data/lib/graphql/schema/validator.rb +163 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +31 -0
- data/lib/graphql/schema/validator/format_validator.rb +49 -0
- data/lib/graphql/schema/validator/inclusion_validator.rb +33 -0
- data/lib/graphql/schema/validator/length_validator.rb +57 -0
- data/lib/graphql/schema/validator/numericality_validator.rb +71 -0
- data/lib/graphql/schema/validator/required_validator.rb +68 -0
- data/lib/graphql/schema/warden.rb +2 -3
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +25 -17
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +31 -7
- data/lib/graphql/subscriptions.rb +23 -16
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +21 -7
- data/lib/graphql/tracing.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +12 -2
- data/lib/graphql/tracing/platform_tracing.rb +4 -2
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
- data/lib/graphql/tracing/skylight_tracing.rb +1 -1
- data/lib/graphql/types/int.rb +9 -2
- data/lib/graphql/types/iso_8601_date_time.rb +2 -1
- data/lib/graphql/types/relay.rb +11 -3
- data/lib/graphql/types/relay/base_connection.rb +2 -90
- data/lib/graphql/types/relay/base_edge.rb +2 -34
- data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
- data/lib/graphql/types/relay/default_relay.rb +27 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
- data/lib/graphql/types/relay/has_node_field.rb +41 -0
- data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
- data/lib/graphql/types/relay/node.rb +2 -4
- data/lib/graphql/types/relay/node_behaviors.rb +15 -0
- data/lib/graphql/types/relay/node_field.rb +1 -19
- data/lib/graphql/types/relay/nodes_field.rb +1 -19
- data/lib/graphql/types/relay/page_info.rb +2 -14
- data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
- data/lib/graphql/types/string.rb +7 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/union_type.rb +2 -0
- data/lib/graphql/upgrader/member.rb +1 -0
- data/lib/graphql/upgrader/schema.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -1
- metadata +38 -9
- data/lib/graphql/types/relay/base_field.rb +0 -22
- data/lib/graphql/types/relay/base_interface.rb +0 -29
- data/lib/graphql/types/relay/base_object.rb +0 -26
| @@ -7,6 +7,7 @@ module GraphQL | |
| 7 7 | 
             
                  extend GraphQL::Schema::Member::HasArguments
         | 
| 8 8 | 
             
                  extend GraphQL::Schema::Member::HasArguments::ArgumentObjectLoader
         | 
| 9 9 | 
             
                  extend GraphQL::Schema::Member::ValidatesInput
         | 
| 10 | 
            +
                  extend GraphQL::Schema::Member::HasValidators
         | 
| 10 11 |  | 
| 11 12 | 
             
                  include GraphQL::Dig
         | 
| 12 13 |  | 
| @@ -37,14 +38,15 @@ module GraphQL | |
| 37 38 | 
             
                            load_application_object(arg_defn, loads, value, context)
         | 
| 38 39 | 
             
                          end
         | 
| 39 40 | 
             
                          maybe_lazies << context.schema.after_lazy(loaded_value) do |loaded_value|
         | 
| 40 | 
            -
                             | 
| 41 | 
            +
                            overwrite_argument(ruby_kwargs_key, loaded_value)
         | 
| 41 42 | 
             
                          end
         | 
| 42 43 | 
             
                        end
         | 
| 43 44 |  | 
| 44 45 | 
             
                        # Weirdly, procs are applied during coercion, but not methods.
         | 
| 45 46 | 
             
                        # Probably because these methods require a `self`.
         | 
| 46 47 | 
             
                        if arg_defn.prepare.is_a?(Symbol) || context.nil? || !context.interpreter?
         | 
| 47 | 
            -
                           | 
| 48 | 
            +
                          prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
         | 
| 49 | 
            +
                          overwrite_argument(ruby_kwargs_key, prepared_value)
         | 
| 48 50 | 
             
                        end
         | 
| 49 51 | 
             
                      end
         | 
| 50 52 | 
             
                    end
         | 
| @@ -74,6 +76,9 @@ module GraphQL | |
| 74 76 | 
             
                  def prepare
         | 
| 75 77 | 
             
                    if context
         | 
| 76 78 | 
             
                      context.schema.after_any_lazies(@maybe_lazies) do
         | 
| 79 | 
            +
                        object = context[:current_object]
         | 
| 80 | 
            +
                        # Pass this object's class with `as` so that messages are rendered correctly from inherited validators
         | 
| 81 | 
            +
                        Schema::Validator.validate!(self.class.validators, object, context, @ruby_style_hash, as: self.class)
         | 
| 77 82 | 
             
                        self
         | 
| 78 83 | 
             
                      end
         | 
| 79 84 | 
             
                    else
         | 
| @@ -126,9 +131,11 @@ module GraphQL | |
| 126 131 | 
             
                      argument_defn = super(*args, **kwargs, &block)
         | 
| 127 132 | 
             
                      # Add a method access
         | 
| 128 133 | 
             
                      method_name = argument_defn.keyword
         | 
| 129 | 
            -
                       | 
| 130 | 
            -
                         | 
| 131 | 
            -
             | 
| 134 | 
            +
                      class_eval <<-RUBY, __FILE__, __LINE__
         | 
| 135 | 
            +
                        def #{method_name}
         | 
| 136 | 
            +
                          self[#{method_name.inspect}]
         | 
| 137 | 
            +
                        end
         | 
| 138 | 
            +
                      RUBY
         | 
| 132 139 | 
             
                    end
         | 
| 133 140 |  | 
| 134 141 | 
             
                    def to_graphql
         | 
| @@ -166,17 +173,10 @@ module GraphQL | |
| 166 173 | 
             
                        return result
         | 
| 167 174 | 
             
                      end
         | 
| 168 175 |  | 
| 169 | 
            -
                      input  | 
| 170 | 
            -
                         | 
| 171 | 
            -
             | 
| 172 | 
            -
                         | 
| 173 | 
            -
                          # Handle ActionController::Parameters:
         | 
| 174 | 
            -
                          input.to_unsafe_h
         | 
| 175 | 
            -
                        rescue
         | 
| 176 | 
            -
                          # We're not sure it'll act like a hash, so reject it:
         | 
| 177 | 
            -
                          result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
         | 
| 178 | 
            -
                          return result
         | 
| 179 | 
            -
                        end
         | 
| 176 | 
            +
                      if !(input.respond_to?(:to_h) || input.respond_to?(:to_unsafe_h))
         | 
| 177 | 
            +
                        # We're not sure it'll act like a hash, so reject it:
         | 
| 178 | 
            +
                        result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
         | 
| 179 | 
            +
                        return result
         | 
| 180 180 | 
             
                      end
         | 
| 181 181 |  | 
| 182 182 | 
             
                      # Inject missing required arguments
         | 
| @@ -188,16 +188,19 @@ module GraphQL | |
| 188 188 | 
             
                        m
         | 
| 189 189 | 
             
                      end
         | 
| 190 190 |  | 
| 191 | 
            -
             | 
| 192 | 
            -
             | 
| 193 | 
            -
                         | 
| 194 | 
            -
             | 
| 195 | 
            -
                           | 
| 196 | 
            -
                           | 
| 191 | 
            +
             | 
| 192 | 
            +
                      [input, missing_required_inputs].each do |args_to_validate|
         | 
| 193 | 
            +
                        args_to_validate.each do |argument_name, value|
         | 
| 194 | 
            +
                          argument = warden.get_argument(self, argument_name)
         | 
| 195 | 
            +
                          # Items in the input that are unexpected
         | 
| 196 | 
            +
                          unless argument
         | 
| 197 | 
            +
                            result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
         | 
| 198 | 
            +
                            next
         | 
| 199 | 
            +
                          end
         | 
| 200 | 
            +
                          # Items in the input that are expected, but have invalid values
         | 
| 201 | 
            +
                          argument_result = argument.type.validate_input(value, ctx)
         | 
| 202 | 
            +
                          result.merge_result!(argument_name, argument_result) unless argument_result.valid?
         | 
| 197 203 | 
             
                        end
         | 
| 198 | 
            -
                        # Items in the input that are expected, but have invalid values
         | 
| 199 | 
            -
                        argument_result = argument.type.validate_input(value, ctx)
         | 
| 200 | 
            -
                        result.merge_result!(argument_name, argument_result) unless argument_result.valid?
         | 
| 201 204 | 
             
                      end
         | 
| 202 205 |  | 
| 203 206 | 
             
                      result
         | 
| @@ -238,6 +241,16 @@ module GraphQL | |
| 238 241 | 
             
                      result
         | 
| 239 242 | 
             
                    end
         | 
| 240 243 | 
             
                  end
         | 
| 244 | 
            +
             | 
| 245 | 
            +
                  private
         | 
| 246 | 
            +
             | 
| 247 | 
            +
                  def overwrite_argument(key, value)
         | 
| 248 | 
            +
                    # Argument keywords come in frozen from the interpreter, dup them before modifying them.
         | 
| 249 | 
            +
                    if @ruby_style_hash.frozen?
         | 
| 250 | 
            +
                      @ruby_style_hash = @ruby_style_hash.dup
         | 
| 251 | 
            +
                    end
         | 
| 252 | 
            +
                    @ruby_style_hash[key] = value
         | 
| 253 | 
            +
                  end
         | 
| 241 254 | 
             
                end
         | 
| 242 255 | 
             
              end
         | 
| 243 256 | 
             
            end
         | 
| @@ -15,6 +15,7 @@ module GraphQL | |
| 15 15 | 
             
                    include GraphQL::Schema::Member::Scoped
         | 
| 16 16 | 
             
                    include GraphQL::Schema::Member::HasAstNode
         | 
| 17 17 | 
             
                    include GraphQL::Schema::Member::HasUnresolvedTypeError
         | 
| 18 | 
            +
                    include GraphQL::Schema::Member::HasDirectives
         | 
| 18 19 |  | 
| 19 20 | 
             
                    # Methods defined in this block will be:
         | 
| 20 21 | 
             
                    # - Added as class methods to this interface
         | 
| @@ -30,7 +31,7 @@ module GraphQL | |
| 30 31 |  | 
| 31 32 | 
             
                    # The interface is accessible if any of its possible types are accessible
         | 
| 32 33 | 
             
                    def accessible?(context)
         | 
| 33 | 
            -
                      context.schema.possible_types(self).each do |type|
         | 
| 34 | 
            +
                      context.schema.possible_types(self, context).each do |type|
         | 
| 34 35 | 
             
                        if context.schema.accessible?(type, context)
         | 
| 35 36 | 
             
                          return true
         | 
| 36 37 | 
             
                        end
         | 
| @@ -16,11 +16,11 @@ module GraphQL | |
| 16 16 | 
             
                  end
         | 
| 17 17 |  | 
| 18 18 | 
             
                  def to_non_null_type
         | 
| 19 | 
            -
                    GraphQL::NonNullType.new(of_type: self)
         | 
| 19 | 
            +
                    @to_non_null_type ||= GraphQL::NonNullType.new(of_type: self)
         | 
| 20 20 | 
             
                  end
         | 
| 21 21 |  | 
| 22 22 | 
             
                  def to_list_type
         | 
| 23 | 
            -
                    GraphQL::ListType.new(of_type: self)
         | 
| 23 | 
            +
                    @to_list_type ||= GraphQL::ListType.new(of_type: self)
         | 
| 24 24 | 
             
                  end
         | 
| 25 25 |  | 
| 26 26 | 
             
                  def inspect
         | 
| @@ -4,8 +4,11 @@ require 'graphql/schema/member/base_dsl_methods' | |
| 4 4 | 
             
            require 'graphql/schema/member/cached_graphql_definition'
         | 
| 5 5 | 
             
            require 'graphql/schema/member/graphql_type_names'
         | 
| 6 6 | 
             
            require 'graphql/schema/member/has_ast_node'
         | 
| 7 | 
            +
            require 'graphql/schema/member/has_directives'
         | 
| 8 | 
            +
            require 'graphql/schema/member/has_deprecation_reason'
         | 
| 7 9 | 
             
            require 'graphql/schema/member/has_path'
         | 
| 8 10 | 
             
            require 'graphql/schema/member/has_unresolved_type_error'
         | 
| 11 | 
            +
            require 'graphql/schema/member/has_validators'
         | 
| 9 12 | 
             
            require 'graphql/schema/member/relay_shortcuts'
         | 
| 10 13 | 
             
            require 'graphql/schema/member/scoped'
         | 
| 11 14 | 
             
            require 'graphql/schema/member/type_system_helpers'
         | 
| @@ -30,6 +33,7 @@ module GraphQL | |
| 30 33 | 
             
                  extend RelayShortcuts
         | 
| 31 34 | 
             
                  extend HasPath
         | 
| 32 35 | 
             
                  extend HasAstNode
         | 
| 36 | 
            +
                  extend HasDirectives
         | 
| 33 37 | 
             
                end
         | 
| 34 38 | 
             
              end
         | 
| 35 39 | 
             
            end
         | 
| @@ -4,6 +4,10 @@ module GraphQL | |
| 4 4 | 
             
                class Member
         | 
| 5 5 | 
             
                  # @api private
         | 
| 6 6 | 
             
                  module BuildType
         | 
| 7 | 
            +
                    if !String.method_defined?(:match?)
         | 
| 8 | 
            +
                      using GraphQL::StringMatchBackport
         | 
| 9 | 
            +
                    end
         | 
| 10 | 
            +
             | 
| 7 11 | 
             
                    LIST_TYPE_ERROR = "Use an array of [T] or [T, null: true] for list types; other arrays are not supported"
         | 
| 8 12 |  | 
| 9 13 | 
             
                    module_function
         | 
| @@ -56,11 +60,11 @@ module GraphQL | |
| 56 60 | 
             
                          parse_type(type_expr.first, null: false)
         | 
| 57 61 | 
             
                        when 2
         | 
| 58 62 | 
             
                          inner_type, nullable_option = type_expr
         | 
| 59 | 
            -
                          if nullable_option.keys != [:null] || nullable_option | 
| 63 | 
            +
                          if nullable_option.keys != [:null] || (nullable_option[:null] != true && nullable_option[:null] != false)
         | 
| 60 64 | 
             
                            raise ArgumentError, LIST_TYPE_ERROR
         | 
| 61 65 | 
             
                          end
         | 
| 62 66 | 
             
                          list_type = true
         | 
| 63 | 
            -
                          parse_type(inner_type, null:  | 
| 67 | 
            +
                          parse_type(inner_type, null: nullable_option[:null])
         | 
| 64 68 | 
             
                        else
         | 
| 65 69 | 
             
                          raise ArgumentError, LIST_TYPE_ERROR
         | 
| 66 70 | 
             
                        end
         | 
| @@ -71,7 +75,7 @@ module GraphQL | |
| 71 75 | 
             
                        if type_expr.respond_to?(:graphql_definition)
         | 
| 72 76 | 
             
                          type_expr
         | 
| 73 77 | 
             
                        else
         | 
| 74 | 
            -
                          # Eg `String` => GraphQL:: | 
| 78 | 
            +
                          # Eg `String` => GraphQL::Types::String
         | 
| 75 79 | 
             
                          parse_type(type_expr.name, null: true)
         | 
| 76 80 | 
             
                        end
         | 
| 77 81 | 
             
                      when Proc
         | 
| @@ -162,10 +166,16 @@ module GraphQL | |
| 162 166 | 
             
                    end
         | 
| 163 167 |  | 
| 164 168 | 
             
                    def underscore(string)
         | 
| 165 | 
            -
                      string
         | 
| 166 | 
            -
                         | 
| 167 | 
            -
             | 
| 168 | 
            -
             | 
| 169 | 
            +
                      if string.match?(/\A[a-z_]+\Z/)
         | 
| 170 | 
            +
                        return string
         | 
| 171 | 
            +
                      end
         | 
| 172 | 
            +
                      string2 = string.dup
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                      string2.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2') # URLDecoder -> URL_Decoder
         | 
| 175 | 
            +
                      string2.gsub!(/([a-z\d])([A-Z])/,'\1_\2')     # someThing -> some_Thing
         | 
| 176 | 
            +
                      string2.downcase!
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                      string2
         | 
| 169 179 | 
             
                    end
         | 
| 170 180 | 
             
                  end
         | 
| 171 181 | 
             
                end
         | 
| @@ -43,6 +43,7 @@ module GraphQL | |
| 43 43 | 
             
                    # @param arg_defn [GraphQL::Schema::Argument]
         | 
| 44 44 | 
             
                    # @return [GraphQL::Schema::Argument]
         | 
| 45 45 | 
             
                    def add_argument(arg_defn)
         | 
| 46 | 
            +
                      @own_arguments ||= {}
         | 
| 46 47 | 
             
                      own_arguments[arg_defn.name] = arg_defn
         | 
| 47 48 | 
             
                      arg_defn
         | 
| 48 49 | 
             
                    end
         | 
| @@ -84,70 +85,87 @@ module GraphQL | |
| 84 85 | 
             
                    # @param context [GraphQL::Query::Context]
         | 
| 85 86 | 
             
                    # @return [Hash<Symbol, Object>, Execution::Lazy<Hash>]
         | 
| 86 87 | 
             
                    def coerce_arguments(parent_object, values, context)
         | 
| 87 | 
            -
                      argument_values = {}
         | 
| 88 | 
            -
                      kwarg_arguments = {}
         | 
| 89 88 | 
             
                      # Cache this hash to avoid re-merging it
         | 
| 90 89 | 
             
                      arg_defns = self.arguments
         | 
| 91 90 |  | 
| 92 | 
            -
                       | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
                         | 
| 96 | 
            -
                         | 
| 97 | 
            -
             | 
| 98 | 
            -
                          has_value =  | 
| 99 | 
            -
                           | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
                           | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
                          loads = arg_defn.loads
         | 
| 111 | 
            -
                          loaded_value = nil
         | 
| 112 | 
            -
                          if loads && !arg_defn.from_resolver?
         | 
| 113 | 
            -
                            loaded_value = if arg_defn.type.list?
         | 
| 114 | 
            -
                              loaded_values = value.map { |val| load_application_object(arg_defn, loads, val, context) }
         | 
| 115 | 
            -
                              context.schema.after_any_lazies(loaded_values) { |result| result }
         | 
| 116 | 
            -
                            else
         | 
| 117 | 
            -
                              load_application_object(arg_defn, loads, value, context)
         | 
| 118 | 
            -
                            end
         | 
| 91 | 
            +
                      if arg_defns.empty?
         | 
| 92 | 
            +
                        GraphQL::Execution::Interpreter::Arguments::EMPTY
         | 
| 93 | 
            +
                      else
         | 
| 94 | 
            +
                        argument_values = {}
         | 
| 95 | 
            +
                        arg_lazies = arg_defns.map do |arg_name, arg_defn|
         | 
| 96 | 
            +
                          arg_key = arg_defn.keyword
         | 
| 97 | 
            +
                          has_value = false
         | 
| 98 | 
            +
                          default_used = false
         | 
| 99 | 
            +
                          if values.key?(arg_name)
         | 
| 100 | 
            +
                            has_value = true
         | 
| 101 | 
            +
                            value = values[arg_name]
         | 
| 102 | 
            +
                          elsif values.key?(arg_key)
         | 
| 103 | 
            +
                            has_value = true
         | 
| 104 | 
            +
                            value = values[arg_key]
         | 
| 105 | 
            +
                          elsif arg_defn.default_value?
         | 
| 106 | 
            +
                            has_value = true
         | 
| 107 | 
            +
                            value = arg_defn.default_value
         | 
| 108 | 
            +
                            default_used = true
         | 
| 119 109 | 
             
                          end
         | 
| 120 110 |  | 
| 121 | 
            -
                           | 
| 122 | 
            -
                             | 
| 123 | 
            -
             | 
| 124 | 
            -
                            context.schema.error_handler.with_error_handling(context) do
         | 
| 111 | 
            +
                          if has_value
         | 
| 112 | 
            +
                            loads = arg_defn.loads
         | 
| 113 | 
            +
                            loaded_value = nil
         | 
| 114 | 
            +
                            coerced_value = context.schema.error_handler.with_error_handling(context) do
         | 
| 125 115 | 
             
                              arg_defn.type.coerce_input(value, context)
         | 
| 126 116 | 
             
                            end
         | 
| 127 | 
            -
                          end
         | 
| 128 117 |  | 
| 129 | 
            -
             | 
| 130 | 
            -
                             | 
| 131 | 
            -
                               | 
| 118 | 
            +
                            # TODO this should probably be inside after_lazy
         | 
| 119 | 
            +
                            if loads && !arg_defn.from_resolver?
         | 
| 120 | 
            +
                              loaded_value = if arg_defn.type.list?
         | 
| 121 | 
            +
                                loaded_values = coerced_value.map { |val| load_application_object(arg_defn, loads, val, context) }
         | 
| 122 | 
            +
                                context.schema.after_any_lazies(loaded_values) { |result| result }
         | 
| 123 | 
            +
                              else
         | 
| 124 | 
            +
                                load_application_object(arg_defn, loads, coerced_value, context)
         | 
| 125 | 
            +
                              end
         | 
| 126 | 
            +
                            end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                            coerced_value = if loaded_value
         | 
| 129 | 
            +
                              loaded_value
         | 
| 130 | 
            +
                            else
         | 
| 131 | 
            +
                              coerced_value
         | 
| 132 132 | 
             
                            end
         | 
| 133 133 |  | 
| 134 | 
            -
                             | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 138 | 
            -
                               | 
| 139 | 
            -
             | 
| 140 | 
            -
             | 
| 134 | 
            +
                            context.schema.after_lazy(coerced_value) do |coerced_value|
         | 
| 135 | 
            +
                              validate_directive_argument(arg_defn, coerced_value)
         | 
| 136 | 
            +
                              prepared_value = context.schema.error_handler.with_error_handling(context) do
         | 
| 137 | 
            +
                                arg_defn.prepare_value(parent_object, coerced_value, context: context)
         | 
| 138 | 
            +
                              end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                              # TODO code smell to access such a deeply-nested constant in a distant module
         | 
| 141 | 
            +
                              argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
         | 
| 142 | 
            +
                                value: prepared_value,
         | 
| 143 | 
            +
                                definition: arg_defn,
         | 
| 144 | 
            +
                                default_used: default_used,
         | 
| 145 | 
            +
                              )
         | 
| 146 | 
            +
                            end
         | 
| 147 | 
            +
                          else
         | 
| 148 | 
            +
                            # has_value is false
         | 
| 149 | 
            +
                            validate_directive_argument(arg_defn, nil)
         | 
| 141 150 | 
             
                          end
         | 
| 142 151 | 
             
                        end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                        context.schema.after_any_lazies(arg_lazies) do
         | 
| 154 | 
            +
                          GraphQL::Execution::Interpreter::Arguments.new(
         | 
| 155 | 
            +
                            argument_values: argument_values,
         | 
| 156 | 
            +
                          )
         | 
| 157 | 
            +
                        end
         | 
| 143 158 | 
             
                      end
         | 
| 159 | 
            +
                    end
         | 
| 144 160 |  | 
| 145 | 
            -
             | 
| 146 | 
            -
             | 
| 147 | 
            -
             | 
| 148 | 
            -
             | 
| 149 | 
            -
             | 
| 150 | 
            -
                         | 
| 161 | 
            +
                    # Usually, this is validated statically by RequiredArgumentsArePresent,
         | 
| 162 | 
            +
                    # but not for directives.
         | 
| 163 | 
            +
                    # TODO apply static validations on schema definitions?
         | 
| 164 | 
            +
                    def validate_directive_argument(arg_defn, value)
         | 
| 165 | 
            +
                      if arg_defn.owner.is_a?(Class) && arg_defn.owner < GraphQL::Schema::Directive
         | 
| 166 | 
            +
                        if value.nil? && arg_defn.type.non_null?
         | 
| 167 | 
            +
                          raise ArgumentError, "#{arg_defn.path} is required, but no value was given"
         | 
| 168 | 
            +
                        end
         | 
| 151 169 | 
             
                      end
         | 
| 152 170 | 
             
                    end
         | 
| 153 171 |  | 
| @@ -229,8 +247,9 @@ module GraphQL | |
| 229 247 | 
             
                      end
         | 
| 230 248 | 
             
                    end
         | 
| 231 249 |  | 
| 250 | 
            +
                    NO_ARGUMENTS = {}.freeze
         | 
| 232 251 | 
             
                    def own_arguments
         | 
| 233 | 
            -
                      @own_arguments  | 
| 252 | 
            +
                      @own_arguments || NO_ARGUMENTS
         | 
| 234 253 | 
             
                    end
         | 
| 235 254 | 
             
                  end
         | 
| 236 255 | 
             
                end
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GraphQL
         | 
| 4 | 
            +
              class Schema
         | 
| 5 | 
            +
                class Member
         | 
| 6 | 
            +
                  module HasDeprecationReason
         | 
| 7 | 
            +
                    # @return [String, nil] Explains why this member was deprecated (if present, this will be marked deprecated in introspection)
         | 
| 8 | 
            +
                    def deprecation_reason
         | 
| 9 | 
            +
                      dir = self.directives.find { |d| d.is_a?(GraphQL::Schema::Directive::Deprecated) }
         | 
| 10 | 
            +
                      dir && dir.arguments[:reason]
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    # Set the deprecation reason for this member, or remove it by assigning `nil`
         | 
| 14 | 
            +
                    # @param text [String, nil]
         | 
| 15 | 
            +
                    def deprecation_reason=(text)
         | 
| 16 | 
            +
                      if text.nil?
         | 
| 17 | 
            +
                        remove_directive(GraphQL::Schema::Directive::Deprecated)
         | 
| 18 | 
            +
                      else
         | 
| 19 | 
            +
                        directive(GraphQL::Schema::Directive::Deprecated, reason: text)
         | 
| 20 | 
            +
                      end
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| @@ -0,0 +1,98 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GraphQL
         | 
| 4 | 
            +
              class Schema
         | 
| 5 | 
            +
                class Member
         | 
| 6 | 
            +
                  module HasDirectives
         | 
| 7 | 
            +
                    # Create an instance of `dir_class` for `self`, using `options`.
         | 
| 8 | 
            +
                    #
         | 
| 9 | 
            +
                    # It removes a previously-attached instance of `dir_class`, if there is one.
         | 
| 10 | 
            +
                    #
         | 
| 11 | 
            +
                    # @return [void]
         | 
| 12 | 
            +
                    def directive(dir_class, **options)
         | 
| 13 | 
            +
                      @own_directives ||= []
         | 
| 14 | 
            +
                      remove_directive(dir_class)
         | 
| 15 | 
            +
                      @own_directives << dir_class.new(self, **options)
         | 
| 16 | 
            +
                      nil
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    # Remove an attached instance of `dir_class`, if there is one
         | 
| 20 | 
            +
                    # @param dir_class [Class<GraphQL::Schema::Directive>]
         | 
| 21 | 
            +
                    # @return [viod]
         | 
| 22 | 
            +
                    def remove_directive(dir_class)
         | 
| 23 | 
            +
                      @own_directives && @own_directives.reject! { |d| d.is_a?(dir_class) }
         | 
| 24 | 
            +
                      nil
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    NO_DIRECTIVES = [].freeze
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    def directives
         | 
| 30 | 
            +
                      case self
         | 
| 31 | 
            +
                      when Class
         | 
| 32 | 
            +
                        inherited_directives = if superclass.respond_to?(:directives)
         | 
| 33 | 
            +
                          superclass.directives
         | 
| 34 | 
            +
                        else
         | 
| 35 | 
            +
                          NO_DIRECTIVES
         | 
| 36 | 
            +
                        end
         | 
| 37 | 
            +
                        if inherited_directives.any? && @own_directives
         | 
| 38 | 
            +
                          dirs = []
         | 
| 39 | 
            +
                          merge_directives(dirs, inherited_directives)
         | 
| 40 | 
            +
                          merge_directives(dirs, @own_directives)
         | 
| 41 | 
            +
                          dirs
         | 
| 42 | 
            +
                        elsif @own_directives
         | 
| 43 | 
            +
                          @own_directives
         | 
| 44 | 
            +
                        elsif inherited_directives.any?
         | 
| 45 | 
            +
                          inherited_directives
         | 
| 46 | 
            +
                        else
         | 
| 47 | 
            +
                          NO_DIRECTIVES
         | 
| 48 | 
            +
                        end
         | 
| 49 | 
            +
                      when Module
         | 
| 50 | 
            +
                        dirs = nil
         | 
| 51 | 
            +
                        self.ancestors.reverse_each do |ancestor|
         | 
| 52 | 
            +
                          if ancestor.respond_to?(:own_directives) &&
         | 
| 53 | 
            +
                              (anc_dirs = ancestor.own_directives).any?
         | 
| 54 | 
            +
                            dirs ||= []
         | 
| 55 | 
            +
                            merge_directives(dirs, anc_dirs)
         | 
| 56 | 
            +
                          end
         | 
| 57 | 
            +
                        end
         | 
| 58 | 
            +
                        if own_directives
         | 
| 59 | 
            +
                          dirs ||= []
         | 
| 60 | 
            +
                          merge_directives(dirs, own_directives)
         | 
| 61 | 
            +
                        end
         | 
| 62 | 
            +
                        dirs || NO_DIRECTIVES
         | 
| 63 | 
            +
                      when HasDirectives
         | 
| 64 | 
            +
                        @own_directives || NO_DIRECTIVES
         | 
| 65 | 
            +
                      else
         | 
| 66 | 
            +
                        raise "Invariant: how could #{self} not be a Class, Module, or instance of HasDirectives?"
         | 
| 67 | 
            +
                      end
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    protected
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    def own_directives
         | 
| 73 | 
            +
                      @own_directives
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    private
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    # Modify `target` by adding items from `dirs` such that:
         | 
| 79 | 
            +
                    # - Any name conflict is overriden by the incoming member of `dirs`
         | 
| 80 | 
            +
                    # - Any other member of `dirs` is appended
         | 
| 81 | 
            +
                    # @param target [Array<GraphQL::Schema::Directive>]
         | 
| 82 | 
            +
                    # @param dirs [Array<GraphQL::Schema::Directive>]
         | 
| 83 | 
            +
                    # @return [void]
         | 
| 84 | 
            +
                    def merge_directives(target, dirs)
         | 
| 85 | 
            +
                      dirs.each do |dir|
         | 
| 86 | 
            +
                        if (idx = target.find_index { |d| d.graphql_name == dir.graphql_name })
         | 
| 87 | 
            +
                          target.slice!(idx)
         | 
| 88 | 
            +
                          target.insert(idx, dir)
         | 
| 89 | 
            +
                        else
         | 
| 90 | 
            +
                          target << dir
         | 
| 91 | 
            +
                        end
         | 
| 92 | 
            +
                      end
         | 
| 93 | 
            +
                      nil
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
            end
         |