# sequelize-middleware [![pipeline status](https://framagit.org/dbroqua/sequelize-middleware/badges/develop/pipeline.svg)](https://framagit.org/dbroqua/sequelize-middleware/commits/develop) [![coverage report](https://framagit.org/dbroqua/sequelize-middleware/badges/develop/coverage.svg)](https://framagit.org/dbroqua/sequelize-middleware/commits/develop) 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); }); }); ```