mirror of
https://github.com/graphql-hive/console
synced 2026-05-24 01:28:32 +00:00
fix: mark types whose fields are all inaccessible as inaccessible if … (#3927)
This commit is contained in:
parent
8ca25fb114
commit
6ec13ab203
3 changed files with 80 additions and 41 deletions
|
|
@ -1742,6 +1742,59 @@ describe('applyTagFilterOnSubgraphs', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
test('object type that is only defined in one subgraph is inaccessible', () => {
|
||||
const filter: Federation2SubgraphDocumentNodeByTagsFilter = {
|
||||
include: new Set(['tag1']),
|
||||
exclude: null,
|
||||
};
|
||||
const typeDefs1 = parse(/* GraphQL */ `
|
||||
extend schema @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@tag"])
|
||||
|
||||
type Query {
|
||||
helloWorld: String @tag(name: "tag1")
|
||||
}
|
||||
`);
|
||||
|
||||
const typeDefs2 = parse(/* GraphQL */ `
|
||||
extend schema @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key"])
|
||||
|
||||
type Query {
|
||||
users: [User]
|
||||
}
|
||||
|
||||
type User @key(fields: "id") {
|
||||
id: ID!
|
||||
}
|
||||
`);
|
||||
|
||||
const result = applyTagFilterOnSubgraphs(
|
||||
[
|
||||
{ typeDefs: typeDefs1, name: 'subgraph1' },
|
||||
{ typeDefs: typeDefs2, name: 'subgraph2' },
|
||||
],
|
||||
filter,
|
||||
);
|
||||
|
||||
expect(print(result[0].typeDefs)).toMatchInlineSnapshot(`
|
||||
extend schema @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@tag"])
|
||||
|
||||
type Query {
|
||||
helloWorld: String
|
||||
}
|
||||
`);
|
||||
expect(print(result[1].typeDefs)).toMatchInlineSnapshot(`
|
||||
extend schema @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key"])
|
||||
|
||||
type Query {
|
||||
users: [User] @federation__inaccessible
|
||||
}
|
||||
|
||||
type User @key(fields: "id") @federation__inaccessible {
|
||||
id: ID! @federation__inaccessible
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('object types are accessible because at least one field is accessible in one subgraph, but not in another', () => {
|
||||
const filter: Federation2SubgraphDocumentNodeByTagsFilter = {
|
||||
include: new Set(['tag1']),
|
||||
|
|
@ -1778,7 +1831,7 @@ describe('applyTagFilterOnSubgraphs', () => {
|
|||
field2: Type1! @inaccessible
|
||||
}
|
||||
|
||||
type Type1 {
|
||||
type Type1 @inaccessible {
|
||||
field1: String! @inaccessible
|
||||
}
|
||||
`);
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ export function applyTagFilterToInaccessibleTransformOnSubgraphSchema(
|
|||
filter: Federation2SubgraphDocumentNodeByTagsFilter,
|
||||
): {
|
||||
typeDefs: DocumentNode;
|
||||
typesWhereAllFieldsAreInaccessible: Set<string>;
|
||||
typesWithAllFieldsInaccessible: Map<string, boolean>;
|
||||
transformTagDirectives: ReturnType<typeof createTransformTagDirectives>;
|
||||
} {
|
||||
const tagDirectiveName = getFederationTagDirectiveNameForSubgraphSDL(documentNode);
|
||||
|
|
@ -419,47 +419,13 @@ export function applyTagFilterToInaccessibleTransformOnSubgraphSchema(
|
|||
typesWithAllFieldsInaccessibleTracker.delete(rootTypeName);
|
||||
}
|
||||
|
||||
const typesWhereAllFieldsAreInaccessible = new Set(
|
||||
Array.from(typesWithAllFieldsInaccessibleTracker).map(([typeName, isAllFieldsInaccessible]) =>
|
||||
isAllFieldsInaccessible ? typeName : null,
|
||||
),
|
||||
);
|
||||
typesWhereAllFieldsAreInaccessible.delete(null);
|
||||
|
||||
return {
|
||||
typeDefs,
|
||||
typesWhereAllFieldsAreInaccessible: typesWhereAllFieldsAreInaccessible as Set<string>,
|
||||
typesWithAllFieldsInaccessible: typesWithAllFieldsInaccessibleTracker,
|
||||
transformTagDirectives,
|
||||
};
|
||||
}
|
||||
|
||||
function intersectSets(sets: Set<string>[]): Set<string> {
|
||||
if (sets.length === 0) {
|
||||
// If the input array is empty, return an empty set
|
||||
return new Set();
|
||||
}
|
||||
|
||||
// Create a copy of the first set to modify
|
||||
const result = new Set(sets[0]);
|
||||
|
||||
// Iterate through the rest of the sets
|
||||
for (let i = 1; i < sets.length; i++) {
|
||||
// Filter the current set and keep only elements present in the result set
|
||||
result.forEach(value => {
|
||||
if (!sets[i].has(value)) {
|
||||
result.delete(value);
|
||||
}
|
||||
});
|
||||
|
||||
// If the result set becomes empty, no need to continue
|
||||
if (result.size === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function makeTypesFromSetInaccessible(
|
||||
documentNode: DocumentNode,
|
||||
types: Set<string>,
|
||||
|
|
@ -511,9 +477,29 @@ export function applyTagFilterOnSubgraphs<
|
|||
...applyTagFilterToInaccessibleTransformOnSubgraphSchema(subgraph.typeDefs, filter),
|
||||
}));
|
||||
|
||||
const intersectionOfTypesWhereAllFieldsAreInaccessible = intersectSets(
|
||||
filteredSubgraphs.map(subgraph => subgraph.typesWhereAllFieldsAreInaccessible),
|
||||
);
|
||||
const intersectionOfTypesWhereAllFieldsAreInaccessible = new Set<string>();
|
||||
// We need to traverse all subgraphs to find the intersection of types where all fields are inaccessible.
|
||||
// If a type is not present in any other subgraph, we can safely mark it as inaccessible.
|
||||
filteredSubgraphs.forEach(subgraph => {
|
||||
const otherSubgraphs = filteredSubgraphs.filter(sub => sub !== subgraph);
|
||||
|
||||
for (const [type, allFieldsInaccessible] of subgraph.typesWithAllFieldsInaccessible) {
|
||||
if (
|
||||
allFieldsInaccessible &&
|
||||
otherSubgraphs.every(
|
||||
sub =>
|
||||
!sub.typesWithAllFieldsInaccessible.has(type) ||
|
||||
sub.typesWithAllFieldsInaccessible.get(type) === true,
|
||||
)
|
||||
) {
|
||||
intersectionOfTypesWhereAllFieldsAreInaccessible.add(type);
|
||||
}
|
||||
// let's not visit this type a second time...
|
||||
otherSubgraphs.forEach(sub => {
|
||||
sub.typesWithAllFieldsInaccessible.delete(type);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!intersectionOfTypesWhereAllFieldsAreInaccessible.size) {
|
||||
filteredSubgraphs;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: Schema Contracts for Federation
|
||||
description: We added support schema contracts for Federation projects.
|
||||
date: 2023-12-05
|
||||
date: 2024-02-06
|
||||
authors: [laurin]
|
||||
---
|
||||
|
||||
Loading…
Reference in a new issue