2016-03-16 14:41:15 +01:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Module dependencies
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Public node modules.
|
|
|
|
const _ = require('lodash');
|
|
|
|
|
2016-03-22 15:50:33 +01:00
|
|
|
// Node.js core
|
|
|
|
const path = require('path');
|
|
|
|
|
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-07-06 15:51:52 +02:00
|
|
|
|
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;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
2017-12-15 18:04:56 +01:00
|
|
|
if (association.hasOwnProperty('via') && association.hasOwnProperty('collection')) {
|
|
|
|
const relatedAttribute = models[association.collection].attributes[association.via];
|
2016-03-16 14:41:15 +01:00
|
|
|
|
2017-12-15 18:04:56 +01:00
|
|
|
types.current = 'collection';
|
2016-03-16 14:41:15 +01:00
|
|
|
|
2017-12-15 18:04:56 +01:00
|
|
|
if (relatedAttribute.hasOwnProperty('collection') && relatedAttribute.hasOwnProperty('via')) {
|
|
|
|
types.other = 'collection';
|
|
|
|
} else if (relatedAttribute.hasOwnProperty('collection') && !relatedAttribute.hasOwnProperty('via')) {
|
|
|
|
types.other = 'collectionD';
|
|
|
|
} else if (relatedAttribute.hasOwnProperty('model')) {
|
|
|
|
types.other = 'model';
|
2018-02-09 10:43:09 +01:00
|
|
|
} else if (relatedAttribute.hasOwnProperty('key')) {
|
|
|
|
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
|
|
|
|
_.forIn(_.omit(models, currentModelName || ''), model => {
|
|
|
|
_.forIn(model.attributes, attribute => {
|
|
|
|
if (attribute.hasOwnProperty('via') && attribute.via === key && attribute.hasOwnProperty('collection')) {
|
2016-08-08 11:12:09 +02:00
|
|
|
types.other = 'collection';
|
|
|
|
|
|
|
|
// Break loop
|
|
|
|
return false;
|
|
|
|
} else if (attribute.hasOwnProperty('model')) {
|
2017-12-15 18:04:56 +01:00
|
|
|
types.other = 'model';
|
2016-08-08 11:12:09 +02:00
|
|
|
|
|
|
|
// Break loop
|
|
|
|
return false;
|
2018-02-09 10:43:09 +01:00
|
|
|
} else if (attribute.hasOwnProperty('key')) {
|
2018-02-12 18:54:34 +01:00
|
|
|
types.other = 'morphTo';
|
|
|
|
|
|
|
|
// Break loop
|
|
|
|
return false;
|
2016-08-08 11:12:09 +02:00
|
|
|
}
|
2017-12-15 18:04:56 +01:00
|
|
|
});
|
2016-08-08 11:12:09 +02:00
|
|
|
});
|
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';
|
|
|
|
|
|
|
|
// Break loop
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2018-02-09 10:43:09 +01:00
|
|
|
} else if (association.hasOwnProperty('key')) {
|
|
|
|
types.current = 'morphTo';
|
2018-02-12 18:54:34 +01:00
|
|
|
|
|
|
|
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 => {
|
|
|
|
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 = 'model';
|
|
|
|
|
|
|
|
// 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-09 10:43:09 +01:00
|
|
|
return {
|
2018-02-12 18:54:34 +01:00
|
|
|
nature: 'manyToMorph',
|
|
|
|
verbose: 'belongsToMany'
|
2018-02-09 10:43:09 +01:00
|
|
|
};
|
|
|
|
} else if (types.current === 'modelD' && types.other === 'morphTo') {
|
|
|
|
return {
|
2018-02-12 18:54:34 +01:00
|
|
|
nature: 'oneToMorph',
|
|
|
|
verbose: 'belongsTo'
|
2018-02-09 10:43:09 +01:00
|
|
|
};
|
2018-02-12 18:54:34 +01:00
|
|
|
} else if (types.current === 'morphTo' && types.other === 'collection') {
|
2018-02-09 10:43:09 +01:00
|
|
|
return {
|
2018-02-12 18:54:34 +01:00
|
|
|
nature: 'morphToMany',
|
|
|
|
verbose: 'belongsToMorph'
|
|
|
|
};
|
|
|
|
} else if (types.current === 'morphTo' && types.other === 'model') {
|
|
|
|
return {
|
|
|
|
nature: 'morphToOne',
|
|
|
|
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}\``);
|
|
|
|
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');
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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-12 18:54:34 +01:00
|
|
|
if (!association.hasOwnProperty('collection') && !association.hasOwnProperty('model') && !association.hasOwnProperty('key')) {
|
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
|
|
|
|
const infos = this.getNature(association, key, undefined, model.toLowerCase());
|
|
|
|
const details = _.get(strapi.models, `${association.model || association.collection}.attributes.${association.via}`, {});
|
|
|
|
|
|
|
|
// Build associations object
|
|
|
|
if (association.hasOwnProperty('collection')) {
|
|
|
|
definition.associations.push({
|
|
|
|
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-12 18:54:34 +01:00
|
|
|
where: details.where,
|
2017-12-16 18:26:04 +01:00
|
|
|
});
|
|
|
|
} else if (association.hasOwnProperty('model')) {
|
|
|
|
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-12 18:54:34 +01:00
|
|
|
where: details.where,
|
|
|
|
});
|
|
|
|
} else if (association.hasOwnProperty('key')) {
|
|
|
|
definition.associations.push({
|
|
|
|
alias: key,
|
|
|
|
type: 'collection',
|
|
|
|
nature: infos.nature,
|
|
|
|
autoPopulate: _.get(association, 'autoPopulate', true),
|
|
|
|
key: association.key,
|
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}\``);
|
|
|
|
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
|
|
|
},
|
|
|
|
|
2017-09-13 10:30:37 +02:00
|
|
|
convertParams: (entity, params) => {
|
|
|
|
if (!entity) {
|
2017-10-10 15:15:17 +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
|
|
|
}
|
|
|
|
|
2017-09-13 10:30:37 +02:00
|
|
|
const model = entity.toLowerCase();
|
2017-11-20 14:35:24 +01:00
|
|
|
|
|
|
|
const models = _.assign(_.clone(strapi.models), Object.keys(strapi.plugins).reduce((acc, current) => {
|
2017-11-17 12:05:03 +01:00
|
|
|
_.assign(acc, _.get(strapi.plugins[current], ['models'], {}));
|
|
|
|
return acc;
|
|
|
|
}, {}));
|
2017-09-13 10:30:37 +02:00
|
|
|
|
2017-11-17 12:05:03 +01:00
|
|
|
if (!models.hasOwnProperty(model)) {
|
2017-09-13 10:30:37 +02:00
|
|
|
return this.log.error(`The model ${model} can't be found.`);
|
|
|
|
}
|
|
|
|
|
2017-11-17 12:05:03 +01:00
|
|
|
const connector = models[model].orm;
|
2017-09-13 10:30:37 +02:00
|
|
|
|
|
|
|
if (!connector) {
|
2017-12-07 14:35:35 +01:00
|
|
|
throw new Error(`Impossible to determine the ORM used for the model ${model}.`);
|
2017-09-13 10:30:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const convertor = strapi.hook[connector].load().getQueryParams;
|
2017-09-12 17:58:31 +02:00
|
|
|
const convertParams = {
|
|
|
|
where: {},
|
2017-09-13 12:18:54 +02:00
|
|
|
sort: '',
|
2017-09-12 17:58:31 +02:00
|
|
|
start: 0,
|
|
|
|
limit: 100
|
|
|
|
};
|
|
|
|
|
|
|
|
_.forEach(params, (value, key) => {
|
|
|
|
let result;
|
|
|
|
|
|
|
|
if (_.includes(['_start', '_limit'], key)) {
|
|
|
|
result = convertor(value, key);
|
|
|
|
} else if (key === '_sort') {
|
|
|
|
const [attr, order] = value.split(':');
|
|
|
|
result = convertor(order, key, attr);
|
|
|
|
} else {
|
|
|
|
const suffix = key.split('_');
|
|
|
|
|
|
|
|
let type;
|
|
|
|
|
|
|
|
if (_.includes(['ne', 'lt', 'gt', 'lte', 'gte'], _.last(suffix))) {
|
|
|
|
type = `_${_.last(suffix)}`;
|
|
|
|
key = _.dropRight(suffix).join('_');
|
|
|
|
} else {
|
|
|
|
type = '=';
|
|
|
|
}
|
|
|
|
|
|
|
|
result = convertor(value, type, key);
|
|
|
|
}
|
|
|
|
|
|
|
|
_.set(convertParams, result.key, result.value);
|
|
|
|
});
|
|
|
|
|
|
|
|
return convertParams;
|
2016-03-16 14:41:15 +01:00
|
|
|
}
|
|
|
|
};
|