@@ -12,12 +12,13 @@ import {
12
12
resolvePath ,
13
13
} from '@zenstackhq/sdk' ;
14
14
import { DataModel , DataModelField , DataModelFieldType , Enum , isDataModel , isEnum } from '@zenstackhq/sdk/ast' ;
15
- import * as fs from 'fs' ;
15
+ import fs from 'fs' ;
16
16
import { lowerCaseFirst } from 'lower-case-first' ;
17
17
import type { OpenAPIV3_1 as OAPI } from 'openapi-types' ;
18
- import * as path from 'path' ;
18
+ import path from 'path' ;
19
19
import pluralize from 'pluralize' ;
20
20
import invariant from 'tiny-invariant' ;
21
+ import { P , match } from 'ts-pattern' ;
21
22
import YAML from 'yaml' ;
22
23
import { name } from '.' ;
23
24
import { OpenAPIGeneratorBase } from './generator-base' ;
@@ -49,7 +50,7 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase {
49
50
}
50
51
51
52
const openapi : OAPI . Document = {
52
- openapi : this . getOption ( 'specVersion' , '3.1.0' ) ,
53
+ openapi : this . getOption ( 'specVersion' , this . DEFAULT_SPEC_VERSION ) ,
53
54
info : {
54
55
title : this . getOption ( 'title' , 'ZenStack Generated API' ) ,
55
56
version : this . getOption ( 'version' , '1.0.0' ) ,
@@ -483,9 +484,8 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase {
483
484
schema = this . fieldTypeToOpenAPISchema ( field . type ) ;
484
485
}
485
486
}
486
- if ( array ) {
487
- schema = { type : 'array' , items : schema } ;
488
- }
487
+
488
+ schema = this . wrapArray ( schema , array ) ;
489
489
490
490
return {
491
491
name : name === 'id' ? 'filter[id]' : `filter[${ field . name } ${ name } ]` ,
@@ -576,10 +576,10 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase {
576
576
description : 'Pagination information' ,
577
577
required : [ 'first' , 'last' , 'prev' , 'next' ] ,
578
578
properties : {
579
- first : this . nullable ( { type : 'string' , description : 'Link to the first page' } ) ,
580
- last : this . nullable ( { type : 'string' , description : 'Link to the last page' } ) ,
581
- prev : this . nullable ( { type : 'string' , description : 'Link to the previous page' } ) ,
582
- next : this . nullable ( { type : 'string' , description : 'Link to the next page' } ) ,
579
+ first : this . wrapNullable ( { type : 'string' , description : 'Link to the first page' } , true ) ,
580
+ last : this . wrapNullable ( { type : 'string' , description : 'Link to the last page' } , true ) ,
581
+ prev : this . wrapNullable ( { type : 'string' , description : 'Link to the previous page' } , true ) ,
582
+ next : this . wrapNullable ( { type : 'string' , description : 'Link to the next page' } , true ) ,
583
583
} ,
584
584
} ,
585
585
_errors : {
@@ -634,7 +634,7 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase {
634
634
type : 'object' ,
635
635
description : 'A to-one relationship' ,
636
636
properties : {
637
- data : this . nullable ( this . ref ( '_resourceIdentifier' ) ) ,
637
+ data : this . wrapNullable ( this . ref ( '_resourceIdentifier' ) , true ) ,
638
638
} ,
639
639
} ,
640
640
_toOneRelationshipWithLinks : {
@@ -643,7 +643,7 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase {
643
643
description : 'A to-one relationship with links' ,
644
644
properties : {
645
645
links : this . ref ( '_relationLinks' ) ,
646
- data : this . nullable ( this . ref ( '_resourceIdentifier' ) ) ,
646
+ data : this . wrapNullable ( this . ref ( '_resourceIdentifier' ) , true ) ,
647
647
} ,
648
648
} ,
649
649
_toManyRelationship : {
@@ -680,13 +680,16 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase {
680
680
} ,
681
681
_toOneRelationshipRequest : {
682
682
description : 'Input for manipulating a to-one relationship' ,
683
- ...this . nullable ( {
684
- type : 'object' ,
685
- required : [ 'data' ] ,
686
- properties : {
687
- data : this . ref ( '_resourceIdentifier' ) ,
683
+ ...this . wrapNullable (
684
+ {
685
+ type : 'object' ,
686
+ required : [ 'data' ] ,
687
+ properties : {
688
+ data : this . ref ( '_resourceIdentifier' ) ,
689
+ } ,
688
690
} ,
689
- } ) ,
691
+ true
692
+ ) ,
690
693
} ,
691
694
_toManyRelationshipResponse : {
692
695
description : 'Response for a to-many relationship' ,
@@ -841,7 +844,7 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase {
841
844
const fields = model . fields . filter ( ( f ) => ! isIdField ( f ) ) ;
842
845
843
846
const attributes : Record < string , OAPI . SchemaObject > = { } ;
844
- const relationships : Record < string , OAPI . ReferenceObject > = { } ;
847
+ const relationships : Record < string , OAPI . ReferenceObject | OAPI . SchemaObject > = { } ;
845
848
846
849
const required : string [ ] = [ ] ;
847
850
@@ -853,7 +856,7 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase {
853
856
} else {
854
857
relType = field . type . array ? '_toManyRelationshipWithLinks' : '_toOneRelationshipWithLinks' ;
855
858
}
856
- relationships [ field . name ] = this . ref ( relType ) ;
859
+ relationships [ field . name ] = this . wrapNullable ( this . ref ( relType ) , field . type . optional ) ;
857
860
} else {
858
861
attributes [ field . name ] = this . generateField ( field ) ;
859
862
if (
@@ -911,48 +914,33 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase {
911
914
}
912
915
913
916
private generateField ( field : DataModelField ) {
914
- return this . wrapArray ( this . fieldTypeToOpenAPISchema ( field . type ) , field . type . array ) ;
915
- }
916
-
917
- private get specVersion ( ) {
918
- return this . getOption ( 'specVersion' , '3.0.0' ) ;
917
+ return this . wrapArray (
918
+ this . wrapNullable ( this . fieldTypeToOpenAPISchema ( field . type ) , field . type . optional ) ,
919
+ field . type . array
920
+ ) ;
919
921
}
920
922
921
923
private fieldTypeToOpenAPISchema ( type : DataModelFieldType ) : OAPI . ReferenceObject | OAPI . SchemaObject {
922
- switch ( type . type ) {
923
- case 'String' :
924
- return { type : 'string' } ;
925
- case 'Int' :
926
- case 'BigInt' :
927
- return { type : 'integer' } ;
928
- case 'Float' :
929
- return { type : 'number' } ;
930
- case 'Decimal' :
931
- return this . oneOf ( { type : 'number' } , { type : 'string' } ) ;
932
- case 'Boolean' :
933
- return { type : 'boolean' } ;
934
- case 'DateTime' :
935
- return { type : 'string' , format : 'date-time' } ;
936
- case 'Bytes' :
937
- return { type : 'string' , format : 'byte' , description : 'Base64 encoded byte array' } ;
938
- case 'Json' :
939
- return { } ;
940
- default : {
924
+ return match ( type . type )
925
+ . with ( 'String' , ( ) => ( { type : 'string' } ) )
926
+ . with ( P . union ( 'Int' , 'BigInt' ) , ( ) => ( { type : 'integer' } ) )
927
+ . with ( 'Float' , ( ) => ( { type : 'number' } ) )
928
+ . with ( 'Decimal' , ( ) => this . oneOf ( { type : 'number' } , { type : 'string' } ) )
929
+ . with ( 'Boolean' , ( ) => ( { type : 'boolean' } ) )
930
+ . with ( 'DateTime' , ( ) => ( { type : 'string' , format : 'date-time' } ) )
931
+ . with ( 'Bytes' , ( ) => ( { type : 'string' , format : 'byte' , description : 'Base64 encoded byte array' } ) )
932
+ . with ( 'Json' , ( ) => ( { } ) )
933
+ . otherwise ( ( t ) => {
941
934
const fieldDecl = type . reference ?. ref ;
942
- invariant ( fieldDecl ) ;
935
+ invariant ( fieldDecl , `Type ${ t } is not a model reference` ) ;
943
936
return this . ref ( fieldDecl ?. name ) ;
944
- }
945
- }
937
+ } ) ;
946
938
}
947
939
948
940
private ref ( type : string ) {
949
941
return { $ref : `#/components/schemas/${ type } ` } ;
950
942
}
951
943
952
- private nullable ( schema : OAPI . SchemaObject | OAPI . ReferenceObject ) {
953
- return this . specVersion === '3.0.0' ? { ...schema , nullable : true } : this . oneOf ( schema , { type : 'null' } ) ;
954
- }
955
-
956
944
private parameter ( type : string ) {
957
945
return { $ref : `#/components/parameters/${ type } ` } ;
958
946
}
0 commit comments