66import { Plugin } from "graphile-build" ;
77import printFederatedSchema from "./printFederatedSchema" ;
88import { 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