@@ -32,111 +32,168 @@ const getLogicalModelsFromProperties = (
32
32
) : LogicalModel [ ] => {
33
33
const logicalModels : LogicalModel [ ] = [ ] ;
34
34
const fields : LogicalModelField [ ] = [ ] ;
35
-
36
- type ItemSchemaTypes = {
37
- anyOf ?: Array < { type : string } > ;
38
- type ?: string ;
39
- } ;
40
- const isMixedArray = ( itemsSchema : ItemSchemaTypes ) : boolean => {
41
- if ( itemsSchema . anyOf ) {
42
- const types = itemsSchema . anyOf . map ( subSchema => subSchema . type ) ;
43
- return (
44
- types . includes ( 'object' ) && ! types . every ( type => type === 'object' )
45
- ) ;
46
- }
47
- return false ;
48
- } ;
49
-
50
35
for ( const [ rawFieldName , fieldSchema ] of Object . entries ( properties ) ) {
51
36
const fieldName = sanitizeGraphQLFieldNames ( rawFieldName ) ;
52
-
53
- if ( fieldName === '_id' ) {
54
- fields . push ( {
55
- name : fieldName ,
56
- type : {
57
- scalar : 'objectId' ,
58
- nullable : false ,
59
- } ,
60
- } ) ;
61
- continue ;
62
- }
63
-
64
37
const nullable = ! requiredProperties . includes ( fieldName ) ;
65
38
const logicalModelPath = parentName
66
39
? `${ parentName } _${ fieldName } `
67
40
: fieldName ;
68
41
69
- if ( fieldSchema . type === 'object' ) {
70
- if ( fieldSchema . properties ) {
71
- const newLogicalModels = getLogicalModelsFromProperties (
72
- collectionName ,
73
- `${ collectionName } _${ logicalModelPath } ` ,
74
- fieldSchema . properties ,
75
- fieldSchema . required ,
76
- logicalModelPath
77
- ) ;
78
-
79
- logicalModels . push ( ...newLogicalModels ) ;
42
+ // Get scalars from MongoDB objectid and date objects
43
+ const handleMongoDBFieldTypes = (
44
+ properties : ObjectSchema [ 'properties' ]
45
+ ) : { type : 'objectId' | 'date' | 'string' | 'none' ; name ?: string } => {
46
+ if ( ! properties ) {
47
+ return { type : 'string' } ;
48
+ }
49
+ if ( Object . prototype . hasOwnProperty . call ( properties , '$oid' ) ) {
50
+ return { type : 'objectId' } ;
51
+ }
52
+ if ( Object . prototype . hasOwnProperty . call ( properties , '$date' ) ) {
53
+ return { type : 'date' } ;
54
+ }
55
+ return { type : 'none' } ;
56
+ } ;
80
57
58
+ if ( fieldSchema . type === 'object' ) {
59
+ // Seperate MongoDB objectid and date scalars from logical model objects
60
+ const mongoDBFieldType = handleMongoDBFieldTypes ( fieldSchema . properties ) ;
61
+ if ( mongoDBFieldType . type !== 'none' ) {
81
62
fields . push ( {
82
63
name : fieldName ,
83
64
type : {
84
- logical_model : `${ collectionName } _${ logicalModelPath } ` ,
85
- nullable,
86
- } ,
87
- } ) ;
88
- } else {
89
- // Empty object just being casted to `string`
90
- fields . push ( {
91
- name : fieldName ,
92
- type : {
93
- scalar : 'string' ,
94
- nullable,
65
+ scalar : mongoDBFieldType . type ,
66
+ nullable : false ,
95
67
} ,
96
68
} ) ;
69
+ continue ;
97
70
}
71
+ // Make new logical model
72
+ const newLogicalModels = getLogicalModelsFromProperties (
73
+ collectionName ,
74
+ `${ collectionName } _${ logicalModelPath } ` ,
75
+ fieldSchema . properties ,
76
+ fieldSchema . required ,
77
+ logicalModelPath
78
+ ) ;
79
+ logicalModels . push ( ...newLogicalModels ) ;
80
+ fields . push ( {
81
+ name : fieldName ,
82
+ type : {
83
+ logical_model : `${ collectionName } _${ logicalModelPath } ` ,
84
+ nullable,
85
+ } ,
86
+ } ) ;
98
87
}
99
88
100
89
if ( fieldSchema . type === 'array' ) {
101
- if ( isMixedArray ( fieldSchema . items ) ) {
90
+ // Throw error for mixed object / scalar array
91
+ // Schema inferer returns anyOf if there are any type conflicts
92
+ const hasNestedAnyOf = ( function checkNestedAnyOf ( obj : any ) : boolean {
93
+ if ( typeof obj !== 'object' || obj === null ) return false ;
94
+ if ( 'anyOf' in obj ) return true ;
95
+ return Object . values ( obj ) . some (
96
+ val => typeof val === 'object' && checkNestedAnyOf ( val )
97
+ ) ;
98
+ } ) ( fieldSchema . items ) ;
99
+ if ( hasNestedAnyOf ) {
102
100
throw new Error (
103
- `The array for field "${ fieldName } " contains both objects and scalars ( string, int, etc.). Please check and ensure it only contains one for inference. \n Exact key with issue: "${ logicalModelPath } "`
101
+ `The array for field "${ fieldName } " contains both multiple types (objects, string, int, etc.). Please check and ensure it only contains one for inference. \n Exact key with issue: "${ logicalModelPath } "`
104
102
) ;
105
103
}
104
+
105
+ // Array of objects
106
106
if ( fieldSchema . items . type === 'object' ) {
107
- const newLogicalModels = getLogicalModelsFromProperties (
108
- collectionName ,
109
- `${ collectionName } _${ logicalModelPath } ` ,
110
- fieldSchema . items . properties ,
111
- fieldSchema . items ?. required || [ ] ,
112
- logicalModelPath
107
+ // Check for special mongo scalars
108
+ const mongoDBFieldType = handleMongoDBFieldTypes (
109
+ fieldSchema . items . properties
113
110
) ;
111
+ if ( mongoDBFieldType . type !== 'none' ) {
112
+ fields . push ( {
113
+ name : fieldName ,
114
+ type : {
115
+ array : {
116
+ scalar : mongoDBFieldType . type ,
117
+ nullable,
118
+ } ,
119
+ } ,
120
+ } ) ;
121
+ } else {
122
+ // Make new logical model for array
123
+ const newLogicalModels = getLogicalModelsFromProperties (
124
+ collectionName ,
125
+ `${ collectionName } _${ logicalModelPath } ` ,
126
+ fieldSchema . items . properties ,
127
+ fieldSchema . items ?. required || [ ] ,
128
+ logicalModelPath
129
+ ) ;
114
130
115
- logicalModels . push ( ...newLogicalModels ) ;
131
+ logicalModels . push ( ...newLogicalModels ) ;
116
132
117
- fields . push ( {
118
- name : fieldName ,
119
- type : {
120
- array : {
121
- logical_model : `${ collectionName } _${ logicalModelPath } ` ,
122
- nullable,
133
+ fields . push ( {
134
+ name : fieldName ,
135
+ type : {
136
+ array : {
137
+ logical_model : `${ collectionName } _${ logicalModelPath } ` ,
138
+ nullable,
139
+ } ,
123
140
} ,
124
- } ,
125
- } ) ;
126
- } else {
127
- // scalar array
128
- fields . push ( {
129
- name : fieldName ,
130
- type : {
131
- array : {
132
- scalar : fieldSchema . items . type ,
133
- nullable,
141
+ } ) ;
142
+ }
143
+ continue ;
144
+ }
145
+ // Array of scalars
146
+ if ( fieldSchema . items . type !== 'object' ) {
147
+ // Process scalar types in array
148
+ // TODO: DRY this out with scalar processing below
149
+ if ( fieldSchema . items . type === 'string' ) {
150
+ fields . push ( {
151
+ name : fieldName ,
152
+ type : {
153
+ array : {
154
+ scalar : 'string' ,
155
+ nullable,
156
+ } ,
134
157
} ,
135
- } ,
136
- } ) ;
158
+ } ) ;
159
+ }
160
+ if ( fieldSchema . items . type === 'integer' ) {
161
+ fields . push ( {
162
+ name : fieldName ,
163
+ type : {
164
+ array : {
165
+ scalar : 'int' ,
166
+ nullable,
167
+ } ,
168
+ } ,
169
+ } ) ;
170
+ }
171
+ if ( fieldSchema . items . type === 'number' ) {
172
+ fields . push ( {
173
+ name : fieldName ,
174
+ type : {
175
+ array : {
176
+ scalar : 'double' ,
177
+ nullable,
178
+ } ,
179
+ } ,
180
+ } ) ;
181
+ }
182
+ if ( fieldSchema . items . type === 'boolean' ) {
183
+ fields . push ( {
184
+ name : fieldName ,
185
+ type : {
186
+ array : {
187
+ scalar : 'bool' ,
188
+ nullable,
189
+ } ,
190
+ } ,
191
+ } ) ;
192
+ }
137
193
}
138
194
}
139
195
196
+ // Process scalars
140
197
if ( fieldSchema . type === 'string' ) {
141
198
fields . push ( {
142
199
name : fieldName ,
@@ -177,7 +234,6 @@ const getLogicalModelsFromProperties = (
177
234
} ) ;
178
235
}
179
236
}
180
-
181
237
return [
182
238
{
183
239
name,
0 commit comments