From 5c0b65eecbcec82a74da49ab876473705ee635d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20M?= Date: Wed, 27 Mar 2024 10:56:04 +0100 Subject: [PATCH] feat: simplification of default-value specification in FieldMetadata (#4592) * feat: wip refactor default-value * feat: health check to migrate default value * fix: tests * fix: refactor defaultValue to make it more clean * fix: unit tests * fix: front-end default value --- .../hooks/useFieldMetadataItem.ts | 14 +- .../formatFieldMetadataItemInput.test.ts | 2 +- .../utils/formatFieldMetadataItemInput.ts | 2 +- .../data-model/SettingsObjectFieldEdit.tsx | 2 +- packages/twenty-server/src/app.module.ts | 16 +- .../currency.composite-type.ts | 10 +- .../full-name.composite-type.ts | 8 +- .../composite-types/link.composite-type.ts | 8 +- .../dtos/default-value.input.ts | 63 ++++---- .../field-metadata/field-metadata.service.ts | 3 +- .../field-metadata-default-value.interface.ts | 49 ++---- .../__tests__/serialize-default-value.spec.ts | 14 +- ...lidate-default-value-based-on-type.spec.ts | 93 +++++------ .../utils/generate-default-value.ts | 14 +- .../utils/is-function-default-value.util.ts | 21 +++ .../utils/serialize-default-value.ts | 20 ++- .../serialize-function-default-value.util.ts | 14 ++ .../serialize-type-default-value.util.ts | 18 --- .../validate-default-value-for-type.util.ts | 22 ++- .../validators/is-quoted-string.validator.ts | 24 +++ .../object-metadata.service.ts | 8 +- .../factories/basic-column-action.factory.ts | 26 +-- .../factories/enum-column-action.factory.ts | 9 +- .../fixer/workspace-default-value.fixer.ts | 152 ++++++++++++++++-- .../services/database-structure.service.ts | 62 +++++-- .../services/field-metadata-health.service.ts | 69 ++++---- .../services/workspace-fix.service.ts | 10 ++ .../custom-objects/custom.object-metadata.ts | 2 +- .../standard-objects/base.object-metadata.ts | 6 +- .../activity.object-metadata.ts | 2 +- .../calendar-channel.object-metadata.ts | 6 +- ...calendar-event-attendee.object-metadata.ts | 2 +- .../company.object-metadata.ts | 2 +- .../favorite.object-metadata.ts | 2 +- .../message-channel.object-metadata.ts | 6 +- .../message-participant.object-metadata.ts | 2 +- .../message.object-metadata.ts | 2 +- .../opportunity.object-metadata.ts | 4 +- .../view-field.object-metadata.ts | 6 +- .../view-filter.object-metadata.ts | 2 +- .../view-sort.object-metadata.ts | 2 +- .../standard-objects/view.object-metadata.ts | 6 +- .../workspace-member.object-metadata.ts | 4 +- 43 files changed, 481 insertions(+), 328 deletions(-) create mode 100644 packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/is-function-default-value.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/serialize-function-default-value.util.ts delete mode 100644 packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/serialize-type-default-value.util.ts create mode 100644 packages/twenty-server/src/engine/metadata-modules/field-metadata/validators/is-quoted-string.validator.ts diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useFieldMetadataItem.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useFieldMetadataItem.ts index 7c27696a4..2c0a196fb 100644 --- a/packages/twenty-front/src/modules/object-metadata/hooks/useFieldMetadataItem.ts +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useFieldMetadataItem.ts @@ -23,13 +23,19 @@ export const useFieldMetadataItem = () => { options?: Omit[]; type: FieldMetadataType; }, - ) => - createOneFieldMetadataItem({ - ...formatFieldMetadataItemInput(input), - defaultValue: input.defaultValue, + ) => { + const formatedInput = formatFieldMetadataItemInput(input); + const defaultValue = input.defaultValue + ? `'${input.defaultValue}'` + : formatedInput.defaultValue ?? undefined; + + return createOneFieldMetadataItem({ + ...formatedInput, + defaultValue, objectMetadataId: input.objectMetadataId, type: input.type, }); + }; const editMetadataField = ( input: Pick & { diff --git a/packages/twenty-front/src/modules/object-metadata/utils/__tests__/formatFieldMetadataItemInput.test.ts b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/formatFieldMetadataItemInput.test.ts index 12e724bbf..301a8e8e5 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/__tests__/formatFieldMetadataItemInput.test.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/__tests__/formatFieldMetadataItemInput.test.ts @@ -74,7 +74,7 @@ describe('formatFieldMetadataItemInput', () => { value: 'OPTION_2', }, ], - defaultValue: 'OPTION_1', + defaultValue: "'OPTION_1'", }; const result = formatFieldMetadataItemInput(input); diff --git a/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemInput.ts b/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemInput.ts index 1174b5e0b..7b72e8b23 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemInput.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemInput.ts @@ -44,7 +44,7 @@ export const formatFieldMetadataItemInput = ( return { defaultValue: defaultOption - ? getOptionValueFromLabel(defaultOption.label) + ? `'${getOptionValueFromLabel(defaultOption.label)}'` : undefined, description: input.description?.trim() ?? null, icon: input.icon, diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx index bb50c5ee2..88f463be8 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx @@ -107,7 +107,7 @@ export const SettingsObjectFieldEdit = () => { const selectOptions = activeMetadataField.options?.map((option) => ({ ...option, - isDefault: defaultValue?.value === option.value, + isDefault: defaultValue === `'${option.value}'`, })); selectOptions?.sort( (optionA, optionB) => optionA.position - optionB.position, diff --git a/packages/twenty-server/src/app.module.ts b/packages/twenty-server/src/app.module.ts index 24c9e07f6..fb14f7dfb 100644 --- a/packages/twenty-server/src/app.module.ts +++ b/packages/twenty-server/src/app.module.ts @@ -1,7 +1,6 @@ import { DynamicModule, Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { ServeStaticModule } from '@nestjs/serve-static'; -import { DevtoolsModule } from '@nestjs/devtools-integration'; import { GraphQLModule } from '@nestjs/graphql'; import { existsSync } from 'fs'; @@ -11,7 +10,6 @@ import { YogaDriverConfig, YogaDriver } from '@graphql-yoga/nestjs'; import { ApiRestModule } from 'src/engine/api/rest/api-rest.module'; import { ModulesModule } from 'src/modules/modules.module'; -import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { CoreGraphQLApiModule } from 'src/engine/api/graphql/core-graphql-api.module'; import { MetadataGraphQLApiModule } from 'src/engine/api/graphql/metadata-graphql-api.module'; import { GraphQLConfigModule } from 'src/engine/api/graphql/graphql-config/graphql-config.module'; @@ -23,13 +21,13 @@ import { IntegrationsModule } from './engine/integrations/integrations.module'; @Module({ imports: [ // Nest.js devtools, use devtools.nestjs.com to debug - DevtoolsModule.registerAsync({ - useFactory: (environmentService: EnvironmentService) => ({ - http: environmentService.get('DEBUG_MODE'), - port: environmentService.get('DEBUG_PORT'), - }), - inject: [EnvironmentService], - }), + // DevtoolsModule.registerAsync({ + // useFactory: (environmentService: EnvironmentService) => ({ + // http: environmentService.get('DEBUG_MODE'), + // port: environmentService.get('DEBUG_PORT'), + // }), + // inject: [EnvironmentService], + // }), ConfigModule.forRoot({ isGlobal: true, }), diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/currency.composite-type.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/currency.composite-type.ts index b9fe00f45..3a94dcda0 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/currency.composite-type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/currency.composite-type.ts @@ -34,9 +34,8 @@ export const currencyFields = ( isNullable: true, ...(inferredFieldMetadata ? { - defaultValue: { - value: inferredFieldMetadata.defaultValue?.amountMicros ?? null, - }, + defaultValue: + inferredFieldMetadata.defaultValue?.amountMicros ?? null, } : {}), } satisfies FieldMetadataInterface, @@ -52,9 +51,8 @@ export const currencyFields = ( isNullable: true, ...(inferredFieldMetadata ? { - defaultValue: { - value: inferredFieldMetadata.defaultValue?.currencyCode ?? null, - }, + defaultValue: + inferredFieldMetadata.defaultValue?.currencyCode ?? null, } : {}), } satisfies FieldMetadataInterface, diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/full-name.composite-type.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/full-name.composite-type.ts index 8cada9ccc..614cd6677 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/full-name.composite-type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/full-name.composite-type.ts @@ -34,9 +34,7 @@ export const fullNameFields = ( isNullable: true, ...(inferredFieldMetadata ? { - defaultValue: { - value: inferredFieldMetadata.defaultValue?.firstName ?? null, - }, + defaultValue: inferredFieldMetadata.defaultValue?.firstName ?? null, } : {}), } satisfies FieldMetadataInterface, @@ -52,9 +50,7 @@ export const fullNameFields = ( isNullable: true, ...(inferredFieldMetadata ? { - defaultValue: { - value: inferredFieldMetadata.defaultValue?.lastName ?? null, - }, + defaultValue: inferredFieldMetadata.defaultValue?.lastName ?? null, } : {}), } satisfies FieldMetadataInterface, diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/link.composite-type.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/link.composite-type.ts index 0202ecad7..88561f131 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/link.composite-type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/link.composite-type.ts @@ -34,9 +34,7 @@ export const linkFields = ( isNullable: true, ...(inferredFieldMetadata ? { - defaultValue: { - value: inferredFieldMetadata.defaultValue?.label ?? null, - }, + defaultValue: inferredFieldMetadata.defaultValue?.label ?? null, } : {}), } satisfies FieldMetadataInterface, @@ -52,9 +50,7 @@ export const linkFields = ( isNullable: true, ...(inferredFieldMetadata ? { - defaultValue: { - value: inferredFieldMetadata.defaultValue?.url ?? null, - }, + defaultValue: inferredFieldMetadata.defaultValue?.url ?? null, } : {}), } satisfies FieldMetadataInterface, diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts index 1d2b5f86f..c39b92a6b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts @@ -6,88 +6,95 @@ import { IsNotEmpty, IsNumber, IsNumberString, - IsString, Matches, ValidateIf, } from 'class-validator'; +import { IsQuotedString } from 'src/engine/metadata-modules/field-metadata/validators/is-quoted-string.validator'; + +export const fieldMetadataDefaultValueFunctionName = { + UUID: 'uuid', + NOW: 'now', +} as const; + +export type FieldMetadataDefaultValueFunctionNames = + (typeof fieldMetadataDefaultValueFunctionName)[keyof typeof fieldMetadataDefaultValueFunctionName]; + export class FieldMetadataDefaultValueString { - @ValidateIf((_object, value) => value !== null) - @IsString() + @ValidateIf((object, value) => value !== null) + @IsQuotedString() value: string | null; } export class FieldMetadataDefaultValueRawJson { - @ValidateIf((_object, value) => value !== null) + @ValidateIf((object, value) => value !== null) @IsJSON() value: JSON | null; } export class FieldMetadataDefaultValueNumber { - @ValidateIf((_object, value) => value !== null) + @ValidateIf((object, value) => value !== null) @IsNumber() value: number | null; } export class FieldMetadataDefaultValueBoolean { - @ValidateIf((_object, value) => value !== null) + @ValidateIf((object, value) => value !== null) @IsBoolean() value: boolean | null; } export class FieldMetadataDefaultValueStringArray { - @ValidateIf((_object, value) => value !== null) + @ValidateIf((object, value) => value !== null) @IsArray() - @IsString({ each: true }) + @IsQuotedString({ each: true }) value: string[] | null; } export class FieldMetadataDefaultValueDateTime { - @ValidateIf((_object, value) => value !== null) + @ValidateIf((object, value) => value !== null) @IsDate() value: Date | null; } export class FieldMetadataDefaultValueLink { - @ValidateIf((_object, value) => value !== null) - @IsString() + @ValidateIf((object, value) => value !== null) + @IsQuotedString() label: string | null; - @ValidateIf((_object, value) => value !== null) - @IsString() + @ValidateIf((object, value) => value !== null) + @IsQuotedString() url: string | null; } export class FieldMetadataDefaultValueCurrency { - @ValidateIf((_object, value) => value !== null) + @ValidateIf((object, value) => value !== null) @IsNumberString() amountMicros: string | null; - @ValidateIf((_object, value) => value !== null) - @IsString() + @ValidateIf((object, value) => value !== null) + @IsQuotedString() currencyCode: string | null; } export class FieldMetadataDefaultValueFullName { - @ValidateIf((_object, value) => value !== null) - @IsString() + @ValidateIf((object, value) => value !== null) + @IsQuotedString() firstName: string | null; - @ValidateIf((_object, value) => value !== null) - @IsString() + @ValidateIf((object, value) => value !== null) + @IsQuotedString() lastName: string | null; } -export class FieldMetadataDynamicDefaultValueUuid { - @Matches('uuid') +export class FieldMetadataDefaultValueUuidFunction { + @Matches(fieldMetadataDefaultValueFunctionName.UUID) @IsNotEmpty() - @IsString() - type: 'uuid'; + value: typeof fieldMetadataDefaultValueFunctionName.UUID; } -export class FieldMetadataDynamicDefaultValueNow { - @Matches('now') +export class FieldMetadataDefaultValueNowFunction { + @Matches(fieldMetadataDefaultValueFunctionName.NOW) @IsNotEmpty() - @IsString() - type: 'now'; + value: typeof fieldMetadataDefaultValueFunctionName.NOW; } diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts index d36c7cc97..b8c41f8e7 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.service.ts @@ -300,8 +300,7 @@ export class FieldMetadataService extends TypeOrmQueryService = T extends { value: infer V } ? V : T; -export type FieldMetadataDynamicDefaultValue = - | FieldMetadataDynamicDefaultValueUuid - | FieldMetadataDynamicDefaultValueNow; - -type AllFieldMetadataDefaultValueTypes = - | FieldMetadataScalarDefaultValue - | FieldMetadataDynamicDefaultValue - | FieldMetadataDefaultValueLink - | FieldMetadataDefaultValueCurrency - | FieldMetadataDefaultValueFullName; +type UnionOfValues = T[keyof T]; type FieldMetadataDefaultValueMapping = { [FieldMetadataType.UUID]: | FieldMetadataDefaultValueString - | FieldMetadataDynamicDefaultValueUuid; + | FieldMetadataDefaultValueUuidFunction; [FieldMetadataType.TEXT]: FieldMetadataDefaultValueString; [FieldMetadataType.PHONE]: FieldMetadataDefaultValueString; [FieldMetadataType.EMAIL]: FieldMetadataDefaultValueString; [FieldMetadataType.DATE_TIME]: | FieldMetadataDefaultValueDateTime - | FieldMetadataDynamicDefaultValueNow; + | FieldMetadataDefaultValueNowFunction; [FieldMetadataType.BOOLEAN]: FieldMetadataDefaultValueBoolean; [FieldMetadataType.NUMBER]: FieldMetadataDefaultValueNumber; [FieldMetadataType.POSITION]: FieldMetadataDefaultValueNumber; @@ -54,35 +41,31 @@ type FieldMetadataDefaultValueMapping = { [FieldMetadataType.RAW_JSON]: FieldMetadataDefaultValueRawJson; }; +export type FieldMetadataClassValidation = + UnionOfValues; + +export type FieldMetadataFunctionDefaultValue = ExtractValueType< + FieldMetadataDefaultValueUuidFunction | FieldMetadataDefaultValueNowFunction +>; + type DefaultValueByFieldMetadata = [ T, ] extends [keyof FieldMetadataDefaultValueMapping] - ? FieldMetadataDefaultValueMapping[T] | null + ? ExtractValueType | null : T extends 'default' - ? AllFieldMetadataDefaultValueTypes | null + ? ExtractValueType> | null : never; export type FieldMetadataDefaultValue< T extends FieldMetadataType | 'default' = 'default', > = DefaultValueByFieldMetadata; -type FieldMetadataDefaultValueExtractNestedType = T extends { - value: infer U; -} - ? U - : T extends object - ? { [K in keyof T]: T[K] } extends { value: infer V } - ? V - : T[keyof T] - : never; - type FieldMetadataDefaultValueExtractedTypes = { - [K in keyof FieldMetadataDefaultValueMapping]: FieldMetadataDefaultValueExtractNestedType< + [K in keyof FieldMetadataDefaultValueMapping]: ExtractValueType< FieldMetadataDefaultValueMapping[K] >; }; export type FieldMetadataDefaultSerializableValue = | FieldMetadataDefaultValueExtractedTypes[keyof FieldMetadataDefaultValueExtractedTypes] - | FieldMetadataDynamicDefaultValue | null; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/__tests__/serialize-default-value.spec.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/__tests__/serialize-default-value.spec.ts index 160c77579..61e46ec6a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/__tests__/serialize-default-value.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/__tests__/serialize-default-value.spec.ts @@ -8,25 +8,19 @@ describe('serializeDefaultValue', () => { }); it('should handle uuid dynamic default value', () => { - expect(serializeDefaultValue({ type: 'uuid' })).toBe( - 'public.uuid_generate_v4()', - ); + expect(serializeDefaultValue('uuid')).toBe('public.uuid_generate_v4()'); }); it('should handle now dynamic default value', () => { - expect(serializeDefaultValue({ type: 'now' })).toBe('now()'); + expect(serializeDefaultValue('now')).toBe('now()'); }); it('should throw BadRequestException for invalid dynamic default value type', () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error Just for testing purposes - expect(() => serializeDefaultValue({ type: 'invalid' })).toThrow( - BadRequestException, - ); + expect(() => serializeDefaultValue('invalid')).toThrow(BadRequestException); }); it('should handle string static default value', () => { - expect(serializeDefaultValue('test')).toBe("'test'"); + expect(serializeDefaultValue("'test'")).toBe("'test'"); }); it('should handle number static default value', () => { diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/__tests__/validate-default-value-based-on-type.spec.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/__tests__/validate-default-value-based-on-type.spec.ts index bc8146b3f..6768baf75 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/__tests__/validate-default-value-based-on-type.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/__tests__/validate-default-value-based-on-type.spec.ts @@ -10,110 +10,105 @@ describe('validateDefaultValueForType', () => { // Dynamic default values it('should validate uuid dynamic default value for UUID type', () => { - expect( - validateDefaultValueForType(FieldMetadataType.UUID, { type: 'uuid' }), - ).toBe(true); + expect(validateDefaultValueForType(FieldMetadataType.UUID, 'uuid')).toBe( + true, + ); }); it('should validate now dynamic default value for DATE_TIME type', () => { expect( - validateDefaultValueForType(FieldMetadataType.DATE_TIME, { type: 'now' }), + validateDefaultValueForType(FieldMetadataType.DATE_TIME, 'now'), ).toBe(true); }); it('should return false for mismatched dynamic default value', () => { - expect( - validateDefaultValueForType(FieldMetadataType.UUID, { type: 'now' }), - ).toBe(false); + expect(validateDefaultValueForType(FieldMetadataType.UUID, 'now')).toBe( + false, + ); }); // Static default values it('should validate string default value for TEXT type', () => { - expect( - validateDefaultValueForType(FieldMetadataType.TEXT, { value: 'test' }), - ).toBe(true); + expect(validateDefaultValueForType(FieldMetadataType.TEXT, "'test'")).toBe( + true, + ); }); it('should return false for invalid string default value for TEXT type', () => { - expect( - validateDefaultValueForType(FieldMetadataType.TEXT, { value: 123 }), - ).toBe(false); + expect(validateDefaultValueForType(FieldMetadataType.TEXT, 123)).toBe( + false, + ); }); it('should validate string default value for PHONE type', () => { expect( - validateDefaultValueForType(FieldMetadataType.PHONE, { - value: '+123456789', - }), + validateDefaultValueForType(FieldMetadataType.PHONE, "'+123456789'"), ).toBe(true); }); it('should return false for invalid string default value for PHONE type', () => { - expect( - validateDefaultValueForType(FieldMetadataType.PHONE, { value: 123 }), - ).toBe(false); + expect(validateDefaultValueForType(FieldMetadataType.PHONE, 123)).toBe( + false, + ); }); it('should validate string default value for EMAIL type', () => { expect( - validateDefaultValueForType(FieldMetadataType.EMAIL, { - value: 'test@example.com', - }), + validateDefaultValueForType( + FieldMetadataType.EMAIL, + "'test@example.com'", + ), ).toBe(true); }); it('should return false for invalid string default value for EMAIL type', () => { - expect( - validateDefaultValueForType(FieldMetadataType.EMAIL, { value: 123 }), - ).toBe(false); + expect(validateDefaultValueForType(FieldMetadataType.EMAIL, 123)).toBe( + false, + ); }); it('should validate number default value for NUMBER type', () => { - expect( - validateDefaultValueForType(FieldMetadataType.NUMBER, { value: 100 }), - ).toBe(true); + expect(validateDefaultValueForType(FieldMetadataType.NUMBER, 100)).toBe( + true, + ); }); it('should return false for invalid number default value for NUMBER type', () => { - expect( - validateDefaultValueForType(FieldMetadataType.NUMBER, { value: '100' }), - ).toBe(false); + expect(validateDefaultValueForType(FieldMetadataType.NUMBER, '100')).toBe( + false, + ); }); it('should validate number default value for PROBABILITY type', () => { expect( - validateDefaultValueForType(FieldMetadataType.PROBABILITY, { - value: 0.5, - }), + validateDefaultValueForType(FieldMetadataType.PROBABILITY, 0.5), ).toBe(true); }); it('should return false for invalid number default value for PROBABILITY type', () => { expect( - validateDefaultValueForType(FieldMetadataType.PROBABILITY, { - value: '50%', - }), + validateDefaultValueForType(FieldMetadataType.PROBABILITY, '50%'), ).toBe(false); }); it('should validate boolean default value for BOOLEAN type', () => { - expect( - validateDefaultValueForType(FieldMetadataType.BOOLEAN, { value: true }), - ).toBe(true); + expect(validateDefaultValueForType(FieldMetadataType.BOOLEAN, true)).toBe( + true, + ); }); it('should return false for invalid boolean default value for BOOLEAN type', () => { - expect( - validateDefaultValueForType(FieldMetadataType.BOOLEAN, { value: 'true' }), - ).toBe(false); + expect(validateDefaultValueForType(FieldMetadataType.BOOLEAN, 'true')).toBe( + false, + ); }); // LINK type it('should validate LINK default value', () => { expect( validateDefaultValueForType(FieldMetadataType.LINK, { - label: 'http://example.com', - url: 'Example', + label: "'http://example.com'", + url: "'Example'", }), ).toBe(true); }); @@ -134,7 +129,7 @@ describe('validateDefaultValueForType', () => { expect( validateDefaultValueForType(FieldMetadataType.CURRENCY, { amountMicros: '100', - currencyCode: 'USD', + currencyCode: "'USD'", }), ).toBe(true); }); @@ -144,7 +139,7 @@ describe('validateDefaultValueForType', () => { validateDefaultValueForType( // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error Just for testing purposes - { amountMicros: 100, currencyCode: 'USD' }, + { amountMicros: 100, currencyCode: "'USD'" }, FieldMetadataType.CURRENCY, ), ).toBe(false); @@ -153,9 +148,7 @@ describe('validateDefaultValueForType', () => { // Unknown type it('should return false for unknown type', () => { expect( - validateDefaultValueForType('unknown' as FieldMetadataType, { - value: 'test', - }), + validateDefaultValueForType('unknown' as FieldMetadataType, "'test'"), ).toBe(false); }); }); diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/generate-default-value.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/generate-default-value.ts index 426a0c510..2ff8f264a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/generate-default-value.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/generate-default-value.ts @@ -9,23 +9,21 @@ export function generateDefaultValue( case FieldMetadataType.TEXT: case FieldMetadataType.PHONE: case FieldMetadataType.EMAIL: - return { - value: '', - }; + return "''"; case FieldMetadataType.FULL_NAME: return { - firstName: '', - lastName: '', + firstName: "''", + lastName: "''", }; case FieldMetadataType.LINK: return { - url: '', - label: '', + url: "''", + label: "''", }; case FieldMetadataType.CURRENCY: return { amountMicros: null, - currencyCode: '', + currencyCode: "''", }; default: return null; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/is-function-default-value.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/is-function-default-value.util.ts new file mode 100644 index 000000000..d4aaa00bb --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/is-function-default-value.util.ts @@ -0,0 +1,21 @@ +import { + FieldMetadataDefaultSerializableValue, + FieldMetadataFunctionDefaultValue, +} from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface'; + +import { + FieldMetadataDefaultValueFunctionNames, + fieldMetadataDefaultValueFunctionName, +} from 'src/engine/metadata-modules/field-metadata/dtos/default-value.input'; + +export const isFunctionDefaultValue = ( + defaultValue: FieldMetadataDefaultSerializableValue, +): defaultValue is FieldMetadataFunctionDefaultValue => { + return ( + typeof defaultValue === 'string' && + !defaultValue.startsWith("'") && + Object.values(fieldMetadataDefaultValueFunctionName).includes( + defaultValue as FieldMetadataDefaultValueFunctionNames, + ) + ); +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/serialize-default-value.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/serialize-default-value.ts index 6ce2b5153..3751aa8ed 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/serialize-default-value.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/serialize-default-value.ts @@ -2,7 +2,8 @@ import { BadRequestException } from '@nestjs/common'; import { FieldMetadataDefaultSerializableValue } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface'; -import { serializeTypeDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/serialize-type-default-value.util'; +import { isFunctionDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/is-function-default-value.util'; +import { serializeFunctionDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/serialize-function-default-value.util'; export const serializeDefaultValue = ( defaultValue?: FieldMetadataDefaultSerializableValue, @@ -11,13 +12,10 @@ export const serializeDefaultValue = ( return null; } - // Dynamic default values - if ( - !Array.isArray(defaultValue) && - typeof defaultValue === 'object' && - 'type' in defaultValue - ) { - const serializedTypeDefaultValue = serializeTypeDefaultValue(defaultValue); + // Function default values + if (isFunctionDefaultValue(defaultValue)) { + const serializedTypeDefaultValue = + serializeFunctionDefaultValue(defaultValue); if (!serializedTypeDefaultValue) { throw new BadRequestException('Invalid default value'); @@ -27,8 +25,8 @@ export const serializeDefaultValue = ( } // Static default values - if (typeof defaultValue === 'string') { - return `'${defaultValue}'`; + if (typeof defaultValue === 'string' && defaultValue.startsWith("'")) { + return defaultValue; } if (typeof defaultValue === 'number') { @@ -51,5 +49,5 @@ export const serializeDefaultValue = ( return `'${JSON.stringify(defaultValue)}'`; } - throw new BadRequestException('Invalid default value'); + throw new BadRequestException(`Invalid default value "${defaultValue}"`); }; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/serialize-function-default-value.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/serialize-function-default-value.util.ts new file mode 100644 index 000000000..645abd556 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/serialize-function-default-value.util.ts @@ -0,0 +1,14 @@ +import { FieldMetadataFunctionDefaultValue } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface'; + +export const serializeFunctionDefaultValue = ( + defaultValue?: FieldMetadataFunctionDefaultValue, +) => { + switch (defaultValue) { + case 'uuid': + return 'public.uuid_generate_v4()'; + case 'now': + return 'now()'; + default: + return null; + } +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/serialize-type-default-value.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/serialize-type-default-value.util.ts deleted file mode 100644 index 02831c631..000000000 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/serialize-type-default-value.util.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { FieldMetadataDynamicDefaultValue } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface'; - -export const serializeTypeDefaultValue = ( - defaultValue?: FieldMetadataDynamicDefaultValue, -) => { - if (!defaultValue?.type) { - return null; - } - - switch (defaultValue.type) { - case 'uuid': - return 'public.uuid_generate_v4()'; - case 'now': - return 'now()'; - default: - return null; - } -}; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts index 5da968a42..a5ddf44d2 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts @@ -1,7 +1,10 @@ import { plainToInstance } from 'class-transformer'; import { validateSync } from 'class-validator'; -import { FieldMetadataDefaultValue } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface'; +import { + FieldMetadataClassValidation, + FieldMetadataDefaultValue, +} from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { @@ -14,21 +17,22 @@ import { FieldMetadataDefaultValueNumber, FieldMetadataDefaultValueString, FieldMetadataDefaultValueStringArray, - FieldMetadataDynamicDefaultValueNow, - FieldMetadataDynamicDefaultValueUuid, + FieldMetadataDefaultValueNowFunction, + FieldMetadataDefaultValueUuidFunction, } from 'src/engine/metadata-modules/field-metadata/dtos/default-value.input'; +import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; export const defaultValueValidatorsMap = { [FieldMetadataType.UUID]: [ FieldMetadataDefaultValueString, - FieldMetadataDynamicDefaultValueUuid, + FieldMetadataDefaultValueUuidFunction, ], [FieldMetadataType.TEXT]: [FieldMetadataDefaultValueString], [FieldMetadataType.PHONE]: [FieldMetadataDefaultValueString], [FieldMetadataType.EMAIL]: [FieldMetadataDefaultValueString], [FieldMetadataType.DATE_TIME]: [ FieldMetadataDefaultValueDateTime, - FieldMetadataDynamicDefaultValueNow, + FieldMetadataDefaultValueNowFunction, ], [FieldMetadataType.BOOLEAN]: [FieldMetadataDefaultValueBoolean], [FieldMetadataType.NUMBER]: [FieldMetadataDefaultValueNumber], @@ -54,10 +58,14 @@ export const validateDefaultValueForType = ( if (!validators) return false; const isValid = validators.some((validator) => { + const conputedDefaultValue = isCompositeFieldMetadataType(type) + ? defaultValue + : { value: defaultValue }; + const defaultValueInstance = plainToInstance< any, - FieldMetadataDefaultValue - >(validator, defaultValue); + FieldMetadataClassValidation + >(validator, conputedDefaultValue as FieldMetadataClassValidation); return ( validateSync(defaultValueInstance, { diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/validators/is-quoted-string.validator.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/validators/is-quoted-string.validator.ts new file mode 100644 index 000000000..bcff44500 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/validators/is-quoted-string.validator.ts @@ -0,0 +1,24 @@ +import { + ValidationArguments, + ValidationOptions, + registerDecorator, +} from 'class-validator'; + +export function IsQuotedString(validationOptions?: ValidationOptions) { + return function (object: object, propertyName: string) { + registerDecorator({ + name: 'isQuotedString', + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + validator: { + validate(value: any) { + return typeof value === 'string' && /^'.*'$/.test(value); + }, + defaultMessage(args: ValidationArguments) { + return `${args.property} must be a quoted string`; + }, + }, + }); + }; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts index e63045454..a694a4fde 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/object-metadata.service.ts @@ -256,7 +256,7 @@ export class ObjectMetadataService extends TypeOrmQueryService, options?: WorkspaceColumnActionOptions, ): WorkspaceMigrationColumnCreate { - const defaultValue = - this.getDefaultValue(fieldMetadata.defaultValue) ?? options?.defaultValue; + const defaultValue = fieldMetadata.defaultValue ?? options?.defaultValue; const serializedDefaultValue = serializeDefaultValue(defaultValue); return { @@ -54,8 +52,7 @@ export class BasicColumnActionFactory extends ColumnActionAbstractFactory - | undefined - | null, - ) { - if (!defaultValue) return null; - - if ('type' in defaultValue) { - return defaultValue; - } else { - return defaultValue?.value; - } - } } diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/enum-column-action.factory.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/enum-column-action.factory.ts index 6829ad6ae..3af7cd7f3 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/enum-column-action.factory.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/enum-column-action.factory.ts @@ -26,8 +26,7 @@ export class EnumColumnActionFactory extends ColumnActionAbstractFactory, options: WorkspaceColumnActionOptions, ): WorkspaceMigrationColumnCreate { - const defaultValue = - fieldMetadata.defaultValue?.value ?? options?.defaultValue; + const defaultValue = fieldMetadata.defaultValue ?? options?.defaultValue; const serializedDefaultValue = serializeDefaultValue(defaultValue); const enumOptions = fieldMetadata.options ? [...fieldMetadata.options.map((option) => option.value)] @@ -50,7 +49,7 @@ export class EnumColumnActionFactory extends ColumnActionAbstractFactory { +export class WorkspaceDefaultValueFixer extends AbstractWorkspaceFixer { constructor( private readonly workspaceMigrationFieldFactory: WorkspaceMigrationFieldFactory, ) { - super(WorkspaceHealthIssueType.COLUMN_DEFAULT_VALUE_CONFLICT); + super( + WorkspaceHealthIssueType.COLUMN_DEFAULT_VALUE_CONFLICT, + WorkspaceHealthIssueType.COLUMN_DEFAULT_VALUE_NOT_VALID, + ); } async createWorkspaceMigrations( manager: EntityManager, objectMetadataCollection: ObjectMetadataEntity[], - issues: WorkspaceHealthColumnIssue[], + issues: WorkspaceHealthColumnIssue[], ): Promise[]> { if (issues.length <= 0) { return []; } + const splittedIssues = this.splitIssuesByType(issues); + const issueNeedingMigration = + splittedIssues[WorkspaceHealthIssueType.COLUMN_DEFAULT_VALUE_CONFLICT] ?? + []; - return this.fixColumnDefaultValueIssues(objectMetadataCollection, issues); + return this.fixColumnDefaultValueConflictIssues( + objectMetadataCollection, + issueNeedingMigration as WorkspaceHealthColumnIssue[], + ); } - private async fixColumnDefaultValueIssues( + async createMetadataUpdates( + manager: EntityManager, + objectMetadataCollection: ObjectMetadataEntity[], + issues: WorkspaceHealthColumnIssue[], + ): Promise[]> { + if (issues.length <= 0) { + return []; + } + + const splittedIssues = this.splitIssuesByType(issues); + const issueNeedingMetadataUpdate = + splittedIssues[WorkspaceHealthIssueType.COLUMN_DEFAULT_VALUE_NOT_VALID] ?? + []; + + return this.fixColumnDefaultValueNotValidIssues( + manager, + issueNeedingMetadataUpdate as WorkspaceHealthColumnIssue[], + ); + } + + private async fixColumnDefaultValueConflictIssues( objectMetadataCollection: ObjectMetadataEntity[], issues: WorkspaceHealthColumnIssue[], ): Promise[]> { @@ -61,6 +103,90 @@ export class WorkspaceDefaultValueFixer extends AbstractWorkspaceFixer[], + ): Promise[]> { + const fieldMetadataRepository = manager.getRepository(FieldMetadataEntity); + const updatedEntities: CompareEntity[] = []; + + for (const issue of issues) { + const currentDefaultValue: + | FieldMetadataDefaultValue<'default'> + // Old format for default values + // TODO: Remove this after all workspaces are migrated + | { type: FieldMetadataDefaultValueFunctionNames } + | null = issue.fieldMetadata.defaultValue; + let alteredDefaultValue: FieldMetadataDefaultValue<'default'> | null = + null; + + // Check if it's an old function default value + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + if (currentDefaultValue && 'type' in currentDefaultValue) { + alteredDefaultValue = + currentDefaultValue.type as FieldMetadataDefaultValueFunctionNames; + } + + // Check if it's an old string default value + if (currentDefaultValue) { + for (const key of Object.keys(currentDefaultValue)) { + if (key === 'type') { + continue; + } + + const value = currentDefaultValue[key]; + + const newValue = + typeof value === 'string' && + !value.startsWith("'") && + !Object.values(fieldMetadataDefaultValueFunctionName).includes( + value as FieldMetadataDefaultValueFunctionNames, + ) + ? `'${value}'` + : value; + + alteredDefaultValue = { + ...(currentDefaultValue as any), + ...(alteredDefaultValue as any), + [key]: newValue, + }; + } + } + + // Old formart default values + if ( + alteredDefaultValue && + typeof alteredDefaultValue === 'object' && + 'value' in alteredDefaultValue + ) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + alteredDefaultValue = alteredDefaultValue.value; + } + + if (alteredDefaultValue === null) { + continue; + } + + await fieldMetadataRepository.update(issue.fieldMetadata.id, { + defaultValue: alteredDefaultValue, + }); + const alteredEntity = await fieldMetadataRepository.findOne({ + where: { + id: issue.fieldMetadata.id, + }, + }); + + updatedEntities.push({ + current: issue.fieldMetadata, + altered: alteredEntity as FieldMetadataEntity | null, + }); + } + + return updatedEntities; + } + private computeFieldMetadataDefaultValueFromColumnDefault( columnDefault: string | undefined, ): FieldMetadataDefaultValue<'default'> { @@ -73,29 +199,29 @@ export class WorkspaceDefaultValueFixer extends AbstractWorkspaceFixer tableDefinition.columnName === columnName, @@ -178,7 +191,7 @@ export class FieldMetadataHealthService { if (columnDefaultValue && isEnumFieldMetadataType(fieldMetadata.type)) { const enumValues = fieldMetadata.options?.map((option) => - serializeDefaultValue(option.value), + serializeDefaultValue(`'${option.value}'`), ); if (!enumValues.includes(columnDefaultValue)) { @@ -325,10 +338,11 @@ export class FieldMetadataHealthService { isEnumFieldMetadataType(fieldMetadata.type) && fieldMetadata.defaultValue ) { - const enumValues = fieldMetadata.options?.map((option) => option.value); - const metadataDefaultValue = ( - fieldMetadata.defaultValue as FieldMetadataDefaultValue - )?.value; + const enumValues = fieldMetadata.options?.map((option) => + serializeDefaultValue(`'${option.value}'`), + ); + const metadataDefaultValue = + fieldMetadata.defaultValue as FieldMetadataDefaultValue; if (metadataDefaultValue && !enumValues.includes(metadataDefaultValue)) { issues.push({ @@ -341,29 +355,4 @@ export class FieldMetadataHealthService { return issues; } - - private isCompositeObjectWellStructured( - fieldMetadataType: FieldMetadataType, - object: any, - ): boolean { - const subFields = compositeDefinitions.get(fieldMetadataType)?.() ?? []; - - if (!object) { - return true; - } - - if (subFields.length === 0) { - throw new InternalServerErrorException( - `The composite field type ${fieldMetadataType} doesn't have any sub fields, it seems this one is not implemented in the composite definitions map`, - ); - } - - for (const subField of subFields) { - if (!object[subField.name]) { - return false; - } - } - - return true; - } } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-health/services/workspace-fix.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-health/services/workspace-fix.service.ts index 523ba4563..dae84af7a 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-health/services/workspace-fix.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-health/services/workspace-fix.service.ts @@ -90,6 +90,16 @@ export class WorkspaceFixService { filteredIssues, ); } + case WorkspaceHealthFixKind.DefaultValue: { + const filteredIssues = + this.workspaceDefaultValueFixer.filterIssues(issues); + + return this.workspaceDefaultValueFixer.createMetadataUpdates( + manager, + objectMetadataCollection, + filteredIssues, + ); + } default: { return []; } diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/custom-objects/custom.object-metadata.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/custom-objects/custom.object-metadata.ts index b7daff7e9..c38fb3a95 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/custom-objects/custom.object-metadata.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/custom-objects/custom.object-metadata.ts @@ -23,7 +23,7 @@ export class CustomObjectMetadata extends BaseObjectMetadata { description: 'Name', type: FieldMetadataType.TEXT, icon: 'IconAbc', - defaultValue: { value: 'Untitled' }, + defaultValue: "'Untitled'", }) name: string; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-objects/base.object-metadata.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-objects/base.object-metadata.ts index 5277c7c8c..533431ffa 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-objects/base.object-metadata.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/standard-objects/base.object-metadata.ts @@ -9,7 +9,7 @@ export abstract class BaseObjectMetadata { type: FieldMetadataType.UUID, label: 'Id', description: 'Id', - defaultValue: { type: 'uuid' }, + defaultValue: 'uuid', icon: 'Icon123', }) @IsSystem() @@ -21,7 +21,7 @@ export abstract class BaseObjectMetadata { label: 'Creation date', description: 'Creation date', icon: 'IconCalendar', - defaultValue: { type: 'now' }, + defaultValue: 'now', }) createdAt: Date; @@ -31,7 +31,7 @@ export abstract class BaseObjectMetadata { label: 'Update date', description: 'Update date', icon: 'IconCalendar', - defaultValue: { type: 'now' }, + defaultValue: 'now', }) @IsSystem() updatedAt: Date; diff --git a/packages/twenty-server/src/modules/activity/standard-objects/activity.object-metadata.ts b/packages/twenty-server/src/modules/activity/standard-objects/activity.object-metadata.ts index f9e6bfa16..452ff189a 100644 --- a/packages/twenty-server/src/modules/activity/standard-objects/activity.object-metadata.ts +++ b/packages/twenty-server/src/modules/activity/standard-objects/activity.object-metadata.ts @@ -47,7 +47,7 @@ export class ActivityObjectMetadata extends BaseObjectMetadata { label: 'Type', description: 'Activity type', icon: 'IconCheckbox', - defaultValue: { value: 'Note' }, + defaultValue: "'Note'", }) type: string; diff --git a/packages/twenty-server/src/modules/calendar/standard-objects/calendar-channel.object-metadata.ts b/packages/twenty-server/src/modules/calendar/standard-objects/calendar-channel.object-metadata.ts index 8acb2b869..d5ec0c1dd 100644 --- a/packages/twenty-server/src/modules/calendar/standard-objects/calendar-channel.object-metadata.ts +++ b/packages/twenty-server/src/modules/calendar/standard-objects/calendar-channel.object-metadata.ts @@ -72,7 +72,7 @@ export class CalendarChannelObjectMetadata extends BaseObjectMetadata { color: 'orange', }, ], - defaultValue: { value: CalendarChannelVisibility.SHARE_EVERYTHING }, + defaultValue: `'${CalendarChannelVisibility.SHARE_EVERYTHING}'`, }) visibility: string; @@ -82,7 +82,7 @@ export class CalendarChannelObjectMetadata extends BaseObjectMetadata { label: 'Is Contact Auto Creation Enabled', description: 'Is Contact Auto Creation Enabled', icon: 'IconUserCircle', - defaultValue: { value: true }, + defaultValue: true, }) isContactAutoCreationEnabled: boolean; @@ -92,7 +92,7 @@ export class CalendarChannelObjectMetadata extends BaseObjectMetadata { label: 'Is Sync Enabled', description: 'Is Sync Enabled', icon: 'IconRefresh', - defaultValue: { value: true }, + defaultValue: true, }) isSyncEnabled: boolean; diff --git a/packages/twenty-server/src/modules/calendar/standard-objects/calendar-event-attendee.object-metadata.ts b/packages/twenty-server/src/modules/calendar/standard-objects/calendar-event-attendee.object-metadata.ts index f346d8eb0..d36ef72e5 100644 --- a/packages/twenty-server/src/modules/calendar/standard-objects/calendar-event-attendee.object-metadata.ts +++ b/packages/twenty-server/src/modules/calendar/standard-objects/calendar-event-attendee.object-metadata.ts @@ -100,7 +100,7 @@ export class CalendarEventAttendeeObjectMetadata extends BaseObjectMetadata { color: 'green', }, ], - defaultValue: { value: CalendarEventAttendeeResponseStatus.NEEDS_ACTION }, + defaultValue: `'${CalendarEventAttendeeResponseStatus.NEEDS_ACTION}'`, }) responseStatus: string; diff --git a/packages/twenty-server/src/modules/company/standard-objects/company.object-metadata.ts b/packages/twenty-server/src/modules/company/standard-objects/company.object-metadata.ts index 0b6b503ac..af5db2ed7 100644 --- a/packages/twenty-server/src/modules/company/standard-objects/company.object-metadata.ts +++ b/packages/twenty-server/src/modules/company/standard-objects/company.object-metadata.ts @@ -106,7 +106,7 @@ export class CompanyObjectMetadata extends BaseObjectMetadata { description: 'Ideal Customer Profile: Indicates whether the company is the most suitable and valuable customer for you', icon: 'IconTarget', - defaultValue: { value: false }, + defaultValue: false, }) idealCustomerProfile: boolean; diff --git a/packages/twenty-server/src/modules/favorite/standard-objects/favorite.object-metadata.ts b/packages/twenty-server/src/modules/favorite/standard-objects/favorite.object-metadata.ts index c39257542..104f9406c 100644 --- a/packages/twenty-server/src/modules/favorite/standard-objects/favorite.object-metadata.ts +++ b/packages/twenty-server/src/modules/favorite/standard-objects/favorite.object-metadata.ts @@ -29,7 +29,7 @@ export class FavoriteObjectMetadata extends BaseObjectMetadata { label: 'Position', description: 'Favorite position', icon: 'IconList', - defaultValue: { value: 0 }, + defaultValue: 0, }) position: number; diff --git a/packages/twenty-server/src/modules/messaging/standard-objects/message-channel.object-metadata.ts b/packages/twenty-server/src/modules/messaging/standard-objects/message-channel.object-metadata.ts index b5167c866..2d0ec4072 100644 --- a/packages/twenty-server/src/modules/messaging/standard-objects/message-channel.object-metadata.ts +++ b/packages/twenty-server/src/modules/messaging/standard-objects/message-channel.object-metadata.ts @@ -40,7 +40,7 @@ export class MessageChannelObjectMetadata extends BaseObjectMetadata { color: 'orange', }, ], - defaultValue: { value: 'share_everything' }, + defaultValue: "'share_everything'", }) visibility: string; @@ -73,7 +73,7 @@ export class MessageChannelObjectMetadata extends BaseObjectMetadata { { value: 'email', label: 'Email', position: 0, color: 'green' }, { value: 'sms', label: 'SMS', position: 1, color: 'blue' }, ], - defaultValue: { value: 'email' }, + defaultValue: "'email'", }) type: string; @@ -83,7 +83,7 @@ export class MessageChannelObjectMetadata extends BaseObjectMetadata { label: 'Is Contact Auto Creation Enabled', description: 'Is Contact Auto Creation Enabled', icon: 'IconUserCircle', - defaultValue: { value: true }, + defaultValue: true, }) isContactAutoCreationEnabled: boolean; diff --git a/packages/twenty-server/src/modules/messaging/standard-objects/message-participant.object-metadata.ts b/packages/twenty-server/src/modules/messaging/standard-objects/message-participant.object-metadata.ts index c3de6e697..6c0e39c6f 100644 --- a/packages/twenty-server/src/modules/messaging/standard-objects/message-participant.object-metadata.ts +++ b/packages/twenty-server/src/modules/messaging/standard-objects/message-participant.object-metadata.ts @@ -42,7 +42,7 @@ export class MessageParticipantObjectMetadata extends BaseObjectMetadata { { value: 'cc', label: 'Cc', position: 2, color: 'orange' }, { value: 'bcc', label: 'Bcc', position: 3, color: 'red' }, ], - defaultValue: { value: 'from' }, + defaultValue: "'from'", }) role: string; diff --git a/packages/twenty-server/src/modules/messaging/standard-objects/message.object-metadata.ts b/packages/twenty-server/src/modules/messaging/standard-objects/message.object-metadata.ts index 4b81f30ef..f7d944e21 100644 --- a/packages/twenty-server/src/modules/messaging/standard-objects/message.object-metadata.ts +++ b/packages/twenty-server/src/modules/messaging/standard-objects/message.object-metadata.ts @@ -55,7 +55,7 @@ export class MessageObjectMetadata extends BaseObjectMetadata { { value: 'incoming', label: 'Incoming', position: 0, color: 'green' }, { value: 'outgoing', label: 'Outgoing', position: 1, color: 'blue' }, ], - defaultValue: { value: 'incoming' }, + defaultValue: "'incoming'", }) direction: string; diff --git a/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.object-metadata.ts b/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.object-metadata.ts index 2ed50cf21..2708cc2fa 100644 --- a/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.object-metadata.ts +++ b/packages/twenty-server/src/modules/opportunity/standard-objects/opportunity.object-metadata.ts @@ -63,7 +63,7 @@ export class OpportunityObjectMetadata extends BaseObjectMetadata { label: 'Probability', description: 'Opportunity probability', icon: 'IconProgressCheck', - defaultValue: { value: '0' }, + defaultValue: "'0'", }) probability: string; @@ -85,7 +85,7 @@ export class OpportunityObjectMetadata extends BaseObjectMetadata { }, { value: 'CUSTOMER', label: 'Customer', position: 4, color: 'yellow' }, ], - defaultValue: { value: 'NEW' }, + defaultValue: "'NEW'", }) stage: string; diff --git a/packages/twenty-server/src/modules/view/standard-objects/view-field.object-metadata.ts b/packages/twenty-server/src/modules/view/standard-objects/view-field.object-metadata.ts index 06b3c44c6..7c363a84e 100644 --- a/packages/twenty-server/src/modules/view/standard-objects/view-field.object-metadata.ts +++ b/packages/twenty-server/src/modules/view/standard-objects/view-field.object-metadata.ts @@ -33,7 +33,7 @@ export class ViewFieldObjectMetadata extends BaseObjectMetadata { label: 'Visible', description: 'View Field visibility', icon: 'IconEye', - defaultValue: { value: true }, + defaultValue: true, }) isVisible: boolean; @@ -43,7 +43,7 @@ export class ViewFieldObjectMetadata extends BaseObjectMetadata { label: 'Size', description: 'View Field size', icon: 'IconEye', - defaultValue: { value: 0 }, + defaultValue: 0, }) size: number; @@ -53,7 +53,7 @@ export class ViewFieldObjectMetadata extends BaseObjectMetadata { label: 'Position', description: 'View Field position', icon: 'IconList', - defaultValue: { value: 0 }, + defaultValue: 0, }) position: number; diff --git a/packages/twenty-server/src/modules/view/standard-objects/view-filter.object-metadata.ts b/packages/twenty-server/src/modules/view/standard-objects/view-filter.object-metadata.ts index 7e2588c85..0912b8ddb 100644 --- a/packages/twenty-server/src/modules/view/standard-objects/view-filter.object-metadata.ts +++ b/packages/twenty-server/src/modules/view/standard-objects/view-filter.object-metadata.ts @@ -31,7 +31,7 @@ export class ViewFilterObjectMetadata extends BaseObjectMetadata { type: FieldMetadataType.TEXT, label: 'Operand', description: 'View Filter operand', - defaultValue: { value: 'Contains' }, + defaultValue: "'Contains'", }) operand: string; diff --git a/packages/twenty-server/src/modules/view/standard-objects/view-sort.object-metadata.ts b/packages/twenty-server/src/modules/view/standard-objects/view-sort.object-metadata.ts index d41ff278a..829b479f3 100644 --- a/packages/twenty-server/src/modules/view/standard-objects/view-sort.object-metadata.ts +++ b/packages/twenty-server/src/modules/view/standard-objects/view-sort.object-metadata.ts @@ -32,7 +32,7 @@ export class ViewSortObjectMetadata extends BaseObjectMetadata { type: FieldMetadataType.TEXT, label: 'Direction', description: 'View Sort direction', - defaultValue: { value: 'asc' }, + defaultValue: "'asc'", }) direction: string; diff --git a/packages/twenty-server/src/modules/view/standard-objects/view.object-metadata.ts b/packages/twenty-server/src/modules/view/standard-objects/view.object-metadata.ts index 9669fa414..c644497b8 100644 --- a/packages/twenty-server/src/modules/view/standard-objects/view.object-metadata.ts +++ b/packages/twenty-server/src/modules/view/standard-objects/view.object-metadata.ts @@ -43,7 +43,7 @@ export class ViewObjectMetadata extends BaseObjectMetadata { type: FieldMetadataType.TEXT, label: 'Type', description: 'View type', - defaultValue: { value: 'table' }, + defaultValue: "'table'", }) type: string; @@ -53,7 +53,7 @@ export class ViewObjectMetadata extends BaseObjectMetadata { label: 'Key', description: 'View key', options: [{ value: 'INDEX', label: 'Index', position: 0, color: 'red' }], - defaultValue: { value: 'INDEX' }, + defaultValue: "'INDEX'", }) @IsNullable() key: string; @@ -88,7 +88,7 @@ export class ViewObjectMetadata extends BaseObjectMetadata { type: FieldMetadataType.BOOLEAN, label: 'Compact View', description: 'Describes if the view is in compact mode', - defaultValue: { value: false }, + defaultValue: false, }) isCompact: boolean; diff --git a/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.object-metadata.ts b/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.object-metadata.ts index 29512c6b4..6a961658f 100644 --- a/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.object-metadata.ts +++ b/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.object-metadata.ts @@ -49,7 +49,7 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata { label: 'Color Scheme', description: 'Preferred color scheme', icon: 'IconColorSwatch', - defaultValue: { value: 'Light' }, + defaultValue: "'Light'", }) colorScheme: string; @@ -59,7 +59,7 @@ export class WorkspaceMemberObjectMetadata extends BaseObjectMetadata { label: 'Language', description: 'Preferred language', icon: 'IconLanguage', - defaultValue: { value: 'en' }, + defaultValue: "'en'", }) locale: string;