diff --git a/.changeset/dull-goats-brush.md b/.changeset/dull-goats-brush.md new file mode 100644 index 0000000000..16df369233 --- /dev/null +++ b/.changeset/dull-goats-brush.md @@ -0,0 +1,5 @@ +--- +'hive': patch +--- + +Fix failing schema contract composition. diff --git a/integration-tests/tests/schema/contracts.spec.ts b/integration-tests/tests/schema/contracts.spec.ts index 06f1213ffc..927748edd9 100644 --- a/integration-tests/tests/schema/contracts.spec.ts +++ b/integration-tests/tests/schema/contracts.spec.ts @@ -348,3 +348,47 @@ test('failed contract composition has errors and no sdl and supergraph', async ( expect(result.contracts?.[0].supergraph).toEqual(null); expect(result.contracts?.[0].errors).toBeDefined(); }); + +test('type is marked as inaccessible if all fields are inaccessible and the type is not used', async () => { + const result = await client.composeAndValidate.mutate({ + type: 'federation', + native: true, + schemas: [ + { + raw: /* GraphQL */ ` + extend schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/federation/v2.8", import: ["@tag"]) + + type Query { + hello: String @tag(name: "public") + } + + type Brr { + a: String + b: String + c: String + } + `, + source: 'foo.graphql', + url: null, + }, + ], + external: null, + contracts: [ + { + id: 'foo', + filter: { + removeUnreachableTypesFromPublicApiSchema: true, + exclude: null, + include: ['public'], + }, + }, + ], + }); + + expect(result.contracts?.[0].errors).toEqual([]); + expect(result.contracts?.[0].supergraph).toContain( + 'type Brr @join__type(graph: FOO_GRAPHQL) @inaccessible {', + ); +}); diff --git a/packages/services/schema/src/orchestrators.ts b/packages/services/schema/src/orchestrators.ts index 6e98f888c6..d449c39c34 100644 --- a/packages/services/schema/src/orchestrators.ts +++ b/packages/services/schema/src/orchestrators.ts @@ -31,10 +31,9 @@ import { } from './lib/compose'; import { CompositionErrorSource, errorWithSource, toValidationError } from './lib/errors'; import { - applyTagFilterToInaccessibleTransformOnSubgraphSchema, + applyTagFilterOnSubgraphs, createTagDirectiveNameExtractionStrategy, extractTagsFromDocument, - Federation2SubgraphDocumentNodeByTagsFilter, } from './lib/federation-tag-extraction'; import { extractMetadata, mergeMetadata } from './lib/metadata-extraction'; import { SetMap } from './lib/setmap'; @@ -348,20 +347,9 @@ const createFederation: ( const contractResults = await Promise.all( contracts.map(async contract => { // apply contracts to replace tags with inaccessible directives - const filteredSubgraphs = subgraphs.map(subgraph => { - const filter: Federation2SubgraphDocumentNodeByTagsFilter = { - include: new Set(contract.filter.include), - exclude: new Set(contract.filter.exclude), - }; - const filteredSubgraph = applyTagFilterToInaccessibleTransformOnSubgraphSchema( - subgraph.typeDefs, - filter, - ); - return { - // @note Although it can differ from the supergraph's, ignore metadata on contracts. - ...subgraph, - typeDefs: filteredSubgraph.typeDefs, - }; + const filteredSubgraphs = applyTagFilterOnSubgraphs(subgraphs, { + include: new Set(contract.filter.include), + exclude: new Set(contract.filter.exclude), }); // attempt to compose the contract filtered subgraph