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,15 +166,16 @@ export class WorkspaceDatasourceFactory {
);
} else {
const entitySchemas = await Promise.all(
Object.values(cachedObjectMetadataMaps.byId).map(
(objectMetadata) =>
Object.values(cachedObjectMetadataMaps.byId)
.filter(isDefined)
.map((objectMetadata) =>
this.entitySchemaFactory.create(
workspaceId,
dataSourceMetadataVersion,
objectMetadata,
cachedObjectMetadataMaps,
),
),
),
);
await this.workspaceCacheStorageService.setORMEntitySchema(

View File

@ -1,4 +1,6 @@
import { isNonEmptyString } from '@sniptt/guards';
import { ObjectRecordsPermissions } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { QueryExpressionMap } from 'typeorm/query-builder/QueryExpressionMap';
import {
@ -40,8 +42,23 @@ export const validateOperationIsPermittedOrThrow = ({
const objectMetadataIdForEntity =
objectMetadataMaps.idByNameSingular[entityName];
const objectMetadataIsSystem =
objectMetadataMaps.byId[objectMetadataIdForEntity]?.isSystem === true;
if (!isNonEmptyString(objectMetadataIdForEntity)) {
throw new PermissionsException(
PermissionsExceptionMessage.PERMISSION_DENIED,
PermissionsExceptionCode.PERMISSION_DENIED,
);
}
const objectMetadata = objectMetadataMaps.byId[objectMetadataIdForEntity];
if (!isDefined(objectMetadata)) {
throw new PermissionsException(
PermissionsExceptionMessage.PERMISSION_DENIED,
PermissionsExceptionCode.PERMISSION_DENIED,
);
}
const objectMetadataIsSystem = objectMetadata.isSystem === true;
if (objectMetadataIsSystem) {
return;