@@ -8,21 +8,35 @@ import {
88 ParsedAppSyncModelConfig ,
99 RawAppSyncModelConfig ,
1010 CodeGenEnum ,
11+ CodeGenUnion ,
12+ CodeGenInterface ,
1113} from './appsync-visitor' ;
1214import { METADATA_SCALAR_MAP } from '../scalars' ;
1315export type JSONSchema = {
1416 models : JSONSchemaModels ;
1517 enums : JSONSchemaEnums ;
1618 nonModels : JSONSchemaTypes ;
19+ interfaces : JSONSchemaInterfaces ;
20+ unions : JSONSchemaUnions ;
1721 version : string ;
1822 codegenVersion : string ;
1923} ;
2024export type JSONSchemaModels = Record < string , JSONSchemaModel > ;
2125export type JSONSchemaTypes = Record < string , JSONSchemaNonModel > ;
26+ export type JSONSchemaInterfaces = Record < string , JSONSchemaInterface > ;
27+ export type JSONSchemaUnions = Record < string , JSONSchemaUnion > ;
2228export type JSONSchemaNonModel = {
2329 name : string ;
2430 fields : JSONModelFields ;
2531} ;
32+ export type JSONSchemaInterface = {
33+ name : string ;
34+ fields : JSONModelFields ;
35+ } ;
36+ export type JSONSchemaUnion = {
37+ name : string ;
38+ types : JSONModelFieldType [ ] ;
39+ } ;
2640type JSONSchemaModel = {
2741 name : string ;
2842 attributes ?: JSONModelAttributes ;
@@ -58,7 +72,7 @@ type AssociationBelongsTo = AssociationBaseType & {
5872
5973type AssociationType = AssociationHasMany | AssociationHasOne | AssociationBelongsTo ;
6074
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 } ;
6276type JSONModelField = {
6377 name : string ;
6478 type : JSONModelFieldType ;
@@ -147,7 +161,28 @@ export class AppSyncJSONVisitor<
147161 }
148162
149163 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+ `
151186 }
152187
153188 protected generateJSONMetadata ( ) : string {
@@ -160,6 +195,8 @@ export class AppSyncJSONVisitor<
160195 models : { } ,
161196 enums : { } ,
162197 nonModels : { } ,
198+ interfaces : { } ,
199+ unions : { } ,
163200 // This is hard-coded for the schema version purpose instead of codegen version
164201 // To avoid the failure of validation method checkCodegenSchema in JS Datastore
165202 // The hard code is starting from amplify codegen major version 4
@@ -175,11 +212,19 @@ export class AppSyncJSONVisitor<
175212 return { ...acc , [ nonModel . name ] : this . generateNonModelMetadata ( nonModel ) } ;
176213 } , { } ) ;
177214
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+
178223 const enums = Object . values ( this . enumMap ) . reduce ( ( acc , enumObj ) => {
179224 const enumV = this . generateEnumMetadata ( enumObj ) ;
180225 return { ...acc , [ this . getEnumName ( enumObj ) ] : enumV } ;
181226 } , { } ) ;
182- return { ...result , models, nonModels : nonModels , enums } ;
227+ return { ...result , models, nonModels : nonModels , enums, interfaces , unions } ;
183228 }
184229
185230 private getFieldAssociation ( field : CodeGenField ) : AssociationType | void {
@@ -229,39 +274,58 @@ export class AppSyncJSONVisitor<
229274 private generateNonModelMetadata ( nonModel : CodeGenModel ) : JSONSchemaNonModel {
230275 return {
231276 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+ }
244280
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+ }
248287
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 ) )
256292 } ;
257293 }
294+
258295 private generateEnumMetadata ( enumObj : CodeGenEnum ) : JSONSchemaEnum {
259296 return {
260297 name : enumObj . name ,
261298 values : Object . values ( enumObj . values ) ,
262299 } ;
263300 }
264301
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+
265329 private getType ( gqlType : string ) : JSONModelFieldType {
266330 // Todo: Handle unlisted scalars
267331 if ( gqlType in METADATA_SCALAR_MAP ) {
@@ -273,6 +337,12 @@ export class AppSyncJSONVisitor<
273337 if ( gqlType in this . nonModelMap ) {
274338 return { nonModel : gqlType } ;
275339 }
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+ }
276346 if ( gqlType in this . modelMap ) {
277347 return { model : gqlType } ;
278348 }
0 commit comments