fix: enforce the user to pass in property with id suffix for morph relations in Rest API (#18335)
REST API allowed users to pass in targetOpportunity, targetPerson, targetCompany etc when trying to create a noteTarget or a taskTarget. The request went through, we got back a 201, the record was created, but the relationship was never established since the FK was empty in the database. This PR enforces users to send in the property with the `Id` suffix for consistency. So, the user sends in targetOpportunityId, targetPersonId, targetCompanyId etc. <p align="center"> <img width="854" height="480" alt="image" src="https://github.com/user-attachments/assets/ed50a623-68d4-4266-baf1-e94a657c3fd4" /> </p> If the users try to send without the "Id" suffix, they get an error explaining what to do. <p align="center"> <img width="854" height="480" alt="image" src="https://github.com/user-attachments/assets/8bab399b-7a04-4b6a-86b5-6f0e0b1ecd5d" /> </p> Additionally, the documentation itself contains the correct property names. <p align="center"> <img width="854" height="480" alt="image" src="https://github.com/user-attachments/assets/83e51cd6-8ef7-4a4d-8696-ab37cc4a9dd6" /> </p> Finally, the filters also enforce this "Id" suffix convention in the GET request. <p align="center"> <img width="854" height="480" alt="image" src="https://github.com/user-attachments/assets/168a2f09-1242-40fa-bd84-1f7d9c60357c" /> </p> Edit: Updated error messages after the screenshots were taken to make them a little generic. Secondly, this PR also fixes the issue of morph relation ids and objects not appearing in the response (when depth is 1).
This commit is contained in:
+1
-1
@@ -161,7 +161,7 @@ export const fieldMetadataConfigByFieldName: Record<
|
||||
isNullable: true,
|
||||
},
|
||||
[joinColumnNameForManyToOneMorphRelationField1]: {
|
||||
name: joinColumnNameForManyToOneMorphRelationField1,
|
||||
name: joinColumnNameForManyToOneMorphRelationField1.replace(/Id$/, ''),
|
||||
type: FieldMetadataType.MORPH_RELATION,
|
||||
isNullable: true,
|
||||
settings: {
|
||||
|
||||
+3
-1
@@ -53,7 +53,9 @@ export const successfulInputsByFieldMetadataType: {
|
||||
},
|
||||
{
|
||||
input: { [joinColumnNameForManyToOneMorphRelationField1]: null },
|
||||
expectedOutput: { [joinColumnNameForManyToOneMorphRelationField1]: null },
|
||||
expectedOutput: {
|
||||
[joinColumnNameForManyToOneMorphRelationField1]: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
[FieldMetadataType.RAW_JSON]: [
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ describe('DataArgProcessorService', () => {
|
||||
|
||||
byUniversalIdentifier[universalId] = {
|
||||
id: fieldId,
|
||||
name: fieldName,
|
||||
name: config.name,
|
||||
type: config.type ?? FieldMetadataType.TEXT,
|
||||
isNullable: config.isNullable ?? true,
|
||||
objectMetadataId: 'object-id',
|
||||
|
||||
+25
-7
@@ -15,6 +15,7 @@ import {
|
||||
} from 'twenty-shared/utils';
|
||||
|
||||
import { transformActorField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-actor-field.util';
|
||||
import { isRelationNestedOperation } from 'src/engine/api/common/common-args-processors/data-arg-processor/utils/is-relation-nested-operation.util';
|
||||
import { transformAddressField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-address-field.util';
|
||||
import { transformArrayField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-array-field.util';
|
||||
import { transformCurrencyField } from 'src/engine/api/common/common-args-processors/data-arg-processor/transformer-utils/transform-currency-field.util';
|
||||
@@ -57,7 +58,9 @@ import { transformRichTextV2Value } from 'src/engine/core-modules/record-transfo
|
||||
import { WorkspaceNotFoundDefaultError } from 'src/engine/core-modules/workspace/workspace.exception';
|
||||
import { FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { buildFieldMapsFromFlatObjectMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/build-field-maps-from-flat-object-metadata.util';
|
||||
import { FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
@@ -224,13 +227,11 @@ export class DataArgProcessorService {
|
||||
}
|
||||
case FieldMetadataType.RELATION:
|
||||
case FieldMetadataType.MORPH_RELATION: {
|
||||
const fieldMetadataRelationSettings =
|
||||
fieldMetadata.settings as FieldMetadataSettingsMapping['RELATION'];
|
||||
const relationSettings = fieldMetadata.settings as
|
||||
| FieldMetadataSettingsMapping['RELATION']
|
||||
| FieldMetadataSettingsMapping['MORPH_RELATION'];
|
||||
|
||||
if (
|
||||
fieldMetadataRelationSettings.relationType ===
|
||||
RelationType.ONE_TO_MANY
|
||||
) {
|
||||
if (relationSettings.relationType === RelationType.ONE_TO_MANY) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`One-to-many relation ${key} field does not support write operations.`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA,
|
||||
@@ -238,10 +239,27 @@ export class DataArgProcessorService {
|
||||
);
|
||||
}
|
||||
|
||||
if (key === fieldMetadataRelationSettings.joinColumnName) {
|
||||
const joinColumnName = isFlatFieldMetadataOfType(
|
||||
fieldMetadata,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)
|
||||
? computeMorphOrRelationFieldJoinColumnName({
|
||||
name: fieldMetadata.name,
|
||||
})
|
||||
: relationSettings.joinColumnName;
|
||||
|
||||
if (key === joinColumnName) {
|
||||
return validateUUIDFieldOrThrow(value, key);
|
||||
}
|
||||
|
||||
if (isDefined(joinColumnName) && !isRelationNestedOperation(value)) {
|
||||
throw new CommonQueryRunnerException(
|
||||
`Relation "${key}" requires connect or disconnect operation`,
|
||||
CommonQueryRunnerExceptionCode.INVALID_ARGS_DATA,
|
||||
{ userFriendlyMessage: STANDARD_ERROR_MESSAGE },
|
||||
);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
case FieldMetadataType.PHONES: {
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { isObject } from '@sniptt/guards';
|
||||
import { RELATION_NESTED_QUERY_KEYWORDS } from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const { CONNECT, DISCONNECT } = RELATION_NESTED_QUERY_KEYWORDS;
|
||||
|
||||
export const isRelationNestedOperation = (value: unknown): boolean => {
|
||||
if (!isObject(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const obj = value as Record<string, unknown>;
|
||||
|
||||
return (
|
||||
(CONNECT in obj && isDefined(obj[CONNECT])) ||
|
||||
(DISCONNECT in obj && isDefined(obj[DISCONNECT]))
|
||||
);
|
||||
};
|
||||
+4
-1
@@ -92,7 +92,10 @@ export class CommonSelectFieldsHelper {
|
||||
flatEntityId: fieldId,
|
||||
});
|
||||
|
||||
if (!isFlatFieldMetadataOfType(flatField, FieldMetadataType.RELATION)) {
|
||||
if (
|
||||
!isFlatFieldMetadataOfType(flatField, FieldMetadataType.RELATION) &&
|
||||
!isFlatFieldMetadataOfType(flatField, FieldMetadataType.MORPH_RELATION)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -523,7 +523,7 @@ describe('getAllSelectableFields', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should include MANY_TO_ONE relations without joinColumnName as regular fields', () => {
|
||||
it('should compute join column name for MANY_TO_ONE relations without joinColumnName in settings', () => {
|
||||
const nameField = createMockField({
|
||||
id: 'field-1',
|
||||
name: 'name',
|
||||
@@ -555,7 +555,7 @@ describe('getAllSelectableFields', () => {
|
||||
|
||||
expect(result).toEqual({
|
||||
name: true,
|
||||
company: true, // MANY_TO_ONE without joinColumnName are included as regular fields
|
||||
companyId: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+12
-5
@@ -8,9 +8,9 @@ import {
|
||||
type RestrictedFieldsPermissions,
|
||||
compositeTypeDefinitions,
|
||||
} from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
@@ -80,11 +80,18 @@ export const getAllSelectableFields = ({
|
||||
|
||||
result[flatField.name] = compositeFields;
|
||||
} else if (
|
||||
isFlatFieldMetadataOfType(flatField, FieldMetadataType.RELATION) &&
|
||||
flatField.settings.relationType === RelationType.MANY_TO_ONE &&
|
||||
isDefined(flatField.settings.joinColumnName)
|
||||
(isFlatFieldMetadataOfType(flatField, FieldMetadataType.RELATION) ||
|
||||
isFlatFieldMetadataOfType(
|
||||
flatField,
|
||||
FieldMetadataType.MORPH_RELATION,
|
||||
)) &&
|
||||
flatField.settings.relationType === RelationType.MANY_TO_ONE
|
||||
) {
|
||||
result[flatField.settings.joinColumnName] = true;
|
||||
const joinColumnName = computeMorphOrRelationFieldJoinColumnName({
|
||||
name: flatField.name,
|
||||
});
|
||||
|
||||
result[joinColumnName] = true;
|
||||
} else {
|
||||
result[flatField.name] = true;
|
||||
}
|
||||
|
||||
+4
-3
@@ -436,11 +436,12 @@ describe('RestToCommonSelectedFieldsHandler', () => {
|
||||
|
||||
expect(result).toEqual({
|
||||
name: true,
|
||||
companyId: true,
|
||||
company: {
|
||||
name: true,
|
||||
people: {
|
||||
name: true,
|
||||
company: true, // Nested relation at depth 2 shows as boolean
|
||||
companyId: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -573,12 +574,12 @@ describe('RestToCommonSelectedFieldsHandler', () => {
|
||||
depth: 1,
|
||||
});
|
||||
|
||||
// For noteTarget, only note and task relations are included
|
||||
// but company is a MANY_TO_ONE so it's included as a boolean
|
||||
expect(result).toEqual({
|
||||
noteId: true,
|
||||
note: {
|
||||
title: true,
|
||||
},
|
||||
companyId: true,
|
||||
company: {
|
||||
name: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
type FieldMetadataDefaultValue,
|
||||
FieldMetadataType,
|
||||
} from 'twenty-shared/types';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
@@ -99,43 +99,61 @@ const getSchemaComponentsRelationProperties = (
|
||||
>['flatObjectMetadataMaps'],
|
||||
): Properties => {
|
||||
return flatFieldMetadatas.reduce((node, field) => {
|
||||
if (field.type !== FieldMetadataType.RELATION) {
|
||||
const isRelationField =
|
||||
isFieldMetadataEntityOfType(field, FieldMetadataType.RELATION) ||
|
||||
isFieldMetadataEntityOfType(field, FieldMetadataType.MORPH_RELATION);
|
||||
|
||||
if (!isRelationField) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (!isDefined(field.relationTargetObjectMetadataId)) {
|
||||
throw new Error(
|
||||
`Relation field "${field.name}" has no relationTargetObjectMetadataId`,
|
||||
);
|
||||
}
|
||||
|
||||
const relationType = field.settings?.relationType;
|
||||
|
||||
if (!isDefined(relationType)) {
|
||||
throw new Error(
|
||||
`Relation field "${field.name}" has no relationType in settings`,
|
||||
);
|
||||
}
|
||||
|
||||
const targetObjectMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: field.relationTargetObjectMetadataId,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!targetObjectMetadata) {
|
||||
throw new Error(
|
||||
`Relation field "${field.name}" target object metadata not found for id ${field.relationTargetObjectMetadataId}`,
|
||||
);
|
||||
}
|
||||
|
||||
let itemProperty = {} as Property;
|
||||
|
||||
if (isFieldMetadataEntityOfType(field, FieldMetadataType.RELATION)) {
|
||||
const targetObjectMetadata = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: field.relationTargetObjectMetadataId,
|
||||
flatEntityMaps: flatObjectMetadataMaps,
|
||||
});
|
||||
|
||||
if (!targetObjectMetadata) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (field.settings?.relationType === RelationType.MANY_TO_ONE) {
|
||||
itemProperty = {
|
||||
type: 'object',
|
||||
oneOf: [
|
||||
{
|
||||
$ref: `#/components/schemas/${capitalize(
|
||||
targetObjectMetadata.nameSingular,
|
||||
)}ForResponse`,
|
||||
},
|
||||
],
|
||||
};
|
||||
} else if (field.settings?.relationType === RelationType.ONE_TO_MANY) {
|
||||
itemProperty = {
|
||||
type: 'array',
|
||||
items: {
|
||||
if (relationType === RelationType.MANY_TO_ONE) {
|
||||
itemProperty = {
|
||||
type: 'object',
|
||||
oneOf: [
|
||||
{
|
||||
$ref: `#/components/schemas/${capitalize(
|
||||
targetObjectMetadata.nameSingular,
|
||||
)}ForResponse`,
|
||||
},
|
||||
};
|
||||
}
|
||||
],
|
||||
};
|
||||
} else if (relationType === RelationType.ONE_TO_MANY) {
|
||||
itemProperty = {
|
||||
type: 'array',
|
||||
items: {
|
||||
$ref: `#/components/schemas/${capitalize(
|
||||
targetObjectMetadata.nameSingular,
|
||||
)}ForResponse`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (field.description) {
|
||||
|
||||
+13
-8
@@ -1,7 +1,10 @@
|
||||
import { FieldMetadataType, RelationType } from 'twenty-shared/types';
|
||||
|
||||
import { getFlatFieldsFromFlatObjectMetadata } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-flat-fields-for-flat-object-metadata.util';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
import { isMorphOrRelationFlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-morph-or-relation-flat-field-metadata.util';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
|
||||
export type FieldMapsForObject = {
|
||||
@@ -24,14 +27,16 @@ export const buildFieldMapsFromFlatObjectMetadata = (
|
||||
for (const field of objectFields) {
|
||||
fieldIdByName[field.name] = field.id;
|
||||
|
||||
if (isMorphOrRelationFlatFieldMetadata(field)) {
|
||||
const joinColumnName = (
|
||||
field.settings as { joinColumnName?: string } | undefined
|
||||
)?.joinColumnName;
|
||||
if (
|
||||
(isFlatFieldMetadataOfType(field, FieldMetadataType.RELATION) ||
|
||||
isFlatFieldMetadataOfType(field, FieldMetadataType.MORPH_RELATION)) &&
|
||||
field.settings.relationType === RelationType.MANY_TO_ONE
|
||||
) {
|
||||
const joinColumnName = computeMorphOrRelationFieldJoinColumnName({
|
||||
name: field.name,
|
||||
});
|
||||
|
||||
if (joinColumnName) {
|
||||
fieldIdByJoinColumnName[joinColumnName] = field.id;
|
||||
}
|
||||
fieldIdByJoinColumnName[joinColumnName] = field.id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-6
@@ -8,6 +8,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { type FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { isFieldMetadataEntityOfType } from 'src/engine/utils/is-field-metadata-of-type.util';
|
||||
|
||||
@@ -102,13 +103,19 @@ export const convertObjectMetadataToSchemaProperties = ({
|
||||
return node;
|
||||
}
|
||||
|
||||
if (
|
||||
isFieldMetadataEntityOfType(field, FieldMetadataType.RELATION) &&
|
||||
field.settings?.relationType === RelationType.MANY_TO_ONE
|
||||
) {
|
||||
const isRelationManyToOne =
|
||||
(isFieldMetadataEntityOfType(field, FieldMetadataType.RELATION) ||
|
||||
isFieldMetadataEntityOfType(field, FieldMetadataType.MORPH_RELATION)) &&
|
||||
field.settings?.relationType === RelationType.MANY_TO_ONE;
|
||||
|
||||
if (isRelationManyToOne) {
|
||||
const key = computeMorphOrRelationFieldJoinColumnName({
|
||||
name: field.name,
|
||||
});
|
||||
|
||||
return {
|
||||
...node,
|
||||
[`${field.name}Id`]: {
|
||||
[key]: {
|
||||
type: 'string',
|
||||
format: 'uuid',
|
||||
},
|
||||
@@ -116,7 +123,8 @@ export const convertObjectMetadataToSchemaProperties = ({
|
||||
}
|
||||
|
||||
if (
|
||||
isFieldMetadataEntityOfType(field, FieldMetadataType.RELATION) &&
|
||||
(isFieldMetadataEntityOfType(field, FieldMetadataType.RELATION) ||
|
||||
isFieldMetadataEntityOfType(field, FieldMetadataType.MORPH_RELATION)) &&
|
||||
field.settings?.relationType === RelationType.ONE_TO_MANY
|
||||
) {
|
||||
return node;
|
||||
|
||||
+1
-3
@@ -270,9 +270,7 @@ export const prefillWorkflows = async (
|
||||
'{{6e089bc9-aabd-435f-865f-f31c01c8f4a7.email}}',
|
||||
additionalEmails: [],
|
||||
},
|
||||
company: {
|
||||
id: '{{0715b6cd-7cc1-4b98-971b-00f54dfe643b.id}}',
|
||||
},
|
||||
companyId: '{{0715b6cd-7cc1-4b98-971b-00f54dfe643b.id}}',
|
||||
},
|
||||
},
|
||||
outputSchema: {
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import { isObject, isString } from '@sniptt/guards';
|
||||
import { FieldMetadataType, RelationType } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { getFlatFieldsFromFlatObjectMetadata } from 'src/engine/api/graphql/workspace-schema-builder/utils/get-flat-fields-for-flat-object-metadata.util';
|
||||
import { computeMorphOrRelationFieldJoinColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-morph-or-relation-field-join-column-name.util';
|
||||
import { isFlatFieldMetadataOfType } from 'src/engine/metadata-modules/flat-field-metadata/utils/is-flat-field-metadata-of-type.util';
|
||||
import { type ObjectMetadataInfo } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
|
||||
|
||||
const extractLegacyRelationId = (value: unknown): string | undefined => {
|
||||
if (!isObject(value)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const record = value as Record<string, unknown>;
|
||||
|
||||
if (Object.keys(record).length !== 1 || !isString(record.id)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return record.id;
|
||||
};
|
||||
|
||||
export const formatWorkflowRecordRelationFields = (
|
||||
record: Record<string, unknown>,
|
||||
objectMetadataInfo: ObjectMetadataInfo,
|
||||
): Record<string, unknown> => {
|
||||
const { flatObjectMetadata, flatFieldMetadataMaps } = objectMetadataInfo;
|
||||
|
||||
const objectFields = getFlatFieldsFromFlatObjectMetadata(
|
||||
flatObjectMetadata,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
const manyToOneRelationFieldNames = new Set<string>();
|
||||
|
||||
for (const field of objectFields) {
|
||||
if (
|
||||
(isFlatFieldMetadataOfType(field, FieldMetadataType.RELATION) ||
|
||||
isFlatFieldMetadataOfType(field, FieldMetadataType.MORPH_RELATION)) &&
|
||||
field.settings.relationType === RelationType.MANY_TO_ONE
|
||||
) {
|
||||
manyToOneRelationFieldNames.add(field.name);
|
||||
}
|
||||
}
|
||||
|
||||
const formattedRecord: Record<string, unknown> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(record)) {
|
||||
if (!manyToOneRelationFieldNames.has(key)) {
|
||||
formattedRecord[key] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
const legacyId = extractLegacyRelationId(value);
|
||||
|
||||
if (!isDefined(legacyId)) {
|
||||
formattedRecord[key] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
const joinColumnName = computeMorphOrRelationFieldJoinColumnName({
|
||||
name: key,
|
||||
});
|
||||
|
||||
if (!isDefined(record[joinColumnName])) {
|
||||
formattedRecord[joinColumnName] = legacyId;
|
||||
}
|
||||
}
|
||||
|
||||
return formattedRecord;
|
||||
};
|
||||
+7
-1
@@ -11,6 +11,7 @@ import { type WorkflowActionInput } from 'src/modules/workflow/workflow-executor
|
||||
import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { buildWorkflowActorMetadata } from 'src/modules/workflow/workflow-executor/utils/build-workflow-actor-metadata.util';
|
||||
import { filterValidFieldsInRecord } from 'src/modules/workflow/workflow-executor/utils/filter-valid-fields-in-record.util';
|
||||
import { formatWorkflowRecordRelationFields } from 'src/modules/workflow/workflow-executor/utils/format-workflow-record-relation-fields.util';
|
||||
import { findStepOrThrow } from 'src/modules/workflow/workflow-executor/utils/find-step-or-throw.util';
|
||||
import { resolveRichTextFieldsInRecord } from 'src/modules/workflow/workflow-executor/utils/resolve-rich-text-fields-in-record.util';
|
||||
import { type WorkflowCreateRecordActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/types/workflow-record-crud-action-input.type';
|
||||
@@ -58,8 +59,13 @@ export class CreateRecordWorkflowAction implements WorkflowAction {
|
||||
context,
|
||||
) as WorkflowCreateRecordActionInput;
|
||||
|
||||
const filteredObjectRecord = filterValidFieldsInRecord(
|
||||
const formattedObjectRecord = formatWorkflowRecordRelationFields(
|
||||
workflowActionInput.objectRecord,
|
||||
objectMetadataInfo,
|
||||
);
|
||||
|
||||
const filteredObjectRecord = filterValidFieldsInRecord(
|
||||
formattedObjectRecord,
|
||||
objectMetadataInfo.flatObjectMetadata,
|
||||
objectMetadataInfo.flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
+7
-1
@@ -15,6 +15,7 @@ import { type WorkflowActionInput } from 'src/modules/workflow/workflow-executor
|
||||
import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { buildWorkflowActorMetadata } from 'src/modules/workflow/workflow-executor/utils/build-workflow-actor-metadata.util';
|
||||
import { filterValidFieldsInRecord } from 'src/modules/workflow/workflow-executor/utils/filter-valid-fields-in-record.util';
|
||||
import { formatWorkflowRecordRelationFields } from 'src/modules/workflow/workflow-executor/utils/format-workflow-record-relation-fields.util';
|
||||
import { findStepOrThrow } from 'src/modules/workflow/workflow-executor/utils/find-step-or-throw.util';
|
||||
import { resolveRichTextFieldsInRecord } from 'src/modules/workflow/workflow-executor/utils/resolve-rich-text-fields-in-record.util';
|
||||
import { isWorkflowUpdateRecordAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/guards/is-workflow-update-record-action.guard';
|
||||
@@ -81,8 +82,13 @@ export class UpdateRecordWorkflowAction implements WorkflowAction {
|
||||
);
|
||||
}
|
||||
|
||||
const filteredObjectRecord = filterValidFieldsInRecord(
|
||||
const formattedObjectRecord = formatWorkflowRecordRelationFields(
|
||||
workflowActionInput.objectRecord,
|
||||
objectMetadataInfo,
|
||||
);
|
||||
|
||||
const filteredObjectRecord = filterValidFieldsInRecord(
|
||||
formattedObjectRecord,
|
||||
objectMetadataInfo.flatObjectMetadata,
|
||||
objectMetadataInfo.flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
+7
-1
@@ -14,6 +14,7 @@ import { WorkflowExecutionContextService } from 'src/modules/workflow/workflow-e
|
||||
import { type WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { filterValidFieldsInRecord } from 'src/modules/workflow/workflow-executor/utils/filter-valid-fields-in-record.util';
|
||||
import { formatWorkflowRecordRelationFields } from 'src/modules/workflow/workflow-executor/utils/format-workflow-record-relation-fields.util';
|
||||
import { findStepOrThrow } from 'src/modules/workflow/workflow-executor/utils/find-step-or-throw.util';
|
||||
import { resolveRichTextFieldsInRecord } from 'src/modules/workflow/workflow-executor/utils/resolve-rich-text-fields-in-record.util';
|
||||
import { isWorkflowUpsertRecordAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/guards/is-workflow-upsert-record-action.guard';
|
||||
@@ -76,8 +77,13 @@ export class UpsertRecordWorkflowAction implements WorkflowAction {
|
||||
);
|
||||
}
|
||||
|
||||
const filteredObjectRecord = filterValidFieldsInRecord(
|
||||
const formattedObjectRecord = formatWorkflowRecordRelationFields(
|
||||
workflowActionInput.objectRecord,
|
||||
objectMetadataInfo,
|
||||
);
|
||||
|
||||
const filteredObjectRecord = filterValidFieldsInRecord(
|
||||
formattedObjectRecord,
|
||||
objectMetadataInfo.flatObjectMetadata,
|
||||
objectMetadataInfo.flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user