graphql-stitching 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/docs/composer.md +3 -0
- data/docs/executor.md +2 -2
- data/lib/graphql/stitching/composer.rb +17 -10
- data/lib/graphql/stitching/executor/boundary_source.rb +3 -3
- data/lib/graphql/stitching/{selection_hint.rb → export_selection.rb} +4 -4
- data/lib/graphql/stitching/planner.rb +10 -8
- data/lib/graphql/stitching/request.rb +1 -1
- data/lib/graphql/stitching/shaper.rb +3 -3
- data/lib/graphql/stitching/skip_include.rb +1 -1
- data/lib/graphql/stitching/version.rb +1 -1
- data/lib/graphql/stitching.rb +1 -1
- metadata +3 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 2d90338dfde5c26515a55afdee7f5bd936a4563b2b8f83e1ffc1a4420c56a5cf
         | 
| 4 | 
            +
              data.tar.gz: d728c5dfabb5e0250c38a6718ac7b535cfa7a183872dc60fa95a9b91b777a64a
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: cc5ff912f354e08b9f204028d5e6eed85a81bf283bd5dc13bb7ee9b69b83d0bb39ba83f9fbaac66be1363e86ce3f6bd1b3f7cc4466ef83998a751e3dbccf9013
         | 
| 7 | 
            +
              data.tar.gz: 055e1c09f88e8f893a3ebb0aa3d7558156ec5c4a33b704cca69499efee4fd02e3a70e6efedeca1332f92392c7093ea8024ba9aefb159ce77b4f11510cd8d7a1c
         | 
    
        data/docs/composer.md
    CHANGED
    
    | @@ -12,6 +12,7 @@ composer = GraphQL::Stitching::Composer.new( | |
| 12 12 | 
             
              mutation_name: "Mutation",
         | 
| 13 13 | 
             
              description_merger: ->(values_by_location, info) { values_by_location.values.join("\n") },
         | 
| 14 14 | 
             
              deprecation_merger: ->(values_by_location, info) { values_by_location.values.first },
         | 
| 15 | 
            +
              default_value_merger: ->(values_by_location, info) { values_by_location.values.first },
         | 
| 15 16 | 
             
              directive_kwarg_merger: ->(values_by_location, info) { values_by_location.values.last },
         | 
| 16 17 | 
             
              root_field_location_selector: ->(locations, info) { locations.last },
         | 
| 17 18 | 
             
            )
         | 
| @@ -27,6 +28,8 @@ Constructor arguments: | |
| 27 28 |  | 
| 28 29 | 
             
            - **`deprecation_merger:`** _optional_, a [value merger function](#value-merger-functions) for merging element deprecation strings from across locations.
         | 
| 29 30 |  | 
| 31 | 
            +
            - **`default_value_merger:`** _optional_, a [value merger function](#value-merger-functions) for merging argument default values from across locations.
         | 
| 32 | 
            +
             | 
| 30 33 | 
             
            - **`directive_kwarg_merger:`** _optional_, a [value merger function](#value-merger-functions) for merging directive keyword arguments from across locations.
         | 
| 31 34 |  | 
| 32 35 | 
             
            - **`root_field_location_selector:`** _optional_, selects a default routing location for root fields with multiple locations. Use this to prioritize sending root fields to their primary data sources (only applies while routing the root operation scope). This handler receives an array of possible locations and an info object with field information, and should return the prioritized location. The last location is used by default.
         | 
    
        data/docs/executor.md
    CHANGED
    
    | @@ -50,8 +50,8 @@ The raw result will contain many irregularities from the stitching process, howe | |
| 50 50 | 
             
              "data" => {
         | 
| 51 51 | 
             
                "product" => {
         | 
| 52 52 | 
             
                  "upc" => "1",
         | 
| 53 | 
            -
                  " | 
| 54 | 
            -
                  " | 
| 53 | 
            +
                  "_export_upc" => "1",
         | 
| 54 | 
            +
                  "_export_typename" => "Product",
         | 
| 55 55 | 
             
                  "name" => "iPhone",
         | 
| 56 56 | 
             
                  "price" => nil,
         | 
| 57 57 | 
             
                }
         | 
| @@ -5,9 +5,13 @@ module GraphQL | |
| 5 5 | 
             
                class Composer
         | 
| 6 6 | 
             
                  class ComposerError < StitchingError; end
         | 
| 7 7 | 
             
                  class ValidationError < ComposerError; end
         | 
| 8 | 
            +
                  class ReferenceType < GraphQL::Schema::Object
         | 
| 9 | 
            +
                    field(:f, String) do
         | 
| 10 | 
            +
                      argument(:a, String)
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
                  end
         | 
| 8 13 |  | 
| 9 | 
            -
                   | 
| 10 | 
            -
             | 
| 14 | 
            +
                  NO_DEFAULT_VALUE = ReferenceType.get_field("f").get_argument("a").default_value
         | 
| 11 15 | 
             
                  BASIC_VALUE_MERGER = ->(values_by_location, _info) { values_by_location.values.find { !_1.nil? } }
         | 
| 12 16 | 
             
                  BASIC_ROOT_FIELD_LOCATION_SELECTOR = ->(locations, _info) { locations.last }
         | 
| 13 17 |  | 
| @@ -16,6 +20,8 @@ module GraphQL | |
| 16 20 | 
             
                    "ValidateBoundaries",
         | 
| 17 21 | 
             
                  ].freeze
         | 
| 18 22 |  | 
| 23 | 
            +
                  attr_reader :query_name, :mutation_name, :candidate_types_by_name_and_location, :schema_directives
         | 
| 24 | 
            +
             | 
| 19 25 | 
             
                  def initialize(
         | 
| 20 26 | 
             
                    query_name: "Query",
         | 
| 21 27 | 
             
                    mutation_name: "Mutation",
         | 
| @@ -195,7 +201,7 @@ module GraphQL | |
| 195 201 | 
             
                      description(builder.merge_descriptions(directive_name, directives_by_location))
         | 
| 196 202 | 
             
                      repeatable(directives_by_location.values.any?(&:repeatable?))
         | 
| 197 203 | 
             
                      locations(*directives_by_location.values.flat_map(&:locations).uniq)
         | 
| 198 | 
            -
                      builder.build_merged_arguments(directive_name, directives_by_location, self)
         | 
| 204 | 
            +
                      builder.build_merged_arguments(directive_name, directives_by_location, self, directive_name: directive_name)
         | 
| 199 205 | 
             
                    end
         | 
| 200 206 | 
             
                  end
         | 
| 201 207 |  | 
| @@ -343,7 +349,7 @@ module GraphQL | |
| 343 349 | 
             
                    end
         | 
| 344 350 | 
             
                  end
         | 
| 345 351 |  | 
| 346 | 
            -
                  def build_merged_arguments(type_name, members_by_location, owner, field_name: nil)
         | 
| 352 | 
            +
                  def build_merged_arguments(type_name, members_by_location, owner, field_name: nil, directive_name: nil)
         | 
| 347 353 | 
             
                    # "argument_name" => "location" => argument
         | 
| 348 354 | 
             
                    args_by_name_location = members_by_location.each_with_object({}) do |(location, member_candidate), memo|
         | 
| 349 355 | 
             
                      member_candidate.arguments.each do |argument_name, argument|
         | 
| @@ -369,7 +375,7 @@ module GraphQL | |
| 369 375 |  | 
| 370 376 | 
             
                      kwargs = {}
         | 
| 371 377 | 
             
                      default_values_by_location = arguments_by_location.each_with_object({}) do |(location, argument), memo|
         | 
| 372 | 
            -
                        next if argument.default_value | 
| 378 | 
            +
                        next if argument.default_value == NO_DEFAULT_VALUE
         | 
| 373 379 | 
             
                        memo[location] = argument.default_value
         | 
| 374 380 | 
             
                      end
         | 
| 375 381 |  | 
| @@ -378,7 +384,8 @@ module GraphQL | |
| 378 384 | 
             
                          type_name: type_name,
         | 
| 379 385 | 
             
                          field_name: field_name,
         | 
| 380 386 | 
             
                          argument_name: argument_name,
         | 
| 381 | 
            -
             | 
| 387 | 
            +
                          directive_name: directive_name,
         | 
| 388 | 
            +
                        }.tap(&:compact!))
         | 
| 382 389 | 
             
                      end
         | 
| 383 390 |  | 
| 384 391 | 
             
                      type = merge_value_types(type_name, value_types, argument_name: argument_name, field_name: field_name)
         | 
| @@ -430,7 +437,7 @@ module GraphQL | |
| 430 437 | 
             
                          enum_value: enum_value,
         | 
| 431 438 | 
             
                          directive_name: directive_name,
         | 
| 432 439 | 
             
                          kwarg_name: kwarg_name,
         | 
| 433 | 
            -
                        }.compact!)
         | 
| 440 | 
            +
                        }.tap(&:compact!))
         | 
| 434 441 | 
             
                      end
         | 
| 435 442 |  | 
| 436 443 | 
             
                      owner.directive(directive_class, **kwargs)
         | 
| @@ -438,7 +445,7 @@ module GraphQL | |
| 438 445 | 
             
                  end
         | 
| 439 446 |  | 
| 440 447 | 
             
                  def merge_value_types(type_name, type_candidates, field_name: nil, argument_name: nil)
         | 
| 441 | 
            -
                    path = [type_name, field_name, argument_name].compact.join(".")
         | 
| 448 | 
            +
                    path = [type_name, field_name, argument_name].tap(&:compact!).join(".")
         | 
| 442 449 | 
             
                    alt_structures = type_candidates.map { Util.flatten_type_structure(_1) }
         | 
| 443 450 | 
             
                    basis_structure = alt_structures.shift
         | 
| 444 451 |  | 
| @@ -475,7 +482,7 @@ module GraphQL | |
| 475 482 | 
             
                      field_name: field_name,
         | 
| 476 483 | 
             
                      argument_name: argument_name,
         | 
| 477 484 | 
             
                      enum_value: enum_value,
         | 
| 478 | 
            -
                    }.compact!)
         | 
| 485 | 
            +
                    }.tap(&:compact!))
         | 
| 479 486 | 
             
                  end
         | 
| 480 487 |  | 
| 481 488 | 
             
                  def merge_deprecations(type_name, members_by_location, field_name: nil, argument_name: nil, enum_value: nil)
         | 
| @@ -485,7 +492,7 @@ module GraphQL | |
| 485 492 | 
             
                      field_name: field_name,
         | 
| 486 493 | 
             
                      argument_name: argument_name,
         | 
| 487 494 | 
             
                      enum_value: enum_value,
         | 
| 488 | 
            -
                    }.compact!)
         | 
| 495 | 
            +
                    }.tap(&:compact!))
         | 
| 489 496 | 
             
                  end
         | 
| 490 497 |  | 
| 491 498 | 
             
                  def extract_boundaries(type_name, types_by_location)
         | 
| @@ -16,7 +16,7 @@ module GraphQL | |
| 16 16 |  | 
| 17 17 | 
             
                      if op.if_type
         | 
| 18 18 | 
             
                        # operations planned around unused fragment conditions should not trigger requests
         | 
| 19 | 
            -
                        origin_set.select! { _1[ | 
| 19 | 
            +
                        origin_set.select! { _1[ExportSelection.typename_node.alias] == op.if_type }
         | 
| 20 20 | 
             
                      end
         | 
| 21 21 |  | 
| 22 22 | 
             
                      memo[op] = origin_set if origin_set.any?
         | 
| @@ -94,9 +94,9 @@ module GraphQL | |
| 94 94 | 
             
                  end
         | 
| 95 95 |  | 
| 96 96 | 
             
                  def build_key(key, origin_obj, federation: false)
         | 
| 97 | 
            -
                    key_value = JSON.generate(origin_obj[ | 
| 97 | 
            +
                    key_value = JSON.generate(origin_obj[ExportSelection.key(key)])
         | 
| 98 98 | 
             
                    if federation
         | 
| 99 | 
            -
                      "{ __typename: \"#{origin_obj[ | 
| 99 | 
            +
                      "{ __typename: \"#{origin_obj[ExportSelection.typename_node.alias]}\", #{key}: #{key_value} }"
         | 
| 100 100 | 
             
                    else
         | 
| 101 101 | 
             
                      key_value
         | 
| 102 102 | 
             
                    end
         | 
| @@ -4,18 +4,18 @@ module GraphQL | |
| 4 4 | 
             
              module Stitching
         | 
| 5 5 | 
             
                # Builds hidden selection fields added by stitiching code,
         | 
| 6 6 | 
             
                # used to request operational data about resolved objects.
         | 
| 7 | 
            -
                class  | 
| 8 | 
            -
                   | 
| 7 | 
            +
                class ExportSelection
         | 
| 8 | 
            +
                  EXPORT_PREFIX = "_export_"
         | 
| 9 9 |  | 
| 10 10 | 
             
                  class << self
         | 
| 11 11 | 
             
                    def key?(name)
         | 
| 12 12 | 
             
                      return false unless name
         | 
| 13 13 |  | 
| 14 | 
            -
                      name.start_with?( | 
| 14 | 
            +
                      name.start_with?(EXPORT_PREFIX)
         | 
| 15 15 | 
             
                    end
         | 
| 16 16 |  | 
| 17 17 | 
             
                    def key(name)
         | 
| 18 | 
            -
                      "#{ | 
| 18 | 
            +
                      "#{EXPORT_PREFIX}#{name}"
         | 
| 19 19 | 
             
                    end
         | 
| 20 20 |  | 
| 21 21 | 
             
                    def key_node(field_name)
         | 
| @@ -42,7 +42,7 @@ module GraphQL | |
| 42 42 | 
             
                  #      Adjoining selections not available here get split off into new entrypoints (C).
         | 
| 43 43 | 
             
                  # B.3) Collect all variable definitions used within the filtered selection.
         | 
| 44 44 | 
             
                  #      These specify which request variables to pass along with each step.
         | 
| 45 | 
            -
                  # B.4) Add a `__typename`  | 
| 45 | 
            +
                  # B.4) Add a `__typename` export to abstracts and types that implement fragments.
         | 
| 46 46 | 
             
                  #      This provides resolved type information used during execution.
         | 
| 47 47 | 
             
                  #
         | 
| 48 48 | 
             
                  # C) Delegate adjoining selections to new entrypoint locations.
         | 
| @@ -199,7 +199,9 @@ module GraphQL | |
| 199 199 | 
             
                    input_selections.each do |node|
         | 
| 200 200 | 
             
                      case node
         | 
| 201 201 | 
             
                      when GraphQL::Language::Nodes::Field
         | 
| 202 | 
            -
                        if node. | 
| 202 | 
            +
                        if node.alias&.start_with?(ExportSelection::EXPORT_PREFIX)
         | 
| 203 | 
            +
                          raise StitchingError, %(Alias "#{node.alias}" is not allowed because "#{ExportSelection::EXPORT_PREFIX}" is a reserved prefix.)
         | 
| 204 | 
            +
                        elsif node.name == TYPENAME
         | 
| 203 205 | 
             
                          locale_selections << node
         | 
| 204 206 | 
             
                          next
         | 
| 205 207 | 
             
                        end
         | 
| @@ -257,10 +259,10 @@ module GraphQL | |
| 257 259 | 
             
                      end
         | 
| 258 260 | 
             
                    end
         | 
| 259 261 |  | 
| 260 | 
            -
                    # B.4) Add a `__typename`  | 
| 262 | 
            +
                    # B.4) Add a `__typename` export to abstracts and types that implement
         | 
| 261 263 | 
             
                    # fragments so that resolved type information is available during execution.
         | 
| 262 264 | 
             
                    if requires_typename
         | 
| 263 | 
            -
                      locale_selections <<  | 
| 265 | 
            +
                      locale_selections << ExportSelection.typename_node
         | 
| 264 266 | 
             
                    end
         | 
| 265 267 |  | 
| 266 268 | 
             
                    if remote_selections
         | 
| @@ -274,18 +276,18 @@ module GraphQL | |
| 274 276 | 
             
                      routes.each_value do |route|
         | 
| 275 277 | 
             
                        route.reduce(locale_selections) do |parent_selections, boundary|
         | 
| 276 278 | 
             
                          # E.1) Add the key of each boundary query into the prior location's selection set.
         | 
| 277 | 
            -
                          foreign_key =  | 
| 279 | 
            +
                          foreign_key = ExportSelection.key(boundary.key)
         | 
| 278 280 | 
             
                          has_key = false
         | 
| 279 281 | 
             
                          has_typename = false
         | 
| 280 282 |  | 
| 281 283 | 
             
                          parent_selections.each do |node|
         | 
| 282 284 | 
             
                            next unless node.is_a?(GraphQL::Language::Nodes::Field)
         | 
| 283 285 | 
             
                            has_key ||= node.alias == foreign_key
         | 
| 284 | 
            -
                            has_typename ||= node.alias ==  | 
| 286 | 
            +
                            has_typename ||= node.alias == ExportSelection.typename_node.alias
         | 
| 285 287 | 
             
                          end
         | 
| 286 288 |  | 
| 287 | 
            -
                          parent_selections <<  | 
| 288 | 
            -
                          parent_selections <<  | 
| 289 | 
            +
                          parent_selections << ExportSelection.key_node(boundary.key) unless has_key
         | 
| 290 | 
            +
                          parent_selections << ExportSelection.typename_node unless has_typename
         | 
| 289 291 |  | 
| 290 292 | 
             
                          # E.2) Add a planner step for each new entrypoint location.
         | 
| 291 293 | 
             
                          add_step(
         | 
| @@ -4,7 +4,7 @@ | |
| 4 4 | 
             
            module GraphQL
         | 
| 5 5 | 
             
              module Stitching
         | 
| 6 6 | 
             
                # Shapes the final results payload to the request selection and schema definition.
         | 
| 7 | 
            -
                # This eliminates unrequested  | 
| 7 | 
            +
                # This eliminates unrequested export selections and applies null bubbling.
         | 
| 8 8 | 
             
                class Shaper
         | 
| 9 9 | 
             
                  def initialize(supergraph:, request:)
         | 
| 10 10 | 
             
                    @supergraph = supergraph
         | 
| @@ -21,8 +21,8 @@ module GraphQL | |
| 21 21 | 
             
                  def resolve_object_scope(raw_object, parent_type, selections, typename = nil)
         | 
| 22 22 | 
             
                    return nil if raw_object.nil?
         | 
| 23 23 |  | 
| 24 | 
            -
                    typename ||= raw_object[ | 
| 25 | 
            -
                    raw_object.reject! { |key, _v|  | 
| 24 | 
            +
                    typename ||= raw_object[ExportSelection.typename_node.alias]
         | 
| 25 | 
            +
                    raw_object.reject! { |key, _v| ExportSelection.key?(key) }
         | 
| 26 26 |  | 
| 27 27 | 
             
                    selections.each do |node|
         | 
| 28 28 | 
             
                      case node
         | 
    
        data/lib/graphql/stitching.rb
    CHANGED
    
    | @@ -28,12 +28,12 @@ require_relative "stitching/boundary" | |
| 28 28 | 
             
            require_relative "stitching/client"
         | 
| 29 29 | 
             
            require_relative "stitching/composer"
         | 
| 30 30 | 
             
            require_relative "stitching/executor"
         | 
| 31 | 
            +
            require_relative "stitching/export_selection"
         | 
| 31 32 | 
             
            require_relative "stitching/http_executable"
         | 
| 32 33 | 
             
            require_relative "stitching/plan"
         | 
| 33 34 | 
             
            require_relative "stitching/planner_step"
         | 
| 34 35 | 
             
            require_relative "stitching/planner"
         | 
| 35 36 | 
             
            require_relative "stitching/request"
         | 
| 36 | 
            -
            require_relative "stitching/selection_hint"
         | 
| 37 37 | 
             
            require_relative "stitching/shaper"
         | 
| 38 38 | 
             
            require_relative "stitching/skip_include"
         | 
| 39 39 | 
             
            require_relative "stitching/util"
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: graphql-stitching
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.0. | 
| 4 | 
            +
              version: 1.0.5
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Greg MacWilliam
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2023-11- | 
| 11 | 
            +
            date: 2023-11-19 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: graphql
         | 
| @@ -106,12 +106,12 @@ files: | |
| 106 106 | 
             
            - lib/graphql/stitching/executor.rb
         | 
| 107 107 | 
             
            - lib/graphql/stitching/executor/boundary_source.rb
         | 
| 108 108 | 
             
            - lib/graphql/stitching/executor/root_source.rb
         | 
| 109 | 
            +
            - lib/graphql/stitching/export_selection.rb
         | 
| 109 110 | 
             
            - lib/graphql/stitching/http_executable.rb
         | 
| 110 111 | 
             
            - lib/graphql/stitching/plan.rb
         | 
| 111 112 | 
             
            - lib/graphql/stitching/planner.rb
         | 
| 112 113 | 
             
            - lib/graphql/stitching/planner_step.rb
         | 
| 113 114 | 
             
            - lib/graphql/stitching/request.rb
         | 
| 114 | 
            -
            - lib/graphql/stitching/selection_hint.rb
         | 
| 115 115 | 
             
            - lib/graphql/stitching/shaper.rb
         | 
| 116 116 | 
             
            - lib/graphql/stitching/skip_include.rb
         | 
| 117 117 | 
             
            - lib/graphql/stitching/supergraph.rb
         |