From 8703d32fe124fc959e5ac83c76eb1ba00cf1385c Mon Sep 17 00:00:00 2001 From: Greg MacWilliam Date: Sat, 1 Jun 2024 23:19:54 -0400 Subject: [PATCH] Rename `federation` resolver configuration to `representations`. --- README.md | 8 ++++---- docs/README.md | 3 ++- lib/graphql/stitching/composer.rb | 2 +- .../stitching/composer/resolver_config.rb | 10 +++++----- .../stitching/executor/resolver_source.rb | 8 ++++---- lib/graphql/stitching/resolver.rb | 19 ++++++++++++++++--- lib/graphql/stitching/supergraph.rb | 6 +++--- .../supergraph/resolver_directive.rb | 2 +- .../stitching/composer/configuration_test.rb | 4 ++-- .../composer/merge_resolvers_test.rb | 10 +++++----- test/test_helper.rb | 2 +- 11 files changed, 44 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 2888dd5a..4e98c965 100644 --- a/README.md +++ b/README.md @@ -265,7 +265,7 @@ type Query { The `@stitch` directive can be added to class-based schemas with a directive class: ```ruby -class StitchField < GraphQL::Schema::Directive +class StitchingResolver < GraphQL::Schema::Directive graphql_name "stitch" locations FIELD_DEFINITION repeatable true @@ -274,7 +274,7 @@ end class Query < GraphQL::Schema::Object field :product, Product, null: false do - directive StitchField, key: "id" + directive StitchingResolver, key: "id" argument :id, ID, required: true end end @@ -284,7 +284,7 @@ The `@stitch` directive can be exported from a class-based schema to an SDL stri #### SDL-based schemas -A clean SDL string may also have stitching directives applied via static configuration by passing a `stitch` array in [location settings](./docs/composer.md#performing-composition): +A clean schema may also have stitching directives applied via static configuration by passing a `stitch` array in [location settings](./docs/composer.md#performing-composition): ```ruby sdl_string = <<~GRAPHQL @@ -316,7 +316,7 @@ supergraph = GraphQL::Stitching::Composer.new.perform({ The library is configured to use a `@stitch` directive by default. You may customize this by setting a new name during initialization: ```ruby -GraphQL::Stitching.stitch_directive = "merge" +GraphQL::Stitching.stitch_directive = "resolver" ``` ## Executables diff --git a/docs/README.md b/docs/README.md index 1af613a2..54b9664e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,4 +14,5 @@ Major components include: Additional topics: -- [Stitching mechanics](./mechanics.md) - learn more about building for stitching. +- [Stitching mechanics](./mechanics.md) - more about building for stitching and how it operates. +- [Federation entities](./federation_entities.md) - more about Apollo Federation compatibility. diff --git a/lib/graphql/stitching/composer.rb b/lib/graphql/stitching/composer.rb index 3612494f..62f9f90d 100644 --- a/lib/graphql/stitching/composer.rb +++ b/lib/graphql/stitching/composer.rb @@ -584,7 +584,7 @@ def extract_resolvers(type_name, types_by_location) field: field_candidate.name, arg: argument_name, list: resolver_structure.first.list?, - federation: config.federation, + representations: config.representations, ) end end diff --git a/lib/graphql/stitching/composer/resolver_config.rb b/lib/graphql/stitching/composer/resolver_config.rb index ebde4c64..9a5a57f4 100644 --- a/lib/graphql/stitching/composer/resolver_config.rb +++ b/lib/graphql/stitching/composer/resolver_config.rb @@ -38,7 +38,7 @@ def extract_federation_entities(schema, location) memo[field_path] << new( key: key, type_name: entity_type.graphql_name, - federation: true, + representations: true, ) end end @@ -48,7 +48,7 @@ def from_kwargs(kwargs) new( key: kwargs[:key], type_name: kwargs[:type_name] || kwargs[:typeName], - federation: kwargs[:federation] || false, + representations: kwargs[:representations] || false, ) end @@ -61,12 +61,12 @@ def federation_entities_schema?(schema) end end - attr_reader :key, :type_name, :federation + attr_reader :key, :type_name, :representations - def initialize(key:, type_name:, federation: false) + def initialize(key:, type_name:, representations: false) @key = key @type_name = type_name - @federation = federation + @representations = representations end end end diff --git a/lib/graphql/stitching/executor/resolver_source.rb b/lib/graphql/stitching/executor/resolver_source.rb index 195a267f..13b15f4b 100644 --- a/lib/graphql/stitching/executor/resolver_source.rb +++ b/lib/graphql/stitching/executor/resolver_source.rb @@ -57,14 +57,14 @@ def build_document(origin_sets_by_operation, operation_name = nil, operation_dir if resolver.list? input = origin_set.each_with_index.reduce(String.new) do |memo, (origin_obj, index)| memo << "," if index > 0 - memo << build_key(resolver.key, origin_obj, federation: resolver.federation) + memo << build_key(resolver.key, origin_obj, as_representation: resolver.representations?) memo end "_#{batch_index}_result: #{resolver.field}(#{resolver.arg}:[#{input}]) #{op.selections}" else origin_set.map.with_index do |origin_obj, index| - input = build_key(resolver.key, origin_obj, federation: resolver.federation?) + input = build_key(resolver.key, origin_obj, as_representation: resolver.representations?) "_#{batch_index}_#{index}_result: #{resolver.field}(#{resolver.arg}:#{input}) #{op.selections}" end end @@ -93,9 +93,9 @@ def build_document(origin_sets_by_operation, operation_name = nil, operation_dir return doc, variable_defs.keys end - def build_key(key, origin_obj, federation: false) + def build_key(key, origin_obj, as_representation: false) key_value = JSON.generate(origin_obj[ExportSelection.key(key)]) - if federation + if as_representation "{ __typename: \"#{origin_obj[ExportSelection.typename_node.alias]}\", #{key}: #{key_value} }" else key_value diff --git a/lib/graphql/stitching/resolver.rb b/lib/graphql/stitching/resolver.rb index 07858a7c..0a7764ca 100644 --- a/lib/graphql/stitching/resolver.rb +++ b/lib/graphql/stitching/resolver.rb @@ -4,17 +4,30 @@ module GraphQL module Stitching # Defines a root resolver query that provides direct access to an entity type. Resolver = Struct.new( + # location name providing the resolver query. :location, + + # name of merged type fulfilled through this resolver. :type_name, + + # a key field to select from prior locations, sent as resolver argument. :key, + + # name of the root field to query. :field, + + # name of the root field argument used to send the key. :arg, + + # specifies when the resolver is a list query. :list, - :federation, + + # specifies that keys should be sent as JSON representations with __typename and key. + :representations, keyword_init: true ) do alias_method :list?, :list - alias_method :federation?, :federation + alias_method :representations?, :representations def as_json { @@ -24,7 +37,7 @@ def as_json field: field, arg: arg, list: list, - federation: federation, + representations: representations, }.tap(&:compact!) end end diff --git a/lib/graphql/stitching/supergraph.rb b/lib/graphql/stitching/supergraph.rb index de1a55cd..add1fe93 100644 --- a/lib/graphql/stitching/supergraph.rb +++ b/lib/graphql/stitching/supergraph.rb @@ -37,7 +37,7 @@ def from_definition(schema, executables:) field: kwargs[:field], arg: kwargs[:arg], list: kwargs[:list] || false, - federation: kwargs[:federation] || false, + representations: kwargs[:representations] || false, ) end @@ -130,7 +130,7 @@ def to_definition kwargs[:field] == resolver.field && kwargs[:arg] == resolver.arg && kwargs.fetch(:list, false) == resolver.list && - kwargs.fetch(:federation, false) == resolver.federation + kwargs.fetch(:representations, false) == resolver.representations end type.directive(ResolverDirective, **{ @@ -140,7 +140,7 @@ def to_definition field: resolver.field, arg: resolver.arg, list: resolver.list || nil, - federation: resolver.federation || nil, + representations: resolver.representations || nil, }.tap(&:compact!)) if existing.nil? end end diff --git a/lib/graphql/stitching/supergraph/resolver_directive.rb b/lib/graphql/stitching/supergraph/resolver_directive.rb index 4214325b..1040663e 100644 --- a/lib/graphql/stitching/supergraph/resolver_directive.rb +++ b/lib/graphql/stitching/supergraph/resolver_directive.rb @@ -11,7 +11,7 @@ class ResolverDirective < GraphQL::Schema::Directive argument :field, String, required: true argument :arg, String, required: true argument :list, Boolean, required: false - argument :federation, Boolean, required: false + argument :representations, Boolean, required: false repeatable true end end diff --git a/test/graphql/stitching/composer/configuration_test.rb b/test/graphql/stitching/composer/configuration_test.rb index dae615c4..23beb0b5 100644 --- a/test/graphql/stitching/composer/configuration_test.rb +++ b/test/graphql/stitching/composer/configuration_test.rb @@ -51,7 +51,7 @@ def test_perform_with_static_resolver_config key: "id", arg: "id", list: false, - federation: false, + representations: false, ), GraphQL::Stitching::Resolver.new( location: "bravo", @@ -60,7 +60,7 @@ def test_perform_with_static_resolver_config key: "id", arg: "key", list: false, - federation: false, + representations: false, ), ] } diff --git a/test/graphql/stitching/composer/merge_resolvers_test.rb b/test/graphql/stitching/composer/merge_resolvers_test.rb index bc8acab9..b907fe8c 100644 --- a/test/graphql/stitching/composer/merge_resolvers_test.rb +++ b/test/graphql/stitching/composer/merge_resolvers_test.rb @@ -16,7 +16,7 @@ def test_creates_resolver_map field: "a", arg: "id", list: false, - federation: false, + representations: false, type_name: "Test" ), GraphQL::Stitching::Resolver.new( @@ -25,7 +25,7 @@ def test_creates_resolver_map field: "b", arg: "ids", list: true, - federation: false, + representations: false, type_name: "Test" ), ], @@ -130,7 +130,7 @@ def test_builds_union_resolvers_for_select_typenames type Query { fruitA(id:ID!):Fruit @stitch(key: "id", typeName: "Apple") - @stitch(key: "id", typeName: "Banana", federation: true) + @stitch(key: "id", typeName: "Banana", representations: true) coconut(id: ID!): Coconut @stitch(key: "id") } @@ -151,8 +151,8 @@ def test_builds_union_resolvers_for_select_typenames assert_equal ["coconut", "fruitB"], supergraph.resolvers["Coconut"].map(&:field).sort assert_equal ["fruitB"], supergraph.resolvers["Fruit"].map(&:field).sort - assert_equal false, supergraph.resolvers["Apple"].find { _1.location == "a" }.federation - assert_equal true, supergraph.resolvers["Banana"].find { _1.location == "a" }.federation + assert_equal false, supergraph.resolvers["Apple"].find { _1.location == "a" }.representations? + assert_equal true, supergraph.resolvers["Banana"].find { _1.location == "a" }.representations? end def test_raises_when_given_typename_is_not_a_possible_type diff --git a/test/test_helper.rb b/test/test_helper.rb index 6ee8ef52..6fbe7933 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -18,7 +18,7 @@ ComposerError = GraphQL::Stitching::Composer::ComposerError ValidationError = GraphQL::Stitching::Composer::ValidationError -STITCH_DEFINITION = "directive @stitch(key: String!, typeName: String, federation: Boolean=false) repeatable on FIELD_DEFINITION\n" +STITCH_DEFINITION = "directive @stitch(key: String!, typeName: String, representations: Boolean=false) repeatable on FIELD_DEFINITION\n" def squish_string(str) str.gsub(/\s+/, " ").strip