/* eslint-disable @typescript-eslint/ban-types */ import isEqual from "fast-deep-equal"; import isArray from "lodash/isArray"; import isObject from "lodash/isObject"; import pick from "lodash/pick"; import { FindOptions, NonAttribute } from "sequelize"; import { Model as SequelizeModel } from "sequelize-typescript"; class Model< TModelAttributes extends {} = any, TCreationAttributes extends {} = TModelAttributes > extends SequelizeModel { /** * Find all models in batches, calling the callback function for each batch. * * @param query The query options. * @param callback The function to call for each batch of results */ static async findAllInBatches( query: FindOptions, callback: (results: Array, query: FindOptions) => Promise ) { if (!query.offset) { query.offset = 0; } if (!query.limit) { query.limit = 10; } let results; do { // @ts-expect-error this T results = await this.findAll(query); await callback(results, query); query.offset += query.limit; } while (results.length >= query.limit); } /** * Returns the attributes that have changed since the last save and their previous values. * * @returns An object with `attributes` and `previousAttributes` keys. */ public get changeset(): NonAttribute<{ attributes: Partial; previous: Partial; }> { const changes = this.changed() as Array | false; const attributes: Partial = {}; const previousAttributes: Partial = {}; if (!changes) { return { attributes, previous: previousAttributes, }; } for (const change of changes) { const previous = this.previous(change); const current = this.getDataValue(change); if ( isObject(previous) && isObject(current) && !isArray(previous) && !isArray(current) ) { const difference = Object.keys(previous) .concat(Object.keys(current)) .filter((key) => !isEqual(previous[key], current[key])); previousAttributes[change] = pick( previous, difference ) as TModelAttributes[keyof TModelAttributes]; attributes[change] = pick( current, difference ) as TModelAttributes[keyof TModelAttributes]; } else { previousAttributes[change] = previous; attributes[change] = current; } } return { attributes, previous: previousAttributes, }; } } export default Model;