Alter comment on foreign key deletion (#5406)
We do not update the comment on the local table when a foreign table key is deleted. This was not breaking, which is why we did not see it. But comments should be kept up to date. --------- Co-authored-by: Thomas Trompette <thomast@twenty.com>
This commit is contained in:
@ -54,14 +54,15 @@ 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 { createWorkspaceMigrationsForCustomObject } from 'src/engine/metadata-modules/object-metadata/utils/create-workspace-migrations-for-custom-object.util';
|
import { createWorkspaceMigrationsForCustomObjectRelations } from 'src/engine/metadata-modules/object-metadata/utils/create-migrations-for-custom-object-relations.util';
|
||||||
import { createWorkspaceMigrationsForRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/create-workspace-migrations-for-remote-object.util';
|
import { createWorkspaceMigrationsForRemoteObjectRelations } from 'src/engine/metadata-modules/object-metadata/utils/create-workspace-migrations-for-remote-object-relations.util';
|
||||||
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 { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||||
import { validateObjectMetadataInput } from 'src/engine/metadata-modules/object-metadata/utils/validate-object-metadata-input.util';
|
import { validateObjectMetadataInput } 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 { 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 { 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 { UpdateOneObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/update-object.input';
|
||||||
|
import { createMigrationToAlterCommentOnForeignKeyDeletion } from 'src/engine/metadata-modules/object-metadata/utils/create-migration-for-foreign-key-comment-alteration.util';
|
||||||
|
|
||||||
import { ObjectMetadataEntity } from './object-metadata.entity';
|
import { ObjectMetadataEntity } from './object-metadata.entity';
|
||||||
|
|
||||||
@ -205,6 +206,17 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// for remote objects, we need to update the comment of the foreign key column
|
||||||
|
if (objectMetadata.isRemote) {
|
||||||
|
await createMigrationToAlterCommentOnForeignKeyDeletion(
|
||||||
|
this.dataSourceService,
|
||||||
|
this.typeORMService,
|
||||||
|
this.workspaceMigrationService,
|
||||||
|
workspaceId,
|
||||||
|
relationToDelete,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,7 +555,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
generateMigrationName(`create-${createdObjectMetadata.nameSingular}`),
|
generateMigrationName(`create-${createdObjectMetadata.nameSingular}`),
|
||||||
createdObjectMetadata.workspaceId,
|
createdObjectMetadata.workspaceId,
|
||||||
isRemoteObject
|
isRemoteObject
|
||||||
? await createWorkspaceMigrationsForRemoteObject(
|
? await createWorkspaceMigrationsForRemoteObjectRelations(
|
||||||
createdObjectMetadata,
|
createdObjectMetadata,
|
||||||
activityTargetObjectMetadata,
|
activityTargetObjectMetadata,
|
||||||
attachmentObjectMetadata,
|
attachmentObjectMetadata,
|
||||||
@ -553,7 +565,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
objectMetadataInput.primaryKeyColumnType ?? 'uuid',
|
objectMetadataInput.primaryKeyColumnType ?? 'uuid',
|
||||||
workspaceDataSource,
|
workspaceDataSource,
|
||||||
)
|
)
|
||||||
: createWorkspaceMigrationsForCustomObject(
|
: createWorkspaceMigrationsForCustomObjectRelations(
|
||||||
createdObjectMetadata,
|
createdObjectMetadata,
|
||||||
activityTargetObjectMetadata,
|
activityTargetObjectMetadata,
|
||||||
attachmentObjectMetadata,
|
attachmentObjectMetadata,
|
||||||
|
|||||||
@ -0,0 +1,76 @@
|
|||||||
|
import { buildAlteredCommentOnForeignKeyDeletion } from 'src/engine/metadata-modules/object-metadata/utils/create-migration-for-foreign-key-comment-alteration.util';
|
||||||
|
|
||||||
|
describe('buildAlteredCommentOnForeignKeyDeletion', () => {
|
||||||
|
const localObjectMetadataName = 'favorite';
|
||||||
|
const remoteObjectMetadataName = 'blog';
|
||||||
|
const schema = 'schema';
|
||||||
|
const workspaceDataSource = {
|
||||||
|
query: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should return null if no comment ', async () => {
|
||||||
|
workspaceDataSource.query.mockResolvedValueOnce([]);
|
||||||
|
|
||||||
|
const result = await buildAlteredCommentOnForeignKeyDeletion(
|
||||||
|
localObjectMetadataName,
|
||||||
|
remoteObjectMetadataName,
|
||||||
|
schema,
|
||||||
|
workspaceDataSource as any,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null if the existing comment does not contain foreign keys', async () => {
|
||||||
|
workspaceDataSource.query.mockResolvedValueOnce([
|
||||||
|
{ col_description: '@graphql({"totalCount":{"enabled":true}})' },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const result = await buildAlteredCommentOnForeignKeyDeletion(
|
||||||
|
localObjectMetadataName,
|
||||||
|
remoteObjectMetadataName,
|
||||||
|
schema,
|
||||||
|
workspaceDataSource as any,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return altered comment without foreign key', async () => {
|
||||||
|
const existingComment = {
|
||||||
|
col_description: `@graphql({"totalCount":{"enabled":true},"foreign_keys":[{"local_name":"favoriteCollection","local_columns":["${remoteObjectMetadataName}Id"],"foreign_name":"${remoteObjectMetadataName}","foreign_schema":"schema","foreign_table":"${remoteObjectMetadataName}","foreign_columns":["id"]}]})`,
|
||||||
|
};
|
||||||
|
|
||||||
|
workspaceDataSource.query.mockResolvedValueOnce([existingComment]);
|
||||||
|
|
||||||
|
const result = await buildAlteredCommentOnForeignKeyDeletion(
|
||||||
|
localObjectMetadataName,
|
||||||
|
remoteObjectMetadataName,
|
||||||
|
schema,
|
||||||
|
workspaceDataSource as any,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe(
|
||||||
|
'@graphql({"totalCount":{"enabled":true},"foreign_keys":[]})',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return altered comment without the input foreign key', async () => {
|
||||||
|
const existingComment = {
|
||||||
|
col_description: `@graphql({"totalCount":{"enabled":true},"foreign_keys":[{"local_name":"favoriteCollection","local_columns":["${remoteObjectMetadataName}Id"],"foreign_name":"${remoteObjectMetadataName}","foreign_schema":"schema","foreign_table":"${remoteObjectMetadataName}","foreign_columns":["id"]}, {"local_name":"favoriteCollection","local_columns":["testId"],"foreign_name":"test","foreign_schema":"schema","foreign_table":"test","foreign_columns":["id"]}]})`,
|
||||||
|
};
|
||||||
|
|
||||||
|
workspaceDataSource.query.mockResolvedValueOnce([existingComment]);
|
||||||
|
|
||||||
|
const result = await buildAlteredCommentOnForeignKeyDeletion(
|
||||||
|
localObjectMetadataName,
|
||||||
|
remoteObjectMetadataName,
|
||||||
|
schema,
|
||||||
|
workspaceDataSource as any,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe(
|
||||||
|
'@graphql({"totalCount":{"enabled":true},"foreign_keys":[{"local_name":"favoriteCollection","local_columns":["testId"],"foreign_name":"test","foreign_schema":"schema","foreign_table":"test","foreign_columns":["id"]}]})',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,97 @@
|
|||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
|
||||||
|
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||||
|
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||||
|
import { RelationToDelete } from 'src/engine/metadata-modules/relation-metadata/types/relation-to-delete';
|
||||||
|
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
|
||||||
|
import {
|
||||||
|
WorkspaceMigrationTableActionType,
|
||||||
|
WorkspaceMigrationColumnActionType,
|
||||||
|
WorkspaceMigrationCreateComment,
|
||||||
|
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||||
|
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
|
||||||
|
import { computeTableName } from 'src/engine/utils/compute-table-name.util';
|
||||||
|
|
||||||
|
export const buildAlteredCommentOnForeignKeyDeletion = async (
|
||||||
|
localObjectMetadataName: string,
|
||||||
|
remoteObjectMetadataName: string,
|
||||||
|
schema: string,
|
||||||
|
workspaceDataSource: DataSource | undefined,
|
||||||
|
): Promise<string | null> => {
|
||||||
|
const existingComment = await workspaceDataSource?.query(
|
||||||
|
`SELECT col_description('${schema}."${localObjectMetadataName}"'::regclass, 0)`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!existingComment[0]?.col_description) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const commentWithoutGraphQL = existingComment[0].col_description
|
||||||
|
.replace('@graphql(', '')
|
||||||
|
.replace(')', '');
|
||||||
|
|
||||||
|
const parsedComment = JSON.parse(commentWithoutGraphQL);
|
||||||
|
|
||||||
|
const currentForeignKeys = parsedComment.foreign_keys;
|
||||||
|
|
||||||
|
if (!currentForeignKeys) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedForeignKeys = currentForeignKeys.filter(
|
||||||
|
(foreignKey: any) =>
|
||||||
|
foreignKey.foreign_name !== remoteObjectMetadataName &&
|
||||||
|
foreignKey.foreign_table !== remoteObjectMetadataName,
|
||||||
|
);
|
||||||
|
|
||||||
|
parsedComment.foreign_keys = updatedForeignKeys;
|
||||||
|
|
||||||
|
return `@graphql(${JSON.stringify(parsedComment)})`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createMigrationToAlterCommentOnForeignKeyDeletion = async (
|
||||||
|
dataSourceService: DataSourceService,
|
||||||
|
typeORMService: TypeORMService,
|
||||||
|
workspaceMigrationService: WorkspaceMigrationService,
|
||||||
|
workspaceId: string,
|
||||||
|
relationToDelete: RelationToDelete,
|
||||||
|
) => {
|
||||||
|
const dataSourceMetadata =
|
||||||
|
await dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||||
|
workspaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const workspaceDataSource =
|
||||||
|
await typeORMService.connectToDataSource(dataSourceMetadata);
|
||||||
|
|
||||||
|
const alteredComment = await buildAlteredCommentOnForeignKeyDeletion(
|
||||||
|
relationToDelete.toObjectName,
|
||||||
|
relationToDelete.fromObjectName,
|
||||||
|
dataSourceMetadata.schema,
|
||||||
|
workspaceDataSource,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (alteredComment) {
|
||||||
|
await workspaceMigrationService.createCustomMigration(
|
||||||
|
generateMigrationName(
|
||||||
|
`alter-comment-${relationToDelete.fromObjectName}-${relationToDelete.toObjectName}`,
|
||||||
|
),
|
||||||
|
workspaceId,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: computeTableName(
|
||||||
|
relationToDelete.toObjectName,
|
||||||
|
relationToDelete.toObjectMetadataIsCustom,
|
||||||
|
),
|
||||||
|
action: WorkspaceMigrationTableActionType.ALTER,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
action: WorkspaceMigrationColumnActionType.CREATE_COMMENT,
|
||||||
|
comment: alteredComment,
|
||||||
|
} satisfies WorkspaceMigrationCreateComment,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -9,7 +9,7 @@ import {
|
|||||||
} 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';
|
||||||
|
|
||||||
export const createWorkspaceMigrationsForCustomObject = (
|
export const createWorkspaceMigrationsForCustomObjectRelations = (
|
||||||
createdObjectMetadata: ObjectMetadataEntity,
|
createdObjectMetadata: ObjectMetadataEntity,
|
||||||
activityTargetObjectMetadata: ObjectMetadataEntity,
|
activityTargetObjectMetadata: ObjectMetadataEntity,
|
||||||
attachmentObjectMetadata: ObjectMetadataEntity,
|
attachmentObjectMetadata: ObjectMetadataEntity,
|
||||||
@ -47,7 +47,7 @@ const buildCommentForRemoteObjectForeignKey = async (
|
|||||||
return `@graphql(${JSON.stringify(parsedComment)})`;
|
return `@graphql(${JSON.stringify(parsedComment)})`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createWorkspaceMigrationsForRemoteObject = async (
|
export const createWorkspaceMigrationsForRemoteObjectRelations = async (
|
||||||
createdObjectMetadata: ObjectMetadataEntity,
|
createdObjectMetadata: ObjectMetadataEntity,
|
||||||
activityTargetObjectMetadata: ObjectMetadataEntity,
|
activityTargetObjectMetadata: ObjectMetadataEntity,
|
||||||
attachmentObjectMetadata: ObjectMetadataEntity,
|
attachmentObjectMetadata: ObjectMetadataEntity,
|
||||||
Reference in New Issue
Block a user