@@ -8,21 +8,35 @@ import {
8
8
ParsedAppSyncModelConfig ,
9
9
RawAppSyncModelConfig ,
10
10
CodeGenEnum ,
11
+ CodeGenUnion ,
12
+ CodeGenInterface ,
11
13
} from './appsync-visitor' ;
12
14
import { METADATA_SCALAR_MAP } from '../scalars' ;
13
15
export type JSONSchema = {
14
16
models : JSONSchemaModels ;
15
17
enums : JSONSchemaEnums ;
16
18
nonModels : JSONSchemaTypes ;
19
+ interfaces : JSONSchemaInterfaces ;
20
+ unions : JSONSchemaUnions ;
17
21
version : string ;
18
22
codegenVersion : string ;
19
23
} ;
20
24
export type JSONSchemaModels = Record < string , JSONSchemaModel > ;
21
25
export type JSONSchemaTypes = Record < string , JSONSchemaNonModel > ;
26
+ export type JSONSchemaInterfaces = Record < string , JSONSchemaInterface > ;
27
+ export type JSONSchemaUnions = Record < string , JSONSchemaUnion > ;
22
28
export type JSONSchemaNonModel = {
23
29
name : string ;
24
30
fields : JSONModelFields ;
25
31
} ;
32
+ export type JSONSchemaInterface = {
33
+ name : string ;
34
+ fields : JSONModelFields ;
35
+ } ;
36
+ export type JSONSchemaUnion = {
37
+ name : string ;
38
+ types : JSONModelFieldType [ ] ;
39
+ } ;
26
40
type JSONSchemaModel = {
27
41
name : string ;
28
42
attributes ?: JSONModelAttributes ;
@@ -58,7 +72,7 @@ type AssociationBelongsTo = AssociationBaseType & {
58
72
59
73
type AssociationType = AssociationHasMany | AssociationHasOne | AssociationBelongsTo ;
60
74
61
- type JSONModelFieldType = keyof typeof METADATA_SCALAR_MAP | { model : string } | { enum : string } | { nonModel : string } ;
75
+ type JSONModelFieldType = keyof typeof METADATA_SCALAR_MAP | { model : string } | { enum : string } | { nonModel : string } | { interface : string } | { union : string } ;
62
76
type JSONModelField = {
63
77
name : string ;
64
78
type : JSONModelFieldType ;
@@ -147,7 +161,28 @@ export class AppSyncJSONVisitor<
147
161
}
148
162
149
163
protected generateTypeDeclaration ( ) {
150
- return [ "import { Schema } from '@aws-amplify/datastore';" , '' , 'export declare const schema: Schema;' ] . join ( '\n' ) ;
164
+ return `import type { Schema, SchemaNonModel, ModelField, ModelFieldType } from '@aws-amplify/datastore';
165
+
166
+ type Replace<T, R> = Omit<T, keyof R> & R;
167
+ type WithFields = { fields: Record<string, ModelField> };
168
+ type SchemaTypes = Record<string, WithFields>;
169
+
170
+ export type ExtendModelFieldType = ModelField['type'] | { interface: string } | { union: string };
171
+ export type ExtendModelField = Replace<ModelField, { type: ExtendModelFieldType }>;
172
+ export type ExtendType<T extends WithFields> = Replace<T, { fields: Record<string, ExtendModelField> }>
173
+ export type ExtendFields<Types extends SchemaTypes | undefined> = Types extends SchemaTypes ? {
174
+ [TypeName in keyof Types]: ExtendType<Types[TypeName]>
175
+ } : Types;
176
+
177
+ type ExtendFieldsAll<T> = {
178
+ [K in keyof T]: T[K] extends SchemaTypes | undefined ? ExtendFields<T[K]> : T[K];
179
+ };
180
+
181
+ export declare const schema: ExtendFieldsAll<Schema & {
182
+ interfaces: Schema['nonModels'];
183
+ unions?: Record<string, {name: string, types: ExtendModelFieldType[]}>;
184
+ }>;
185
+ `
151
186
}
152
187
153
188
protected generateJSONMetadata ( ) : string {
@@ -160,6 +195,8 @@ export class AppSyncJSONVisitor<
160
195
models : { } ,
161
196
enums : { } ,
162
197
nonModels : { } ,
198
+ interfaces : { } ,
199
+ unions : { } ,
163
200
// This is hard-coded for the schema version purpose instead of codegen version
164
201
// To avoid the failure of validation method checkCodegenSchema in JS Datastore
165
202
// The hard code is starting from amplify codegen major version 4
@@ -175,11 +212,19 @@ export class AppSyncJSONVisitor<
175
212
return { ...acc , [ nonModel . name ] : this . generateNonModelMetadata ( nonModel ) } ;
176
213
} , { } ) ;
177
214
215
+ const interfaces = Object . values ( this . getSelectedInterfaces ( ) ) . reduce ( ( acc , codegenInterface : CodeGenInterface ) => {
216
+ return { ...acc , [ codegenInterface . name ] : this . generateInterfaceMetadata ( codegenInterface ) } ;
217
+ } , { } ) ;
218
+
219
+ const unions = Object . values ( this . getSelectedUnions ( ) ) . reduce ( ( acc , union : CodeGenUnion ) => {
220
+ return { ...acc , [ union . name ] : this . generateUnionMetadata ( union ) } ;
221
+ } , { } ) ;
222
+
178
223
const enums = Object . values ( this . enumMap ) . reduce ( ( acc , enumObj ) => {
179
224
const enumV = this . generateEnumMetadata ( enumObj ) ;
180
225
return { ...acc , [ this . getEnumName ( enumObj ) ] : enumV } ;
181
226
} , { } ) ;
182
- return { ...result , models, nonModels : nonModels , enums } ;
227
+ return { ...result , models, nonModels : nonModels , enums, interfaces , unions } ;
183
228
}
184
229
185
230
private getFieldAssociation ( field : CodeGenField ) : AssociationType | void {
@@ -229,39 +274,58 @@ export class AppSyncJSONVisitor<
229
274
private generateNonModelMetadata ( nonModel : CodeGenModel ) : JSONSchemaNonModel {
230
275
return {
231
276
name : this . getModelName ( nonModel ) ,
232
- fields : nonModel . fields . reduce ( ( acc : JSONModelFields , field : CodeGenField ) => {
233
- const fieldMeta : JSONModelField = {
234
- name : this . getFieldName ( field ) ,
235
- isArray : field . isList ,
236
- type : this . getType ( field . type ) ,
237
- isRequired : ! field . isNullable ,
238
- attributes : [ ] ,
239
- } ;
240
-
241
- if ( field . isListNullable !== undefined ) {
242
- fieldMeta . isArrayNullable = field . isListNullable ;
243
- }
277
+ fields : this . generateFieldsMetadata ( nonModel . fields )
278
+ } ;
279
+ }
244
280
245
- if ( field . isReadOnly !== undefined ) {
246
- fieldMeta . isReadOnly = field . isReadOnly ;
247
- }
281
+ private generateInterfaceMetadata ( codeGenInterface : CodeGenInterface ) : JSONSchemaInterface {
282
+ return {
283
+ name : codeGenInterface . name ,
284
+ fields : this . generateFieldsMetadata ( codeGenInterface . fields ) ,
285
+ } ;
286
+ }
248
287
249
- const association : AssociationType | void = this . getFieldAssociation ( field ) ;
250
- if ( association ) {
251
- fieldMeta . association = association ;
252
- }
253
- acc [ fieldMeta . name ] = fieldMeta ;
254
- return acc ;
255
- } , { } ) ,
288
+ private generateUnionMetadata ( codeGenUnion : CodeGenUnion ) : JSONSchemaUnion {
289
+ return {
290
+ name : codeGenUnion . name ,
291
+ types : codeGenUnion . typeNames . map ( t => this . getType ( t ) )
256
292
} ;
257
293
}
294
+
258
295
private generateEnumMetadata ( enumObj : CodeGenEnum ) : JSONSchemaEnum {
259
296
return {
260
297
name : enumObj . name ,
261
298
values : Object . values ( enumObj . values ) ,
262
299
} ;
263
300
}
264
301
302
+ private generateFieldsMetadata ( fields : CodeGenField [ ] ) : JSONModelFields {
303
+ return fields . reduce ( ( acc : JSONModelFields , field : CodeGenField ) => {
304
+ const fieldMeta : JSONModelField = {
305
+ name : this . getFieldName ( field ) ,
306
+ isArray : field . isList ,
307
+ type : this . getType ( field . type ) ,
308
+ isRequired : ! field . isNullable ,
309
+ attributes : [ ] ,
310
+ } ;
311
+
312
+ if ( field . isListNullable !== undefined ) {
313
+ fieldMeta . isArrayNullable = field . isListNullable ;
314
+ }
315
+
316
+ if ( field . isReadOnly !== undefined ) {
317
+ fieldMeta . isReadOnly = field . isReadOnly ;
318
+ }
319
+
320
+ const association : AssociationType | void = this . getFieldAssociation ( field ) ;
321
+ if ( association ) {
322
+ fieldMeta . association = association ;
323
+ }
324
+ acc [ fieldMeta . name ] = fieldMeta ;
325
+ return acc ;
326
+ } , { } )
327
+ }
328
+
265
329
private getType ( gqlType : string ) : JSONModelFieldType {
266
330
// Todo: Handle unlisted scalars
267
331
if ( gqlType in METADATA_SCALAR_MAP ) {
@@ -273,6 +337,12 @@ export class AppSyncJSONVisitor<
273
337
if ( gqlType in this . nonModelMap ) {
274
338
return { nonModel : gqlType } ;
275
339
}
340
+ if ( gqlType in this . interfaceMap ) {
341
+ return { interface : this . interfaceMap [ gqlType ] . name } ;
342
+ }
343
+ if ( gqlType in this . unionMap ) {
344
+ return { union : this . unionMap [ gqlType ] . name } ;
345
+ }
276
346
if ( gqlType in this . modelMap ) {
277
347
return { model : gqlType } ;
278
348
}
0 commit comments