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:
Abdullah.
2026-03-06 18:21:19 +05:00
committed by GitHub
parent 1f1da901ea
commit 9f9a6a45dd
17 changed files with 238 additions and 70 deletions
@@ -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;