Move duplicate key error handling in ORM (#13893)

Error available in REST API
<img width="1360" height="653" alt="Screenshot 2025-08-13 at 11 48 19"
src="https://github.com/user-attachments/assets/a055859d-ce54-4ff6-9aba-fac48e03a98d"
/>

`curl http://localhost:3000/rest/people \
  --request POST \
  --header 'Content-Type: application/json' \
  --data '{
  "emails": {
    "primaryEmail": "test@test.com"
  }
}'`

closes https://github.com/twentyhq/twenty/issues/13567
This commit is contained in:
Etienne
2025-08-14 18:38:04 +02:00
committed by GitHub
parent 5c61daa201
commit f6d0af5fb8
13 changed files with 156 additions and 50 deletions
@@ -0,0 +1,36 @@
import { isDefined } from 'twenty-shared/utils';
import {
type EntityTarget,
InstanceChecker,
type ObjectLiteral,
type SaveOptions,
} from 'typeorm';
import { type DeepPartialWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/deep-partial-entity-with-nested-relation-fields.type';
export const getEntityTarget = <
Entity extends ObjectLiteral,
T extends DeepPartialWithNestedRelationFields<Entity>,
>(
targetOrEntity: EntityTarget<Entity> | Entity | Entity[],
entityOrMaybeOptions:
| T
| T[]
| SaveOptions
| (SaveOptions & { reload: false }),
) => {
const isEntityTarget =
(typeof targetOrEntity === 'function' ||
InstanceChecker.isEntitySchema(targetOrEntity) ||
typeof targetOrEntity === 'string') &&
isDefined(targetOrEntity);
const entityTarget = isEntityTarget ? targetOrEntity : null;
if (entityTarget) return entityTarget;
const entityData = isEntityTarget ? entityOrMaybeOptions : targetOrEntity;
const isEntityArray = Array.isArray(entityData);
return isEntityArray ? entityData[0]?.constructor : entityData.constructor;
};
@@ -44,6 +44,7 @@ import { type ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-module
import { type WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
import { type DeepPartialWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/deep-partial-entity-with-nested-relation-fields.type';
import { type QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
import { getEntityTarget } from 'src/engine/twenty-orm/entity-manager/utils/get-entity-target';
import { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception';
import { RelationNestedQueries } from 'src/engine/twenty-orm/relation-nested-queries/relation-nested-queries';
import {
@@ -1230,7 +1231,12 @@ export class WorkspaceEntityManager extends EntityManager {
return isEntityArray ? formattedResult : formattedResult[0];
} catch (error) {
throw computeTwentyORMException(error);
const objectMetadataItem = getObjectMetadataFromEntityTarget(
getEntityTarget(targetOrEntity, entityOrMaybeOptions),
this.internalContext,
);
throw computeTwentyORMException(error, objectMetadataItem);
}
}