From 2aaa35ff2d12289ece93914e420ee97f93bcd985 Mon Sep 17 00:00:00 2001 From: Greg MacWilliam Date: Wed, 29 Nov 2023 23:42:50 -0500 Subject: [PATCH] export supergraph as SDL. --- docs/supergraph.md | 18 +- lib/graphql/stitching/composer.rb | 2 +- lib/graphql/stitching/planner.rb | 2 +- lib/graphql/stitching/supergraph.rb | 164 ++++++++++++++---- test/graphql/stitching/client_test.rb | 5 +- .../stitching/composer/configuration_test.rb | 2 + .../composer/merge_boundaries_test.rb | 38 ++-- .../composer/merge_directive_test.rb | 24 +-- .../stitching/composer/merge_fields_test.rb | 8 +- test/graphql/stitching/supergraph_test.rb | 131 +++++++------- test/test_helper.rb | 15 +- 11 files changed, 255 insertions(+), 154 deletions(-) diff --git a/docs/supergraph.md b/docs/supergraph.md index 008c741b..12df6569 100644 --- a/docs/supergraph.md +++ b/docs/supergraph.md @@ -4,29 +4,25 @@ A `Supergraph` is the singuar representation of a stitched graph. `Supergraph` i ### Export and caching -A Supergraph is designed to be composed, cached, and restored. Calling the `export` method will return an SDL (Schema Definition Language) print of the combined graph schema and a delegation mapping hash. These can be persisted in any raw format that suits your stack: +A Supergraph is designed to be composed, cached, and restored. Calling `to_definition` will return an SDL (Schema Definition Language) print of the combined graph schema with delegation mapping directives. This pre-composed schema can be persisted in any raw format that suits your stack: ```ruby -supergraph_sdl, delegation_map = supergraph.export +supergraph_sdl = supergraph.to_definition -# stash these resources in Redis... +# stash this composed schema in a cache... $redis.set("cached_supergraph_sdl", supergraph_sdl) -$redis.set("cached_delegation_map", JSON.generate(delegation_map)) -# or, write the resources as files and commit them to your repo... +# or, write the composed schema as a file into your repo... File.write("supergraph/schema.graphql", supergraph_sdl) -File.write("supergraph/delegation_map.json", JSON.generate(delegation_map)) ``` -To restore a Supergraph, call `from_export` proving the cached SDL string, the parsed JSON delegation mapping, and a hash of executables keyed by their location names: +To restore a Supergraph, call `from_definition` providing the cached SDL string and a hash of executables keyed by their location names: ```ruby supergraph_sdl = $redis.get("cached_supergraph_sdl") -delegation_map = JSON.parse($redis.get("cached_delegation_map")) -supergraph = GraphQL::Stitching::Supergraph.from_export( - schema: supergraph_sdl, - delegation_map: delegation_map, +supergraph = GraphQL::Stitching::Supergraph.from_definition( + supergraph_sdl, executables: { my_remote: GraphQL::Stitching::HttpExecutable.new(url: "http://localhost:3000"), my_local: MyLocalSchema, diff --git a/lib/graphql/stitching/composer.rb b/lib/graphql/stitching/composer.rb index 1c300a8f..6a721f61 100644 --- a/lib/graphql/stitching/composer.rb +++ b/lib/graphql/stitching/composer.rb @@ -541,7 +541,7 @@ def extract_boundaries(type_name, types_by_location) field: field_candidate.name, arg: argument_name, list: boundary_structure.first.list?, - federation: kwargs[:federation], + federation: kwargs[:federation] || false, ) end end diff --git a/lib/graphql/stitching/planner.rb b/lib/graphql/stitching/planner.rb index 44672103..61da8835 100644 --- a/lib/graphql/stitching/planner.rb +++ b/lib/graphql/stitching/planner.rb @@ -3,7 +3,7 @@ module GraphQL module Stitching class Planner - SUPERGRAPH_LOCATIONS = [Supergraph::LOCATION].freeze + SUPERGRAPH_LOCATIONS = [Supergraph::SUPERGRAPH_LOCATION].freeze TYPENAME = "__typename" QUERY_OP = "query" MUTATION_OP = "mutation" diff --git a/lib/graphql/stitching/supergraph.rb b/lib/graphql/stitching/supergraph.rb index 62ed8f81..47a11c01 100644 --- a/lib/graphql/stitching/supergraph.rb +++ b/lib/graphql/stitching/supergraph.rb @@ -3,34 +3,89 @@ module GraphQL module Stitching class Supergraph - LOCATION = "__super" + SUPERGRAPH_LOCATION = "__super" + + class BoundaryDirective < GraphQL::Schema::Directive + graphql_name "boundary" + locations OBJECT, INTERFACE, UNION + argument :location, String, required: true + argument :key, String, required: true + argument :field, String, required: true + argument :arg, String, required: true + argument :list, Boolean, required: false + argument :federation, Boolean, required: false + repeatable true + end - def self.validate_executable!(location, executable) - return true if executable.is_a?(Class) && executable <= GraphQL::Schema - return true if executable && executable.respond_to?(:call) - raise StitchingError, "Invalid executable provided for location `#{location}`." + class SourceDirective < GraphQL::Schema::Directive + graphql_name "source" + locations FIELD_DEFINITION + argument :location, String, required: true + repeatable true end - def self.from_export(schema:, delegation_map:, executables:) - schema = GraphQL::Schema.from_definition(schema) if schema.is_a?(String) + class << self + def validate_executable!(location, executable) + return true if executable.is_a?(Class) && executable <= GraphQL::Schema + return true if executable && executable.respond_to?(:call) + raise StitchingError, "Invalid executable provided for location `#{location}`." + end + + def from_definition(schema, executables:) + schema = GraphQL::Schema.from_definition(schema) if schema.is_a?(String) + field_map = {} + boundary_map = {} + possible_locations = {} + introspection_types = schema.introspection_system.types.keys + + schema.types.each do |type_name, type| + next if introspection_types.include?(type_name) + + type.directives.each do |directive| + next unless directive.graphql_name == BoundaryDirective.graphql_name + + kwargs = directive.arguments.keyword_arguments + boundary_map[type_name] ||= [] + boundary_map[type_name] << Boundary.new( + type_name: type_name, + location: kwargs[:location], + key: kwargs[:key], + field: kwargs[:field], + arg: kwargs[:arg], + list: kwargs[:list] || false, + federation: kwargs[:federation] || false, + ) + end + + next unless type.kind.fields? - executables = delegation_map["locations"].each_with_object({}) do |location, memo| - executable = executables[location] || executables[location.to_sym] - if validate_executable!(location, executable) - memo[location] = executable + type.fields.each do |field_name, field| + field.directives.each do |d| + next unless d.graphql_name == SourceDirective.graphql_name + + location = d.arguments.keyword_arguments[:location] + field_map[type_name] ||= {} + field_map[type_name][field_name] ||= [] + field_map[type_name][field_name] << location + possible_locations[location] = true + end + end end - end - boundaries = delegation_map["boundaries"].map do |k, b| - [k, b.map { Boundary.new(**_1) }] - end + executables = possible_locations.keys.each_with_object({}) do |location, memo| + executable = executables[location] || executables[location.to_sym] + if validate_executable!(location, executable) + memo[location] = executable + end + end - new( - schema: schema, - fields: delegation_map["fields"], - boundaries: boundaries.to_h, - executables: executables, - ) + new( + schema: schema, + fields: field_map, + boundaries: boundary_map, + executables: executables, + ) + end end attr_reader :schema, :boundaries, :locations_by_type_and_field, :executables @@ -48,32 +103,77 @@ def initialize(schema:, fields:, boundaries:, executables:) next unless type.kind.fields? memo[type_name] = type.fields.keys.each_with_object({}) do |field_name, m| - m[field_name] = [LOCATION] + m[field_name] = [SUPERGRAPH_LOCATION] end end.freeze # validate and normalize executable references - @executables = executables.each_with_object({ LOCATION => @schema }) do |(location, executable), memo| + @executables = executables.each_with_object({ SUPERGRAPH_LOCATION => @schema }) do |(location, executable), memo| if self.class.validate_executable!(location, executable) memo[location.to_s] = executable end end.freeze end + def to_definition + if @schema.directives[BoundaryDirective.graphql_name].nil? + @schema.directive(BoundaryDirective) + end + if @schema.directives[SourceDirective.graphql_name].nil? + @schema.directive(SourceDirective) + end + + @schema.types.each do |type_name, type| + if boundaries_for_type = @boundaries.dig(type_name) + boundaries_for_type.each do |boundary| + existing = type.directives.find do |d| + kwargs = d.arguments.keyword_arguments + d.graphql_name == BoundaryDirective.graphql_name && + kwargs[:location] == boundary.location && + kwargs[:key] == boundary.key && + kwargs[:field] == boundary.field && + kwargs[:arg] == boundary.arg && + kwargs.fetch(:list, false) == boundary.list && + kwargs.fetch(:federation, false) == boundary.federation + end + + type.directive(BoundaryDirective, **{ + location: boundary.location, + key: boundary.key, + field: boundary.field, + arg: boundary.arg, + list: boundary.list || nil, + federation: boundary.federation || nil, + }.tap(&:compact!)) if existing.nil? + end + end + + next unless type.kind.fields? + + type.fields.each do |field_name, field| + locations_for_field = @locations_by_type_and_field.dig(type_name, field_name) + next if locations_for_field.nil? + + locations_for_field.each do |location| + existing = field.directives.find do |d| + d.graphql_name == SourceDirective.graphql_name && + d.arguments.keyword_arguments[:location] == location + end + + field.directive(SourceDirective, location: location) if existing.nil? + end + end + end + + @schema.to_definition + end + def fields @locations_by_type_and_field.reject { |k, _v| memoized_introspection_types[k] } end def locations - @executables.keys.reject { _1 == LOCATION } - end - - def export - return GraphQL::Schema::Printer.print_schema(@schema), { - "locations" => locations, - "fields" => fields, - "boundaries" => @boundaries.map { |k, b| [k, b.map(&:as_json)] }.to_h, - } + @executables.keys.reject { _1 == SUPERGRAPH_LOCATION } end def memoized_introspection_types diff --git a/test/graphql/stitching/client_test.rb b/test/graphql/stitching/client_test.rb index d55c646c..a7db5a8d 100644 --- a/test/graphql/stitching/client_test.rb +++ b/test/graphql/stitching/client_test.rb @@ -128,9 +128,8 @@ def test_prepares_requests_before_handling end def test_client_builds_with_provided_supergraph - supergraph = GraphQL::Stitching::Supergraph.from_export( - schema: "type Thing { id: String } type Query { thing: Thing }", - delegation_map: { "fields" => {}, "boundaries" => {}, "locations" => ["alpha"] }, + supergraph = supergraph_from_schema( + "type Thing { id: String } type Query { thing: Thing }", executables: { alpha: Proc.new { true }, } diff --git a/test/graphql/stitching/composer/configuration_test.rb b/test/graphql/stitching/composer/configuration_test.rb index 3cad4589..db6b7af1 100644 --- a/test/graphql/stitching/composer/configuration_test.rb +++ b/test/graphql/stitching/composer/configuration_test.rb @@ -51,6 +51,7 @@ def test_perform_with_static_boundary_config key: "id", arg: "id", list: false, + federation: false, ), GraphQL::Stitching::Boundary.new( location: "bravo", @@ -59,6 +60,7 @@ def test_perform_with_static_boundary_config key: "id", arg: "key", list: false, + federation: false, ), ] } diff --git a/test/graphql/stitching/composer/merge_boundaries_test.rb b/test/graphql/stitching/composer/merge_boundaries_test.rb index b820eff4..68a14ab9 100644 --- a/test/graphql/stitching/composer/merge_boundaries_test.rb +++ b/test/graphql/stitching/composer/merge_boundaries_test.rb @@ -4,8 +4,8 @@ describe 'GraphQL::Stitching::Composer, merging boundary queries' do def test_creates_boundary_map - a = %{type Test { id: ID!, a: String } type Query { a(id: ID!):Test @stitch(key: "id") }} - b = %{type Test { id: ID!, b: String } type Query { b(ids: [ID!]!):[Test]! @stitch(key: "id") }} + a = %|type Test { id: ID!, a: String } type Query { a(id: ID!):Test @stitch(key: "id") }| + b = %|type Test { id: ID!, b: String } type Query { b(ids: [ID!]!):[Test]! @stitch(key: "id") }| supergraph = compose_definitions({ "a" => a, "b" => b }) expected_boundaries_map = { @@ -16,6 +16,7 @@ def test_creates_boundary_map field: "a", arg: "id", list: false, + federation: false, type_name: "Test" ), GraphQL::Stitching::Boundary.new( @@ -24,6 +25,7 @@ def test_creates_boundary_map field: "b", arg: "ids", list: true, + federation: false, type_name: "Test" ), ], @@ -36,18 +38,18 @@ def test_merges_boundaries_with_multiple_keys # repeatable directives don't work before v2.0.15 skip unless minimum_graphql_version?("2.0.15") - a = %{ + a = %| type T { upc:ID! } type Query { a(upc:ID!):T @stitch(key: "upc") } - } - b = %{ + | + b = %| type T { id:ID! upc:ID! } type Query { b(id: ID, upc:ID):T @stitch(key: "id:id") @stitch(key: "upc:upc") } - } - c = %{ + | + c = %| type T { id:ID! } type Query { c(id:ID!):T @stitch(key: "id") } - } + | supergraph = compose_definitions({ "a" => a, "b" => b, "c" => c }) @@ -58,21 +60,21 @@ def test_merges_boundaries_with_multiple_keys end def test_expands_interface_boundary_accessors_to_relevant_types - a = %{ + a = %| interface Fruit { id:ID! } type Apple implements Fruit { id:ID! name:String } type Banana implements Fruit { id:ID! name:String } type Coconut implements Fruit { id:ID! name:String } type Query { fruit(id:ID!):Fruit @stitch(key: "id") } - } - b = %{ + | + b = %| type Apple { id:ID! color:String } type Banana { id:ID! color:String } type Query { a(id:ID!):Apple @stitch(key: "id") b(id:ID!):Banana @stitch(key: "id") } - } + | supergraph = compose_definitions({ "a" => a, "b" => b }) @@ -89,23 +91,23 @@ def test_expands_interface_boundary_accessors_to_relevant_types end def test_expands_union_boundary_accessors_to_relevant_types - a = %{ + a = %| type Apple { id:ID! name:String } type Banana { id:ID! name:String } - union Fruit = Apple | Banana + union Fruit = Apple \| Banana type Query { fruit(id:ID!):Fruit @stitch(key: "id") } - } - b = %{ + | + b = %| type Apple { id:ID! color:String } type Coconut { id:ID! name:String } - union Fruit = Apple | Coconut + union Fruit = Apple \| Coconut type Query { a(id:ID!):Apple @stitch(key: "id") c(id:ID!):Coconut } - } + | supergraph = compose_definitions({ "a" => a, "b" => b }) assert_equal 1, supergraph.boundaries["Fruit"].length diff --git a/test/graphql/stitching/composer/merge_directive_test.rb b/test/graphql/stitching/composer/merge_directive_test.rb index 1705c262..5685b67c 100644 --- a/test/graphql/stitching/composer/merge_directive_test.rb +++ b/test/graphql/stitching/composer/merge_directive_test.rb @@ -5,19 +5,19 @@ describe 'GraphQL::Stitching::Composer, merging directives' do def test_merges_directive_definitions - a = <<~GRAPHQL + a = %| """a""" directive @fizzbuzz(a: String!) on OBJECT type Test @fizzbuzz(a: "A") { field: String } type Query { test: Test } - GRAPHQL + | - b = <<~GRAPHQL + b = %| """b""" directive @fizzbuzz(a: String!, b: String) on OBJECT type Test @fizzbuzz(a: "A", b: "B") { field: String } type Query { test: Test } - GRAPHQL + | supergraph = compose_definitions({ "a" => a, "b" => b }, { description_merger: ->(str_by_location, _info) { str_by_location.values.join("/") } @@ -29,19 +29,19 @@ def test_merges_directive_definitions end def test_combines_distinct_directives_assigned_to_an_element - a = <<~GRAPHQL + a = %| directive @fizz(arg: String!) on OBJECT directive @buzz on OBJECT type Test @fizz(arg: "a") @buzz { field: String } type Query { test:Test } - GRAPHQL + | - b = <<~GRAPHQL + b = %| directive @fizz(arg: String!) on OBJECT directive @widget on OBJECT type Test @fizz(arg: "b") @widget { field: String } type Query { test:Test } - GRAPHQL + | supergraph = compose_definitions({ "a" => a, "b" => b }, { directive_kwarg_merger: ->(str_by_location, _info) { str_by_location.values.join("/") } @@ -55,17 +55,17 @@ def test_combines_distinct_directives_assigned_to_an_element end def test_omits_stitching_directives - a = <<~GRAPHQL + a = %| directive @stitch(key: String!) repeatable on FIELD_DEFINITION type Test { id: ID! a: String } type Query { testA(id: ID!): Test @stitch(key: "id") } - GRAPHQL + | - b = <<~GRAPHQL + b = %| directive @stitch(key: String!) repeatable on FIELD_DEFINITION type Test { id: ID! b: String } type Query { testB(id: ID!): Test @stitch(key: "id") } - GRAPHQL + | supergraph = compose_definitions({ "a" => a, "b" => b }, { directive_kwarg_merger: ->(str_by_location, _info) { str_by_location.values.join("/") } diff --git a/test/graphql/stitching/composer/merge_fields_test.rb b/test/graphql/stitching/composer/merge_fields_test.rb index 8f0858d3..7f449cbd 100644 --- a/test/graphql/stitching/composer/merge_fields_test.rb +++ b/test/graphql/stitching/composer/merge_fields_test.rb @@ -27,15 +27,15 @@ def test_merges_field_deprecations end def test_merges_field_directives - a = <<~GRAPHQL + a = %| directive @fizzbuzz(arg: String!) on FIELD_DEFINITION type Query { test(arg:String):String @fizzbuzz(arg:"a") } - GRAPHQL + | - b = <<~GRAPHQL + b = %| directive @fizzbuzz(arg: String!) on FIELD_DEFINITION type Query { test(arg:String):String @fizzbuzz(arg:"b") } - GRAPHQL + | supergraph = compose_definitions({ "a" => a, "b" => b }, { directive_kwarg_merger: ->(str_by_location, _info) { str_by_location.values.join("/") } diff --git a/test/graphql/stitching/supergraph_test.rb b/test/graphql/stitching/supergraph_test.rb index c30cf241..621176d6 100644 --- a/test/graphql/stitching/supergraph_test.rb +++ b/test/graphql/stitching/supergraph_test.rb @@ -222,21 +222,21 @@ def test_rejects_invalid_executables_with_error end def test_route_type_to_locations_connects_types_across_locations - a = %{ + a = %| type T { upc:ID! } type Query { a(upc:ID!):T @stitch(key: "upc") } - } - b = %{ + | + b = %| type T { id:ID! upc:ID! } type Query { ba(upc:ID!):T @stitch(key: "upc") bc(id:ID!):T @stitch(key: "id") } - } - c = %{ + | + c = %| type T { id:ID! } type Query { c(id:ID!):T @stitch(key: "id") } - } + | supergraph = compose_definitions({ "a" => a, "b" => b, "c" => c }) @@ -254,38 +254,38 @@ def test_route_type_to_locations_connects_types_across_locations end def test_route_type_to_locations_favors_longer_paths_through_necessary_locations - a = %{ + a = %| type T { id:ID! } type Query { a(id:ID!):T @stitch(key: "id") } - } - b = %{ + | + b = %| type T { id:ID! upc:ID! } type Query { ba(id:ID!):T @stitch(key: "id") bc(upc:ID!):T @stitch(key: "upc") } - } - c = %{ + | + c = %| type T { upc:ID! gid:ID! } type Query { cb(upc:ID!):T @stitch(key: "upc") cd(gid:ID!):T @stitch(key: "gid") } - } - d = %{ + | + d = %| type T { gid:ID! code:ID! } type Query { dc(gid:ID!):T @stitch(key: "gid") de(code:ID!):T @stitch(key: "code") } - } - e = %{ + | + e = %| type T { code:ID! id:ID! } type Query { ed(code:ID!):T @stitch(key: "code") ea(id:ID!):T @stitch(key: "id") } - } + | supergraph = compose_definitions({ "a" => a, "b" => b, "c" => c, "d" => d, "e" => e }) @@ -295,18 +295,18 @@ def test_route_type_to_locations_favors_longer_paths_through_necessary_locations end def test_route_type_to_locations_returns_nil_for_unreachable_locations - a = %{ + a = %| type T { upc:ID! } type Query { a(upc:ID!):T @stitch(key: "upc") } - } - b = %{ + | + b = %| type T { id:ID! } type Query { b(id:ID!):T @stitch(key: "id") } - } - c = %{ + | + c = %| type T { id:ID! } type Query { c(id:ID!):T @stitch(key: "id") } - } + | supergraph = compose_definitions({ "a" => a, "b" => b, "c" => c }) @@ -315,34 +315,51 @@ def test_route_type_to_locations_returns_nil_for_unreachable_locations assert_nil routes["a"] end - describe "GraphQL::Stitching::Supergraph" do + describe "#to_definition / #from_definition" do def setup - alpha = %{ + alpha = %| type T { id:ID! a:String } type Query { a(id:ID!):T @stitch(key: "id") } - } - bravo = %{ + | + bravo = %| type T { id:ID! b:String } type Query { b(id:ID!):T @stitch(key: "id") } - } + | @supergraph = compose_definitions({ "alpha" => alpha, "bravo" => bravo }) - @schema_sdl, @delegation_map = @supergraph.export + @schema_sdl = @supergraph.to_definition end - def test_exports_and_restores_supergraph - assert_equal @delegation_map["fields"], @supergraph.fields - assert_equal @delegation_map["boundaries"], @supergraph.boundaries.map {|k, b| [k, b.map(&:as_json)] }.to_h - assert_equal @delegation_map["locations"], @supergraph.locations + def test_to_definition_annotates_schema + @schema_sdl = squish_string(@schema_sdl) + assert @schema_sdl.include?("directive @boundary"), "expected @boundary directive" + assert @schema_sdl.include?("directive @source") + assert @schema_sdl.include?(squish_string(%| + type T @boundary(location: "alpha", key: "id", field: "a", arg: "id") + @boundary(location: "bravo", key: "id", field: "b", arg: "id") { + |)) + assert @schema_sdl.include?(%|id: ID! @source(location: "alpha") @source(location: "bravo")|) + assert @schema_sdl.include?(%|a: String @source(location: "alpha")|) + assert @schema_sdl.include?(%|b: String @source(location: "bravo")|) + assert @schema_sdl.include?(%|a(id: ID!): T @source(location: "alpha")|) + assert @schema_sdl.include?(%|b(id: ID!): T @source(location: "bravo")|) + end - supergraph_import = GraphQL::Stitching::Supergraph.from_export( - schema: @schema_sdl, - delegation_map: @delegation_map, - executables: { - "alpha" => Proc.new { true }, - "bravo" => Proc.new { true }, - } - ) + def test_to_definition_annotations_are_idempotent + @supergraph.to_definition + assert_equal 2, @supergraph.schema.get_type("T").directives.length + assert_equal 2, @supergraph.schema.get_type("T").get_field("id").directives.length + + @supergraph.to_definition + assert_equal 2, @supergraph.schema.get_type("T").directives.length + assert_equal 2, @supergraph.schema.get_type("T").get_field("id").directives.length + end + + def test_from_definition_restores_supergraph + supergraph_import = GraphQL::Stitching::Supergraph.from_definition(@schema_sdl, executables: { + "alpha" => Proc.new { true }, + "bravo" => Proc.new { true }, + }) assert_equal @supergraph.fields, supergraph_import.fields assert_equal @supergraph.boundaries, supergraph_import.boundaries @@ -351,40 +368,28 @@ def test_exports_and_restores_supergraph end def test_normalizes_executable_location_names - supergraph_import = GraphQL::Stitching::Supergraph.from_export( - schema: @schema_sdl, - delegation_map: @delegation_map, - executables: { - alpha: Proc.new { true }, - bravo: Proc.new { true }, - } - ) + supergraph_import = GraphQL::Stitching::Supergraph.from_definition(@schema_sdl, executables: { + alpha: Proc.new { true }, + bravo: Proc.new { true }, + }) assert_equal ["alpha", "bravo"], supergraph_import.locations.sort end def test_errors_for_invalid_executables assert_error "Invalid executable provided for location" do - GraphQL::Stitching::Supergraph.from_export( - schema: @schema_sdl, - delegation_map: @delegation_map, - executables: { - alpha: Proc.new { true }, - bravo: "nope", - } - ) + GraphQL::Stitching::Supergraph.from_definition(@schema_sdl, executables: { + alpha: Proc.new { true }, + bravo: "nope", + }) end end def test_errors_for_missing_executables assert_error "Invalid executable provided for location" do - GraphQL::Stitching::Supergraph.from_export( - schema: @schema_sdl, - delegation_map: @delegation_map, - executables: { - alpha: Proc.new { true }, - } - ) + GraphQL::Stitching::Supergraph.from_definition(@schema_sdl, executables: { + alpha: Proc.new { true }, + }) end end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 4fb7ccb9..d736d620 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -48,15 +48,12 @@ def compose_definitions(locations, options={}) GraphQL::Stitching::Composer.new(**options).perform(locations) end -def supergraph_from_schema(schema) - GraphQL::Stitching::Supergraph.from_export( - schema: schema, - delegation_map: { - "locations" => [], - "fields" => {}, - "boundaries" => {}, - }, - executables: {}, +def supergraph_from_schema(schema, fields: {}, boundaries: {}, executables: {}) + GraphQL::Stitching::Supergraph.new( + schema: schema.is_a?(String) ? GraphQL::Schema.from_definition(schema) : schema, + fields: fields, + boundaries: boundaries, + executables: executables, ) end