[COMMAND MENU ITEMS] Create union type for command menu item payload (#19432)
Replace generic JSON scalar with a typed GraphQL union CommandMenuItemPayload (PathNavigationPayload | ObjectMetadataNavigationPayload) for the CommandMenuItem.payload field
This commit is contained in:
@@ -2575,7 +2575,7 @@ type CommandMenuItem {
|
||||
position: Float!
|
||||
isPinned: Boolean!
|
||||
availabilityType: CommandMenuItemAvailabilityType!
|
||||
payload: JSON
|
||||
payload: CommandMenuItemPayload
|
||||
hotKeys: [String!]
|
||||
conditionalAvailabilityExpression: String
|
||||
availabilityObjectMetadataId: UUID
|
||||
@@ -2659,6 +2659,16 @@ enum CommandMenuItemAvailabilityType {
|
||||
FALLBACK
|
||||
}
|
||||
|
||||
union CommandMenuItemPayload = PathCommandMenuItemPayload | ObjectMetadataCommandMenuItemPayload
|
||||
|
||||
type PathCommandMenuItemPayload {
|
||||
path: String!
|
||||
}
|
||||
|
||||
type ObjectMetadataCommandMenuItemPayload {
|
||||
objectMetadataItemId: UUID!
|
||||
}
|
||||
|
||||
type ToolIndexEntry {
|
||||
name: String!
|
||||
description: String!
|
||||
|
||||
@@ -2286,7 +2286,7 @@ export interface CommandMenuItem {
|
||||
position: Scalars['Float']
|
||||
isPinned: Scalars['Boolean']
|
||||
availabilityType: CommandMenuItemAvailabilityType
|
||||
payload?: Scalars['JSON']
|
||||
payload?: CommandMenuItemPayload
|
||||
hotKeys?: Scalars['String'][]
|
||||
conditionalAvailabilityExpression?: Scalars['String']
|
||||
availabilityObjectMetadataId?: Scalars['UUID']
|
||||
@@ -2300,6 +2300,18 @@ export type EngineComponentKey = 'NAVIGATE_TO_NEXT_RECORD' | 'NAVIGATE_TO_PREVIO
|
||||
|
||||
export type CommandMenuItemAvailabilityType = 'GLOBAL' | 'RECORD_SELECTION' | 'FALLBACK'
|
||||
|
||||
export type CommandMenuItemPayload = (PathCommandMenuItemPayload | ObjectMetadataCommandMenuItemPayload) & { __isUnion?: true }
|
||||
|
||||
export interface PathCommandMenuItemPayload {
|
||||
path: Scalars['String']
|
||||
__typename: 'PathCommandMenuItemPayload'
|
||||
}
|
||||
|
||||
export interface ObjectMetadataCommandMenuItemPayload {
|
||||
objectMetadataItemId: Scalars['UUID']
|
||||
__typename: 'ObjectMetadataCommandMenuItemPayload'
|
||||
}
|
||||
|
||||
export interface ToolIndexEntry {
|
||||
name: Scalars['String']
|
||||
description: Scalars['String']
|
||||
@@ -5486,7 +5498,7 @@ export interface CommandMenuItemGenqlSelection{
|
||||
position?: boolean | number
|
||||
isPinned?: boolean | number
|
||||
availabilityType?: boolean | number
|
||||
payload?: boolean | number
|
||||
payload?: CommandMenuItemPayloadGenqlSelection
|
||||
hotKeys?: boolean | number
|
||||
conditionalAvailabilityExpression?: boolean | number
|
||||
availabilityObjectMetadataId?: boolean | number
|
||||
@@ -5497,6 +5509,24 @@ export interface CommandMenuItemGenqlSelection{
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface CommandMenuItemPayloadGenqlSelection{
|
||||
on_PathCommandMenuItemPayload?:PathCommandMenuItemPayloadGenqlSelection,
|
||||
on_ObjectMetadataCommandMenuItemPayload?:ObjectMetadataCommandMenuItemPayloadGenqlSelection,
|
||||
__typename?: boolean | number
|
||||
}
|
||||
|
||||
export interface PathCommandMenuItemPayloadGenqlSelection{
|
||||
path?: boolean | number
|
||||
__typename?: boolean | number
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface ObjectMetadataCommandMenuItemPayloadGenqlSelection{
|
||||
objectMetadataItemId?: boolean | number
|
||||
__typename?: boolean | number
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface ToolIndexEntryGenqlSelection{
|
||||
name?: boolean | number
|
||||
description?: boolean | number
|
||||
@@ -8429,6 +8459,30 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
|
||||
|
||||
|
||||
|
||||
const CommandMenuItemPayload_possibleTypes: string[] = ['PathCommandMenuItemPayload','ObjectMetadataCommandMenuItemPayload']
|
||||
export const isCommandMenuItemPayload = (obj?: { __typename?: any } | null): obj is CommandMenuItemPayload => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isCommandMenuItemPayload"')
|
||||
return CommandMenuItemPayload_possibleTypes.includes(obj.__typename)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const PathCommandMenuItemPayload_possibleTypes: string[] = ['PathCommandMenuItemPayload']
|
||||
export const isPathCommandMenuItemPayload = (obj?: { __typename?: any } | null): obj is PathCommandMenuItemPayload => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isPathCommandMenuItemPayload"')
|
||||
return PathCommandMenuItemPayload_possibleTypes.includes(obj.__typename)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const ObjectMetadataCommandMenuItemPayload_possibleTypes: string[] = ['ObjectMetadataCommandMenuItemPayload']
|
||||
export const isObjectMetadataCommandMenuItemPayload = (obj?: { __typename?: any } | null): obj is ObjectMetadataCommandMenuItemPayload => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isObjectMetadataCommandMenuItemPayload"')
|
||||
return ObjectMetadataCommandMenuItemPayload_possibleTypes.includes(obj.__typename)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const ToolIndexEntry_possibleTypes: string[] = ['ToolIndexEntry']
|
||||
export const isToolIndexEntry = (obj?: { __typename?: any } | null): obj is ToolIndexEntry => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isToolIndexEntry"')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+6
-8
@@ -1,7 +1,7 @@
|
||||
import { HeadlessEngineCommandWrapperEffect } from '@/command-menu-item/engine-command/components/HeadlessEngineCommandWrapperEffect';
|
||||
import { useHeadlessCommandContextApi } from '@/command-menu-item/engine-command/hooks/useHeadlessCommandContextApi';
|
||||
import { isObjectMetadataNavigationPayload } from '@/command-menu-item/engine-command/utils/isObjectMetadataNavigationPayload';
|
||||
import { isPathNavigationPayload } from '@/command-menu-item/engine-command/utils/isPathNavigationPayload';
|
||||
import { isObjectMetadataCommandMenuItemPayload } from '@/command-menu-item/engine-command/utils/isObjectMetadataCommandMenuItemPayload';
|
||||
import { isPathCommandMenuItemPayload } from '@/command-menu-item/engine-command/utils/isPathCommandMenuItemPayload';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { AppPath } from 'twenty-shared/types';
|
||||
@@ -17,11 +17,9 @@ export const NavigationEngineCommand = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const navigationPayload = payload;
|
||||
|
||||
if (isObjectMetadataNavigationPayload(navigationPayload)) {
|
||||
if (isObjectMetadataCommandMenuItemPayload(payload)) {
|
||||
const objectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.id === navigationPayload.objectMetadataItemId,
|
||||
(item) => item.id === payload.objectMetadataItemId,
|
||||
);
|
||||
|
||||
if (!isDefined(objectMetadataItem)) {
|
||||
@@ -38,9 +36,9 @@ export const NavigationEngineCommand = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPathNavigationPayload(navigationPayload)) {
|
||||
if (isPathCommandMenuItemPayload(payload)) {
|
||||
// eslint-disable-next-line twenty/no-navigate-prefer-link
|
||||
navigate(navigationPayload.path);
|
||||
navigate(payload.path);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
type CommandMenuItemAvailabilityType,
|
||||
type EngineComponentKey,
|
||||
type CommandMenuItemPayload,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
type MountCommandParams = {
|
||||
@@ -18,7 +19,7 @@ type MountCommandParams = {
|
||||
workflowVersionId?: string;
|
||||
availabilityType?: CommandMenuItemAvailabilityType;
|
||||
availabilityObjectMetadataId?: string | null;
|
||||
payload?: Record<string, unknown> | null;
|
||||
payload?: CommandMenuItemPayload | null;
|
||||
};
|
||||
|
||||
export const useMountCommand = () => {
|
||||
|
||||
+5
-2
@@ -5,7 +5,10 @@ import {
|
||||
type Nullable,
|
||||
type RecordGqlOperationFilter,
|
||||
} from 'twenty-shared/types';
|
||||
import { type EngineComponentKey } from '~/generated-metadata/graphql';
|
||||
import {
|
||||
type EngineComponentKey,
|
||||
type CommandMenuItemPayload,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export type HeadlessEngineCommandContextApi = {
|
||||
engineComponentKey: EngineComponentKey;
|
||||
@@ -16,7 +19,7 @@ export type HeadlessEngineCommandContextApi = {
|
||||
targetedRecordsRule: ContextStoreTargetedRecordsRule;
|
||||
selectedRecords: ObjectRecord[];
|
||||
graphqlFilter: Nullable<RecordGqlOperationFilter>;
|
||||
payload: Nullable<Record<string, unknown>>;
|
||||
payload: Nullable<CommandMenuItemPayload>;
|
||||
};
|
||||
|
||||
export type HeadlessFrontComponentCommandContextApi =
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { isObjectMetadataCommandMenuItemPayload } from '@/command-menu-item/engine-command/utils/isObjectMetadataCommandMenuItemPayload';
|
||||
import { type CommandMenuItemPayload } from '~/generated-metadata/graphql';
|
||||
|
||||
describe('isObjectMetadataCommandMenuItemPayload', () => {
|
||||
it('should return true for an ObjectMetadataCommandMenuItemPayload', () => {
|
||||
const payload: CommandMenuItemPayload = {
|
||||
__typename: 'ObjectMetadataCommandMenuItemPayload',
|
||||
objectMetadataItemId: 'some-uuid',
|
||||
};
|
||||
|
||||
expect(isObjectMetadataCommandMenuItemPayload(payload)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for a PathCommandMenuItemPayload', () => {
|
||||
const payload: CommandMenuItemPayload = {
|
||||
__typename: 'PathCommandMenuItemPayload',
|
||||
path: '/settings',
|
||||
};
|
||||
|
||||
expect(isObjectMetadataCommandMenuItemPayload(payload)).toBe(false);
|
||||
});
|
||||
});
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { isPathCommandMenuItemPayload } from '@/command-menu-item/engine-command/utils/isPathCommandMenuItemPayload';
|
||||
import { type CommandMenuItemPayload } from '~/generated-metadata/graphql';
|
||||
|
||||
describe('isPathCommandMenuItemPayload', () => {
|
||||
it('should return true for a PathCommandMenuItemPayload', () => {
|
||||
const payload: CommandMenuItemPayload = {
|
||||
__typename: 'PathCommandMenuItemPayload',
|
||||
path: '/settings',
|
||||
};
|
||||
|
||||
expect(isPathCommandMenuItemPayload(payload)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for an ObjectMetadataCommandMenuItemPayload', () => {
|
||||
const payload: CommandMenuItemPayload = {
|
||||
__typename: 'ObjectMetadataCommandMenuItemPayload',
|
||||
objectMetadataItemId: 'some-uuid',
|
||||
};
|
||||
|
||||
expect(isPathCommandMenuItemPayload(payload)).toBe(false);
|
||||
});
|
||||
});
|
||||
+5
-2
@@ -14,7 +14,10 @@ import { type EnrichedObjectMetadataItem } from '@/object-metadata/types/Enriche
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type EngineComponentKey } from '~/generated-metadata/graphql';
|
||||
import {
|
||||
type EngineComponentKey,
|
||||
type CommandMenuItemPayload,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const buildHeadlessCommandContextApi = ({
|
||||
store,
|
||||
@@ -25,7 +28,7 @@ export const buildHeadlessCommandContextApi = ({
|
||||
store: Store;
|
||||
contextStoreInstanceId: string;
|
||||
engineComponentKey: EngineComponentKey;
|
||||
payload?: Record<string, unknown> | null;
|
||||
payload?: CommandMenuItemPayload | null;
|
||||
}): HeadlessEngineCommandContextApi => {
|
||||
const objectMetadataItemId = store.get(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState.atomFamily({
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import {
|
||||
type CommandMenuItemPayload,
|
||||
type ObjectMetadataCommandMenuItemPayload,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const isObjectMetadataCommandMenuItemPayload = (
|
||||
payload: CommandMenuItemPayload,
|
||||
): payload is ObjectMetadataCommandMenuItemPayload =>
|
||||
payload.__typename === 'ObjectMetadataCommandMenuItemPayload';
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
export const isObjectMetadataNavigationPayload = (
|
||||
payload: Record<string, unknown>,
|
||||
): payload is { objectMetadataItemId: string } =>
|
||||
'objectMetadataItemId' in payload &&
|
||||
typeof payload.objectMetadataItemId === 'string';
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import {
|
||||
type CommandMenuItemPayload,
|
||||
type PathCommandMenuItemPayload,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const isPathCommandMenuItemPayload = (
|
||||
payload: CommandMenuItemPayload,
|
||||
): payload is PathCommandMenuItemPayload =>
|
||||
payload.__typename === 'PathCommandMenuItemPayload';
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
export const isPathNavigationPayload = (
|
||||
payload: Record<string, unknown>,
|
||||
): payload is { path: string } =>
|
||||
'path' in payload && typeof payload.path === 'string';
|
||||
+8
-1
@@ -16,7 +16,14 @@ export const COMMAND_MENU_ITEM_FRAGMENT = gql`
|
||||
shortLabel
|
||||
position
|
||||
isPinned
|
||||
payload
|
||||
payload {
|
||||
... on PathCommandMenuItemPayload {
|
||||
path
|
||||
}
|
||||
... on ObjectMetadataCommandMenuItemPayload {
|
||||
objectMetadataItemId
|
||||
}
|
||||
}
|
||||
hotKeys
|
||||
conditionalAvailabilityExpression
|
||||
availabilityType
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { createUnionType } from '@nestjs/graphql';
|
||||
|
||||
import { ObjectMetadataCommandMenuItemPayloadDTO } from 'src/engine/metadata-modules/command-menu-item/dtos/object-metadata-command-menu-item-payload.dto';
|
||||
import { PathCommandMenuItemPayloadDTO } from 'src/engine/metadata-modules/command-menu-item/dtos/path-command-menu-item-payload.dto';
|
||||
import { type ObjectMetadataCommandMenuItemPayload } from 'src/engine/metadata-modules/command-menu-item/dtos/types/object-metadata-command-menu-item-payload.type';
|
||||
import { type PathCommandMenuItemPayload } from 'src/engine/metadata-modules/command-menu-item/dtos/types/path-command-menu-item-payload.type';
|
||||
|
||||
export type CommandMenuItemPayload =
|
||||
| PathCommandMenuItemPayload
|
||||
| ObjectMetadataCommandMenuItemPayload;
|
||||
|
||||
export const CommandMenuItemPayloadUnion = createUnionType({
|
||||
name: 'CommandMenuItemPayload',
|
||||
types: () => [
|
||||
PathCommandMenuItemPayloadDTO,
|
||||
ObjectMetadataCommandMenuItemPayloadDTO,
|
||||
],
|
||||
resolveType(payload: CommandMenuItemPayload) {
|
||||
if ('path' in payload) {
|
||||
return PathCommandMenuItemPayloadDTO;
|
||||
}
|
||||
|
||||
if ('objectMetadataItemId' in payload) {
|
||||
return ObjectMetadataCommandMenuItemPayloadDTO;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
});
|
||||
+6
-3
@@ -10,10 +10,13 @@ import {
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
import { GraphQLJSON } from 'graphql-type-json';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
|
||||
import {
|
||||
type CommandMenuItemPayload,
|
||||
CommandMenuItemPayloadUnion,
|
||||
} from 'src/engine/metadata-modules/command-menu-item/dtos/command-menu-item-payload.union';
|
||||
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
|
||||
import { FrontComponentDTO } from 'src/engine/metadata-modules/front-component/dtos/front-component.dto';
|
||||
|
||||
@@ -70,8 +73,8 @@ export class CommandMenuItemDTO {
|
||||
availabilityType: CommandMenuItemAvailabilityType;
|
||||
|
||||
@IsOptional()
|
||||
@Field(() => GraphQLJSON, { nullable: true })
|
||||
payload?: Record<string, unknown>;
|
||||
@Field(() => CommandMenuItemPayloadUnion, { nullable: true })
|
||||
payload?: CommandMenuItemPayload;
|
||||
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
|
||||
+2
-1
@@ -13,6 +13,7 @@ import {
|
||||
import GraphQLJSON from 'graphql-type-json';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { type CommandMenuItemPayload } from 'src/engine/metadata-modules/command-menu-item/dtos/command-menu-item-payload.union';
|
||||
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
|
||||
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
|
||||
|
||||
@@ -81,5 +82,5 @@ export class CreateCommandMenuItemInput {
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
@Field(() => GraphQLJSON, { nullable: true })
|
||||
payload?: Record<string, unknown>;
|
||||
payload?: CommandMenuItemPayload;
|
||||
}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
@ObjectType('ObjectMetadataCommandMenuItemPayload')
|
||||
export class ObjectMetadataCommandMenuItemPayloadDTO {
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field(() => UUIDScalarType)
|
||||
objectMetadataItemId: string;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
@ObjectType('PathCommandMenuItemPayload')
|
||||
export class PathCommandMenuItemPayloadDTO {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
path: string;
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export type ObjectMetadataCommandMenuItemPayload = {
|
||||
objectMetadataItemId: string;
|
||||
};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export type PathCommandMenuItemPayload = {
|
||||
path: string;
|
||||
};
|
||||
+2
-1
@@ -11,6 +11,7 @@ import {
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import { type CommandMenuItemPayload } from 'src/engine/metadata-modules/command-menu-item/dtos/command-menu-item-payload.union';
|
||||
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
|
||||
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
|
||||
import { FrontComponentEntity } from 'src/engine/metadata-modules/front-component/entities/front-component.entity';
|
||||
@@ -80,7 +81,7 @@ export class CommandMenuItemEntity
|
||||
availabilityType: CommandMenuItemAvailabilityType;
|
||||
|
||||
@Column({ type: 'jsonb', nullable: true })
|
||||
payload: Record<string, unknown> | null;
|
||||
payload: CommandMenuItemPayload | null;
|
||||
|
||||
@Column({ type: 'text', array: true, nullable: true })
|
||||
hotKeys: string[] | null;
|
||||
|
||||
+65
@@ -6,6 +6,7 @@ import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { CommandMenuItemExceptionCode } from 'src/engine/metadata-modules/command-menu-item/command-menu-item.exception';
|
||||
import { type CommandMenuItemPayload } from 'src/engine/metadata-modules/command-menu-item/dtos/command-menu-item-payload.union';
|
||||
import { EngineComponentKey } from 'src/engine/metadata-modules/command-menu-item/enums/engine-component-key.enum';
|
||||
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
|
||||
import { type FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/types/failed-flat-entity-validation.type';
|
||||
@@ -49,6 +50,7 @@ export class FlatCommandMenuItemValidatorService {
|
||||
workflowVersionId: flatCommandMenuItem.workflowVersionId,
|
||||
frontComponentUniversalIdentifier:
|
||||
flatCommandMenuItem.frontComponentUniversalIdentifier,
|
||||
payload: flatCommandMenuItem.payload,
|
||||
validationResult,
|
||||
});
|
||||
|
||||
@@ -133,11 +135,17 @@ export class FlatCommandMenuItemValidatorService {
|
||||
flatEntityUpdate.engineComponentKey ??
|
||||
fromFlatCommandMenuItem.engineComponentKey;
|
||||
|
||||
const payload =
|
||||
flatEntityUpdate.payload !== undefined
|
||||
? flatEntityUpdate.payload
|
||||
: fromFlatCommandMenuItem.payload;
|
||||
|
||||
this.validateEngineComponentKeyCoherence({
|
||||
engineComponentKey,
|
||||
workflowVersionId: fromFlatCommandMenuItem.workflowVersionId,
|
||||
frontComponentUniversalIdentifier:
|
||||
fromFlatCommandMenuItem.frontComponentUniversalIdentifier,
|
||||
payload,
|
||||
validationResult,
|
||||
});
|
||||
|
||||
@@ -148,11 +156,13 @@ export class FlatCommandMenuItemValidatorService {
|
||||
engineComponentKey,
|
||||
workflowVersionId,
|
||||
frontComponentUniversalIdentifier,
|
||||
payload,
|
||||
validationResult,
|
||||
}: {
|
||||
engineComponentKey: EngineComponentKey | null;
|
||||
workflowVersionId: string | null;
|
||||
frontComponentUniversalIdentifier: string | null;
|
||||
payload: CommandMenuItemPayload | null;
|
||||
validationResult: FailedFlatEntityValidation<
|
||||
'commandMenuItem',
|
||||
'create' | 'update'
|
||||
@@ -201,6 +211,27 @@ export class FlatCommandMenuItemValidatorService {
|
||||
|
||||
break;
|
||||
}
|
||||
case EngineComponentKey.NAVIGATION: {
|
||||
this.validateNavigationPayload({ payload, validationResult });
|
||||
|
||||
if (isNonEmptyString(workflowVersionId)) {
|
||||
validationResult.errors.push({
|
||||
code: CommandMenuItemExceptionCode.INVALID_COMMAND_MENU_ITEM_INPUT,
|
||||
message: t`workflowVersionId must not be set for engine component key ${engineComponentKey}`,
|
||||
userFriendlyMessage: msg`Workflow version must not be set for this item type`,
|
||||
});
|
||||
}
|
||||
|
||||
if (isNonEmptyString(frontComponentUniversalIdentifier)) {
|
||||
validationResult.errors.push({
|
||||
code: CommandMenuItemExceptionCode.INVALID_COMMAND_MENU_ITEM_INPUT,
|
||||
message: t`frontComponentId must not be set for engine component key ${engineComponentKey}`,
|
||||
userFriendlyMessage: msg`Front component must not be set for this item type`,
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (isNonEmptyString(workflowVersionId)) {
|
||||
validationResult.errors.push({
|
||||
@@ -222,4 +253,38 @@ export class FlatCommandMenuItemValidatorService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private validateNavigationPayload({
|
||||
payload,
|
||||
validationResult,
|
||||
}: {
|
||||
payload: CommandMenuItemPayload | null;
|
||||
validationResult: FailedFlatEntityValidation<
|
||||
'commandMenuItem',
|
||||
'create' | 'update'
|
||||
>;
|
||||
}): void {
|
||||
if (!isDefined(payload)) {
|
||||
validationResult.errors.push({
|
||||
code: CommandMenuItemExceptionCode.INVALID_COMMAND_MENU_ITEM_INPUT,
|
||||
message: t`payload is required when engineComponentKey is NAVIGATION`,
|
||||
userFriendlyMessage: msg`Payload is required for navigation items`,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const hasPath = 'path' in payload && isNonEmptyString(payload.path);
|
||||
const hasObjectMetadataItemId =
|
||||
'objectMetadataItemId' in payload &&
|
||||
isNonEmptyString(payload.objectMetadataItemId);
|
||||
|
||||
if (!hasPath && !hasObjectMetadataItemId) {
|
||||
validationResult.errors.push({
|
||||
code: CommandMenuItemExceptionCode.INVALID_COMMAND_MENU_ITEM_INPUT,
|
||||
message: t`payload must contain either a "path" or "objectMetadataItemId" property`,
|
||||
userFriendlyMessage: msg`Payload must contain either a path or an object metadata item identifier`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+44
@@ -174,6 +174,50 @@ describe('CommandMenuItem creation should succeed', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should create NAVIGATION command menu item with path payload', async () => {
|
||||
const { data } = await createCommandMenuItem({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
engineComponentKey: EngineComponentKey.NAVIGATION,
|
||||
label: 'Go to Settings',
|
||||
payload: { path: '/settings/accounts' },
|
||||
},
|
||||
});
|
||||
|
||||
createdCommandMenuItemId = data?.createCommandMenuItem?.id;
|
||||
|
||||
expect(data.createCommandMenuItem).toMatchObject({
|
||||
id: expect.any(String),
|
||||
engineComponentKey: EngineComponentKey.NAVIGATION,
|
||||
label: 'Go to Settings',
|
||||
payload: {
|
||||
path: '/settings/accounts',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should create NAVIGATION command menu item with objectMetadataItemId payload', async () => {
|
||||
const { data } = await createCommandMenuItem({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
engineComponentKey: EngineComponentKey.NAVIGATION,
|
||||
label: 'Go to Companies',
|
||||
payload: { objectMetadataItemId: companyObjectMetadataId },
|
||||
},
|
||||
});
|
||||
|
||||
createdCommandMenuItemId = data?.createCommandMenuItem?.id;
|
||||
|
||||
expect(data.createCommandMenuItem).toMatchObject({
|
||||
id: expect.any(String),
|
||||
engineComponentKey: EngineComponentKey.NAVIGATION,
|
||||
label: 'Go to Companies',
|
||||
payload: {
|
||||
objectMetadataItemId: companyObjectMetadataId,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should create command menu item with frontComponentId', async () => {
|
||||
const { data: frontComponentData } = await createFrontComponent({
|
||||
expectToFail: false,
|
||||
|
||||
+9
@@ -13,11 +13,20 @@ const DEFAULT_COMMAND_MENU_ITEM_GQL_FIELDS = `
|
||||
id
|
||||
name
|
||||
}
|
||||
engineComponentKey
|
||||
label
|
||||
icon
|
||||
isPinned
|
||||
availabilityType
|
||||
availabilityObjectMetadataId
|
||||
payload {
|
||||
... on PathCommandMenuItemPayload {
|
||||
path
|
||||
}
|
||||
... on ObjectMetadataCommandMenuItemPayload {
|
||||
objectMetadataItemId
|
||||
}
|
||||
}
|
||||
applicationId
|
||||
createdAt
|
||||
updatedAt
|
||||
|
||||
+9
@@ -4,11 +4,20 @@ import { type PerformMetadataQueryParams } from 'test/integration/metadata/types
|
||||
const DEFAULT_COMMAND_MENU_ITEM_GQL_FIELDS = `
|
||||
id
|
||||
workflowVersionId
|
||||
engineComponentKey
|
||||
label
|
||||
icon
|
||||
isPinned
|
||||
availabilityType
|
||||
availabilityObjectMetadataId
|
||||
payload {
|
||||
... on PathCommandMenuItemPayload {
|
||||
path
|
||||
}
|
||||
... on ObjectMetadataCommandMenuItemPayload {
|
||||
objectMetadataItemId
|
||||
}
|
||||
}
|
||||
applicationId
|
||||
createdAt
|
||||
updatedAt
|
||||
|
||||
+9
@@ -8,11 +8,20 @@ export type UpdateCommandMenuItemFactoryInput = UpdateCommandMenuItemInput;
|
||||
const DEFAULT_COMMAND_MENU_ITEM_GQL_FIELDS = `
|
||||
id
|
||||
workflowVersionId
|
||||
engineComponentKey
|
||||
label
|
||||
icon
|
||||
isPinned
|
||||
availabilityType
|
||||
availabilityObjectMetadataId
|
||||
payload {
|
||||
... on PathCommandMenuItemPayload {
|
||||
path
|
||||
}
|
||||
... on ObjectMetadataCommandMenuItemPayload {
|
||||
objectMetadataItemId
|
||||
}
|
||||
}
|
||||
applicationId
|
||||
createdAt
|
||||
updatedAt
|
||||
|
||||
Reference in New Issue
Block a user