Create command menu items for workflows with manual trigger (#18746)
- Automatically create and sync command menu items for all workflows with a manual trigger - Refactor `useCommandMenuItemsFromBackend` - Prefill a _Quick Lead_ workflow command menu item during workspace setup and dev seeding - Add a ready prop to `HeadlessEngineCommandWrapperEffect` to prevent premature execution when async data hasn't loaded yet --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
+4
@@ -2,6 +2,8 @@ import { Module } from '@nestjs/common';
|
||||
|
||||
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { CommandMenuItemModule } from 'src/engine/metadata-modules/command-menu-item/command-menu-item.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { RecordPositionModule } from 'src/engine/core-modules/record-position/record-position.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { LogicFunctionModule } from 'src/engine/metadata-modules/logic-function/logic-function.module';
|
||||
@@ -43,6 +45,8 @@ import { WorkflowVersionValidationWorkspaceService } from 'src/modules/workflow/
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
ObjectMetadataModule,
|
||||
CodeStepBuildModule,
|
||||
CommandMenuItemModule,
|
||||
FeatureFlagModule,
|
||||
],
|
||||
providers: [
|
||||
WorkflowCreateOnePreQueryHook,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { CommandMenuItemModule } from 'src/engine/metadata-modules/command-menu-item/command-menu-item.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { LogicFunctionModule } from 'src/engine/metadata-modules/logic-function/logic-function.module';
|
||||
import { WorkflowQueryHookModule } from 'src/modules/workflow/common/query-hooks/workflow-query-hook.module';
|
||||
@@ -10,6 +12,8 @@ import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/work
|
||||
WorkflowQueryHookModule,
|
||||
LogicFunctionModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
CommandMenuItemModule,
|
||||
FeatureFlagModule,
|
||||
],
|
||||
providers: [WorkflowCommonWorkspaceService],
|
||||
exports: [WorkflowCommonWorkspaceService],
|
||||
|
||||
+94
-25
@@ -1,7 +1,10 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { FeatureFlagKey } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { CommandMenuItemService } from 'src/engine/metadata-modules/command-menu-item/command-menu-item.service';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
|
||||
@@ -40,10 +43,14 @@ export type ObjectMetadataInfo = {
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowCommonWorkspaceService {
|
||||
private readonly logger = new Logger(WorkflowCommonWorkspaceService.name);
|
||||
|
||||
constructor(
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly logicFunctionFromSourceService: LogicFunctionFromSourceService,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly commandMenuItemService: CommandMenuItemService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
|
||||
async getWorkflowVersionOrFail({
|
||||
@@ -262,38 +269,100 @@ export class WorkflowCommonWorkspaceService {
|
||||
{ shouldBypassPermissionChecks: true },
|
||||
);
|
||||
|
||||
const workflow = await workflowRepository.findOne({
|
||||
where: { id: workflowId },
|
||||
withDeleted: true,
|
||||
});
|
||||
|
||||
if (workflow?.statuses?.includes(WorkflowStatus.ACTIVE)) {
|
||||
const newStatuses = [
|
||||
...workflow.statuses.filter(
|
||||
(status) => status !== WorkflowStatus.ACTIVE,
|
||||
),
|
||||
WorkflowStatus.DEACTIVATED,
|
||||
];
|
||||
|
||||
await workflowRepository.update(workflowId, {
|
||||
statuses: newStatuses,
|
||||
});
|
||||
}
|
||||
|
||||
const workflowVersions = await workflowVersionRepository.find({
|
||||
where: {
|
||||
workflowId,
|
||||
},
|
||||
where: { workflowId },
|
||||
withDeleted: true,
|
||||
});
|
||||
|
||||
for (const workflowVersion of workflowVersions) {
|
||||
if (workflowVersion.status === WorkflowVersionStatus.ACTIVE) {
|
||||
await workflowVersionRepository.update(workflowVersion.id, {
|
||||
status: WorkflowVersionStatus.DEACTIVATED,
|
||||
});
|
||||
await this.cleanupCommandMenuItemForVersion(
|
||||
workflowVersion.id,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource();
|
||||
|
||||
const queryRunner = workspaceDataSource.createQueryRunner();
|
||||
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
const workflow = await workflowRepository.findOne(
|
||||
{
|
||||
where: { id: workflowId },
|
||||
withDeleted: true,
|
||||
},
|
||||
queryRunner.manager,
|
||||
);
|
||||
|
||||
if (workflow?.statuses?.includes(WorkflowStatus.ACTIVE)) {
|
||||
const newStatuses = [
|
||||
...workflow.statuses.filter(
|
||||
(status) => status !== WorkflowStatus.ACTIVE,
|
||||
),
|
||||
WorkflowStatus.DEACTIVATED,
|
||||
];
|
||||
|
||||
await workflowRepository.update(
|
||||
workflowId,
|
||||
{ statuses: newStatuses },
|
||||
queryRunner.manager,
|
||||
);
|
||||
}
|
||||
|
||||
for (const workflowVersion of workflowVersions) {
|
||||
if (workflowVersion.status === WorkflowVersionStatus.ACTIVE) {
|
||||
await workflowVersionRepository.update(
|
||||
workflowVersion.id,
|
||||
{ status: WorkflowVersionStatus.DEACTIVATED },
|
||||
queryRunner.manager,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (error) {
|
||||
if (queryRunner.isTransactionActive) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
}
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
private async cleanupCommandMenuItemForVersion(
|
||||
workflowVersionId: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const isCommandMenuItemEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!isCommandMenuItemEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const existingCommandMenuItem =
|
||||
await this.commandMenuItemService.findByWorkflowVersionId(
|
||||
workflowVersionId,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (isDefined(existingCommandMenuItem)) {
|
||||
await this.commandMenuItemService.delete(
|
||||
existingCommandMenuItem.id,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async handleLogicFunctionSubEntities({
|
||||
|
||||
Reference in New Issue
Block a user