11import {
22 AdminForthResource , IAdminForthDataSourceConnectorBase ,
33 AdminForthResourceColumn ,
4- IAdminForthSort , IAdminForthFilter
4+ IAdminForthSort , IAdminForthSingleFilter , IAdminForthAndOrFilter
55} from "../types/Back.js" ;
66
77
@@ -15,7 +15,7 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
1515 client : any ;
1616
1717 get db ( ) {
18- console . warn ( 'db is deprecated, use client instead' ) ;
18+ console . warn ( '. db is deprecated, use . client instead' ) ;
1919 return this . client ;
2020 }
2121
@@ -32,27 +32,84 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
3232 }
3333
3434 async getRecordByPrimaryKeyWithOriginalTypes ( resource : AdminForthResource , id : string ) : Promise < any > {
35- const data = await this . getDataWithOriginalTypes ( {
36- resource,
37- limit : 1 ,
35+ const data = await this . getDataWithOriginalTypes ( {
36+ resource,
37+ limit : 1 ,
3838 offset : 0 ,
39- sort : [ ] ,
40- filters : [ { field : this . getPrimaryKey ( resource ) , operator : AdminForthFilterOperators . EQ , value : id } ] ,
39+ sort : [ ] ,
40+ filters : { operator : AdminForthFilterOperators . AND , subFilters : [ { field : this . getPrimaryKey ( resource ) , operator : AdminForthFilterOperators . EQ , value : id } ] } ,
4141 } ) ;
4242 return data . length > 0 ? data [ 0 ] : null ;
4343 }
4444
45+ validateAndNormalizeFilters ( filters : IAdminForthSingleFilter | IAdminForthAndOrFilter | Array < IAdminForthSingleFilter | IAdminForthAndOrFilter > , resource : AdminForthResource ) : { ok : boolean , error : string } {
46+ if ( Array . isArray ( filters ) ) {
47+ // go through all filters in array and call validation+normalization for each
48+ // as soon as error is encountered, there is no point in calling validation for other filters
49+ // if error is not encountered all filters will be validated and normalized
50+ return filters . reduce ( ( result , f ) => {
51+ if ( ! result . ok ) {
52+ return result ;
53+ }
54+
55+ return this . validateAndNormalizeFilters ( f , resource ) ;
56+ } , { ok : true , error : '' } ) ;
57+ }
58+
59+ if ( ! filters . operator ) {
60+ return { ok : false , error : `Field "operator" not specified in filter object: ${ JSON . stringify ( filters ) } ` } ;
61+ }
62+
63+ if ( ( filters as IAdminForthSingleFilter ) . field ) {
64+ // if "field" is present, filter must be Single
65+ if ( ! [ AdminForthFilterOperators . EQ , AdminForthFilterOperators . NE , AdminForthFilterOperators . GT ,
66+ AdminForthFilterOperators . LT , AdminForthFilterOperators . GTE , AdminForthFilterOperators . LTE ,
67+ AdminForthFilterOperators . LIKE , AdminForthFilterOperators . ILIKE , AdminForthFilterOperators . IN ,
68+ AdminForthFilterOperators . NIN ] . includes ( filters . operator ) ) {
69+ return { ok : false , error : `Field "operator" has wrong value in filter object: ${ JSON . stringify ( filters ) } ` } ;
70+ }
71+ const fieldObj = resource . dataSourceColumns . find ( ( col ) => col . name == ( filters as IAdminForthSingleFilter ) . field ) ;
72+ if ( ! fieldObj ) {
73+ const similar = suggestIfTypo ( resource . dataSourceColumns . map ( ( col ) => col . name ) , ( filters as IAdminForthSingleFilter ) . field ) ;
74+ throw new Error ( `Field '${ ( filters as IAdminForthSingleFilter ) . field } ' not found in resource '${ resource . resourceId } '. ${ similar ? `Did you mean '${ similar } '?` : '' } ` ) ;
75+ }
76+ if ( filters . operator == AdminForthFilterOperators . IN || filters . operator == AdminForthFilterOperators . NIN ) {
77+ if ( ! Array . isArray ( filters . value ) ) {
78+ return { ok : false , error : `Value for operator '${ filters . operator } ' should be an array, in filter object: ${ JSON . stringify ( filters ) } ` } ;
79+ }
80+ if ( filters . value . length === 0 ) {
81+ // nonsense
82+ return { ok : false , error : `Filter has IN operator but empty value: ${ JSON . stringify ( filters ) } ` } ;
83+ }
84+ filters . value = filters . value . map ( ( val : any ) => this . setFieldValue ( fieldObj , val ) ) ;
85+ } else {
86+ ( filters as IAdminForthSingleFilter ) . value = this . setFieldValue ( fieldObj , ( filters as IAdminForthSingleFilter ) . value ) ;
87+ }
88+ } else if ( ( filters as IAdminForthAndOrFilter ) . subFilters ) {
89+ // if "subFilters" is present, filter must be AndOr
90+ if ( ! [ AdminForthFilterOperators . AND , AdminForthFilterOperators . OR ] . includes ( filters . operator ) ) {
91+ return { ok : false , error : `Field "operator" has wrong value in filter object: ${ JSON . stringify ( filters ) } ` } ;
92+ }
93+
94+ return this . validateAndNormalizeFilters ( ( filters as IAdminForthAndOrFilter ) . subFilters , resource ) ;
95+ } else {
96+ return { ok : false , error : `Fields "field" or "subFilters" are not specified in filter object: ${ JSON . stringify ( filters ) } ` } ;
97+ }
98+
99+ return { ok : true , error : '' } ;
100+ }
101+
45102 getDataWithOriginalTypes ( { resource, limit, offset, sort, filters } : {
46103 resource : AdminForthResource ,
47104 limit : number ,
48105 offset : number ,
49106 sort : IAdminForthSort [ ] ,
50- filters : IAdminForthFilter [ ] ,
107+ filters : IAdminForthAndOrFilter ,
51108 } ) : Promise < any [ ] > {
52109 throw new Error ( 'Method not implemented.' ) ;
53110 }
54111
55- getCount ( { resource, filters } : { resource : AdminForthResource ; filters : { field : string ; operator : AdminForthFilterOperators ; value : any ; } [ ] ; } ) : Promise < number > {
112+ getCount ( { resource, filters } : { resource : AdminForthResource ; filters : IAdminForthAndOrFilter ; } ) : Promise < number > {
56113 throw new Error ( 'Method not implemented.' ) ;
57114 }
58115
@@ -80,7 +137,7 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
80137 process . env . HEAVY_DEBUG && console . log ( '☝️🪲🪲🪲🪲 checkUnique|||' , column , value ) ;
81138 const existingRecord = await this . getData ( {
82139 resource,
83- filters : [ { field : column . name , operator : AdminForthFilterOperators . EQ , value } ] ,
140+ filters : { operator : AdminForthFilterOperators . AND , subFilters : [ { field : column . name , operator : AdminForthFilterOperators . EQ , value } ] } ,
84141 limit : 1 ,
85142 sort : [ ] ,
86143 offset : 0 ,
@@ -130,7 +187,12 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
130187 }
131188
132189 process . env . HEAVY_DEBUG && console . log ( '🪲🆕 creating record' , JSON . stringify ( recordWithOriginalValues ) ) ;
133- const pkValue = await this . createRecordOriginalValues ( { resource, record : recordWithOriginalValues } ) ;
190+ let pkValue = await this . createRecordOriginalValues ( { resource, record : recordWithOriginalValues } ) ;
191+ if ( recordWithOriginalValues [ this . getPrimaryKey ( resource ) ] !== undefined ) {
192+ // some data sources always return some value for pk, even if it is was not auto generated
193+ // this check prevents wrong value from being used later in get request
194+ pkValue = recordWithOriginalValues [ this . getPrimaryKey ( resource ) ] ;
195+ }
134196
135197 let createdRecord = recordWithOriginalValues ;
136198 if ( pkValue ) {
@@ -175,38 +237,19 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
175237 throw new Error ( 'Method not implemented.' ) ;
176238 }
177239
178-
179240 async getData ( { resource, limit, offset, sort, filters, getTotals } : {
180241 resource : AdminForthResource ,
181242 limit : number ,
182243 offset : number ,
183244 sort : { field : string , direction : AdminForthSortDirections } [ ] ,
184- filters : { field : string , operator : AdminForthFilterOperators , value : any } [ ] ,
245+ filters : IAdminForthAndOrFilter ,
185246 getTotals : boolean ,
186247 } ) : Promise < { data : any [ ] , total : number } > {
187248 if ( filters ) {
188- for ( const f of filters ) {
189- if ( ! f . field ) {
190- throw new Error ( `Field "field" not specified in filter object: ${ JSON . stringify ( f ) } ` ) ;
191- }
192- if ( ! f . operator ) {
193- throw new Error ( `Field "operator" not specified in filter object: ${ JSON . stringify ( f ) } ` ) ;
194- }
195- const fieldObj = resource . dataSourceColumns . find ( ( col ) => col . name == f . field ) ;
196- if ( ! fieldObj ) {
197- const similar = suggestIfTypo ( resource . dataSourceColumns . map ( ( col ) => col . name ) , f . field ) ;
198- throw new Error ( `Field '${ f . field } ' not found in resource '${ resource . resourceId } '. ${ similar ? `Did you mean '${ similar } '?` : '' } ` ) ;
199- }
200- if ( f . operator == AdminForthFilterOperators . IN || f . operator == AdminForthFilterOperators . NIN ) {
201- f . value = f . value . map ( ( val ) => this . setFieldValue ( fieldObj , val ) ) ;
202- } else {
203- f . value = this . setFieldValue ( fieldObj , f . value ) ;
204- }
205- if ( f . operator === AdminForthFilterOperators . IN && f . value . length === 0 ) {
206- // nonsense
207- return { data : [ ] , total : 0 } ;
208- }
209- } ;
249+ const filterValidation = this . validateAndNormalizeFilters ( filters , resource ) ;
250+ if ( ! filterValidation . ok ) {
251+ throw new Error ( filterValidation . error ) ;
252+ }
210253 }
211254
212255 const promises : Promise < any > [ ] = [ this . getDataWithOriginalTypes ( { resource, limit, offset, sort, filters } ) ] ;
0 commit comments