Files
twenty/packages/twenty-server/src/engine/twenty-orm/utils/format-data.util.ts
Baptiste Devessier 17bf2b6173 Move the workflow draft version overriding to the backend (#9328)
- In the `formatFieldMetadataValue` function, allow people to call
TypeORM's `save()` method with unserialized JSON data.
- Create an `overrideWorkflowDraftVersion` mutation that takes a
workflow id and the id of the workflow version to use as the new draft
  - If no draft exists yet, create one
  - If a draft already exists, deactivate its serverless functions
- Duplicate every step. For serverless function steps, it includes
duplicating the functions
  - Save the data of the step in DB
- Call the `overrideWorkflowDraftVersion` mutation in the old workflow
header and in the new Cmd+K actions
- I chose to not update the Apollo cache manually as the information of
the new draft are going to be automatically fetched once the user lands
on the workflow's show page. Note that we redirect the user to this page
after overriding the draft version.
2025-01-06 14:56:09 +01:00

94 lines
2.8 KiB
TypeScript

import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface';
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
import { capitalize } from 'src/utils/capitalize';
export function formatData<T>(
data: T,
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
): T {
if (!data) {
return data;
}
if (Array.isArray(data)) {
return data.map((item) =>
formatData(item, objectMetadataItemWithFieldMaps),
) as T;
}
const newData: Record<string, any> = {};
for (const [key, value] of Object.entries(data)) {
const fieldMetadata = objectMetadataItemWithFieldMaps.fieldsByName[key];
if (!fieldMetadata) {
throw new Error(
`Field metadata for field "${key}" is missing in object metadata`,
);
}
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
const formattedCompositeField = formatCompositeField(
value,
fieldMetadata,
);
Object.assign(newData, formattedCompositeField);
} else {
newData[key] = formatFieldMetadataValue(value, fieldMetadata);
}
}
return newData as T;
}
function formatCompositeField(
value: any,
fieldMetadata: FieldMetadataInterface,
): Record<string, any> {
const compositeType = compositeTypeDefinitions.get(
fieldMetadata.type as CompositeFieldMetadataType,
);
if (!compositeType) {
throw new Error(
`Composite type definition not found for type: ${fieldMetadata.type}`,
);
}
const formattedCompositeField: Record<string, any> = {};
for (const property of compositeType.properties) {
const subFieldKey = property.name;
const fullFieldName = `${fieldMetadata.name}${capitalize(subFieldKey)}`;
if (value && value[subFieldKey] !== undefined) {
formattedCompositeField[fullFieldName] = formatFieldMetadataValue(
value[subFieldKey],
property as unknown as FieldMetadataInterface,
);
}
}
return formattedCompositeField;
}
function formatFieldMetadataValue(
value: any,
fieldMetadata: FieldMetadataInterface,
) {
if (
fieldMetadata.type === FieldMetadataType.RAW_JSON &&
typeof value === 'string'
) {
return JSON.parse(value as string);
}
return value;
}