1+ import { UpdateArgs } from "./mongoDbUpdate" ;
2+ import { GraphQLObjectType , GraphQLType , GraphQLNonNull , GraphQLList , GraphQLFieldMap } from "graphql" ;
3+ import { isNonNullType , getInnerType , flatten , isListType } from "./common" ;
4+
5+ export interface UpdateField {
6+ [ key : string ] : UpdateField | UpdateField [ ] | 1
7+ }
8+
9+ export function validateUpdateArgs ( updateArgs : UpdateArgs , graphQLType : GraphQLObjectType ) : void {
10+ let errors : string [ ] = [ ] ;
11+
12+ errors = [ ...errors , ...validateNonNullableFields ( Object . values ( updateArgs ) , graphQLType ) ] ;
13+
14+ if ( errors . length > 0 ) {
15+ throw errors . join ( "\n" ) ;
16+ }
17+ }
18+
19+ export function validateNonNullableFields ( objects : object [ ] , graphQLType : GraphQLObjectType , path : string [ ] = [ ] ) : string [ ] {
20+ const typeFields = graphQLType . getFields ( ) ;
21+
22+ const errors = validateNonNullableFieldsAssert ( objects , typeFields , path ) ;
23+
24+ return [ ...errors , ...validateNonNullableFieldsTraverse ( objects , typeFields , path ) ] ;
25+ }
26+
27+ export function validateNonNullableFieldsAssert ( objects : object [ ] , typeFields : GraphQLFieldMap < any , any > , path : string [ ] = [ ] ) : string [ ] {
28+ return Object
29+ . keys ( typeFields )
30+ . map ( key => ( { key, type : typeFields [ key ] . type } ) )
31+ . filter ( field => isNonNullType ( field . type ) )
32+ . reduce ( ( agg , field ) => {
33+ let fieldPath = [ ...path , field . key ] . join ( "." ) ;
34+ const fieldValues = objects . map ( _ => _ [ field . key ] ) . filter ( _ => _ !== undefined ) ;
35+ if ( field . type instanceof GraphQLNonNull ) {
36+ if ( fieldValues . some ( _ => _ === null ) )
37+ return [ ...agg , `Non-nullable field "${ fieldPath } " is set to null` ] ;
38+ if ( fieldValues . length === 0 )
39+ return [ ...agg , `Missing non-nullable field "${ fieldPath } "` ] ;
40+ }
41+ if ( isListType ( field . type ) && ! validateNonNullListField ( fieldValues , field . type ) ) {
42+ return [ ...agg , `Non-nullable element of array "${ fieldPath } " is set to null` ] ;
43+ }
44+
45+ return agg ;
46+ } , [ ] ) ;
47+ }
48+
49+ export function validateNonNullListField ( fieldValues : object [ ] , type : GraphQLType ) : boolean {
50+ if ( type instanceof GraphQLNonNull ) {
51+ if ( fieldValues . some ( _ => _ === null ) ) {
52+ return false ;
53+ }
54+
55+ return validateNonNullListField ( fieldValues , type . ofType ) ;
56+ }
57+
58+ if ( type instanceof GraphQLList ) {
59+ return validateNonNullListField ( flatten ( fieldValues . filter ( _ => _ ) as object [ ] [ ] ) , type . ofType ) ;
60+ }
61+
62+ return true ;
63+ }
64+
65+ export function validateNonNullableFieldsTraverse ( objects : object [ ] , typeFields : GraphQLFieldMap < any , any > , path : string [ ] = [ ] ) : string [ ] {
66+ let keys : string [ ] = Array . from ( new Set ( flatten ( objects . map ( _ => Object . keys ( _ ) ) ) ) ) ;
67+
68+ return keys . reduce ( ( agg , key ) => {
69+ const field = typeFields [ key ] ;
70+ const type = field . type ;
71+ const innerType = getInnerType ( type ) ;
72+
73+ if ( ! ( innerType instanceof GraphQLObjectType ) || field . resolve ) {
74+ return agg ;
75+ }
76+
77+ const newPath = [ ...path , key ] ;
78+ const values = objects . map ( _ => _ [ key ] ) . filter ( _ => _ ) ;
79+
80+ if ( isListType ( type ) ) {
81+ return [ ...agg , ...flatten ( flattenListField ( values , type ) . map ( _ => validateNonNullableFields ( [ _ ] , innerType , newPath ) ) ) ] ;
82+ } else {
83+ return [ ...agg , ...validateNonNullableFields ( values , innerType , newPath ) ] ;
84+ }
85+ } , [ ] ) ;
86+ }
87+
88+ export function flattenListField ( objects : object [ ] , type : GraphQLType ) : object [ ] {
89+ if ( type instanceof GraphQLNonNull ) {
90+ return flattenListField ( objects , type . ofType ) ;
91+ }
92+
93+ if ( type instanceof GraphQLList ) {
94+ return flattenListField ( flatten ( objects as object [ ] [ ] ) . filter ( _ => _ ) , type . ofType ) ;
95+ }
96+
97+ return objects ;
98+ }
0 commit comments