Revert "Common - Field validation (#15491)" (#15821)

This reverts commit 261cba40ef.
This commit is contained in:
martmull
2025-11-14 13:47:22 +01:00
committed by GitHub
parent df58f4102e
commit 867422627c
114 changed files with 1189 additions and 3524 deletions
@@ -32,9 +32,9 @@ describe('computeSchemaComponents', () => {
"lastName": "Osinski",
},
"fieldLinks": {
"additionalLinks": [],
"primaryLinkLabel": "",
"primaryLinkUrl": "https://narrow-help.net/",
"secondaryLinks": [],
},
"fieldMultiSelect": [
"OPTION_1",
@@ -46,7 +46,9 @@ describe('computeSchemaComponents', () => {
"primaryPhoneCountryCode": "FR",
"primaryPhoneNumber": "06 10 20 30 40",
},
"fieldSelect": "OPTION_1",
"fieldSelect": [
"OPTION_1",
],
},
"properties": {
"fieldActor": {
@@ -533,9 +535,9 @@ describe('computeSchemaComponents', () => {
"lastName": "Jones",
},
"fieldLinks": {
"additionalLinks": [],
"primaryLinkLabel": "",
"primaryLinkUrl": "https://unlawful-blowgun.biz",
"secondaryLinks": [],
},
"fieldMultiSelect": [
"OPTION_1",
@@ -547,7 +549,9 @@ describe('computeSchemaComponents', () => {
"primaryPhoneCountryCode": "FR",
"primaryPhoneNumber": "06 10 20 30 40",
},
"fieldSelect": "OPTION_1",
"fieldSelect": [
"OPTION_1",
],
},
"properties": {
"fieldActor": {
@@ -58,7 +58,7 @@ export const generateRandomFieldValue = ({
return {
primaryLinkLabel: '',
primaryLinkUrl: faker.internet.url(),
secondaryLinks: [],
additionalLinks: [],
};
}
@@ -82,10 +82,10 @@ export const generateRandomFieldValue = ({
case FieldMetadataType.SELECT: {
if (!isDefined(field.options) || !isDefined(field.options[0].value)) {
return null;
return [];
}
return field.options[0].value;
return [field.options[0].value];
}
case FieldMetadataType.MULTI_SELECT: {
@@ -2,16 +2,15 @@ import { Injectable } from '@nestjs/common';
import {
FieldMetadataType,
ObjectRecord,
compositeTypeDefinitions,
type RichTextV2Metadata,
richTextV2ValueSchema,
} from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { transformEmailsValue } from 'src/engine/core-modules/record-transformer/utils/transform-emails-value.util';
import { transformLinksValue } from 'src/engine/core-modules/record-transformer/utils/transform-links-value.util';
import { transformPhonesValue } from 'src/engine/core-modules/record-transformer/utils/transform-phones-value.util';
import { transformRichTextV2Value } from 'src/engine/core-modules/record-transformer/utils/transform-rich-text-v2.util';
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
@Injectable()
export class RecordInputTransformerService {
@@ -19,9 +18,15 @@ export class RecordInputTransformerService {
recordInput,
objectMetadataMapItem,
}: {
recordInput: Partial<ObjectRecord>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
recordInput: Record<string, any>;
objectMetadataMapItem: ObjectMetadataItemWithFieldMaps;
}): Promise<Partial<ObjectRecord>> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}): Promise<Record<string, any>> {
if (!recordInput) {
return recordInput;
}
let transformedEntries = {};
for (const [key, value] of Object.entries(recordInput)) {
@@ -67,11 +72,11 @@ export class RecordInputTransformerService {
'Rich text is not supported, please use RICH_TEXT_V2 instead',
);
case FieldMetadataType.RICH_TEXT_V2:
return await transformRichTextV2Value(value);
return this.transformRichTextV2Value(value);
case FieldMetadataType.LINKS:
return transformLinksValue(value);
case FieldMetadataType.EMAILS:
return transformEmailsValue(value);
return this.transformEmailsValue(value);
case FieldMetadataType.PHONES:
return transformPhonesValue({ input: value });
default:
@@ -79,6 +84,73 @@ export class RecordInputTransformerService {
}
}
private async transformRichTextV2Value(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
richTextValue: any,
): Promise<RichTextV2Metadata> {
const parsedValue = richTextV2ValueSchema.parse(richTextValue);
const { ServerBlockNoteEditor } = await import('@blocknote/server-util');
const serverBlockNoteEditor = ServerBlockNoteEditor.create();
// Patch: Handle cases where blocknote to markdown conversion fails for certain block types (custom/code blocks)
// Todo : This may be resolved once the server-utils library is updated with proper conversion support - #947
let convertedMarkdown: string | null = null;
try {
convertedMarkdown = isDefined(parsedValue.blocknote)
? await serverBlockNoteEditor.blocksToMarkdownLossy(
JSON.parse(parsedValue.blocknote),
)
: null;
} catch {
convertedMarkdown = parsedValue.blocknote || null;
}
const convertedBlocknote = parsedValue.markdown
? JSON.stringify(
await serverBlockNoteEditor.tryParseMarkdownToBlocks(
parsedValue.markdown,
),
)
: null;
return {
markdown: parsedValue.markdown || convertedMarkdown,
blocknote: parsedValue.blocknote || convertedBlocknote,
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private transformEmailsValue(value: any): any {
if (!value) {
return value;
}
let additionalEmails = value?.additionalEmails;
const primaryEmail = value?.primaryEmail
? value.primaryEmail.toLowerCase()
: '';
if (additionalEmails) {
try {
const emailArray = JSON.parse(additionalEmails) as string[];
additionalEmails = JSON.stringify(
emailArray.map((email) => email.toLowerCase()),
);
} catch {
/* empty */
}
}
return {
primaryEmail,
additionalEmails,
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private stringifySubFields(fieldMetadataType: FieldMetadataType, value: any) {
const compositeType = compositeTypeDefinitions.get(fieldMetadataType);
@@ -1,34 +0,0 @@
import { isNonEmptyString } from '@sniptt/guards';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const transformEmailsValue = (value: any): any => {
if (!value) {
return value;
}
let additionalEmails = value?.additionalEmails;
const primaryEmail = value?.primaryEmail
? value.primaryEmail.toLowerCase()
: '';
if (additionalEmails) {
try {
const emailArray = (
isNonEmptyString(additionalEmails)
? JSON.parse(additionalEmails)
: additionalEmails
) as string[];
additionalEmails = JSON.stringify(
emailArray.map((email) => email.toLowerCase()),
);
} catch {
/* empty */
}
}
return {
primaryEmail,
additionalEmails,
};
};
@@ -32,7 +32,7 @@ export const transformLinksValue = (
const secondaryLinksArray = isNonEmptyString(secondaryLinksRaw)
? parseJson<LinkMetadataNullable[]>(secondaryLinksRaw)
: secondaryLinksRaw;
: null;
const { primaryLinkLabel, primaryLinkUrl, secondaryLinks } = removeEmptyLinks(
{
@@ -1,46 +0,0 @@
import { isNonEmptyString } from '@sniptt/guards';
import {
type RichTextV2Metadata,
richTextV2ValueSchema,
} from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
export const transformRichTextV2Value = async (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
richTextValue: any,
): Promise<RichTextV2Metadata> => {
const parsedValue = isNonEmptyString(richTextValue)
? richTextV2ValueSchema.parse(richTextValue)
: richTextValue;
const { ServerBlockNoteEditor } = await import('@blocknote/server-util');
const serverBlockNoteEditor = ServerBlockNoteEditor.create();
// Patch: Handle cases where blocknote to markdown conversion fails for certain block types (custom/code blocks)
// Todo : This may be resolved once the server-utils library is updated with proper conversion support - #947
let convertedMarkdown: string | null = null;
try {
convertedMarkdown = isDefined(parsedValue.blocknote)
? await serverBlockNoteEditor.blocksToMarkdownLossy(
JSON.parse(parsedValue.blocknote),
)
: null;
} catch {
convertedMarkdown = parsedValue.blocknote || null;
}
const convertedBlocknote = parsedValue.markdown
? JSON.stringify(
await serverBlockNoteEditor.tryParseMarkdownToBlocks(
parsedValue.markdown,
),
)
: null;
return {
markdown: parsedValue.markdown || convertedMarkdown,
blocknote: parsedValue.blocknote || convertedBlocknote,
};
};