Add field create and delete migration to metadata sync (#2942)
add field create and delete migration to metadata sync
This commit is contained in:
@ -9,6 +9,7 @@ export enum WorkspaceMigrationColumnActionType {
|
||||
CREATE = 'CREATE',
|
||||
ALTER = 'ALTER',
|
||||
RELATION = 'RELATION',
|
||||
DROP = 'DROP',
|
||||
}
|
||||
|
||||
export type WorkspaceMigrationEnum = string | { from: string; to: string };
|
||||
@ -41,12 +42,18 @@ export type WorkspaceMigrationColumnRelation = {
|
||||
isUnique?: boolean;
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationColumnDrop = {
|
||||
action: WorkspaceMigrationColumnActionType.DROP;
|
||||
columnName: string;
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationColumnAction = {
|
||||
action: WorkspaceMigrationColumnActionType;
|
||||
} & (
|
||||
| WorkspaceMigrationColumnCreate
|
||||
| WorkspaceMigrationColumnAlter
|
||||
| WorkspaceMigrationColumnRelation
|
||||
| WorkspaceMigrationColumnDrop
|
||||
);
|
||||
|
||||
export type WorkspaceMigrationTableAction = {
|
||||
|
||||
@ -187,6 +187,12 @@ export class WorkspaceMigrationRunnerService {
|
||||
columnMigration,
|
||||
);
|
||||
break;
|
||||
case WorkspaceMigrationColumnActionType.DROP:
|
||||
await queryRunner.dropColumn(
|
||||
`${schemaName}.${tableName}`,
|
||||
columnMigration.columnName,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Migration column action not supported`);
|
||||
}
|
||||
|
||||
@ -144,7 +144,8 @@ function generateFieldMetadata<T extends FieldMetadataType>(
|
||||
name: fieldKey,
|
||||
...metadata,
|
||||
targetColumnMap: targetColumnMap,
|
||||
isNullable: metadata.type === FieldMetadataType.RELATION ? true : isNullable,
|
||||
isNullable:
|
||||
metadata.type === FieldMetadataType.RELATION ? true : isNullable,
|
||||
isSystem,
|
||||
isCustom: false,
|
||||
options: null, // TODO: handle options + stringify for the diff.
|
||||
|
||||
@ -125,6 +125,7 @@ export class WorkspaceSyncMetadataService {
|
||||
if (value === null || typeof value !== 'object') {
|
||||
return [key, value];
|
||||
}
|
||||
|
||||
return [
|
||||
key,
|
||||
filterIgnoredProperties(
|
||||
@ -134,6 +135,7 @@ export class WorkspaceSyncMetadataService {
|
||||
if (property !== null && typeof property === 'object') {
|
||||
return JSON.stringify(property);
|
||||
}
|
||||
|
||||
return property;
|
||||
},
|
||||
),
|
||||
@ -157,6 +159,7 @@ export class WorkspaceSyncMetadataService {
|
||||
// We only handle CHANGE here as REMOVE and CREATE are handled earlier.
|
||||
if (diff.type === 'CHANGE') {
|
||||
const property = diff.path[0];
|
||||
|
||||
objectsToUpdate[objectInDB.id] = {
|
||||
...objectsToUpdate[objectInDB.id],
|
||||
[property]: diff.value,
|
||||
@ -166,6 +169,7 @@ export class WorkspaceSyncMetadataService {
|
||||
|
||||
for (const diff of fieldsDiff) {
|
||||
const fieldName = diff.path[0];
|
||||
|
||||
if (diff.type === 'CREATE')
|
||||
fieldsToCreate.push({
|
||||
...standardObjectFields[fieldName],
|
||||
@ -175,6 +179,7 @@ export class WorkspaceSyncMetadataService {
|
||||
fieldsToDelete.push(objectInDBFields[fieldName]);
|
||||
if (diff.type === 'CHANGE') {
|
||||
const property = diff.path[diff.path.length - 1];
|
||||
|
||||
fieldsToUpdate[objectInDBFields[fieldName].id] = {
|
||||
...fieldsToUpdate[objectInDBFields[fieldName].id],
|
||||
[property]: diff.value,
|
||||
@ -221,6 +226,7 @@ export class WorkspaceSyncMetadataService {
|
||||
const fieldsToDeleteWithoutRelationType = fieldsToDelete.filter(
|
||||
(field) => field.type !== FieldMetadataType.RELATION,
|
||||
);
|
||||
|
||||
if (fieldsToDeleteWithoutRelationType.length > 0) {
|
||||
await this.fieldMetadataRepository.delete(
|
||||
fieldsToDeleteWithoutRelationType.map((field) => field.id),
|
||||
@ -233,6 +239,7 @@ export class WorkspaceSyncMetadataService {
|
||||
objectsToDelete,
|
||||
fieldsToCreate,
|
||||
fieldsToDelete,
|
||||
objectsInDB,
|
||||
);
|
||||
|
||||
// We run syncRelationMetadata after everything to ensure that all objects and fields are
|
||||
@ -263,7 +270,9 @@ export class WorkspaceSyncMetadataService {
|
||||
objectsInDBByName,
|
||||
).reduce((result, currentObject) => {
|
||||
const key = `${currentObject.fromObjectMetadataId}->${currentObject.fromFieldMetadataId}`;
|
||||
|
||||
result[key] = currentObject;
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
@ -279,7 +288,9 @@ export class WorkspaceSyncMetadataService {
|
||||
)
|
||||
.reduce((result, currentObject) => {
|
||||
const key = `${currentObject.fromObjectMetadataId}->${currentObject.fromFieldMetadataId}`;
|
||||
|
||||
result[key] = currentObject;
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
@ -324,11 +335,12 @@ export class WorkspaceSyncMetadataService {
|
||||
private async generateMigrationsFromSync(
|
||||
objectsToCreate: ObjectMetadataEntity[],
|
||||
_objectsToDelete: ObjectMetadataEntity[],
|
||||
_fieldsToCreate: FieldMetadataEntity[],
|
||||
_fieldsToDelete: FieldMetadataEntity[],
|
||||
fieldsToCreate: FieldMetadataEntity[],
|
||||
fieldsToDelete: FieldMetadataEntity[],
|
||||
objectsInDB: ObjectMetadataEntity[],
|
||||
) {
|
||||
const migrationsToSave: Partial<WorkspaceMigrationEntity>[] = [];
|
||||
|
||||
|
||||
if (objectsToCreate.length > 0) {
|
||||
objectsToCreate.map((object) => {
|
||||
const migrations = [
|
||||
@ -359,6 +371,59 @@ export class WorkspaceSyncMetadataService {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: handle object delete migrations.
|
||||
// Note: we need to delete the relation first due to the DB constraint.
|
||||
|
||||
const objectsInDbById = objectsInDB.reduce((result, currentObject) => {
|
||||
result[currentObject.id] = currentObject;
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
if (fieldsToCreate.length > 0) {
|
||||
fieldsToCreate.map((field) => {
|
||||
const migrations = [
|
||||
{
|
||||
name: objectsInDbById[field.objectMetadataId].targetTableName,
|
||||
action: 'alter',
|
||||
columns: this.workspaceMigrationFactory.createColumnActions(
|
||||
WorkspaceMigrationColumnActionType.CREATE,
|
||||
field,
|
||||
),
|
||||
} satisfies WorkspaceMigrationTableAction,
|
||||
];
|
||||
|
||||
migrationsToSave.push({
|
||||
workspaceId: field.workspaceId,
|
||||
isCustom: false,
|
||||
migrations,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (fieldsToDelete.length > 0) {
|
||||
fieldsToDelete.map((field) => {
|
||||
const migrations = [
|
||||
{
|
||||
name: objectsInDbById[field.objectMetadataId].targetTableName,
|
||||
action: 'alter',
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.DROP,
|
||||
columnName: field.name,
|
||||
},
|
||||
],
|
||||
} satisfies WorkspaceMigrationTableAction,
|
||||
];
|
||||
|
||||
migrationsToSave.push({
|
||||
workspaceId: field.workspaceId,
|
||||
isCustom: false,
|
||||
migrations,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
await this.workspaceMigrationRepository.save(migrationsToSave);
|
||||
|
||||
// TODO: handle delete migrations
|
||||
|
||||
Reference in New Issue
Block a user