Go to file
dbroqua 02734db677 Added LICENCE 2020-02-26 16:45:35 +01:00
config Updated docker images and tests suites 2020-02-18 13:38:40 +01:00
libs Added some new tests 2020-02-13 22:36:14 +01:00
migrations Added some new tests 2020-02-13 22:36:14 +01:00
models Updated model index 2020-02-18 11:20:10 +01:00
rules Added some new tests 2020-02-13 22:36:14 +01:00
seeders Added some new tests 2020-02-13 22:36:14 +01:00
test Added deleteOne tests 2020-02-17 15:16:00 +01:00
.babelrc Added some tests (createOne and getAll) 2020-02-11 15:38:28 +01:00
.eslintignore Added first iteration for the module 2020-02-10 16:13:42 +01:00
.eslintrc.js Added first iteration for the module 2020-02-10 16:13:42 +01:00
.gitignore Added some tests (createOne and getAll) 2020-02-11 15:38:28 +01:00
.gitlab-ci.yml Added some new tests 2020-02-13 22:36:14 +01:00
docker-compose.yml Updated docker images and tests suites 2020-02-18 13:38:40 +01:00
index.js Added first iteration for the module 2020-02-10 16:13:42 +01:00
LICENCE.txt Added LICENCE 2020-02-26 16:45:35 +01:00
package.json Updated docker images and tests suites 2020-02-18 13:38:40 +01:00
README.md Debug 2020-02-11 20:37:34 +01:00
test.sh Updated docker images and tests suites 2020-02-18 13:38:40 +01:00
yarn.lock Added some tests (createOne and getAll) 2020-02-11 15:38:28 +01:00

sequelize-middleware

pipeline status coverage report

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.

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 :

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

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 :

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

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 :

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);
    });
  });