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:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user