2016-03-16 14:41:15 +01:00
'use strict' ;
/ * *
* Module dependencies
* /
2016-03-22 15:50:33 +01:00
// Node.js core
const path = require ( 'path' ) ;
2018-05-03 18:13:22 +02:00
// Public node modules.
const _ = require ( 'lodash' ) ;
2018-09-14 00:19:20 +02:00
const pluralize = require ( 'pluralize' ) ;
2018-05-03 18:13:22 +02:00
2018-06-17 23:34:25 +02:00
// Following this discussion https://stackoverflow.com/questions/18082/validate-decimal-numbers-in-javascript-isnumeric this function is the best implem to determine if a value is a valid number candidate
const isNumeric = ( value ) => {
2018-09-15 23:16:20 -04:00
return ! _ . isObject ( value ) && ! isNaN ( parseFloat ( value ) ) && isFinite ( value ) ;
2018-06-22 16:28:54 +02:00
} ;
2018-06-17 23:34:25 +02:00
2018-09-24 21:20:30 +02:00
// Constants
const ORDERS = [ 'ASC' , 'DESC' ] ;
2018-05-03 18:13:22 +02:00
/* eslint-disable prefer-template */
2016-03-16 14:41:15 +01:00
/ *
* Set of utils for models
* /
module . exports = {
2016-07-05 14:13:35 +02:00
/ * *
* Initialize to prevent some mistakes
* /
initialize : cb => {
cb ( ) ;
} ,
2016-03-17 15:05:47 +01:00
/ * *
* Find primary key per ORM
* /
2017-01-11 17:53:40 +01:00
getPK : function ( collectionIdentity , collection , models ) {
2016-03-17 15:05:47 +01:00
if ( _ . isString ( collectionIdentity ) ) {
const ORM = this . getORM ( collectionIdentity ) ;
2016-03-22 15:50:33 +01:00
try {
const GraphQLFunctions = require ( path . resolve ( strapi . config . appPath , 'node_modules' , 'strapi-' + ORM , 'lib' , 'utils' ) ) ;
2016-07-06 15:51:52 +02:00
2016-03-22 15:50:33 +01:00
if ( ! _ . isUndefined ( GraphQLFunctions ) ) {
return GraphQLFunctions . getPK ( collectionIdentity , collection , models || strapi . models ) ;
}
} catch ( err ) {
return undefined ;
2016-03-17 15:05:47 +01:00
}
}
return undefined ;
} ,
2018-05-10 12:19:33 +02:00
/ * *
* Retrieve the value based on the primary key
* /
getValuePrimaryKey : ( value , defaultKey ) => {
return value [ defaultKey ] || value . id || value . _id ;
} ,
2016-03-17 15:05:47 +01:00
/ * *
* Find primary key per ORM
* /
2017-01-11 17:53:40 +01:00
getCount : function ( collectionIdentity ) {
2016-03-17 15:05:47 +01:00
if ( _ . isString ( collectionIdentity ) ) {
const ORM = this . getORM ( collectionIdentity ) ;
2016-07-06 15:51:52 +02:00
2016-03-22 15:50:33 +01:00
try {
const ORMFunctions = require ( path . resolve ( strapi . config . appPath , 'node_modules' , 'strapi-' + ORM , 'lib' , 'utils' ) ) ;
2016-07-06 15:51:52 +02:00
2016-03-22 15:50:33 +01:00
if ( ! _ . isUndefined ( ORMFunctions ) ) {
return ORMFunctions . getCount ( collectionIdentity ) ;
}
} catch ( err ) {
return undefined ;
2016-03-17 15:05:47 +01:00
}
}
return undefined ;
} ,
2016-03-16 14:41:15 +01:00
/ * *
* Find relation nature with verbose
* /
2017-09-07 17:16:31 +02:00
getNature : ( association , key , models , currentModelName ) => {
2017-12-15 18:04:56 +01:00
try {
const types = {
current : '' ,
other : ''
} ;
2016-04-19 17:29:19 +02:00
2017-12-15 18:04:56 +01:00
if ( _ . isUndefined ( models ) ) {
models = association . plugin ? strapi . plugins [ association . plugin ] . models : strapi . models ;
}
2016-04-19 17:29:19 +02:00
2018-02-22 15:34:33 +01:00
if ( ( association . hasOwnProperty ( 'collection' ) && association . collection === '*' ) || ( association . hasOwnProperty ( 'model' ) && association . model === '*' ) ) {
if ( association . model ) {
types . current = 'morphToD' ;
} else {
types . current = 'morphTo' ;
}
const flattenedPluginsModels = Object . keys ( strapi . plugins ) . reduce ( ( acc , current ) => {
Object . keys ( strapi . plugins [ current ] . models ) . forEach ( ( model ) => {
acc [ ` ${ current } _ ${ model } ` ] = strapi . plugins [ current ] . models [ model ] ;
} ) ;
return acc ;
} , { } ) ;
const allModels = _ . merge ( { } , strapi . models , flattenedPluginsModels ) ;
// We have to find if they are a model linked to this key
_ . forIn ( allModels , model => {
_ . forIn ( model . attributes , attribute => {
2018-05-02 17:52:23 +02:00
if ( attribute . hasOwnProperty ( 'via' ) && attribute . via === key && attribute . model === currentModelName ) {
2018-02-22 15:34:33 +01:00
if ( attribute . hasOwnProperty ( 'collection' ) ) {
types . other = 'collection' ;
// Break loop
return false ;
} else if ( attribute . hasOwnProperty ( 'model' ) ) {
types . other = 'model' ;
// Break loop
return false ;
}
}
} ) ;
} ) ;
} else if ( association . hasOwnProperty ( 'via' ) && association . hasOwnProperty ( 'collection' ) ) {
2017-12-15 18:04:56 +01:00
const relatedAttribute = models [ association . collection ] . attributes [ association . via ] ;
2016-03-16 14:41:15 +01:00
2018-02-27 11:52:18 +01:00
if ( ! relatedAttribute ) {
throw new Error ( ` The attribute \` ${ association . via } \` is missing in the model ${ _ . upperFirst ( association . collection ) } ${ association . plugin ? '(plugin - ' + association . plugin + ')' : '' } ` ) ;
}
2017-12-15 18:04:56 +01:00
types . current = 'collection' ;
2016-03-16 14:41:15 +01:00
2018-02-22 15:34:33 +01:00
if ( relatedAttribute . hasOwnProperty ( 'collection' ) && relatedAttribute . collection !== '*' && relatedAttribute . hasOwnProperty ( 'via' ) ) {
2017-12-15 18:04:56 +01:00
types . other = 'collection' ;
2018-02-22 15:34:33 +01:00
} else if ( relatedAttribute . hasOwnProperty ( 'collection' ) && relatedAttribute . collection !== '*' && ! relatedAttribute . hasOwnProperty ( 'via' ) ) {
2017-12-15 18:04:56 +01:00
types . other = 'collectionD' ;
2018-02-22 15:34:33 +01:00
} else if ( relatedAttribute . hasOwnProperty ( 'model' ) && relatedAttribute . model !== '*' ) {
2017-12-15 18:04:56 +01:00
types . other = 'model' ;
2018-02-22 15:34:33 +01:00
} else if ( relatedAttribute . hasOwnProperty ( 'collection' ) || relatedAttribute . hasOwnProperty ( 'model' ) ) {
2018-02-09 10:43:09 +01:00
types . other = 'morphTo' ;
2017-12-15 18:04:56 +01:00
}
} else if ( association . hasOwnProperty ( 'via' ) && association . hasOwnProperty ( 'model' ) ) {
types . current = 'modelD' ;
// We have to find if they are a model linked to this key
2018-05-16 12:07:02 +02:00
const model = models [ association . model ] ;
2018-04-30 17:14:13 +02:00
const attribute = model . attributes [ association . via ] ;
2018-02-26 14:39:06 +01:00
2018-04-30 17:14:13 +02:00
if ( attribute . hasOwnProperty ( 'via' ) && attribute . via === key && attribute . hasOwnProperty ( 'collection' ) && attribute . collection !== '*' ) {
types . other = 'collection' ;
} else if ( attribute . hasOwnProperty ( 'model' ) && attribute . model !== '*' ) {
types . other = 'model' ;
} else if ( attribute . hasOwnProperty ( 'collection' ) || attribute . hasOwnProperty ( 'model' ) ) {
types . other = 'morphTo' ;
}
2017-12-15 18:04:56 +01:00
} else if ( association . hasOwnProperty ( 'model' ) ) {
types . current = 'model' ;
// We have to find if they are a model linked to this key
_ . forIn ( models , model => {
_ . forIn ( model . attributes , attribute => {
if ( attribute . hasOwnProperty ( 'via' ) && attribute . via === key ) {
if ( attribute . hasOwnProperty ( 'collection' ) ) {
types . other = 'collection' ;
// Break loop
return false ;
} else if ( attribute . hasOwnProperty ( 'model' ) ) {
types . other = 'modelD' ;
// Break loop
return false ;
}
2016-03-16 14:41:15 +01:00
}
2017-12-15 18:04:56 +01:00
} ) ;
2016-03-16 14:41:15 +01:00
} ) ;
2017-12-15 18:04:56 +01:00
} else if ( association . hasOwnProperty ( 'collection' ) ) {
types . current = 'collectionD' ;
// We have to find if they are a model linked to this key
_ . forIn ( models , model => {
_ . forIn ( model . attributes , attribute => {
if ( attribute . hasOwnProperty ( 'via' ) && attribute . via === key ) {
if ( attribute . hasOwnProperty ( 'collection' ) ) {
types . other = 'collection' ;
// Break loop
return false ;
} else if ( attribute . hasOwnProperty ( 'model' ) ) {
types . other = 'modelD' ;
2018-02-12 18:54:34 +01:00
// Break loop
return false ;
}
}
} ) ;
} ) ;
2017-12-15 18:04:56 +01:00
}
2016-03-16 14:41:15 +01:00
2018-02-12 18:54:34 +01:00
if ( types . current === 'collection' && types . other === 'morphTo' ) {
2018-02-21 17:33:30 +01:00
return {
nature : 'manyToManyMorph' ,
verbose : 'morphMany'
} ;
} else if ( types . current === 'collection' && types . other === 'morphToD' ) {
2018-02-09 10:43:09 +01:00
return {
2018-02-22 15:34:33 +01:00
nature : 'manyToOneMorph' ,
2018-02-20 19:59:05 +01:00
verbose : 'morphMany'
2018-02-09 10:43:09 +01:00
} ;
2018-02-21 17:33:30 +01:00
} else if ( types . current === 'modelD' && types . other === 'morphTo' ) {
return {
nature : 'oneToManyMorph' ,
verbose : 'morphOne'
} ;
} else if ( types . current === 'modelD' && types . other === 'morphToD' ) {
2018-02-09 10:43:09 +01:00
return {
2018-02-22 15:34:33 +01:00
nature : 'oneToOneMorph' ,
2018-02-20 19:59:05 +01:00
verbose : 'morphOne'
2018-02-09 10:43:09 +01:00
} ;
2018-02-21 17:33:30 +01:00
} else if ( types . current === 'morphToD' && types . other === 'collection' ) {
2018-02-09 10:43:09 +01:00
return {
2018-02-22 15:34:33 +01:00
nature : 'oneMorphToMany' ,
2018-02-12 18:54:34 +01:00
verbose : 'belongsToMorph'
} ;
2018-02-21 17:33:30 +01:00
} else if ( types . current === 'morphToD' && types . other === 'model' ) {
2018-02-12 18:54:34 +01:00
return {
2018-02-22 15:34:33 +01:00
nature : 'oneMorphToOne' ,
2018-02-21 17:33:30 +01:00
verbose : 'belongsToMorph'
} ;
2018-02-26 11:12:49 +01:00
} else if ( types . current === 'morphTo' && ( types . other === 'model' || association . hasOwnProperty ( 'model' ) ) ) {
2018-02-21 17:33:30 +01:00
return {
nature : 'manyMorphToOne' ,
verbose : 'belongsToManyMorph'
} ;
2018-02-26 11:12:49 +01:00
} else if ( types . current === 'morphTo' && ( types . other === 'collection' || association . hasOwnProperty ( 'collection' ) ) ) {
2018-02-21 17:33:30 +01:00
return {
nature : 'manyMorphToMany' ,
2018-02-12 18:54:34 +01:00
verbose : 'belongsToManyMorph'
2018-02-09 10:43:09 +01:00
} ;
} else if ( types . current === 'modelD' && types . other === 'model' ) {
2017-12-15 18:04:56 +01:00
return {
nature : 'oneToOne' ,
verbose : 'belongsTo'
} ;
} else if ( types . current === 'model' && types . other === 'modelD' ) {
return {
nature : 'oneToOne' ,
verbose : 'hasOne'
} ;
} else if ( ( types . current === 'model' || types . current === 'modelD' ) && types . other === 'collection' ) {
return {
nature : 'manyToOne' ,
verbose : 'belongsTo'
} ;
} else if ( types . current === 'modelD' && types . other === 'collection' ) {
return {
nature : 'oneToMany' ,
verbose : 'hasMany'
} ;
} else if ( types . current === 'collection' && types . other === 'model' ) {
return {
nature : 'oneToMany' ,
verbose : 'hasMany'
} ;
} else if ( types . current === 'collection' && types . other === 'collection' ) {
return {
nature : 'manyToMany' ,
verbose : 'belongsToMany'
} ;
} else if ( types . current === 'collectionD' && types . other === 'collection' || types . current === 'collection' && types . other === 'collectionD' ) {
return {
nature : 'manyToMany' ,
verbose : 'belongsToMany'
} ;
} else if ( types . current === 'collectionD' && types . other === '' ) {
return {
nature : 'manyWay' ,
verbose : 'belongsToMany'
} ;
} else if ( types . current === 'model' && types . other === '' ) {
return {
nature : 'oneWay' ,
verbose : 'belongsTo'
} ;
}
2016-03-16 14:41:15 +01:00
2017-12-15 18:04:56 +01:00
return undefined ;
} catch ( e ) {
strapi . log . error ( ` Something went wrong in the model \` ${ _ . upperFirst ( currentModelName ) } \` with the attribute \` ${ key } \` ` ) ;
2018-02-21 17:33:30 +01:00
strapi . log . error ( e ) ;
2017-12-15 18:04:56 +01:00
strapi . stop ( ) ;
}
2016-03-16 14:41:15 +01:00
} ,
/ * *
* Return ORM used for this collection .
* /
2016-11-07 16:31:34 +01:00
getORM : collectionIdentity => {
2016-03-16 14:41:15 +01:00
return _ . get ( strapi . models , collectionIdentity . toLowerCase ( ) + '.orm' ) ;
} ,
2018-09-14 00:19:20 +02:00
/ * *
* Return table name for a collection many - to - many
* /
getCollectionName : ( associationA , associationB ) => {
return [ associationA , associationB ]
. sort ( ( a , b ) => a . collection < b . collection ? - 1 : 1 )
. map ( table => _ . snakeCase ( ` ${ pluralize . plural ( table . collection ) } ${ pluralize . plural ( table . via ) } ` ) )
. join ( '__' ) ;
} ,
2016-03-16 14:41:15 +01:00
/ * *
* Define associations key to models
* /
2017-01-11 17:53:40 +01:00
defineAssociations : function ( model , definition , association , key ) {
2017-12-16 18:26:04 +01:00
try {
// Initialize associations object
if ( definition . associations === undefined ) {
definition . associations = [ ] ;
}
2016-03-16 14:41:15 +01:00
2017-12-16 18:26:04 +01:00
// Exclude non-relational attribute
2018-02-22 15:34:33 +01:00
if ( ! association . hasOwnProperty ( 'collection' ) && ! association . hasOwnProperty ( 'model' ) ) {
2017-12-16 18:26:04 +01:00
return undefined ;
}
2016-03-16 14:41:15 +01:00
2017-12-16 18:26:04 +01:00
// Get relation nature
2018-02-22 15:34:33 +01:00
let details ;
const globalName = association . model || association . collection || '' ;
2017-12-16 18:26:04 +01:00
const infos = this . getNature ( association , key , undefined , model . toLowerCase ( ) ) ;
2018-02-22 15:34:33 +01:00
if ( globalName !== '*' ) {
2018-02-27 11:52:18 +01:00
details = association . plugin ?
_ . get ( strapi . plugins , ` ${ association . plugin } .models. ${ globalName } .attributes. ${ association . via } ` , { } ) :
_ . get ( strapi . models , ` ${ globalName } .attributes. ${ association . via } ` , { } ) ;
2018-02-22 15:34:33 +01:00
}
2017-12-16 18:26:04 +01:00
// Build associations object
2018-02-22 15:34:33 +01:00
if ( association . hasOwnProperty ( 'collection' ) && association . collection !== '*' ) {
2018-09-14 00:19:20 +02:00
const ast = {
2017-12-16 18:26:04 +01:00
alias : key ,
type : 'collection' ,
collection : association . collection ,
via : association . via || undefined ,
nature : infos . nature ,
autoPopulate : _ . get ( association , 'autoPopulate' , true ) ,
dominant : details . dominant !== true ,
plugin : association . plugin || undefined ,
2018-02-22 16:08:11 +01:00
filter : details . filter ,
2018-09-14 00:19:20 +02:00
} ;
if ( infos . nature === 'manyToMany' && ! association . plugin && definition . orm === 'bookshelf' ) {
ast . tableCollectionName = this . getCollectionName ( association , details ) ;
}
definition . associations . push ( ast ) ;
2018-02-22 15:34:33 +01:00
} else if ( association . hasOwnProperty ( 'model' ) && association . model !== '*' ) {
2017-12-16 18:26:04 +01:00
definition . associations . push ( {
alias : key ,
type : 'model' ,
model : association . model ,
via : association . via || undefined ,
nature : infos . nature ,
autoPopulate : _ . get ( association , 'autoPopulate' , true ) ,
dominant : details . dominant !== true ,
plugin : association . plugin || undefined ,
2018-02-22 16:08:11 +01:00
filter : details . filter ,
2018-02-12 18:54:34 +01:00
} ) ;
2018-02-22 15:34:33 +01:00
} else if ( association . hasOwnProperty ( 'collection' ) || association . hasOwnProperty ( 'model' ) ) {
2018-02-20 19:59:05 +01:00
const pluginsModels = Object . keys ( strapi . plugins ) . reduce ( ( acc , current ) => {
Object . keys ( strapi . plugins [ current ] . models ) . forEach ( ( entity ) => {
Object . keys ( strapi . plugins [ current ] . models [ entity ] . attributes ) . forEach ( ( attribute ) => {
const attr = strapi . plugins [ current ] . models [ entity ] . attributes [ attribute ] ;
2018-02-26 11:12:49 +01:00
2018-02-20 19:59:05 +01:00
if (
( attr . collection || attr . model || '' ) . toLowerCase ( ) === model . toLowerCase ( ) &&
strapi . plugins [ current ] . models [ entity ] . globalId !== definition . globalId
) {
acc . push ( strapi . plugins [ current ] . models [ entity ] . globalId ) ;
}
} ) ;
} ) ;
return acc ;
} , [ ] ) ;
const appModels = Object . keys ( strapi . models ) . reduce ( ( acc , entity ) => {
Object . keys ( strapi . models [ entity ] . attributes ) . forEach ( ( attribute ) => {
const attr = strapi . models [ entity ] . attributes [ attribute ] ;
if (
( attr . collection || attr . model || '' ) . toLowerCase ( ) === model . toLowerCase ( ) &&
strapi . models [ entity ] . globalId !== definition . globalId
) {
acc . push ( strapi . models [ entity ] . globalId ) ;
}
} ) ;
return acc ;
} , [ ] ) ;
const models = _ . uniq ( appModels . concat ( pluginsModels ) ) ;
2018-02-12 18:54:34 +01:00
definition . associations . push ( {
alias : key ,
2018-02-22 15:34:33 +01:00
type : association . model ? 'model' : 'collection' ,
2018-02-20 19:59:05 +01:00
related : models ,
2018-02-12 18:54:34 +01:00
nature : infos . nature ,
autoPopulate : _ . get ( association , 'autoPopulate' , true ) ,
2018-02-22 15:34:33 +01:00
filter : association . filter ,
2017-12-16 18:26:04 +01:00
} ) ;
}
} catch ( e ) {
strapi . log . error ( ` Something went wrong in the model \` ${ _ . upperFirst ( model ) } \` with the attribute \` ${ key } \` ` ) ;
2018-02-21 17:33:30 +01:00
strapi . log . error ( e ) ;
2017-12-16 18:26:04 +01:00
strapi . stop ( ) ;
2016-03-16 14:41:15 +01:00
}
2016-08-08 11:12:09 +02:00
} ,
2016-11-07 16:31:34 +01:00
getVia : ( attribute , association ) => {
2016-09-28 11:42:26 +02:00
return _ . findKey ( strapi . models [ association . model || association . collection ] . attributes , { via : attribute } ) ;
2017-09-12 17:58:31 +02:00
} ,
2018-10-08 22:35:25 +02:00
mergeStages : ( ... stages ) => {
return _ . unionWith ( ... stages , _ . isEqual ) ;
} ,
2018-10-09 01:10:15 +02:00
convertParams : function ( entity , params ) {
2018-09-24 21:20:30 +02:00
const { model , models , convertor , postProcessValue } = this . prepareStage (
entity ,
params
) ;
const _filter = this . splitPrimitiveAndRelationValues ( params ) ;
// Execute Steps in the given order
return _ . flow ( [
this . processValues ( { model , models , convertor , postProcessValue } ) ,
this . processPredicates ( { model , models , convertor } ) ,
this . processGeneratedResults ( ) ,
] ) ( _filter ) ;
} ,
2018-10-09 01:10:15 +02:00
prepareStage : function ( entity , params ) {
2017-09-13 10:30:37 +02:00
if ( ! entity ) {
2018-09-24 21:20:30 +02:00
throw new Error (
'You can\'t call the convert params method without passing the model\'s name as a first argument.'
) ;
2017-09-12 17:58:31 +02:00
}
2018-06-04 17:46:08 +02:00
// Remove the source params (that can be sent from the ctm plugin) since it is not a filter
if ( params . source ) {
delete params . source ;
}
2018-09-24 21:20:30 +02:00
const modelName = entity . toLowerCase ( ) ;
const models = this . getStrapiModels ( ) ;
const model = models [ modelName ] ;
2017-11-20 14:35:24 +01:00
2018-09-24 21:20:30 +02:00
if ( ! model ) {
throw new Error ( ` The model ${ modelName } can't be found. ` ) ;
}
2017-09-13 10:30:37 +02:00
2018-09-24 21:20:30 +02:00
if ( ! model . orm ) {
throw new Error (
` Impossible to determine the ORM used for the model ${ modelName } . `
) ;
2017-09-13 10:30:37 +02:00
}
2018-09-24 21:20:30 +02:00
const hook = strapi . hook [ model . orm ] ;
const convertor = hook . load ( ) . getQueryParams ;
const postProcessValue = hook . load ( ) . postProcessValue || _ . identity ;
2017-09-13 10:30:37 +02:00
2018-09-24 21:20:30 +02:00
return {
models ,
model ,
hook ,
convertor ,
postProcessValue ,
} ;
} ,
2017-09-13 10:30:37 +02:00
2018-10-09 01:10:15 +02:00
getStrapiModels : function ( ) {
2018-09-24 21:20:30 +02:00
return {
... strapi . models ,
... Object . keys ( strapi . plugins ) . reduce (
( acc , pluginName ) => ( {
... acc ,
... _ . get ( strapi . plugins [ pluginName ] , 'models' , { } ) ,
} ) ,
{ }
) ,
2017-09-12 17:58:31 +02:00
} ;
2018-09-24 21:20:30 +02:00
} ,
2017-09-12 17:58:31 +02:00
2018-10-09 01:10:15 +02:00
splitPrimitiveAndRelationValues : function ( _query ) {
2018-09-24 21:20:30 +02:00
const result = _ . reduce (
_query ,
( acc , value , key ) => {
if ( _ . startsWith ( key , '_' ) ) {
acc [ key ] = value ;
} else if ( ! _ . includes ( key , '.' ) ) {
acc . where [ key ] = value ;
} else {
_ . set ( acc . relations , this . injectRelationInKey ( key ) , value ) ;
2018-07-08 09:33:46 -04:00
}
2018-09-24 21:20:30 +02:00
return acc ;
} ,
{
where : { } ,
relations : { } ,
sort : '' ,
start : 0 ,
limit : 100 ,
2018-07-08 09:33:46 -04:00
}
2018-09-24 21:20:30 +02:00
) ;
return result ;
} ,
2017-09-12 17:58:31 +02:00
2018-10-09 01:10:15 +02:00
injectRelationInKey : function ( key ) {
2018-09-24 21:20:30 +02:00
const numberOfRelations = key . match ( /\./gi ) . length - 1 ;
const relationStrings = _ . times ( numberOfRelations , _ . constant ( 'relations' ) ) ;
return _ . chain ( key )
. split ( '.' )
. zip ( relationStrings )
. flatten ( )
. compact ( )
. join ( '.' )
. value ( ) ;
} ,
2017-09-12 17:58:31 +02:00
2018-10-09 01:10:15 +02:00
transformFilter : function ( filter , iteratee ) {
2018-09-24 21:20:30 +02:00
if ( ! _ . isArray ( filter ) && ! _ . isPlainObject ( filter ) ) {
return filter ;
}
2018-05-31 15:06:20 +02:00
2018-09-24 21:20:30 +02:00
return _ . transform ( filter , ( updatedFilter , value , key ) => {
const updatedValue = iteratee ( value , key ) ;
updatedFilter [ key ] = this . transformFilter ( updatedValue , iteratee ) ;
return updatedFilter ;
} ) ;
} ,
2017-09-12 17:58:31 +02:00
2018-10-09 01:10:15 +02:00
processValues : function ( { model , models , convertor , postProcessValue } ) {
return filter => {
let parentModel = model ;
return this . transformFilter ( filter , ( value , key ) => {
const field = this . getFieldFromKey ( key , parentModel ) ;
if ( ! field ) {
return this . processMeta ( value , key , {
field ,
client : model . client ,
model ,
convertor ,
} ) ;
}
if ( field . collection || field . model ) {
parentModel = models [ field . collection || field . model ] ;
}
return postProcessValue (
this . processValue ( value , key , { field , client : model . client , model } )
) ;
} ) ;
} ;
2018-09-24 21:20:30 +02:00
} ,
2017-09-12 17:58:31 +02:00
2018-10-09 01:10:15 +02:00
getFieldFromKey : function ( key , model ) {
2018-09-24 21:20:30 +02:00
let field ;
// Primary key is a unique case because it doesn't belong to the model's attributes
if ( key === model . primaryKey ) {
field = {
type : 'ID' , // Just in case
} ;
} else if ( model . attributes [ key ] ) {
field = model . attributes [ key ] ;
} else {
// Remove the filter keyword at the end
let splitKey = key . split ( '_' ) . slice ( 0 , - 1 ) ;
splitKey = splitKey . join ( '_' ) ;
if ( model . attributes [ splitKey ] ) {
field = model . attributes [ splitKey ] ;
2017-09-12 17:58:31 +02:00
}
2018-09-24 21:20:30 +02:00
}
2017-09-12 17:58:31 +02:00
2018-09-24 21:20:30 +02:00
return field ;
} ,
2018-10-09 01:10:15 +02:00
processValue : function ( value , key , { field , client } ) {
2018-09-24 21:20:30 +02:00
if ( field . type === 'boolean' && client === 'mysql' ) {
return value === 'true' ? '1' : '0' ;
}
return value ;
} ,
2018-10-09 01:10:15 +02:00
processMeta : function ( value , key , { convertor , model } ) {
2018-09-24 21:20:30 +02:00
if ( _ . includes ( [ '_start' , '_limit' ] , key ) ) {
return convertor ( value , key ) ;
} else if ( key === '_sort' ) {
return this . processSortMeta ( value , key , { convertor , model } ) ;
}
return value ;
} ,
2018-10-09 01:10:15 +02:00
processSortMeta : function ( value , key , { convertor , model } ) {
2018-09-24 21:20:30 +02:00
const [ attr , order = 'ASC' ] = value . split ( ':' ) ;
if ( ! _ . includes ( ORDERS , order ) ) {
throw new Error (
` Unkown order value: " ${ order } ", available values are: ${ ORDERS . join (
', '
) } `
) ;
}
const field = this . getFieldFromKey ( attr , model ) ;
if ( ! field ) {
throw new Error ( ` Unkown field: " ${ attr } " ` ) ;
}
return convertor ( order , key , attr ) ;
} ,
2018-10-09 01:10:15 +02:00
processPredicates : function ( { model , models , convertor } ) {
return filter => {
let parentModel = model ;
return this . transformFilter ( filter , ( value , key ) => {
const field = this . getFieldFromKey ( key , parentModel ) ;
if ( ! field ) {
return value ;
}
if ( field . collection || field . model ) {
parentModel = models [ field . collection || field . model ] ;
}
return this . processCriteriaMeta ( value , key , { convertor } ) ;
} ) ;
} ;
2018-09-24 21:20:30 +02:00
} ,
2018-10-09 01:10:15 +02:00
processCriteriaMeta : function ( value , key , { convertor } ) {
2018-09-24 21:20:30 +02:00
let type = '=' ;
if ( key . match ( /_{1}(?:ne|lte?|gte?|containss?|in)/ ) ) {
type = key . match ( /_{1}(?:ne|lte?|gte?|containss?|in)/ ) [ 0 ] ;
key = key . replace ( type , '' ) ;
}
return convertor ( value , type , key ) ;
} ,
2017-09-12 17:58:31 +02:00
2018-10-09 01:10:15 +02:00
processGeneratedResults : function ( ) {
return filter => {
if ( ! _ . isArray ( filter ) && ! _ . isPlainObject ( filter ) ) {
return filter ;
2018-09-24 21:20:30 +02:00
}
2018-10-09 01:10:15 +02:00
return _ . transform ( filter , ( updatedFilter , value , key ) => {
// Only set results for object of shape { value, key }
if ( _ . has ( value , 'value' ) && _ . has ( value , 'key' ) ) {
const cleanKey = _ . replace ( value . key , 'where.' , '' ) ;
_ . set ( updatedFilter , cleanKey , this . processGeneratedResults ( ) ( value . value ) ) ;
} else {
updatedFilter [ key ] = this . processGeneratedResults ( ) ( value ) ;
}
return updatedFilter ;
} ) ;
} ;
}
2016-03-16 14:41:15 +01:00
} ;