Remove dead code to backfill record positions (#11439)

Fixes https://github.com/twentyhq/core-team-issues/issues/767
This commit is contained in:
Félix Malfait
2025-04-08 11:02:40 +02:00
committed by GitHub
parent 8b5c259c36
commit 89abf3db4f
3 changed files with 0 additions and 283 deletions

View File

@ -1,142 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { FieldMetadataType } from 'twenty-shared/types';
import { RecordPositionBackfillService } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-service';
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
describe('RecordPositionBackfillService', () => {
let recordPositionService;
let objectMetadataRepository;
let workspaceDataSourceService;
let service: RecordPositionBackfillService;
beforeEach(async () => {
recordPositionService = {
buildRecordPosition: jest.fn().mockResolvedValue([
{
position: 1,
},
]),
};
objectMetadataRepository = {
find: jest.fn().mockReturnValue([]),
};
workspaceDataSourceService = {
getSchemaName: jest.fn().mockReturnValue('schemaName'),
executeRawQuery: jest.fn().mockResolvedValue([]),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
RecordPositionBackfillService,
{
provide: RecordPositionService,
useValue: recordPositionService,
},
{
provide: WorkspaceDataSourceService,
useValue: workspaceDataSourceService,
},
{
provide: getRepositoryToken(ObjectMetadataEntity, 'metadata'),
useValue: objectMetadataRepository,
},
],
}).compile();
service = module.get<RecordPositionBackfillService>(
RecordPositionBackfillService,
);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('when no object metadata found, should do nothing', async () => {
await service.backfill('workspaceId', false);
expect(workspaceDataSourceService.executeRawQuery).not.toHaveBeenCalled();
});
it('when objectMetadata without position, should do nothing', async () => {
objectMetadataRepository.find.mockReturnValue([]);
await service.backfill('workspaceId', false);
expect(workspaceDataSourceService.executeRawQuery).not.toHaveBeenCalled();
});
it('when objectMetadata but all record with position, should create and run query once', async () => {
objectMetadataRepository.find.mockReturnValue([
{
id: '1',
nameSingular: 'company',
fields: [
{
type: FieldMetadataType.POSITION,
isCustom: true,
nameSingular: 'position',
},
],
},
]);
await service.backfill('workspaceId', false);
expect(workspaceDataSourceService.executeRawQuery).toHaveBeenCalledTimes(1);
});
it('when record without position, should create and run query twice', async () => {
objectMetadataRepository.find.mockReturnValue([
{
id: '1',
nameSingular: 'company',
fields: [
{
type: FieldMetadataType.POSITION,
isCustom: true,
nameSingular: 'position',
},
],
},
]);
workspaceDataSourceService.executeRawQuery.mockResolvedValueOnce([
{
id: '1',
},
]);
await service.backfill('workspaceId', false);
expect(workspaceDataSourceService.executeRawQuery).toHaveBeenCalledTimes(2);
expect(recordPositionService.buildRecordPosition).toHaveBeenCalledTimes(1);
});
it('when dryRun is true, should not update position', async () => {
objectMetadataRepository.find.mockReturnValue([
{
id: '1',
nameSingular: 'company',
fields: [
{
type: FieldMetadataType.POSITION,
isCustom: true,
nameSingular: 'position',
},
],
},
]);
workspaceDataSourceService.executeRawQuery.mockResolvedValueOnce([
{
id: '1',
},
]);
await service.backfill('workspaceId', true);
expect(workspaceDataSourceService.executeRawQuery).toHaveBeenCalledTimes(1);
expect(recordPositionService.buildRecordPosition).toHaveBeenCalledTimes(1);
});
});

View File

@ -1,17 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { RecordPositionBackfillService } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-service';
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
@Module({
imports: [
WorkspaceDataSourceModule,
TypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'),
],
providers: [RecordPositionService, RecordPositionBackfillService],
exports: [RecordPositionBackfillService],
})
export class RecordPositionBackfillModule {}

View File

@ -1,124 +0,0 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { isDefined } from 'class-validator';
import { FieldMetadataType } from 'twenty-shared/types';
import { Repository } from 'typeorm';
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
import { RecordPositionQueryType } from 'src/engine/core-modules/record-position/types/record-position-query.type';
import { buildRecordPositionQuery } from 'src/engine/core-modules/record-position/utils/build-record-position-query.util';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
@Injectable()
export class RecordPositionBackfillService {
private readonly logger = new Logger(RecordPositionBackfillService.name);
constructor(
@InjectRepository(ObjectMetadataEntity, 'metadata')
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
private readonly recordPositionService: RecordPositionService,
) {}
async backfill(workspaceId: string, dryRun: boolean) {
this.logger.log(
`Starting backfilling record positions for workspace ${workspaceId}`,
);
const dataSourceSchema =
this.workspaceDataSourceService.getSchemaName(workspaceId);
const objectMetadataCollection = await this.objectMetadataRepository.find({
where: {
workspaceId,
fields: {
name: 'position',
type: FieldMetadataType.POSITION,
},
},
relations: {
fields: true,
},
});
for (const objectMetadata of objectMetadataCollection) {
const [recordsWithoutPositionQuery, recordsWithoutPositionQueryParams] =
buildRecordPositionQuery(
{
recordPositionQueryType: RecordPositionQueryType.FIND_BY_POSITION,
positionValue: null,
},
objectMetadata,
dataSourceSchema,
);
const recordsWithoutPosition =
await this.workspaceDataSourceService.executeRawQuery(
recordsWithoutPositionQuery,
recordsWithoutPositionQueryParams,
workspaceId,
);
if (
!isDefined(recordsWithoutPosition) ||
recordsWithoutPosition?.length === 0
) {
this.logger.log(
`No records without position for ${objectMetadata.nameSingular}`,
);
continue;
}
const position = await this.recordPositionService.buildRecordPosition({
objectMetadata: {
isCustom: objectMetadata.isCustom,
nameSingular: objectMetadata.nameSingular,
},
value: 'last',
workspaceId,
});
for (
let recordIndex = 0;
recordIndex < recordsWithoutPosition.length;
recordIndex++
) {
const recordId = recordsWithoutPosition[recordIndex].id;
if (!recordId) {
this.logger.log(
`Fetched record without id for ${objectMetadata.nameSingular}`,
);
continue;
}
const backfilledPosition = position + recordIndex;
this.logger.log(
`Backfilling position ${backfilledPosition} for ${objectMetadata.nameSingular} ${recordId}`,
);
if (dryRun) {
continue;
}
const [query, params] = buildRecordPositionQuery(
{
recordPositionQueryType: RecordPositionQueryType.UPDATE_POSITION,
recordId: recordsWithoutPosition[recordIndex].id,
positionValue: position + recordIndex,
},
objectMetadata,
dataSourceSchema,
);
await this.workspaceDataSourceService.executeRawQuery(
query,
params,
workspaceId,
);
}
}
}
}