Add note and task target relation creation for customs (#6515)

As title

Urgent bug to fix, I will take the time to refacto this code next week

---------

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
Thomas Trompette
2024-08-02 18:04:32 +02:00
committed by GitHub
parent 268a0b09e0
commit 277c51d58e
3 changed files with 312 additions and 28 deletions

View File

@ -3,58 +3,59 @@ import { InjectRepository } from '@nestjs/typeorm';
import console from 'console'; import console from 'console';
import { FindManyOptions, FindOneOptions, Repository } from 'typeorm';
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import { Query, QueryOptions } from '@ptc-org/nestjs-query-core'; import { Query, QueryOptions } from '@ptc-org/nestjs-query-core';
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import { FindManyOptions, FindOneOptions, Repository } from 'typeorm';
import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface'; import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service'; import { TypeORMService } from 'src/database/typeorm/typeorm.service';
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationColumnDrop,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { import {
FieldMetadataEntity, FieldMetadataEntity,
FieldMetadataType, FieldMetadataType,
} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { TypeORMService } from 'src/database/typeorm/typeorm.service'; import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input';
import { UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
import {
ObjectMetadataException,
ObjectMetadataExceptionCode,
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
import { buildMigrationsForCustomObjectRelations } from 'src/engine/metadata-modules/object-metadata/utils/build-migrations-for-custom-object-relations.util';
import { validateObjectMetadataInputOrThrow } from 'src/engine/metadata-modules/object-metadata/utils/validate-object-metadata-input.util';
import { import {
RelationMetadataEntity, RelationMetadataEntity,
RelationMetadataType, RelationMetadataType,
RelationOnDeleteAction, RelationOnDeleteAction,
} from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import { DeleteOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/delete-object.input';
import { RelationToDelete } from 'src/engine/metadata-modules/relation-metadata/types/relation-to-delete'; import { RelationToDelete } from 'src/engine/metadata-modules/relation-metadata/types/relation-to-delete';
import { RemoteTableRelationsService } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table-relations/remote-table-relations.service';
import { mapUdtNameToFieldType } from 'src/engine/metadata-modules/remote-server/remote-table/utils/udt-name-mapper.util';
import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service';
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util'; import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
import {
WorkspaceMigrationColumnActionType,
WorkspaceMigrationColumnDrop,
WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
import { import {
ACTIVITY_TARGET_STANDARD_FIELD_IDS, ACTIVITY_TARGET_STANDARD_FIELD_IDS,
ATTACHMENT_STANDARD_FIELD_IDS, ATTACHMENT_STANDARD_FIELD_IDS,
BASE_OBJECT_STANDARD_FIELD_IDS, BASE_OBJECT_STANDARD_FIELD_IDS,
CUSTOM_OBJECT_STANDARD_FIELD_IDS, CUSTOM_OBJECT_STANDARD_FIELD_IDS,
FAVORITE_STANDARD_FIELD_IDS, FAVORITE_STANDARD_FIELD_IDS,
NOTE_TARGET_STANDARD_FIELD_IDS,
TIMELINE_ACTIVITY_STANDARD_FIELD_IDS, TIMELINE_ACTIVITY_STANDARD_FIELD_IDS,
} from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids'; } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { import {
createForeignKeyDeterministicUuid, createForeignKeyDeterministicUuid,
createRelationDeterministicUuid, createRelationDeterministicUuid,
} from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util'; } from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util';
import { buildMigrationsForCustomObjectRelations } from 'src/engine/metadata-modules/object-metadata/utils/build-migrations-for-custom-object-relations.util';
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
import { validateObjectMetadataInputOrThrow } from 'src/engine/metadata-modules/object-metadata/utils/validate-object-metadata-input.util';
import { mapUdtNameToFieldType } from 'src/engine/metadata-modules/remote-server/remote-table/utils/udt-name-mapper.util';
import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service';
import { UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
import { RemoteTableRelationsService } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table-relations/remote-table-relations.service';
import {
ObjectMetadataException,
ObjectMetadataExceptionCode,
} from 'src/engine/metadata-modules/object-metadata/object-metadata.exception';
import { ObjectMetadataEntity } from './object-metadata.entity'; import { ObjectMetadataEntity } from './object-metadata.entity';
@ -477,6 +478,20 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
objectMetadataInput.primaryKeyFieldMetadataSettings, objectMetadataInput.primaryKeyFieldMetadataSettings,
); );
const { noteTargetObjectMetadata } = await this.createNoteTargetRelation(
objectMetadataInput.workspaceId,
createdObjectMetadata,
mapUdtNameToFieldType(objectMetadataInput.primaryKeyColumnType ?? 'uuid'),
objectMetadataInput.primaryKeyFieldMetadataSettings,
);
const { taskTargetObjectMetadata } = await this.createTaskTargetRelation(
objectMetadataInput.workspaceId,
createdObjectMetadata,
mapUdtNameToFieldType(objectMetadataInput.primaryKeyColumnType ?? 'uuid'),
objectMetadataInput.primaryKeyFieldMetadataSettings,
);
return this.workspaceMigrationService.createCustomMigration( return this.workspaceMigrationService.createCustomMigration(
generateMigrationName(`create-${createdObjectMetadata.nameSingular}`), generateMigrationName(`create-${createdObjectMetadata.nameSingular}`),
createdObjectMetadata.workspaceId, createdObjectMetadata.workspaceId,
@ -486,6 +501,8 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
attachmentObjectMetadata, attachmentObjectMetadata,
timelineActivityObjectMetadata, timelineActivityObjectMetadata,
favoriteObjectMetadata, favoriteObjectMetadata,
noteTargetObjectMetadata,
taskTargetObjectMetadata,
), ),
); );
} }
@ -536,6 +553,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
workspaceId: workspaceId, workspaceId: workspaceId,
isCustom: false, isCustom: false,
isActive: true, isActive: true,
isSystem: true,
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
name: 'activityTargets', name: 'activityTargets',
label: 'Activities', label: 'Activities',
@ -553,6 +571,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
workspaceId: workspaceId, workspaceId: workspaceId,
isCustom: false, isCustom: false,
isActive: true, isActive: true,
isSystem: true,
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
name: createdObjectMetadata.nameSingular, name: createdObjectMetadata.nameSingular,
label: createdObjectMetadata.labelSingular, label: createdObjectMetadata.labelSingular,
@ -593,6 +612,208 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
return { activityTargetObjectMetadata }; return { activityTargetObjectMetadata };
} }
private async createNoteTargetRelation(
workspaceId: string,
createdObjectMetadata: ObjectMetadataEntity,
objectPrimaryKeyType: FieldMetadataType,
objectPrimaryKeyFieldSettings:
| FieldMetadataSettings<FieldMetadataType | 'default'>
| undefined,
) {
const noteTargetObjectMetadata =
await this.objectMetadataRepository.findOneByOrFail({
nameSingular: 'noteTarget',
workspaceId: workspaceId,
});
await this.fieldMetadataRepository.save(
// Foreign key
{
standardId: createForeignKeyDeterministicUuid({
objectId: createdObjectMetadata.id,
standardId: NOTE_TARGET_STANDARD_FIELD_IDS.custom,
}),
objectMetadataId: noteTargetObjectMetadata.id,
workspaceId: workspaceId,
isCustom: false,
isActive: true,
type: objectPrimaryKeyType,
name: `${createdObjectMetadata.nameSingular}Id`,
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
description: `NoteTarget ${createdObjectMetadata.labelSingular} id foreign key`,
icon: undefined,
isNullable: true,
isSystem: true,
defaultValue: undefined,
settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true },
},
);
const noteTargetRelationFieldMetadata =
await this.fieldMetadataRepository.save([
// FROM
{
standardId: CUSTOM_OBJECT_STANDARD_FIELD_IDS.noteTargets,
objectMetadataId: createdObjectMetadata.id,
workspaceId: workspaceId,
isCustom: false,
isActive: true,
type: FieldMetadataType.RELATION,
name: 'noteTargets',
label: 'Notes',
description: `Notes tied to the ${createdObjectMetadata.labelSingular}`,
icon: 'IconCheckbox',
isNullable: true,
},
// TO
{
standardId: createRelationDeterministicUuid({
objectId: createdObjectMetadata.id,
standardId: NOTE_TARGET_STANDARD_FIELD_IDS.custom,
}),
objectMetadataId: noteTargetObjectMetadata.id,
workspaceId: workspaceId,
isCustom: false,
isActive: true,
type: FieldMetadataType.RELATION,
name: createdObjectMetadata.nameSingular,
label: createdObjectMetadata.labelSingular,
description: `NoteTarget ${createdObjectMetadata.labelSingular}`,
icon: 'IconBuildingSkyscraper',
isNullable: true,
},
]);
const noteTargetRelationFieldMetadataMap =
noteTargetRelationFieldMetadata.reduce(
(acc, fieldMetadata: FieldMetadataEntity) => {
if (fieldMetadata.type === FieldMetadataType.RELATION) {
acc[fieldMetadata.objectMetadataId] = fieldMetadata;
}
return acc;
},
{},
);
await this.relationMetadataRepository.save([
{
workspaceId: workspaceId,
relationType: RelationMetadataType.ONE_TO_MANY,
fromObjectMetadataId: createdObjectMetadata.id,
toObjectMetadataId: noteTargetObjectMetadata.id,
fromFieldMetadataId:
noteTargetRelationFieldMetadataMap[createdObjectMetadata.id].id,
toFieldMetadataId:
noteTargetRelationFieldMetadataMap[noteTargetObjectMetadata.id].id,
onDeleteAction: RelationOnDeleteAction.CASCADE,
},
]);
return { noteTargetObjectMetadata };
}
private async createTaskTargetRelation(
workspaceId: string,
createdObjectMetadata: ObjectMetadataEntity,
objectPrimaryKeyType: FieldMetadataType,
objectPrimaryKeyFieldSettings:
| FieldMetadataSettings<FieldMetadataType | 'default'>
| undefined,
) {
const taskTargetObjectMetadata =
await this.objectMetadataRepository.findOneByOrFail({
nameSingular: 'taskTarget',
workspaceId: workspaceId,
});
await this.fieldMetadataRepository.save(
// Foreign key
{
standardId: createForeignKeyDeterministicUuid({
objectId: createdObjectMetadata.id,
standardId: NOTE_TARGET_STANDARD_FIELD_IDS.custom,
}),
objectMetadataId: taskTargetObjectMetadata.id,
workspaceId: workspaceId,
isCustom: false,
isActive: true,
type: objectPrimaryKeyType,
name: `${createdObjectMetadata.nameSingular}Id`,
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
description: `TaskTarget ${createdObjectMetadata.labelSingular} id foreign key`,
icon: undefined,
isNullable: true,
isSystem: true,
defaultValue: undefined,
settings: { ...objectPrimaryKeyFieldSettings, isForeignKey: true },
},
);
const taskTargetRelationFieldMetadata =
await this.fieldMetadataRepository.save([
// FROM
{
standardId: CUSTOM_OBJECT_STANDARD_FIELD_IDS.taskTargets,
objectMetadataId: createdObjectMetadata.id,
workspaceId: workspaceId,
isCustom: false,
isActive: true,
type: FieldMetadataType.RELATION,
name: 'taskTargets',
label: 'Tasks',
description: `Tasks tied to the ${createdObjectMetadata.labelSingular}`,
icon: 'IconCheckbox',
isNullable: true,
},
// TO
{
standardId: createRelationDeterministicUuid({
objectId: createdObjectMetadata.id,
standardId: NOTE_TARGET_STANDARD_FIELD_IDS.custom,
}),
objectMetadataId: taskTargetObjectMetadata.id,
workspaceId: workspaceId,
isCustom: false,
isActive: true,
type: FieldMetadataType.RELATION,
name: createdObjectMetadata.nameSingular,
label: createdObjectMetadata.labelSingular,
description: `TaskTarget ${createdObjectMetadata.labelSingular}`,
icon: 'IconBuildingSkyscraper',
isNullable: true,
},
]);
const taskTargetRelationFieldMetadataMap =
taskTargetRelationFieldMetadata.reduce(
(acc, fieldMetadata: FieldMetadataEntity) => {
if (fieldMetadata.type === FieldMetadataType.RELATION) {
acc[fieldMetadata.objectMetadataId] = fieldMetadata;
}
return acc;
},
{},
);
await this.relationMetadataRepository.save([
{
workspaceId: workspaceId,
relationType: RelationMetadataType.ONE_TO_MANY,
fromObjectMetadataId: createdObjectMetadata.id,
toObjectMetadataId: taskTargetObjectMetadata.id,
fromFieldMetadataId:
taskTargetRelationFieldMetadataMap[createdObjectMetadata.id].id,
toFieldMetadataId:
taskTargetRelationFieldMetadataMap[taskTargetObjectMetadata.id].id,
onDeleteAction: RelationOnDeleteAction.CASCADE,
},
]);
return { taskTargetObjectMetadata };
}
private async createAttachmentRelation( private async createAttachmentRelation(
workspaceId: string, workspaceId: string,
createdObjectMetadata: ObjectMetadataEntity, createdObjectMetadata: ObjectMetadataEntity,

View File

@ -2,9 +2,9 @@ import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/ut
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; import { RelationOnDeleteAction } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
import { import {
WorkspaceMigrationTableAction,
WorkspaceMigrationColumnActionType, WorkspaceMigrationColumnActionType,
WorkspaceMigrationColumnCreate, WorkspaceMigrationColumnCreate,
WorkspaceMigrationTableAction,
WorkspaceMigrationTableActionType, WorkspaceMigrationTableActionType,
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
@ -15,6 +15,8 @@ export const buildMigrationsForCustomObjectRelations = (
attachmentObjectMetadata: ObjectMetadataEntity, attachmentObjectMetadata: ObjectMetadataEntity,
timelineActivityObjectMetadata: ObjectMetadataEntity, timelineActivityObjectMetadata: ObjectMetadataEntity,
favoriteObjectMetadata: ObjectMetadataEntity, favoriteObjectMetadata: ObjectMetadataEntity,
noteTargetObjectMetadata: ObjectMetadataEntity,
taskTargetObjectMetadata: ObjectMetadataEntity,
): WorkspaceMigrationTableAction[] => [ ): WorkspaceMigrationTableAction[] => [
{ {
name: computeObjectTargetTable(createdObjectMetadata), name: computeObjectTargetTable(createdObjectMetadata),
@ -50,6 +52,66 @@ export const buildMigrationsForCustomObjectRelations = (
}, },
], ],
}, },
// Add note target relation
{
name: computeObjectTargetTable(noteTargetObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
isForeignKey: true,
}),
columnType: 'uuid',
isNullable: true,
} satisfies WorkspaceMigrationColumnCreate,
],
},
{
name: computeObjectTargetTable(noteTargetObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
isForeignKey: true,
}),
referencedTableName: computeObjectTargetTable(createdObjectMetadata),
referencedTableColumnName: 'id',
onDelete: RelationOnDeleteAction.CASCADE,
},
],
},
// Add task target relation
{
name: computeObjectTargetTable(taskTargetObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
isForeignKey: true,
}),
columnType: 'uuid',
isNullable: true,
} satisfies WorkspaceMigrationColumnCreate,
],
},
{
name: computeObjectTargetTable(taskTargetObjectMetadata),
action: WorkspaceMigrationTableActionType.ALTER,
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
isForeignKey: true,
}),
referencedTableName: computeObjectTargetTable(createdObjectMetadata),
referencedTableColumnName: 'id',
onDelete: RelationOnDeleteAction.CASCADE,
},
],
},
// Add attachment relation // Add attachment relation
{ {
name: computeObjectTargetTable(attachmentObjectMetadata), name: computeObjectTargetTable(attachmentObjectMetadata),

View File

@ -143,8 +143,9 @@ export class PersonWorkspaceEntity extends BaseWorkspaceEntity {
@WorkspaceRelation({ @WorkspaceRelation({
standardId: PERSON_STANDARD_FIELD_IDS.pointOfContactForOpportunities, standardId: PERSON_STANDARD_FIELD_IDS.pointOfContactForOpportunities,
type: RelationMetadataType.ONE_TO_MANY, type: RelationMetadataType.ONE_TO_MANY,
label: 'POC for Opportunities', label: 'Linked Opportunities',
description: 'Point of Contact for Opportunities', description:
'List of opportunities for which that person is the point of contact',
icon: 'IconTargetArrow', icon: 'IconTargetArrow',
inverseSideTarget: () => OpportunityWorkspaceEntity, inverseSideTarget: () => OpportunityWorkspaceEntity,
inverseSideFieldKey: 'pointOfContact', inverseSideFieldKey: 'pointOfContact',