Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: manually implement joinColumn #6022

Merged
merged 10 commits into from
Jun 27, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ export class TimelineCalendarEventService {
const calendarEventIds = await this.calendarEventRepository.find({
where: {
calendarEventParticipants: {
person: {
id: Any(personIds),
},
personId: Any(personIds),
},
},
select: {
Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -135,9 +133,7 @@ export class TimelineCalendarEventService {
): Promise<TimelineCalendarEventsWithTotal> {
const personIds = await this.personRepository.find({
where: {
company: {
id: companyId,
},
companyId,
},
select: {
id: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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');
Expand Down
Original file line number Diff line number Diff line change
@@ -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(),
});
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -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,
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand All @@ -22,6 +24,7 @@ export class EntitySchemaColumnFactory {
create(
fieldMetadataArgsCollection: WorkspaceFieldMetadataArgs[],
relationMetadataArgsCollection: WorkspaceRelationMetadataArgs[],
joinColumnsMetadataArgsCollection: WorkspaceJoinColumnsMetadataArgs[],
): EntitySchemaColumnMap {
let entitySchemaColumnMap: EntitySchemaColumnMap = {};

Expand Down Expand Up @@ -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,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 = {};

Expand All @@ -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,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,6 @@ export interface WorkspaceRelationMetadataArgs {
*/
readonly onDelete?: RelationOnDeleteAction;

/**
* Relation join column.
*/
readonly joinColumn?: string;

/**
* Is primary field.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = [];
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)[],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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,
): string | null => {
if (
relationMetadataArgs.type === RelationMetadataType.ONE_TO_MANY ||
relationMetadataArgs.type === RelationMetadataType.MANY_TO_MANY
) {
return null;
}

const filteredJoinColumnsMetadataArgsCollection =
joinColumnsMetadataArgsCollection.filter(
(joinColumnsMetadataArgs) =>
joinColumnsMetadataArgs.relationName === relationMetadataArgs.name,
);

// 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
) {
const inverseSideTarget = relationMetadataArgs.inverseSideTarget();
const inverseSideJoinColumnsMetadataArgsCollection =
metadataArgsStorage.filterJoinColumns(inverseSideTarget);
const inverseSideRelationMetadataArgsCollection =
metadataArgsStorage.filterRelations(inverseSideTarget);
const inverseSideRelationMetadataArgs =
inverseSideRelationMetadataArgsCollection.find(
(inverseSideRelationMetadataArgs) =>
inverseSideRelationMetadataArgs.inverseSideFieldKey ===
relationMetadataArgs.name,
);

if (!inverseSideRelationMetadataArgs) {
throw new Error(
`Inverse side relation metadata args are missing for relation ${relationMetadataArgs.name}`,
);
}

return getJoinColumn(
inverseSideJoinColumnsMetadataArgsCollection,
inverseSideRelationMetadataArgs,
);
}

// Check if there are multiple join columns for the relation
if (filteredJoinColumnsMetadataArgsCollection.length > 1) {
throw new Error(
`Multiple join columns metadata args found for relation ${relationMetadataArgs.name}`,
);
}

const joinColumnsMetadataArgs = filteredJoinColumnsMetadataArgsCollection[0];

if (!joinColumnsMetadataArgs) {
throw new Error(
`Join columns metadata args are missing for relation ${relationMetadataArgs.name}`,
);
}

return joinColumnsMetadataArgs.joinColumn;
};
Loading
Loading