feat: manually implement joinColumn (#6022)
This PR introduce a new decorator named `@WorkspaceJoinColumn`, the goal
of this one is to manually declare the join columns inside the workspace
entities, so we don't have to rely on `ObjectRecord` type.
This decorator can be used that way:
```typescript
@WorkspaceRelation({
standardId: ACTIVITY_TARGET_STANDARD_FIELD_IDS.company,
type: RelationMetadataType.MANY_TO_ONE,
label: 'Company',
description: 'ActivityTarget company',
icon: 'IconBuildingSkyscraper',
inverseSideTarget: () => CompanyWorkspaceEntity,
inverseSideFieldKey: 'activityTargets',
})
@WorkspaceIsNullable()
company: Relation<CompanyWorkspaceEntity> | null;
// The argument is the name of the relation above
@WorkspaceJoinColumn('company')
companyId: string | null;
```
This commit is contained in:
@ -31,9 +31,7 @@ export class TimelineCalendarEventService {
|
||||
const calendarEventIds = await this.calendarEventRepository.find({
|
||||
where: {
|
||||
calendarEventParticipants: {
|
||||
person: {
|
||||
id: Any(personIds),
|
||||
},
|
||||
personId: Any(personIds),
|
||||
},
|
||||
},
|
||||
select: {
|
||||
@ -81,8 +79,8 @@ export class TimelineCalendarEventService {
|
||||
const participants = event.calendarEventParticipants.map(
|
||||
(participant) => ({
|
||||
calendarEventId: event.id,
|
||||
personId: participant.person?.id ?? null,
|
||||
workspaceMemberId: participant.workspaceMember?.id ?? null,
|
||||
personId: participant.personId ?? null,
|
||||
workspaceMemberId: participant.workspaceMemberId ?? null,
|
||||
firstName:
|
||||
participant.person?.name?.firstName ||
|
||||
participant.workspaceMember?.name.firstName ||
|
||||
@ -135,9 +133,7 @@ export class TimelineCalendarEventService {
|
||||
): Promise<TimelineCalendarEventsWithTotal> {
|
||||
const personIds = await this.personRepository.find({
|
||||
where: {
|
||||
company: {
|
||||
id: companyId,
|
||||
},
|
||||
companyId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
|
||||
@ -12,7 +12,6 @@ import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
import { ObjectRecordDeleteEvent } from 'src/engine/integrations/event-emitter/types/object-record-delete.event';
|
||||
import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record';
|
||||
import { WorkspaceService } from 'src/engine/core-modules/workspace/services/workspace.service';
|
||||
|
||||
export class UserService extends TypeOrmQueryService<User> {
|
||||
@ -113,8 +112,7 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
`SELECT * FROM ${dataSourceMetadata.schema}."workspaceMember"`,
|
||||
);
|
||||
const workspaceMember = workspaceMembers.filter(
|
||||
(member: ObjectRecord<WorkspaceMemberWorkspaceEntity>) =>
|
||||
member.userId === userId,
|
||||
(member: WorkspaceMemberWorkspaceEntity) => member.userId === userId,
|
||||
)?.[0];
|
||||
|
||||
assert(workspaceMember, 'WorkspaceMember not found');
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage';
|
||||
|
||||
export function WorkspaceJoinColumn(
|
||||
relationPropertyKey: string,
|
||||
): PropertyDecorator {
|
||||
return (object, propertyKey) => {
|
||||
metadataArgsStorage.addJoinColumns({
|
||||
target: object.constructor,
|
||||
relationName: relationPropertyKey,
|
||||
joinColumn: propertyKey.toString(),
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -8,35 +8,19 @@ import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args
|
||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
|
||||
interface WorkspaceBaseRelationOptions<TType, TClass> {
|
||||
interface WorkspaceRelationOptions<TClass> {
|
||||
standardId: string;
|
||||
label: string | ((objectMetadata: ObjectMetadataEntity) => string);
|
||||
description?: string | ((objectMetadata: ObjectMetadataEntity) => string);
|
||||
icon?: string;
|
||||
type: TType;
|
||||
type: RelationMetadataType;
|
||||
inverseSideTarget: () => ObjectType<TClass>;
|
||||
inverseSideFieldKey?: keyof TClass;
|
||||
onDelete?: RelationOnDeleteAction;
|
||||
}
|
||||
|
||||
export interface WorkspaceManyToOneRelationOptions<TClass>
|
||||
extends WorkspaceBaseRelationOptions<
|
||||
RelationMetadataType.MANY_TO_ONE | RelationMetadataType.ONE_TO_ONE,
|
||||
TClass
|
||||
> {
|
||||
joinColumn?: string;
|
||||
}
|
||||
|
||||
export interface WorkspaceOtherRelationOptions<TClass>
|
||||
extends WorkspaceBaseRelationOptions<
|
||||
RelationMetadataType.ONE_TO_MANY | RelationMetadataType.MANY_TO_MANY,
|
||||
TClass
|
||||
> {}
|
||||
|
||||
export function WorkspaceRelation<TClass extends object>(
|
||||
options:
|
||||
| WorkspaceManyToOneRelationOptions<TClass>
|
||||
| WorkspaceOtherRelationOptions<TClass>,
|
||||
options: WorkspaceRelationOptions<TClass>,
|
||||
): PropertyDecorator {
|
||||
return (object, propertyKey) => {
|
||||
const isPrimary =
|
||||
@ -63,14 +47,6 @@ export function WorkspaceRelation<TClass extends object>(
|
||||
propertyKey.toString(),
|
||||
);
|
||||
|
||||
let joinColumn: string | undefined;
|
||||
|
||||
if ('joinColumn' in options) {
|
||||
joinColumn = options.joinColumn
|
||||
? options.joinColumn
|
||||
: `${propertyKey.toString()}Id`;
|
||||
}
|
||||
|
||||
metadataArgsStorage.addRelations({
|
||||
target: object.constructor,
|
||||
standardId: options.standardId,
|
||||
@ -82,7 +58,6 @@ export function WorkspaceRelation<TClass extends object>(
|
||||
inverseSideTarget: options.inverseSideTarget,
|
||||
inverseSideFieldKey: options.inverseSideFieldKey as string | undefined,
|
||||
onDelete: options.onDelete,
|
||||
joinColumn,
|
||||
isPrimary,
|
||||
isNullable,
|
||||
isSystem,
|
||||
|
||||
@ -4,6 +4,7 @@ import { ColumnType, EntitySchemaColumnOptions } from 'typeorm';
|
||||
|
||||
import { WorkspaceFieldMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-field-metadata-args.interface';
|
||||
import { WorkspaceRelationMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface';
|
||||
import { WorkspaceJoinColumnsMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-join-columns-metadata-args.interface';
|
||||
|
||||
import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
||||
import { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||
@ -12,6 +13,7 @@ import { computeCompositeColumnName } from 'src/engine/metadata-modules/field-me
|
||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||
import { compositeTypeDefintions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { getJoinColumn } from 'src/engine/twenty-orm/utils/get-join-column.util';
|
||||
|
||||
type EntitySchemaColumnMap = {
|
||||
[key: string]: EntitySchemaColumnOptions;
|
||||
@ -22,6 +24,7 @@ export class EntitySchemaColumnFactory {
|
||||
create(
|
||||
fieldMetadataArgsCollection: WorkspaceFieldMetadataArgs[],
|
||||
relationMetadataArgsCollection: WorkspaceRelationMetadataArgs[],
|
||||
joinColumnsMetadataArgsCollection: WorkspaceJoinColumnsMetadataArgs[],
|
||||
): EntitySchemaColumnMap {
|
||||
let entitySchemaColumnMap: EntitySchemaColumnMap = {};
|
||||
|
||||
@ -56,9 +59,14 @@ export class EntitySchemaColumnFactory {
|
||||
};
|
||||
|
||||
for (const relationMetadataArgs of relationMetadataArgsCollection) {
|
||||
if (relationMetadataArgs.joinColumn) {
|
||||
entitySchemaColumnMap[relationMetadataArgs.joinColumn] = {
|
||||
name: relationMetadataArgs.joinColumn,
|
||||
const joinColumn = getJoinColumn(
|
||||
joinColumnsMetadataArgsCollection,
|
||||
relationMetadataArgs,
|
||||
);
|
||||
|
||||
if (joinColumn) {
|
||||
entitySchemaColumnMap[joinColumn] = {
|
||||
name: joinColumn,
|
||||
type: 'uuid',
|
||||
nullable: relationMetadataArgs.isNullable,
|
||||
};
|
||||
|
||||
@ -4,9 +4,11 @@ import { EntitySchemaRelationOptions } from 'typeorm';
|
||||
import { RelationType } from 'typeorm/metadata/types/RelationTypes';
|
||||
|
||||
import { WorkspaceRelationMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface';
|
||||
import { WorkspaceJoinColumnsMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-join-columns-metadata-args.interface';
|
||||
|
||||
import { convertClassNameToObjectMetadataName } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util';
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { getJoinColumn } from 'src/engine/twenty-orm/utils/get-join-column.util';
|
||||
|
||||
type EntitySchemaRelationMap = {
|
||||
[key: string]: EntitySchemaRelationOptions;
|
||||
@ -18,6 +20,7 @@ export class EntitySchemaRelationFactory {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
target: Function,
|
||||
relationMetadataArgsCollection: WorkspaceRelationMetadataArgs[],
|
||||
joinColumnsMetadataArgsCollection: WorkspaceJoinColumnsMetadataArgs[],
|
||||
): EntitySchemaRelationMap {
|
||||
const entitySchemaRelationMap: EntitySchemaRelationMap = {};
|
||||
|
||||
@ -27,16 +30,19 @@ export class EntitySchemaRelationFactory {
|
||||
const oppositeObjectName = convertClassNameToObjectMetadataName(
|
||||
oppositeTarget.name,
|
||||
);
|
||||
|
||||
const relationType = this.getRelationType(relationMetadataArgs);
|
||||
const joinColumn = getJoinColumn(
|
||||
joinColumnsMetadataArgsCollection,
|
||||
relationMetadataArgs,
|
||||
);
|
||||
|
||||
entitySchemaRelationMap[relationMetadataArgs.name] = {
|
||||
type: relationType,
|
||||
target: oppositeObjectName,
|
||||
inverseSide: relationMetadataArgs.inverseSideFieldKey ?? objectName,
|
||||
joinColumn: relationMetadataArgs.joinColumn
|
||||
joinColumn: joinColumn
|
||||
? {
|
||||
name: relationMetadataArgs.joinColumn,
|
||||
name: joinColumn,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
|
||||
@ -23,17 +23,21 @@ export class EntitySchemaFactory {
|
||||
|
||||
const fieldMetadataArgsCollection =
|
||||
metadataArgsStorage.filterFields(target);
|
||||
const joinColumnsMetadataArgsCollection =
|
||||
metadataArgsStorage.filterJoinColumns(target);
|
||||
const relationMetadataArgsCollection =
|
||||
metadataArgsStorage.filterRelations(target);
|
||||
|
||||
const columns = this.entitySchemaColumnFactory.create(
|
||||
fieldMetadataArgsCollection,
|
||||
relationMetadataArgsCollection,
|
||||
joinColumnsMetadataArgsCollection,
|
||||
);
|
||||
|
||||
const relations = this.entitySchemaRelationFactory.create(
|
||||
target,
|
||||
relationMetadataArgsCollection,
|
||||
joinColumnsMetadataArgsCollection,
|
||||
);
|
||||
|
||||
const entitySchema = new EntitySchema({
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
export interface WorkspaceJoinColumnsMetadataArgs {
|
||||
/**
|
||||
* Class to which relation is applied.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
readonly target: Function;
|
||||
|
||||
/**
|
||||
* Relation name.
|
||||
*/
|
||||
readonly relationName: string;
|
||||
|
||||
/**
|
||||
* Relation label.
|
||||
*/
|
||||
readonly joinColumn: string;
|
||||
}
|
||||
@ -62,11 +62,6 @@ export interface WorkspaceRelationMetadataArgs {
|
||||
*/
|
||||
readonly onDelete?: RelationOnDeleteAction;
|
||||
|
||||
/**
|
||||
* Relation join column.
|
||||
*/
|
||||
readonly joinColumn?: string;
|
||||
|
||||
/**
|
||||
* Is primary field.
|
||||
*/
|
||||
|
||||
@ -6,6 +6,7 @@ import { WorkspaceEntityMetadataArgs } from 'src/engine/twenty-orm/interfaces/wo
|
||||
import { WorkspaceRelationMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface';
|
||||
import { WorkspaceExtendedEntityMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-extended-entity-metadata-args.interface';
|
||||
import { WorkspaceIndexMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-index-metadata-args.interface';
|
||||
import { WorkspaceJoinColumnsMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-join-columns-metadata-args.interface';
|
||||
|
||||
export class MetadataArgsStorage {
|
||||
private readonly entities: WorkspaceEntityMetadataArgs[] = [];
|
||||
@ -15,6 +16,7 @@ export class MetadataArgsStorage {
|
||||
private readonly dynamicRelations: WorkspaceDynamicRelationMetadataArgs[] =
|
||||
[];
|
||||
private readonly indexes: WorkspaceIndexMetadataArgs[] = [];
|
||||
private readonly joinColumns: WorkspaceJoinColumnsMetadataArgs[] = [];
|
||||
|
||||
addEntities(...entities: WorkspaceEntityMetadataArgs[]): void {
|
||||
this.entities.push(...entities);
|
||||
@ -44,6 +46,10 @@ export class MetadataArgsStorage {
|
||||
this.dynamicRelations.push(...dynamicRelations);
|
||||
}
|
||||
|
||||
addJoinColumns(...joinColumns: WorkspaceJoinColumnsMetadataArgs[]): void {
|
||||
this.joinColumns.push(...joinColumns);
|
||||
}
|
||||
|
||||
filterEntities(
|
||||
target: Function | string,
|
||||
): WorkspaceEntityMetadataArgs | undefined;
|
||||
@ -123,6 +129,20 @@ export class MetadataArgsStorage {
|
||||
return this.filterByTarget(this.dynamicRelations, target);
|
||||
}
|
||||
|
||||
filterJoinColumns(
|
||||
target: Function | string,
|
||||
): WorkspaceJoinColumnsMetadataArgs[];
|
||||
|
||||
filterJoinColumns(
|
||||
target: (Function | string)[],
|
||||
): WorkspaceJoinColumnsMetadataArgs[];
|
||||
|
||||
filterJoinColumns(
|
||||
target: (Function | string) | (Function | string)[],
|
||||
): WorkspaceJoinColumnsMetadataArgs[] {
|
||||
return this.filterByTarget(this.joinColumns, target);
|
||||
}
|
||||
|
||||
protected filterByTarget<T extends { target: Function | string }>(
|
||||
array: T[],
|
||||
target: (Function | string) | (Function | string)[],
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
import { WorkspaceJoinColumnsMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-join-columns-metadata-args.interface';
|
||||
import { WorkspaceRelationMetadataArgs } from 'src/engine/twenty-orm/interfaces/workspace-relation-metadata-args.interface';
|
||||
|
||||
import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args.storage';
|
||||
|
||||
export const getJoinColumn = (
|
||||
joinColumnsMetadataArgsCollection: WorkspaceJoinColumnsMetadataArgs[],
|
||||
relationMetadataArgs: WorkspaceRelationMetadataArgs,
|
||||
opposite = false,
|
||||
): string | null => {
|
||||
if (
|
||||
relationMetadataArgs.type === RelationMetadataType.ONE_TO_MANY ||
|
||||
relationMetadataArgs.type === RelationMetadataType.MANY_TO_MANY
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const inverseSideTarget = relationMetadataArgs.inverseSideTarget();
|
||||
const inverseSideJoinColumnsMetadataArgsCollection =
|
||||
metadataArgsStorage.filterJoinColumns(inverseSideTarget);
|
||||
const filteredJoinColumnsMetadataArgsCollection =
|
||||
joinColumnsMetadataArgsCollection.filter(
|
||||
(joinColumnsMetadataArgs) =>
|
||||
joinColumnsMetadataArgs.relationName === relationMetadataArgs.name,
|
||||
);
|
||||
const oppositeFilteredJoinColumnsMetadataArgsCollection =
|
||||
inverseSideJoinColumnsMetadataArgsCollection.filter(
|
||||
(joinColumnsMetadataArgs) =>
|
||||
joinColumnsMetadataArgs.relationName === relationMetadataArgs.name,
|
||||
);
|
||||
|
||||
if (
|
||||
filteredJoinColumnsMetadataArgsCollection.length > 0 &&
|
||||
oppositeFilteredJoinColumnsMetadataArgsCollection.length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`Join column for ${relationMetadataArgs.name} relation is present on both sides`,
|
||||
);
|
||||
}
|
||||
|
||||
// If we're in a ONE_TO_ONE relation and there are no join columns, we need to find the join column on the inverse side
|
||||
if (
|
||||
relationMetadataArgs.type === RelationMetadataType.ONE_TO_ONE &&
|
||||
filteredJoinColumnsMetadataArgsCollection.length === 0 &&
|
||||
!opposite
|
||||
) {
|
||||
const inverseSideRelationMetadataArgsCollection =
|
||||
metadataArgsStorage.filterRelations(inverseSideTarget);
|
||||
const inverseSideRelationMetadataArgs =
|
||||
inverseSideRelationMetadataArgsCollection.find(
|
||||
(inverseSideRelationMetadataArgs) =>
|
||||
inverseSideRelationMetadataArgs.inverseSideFieldKey ===
|
||||
relationMetadataArgs.name,
|
||||
);
|
||||
|
||||
if (!inverseSideRelationMetadataArgs) {
|
||||
throw new Error(
|
||||
`Inverse side join column of relation ${relationMetadataArgs.name} is missing`,
|
||||
);
|
||||
}
|
||||
|
||||
return getJoinColumn(
|
||||
inverseSideJoinColumnsMetadataArgsCollection,
|
||||
inverseSideRelationMetadataArgs,
|
||||
// Avoid infinite recursion
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
// Check if there are multiple join columns for the relation
|
||||
if (filteredJoinColumnsMetadataArgsCollection.length > 1) {
|
||||
throw new Error(
|
||||
`Multiple join columns found for relation ${relationMetadataArgs.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
const joinColumnsMetadataArgs = filteredJoinColumnsMetadataArgsCollection[0];
|
||||
|
||||
if (!joinColumnsMetadataArgs) {
|
||||
throw new Error(
|
||||
`Join column is missing for relation ${relationMetadataArgs.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
return joinColumnsMetadataArgs.joinColumn;
|
||||
};
|
||||
@ -16,6 +16,7 @@ import { metadataArgsStorage } from 'src/engine/twenty-orm/storage/metadata-args
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { createDeterministicUuid } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util';
|
||||
import { getJoinColumn } from 'src/engine/twenty-orm/utils/get-join-column.util';
|
||||
|
||||
@Injectable()
|
||||
export class StandardFieldFactory {
|
||||
@ -139,6 +140,14 @@ export class StandardFieldFactory {
|
||||
const foreignKeyStandardId = createDeterministicUuid(
|
||||
workspaceRelationMetadataArgs.standardId,
|
||||
);
|
||||
const joinColumnMetadataArgsCollection =
|
||||
metadataArgsStorage.filterJoinColumns(
|
||||
workspaceRelationMetadataArgs.target,
|
||||
);
|
||||
const joinColumn = getJoinColumn(
|
||||
joinColumnMetadataArgsCollection,
|
||||
workspaceRelationMetadataArgs,
|
||||
);
|
||||
|
||||
if (
|
||||
isGatedAndNotEnabled(
|
||||
@ -149,11 +158,11 @@ export class StandardFieldFactory {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (workspaceRelationMetadataArgs.joinColumn) {
|
||||
if (joinColumn) {
|
||||
fieldMetadataCollection.push({
|
||||
type: FieldMetadataType.UUID,
|
||||
standardId: foreignKeyStandardId,
|
||||
name: workspaceRelationMetadataArgs.joinColumn,
|
||||
name: joinColumn,
|
||||
label: `${workspaceRelationMetadataArgs.label} id (foreign key)`,
|
||||
description: `${workspaceRelationMetadataArgs.description} id foreign key`,
|
||||
icon: workspaceRelationMetadataArgs.icon,
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
import { BaseWorkspaceEntity } from 'src/engine/twenty-orm/base.workspace-entity';
|
||||
|
||||
type RelationKeys<T> = {
|
||||
[K in keyof T]: NonNullable<T[K]> extends BaseWorkspaceEntity ? K : never;
|
||||
}[keyof T];
|
||||
|
||||
type ForeignKeyMap<T> = {
|
||||
[K in RelationKeys<T> as `${K & string}Id`]: string;
|
||||
};
|
||||
|
||||
type RecursiveObjectRecord<T> = {
|
||||
[P in keyof T]: NonNullable<T[P]> extends BaseWorkspaceEntity
|
||||
? ObjectRecord<NonNullable<T[P]>> & ForeignKeyMap<NonNullable<T[P]>>
|
||||
: T[P];
|
||||
};
|
||||
|
||||
// TODO: We should get rid of that it's causing too much issues
|
||||
// Some relations can be null or undefined because they're not mendatory and other cannot
|
||||
// This utility type put as defined all the joinColumn, so it's not well typed
|
||||
export type ObjectRecord<T> = RecursiveObjectRecord<T> & ForeignKeyMap<T>;
|
||||
Reference in New Issue
Block a user