From 258720cc92a3c4e40807c4255667acc7cb577ff6 Mon Sep 17 00:00:00 2001 From: jgzuke Date: Sun, 30 Jun 2019 18:41:24 -0400 Subject: [PATCH 1/4] Use primary key fields if available for the @key directive --- src/index.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index ec04f85..239616b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -153,7 +153,7 @@ const AddKeyPlugin: Plugin = builder => { const { GraphQLObjectType: spec, Self, - scope: { isRootQuery }, + scope: { isRootQuery, pgIntrospection }, } = context; const NodeInterface = getTypeByName(inflection.builtin("Node")); @@ -169,7 +169,8 @@ const AddKeyPlugin: Plugin = builder => { build.federationEntityTypes.push(Self); /* - * We're going to add the `@key(fields: "nodeId")` directive to this type. + * We're going to add the key directive to this type. If this type has a + * defined primary key we will use that, otherwise `@key(fields: "nodeId")` * First, we need to generate an `astNode` as if the type was generateted * from a GraphQL SDL initially; then we assign this astNode to to the type * (via type mutation, ick) so that Apollo Federation's `printSchema` can @@ -179,8 +180,17 @@ const AddKeyPlugin: Plugin = builder => { ...ObjectTypeDefinition(spec), ...Self.astNode, }; + + const { + primaryKeyConstraint: { keyAttributes } + } = pgIntrospection; + const primaryKeyNames = keyAttributes.map(attr => inflection.column(attr)); + const keyName = primaryKeyNames.length + ? primaryKeyNames.join(' ') + : nodeIdFieldName; + astNode.directives.push( - Directive("key", { fields: StringValue(nodeIdFieldName) }) + Directive("key", { fields: StringValue(keyName) }) ); Self.astNode = astNode; From 6f4c760a3282e680d14fd74d351782331ee0caa3 Mon Sep 17 00:00:00 2001 From: jgzuke Date: Sat, 6 Jul 2019 18:35:11 -0400 Subject: [PATCH 2/4] Fix entity resolution --- src/index.ts | 66 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/src/index.ts b/src/index.ts index 239616b..c3018ef 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,8 +22,10 @@ const SchemaExtensionPlugin = makeExtendSchemaPlugin(build => { $$isQuery, $$nodeType, getTypeByName, + scopeByType, inflection, nodeIdFieldName, + getNodeIdForTypeAndIdentifiers, } = build; // Cache let Query: any; @@ -88,18 +90,60 @@ const SchemaExtensionPlugin = makeExtendSchemaPlugin(build => { if (!representation || typeof representation !== "object") { throw new Error("Invalid representation"); } - const { __typename, [nodeIdFieldName]: nodeId } = representation; - if (!__typename || typeof nodeId !== "string") { - throw new Error("Failed to interpret representation"); + + const { __typename } = representation; + if (!__typename) { + throw new Error( + "Failed to interpret representation, no typename" + ); + } + + if (nodeIdFieldName in representation) { + const { [nodeIdFieldName]: nodeId } = representation; + if (typeof nodeId !== "string") { + throw new Error( + "Failed to interpret representation, invalid nodeId" + ); + } + + return resolveNode( + nodeId, + build, + fieldContext, + data, + context, + resolveInfo + ); + } else { + // This only works with NodePlugin enabled + if (!getNodeIdForTypeAndIdentifiers) { + throw new Error("Failed to resolve node by identifiers"); + } + + const type = getTypeByName(__typename); + const { + pgIntrospection: { + primaryKeyConstraint: { keyAttributes: attrs }, + }, + } = scopeByType.get(type); + const keyNames = attrs.map(attr => inflection.column(attr)); + const identifiers = keyNames.map(name => representation[name]); + + const nodeId = getNodeIdForTypeAndIdentifiers.call( + build, + type, + ...identifiers + ); + + return resolveNode( + nodeId, + build, + fieldContext, + data, + context, + resolveInfo + ); } - return resolveNode( - nodeId, - build, - fieldContext, - data, - context, - resolveInfo - ); }); }, From ccf0a16f099288bd4fed5d6b25428e30921d1eb6 Mon Sep 17 00:00:00 2001 From: jgzuke Date: Sat, 6 Jul 2019 18:36:43 -0400 Subject: [PATCH 3/4] Fix lint issues --- src/index.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index c3018ef..6b8d11b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -226,16 +226,14 @@ const AddKeyPlugin: Plugin = builder => { }; const { - primaryKeyConstraint: { keyAttributes } + primaryKeyConstraint: { keyAttributes }, } = pgIntrospection; const primaryKeyNames = keyAttributes.map(attr => inflection.column(attr)); const keyName = primaryKeyNames.length - ? primaryKeyNames.join(' ') + ? primaryKeyNames.join(" ") : nodeIdFieldName; - astNode.directives.push( - Directive("key", { fields: StringValue(keyName) }) - ); + astNode.directives.push(Directive("key", { fields: StringValue(keyName) })); Self.astNode = astNode; // We're not changing the interfaces, so return them unmodified. From 59398556d7e8a4d8170d24389be54d15fd453652 Mon Sep 17 00:00:00 2001 From: jgzuke Date: Sat, 6 Jul 2019 18:39:11 -0400 Subject: [PATCH 4/4] Skip second map --- src/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 6b8d11b..0ab4687 100644 --- a/src/index.ts +++ b/src/index.ts @@ -126,8 +126,9 @@ const SchemaExtensionPlugin = makeExtendSchemaPlugin(build => { primaryKeyConstraint: { keyAttributes: attrs }, }, } = scopeByType.get(type); - const keyNames = attrs.map(attr => inflection.column(attr)); - const identifiers = keyNames.map(name => representation[name]); + const identifiers = attrs.map( + attr => representation[inflection.column(attr)] + ); const nodeId = getNodeIdForTypeAndIdentifiers.call( build,