huridocs/uwazi

View on GitHub
app/api/odm/logHelper.ts

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
/* eslint-disable max-classes-per-file */
//@ts-ignore
import PromisePool from '@supercharge/promise-pool';
import { SyncDBDataSource } from 'api/common.v2/database/SyncDBDataSource';
import { model as updatelogsModel } from 'api/updatelogs';
import mongoose from 'mongoose';
import { EntitySchema } from 'shared/types/entityType';
import { FileType } from 'shared/types/fileType';
import { DataType, UwaziFilterQuery, models } from './model';

const getBatchSteps = async <T, U>(
  model: SyncDBDataSource<T, U>,
  query: UwaziFilterQuery<DataType<U>>,
  batchSize: number
): Promise<U[]> => {
  const allIds = await model.get(query, '_id', { sort: { _id: 1 } });

  const steps = [];
  for (let i = 0; i < allIds.length; i += batchSize) {
    steps.push(allIds[i]);
  }

  return steps;
};

export class UpdateLogHelper<T> implements UpdateLogger<T> {
  collectionName: string;

  static batchSizeUpsertMany = 50000;

  constructor(collectionName: string) {
    this.collectionName = collectionName;
  }

  async upsertLogOne(doc: mongoose.Document) {
    const logData = { namespace: this.collectionName, mongoId: doc._id };
    await updatelogsModel.findOneAndUpdate(
      logData,
      { ...logData, timestamp: Date.now(), deleted: false },
      { upsert: true }
    );
  }

  async upsertLogMany(
    query: UwaziFilterQuery<DataType<T>>,
    deleted = false,
    batchSize = UpdateLogHelper.batchSizeUpsertMany
  ) {
    await new PromisePool()
      .for(await getBatchSteps(models[this.collectionName](), query, batchSize))
      .withConcurrency(5)
      .process(async (stepIndex: T) => {
        const batch = await models[this.collectionName]().get(
          { ...query, $and: [{ _id: { $gte: stepIndex } }] },
          { _id: 1 },
          { limit: batchSize }
        );

        await updatelogsModel._updateMany(
          { mongoId: { $in: batch }, namespace: this.collectionName },
          { $set: { timestamp: Date.now(), deleted } },
          { lean: true }
        );
      });
  }
}

export class NoLogger<T> implements UpdateLogger<T> {
  // eslint-disable-next-line class-methods-use-this
  async upsertLogOne() {
    return Promise.resolve();
  }

  // eslint-disable-next-line class-methods-use-this
  async upsertLogMany() {
    return Promise.resolve();
  }
}

export class EntitiesUpdateLogHelper<T> extends UpdateLogHelper<T> {
  private filesHelper: UpdateLogger<FileType>;

  constructor(collectionName: string, filesHelper: UpdateLogger<FileType>) {
    super(collectionName);
    this.filesHelper = filesHelper;
  }

  async upsertLogOne(entity: mongoose.Document): Promise<void> {
    await super.upsertLogOne(entity);
    const typedEntity = entity as unknown as EntitySchema;
    await this.filesHelper.upsertLogMany({ entity: typedEntity.sharedId });
  }
}

export function createUpdateLogHelper<T>(collectionName: string): UpdateLogger<T> {
  if (collectionName === 'activitylog') return new NoLogger<T>();
  if (collectionName === 'entities') {
    return new EntitiesUpdateLogHelper<T>(collectionName, createUpdateLogHelper<FileType>('files'));
  }
  return new UpdateLogHelper<T>(collectionName);
}

export interface UpdateLogger<T> {
  upsertLogOne(doc: mongoose.Document): Promise<void>;

  upsertLogMany(query: UwaziFilterQuery<T>, deleted?: boolean, batchSize?: number): Promise<void>;
}