Skip to content

Commit

Permalink
Fix generating misleading "value type" hint on entity interfaces (#2412)
Browse files Browse the repository at this point in the history
The code that generates the `INCONSISTENT_INTERFACE_VALUE_TYPE_FIELD`
was run inconditionally on all interfaces. However, with the
introduction of `@interfaceObject` and `@key` on interfaces, we now
essentially support "entity" interfaces, and it is misleading to
generate this hint for those interfaces. This commit fixes that.

Fixes #2397
  • Loading branch information
Sylvain Lebresne committed Feb 22, 2023
1 parent 4ebaf10 commit 71a07f3
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 4 deletions.
6 changes: 6 additions & 0 deletions .changeset/eighty-knives-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@apollo/composition": patch
---

Stop generating misleading "hint" regarding value type fields for interface types that are entity interfaces (they have a `@key` defined).

24 changes: 24 additions & 0 deletions composition-js/src/__tests__/hints.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,30 @@ test('hints on field of interface value type not being in all subgraphs', () =>
);
})

test('*No* hint on field of interface _with @key_ not being in all subgraphs', () => {
const subgraph1 = gql`
type Query {
a: Int
}
interface T @key(fields: "id") {
id: ID!
a: Int
b: Int
}
`;

const subgraph2 = gql`
type T @interfaceObject @key(fields: "id") {
id: ID!
a: Int
}
`;

const result = mergeDocuments(subgraph1, subgraph2);
expect(result).toNotRaiseHints();
})

test('hints on field of input object value type not being in all subgraphs', () => {
const subgraph1 = gql`
type Query {
Expand Down
15 changes: 11 additions & 4 deletions composition-js/src/merging/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1780,19 +1780,22 @@ class Merger {
}

private mergeInterface(sources: (InterfaceType | ObjectType | undefined)[], dest: InterfaceType) {
this.validateInterfaceKeys(sources, dest);
const hasKey = this.validateInterfaceKeys(sources, dest);
this.validateInterfaceObjects(sources, dest);

this.addFieldsShallow(sources, dest);
for (const destField of dest.fields()) {
this.hintOnInconsistentValueTypeField(sources, dest, destField);
if (!hasKey) {
this.hintOnInconsistentValueTypeField(sources, dest, destField);
}
const subgraphFields = sources.map(t => t?.field(destField.name));
const mergeContext = this.validateOverride(subgraphFields, destField);
this.mergeField(subgraphFields, destField, mergeContext);
}
}

private validateInterfaceKeys(sources: (InterfaceType | ObjectType | undefined)[], dest: InterfaceType) {
// Returns whether the interface has a key (even a non-resolvable one) in any subgraph.
private validateInterfaceKeys(sources: (InterfaceType | ObjectType | undefined)[], dest: InterfaceType): boolean {
// Remark: it might be ok to filter @inaccessible types in `supergraphImplementations`, but this requires
// some more thinking (and I'm not even sure it makes a practical difference given the rules for validity
// of @inaccessible) and it will be backward compatible to filter them later, while the reverse wouldn't
Expand All @@ -1801,12 +1804,15 @@ class Merger {

// Validate that if a source defines a (resolvable) @key on an interface, then that subgraph defines
// all the implementations of that interface in the supergraph.
let hasKey = false;
for (const [idx, source] of sources.entries()) {
if (!source || !isInterfaceType(source)) {
continue;
}
const sourceMetadata = this.subgraphs.values()[idx].metadata();
const resolvableKey = source.appliedDirectivesOf(sourceMetadata.keyDirective()).find((k) => k.arguments().resolvable !== false);
const keys = source.appliedDirectivesOf(sourceMetadata.keyDirective());
hasKey ||= keys.length > 0;
const resolvableKey = keys.find((k) => k.arguments().resolvable !== false);
if (!resolvableKey) {
continue;
}
Expand All @@ -1824,6 +1830,7 @@ class Merger {
));
}
}
return hasKey;
}

private validateInterfaceObjects(sources: (InterfaceType | ObjectType | undefined)[], dest: InterfaceType) {
Expand Down

0 comments on commit 71a07f3

Please sign in to comment.