mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-23 12:02:10 +03:00
Refactor standard relations update at custom object renaming (#8638)
Refactoring update of standard relations when a custom object is renamed, after observing occasional issues. --------- Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
parent
cf73e32969
commit
670c8a0b98
@ -424,14 +424,14 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
existingObjectMetadata,
|
existingObjectMetadata,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!(newTargetTableName === existingTargetTableName)) {
|
if (newTargetTableName !== existingTargetTableName) {
|
||||||
await this.objectMetadataMigrationService.createRenameTableMigration(
|
await this.objectMetadataMigrationService.createRenameTableMigration(
|
||||||
existingObjectMetadata,
|
existingObjectMetadata,
|
||||||
objectMetadataForUpdate,
|
objectMetadataForUpdate,
|
||||||
objectMetadataForUpdate.workspaceId,
|
objectMetadataForUpdate.workspaceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.objectMetadataMigrationService.createRelationsUpdatesMigrations(
|
await this.objectMetadataMigrationService.createStandardRelationsUpdatesMigrations(
|
||||||
existingObjectMetadata,
|
existingObjectMetadata,
|
||||||
objectMetadataForUpdate,
|
objectMetadataForUpdate,
|
||||||
objectMetadataForUpdate.workspaceId,
|
objectMetadataForUpdate.workspaceId,
|
||||||
|
@ -6,7 +6,10 @@ import { Repository } from 'typeorm';
|
|||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||||
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 { buildDescriptionForRelationFieldMetadataOnFromField } from 'src/engine/metadata-modules/object-metadata/utils/build-description-for-relation-field-on-from-field.util';
|
||||||
|
import { buildDescriptionForRelationFieldMetadataOnToField } from 'src/engine/metadata-modules/object-metadata/utils/build-description-for-relation-field-on-to-field.util';
|
||||||
import { buildMigrationsForCustomObjectRelations } from 'src/engine/metadata-modules/object-metadata/utils/build-migrations-for-custom-object-relations.util';
|
import { buildMigrationsForCustomObjectRelations } from 'src/engine/metadata-modules/object-metadata/utils/build-migrations-for-custom-object-relations.util';
|
||||||
|
import { buildNameLabelAndDescriptionForForeignKeyFieldMetadata } from 'src/engine/metadata-modules/object-metadata/utils/build-name-label-and-description-for-foreign-key-field-metadata.util';
|
||||||
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||||
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 { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
import { fieldMetadataTypeToColumnType } from 'src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util';
|
||||||
@ -115,7 +118,7 @@ export class ObjectMetadataMigrationService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createRelationsUpdatesMigrations(
|
public async createStandardRelationsUpdatesMigrations(
|
||||||
existingObjectMetadata: ObjectMetadataEntity,
|
existingObjectMetadata: ObjectMetadataEntity,
|
||||||
updatedObjectMetadata: ObjectMetadataEntity,
|
updatedObjectMetadata: ObjectMetadataEntity,
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
@ -124,87 +127,155 @@ export class ObjectMetadataMigrationService {
|
|||||||
const newTableName = computeObjectTargetTable(updatedObjectMetadata);
|
const newTableName = computeObjectTargetTable(updatedObjectMetadata);
|
||||||
|
|
||||||
if (existingTableName !== newTableName) {
|
if (existingTableName !== newTableName) {
|
||||||
const searchCriteria = {
|
const relatedObjectsIds = await this.relationMetadataRepository
|
||||||
isCustom: false,
|
.find({
|
||||||
settings: {
|
where: {
|
||||||
isForeignKey: true,
|
workspaceId,
|
||||||
},
|
fromObjectMetadataId: existingObjectMetadata.id,
|
||||||
name: `${existingObjectMetadata.nameSingular}Id`,
|
},
|
||||||
workspaceId: workspaceId,
|
})
|
||||||
};
|
.then((relations) =>
|
||||||
|
relations.map((relation) => relation.toObjectMetadataId),
|
||||||
|
);
|
||||||
|
|
||||||
const fieldsWihStandardRelation = await this.fieldMetadataRepository.find(
|
const foreignKeyFieldMetadataForStandardRelation =
|
||||||
{
|
await this.fieldMetadataRepository.find({
|
||||||
where: {
|
where: {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
settings: {
|
settings: {
|
||||||
isForeignKey: true,
|
isForeignKey: true,
|
||||||
},
|
},
|
||||||
name: `${existingObjectMetadata.nameSingular}Id`,
|
name: `${existingObjectMetadata.nameSingular}Id`,
|
||||||
|
workspaceId: workspaceId,
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
await this.fieldMetadataRepository.update(searchCriteria, {
|
|
||||||
name: `${updatedObjectMetadata.nameSingular}Id`,
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
fieldsWihStandardRelation.map(async (fieldWihStandardRelation) => {
|
foreignKeyFieldMetadataForStandardRelation.map(
|
||||||
const relatedObject = await this.objectMetadataRepository.findOneBy({
|
async (foreignKeyFieldMetadata) => {
|
||||||
id: fieldWihStandardRelation.objectMetadataId,
|
if (
|
||||||
workspaceId: workspaceId,
|
relatedObjectsIds.includes(
|
||||||
});
|
foreignKeyFieldMetadata.objectMetadataId,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
const relatedObject =
|
||||||
|
await this.objectMetadataRepository.findOneBy({
|
||||||
|
id: foreignKeyFieldMetadata.objectMetadataId,
|
||||||
|
workspaceId: workspaceId,
|
||||||
|
});
|
||||||
|
|
||||||
if (relatedObject) {
|
if (relatedObject) {
|
||||||
await this.fieldMetadataRepository.update(
|
// 1. Update to and from relation fieldMetadata)
|
||||||
{
|
const toFieldRelationFieldMetadataId =
|
||||||
name: existingObjectMetadata.nameSingular,
|
await this.fieldMetadataRepository
|
||||||
label: existingObjectMetadata.labelSingular,
|
.findOneByOrFail({
|
||||||
},
|
name: existingObjectMetadata.nameSingular,
|
||||||
{
|
label: existingObjectMetadata.labelSingular,
|
||||||
name: updatedObjectMetadata.nameSingular,
|
objectMetadataId: relatedObject.id,
|
||||||
label: updatedObjectMetadata.labelSingular,
|
workspaceId: workspaceId,
|
||||||
},
|
})
|
||||||
);
|
.then((field) => field.id);
|
||||||
|
|
||||||
const relationTableName = computeObjectTargetTable(relatedObject);
|
const { description: descriptionForToField } =
|
||||||
const columnName = `${existingObjectMetadata.nameSingular}Id`;
|
buildDescriptionForRelationFieldMetadataOnToField({
|
||||||
const columnType = fieldMetadataTypeToColumnType(
|
relationObjectMetadataNamePlural: relatedObject.namePlural,
|
||||||
fieldWihStandardRelation.type,
|
targetObjectLabelSingular:
|
||||||
);
|
updatedObjectMetadata.labelSingular,
|
||||||
|
});
|
||||||
|
|
||||||
await this.workspaceMigrationService.createCustomMigration(
|
await this.fieldMetadataRepository.update(
|
||||||
generateMigrationName(
|
toFieldRelationFieldMetadataId,
|
||||||
`rename-${existingObjectMetadata.nameSingular}-to-${updatedObjectMetadata.nameSingular}-in-${relatedObject.nameSingular}`,
|
{
|
||||||
),
|
name: updatedObjectMetadata.nameSingular,
|
||||||
workspaceId,
|
label: updatedObjectMetadata.labelSingular,
|
||||||
[
|
description: descriptionForToField,
|
||||||
{
|
},
|
||||||
name: relationTableName,
|
);
|
||||||
action: WorkspaceMigrationTableActionType.ALTER,
|
|
||||||
columns: [
|
const fromFieldRelationFieldMetadataId =
|
||||||
|
await this.relationMetadataRepository
|
||||||
|
.findOneByOrFail({
|
||||||
|
fromObjectMetadataId: existingObjectMetadata.id,
|
||||||
|
toObjectMetadataId: relatedObject.id,
|
||||||
|
toFieldMetadataId: toFieldRelationFieldMetadataId,
|
||||||
|
workspaceId,
|
||||||
|
})
|
||||||
|
.then((relation) => relation?.fromFieldMetadataId);
|
||||||
|
|
||||||
|
await this.fieldMetadataRepository.update(
|
||||||
|
fromFieldRelationFieldMetadataId,
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
buildDescriptionForRelationFieldMetadataOnFromField({
|
||||||
|
relationObjectMetadataNamePlural:
|
||||||
|
relatedObject.namePlural,
|
||||||
|
targetObjectLabelSingular:
|
||||||
|
updatedObjectMetadata.labelSingular,
|
||||||
|
}).description,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Update foreign key fieldMetadata
|
||||||
|
const {
|
||||||
|
name: updatedNameForForeignKeyFieldMetadata,
|
||||||
|
label: updatedLabelForForeignKeyFieldMetadata,
|
||||||
|
description: updatedDescriptionForForeignKeyFieldMetadata,
|
||||||
|
} = buildNameLabelAndDescriptionForForeignKeyFieldMetadata({
|
||||||
|
targetObjectNameSingular: updatedObjectMetadata.nameSingular,
|
||||||
|
targetObjectLabelSingular:
|
||||||
|
updatedObjectMetadata.labelSingular,
|
||||||
|
relatedObjectLabelSingular: relatedObject.labelSingular,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.fieldMetadataRepository.update(
|
||||||
|
foreignKeyFieldMetadata.id,
|
||||||
|
{
|
||||||
|
name: updatedNameForForeignKeyFieldMetadata,
|
||||||
|
label: updatedLabelForForeignKeyFieldMetadata,
|
||||||
|
description: updatedDescriptionForForeignKeyFieldMetadata,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const relatedObjectTableName =
|
||||||
|
computeObjectTargetTable(relatedObject);
|
||||||
|
const columnName = `${existingObjectMetadata.nameSingular}Id`;
|
||||||
|
const columnType = fieldMetadataTypeToColumnType(
|
||||||
|
foreignKeyFieldMetadata.type,
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.workspaceMigrationService.createCustomMigration(
|
||||||
|
generateMigrationName(
|
||||||
|
`rename-${existingObjectMetadata.nameSingular}-to-${updatedObjectMetadata.nameSingular}-in-${relatedObject.nameSingular}`,
|
||||||
|
),
|
||||||
|
workspaceId,
|
||||||
|
[
|
||||||
{
|
{
|
||||||
action: WorkspaceMigrationColumnActionType.ALTER,
|
name: relatedObjectTableName,
|
||||||
currentColumnDefinition: {
|
action: WorkspaceMigrationTableActionType.ALTER,
|
||||||
columnName,
|
columns: [
|
||||||
columnType,
|
{
|
||||||
isNullable: true,
|
action: WorkspaceMigrationColumnActionType.ALTER,
|
||||||
defaultValue: null,
|
currentColumnDefinition: {
|
||||||
},
|
columnName,
|
||||||
alteredColumnDefinition: {
|
columnType,
|
||||||
columnName: `${updatedObjectMetadata.nameSingular}Id`,
|
isNullable: true,
|
||||||
columnType,
|
defaultValue: null,
|
||||||
isNullable: true,
|
},
|
||||||
defaultValue: null,
|
alteredColumnDefinition: {
|
||||||
},
|
columnName: `${updatedObjectMetadata.nameSingular}Id`,
|
||||||
|
columnType,
|
||||||
|
isNullable: true,
|
||||||
|
defaultValue: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
);
|
||||||
],
|
}
|
||||||
);
|
}
|
||||||
}
|
},
|
||||||
}),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,9 @@ import {
|
|||||||
FieldMetadataType,
|
FieldMetadataType,
|
||||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
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 { buildDescriptionForRelationFieldMetadataOnFromField } from 'src/engine/metadata-modules/object-metadata/utils/build-description-for-relation-field-on-from-field.util';
|
||||||
|
import { buildDescriptionForRelationFieldMetadataOnToField } from 'src/engine/metadata-modules/object-metadata/utils/build-description-for-relation-field-on-to-field.util';
|
||||||
|
import { buildNameLabelAndDescriptionForForeignKeyFieldMetadata } from 'src/engine/metadata-modules/object-metadata/utils/build-name-label-and-description-for-foreign-key-field-metadata.util';
|
||||||
import {
|
import {
|
||||||
RelationMetadataEntity,
|
RelationMetadataEntity,
|
||||||
RelationMetadataType,
|
RelationMetadataType,
|
||||||
@ -94,6 +97,13 @@ export class ObjectMetadataRelationService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { name, label, description } =
|
||||||
|
buildNameLabelAndDescriptionForForeignKeyFieldMetadata({
|
||||||
|
targetObjectNameSingular: createdObjectMetadata.nameSingular,
|
||||||
|
targetObjectLabelSingular: createdObjectMetadata.labelSingular,
|
||||||
|
relatedObjectLabelSingular: relatedObjectMetadata.labelSingular,
|
||||||
|
});
|
||||||
|
|
||||||
await this.fieldMetadataRepository.save({
|
await this.fieldMetadataRepository.save({
|
||||||
standardId: createForeignKeyDeterministicUuid({
|
standardId: createForeignKeyDeterministicUuid({
|
||||||
objectId: createdObjectMetadata.id,
|
objectId: createdObjectMetadata.id,
|
||||||
@ -104,9 +114,9 @@ export class ObjectMetadataRelationService {
|
|||||||
isCustom: false,
|
isCustom: false,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
type: objectPrimaryKeyType,
|
type: objectPrimaryKeyType,
|
||||||
name: `${createdObjectMetadata.nameSingular}Id`,
|
name,
|
||||||
label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
|
label,
|
||||||
description: `${relatedObjectMetadata.labelSingular} ${createdObjectMetadata.labelSingular} id foreign key`,
|
description,
|
||||||
icon: undefined,
|
icon: undefined,
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
isSystem: true,
|
isSystem: true,
|
||||||
@ -141,6 +151,13 @@ export class ObjectMetadataRelationService {
|
|||||||
) {
|
) {
|
||||||
const relationObjectMetadataNamePlural = relatedObjectMetadata.namePlural;
|
const relationObjectMetadataNamePlural = relatedObjectMetadata.namePlural;
|
||||||
|
|
||||||
|
const { description } = buildDescriptionForRelationFieldMetadataOnFromField(
|
||||||
|
{
|
||||||
|
relationObjectMetadataNamePlural,
|
||||||
|
targetObjectLabelSingular: createdObjectMetadata.labelSingular,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
standardId:
|
standardId:
|
||||||
CUSTOM_OBJECT_STANDARD_FIELD_IDS[relationObjectMetadataNamePlural],
|
CUSTOM_OBJECT_STANDARD_FIELD_IDS[relationObjectMetadataNamePlural],
|
||||||
@ -152,7 +169,7 @@ export class ObjectMetadataRelationService {
|
|||||||
type: FieldMetadataType.RELATION,
|
type: FieldMetadataType.RELATION,
|
||||||
name: relatedObjectMetadata.namePlural,
|
name: relatedObjectMetadata.namePlural,
|
||||||
label: capitalize(relationObjectMetadataNamePlural),
|
label: capitalize(relationObjectMetadataNamePlural),
|
||||||
description: `${capitalize(relationObjectMetadataNamePlural)} tied to the ${createdObjectMetadata.labelSingular}`,
|
description,
|
||||||
icon:
|
icon:
|
||||||
STANDARD_OBJECT_ICONS[relatedObjectMetadata.nameSingular] ||
|
STANDARD_OBJECT_ICONS[relatedObjectMetadata.nameSingular] ||
|
||||||
'IconBuildingSkyscraper',
|
'IconBuildingSkyscraper',
|
||||||
@ -174,6 +191,11 @@ export class ObjectMetadataRelationService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { description } = buildDescriptionForRelationFieldMetadataOnToField({
|
||||||
|
relationObjectMetadataNamePlural: relatedObjectMetadata.namePlural,
|
||||||
|
targetObjectLabelSingular: createdObjectMetadata.labelSingular,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
standardId: createRelationDeterministicUuid({
|
standardId: createRelationDeterministicUuid({
|
||||||
objectId: createdObjectMetadata.id,
|
objectId: createdObjectMetadata.id,
|
||||||
@ -187,7 +209,7 @@ export class ObjectMetadataRelationService {
|
|||||||
type: FieldMetadataType.RELATION,
|
type: FieldMetadataType.RELATION,
|
||||||
name: createdObjectMetadata.nameSingular,
|
name: createdObjectMetadata.nameSingular,
|
||||||
label: createdObjectMetadata.labelSingular,
|
label: createdObjectMetadata.labelSingular,
|
||||||
description: `${capitalize(relatedObjectMetadata.nameSingular)} ${createdObjectMetadata.labelSingular}`,
|
description,
|
||||||
icon: 'IconBuildingSkyscraper',
|
icon: 'IconBuildingSkyscraper',
|
||||||
isNullable: true,
|
isNullable: true,
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
import { capitalize } from 'src/utils/capitalize';
|
||||||
|
|
||||||
|
export const buildDescriptionForRelationFieldMetadataOnFromField = ({
|
||||||
|
relationObjectMetadataNamePlural,
|
||||||
|
targetObjectLabelSingular,
|
||||||
|
}: {
|
||||||
|
relationObjectMetadataNamePlural: string;
|
||||||
|
targetObjectLabelSingular: string;
|
||||||
|
}) => {
|
||||||
|
const description = `${capitalize(relationObjectMetadataNamePlural)} tied to the ${targetObjectLabelSingular}`;
|
||||||
|
|
||||||
|
return { description };
|
||||||
|
};
|
@ -0,0 +1,13 @@
|
|||||||
|
import { capitalize } from 'src/utils/capitalize';
|
||||||
|
|
||||||
|
export const buildDescriptionForRelationFieldMetadataOnToField = ({
|
||||||
|
relationObjectMetadataNamePlural,
|
||||||
|
targetObjectLabelSingular,
|
||||||
|
}: {
|
||||||
|
relationObjectMetadataNamePlural: string;
|
||||||
|
targetObjectLabelSingular: string;
|
||||||
|
}) => {
|
||||||
|
const description = `${capitalize(relationObjectMetadataNamePlural)} ${targetObjectLabelSingular}`;
|
||||||
|
|
||||||
|
return { description };
|
||||||
|
};
|
@ -0,0 +1,15 @@
|
|||||||
|
export const buildNameLabelAndDescriptionForForeignKeyFieldMetadata = ({
|
||||||
|
targetObjectNameSingular,
|
||||||
|
targetObjectLabelSingular,
|
||||||
|
relatedObjectLabelSingular,
|
||||||
|
}: {
|
||||||
|
targetObjectNameSingular: string;
|
||||||
|
targetObjectLabelSingular: string;
|
||||||
|
relatedObjectLabelSingular: string;
|
||||||
|
}) => {
|
||||||
|
const name = `${targetObjectNameSingular}Id`;
|
||||||
|
const label = `${targetObjectLabelSingular} ID (foreign key)`;
|
||||||
|
const description = `${relatedObjectLabelSingular} ${targetObjectLabelSingular} id foreign key`;
|
||||||
|
|
||||||
|
return { name, label, description };
|
||||||
|
};
|
@ -332,6 +332,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
|||||||
type: FieldMetadataType.UUID,
|
type: FieldMetadataType.UUID,
|
||||||
objectMetadataId: relationMetadataInput.toObjectMetadataId,
|
objectMetadataId: relationMetadataInput.toObjectMetadataId,
|
||||||
workspaceId: relationMetadataInput.workspaceId,
|
workspaceId: relationMetadataInput.workspaceId,
|
||||||
|
settings: { isForeignKey: true },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user