graphql 2.0.6 → 2.0.9
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/relay.rb +3 -17
- data/lib/graphql/analysis/ast/field_usage.rb +3 -1
- data/lib/graphql/execution/interpreter/resolve.rb +7 -0
- data/lib/graphql/execution/interpreter/runtime.rb +23 -3
- data/lib/graphql/execution/lazy.rb +0 -1
- data/lib/graphql/language/parser.rb +338 -327
- data/lib/graphql/language/parser.y +12 -8
- data/lib/graphql/language/printer.rb +9 -5
- data/lib/graphql/pagination/connection.rb +31 -4
- data/lib/graphql/pagination/connections.rb +1 -0
- data/lib/graphql/schema/field/connection_extension.rb +4 -0
- data/lib/graphql/schema/field.rb +54 -14
- data/lib/graphql/schema/resolver.rb +21 -0
- data/lib/graphql/schema/warden.rb +6 -2
- data/lib/graphql/schema.rb +24 -8
- data/lib/graphql/subscriptions.rb +15 -3
- data/lib/graphql/tracing/data_dog_tracing.rb +16 -1
- data/lib/graphql/tracing/opentelemetry_tracing.rb +101 -0
- data/lib/graphql/tracing/platform_tracing.rb +8 -4
- data/lib/graphql/tracing.rb +1 -0
- data/lib/graphql/types/iso_8601_date_time.rb +3 -0
- data/lib/graphql/types/relay/base_connection.rb +16 -6
- data/lib/graphql/version.rb +1 -1
- metadata +3 -3
- data/lib/graphql/execution/lazy/resolve.rb +0 -91
| @@ -321,13 +321,13 @@ rule | |
| 321 321 |  | 
| 322 322 | 
             
              object_type_extension:
         | 
| 323 323 | 
             
                  /* TODO - This first one shouldn't be necessary but parser is getting confused */
         | 
| 324 | 
            -
                  EXTEND TYPE name implements  | 
| 325 | 
            -
                | EXTEND TYPE name implements_opt directives_list_opt  | 
| 324 | 
            +
                  EXTEND TYPE name implements field_definition_list_opt { result = make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: [], fields: val[4], position_source: val[0]) }
         | 
| 325 | 
            +
                | EXTEND TYPE name implements_opt directives_list_opt field_definition_list_opt { result = make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: val[4], fields: val[5], position_source: val[0]) }
         | 
| 326 326 | 
             
                | EXTEND TYPE name implements_opt directives_list { result = make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: val[4], fields: [], position_source: val[0]) }
         | 
| 327 327 | 
             
                | EXTEND TYPE name implements { result = make_node(:ObjectTypeExtension, name: val[2], interfaces: val[3], directives: [], fields: [], position_source: val[0]) }
         | 
| 328 328 |  | 
| 329 329 | 
             
              interface_type_extension:
         | 
| 330 | 
            -
                  EXTEND INTERFACE name implements_opt directives_list_opt  | 
| 330 | 
            +
                  EXTEND INTERFACE name implements_opt directives_list_opt field_definition_list_opt { result = make_node(:InterfaceTypeExtension, name: val[2], interfaces: val[3], directives: val[4], fields: val[5], position_source: val[0]) }
         | 
| 331 331 | 
             
                | EXTEND INTERFACE name implements_opt directives_list { result = make_node(:InterfaceTypeExtension, name: val[2], interfaces: val[3], directives: val[4], fields: [], position_source: val[0]) }
         | 
| 332 332 | 
             
                | EXTEND INTERFACE name implements { result = make_node(:InterfaceTypeExtension, name: val[2], interfaces: val[3], directives: [], fields: [], position_source: val[0]) }
         | 
| 333 333 |  | 
| @@ -355,8 +355,8 @@ rule | |
| 355 355 | 
             
                  }
         | 
| 356 356 |  | 
| 357 357 | 
             
              object_type_definition:
         | 
| 358 | 
            -
                  description_opt TYPE name implements_opt directives_list_opt  | 
| 359 | 
            -
                    result = make_node(:ObjectTypeDefinition, name: val[2], interfaces: val[3], directives: val[4], fields: val[ | 
| 358 | 
            +
                  description_opt TYPE name implements_opt directives_list_opt field_definition_list_opt {
         | 
| 359 | 
            +
                    result = make_node(:ObjectTypeDefinition, name: val[2], interfaces: val[3], directives: val[4], fields: val[5], description: val[0] || get_description(val[1]), definition_line: val[1].line, position_source: val[0] || val[1])
         | 
| 360 360 | 
             
                  }
         | 
| 361 361 |  | 
| 362 362 | 
             
              implements_opt:
         | 
| @@ -394,14 +394,18 @@ rule | |
| 394 394 | 
             
                    result = make_node(:FieldDefinition, name: val[1], arguments: val[2], type: val[4], directives: val[5], description: val[0] || get_description(val[1]), definition_line: val[1].line, position_source: val[0] || val[1])
         | 
| 395 395 | 
             
                  }
         | 
| 396 396 |  | 
| 397 | 
            -
               | 
| 397 | 
            +
              field_definition_list_opt:
         | 
| 398 398 | 
             
                /* none */ { result = EMPTY_ARRAY }
         | 
| 399 | 
            +
                | LCURLY field_definition_list RCURLY { result = val[1] }
         | 
| 400 | 
            +
             | 
| 401 | 
            +
              field_definition_list:
         | 
| 402 | 
            +
                /* none - this is not actually valid but graphql-ruby used to print this */ { result = EMPTY_ARRAY }
         | 
| 399 403 | 
             
                | field_definition                       { result = [val[0]] }
         | 
| 400 404 | 
             
                | field_definition_list field_definition { val[0] << val[1] }
         | 
| 401 405 |  | 
| 402 406 | 
             
              interface_type_definition:
         | 
| 403 | 
            -
                  description_opt INTERFACE name implements_opt directives_list_opt  | 
| 404 | 
            -
                    result = make_node(:InterfaceTypeDefinition, name: val[2], interfaces: val[3], directives: val[4], fields: val[ | 
| 407 | 
            +
                  description_opt INTERFACE name implements_opt directives_list_opt field_definition_list_opt {
         | 
| 408 | 
            +
                    result = make_node(:InterfaceTypeDefinition, name: val[2], interfaces: val[3], directives: val[4], fields: val[5], description: val[0] || get_description(val[1]), definition_line: val[1].line, position_source: val[0] || val[1])
         | 
| 405 409 | 
             
                  }
         | 
| 406 410 |  | 
| 407 411 | 
             
              union_members:
         | 
| @@ -267,12 +267,16 @@ module GraphQL | |
| 267 267 | 
             
                  end
         | 
| 268 268 |  | 
| 269 269 | 
             
                  def print_field_definitions(fields)
         | 
| 270 | 
            -
                     | 
| 271 | 
            -
             | 
| 272 | 
            -
             | 
| 273 | 
            -
                      out  | 
| 270 | 
            +
                    if fields.empty?
         | 
| 271 | 
            +
                      ""
         | 
| 272 | 
            +
                    else
         | 
| 273 | 
            +
                      out = " {\n".dup
         | 
| 274 | 
            +
                      fields.each.with_index do |field, i|
         | 
| 275 | 
            +
                        out << print_description(field, indent: '  ', first_in_block: i == 0)
         | 
| 276 | 
            +
                        out << "  #{print_field_definition(field)}\n"
         | 
| 277 | 
            +
                      end
         | 
| 278 | 
            +
                      out << "}"
         | 
| 274 279 | 
             
                    end
         | 
| 275 | 
            -
                    out << "}"
         | 
| 276 280 | 
             
                  end
         | 
| 277 281 |  | 
| 278 282 | 
             
                  def print_directives(directives)
         | 
| @@ -56,8 +56,9 @@ module GraphQL | |
| 56 56 | 
             
                  # @param last [Integer, nil] Limit parameter from the client, if provided
         | 
| 57 57 | 
             
                  # @param before [String, nil] A cursor for pagination, if the client provided one.
         | 
| 58 58 | 
             
                  # @param arguments [Hash] The arguments to the field that returned the collection wrapped by this connection
         | 
| 59 | 
            -
                  # @param max_page_size [Integer, nil] A configured value to cap the result size. Applied as `first` if neither first or last are given.
         | 
| 60 | 
            -
                   | 
| 59 | 
            +
                  # @param max_page_size [Integer, nil] A configured value to cap the result size. Applied as `first` if neither first or last are given and no `default_page_size` is set.
         | 
| 60 | 
            +
                  # @param default_page_size [Integer, nil] A configured value to determine the result size when neither first or last are given.
         | 
| 61 | 
            +
                  def initialize(items, parent: nil, field: nil, context: nil, first: nil, after: nil, max_page_size: :not_given, default_page_size: :not_given, last: nil, before: nil, edge_class: nil, arguments: nil)
         | 
| 61 62 | 
             
                    @items = items
         | 
| 62 63 | 
             
                    @parent = parent
         | 
| 63 64 | 
             
                    @context = context
         | 
| @@ -76,6 +77,12 @@ module GraphQL | |
| 76 77 | 
             
                    else
         | 
| 77 78 | 
             
                      max_page_size
         | 
| 78 79 | 
             
                    end
         | 
| 80 | 
            +
                    @has_default_page_size_override = default_page_size != :not_given
         | 
| 81 | 
            +
                    @default_page_size = if default_page_size == :not_given
         | 
| 82 | 
            +
                      nil
         | 
| 83 | 
            +
                    else
         | 
| 84 | 
            +
                      default_page_size
         | 
| 85 | 
            +
                    end
         | 
| 79 86 | 
             
                  end
         | 
| 80 87 |  | 
| 81 88 | 
             
                  def max_page_size=(new_value)
         | 
| @@ -95,16 +102,36 @@ module GraphQL | |
| 95 102 | 
             
                    @has_max_page_size_override
         | 
| 96 103 | 
             
                  end
         | 
| 97 104 |  | 
| 105 | 
            +
                  def default_page_size=(new_value)
         | 
| 106 | 
            +
                    @has_default_page_size_override = true
         | 
| 107 | 
            +
                    @default_page_size = new_value
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  def default_page_size
         | 
| 111 | 
            +
                    if @has_default_page_size_override
         | 
| 112 | 
            +
                      @default_page_size
         | 
| 113 | 
            +
                    else
         | 
| 114 | 
            +
                      context.schema.default_page_size
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  def has_default_page_size_override?
         | 
| 119 | 
            +
                    @has_default_page_size_override
         | 
| 120 | 
            +
                  end
         | 
| 121 | 
            +
             | 
| 98 122 | 
             
                  attr_writer :first
         | 
| 99 123 | 
             
                  # @return [Integer, nil]
         | 
| 100 124 | 
             
                  #   A clamped `first` value.
         | 
| 101 125 | 
             
                  #   (The underlying instance variable doesn't have limits on it.)
         | 
| 102 | 
            -
                  #   If neither `first` nor `last` is given, but ` | 
| 126 | 
            +
                  #   If neither `first` nor `last` is given, but `default_page_size` is
         | 
| 127 | 
            +
                  #   present, default_page_size is used for first. If `default_page_size`
         | 
| 128 | 
            +
                  #   is greater than `max_page_size``, it'll be clamped down to
         | 
| 129 | 
            +
                  #   `max_page_size`. If `default_page_size` is nil, use `max_page_size`.
         | 
| 103 130 | 
             
                  def first
         | 
| 104 131 | 
             
                    @first ||= begin
         | 
| 105 132 | 
             
                      capped = limit_pagination_argument(@first_value, max_page_size)
         | 
| 106 133 | 
             
                      if capped.nil? && last.nil?
         | 
| 107 | 
            -
                        capped = max_page_size
         | 
| 134 | 
            +
                        capped = limit_pagination_argument(default_page_size, max_page_size) || max_page_size
         | 
| 108 135 | 
             
                      end
         | 
| 109 136 | 
             
                      capped
         | 
| 110 137 | 
             
                    end
         | 
| @@ -70,6 +70,7 @@ module GraphQL | |
| 70 70 | 
             
                        parent: parent,
         | 
| 71 71 | 
             
                        field: field,
         | 
| 72 72 | 
             
                        max_page_size: field.has_max_page_size? ? field.max_page_size : context.schema.default_max_page_size,
         | 
| 73 | 
            +
                        default_page_size: field.has_default_page_size? ? field.default_page_size : context.schema.default_page_size,
         | 
| 73 74 | 
             
                        first: arguments[:first],
         | 
| 74 75 | 
             
                        after: arguments[:after],
         | 
| 75 76 | 
             
                        last: arguments[:last],
         | 
| @@ -47,6 +47,9 @@ module GraphQL | |
| 47 47 | 
             
                          if field.has_max_page_size? && !value.has_max_page_size_override?
         | 
| 48 48 | 
             
                            value.max_page_size = field.max_page_size
         | 
| 49 49 | 
             
                          end
         | 
| 50 | 
            +
                          if field.has_default_page_size? && !value.has_default_page_size_override?
         | 
| 51 | 
            +
                            value.default_page_size = field.default_page_size
         | 
| 52 | 
            +
                          end
         | 
| 50 53 | 
             
                          if context.schema.new_connections? && (custom_t = context.schema.connections.edge_class_for_field(@field))
         | 
| 51 54 | 
             
                            value.edge_class = custom_t
         | 
| 52 55 | 
             
                          end
         | 
| @@ -64,6 +67,7 @@ module GraphQL | |
| 64 67 | 
             
                            original_arguments,
         | 
| 65 68 | 
             
                            field: field,
         | 
| 66 69 | 
             
                            max_page_size: field.max_page_size,
         | 
| 70 | 
            +
                            default_page_size: field.default_page_size,
         | 
| 67 71 | 
             
                            parent: object,
         | 
| 68 72 | 
             
                            context: context,
         | 
| 69 73 | 
             
                          )
         | 
    
        data/lib/graphql/schema/field.rb
    CHANGED
    
    | @@ -200,6 +200,7 @@ module GraphQL | |
| 200 200 | 
             
                  # @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
         | 
| 201 201 | 
             
                  # @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
         | 
| 202 202 | 
             
                  # @param max_page_size [Integer, nil] For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results.
         | 
| 203 | 
            +
                  # @param default_page_size [Integer, nil] For connections, the default number of items to return from this field, or `nil` to return unlimited results.
         | 
| 203 204 | 
             
                  # @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
         | 
| 204 205 | 
             
                  # @param resolver_class [Class] (Private) A {Schema::Resolver} which this field was derived from. Use `resolver:` to create a field with a resolver.
         | 
| 205 206 | 
             
                  # @param arguments [{String=>GraphQL::Schema::Argument, Hash}] Arguments for this field (may be added in the block, also)
         | 
| @@ -214,7 +215,8 @@ module GraphQL | |
| 214 215 | 
             
                  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
         | 
| 215 216 | 
             
                  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
         | 
| 216 217 | 
             
                  # @param validates [Array<Hash>] Configurations for validating this field
         | 
| 217 | 
            -
                   | 
| 218 | 
            +
                  # @fallback_value [Object] A fallback value if the method is not defined
         | 
| 219 | 
            +
                  def initialize(type: nil, name: nil, owner: nil, null: nil, description: :not_given, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: :not_given, default_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: :not_given, &definition_block)
         | 
| 218 220 | 
             
                    if name.nil?
         | 
| 219 221 | 
             
                      raise ArgumentError, "missing first `name` argument or keyword `name:`"
         | 
| 220 222 | 
             
                    end
         | 
| @@ -246,9 +248,12 @@ module GraphQL | |
| 246 248 | 
             
                      end
         | 
| 247 249 | 
             
                    end
         | 
| 248 250 |  | 
| 249 | 
            -
                    method_name = method || name_s
         | 
| 251 | 
            +
                    method_name = method || hash_key || name_s
         | 
| 250 252 | 
             
                    @dig_keys = dig
         | 
| 251 | 
            -
                     | 
| 253 | 
            +
                    if hash_key
         | 
| 254 | 
            +
                      @hash_key = hash_key
         | 
| 255 | 
            +
                      @hash_key_str = hash_key.to_s
         | 
| 256 | 
            +
                    end
         | 
| 252 257 |  | 
| 253 258 | 
             
                    @method_str = -method_name.to_s
         | 
| 254 259 | 
             
                    @method_sym = method_name.to_sym
         | 
| @@ -265,6 +270,8 @@ module GraphQL | |
| 265 270 | 
             
                    @connection = connection
         | 
| 266 271 | 
             
                    @has_max_page_size = max_page_size != :not_given
         | 
| 267 272 | 
             
                    @max_page_size = max_page_size == :not_given ? nil : max_page_size
         | 
| 273 | 
            +
                    @has_default_page_size = default_page_size != :not_given
         | 
| 274 | 
            +
                    @default_page_size = default_page_size == :not_given ? nil : default_page_size
         | 
| 268 275 | 
             
                    @introspection = introspection
         | 
| 269 276 | 
             
                    @extras = extras
         | 
| 270 277 | 
             
                    if !broadcastable.nil?
         | 
| @@ -277,6 +284,7 @@ module GraphQL | |
| 277 284 | 
             
                    @relay_nodes_field = relay_nodes_field
         | 
| 278 285 | 
             
                    @ast_node = ast_node
         | 
| 279 286 | 
             
                    @method_conflict_warning = method_conflict_warning
         | 
| 287 | 
            +
                    @fallback_value = fallback_value
         | 
| 280 288 |  | 
| 281 289 | 
             
                    arguments.each do |name, arg|
         | 
| 282 290 | 
             
                      case arg
         | 
| @@ -459,11 +467,11 @@ module GraphQL | |
| 459 467 | 
             
                      end
         | 
| 460 468 |  | 
| 461 469 | 
             
                      if max_possible_page_size.nil?
         | 
| 462 | 
            -
                        max_possible_page_size = max_page_size || query.schema.default_max_page_size
         | 
| 470 | 
            +
                        max_possible_page_size = default_page_size || query.schema.default_page_size || max_page_size || query.schema.default_max_page_size
         | 
| 463 471 | 
             
                      end
         | 
| 464 472 |  | 
| 465 473 | 
             
                      if max_possible_page_size.nil?
         | 
| 466 | 
            -
                        raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `max_page_size` or `default_max_page_size`"
         | 
| 474 | 
            +
                        raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `default_page_size`, `max_page_size` or `default_max_page_size`"
         | 
| 467 475 | 
             
                      else
         | 
| 468 476 | 
             
                        metadata_complexity = 0
         | 
| 469 477 | 
             
                        lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
         | 
| @@ -491,7 +499,13 @@ module GraphQL | |
| 491 499 | 
             
                      case defined_complexity
         | 
| 492 500 | 
             
                      when Proc
         | 
| 493 501 | 
             
                        arguments = query.arguments_for(nodes.first, self)
         | 
| 494 | 
            -
                         | 
| 502 | 
            +
                        if arguments.is_a?(GraphQL::ExecutionError)
         | 
| 503 | 
            +
                          return child_complexity
         | 
| 504 | 
            +
                        elsif arguments.respond_to?(:keyword_arguments)
         | 
| 505 | 
            +
                          arguments = arguments.keyword_arguments
         | 
| 506 | 
            +
                        end
         | 
| 507 | 
            +
             | 
| 508 | 
            +
                        defined_complexity.call(query.context, arguments, child_complexity)
         | 
| 495 509 | 
             
                      when Numeric
         | 
| 496 510 | 
             
                        defined_complexity + child_complexity
         | 
| 497 511 | 
             
                      else
         | 
| @@ -534,6 +548,16 @@ module GraphQL | |
| 534 548 | 
             
                    @max_page_size || (@resolver_class && @resolver_class.max_page_size)
         | 
| 535 549 | 
             
                  end
         | 
| 536 550 |  | 
| 551 | 
            +
                  # @return [Boolean] True if this field's {#default_page_size} should override the schema default.
         | 
| 552 | 
            +
                  def has_default_page_size?
         | 
| 553 | 
            +
                    @has_default_page_size || (@resolver_class && @resolver_class.has_default_page_size?)
         | 
| 554 | 
            +
                  end
         | 
| 555 | 
            +
             | 
| 556 | 
            +
                  # @return [Integer, nil] Applied to connections if {#has_default_page_size?}
         | 
| 557 | 
            +
                  def default_page_size
         | 
| 558 | 
            +
                    @default_page_size || (@resolver_class && @resolver_class.default_page_size)
         | 
| 559 | 
            +
                  end
         | 
| 560 | 
            +
             | 
| 537 561 | 
             
                  class MissingReturnTypeError < GraphQL::Error; end
         | 
| 538 562 | 
             
                  attr_writer :type
         | 
| 539 563 |  | 
| @@ -639,10 +663,8 @@ module GraphQL | |
| 639 663 |  | 
| 640 664 | 
             
                          inner_object = obj.object
         | 
| 641 665 |  | 
| 642 | 
            -
                          if @hash_key
         | 
| 643 | 
            -
                            inner_object[@hash_key]
         | 
| 644 | 
            -
                          elsif @dig_keys
         | 
| 645 | 
            -
                            inner_object.dig(*@dig_keys)
         | 
| 666 | 
            +
                          if defined?(@hash_key)
         | 
| 667 | 
            +
                            inner_object[@hash_key] || inner_object[@hash_key_str] || (@fallback_value != :not_given ? @fallback_value : nil)
         | 
| 646 668 | 
             
                          elsif obj.respond_to?(resolver_method)
         | 
| 647 669 | 
             
                            method_to_call = resolver_method
         | 
| 648 670 | 
             
                            method_receiver = obj
         | 
| @@ -653,10 +675,26 @@ module GraphQL | |
| 653 675 | 
             
                              obj.public_send(resolver_method)
         | 
| 654 676 | 
             
                            end
         | 
| 655 677 | 
             
                          elsif inner_object.is_a?(Hash)
         | 
| 656 | 
            -
                            if  | 
| 678 | 
            +
                            if @dig_keys
         | 
| 679 | 
            +
                              inner_object.dig(*@dig_keys)
         | 
| 680 | 
            +
                            elsif defined?(@hash_key)
         | 
| 681 | 
            +
                              if inner_object.key?(@hash_key)
         | 
| 682 | 
            +
                                inner_object[@hash_key]
         | 
| 683 | 
            +
                              elsif inner_object.key?(@hash_key_str)
         | 
| 684 | 
            +
                                inner_object[@hash_key_str]
         | 
| 685 | 
            +
                              elsif @fallback_value != :not_given
         | 
| 686 | 
            +
                                @fallback_value
         | 
| 687 | 
            +
                              else
         | 
| 688 | 
            +
                                nil
         | 
| 689 | 
            +
                              end
         | 
| 690 | 
            +
                            elsif inner_object.key?(@method_sym)
         | 
| 657 691 | 
             
                              inner_object[@method_sym]
         | 
| 658 | 
            -
                             | 
| 692 | 
            +
                            elsif inner_object.key?(@method_str)
         | 
| 659 693 | 
             
                              inner_object[@method_str]
         | 
| 694 | 
            +
                            elsif @fallback_value != :not_given
         | 
| 695 | 
            +
                              @fallback_value
         | 
| 696 | 
            +
                            else
         | 
| 697 | 
            +
                              nil
         | 
| 660 698 | 
             
                            end
         | 
| 661 699 | 
             
                          elsif inner_object.respond_to?(@method_sym)
         | 
| 662 700 | 
             
                            method_to_call = @method_sym
         | 
| @@ -666,6 +704,8 @@ module GraphQL | |
| 666 704 | 
             
                            else
         | 
| 667 705 | 
             
                              inner_object.public_send(@method_sym)
         | 
| 668 706 | 
             
                            end
         | 
| 707 | 
            +
                          elsif @fallback_value != :not_given
         | 
| 708 | 
            +
                            @fallback_value
         | 
| 669 709 | 
             
                          else
         | 
| 670 710 | 
             
                            raise <<-ERR
         | 
| 671 711 | 
             
                          Failed to implement #{@owner.graphql_name}.#{@name}, tried:
         | 
| @@ -674,7 +714,7 @@ module GraphQL | |
| 674 714 | 
             
                          - `#{inner_object.class}##{@method_sym}`, which did not exist
         | 
| 675 715 | 
             
                          - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{inner_object}`, but it wasn't a Hash
         | 
| 676 716 |  | 
| 677 | 
            -
                          To implement this field, define one of the methods above (and check for typos)
         | 
| 717 | 
            +
                          To implement this field, define one of the methods above (and check for typos), or supply a `fallback_value`.
         | 
| 678 718 | 
             
                          ERR
         | 
| 679 719 | 
             
                          end
         | 
| 680 720 | 
             
                        end
         | 
| @@ -746,7 +786,7 @@ module GraphQL | |
| 746 786 |  | 
| 747 787 | 
             
                    if unsatisfied_ruby_kwargs.any? || unsatisfied_method_params.any?
         | 
| 748 788 | 
             
                      raise FieldImplementationFailed.new, <<-ERR
         | 
| 749 | 
            -
            Failed to call  | 
| 789 | 
            +
            Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
         | 
| 750 790 |  | 
| 751 791 | 
             
            #{ unsatisfied_ruby_kwargs
         | 
| 752 792 | 
             
                .map { |key, value| "- `#{key}: #{value}` was given by GraphQL but not defined in the Ruby method. Add `#{key}:` to the method parameters." }
         | 
| @@ -328,6 +328,27 @@ module GraphQL | |
| 328 328 | 
             
                      (!!defined?(@max_page_size)) || (superclass.respond_to?(:has_max_page_size?) && superclass.has_max_page_size?)
         | 
| 329 329 | 
             
                    end
         | 
| 330 330 |  | 
| 331 | 
            +
                    # Get or set the `default_page_size:` which will be configured for fields using this resolver
         | 
| 332 | 
            +
                    # (`nil` means "unlimited default page size".)
         | 
| 333 | 
            +
                    # @param default_page_size [Integer, nil] Set a new value
         | 
| 334 | 
            +
                    # @return [Integer, nil] The `default_page_size` assigned to fields that use this resolver
         | 
| 335 | 
            +
                    def default_page_size(new_default_page_size = :not_given)
         | 
| 336 | 
            +
                      if new_default_page_size != :not_given
         | 
| 337 | 
            +
                        @default_page_size = new_default_page_size
         | 
| 338 | 
            +
                      elsif defined?(@default_page_size)
         | 
| 339 | 
            +
                        @default_page_size
         | 
| 340 | 
            +
                      elsif superclass.respond_to?(:default_page_size)
         | 
| 341 | 
            +
                        superclass.default_page_size
         | 
| 342 | 
            +
                      else
         | 
| 343 | 
            +
                        nil
         | 
| 344 | 
            +
                      end
         | 
| 345 | 
            +
                    end
         | 
| 346 | 
            +
             | 
| 347 | 
            +
                    # @return [Boolean] `true` if this resolver or a superclass has an assigned `default_page_size`
         | 
| 348 | 
            +
                    def has_default_page_size?
         | 
| 349 | 
            +
                      (!!defined?(@default_page_size)) || (superclass.respond_to?(:has_default_page_size?) && superclass.has_default_page_size?)
         | 
| 350 | 
            +
                    end
         | 
| 351 | 
            +
             | 
| 331 352 | 
             
                    # A non-normalized type configuration, without `null` applied
         | 
| 332 353 | 
             
                    def type_expr
         | 
| 333 354 | 
             
                      @type_expr || (superclass.respond_to?(:type_expr) ? superclass.type_expr : nil)
         | 
| @@ -55,7 +55,9 @@ module GraphQL | |
| 55 55 | 
             
                          if visible_item.nil?
         | 
| 56 56 | 
             
                            visible_item = item
         | 
| 57 57 | 
             
                          else
         | 
| 58 | 
            -
                            raise  | 
| 58 | 
            +
                            raise DuplicateNamesError.new(
         | 
| 59 | 
            +
                              duplicated_name: item.path, duplicated_definition_1: visible_item.inspect, duplicated_definition_2: item.inspect
         | 
| 60 | 
            +
                            )
         | 
| 59 61 | 
             
                          end
         | 
| 60 62 | 
             
                        end
         | 
| 61 63 | 
             
                      end
         | 
| @@ -362,7 +364,9 @@ module GraphQL | |
| 362 364 | 
             
                      if @reachable_type_set.add?(type)
         | 
| 363 365 | 
             
                        type_by_name = rt_hash[type.graphql_name] ||= type
         | 
| 364 366 | 
             
                        if type_by_name != type
         | 
| 365 | 
            -
                          raise DuplicateNamesError | 
| 367 | 
            +
                          raise DuplicateNamesError.new(
         | 
| 368 | 
            +
                            duplicated_name: type.graphql_name, duplicated_definition_1: type.inspect, duplicated_definition_2: type_by_name.inspect
         | 
| 369 | 
            +
                          )
         | 
| 366 370 | 
             
                        end
         | 
| 367 371 | 
             
                        if type.kind.input_object?
         | 
| 368 372 | 
             
                          # recurse into visible arguments
         | 
    
        data/lib/graphql/schema.rb
    CHANGED
    
    | @@ -73,14 +73,16 @@ module GraphQL | |
| 73 73 | 
             
                extend GraphQL::Schema::Member::HasAstNode
         | 
| 74 74 | 
             
                extend GraphQL::Schema::FindInheritedValue
         | 
| 75 75 |  | 
| 76 | 
            -
                class  | 
| 77 | 
            -
                   | 
| 78 | 
            -
             | 
| 76 | 
            +
                class DuplicateNamesError < GraphQL::Error
         | 
| 77 | 
            +
                  attr_reader :duplicated_name
         | 
| 78 | 
            +
                  def initialize(duplicated_name:, duplicated_definition_1:, duplicated_definition_2:)
         | 
| 79 | 
            +
                    @duplicated_name = duplicated_name
         | 
| 80 | 
            +
                    super(
         | 
| 81 | 
            +
                      "Found two visible definitions for `#{duplicated_name}`: #{duplicated_definition_1}, #{duplicated_definition_2}"
         | 
| 82 | 
            +
                    )
         | 
| 79 83 | 
             
                  end
         | 
| 80 84 | 
             
                end
         | 
| 81 85 |  | 
| 82 | 
            -
                class DuplicateNamesError < GraphQL::Error; end
         | 
| 83 | 
            -
             | 
| 84 86 | 
             
                class UnresolvedLateBoundTypeError < GraphQL::Error
         | 
| 85 87 | 
             
                  attr_reader :type
         | 
| 86 88 | 
             
                  def initialize(type:)
         | 
| @@ -225,7 +227,9 @@ module GraphQL | |
| 225 227 | 
             
                            if visible_t.nil?
         | 
| 226 228 | 
             
                              visible_t = t
         | 
| 227 229 | 
             
                            else
         | 
| 228 | 
            -
                              raise DuplicateNamesError | 
| 230 | 
            +
                              raise DuplicateNamesError.new(
         | 
| 231 | 
            +
                                duplicated_name: k, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect
         | 
| 232 | 
            +
                              )
         | 
| 229 233 | 
             
                            end
         | 
| 230 234 | 
             
                          end
         | 
| 231 235 | 
             
                        end
         | 
| @@ -252,7 +256,9 @@ module GraphQL | |
| 252 256 | 
             
                          if visible_t.nil?
         | 
| 253 257 | 
             
                            visible_t = t
         | 
| 254 258 | 
             
                          else
         | 
| 255 | 
            -
                            raise DuplicateNamesError | 
| 259 | 
            +
                            raise DuplicateNamesError.new(
         | 
| 260 | 
            +
                              duplicated_name: type_name, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect
         | 
| 261 | 
            +
                            )
         | 
| 256 262 | 
             
                          end
         | 
| 257 263 | 
             
                        end
         | 
| 258 264 | 
             
                      end
         | 
| @@ -500,6 +506,14 @@ module GraphQL | |
| 500 506 | 
             
                    end
         | 
| 501 507 | 
             
                  end
         | 
| 502 508 |  | 
| 509 | 
            +
                  def default_page_size(new_default_page_size = nil)
         | 
| 510 | 
            +
                    if new_default_page_size
         | 
| 511 | 
            +
                      @default_page_size = new_default_page_size
         | 
| 512 | 
            +
                    else
         | 
| 513 | 
            +
                      @default_page_size || find_inherited_value(:default_page_size)
         | 
| 514 | 
            +
                    end
         | 
| 515 | 
            +
                  end
         | 
| 516 | 
            +
             | 
| 503 517 | 
             
                  def query_execution_strategy(new_query_execution_strategy = nil)
         | 
| 504 518 | 
             
                    if new_query_execution_strategy
         | 
| 505 519 | 
             
                      @query_execution_strategy = new_query_execution_strategy
         | 
| @@ -977,7 +991,9 @@ module GraphQL | |
| 977 991 | 
             
                    if !defined?(@subscription_extension_added) && subscription && self.subscriptions
         | 
| 978 992 | 
             
                      @subscription_extension_added = true
         | 
| 979 993 | 
             
                      subscription.all_field_definitions.each do |field|
         | 
| 980 | 
            -
                        field. | 
| 994 | 
            +
                        if !field.extensions.any? { |ext| ext.is_a?(Subscriptions::DefaultSubscriptionResolveExtension) }
         | 
| 995 | 
            +
                          field.extension(Subscriptions::DefaultSubscriptionResolveExtension)
         | 
| 996 | 
            +
                        end
         | 
| 981 997 | 
             
                      end
         | 
| 982 998 | 
             
                    end
         | 
| 983 999 | 
             
                  end
         | 
| @@ -39,12 +39,14 @@ module GraphQL | |
| 39 39 | 
             
                end
         | 
| 40 40 |  | 
| 41 41 | 
             
                # @param schema [Class] the GraphQL schema this manager belongs to
         | 
| 42 | 
            -
                 | 
| 42 | 
            +
                # @param validate_update [Boolean] If false, then validation is skipped when executing updates
         | 
| 43 | 
            +
                def initialize(schema:, validate_update: true, broadcast: false, default_broadcastable: false, **rest)
         | 
| 43 44 | 
             
                  if broadcast
         | 
| 44 45 | 
             
                    schema.query_analyzer(Subscriptions::BroadcastAnalyzer)
         | 
| 45 46 | 
             
                  end
         | 
| 46 47 | 
             
                  @default_broadcastable = default_broadcastable
         | 
| 47 48 | 
             
                  @schema = schema
         | 
| 49 | 
            +
                  @validate_update = validate_update
         | 
| 48 50 | 
             
                end
         | 
| 49 51 |  | 
| 50 52 | 
             
                # @return [Boolean] Used when fields don't have `broadcastable:` explicitly set
         | 
| @@ -117,14 +119,16 @@ module GraphQL | |
| 117 119 | 
             
                  variables = query_data.fetch(:variables)
         | 
| 118 120 | 
             
                  context = query_data.fetch(:context)
         | 
| 119 121 | 
             
                  operation_name = query_data.fetch(:operation_name)
         | 
| 120 | 
            -
                   | 
| 122 | 
            +
                  execute_options = {
         | 
| 121 123 | 
             
                    query: query_string,
         | 
| 122 124 | 
             
                    context: context,
         | 
| 123 125 | 
             
                    subscription_topic: event.topic,
         | 
| 124 126 | 
             
                    operation_name: operation_name,
         | 
| 125 127 | 
             
                    variables: variables,
         | 
| 126 128 | 
             
                    root_value: object,
         | 
| 127 | 
            -
                   | 
| 129 | 
            +
                  }
         | 
| 130 | 
            +
                  execute_options[:validate] = validate_update?(**execute_options)
         | 
| 131 | 
            +
                  result = @schema.execute(**execute_options)
         | 
| 128 132 | 
             
                  subscriptions_context = result.context.namespace(:subscriptions)
         | 
| 129 133 | 
             
                  if subscriptions_context[:no_update]
         | 
| 130 134 | 
             
                    result = nil
         | 
| @@ -142,6 +146,14 @@ module GraphQL | |
| 142 146 | 
             
                  result
         | 
| 143 147 | 
             
                end
         | 
| 144 148 |  | 
| 149 | 
            +
                # Define this method to customize whether to validate
         | 
| 150 | 
            +
                # this subscription when executing an update.
         | 
| 151 | 
            +
                #
         | 
| 152 | 
            +
                # @return [Boolean] defaults to `true`, or false if `validate: false` is provided.
         | 
| 153 | 
            +
                def validate_update?(query:, context:, root_value:, subscription_topic:, operation_name:, variables:)
         | 
| 154 | 
            +
                  @validate_update
         | 
| 155 | 
            +
                end
         | 
| 156 | 
            +
             | 
| 145 157 | 
             
                # Run the update query for this subscription and deliver it
         | 
| 146 158 | 
             
                # @see {#execute_update}
         | 
| 147 159 | 
             
                # @see {#deliver}
         | 
| @@ -17,15 +17,21 @@ module GraphQL | |
| 17 17 | 
             
                  def platform_trace(platform_key, key, data)
         | 
| 18 18 | 
             
                    tracer.trace(platform_key, service: service_name) do |span|
         | 
| 19 19 | 
             
                      span.span_type = 'custom'
         | 
| 20 | 
            +
                      if defined?(Datadog::Tracing::Metadata::Ext) # Introduced in ddtrace 1.0
         | 
| 21 | 
            +
                        span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_COMPONENT, 'graphql')
         | 
| 22 | 
            +
                        span.set_tag(Datadog::Tracing::Metadata::Ext::TAG_OPERATION, key)
         | 
| 23 | 
            +
                      end
         | 
| 20 24 |  | 
| 21 25 | 
             
                      if key == 'execute_multiplex'
         | 
| 22 26 | 
             
                        operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
         | 
| 23 | 
            -
             | 
| 27 | 
            +
             | 
| 28 | 
            +
                        resource = if operations.empty?
         | 
| 24 29 | 
             
                          first_query = data[:multiplex].queries.first
         | 
| 25 30 | 
             
                          fallback_transaction_name(first_query && first_query.context)
         | 
| 26 31 | 
             
                        else
         | 
| 27 32 | 
             
                          operations
         | 
| 28 33 | 
             
                        end
         | 
| 34 | 
            +
                        span.resource = resource if resource
         | 
| 29 35 |  | 
| 30 36 | 
             
                        # For top span of query, set the analytics sample rate tag, if available.
         | 
| 31 37 | 
             
                        if analytics_enabled?
         | 
| @@ -39,6 +45,8 @@ module GraphQL | |
| 39 45 | 
             
                        span.set_tag(:query_string, data[:query].query_string)
         | 
| 40 46 | 
             
                      end
         | 
| 41 47 |  | 
| 48 | 
            +
                      prepare_span(key, data, span)
         | 
| 49 | 
            +
             | 
| 42 50 | 
             
                      yield
         | 
| 43 51 | 
             
                    end
         | 
| 44 52 | 
             
                  end
         | 
| @@ -47,6 +55,13 @@ module GraphQL | |
| 47 55 | 
             
                    options.fetch(:service, 'ruby-graphql')
         | 
| 48 56 | 
             
                  end
         | 
| 49 57 |  | 
| 58 | 
            +
                  # Implement this method in a subclass to apply custom tags to datadog spans
         | 
| 59 | 
            +
                  # @param key [String] The event being traced
         | 
| 60 | 
            +
                  # @param data [Hash] The runtime data for this event (@see GraphQL::Tracing for keys for each event)
         | 
| 61 | 
            +
                  # @param span [Datadog::Tracing::SpanOperation] The datadog span for this event
         | 
| 62 | 
            +
                  def prepare_span(key, data, span)
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 50 65 | 
             
                  def tracer
         | 
| 51 66 | 
             
                    default_tracer = defined?(Datadog::Tracing) ? Datadog::Tracing : Datadog.tracer
         | 
| 52 67 |  | 
| @@ -0,0 +1,101 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GraphQL
         | 
| 4 | 
            +
              module Tracing
         | 
| 5 | 
            +
                class OpenTelemetryTracing < PlatformTracing
         | 
| 6 | 
            +
                  self.platform_keys = {
         | 
| 7 | 
            +
                    'lex' => 'graphql.lex',
         | 
| 8 | 
            +
                    'parse' => 'graphql.parse',
         | 
| 9 | 
            +
                    'validate' => 'graphql.validate',
         | 
| 10 | 
            +
                    'analyze_query' => 'graphql.analyze_query',
         | 
| 11 | 
            +
                    'analyze_multiplex' => 'graphql.analyze_multiplex',
         | 
| 12 | 
            +
                    'execute_query' => 'graphql.execute_query',
         | 
| 13 | 
            +
                    'execute_query_lazy' => 'graphql.execute_query_lazy',
         | 
| 14 | 
            +
                    'execute_multiplex' => 'graphql.execute_multiplex'
         | 
| 15 | 
            +
                  }
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def platform_trace(platform_key, key, data)
         | 
| 18 | 
            +
                    return yield if platform_key.nil?
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    tracer.in_span(platform_key, attributes: attributes_for(key, data)) do |span, _context|
         | 
| 21 | 
            +
                      yield.tap do |response|
         | 
| 22 | 
            +
                        errors = response[:errors]&.compact&.map { |e| e.to_h }&.to_json if key == 'validate'
         | 
| 23 | 
            +
                        unless errors.nil?
         | 
| 24 | 
            +
                          span.add_event(
         | 
| 25 | 
            +
                            'graphql.validation.error',
         | 
| 26 | 
            +
                            attributes: {
         | 
| 27 | 
            +
                              'message' => errors
         | 
| 28 | 
            +
                            }
         | 
| 29 | 
            +
                          )
         | 
| 30 | 
            +
                        end
         | 
| 31 | 
            +
                      end
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  def platform_field_key(type, field)
         | 
| 36 | 
            +
                    "#{type.graphql_name}.#{field.graphql_name}"
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  def platform_authorized_key(type)
         | 
| 40 | 
            +
                    "#{type.graphql_name}.authorized"
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  def platform_resolve_type_key(type)
         | 
| 44 | 
            +
                    "#{type.graphql_name}.resolve_type"
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  private
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  def tracer
         | 
| 50 | 
            +
                    OpenTelemetry::Instrumentation::GraphQL::Instrumentation.instance.tracer
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  def config
         | 
| 54 | 
            +
                    OpenTelemetry::Instrumentation::GraphQL::Instrumentation.instance.config
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def platform_key_enabled?(ctx, key)
         | 
| 58 | 
            +
                    return false unless config[key]
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    ns = ctx.namespace(:opentelemetry)
         | 
| 61 | 
            +
                    return true if ns.empty? # restores original behavior so that keys are returned if tracing is not set in context.
         | 
| 62 | 
            +
                    return false unless ns.key?(key) && ns[key]
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    return true
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  def attributes_for(key, data)
         | 
| 68 | 
            +
                    attributes = {}
         | 
| 69 | 
            +
                    case key
         | 
| 70 | 
            +
                    when 'execute_query'
         | 
| 71 | 
            +
                      attributes['selected_operation_name'] = data[:query].selected_operation_name if data[:query].selected_operation_name
         | 
| 72 | 
            +
                      attributes['selected_operation_type'] = data[:query].selected_operation.operation_type
         | 
| 73 | 
            +
                      attributes['query_string'] = data[:query].query_string
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
                    attributes
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  def cached_platform_key(ctx, key, trace_phase)
         | 
| 79 | 
            +
                    cache = ctx.namespace(self.class)[:platform_key_cache] ||= {}
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    cache.fetch(key) do
         | 
| 82 | 
            +
                      cache[key] = if trace_phase == :field
         | 
| 83 | 
            +
                        return unless platform_key_enabled?(ctx, :enable_platform_field)
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                        yield
         | 
| 86 | 
            +
                      elsif trace_phase == :authorized
         | 
| 87 | 
            +
                        return unless platform_key_enabled?(ctx, :enable_platform_authorized)
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                        yield
         | 
| 90 | 
            +
                      elsif trace_phase == :resolve_type
         | 
| 91 | 
            +
                        return unless platform_key_enabled?(ctx, :enable_platform_resolve_type)
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                        yield
         | 
| 94 | 
            +
                      else
         | 
| 95 | 
            +
                        raise "Unknown trace phase"
         | 
| 96 | 
            +
                      end
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
            end
         |