Add onDeleteAction to RelationMetadata (#4100)
* Add onDeleteAction to relationMetadata * rename to SET NULL * fix migration * fix migration * fix after review
This commit is contained in:
@ -0,0 +1,25 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddOnDeleteActionToRelationMetadata1708449210922
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'AddOnDeleteActionToRelationMetadata1708449210922';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "metadata"."relationMetadata_ondeleteaction_enum" AS ENUM('CASCADE', 'RESTRICT', 'SET_NULL')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."relationMetadata" ADD "onDeleteAction" "metadata"."relationMetadata_ondeleteaction_enum" NOT NULL DEFAULT 'SET_NULL'`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "metadata"."relationMetadata" DROP COLUMN "onDeleteAction"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "metadata"."relationMetadata_ondeleteaction_enum"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -347,7 +347,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
action: 'alter',
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.RELATION,
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: `${computeObjectTargetTable(
|
||||
createdObjectMetadata,
|
||||
)}Id`,
|
||||
@ -378,7 +378,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
action: 'alter',
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.RELATION,
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: `${computeObjectTargetTable(
|
||||
createdObjectMetadata,
|
||||
)}Id`,
|
||||
|
||||
@ -20,6 +20,12 @@ export enum RelationMetadataType {
|
||||
MANY_TO_MANY = 'MANY_TO_MANY',
|
||||
}
|
||||
|
||||
export enum RelationOnDeleteAction {
|
||||
CASCADE = 'CASCADE',
|
||||
RESTRICT = 'RESTRICT',
|
||||
SET_NULL = 'SET_NULL',
|
||||
}
|
||||
|
||||
@Entity('relationMetadata')
|
||||
export class RelationMetadataEntity implements RelationMetadataInterface {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
@ -28,6 +34,14 @@ export class RelationMetadataEntity implements RelationMetadataInterface {
|
||||
@Column({ nullable: false })
|
||||
relationType: RelationMetadataType;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
default: RelationOnDeleteAction.SET_NULL,
|
||||
type: 'enum',
|
||||
enum: RelationOnDeleteAction,
|
||||
})
|
||||
onDeleteAction: RelationOnDeleteAction;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
fromObjectMetadataId: string;
|
||||
|
||||
|
||||
@ -200,7 +200,7 @@ export class RelationMetadataService extends TypeOrmQueryService<RelationMetadat
|
||||
action: 'alter',
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.RELATION,
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: foreignKeyColumnName,
|
||||
referencedTableName: computeObjectTargetTable(
|
||||
objectMetadataMap[relationMetadataInput.fromObjectMetadataId],
|
||||
|
||||
@ -5,10 +5,13 @@ import {
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import { RelationOnDeleteAction } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
|
||||
export enum WorkspaceMigrationColumnActionType {
|
||||
CREATE = 'CREATE',
|
||||
ALTER = 'ALTER',
|
||||
RELATION = 'RELATION',
|
||||
CREATE_FOREIGN_KEY = 'CREATE_FOREIGN_KEY',
|
||||
DROP_FOREIGN_KEY = 'DROP_FOREIGN_KEY',
|
||||
DROP = 'DROP',
|
||||
}
|
||||
|
||||
@ -34,12 +37,18 @@ export type WorkspaceMigrationColumnAlter = {
|
||||
alteredColumnDefinition: WorkspaceMigrationColumnDefinition;
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationColumnRelation = {
|
||||
action: WorkspaceMigrationColumnActionType.RELATION;
|
||||
export type WorkspaceMigrationColumnCreateRelation = {
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY;
|
||||
columnName: string;
|
||||
referencedTableName: string;
|
||||
referencedTableColumnName: string;
|
||||
isUnique?: boolean;
|
||||
onDelete?: RelationOnDeleteAction;
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationColumnDropRelation = {
|
||||
action: WorkspaceMigrationColumnActionType.DROP_FOREIGN_KEY;
|
||||
columnName: string;
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationColumnDrop = {
|
||||
@ -52,7 +61,8 @@ export type WorkspaceMigrationColumnAction = {
|
||||
} & (
|
||||
| WorkspaceMigrationColumnCreate
|
||||
| WorkspaceMigrationColumnAlter
|
||||
| WorkspaceMigrationColumnRelation
|
||||
| WorkspaceMigrationColumnCreateRelation
|
||||
| WorkspaceMigrationColumnDropRelation
|
||||
| WorkspaceMigrationColumnDrop
|
||||
);
|
||||
|
||||
|
||||
@ -29,6 +29,7 @@ export enum WorkspaceHealthIssueType {
|
||||
RELATION_FROM_OR_TO_FIELD_METADATA_NOT_VALID = 'RELATION_FROM_OR_TO_FIELD_METADATA_NOT_VALID',
|
||||
RELATION_FOREIGN_KEY_NOT_VALID = 'RELATION_FOREIGN_KEY_NOT_VALID',
|
||||
RELATION_FOREIGN_KEY_CONFLICT = 'RELATION_FOREIGN_KEY_CONFLICT',
|
||||
RELATION_FOREIGN_KEY_ON_DELETE_ACTION_CONFLICT = 'RELATION_FOREIGN_KEY_ON_DELETE_ACTION_CONFLICT',
|
||||
RELATION_TYPE_NOT_VALID = 'RELATION_TYPE_NOT_VALID',
|
||||
}
|
||||
|
||||
@ -85,6 +86,7 @@ export type WorkspaceRelationIssueTypes =
|
||||
| WorkspaceHealthIssueType.RELATION_FROM_OR_TO_FIELD_METADATA_NOT_VALID
|
||||
| WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_NOT_VALID
|
||||
| WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_CONFLICT
|
||||
| WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_ON_DELETE_ACTION_CONFLICT
|
||||
| WorkspaceHealthIssueType.RELATION_TYPE_NOT_VALID;
|
||||
|
||||
export interface WorkspaceHealthRelationIssue<
|
||||
|
||||
@ -8,6 +8,8 @@ export interface WorkspaceTableStructure {
|
||||
isPrimaryKey: boolean;
|
||||
isForeignKey: boolean;
|
||||
isUnique: boolean;
|
||||
onUpdateAction: string;
|
||||
onDeleteAction: string;
|
||||
}
|
||||
|
||||
export type WorkspaceTableStructureResult = {
|
||||
|
||||
@ -85,7 +85,9 @@ export class DatabaseStructureService {
|
||||
CASE
|
||||
WHEN uc.column_name IS NOT NULL THEN 'TRUE'
|
||||
ELSE 'FALSE'
|
||||
END AS "isUnique"
|
||||
END AS "isUnique",
|
||||
rc.update_rule AS "onUpdateAction",
|
||||
rc.delete_rule AS "onDeleteAction"
|
||||
FROM
|
||||
information_schema.columns AS c
|
||||
LEFT JOIN
|
||||
@ -109,6 +111,9 @@ export class DatabaseStructureService {
|
||||
ON c.table_schema = uc.schema_name
|
||||
AND c.table_name = uc.table_name
|
||||
AND c.column_name = uc.column_name
|
||||
LEFT JOIN
|
||||
information_schema.referential_constraints AS rc
|
||||
ON rc.constraint_name = fk.constraint_name
|
||||
WHERE
|
||||
c.table_schema = '${schemaName}'
|
||||
AND c.table_name = '${tableName}';
|
||||
|
||||
@ -209,6 +209,16 @@ export class RelationMetadataHealthService {
|
||||
});
|
||||
}
|
||||
|
||||
if (relationMetadata.onDeleteAction !== relationColumn.onDeleteAction) {
|
||||
issues.push({
|
||||
type: WorkspaceHealthIssueType.RELATION_FOREIGN_KEY_ON_DELETE_ACTION_CONFLICT,
|
||||
fromFieldMetadata,
|
||||
toFieldMetadata,
|
||||
relationMetadata,
|
||||
message: `Relation ${relationMetadata.id} foreign key onDeleteAction is not properly set`,
|
||||
});
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
|
||||
@ -43,11 +43,92 @@ export class WorkspaceMigrationRelationFactory {
|
||||
originalObjectMetadataMap,
|
||||
relationMetadataCollection,
|
||||
);
|
||||
case WorkspaceMigrationBuilderAction.UPDATE:
|
||||
return this.updateRelationMigration(
|
||||
originalObjectMetadataMap,
|
||||
relationMetadataCollection,
|
||||
);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private async updateRelationMigration(
|
||||
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
|
||||
relationMetadataCollection: RelationMetadataEntity[],
|
||||
): Promise<Partial<WorkspaceMigrationEntity>[]> {
|
||||
const workspaceMigrations: Partial<WorkspaceMigrationEntity>[] = [];
|
||||
|
||||
for (const relationMetadata of relationMetadataCollection) {
|
||||
const toObjectMetadata =
|
||||
originalObjectMetadataMap[relationMetadata.toObjectMetadataId];
|
||||
const fromObjectMetadata =
|
||||
originalObjectMetadataMap[relationMetadata.fromObjectMetadataId];
|
||||
|
||||
if (!toObjectMetadata) {
|
||||
throw new Error(
|
||||
`ObjectMetadata with id ${relationMetadata.toObjectMetadataId} not found`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!fromObjectMetadata) {
|
||||
throw new Error(
|
||||
`ObjectMetadata with id ${relationMetadata.fromObjectMetadataId} not found`,
|
||||
);
|
||||
}
|
||||
|
||||
const toFieldMetadata = toObjectMetadata.fields.find(
|
||||
(field) => field.id === relationMetadata.toFieldMetadataId,
|
||||
);
|
||||
|
||||
if (!toFieldMetadata) {
|
||||
throw new Error(
|
||||
`FieldMetadata with id ${relationMetadata.toFieldMetadataId} not found`,
|
||||
);
|
||||
}
|
||||
|
||||
const migrations: WorkspaceMigrationTableAction[] = [
|
||||
{
|
||||
name: computeObjectTargetTable(toObjectMetadata),
|
||||
action: 'alter',
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.DROP_FOREIGN_KEY,
|
||||
columnName: `${camelCase(toFieldMetadata.name)}Id`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(toObjectMetadata),
|
||||
action: 'alter',
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: `${camelCase(toFieldMetadata.name)}Id`,
|
||||
referencedTableName: computeObjectTargetTable(fromObjectMetadata),
|
||||
referencedTableColumnName: 'id',
|
||||
isUnique:
|
||||
relationMetadata.relationType ===
|
||||
RelationMetadataType.ONE_TO_ONE,
|
||||
onDelete: relationMetadata.onDeleteAction,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
workspaceMigrations.push({
|
||||
workspaceId: relationMetadata.workspaceId,
|
||||
name: generateMigrationName(
|
||||
`update-relation-from-${fromObjectMetadata.nameSingular}-to-${toObjectMetadata.nameSingular}`,
|
||||
),
|
||||
isCustom: false,
|
||||
migrations,
|
||||
});
|
||||
}
|
||||
|
||||
return workspaceMigrations;
|
||||
}
|
||||
|
||||
private async createRelationMigration(
|
||||
originalObjectMetadataMap: Record<string, ObjectMetadataEntity>,
|
||||
relationMetadataCollection: RelationMetadataEntity[],
|
||||
@ -88,13 +169,14 @@ export class WorkspaceMigrationRelationFactory {
|
||||
action: 'alter',
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.RELATION,
|
||||
action: WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY,
|
||||
columnName: `${camelCase(toFieldMetadata.name)}Id`,
|
||||
referencedTableName: computeObjectTargetTable(fromObjectMetadata),
|
||||
referencedTableColumnName: 'id',
|
||||
isUnique:
|
||||
relationMetadata.relationType ===
|
||||
RelationMetadataType.ONE_TO_ONE,
|
||||
onDelete: relationMetadata.onDeleteAction,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@ -15,8 +15,9 @@ import {
|
||||
WorkspaceMigrationColumnAction,
|
||||
WorkspaceMigrationColumnActionType,
|
||||
WorkspaceMigrationColumnCreate,
|
||||
WorkspaceMigrationColumnRelation,
|
||||
WorkspaceMigrationColumnCreateRelation,
|
||||
WorkspaceMigrationColumnAlter,
|
||||
WorkspaceMigrationColumnDropRelation,
|
||||
} from 'src/metadata/workspace-migration/workspace-migration.entity';
|
||||
import { WorkspaceCacheVersionService } from 'src/metadata/workspace-cache-version/workspace-cache-version.service';
|
||||
import { WorkspaceMigrationEnumService } from 'src/workspace/workspace-migration-runner/services/workspace-migration-enum.service';
|
||||
@ -200,7 +201,7 @@ export class WorkspaceMigrationRunnerService {
|
||||
columnMigration,
|
||||
);
|
||||
break;
|
||||
case WorkspaceMigrationColumnActionType.RELATION:
|
||||
case WorkspaceMigrationColumnActionType.CREATE_FOREIGN_KEY:
|
||||
await this.createRelation(
|
||||
queryRunner,
|
||||
schemaName,
|
||||
@ -208,6 +209,14 @@ export class WorkspaceMigrationRunnerService {
|
||||
columnMigration,
|
||||
);
|
||||
break;
|
||||
case WorkspaceMigrationColumnActionType.DROP_FOREIGN_KEY:
|
||||
await this.dropRelation(
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
columnMigration,
|
||||
);
|
||||
break;
|
||||
case WorkspaceMigrationColumnActionType.DROP:
|
||||
await queryRunner.dropColumn(
|
||||
`${schemaName}.${tableName}`,
|
||||
@ -325,7 +334,7 @@ export class WorkspaceMigrationRunnerService {
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
tableName: string,
|
||||
migrationColumn: WorkspaceMigrationColumnRelation,
|
||||
migrationColumn: WorkspaceMigrationColumnCreateRelation,
|
||||
) {
|
||||
await queryRunner.createForeignKey(
|
||||
`${schemaName}.${tableName}`,
|
||||
@ -334,7 +343,7 @@ export class WorkspaceMigrationRunnerService {
|
||||
referencedColumnNames: [migrationColumn.referencedTableColumnName],
|
||||
referencedTableName: migrationColumn.referencedTableName,
|
||||
referencedSchema: schemaName,
|
||||
onDelete: 'CASCADE',
|
||||
onDelete: migrationColumn.onDelete?.replace(/_/g, ' '),
|
||||
}),
|
||||
);
|
||||
|
||||
@ -349,4 +358,57 @@ export class WorkspaceMigrationRunnerService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async dropRelation(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
tableName: string,
|
||||
migrationColumn: WorkspaceMigrationColumnDropRelation,
|
||||
) {
|
||||
const foreignKeyName = await this.getForeignKeyName(
|
||||
queryRunner,
|
||||
schemaName,
|
||||
tableName,
|
||||
migrationColumn.columnName,
|
||||
);
|
||||
|
||||
if (!foreignKeyName) {
|
||||
throw new Error(
|
||||
`Foreign key not found for column ${migrationColumn.columnName}`,
|
||||
);
|
||||
}
|
||||
|
||||
await queryRunner.dropForeignKey(
|
||||
`${schemaName}.${tableName}`,
|
||||
foreignKeyName,
|
||||
);
|
||||
}
|
||||
|
||||
private async getForeignKeyName(
|
||||
queryRunner: QueryRunner,
|
||||
schemaName: string,
|
||||
tableName: string,
|
||||
columnName: string,
|
||||
): Promise<string | undefined> {
|
||||
const foreignKeys = await queryRunner.query(
|
||||
`
|
||||
SELECT
|
||||
tc.constraint_name AS constraint_name
|
||||
FROM
|
||||
information_schema.table_constraints AS tc
|
||||
JOIN
|
||||
information_schema.key_column_usage AS kcu
|
||||
ON tc.constraint_name = kcu.constraint_name
|
||||
AND tc.table_schema = kcu.table_schema
|
||||
WHERE
|
||||
tc.constraint_type = 'FOREIGN KEY'
|
||||
AND tc.table_schema = $1
|
||||
AND tc.table_name = $2
|
||||
AND kcu.column_name = $3
|
||||
`,
|
||||
[schemaName, tableName, columnName],
|
||||
);
|
||||
|
||||
return foreignKeys[0]?.constraint_name;
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,6 +48,28 @@ describe('WorkspaceRelationComparator', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('should generate UPDATE action for changed relations', () => {
|
||||
const original = [
|
||||
createMockRelationMetadata({ onDeleteAction: 'CASCADE' }),
|
||||
];
|
||||
const standard = [
|
||||
createMockRelationMetadata({ onDeleteAction: 'SET_NULL' }),
|
||||
];
|
||||
|
||||
const result = comparator.compare(original, standard);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
action: ComparatorAction.UPDATE,
|
||||
object: expect.objectContaining({
|
||||
fromObjectMetadataId: 'object-1',
|
||||
fromFieldMetadataId: 'field-1',
|
||||
onDeleteAction: 'SET_NULL',
|
||||
}),
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should not generate any action for identical relations', () => {
|
||||
const relation = createMockRelationMetadata({});
|
||||
const original = [{ id: '1', ...relation }];
|
||||
|
||||
@ -11,6 +11,7 @@ import { RelationMetadataEntity } from 'src/metadata/relation-metadata/relation-
|
||||
import { transformMetadataForComparison } from 'src/workspace/workspace-sync-metadata/comparators/utils/transform-metadata-for-comparison.util';
|
||||
|
||||
const relationPropertiesToIgnore = ['createdAt', 'updatedAt'] as const;
|
||||
const relationPropertiesToUpdate = ['onDeleteAction'];
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceRelationComparator {
|
||||
@ -51,19 +52,54 @@ export class WorkspaceRelationComparator {
|
||||
);
|
||||
|
||||
for (const difference of relationMetadataDifference) {
|
||||
if (difference.type === 'CREATE') {
|
||||
results.push({
|
||||
action: ComparatorAction.CREATE,
|
||||
object: difference.value,
|
||||
});
|
||||
} else if (
|
||||
difference.type === 'REMOVE' &&
|
||||
difference.path[difference.path.length - 1] !== 'id'
|
||||
) {
|
||||
results.push({
|
||||
action: ComparatorAction.DELETE,
|
||||
object: difference.oldValue,
|
||||
});
|
||||
switch (difference.type) {
|
||||
case 'CREATE':
|
||||
results.push({
|
||||
action: ComparatorAction.CREATE,
|
||||
object: difference.value,
|
||||
});
|
||||
break;
|
||||
case 'REMOVE':
|
||||
if (difference.path[difference.path.length - 1] !== 'id') {
|
||||
results.push({
|
||||
action: ComparatorAction.DELETE,
|
||||
object: difference.oldValue,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'CHANGE':
|
||||
const fieldName = difference.path[0];
|
||||
const property = difference.path[difference.path.length - 1];
|
||||
|
||||
if (!relationPropertiesToUpdate.includes(property as string)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const originalRelationMetadata =
|
||||
originalRelationMetadataMap[fieldName];
|
||||
|
||||
if (!originalRelationMetadata) {
|
||||
throw new Error(
|
||||
`Relation ${fieldName} not found in originalRelationMetadataMap`,
|
||||
);
|
||||
}
|
||||
|
||||
results.push({
|
||||
action: ComparatorAction.UPDATE,
|
||||
object: {
|
||||
id: originalRelationMetadata.id,
|
||||
fromObjectMetadataId:
|
||||
originalRelationMetadata.fromObjectMetadataId,
|
||||
fromFieldMetadataId: originalRelationMetadata.fromFieldMetadataId,
|
||||
toObjectMetadataId: originalRelationMetadata.toObjectMetadataId,
|
||||
toFieldMetadataId: originalRelationMetadata.toFieldMetadataId,
|
||||
workspaceId: originalRelationMetadata.workspaceId,
|
||||
...{
|
||||
[property]: difference.value,
|
||||
},
|
||||
},
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
import { RelationMetadataDecoratorParams } from 'src/workspace/workspace-sync-metadata/interfaces/reflect-relation-metadata.interface';
|
||||
import {
|
||||
ReflectRelationMetadata,
|
||||
RelationMetadataDecoratorParams,
|
||||
} from 'src/workspace/workspace-sync-metadata/interfaces/reflect-relation-metadata.interface';
|
||||
|
||||
import { TypedReflect } from 'src/utils/typed-reflect';
|
||||
import { convertClassNameToObjectMetadataName } from 'src/workspace/workspace-sync-metadata/utils/convert-class-to-object-metadata-name.util';
|
||||
import { RelationOnDeleteAction } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
|
||||
export function RelationMetadata(
|
||||
params: RelationMetadataDecoratorParams,
|
||||
@ -29,8 +33,9 @@ export function RelationMetadata(
|
||||
toObjectNameSingular: params.objectName,
|
||||
fromFieldMetadataName: fieldKey,
|
||||
toFieldMetadataName: params.inverseSideFieldName ?? objectName,
|
||||
onDelete: params.onDelete ?? RelationOnDeleteAction.SET_NULL,
|
||||
gate,
|
||||
},
|
||||
} satisfies ReflectRelationMetadata,
|
||||
],
|
||||
target.constructor,
|
||||
);
|
||||
|
||||
@ -110,6 +110,7 @@ export class StandardRelationFactory {
|
||||
fromFieldMetadataId: fromFieldMetadata?.id,
|
||||
toFieldMetadataId: toFieldMetadata?.id,
|
||||
workspaceId: context.workspaceId,
|
||||
onDeleteAction: relationMetadata.onDelete,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@ -43,4 +43,5 @@ export type FieldComparatorResult =
|
||||
|
||||
export type RelationComparatorResult =
|
||||
| ComparatorCreateResult<Partial<RelationMetadataEntity>>
|
||||
| ComparatorDeleteResult<RelationMetadataEntity>;
|
||||
| ComparatorDeleteResult<RelationMetadataEntity>
|
||||
| ComparatorUpdateResult<Partial<RelationMetadataEntity>>;
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
import { ReflectRelationMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/reflect-Relation-metadata.interface';
|
||||
|
||||
export type PartialRelationMetadata = ReflectRelationMetadata & {
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
fromObjectMetadataId: string;
|
||||
toObjectMetadataId: string;
|
||||
fromFieldMetadataId: string;
|
||||
toFieldMetadataId: string;
|
||||
};
|
||||
@ -1,11 +1,15 @@
|
||||
import { GateDecoratorParams } from 'src/workspace/workspace-sync-metadata/interfaces/gate-decorator.interface';
|
||||
|
||||
import { RelationMetadataType } from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
import {
|
||||
RelationOnDeleteAction,
|
||||
RelationMetadataType,
|
||||
} from 'src/metadata/relation-metadata/relation-metadata.entity';
|
||||
|
||||
export interface RelationMetadataDecoratorParams {
|
||||
type: RelationMetadataType;
|
||||
objectName: string;
|
||||
inverseSideFieldName?: string;
|
||||
onDelete?: RelationOnDeleteAction;
|
||||
}
|
||||
|
||||
export interface ReflectRelationMetadata {
|
||||
@ -15,4 +19,5 @@ export interface ReflectRelationMetadata {
|
||||
fromFieldMetadataName: string;
|
||||
toFieldMetadataName: string;
|
||||
gate?: GateDecoratorParams;
|
||||
onDelete: RelationOnDeleteAction;
|
||||
}
|
||||
|
||||
@ -209,6 +209,7 @@ export class WorkspaceMetadataUpdaterService {
|
||||
storage: WorkspaceSyncStorage,
|
||||
): Promise<{
|
||||
createdRelationMetadataCollection: RelationMetadataEntity[];
|
||||
updatedRelationMetadataCollection: RelationMetadataEntity[];
|
||||
}> {
|
||||
const relationMetadataRepository = manager.getRepository(
|
||||
RelationMetadataEntity,
|
||||
@ -223,6 +224,15 @@ export class WorkspaceMetadataUpdaterService {
|
||||
storage.relationMetadataCreateCollection,
|
||||
);
|
||||
|
||||
/**
|
||||
* Update relation metadata
|
||||
*/
|
||||
|
||||
const updatedRelationMetadataCollection =
|
||||
await relationMetadataRepository.save(
|
||||
storage.relationMetadataUpdateCollection,
|
||||
);
|
||||
|
||||
/**
|
||||
* Delete relation metadata
|
||||
*/
|
||||
@ -250,6 +260,7 @@ export class WorkspaceMetadataUpdaterService {
|
||||
|
||||
return {
|
||||
createdRelationMetadataCollection,
|
||||
updatedRelationMetadataCollection,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,6 +84,8 @@ export class WorkspaceSyncRelationMetadataService {
|
||||
for (const relationComparatorResult of relationComparatorResults) {
|
||||
if (relationComparatorResult.action === ComparatorAction.CREATE) {
|
||||
storage.addCreateRelationMetadata(relationComparatorResult.object);
|
||||
} else if (relationComparatorResult.action === ComparatorAction.UPDATE) {
|
||||
storage.addUpdateRelationMetadata(relationComparatorResult.object);
|
||||
} else if (relationComparatorResult.action === ComparatorAction.DELETE) {
|
||||
storage.addDeleteRelationMetadata(relationComparatorResult.object);
|
||||
}
|
||||
@ -103,6 +105,16 @@ export class WorkspaceSyncRelationMetadataService {
|
||||
WorkspaceMigrationBuilderAction.CREATE,
|
||||
);
|
||||
|
||||
return createRelationWorkspaceMigrations;
|
||||
const updateRelationWorkspaceMigrations =
|
||||
await this.workspaceMigrationRelationFactory.create(
|
||||
originalObjectMetadataCollection,
|
||||
metadataRelationUpdaterResult.updatedRelationMetadataCollection,
|
||||
WorkspaceMigrationBuilderAction.UPDATE,
|
||||
);
|
||||
|
||||
return [
|
||||
...createRelationWorkspaceMigrations,
|
||||
...updateRelationWorkspaceMigrations,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { PartialObjectMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/partial-object-metadata.interface';
|
||||
import { PartialFieldMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/partial-field-metadata.interface';
|
||||
import { PartialRelationMetadata } from 'src/workspace/workspace-sync-metadata/interfaces/partial-relation-metadata.interface';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/metadata/object-metadata/object-metadata.entity';
|
||||
import { FieldMetadataEntity } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
@ -25,6 +26,8 @@ export class WorkspaceSyncStorage {
|
||||
[];
|
||||
private readonly _relationMetadataDeleteCollection: RelationMetadataEntity[] =
|
||||
[];
|
||||
private readonly _relationMetadataUpdateCollection: Partial<PartialRelationMetadata>[] =
|
||||
[];
|
||||
|
||||
constructor() {}
|
||||
|
||||
@ -56,6 +59,10 @@ export class WorkspaceSyncStorage {
|
||||
return this._relationMetadataCreateCollection;
|
||||
}
|
||||
|
||||
get relationMetadataUpdateCollection() {
|
||||
return this._relationMetadataUpdateCollection;
|
||||
}
|
||||
|
||||
get relationMetadataDeleteCollection() {
|
||||
return this._relationMetadataDeleteCollection;
|
||||
}
|
||||
@ -90,6 +97,10 @@ export class WorkspaceSyncStorage {
|
||||
this._relationMetadataCreateCollection.push(relation);
|
||||
}
|
||||
|
||||
addUpdateRelationMetadata(relation: Partial<PartialRelationMetadata>) {
|
||||
this._relationMetadataUpdateCollection.push(relation);
|
||||
}
|
||||
|
||||
addDeleteRelationMetadata(relation: RelationMetadataEntity) {
|
||||
this._relationMetadataDeleteCollection.push(relation);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user