Add command menu item entity (#17181)

This commit is contained in:
Abdul Rahman
2026-01-17 00:32:07 +05:30
committed by GitHub
parent b7fbe1c49e
commit 5a617e4718
66 changed files with 2960 additions and 15 deletions
@@ -0,0 +1,9 @@
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
export const FLAT_COMMAND_MENU_ITEM_EDITABLE_PROPERTIES = [
'label',
'icon',
'isPinned',
'availabilityType',
'availabilityObjectMetadataId',
] as const satisfies (keyof FlatCommandMenuItem)[];
@@ -0,0 +1,16 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CommandMenuItemEntity } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { WorkspaceFlatCommandMenuItemMapCacheService } from 'src/engine/metadata-modules/flat-command-menu-item/services/workspace-flat-command-menu-item-map-cache.service';
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
@Module({
imports: [
TypeOrmModule.forFeature([CommandMenuItemEntity]),
WorkspaceManyOrAllFlatEntityMapsCacheModule,
],
providers: [WorkspaceFlatCommandMenuItemMapCacheService],
exports: [WorkspaceFlatCommandMenuItemMapCacheService],
})
export class FlatCommandMenuItemModule {}
@@ -0,0 +1,45 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service';
import { CommandMenuItemEntity } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { type FlatCommandMenuItemMaps } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item-maps.type';
import { fromCommandMenuItemEntityToFlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/utils/from-command-menu-item-entity-to-flat-command-menu-item.util';
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator';
import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util';
@Injectable()
@WorkspaceCache('flatCommandMenuItemMaps')
export class WorkspaceFlatCommandMenuItemMapCacheService extends WorkspaceCacheProvider<FlatCommandMenuItemMaps> {
constructor(
@InjectRepository(CommandMenuItemEntity)
private readonly commandMenuItemRepository: Repository<CommandMenuItemEntity>,
) {
super();
}
async computeForCache(workspaceId: string): Promise<FlatCommandMenuItemMaps> {
const commandMenuItems = await this.commandMenuItemRepository.find({
where: { workspaceId },
withDeleted: true,
});
const flatCommandMenuItemMaps = createEmptyFlatEntityMaps();
for (const commandMenuItemEntity of commandMenuItems) {
const flatCommandMenuItem =
fromCommandMenuItemEntityToFlatCommandMenuItem(commandMenuItemEntity);
addFlatEntityToFlatEntityMapsThroughMutationOrThrow({
flatEntity: flatCommandMenuItem,
flatEntityMapsToMutate: flatCommandMenuItemMaps,
});
}
return flatCommandMenuItemMaps;
}
}
@@ -0,0 +1,4 @@
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
export type FlatCommandMenuItemMaps = FlatEntityMaps<FlatCommandMenuItem>;
@@ -0,0 +1,4 @@
import { type CommandMenuItemEntity } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity.type';
export type FlatCommandMenuItem = FlatEntityFrom<CommandMenuItemEntity>;
@@ -0,0 +1,22 @@
import { type CommandMenuItemEntity } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
export const fromCommandMenuItemEntityToFlatCommandMenuItem = (
commandMenuItemEntity: CommandMenuItemEntity,
): FlatCommandMenuItem => {
return {
id: commandMenuItemEntity.id,
workflowVersionId: commandMenuItemEntity.workflowVersionId,
label: commandMenuItemEntity.label,
icon: commandMenuItemEntity.icon,
isPinned: commandMenuItemEntity.isPinned,
availabilityType: commandMenuItemEntity.availabilityType,
availabilityObjectMetadataId:
commandMenuItemEntity.availabilityObjectMetadataId,
workspaceId: commandMenuItemEntity.workspaceId,
universalIdentifier: commandMenuItemEntity.universalIdentifier,
applicationId: commandMenuItemEntity.applicationId,
createdAt: commandMenuItemEntity.createdAt.toISOString(),
updatedAt: commandMenuItemEntity.updatedAt.toISOString(),
};
};
@@ -0,0 +1,36 @@
import { v4 as uuidv4 } from 'uuid';
import { type CreateCommandMenuItemInput } from 'src/engine/metadata-modules/command-menu-item/dtos/create-command-menu-item.input';
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
export const fromCreateCommandMenuItemInputToFlatCommandMenuItemToCreate = ({
createCommandMenuItemInput,
workspaceId,
applicationId,
}: {
createCommandMenuItemInput: CreateCommandMenuItemInput;
workspaceId: string;
applicationId: string;
}): FlatCommandMenuItem => {
const id = uuidv4();
const now = new Date().toISOString();
return {
id,
universalIdentifier: id,
workflowVersionId: createCommandMenuItemInput.workflowVersionId,
label: createCommandMenuItemInput.label,
icon: createCommandMenuItemInput.icon ?? null,
isPinned: createCommandMenuItemInput.isPinned ?? false,
availabilityType:
createCommandMenuItemInput.availabilityType ??
CommandMenuItemAvailabilityType.GLOBAL,
availabilityObjectMetadataId:
createCommandMenuItemInput.availabilityObjectMetadataId ?? null,
workspaceId,
applicationId,
createdAt: now,
updatedAt: now,
};
};
@@ -0,0 +1,31 @@
import { isDefined } from 'twenty-shared/utils';
import {
CommandMenuItemException,
CommandMenuItemExceptionCode,
} from 'src/engine/metadata-modules/command-menu-item/command-menu-item.exception';
import { type FlatCommandMenuItemMaps } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item-maps.type';
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
export const fromDeleteCommandMenuItemInputToFlatCommandMenuItemOrThrow = ({
flatCommandMenuItemMaps,
commandMenuItemId,
}: {
flatCommandMenuItemMaps: FlatCommandMenuItemMaps;
commandMenuItemId: string;
}): FlatCommandMenuItem => {
const existingFlatCommandMenuItem = findFlatEntityByIdInFlatEntityMaps({
flatEntityId: commandMenuItemId,
flatEntityMaps: flatCommandMenuItemMaps,
});
if (!isDefined(existingFlatCommandMenuItem)) {
throw new CommandMenuItemException(
'Command menu item not found',
CommandMenuItemExceptionCode.COMMAND_MENU_ITEM_NOT_FOUND,
);
}
return existingFlatCommandMenuItem;
};
@@ -0,0 +1,19 @@
import { type CommandMenuItemDTO } from 'src/engine/metadata-modules/command-menu-item/dtos/command-menu-item.dto';
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
export const fromFlatCommandMenuItemToCommandMenuItemDto = (
flatCommandMenuItem: FlatCommandMenuItem,
): CommandMenuItemDTO => ({
id: flatCommandMenuItem.id,
workflowVersionId: flatCommandMenuItem.workflowVersionId,
label: flatCommandMenuItem.label,
icon: flatCommandMenuItem.icon ?? undefined,
isPinned: flatCommandMenuItem.isPinned,
availabilityType: flatCommandMenuItem.availabilityType,
availabilityObjectMetadataId:
flatCommandMenuItem.availabilityObjectMetadataId ?? undefined,
workspaceId: flatCommandMenuItem.workspaceId,
applicationId: flatCommandMenuItem.applicationId ?? undefined,
createdAt: new Date(flatCommandMenuItem.createdAt),
updatedAt: new Date(flatCommandMenuItem.updatedAt),
});
@@ -0,0 +1,44 @@
import { isDefined } from 'twenty-shared/utils';
import {
CommandMenuItemException,
CommandMenuItemExceptionCode,
} from 'src/engine/metadata-modules/command-menu-item/command-menu-item.exception';
import { type UpdateCommandMenuItemInput } from 'src/engine/metadata-modules/command-menu-item/dtos/update-command-menu-item.input';
import { FLAT_COMMAND_MENU_ITEM_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-command-menu-item/constants/flat-command-menu-item-editable-properties.constant';
import { type FlatCommandMenuItemMaps } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item-maps.type';
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util';
export const fromUpdateCommandMenuItemInputToFlatCommandMenuItemToUpdateOrThrow =
({
flatCommandMenuItemMaps,
updateCommandMenuItemInput,
}: {
flatCommandMenuItemMaps: FlatCommandMenuItemMaps;
updateCommandMenuItemInput: UpdateCommandMenuItemInput;
}): FlatCommandMenuItem => {
const existingFlatCommandMenuItem = findFlatEntityByIdInFlatEntityMaps({
flatEntityId: updateCommandMenuItemInput.id,
flatEntityMaps: flatCommandMenuItemMaps,
});
if (!isDefined(existingFlatCommandMenuItem)) {
throw new CommandMenuItemException(
'Command menu item not found',
CommandMenuItemExceptionCode.COMMAND_MENU_ITEM_NOT_FOUND,
);
}
const { id: _id, ...updates } = updateCommandMenuItemInput;
return {
...mergeUpdateInExistingRecord({
existing: existingFlatCommandMenuItem,
properties: [...FLAT_COMMAND_MENU_ITEM_EDITABLE_PROPERTIES],
update: updates,
}),
updatedAt: new Date().toISOString(),
};
};