Fix relation field unknown target object (#13129)

Fixes https://github.com/twentyhq/twenty/issues/12867

Issue:
when you have a variable `toto` which is: `Record<string, MyType>` and
you do toto['xxx'], this will be typed as `MyType` instead of `MyType |
undefined`

Solutions:
- activate `noUncheckedIndexedAccess` check in tsconfig, this is the
preferred solution but will take time to get there (this raises 600+
errors)
- use a Map: cf https://github.com/twentyhq/twenty/pull/13125/files
- set the type to Partial<Record<string, MyType>>. Drawback is that when
you do Object.values(toto), you'll get `Array<MyType | undefined>`.
Hence why we have to filter these behind


<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/d0a0bfed-c441-4e53-84c2-2da98ccbcf50"
/>
This commit is contained in:
Charles Bochet
2025-07-09 15:43:11 +02:00
committed by GitHub
parent 156cb1b52f
commit 867619247f
23 changed files with 170 additions and 71 deletions

View File

@ -166,6 +166,13 @@ export class FieldMetadataRelationService {
relationCreationPayload.targetObjectMetadataId
];
if (!isDefined(objectMetadataTarget)) {
throw new FieldMetadataException(
`Object metadata relation target not found for relation creation payload`,
FieldMetadataExceptionCode.FIELD_METADATA_RELATION_MALFORMED,
);
}
validateFieldNameAvailabilityOrThrow(
computedMetadataNameFromLabel,
objectMetadataTarget,

View File

@ -107,7 +107,9 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
let existingFieldMetadata: FieldMetadataInterface | undefined;
for (const objectMetadataItem of Object.values(objectMetadataMaps.byId)) {
for (const objectMetadataItem of Object.values(
objectMetadataMaps.byId,
).filter(isDefined)) {
const fieldMetadata = objectMetadataItem.fieldsById[id];
if (fieldMetadata) {
@ -126,6 +128,13 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
const objectMetadataItemWithFieldMaps =
objectMetadataMaps.byId[existingFieldMetadata.objectMetadataId];
if (!isDefined(objectMetadataItemWithFieldMaps)) {
throw new FieldMetadataException(
'Object metadata does not exist',
FieldMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
);
}
const queryRunner = this.coreDataSource.createQueryRunner();
await queryRunner.connect();
@ -703,7 +712,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
isRemoteCreation,
}: {
createdFieldMetadataItems: FieldMetadataEntity[];
objectMetadataMap: Record<string, ObjectMetadataItemWithFieldMaps>;
objectMetadataMap: ObjectMetadataMaps['byId'];
isRemoteCreation: boolean;
}): Promise<WorkspaceMigrationTableAction[]> {
if (isRemoteCreation) {
@ -726,10 +735,18 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
}
}
const objectMetadata =
objectMetadataMap[createdFieldMetadata.objectMetadataId];
if (!isDefined(objectMetadata)) {
throw new FieldMetadataException(
'Object metadata does not exist',
FieldMetadataExceptionCode.OBJECT_METADATA_NOT_FOUND,
);
}
migrationActions.push({
name: computeObjectTargetTable(
objectMetadataMap[createdFieldMetadata.objectMetadataId],
),
name: computeObjectTargetTable(objectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: this.workspaceMigrationFactory.createColumnActions(
WorkspaceMigrationColumnActionType.CREATE,