betagouv/service-national-universel

View on GitHub
api/src/models/referent.ts

Summary

Maintainability
A
0 mins
Test Coverage
import config from "config";
import mongoose, { Schema } from "mongoose";
import patchHistory from "mongoose-patch-history";
import bcrypt from "bcryptjs";

import mongooseElastic from "@selego/mongoose-elastic";
import { ReferentSchema, ReferentType } from "snu-lib";

import { DocumentExtended, CustomSaveParams, UserExtension, UserSaved } from "./types";
import esClient from "../es";
import * as brevo from "../brevo";
import anonymize from "../anonymization/referent";

const MODELNAME = "referent";

const schema = new Schema(ReferentSchema);

schema.virtual("fullName").get(function () {
  const { firstName, lastName } = this;
  if (firstName && lastName) return `${firstName} ${lastName}`;
  else return firstName || lastName;
});

schema.pre<SchemaExtended>("save", async function (next) {
  if (this.isModified("password")) {
    const hashedPassword = await bcrypt.hash(this.password, 10);
    this.password = hashedPassword;
  }
  return next();
});

schema.methods.comparePassword = async function (p: string) {
  const user = await ReferentModel.findById(this._id).select("password");
  return bcrypt.compare(p, user!.password || "");
};

schema.methods.anonymise = function () {
  return anonymize(this);
};

//Sync with Sendinblue
schema.post("save", function (doc) {
  brevo.sync(doc, MODELNAME);
});
schema.post("findOneAndUpdate", function (doc) {
  brevo.sync(doc, MODELNAME);
});
schema.post("deleteOne", function (doc) {
  brevo.unsync(doc);
});

schema.virtual("user").set<SchemaExtended>(function (user: UserSaved) {
  if (user) {
    const { _id, role, department, region, email, firstName, lastName, model } = user;
    this._user = { _id, role, department, region, email, firstName, lastName, model };
  }
});

schema.pre<SchemaExtended>("save", function (next, params: CustomSaveParams) {
  this.user = params?.fromUser;
  this.updatedAt = new Date();
  next();
});

schema.plugin(patchHistory, {
  mongoose,
  name: `${MODELNAME}Patches`,
  trackOriginalValue: true,
  includes: {
    modelName: { type: String, required: true, default: MODELNAME },
    user: { type: Object, required: false, from: "_user" },
  },
  excludes: [
    "/password",
    "/lastLoginAt",
    "/lastActivityAt",
    "/lastLogoutAt",
    "/passwordChangedAt",
    "/nextLoginAttemptIn",
    "/forgotPasswordResetToken",
    "/forgotPasswordResetExpires",
    "/invitationToken",
    "/invitationExpires",
    "/loginAttempts",
    "/attempts2FA",
    "/updatedAt",
    "/token2FA",
    "/token2FAExpires",
  ],
});

if (config.get("ENABLE_MONGOOSE_ELASTIC")) {
  schema.plugin(
    mongooseElastic(esClient, {
      selectiveIndexing: true,
      ignore: [
        "password",
        "lastLogoutAt",
        "passwordChangedAt",
        "nextLoginAttemptIn",
        "forgotPasswordResetToken",
        "forgotPasswordResetExpires",
        "invitationToken",
        "invitationExpires",
        "loginAttempts",
        "attempts2FA",
        "updatedAt",
        "lastActivityAt",
        "token2FA",
        "token2FAExpires",
      ],
    }),
    MODELNAME,
  );
}

schema.set("toObject", { virtuals: true });
schema.set("toJSON", { virtuals: true });

export type ReferentDocument<T = {}> = DocumentExtended<
  ReferentType & {
    // virtual fields
    fullName?: string;
  } & T
>;
type SchemaExtended = ReferentDocument & UserExtension;

export const ReferentModel = mongoose.model<ReferentDocument>(MODELNAME, schema);