Added first iteration for the module
This commit is contained in:
parent
6f41f71269
commit
ef7ca0315b
11 changed files with 6003 additions and 0 deletions
1
.eslintignore
Normal file
1
.eslintignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/node_modules/
|
30
.eslintrc.js
Normal file
30
.eslintrc.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
module.exports = {
|
||||||
|
parser: "babel-eslint",
|
||||||
|
extends: ["airbnb-base", "prettier", "plugin:jest/recommended"],
|
||||||
|
plugins: ["prettier", "jest"],
|
||||||
|
env: {
|
||||||
|
es6: true,
|
||||||
|
browser: false,
|
||||||
|
node: true,
|
||||||
|
"jest/globals": true
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
__DEV__: true
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// enable additional rules
|
||||||
|
"no-plusplus": [2, { allowForLoopAfterthoughts: true }],
|
||||||
|
"no-underscore-dangle": "off",
|
||||||
|
indent: ["error", 2, { SwitchCase: 1 }],
|
||||||
|
"linebreak-style": ["error", "unix"],
|
||||||
|
quotes: ["error", "double"],
|
||||||
|
semi: ["error", "always"],
|
||||||
|
// Forbid the use of extraneous packages
|
||||||
|
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-extraneous-dependencies.md
|
||||||
|
"import/no-extraneous-dependencies": ["error", { packageDir: "." }],
|
||||||
|
// ESLint plugin for prettier formatting
|
||||||
|
// https://github.com/prettier/eslint-plugin-prettier
|
||||||
|
"prettier/prettier": "error",
|
||||||
|
"func-names": ["error", "never"]
|
||||||
|
}
|
||||||
|
};
|
68
.gitignore
vendored
Normal file
68
.gitignore
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
/reports/
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Typescript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
/dist/
|
||||||
|
|
||||||
|
# Seeder info, proper to each environment
|
||||||
|
sequelize-data.json
|
||||||
|
|
213
README.md
213
README.md
|
@ -1,2 +1,215 @@
|
||||||
# sequelize-middleware
|
# sequelize-middleware
|
||||||
|
|
||||||
|
Sequelize-middleware est un module NodeJS permettant d'automatiser la création d'une API REST utilisant Sequelize comme SGBD.
|
||||||
|
|
||||||
|
Sequelize-middleware s'appuie également sur Joi pour valider les données reçues ainsi que les appels API effectuées.
|
||||||
|
|
||||||
|
## Fonctionnalités
|
||||||
|
|
||||||
|
Ce module est découpé en 3 fonctionnalités principales :
|
||||||
|
* Le `Middleware`, partie centrale du projet
|
||||||
|
* `ErrorBuilder` qui permet de surcharger la méthode `new Error()`
|
||||||
|
* `ResponseFormater` qui se charge de formater les données retournée au client final
|
||||||
|
|
||||||
|
### Middleware
|
||||||
|
|
||||||
|
Méthode par défaut, permettant de générer tout le code propre à un endpoint.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import Middleware from "sequelize-middleare";
|
||||||
|
|
||||||
|
import models from "../models";
|
||||||
|
import params from "../rules/Endpoint";
|
||||||
|
|
||||||
|
const middleware = new Middleware(params, models);
|
||||||
|
|
||||||
|
middleware.getAll(req, callback);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Initilisation
|
||||||
|
|
||||||
|
Le constructeur doit recevoir 2 paramètres :
|
||||||
|
* params
|
||||||
|
* models
|
||||||
|
|
||||||
|
`params` représente le fichier des règles pour cet endpoint.
|
||||||
|
`models` fait référence au fichier `models/index.js` généré par Sequelize.
|
||||||
|
|
||||||
|
Le fichier params doit ressembler à ceci :
|
||||||
|
|
||||||
|
```js
|
||||||
|
import Joi from "@hapi/joi";
|
||||||
|
const models = require("../models"); // Facultatif
|
||||||
|
|
||||||
|
const Rules = {
|
||||||
|
model: "Areas", // Nom de la collection Sequelize
|
||||||
|
crud: { // Liste des rôles (req.user.roles) autorisés en fonction de l'action
|
||||||
|
read: ["installer", "admin", "user"],
|
||||||
|
write: ["installer", "admin"],
|
||||||
|
edit: ["installer", "admin"],
|
||||||
|
delete: ["installer", "admin"]
|
||||||
|
},
|
||||||
|
includes: [ // Liste des inclusions à faire lors d'un getOne/getAll
|
||||||
|
{
|
||||||
|
collection: "Devices", // Nom de la collection à inclure
|
||||||
|
requiredRole: ["installer", "admin", "user"], // Rôles autorisés à voir cette inclusion
|
||||||
|
required: false, // Sauf cas particulier toujours mettre false
|
||||||
|
model: models.AreasDevices, // Nom du modèle Sequelize à inclure
|
||||||
|
include: [ // Liste des sous-inclusions
|
||||||
|
{
|
||||||
|
collection: "Device",
|
||||||
|
requiredRole: ["installer", "admin", "user"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
format: { // Formatage des données en fonction du rôle de l'utilisateur (dans le cas ou l'on veut surcharger le formatage du modèle)
|
||||||
|
user: {
|
||||||
|
idRetourne: "_idSql", // Clé retournée: Clé du modèle
|
||||||
|
id: "id",
|
||||||
|
nom: "name"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemId: "areaId", // Id de l'item dans la req.params
|
||||||
|
validate: { // Définition des des règles de valeurs acceptées en fonction du verbe
|
||||||
|
// Création d'un nouvele élément
|
||||||
|
create: Joi.object({
|
||||||
|
name: Joi.string().required(),
|
||||||
|
type: Joi.string()
|
||||||
|
.valid("OFFICE", "COMMUNAL-AREAS")
|
||||||
|
.required()
|
||||||
|
}),
|
||||||
|
// Mise à jour d'un élément
|
||||||
|
update: Joi.object({
|
||||||
|
name: Joi.string(),
|
||||||
|
type: Joi.string().valid("OFFICE", "COMMUNAL-AREAS")
|
||||||
|
}),
|
||||||
|
// Sélection d'un élément (req.params)
|
||||||
|
item: Joi.object({
|
||||||
|
areaId: Joi.number().required()
|
||||||
|
}),
|
||||||
|
// Sélection d'une liste d'éléments (req.query)
|
||||||
|
list: Joi.object({
|
||||||
|
limit: Joi.number()
|
||||||
|
.integer()
|
||||||
|
.min(1)
|
||||||
|
.max(50),
|
||||||
|
page: Joi.number()
|
||||||
|
.integer()
|
||||||
|
.min(1),
|
||||||
|
type: Joi.string().valid("OFFICE", "COMMUNAL-AREAS"),
|
||||||
|
sort: Joi.string()
|
||||||
|
.valid("id", "name", "type", "createdAt", "updatedAt")
|
||||||
|
.only(),
|
||||||
|
order: Joi.string()
|
||||||
|
.valid("asc", "desc")
|
||||||
|
.only()
|
||||||
|
})
|
||||||
|
.with("limit", "page")
|
||||||
|
.with("page", "limit")
|
||||||
|
.with("sort", "order")
|
||||||
|
.with("order", "sort")
|
||||||
|
},
|
||||||
|
removeKeys: {
|
||||||
|
// Permet de supprimer automatiquement des valeurs de requêtes
|
||||||
|
item: ["partnerId"] // Sur la sélection d'un item on supprime req.params.partnerId
|
||||||
|
},
|
||||||
|
override: {
|
||||||
|
list: { // Permet de convertir des paramètres reçus via req.query pour en générer des filtres complexes
|
||||||
|
filters: {
|
||||||
|
'category': { // On converti req.query.category
|
||||||
|
$or: [
|
||||||
|
{
|
||||||
|
categoryId: '_TERM_',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoriesId: {
|
||||||
|
$contains: '_TERM_',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'countryId': { // On converti req.query pour dire à Sequelize que ça correspond à $Details.countryId$
|
||||||
|
'$Details.countryId$': '_TERM_',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
// Création d'un nouvel item
|
||||||
|
body: [
|
||||||
|
// On modifie req.body
|
||||||
|
{
|
||||||
|
append: "offerId", // On rajoute un attribut offerId
|
||||||
|
from: "params", // Que l'on prends dans req.params
|
||||||
|
value: "offerId" // Et dont la clé est offerId
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
restrictOn: { // Permet de rajouter des restrictions en fonction des rôles
|
||||||
|
update: [ // Lors d'une mise à jour au autorise l'utilisateur à modifier uniquement les éléments donc state est égal à NEW.
|
||||||
|
{
|
||||||
|
roles: ['user'],
|
||||||
|
type: 'raw', // raw permet de forcer une valeur, sinon user/params/body/query
|
||||||
|
field: 'state',
|
||||||
|
value: 'NEW',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
delete: [],
|
||||||
|
list: [],
|
||||||
|
create: []
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Rules;
|
||||||
|
```
|
||||||
|
|
||||||
|
### ErrorBuilder
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { ErrorBuilder } from "sequelize-middleare";
|
||||||
|
```
|
||||||
|
|
||||||
|
Ce module permet de remplacer la méthode `new Error()` de JS en ajoutant la possibilité de générer un code d'erreur qui sera ensuite retourné via `res.status().json()`.
|
||||||
|
|
||||||
|
Le code reçu doit être un `float` dont la partie entière représente un code HTTP valide et la partie réelle représente un code d'erreur plus détaillé de l'erreur.
|
||||||
|
|
||||||
|
Exemples d'utilistion :
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { ErrorBuilder } from "sequelize-middleare";
|
||||||
|
|
||||||
|
new ErrorBuilder(406.0, "Erreur générique de type 406");
|
||||||
|
new ErrorBuilder(406.1, "Le champs mot de passe est absent");
|
||||||
|
new ErrorBuilder(406.2, "Le champs mot de passe doit contenir 8 caractères minimum");
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### ResponseFormater
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { ResponseFormater } from "sequelize-middleare";
|
||||||
|
```
|
||||||
|
|
||||||
|
Ce module permet de formater à la fois le json ainsi que le code http retourné au client une fois que le middleware à fini son traitement.
|
||||||
|
|
||||||
|
Exemples d'utilisation :
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { ResponseFormater } from "sequelize-middleare";
|
||||||
|
|
||||||
|
router
|
||||||
|
.route("/")
|
||||||
|
.get(passport.authenticate(["jwt"]), function(req, res, next) {
|
||||||
|
middleware.getAll(req, (err, response) => {
|
||||||
|
ResponseFormater(req, res, next, err, response);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.post(passport.authenticate(["jwt"]), function(req, res, next) {
|
||||||
|
middleware.createOne(req, (err, response) => {
|
||||||
|
ResponseFormater(req, res, next, err, response);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
7
index.js
Normal file
7
index.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import _ErrorBuilder from "./libs/ErrorBuilder";
|
||||||
|
import Middleware from "./libs/Middleware";
|
||||||
|
import _ResponseFormater from "./libs/ResponseFormater";
|
||||||
|
|
||||||
|
export default Middleware;
|
||||||
|
export const ErrorBuilder = _ErrorBuilder;
|
||||||
|
export const ResponseFormater = _ResponseFormater;
|
21
libs/ErrorBuilder.js
Normal file
21
libs/ErrorBuilder.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
* Classe permettant la gestion des erreurs personilisées
|
||||||
|
*/
|
||||||
|
class ErrorBuilder extends Error {
|
||||||
|
/**
|
||||||
|
* @param {Number} errorCode
|
||||||
|
* @param {Mixed} ...params
|
||||||
|
*/
|
||||||
|
constructor(errorCode, ...params) {
|
||||||
|
super(...params);
|
||||||
|
|
||||||
|
if (Error.captureStackTrace) {
|
||||||
|
Error.captureStackTrace(this, ErrorBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.errorCode = errorCode.toFixed(1);
|
||||||
|
this.date = new Date();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ErrorBuilder;
|
212
libs/Middleware.js
Normal file
212
libs/Middleware.js
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
import QueryBuilder from "./QueryBuilder";
|
||||||
|
import ErrorBuilder from "./ErrorBuilder";
|
||||||
|
|
||||||
|
class Middleware extends QueryBuilder {
|
||||||
|
/**
|
||||||
|
* Initialisation de la classe
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {Object} models
|
||||||
|
*/
|
||||||
|
constructor(params, models) {
|
||||||
|
super(params, models);
|
||||||
|
this.params = params || {};
|
||||||
|
this.models = models || {};
|
||||||
|
this.includes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction permettant de créer un nouvel élément
|
||||||
|
* @param {Object} req
|
||||||
|
* @param {Function} callback
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
createOne(req, callback) {
|
||||||
|
// On test les droits
|
||||||
|
if (!this._haveRight(req)) {
|
||||||
|
callback(new ErrorBuilder(401.1, "You're not allowed"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On teste le body
|
||||||
|
this._checkCreateOneValues(req, (err, value) => {
|
||||||
|
// Il manque un paramètre dans le body
|
||||||
|
if (err) {
|
||||||
|
callback(err, value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._insertItem(req, value, callback);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction permettant de retourner une liste d'items
|
||||||
|
* @param {Object} req
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
getAll(req, callback) {
|
||||||
|
// On spécifie le type de requête (list ou item)
|
||||||
|
req.getType = "list";
|
||||||
|
|
||||||
|
this._createQuery(req, (err, query, limit) => {
|
||||||
|
if (err) {
|
||||||
|
callback(err, query);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.models[this.params.model]
|
||||||
|
.findAndCountAll(query)
|
||||||
|
.then(res => {
|
||||||
|
if (!res || res.count === 0) {
|
||||||
|
callback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const rows = [];
|
||||||
|
for (let i = 0; i < res.rows.length; i += 1) {
|
||||||
|
const current = res.rows[i];
|
||||||
|
let found = false;
|
||||||
|
for (let j = 0; j < rows.length; j += 1) {
|
||||||
|
if (rows[j].id === current.id) {
|
||||||
|
found = true;
|
||||||
|
rows[j].assign({}, rows[j], current);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found || rows.length === 0) {
|
||||||
|
rows.push(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const total = rows.length;
|
||||||
|
const values = rows.slice(limit.start, limit.start + limit.limit);
|
||||||
|
|
||||||
|
callback(null, {
|
||||||
|
data: this._formatItems(req, values),
|
||||||
|
...QueryBuilder._createPagination(
|
||||||
|
req,
|
||||||
|
total,
|
||||||
|
limit.limit,
|
||||||
|
limit.start
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(callback);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction permettant de retourner un item par son Id
|
||||||
|
* @param {Object} req
|
||||||
|
* @param {Function} callback
|
||||||
|
*/
|
||||||
|
getOne(req, callback) {
|
||||||
|
// On spécifie le type de requête (list ou item)
|
||||||
|
req.getType = "item";
|
||||||
|
|
||||||
|
this._createQuery(req, (err, query) => {
|
||||||
|
if (err) {
|
||||||
|
callback(err, query);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.models[this.params.model]
|
||||||
|
.findOne(query)
|
||||||
|
.then(item => {
|
||||||
|
if (!item) {
|
||||||
|
callback(new ErrorBuilder(404, "Item not found"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let formatedItem = item;
|
||||||
|
if (this.params.format) {
|
||||||
|
const formatRules = this.params.format[req.user.role];
|
||||||
|
|
||||||
|
if (formatRules) {
|
||||||
|
formatedItem = this._formatItem(item, formatRules);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback(null, formatedItem);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(callback);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction permettant de mettre à jour un item
|
||||||
|
* @param {Object} req
|
||||||
|
* @param {Function} callback
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
patchOne(req, callback) {
|
||||||
|
// On test les droits
|
||||||
|
if (!this._haveRight(req)) {
|
||||||
|
callback(new ErrorBuilder(401.1, "You're not allowed"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On teste les params
|
||||||
|
const { error, value } = this.params.validate.update.validate(req.body, {
|
||||||
|
abortEarly: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Un paramètre n'est pas bon dans les params
|
||||||
|
if (error) {
|
||||||
|
callback(new ErrorBuilder(406, error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getOne(req, (err, item) => {
|
||||||
|
if (err) {
|
||||||
|
callback(err, item);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
item
|
||||||
|
.update(value)
|
||||||
|
.then(updated => {
|
||||||
|
callback(null, updated);
|
||||||
|
})
|
||||||
|
.catch(callback);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction permettant de supprimer un item
|
||||||
|
* @param {Object} req
|
||||||
|
* @param {Function} callback
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
deleteOne(req, callback) {
|
||||||
|
// On test les droits
|
||||||
|
if (!this._haveRight(req)) {
|
||||||
|
callback(new ErrorBuilder(401.1, "You're not allowed"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getOne(req, (err, item) => {
|
||||||
|
if (err) {
|
||||||
|
callback(err, item);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
item
|
||||||
|
.destroy()
|
||||||
|
.then(() => {
|
||||||
|
callback(null, {});
|
||||||
|
})
|
||||||
|
.catch(callback);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Middleware;
|
576
libs/QueryBuilder.js
Normal file
576
libs/QueryBuilder.js
Normal file
|
@ -0,0 +1,576 @@
|
||||||
|
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;
|
||||||
|
switch (page) {
|
||||||
|
case "first":
|
||||||
|
newIndex = 1;
|
||||||
|
break;
|
||||||
|
case "last":
|
||||||
|
newIndex = maxPage;
|
||||||
|
break;
|
||||||
|
case "prev":
|
||||||
|
newIndex = currentPage - 1;
|
||||||
|
break;
|
||||||
|
case "next":
|
||||||
|
newIndex = currentPage + 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
newIndex = currentPage;
|
||||||
|
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;
|
||||||
|
default:
|
||||||
|
newObject[key] = [value];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newObject[key] = value;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params) {
|
||||||
|
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) {
|
||||||
|
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 => {
|
||||||
|
if (err.name === "SequelizeUniqueConstraintError") {
|
||||||
|
callback(new ErrorBuilder(409, "Duplicate item"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
callback(err);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction permettant de formater un item en fonction du user
|
||||||
|
* @param {Object} item
|
||||||
|
* @param {Object} formatRule
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
_formatItem(item, formatRule) {
|
||||||
|
const formated = {};
|
||||||
|
Object.keys(formatRule).map(key => {
|
||||||
|
switch (typeof formatRule[key]) {
|
||||||
|
case "string":
|
||||||
|
formated[key] = item[key] || null;
|
||||||
|
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) {
|
||||||
|
callback(new ErrorBuilder(406, error));
|
||||||
|
} 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)) {
|
||||||
|
callback(new ErrorBuilder(401, "You're not allowed"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On teste la query (ou les params)
|
||||||
|
const toValidate = req.getType === "list" ? req.query : req.params;
|
||||||
|
const { error, value } = this.params.validate[
|
||||||
|
req.getType
|
||||||
|
].validate(toValidate, { abortEarly: false });
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Un paramètre n'est pas bon dans la query
|
||||||
|
if (error) {
|
||||||
|
callback(new ErrorBuilder(406, error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.getType === "list") {
|
||||||
|
// Aucune pagination n'est passée, on set celle par défaut
|
||||||
|
if (!value.page || !value.limit) {
|
||||||
|
value.page = 1;
|
||||||
|
value.limit = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 ( value.page) {
|
||||||
|
// query.offset = ( value.page - 1 ) * value.limit;
|
||||||
|
// query.limit = value.limit;
|
||||||
|
// }
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (!value.page || !value.limit) {
|
||||||
|
value.page = 1;
|
||||||
|
value.limit = 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, query, {
|
||||||
|
start: value.page * value.limit - value.limit,
|
||||||
|
limit: value.limit
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = QueryBuilder;
|
53
libs/ResponseFormater.js
Normal file
53
libs/ResponseFormater.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
* Fonction permettant de formater une réponse API
|
||||||
|
* @param {Object} req
|
||||||
|
* @param {Object} res
|
||||||
|
* @param {Functiont} next
|
||||||
|
* @param {Object} err
|
||||||
|
* @param {Object} response {code: Integer, res: Array/Object}
|
||||||
|
*/
|
||||||
|
const ResponseFormater = (req, res, next, err, response) => {
|
||||||
|
if (err) {
|
||||||
|
const code = err.errorCode || 500;
|
||||||
|
|
||||||
|
res
|
||||||
|
.status(Math.trunc(code))
|
||||||
|
.json({
|
||||||
|
code,
|
||||||
|
message: err.message
|
||||||
|
})
|
||||||
|
.end();
|
||||||
|
} else {
|
||||||
|
switch (req.method) {
|
||||||
|
case "GET":
|
||||||
|
res
|
||||||
|
.status(response ? 200 : 204)
|
||||||
|
.json(response)
|
||||||
|
.end();
|
||||||
|
break;
|
||||||
|
case "PATCH":
|
||||||
|
res
|
||||||
|
.status(200)
|
||||||
|
.json(response)
|
||||||
|
.end();
|
||||||
|
break;
|
||||||
|
case "DELETE":
|
||||||
|
res
|
||||||
|
.status(200)
|
||||||
|
.json(response)
|
||||||
|
.end();
|
||||||
|
break;
|
||||||
|
case "POST":
|
||||||
|
res
|
||||||
|
.status(201)
|
||||||
|
.json(response)
|
||||||
|
.end();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
next(new Error("Not implemented"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ResponseFormater;
|
87
package.json
Normal file
87
package.json
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
{
|
||||||
|
"name": "sequelize-middleware",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Middleware to automate tasks with Sequelize",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "./node_modules/.bin/eslint . --fix",
|
||||||
|
"test": "jest --forceExit --detectOpenHandles --maxWorkers=10",
|
||||||
|
"coverage": "jest --coverage"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git@framagit.org:dbroqua/sequelize-middleware.git"
|
||||||
|
},
|
||||||
|
"author": "Damien Broqua <contact@darkou.fr>",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "13.x",
|
||||||
|
"yarn": "1.x"
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.js": [
|
||||||
|
"./node_modules/.bin/eslint --fix",
|
||||||
|
"git add"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"verbose": true,
|
||||||
|
"roots": [
|
||||||
|
"<rootDir>",
|
||||||
|
"<rootDir>/test/"
|
||||||
|
],
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.js$": "babel-jest"
|
||||||
|
},
|
||||||
|
"transformIgnorePatterns": [
|
||||||
|
"<rootDir>/node_modules/(?!(jest-test))"
|
||||||
|
],
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"setupFilesAfterEnv": [
|
||||||
|
"<rootDir>/test/utils/setup.js"
|
||||||
|
],
|
||||||
|
"coverageReporters": [
|
||||||
|
"html",
|
||||||
|
"cobertura",
|
||||||
|
"jest-junit"
|
||||||
|
],
|
||||||
|
"reporters": [
|
||||||
|
"default",
|
||||||
|
"jest-junit",
|
||||||
|
[
|
||||||
|
"./node_modules/jest-html-reporter",
|
||||||
|
{
|
||||||
|
"pageTitle": "Test Report",
|
||||||
|
"outputPath": "reports/html/test-results.html",
|
||||||
|
"includeFailureMsg": true,
|
||||||
|
"includeConsoleLog": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nodemonConfig": {
|
||||||
|
"ext": "*.js,*.json,*yaml"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-eslint": "^10.0.3",
|
||||||
|
"eslint": "^6.8.0",
|
||||||
|
"eslint-config-airbnb-base": "^14.0.0",
|
||||||
|
"eslint-config-prettier": "^6.10.0",
|
||||||
|
"eslint-plugin-import": "^2.20.1",
|
||||||
|
"eslint-plugin-jest": "^23.7.0",
|
||||||
|
"eslint-plugin-prettier": "^3.1.2",
|
||||||
|
"husky": "^4.2.1",
|
||||||
|
"jest": "^25.1.0",
|
||||||
|
"jest-html-reporter": "^2.8.0",
|
||||||
|
"lint-staged": "^10.0.7",
|
||||||
|
"prettier": "^1.19.1",
|
||||||
|
"sinon": "^8.1.1",
|
||||||
|
"supertest": "^4.0.2"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue