Skip to content

Commit a3645eb

Browse files
author
Lenz Weber
committed
try to use primary key as additional @key directive
1 parent 53e7448 commit a3645eb

2 files changed

Lines changed: 102 additions & 13 deletions

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
"typescript": "^3.4.4"
5151
},
5252
"peerDependencies": {
53-
"graphile-build": "^4.4.2"
53+
"graphile-build": "^4.4.2",
54+
"graphile-build-pg": "^4.4.5"
5455
},
5556
"files": [
5657
"build"

src/index.ts

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
import { Plugin } from "graphile-build";
77
import printFederatedSchema from "./printFederatedSchema";
88
import { ObjectTypeDefinition, Directive, StringValue } from "./AST";
9+
import { PgAttribute, PgClass } from "graphile-build-pg";
910

1011
/**
1112
* This plugin installs the schema outlined in the Apollo Federation spec, and
@@ -22,8 +23,10 @@ const SchemaExtensionPlugin = makeExtendSchemaPlugin(build => {
2223
$$isQuery,
2324
$$nodeType,
2425
getTypeByName,
26+
scopeByType,
2527
inflection,
2628
nodeIdFieldName,
29+
pgSql: sql,
2730
} = build;
2831
// Cache
2932
let Query: any;
@@ -84,22 +87,67 @@ const SchemaExtensionPlugin = makeExtendSchemaPlugin(build => {
8487
const {
8588
graphile: { fieldContext },
8689
} = resolveInfo;
87-
return representations.map((representation: any) => {
90+
return representations.map(async (representation: any) => {
8891
if (!representation || typeof representation !== "object") {
8992
throw new Error("Invalid representation");
9093
}
94+
9195
const { __typename, [nodeIdFieldName]: nodeId } = representation;
92-
if (!__typename || typeof nodeId !== "string") {
93-
throw new Error("Failed to interpret representation");
96+
if (!__typename) {
97+
throw new Error(
98+
"Failed to interpret representation, no typename"
99+
);
100+
}
101+
if (nodeId) {
102+
if (typeof nodeId !== "string") {
103+
throw new Error(
104+
"Failed to interpret representation, invalid nodeId"
105+
);
106+
}
107+
const x = resolveNode(
108+
nodeId,
109+
build,
110+
fieldContext,
111+
data,
112+
context,
113+
resolveInfo
114+
);
115+
116+
return x;
117+
} else {
118+
const type = getTypeByName(__typename);
119+
const { pgIntrospection: table } = scopeByType.get(type);
120+
121+
if (!table.primaryKeyConstraint) {
122+
throw new Error("Failed to interpret representation");
123+
}
124+
const {
125+
primaryKeyConstraint: { keyAttributes },
126+
} = table;
127+
128+
const whereClause = sql.fragment`(${sql.join(
129+
keyAttributes.map(
130+
(attr: PgAttribute) =>
131+
sql.fragment`${sql.identifier(attr.name)} = ${sql.value(
132+
representation[inflection.column(attr)]
133+
)}`
134+
),
135+
") and ("
136+
)})`;
137+
138+
const rows = await resolveInfo.graphile.selectGraphQLResultFromTable(
139+
sql.identifier(table.namespace, table.name),
140+
(_alias, queryBuilder) => {
141+
queryBuilder.where(whereClause);
142+
}
143+
);
144+
145+
if (rows.count !== 1) {
146+
throw new Error("Failed to interpret representation");
147+
}
148+
149+
return rows[0];
94150
}
95-
return resolveNode(
96-
nodeId,
97-
build,
98-
fieldContext,
99-
data,
100-
context,
101-
resolveInfo
102-
);
103151
});
104152
},
105153

@@ -131,7 +179,7 @@ const SchemaExtensionPlugin = makeExtendSchemaPlugin(build => {
131179
serialize(value: any) {
132180
return value;
133181
},
134-
}),
182+
}) as any,
135183
},
136184
};
137185
});
@@ -147,6 +195,44 @@ const AddKeyPlugin: Plugin = builder => {
147195
return build;
148196
});
149197

198+
builder.hook("GraphQLObjectType", (type, build, context) => {
199+
const {
200+
scope: { pgIntrospection, isPgRowType },
201+
} = context;
202+
203+
const { inflection } = build;
204+
205+
if (
206+
!(
207+
isPgRowType &&
208+
pgIntrospection.isSelectable &&
209+
pgIntrospection.namespace &&
210+
pgIntrospection.primaryKeyConstraint
211+
)
212+
) {
213+
return type;
214+
}
215+
216+
const primaryKeyNames = pgIntrospection.primaryKeyConstraint.keyAttributes.map(
217+
(attr: PgAttribute) => inflection.column(attr)
218+
);
219+
220+
if (!primaryKeyNames.length) {
221+
return type;
222+
}
223+
224+
const astNode = {
225+
...ObjectTypeDefinition({ name: type.name }),
226+
...type.astNode,
227+
};
228+
229+
(astNode.directives as any).push(
230+
Directive("key", { fields: StringValue(primaryKeyNames.join(" ")) })
231+
);
232+
233+
return { ...type, astNode } as typeof type;
234+
});
235+
150236
// Find out what types implement the Node interface
151237
builder.hook("GraphQLObjectType:interfaces", (interfaces, build, context) => {
152238
const { getTypeByName, inflection, nodeIdFieldName } = build;
@@ -197,6 +283,8 @@ const AddKeyPlugin: Plugin = builder => {
197283
}
198284
const { federationEntityTypes } = build;
199285

286+
console.log(federationEntityTypes.map((type: any) => type.name));
287+
200288
// Add our types to the entity types
201289
return [...types, ...federationEntityTypes];
202290
});

0 commit comments

Comments
 (0)