diff --git a/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMessage.tsx b/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMessage.tsx
index e5cd3507d9..ccd1150722 100644
--- a/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMessage.tsx
+++ b/packages/twenty-front/src/modules/activities/emails/components/EmailThreadMessage.tsx
@@ -6,6 +6,7 @@ import { EmailThreadMessageBodyPreview } from '@/activities/emails/components/Em
import { EmailThreadMessageReceivers } from '@/activities/emails/components/EmailThreadMessageReceivers';
import { EmailThreadMessageSender } from '@/activities/emails/components/EmailThreadMessageSender';
import { type EmailThreadMessageParticipant } from '@/activities/emails/types/EmailThreadMessageParticipant';
+import { MessageParticipantRole } from 'twenty-shared/types';
const StyledThreadMessage = styled.div`
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
@@ -45,7 +46,7 @@ export const EmailThreadMessage = ({
const [isOpen, setIsOpen] = useState(isExpanded);
const receivers = participants.filter(
- (participant) => participant.role !== 'from',
+ (participant) => participant.role !== MessageParticipantRole.FROM,
);
if (!sender || receivers.length === 0) {
diff --git a/packages/twenty-front/src/modules/activities/emails/types/EmailParticipantRole.ts b/packages/twenty-front/src/modules/activities/emails/types/EmailParticipantRole.ts
deleted file mode 100644
index f852b3ac07..0000000000
--- a/packages/twenty-front/src/modules/activities/emails/types/EmailParticipantRole.ts
+++ /dev/null
@@ -1 +0,0 @@
-export type EmailParticipantRole = 'from' | 'to' | 'cc' | 'bcc';
diff --git a/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessageParticipant.ts b/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessageParticipant.ts
index 937e988aab..f531b38672 100644
--- a/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessageParticipant.ts
+++ b/packages/twenty-front/src/modules/activities/emails/types/EmailThreadMessageParticipant.ts
@@ -1,12 +1,12 @@
-import { type EmailParticipantRole } from '@/activities/emails/types/EmailParticipantRole';
import { type Person } from '@/people/types/Person';
import { type WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
+import { type MessageParticipantRole } from 'twenty-shared/types';
export type EmailThreadMessageParticipant = {
id: string;
displayName: string;
handle: string;
- role: EmailParticipantRole;
+ role: MessageParticipantRole;
messageId: string;
person: Person;
workspaceMember: WorkspaceMember;
diff --git a/packages/twenty-front/src/modules/activities/emails/utils/__tests__/getDisplayNameFromParticipant.test.ts b/packages/twenty-front/src/modules/activities/emails/utils/__tests__/getDisplayNameFromParticipant.test.ts
index ae0e2455c5..a343a658c3 100644
--- a/packages/twenty-front/src/modules/activities/emails/utils/__tests__/getDisplayNameFromParticipant.test.ts
+++ b/packages/twenty-front/src/modules/activities/emails/utils/__tests__/getDisplayNameFromParticipant.test.ts
@@ -1,5 +1,6 @@
import { type EmailThreadMessageParticipant } from '@/activities/emails/types/EmailThreadMessageParticipant';
+import { MessageParticipantRole } from 'twenty-shared/types';
import { getDisplayNameFromParticipant } from '../getDisplayNameFromParticipant';
describe('getDisplayNameFromParticipant', () => {
@@ -8,7 +9,7 @@ describe('getDisplayNameFromParticipant', () => {
__typename: 'EmailThreadMessageParticipant',
displayName: '',
handle: '',
- role: 'from',
+ role: MessageParticipantRole.FROM,
messageId: '638f52d1-fd55-4a2b-b0f3-9858ea3b2e91',
person: {
__typename: 'Person',
@@ -51,23 +52,23 @@ describe('getDisplayNameFromParticipant', () => {
},
};
- const participantWithHandle: any = {
+ const participantWithHandle = {
displayName: '',
handle: 'user_handle',
- role: 'from',
- };
+ role: MessageParticipantRole.FROM,
+ } as EmailThreadMessageParticipant;
- const participantWithDisplayName: any = {
+ const participantWithDisplayName = {
displayName: 'User123',
handle: '',
- role: 'from',
- };
+ role: MessageParticipantRole.FROM,
+ } as EmailThreadMessageParticipant;
- const participantWithoutInfo: any = {
+ const participantWithoutInfo = {
displayName: '',
handle: '',
- role: 'from',
- };
+ role: MessageParticipantRole.FROM,
+ } as EmailThreadMessageParticipant;
it('should return full name when shouldUseFullName is true', () => {
expect(
diff --git a/packages/twenty-front/src/modules/command-menu/pages/message-thread/hooks/__tests__/useEmailThreadInCommandMenu.test.tsx b/packages/twenty-front/src/modules/command-menu/pages/message-thread/hooks/__tests__/useEmailThreadInCommandMenu.test.tsx
index bcc2d2ad34..5fded4ab5c 100644
--- a/packages/twenty-front/src/modules/command-menu/pages/message-thread/hooks/__tests__/useEmailThreadInCommandMenu.test.tsx
+++ b/packages/twenty-front/src/modules/command-menu/pages/message-thread/hooks/__tests__/useEmailThreadInCommandMenu.test.tsx
@@ -7,6 +7,7 @@ import {
QUERY_DEFAULT_LIMIT_RECORDS,
QUERY_MAX_RECORDS,
} from 'twenty-shared/constants';
+import { MessageParticipantRole } from 'twenty-shared/types';
import { generateEmptyJestRecordNode } from '~/testing/jest/generateEmptyJestRecordNode';
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
import { useEmailThreadInCommandMenu } from '../useEmailThreadInCommandMenu';
@@ -313,7 +314,10 @@ const mocks = [
}
`,
variables: {
- filter: { messageId: { in: ['1', '2'] }, role: { eq: 'from' } },
+ filter: {
+ messageId: { in: ['1', '2'] },
+ role: { eq: MessageParticipantRole.FROM },
+ },
orderBy: undefined,
lastCursor: undefined,
limit: QUERY_DEFAULT_LIMIT_RECORDS,
@@ -328,7 +332,7 @@ const mocks = [
objectNameSingular: 'messageParticipant',
input: {
id: 'messageParticipant-1',
- role: 'from',
+ role: MessageParticipantRole.FROM,
messageId: '1',
},
}),
@@ -339,7 +343,7 @@ const mocks = [
objectNameSingular: 'messageParticipant',
input: {
id: 'messageParticipant-2',
- role: 'from',
+ role: MessageParticipantRole.FROM,
messageId: '2',
},
}),
@@ -401,7 +405,7 @@ describe('useEmailThreadInCommandMenu', () => {
id: 'messageParticipant-1',
messageId: '1',
person: null,
- role: 'from',
+ role: MessageParticipantRole.FROM,
workspaceMember: null,
},
subject: '',
@@ -422,7 +426,7 @@ describe('useEmailThreadInCommandMenu', () => {
id: 'messageParticipant-2',
messageId: '2',
person: null,
- role: 'from',
+ role: MessageParticipantRole.FROM,
workspaceMember: null,
},
subject: '',
diff --git a/packages/twenty-front/src/modules/command-menu/pages/message-thread/hooks/useEmailThreadInCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/pages/message-thread/hooks/useEmailThreadInCommandMenu.ts
index 09c2d40df4..806255016d 100644
--- a/packages/twenty-front/src/modules/command-menu/pages/message-thread/hooks/useEmailThreadInCommandMenu.ts
+++ b/packages/twenty-front/src/modules/command-menu/pages/message-thread/hooks/useEmailThreadInCommandMenu.ts
@@ -14,6 +14,7 @@ import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
+import { MessageParticipantRole } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
// to improve - https://github.com/twentyhq/twenty/issues/12190
@@ -82,7 +83,7 @@ export const useEmailThreadInCommandMenu = () => {
in: messages.map(({ id }) => id),
},
role: {
- eq: 'from',
+ eq: MessageParticipantRole.FROM,
},
},
objectNameSingular: CoreObjectNameSingular.MessageParticipant,
diff --git a/packages/twenty-front/src/pages/settings/data-model/utils/computeMetadataNameFromLabel.ts b/packages/twenty-front/src/pages/settings/data-model/utils/computeMetadataNameFromLabel.ts
index aaad22006b..f0d5f276c9 100644
--- a/packages/twenty-front/src/pages/settings/data-model/utils/computeMetadataNameFromLabel.ts
+++ b/packages/twenty-front/src/pages/settings/data-model/utils/computeMetadataNameFromLabel.ts
@@ -4,7 +4,7 @@ import { computeMetadataNameFromLabel as computeMetadataNameFromLabelCore } from
// This is needed for form validation and UI components that prefer graceful degradation
export const computeMetadataNameFromLabel = (label: string): string => {
try {
- return computeMetadataNameFromLabelCore(label);
+ return computeMetadataNameFromLabelCore({ label });
} catch {
return '';
}
diff --git a/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts b/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts
index 196b199812..6168eb358d 100644
--- a/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts
+++ b/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts
@@ -3877,7 +3877,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
"isUnique": false,
"createdAt": "2025-06-09T18:53:47.000Z",
"updatedAt": "2025-06-09T18:53:47.000Z",
- "defaultValue": "'from'",
+ "defaultValue": "'FROM'",
"options": [
{
"id": "edc6f452-4ecc-4ff7-b30c-18575dbe0bfd",
@@ -8594,7 +8594,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
"__typename": "Field",
"id": "21f219f9-c531-4ab5-9640-69230baa83d0",
"type": "TEXT",
- "name": "iCalUID",
+ "name": "iCalUid",
"isCustom": false,
"isActive": true,
"isSystem": false,
diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command.ts
new file mode 100644
index 0000000000..cf0d10b1c6
--- /dev/null
+++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command.ts
@@ -0,0 +1,380 @@
+import { InjectRepository } from '@nestjs/typeorm';
+
+import { Command } from 'nest-commander';
+import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata';
+import {
+ FieldMetadataComplexOption,
+ FieldMetadataDefaultValue,
+ FieldMetadataType,
+ FromTo,
+} from 'twenty-shared/types';
+import { isDefined } from 'twenty-shared/utils';
+import { type Repository } from 'typeorm';
+
+import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
+import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner';
+import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
+import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
+import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/services/field-metadata.service';
+import { MetadataFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/metadata-flat-entity-maps.type';
+import { findManyFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-many-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
+import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
+import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
+import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
+import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
+import { AllStandardObjectFieldName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-field-name.type';
+import { AllStandardObjectName } from 'src/engine/workspace-manager/twenty-standard-application/types/all-standard-object-name.type';
+import {
+ CALENDAR_EVENT_STANDARD_FIELD_IDS,
+ MESSAGE_CHANNEL_STANDARD_FIELD_IDS,
+ MESSAGE_PARTICIPANT_STANDARD_FIELD_IDS,
+ MESSAGE_STANDARD_FIELD_IDS,
+ WORKSPACE_MEMBER_STANDARD_FIELD_IDS,
+} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
+
+const findStandardFlatObjectMetadataOrThrow = ({
+ flatObjectMetadataMaps,
+ standardId,
+}: {
+ flatObjectMetadataMaps: MetadataFlatEntityMaps<'objectMetadata'>;
+ standardId: string;
+}): FlatObjectMetadata => {
+ const flatObjectMetadata = Object.values(flatObjectMetadataMaps.byId).find(
+ (flatObjectMetadata) => flatObjectMetadata?.standardId === standardId,
+ );
+
+ if (!isDefined(flatObjectMetadata)) {
+ throw new Error(`Could not find standard object ${standardId}`);
+ }
+
+ return flatObjectMetadata;
+};
+
+const findStandardFlatFieldMetadatawOrThrow = ({
+ flatFieldMetadataMaps,
+ flatObjectMetadata,
+ standardId,
+}: {
+ standardId: string;
+ flatObjectMetadata: FlatObjectMetadata;
+ flatFieldMetadataMaps: MetadataFlatEntityMaps<'fieldMetadata'>;
+}) => {
+ const objectFlatFieldMetadata = findManyFlatEntityByIdInFlatEntityMapsOrThrow(
+ {
+ flatEntityIds: flatObjectMetadata.fieldMetadataIds,
+ flatEntityMaps: flatFieldMetadataMaps,
+ },
+ );
+
+ const flatFieldMetadata = objectFlatFieldMetadata.find(
+ (flatFieldMetadata) => flatFieldMetadata.standardId === standardId,
+ );
+
+ if (!isDefined(flatFieldMetadata)) {
+ throw new Error(`Could not find standard field ${standardId}`);
+ }
+
+ return flatFieldMetadata;
+};
+
+const ENUM_FIELD_OPTIONS_TO_MUTATE = {
+ messageChannelMessageAssociation: {
+ fieldStandardId: MESSAGE_STANDARD_FIELD_IDS.direction,
+ field: 'direction',
+ defaultValue: {
+ fromDefaultValue: "'incoming'",
+ toDefaultValue: "'INCOMING'",
+ },
+ options: [
+ {
+ fromOption: {
+ value: 'incoming',
+ label: 'Incoming',
+ position: 0,
+ color: 'green',
+ },
+ toOption: {
+ value: 'INCOMING',
+ label: 'Incoming',
+ position: 0,
+ color: 'green',
+ },
+ },
+ {
+ fromOption: {
+ value: 'outgoing',
+ label: 'Outgoing',
+ position: 1,
+ color: 'blue',
+ },
+ toOption: {
+ value: 'OUTGOING',
+ label: 'Outgoing',
+ position: 1,
+ color: 'blue',
+ },
+ },
+ ],
+ },
+ messageChannel: {
+ fieldStandardId: MESSAGE_CHANNEL_STANDARD_FIELD_IDS.type,
+ field: 'type',
+ defaultValue: { fromDefaultValue: "'email'", toDefaultValue: "'EMAIL'" },
+ options: [
+ {
+ fromOption: {
+ value: 'email',
+ label: 'Email',
+ position: 0,
+ color: 'green',
+ },
+ toOption: {
+ value: 'EMAIL',
+ label: 'Email',
+ position: 0,
+ color: 'green',
+ },
+ },
+ {
+ fromOption: { value: 'sms', label: 'SMS', position: 1, color: 'blue' },
+ toOption: { value: 'SMS', label: 'SMS', position: 1, color: 'blue' },
+ },
+ ],
+ },
+ messageParticipant: {
+ fieldStandardId: MESSAGE_PARTICIPANT_STANDARD_FIELD_IDS.role,
+ field: 'role',
+ defaultValue: { fromDefaultValue: "'from'", toDefaultValue: "'FROM'" },
+ options: [
+ {
+ fromOption: {
+ value: 'from',
+ label: 'From',
+ position: 0,
+ color: 'green',
+ },
+ toOption: { value: 'FROM', label: 'From', position: 0, color: 'green' },
+ },
+ {
+ fromOption: { value: 'to', label: 'To', position: 1, color: 'blue' },
+ toOption: { value: 'TO', label: 'To', position: 1, color: 'blue' },
+ },
+ {
+ fromOption: { value: 'cc', label: 'Cc', position: 2, color: 'orange' },
+ toOption: { value: 'CC', label: 'Cc', position: 2, color: 'orange' },
+ },
+ {
+ fromOption: { value: 'bcc', label: 'Bcc', position: 3, color: 'red' },
+ toOption: { value: 'BCC', label: 'Bcc', position: 3, color: 'red' },
+ },
+ ],
+ },
+ workspaceMember: {
+ fieldStandardId: WORKSPACE_MEMBER_STANDARD_FIELD_IDS.numberFormat,
+ field: 'numberFormat',
+ defaultValue: { fromDefaultValue: "'system'", toDefaultValue: "'SYSTEM'" },
+ options: [
+ {
+ fromOption: {
+ value: 'SYSTEM',
+ label: 'System',
+ position: 0,
+ color: 'turquoise',
+ },
+ toOption: {
+ value: 'SYSTEM',
+ label: 'System',
+ position: 0,
+ color: 'turquoise',
+ },
+ },
+ {
+ fromOption: {
+ value: 'COMMAS_AND_DOT',
+ label: 'Commas and dot (1,234.56)',
+ position: 1,
+ color: 'blue',
+ },
+ toOption: {
+ value: 'COMMAS_AND_DOT',
+ label: 'Commas and dot',
+ position: 1,
+ color: 'blue',
+ },
+ },
+ {
+ fromOption: {
+ value: 'SPACES_AND_COMMA',
+ label: 'Spaces and comma (1 234,56)',
+ position: 2,
+ color: 'green',
+ },
+ toOption: {
+ value: 'SPACES_AND_COMMA',
+ label: 'Spaces and comma',
+ position: 2,
+ color: 'green',
+ },
+ },
+ {
+ fromOption: {
+ value: 'DOTS_AND_COMMA',
+ label: 'Dots and comma (1.234,56)',
+ position: 3,
+ color: 'orange',
+ },
+ toOption: {
+ value: 'DOTS_AND_COMMA',
+ label: 'Dots and comma',
+ position: 3,
+ color: 'orange',
+ },
+ },
+ {
+ fromOption: {
+ value: 'APOSTROPHE_AND_DOT',
+ label: "Apostrophe and dot (1'234.56)",
+ position: 4,
+ color: 'purple',
+ },
+ toOption: {
+ value: 'APOSTROPHE_AND_DOT',
+ label: 'Apostrophe and dot',
+ position: 4,
+ color: 'purple',
+ },
+ },
+ ],
+ },
+} as const satisfies {
+ [P in AllStandardObjectName]?: {
+ fieldStandardId: string;
+ field: AllStandardObjectFieldName
;
+ options: FromTo[];
+ defaultValue: FromTo;
+ };
+};
+
+@Command({
+ name: 'upgrade:1-13:migrate-standard-invalid-entities',
+ description: 'Migrate standard invalid entities',
+})
+export class MigrateStandardInvalidEntitiesCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
+ constructor(
+ @InjectRepository(WorkspaceEntity)
+ protected readonly workspaceRepository: Repository,
+ protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
+ protected readonly dataSourceService: DataSourceService,
+ private readonly workspaceCacheService: WorkspaceCacheService,
+ private readonly fieldMetadataService: FieldMetadataService,
+ ) {
+ super(workspaceRepository, twentyORMGlobalManager, dataSourceService);
+ }
+
+ override async runOnWorkspace({
+ options,
+ workspaceId,
+ }: RunOnWorkspaceArgs): Promise {
+ const isDryRun = options.dryRun || false;
+
+ this.logger.log(
+ `MigrateStandardInvalidEntitiesCommand starting for workspace ${workspaceId}`,
+ );
+ const { flatFieldMetadataMaps, flatObjectMetadataMaps } =
+ await this.workspaceCacheService.getOrRecompute(workspaceId, [
+ 'flatFieldMetadataMaps',
+ 'flatObjectMetadataMaps',
+ ]);
+
+ const flatCalendarEventObject = findStandardFlatObjectMetadataOrThrow({
+ standardId: STANDARD_OBJECT_IDS.calendarEvent,
+ flatObjectMetadataMaps,
+ });
+
+ const iCalUidFieldId = findStandardFlatFieldMetadatawOrThrow({
+ flatFieldMetadataMaps,
+ flatObjectMetadata: flatCalendarEventObject,
+ standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.iCalUid,
+ }).id;
+
+ const allAffectedObjectNames = Object.keys(
+ ENUM_FIELD_OPTIONS_TO_MUTATE,
+ ).filter(isDefined) as (keyof typeof ENUM_FIELD_OPTIONS_TO_MUTATE)[];
+
+ const flatEnumFieldMetadatasToUpdate = allAffectedObjectNames.flatMap<{
+ id: string;
+ options: FieldMetadataComplexOption[];
+ defaultValue: FieldMetadataDefaultValue;
+ }>((objectName) => {
+ const { defaultValue, field, options, fieldStandardId } =
+ ENUM_FIELD_OPTIONS_TO_MUTATE[objectName];
+
+ const objectStandardId = STANDARD_OBJECT_IDS[objectName];
+
+ const flatObjectMetadata = findStandardFlatObjectMetadataOrThrow({
+ flatObjectMetadataMaps,
+ standardId: objectStandardId,
+ });
+
+ const flatFieldMetadata = findStandardFlatFieldMetadatawOrThrow({
+ flatFieldMetadataMaps,
+ flatObjectMetadata,
+ standardId: fieldStandardId,
+ });
+
+ if (
+ flatFieldMetadata.name !== field ||
+ !isFlatFieldMetadataOfType(flatFieldMetadata, FieldMetadataType.SELECT)
+ ) {
+ throw new Error(
+ `Retrieved flat field is not the one expected expected ${field} got ${flatFieldMetadata.name} in workspace ${workspaceId}`,
+ );
+ }
+
+ const updatedOptions = flatFieldMetadata.options.map((option) => {
+ const matchingNewOption = options.find(
+ ({ fromOption }) => fromOption.value === option.value,
+ );
+
+ if (!isDefined(matchingNewOption)) {
+ return option;
+ }
+
+ return {
+ ...matchingNewOption.toOption,
+ id: option.id,
+ };
+ });
+
+ const updatedDefaultValue: FieldMetadataDefaultValue =
+ flatFieldMetadata.defaultValue === defaultValue.fromDefaultValue
+ ? defaultValue.toDefaultValue
+ : flatFieldMetadata.defaultValue;
+
+ return {
+ id: flatFieldMetadata.id,
+ options: updatedOptions,
+ defaultValue: updatedDefaultValue,
+ };
+ });
+
+ const allUpdates = [
+ {
+ id: iCalUidFieldId,
+ name: 'iCalUid',
+ },
+ ...flatEnumFieldMetadatasToUpdate,
+ ];
+
+ if (!isDryRun) {
+ for (const updateFieldInput of allUpdates) {
+ await this.fieldMetadataService.updateOneField({
+ updateFieldInput,
+ workspaceId,
+ isSystemBuild: true,
+ });
+ }
+ }
+ this.logger.log('Migrated standard invalid entities');
+ }
+}
diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-upgrade-version-command.module.ts
index b05f4db5f0..c3b4b954bf 100644
--- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-upgrade-version-command.module.ts
+++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-13/1-13-upgrade-version-command.module.ts
@@ -5,6 +5,7 @@ import { BackfillPageLayoutUniversalIdentifiersCommand } from 'src/database/comm
import { BackfillViewMainGroupByFieldMetadataIdCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-backfill-view-main-group-by-field-metadata-id.command';
import { CleanEmptyStringNullInTextFieldsCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-clean-empty-string-null-in-text-fields.command';
import { DeduplicateRoleTargetsCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-deduplicate-role-targets.command';
+import { MigrateStandardInvalidEntitiesCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command';
import { RenameIndexNameCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-rename-unique-index.command';
import { UpdateRoleTargetsUniqueConstraintMigrationCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-update-role-targets-unique-constraint-migration.command';
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
@@ -12,6 +13,7 @@ import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
+import { FieldMetadataModule } from 'src/engine/metadata-modules/field-metadata/field-metadata.module';
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout-tab.entity';
@@ -40,6 +42,7 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
DataSourceModule,
ApplicationModule,
WorkspaceCacheModule,
+ FieldMetadataModule,
],
providers: [
CleanEmptyStringNullInTextFieldsCommand,
@@ -47,6 +50,7 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
BackfillPageLayoutUniversalIdentifiersCommand,
DeduplicateRoleTargetsCommand,
RenameIndexNameCommand,
+ MigrateStandardInvalidEntitiesCommand,
UpdateRoleTargetsUniqueConstraintMigrationCommand,
],
exports: [
@@ -55,6 +59,7 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
BackfillPageLayoutUniversalIdentifiersCommand,
DeduplicateRoleTargetsCommand,
RenameIndexNameCommand,
+ MigrateStandardInvalidEntitiesCommand,
UpdateRoleTargetsUniqueConstraintMigrationCommand,
],
})
diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts
index 84cd350622..b8d4f1a85f 100644
--- a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts
+++ b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts
@@ -29,6 +29,7 @@ import { SetStandardApplicationNotUninstallableCommand } from 'src/database/comm
import { WorkspaceCustomApplicationIdNonNullableCommand } from 'src/database/commands/upgrade-version-command/1-12/1-12-workspace-custom-application-id-non-nullable-migration.command';
import { BackfillPageLayoutUniversalIdentifiersCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-backfill-page-layout-universal-identifiers.command';
import { DeduplicateRoleTargetsCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-deduplicate-role-targets.command';
+import { MigrateStandardInvalidEntitiesCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-migrate-standard-invalid-entities.command';
import { UpdateRoleTargetsUniqueConstraintMigrationCommand } from 'src/database/commands/upgrade-version-command/1-13/1-13-update-role-targets-unique-constraint-migration.command';
import { FixLabelIdentifierPositionAndVisibilityCommand } from 'src/database/commands/upgrade-version-command/1-6/1-6-fix-label-identifier-position-and-visibility.command';
import { BackfillWorkflowManualTriggerAvailabilityCommand } from 'src/database/commands/upgrade-version-command/1-7/1-7-backfill-workflow-manual-trigger-availability.command';
@@ -100,6 +101,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
protected readonly deduplicateRoleTargetsCommand: DeduplicateRoleTargetsCommand,
protected readonly updateRoleTargetsUniqueConstraintMigrationCommand: UpdateRoleTargetsUniqueConstraintMigrationCommand,
protected readonly backfillPageLayoutUniversalIdentifiersCommand: BackfillPageLayoutUniversalIdentifiersCommand,
+ protected readonly migrateStandardInvalidEntitiesCommand: MigrateStandardInvalidEntitiesCommand,
) {
super(
workspaceRepository,
@@ -172,6 +174,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
this.deduplicateRoleTargetsCommand,
this.updateRoleTargetsUniqueConstraintMigrationCommand,
this.backfillPageLayoutUniversalIdentifiersCommand,
+ this.migrateStandardInvalidEntitiesCommand,
],
afterSyncMetadata: [],
};
diff --git a/packages/twenty-server/src/engine/core-modules/messaging/services/timeline-messaging.service.ts b/packages/twenty-server/src/engine/core-modules/messaging/services/timeline-messaging.service.ts
index a6c86d149d..adbd2bfa2b 100644
--- a/packages/twenty-server/src/engine/core-modules/messaging/services/timeline-messaging.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/messaging/services/timeline-messaging.service.ts
@@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common';
import { In } from 'typeorm';
+import { MessageParticipantRole } from 'twenty-shared/types';
import { type TimelineThreadDTO } from 'src/engine/core-modules/messaging/dtos/timeline-thread.dto';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
@@ -115,7 +116,9 @@ export class TimelineMessagingService {
.where('message.messageThreadId = ANY(:messageThreadIds)', {
messageThreadIds,
})
- .andWhere('messageParticipant.role = :role', { role: 'from' })
+ .andWhere('messageParticipant.role = :role', {
+ role: MessageParticipantRole.FROM,
+ })
.orderBy('message.messageThreadId')
.distinctOn(['message.messageThreadId', 'messageParticipant.handle'])
.getMany();
diff --git a/packages/twenty-server/src/engine/core-modules/messaging/utils/filter-active-participants.util.ts b/packages/twenty-server/src/engine/core-modules/messaging/utils/filter-active-participants.util.ts
index 2bd414f838..0d624a0a21 100644
--- a/packages/twenty-server/src/engine/core-modules/messaging/utils/filter-active-participants.util.ts
+++ b/packages/twenty-server/src/engine/core-modules/messaging/utils/filter-active-participants.util.ts
@@ -1,7 +1,11 @@
+import { MessageParticipantRole } from 'twenty-shared/types';
+
import { type MessageParticipantWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-participant.workspace-entity';
export const filterActiveParticipants = (
participants: MessageParticipantWorkspaceEntity[],
): MessageParticipantWorkspaceEntity[] => {
- return participants.filter((participant) => participant.role === 'from');
+ return participants.filter(
+ (participant) => participant.role === MessageParticipantRole.FROM,
+ );
};
diff --git a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/from-create-agent-input-to-flat-agent.util.ts b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/from-create-agent-input-to-flat-agent.util.ts
index 5a38cbd6c8..7a6fcadc50 100644
--- a/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/from-create-agent-input-to-flat-agent.util.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/ai/ai-agent/utils/from-create-agent-input-to-flat-agent.util.ts
@@ -1,10 +1,10 @@
import { isNonEmptyString } from '@sniptt/guards';
+import { computeMetadataNameFromLabel } from 'twenty-shared/metadata';
import {
isDefined,
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
} from 'twenty-shared/utils';
import { v4 } from 'uuid';
-import { computeMetadataNameFromLabel } from 'twenty-shared/metadata';
import { type CreateAgentInput } from 'src/engine/metadata-modules/ai/ai-agent/dtos/create-agent.input';
import { type FlatAgent } from 'src/engine/metadata-modules/flat-agent/types/flat-agent.type';
@@ -48,7 +48,7 @@ export const fromCreateAgentInputToFlatAgent = ({
standardId,
name: isNonEmptyString(createAgentInput.name)
? createAgentInput.name
- : computeMetadataNameFromLabel(createAgentInput.label),
+ : computeMetadataNameFromLabel({ label: createAgentInput.label }),
label: createAgentInput.label,
icon: createAgentInput.icon ?? null,
description: createAgentInput.description ?? null,
diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts
index feb781313c..1e626ea5f2 100644
--- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata.service.ts
@@ -181,6 +181,7 @@ export class FieldMetadataService extends TypeOrmQueryService;
// Note: Standard override is way too complex we should land a smoother implemenentation once we standardize
// them across every flat entities
@@ -34,11 +35,13 @@ export const computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate = ({
rawUpdateFieldInput,
flatFieldMetadataMaps,
flatObjectMetadata,
+ isSystemBuild,
}: ComputeFlatFieldToUpdateAndRelatedFlatFieldToUpdateArgs): ComputeFlatFieldToUpdateAndRelatedFlatFieldToUpdateReturnType => {
const { standardOverrides, updatedEditableFieldProperties } =
sanitizeRawUpdateFieldInput({
existingFlatFieldMetadata: fromFlatFieldMetadata,
rawUpdateFieldInput,
+ isSystemBuild,
});
const isStandardField = isStandardMetadata(fromFlatFieldMetadata);
@@ -48,7 +51,7 @@ export const computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate = ({
existing: fromFlatFieldMetadata,
properties:
FLAT_FIELD_METADATA_EDITABLE_PROPERTIES[
- isStandardField ? 'standard' : 'custom'
+ isStandardField && !isSystemBuild ? 'standard' : 'custom'
],
update: updatedEditableFieldProperties,
}),
@@ -104,7 +107,7 @@ export const computeFlatFieldToUpdateAndRelatedFlatFieldToUpdate = ({
>((relatedFlatFieldMetadataFrom) => {
const relatedMorphPropertiesToUpdateTo =
FLAT_FIELD_METADATA_MORPH_RELATION_EDITABLE_PROPERTIES_ON_SIBLING_MORPH_RELATION_UPDATE_CONSTANT[
- isStandardField ? 'standard' : 'custom'
+ isStandardField && !isSystemBuild ? 'standard' : 'custom'
];
const relatedFlatFieldMetadataTo = {
...mergeUpdateInExistingRecord({
diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-update-field-input-to-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-update-field-input-to-flat-field-metadata.util.ts
index 388ada54cf..a8ff395b02 100644
--- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-update-field-input-to-flat-field-metadata.util.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/from-update-field-input-to-flat-field-metadata.util.ts
@@ -24,6 +24,7 @@ import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-fiel
type FromUpdateFieldInputToFlatFieldMetadataArgs = {
updateFieldInput: UpdateFieldInput;
workspaceCustomApplicationId: string;
+ isSystemBuild: boolean;
} & Pick<
AllFlatEntityMaps,
| 'flatObjectMetadataMaps'
@@ -49,6 +50,7 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
flatViewGroupMaps,
flatViewMaps,
flatViewFieldMaps,
+ isSystemBuild,
}: FromUpdateFieldInputToFlatFieldMetadataArgs): FieldInputTranspilationResult => {
const updateFieldInputInformalProperties =
extractAndSanitizeObjectStringFields(rawUpdateFieldInput, [
@@ -98,6 +100,7 @@ export const fromUpdateFieldInputToFlatFieldMetadata = ({
flatObjectMetadata,
fromFlatFieldMetadata: existingFlatFieldMetadataToUpdate,
rawUpdateFieldInput,
+ isSystemBuild,
});
const { flatFieldMetadatasToCreate, flatIndexMetadatasToCreate } =
diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util.ts
index 7b9b085811..fbec150898 100644
--- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/generate-morph-or-relation-flat-field-metadata-pair.util.ts
@@ -1,10 +1,10 @@
+import { computeMetadataNameFromLabel } from 'twenty-shared/metadata';
import {
FieldMetadataType,
RelationOnDeleteAction,
RelationType,
} from 'twenty-shared/types';
import { v4 } from 'uuid';
-import { computeMetadataNameFromLabel } from 'twenty-shared/metadata';
import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
import { type MorphOrRelationFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/types/morph-or-relation-field-metadata-type.type';
@@ -100,7 +100,9 @@ export const generateMorphOrRelationFlatFieldMetadataPair = ({
label: relationCreationPayload.targetFieldLabel,
name:
targetFieldName ??
- computeMetadataNameFromLabel(relationCreationPayload.targetFieldLabel),
+ computeMetadataNameFromLabel({
+ label: relationCreationPayload.targetFieldLabel,
+ }),
objectMetadataId: targetFlatObjectMetadata.id,
type: FieldMetadataType.RELATION,
workspaceId,
diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-name-synced-with-label.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-name-synced-with-label.util.ts
index 884006091d..4279f0855b 100644
--- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-name-synced-with-label.util.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-name-synced-with-label.util.ts
@@ -2,13 +2,20 @@ import { computeMetadataNameFromLabel } from 'twenty-shared/metadata';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
-export const isFlatFieldMetadataNameSyncedWithLabel = (
+export const isFlatFieldMetadataNameSyncedWithLabel = ({
+ flatFieldMetadata,
+ isSystemBuild,
+}: {
flatFieldMetadata: Pick<
FlatFieldMetadata,
'name' | 'isLabelSyncedWithName' | 'label'
- >,
-) => {
- const computedName = computeMetadataNameFromLabel(flatFieldMetadata.label);
+ >;
+ isSystemBuild: boolean;
+}) => {
+ const computedName = computeMetadataNameFromLabel({
+ label: flatFieldMetadata.label,
+ applyCustomSuffix: !isSystemBuild,
+ });
return flatFieldMetadata.name === computedName;
};
diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/sanitize-raw-update-field-input.ts b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/sanitize-raw-update-field-input.ts
index 1a5da3b5c8..a2cdd5a2a0 100644
--- a/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/sanitize-raw-update-field-input.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/flat-field-metadata/utils/sanitize-raw-update-field-input.ts
@@ -18,10 +18,12 @@ import { isStandardMetadata } from 'src/engine/metadata-modules/utils/is-standar
type SanitizeRawUpdateFieldInputArgs = {
rawUpdateFieldInput: UpdateFieldInput;
existingFlatFieldMetadata: FlatFieldMetadata;
+ isSystemBuild: boolean;
};
export const sanitizeRawUpdateFieldInput = ({
existingFlatFieldMetadata,
rawUpdateFieldInput,
+ isSystemBuild,
}: SanitizeRawUpdateFieldInputArgs) => {
const isStandardField = isStandardMetadata(existingFlatFieldMetadata);
const updatedEditableFieldProperties = extractAndSanitizeObjectStringFields(
@@ -43,7 +45,7 @@ export const sanitizeRawUpdateFieldInput = ({
...option,
}));
- if (!isStandardField) {
+ if (!isStandardField || isSystemBuild) {
return {
updatedEditableFieldProperties,
standardOverrides: null,
diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/are-flat-object-metadata-names-synced-with-labels.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/are-flat-object-metadata-names-synced-with-labels.util.ts
index fd62de4f88..1785eeceef 100644
--- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/are-flat-object-metadata-names-synced-with-labels.util.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/utils/are-flat-object-metadata-names-synced-with-labels.util.ts
@@ -2,16 +2,25 @@ import { computeMetadataNameFromLabel } from 'twenty-shared/metadata';
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
-export const areFlatObjectMetadataNamesSyncedWithLabels = (
+export const areFlatObjectMetadataNamesSyncedWithLabels = ({
+ flatObjectdMetadata,
+ isSystemBuild,
+}: {
+ isSystemBuild: boolean;
flatObjectdMetadata: Pick<
FlatObjectMetadata,
'namePlural' | 'nameSingular' | 'labelPlural' | 'labelSingular'
- >,
-) => {
+ >;
+}) => {
const [computedSingularName, computedPluralName] = [
flatObjectdMetadata.labelSingular,
flatObjectdMetadata.labelPlural,
- ].map(computeMetadataNameFromLabel);
+ ].map((label) =>
+ computeMetadataNameFromLabel({
+ label,
+ applyCustomSuffix: !isSystemBuild,
+ }),
+ );
return (
flatObjectdMetadata.nameSingular === computedSingularName &&
diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-name-and-labels.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-name-and-labels.util.ts
index db087b92dd..ff6097a913 100644
--- a/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-name-and-labels.util.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-name-and-labels.util.ts
@@ -8,6 +8,7 @@ import { areFlatObjectMetadataNamesSyncedWithLabels } from 'src/engine/metadata-
import { validateFlatObjectMetadataLabel } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-label.util';
import { validateFlatObjectMetadataNames } from 'src/engine/metadata-modules/flat-object-metadata/validators/utils/validate-flat-object-metadata-name.util';
import { ObjectMetadataExceptionCode } from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
+import { type WorkspaceMigrationBuilderOptions } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-builder-options.type';
type ValidateNoOtherObjectWithSameNameExistsOrThrowsParams = {
objectMetadataNameSingular: string;
@@ -36,9 +37,11 @@ export const doesOtherObjectWithSameNameExists = ({
export const validateFlatObjectMetadataNameAndLabels = ({
optimisticFlatObjectMetadataMaps,
flatObjectMetadataToValidate,
+ buildOptions,
}: {
flatObjectMetadataToValidate: FlatObjectMetadata;
optimisticFlatObjectMetadataMaps: FlatEntityMaps;
+ buildOptions: WorkspaceMigrationBuilderOptions;
}): FlatObjectMetadataValidationError[] => {
const errors: FlatObjectMetadataValidationError[] = [];
@@ -58,7 +61,10 @@ export const validateFlatObjectMetadataNameAndLabels = ({
if (
flatObjectMetadataToValidate.isLabelSyncedWithName &&
- !areFlatObjectMetadataNamesSyncedWithLabels(flatObjectMetadataToValidate)
+ !areFlatObjectMetadataNamesSyncedWithLabels({
+ flatObjectdMetadata: flatObjectMetadataToValidate,
+ isSystemBuild: buildOptions.isSystemBuild,
+ })
) {
errors.push({
code: ObjectMetadataExceptionCode.INVALID_OBJECT_INPUT,
diff --git a/packages/twenty-server/src/engine/metadata-modules/utils/compute-metadata-name-from-label-or-throw.util.ts b/packages/twenty-server/src/engine/metadata-modules/utils/compute-metadata-name-from-label-or-throw.util.ts
index e4f5405e7d..14fccce18d 100644
--- a/packages/twenty-server/src/engine/metadata-modules/utils/compute-metadata-name-from-label-or-throw.util.ts
+++ b/packages/twenty-server/src/engine/metadata-modules/utils/compute-metadata-name-from-label-or-throw.util.ts
@@ -17,7 +17,7 @@ export const computeMetadataNameFromLabelOrThrow = (label: string): string => {
}
try {
- return computeMetadataNameFromLabelCore(label);
+ return computeMetadataNameFromLabelCore({ label });
} catch (error) {
if (error instanceof Error) {
throw new InvalidMetadataException(
diff --git a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts
index 410832b64d..2ec99939c6 100644
--- a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts
+++ b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-field.decorator.ts
@@ -82,7 +82,8 @@ export function WorkspaceField(
generateDefaultValue(options.type)) as FieldMetadataDefaultValue | null;
const name = propertyKey.toString();
const label = options.label.message ?? '';
- const isLabelSyncedWithName = computeMetadataNameFromLabel(label) === name;
+ const isLabelSyncedWithName =
+ computeMetadataNameFromLabel({ label }) === name;
metadataArgsStorage.addFields({
target: object.constructor,
diff --git a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-relation.decorator.ts b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-relation.decorator.ts
index f58e20abe7..a8f68ebcb4 100644
--- a/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-relation.decorator.ts
+++ b/packages/twenty-server/src/engine/twenty-orm/decorators/workspace-relation.decorator.ts
@@ -1,9 +1,9 @@
import { type MessageDescriptor } from '@lingui/core';
import { isDefined, isUUID } from 'class-validator';
+import { computeMetadataNameFromLabel } from 'twenty-shared/metadata';
+import { type RelationOnDeleteAction } from 'twenty-shared/types';
import { CustomError } from 'twenty-shared/utils';
import { type ObjectType } from 'typeorm';
-import { type RelationOnDeleteAction } from 'twenty-shared/types';
-import { computeMetadataNameFromLabel } from 'twenty-shared/metadata';
import { type RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
@@ -88,7 +88,8 @@ export function WorkspaceRelation(
);
const name = propertyKey.toString();
const label = options.label.message ?? '';
- const isLabelSyncedWithName = computeMetadataNameFromLabel(label) === name;
+ const isLabelSyncedWithName =
+ computeMetadataNameFromLabel({ label }) === name;
if (options.isMorphRelation && !isDefined(options.morphId)) {
throw new CustomError(
diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-data-seeds.constant.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-data-seeds.constant.ts
index 2d1016380f..4681fe2180 100644
--- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-data-seeds.constant.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/calendar-event-data-seeds.constant.ts
@@ -9,7 +9,7 @@ type CalendarEventDataSeed = {
externalUpdatedAt: string;
description: string;
location: string;
- iCalUID: string;
+ iCalUid: string;
conferenceSolution: string;
conferenceLinkPrimaryLinkLabel: string;
conferenceLinkPrimaryLinkUrl: string;
@@ -27,7 +27,7 @@ export const CALENDAR_EVENT_DATA_SEED_COLUMNS: (keyof CalendarEventDataSeed)[] =
'externalUpdatedAt',
'description',
'location',
- 'iCalUID',
+ 'iCalUid',
'conferenceSolution',
'conferenceLinkPrimaryLinkLabel',
'conferenceLinkPrimaryLinkUrl',
@@ -217,7 +217,7 @@ const GENERATE_CALENDAR_EVENT_SEEDS = (): CalendarEventDataSeed[] => {
).toISOString(),
description: TEMPLATE.description,
location: LOCATION,
- iCalUID: `event${INDEX}@calendar.twentycrm.com`,
+ iCalUid: `event${INDEX}@calendar.twentycrm.com`,
conferenceSolution: CONFERENCE_SOLUTION,
conferenceLinkPrimaryLinkLabel: CONFERENCE_LINK,
conferenceLinkPrimaryLinkUrl: CONFERENCE_LINK,
diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/message-channel-data-seeds.constant.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/message-channel-data-seeds.constant.ts
index 1d1e1dd0b2..3b7dd728a1 100644
--- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/message-channel-data-seeds.constant.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/message-channel-data-seeds.constant.ts
@@ -1,6 +1,7 @@
import { CONNECTED_ACCOUNT_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/connected-account-data-seeds.constant';
import {
MessageChannelSyncStage,
+ MessageChannelType,
MessageChannelVisibility,
} from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
@@ -10,7 +11,7 @@ type MessageChannelDataSeed = {
updatedAt: Date;
deletedAt: Date | null;
isContactAutoCreationEnabled: boolean;
- type: string;
+ type: MessageChannelType;
connectedAccountId: string;
handle: string;
isSyncEnabled: boolean;
@@ -54,7 +55,7 @@ export const MESSAGE_CHANNEL_DATA_SEEDS: MessageChannelDataSeed[] = [
updatedAt: new Date(),
deletedAt: null,
isContactAutoCreationEnabled: true,
- type: 'email',
+ type: MessageChannelType.EMAIL,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.TIM,
handle: 'tim@apple.dev',
isSyncEnabled: true,
@@ -67,7 +68,7 @@ export const MESSAGE_CHANNEL_DATA_SEEDS: MessageChannelDataSeed[] = [
updatedAt: new Date(),
deletedAt: null,
isContactAutoCreationEnabled: true,
- type: 'email',
+ type: MessageChannelType.EMAIL,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.JONY,
handle: 'jony.ive@apple.dev',
isSyncEnabled: true,
@@ -80,7 +81,7 @@ export const MESSAGE_CHANNEL_DATA_SEEDS: MessageChannelDataSeed[] = [
updatedAt: new Date(),
deletedAt: null,
isContactAutoCreationEnabled: true,
- type: 'email',
+ type: MessageChannelType.EMAIL,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.PHIL,
handle: 'phil.schiler@apple.dev',
isSyncEnabled: true,
@@ -93,7 +94,7 @@ export const MESSAGE_CHANNEL_DATA_SEEDS: MessageChannelDataSeed[] = [
updatedAt: new Date(),
deletedAt: null,
isContactAutoCreationEnabled: true,
- type: 'email',
+ type: MessageChannelType.EMAIL,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.TIM, // Use TIM's connected account for shared inbox
handle: 'support@apple.dev',
isSyncEnabled: true,
@@ -106,7 +107,7 @@ export const MESSAGE_CHANNEL_DATA_SEEDS: MessageChannelDataSeed[] = [
updatedAt: new Date(),
deletedAt: null,
isContactAutoCreationEnabled: true,
- type: 'email',
+ type: MessageChannelType.EMAIL,
connectedAccountId: CONNECTED_ACCOUNT_DATA_SEED_IDS.TIM, // Use TIM's connected account for shared inbox
handle: 'sales@apple.dev',
isSyncEnabled: true,
diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/message-participant-data-seeds.constant.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/message-participant-data-seeds.constant.ts
index b6aea1e228..ce702d879c 100644
--- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/message-participant-data-seeds.constant.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/message-participant-data-seeds.constant.ts
@@ -1,3 +1,5 @@
+import { MessageParticipantRole } from 'twenty-shared/types';
+
import { MESSAGE_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/message-data-seeds.constant';
import { PERSON_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/person-data-seeds.constant';
import {
@@ -14,7 +16,7 @@ export type MessageParticipantDataSeed = {
personId: string;
displayName: string;
handle: string;
- role: string;
+ role: MessageParticipantRole;
messageId: string;
};
@@ -226,7 +228,9 @@ const CREATE_MESSAGE_PARTICIPANTS = (
for (let I = 0; I < TOTAL_PARTICIPANTS; I++) {
const IS_SENDER = I === 0;
- const ROLE = IS_SENDER ? 'from' : 'to';
+ const ROLE = IS_SENDER
+ ? MessageParticipantRole.FROM
+ : MessageParticipantRole.TO;
const HANDLE = IS_SENDER ? 'outgoing' : 'incoming';
// Random date within the last 3 months
diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant.ts
index 839aeab20c..d05a1e327a 100644
--- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/constants/standard-object.constant.ts
@@ -271,8 +271,8 @@ export const STANDARD_OBJECTS = {
location: {
universalIdentifier: CALENDAR_EVENT_STANDARD_FIELD_IDS.location,
},
- iCalUID: {
- universalIdentifier: CALENDAR_EVENT_STANDARD_FIELD_IDS.iCalUID,
+ iCalUid: {
+ universalIdentifier: CALENDAR_EVENT_STANDARD_FIELD_IDS.iCalUid,
},
conferenceSolution: {
universalIdentifier:
diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-calendar-event-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-calendar-event-standard-flat-field-metadata.util.ts
index 09b5e893d0..3f8d1a5761 100644
--- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-calendar-event-standard-flat-field-metadata.util.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-calendar-event-standard-flat-field-metadata.util.ts
@@ -210,11 +210,11 @@ export const buildCalendarEventStandardFlatFieldMetadatas = ({
},
standardFieldMetadataIdByObjectAndFieldName,
}),
- iCalUID: createStandardFieldFlatMetadata({
+ iCalUid: createStandardFieldFlatMetadata({
objectName: 'calendarEvent',
workspaceId,
options: {
- fieldName: 'iCalUID',
+ fieldName: 'iCalUid',
type: FieldMetadataType.TEXT,
label: 'iCal UID',
description: 'iCal UID',
diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-message-channel-message-association-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-message-channel-message-association-standard-flat-field-metadata.util.ts
index 065b7fc882..aa4e00617b 100644
--- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-message-channel-message-association-standard-flat-field-metadata.util.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-message-channel-message-association-standard-flat-field-metadata.util.ts
@@ -9,6 +9,7 @@ import { type AllStandardObjectFieldName } from 'src/engine/workspace-manager/tw
import { createStandardFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/create-standard-relation-field-flat-metadata.util';
import { type StandardFieldMetadataIdByObjectAndFieldName } from 'src/engine/workspace-manager/twenty-standard-application/utils/get-standard-field-metadata-id-by-object-and-field-name.util';
+import { MessageDirection } from 'src/modules/messaging/common/enums/message-direction.enum';
export const buildMessageChannelMessageAssociationStandardFlatFieldMetadatas =
({
@@ -128,10 +129,20 @@ export const buildMessageChannelMessageAssociationStandardFlatFieldMetadatas =
description: 'Message Direction',
icon: 'IconDirection',
isNullable: false,
- defaultValue: "'incoming'",
+ defaultValue: `'${MessageDirection.INCOMING}'`,
options: [
- { value: 'incoming', label: 'Incoming', position: 0, color: 'green' },
- { value: 'outgoing', label: 'Outgoing', position: 1, color: 'blue' },
+ {
+ value: MessageDirection.INCOMING,
+ label: 'Incoming',
+ position: 0,
+ color: 'green',
+ },
+ {
+ value: MessageDirection.OUTGOING,
+ label: 'Outgoing',
+ position: 1,
+ color: 'blue',
+ },
],
createdAt,
},
diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-message-channel-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-message-channel-standard-flat-field-metadata.util.ts
index f9e638577b..f60c92641e 100644
--- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-message-channel-standard-flat-field-metadata.util.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-message-channel-standard-flat-field-metadata.util.ts
@@ -9,6 +9,7 @@ import { type AllStandardObjectFieldName } from 'src/engine/workspace-manager/tw
import { createStandardFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/create-standard-relation-field-flat-metadata.util';
import { type StandardFieldMetadataIdByObjectAndFieldName } from 'src/engine/workspace-manager/twenty-standard-application/utils/get-standard-field-metadata-id-by-object-and-field-name.util';
+import { MessageChannelType } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
export const buildMessageChannelStandardFlatFieldMetadatas = ({
createdAt,
@@ -138,10 +139,20 @@ export const buildMessageChannelStandardFlatFieldMetadatas = ({
description: 'Channel Type',
icon: 'IconMessage',
isNullable: false,
- defaultValue: "'email'",
+ defaultValue: `'${MessageChannelType.EMAIL}'`,
options: [
- { value: 'email', label: 'Email', position: 0, color: 'green' },
- { value: 'sms', label: 'SMS', position: 1, color: 'blue' },
+ {
+ value: MessageChannelType.EMAIL,
+ label: 'Email',
+ position: 0,
+ color: 'green',
+ },
+ {
+ value: MessageChannelType.SMS,
+ label: 'SMS',
+ position: 1,
+ color: 'blue',
+ },
],
createdAt,
},
diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-message-participant-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-message-participant-standard-flat-field-metadata.util.ts
index 83ebc1671f..8a787aa646 100644
--- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-message-participant-standard-flat-field-metadata.util.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-message-participant-standard-flat-field-metadata.util.ts
@@ -1,5 +1,6 @@
import {
FieldMetadataType,
+ MessageParticipantRole,
RelationOnDeleteAction,
RelationType,
} from 'twenty-shared/types';
@@ -99,12 +100,32 @@ export const buildMessageParticipantStandardFlatFieldMetadatas = ({
description: 'Role',
icon: 'IconAt',
isNullable: false,
- defaultValue: "'from'",
+ defaultValue: `'${MessageParticipantRole.FROM}'`,
options: [
- { value: 'from', label: 'From', position: 0, color: 'green' },
- { value: 'to', label: 'To', position: 1, color: 'blue' },
- { value: 'cc', label: 'Cc', position: 2, color: 'orange' },
- { value: 'bcc', label: 'Bcc', position: 3, color: 'red' },
+ {
+ value: MessageParticipantRole.FROM,
+ label: 'From',
+ position: 0,
+ color: 'green',
+ },
+ {
+ value: MessageParticipantRole.TO,
+ label: 'To',
+ position: 1,
+ color: 'blue',
+ },
+ {
+ value: MessageParticipantRole.CC,
+ label: 'Cc',
+ position: 2,
+ color: 'orange',
+ },
+ {
+ value: MessageParticipantRole.BCC,
+ label: 'Bcc',
+ position: 3,
+ color: 'red',
+ },
],
createdAt,
},
diff --git a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-workspace-member-standard-flat-field-metadata.util.ts b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-workspace-member-standard-flat-field-metadata.util.ts
index ae310b2d09..2a4d952e03 100644
--- a/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-workspace-member-standard-flat-field-metadata.util.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/twenty-standard-application/utils/compute-workspace-member-standard-flat-field-metadata.util.ts
@@ -9,6 +9,7 @@ import { type AllStandardObjectFieldName } from 'src/engine/workspace-manager/tw
import { createStandardFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/create-standard-field-flat-metadata.util';
import { createStandardRelationFieldFlatMetadata } from 'src/engine/workspace-manager/twenty-standard-application/utils/create-standard-relation-field-flat-metadata.util';
import { type StandardFieldMetadataIdByObjectAndFieldName } from 'src/engine/workspace-manager/twenty-standard-application/utils/get-standard-field-metadata-id-by-object-and-field-name.util';
+import { WorkspaceMemberNumberFormatEnum } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
export const buildWorkspaceMemberStandardFlatFieldMetadatas = ({
createdAt,
@@ -296,30 +297,35 @@ export const buildWorkspaceMemberStandardFlatFieldMetadatas = ({
icon: 'IconNumbers',
isSystem: true,
isNullable: false,
- defaultValue: "'SYSTEM'",
+ defaultValue: `'${WorkspaceMemberNumberFormatEnum.SYSTEM}'`,
options: [
- { value: 'SYSTEM', label: 'System', position: 0, color: 'turquoise' },
{
- value: 'COMMAS_AND_DOT',
- label: 'Commas and dot (1,234.56)',
+ value: WorkspaceMemberNumberFormatEnum.SYSTEM,
+ label: 'System',
+ position: 0,
+ color: 'turquoise',
+ },
+ {
+ value: WorkspaceMemberNumberFormatEnum.COMMAS_AND_DOT,
+ label: 'Commas and dot',
position: 1,
color: 'blue',
},
{
- value: 'SPACES_AND_COMMA',
- label: 'Spaces and comma (1 234,56)',
+ value: WorkspaceMemberNumberFormatEnum.SPACES_AND_COMMA,
+ label: 'Spaces and comma',
position: 2,
color: 'green',
},
{
- value: 'DOTS_AND_COMMA',
- label: 'Dots and comma (1.234,56)',
+ value: WorkspaceMemberNumberFormatEnum.DOTS_AND_COMMA,
+ label: 'Dots and comma',
position: 3,
color: 'orange',
},
{
- value: 'APOSTROPHE_AND_DOT',
- label: "Apostrophe and dot (1'234.56)",
+ value: WorkspaceMemberNumberFormatEnum.APOSTROPHE_AND_DOT,
+ label: 'Apostrophe and dot',
position: 4,
color: 'purple',
},
diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-field-metadata-validator.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-field-metadata-validator.service.ts
index 5a230abcb3..6023ca3d9b 100644
--- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-field-metadata-validator.service.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-field-metadata-validator.service.ts
@@ -146,7 +146,10 @@ export class FlatFieldMetadataValidatorService {
if (
flatFieldMetadataToValidate.isLabelSyncedWithName &&
- !isFlatFieldMetadataNameSyncedWithLabel(flatFieldMetadataToValidate)
+ !isFlatFieldMetadataNameSyncedWithLabel({
+ flatFieldMetadata: flatFieldMetadataToValidate,
+ isSystemBuild: buildOptions.isSystemBuild,
+ })
) {
validationResult.errors.push({
code: FieldMetadataExceptionCode.FIELD_MUTATION_NOT_ALLOWED,
@@ -318,7 +321,10 @@ export class FlatFieldMetadataValidatorService {
if (
flatFieldMetadataToValidate.isLabelSyncedWithName &&
- !isFlatFieldMetadataNameSyncedWithLabel(flatFieldMetadataToValidate)
+ !isFlatFieldMetadataNameSyncedWithLabel({
+ flatFieldMetadata: flatFieldMetadataToValidate,
+ isSystemBuild: buildOptions.isSystemBuild,
+ })
) {
validationResult.errors.push({
code: FieldMetadataExceptionCode.NAME_NOT_SYNCED_WITH_LABEL,
diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-object-metadata-validator.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-object-metadata-validator.service.ts
index f0d15a9efc..1a9cc78ae4 100644
--- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-object-metadata-validator.service.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/validators/services/flat-object-metadata-validator.service.ts
@@ -65,6 +65,7 @@ export class FlatObjectMetadataValidatorService {
...validateFlatObjectMetadataNameAndLabels({
optimisticFlatObjectMetadataMaps,
flatObjectMetadataToValidate: updatedFlatObjectMetadata,
+ buildOptions,
}),
);
@@ -202,6 +203,7 @@ export class FlatObjectMetadataValidatorService {
...validateFlatObjectMetadataNameAndLabels({
optimisticFlatObjectMetadataMaps,
flatObjectMetadataToValidate,
+ buildOptions,
}),
);
diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids.ts
index 4fda5be8fc..12e5cf20c1 100644
--- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids.ts
+++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids.ts
@@ -102,7 +102,7 @@ export const CALENDAR_EVENT_STANDARD_FIELD_IDS = {
externalUpdatedAt: '20202020-b355-4c18-8825-ef42c8a5a755',
description: '20202020-52c4-4266-a98f-e90af0b4d271',
location: '20202020-641a-4ffe-960d-c3c186d95b17',
- iCalUID: '20202020-f24b-45f4-b6a3-d2f9fcb98714',
+ iCalUid: '20202020-f24b-45f4-b6a3-d2f9fcb98714',
conferenceSolution: '20202020-1c3f-4b5a-b526-5411a82179eb',
conferenceLink: '20202020-35da-43ef-9ca0-e936e9dc237b',
calendarChannelEventAssociations: '20202020-bdf8-4572-a2cc-ecbb6bcc3a02',
diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/caldav/lib/caldav.client.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/caldav/lib/caldav.client.ts
index 6299f85743..3aff9b2197 100644
--- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/caldav/lib/caldav.client.ts
+++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/caldav/lib/caldav.client.ts
@@ -272,7 +272,7 @@ export class CalDAVClient {
return {
id: objectUrl,
title: event.summary || 'Untitled Event',
- iCalUID: event.uid || '',
+ iCalUid: event.uid || '',
description: event.description || '',
startsAt: event.start.toISOString(),
endsAt: event.end.toISOString(),
diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/utils/__tests__/format-google-calendar-event.util.spec.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/utils/__tests__/format-google-calendar-event.util.spec.ts
index e03542029d..ca06a8a365 100644
--- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/utils/__tests__/format-google-calendar-event.util.spec.ts
+++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/utils/__tests__/format-google-calendar-event.util.spec.ts
@@ -120,6 +120,6 @@ describe('formatGoogleCalendarEvents', () => {
mockGoogleEventWithImproperUcalid,
]);
- expect(result[0].iCalUID).toBe(context.expected);
+ expect(result[0].iCalUid).toBe(context.expected);
});
});
diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/utils/format-google-calendar-event.util.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/utils/format-google-calendar-event.util.ts
index 8a47e446b9..2fdd5ee715 100644
--- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/utils/format-google-calendar-event.util.ts
+++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/google-calendar/utils/format-google-calendar-event.util.ts
@@ -38,7 +38,7 @@ const formatGoogleCalendarEvent = (
externalUpdatedAt: event.updated ?? '',
description: event.description ?? '',
location: event.location ?? '',
- iCalUID: event.iCalUID ?? '',
+ iCalUid: event.iCalUID ?? '',
conferenceSolution:
event.conferenceData?.conferenceSolution?.key?.type ?? '',
conferenceLinkLabel: event.conferenceData?.entryPoints?.[0]?.uri ?? '',
@@ -63,7 +63,7 @@ const formatGoogleCalendarEvent = (
'externalUpdatedAt',
'description',
'location',
- 'iCalUID',
+ 'iCalUid',
'conferenceSolution',
'conferenceLinkLabel',
'conferenceLinkUrl',
diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/__tests__/format-microsoft-calendar-event.util.spec.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/__tests__/format-microsoft-calendar-event.util.spec.ts
index 516fa38f8d..fa917da63e 100644
--- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/__tests__/format-microsoft-calendar-event.util.spec.ts
+++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/__tests__/format-microsoft-calendar-event.util.spec.ts
@@ -100,7 +100,7 @@ describe('formatMicrosoftCalendarEvents', () => {
mockMicrosoftEventWithImproperData2,
]);
- expect(result[0].iCalUID).toBe('eventStrange@microsoft.com');
- expect(result[1].iCalUID).toBe('>\u0015-;_�^�W&�p\u001f�');
+ expect(result[0].iCalUid).toBe('eventStrange@microsoft.com');
+ expect(result[1].iCalUid).toBe('>\u0015-;_�^�W&�p\u001f�');
});
});
diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/format-microsoft-calendar-event.util.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/format-microsoft-calendar-event.util.ts
index 3a55f9f777..fb737b2fdf 100644
--- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/format-microsoft-calendar-event.util.ts
+++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/drivers/microsoft-calendar/utils/format-microsoft-calendar-event.util.ts
@@ -42,7 +42,7 @@ const formatMicrosoftCalendarEvent = (event: Event): FetchedCalendarEvent => {
externalUpdatedAt: event.lastModifiedDateTime ?? '',
description: event.body?.content ?? '',
location: event.location?.displayName ?? '',
- iCalUID: event.iCalUId ?? '',
+ iCalUid: event.iCalUId ?? '',
conferenceSolution: event.onlineMeetingProvider ?? '',
conferenceLinkLabel: event.onlineMeeting?.joinUrl ?? '',
conferenceLinkUrl: event.onlineMeeting?.joinUrl ?? '',
@@ -66,7 +66,7 @@ const formatMicrosoftCalendarEvent = (event: Event): FetchedCalendarEvent => {
'externalUpdatedAt',
'description',
'location',
- 'iCalUID',
+ 'iCalUid',
'conferenceSolution',
'conferenceLinkLabel',
'conferenceLinkUrl',
diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-save-events.service.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-save-events.service.ts
index 1f83c1e544..3cb8e2cf0b 100644
--- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-save-events.service.ts
+++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/services/calendar-save-events.service.ts
@@ -44,21 +44,21 @@ export class CalendarSaveEventsService {
const existingCalendarEvents = await calendarEventRepository.find({
where: {
- iCalUID: Any(
- fetchedCalendarEvents.map((event) => event.iCalUID as string),
+ iCalUid: Any(
+ fetchedCalendarEvents.map((event) => event.iCalUid as string),
),
},
});
const fetchedCalendarEventsWithDBEvents: FetchedCalendarEventWithDBEvent[] =
fetchedCalendarEvents.map((event): FetchedCalendarEventWithDBEvent => {
- const existingEventWithSameiCalUID = existingCalendarEvents.find(
- (existingEvent) => existingEvent.iCalUID === event.iCalUID,
+ const existingEventWithSameiCalUid = existingCalendarEvents.find(
+ (existingEvent) => existingEvent.iCalUid === event.iCalUid,
);
return {
fetchedCalendarEvent: event,
- existingCalendarEvent: existingEventWithSameiCalUID ?? null,
+ existingCalendarEvent: existingEventWithSameiCalUid ?? null,
newlyCreatedCalendarEvent: null,
};
});
@@ -78,7 +78,7 @@ export class CalendarSaveEventsService {
.map(
({ fetchedCalendarEvent }) =>
({
- iCalUID: fetchedCalendarEvent.iCalUID,
+ iCalUid: fetchedCalendarEvent.iCalUid,
title: fetchedCalendarEvent.title,
description: fetchedCalendarEvent.description,
startsAt: fetchedCalendarEvent.startsAt,
@@ -113,7 +113,7 @@ export class CalendarSaveEventsService {
({ fetchedCalendarEvent, existingCalendarEvent }) => {
const savedCalendarEvent = savedCalendarEvents.find(
(savedCalendarEvent) =>
- savedCalendarEvent.iCalUID === fetchedCalendarEvent.iCalUID,
+ savedCalendarEvent.iCalUid === fetchedCalendarEvent.iCalUid,
);
return {
@@ -132,13 +132,13 @@ export class CalendarSaveEventsService {
.map(({ fetchedCalendarEvent, existingCalendarEvent }) => {
if (!existingCalendarEvent) {
throw new Error(
- `Existing calendar event with iCalUID ${fetchedCalendarEvent.iCalUID} not found - should never happen`,
+ `Existing calendar event with iCalUid ${fetchedCalendarEvent.iCalUid} not found - should never happen`,
);
}
return {
id: existingCalendarEvent.id,
- iCalUID: fetchedCalendarEvent.iCalUID,
+ iCalUid: fetchedCalendarEvent.iCalUid,
title: fetchedCalendarEvent.title,
description: fetchedCalendarEvent.description,
startsAt: fetchedCalendarEvent.startsAt,
@@ -184,7 +184,7 @@ export class CalendarSaveEventsService {
if (!calendarEventId) {
throw new Error(
- `Calendar event id not found for event with iCalUID ${fetchedCalendarEvent.iCalUID} - should never happen`,
+ `Calendar event id not found for event with iCalUid ${fetchedCalendarEvent.iCalUid} - should never happen`,
);
}
@@ -213,7 +213,7 @@ export class CalendarSaveEventsService {
.flatMap(({ newlyCreatedCalendarEvent, fetchedCalendarEvent }) => {
if (!newlyCreatedCalendarEvent?.id) {
throw new Error(
- `Newly created calendar event with iCalUID ${fetchedCalendarEvent.iCalUID} not found - should never happen`,
+ `Newly created calendar event with iCalUid ${fetchedCalendarEvent.iCalUid} not found - should never happen`,
);
}
@@ -233,7 +233,7 @@ export class CalendarSaveEventsService {
.flatMap(({ fetchedCalendarEvent, existingCalendarEvent }) => {
if (!existingCalendarEvent?.id) {
throw new Error(
- `Existing calendar event with iCalUID ${fetchedCalendarEvent.iCalUID} not found - should never happen`,
+ `Existing calendar event with iCalUid ${fetchedCalendarEvent.iCalUid} not found - should never happen`,
);
}
diff --git a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/utils/calendar-event-mapper.util.ts b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/utils/calendar-event-mapper.util.ts
index a098150532..6536cd61d2 100644
--- a/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/utils/calendar-event-mapper.util.ts
+++ b/packages/twenty-server/src/modules/calendar/calendar-event-import-manager/utils/calendar-event-mapper.util.ts
@@ -5,7 +5,7 @@ export const mapCalendarEventsByICalUID = (
): Map => {
return new Map(
existingCalendarEvents.map((calendarEvent) => [
- calendarEvent.iCalUID ?? '',
+ calendarEvent.iCalUid ?? '',
calendarEvent.id,
]),
);
diff --git a/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service.spec.ts b/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service.spec.ts
index 662aba6e42..485f44c40b 100644
--- a/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service.spec.ts
+++ b/packages/twenty-server/src/modules/calendar/common/query-hooks/calendar-event/services/apply-calendar-events-visibility-restrictions.service.spec.ts
@@ -31,7 +31,7 @@ const createMockCalendarEvent = (
deletedAt: null,
createdAt: '2024-03-20T09:00:00Z',
updatedAt: '2024-03-20T09:00:00Z',
- iCalUID: '',
+ iCalUid: '',
conferenceSolution: '',
calendarChannelEventAssociations: [],
calendarEventParticipants: [],
diff --git a/packages/twenty-server/src/modules/calendar/common/standard-objects/calendar-event.workspace-entity.ts b/packages/twenty-server/src/modules/calendar/common/standard-objects/calendar-event.workspace-entity.ts
index 3ff9042db8..500e4ad968 100644
--- a/packages/twenty-server/src/modules/calendar/common/standard-objects/calendar-event.workspace-entity.ts
+++ b/packages/twenty-server/src/modules/calendar/common/standard-objects/calendar-event.workspace-entity.ts
@@ -127,14 +127,14 @@ export class CalendarEventWorkspaceEntity extends BaseWorkspaceEntity {
location: string | null;
@WorkspaceField({
- standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.iCalUID,
+ standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.iCalUid,
type: FieldMetadataType.TEXT,
label: msg`iCal UID`,
description: msg`iCal UID`,
icon: 'IconKey',
})
@WorkspaceIsNullable()
- iCalUID: string | null;
+ iCalUid: string | null;
@WorkspaceField({
standardId: CALENDAR_EVENT_STANDARD_FIELD_IDS.conferenceSolution,
diff --git a/packages/twenty-server/src/modules/calendar/common/types/fetched-calendar-event.ts b/packages/twenty-server/src/modules/calendar/common/types/fetched-calendar-event.ts
index 85239d585b..e183454139 100644
--- a/packages/twenty-server/src/modules/calendar/common/types/fetched-calendar-event.ts
+++ b/packages/twenty-server/src/modules/calendar/common/types/fetched-calendar-event.ts
@@ -8,7 +8,7 @@ export type FetchedCalendarEventParticipant = {
export type FetchedCalendarEvent = {
id: string;
title: string;
- iCalUID: string;
+ iCalUid: string;
description: string;
startsAt: string;
endsAt: string;
diff --git a/packages/twenty-server/src/modules/messaging/blocklist-manager/jobs/messaging-blocklist-item-delete-messages.job.ts b/packages/twenty-server/src/modules/messaging/blocklist-manager/jobs/messaging-blocklist-item-delete-messages.job.ts
index 12ad56e0af..aedc03c2d1 100644
--- a/packages/twenty-server/src/modules/messaging/blocklist-manager/jobs/messaging-blocklist-item-delete-messages.job.ts
+++ b/packages/twenty-server/src/modules/messaging/blocklist-manager/jobs/messaging-blocklist-item-delete-messages.job.ts
@@ -2,6 +2,7 @@ import { Scope } from '@nestjs/common';
import { isDefined } from 'twenty-shared/utils';
import { And, Any, ILike, In, Not, Or } from 'typeorm';
+import { MessageParticipantRole } from 'twenty-shared/types';
import { type ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
@@ -87,7 +88,10 @@ export class BlocklistItemDeleteMessagesJob {
continue;
}
- const rolesToDelete: ('from' | 'to')[] = ['from', 'to'];
+ const rolesToDelete = [
+ MessageParticipantRole.FROM,
+ MessageParticipantRole.TO,
+ ] as const;
const messageChannels = await messageChannelRepository.find({
select: {
diff --git a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts
index 33f17e8538..d2f4ce6d0d 100644
--- a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts
+++ b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-channel.workspace-entity.ts
@@ -47,8 +47,8 @@ export enum MessageChannelVisibility {
}
export enum MessageChannelType {
- EMAIL = 'email',
- SMS = 'sms',
+ EMAIL = 'EMAIL',
+ SMS = 'SMS',
}
export enum MessageChannelContactAutoCreationPolicy {
diff --git a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-participant.workspace-entity.ts b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-participant.workspace-entity.ts
index 76e1701ff2..3b435fddc3 100644
--- a/packages/twenty-server/src/modules/messaging/common/standard-objects/message-participant.workspace-entity.ts
+++ b/packages/twenty-server/src/modules/messaging/common/standard-objects/message-participant.workspace-entity.ts
@@ -1,6 +1,10 @@
import { msg } from '@lingui/core/macro';
import { STANDARD_OBJECT_IDS } from 'twenty-shared/metadata';
-import { FieldMetadataType, RelationOnDeleteAction } from 'twenty-shared/types';
+import {
+ FieldMetadataType,
+ MessageParticipantRole,
+ RelationOnDeleteAction,
+} from 'twenty-shared/types';
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
import { Relation } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/relation.interface';
@@ -39,14 +43,34 @@ export class MessageParticipantWorkspaceEntity extends BaseWorkspaceEntity {
description: msg`Role`,
icon: 'IconAt',
options: [
- { value: 'from', label: 'From', position: 0, color: 'green' },
- { value: 'to', label: 'To', position: 1, color: 'blue' },
- { value: 'cc', label: 'Cc', position: 2, color: 'orange' },
- { value: 'bcc', label: 'Bcc', position: 3, color: 'red' },
+ {
+ value: MessageParticipantRole.FROM,
+ label: 'From',
+ position: 0,
+ color: 'green',
+ },
+ {
+ value: MessageParticipantRole.TO,
+ label: 'To',
+ position: 1,
+ color: 'blue',
+ },
+ {
+ value: MessageParticipantRole.CC,
+ label: 'Cc',
+ position: 2,
+ color: 'orange',
+ },
+ {
+ value: MessageParticipantRole.BCC,
+ label: 'Bcc',
+ position: 3,
+ color: 'red',
+ },
],
- defaultValue: "'from'",
+ defaultValue: `'${MessageParticipantRole.FROM}'`,
})
- role: string;
+ role: MessageParticipantRole;
@WorkspaceField({
standardId: MESSAGE_PARTICIPANT_STANDARD_FIELD_IDS.handle,
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/types/gmail-message.type.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/types/gmail-message.type.ts
index ee87048f71..9f8c04d9a8 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/types/gmail-message.type.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/types/gmail-message.type.ts
@@ -1,3 +1,5 @@
+import { type MessageParticipantRole } from 'twenty-shared/types';
+
export type GmailMessage = {
historyId: string;
externalId: string;
@@ -13,7 +15,7 @@ export type GmailMessage = {
};
export type Participant = {
- role: 'from' | 'to' | 'cc' | 'bcc';
+ role: MessageParticipantRole;
handle: string;
displayName: string;
};
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-and-format-gmail-message.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-and-format-gmail-message.util.ts
index 3e1d31debd..40f006569f 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-and-format-gmail-message.util.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/gmail/utils/parse-and-format-gmail-message.util.ts
@@ -1,5 +1,6 @@
import { type gmail_v1 as gmailV1 } from 'googleapis';
import planer from 'planer';
+import { MessageParticipantRole } from 'twenty-shared/types';
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { computeMessageDirection } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/compute-message-direction.util';
@@ -43,17 +44,28 @@ export const parseAndFormatGmailMessage = (
const participants = [
...(from
- ? formatAddressObjectAsParticipants([{ address: from }], 'from')
+ ? formatAddressObjectAsParticipants(
+ [{ address: from }],
+ MessageParticipantRole.FROM,
+ )
: []),
...(toParticipants
? formatAddressObjectAsParticipants(
[{ address: toParticipants, name: '' }],
- 'to',
+ MessageParticipantRole.TO,
+ )
+ : []),
+ ...(cc
+ ? formatAddressObjectAsParticipants(
+ [{ address: cc }],
+ MessageParticipantRole.CC,
)
: []),
- ...(cc ? formatAddressObjectAsParticipants([{ address: cc }], 'cc') : []),
...(bcc
- ? formatAddressObjectAsParticipants([{ address: bcc }], 'bcc')
+ ? formatAddressObjectAsParticipants(
+ [{ address: bcc }],
+ MessageParticipantRole.BCC,
+ )
: []),
];
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-messages.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-messages.service.ts
index ed50476aa0..343ad54072 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-messages.service.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/imap/services/imap-get-messages.service.ts
@@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
import { type ImapFlow } from 'imapflow';
import { Address, type Email as ParsedMail } from 'postal-mime';
+import { MessageParticipantRole } from 'twenty-shared/types';
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { computeMessageDirection } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/compute-message-direction.util';
@@ -203,11 +204,11 @@ export class ImapGetMessagesService {
private extractParticipants(parsed: ParsedMail) {
const addressFields = [
- { field: parsed.from, role: 'from' as const },
- { field: parsed.to, role: 'to' as const },
- { field: parsed.cc, role: 'cc' as const },
- { field: parsed.bcc, role: 'bcc' as const },
- ];
+ { field: parsed.from, role: MessageParticipantRole.FROM },
+ { field: parsed.to, role: MessageParticipantRole.TO },
+ { field: parsed.cc, role: MessageParticipantRole.CC },
+ { field: parsed.bcc, role: MessageParticipantRole.BCC },
+ ] as const;
return addressFields.flatMap(({ field, role }) =>
formatAddressObjectAsParticipants(this.extractAddresses(field), role),
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.spec.ts
index 1ec09e6f02..da35e1f39f 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.spec.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.spec.ts
@@ -2,7 +2,10 @@ import { Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Test, type TestingModule } from '@nestjs/testing';
-import { ConnectedAccountProvider } from 'twenty-shared/types';
+import {
+ ConnectedAccountProvider,
+ MessageParticipantRole,
+} from 'twenty-shared/types';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { GoogleOAuth2ClientManagerService } from 'src/modules/connected-account/oauth2-client-manager/drivers/google/google-oauth2-client-manager.service';
@@ -97,12 +100,12 @@ describe('Microsoft get messages service', () => {
{
displayName: 'John l',
handle: 'john.l@outlook.fr',
- role: 'from',
+ role: MessageParticipantRole.FROM,
},
{
displayName: 'Walker',
handle: 'walker@felixacme.onmicrosoft.com',
- role: 'to',
+ role: MessageParticipantRole.TO,
},
],
attachments: [],
@@ -130,22 +133,22 @@ describe('Microsoft get messages service', () => {
{
displayName: 'Microsoft',
handle: 'microsoft-noreply@microsoft.com',
- role: 'from',
+ role: MessageParticipantRole.FROM,
},
{
displayName: 'Walker',
handle: 'walker@felixacme.onmicrosoft.com',
- role: 'to',
+ role: MessageParticipantRole.TO,
},
{
displayName: 'Antoine',
handle: 'antoine@gmail.com',
- role: 'cc',
+ role: MessageParticipantRole.CC,
},
{
displayName: 'Cyril@acme2.com',
handle: 'cyril@acme2.com',
- role: 'cc',
+ role: MessageParticipantRole.CC,
},
],
attachments: [],
@@ -184,7 +187,7 @@ describe('Microsoft get messages service', () => {
displayName: responseExample.body.sender.emailAddress.name,
handle:
responseExample.body.sender.emailAddress.address.toLowerCase(),
- role: 'from',
+ role: MessageParticipantRole.FROM,
},
],
attachments: [],
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.ts
index cf9b4e016a..021957bee9 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/drivers/microsoft/services/microsoft-get-messages.service.ts
@@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
import { type EmailAddress } from 'addressparser';
import { isDefined } from 'twenty-shared/utils';
+import { MessageParticipantRole } from 'twenty-shared/types';
import { type ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity';
import { MessageDirection } from 'src/modules/messaging/common/enums/message-direction.enum';
@@ -109,16 +110,28 @@ export class MicrosoftGetMessagesService {
const participants = [
...(safeParseFrom
- ? formatAddressObjectAsParticipants(safeParseFrom, 'from')
+ ? formatAddressObjectAsParticipants(
+ safeParseFrom,
+ MessageParticipantRole.FROM,
+ )
: []),
...(safeParseTo
- ? formatAddressObjectAsParticipants(safeParseTo, 'to')
+ ? formatAddressObjectAsParticipants(
+ safeParseTo,
+ MessageParticipantRole.TO,
+ )
: []),
...(safeParseCc
- ? formatAddressObjectAsParticipants(safeParseCc, 'cc')
+ ? formatAddressObjectAsParticipants(
+ safeParseCc,
+ MessageParticipantRole.CC,
+ )
: []),
...(safeParseBcc
- ? formatAddressObjectAsParticipants(safeParseBcc, 'bcc')
+ ? formatAddressObjectAsParticipants(
+ safeParseBcc,
+ MessageParticipantRole.BCC,
+ )
: []),
];
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-delete-group-email-messages.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-delete-group-email-messages.service.ts
index a2a6209a1c..74d289b798 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-delete-group-email-messages.service.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-delete-group-email-messages.service.ts
@@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
import chunk from 'lodash.chunk';
import { isDefined } from 'twenty-shared/utils';
+import { MessageParticipantRole } from 'twenty-shared/types';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { MessageChannelMessageAssociationWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel-message-association.workspace-entity';
@@ -55,7 +56,7 @@ export class MessagingDeleteGroupEmailMessagesService {
'message.messageParticipants',
'participant',
'participant.role = :role',
- { role: 'from' },
+ { role: MessageParticipantRole.FROM },
)
.where('mcma.messageChannelId = :messageChannelId', {
messageChannelId,
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts
index 71a74f9daf..ccc804670b 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.spec.ts
@@ -1,7 +1,7 @@
import { Test, type TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
-import { FieldActorSource } from 'twenty-shared/types';
+import { FieldActorSource, MessageParticipantRole } from 'twenty-shared/types';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
@@ -56,8 +56,16 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => {
messageThreadExternalId: 'thread-1',
direction: MessageDirection.OUTGOING,
participants: [
- { role: 'from', handle: 'test@example.com', displayName: 'Test User' },
- { role: 'to', handle: 'contact@company.com', displayName: 'Contact' },
+ {
+ role: MessageParticipantRole.FROM,
+ handle: 'test@example.com',
+ displayName: 'Test User',
+ },
+ {
+ role: MessageParticipantRole.TO,
+ handle: 'contact@company.com',
+ displayName: 'Contact',
+ },
],
},
{
@@ -70,11 +78,23 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => {
messageThreadExternalId: 'thread-1',
direction: MessageDirection.INCOMING,
participants: [
- { role: 'from', handle: 'contact@company.com', displayName: 'Contact' },
- { role: 'to', handle: 'test@example.com', displayName: 'Test User' },
- { role: 'to', handle: 'personal@gmail.com', displayName: 'Personal' },
{
- role: 'to',
+ role: MessageParticipantRole.FROM,
+ handle: 'contact@company.com',
+ displayName: 'Contact',
+ },
+ {
+ role: MessageParticipantRole.TO,
+ handle: 'test@example.com',
+ displayName: 'Test User',
+ },
+ {
+ role: MessageParticipantRole.TO,
+ handle: 'personal@gmail.com',
+ displayName: 'Personal',
+ },
+ {
+ role: MessageParticipantRole.TO,
handle: 'team@lists.company.com',
displayName: 'Group email',
},
@@ -199,7 +219,7 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => {
...mockMessages[1],
participants: [
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'tim@apple.com',
displayName: 'participant email',
},
@@ -221,7 +241,7 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => {
{
handle: 'tim@apple.com',
displayName: 'participant email',
- role: 'from',
+ role: MessageParticipantRole.FROM,
shouldCreateContact: true,
messageId: 'db-message-id-2',
},
@@ -237,7 +257,7 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => {
...mockMessages[0],
participants: [
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'test@gmail.com',
displayName: 'participant personal email',
},
@@ -265,7 +285,7 @@ describe('MessagingSaveMessagesAndEnqueueContactCreationService', () => {
...mockMessages[0],
participants: [
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'connected@account.com',
displayName: 'participant that is the Connected Account',
},
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts
index 1b71c9868c..bffa6656d4 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/services/messaging-save-messages-and-enqueue-contact-creation.service.ts
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
-import { FieldActorSource } from 'twenty-shared/types';
+import { FieldActorSource, MessageParticipantRole } from 'twenty-shared/types';
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
@@ -66,8 +66,9 @@ export class MessagingSaveMessagesAndEnqueueContactCreationService {
return messageId
? message.participants.map((participant: Participant) => {
const fromHandle =
- message.participants.find((p) => p.role === 'from')?.handle ||
- '';
+ message.participants.find(
+ (p) => p.role === MessageParticipantRole.FROM,
+ )?.handle || '';
const isMessageSentByConnectedAccount =
handleAliases.includes(fromHandle) ||
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__mocks__/messages.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__mocks__/messages.ts
index 4cc2ead8f9..761a876235 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__mocks__/messages.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__mocks__/messages.ts
@@ -1,3 +1,6 @@
+import { MessageParticipantRole } from 'twenty-shared/types';
+
+import { MessageDirection } from 'src/modules/messaging/common/enums/message-direction.enum';
import { type MessageWithParticipants } from 'src/modules/messaging/message-import-manager/types/message';
export const messagingGetMessagesServiceGetMessages = [
@@ -8,15 +11,15 @@ export const messagingGetMessagesServiceGetMessages = [
text: 'Just a test',
headerMessageId: '',
messageThreadExternalId: 'AAQkAGZlMDQ1NjU5Lk=',
- direction: 'OUTGOING',
+ direction: MessageDirection.OUTGOING,
participants: [
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'from@acme.com',
displayName: 'From',
},
{
- role: 'to',
+ role: MessageParticipantRole.TO,
handle: 'to@acme.com',
displayName: 'To',
},
@@ -30,15 +33,15 @@ export const messagingGetMessagesServiceGetMessages = [
text: 'Just a test',
headerMessageId: '',
messageThreadExternalId: 'AAQkAGZlMDQ1NjU5Lk=',
- direction: 'OUTGOING',
+ direction: MessageDirection.OUTGOING,
participants: [
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'from@acme.com',
displayName: 'From',
},
{
- role: 'to',
+ role: MessageParticipantRole.TO,
handle: 'to@external.com',
displayName: 'To',
},
@@ -52,19 +55,19 @@ export const messagingGetMessagesServiceGetMessages = [
text: 'Learn with interactive sandboxes, curated collections, and bookmarks',
headerMessageId: '',
messageThreadExternalId: 'AAQkAGZlMDQ1NNc=',
- direction: 'INCOMING',
+ direction: MessageDirection.INCOMING,
participants: [
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'learn@mails.microsoft.com',
displayName: 'Microsoft Learn',
},
{
- role: 'to',
+ role: MessageParticipantRole.TO,
handle: 'to@gmail.com',
displayName: 'To',
},
],
attachments: [],
},
-] as MessageWithParticipants[];
+] satisfies MessageWithParticipants[];
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/filter-emails.util.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/filter-emails.util.spec.ts
index e11678741c..8b3ab767bd 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/filter-emails.util.spec.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/filter-emails.util.spec.ts
@@ -1,3 +1,5 @@
+import { MessageParticipantRole } from 'twenty-shared/types';
+
import { MessageDirection } from 'src/modules/messaging/common/enums/message-direction.enum';
import { type MessageWithParticipants } from 'src/modules/messaging/message-import-manager/types/message';
import { messagingGetMessagesServiceGetMessages } from 'src/modules/messaging/message-import-manager/utils/__mocks__/messages';
@@ -64,7 +66,7 @@ describe('filterEmails', () => {
direction: MessageDirection.INCOMING,
participants: [
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'noreply@example.com',
displayName: 'No Reply',
},
@@ -81,7 +83,7 @@ describe('filterEmails', () => {
direction: MessageDirection.INCOMING,
participants: [
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'support@company.com',
displayName: 'Support Team',
},
@@ -98,7 +100,7 @@ describe('filterEmails', () => {
direction: MessageDirection.INCOMING,
participants: [
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'john@example.com',
displayName: 'John Doe',
},
@@ -126,7 +128,7 @@ describe('filterEmails', () => {
direction: MessageDirection.INCOMING,
participants: [
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'noreply@example.com',
displayName: 'No Reply',
},
@@ -174,7 +176,7 @@ describe('filterEmails', () => {
direction: MessageDirection.INCOMING,
participants: [
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'john.doe@example.com',
displayName: 'John Doe',
},
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/format-address-object-as-participants.util.spec.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/format-address-object-as-participants.util.spec.ts
index cff04d2dae..79d72f3161 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/format-address-object-as-participants.util.spec.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/__tests__/format-address-object-as-participants.util.spec.ts
@@ -1,3 +1,5 @@
+import { MessageParticipantRole } from 'twenty-shared/types';
+
import { formatAddressObjectAsParticipants } from 'src/modules/messaging/message-import-manager/utils/format-address-object-as-participants.util';
describe('formatAddressObjectAsParticipants', () => {
@@ -7,16 +9,19 @@ describe('formatAddressObjectAsParticipants', () => {
{ name: 'Jane Smith', address: 'jane.smith@example.com ' },
];
- const result = formatAddressObjectAsParticipants(addresses, 'from');
+ const result = formatAddressObjectAsParticipants(
+ addresses,
+ MessageParticipantRole.FROM,
+ );
expect(result).toEqual([
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'john.doe@example.com',
displayName: 'John Doe',
},
{
- role: 'from',
+ role: MessageParticipantRole.FROM,
handle: 'jane.smith@example.com',
displayName: 'Jane Smith',
},
@@ -29,7 +34,10 @@ describe('formatAddressObjectAsParticipants', () => {
address: 'john.doe',
};
- const result = formatAddressObjectAsParticipants([addressObject], 'to');
+ const result = formatAddressObjectAsParticipants(
+ [addressObject],
+ MessageParticipantRole.TO,
+ );
expect(result).toEqual([]);
});
@@ -40,7 +48,10 @@ describe('formatAddressObjectAsParticipants', () => {
address: '',
};
- const result = formatAddressObjectAsParticipants([addressObject], 'to');
+ const result = formatAddressObjectAsParticipants(
+ [addressObject],
+ MessageParticipantRole.TO,
+ );
expect(result).toEqual([]);
});
@@ -51,11 +62,14 @@ describe('formatAddressObjectAsParticipants', () => {
address: 'John.Doe@example.com',
};
- const result = formatAddressObjectAsParticipants([addressObject], 'to');
+ const result = formatAddressObjectAsParticipants(
+ [addressObject],
+ MessageParticipantRole.TO,
+ );
expect(result).toEqual([
{
- role: 'to',
+ role: MessageParticipantRole.TO,
handle: 'john.doe@example.com',
displayName: 'John Doe',
},
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/filter-out-group-emails.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/filter-out-group-emails.util.ts
index bd6b98c032..09f0190ef8 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/filter-out-group-emails.util.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/filter-out-group-emails.util.ts
@@ -1,3 +1,5 @@
+import { MessageParticipantRole } from 'twenty-shared/types';
+
import { type MessageWithParticipants } from 'src/modules/messaging/message-import-manager/types/message';
import { isGroupEmail } from 'src/modules/messaging/message-import-manager/utils/is-group-email';
@@ -8,7 +10,7 @@ export const filterOutGroupEmails = (messages: MessageWithParticipants[]) => {
}
const fromParticipant = message.participants.find(
- (participant) => participant.role === 'from',
+ (participant) => participant.role === MessageParticipantRole.FROM,
);
if (!fromParticipant || !fromParticipant.handle) {
diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/format-address-object-as-participants.util.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/format-address-object-as-participants.util.ts
index 1745f9f917..38bf0af196 100644
--- a/packages/twenty-server/src/modules/messaging/message-import-manager/utils/format-address-object-as-participants.util.ts
+++ b/packages/twenty-server/src/modules/messaging/message-import-manager/utils/format-address-object-as-participants.util.ts
@@ -1,4 +1,5 @@
import { isDefined } from 'twenty-shared/utils';
+import { type MessageParticipantRole } from 'twenty-shared/types';
import { type Participant } from 'src/modules/messaging/message-import-manager/drivers/gmail/types/gmail-message.type';
import { type EmailAddress } from 'src/modules/messaging/message-import-manager/types/email-address';
@@ -9,7 +10,7 @@ const removeSpacesAndLowerCase = (email: string): string => {
export const formatAddressObjectAsParticipants = (
addressObjects: EmailAddress[],
- role: 'from' | 'to' | 'cc' | 'bcc',
+ role: MessageParticipantRole,
): Participant[] => {
const participants = addressObjects.map((addressObject) => {
const address = addressObject.address;
diff --git a/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.workspace-entity.ts b/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.workspace-entity.ts
index 294ec08943..50f5ee9ed8 100644
--- a/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.workspace-entity.ts
+++ b/packages/twenty-server/src/modules/workspace-member/standard-objects/workspace-member.workspace-entity.ts
@@ -410,25 +410,25 @@ export class WorkspaceMemberWorkspaceEntity extends BaseWorkspaceEntity {
},
{
value: WorkspaceMemberNumberFormatEnum.COMMAS_AND_DOT,
- label: 'Commas and dot (1,234.56)',
+ label: 'Commas and dot',
position: 1,
color: 'blue',
},
{
value: WorkspaceMemberNumberFormatEnum.SPACES_AND_COMMA,
- label: 'Spaces and comma (1 234,56)',
+ label: 'Spaces and comma',
position: 2,
color: 'green',
},
{
value: WorkspaceMemberNumberFormatEnum.DOTS_AND_COMMA,
- label: 'Dots and comma (1.234,56)',
+ label: 'Dots and comma',
position: 3,
color: 'orange',
},
{
value: WorkspaceMemberNumberFormatEnum.APOSTROPHE_AND_DOT,
- label: "Apostrophe and dot (1'234.56)",
+ label: 'Apostrophe and dot',
position: 4,
color: 'purple',
},
diff --git a/packages/twenty-shared/src/metadata/sanitize-reserved-keyword.util.ts b/packages/twenty-shared/src/metadata/add-custom-suffix-if-reserved.util.ts
similarity index 57%
rename from packages/twenty-shared/src/metadata/sanitize-reserved-keyword.util.ts
rename to packages/twenty-shared/src/metadata/add-custom-suffix-if-reserved.util.ts
index 60b10a0083..c3e4b8534e 100644
--- a/packages/twenty-shared/src/metadata/sanitize-reserved-keyword.util.ts
+++ b/packages/twenty-shared/src/metadata/add-custom-suffix-if-reserved.util.ts
@@ -1,10 +1,9 @@
-import { capitalize } from '../utils';
import { RESERVED_METADATA_NAME_KEYWORDS } from './reserved-metadata-name-keywords.constant';
-export const sanitizeReservedKeyword = (name: string): string => {
+export const addCustomSuffixIfIsReserved = (name: string): string => {
if (!name) return name;
return RESERVED_METADATA_NAME_KEYWORDS.includes(name)
- ? `${name}${capitalize('custom')}`
+ ? `${name}Custom`
: name;
};
diff --git a/packages/twenty-shared/src/metadata/compute-metadata-name-from-label.util.ts b/packages/twenty-shared/src/metadata/compute-metadata-name-from-label.util.ts
index ba612410c2..0b935b5917 100644
--- a/packages/twenty-shared/src/metadata/compute-metadata-name-from-label.util.ts
+++ b/packages/twenty-shared/src/metadata/compute-metadata-name-from-label.util.ts
@@ -1,9 +1,14 @@
+import { addCustomSuffixIfIsReserved } from '@/metadata/add-custom-suffix-if-reserved.util';
import camelCase from 'lodash.camelcase';
import { slugify } from 'transliteration';
-import { sanitizeReservedKeyword } from './sanitize-reserved-keyword.util';
-
-export const computeMetadataNameFromLabel = (label: string): string => {
+export const computeMetadataNameFromLabel = ({
+ label,
+ applyCustomSuffix = true,
+}: {
+ label: string;
+ applyCustomSuffix?: boolean;
+}): string => {
if (!label) return '';
const prefixedLabel = /^\d/.test(label) ? `n${label}` : label;
@@ -22,5 +27,7 @@ export const computeMetadataNameFromLabel = (label: string): string => {
const computedName = camelCase(formattedString);
- return sanitizeReservedKeyword(computedName);
+ return applyCustomSuffix
+ ? addCustomSuffixIfIsReserved(computedName)
+ : computedName;
};
diff --git a/packages/twenty-shared/src/metadata/index.ts b/packages/twenty-shared/src/metadata/index.ts
index 9310bde475..7b83687eee 100644
--- a/packages/twenty-shared/src/metadata/index.ts
+++ b/packages/twenty-shared/src/metadata/index.ts
@@ -7,6 +7,7 @@
* |___/
*/
+export { addCustomSuffixIfIsReserved } from './add-custom-suffix-if-reserved.util';
export { ALL_METADATA_NAME } from './all-metadata-name.constant';
export type { AllMetadataName } from './all-metadata-name.type';
export { computeMetadataNameFromLabel } from './compute-metadata-name-from-label.util';
@@ -20,5 +21,4 @@ export {
CORE_OBJECT_NAMES,
RESERVED_METADATA_NAME_KEYWORDS,
} from './reserved-metadata-name-keywords.constant';
-export { sanitizeReservedKeyword } from './sanitize-reserved-keyword.util';
export { STANDARD_OBJECT_IDS } from './standard-object-ids';
diff --git a/packages/twenty-shared/src/types/MessageParticipantRole.ts b/packages/twenty-shared/src/types/MessageParticipantRole.ts
new file mode 100644
index 0000000000..6f0739d706
--- /dev/null
+++ b/packages/twenty-shared/src/types/MessageParticipantRole.ts
@@ -0,0 +1,6 @@
+export enum MessageParticipantRole {
+ 'FROM' = 'FROM',
+ 'TO' = 'TO',
+ 'CC' = 'CC',
+ 'BCC' = 'BCC',
+}
diff --git a/packages/twenty-shared/src/types/index.ts b/packages/twenty-shared/src/types/index.ts
index f1cbfddb6d..38d036572d 100644
--- a/packages/twenty-shared/src/types/index.ts
+++ b/packages/twenty-shared/src/types/index.ts
@@ -114,6 +114,7 @@ export type { FromTo } from './FromToType';
export { HTTPMethod } from './HttpMethod';
export type { IsEmptyRecord } from './IsEmptyRecord.type';
export type { IsExactly } from './IsExactly';
+export { MessageParticipantRole } from './MessageParticipantRole';
export type { ModifiedProperties } from './ModifiedProperties';
export type { NonNullableRequired } from './NonNullableRequired';
export type { Nullable } from './Nullable';