88 type ClientContract ,
99} from '@zenstackhq/orm' ;
1010import type { FieldDef , ModelDef , SchemaDef } from '@zenstackhq/orm/schema' ;
11+ import { Decimal } from 'decimal.js' ;
1112import SuperJSON from 'superjson' ;
1213import { Linker , Paginator , Relator , Serializer , type SerializerOptions } from 'ts-japi' ;
1314import UrlPattern from 'url-pattern' ;
@@ -37,7 +38,7 @@ export type RestApiHandlerOptions<Schema extends SchemaDef = SchemaDef> = {
3738 /**
3839 * The default page size for limiting the number of results returned
3940 * from collection queries, including resource collection, related data
40- * of collection types, and relashionship of collection types.
41+ * of collection types, and relationship of collection types.
4142 *
4243 * Defaults to 100. Set to Infinity to disable pagination.
4344 */
@@ -200,7 +201,7 @@ export class RestApiHandler<Schema extends SchemaDef> implements ApiHandler<Sche
200201 title : 'Error occurred while executing the query' ,
201202 } ,
202203 unknownError : {
203- status : 400 ,
204+ status : 500 ,
204205 title : 'Unknown error' ,
205206 } ,
206207 } ;
@@ -851,10 +852,20 @@ export class RestApiHandler<Schema extends SchemaDef> implements ApiHandler<Sche
851852 body = SuperJSON . deserialize ( { json : body , meta : body . meta . serialization } ) ;
852853 }
853854
854- const parsed = this . createUpdatePayloadSchema . parse ( body ) ;
855- const attributes : any = parsed . data . attributes ;
855+ const parseResult = this . createUpdatePayloadSchema . safeParse ( body ) ;
856+ if ( ! parseResult . success ) {
857+ return {
858+ attributes : undefined ,
859+ relationships : undefined ,
860+ error : this . makeError ( 'invalidPayload' , getZodErrorMessage ( parseResult . error ) ) ,
861+ } ;
862+ }
856863
857- return { attributes, relationships : parsed . data . relationships } ;
864+ return {
865+ attributes : parseResult . data . data . attributes ,
866+ relationships : parseResult . data . data . relationships ,
867+ error : undefined ,
868+ } ;
858869 }
859870
860871 private async processCreate (
@@ -868,7 +879,10 @@ export class RestApiHandler<Schema extends SchemaDef> implements ApiHandler<Sche
868879 return this . makeUnsupportedModelError ( type ) ;
869880 }
870881
871- const { attributes, relationships } = this . processRequestBody ( requestBody ) ;
882+ const { attributes, relationships, error } = this . processRequestBody ( requestBody ) ;
883+ if ( error ) {
884+ return error ;
885+ }
872886
873887 const createPayload : any = { data : { ...attributes } } ;
874888
@@ -929,9 +943,16 @@ export class RestApiHandler<Schema extends SchemaDef> implements ApiHandler<Sche
929943 }
930944
931945 const modelName = typeInfo . name ;
932- const { attributes, relationships } = this . processRequestBody ( requestBody ) ;
946+ const { attributes, relationships, error } = this . processRequestBody ( requestBody ) ;
947+ if ( error ) {
948+ return error ;
949+ }
933950
934- const matchFields = this . upsertMetaSchema . parse ( requestBody ) . meta . matchFields ;
951+ const parseResult = this . upsertMetaSchema . safeParse ( requestBody ) ;
952+ if ( parseResult . error ) {
953+ return this . makeError ( 'invalidPayload' , getZodErrorMessage ( parseResult . error ) ) ;
954+ }
955+ const matchFields = parseResult . data . meta . matchFields ;
935956 const uniqueFieldSets = this . getUniqueFieldSets ( modelName ) ;
936957
937958 if ( ! uniqueFieldSets . some ( ( set ) => set . every ( ( field ) => matchFields . includes ( field ) ) ) ) {
@@ -942,7 +963,7 @@ export class RestApiHandler<Schema extends SchemaDef> implements ApiHandler<Sche
942963 where : this . makeUpsertWhere ( matchFields , attributes , typeInfo ) ,
943964 create : { ...attributes } ,
944965 update : {
945- ...Object . fromEntries ( Object . entries ( attributes ) . filter ( ( e ) => ! matchFields . includes ( e [ 0 ] ) ) ) ,
966+ ...Object . fromEntries ( Object . entries ( attributes ?? { } ) . filter ( ( e ) => ! matchFields . includes ( e [ 0 ] ) ) ) ,
946967 } ,
947968 } ;
948969
@@ -1110,7 +1131,10 @@ export class RestApiHandler<Schema extends SchemaDef> implements ApiHandler<Sche
11101131 return this . makeUnsupportedModelError ( type ) ;
11111132 }
11121133
1113- const { attributes, relationships } = this . processRequestBody ( requestBody ) ;
1134+ const { attributes, relationships, error } = this . processRequestBody ( requestBody ) ;
1135+ if ( error ) {
1136+ return error ;
1137+ }
11141138
11151139 const updatePayload : any = {
11161140 where : this . makeIdFilter ( typeInfo . idFields , resourceId ) ,
@@ -1198,9 +1222,11 @@ export class RestApiHandler<Schema extends SchemaDef> implements ApiHandler<Sche
11981222 const externalIdName = this . externalIdMapping [ modelLower ] ;
11991223 for ( const [ name , info ] of Object . entries ( modelDef . uniqueFields ) ) {
12001224 if ( name === externalIdName ) {
1201- if ( typeof info === 'string' ) {
1202- return [ this . requireField ( model , info ) ] ;
1225+ if ( typeof info . type === 'string' ) {
1226+ // single unique field
1227+ return [ this . requireField ( model , info . type ) ] ;
12031228 } else {
1229+ // compound unique fields
12041230 return Object . keys ( info ) . map ( ( f ) => this . requireField ( model , f ) ) ;
12051231 }
12061232 }
@@ -1561,18 +1587,30 @@ export class RestApiHandler<Schema extends SchemaDef> implements ApiHandler<Sche
15611587 }
15621588
15631589 const type = fieldDef . type ;
1564- if ( type === 'Int' || type === 'BigInt' ) {
1590+ if ( type === 'Int' ) {
15651591 const parsed = parseInt ( value ) ;
15661592 if ( isNaN ( parsed ) ) {
15671593 throw new InvalidValueError ( `invalid ${ type } value: ${ value } ` ) ;
15681594 }
15691595 return parsed ;
1570- } else if ( type === 'Float' || type === 'Decimal' ) {
1596+ } else if ( type === 'BigInt' ) {
1597+ try {
1598+ return BigInt ( value ) ;
1599+ } catch {
1600+ throw new InvalidValueError ( `invalid ${ type } value: ${ value } ` ) ;
1601+ }
1602+ } else if ( type === 'Float' ) {
15711603 const parsed = parseFloat ( value ) ;
15721604 if ( isNaN ( parsed ) ) {
15731605 throw new InvalidValueError ( `invalid ${ type } value: ${ value } ` ) ;
15741606 }
15751607 return parsed ;
1608+ } else if ( type === 'Decimal' ) {
1609+ try {
1610+ return new Decimal ( value ) ;
1611+ } catch {
1612+ throw new InvalidValueError ( `invalid ${ type } value: ${ value } ` ) ;
1613+ }
15761614 } else if ( type === 'Boolean' ) {
15771615 if ( value === 'true' ) {
15781616 return true ;
@@ -1592,6 +1630,7 @@ export class RestApiHandler<Schema extends SchemaDef> implements ApiHandler<Sche
15921630 if (
15931631 key . startsWith ( 'filter[' ) ||
15941632 key . startsWith ( 'sort[' ) ||
1633+ key === 'include' ||
15951634 key . startsWith ( 'include[' ) ||
15961635 key . startsWith ( 'fields[' )
15971636 ) {
@@ -1678,7 +1717,6 @@ export class RestApiHandler<Schema extends SchemaDef> implements ApiHandler<Sche
16781717 let curr = item ;
16791718 let currType = typeInfo ;
16801719
1681- const idFields = this . getIdFields ( typeInfo . name ) ;
16821720 for ( const filterValue of enumerate ( value ) ) {
16831721 for ( let i = 0 ; i < filterKeys . length ; i ++ ) {
16841722 // extract filter operation from (optional) trailing $op
@@ -1697,6 +1735,7 @@ export class RestApiHandler<Schema extends SchemaDef> implements ApiHandler<Sche
16971735 } ;
16981736 }
16991737
1738+ const idFields = this . getIdFields ( currType . name ) ;
17001739 const fieldDef =
17011740 filterKey === 'id'
17021741 ? Object . values ( currType . fields ) . find ( ( f ) => idFields . some ( ( idf ) => idf . name === f . name ) )
0 commit comments