Add command menu item entity (#17181)
This commit is contained in:
+37
@@ -0,0 +1,37 @@
|
||||
import { type MessageDescriptor } from '@lingui/core';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export enum CommandMenuItemExceptionCode {
|
||||
COMMAND_MENU_ITEM_NOT_FOUND = 'COMMAND_MENU_ITEM_NOT_FOUND',
|
||||
INVALID_COMMAND_MENU_ITEM_INPUT = 'INVALID_COMMAND_MENU_ITEM_INPUT',
|
||||
}
|
||||
|
||||
const getCommandMenuItemExceptionUserFriendlyMessage = (
|
||||
code: CommandMenuItemExceptionCode,
|
||||
) => {
|
||||
switch (code) {
|
||||
case CommandMenuItemExceptionCode.COMMAND_MENU_ITEM_NOT_FOUND:
|
||||
return msg`Command menu item not found.`;
|
||||
case CommandMenuItemExceptionCode.INVALID_COMMAND_MENU_ITEM_INPUT:
|
||||
return msg`Invalid command menu item input.`;
|
||||
default:
|
||||
assertUnreachable(code);
|
||||
}
|
||||
};
|
||||
|
||||
export class CommandMenuItemException extends CustomException<CommandMenuItemExceptionCode> {
|
||||
constructor(
|
||||
message: string,
|
||||
code: CommandMenuItemExceptionCode,
|
||||
{ userFriendlyMessage }: { userFriendlyMessage?: MessageDescriptor } = {},
|
||||
) {
|
||||
super(message, code, {
|
||||
userFriendlyMessage:
|
||||
userFriendlyMessage ??
|
||||
getCommandMenuItemExceptionUserFriendlyMessage(code),
|
||||
});
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
||||
import { CommandMenuItemResolver } from 'src/engine/metadata-modules/command-menu-item/command-menu-item.resolver';
|
||||
import { CommandMenuItemService } from 'src/engine/metadata-modules/command-menu-item/command-menu-item.service';
|
||||
import { CommandMenuItemGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/command-menu-item/interceptors/command-menu-item-graphql-api-exception.interceptor';
|
||||
import { FlatCommandMenuItemModule } from 'src/engine/metadata-modules/flat-command-menu-item/flat-command-menu-item.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { WorkspaceMigrationBuilderGraphqlApiExceptionInterceptor } from 'src/engine/workspace-manager/workspace-migration/interceptors/workspace-migration-builder-graphql-api-exception.interceptor';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
WorkspaceMigrationModule,
|
||||
ApplicationModule,
|
||||
FlatCommandMenuItemModule,
|
||||
FeatureFlagModule,
|
||||
],
|
||||
providers: [
|
||||
CommandMenuItemService,
|
||||
CommandMenuItemResolver,
|
||||
CommandMenuItemGraphqlApiExceptionInterceptor,
|
||||
WorkspaceMigrationBuilderGraphqlApiExceptionInterceptor,
|
||||
],
|
||||
exports: [CommandMenuItemService],
|
||||
})
|
||||
export class CommandMenuItemModule {}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
import { UseGuards, UseInterceptors } from '@nestjs/common';
|
||||
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import {
|
||||
FeatureFlagGuard,
|
||||
RequireFeatureFlag,
|
||||
} from 'src/engine/guards/feature-flag.guard';
|
||||
import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { CommandMenuItemService } from 'src/engine/metadata-modules/command-menu-item/command-menu-item.service';
|
||||
import { CommandMenuItemDTO } from 'src/engine/metadata-modules/command-menu-item/dtos/command-menu-item.dto';
|
||||
import { CreateCommandMenuItemInput } from 'src/engine/metadata-modules/command-menu-item/dtos/create-command-menu-item.input';
|
||||
import { UpdateCommandMenuItemInput } from 'src/engine/metadata-modules/command-menu-item/dtos/update-command-menu-item.input';
|
||||
import { CommandMenuItemGraphqlApiExceptionInterceptor } from 'src/engine/metadata-modules/command-menu-item/interceptors/command-menu-item-graphql-api-exception.interceptor';
|
||||
import { WorkspaceMigrationBuilderGraphqlApiExceptionInterceptor } from 'src/engine/workspace-manager/workspace-migration/interceptors/workspace-migration-builder-graphql-api-exception.interceptor';
|
||||
|
||||
@UseGuards(WorkspaceAuthGuard, FeatureFlagGuard)
|
||||
@UseInterceptors(
|
||||
WorkspaceMigrationBuilderGraphqlApiExceptionInterceptor,
|
||||
CommandMenuItemGraphqlApiExceptionInterceptor,
|
||||
)
|
||||
@Resolver(() => CommandMenuItemDTO)
|
||||
export class CommandMenuItemResolver {
|
||||
constructor(
|
||||
private readonly commandMenuItemService: CommandMenuItemService,
|
||||
) {}
|
||||
|
||||
@Query(() => [CommandMenuItemDTO])
|
||||
@UseGuards(NoPermissionGuard)
|
||||
@RequireFeatureFlag(FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED)
|
||||
async commandMenuItems(
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
): Promise<CommandMenuItemDTO[]> {
|
||||
return await this.commandMenuItemService.findAll(workspace.id);
|
||||
}
|
||||
|
||||
@Query(() => CommandMenuItemDTO, { nullable: true })
|
||||
@UseGuards(NoPermissionGuard)
|
||||
@RequireFeatureFlag(FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED)
|
||||
async commandMenuItem(
|
||||
@Args('id', { type: () => UUIDScalarType }) id: string,
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
): Promise<CommandMenuItemDTO | null> {
|
||||
return await this.commandMenuItemService.findById(id, workspace.id);
|
||||
}
|
||||
|
||||
@Mutation(() => CommandMenuItemDTO)
|
||||
@UseGuards(NoPermissionGuard)
|
||||
@RequireFeatureFlag(FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED)
|
||||
async createCommandMenuItem(
|
||||
@Args('input') input: CreateCommandMenuItemInput,
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
): Promise<CommandMenuItemDTO> {
|
||||
return await this.commandMenuItemService.create(input, workspace.id);
|
||||
}
|
||||
|
||||
@Mutation(() => CommandMenuItemDTO)
|
||||
@UseGuards(NoPermissionGuard)
|
||||
@RequireFeatureFlag(FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED)
|
||||
async updateCommandMenuItem(
|
||||
@Args('input') input: UpdateCommandMenuItemInput,
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
): Promise<CommandMenuItemDTO> {
|
||||
return await this.commandMenuItemService.update(input, workspace.id);
|
||||
}
|
||||
|
||||
@Mutation(() => CommandMenuItemDTO)
|
||||
@UseGuards(NoPermissionGuard)
|
||||
@RequireFeatureFlag(FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED)
|
||||
async deleteCommandMenuItem(
|
||||
@Args('id', { type: () => UUIDScalarType }) id: string,
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
): Promise<CommandMenuItemDTO> {
|
||||
return await this.commandMenuItemService.delete(id, workspace.id);
|
||||
}
|
||||
}
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import {
|
||||
CommandMenuItemException,
|
||||
CommandMenuItemExceptionCode,
|
||||
} from 'src/engine/metadata-modules/command-menu-item/command-menu-item.exception';
|
||||
import { type CommandMenuItemDTO } from 'src/engine/metadata-modules/command-menu-item/dtos/command-menu-item.dto';
|
||||
import { type CreateCommandMenuItemInput } from 'src/engine/metadata-modules/command-menu-item/dtos/create-command-menu-item.input';
|
||||
import { type UpdateCommandMenuItemInput } from 'src/engine/metadata-modules/command-menu-item/dtos/update-command-menu-item.input';
|
||||
import { type FlatCommandMenuItem } from 'src/engine/metadata-modules/flat-command-menu-item/types/flat-command-menu-item.type';
|
||||
import { fromCreateCommandMenuItemInputToFlatCommandMenuItemToCreate } from 'src/engine/metadata-modules/flat-command-menu-item/utils/from-create-command-menu-item-input-to-flat-command-menu-item-to-create.util';
|
||||
import { fromDeleteCommandMenuItemInputToFlatCommandMenuItemOrThrow } from 'src/engine/metadata-modules/flat-command-menu-item/utils/from-delete-command-menu-item-input-to-flat-command-menu-item-or-throw.util';
|
||||
import { fromFlatCommandMenuItemToCommandMenuItemDto } from 'src/engine/metadata-modules/flat-command-menu-item/utils/from-flat-command-menu-item-to-command-menu-item-dto.util';
|
||||
import { fromUpdateCommandMenuItemInputToFlatCommandMenuItemToUpdateOrThrow } from 'src/engine/metadata-modules/flat-command-menu-item/utils/from-update-command-menu-item-input-to-flat-command-menu-item-to-update-or-throw.util';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@Injectable()
|
||||
export class CommandMenuItemService {
|
||||
constructor(
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
) {}
|
||||
|
||||
async findAll(workspaceId: string): Promise<CommandMenuItemDTO[]> {
|
||||
const { flatCommandMenuItemMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatCommandMenuItemMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return Object.values(flatCommandMenuItemMaps.byId)
|
||||
.filter(isDefined)
|
||||
.sort((a, b) => a.label.localeCompare(b.label))
|
||||
.map(fromFlatCommandMenuItemToCommandMenuItemDto);
|
||||
}
|
||||
|
||||
async findById(
|
||||
id: string,
|
||||
workspaceId: string,
|
||||
): Promise<CommandMenuItemDTO | null> {
|
||||
const { flatCommandMenuItemMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatCommandMenuItemMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatCommandMenuItem = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: id,
|
||||
flatEntityMaps: flatCommandMenuItemMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatCommandMenuItem)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fromFlatCommandMenuItemToCommandMenuItemDto(flatCommandMenuItem);
|
||||
}
|
||||
|
||||
async findByIdOrThrow(
|
||||
id: string,
|
||||
workspaceId: string,
|
||||
): Promise<CommandMenuItemDTO> {
|
||||
const commandMenuItem = await this.findById(id, workspaceId);
|
||||
|
||||
if (!isDefined(commandMenuItem)) {
|
||||
throw new CommandMenuItemException(
|
||||
'Command menu item not found',
|
||||
CommandMenuItemExceptionCode.COMMAND_MENU_ITEM_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return commandMenuItem;
|
||||
}
|
||||
|
||||
async create(
|
||||
input: CreateCommandMenuItemInput,
|
||||
workspaceId: string,
|
||||
): Promise<CommandMenuItemDTO> {
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
const flatCommandMenuItemToCreate =
|
||||
fromCreateCommandMenuItemInputToFlatCommandMenuItemToCreate({
|
||||
createCommandMenuItemInput: input,
|
||||
workspaceId,
|
||||
applicationId: workspaceCustomFlatApplication.id,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
commandMenuItem: {
|
||||
flatEntityToCreate: [flatCommandMenuItemToCreate],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
isSystemBuild: false,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while creating command menu item',
|
||||
);
|
||||
}
|
||||
|
||||
const { flatCommandMenuItemMaps: recomputedFlatCommandMenuItemMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatCommandMenuItemMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return fromFlatCommandMenuItemToCommandMenuItemDto(
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatCommandMenuItemToCreate.id,
|
||||
flatEntityMaps: recomputedFlatCommandMenuItemMaps,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async update(
|
||||
input: UpdateCommandMenuItemInput,
|
||||
workspaceId: string,
|
||||
): Promise<CommandMenuItemDTO> {
|
||||
const { flatCommandMenuItemMaps: existingFlatCommandMenuItemMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatCommandMenuItemMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatCommandMenuItemToUpdate =
|
||||
fromUpdateCommandMenuItemInputToFlatCommandMenuItemToUpdateOrThrow({
|
||||
flatCommandMenuItemMaps: existingFlatCommandMenuItemMaps,
|
||||
updateCommandMenuItemInput: input,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
commandMenuItem: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [flatCommandMenuItemToUpdate],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
isSystemBuild: false,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while updating command menu item',
|
||||
);
|
||||
}
|
||||
|
||||
const { flatCommandMenuItemMaps: recomputedFlatCommandMenuItemMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatCommandMenuItemMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return fromFlatCommandMenuItemToCommandMenuItemDto(
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: input.id,
|
||||
flatEntityMaps: recomputedFlatCommandMenuItemMaps,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async delete(id: string, workspaceId: string): Promise<CommandMenuItemDTO> {
|
||||
const { flatCommandMenuItemMaps: existingFlatCommandMenuItemMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatCommandMenuItemMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatCommandMenuItemToDelete =
|
||||
fromDeleteCommandMenuItemInputToFlatCommandMenuItemOrThrow({
|
||||
flatCommandMenuItemMaps: existingFlatCommandMenuItemMaps,
|
||||
commandMenuItemId: id,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
commandMenuItem: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [flatCommandMenuItemToDelete],
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
isSystemBuild: false,
|
||||
},
|
||||
);
|
||||
|
||||
if (isDefined(validateAndBuildResult)) {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Multiple validation errors occurred while deleting command menu item',
|
||||
);
|
||||
}
|
||||
|
||||
return fromFlatCommandMenuItemToCommandMenuItemDto(
|
||||
flatCommandMenuItemToDelete,
|
||||
);
|
||||
}
|
||||
|
||||
async findAllFlatCommandMenuItems(
|
||||
workspaceId: string,
|
||||
): Promise<FlatCommandMenuItem[]> {
|
||||
const { flatCommandMenuItemMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatCommandMenuItemMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return Object.values(flatCommandMenuItemMaps.byId)
|
||||
.filter(isDefined)
|
||||
.sort((a, b) => a.label.localeCompare(b.label));
|
||||
}
|
||||
|
||||
async findByWorkflowVersionId(
|
||||
workflowVersionId: string,
|
||||
workspaceId: string,
|
||||
): Promise<CommandMenuItemDTO | null> {
|
||||
const { flatCommandMenuItemMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatCommandMenuItemMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatCommandMenuItem = Object.values(
|
||||
flatCommandMenuItemMaps.byId,
|
||||
).find(
|
||||
(item) => isDefined(item) && item.workflowVersionId === workflowVersionId,
|
||||
);
|
||||
|
||||
if (!isDefined(flatCommandMenuItem)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fromFlatCommandMenuItemToCommandMenuItemDto(flatCommandMenuItem);
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
Field,
|
||||
HideField,
|
||||
ObjectType,
|
||||
registerEnumType,
|
||||
} from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
IsBoolean,
|
||||
IsDateString,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
|
||||
|
||||
registerEnumType(CommandMenuItemAvailabilityType, {
|
||||
name: 'CommandMenuItemAvailabilityType',
|
||||
});
|
||||
|
||||
@ObjectType('CommandMenuItem')
|
||||
export class CommandMenuItemDTO {
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field(() => UUIDScalarType)
|
||||
workflowVersionId: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
label: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
icon?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@Field()
|
||||
isPinned: boolean;
|
||||
|
||||
@IsEnum(CommandMenuItemAvailabilityType)
|
||||
@Field(() => CommandMenuItemAvailabilityType)
|
||||
availabilityType: CommandMenuItemAvailabilityType;
|
||||
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
availabilityObjectMetadataId?: string;
|
||||
|
||||
@HideField()
|
||||
workspaceId: string;
|
||||
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
applicationId?: string;
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
updatedAt: Date;
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
|
||||
|
||||
@InputType()
|
||||
export class CreateCommandMenuItemInput {
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field(() => UUIDScalarType)
|
||||
workflowVersionId: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
label: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
icon?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
isPinned?: boolean;
|
||||
|
||||
@IsEnum(CommandMenuItemAvailabilityType)
|
||||
@IsOptional()
|
||||
@Field(() => CommandMenuItemAvailabilityType, { nullable: true })
|
||||
availabilityType?: CommandMenuItemAvailabilityType;
|
||||
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
availabilityObjectMetadataId?: string;
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import {
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/entities/command-menu-item.entity';
|
||||
|
||||
@InputType()
|
||||
export class UpdateCommandMenuItemInput {
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
label?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
icon?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
isPinned?: boolean;
|
||||
|
||||
@IsEnum(CommandMenuItemAvailabilityType)
|
||||
@IsOptional()
|
||||
@Field(() => CommandMenuItemAvailabilityType, { nullable: true })
|
||||
availabilityType?: CommandMenuItemAvailabilityType;
|
||||
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
availabilityObjectMetadataId?: string;
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
type Relation,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { SyncableEntityRequired } from 'src/engine/workspace-manager/types/syncable-entity-required.interface';
|
||||
|
||||
export enum CommandMenuItemAvailabilityType {
|
||||
GLOBAL = 'GLOBAL',
|
||||
SINGLE_RECORD = 'SINGLE_RECORD',
|
||||
BULK_RECORDS = 'BULK_RECORDS',
|
||||
}
|
||||
|
||||
@Entity({ name: 'commandMenuItem', schema: 'core' })
|
||||
@Index('IDX_COMMAND_MENU_ITEM_WORKFLOW_VERSION_ID_WORKSPACE_ID', [
|
||||
'workflowVersionId',
|
||||
'workspaceId',
|
||||
])
|
||||
export class CommandMenuItemEntity
|
||||
extends SyncableEntityRequired
|
||||
implements Required<CommandMenuItemEntity>
|
||||
{
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
workflowVersionId: string;
|
||||
|
||||
@Column({ nullable: false })
|
||||
label: string;
|
||||
|
||||
@Column({ nullable: true, type: 'varchar' })
|
||||
icon: string | null;
|
||||
|
||||
@Column({ default: false })
|
||||
isPinned: boolean;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: CommandMenuItemAvailabilityType,
|
||||
nullable: false,
|
||||
default: CommandMenuItemAvailabilityType.GLOBAL,
|
||||
})
|
||||
availabilityType: CommandMenuItemAvailabilityType;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
availabilityObjectMetadataId: string | null;
|
||||
|
||||
@ManyToOne(() => ObjectMetadataEntity, {
|
||||
onDelete: 'CASCADE',
|
||||
nullable: true,
|
||||
})
|
||||
@JoinColumn({ name: 'availabilityObjectMetadataId' })
|
||||
availabilityObjectMetadata: Relation<ObjectMetadataEntity> | null;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ type: 'timestamptz' })
|
||||
updatedAt: Date;
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
type CallHandler,
|
||||
type ExecutionContext,
|
||||
Injectable,
|
||||
type NestInterceptor,
|
||||
} from '@nestjs/common';
|
||||
|
||||
import { type Observable, catchError } from 'rxjs';
|
||||
|
||||
import { commandMenuItemGraphqlApiExceptionHandler } from 'src/engine/metadata-modules/command-menu-item/utils/command-menu-item-graphql-api-exception-handler.util';
|
||||
|
||||
@Injectable()
|
||||
export class CommandMenuItemGraphqlApiExceptionInterceptor
|
||||
implements NestInterceptor
|
||||
{
|
||||
intercept(
|
||||
_context: ExecutionContext,
|
||||
next: CallHandler,
|
||||
): Observable<unknown> {
|
||||
return next
|
||||
.handle()
|
||||
.pipe(catchError(commandMenuItemGraphqlApiExceptionHandler));
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
NotFoundError,
|
||||
UserInputError,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import {
|
||||
CommandMenuItemException,
|
||||
CommandMenuItemExceptionCode,
|
||||
} from 'src/engine/metadata-modules/command-menu-item/command-menu-item.exception';
|
||||
|
||||
export const commandMenuItemGraphqlApiExceptionHandler = (error: Error) => {
|
||||
if (error instanceof CommandMenuItemException) {
|
||||
switch (error.code) {
|
||||
case CommandMenuItemExceptionCode.COMMAND_MENU_ITEM_NOT_FOUND:
|
||||
throw new NotFoundError(error);
|
||||
case CommandMenuItemExceptionCode.INVALID_COMMAND_MENU_ITEM_INPUT:
|
||||
throw new UserInputError(error);
|
||||
default: {
|
||||
return assertUnreachable(error.code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
};
|
||||
Reference in New Issue
Block a user