Set record position on workflow creation (#11308)
- Migrate record position factory to core-modules - set position on record creation
This commit is contained in:
+15
-13
@@ -7,14 +7,14 @@ import { ResolverArgsType } from 'src/engine/api/graphql/workspace-resolver-buil
|
||||
|
||||
import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory';
|
||||
import {
|
||||
RecordPositionFactory,
|
||||
RecordPositionFactoryCreateArgs,
|
||||
} from 'src/engine/api/graphql/workspace-query-runner/factories/record-position.factory';
|
||||
RecordPositionService,
|
||||
RecordPositionServiceCreateArgs,
|
||||
} from 'src/engine/core-modules/record-position/services/record-position.service';
|
||||
import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map';
|
||||
|
||||
describe('QueryRunnerArgsFactory', () => {
|
||||
const recordPositionFactory = {
|
||||
create: jest.fn().mockResolvedValue(2),
|
||||
const recordPositionService = {
|
||||
buildRecordPosition: jest.fn().mockResolvedValue(2),
|
||||
};
|
||||
const workspaceId = 'workspaceId';
|
||||
const options = {
|
||||
@@ -66,10 +66,8 @@ describe('QueryRunnerArgsFactory', () => {
|
||||
providers: [
|
||||
QueryRunnerArgsFactory,
|
||||
{
|
||||
provide: RecordPositionFactory,
|
||||
useValue: {
|
||||
create: recordPositionFactory.create,
|
||||
},
|
||||
provide: RecordPositionService,
|
||||
useValue: recordPositionService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
@@ -107,14 +105,16 @@ describe('QueryRunnerArgsFactory', () => {
|
||||
ResolverArgsType.CreateMany,
|
||||
);
|
||||
|
||||
const expectedArgs: RecordPositionFactoryCreateArgs = {
|
||||
const expectedArgs: RecordPositionServiceCreateArgs = {
|
||||
value: 'last',
|
||||
objectMetadata: { isCustom: true, nameSingular: 'testNumber' },
|
||||
workspaceId,
|
||||
index: 0,
|
||||
};
|
||||
|
||||
expect(recordPositionFactory.create).toHaveBeenCalledWith(expectedArgs);
|
||||
expect(recordPositionService.buildRecordPosition).toHaveBeenCalledWith(
|
||||
expectedArgs,
|
||||
);
|
||||
expect(result).toEqual({
|
||||
id: 'uuid',
|
||||
data: [{ position: 2, testNumber: 1 }],
|
||||
@@ -133,14 +133,16 @@ describe('QueryRunnerArgsFactory', () => {
|
||||
ResolverArgsType.CreateMany,
|
||||
);
|
||||
|
||||
const expectedArgs: RecordPositionFactoryCreateArgs = {
|
||||
const expectedArgs: RecordPositionServiceCreateArgs = {
|
||||
value: 'first',
|
||||
objectMetadata: { isCustom: true, nameSingular: 'testNumber' },
|
||||
workspaceId,
|
||||
index: 0,
|
||||
};
|
||||
|
||||
expect(recordPositionFactory.create).toHaveBeenCalledWith(expectedArgs);
|
||||
expect(recordPositionService.buildRecordPosition).toHaveBeenCalledWith(
|
||||
expectedArgs,
|
||||
);
|
||||
expect(result).toEqual({
|
||||
id: 'uuid',
|
||||
data: [{ position: 2, testNumber: 1 }],
|
||||
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
import { Test, TestingModule } 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 { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
|
||||
describe('RecordPositionFactory', () => {
|
||||
const recordPositionQueryFactory = {
|
||||
create: jest.fn().mockReturnValue(['query', []]),
|
||||
};
|
||||
|
||||
let workspaceDataSourceService;
|
||||
|
||||
let factory: RecordPositionFactory;
|
||||
|
||||
beforeEach(async () => {
|
||||
workspaceDataSourceService = {
|
||||
getSchemaName: jest.fn().mockReturnValue('schemaName'),
|
||||
executeRawQuery: jest.fn().mockResolvedValue([{ position: 1 }]),
|
||||
};
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
RecordPositionFactory,
|
||||
{
|
||||
provide: RecordPositionQueryFactory,
|
||||
useValue: recordPositionQueryFactory,
|
||||
},
|
||||
{
|
||||
provide: WorkspaceDataSourceService,
|
||||
useValue: workspaceDataSourceService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
factory = module.get<RecordPositionFactory>(RecordPositionFactory);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(factory).toBeDefined();
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
const objectMetadata = { isCustom: false, nameSingular: 'company' };
|
||||
const workspaceId = 'workspaceId';
|
||||
|
||||
it('should return the value when value is a number', async () => {
|
||||
const value = 1;
|
||||
|
||||
const result = await factory.create({
|
||||
value,
|
||||
objectMetadata,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
expect(result).toEqual(value);
|
||||
});
|
||||
|
||||
it('should return the existing position -1 when value is first', async () => {
|
||||
const value = 'first';
|
||||
const result = await factory.create({
|
||||
value,
|
||||
objectMetadata,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
expect(result).toEqual(0);
|
||||
});
|
||||
|
||||
it('should return the existing position + 1 when value is last', async () => {
|
||||
const value = 'last';
|
||||
const result = await factory.create({
|
||||
value,
|
||||
objectMetadata,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
expect(result).toEqual(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
-2
@@ -1,10 +1,8 @@
|
||||
import { QueryRunnerArgsFactory } from './query-runner-args.factory';
|
||||
import { RecordPositionFactory } from './record-position.factory';
|
||||
|
||||
import { QueryResultGettersFactory } from './query-result-getters/query-result-getters.factory';
|
||||
|
||||
export const workspaceQueryRunnerFactories = [
|
||||
QueryRunnerArgsFactory,
|
||||
RecordPositionFactory,
|
||||
QueryResultGettersFactory,
|
||||
];
|
||||
|
||||
+14
-13
@@ -22,14 +22,13 @@ import {
|
||||
import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
|
||||
|
||||
import { lowercaseDomain } from 'src/engine/api/graphql/workspace-query-runner/utils/query-runner-links.util';
|
||||
import { RecordPositionService } from 'src/engine/core-modules/record-position/services/record-position.service';
|
||||
import {
|
||||
RichTextV2Metadata,
|
||||
richTextV2ValueSchema,
|
||||
} from 'src/engine/metadata-modules/field-metadata/composite-types/rich-text-v2.composite-type';
|
||||
import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map';
|
||||
|
||||
import { RecordPositionFactory } from './record-position.factory';
|
||||
|
||||
type ArgPositionBackfillInput = {
|
||||
argIndex?: number;
|
||||
shouldBackfillPosition: boolean;
|
||||
@@ -37,7 +36,7 @@ type ArgPositionBackfillInput = {
|
||||
|
||||
@Injectable()
|
||||
export class QueryRunnerArgsFactory {
|
||||
constructor(private readonly recordPositionFactory: RecordPositionFactory) {}
|
||||
constructor(private readonly recordPositionService: RecordPositionService) {}
|
||||
|
||||
async create(
|
||||
args: ResolverArgs,
|
||||
@@ -190,16 +189,18 @@ export class QueryRunnerArgsFactory {
|
||||
case FieldMetadataType.POSITION: {
|
||||
isFieldPositionPresent = true;
|
||||
|
||||
const newValue = await this.recordPositionFactory.create({
|
||||
value,
|
||||
workspaceId,
|
||||
objectMetadata: {
|
||||
isCustom: options.objectMetadataItemWithFieldMaps.isCustom,
|
||||
nameSingular:
|
||||
options.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
const newValue = await this.recordPositionService.buildRecordPosition(
|
||||
{
|
||||
value,
|
||||
workspaceId,
|
||||
objectMetadata: {
|
||||
isCustom: options.objectMetadataItemWithFieldMaps.isCustom,
|
||||
nameSingular:
|
||||
options.objectMetadataItemWithFieldMaps.nameSingular,
|
||||
},
|
||||
index: argPositionBackfillInput.argIndex,
|
||||
},
|
||||
index: argPositionBackfillInput.argIndex,
|
||||
});
|
||||
);
|
||||
|
||||
return [key, newValue];
|
||||
}
|
||||
@@ -307,7 +308,7 @@ export class QueryRunnerArgsFactory {
|
||||
...newArgEntries,
|
||||
[
|
||||
'position',
|
||||
await this.recordPositionFactory.create({
|
||||
await this.recordPositionService.buildRecordPosition({
|
||||
value: 'first',
|
||||
workspaceId,
|
||||
objectMetadata: {
|
||||
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'class-validator';
|
||||
|
||||
import {
|
||||
RecordPositionQueryArgs,
|
||||
RecordPositionQueryFactory,
|
||||
RecordPositionQueryType,
|
||||
} from 'src/engine/api/graphql/workspace-query-builder/factories/record-position-query.factory';
|
||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||
|
||||
export type RecordPositionFactoryCreateArgs = {
|
||||
value: number | 'first' | 'last';
|
||||
objectMetadata: { isCustom: boolean; nameSingular: string };
|
||||
workspaceId: string;
|
||||
index?: number;
|
||||
};
|
||||
@Injectable()
|
||||
export class RecordPositionFactory {
|
||||
constructor(
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
private readonly recordPositionQueryFactory: RecordPositionQueryFactory,
|
||||
) {}
|
||||
|
||||
async create({
|
||||
objectMetadata,
|
||||
value,
|
||||
workspaceId,
|
||||
index = 0,
|
||||
}: RecordPositionFactoryCreateArgs): Promise<number> {
|
||||
const dataSourceSchema =
|
||||
this.workspaceDataSourceService.getSchemaName(workspaceId);
|
||||
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value === 'first') {
|
||||
const recordWithMinPosition = await this.findRecordPosition(
|
||||
{
|
||||
recordPositionQueryType: RecordPositionQueryType.FIND_MIN_POSITION,
|
||||
},
|
||||
objectMetadata,
|
||||
dataSourceSchema,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return isDefined(recordWithMinPosition?.position)
|
||||
? recordWithMinPosition.position - index - 1
|
||||
: 1;
|
||||
}
|
||||
|
||||
const recordWithMaxPosition = await this.findRecordPosition(
|
||||
{
|
||||
recordPositionQueryType: RecordPositionQueryType.FIND_MAX_POSITION,
|
||||
},
|
||||
objectMetadata,
|
||||
dataSourceSchema,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return isDefined(recordWithMaxPosition?.position)
|
||||
? recordWithMaxPosition.position + index + 1
|
||||
: 1;
|
||||
}
|
||||
|
||||
private async findRecordPosition(
|
||||
recordPositionQueryArgs: RecordPositionQueryArgs,
|
||||
objectMetadata: { isCustom: boolean; nameSingular: string },
|
||||
dataSourceSchema: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const [query, params] = this.recordPositionQueryFactory.create(
|
||||
recordPositionQueryArgs,
|
||||
objectMetadata,
|
||||
dataSourceSchema,
|
||||
);
|
||||
|
||||
const records = await this.workspaceDataSourceService.executeRawQuery(
|
||||
query,
|
||||
params,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return records?.[0];
|
||||
}
|
||||
}
|
||||
+8
-20
@@ -3,27 +3,21 @@ import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
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 { 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 recordPositionQueryFactory;
|
||||
let recordPositionFactory;
|
||||
let recordPositionService;
|
||||
let objectMetadataRepository;
|
||||
let workspaceDataSourceService;
|
||||
|
||||
let service: RecordPositionBackfillService;
|
||||
|
||||
beforeEach(async () => {
|
||||
recordPositionQueryFactory = {
|
||||
create: jest.fn().mockReturnValue(['query', []]),
|
||||
};
|
||||
|
||||
recordPositionFactory = {
|
||||
create: jest.fn().mockResolvedValue([
|
||||
recordPositionService = {
|
||||
buildRecordPosition: jest.fn().mockResolvedValue([
|
||||
{
|
||||
position: 1,
|
||||
},
|
||||
@@ -42,12 +36,8 @@ describe('RecordPositionBackfillService', () => {
|
||||
providers: [
|
||||
RecordPositionBackfillService,
|
||||
{
|
||||
provide: RecordPositionQueryFactory,
|
||||
useValue: recordPositionQueryFactory,
|
||||
},
|
||||
{
|
||||
provide: RecordPositionFactory,
|
||||
useValue: recordPositionFactory,
|
||||
provide: RecordPositionService,
|
||||
useValue: recordPositionService,
|
||||
},
|
||||
{
|
||||
provide: WorkspaceDataSourceService,
|
||||
@@ -123,8 +113,7 @@ describe('RecordPositionBackfillService', () => {
|
||||
]);
|
||||
await service.backfill('workspaceId', false);
|
||||
expect(workspaceDataSourceService.executeRawQuery).toHaveBeenCalledTimes(2);
|
||||
expect(recordPositionFactory.create).toHaveBeenCalledTimes(1);
|
||||
expect(recordPositionQueryFactory.create).toHaveBeenCalledTimes(2);
|
||||
expect(recordPositionService.buildRecordPosition).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('when dryRun is true, should not update position', async () => {
|
||||
@@ -148,7 +137,6 @@ describe('RecordPositionBackfillService', () => {
|
||||
]);
|
||||
await service.backfill('workspaceId', true);
|
||||
expect(workspaceDataSourceService.executeRawQuery).toHaveBeenCalledTimes(1);
|
||||
expect(recordPositionFactory.create).toHaveBeenCalledTimes(1);
|
||||
expect(recordPositionQueryFactory.create).toHaveBeenCalledTimes(1);
|
||||
expect(recordPositionService.buildRecordPosition).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
+2
-7
@@ -1,9 +1,8 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
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 { 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';
|
||||
|
||||
@@ -12,11 +11,7 @@ import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/works
|
||||
WorkspaceDataSourceModule,
|
||||
TypeOrmModule.forFeature([ObjectMetadataEntity], 'metadata'),
|
||||
],
|
||||
providers: [
|
||||
RecordPositionFactory,
|
||||
RecordPositionQueryFactory,
|
||||
RecordPositionBackfillService,
|
||||
],
|
||||
providers: [RecordPositionService, RecordPositionBackfillService],
|
||||
exports: [RecordPositionBackfillService],
|
||||
})
|
||||
export class RecordPositionBackfillModule {}
|
||||
|
||||
+8
-11
@@ -2,14 +2,12 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { isDefined } from 'class-validator';
|
||||
import { Repository } from 'typeorm';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
RecordPositionQueryFactory,
|
||||
RecordPositionQueryType,
|
||||
} 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 { 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';
|
||||
|
||||
@@ -19,9 +17,8 @@ export class RecordPositionBackfillService {
|
||||
constructor(
|
||||
@InjectRepository(ObjectMetadataEntity, 'metadata')
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly recordPositionFactory: RecordPositionFactory,
|
||||
private readonly recordPositionQueryFactory: RecordPositionQueryFactory,
|
||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
||||
private readonly recordPositionService: RecordPositionService,
|
||||
) {}
|
||||
|
||||
async backfill(workspaceId: string, dryRun: boolean) {
|
||||
@@ -47,7 +44,7 @@ export class RecordPositionBackfillService {
|
||||
|
||||
for (const objectMetadata of objectMetadataCollection) {
|
||||
const [recordsWithoutPositionQuery, recordsWithoutPositionQueryParams] =
|
||||
this.recordPositionQueryFactory.create(
|
||||
buildRecordPositionQuery(
|
||||
{
|
||||
recordPositionQueryType: RecordPositionQueryType.FIND_BY_POSITION,
|
||||
positionValue: null,
|
||||
@@ -73,7 +70,7 @@ export class RecordPositionBackfillService {
|
||||
continue;
|
||||
}
|
||||
|
||||
const position = await this.recordPositionFactory.create({
|
||||
const position = await this.recordPositionService.buildRecordPosition({
|
||||
objectMetadata: {
|
||||
isCustom: objectMetadata.isCustom,
|
||||
nameSingular: objectMetadata.nameSingular,
|
||||
@@ -106,7 +103,7 @@ export class RecordPositionBackfillService {
|
||||
continue;
|
||||
}
|
||||
|
||||
const [query, params] = this.recordPositionQueryFactory.create(
|
||||
const [query, params] = buildRecordPositionQuery(
|
||||
{
|
||||
recordPositionQueryType: RecordPositionQueryType.UPDATE_POSITION,
|
||||
recordId: recordsWithoutPosition[recordIndex].id,
|
||||
|
||||
+2
@@ -10,6 +10,7 @@ import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
|
||||
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
import { RecordPositionModule } from 'src/engine/core-modules/record-position/record-position.module';
|
||||
import { TelemetryModule } from 'src/engine/core-modules/telemetry/telemetry.module';
|
||||
import { ObjectMetadataRepositoryModule } from 'src/engine/object-metadata-repository/object-metadata-repository.module';
|
||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||
@@ -29,6 +30,7 @@ import { EntityEventsToDbListener } from './listeners/entity-events-to-db.listen
|
||||
TelemetryModule,
|
||||
FileModule,
|
||||
FeatureFlagModule,
|
||||
RecordPositionModule,
|
||||
],
|
||||
providers: [
|
||||
...workspaceQueryRunnerFactories,
|
||||
|
||||
Reference in New Issue
Block a user