2019-03-13 19:27:18 +01:00
const _ = require ( 'lodash' ) ;
2019-03-22 12:16:09 +01:00
const findModelByAssoc = assoc => {
const { models } = assoc . plugin ? strapi . plugins [ assoc . plugin ] : strapi ;
return models [ assoc . collection || assoc . model ] ;
} ;
2019-07-18 19:28:52 +02:00
const isAttribute = ( model , field ) =>
_ . has ( model . allAttributes , field ) || model . primaryKey === field ;
2019-03-22 12:16:09 +01:00
2019-03-28 17:12:43 +01:00
/ * *
* Returns the model , attribute name and association from a path of relation
* @ param { Object } options - Options
* @ param { string } options . model - Strapi model
* @ param { string } options . field - pathj of relation / attribute
* /
const getAssociationFromFieldKey = ( { model , field } ) => {
2019-03-13 19:27:18 +01:00
const fieldParts = field . split ( '.' ) ;
2019-03-22 12:16:09 +01:00
let tmpModel = model ;
2019-03-28 17:12:43 +01:00
let association ;
let attribute ;
2019-03-22 12:16:09 +01:00
for ( let i = 0 ; i < fieldParts . length ; i ++ ) {
2019-03-28 17:12:43 +01:00
const part = fieldParts [ i ] ;
attribute = part ;
2019-03-22 12:16:09 +01:00
2019-03-28 17:12:43 +01:00
const assoc = tmpModel . associations . find ( ast => ast . alias === part ) ;
2019-03-22 12:16:09 +01:00
if ( assoc ) {
2019-03-28 17:12:43 +01:00
association = assoc ;
2019-03-22 12:16:09 +01:00
tmpModel = findModelByAssoc ( assoc ) ;
continue ;
}
2019-07-18 19:28:52 +02:00
if (
! assoc &&
( ! isAttribute ( tmpModel , part ) || i !== fieldParts . length - 1 )
) {
2019-03-28 17:12:43 +01:00
const err = new Error (
` Your filters contain a field ' ${ field } ' that doesn't appear on your model definition nor it's relations `
) ;
err . status = 400 ;
throw err ;
2019-03-13 19:27:18 +01:00
}
2019-03-22 12:16:09 +01:00
}
2019-03-13 19:27:18 +01:00
2019-03-28 17:12:43 +01:00
return {
association ,
model : tmpModel ,
attribute ,
} ;
} ;
/ * *
* Cast basic values based on attribute type
* @ param { Object } options - Options
* @ param { string } options . type - type of the atribute
* @ param { * } options . value - value tu cast
* /
const castValueToType = ( { type , value } ) => {
switch ( type ) {
case 'boolean' : {
if ( [ 'true' , 't' , '1' , 1 , true ] . includes ( value ) ) {
return true ;
}
if ( [ 'false' , 'f' , '0' , 0 ] . includes ( value ) ) {
return false ;
}
2019-07-04 19:10:17 -03:00
return Boolean ( value ) ;
2019-03-28 17:12:43 +01:00
}
case 'integer' :
case 'biginteger' :
case 'float' :
case 'decimal' : {
return _ . toNumber ( value ) ;
}
default :
return value ;
}
2019-03-13 19:27:18 +01:00
} ;
2019-07-04 19:10:17 -03:00
/ * *
* Cast basic values based on attribute type
* @ param { Object } options - Options
* @ param { string } options . type - type of the atribute
* @ param { * } options . value - value tu cast
* @ param { string } options . operator - name of operator
* /
2019-07-18 19:28:52 +02:00
const castValue = ( { type , value , operator } ) => {
if ( operator === 'null' ) return castValueToType ( { type : 'boolean' , value } ) ;
return castValueToType ( { type , value } ) ;
} ;
2019-03-26 14:57:02 +01:00
/ * *
*
* @ param { Object } options - Options
* @ param { Object } options . model - The model for which the query will be built
* @ param { Object } options . filters - The filters for the query ( start , sort , limit , and where clauses )
* @ param { Object } options . rest - In case the database layer requires any other params pass them
* /
2019-03-28 17:12:43 +01:00
const buildQuery = ( { model , filters = { } , ... rest } ) => {
2019-03-26 14:57:02 +01:00
// Validate query clauses
2019-03-13 19:27:18 +01:00
if ( filters . where && Array . isArray ( filters . where ) ) {
2019-07-18 19:28:52 +02:00
const deepFilters = filters . where . filter (
( { field } ) => field . split ( '.' ) . length > 1
) ;
2019-03-26 14:57:02 +01:00
if ( deepFilters . length > 0 ) {
strapi . log . warn (
'Deep filtering queries should be used carefully (e.g Can cause performance issues).\nWhen possible build custom routes which will in most case be more optimised.'
) ;
}
2019-03-28 17:12:43 +01:00
// cast where clauses to match the inner types
filters . where = filters . where
2019-04-23 13:18:09 +02:00
. filter ( ( { value } ) => ! _ . isNil ( value ) )
2019-03-28 17:12:43 +01:00
. map ( ( { field , operator , value } ) => {
const { model : assocModel , attribute } = getAssociationFromFieldKey ( {
model ,
field ,
} ) ;
2019-03-22 12:16:09 +01:00
2019-03-28 17:12:43 +01:00
const { type } = _ . get ( assocModel , [ 'attributes' , attribute ] , { } ) ;
2019-04-25 18:18:54 +02:00
// cast value or array of values
const castedValue = Array . isArray ( value )
2019-07-18 19:28:52 +02:00
? value . map ( val => castValue ( { type , operator , value : val } ) )
: castValue ( { type , operator , value : value } ) ;
2019-04-25 18:18:54 +02:00
return { field , operator , value : castedValue } ;
2019-03-28 17:12:43 +01:00
} ) ;
2019-03-13 19:27:18 +01:00
}
2019-03-26 14:57:02 +01:00
const orm = strapi . hook [ model . orm ] ;
2019-03-13 19:27:18 +01:00
2019-03-26 14:57:02 +01:00
// call the orm's buildQuery implementation
2019-08-14 14:15:45 +02:00
return orm . load . buildQuery ( { model , filters , ... rest } ) ;
2019-03-13 19:27:18 +01:00
} ;
module . exports = buildQuery ;