[BREAKING_CHANGE/GRAPHQL/OBJECT_METADATA_CREATE_ONE] Remove object/fields/view-fields v1 implementation (#15823)

# Introduction
Remove the v2 feature flag for view-field field-metadata and
object-metadata metadata entities

## Some details
- Disabled nestjs-query for object metadata creation and explicitly
calling it
- removed all v1 integration tests files

## Remarks
Not remove v2 referencing in both filenaming right now will handle that
globally later

## Breaking change
Due to object metadata resolver createOne standardization had to rename
the input from `CreateObjectInput` to `CreateOneObjectInput`
This commit is contained in:
Paul Rastoin
2025-11-17 10:55:19 +01:00
committed by GitHub
parent c1dcda6475
commit a39efeb1ab
75 changed files with 425 additions and 15441 deletions
@@ -6,7 +6,7 @@ import { DeleteRecordService } from 'src/engine/core-modules/record-crud/service
import { FindRecordsService } from 'src/engine/core-modules/record-crud/services/find-records.service';
import { UpdateRecordService } from 'src/engine/core-modules/record-crud/services/update-record.service';
import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { ObjectMetadataServiceV2 } from 'src/engine/metadata-modules/object-metadata/object-metadata-v2.service';
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
@@ -57,7 +57,7 @@ describe('ToolService', () => {
},
},
{
provide: ObjectMetadataService,
provide: ObjectMetadataServiceV2,
useValue: {
findManyWithinWorkspace: jest.fn().mockResolvedValue([testObject]),
},
@@ -17,7 +17,7 @@ import {
type ToolHints,
type ToolOperation,
} from 'src/engine/metadata-modules/ai-router/types/tool-hints.interface';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { ObjectMetadataServiceV2 } from 'src/engine/metadata-modules/object-metadata/object-metadata-v2.service';
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
@@ -29,7 +29,7 @@ export class ToolService {
constructor(
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
private readonly objectMetadataService: ObjectMetadataService,
private readonly objectMetadataService: ObjectMetadataServiceV2,
protected readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
private readonly createRecordService: CreateRecordService,
private readonly updateRecordService: UpdateRecordService,
@@ -219,7 +219,7 @@ export class ApplicationSyncService {
);
for (const fieldToDelete of fieldsToDelete) {
await this.fieldMetadataServiceV2.updateOne({
await this.fieldMetadataServiceV2.updateOneField({
updateFieldInput: {
id: fieldToDelete.id,
isActive: false,
@@ -256,7 +256,7 @@ export class ApplicationSyncService {
isNullable: fieldToSync.isNullable ?? true,
};
await this.fieldMetadataServiceV2.updateOne({
await this.fieldMetadataServiceV2.updateOneField({
updateFieldInput,
workspaceId,
});
@@ -281,7 +281,7 @@ export class ApplicationSyncService {
workspaceId,
};
await this.fieldMetadataServiceV2.createOne({
await this.fieldMetadataServiceV2.createOneField({
createFieldInput,
workspaceId,
});
@@ -341,7 +341,7 @@ export class ApplicationSyncService {
);
for (const objectToDelete of objectsToDelete) {
await this.objectMetadataServiceV2.deleteOne({
await this.objectMetadataServiceV2.deleteOneObject({
deleteObjectInput: { id: objectToDelete.id },
workspaceId,
isSystemBuild: true,
@@ -372,7 +372,7 @@ export class ApplicationSyncService {
},
};
await this.objectMetadataServiceV2.updateOne({
await this.objectMetadataServiceV2.updateOneObject({
updateObjectInput,
workspaceId,
});
@@ -404,7 +404,7 @@ export class ApplicationSyncService {
applicationId,
};
const createdObject = await this.objectMetadataServiceV2.createOne({
const createdObject = await this.objectMetadataServiceV2.createOneObject({
createObjectInput,
workspaceId,
});
@@ -4,7 +4,7 @@ import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { OpenApiService } from 'src/engine/core-modules/open-api/open-api.service';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { ObjectMetadataServiceV2 } from 'src/engine/metadata-modules/object-metadata/object-metadata-v2.service';
describe('OpenApiService', () => {
let service: OpenApiService;
@@ -18,7 +18,7 @@ describe('OpenApiService', () => {
useValue: {},
},
{
provide: ObjectMetadataService,
provide: ObjectMetadataServiceV2,
useValue: {},
},
{
@@ -47,8 +47,8 @@ import {
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception';
import { ObjectMetadataServiceV2 } from 'src/engine/metadata-modules/object-metadata/object-metadata-v2.service';
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
import { standardObjectMetadataDefinitions } from 'src/engine/workspace-manager/workspace-sync-metadata/standard-objects';
import { shouldExcludeFromWorkspaceApi } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/should-exclude-from-workspace-api.util';
import { getServerUrl } from 'src/utils/get-server-url';
@@ -58,7 +58,7 @@ export class OpenApiService {
constructor(
private readonly accessTokenService: AccessTokenService,
private readonly twentyConfigService: TwentyConfigService,
private readonly objectMetadataService: ObjectMetadataService,
private readonly objectMetadataService: ObjectMetadataServiceV2,
private readonly featureFlagService: FeatureFlagService,
) {}
@@ -1,215 +0,0 @@
import { Transform } from 'class-transformer';
import {
IsArray,
IsBoolean,
IsEnum,
IsNumber,
IsString,
} from 'class-validator';
import { ConfigVariableType } from 'src/engine/core-modules/twenty-config/enums/config-variable-type.enum';
import { applyBasicValidators } from 'src/engine/core-modules/twenty-config/utils/apply-basic-validators.util';
import { configTransformers } from 'src/engine/core-modules/twenty-config/utils/config-transformers.util';
jest.mock('class-transformer', () => ({
Transform: jest.fn(),
}));
jest.mock('class-validator', () => ({
IsBoolean: jest.fn().mockReturnValue(jest.fn()),
IsNumber: jest.fn().mockReturnValue(jest.fn()),
IsString: jest.fn().mockReturnValue(jest.fn()),
IsEnum: jest.fn().mockReturnValue(jest.fn()),
IsArray: jest.fn().mockReturnValue(jest.fn()),
}));
jest.mock(
'src/engine/core-modules/twenty-config/utils/config-transformers.util',
() => ({
configTransformers: {
boolean: jest.fn(),
number: jest.fn(),
},
}),
);
describe('applyBasicValidators', () => {
const mockTarget = {};
const mockPropertyKey = 'testProperty';
beforeEach(() => {
jest.clearAllMocks();
});
describe('boolean type', () => {
it('should apply boolean transformers and validators', () => {
let capturedTransformFn;
(Transform as jest.Mock).mockImplementation((transformFn) => {
capturedTransformFn = transformFn;
return jest.fn();
});
applyBasicValidators(
ConfigVariableType.BOOLEAN,
mockTarget,
mockPropertyKey,
);
expect(Transform).toHaveBeenCalled();
expect(IsBoolean).toHaveBeenCalled();
const transformFn = capturedTransformFn;
const mockTransformParams = { value: 'true' };
(configTransformers.boolean as jest.Mock).mockReturnValueOnce(true);
// @ts-expect-error legacy noImplicitAny
const result1 = transformFn(mockTransformParams);
expect(configTransformers.boolean).toHaveBeenCalledWith('true');
expect(result1).toBe(true);
(configTransformers.boolean as jest.Mock).mockReturnValueOnce(undefined);
// @ts-expect-error legacy noImplicitAny
const result2 = transformFn(mockTransformParams);
expect(result2).toBe('true');
});
});
describe('number type', () => {
it('should apply number transformers and validators', () => {
let capturedTransformFn;
(Transform as jest.Mock).mockImplementation((transformFn) => {
capturedTransformFn = transformFn;
return jest.fn();
});
applyBasicValidators(
ConfigVariableType.NUMBER,
mockTarget,
mockPropertyKey,
);
expect(Transform).toHaveBeenCalled();
expect(IsNumber).toHaveBeenCalled();
const transformFn = capturedTransformFn;
const mockTransformParams = { value: '42' };
(configTransformers.number as jest.Mock).mockReturnValueOnce(42);
// @ts-expect-error legacy noImplicitAny
const result1 = transformFn(mockTransformParams);
expect(configTransformers.number).toHaveBeenCalledWith('42');
expect(result1).toBe(42);
(configTransformers.number as jest.Mock).mockReturnValueOnce(undefined);
// @ts-expect-error legacy noImplicitAny
const result2 = transformFn(mockTransformParams);
expect(result2).toBe('42');
});
});
describe('string type', () => {
it('should apply string validator', () => {
applyBasicValidators(
ConfigVariableType.STRING,
mockTarget,
mockPropertyKey,
);
expect(IsString).toHaveBeenCalled();
expect(Transform).not.toHaveBeenCalled();
});
});
describe('enum type', () => {
it('should apply enum validator with string array options', () => {
const enumOptions = ['option1', 'option2', 'option3'];
applyBasicValidators(
ConfigVariableType.ENUM,
mockTarget,
mockPropertyKey,
enumOptions,
);
expect(IsEnum).toHaveBeenCalledWith(enumOptions);
expect(Transform).not.toHaveBeenCalled();
});
it('should apply enum validator with enum object options', () => {
enum TestEnum {
Option1 = 'value1',
Option2 = 'value2',
Option3 = 'value3',
}
jest.mock(
'src/engine/core-modules/twenty-config/utils/type-transformers.registry',
() => ({
typeTransformers: {
enum: {
getValidators: jest.fn().mockImplementation((options) => {
if (options && Object.keys(options).length > 0) {
return [IsEnum(options)];
}
return [];
}),
getTransformers: jest.fn().mockReturnValue([]),
},
},
}),
{ virtual: true },
);
applyBasicValidators(
ConfigVariableType.ENUM,
mockTarget,
mockPropertyKey,
TestEnum,
);
expect(IsEnum).toHaveBeenCalledWith(TestEnum);
expect(Transform).not.toHaveBeenCalled();
});
it('should not apply enum validator without options', () => {
applyBasicValidators(
ConfigVariableType.ENUM,
mockTarget,
mockPropertyKey,
);
expect(IsEnum).not.toHaveBeenCalled();
expect(Transform).not.toHaveBeenCalled();
});
});
describe('array type', () => {
it('should apply array validator', () => {
applyBasicValidators(
ConfigVariableType.ARRAY,
mockTarget,
mockPropertyKey,
);
expect(IsArray).toHaveBeenCalled();
expect(Transform).not.toHaveBeenCalled();
});
});
describe('unsupported type', () => {
it('should throw error for unsupported types', () => {
expect(() => {
applyBasicValidators('unsupported' as any, mockTarget, mockPropertyKey);
}).toThrow('Unsupported config variable type: unsupported');
});
});
});