Fix remote object read-only + remove relations (#4921)
- Set `readOnly` boolean in table row context. Preventing updates and deletion - Show page is null for remote objects. No need for complicated design since this is temporary? - Relation creations are now behind a feature flag for remote objects - Refetch objects and views after syncing objects --------- Co-authored-by: Thomas Trompette <thomast@twenty.com>
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import {
|
||||
NestjsQueryGraphQLModule,
|
||||
@ -15,6 +16,8 @@ import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||
import { ObjectMetadataResolver } from 'src/engine/metadata-modules/object-metadata/object-metadata.resolver';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
|
||||
import { ObjectMetadataService } from './object-metadata.service';
|
||||
import { ObjectMetadataEntity } from './object-metadata.entity';
|
||||
@ -32,9 +35,11 @@ import { ObjectMetadataDTO } from './dtos/object-metadata.dto';
|
||||
[ObjectMetadataEntity, FieldMetadataEntity, RelationMetadataEntity],
|
||||
'metadata',
|
||||
),
|
||||
TypeOrmModule.forFeature([FeatureFlagEntity], 'core'),
|
||||
DataSourceModule,
|
||||
WorkspaceMigrationModule,
|
||||
WorkspaceMigrationRunnerModule,
|
||||
FeatureFlagModule,
|
||||
],
|
||||
services: [ObjectMetadataService],
|
||||
resolvers: [
|
||||
|
||||
@ -7,7 +7,12 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import console from 'console';
|
||||
|
||||
import { FindManyOptions, FindOneOptions, Repository } from 'typeorm';
|
||||
import {
|
||||
DataSource,
|
||||
FindManyOptions,
|
||||
FindOneOptions,
|
||||
Repository,
|
||||
} from 'typeorm';
|
||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||
import { Query, QueryOptions } from '@ptc-org/nestjs-query-core';
|
||||
|
||||
@ -46,9 +51,14 @@ import {
|
||||
createForeignKeyDeterministicUuid,
|
||||
createRelationDeterministicUuid,
|
||||
} from 'src/engine/workspace-manager/workspace-sync-metadata/utils/create-deterministic-uuid.util';
|
||||
import { buildWorkspaceMigrationsForCustomObject } from 'src/engine/metadata-modules/object-metadata/utils/build-workspace-migrations-for-custom-object.util';
|
||||
import { buildWorkspaceMigrationsForRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/build-workspace-migrations-for-remote-object.util';
|
||||
import { createWorkspaceMigrationsForCustomObject } from 'src/engine/metadata-modules/object-metadata/utils/create-workspace-migrations-for-custom-object.util';
|
||||
import { createWorkspaceMigrationsForRemoteObject } from 'src/engine/metadata-modules/object-metadata/utils/create-workspace-migrations-for-remote-object.util';
|
||||
import { computeColumnName } from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||
import {
|
||||
FeatureFlagEntity,
|
||||
FeatureFlagKeys,
|
||||
} from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
|
||||
import { ObjectMetadataEntity } from './object-metadata.entity';
|
||||
|
||||
@ -70,6 +80,8 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
private readonly typeORMService: TypeORMService,
|
||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||
private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService,
|
||||
@InjectRepository(FeatureFlagEntity, 'core')
|
||||
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
|
||||
) {
|
||||
super(objectMetadataRepository);
|
||||
}
|
||||
@ -322,27 +334,6 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
[],
|
||||
});
|
||||
|
||||
const { eventObjectMetadata } = await this.createEventRelation(
|
||||
objectMetadataInput.workspaceId,
|
||||
createdObjectMetadata,
|
||||
);
|
||||
|
||||
const { activityTargetObjectMetadata } =
|
||||
await this.createActivityTargetRelation(
|
||||
objectMetadataInput.workspaceId,
|
||||
createdObjectMetadata,
|
||||
);
|
||||
|
||||
const { favoriteObjectMetadata } = await this.createFavoriteRelation(
|
||||
objectMetadataInput.workspaceId,
|
||||
createdObjectMetadata,
|
||||
);
|
||||
|
||||
const { attachmentObjectMetadata } = await this.createAttachmentRelation(
|
||||
objectMetadataInput.workspaceId,
|
||||
createdObjectMetadata,
|
||||
);
|
||||
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
createdObjectMetadata.workspaceId,
|
||||
@ -351,27 +342,12 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
const workspaceDataSource =
|
||||
await this.typeORMService.connectToDataSource(dataSourceMetadata);
|
||||
|
||||
await this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(`create-${createdObjectMetadata.nameSingular}`),
|
||||
createdObjectMetadata.workspaceId,
|
||||
isCustom
|
||||
? buildWorkspaceMigrationsForCustomObject(
|
||||
createdObjectMetadata,
|
||||
activityTargetObjectMetadata,
|
||||
attachmentObjectMetadata,
|
||||
eventObjectMetadata,
|
||||
favoriteObjectMetadata,
|
||||
)
|
||||
: await buildWorkspaceMigrationsForRemoteObject(
|
||||
createdObjectMetadata,
|
||||
activityTargetObjectMetadata,
|
||||
attachmentObjectMetadata,
|
||||
eventObjectMetadata,
|
||||
favoriteObjectMetadata,
|
||||
lastDataSourceMetadata.schema,
|
||||
objectMetadataInput.remoteTablePrimaryKeyColumnType ?? 'uuid',
|
||||
workspaceDataSource,
|
||||
),
|
||||
await this.createObjectRelationsMetadataAndMigrations(
|
||||
objectMetadataInput,
|
||||
createdObjectMetadata,
|
||||
lastDataSourceMetadata,
|
||||
workspaceDataSource,
|
||||
objectMetadataInput.isRemote,
|
||||
);
|
||||
|
||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||
@ -483,6 +459,67 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
await this.objectMetadataRepository.delete({ workspaceId });
|
||||
}
|
||||
|
||||
private async createObjectRelationsMetadataAndMigrations(
|
||||
objectMetadataInput: CreateObjectInput,
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
lastDataSourceMetadata: DataSourceEntity,
|
||||
workspaceDataSource: DataSource | undefined,
|
||||
isRemoteObject: boolean = false,
|
||||
) {
|
||||
const isRelationEnabledForRemoteObjects =
|
||||
await this.isRelationEnabledForRemoteObjects(
|
||||
objectMetadataInput.workspaceId,
|
||||
);
|
||||
|
||||
if (isRemoteObject && !isRelationEnabledForRemoteObjects) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { eventObjectMetadata } = await this.createEventRelation(
|
||||
objectMetadataInput.workspaceId,
|
||||
createdObjectMetadata,
|
||||
);
|
||||
|
||||
const { activityTargetObjectMetadata } =
|
||||
await this.createActivityTargetRelation(
|
||||
objectMetadataInput.workspaceId,
|
||||
createdObjectMetadata,
|
||||
);
|
||||
|
||||
const { favoriteObjectMetadata } = await this.createFavoriteRelation(
|
||||
objectMetadataInput.workspaceId,
|
||||
createdObjectMetadata,
|
||||
);
|
||||
|
||||
const { attachmentObjectMetadata } = await this.createAttachmentRelation(
|
||||
objectMetadataInput.workspaceId,
|
||||
createdObjectMetadata,
|
||||
);
|
||||
|
||||
return this.workspaceMigrationService.createCustomMigration(
|
||||
generateMigrationName(`create-${createdObjectMetadata.nameSingular}`),
|
||||
createdObjectMetadata.workspaceId,
|
||||
isRemoteObject
|
||||
? await createWorkspaceMigrationsForRemoteObject(
|
||||
createdObjectMetadata,
|
||||
activityTargetObjectMetadata,
|
||||
attachmentObjectMetadata,
|
||||
eventObjectMetadata,
|
||||
favoriteObjectMetadata,
|
||||
lastDataSourceMetadata.schema,
|
||||
objectMetadataInput.remoteTablePrimaryKeyColumnType ?? 'uuid',
|
||||
workspaceDataSource,
|
||||
)
|
||||
: createWorkspaceMigrationsForCustomObject(
|
||||
createdObjectMetadata,
|
||||
activityTargetObjectMetadata,
|
||||
attachmentObjectMetadata,
|
||||
eventObjectMetadata,
|
||||
favoriteObjectMetadata,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private async createActivityTargetRelation(
|
||||
workspaceId: string,
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
@ -855,4 +892,14 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
|
||||
return { favoriteObjectMetadata };
|
||||
}
|
||||
|
||||
private async isRelationEnabledForRemoteObjects(workspaceId: string) {
|
||||
const featureFlag = await this.featureFlagRepository.findOneBy({
|
||||
workspaceId,
|
||||
key: FeatureFlagKeys.IsRelationForRemoteObjectsEnabled,
|
||||
value: true,
|
||||
});
|
||||
|
||||
return featureFlag && featureFlag.value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
|
||||
export const buildWorkspaceMigrationsForCustomObject = (
|
||||
export const createWorkspaceMigrationsForCustomObject = (
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
activityTargetObjectMetadata: ObjectMetadataEntity,
|
||||
attachmentObjectMetadata: ObjectMetadataEntity,
|
||||
@ -47,7 +47,7 @@ const buildCommentForRemoteObjectForeignKey = async (
|
||||
return `@graphql(${JSON.stringify(parsedComment)})`;
|
||||
};
|
||||
|
||||
export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
export const createWorkspaceMigrationsForRemoteObject = async (
|
||||
createdObjectMetadata: ObjectMetadataEntity,
|
||||
activityTargetObjectMetadata: ObjectMetadataEntity,
|
||||
attachmentObjectMetadata: ObjectMetadataEntity,
|
||||
@ -74,19 +74,6 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(activityTargetObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: remoteTablePrimaryKeyColumnType,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(activityTargetObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
@ -117,19 +104,6 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(attachmentObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: remoteTablePrimaryKeyColumnType,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(attachmentObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
@ -160,19 +134,6 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(eventObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: remoteTablePrimaryKeyColumnType,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(eventObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
@ -203,19 +164,6 @@ export const buildWorkspaceMigrationsForRemoteObject = async (
|
||||
} satisfies WorkspaceMigrationColumnCreate,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(favoriteObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
columns: [
|
||||
{
|
||||
action: WorkspaceMigrationColumnActionType.CREATE,
|
||||
columnName: computeColumnName(createdObjectMetadata.nameSingular, {
|
||||
isForeignKey: true,
|
||||
}),
|
||||
columnType: remoteTablePrimaryKeyColumnType,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: computeObjectTargetTable(favoriteObjectMetadata),
|
||||
action: WorkspaceMigrationTableActionType.ALTER,
|
||||
@ -197,7 +197,7 @@ export class RemoteTableService {
|
||||
description: 'Remote table',
|
||||
dataSourceId: dataSourceMetatada.id,
|
||||
workspaceId: workspaceId,
|
||||
icon: 'IconUser',
|
||||
icon: 'IconPlug',
|
||||
isRemote: true,
|
||||
remoteTablePrimaryKeyColumnType: remoteTableIdColumn.udtName,
|
||||
} satisfies CreateObjectInput);
|
||||
@ -213,7 +213,7 @@ export class RemoteTableService {
|
||||
objectMetadataId: objectMetadata.id,
|
||||
isRemoteCreation: true,
|
||||
isNullable: true,
|
||||
icon: 'IconUser',
|
||||
icon: 'IconPlug',
|
||||
} satisfies CreateFieldInput);
|
||||
|
||||
if (column.columnName === 'id') {
|
||||
|
||||
Reference in New Issue
Block a user