Add dashboard rollout commands (#15567)
## Tests ### makeSureDashboardNamingAvailableCommand Case 1: no dashboard custom object Case 2: with dashboard custom object ### SeedDashboardViewCommand Case 1: no existing view Case 2: with existing view
This commit is contained in:
+81
@@ -0,0 +1,81 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Command } from 'nest-commander';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
|
||||
type RunOnWorkspaceArgs,
|
||||
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { ObjectMetadataServiceV2 } from 'src/engine/metadata-modules/object-metadata/object-metadata-v2.service';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
@Command({
|
||||
name: 'upgrade:1-10:make-sure-dashboard-naming-available',
|
||||
description: 'Make sure the dashboard naming is available',
|
||||
})
|
||||
export class MakeSureDashboardNamingAvailableCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||
constructor(
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
@InjectRepository(ObjectMetadataEntity)
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
private readonly objectMetadataServiceV2: ObjectMetadataServiceV2,
|
||||
) {
|
||||
super(workspaceRepository, twentyORMGlobalManager);
|
||||
}
|
||||
|
||||
override async runOnWorkspace({
|
||||
workspaceId,
|
||||
options,
|
||||
}: RunOnWorkspaceArgs): Promise<void> {
|
||||
const potentialCustomDashboardObjectMetadata =
|
||||
await this.objectMetadataRepository.findOne({
|
||||
where: {
|
||||
nameSingular: 'dashboard',
|
||||
workspaceId,
|
||||
isCustom: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(potentialCustomDashboardObjectMetadata)) {
|
||||
this.logger.log(
|
||||
`No custom dashboard object metadata found for workspace ${workspaceId}. Skipping...`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.dryRun) {
|
||||
this.logger.log(
|
||||
`Would have updated the dashboard object metadata for workspace ${workspaceId}. Skipping...`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`Updating the dashboard object metadata for workspace ${workspaceId}...`,
|
||||
);
|
||||
|
||||
await this.objectMetadataServiceV2.updateOne({
|
||||
workspaceId,
|
||||
updateObjectInput: {
|
||||
id: potentialCustomDashboardObjectMetadata.id,
|
||||
update: {
|
||||
nameSingular: 'myDashboard',
|
||||
namePlural: 'myDashboards',
|
||||
labelSingular: 'My Dashboard',
|
||||
labelPlural: 'My Dashboards',
|
||||
},
|
||||
},
|
||||
});
|
||||
this.logger.log(
|
||||
`Updated the dashboard object metadata for workspace ${workspaceId}...`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Command } from 'nest-commander';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
|
||||
import {
|
||||
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
|
||||
type RunOnWorkspaceArgs,
|
||||
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
import { ViewKey } from 'src/engine/metadata-modules/view/enums/view-key.enum';
|
||||
import { WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { createCoreViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-core-views';
|
||||
import { prefillWorkspaceFavorites } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workspace-favorites';
|
||||
import { dashboardsAllView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/dashboards-all.view';
|
||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||
|
||||
@Command({
|
||||
name: 'upgrade:1-10:seed-dashboard-view',
|
||||
description: 'Seed the dashboard view',
|
||||
})
|
||||
export class SeedDashboardViewCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||
constructor(
|
||||
@InjectDataSource()
|
||||
private readonly coreDataSource: DataSource,
|
||||
@InjectRepository(WorkspaceEntity)
|
||||
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
|
||||
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
@InjectRepository(ObjectMetadataEntity)
|
||||
private readonly objectMetadataRepository: Repository<ObjectMetadataEntity>,
|
||||
@InjectRepository(DataSourceEntity)
|
||||
private readonly dataSourceRepository: Repository<DataSourceEntity>,
|
||||
@InjectRepository(ViewEntity)
|
||||
private readonly viewRepository: Repository<ViewEntity>,
|
||||
) {
|
||||
super(workspaceRepository, twentyORMGlobalManager);
|
||||
}
|
||||
|
||||
override async runOnWorkspace({
|
||||
workspaceId,
|
||||
options,
|
||||
}: RunOnWorkspaceArgs): Promise<void> {
|
||||
const [dashboardObjectMetadata] = await this.objectMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
standardId: STANDARD_OBJECT_IDS.dashboard,
|
||||
},
|
||||
relations: ['fields'],
|
||||
});
|
||||
|
||||
if (!isDefined(dashboardObjectMetadata)) {
|
||||
throw new Error(
|
||||
`Dashboard object metadata not found for workspace ${workspaceId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const views = [dashboardsAllView([dashboardObjectMetadata], true)];
|
||||
|
||||
const schema = await this.dataSourceRepository.findOne({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!isDefined(schema)) {
|
||||
throw new Error(`Schema not found for workspace ${workspaceId}`);
|
||||
}
|
||||
|
||||
const existingViews = await this.viewRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
objectMetadataId: dashboardObjectMetadata.id,
|
||||
key: ViewKey.INDEX,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingViews.length > 0) {
|
||||
this.logger.log(
|
||||
`Dashboard view already exists for workspace ${workspaceId}. Skipping...`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.dryRun) {
|
||||
this.logger.log(
|
||||
`Would have seeded dashboard view for workspace ${workspaceId}. Skipping...`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const queryRunner = this.coreDataSource.createQueryRunner();
|
||||
|
||||
await queryRunner.connect();
|
||||
|
||||
const createdViews = await createCoreViews(queryRunner, workspaceId, views);
|
||||
|
||||
await prefillWorkspaceFavorites(
|
||||
createdViews.map((view) => view.id),
|
||||
queryRunner.manager as WorkspaceEntityManager,
|
||||
schema.schema,
|
||||
);
|
||||
|
||||
await queryRunner.release();
|
||||
this.logger.log(
|
||||
`Successfully seeded dashboard view for workspace ${workspaceId}: ${createdViews.map((view) => view.name).join(', ')}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+10
@@ -4,14 +4,18 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { AddWorkflowRunStopStatusesCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-add-workflow-run-stop-statuses.command';
|
||||
import { CleanOrphanedKanbanAggregateOperationFieldMetadataIdCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-clean-orphaned-kanban-aggregate-operation-field-metadata-id.command';
|
||||
import { CreateViewKanbanFieldMetadataIdForeignKeyMigrationCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-create-view-kanban-field-metadata-id-foreign-key-migration.command';
|
||||
import { MakeSureDashboardNamingAvailableCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-make-sure-dashboard-naming-available.command';
|
||||
import { MigrateAttachmentAuthorToCreatedByCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-attachment-author-to-created-by.command';
|
||||
import { MigrateAttachmentTypeToFileCategoryCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-attachment-type-to-file-category.command';
|
||||
import { MigrateChannelPartialFullSyncStagesCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-channel-partial-full-sync-stages.command';
|
||||
import { RegenerateSearchVectorsCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-regenerate-search-vectors.command';
|
||||
import { SeedDashboardViewCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-seed-dashboard-view.command';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
import { IndexMetadataEntity } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
import { ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
||||
import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.module';
|
||||
|
||||
@@ -23,8 +27,10 @@ import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-sc
|
||||
FieldMetadataEntity,
|
||||
IndexMetadataEntity,
|
||||
ViewEntity,
|
||||
DataSourceEntity,
|
||||
]),
|
||||
WorkspaceSchemaManagerModule,
|
||||
ObjectMetadataModule,
|
||||
],
|
||||
providers: [
|
||||
MigrateChannelPartialFullSyncStagesCommand,
|
||||
@@ -33,6 +39,8 @@ import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-sc
|
||||
RegenerateSearchVectorsCommand,
|
||||
AddWorkflowRunStopStatusesCommand,
|
||||
CleanOrphanedKanbanAggregateOperationFieldMetadataIdCommand,
|
||||
MakeSureDashboardNamingAvailableCommand,
|
||||
SeedDashboardViewCommand,
|
||||
CreateViewKanbanFieldMetadataIdForeignKeyMigrationCommand,
|
||||
],
|
||||
exports: [
|
||||
@@ -42,6 +50,8 @@ import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-sc
|
||||
AddWorkflowRunStopStatusesCommand,
|
||||
CleanOrphanedKanbanAggregateOperationFieldMetadataIdCommand,
|
||||
MigrateChannelPartialFullSyncStagesCommand,
|
||||
MakeSureDashboardNamingAvailableCommand,
|
||||
SeedDashboardViewCommand,
|
||||
CreateViewKanbanFieldMetadataIdForeignKeyMigrationCommand,
|
||||
],
|
||||
})
|
||||
|
||||
+6
@@ -12,10 +12,12 @@ import {
|
||||
import { AddWorkflowRunStopStatusesCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-add-workflow-run-stop-statuses.command';
|
||||
import { CleanOrphanedKanbanAggregateOperationFieldMetadataIdCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-clean-orphaned-kanban-aggregate-operation-field-metadata-id.command';
|
||||
import { CreateViewKanbanFieldMetadataIdForeignKeyMigrationCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-create-view-kanban-field-metadata-id-foreign-key-migration.command';
|
||||
import { MakeSureDashboardNamingAvailableCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-make-sure-dashboard-naming-available.command';
|
||||
import { MigrateAttachmentAuthorToCreatedByCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-attachment-author-to-created-by.command';
|
||||
import { MigrateAttachmentTypeToFileCategoryCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-attachment-type-to-file-category.command';
|
||||
import { MigrateChannelPartialFullSyncStagesCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-channel-partial-full-sync-stages.command';
|
||||
import { RegenerateSearchVectorsCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-regenerate-search-vectors.command';
|
||||
import { SeedDashboardViewCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-seed-dashboard-view.command';
|
||||
import { FixLabelIdentifierPositionAndVisibilityCommand } from 'src/database/commands/upgrade-version-command/1-6/1-6-fix-label-identifier-position-and-visibility.command';
|
||||
import { BackfillWorkflowManualTriggerAvailabilityCommand } from 'src/database/commands/upgrade-version-command/1-7/1-7-backfill-workflow-manual-trigger-availability.command';
|
||||
import { DeduplicateUniqueFieldsCommand } from 'src/database/commands/upgrade-version-command/1-8/1-8-deduplicate-unique-fields.command';
|
||||
@@ -62,6 +64,8 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
protected readonly addWorkflowRunStopStatusesCommand: AddWorkflowRunStopStatusesCommand,
|
||||
protected readonly cleanOrphanedKanbanAggregateOperationFieldMetadataIdCommand: CleanOrphanedKanbanAggregateOperationFieldMetadataIdCommand,
|
||||
protected readonly migrateChannelPartialFullSyncStagesCommand: MigrateChannelPartialFullSyncStagesCommand,
|
||||
protected readonly makeSureDashboardNamingAvailableCommand: MakeSureDashboardNamingAvailableCommand,
|
||||
protected readonly seedDashboardViewCommand: SeedDashboardViewCommand,
|
||||
protected readonly createViewKanbanFieldMetadataIdForeignKeyMigrationCommand: CreateViewKanbanFieldMetadataIdForeignKeyMigrationCommand,
|
||||
) {
|
||||
super(
|
||||
@@ -101,10 +105,12 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
this.cleanOrphanedKanbanAggregateOperationFieldMetadataIdCommand,
|
||||
this.createViewKanbanFieldMetadataIdForeignKeyMigrationCommand,
|
||||
this.migrateChannelPartialFullSyncStagesCommand,
|
||||
this.makeSureDashboardNamingAvailableCommand,
|
||||
],
|
||||
afterSyncMetadata: [
|
||||
this.migrateAttachmentAuthorToCreatedByCommand,
|
||||
this.migrateAttachmentTypeToFileCategoryCommand,
|
||||
this.seedDashboardViewCommand,
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user