2020-02-10 16:13:42 +01:00
|
|
|
import ErrorBuilder from "./ErrorBuilder";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Classe permettant de gérer les tables simples
|
|
|
|
*/
|
|
|
|
class QueryBuilder {
|
|
|
|
/**
|
|
|
|
* Initialisation de la classe
|
|
|
|
* @param {Object} params
|
|
|
|
* @param {Object} models
|
|
|
|
*/
|
|
|
|
constructor(params, models) {
|
|
|
|
this.params = params;
|
|
|
|
this.models = models;
|
|
|
|
this.includes = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Méthode permettant de générer le lien vers une ressource
|
|
|
|
* @param {String} href
|
|
|
|
* @param {Number} currentPage
|
|
|
|
* @param {Number} maxPage
|
|
|
|
* @param {String} page
|
|
|
|
*/
|
|
|
|
static _createLink(href, currentPage, maxPage, page) {
|
|
|
|
if (
|
|
|
|
(page === "first" && currentPage > 1) ||
|
|
|
|
(page === "last" && currentPage < maxPage) ||
|
|
|
|
(page === "prev" && currentPage > 1) ||
|
|
|
|
(page === "next" && currentPage < maxPage)
|
|
|
|
) {
|
|
|
|
let newIndex = 0;
|
2020-02-11 15:38:28 +01:00
|
|
|
// eslint-disable-next-line default-case
|
2020-02-10 16:13:42 +01:00
|
|
|
switch (page) {
|
|
|
|
case "first":
|
|
|
|
newIndex = 1;
|
|
|
|
break;
|
|
|
|
case "last":
|
|
|
|
newIndex = maxPage;
|
|
|
|
break;
|
|
|
|
case "prev":
|
|
|
|
newIndex = currentPage - 1;
|
|
|
|
break;
|
|
|
|
case "next":
|
|
|
|
newIndex = currentPage + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
href: href.replace(`page=${currentPage}`, `page=${newIndex}`),
|
|
|
|
methdod: "GET"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Méthde permettant de générer les élements de pagination
|
|
|
|
* @param {Object} req
|
|
|
|
* @param {Number} total
|
|
|
|
* @param {Number} limit
|
|
|
|
* @param {Number} skip
|
|
|
|
* @return {Object}
|
|
|
|
*/
|
|
|
|
static _createPagination(req, total, limit, skip) {
|
|
|
|
const maxPage = Math.ceil(total / limit); // Nombre total de pages
|
|
|
|
const currentPage = Math.ceil(skip / limit) + 1; // Numéro de la page courante
|
|
|
|
const href = `${req.protocol}://${req.get("host")}${req.originalUrl}`; // Lien vers la page actuelle
|
|
|
|
|
|
|
|
return {
|
|
|
|
paging: {
|
|
|
|
first: QueryBuilder._createLink(href, currentPage, maxPage, "first"),
|
|
|
|
prev: QueryBuilder._createLink(href, currentPage, maxPage, "prev"),
|
|
|
|
next: QueryBuilder._createLink(href, currentPage, maxPage, "next"),
|
|
|
|
last: QueryBuilder._createLink(href, currentPage, maxPage, "last")
|
|
|
|
},
|
|
|
|
total,
|
|
|
|
maxPage
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fonction permettant de remplacer les attributs du genre $lte pat Op.lte
|
|
|
|
* @param {Object} obj
|
|
|
|
* @return {Object}
|
|
|
|
*/
|
|
|
|
_replaceKeys(obj) {
|
|
|
|
const { Op } = this.models.Sequelize;
|
|
|
|
let newObject = {};
|
|
|
|
|
|
|
|
if (Array.isArray(obj)) {
|
|
|
|
newObject = [];
|
|
|
|
for (let i = 0; i < obj.length; i += 1) {
|
|
|
|
const value =
|
|
|
|
typeof obj[i] === "object" ? this._replaceKeys(obj[i]) : obj[i];
|
|
|
|
newObject.push(value);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Object.keys(obj).map(key => {
|
|
|
|
const value =
|
|
|
|
typeof obj[key] === "object" ? this._replaceKeys(obj[key]) : obj[key];
|
|
|
|
if (key.indexOf("$") === 0 && key.slice(-1) !== "$") {
|
|
|
|
switch (key) {
|
|
|
|
case "$or":
|
|
|
|
newObject[Op.or] = value;
|
|
|
|
break;
|
|
|
|
case "$lte":
|
|
|
|
newObject[Op.lte] = value;
|
|
|
|
break;
|
|
|
|
case "$gte":
|
|
|
|
newObject[Op.gte] = value;
|
|
|
|
break;
|
|
|
|
case "$contains":
|
|
|
|
newObject[Op.contains] = [value];
|
|
|
|
break;
|
|
|
|
case "$in":
|
|
|
|
newObject[Op.in] = value.split(",");
|
|
|
|
break;
|
2020-02-11 15:38:28 +01:00
|
|
|
case "$like":
|
|
|
|
newObject[Op.like] = value;
|
|
|
|
break;
|
2020-02-10 16:13:42 +01:00
|
|
|
default:
|
|
|
|
newObject[key] = [value];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
newObject[key] = value;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-02-11 15:38:28 +01:00
|
|
|
console.log("newObject:", newObject);
|
|
|
|
|
2020-02-10 16:13:42 +01:00
|
|
|
return newObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fonction permettant de savoir si un utilisateur a assez de droit pour effectuer une action
|
|
|
|
* @param {Object} req
|
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
|
|
|
_haveRight(req) {
|
|
|
|
let allowedRole = "all";
|
2020-02-11 15:38:28 +01:00
|
|
|
if (!req.user) {
|
|
|
|
req.user = {};
|
|
|
|
}
|
|
|
|
if (!req.user.role) {
|
|
|
|
req.user.role = "guest";
|
|
|
|
}
|
2020-02-10 16:13:42 +01:00
|
|
|
|
|
|
|
// eslint-disable-next-line default-case
|
|
|
|
switch (req.method) {
|
|
|
|
case "POST":
|
|
|
|
allowedRole = this.params.crud.write;
|
|
|
|
break;
|
|
|
|
case "PATCH":
|
|
|
|
case "PUT":
|
|
|
|
allowedRole = this.params.crud.edit;
|
|
|
|
break;
|
|
|
|
case "GET":
|
|
|
|
allowedRole = this.params.crud.read;
|
|
|
|
break;
|
|
|
|
case "DELETE":
|
|
|
|
allowedRole = this.params.crud.delete;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return allowedRole === "all" || allowedRole.indexOf(req.user.role) !== -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fonction permettant d'ajouter des restructions sur un get
|
|
|
|
* @param {Object} req
|
|
|
|
* @return {Object}
|
|
|
|
*/
|
|
|
|
_restrictOn(req) {
|
|
|
|
const where = {};
|
|
|
|
if (!this.params || !this.params.restrictOn) {
|
|
|
|
return where;
|
|
|
|
}
|
|
|
|
|
|
|
|
const _overrideWhere = restrictions => {
|
|
|
|
if (!restrictions) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (let i = 0; i < restrictions.length; i += 1) {
|
|
|
|
const restrict = restrictions[i];
|
|
|
|
|
|
|
|
const value =
|
|
|
|
restrict.type === "raw"
|
|
|
|
? restrict.value
|
|
|
|
: req[restrict.type][restrict.value];
|
|
|
|
|
|
|
|
if (restrict.roles.indexOf(req.user.role) !== -1) {
|
|
|
|
where[restrict.field] = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
_overrideWhere(this.params.restrictOn[req.getType]);
|
|
|
|
|
|
|
|
switch (req.method) {
|
|
|
|
case "PATCH":
|
|
|
|
_overrideWhere(this.params.restrictOn.update);
|
|
|
|
break;
|
|
|
|
case "DELETE":
|
|
|
|
_overrideWhere(this.params.restrictOn.delete);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Do nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
return where;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fonction permettant de surcharger des valeurs
|
|
|
|
* @param {Object} req
|
|
|
|
* @param {String} method
|
|
|
|
* @return {Object}
|
|
|
|
*/
|
|
|
|
_override(req, method) {
|
|
|
|
let override = {};
|
|
|
|
const params = this.params.override ? this.params.override[method] : {};
|
|
|
|
if (!this.params.override) {
|
|
|
|
return override;
|
|
|
|
}
|
|
|
|
|
|
|
|
// On surcharge certains paramètres passé en query
|
|
|
|
if (params.filters) {
|
|
|
|
Object.keys(params.filters).map(column => {
|
|
|
|
if (req.query[column]) {
|
|
|
|
const value = req.query[column];
|
|
|
|
|
|
|
|
const query = JSON.parse(
|
|
|
|
JSON.stringify(params.filters[column]).replace(
|
|
|
|
new RegExp("_TERM_", "g"),
|
|
|
|
value
|
|
|
|
)
|
|
|
|
);
|
|
|
|
override = Object.assign(override, this._replaceKeys(query));
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// On rajoute des paramètres à la requête
|
|
|
|
if (params.params) {
|
|
|
|
for (let i = 0; i < params.params.length; i += 1) {
|
|
|
|
const currentParam = params.params[i];
|
|
|
|
override[currentParam.append] =
|
|
|
|
req[currentParam.from][currentParam.value];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return override;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fonction permettant de charger la liste des relations à inclures lors d'un get
|
|
|
|
* @param {Object} req
|
|
|
|
* @param {Array} include
|
|
|
|
* @return {Mixed}
|
|
|
|
*/
|
|
|
|
_setInclusions(req, include) {
|
2020-02-11 15:38:28 +01:00
|
|
|
if (!req.user) {
|
|
|
|
req.user = {};
|
|
|
|
}
|
|
|
|
if (!req.user.role) {
|
|
|
|
req.user.role = "guest";
|
|
|
|
}
|
|
|
|
|
2020-02-10 16:13:42 +01:00
|
|
|
const includes = [];
|
|
|
|
|
|
|
|
const listOfIncludes = include || this.params.includes;
|
|
|
|
|
|
|
|
for (let i = 0; i < listOfIncludes.length; i += 1) {
|
|
|
|
const current = listOfIncludes[i];
|
|
|
|
// const include = current.collection;
|
|
|
|
|
|
|
|
if (
|
|
|
|
!current.requiredRole ||
|
|
|
|
current.requiredRole.indexOf(req.user.role) !== -1
|
|
|
|
) {
|
|
|
|
let currentInclude = null;
|
|
|
|
|
|
|
|
if (!current.model) {
|
|
|
|
currentInclude = current.collection;
|
|
|
|
} else {
|
|
|
|
currentInclude = {
|
|
|
|
as: current.collection,
|
|
|
|
model: current.model,
|
|
|
|
required: current.required || false
|
|
|
|
};
|
|
|
|
|
|
|
|
// Pour cette inclusion il y a des filtres à appliquer
|
|
|
|
if (current.restrictOn || current.include) {
|
|
|
|
currentInclude.where = {};
|
|
|
|
|
|
|
|
if (current.include) {
|
|
|
|
currentInclude.include = this._setInclusions(
|
|
|
|
req,
|
|
|
|
current.include
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// On parcours la liste des règles d'inclusion pour ce modèle
|
|
|
|
if (current.restrictOn) {
|
|
|
|
for (let j = 0; j < current.restrictOn.length; j += 1) {
|
|
|
|
const currentRestriction = current.restrictOn[j];
|
|
|
|
// Cette restriction s'applique à tout le monde (pas de field roles)
|
|
|
|
// ou alors elle s'applique juste sur une liste de groupes
|
|
|
|
if (
|
|
|
|
!currentRestriction.roles ||
|
|
|
|
currentRestriction.roles.indexOf(req.user.role) !== -1
|
|
|
|
) {
|
|
|
|
if (currentRestriction.type === "raw") {
|
|
|
|
currentInclude.where[currentRestriction.field] =
|
|
|
|
currentRestriction.value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
includes.push(currentInclude);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mode reccursif
|
|
|
|
if (include) {
|
|
|
|
return includes;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.includes = includes;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Méthode interne permettant de créer un item
|
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} value
|
|
|
|
* @param {Function} callback
|
|
|
|
*/
|
|
|
|
_insertItem(req, value, callback) {
|
|
|
|
const values = {};
|
|
|
|
// On converti les 'null' en null (formData par exemple)
|
|
|
|
Object.keys(value).map(key => {
|
|
|
|
if (value[key] === "null") {
|
|
|
|
values[key] = null;
|
|
|
|
} else {
|
|
|
|
values[key] = value[key];
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
// Création de l'élément
|
|
|
|
this.models[this.params.model]
|
|
|
|
.create(values)
|
|
|
|
.then(item => {
|
|
|
|
let createdItem = item;
|
|
|
|
if (this.params.format) {
|
|
|
|
const formatRules = req.user
|
|
|
|
? this.params.format[req.user.role]
|
|
|
|
: null;
|
|
|
|
|
|
|
|
if (formatRules) {
|
|
|
|
createdItem = this._formatItem(item, formatRules);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
callback(null, createdItem);
|
|
|
|
})
|
|
|
|
.catch(err => {
|
2020-02-11 15:38:28 +01:00
|
|
|
switch (err.name) {
|
|
|
|
case "SequelizeUniqueConstraintError":
|
|
|
|
callback(new ErrorBuilder(409.1, "Duplicate item"));
|
|
|
|
break;
|
|
|
|
case "SequelizeForeignKeyConstraintError":
|
|
|
|
callback(new ErrorBuilder(406.2, "Bad foreign key"));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
callback(new ErrorBuilder(500, err));
|
2020-02-10 16:13:42 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fonction permettant de formater un item en fonction du user
|
|
|
|
* @param {Object} item
|
|
|
|
* @param {Object} formatRule
|
|
|
|
* @return {Object}
|
|
|
|
*/
|
|
|
|
_formatItem(item, formatRule) {
|
|
|
|
const formated = {};
|
2020-02-11 15:38:28 +01:00
|
|
|
|
2020-02-10 16:13:42 +01:00
|
|
|
Object.keys(formatRule).map(key => {
|
|
|
|
switch (typeof formatRule[key]) {
|
|
|
|
case "string":
|
2020-02-11 15:38:28 +01:00
|
|
|
if (item) {
|
|
|
|
formated[key] = item[key] || null;
|
|
|
|
}
|
2020-02-10 16:13:42 +01:00
|
|
|
break;
|
|
|
|
case "object":
|
|
|
|
if (Array.isArray(item[key])) {
|
|
|
|
formated[key] = [];
|
|
|
|
for (let i = 0; i < item[key].length; i += 1) {
|
|
|
|
formated[key].push(
|
|
|
|
this._formatItem(item[key][i], formatRule[key])
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
formated[key] = this._formatItem(item[key], formatRule[key]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Do nothing
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
return formated;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fonction permettant de formater une liste d'items en fonction du user
|
|
|
|
* @param {Object} req
|
|
|
|
* @param {Object} items
|
|
|
|
* @return {Object}
|
|
|
|
*/
|
|
|
|
_formatItems(req, items) {
|
|
|
|
if (!this.params.format) {
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
const formatRules = this.params.format[req.user.role];
|
|
|
|
|
|
|
|
if (!formatRules) {
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
|
|
|
const formated = [];
|
|
|
|
for (let i = 0; i < items.length; i += 1) {
|
|
|
|
formated.push(this._formatItem(items[i], formatRules));
|
|
|
|
}
|
|
|
|
|
|
|
|
return formated;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Méthode permettant de vérifier que les valeurs reçues dans le body sont valides
|
|
|
|
* @param {Object} req
|
|
|
|
* @param {Function} callback
|
|
|
|
*/
|
|
|
|
_checkCreateOneValues(req, callback) {
|
|
|
|
// On regarde s'il faut surcharger les valeurs du body avec des valeurs dérivées (req.user, req.params...)
|
|
|
|
if (this.params.override && this.params.override.create) {
|
|
|
|
if (this.params.override.create.body) {
|
|
|
|
for (let i = 0; i < this.params.override.create.body.length; i += 1) {
|
|
|
|
const override = this.params.override.create.body[i];
|
|
|
|
req.body[override.append] = req[override.from][override.value];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// On teste le body
|
|
|
|
const { error, value } = this.params.validate.create.validate(req.body, {
|
|
|
|
abortEarly: false
|
|
|
|
});
|
|
|
|
|
|
|
|
if (error) {
|
2020-02-11 15:38:28 +01:00
|
|
|
callback(new ErrorBuilder(406.1, error));
|
2020-02-10 16:13:42 +01:00
|
|
|
} else {
|
|
|
|
callback(null, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fonction permettant de vérifier et de surcharger des valeurs lors d'un get
|
|
|
|
* @param {Object} req
|
|
|
|
* @param {Function} callback
|
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
|
|
|
_createQuery(req, callback) {
|
|
|
|
this._setInclusions(req);
|
|
|
|
const query = {};
|
|
|
|
let where = {};
|
|
|
|
let order = [];
|
|
|
|
|
|
|
|
// On test les droits
|
|
|
|
if (!this._haveRight(req)) {
|
2020-02-11 15:38:28 +01:00
|
|
|
callback(new ErrorBuilder(401.1, "You're not allowed"));
|
2020-02-10 16:13:42 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// On teste la query (ou les params)
|
|
|
|
const toValidate = req.getType === "list" ? req.query : req.params;
|
2020-02-11 15:38:28 +01:00
|
|
|
|
|
|
|
if (!toValidate) {
|
|
|
|
callback(
|
|
|
|
new ErrorBuilder(
|
|
|
|
406.0,
|
|
|
|
`Missing ${req.getType === "list" ? "query" : "params"}`
|
|
|
|
)
|
|
|
|
);
|
|
|
|
return false;
|
|
|
|
}
|
2020-02-10 16:13:42 +01:00
|
|
|
const { error, value } = this.params.validate[
|
|
|
|
req.getType
|
|
|
|
].validate(toValidate, { abortEarly: false });
|
|
|
|
|
2020-02-11 15:38:28 +01:00
|
|
|
// Un paramètre n'est pas bon dans la query
|
|
|
|
if (error) {
|
|
|
|
callback(new ErrorBuilder(406.1, error));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-02-10 16:13:42 +01:00
|
|
|
// On vire, pour le moment la liste des filtres un peu particuliers
|
|
|
|
let listOfIgnoredFilters = [
|
|
|
|
"limit",
|
|
|
|
"page",
|
|
|
|
"sort",
|
|
|
|
"order",
|
|
|
|
this.params.itemId
|
|
|
|
];
|
|
|
|
if (this.params.removeKeys && this.params.removeKeys[req.getType]) {
|
|
|
|
listOfIgnoredFilters = listOfIgnoredFilters.concat(
|
|
|
|
this.params.removeKeys[req.getType]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
this.params.override &&
|
|
|
|
this.params.override[req.getType] &&
|
|
|
|
this.params.override[req.getType].filters
|
|
|
|
) {
|
|
|
|
Object.keys(this.params.override[req.getType].filters).map(key => {
|
|
|
|
listOfIgnoredFilters.push(key);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
// On rajoute les filtres autorisés
|
|
|
|
Object.keys(value).map(key => {
|
|
|
|
if (listOfIgnoredFilters.indexOf(key) === -1) {
|
|
|
|
where[key] = value[key];
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (req.getType === "list") {
|
|
|
|
// Aucune pagination n'est passée, on set celle par défaut
|
|
|
|
if (!value.page || !value.limit) {
|
|
|
|
value.page = 1;
|
2020-02-11 15:38:28 +01:00
|
|
|
value.limit = 50;
|
2020-02-10 16:13:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Un tri est spécifié
|
|
|
|
if (value.order && value.sort) {
|
|
|
|
order = [[value.sort, value.order.toUpperCase()]];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// On get un item. on set son id
|
|
|
|
where.id = value[this.params.itemId];
|
|
|
|
}
|
|
|
|
|
|
|
|
// S'il y a des restrictions (genre un utilisateur n'a le droit de voir que tel ou tel items)
|
|
|
|
const restrict = this._restrictOn(req);
|
|
|
|
where = Object.assign(where, restrict);
|
|
|
|
|
|
|
|
// On regarde s'il n'y a pas des valeurs à overrider
|
|
|
|
const override = this._override(req, req.getType);
|
|
|
|
where = Object.assign(where, override);
|
|
|
|
|
|
|
|
if (order) {
|
|
|
|
query.order = order;
|
|
|
|
}
|
|
|
|
|
|
|
|
query.distinct = true; // On supprime les id en double (jointure de type hasmany)
|
|
|
|
query.where = where; // On rajoute des filtres
|
|
|
|
if (this.includes) {
|
|
|
|
query.include = this.includes; // On set la liste des modèles à inclure
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hack pour faire un recherche dans les nested de type hasMany
|
|
|
|
query.subQuery = false;
|
|
|
|
|
|
|
|
callback(null, query, {
|
|
|
|
start: value.page * value.limit - value.limit,
|
|
|
|
limit: value.limit
|
|
|
|
});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = QueryBuilder;
|