diff --git a/integration-tests/tests/schema/contracts.spec.ts b/integration-tests/tests/schema/contracts.spec.ts index 6d1763d9c5..68ffd831bf 100644 --- a/integration-tests/tests/schema/contracts.spec.ts +++ b/integration-tests/tests/schema/contracts.spec.ts @@ -618,6 +618,77 @@ test('@external fields are always included if include @tag is used', async () => `); }); +test(`@external object's fields are do not have contract tag applied`, 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: ["@key", "@tag"]) + + type Query { + user: User @tag(name: "public") + } + + type User @key(fields: "id") { + id: ID! @tag(name: "public") + ssn: String + } + `, + source: 'user.graphql', + url: 'https://localhost:3000/graphql', + }, + { + raw: /* GraphQL */ ` + extend schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link( + url: "https://specs.apollo.dev/federation/v2.8" + import: ["@key", "@external", "@requires", "@tag"] + ) + + extend type User @external { + ssn: String + } + + type User @key(fields: "id") { + id: ID! + creditScore: Int @requires(fields: "ssn") @tag(name: "public") + } + `, + source: 'credit.graphql', + url: 'https://localhost:3001/graphql', + }, + ], + external: null, + contracts: [ + { + id: 'foo', + filter: { + removeUnreachableTypesFromPublicApiSchema: false, + exclude: null, + include: ['public'], + }, + }, + ], + }); + expect(result.errors).toEqual([]); + expect(result.contracts?.[0].errors).toEqual([]); + expect(result.contracts?.[0].sdl).toMatchInlineSnapshot(` + type User { + id: ID! + creditScore: Int + } + + type Query { + user: User + } + `); +}); + test('include with exclude is possible', async () => { const result = await client.composeAndValidate.mutate({ type: 'federation', diff --git a/packages/services/schema/src/lib/__tests__/federation-tag-extraction.spec.ts b/packages/services/schema/src/lib/__tests__/federation-tag-extraction.spec.ts index 3b4443294b..540c9c37d3 100644 --- a/packages/services/schema/src/lib/__tests__/federation-tag-extraction.spec.ts +++ b/packages/services/schema/src/lib/__tests__/federation-tag-extraction.spec.ts @@ -449,6 +449,38 @@ describe('applyTagFilterToInaccessibleTransformOnSubgraphSchema', () => { } `); }); + + test('external objects do not apply inaccessible based on tags', () => { + const filter: Federation2SubgraphDocumentNodeByTagsFilter = { + include: new Set(['public']), + exclude: new Set(), + }; + const sdl = parse(/* GraphQL */ ` + extend schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/federation/v2.8", import: ["@external"]) + + type Position @external { + x: Int! + y: Int! + } + `); + + const output = applyTagFilterToInaccessibleTransformOnSubgraphSchema( + sdl, + buildSchemaCoordinateTagRegister([sdl]), + filter, + ); + + expect(print(output.typeDefs)).toMatchInlineSnapshot(` + extend schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/federation/v2.8", import: ["@external"]) + + type Position @external { + x: Int! + y: Int! + } + `); + }); }); describe('interface type', () => { diff --git a/packages/services/schema/src/lib/federation-tag-extraction.ts b/packages/services/schema/src/lib/federation-tag-extraction.ts index 80eaa0be3f..da9e843bf3 100644 --- a/packages/services/schema/src/lib/federation-tag-extraction.ts +++ b/packages/services/schema/src/lib/federation-tag-extraction.ts @@ -144,11 +144,6 @@ export function applyTagFilterToInaccessibleTransformOnSubgraphSchema( fieldLikeNode: InputValueDefinitionNode | FieldDefinitionNode, node: InputValueDefinitionNode, ) { - // Check for external tag because we cannot contribute directives to external fields. - if (node.directives?.find(d => d.name.value === externalDirectiveName)) { - return node; - } - const tagsOnNode = getTagsForSchemaCoordinate( `${objectLikeNode.name.value}.${fieldLikeNode.name.value}(${node.name.value}:)`, ); @@ -204,6 +199,7 @@ export function applyTagFilterToInaccessibleTransformOnSubgraphSchema( for (const node of nodes) { const tagsOnNode = getTagsForSchemaCoordinate(node.name.value); + const nodeIsExternal = !!node.directives?.find(d => d.name.value === externalDirectiveName); let newNode = { ...node, @@ -222,7 +218,10 @@ export function applyTagFilterToInaccessibleTransformOnSubgraphSchema( } // Check for external tag because we cannot contribute directives to external fields. - if (fieldNode.directives?.find(d => d.name.value === externalDirectiveName)) { + const fieldIsExternal = !!fieldNode.directives?.find( + d => d.name.value === externalDirectiveName, + ); + if (nodeIsExternal || fieldIsExternal) { return fieldNode; }