teableio/teable

View on GitHub
apps/nestjs-backend/src/features/calculation/system-field.service.ts

Summary

Maintainability
A
25 mins
Test Coverage
/* eslint-disable @typescript-eslint/naming-convention */
import { Injectable } from '@nestjs/common';
import { FieldType } from '@teable/core';
import { PrismaService } from '@teable/db-main-prisma';
import { Knex } from 'knex';
import { InjectModel } from 'nest-knexjs';
import { ClsService } from 'nestjs-cls';
import type { IClsStore } from '../../types/cls';
import { createFieldInstanceByRaw } from '../field/model/factory';

@Injectable()
export class SystemFieldService {
  constructor(
    private readonly cls: ClsService<IClsStore>,
    private readonly prismaService: PrismaService,
    @InjectModel('CUSTOM_KNEX') private readonly knex: Knex
  ) {}

  private async updateSystemField(
    dbTableName: string,
    recordIds: string[],
    userId: string,
    timeStr: string
  ) {
    if (!recordIds.length) return;

    const nativeQuery = this.knex(dbTableName)
      .update({
        __last_modified_time: timeStr,
        __last_modified_by: userId,
      })
      .whereIn('__id', recordIds)
      .toQuery();

    await this.prismaService.txClient().$executeRawUnsafe(nativeQuery);
  }

  async getModifiedSystemOpsMap(
    tableId: string,
    records: {
      fields: Record<string, unknown>;
      id: string;
    }[]
  ): Promise<
    {
      fields: Record<string, unknown>;
      id: string;
    }[]
  > {
    const user = this.cls.get('user');
    const timeStr = this.cls.get('tx.timeStr') ?? new Date().toISOString();

    const { dbTableName } = await this.prismaService.txClient().tableMeta.findUniqueOrThrow({
      where: { id: tableId },
      select: { dbTableName: true },
    });

    await this.updateSystemField(
      dbTableName,
      records.map((r) => r.id),
      user.id,
      timeStr
    );

    const fieldsRaw = await this.prismaService.txClient().field.findMany({
      where: {
        tableId,
        deletedTime: null,
        type: { in: [FieldType.LastModifiedTime, FieldType.LastModifiedBy] },
      },
    });

    if (!fieldsRaw.length) return records;

    const systemRecordFields = fieldsRaw.reduce<{ [fieldId: string]: unknown }>((pre, fieldRaw) => {
      const field = createFieldInstanceByRaw(fieldRaw);
      const { id, type } = field;
      if (type === FieldType.LastModifiedTime) {
        pre[id] = timeStr;
      }

      if (type === FieldType.LastModifiedBy) {
        pre[id] = field.convertDBValue2CellValue({
          id: user.id,
          title: user.name,
          email: user.email,
        });
      }
      return pre;
    }, {});

    return records.map((record) => {
      return {
        ...record,
        fields: {
          ...record.fields,
          ...systemRecordFields,
        },
      };
    });
  }
}