Skip to content

Commit

Permalink
Add backfill position job by workspace (#5725)
Browse files Browse the repository at this point in the history
- Removing existing listener that was backfilling created records
without position
- Switch to a job that backfill all objects within workspace
- Adapting `FIND_BY_POSITION` so it can fetch objects without position.
Currently we needed to input a number
  • Loading branch information
thomtrp authored Jun 4, 2024
1 parent b1f12d7 commit 25f4e44
Show file tree
Hide file tree
Showing 9 changed files with 307 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@ describe('RecordPositionQueryFactory', () => {
it('should return query and params for FIND_BY_POSITION', async () => {
const positionValue = 1;
const queryType = RecordPositionQueryType.FIND_BY_POSITION;
const [query, params] = await factory.create(
const [query, params] = factory.create(
{ positionValue, recordPositionQueryType: queryType },
objectMetadataItem,
dataSourceSchema,
);

expect(query).toEqual(
`SELECT position FROM ${dataSourceSchema}."${objectMetadataItem.nameSingular}"
`SELECT id, position FROM ${dataSourceSchema}."${objectMetadataItem.nameSingular}"
WHERE "position" = $1`,
);
expect(params).toEqual([positionValue]);
});

it('should return query and params for FIND_MIN_POSITION', async () => {
const queryType = RecordPositionQueryType.FIND_MIN_POSITION;
const [query, params] = await factory.create(
const [query, params] = factory.create(
{ recordPositionQueryType: queryType },
objectMetadataItem,
dataSourceSchema,
Expand All @@ -48,7 +48,7 @@ describe('RecordPositionQueryFactory', () => {

it('should return query and params for FIND_MAX_POSITION', async () => {
const queryType = RecordPositionQueryType.FIND_MAX_POSITION;
const [query, params] = await factory.create(
const [query, params] = factory.create(
{ recordPositionQueryType: queryType },
objectMetadataItem,
dataSourceSchema,
Expand All @@ -64,7 +64,7 @@ describe('RecordPositionQueryFactory', () => {
const positionValue = 1;
const recordId = '1';
const queryType = RecordPositionQueryType.UPDATE_POSITION;
const [query, params] = await factory.create(
const [query, params] = factory.create(
{ positionValue, recordId, recordPositionQueryType: queryType },
objectMetadataItem,
dataSourceSchema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export enum RecordPositionQueryType {
}

type FindByPositionQueryArgs = {
positionValue: number;
positionValue: number | null;
recordPositionQueryType: RecordPositionQueryType.FIND_BY_POSITION;
};

Expand Down Expand Up @@ -77,10 +77,12 @@ export class RecordPositionQueryFactory {
name: string,
dataSourceSchema: string,
): [RecordPositionQuery, RecordPositionQueryParams] {
const positionStringParam = positionValue ? '= $1' : 'IS NULL';

return [
`SELECT position FROM ${dataSourceSchema}."${name}"
WHERE "position" = $1`,
[positionValue],
`SELECT id, position FROM ${dataSourceSchema}."${name}"
WHERE "position" ${positionStringParam}`,
positionValue ? [positionValue] : [],
];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Inject } from '@nestjs/common';

import { Command, CommandRunner, Option } from 'nest-commander';

import {
RecordPositionBackfillJob,
RecordPositionBackfillJobData,
} from 'src/engine/api/graphql/workspace-query-runner/jobs/record-position-backfill.job';
import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service';

export type RecordPositionBackfillCommandOptions = {
workspaceId: string;
dryRun?: boolean;
};

@Command({
name: 'migrate-0.20:backfill-record-position',
description: 'Backfill record position',
})
export class RecordPositionBackfillCommand extends CommandRunner {
constructor(
@Inject(MessageQueue.recordPositionBackfillQueue)
private readonly messageQueueService: MessageQueueService,
) {
super();
}

@Option({
flags: '-w, --workspace-id [workspace_id]',
description: 'workspace id',
required: true,
})
parseWorkspaceId(value: string): string {
return value;
}

@Option({
flags: '-d, --dry-run [dry run]',
description: 'Dry run: Log backfill actions.',
required: false,
})
dryRun(value: string): boolean {
return Boolean(value);
}

async run(
_passedParam: string[],
options: RecordPositionBackfillCommandOptions,
): Promise<void> {
this.messageQueueService.add<RecordPositionBackfillJobData>(
RecordPositionBackfillJob.name,
{ workspaceId: options.workspaceId, dryRun: options.dryRun ?? false },
{ retryLimit: 3 },
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { RecordPositionBackfillService } from 'src/engine/api/graphql/workspace-

export type RecordPositionBackfillJobData = {
workspaceId: string;
objectMetadata: { nameSingular: string; isCustom: boolean };
recordId: string;
dryRun: boolean;
};

@Injectable()
Expand All @@ -19,10 +18,6 @@ export class RecordPositionBackfillJob
) {}

async handle(data: RecordPositionBackfillJobData): Promise<void> {
this.recordPositionBackfillService.backfill(
data.workspaceId,
data.objectMetadata,
data.recordId,
);
this.recordPositionBackfillService.backfill(data.workspaceId, data.dryRun);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { TestingModule, Test } from '@nestjs/testing';

import { RecordPositionQueryFactory } from 'src/engine/api/graphql/workspace-query-builder/factories/record-position-query.factory';
import { RecordPositionFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/record-position.factory';
import { RecordPositionBackfillService } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-service';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';

describe('RecordPositionBackfillService', () => {
let recordPositionQueryFactory;
let recordPositionFactory;
let objectMetadataService;
let workspaceDataSourceService;

let service: RecordPositionBackfillService;

beforeEach(async () => {
recordPositionQueryFactory = {
create: jest.fn().mockReturnValue(['query', []]),
};

recordPositionFactory = {
create: jest.fn().mockResolvedValue([
{
position: 1,
},
]),
};

objectMetadataService = {
findManyWithinWorkspace: jest.fn().mockReturnValue([]),
};

workspaceDataSourceService = {
getSchemaName: jest.fn().mockReturnValue('schemaName'),
executeRawQuery: jest.fn().mockResolvedValue([]),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
RecordPositionBackfillService,
{
provide: RecordPositionQueryFactory,
useValue: recordPositionQueryFactory,
},
{
provide: RecordPositionFactory,
useValue: recordPositionFactory,
},
{
provide: WorkspaceDataSourceService,
useValue: workspaceDataSourceService,
},
{
provide: ObjectMetadataService,
useValue: objectMetadataService,
},
],
}).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 () => {
objectMetadataService.findManyWithinWorkspace.mockReturnValue([
{
id: '1',
nameSingular: 'name',
fields: [],
},
]);
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 () => {
objectMetadataService.findManyWithinWorkspace.mockReturnValue([
{
id: '1',
nameSingular: 'company',
fields: [],
},
]);
await service.backfill('workspaceId', false);
expect(workspaceDataSourceService.executeRawQuery).toHaveBeenCalledTimes(1);
});

it('when record without position, should create and run query twice', async () => {
objectMetadataService.findManyWithinWorkspace.mockReturnValue([
{
id: '1',
nameSingular: 'company',
fields: [],
},
]);
workspaceDataSourceService.executeRawQuery.mockResolvedValueOnce([
{
id: '1',
},
]);
await service.backfill('workspaceId', false);
expect(workspaceDataSourceService.executeRawQuery).toHaveBeenCalledTimes(2);
expect(recordPositionFactory.create).toHaveBeenCalledTimes(1);
expect(recordPositionQueryFactory.create).toHaveBeenCalledTimes(2);
});

it('when dryRun is true, should not update position', async () => {
objectMetadataService.findManyWithinWorkspace.mockReturnValue([
{
id: '1',
nameSingular: 'company',
fields: [],
},
]);
workspaceDataSourceService.executeRawQuery.mockResolvedValueOnce([
{
id: '1',
},
]);
await service.backfill('workspaceId', true);
expect(workspaceDataSourceService.executeRawQuery).toHaveBeenCalledTimes(1);
expect(recordPositionFactory.create).toHaveBeenCalledTimes(1);
expect(recordPositionQueryFactory.create).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/works
import { RecordPositionQueryFactory } from 'src/engine/api/graphql/workspace-query-builder/factories/record-position-query.factory';
import { RecordPositionFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/record-position.factory';
import { RecordPositionBackfillService } from 'src/engine/api/graphql/workspace-query-runner/services/record-position-backfill-service';
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';

@Module({
imports: [WorkspaceDataSourceModule],
imports: [WorkspaceDataSourceModule, ObjectMetadataModule],
providers: [
RecordPositionFactory,
RecordPositionQueryFactory,
Expand Down
Loading

0 comments on commit 25f4e44

Please sign in to comment.