Это руководство потребует предварительных знаний о технике объектно-реляционного сопоставления (ORM) мангуст.

Вступление

По мере роста вашего приложения ведение журнала становится важной частью для отслеживания всего. Это особенно важно для отладки.

В настоящее время в npm уже доступны модули регистрации. Эти модули могут хранить журналы в файле в разных форматах или на разных уровнях. Мы собираемся обсудить ведение журнала API в вашем приложении Node.js Express с помощью популярного ORM Mongoose.

Итак, как бы вы могли создать плагин Mongoose, который будет вести журнал за вас более чистым способом и упростит ведение журнала API?

Что такое плагин в Mongoose?

В Mongoose схемы являются подключаемыми. Плагин похож на функцию, которую вы можете использовать в своей схеме и повторно использовать снова и снова в экземплярах схемы.

Mongoose также предоставляет глобальные плагины, которые можно использовать для всех схем. Например, мы собираемся написать плагин, который будет создавать diff из двух jsons и писать в mongodb.

Шаг 1. Создание базовой модели схемы журнала

Давайте создадим базовую схему журнала со следующими шестью свойствами:

  • Действие: Судя по названию, это будет курс действий API, будь то create update delete или что-то еще.
  • Категория: категория API. Например врачи и пациенты. Это больше похоже на класс.
  • CreatedBy: пользователь, который использует API или вызвал его.
  • Сообщение: здесь вы можете включить любое сообщение, которое вы хотите показать, которое будет иметь смысл или поможет во время отладки.
  • Diff: это основное свойство, которое будет иметь diff двух JSON.

Вы можете добавить больше полей, если хотите, чтобы это имело смысл для вашего собственного приложения. Схема может быть изменена и обновлена ​​в соответствии с требованиями.

Вот наша модель: models/log.js

const mongoose = require('mongoose')
const Schema = mongoose.Schema
const { ObjectId } = Schema
const LogSchema = new Schema({
  action: { type: String, required: true },
  category: { type: String, required: true },
  createdBy: { type: ObjectId, ref: 'Account', required: true },
  message: { type: String, required: true },
  diff: { type: Schema.Types.Mixed },
},{
  timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' },
})
LogSchema.index({ action: 1, category: 1 })
module.exports = mongoose.model('Log', LogSchema)

Шаг 2. Напишите функцию, чтобы узнать разницу между двумя JSON.

Итак, следующий шаг - вам нужна функция многократного использования, которая на лету создаст diff из двух JSON.

Назовем это diff.js

const _ = require('lodash')
exports.getDiff = (curr, prev) => {
  function changes(object, base) {
    return _.transform(object, (result, value, key) => {
      if (!_.isEqual(value, base[key]))
        result[key] = (_.isObject(value) && _.isObject(base[key])) ?                 changes(value, base[key]) : value
    })
 }
 return changes(curr, prev)
}

Я использовал популярную библиотеку lodash, чтобы обеспечить ту же функциональность.

Давайте разберем вышеуказанную функцию и посмотрим, что происходит:

  • _.transform: это альтернатива .reduce для массивов. По сути, он будет перебирать ваши объекты keys и values. Он предоставляет accumulator, который является первым аргументом. result - это аккумулятор, и он изменяемый.
  • _.isEqual: выполняет глубокое сравнение двух значений, чтобы определить, эквивалентны ли они.

isEqual: этот метод поддерживает сравнение массивов, буферов массивов, логических значений, объектов даты, объектов ошибок, карт, чисел, объектов Object, регулярных выражений, наборов, строк, символов и типизированных массивов. Object объекты сравниваются по их собственным, а не унаследованным, перечисляемым свойствам. Функции и узлы DOM сравниваются на строгое равенство, т.е. ===.

Здесь мы перебираем каждое свойство и значение объекта и сравниваем его с нашим старым / предыдущим объектом.

Если value текущего объекта не равно значению того же свойства в предыдущем объекте: base[key] и если это значение является самим объектом, мы вызываем функцию changes рекурсивно, пока она не получит значение который, наконец, будет сохранен в result как result[key] = value.

Шаг 3. Создайте плагин для использования diff и сохраните его в базе данных.

Теперь нам нужно отслеживать предыдущие document в базе данных и создать diff перед сохранением в mongodb.

const _ = require('lodash')
const LogSchema = require('../models/log')
const { getDiff } = require('../utils/diff')
const plugin = function (schema) {
  schema.post('init', doc => {
    doc._original = doc.toObject({transform: false})
  })
  schema.pre('save', function (next) {
    if (this.isNew) {
      next()
    }else {
      this._diff = getDiff(this, this._original)
      next()
    }
})

  schema.methods.log = function (data)  {
    data.diff = {
      before: this._original,
      after: this._diff,
    }
    return LogSchema.create(data)
  }
}
module.exports = plugin

В Mongoose доступны разные крючки. На данный момент нам нужно использовать методы init и save, доступные в схеме.

this.isNew(): Если вы создаете новый документ, просто верните next() промежуточное ПО.

In schema.post('init') toObject():

doc._original = doc.toObject({transform: false})

Mongoose Models наследуются от Documents, у которых есть toObject() метод. Он преобразует document в Object(), а transform:false не позволяет преобразовать возвращаемый объект.

Шаг 4: Использование - Как использовать в API express.js

В вашем основном server.js или app.js:

Инициализируйте глобальный плагин, чтобы он был доступен для всех схем. Вы также можете использовать его для конкретной схемы, инициализировав его в модели схемы.

const mongoose = require('mongoose')
mongoose.plugin(require('./app/utils/diff-plugin'))

Вот базовый пример user API обновления:

const User = require('../models/user')
exports.updateUser = (req, res, next) => {
  return User.findById(req.params.id)
    .then(user => {
        if (!user)
           throw new Error('Target user does not exist. Failed to update.')
       const { name } = req.body
       if (name) user.name = name
       return user.save()
     })
     .then(result => {
       res.json(result)
       return result
     })
     .catch(next)
     .then(user => {
         if (user && typeof user.log === 'function') { 
            const data = {
              action: 'update-user',
              category: 'users',
              createdBy: req.user.id,
              message: 'Updated user name',
         }
         return user.log(data)
     }
     }).catch(err => {
         console.log('Caught error while logging: ', err)
       })
}

Заключение

В этом руководстве вы узнали, как создать плагин Mongoose и использовать его для регистрации changes в вашем API. Вы можете сделать гораздо больше с помощью плагинов, чтобы создать надежное приложение узла.

Вот ресурсы, чтобы узнать больше об использовании Mongoose и плагинов:

Я надеюсь, что вы найдете это руководство полезным, не стесняйтесь обращаться к нам, если у вас есть какие-либо вопросы.

Подпишитесь на Шайлеша Шехавата, чтобы получать уведомления, когда я публикую новый пост.

Не стесняйтесь хлопать в ладоши, если считаете, что это стоит прочитать!

Первоначально опубликовано на 101node.io 2 сентября 2018 г.