33 GraphQLArgument ,
44 GraphQLNamedType ,
55 GraphQLObjectType ,
6- GraphQLScalarType ,
76 GraphQLInterfaceType ,
8- GraphQLList ,
97 GraphQLOutputType ,
108 isCompositeType ,
119 isEnumType ,
@@ -17,8 +15,10 @@ import {
1715 isUnionType ,
1816 Kind ,
1917 ValueNode ,
18+ GraphQLUnionType ,
19+ GraphQLFieldMap ,
20+ GraphQLField ,
2021} from 'graphql' ;
21- import { Maybe } from 'graphql/jsutils/Maybe' ;
2222import { ObjMap } from 'graphql/jsutils/ObjMap' ;
2323import { GraphQLSchema } from 'graphql/type/schema' ;
2424import {
@@ -27,6 +27,7 @@ import {
2727 TypeWeightObject ,
2828 Variables ,
2929 Type ,
30+ Fields ,
3031} from '../@types/buildTypeWeights' ;
3132
3233export const KEYWORDS = [ 'first' , 'last' , 'limit' ] ;
@@ -86,13 +87,9 @@ function parseObjectFields(
8687 ) {
8788 result . fields [ field ] = {
8889 weight : typeWeights . scalar ,
90+ // resolveTo: fields[field].name.toLowerCase(),
8991 } ;
90- } else if (
91- isInterfaceType ( fieldType ) ||
92- isUnionType ( fieldType ) ||
93- isEnumType ( fieldType ) ||
94- isObjectType ( fieldType )
95- ) {
92+ } else if ( isInterfaceType ( fieldType ) || isEnumType ( fieldType ) || isObjectType ( fieldType ) ) {
9693 result . fields [ field ] = {
9794 resolveTo : fieldType . name . toLocaleLowerCase ( ) ,
9895 } ;
@@ -157,14 +154,43 @@ function parseObjectFields(
157154 }
158155 } ) ;
159156 }
157+ } else if ( isUnionType ( fieldType ) ) {
158+ // Users must query union types using inline fragments to resolve field specific to one of the types in the union
159+ // however, if a type is shared by all types in the union it can be queried outside of the inline fragment
160+ // any common fields should be added to fields on the union type itself in addition to the comprising types
161+ // Get all types in the union
162+ // iterate through all types creating a set of type names
163+ // add resulting set to fields
164+ // FIXME: What happens if two types share a name that resolve to different types => invalid query?
165+ result . fields [ field ] = {
166+ resolveTo : fieldType . name . toLocaleLowerCase ( ) ,
167+ } ;
160168 } else {
161169 // ? what else can get through here
170+ throw new Error ( `ERROR: buildTypeWeight: Unsupported field type: ${ fieldType } ` ) ;
162171 }
163172 } ) ;
164173
165174 return result ;
166175}
167176
177+ /**
178+ * Recursively compares two types for type equality based on name
179+ * @param a
180+ * @param b
181+ * @returns
182+ */
183+ function compareTypes ( a : GraphQLOutputType , b : GraphQLOutputType ) : boolean {
184+ return (
185+ ( isObjectType ( b ) && isObjectType ( a ) && a . name === b . name ) ||
186+ ( isUnionType ( b ) && isUnionType ( a ) && a . name === b . name ) ||
187+ ( isInterfaceType ( b ) && isInterfaceType ( a ) && a . name === b . name ) ||
188+ ( isScalarType ( b ) && isScalarType ( a ) && a . name === b . name ) ||
189+ ( isListType ( b ) && isListType ( a ) && compareTypes ( b . ofType , a . ofType ) ) ||
190+ ( isNonNullType ( b ) && isNonNullType ( a ) && compareTypes ( a . ofType , b . ofType ) )
191+ ) ;
192+ }
193+
168194/**
169195 * Parses all types in the provided schema object excempt for Query, Mutation
170196 * and built in types that begin with '__' and outputs a new TypeWeightObject
@@ -177,6 +203,8 @@ function parseTypes(schema: GraphQLSchema, typeWeights: TypeWeightSet): TypeWeig
177203
178204 const result : TypeWeightObject = { } ;
179205
206+ const unions : GraphQLUnionType [ ] = [ ] ;
207+
180208 // Handle Object, Interface, Enum and Union types
181209 Object . keys ( typeMap ) . forEach ( ( type ) => {
182210 const typeName : string = type . toLowerCase ( ) ;
@@ -193,18 +221,103 @@ function parseTypes(schema: GraphQLSchema, typeWeights: TypeWeightSet): TypeWeig
193221 weight : typeWeights . scalar ,
194222 } ;
195223 } else if ( isUnionType ( currentType ) ) {
196- // FIXME: will need information on fields inorder calculate comlpextiy
197- result [ typeName ] = {
198- fields : { } ,
199- weight : typeWeights . object ,
200- } ;
224+ unions . push ( currentType ) ;
201225 } else {
226+ // FIXME: Scalar types are listed here throw new Error(`ERROR: buildTypeWeight: Unsupported type: ${currentType}`);
202227 // ? what else can get through here
203228 // ? inputTypes?
204229 }
205230 }
206231 } ) ;
207232
233+ type FieldMap = { [ index : string ] : GraphQLOutputType } ;
234+ type CommonFields = { [ index : string ] : Type } ;
235+
236+ unions . forEach ( ( unionType : GraphQLUnionType ) => {
237+ /** Start with the fields for the first object. Store fieldnamd and type
238+ * reduce by selecting fields common to each type
239+ * compare both fieldname and output type accounting for lists and non-nulls
240+ * for object
241+ * compare name of output type
242+ * for lists
243+ * compare ofType and ofType name if not onother list/non-null
244+ * for non-nulls
245+ * compare oftype and ofTypeName (if not another non-null)
246+ * */
247+
248+ // types is an array mapping each field name to it's respective output type
249+ const types : FieldMap [ ] = unionType . getTypes ( ) . map ( ( objectType : GraphQLObjectType ) => {
250+ const fields : GraphQLFieldMap < any , any > = objectType . getFields ( ) ;
251+
252+ const fieldMap : { [ index : string ] : GraphQLOutputType } = { } ;
253+ Object . keys ( fields ) . forEach ( ( field : string ) => {
254+ fieldMap [ field ] = fields [ field ] . type ;
255+ } ) ;
256+ return fieldMap ;
257+ } ) ;
258+
259+ const common : FieldMap = types . reduce ( ( prev : FieldMap , fieldMap : FieldMap ) : FieldMap => {
260+ // iterate through the field map checking the types for any common field names
261+ const commonFields : FieldMap = { } ;
262+ Object . keys ( prev ) . forEach ( ( field : string ) => {
263+ if ( fieldMap [ field ] ) {
264+ if ( compareTypes ( prev [ field ] , fieldMap [ field ] ) ) {
265+ // they match add the type to the next set
266+ commonFields [ field ] = prev [ field ] ;
267+ }
268+ }
269+ } ) ;
270+ return commonFields ;
271+ } ) ;
272+
273+ // transform commonFields into the correct format
274+ const fieldTypes : Fields = { } ;
275+
276+ Object . keys ( common ) . forEach ( ( field : string ) => {
277+ // if a scalar => weight
278+ // object => resolveTo
279+ // list => // resolveTo + weight(function)
280+ const current = common [ field ] ;
281+ if ( isScalarType ( current ) ) {
282+ fieldTypes [ field ] = {
283+ weight : typeWeights . scalar ,
284+ } ;
285+ }
286+ // else if (isObjectType(current)) {
287+ // fieldTypes[field] = {
288+ // resolveTo: current.name,
289+ // };
290+ // }
291+ else if ( isListType ( current ) ) {
292+ throw new Error ( 'list types not supported on unions' ) ;
293+ fieldTypes [ field ] = {
294+ resolveTo : 'test' , // get resolve type problem is recursive data structure (i.e. list of lists)
295+ // weight: TODO: Get the function for resolving
296+ } ;
297+ } else if ( isNonNullType ( current ) ) {
298+ throw new Error ( 'non null types not supported on unions' ) ;
299+ // TODO: also a recursive data structure
300+ } else {
301+ throw new Error ( 'Unandled union type. Should never get here' ) ;
302+ }
303+ } ) ;
304+ result [ unionType . name . toLowerCase ( ) ] = {
305+ fields : fieldTypes ,
306+ weight : typeWeights . object ,
307+ } ;
308+
309+ //
310+ // objects are not. they exist at the root.
311+ // FIXME: Is it worth adding objects as a field?
312+ // yes, I think so => refactor fieldNode parser
313+ // if it resolves to object then add commonFields set
314+ // i think we have this already.
315+ // double check the non-null tests
316+ // commonFields.add({
317+ // weight,
318+ // });
319+ } ) ;
320+
208321 return result ;
209322}
210323
0 commit comments