RICH_TEXT_V2 backend (#9848)
- Add RICH_TEXT_V2 composite type to backend. - Add `bodyV2` field to tasks and notes. - Minimum required frontend changes to avoid errors when creating a note [Testing instructions](https://github.com/twentyhq/twenty/pull/9690#issuecomment-2602378218) --------- Co-authored-by: ad-elias <elias@autodiligence.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
@ -288,6 +288,7 @@ export const NOTE_STANDARD_FIELD_IDS = {
|
||||
position: '20202020-368d-4dc2-943f-ed8a49c7fdfb',
|
||||
title: '20202020-faeb-4c76-8ba6-ccbb0b4a965f',
|
||||
body: '20202020-e63d-4e70-95be-a78cd9abe7ef',
|
||||
bodyV2: '20202020-a7bb-4d94-be51-8f25181502c8',
|
||||
createdBy: '20202020-0d79-4e21-ab77-5a394eff97be',
|
||||
noteTargets: '20202020-1f25-43fe-8b00-af212fdde823',
|
||||
attachments: '20202020-4986-4c92-bf19-39934b149b16',
|
||||
@ -355,6 +356,7 @@ export const TASK_STANDARD_FIELD_IDS = {
|
||||
position: '20202020-7d47-4690-8a98-98b9a0c05dd8',
|
||||
title: '20202020-b386-4cb7-aa5a-08d4a4d92680',
|
||||
body: '20202020-ce13-43f4-8821-69388fe1fd26',
|
||||
bodyV2: '20202020-4aa0-4ae8-898d-7df0afd47ab1',
|
||||
dueAt: '20202020-fd99-40da-951b-4cb9a352fce3',
|
||||
status: '20202020-70bc-48f9-89c5-6aa730b151e0',
|
||||
createdBy: '20202020-1a04-48ab-a567-576965ae5387',
|
||||
|
||||
@ -13,58 +13,6 @@ const nameFullNameField = {
|
||||
const jobTitleTextField = { name: 'jobTitle', type: FieldMetadataType.TEXT };
|
||||
const emailsEmailsField = { name: 'emails', type: FieldMetadataType.EMAILS };
|
||||
|
||||
jest.mock(
|
||||
'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util',
|
||||
() => ({
|
||||
computeColumnName: jest.fn((name) => {
|
||||
if (name === 'name') {
|
||||
return 'name';
|
||||
}
|
||||
if (name === 'jobTitle') {
|
||||
return 'jobTitle';
|
||||
}
|
||||
if (name === 'emailsPrimaryEmail') {
|
||||
return 'emailsPrimaryEmail';
|
||||
}
|
||||
if (name === 'emailsAdditionalEmails') {
|
||||
return 'emailsAdditionalEmails';
|
||||
}
|
||||
if (name === 'nameFirstName') {
|
||||
return 'nameFirstName';
|
||||
}
|
||||
if (name === 'nameLastName') {
|
||||
return 'nameLastName';
|
||||
}
|
||||
}),
|
||||
computeCompositeColumnName: jest.fn((field, property) => {
|
||||
if (
|
||||
field.name === emailsEmailsField.name &&
|
||||
property.name === 'primaryEmail'
|
||||
) {
|
||||
return 'emailsPrimaryEmail';
|
||||
}
|
||||
if (
|
||||
field.name === emailsEmailsField.name &&
|
||||
property.name === 'additionalEmails'
|
||||
) {
|
||||
return 'emailsAdditionalEmails';
|
||||
}
|
||||
if (
|
||||
field.name === nameFullNameField.name &&
|
||||
property.name === 'firstName'
|
||||
) {
|
||||
return 'nameFirstName';
|
||||
}
|
||||
if (
|
||||
field.name === nameFullNameField.name &&
|
||||
property.name === 'lastName'
|
||||
) {
|
||||
return 'nameLastName';
|
||||
}
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
describe('getTsVectorColumnExpressionFromFields', () => {
|
||||
it('should generate correct expression for simple text field', () => {
|
||||
const fields = [nameTextField] as FieldTypeAndNameMetadata[];
|
||||
@ -95,4 +43,24 @@ describe('getTsVectorColumnExpressionFromFields', () => {
|
||||
|
||||
expect(result.trim()).toBe(expected);
|
||||
});
|
||||
|
||||
it('should handle rich text fields', () => {
|
||||
const fields = [
|
||||
{ name: 'body', type: FieldMetadataType.RICH_TEXT },
|
||||
] as FieldTypeAndNameMetadata[];
|
||||
const result = getTsVectorColumnExpressionFromFields(fields);
|
||||
|
||||
expect(result).toBe("to_tsvector('simple', COALESCE(\"body\", ''))");
|
||||
});
|
||||
|
||||
it('should handle rich text v2 fields', () => {
|
||||
const fields = [
|
||||
{ name: 'bodyV2', type: FieldMetadataType.RICH_TEXT_V2 },
|
||||
] as FieldTypeAndNameMetadata[];
|
||||
const result = getTsVectorColumnExpressionFromFields(fields);
|
||||
|
||||
expect(result).toBe(
|
||||
"to_tsvector('simple', COALESCE(\"bodyV2Markdown\", ''))",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
isSearchableFieldType,
|
||||
SearchableFieldType,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util';
|
||||
import { isSearchableSubfield } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-subfield.util';
|
||||
|
||||
export type FieldTypeAndNameMetadata = {
|
||||
name: string;
|
||||
@ -55,7 +56,9 @@ const getColumnExpressionsFromField = (
|
||||
}
|
||||
|
||||
return compositeType.properties
|
||||
.filter((property) => property.type === FieldMetadataType.TEXT)
|
||||
.filter((property) =>
|
||||
isSearchableSubfield(compositeType.type, property.type, property.name),
|
||||
)
|
||||
.map((property) => {
|
||||
const columnName = computeCompositeColumnName(
|
||||
fieldMetadataTypeAndName,
|
||||
|
||||
@ -7,6 +7,7 @@ const SEARCHABLE_FIELD_TYPES = [
|
||||
FieldMetadataType.ADDRESS,
|
||||
FieldMetadataType.LINKS,
|
||||
FieldMetadataType.RICH_TEXT,
|
||||
FieldMetadataType.RICH_TEXT_V2,
|
||||
] as const;
|
||||
|
||||
export type SearchableFieldType = (typeof SEARCHABLE_FIELD_TYPES)[number];
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
import { FieldMetadataType } from 'twenty-shared';
|
||||
|
||||
export const isSearchableSubfield = (
|
||||
compositeFieldMetadataType: FieldMetadataType,
|
||||
subFieldMetadataType: FieldMetadataType,
|
||||
subFieldName: string,
|
||||
) => {
|
||||
if (subFieldMetadataType !== FieldMetadataType.TEXT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (compositeFieldMetadataType) {
|
||||
case FieldMetadataType.RICH_TEXT_V2:
|
||||
return ['markdown'].includes(subFieldName);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user