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:
+69
@@ -0,0 +1,69 @@
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { Command } from '@/command-menu-item/display/components/Command';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
|
||||
import { useOpenFrontComponentInSidePanel } from '@/side-panel/hooks/useOpenFrontComponentInSidePanel';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useContext } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const FrontComponentCommandMenuItem = ({
|
||||
frontComponentId,
|
||||
}: {
|
||||
frontComponentId: string;
|
||||
}) => {
|
||||
const { openFrontComponentInSidePanel } = useOpenFrontComponentInSidePanel();
|
||||
const commandMenuItemConfig = useContext(CommandConfigContext);
|
||||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
);
|
||||
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
|
||||
|
||||
const currentObjectMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.id === contextStoreCurrentObjectMetadataItemId,
|
||||
);
|
||||
|
||||
const selectedRecordIds =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds
|
||||
: [];
|
||||
|
||||
const recordId =
|
||||
selectedRecordIds.length === 1 ? selectedRecordIds[0] : undefined;
|
||||
|
||||
const objectNameSingular = currentObjectMetadataItem?.nameSingular;
|
||||
|
||||
const displayLabel =
|
||||
typeof commandMenuItemConfig?.label === 'string'
|
||||
? commandMenuItemConfig.label
|
||||
: '';
|
||||
|
||||
const Icon = commandMenuItemConfig?.Icon;
|
||||
|
||||
const handleClick = () => {
|
||||
if (!isDefined(Icon)) {
|
||||
return;
|
||||
}
|
||||
|
||||
openFrontComponentInSidePanel({
|
||||
frontComponentId,
|
||||
pageTitle: displayLabel,
|
||||
pageIcon: Icon,
|
||||
recordContext:
|
||||
isDefined(recordId) && isDefined(objectNameSingular)
|
||||
? { recordId, objectNameSingular }
|
||||
: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
return <Command onClick={handleClick} />;
|
||||
};
|
||||
+32
-4
@@ -1,19 +1,22 @@
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useMountHeadlessFrontComponent } from '@/front-components/hooks/useMountHeadlessFrontComponent';
|
||||
import { isHeadlessFrontComponentMountedFamilySelector } from '@/front-components/selectors/isHeadlessFrontComponentMountedFamilySelector';
|
||||
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
|
||||
import { HeadlessCommandMenuItem } from './HeadlessCommandMenuItem';
|
||||
|
||||
// TODO: Some code is duplicated in this component and FrontComponentCommandMenuItem
|
||||
// This will be refactored because the logic will differ between headless and non-headless front components.
|
||||
export const HeadlessFrontComponentCommandMenuItem = ({
|
||||
frontComponentId,
|
||||
commandMenuItemId,
|
||||
recordId,
|
||||
objectNameSingular,
|
||||
}: {
|
||||
frontComponentId: string;
|
||||
commandMenuItemId: string;
|
||||
recordId?: string;
|
||||
objectNameSingular?: string;
|
||||
}) => {
|
||||
const mountHeadlessFrontComponent = useMountHeadlessFrontComponent();
|
||||
|
||||
@@ -22,6 +25,31 @@ export const HeadlessFrontComponentCommandMenuItem = ({
|
||||
frontComponentId,
|
||||
);
|
||||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
);
|
||||
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
|
||||
|
||||
const currentObjectMetadataItem = objectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.id === contextStoreCurrentObjectMetadataItemId,
|
||||
);
|
||||
|
||||
const selectedRecordIds =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds
|
||||
: [];
|
||||
|
||||
const recordId =
|
||||
selectedRecordIds.length === 1 ? selectedRecordIds[0] : undefined;
|
||||
|
||||
const objectNameSingular = currentObjectMetadataItem?.nameSingular;
|
||||
|
||||
const handleClick = () => {
|
||||
mountHeadlessFrontComponent(frontComponentId, {
|
||||
commandMenuItemId,
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
import { Command } from '@/command-menu-item/display/components/Command';
|
||||
import { isBulkRecordsManualTrigger } from '@/command-menu-item/record/utils/isBulkRecordsManualTrigger';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||
import { type WorkflowVersion } from '@/workflow/types/Workflow';
|
||||
import { useStore } from 'jotai';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { QUERY_MAX_RECORDS } from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
type CommandMenuItemAvailabilityType,
|
||||
CommandMenuItemAvailabilityType as CommandMenuItemAvailabilityTypeEnum,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const WorkflowCommandMenuItem = ({
|
||||
workflowVersionId,
|
||||
availabilityType,
|
||||
availabilityObjectMetadataId,
|
||||
}: {
|
||||
workflowVersionId: string;
|
||||
availabilityType: CommandMenuItemAvailabilityType;
|
||||
availabilityObjectMetadataId?: string | null;
|
||||
}) => {
|
||||
const store = useStore();
|
||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||
|
||||
const { record: workflowVersion } = useFindOneRecord<
|
||||
Pick<WorkflowVersion, 'id' | 'workflowId' | 'trigger' | '__typename'>
|
||||
>({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
objectRecordId: workflowVersionId,
|
||||
recordGqlFields: { id: true, workflowId: true, trigger: true },
|
||||
});
|
||||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
|
||||
|
||||
const selectedRecordIds =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds
|
||||
: [];
|
||||
|
||||
const handleClick = async () => {
|
||||
if (!isDefined(workflowVersion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (availabilityType) {
|
||||
case CommandMenuItemAvailabilityTypeEnum.RECORD_SELECTION: {
|
||||
if (selectedRecordIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const limitedSelectedRecordIds = selectedRecordIds.slice(
|
||||
0,
|
||||
QUERY_MAX_RECORDS,
|
||||
);
|
||||
|
||||
const objectMetadataItem = objectMetadataItems.find(
|
||||
(metadata) => metadata.id === availabilityObjectMetadataId,
|
||||
);
|
||||
|
||||
if (
|
||||
isDefined(workflowVersion.trigger) &&
|
||||
isBulkRecordsManualTrigger(workflowVersion.trigger)
|
||||
) {
|
||||
const selectedRecords = limitedSelectedRecordIds
|
||||
.map((recordId) =>
|
||||
store.get(recordStoreFamilyState.atomFamily(recordId)),
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
await runWorkflowVersion({
|
||||
workflowId: workflowVersion.workflowId,
|
||||
workflowVersionId: workflowVersion.id,
|
||||
payload: isDefined(objectMetadataItem)
|
||||
? { [objectMetadataItem.namePlural]: selectedRecords }
|
||||
: undefined,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (const selectedRecordId of limitedSelectedRecordIds) {
|
||||
const selectedRecord = store.get(
|
||||
recordStoreFamilyState.atomFamily(selectedRecordId),
|
||||
);
|
||||
|
||||
if (!isDefined(selectedRecord)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await runWorkflowVersion({
|
||||
workflowId: workflowVersion.workflowId,
|
||||
workflowVersionId: workflowVersion.id,
|
||||
payload: selectedRecord,
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case CommandMenuItemAvailabilityTypeEnum.GLOBAL:
|
||||
case CommandMenuItemAvailabilityTypeEnum.FALLBACK: {
|
||||
await runWorkflowVersion({
|
||||
workflowId: workflowVersion.workflowId,
|
||||
workflowVersionId: workflowVersion.id,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Command
|
||||
onClick={handleClick}
|
||||
closeSidePanelOnCommandMenuListExecution={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+4
-1
@@ -7,10 +7,12 @@ import { useEffect } from 'react';
|
||||
|
||||
export type HeadlessEngineCommandWrapperEffectProps = {
|
||||
execute: () => void | Promise<unknown>;
|
||||
ready?: boolean;
|
||||
};
|
||||
|
||||
export const HeadlessEngineCommandWrapperEffect = ({
|
||||
execute,
|
||||
ready = true,
|
||||
}: HeadlessEngineCommandWrapperEffectProps) => {
|
||||
const { isInitializedRef, setIsInitialized } =
|
||||
useIsHeadlessEngineCommandEffectInitialized();
|
||||
@@ -24,7 +26,7 @@ export const HeadlessEngineCommandWrapperEffect = ({
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
|
||||
useEffect(() => {
|
||||
if (isInitializedRef.current) {
|
||||
if (isInitializedRef.current || !ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -39,6 +41,7 @@ export const HeadlessEngineCommandWrapperEffect = ({
|
||||
run();
|
||||
}, [
|
||||
execute,
|
||||
ready,
|
||||
isInitializedRef,
|
||||
setIsInitialized,
|
||||
engineCommandId,
|
||||
|
||||
+6
-1
@@ -28,5 +28,10 @@ export const ActivateWorkflowSingleRecordCommand = () => {
|
||||
});
|
||||
};
|
||||
|
||||
return <HeadlessEngineCommandWrapperEffect execute={handleExecute} />;
|
||||
return (
|
||||
<HeadlessEngineCommandWrapperEffect
|
||||
execute={handleExecute}
|
||||
ready={isDefined(workflowWithCurrentVersion)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+6
-1
@@ -27,5 +27,10 @@ export const DeactivateWorkflowSingleRecordCommand = () => {
|
||||
});
|
||||
};
|
||||
|
||||
return <HeadlessEngineCommandWrapperEffect execute={handleExecute} />;
|
||||
return (
|
||||
<HeadlessEngineCommandWrapperEffect
|
||||
execute={handleExecute}
|
||||
ready={isDefined(workflowWithCurrentVersion)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+6
-1
@@ -27,5 +27,10 @@ export const DiscardDraftWorkflowSingleRecordCommand = () => {
|
||||
});
|
||||
};
|
||||
|
||||
return <HeadlessEngineCommandWrapperEffect execute={handleExecute} />;
|
||||
return (
|
||||
<HeadlessEngineCommandWrapperEffect
|
||||
execute={handleExecute}
|
||||
ready={isDefined(workflowWithCurrentVersion)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+6
-1
@@ -28,5 +28,10 @@ export const TestWorkflowSingleRecordCommand = () => {
|
||||
});
|
||||
};
|
||||
|
||||
return <HeadlessEngineCommandWrapperEffect execute={handleExecute} />;
|
||||
return (
|
||||
<HeadlessEngineCommandWrapperEffect
|
||||
execute={handleExecute}
|
||||
ready={isDefined(workflowWithCurrentVersion)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
+9
-291
@@ -1,217 +1,24 @@
|
||||
import { Command } from '@/command-menu-item/display/components/Command';
|
||||
import { EngineCommandMenuItem } from '@/command-menu-item/display/components/EngineCommandMenuItem';
|
||||
import { HeadlessFrontComponentCommandMenuItem } from '@/command-menu-item/display/components/HeadlessFrontComponentCommandMenuItem';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import { useConvertBackendItemToCommandMenuItemConfig } from '@/command-menu-item/server-items/hooks/useConvertBackendItemToCommandMenuItemConfig';
|
||||
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
|
||||
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
|
||||
import { useOpenFrontComponentInSidePanel } from '@/side-panel/hooks/useOpenFrontComponentInSidePanel';
|
||||
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
import { type CommandMenuContextApi } from 'twenty-shared/types';
|
||||
import {
|
||||
evaluateConditionalAvailabilityExpression,
|
||||
interpolateCommandMenuItemLabel,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
import { type IconComponent, useIcons } from 'twenty-ui/display';
|
||||
|
||||
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
|
||||
import { useQuery } from '@apollo/client/react';
|
||||
import { type CommandMenuContextApi } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
CommandMenuItemAvailabilityType,
|
||||
type CommandMenuItemFieldsFragment,
|
||||
type EngineComponentKey,
|
||||
FindManyCommandMenuItemsDocument,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
type CommandMenuItemWithFrontComponent = CommandMenuItemFieldsFragment & {
|
||||
frontComponentId: string;
|
||||
conditionalAvailabilityExpression?: string | null;
|
||||
};
|
||||
|
||||
type CommandMenuItemWithSource = CommandMenuItemFieldsFragment & {
|
||||
conditionalAvailabilityExpression?: string | null;
|
||||
};
|
||||
|
||||
type BuildCommandMenuItemFromFrontComponentParams = {
|
||||
item: CommandMenuItemWithFrontComponent;
|
||||
type?: CommandMenuItemType;
|
||||
scope: CommandMenuItemScope;
|
||||
isPinned: boolean;
|
||||
getIcon: ReturnType<typeof useIcons>['getIcon'];
|
||||
openFrontComponentInSidePanel: (params: {
|
||||
frontComponentId: string;
|
||||
pageTitle: string;
|
||||
pageIcon: IconComponent;
|
||||
recordContext?: {
|
||||
recordId: string;
|
||||
objectNameSingular: string;
|
||||
};
|
||||
}) => void;
|
||||
recordId?: string;
|
||||
objectNameSingular?: string;
|
||||
commandMenuContextApi: CommandMenuContextApi;
|
||||
};
|
||||
|
||||
const buildCommandMenuItemFromFrontComponent = ({
|
||||
item,
|
||||
type = CommandMenuItemType.FrontComponent,
|
||||
scope,
|
||||
isPinned,
|
||||
getIcon,
|
||||
openFrontComponentInSidePanel,
|
||||
recordId,
|
||||
objectNameSingular,
|
||||
commandMenuContextApi,
|
||||
}: BuildCommandMenuItemFromFrontComponentParams) => {
|
||||
const displayLabel = interpolateCommandMenuItemLabel({
|
||||
label: item.label,
|
||||
context: commandMenuContextApi,
|
||||
});
|
||||
|
||||
const displayShortLabel = interpolateCommandMenuItemLabel({
|
||||
label: item.shortLabel,
|
||||
context: commandMenuContextApi,
|
||||
});
|
||||
|
||||
const Icon = getIcon(item.icon, COMMAND_MENU_DEFAULT_ICON);
|
||||
|
||||
const isHeadless = item.frontComponent?.isHeadless === true;
|
||||
|
||||
const handleNonHeadlessClick = () => {
|
||||
openFrontComponentInSidePanel({
|
||||
frontComponentId: item.frontComponentId,
|
||||
pageTitle: displayLabel ?? '',
|
||||
pageIcon: Icon,
|
||||
recordContext:
|
||||
isDefined(recordId) && isDefined(objectNameSingular)
|
||||
? { recordId, objectNameSingular }
|
||||
: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
type,
|
||||
key: `command-menu-item-front-component-${item.id}`,
|
||||
scope,
|
||||
label: displayLabel,
|
||||
shortLabel: displayShortLabel,
|
||||
position: item.position,
|
||||
isPinned,
|
||||
Icon,
|
||||
hotKeys: item.hotKeys,
|
||||
shouldBeRegistered: () =>
|
||||
evaluateConditionalAvailabilityExpression(
|
||||
item.conditionalAvailabilityExpression,
|
||||
commandMenuContextApi,
|
||||
),
|
||||
component: isHeadless ? (
|
||||
<HeadlessFrontComponentCommandMenuItem
|
||||
frontComponentId={item.frontComponentId}
|
||||
commandMenuItemId={item.id}
|
||||
recordId={recordId}
|
||||
objectNameSingular={objectNameSingular}
|
||||
/>
|
||||
) : (
|
||||
<Command onClick={handleNonHeadlessClick} />
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
type BuildCommandMenuItemFromStandardKeyParams = {
|
||||
item: CommandMenuItemWithSource;
|
||||
engineComponentKey: EngineComponentKey;
|
||||
type?: CommandMenuItemType;
|
||||
scope: CommandMenuItemScope;
|
||||
isPinned: boolean;
|
||||
getIcon: ReturnType<typeof useIcons>['getIcon'];
|
||||
commandMenuContextApi: CommandMenuContextApi;
|
||||
};
|
||||
|
||||
const buildCommandItemFromEngineKey = ({
|
||||
item,
|
||||
engineComponentKey,
|
||||
type = CommandMenuItemType.Standard,
|
||||
scope,
|
||||
isPinned,
|
||||
getIcon,
|
||||
commandMenuContextApi,
|
||||
}: BuildCommandMenuItemFromStandardKeyParams) => {
|
||||
const Icon = getIcon(item.icon, COMMAND_MENU_DEFAULT_ICON);
|
||||
|
||||
return {
|
||||
type,
|
||||
key: `command-menu-item-engine-${item.id}`,
|
||||
scope,
|
||||
label: interpolateCommandMenuItemLabel({
|
||||
label: item.label,
|
||||
context: commandMenuContextApi,
|
||||
}),
|
||||
shortLabel: interpolateCommandMenuItemLabel({
|
||||
label: item.shortLabel,
|
||||
context: commandMenuContextApi,
|
||||
}),
|
||||
position: item.position,
|
||||
isPinned,
|
||||
Icon,
|
||||
hotKeys: item.hotKeys,
|
||||
shouldBeRegistered: () =>
|
||||
evaluateConditionalAvailabilityExpression(
|
||||
item.conditionalAvailabilityExpression,
|
||||
commandMenuContextApi,
|
||||
),
|
||||
component: (
|
||||
<EngineCommandMenuItem
|
||||
commandMenuItemId={item.id}
|
||||
engineComponentKey={engineComponentKey}
|
||||
/>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export const useCommandMenuItemsFromBackend = (
|
||||
commandMenuContextApi: CommandMenuContextApi,
|
||||
) => {
|
||||
const { getIcon } = useIcons();
|
||||
const { openFrontComponentInSidePanel } = useOpenFrontComponentInSidePanel();
|
||||
|
||||
const contextStoreIsPageInEditMode = useAtomComponentStateValue(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
);
|
||||
const { convertBackendItemToCommandMenuItemConfig } =
|
||||
useConvertBackendItemToCommandMenuItemConfig();
|
||||
|
||||
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
|
||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||
);
|
||||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const objectMetadataItems = useAtomStateValue(objectMetadataItemsSelector);
|
||||
|
||||
const currentObjectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.id === contextStoreCurrentObjectMetadataItemId,
|
||||
);
|
||||
|
||||
const selectedRecordIds =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds
|
||||
: [];
|
||||
|
||||
const hasRecordSelection =
|
||||
selectedRecordIds.length >= 1 ||
|
||||
contextStoreTargetedRecordsRule.mode === 'exclusion';
|
||||
|
||||
const recordId =
|
||||
selectedRecordIds.length === 1 ? selectedRecordIds[0] : undefined;
|
||||
|
||||
const objectNameSingular = currentObjectMetadataItem?.nameSingular;
|
||||
|
||||
const { data } = useQuery(FindManyCommandMenuItemsDocument);
|
||||
|
||||
const allItems = data?.commandMenuItems ?? [];
|
||||
@@ -221,101 +28,12 @@ export const useCommandMenuItemsFromBackend = (
|
||||
item.availabilityObjectMetadataId ===
|
||||
contextStoreCurrentObjectMetadataItemId;
|
||||
|
||||
const itemsWithObjectMatches = allItems.filter(objectMatches);
|
||||
|
||||
const buildCommandMenuItem = ({
|
||||
item,
|
||||
scope,
|
||||
isPinned,
|
||||
typeOverride,
|
||||
}: {
|
||||
item: CommandMenuItemFieldsFragment;
|
||||
scope: CommandMenuItemScope;
|
||||
isPinned: boolean;
|
||||
typeOverride?: CommandMenuItemType;
|
||||
}) => {
|
||||
if (isDefined(item.engineComponentKey)) {
|
||||
return buildCommandItemFromEngineKey({
|
||||
item,
|
||||
engineComponentKey: item.engineComponentKey,
|
||||
type: typeOverride,
|
||||
scope,
|
||||
isPinned,
|
||||
getIcon,
|
||||
commandMenuContextApi,
|
||||
});
|
||||
}
|
||||
|
||||
if (isDefined(item.frontComponentId)) {
|
||||
return buildCommandMenuItemFromFrontComponent({
|
||||
item: item as CommandMenuItemWithFrontComponent,
|
||||
type: typeOverride,
|
||||
scope,
|
||||
isPinned,
|
||||
getIcon,
|
||||
openFrontComponentInSidePanel,
|
||||
commandMenuContextApi,
|
||||
recordId,
|
||||
objectNameSingular,
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const globalItems = itemsWithObjectMatches.filter(
|
||||
(item) => item.availabilityType === CommandMenuItemAvailabilityType.GLOBAL,
|
||||
);
|
||||
|
||||
const recordScopedItems = itemsWithObjectMatches.filter(
|
||||
(item) =>
|
||||
item.availabilityType ===
|
||||
CommandMenuItemAvailabilityType.RECORD_SELECTION,
|
||||
);
|
||||
|
||||
const fallbackItems = itemsWithObjectMatches.filter(
|
||||
(item) =>
|
||||
item.availabilityType === CommandMenuItemAvailabilityType.FALLBACK,
|
||||
);
|
||||
|
||||
const globalCommandMenuItems = globalItems
|
||||
return allItems
|
||||
.filter(objectMatches)
|
||||
.map((item) =>
|
||||
buildCommandMenuItem({
|
||||
item,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
isPinned: !contextStoreIsPageInEditMode && item.isPinned,
|
||||
}),
|
||||
convertBackendItemToCommandMenuItemConfig(item, commandMenuContextApi),
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
const recordScopedCommandMenuItems = hasRecordSelection
|
||||
? recordScopedItems
|
||||
.map((item) =>
|
||||
buildCommandMenuItem({
|
||||
item,
|
||||
scope: CommandMenuItemScope.RecordSelection,
|
||||
isPinned: !contextStoreIsPageInEditMode && item.isPinned,
|
||||
}),
|
||||
)
|
||||
.filter(isDefined)
|
||||
: [];
|
||||
|
||||
const fallbackCommandMenuItems = fallbackItems
|
||||
.map((item) =>
|
||||
buildCommandMenuItem({
|
||||
item,
|
||||
scope: CommandMenuItemScope.Global,
|
||||
isPinned: false,
|
||||
typeOverride: CommandMenuItemType.Fallback,
|
||||
}),
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
return [
|
||||
...globalCommandMenuItems,
|
||||
...recordScopedCommandMenuItems,
|
||||
...fallbackCommandMenuItems,
|
||||
]
|
||||
.filter(isDefined)
|
||||
.filter((item) => item.shouldBeRegistered())
|
||||
.sort((a, b) => a.position - b.position);
|
||||
};
|
||||
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
import { EngineCommandMenuItem } from '@/command-menu-item/display/components/EngineCommandMenuItem';
|
||||
import { FrontComponentCommandMenuItem } from '@/command-menu-item/display/components/FrontComponentCommandMenuItem';
|
||||
import { HeadlessFrontComponentCommandMenuItem } from '@/command-menu-item/display/components/HeadlessFrontComponentCommandMenuItem';
|
||||
import { WorkflowCommandMenuItem } from '@/command-menu-item/display/components/WorkflowCommandMenuItem';
|
||||
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
|
||||
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { COMMAND_MENU_DEFAULT_ICON } from '@/workflow/workflow-trigger/constants/CommandMenuDefaultIcon';
|
||||
import { useCallback } from 'react';
|
||||
import { type CommandMenuContextApi } from 'twenty-shared/types';
|
||||
import {
|
||||
evaluateConditionalAvailabilityExpression,
|
||||
interpolateCommandMenuItemLabel,
|
||||
isDefined,
|
||||
} from 'twenty-shared/utils';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import {
|
||||
CommandMenuItemAvailabilityType,
|
||||
type CommandMenuItemFieldsFragment,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
const resolveScope = (
|
||||
availabilityType: CommandMenuItemAvailabilityType,
|
||||
): CommandMenuItemScope => {
|
||||
if (availabilityType === CommandMenuItemAvailabilityType.RECORD_SELECTION) {
|
||||
return CommandMenuItemScope.RecordSelection;
|
||||
}
|
||||
return CommandMenuItemScope.Global;
|
||||
};
|
||||
|
||||
const resolveType = (
|
||||
item: CommandMenuItemFieldsFragment,
|
||||
): CommandMenuItemType => {
|
||||
if (item.availabilityType === CommandMenuItemAvailabilityType.FALLBACK) {
|
||||
return CommandMenuItemType.Fallback;
|
||||
}
|
||||
if (isDefined(item.engineComponentKey)) {
|
||||
return CommandMenuItemType.Standard;
|
||||
}
|
||||
if (isDefined(item.frontComponentId)) {
|
||||
return CommandMenuItemType.FrontComponent;
|
||||
}
|
||||
if (isDefined(item.workflowVersionId)) {
|
||||
return CommandMenuItemType.WorkflowRun;
|
||||
}
|
||||
return CommandMenuItemType.Standard;
|
||||
};
|
||||
|
||||
// TODO: Remove this hook once we finish refactoring and we use
|
||||
// the new types to build command menu items.
|
||||
export const useConvertBackendItemToCommandMenuItemConfig = () => {
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const contextStoreIsPageInEditMode = useAtomComponentStateValue(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
);
|
||||
|
||||
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const selectedRecordIds =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds
|
||||
: [];
|
||||
|
||||
const hasRecordSelection =
|
||||
selectedRecordIds.length >= 1 ||
|
||||
contextStoreTargetedRecordsRule.mode === 'exclusion';
|
||||
|
||||
const convertBackendItemToCommandMenuItemConfig = useCallback(
|
||||
(
|
||||
item: CommandMenuItemFieldsFragment,
|
||||
commandMenuContextApi: CommandMenuContextApi,
|
||||
) => {
|
||||
const scope = resolveScope(item.availabilityType);
|
||||
|
||||
if (
|
||||
scope === CommandMenuItemScope.RecordSelection &&
|
||||
!hasRecordSelection
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isPinned =
|
||||
item.availabilityType !== CommandMenuItemAvailabilityType.FALLBACK &&
|
||||
!contextStoreIsPageInEditMode &&
|
||||
item.isPinned;
|
||||
|
||||
const Icon = getIcon(item.icon, COMMAND_MENU_DEFAULT_ICON);
|
||||
|
||||
const label = interpolateCommandMenuItemLabel({
|
||||
label: item.label,
|
||||
context: commandMenuContextApi,
|
||||
});
|
||||
|
||||
const shortLabel = interpolateCommandMenuItemLabel({
|
||||
label: item.shortLabel,
|
||||
context: commandMenuContextApi,
|
||||
});
|
||||
|
||||
const component = isDefined(item.engineComponentKey) ? (
|
||||
<EngineCommandMenuItem
|
||||
commandMenuItemId={item.id}
|
||||
engineComponentKey={item.engineComponentKey}
|
||||
/>
|
||||
) : isDefined(item.frontComponentId) ? (
|
||||
item.frontComponent?.isHeadless === true ? (
|
||||
<HeadlessFrontComponentCommandMenuItem
|
||||
frontComponentId={item.frontComponentId}
|
||||
commandMenuItemId={item.id}
|
||||
/>
|
||||
) : (
|
||||
<FrontComponentCommandMenuItem
|
||||
frontComponentId={item.frontComponentId}
|
||||
/>
|
||||
)
|
||||
) : isDefined(item.workflowVersionId) ? (
|
||||
<WorkflowCommandMenuItem
|
||||
workflowVersionId={item.workflowVersionId}
|
||||
availabilityType={item.availabilityType}
|
||||
availabilityObjectMetadataId={item.availabilityObjectMetadataId}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
if (!isDefined(component)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
type: resolveType(item),
|
||||
key: `command-menu-item-${item.id}`,
|
||||
scope,
|
||||
label,
|
||||
shortLabel,
|
||||
position: item.position,
|
||||
isPinned,
|
||||
Icon,
|
||||
hotKeys: item.hotKeys,
|
||||
shouldBeRegistered: () =>
|
||||
evaluateConditionalAvailabilityExpression(
|
||||
item.conditionalAvailabilityExpression,
|
||||
commandMenuContextApi,
|
||||
),
|
||||
component,
|
||||
};
|
||||
},
|
||||
[getIcon, contextStoreIsPageInEditMode, hasRecordSelection],
|
||||
);
|
||||
|
||||
return { convertBackendItemToCommandMenuItemConfig };
|
||||
};
|
||||
@@ -207,6 +207,11 @@ export const graphqlMocks = {
|
||||
data: { navigationMenuItems: mockedNavigationMenuItems },
|
||||
});
|
||||
}),
|
||||
metadataGraphql.query('FindManyCommandMenuItems', () => {
|
||||
return HttpResponse.json({
|
||||
data: { commandMenuItems: [] },
|
||||
});
|
||||
}),
|
||||
graphql.query('SearchPeople', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
|
||||
@@ -3099,6 +3099,10 @@ type Query {
|
||||
findManyLogicFunctions: [LogicFunction!]!
|
||||
getAvailablePackages(input: LogicFunctionIdInput!): JSON!
|
||||
getLogicFunctionSourceCode(input: LogicFunctionIdInput!): String
|
||||
commandMenuItems: [CommandMenuItem!]!
|
||||
commandMenuItem(id: UUID!): CommandMenuItem
|
||||
frontComponents: [FrontComponent!]!
|
||||
frontComponent(id: UUID!): FrontComponent
|
||||
objectRecordCounts: [ObjectRecordCount!]!
|
||||
object(
|
||||
"""The id of the record to find."""
|
||||
@@ -3130,10 +3134,6 @@ type Query {
|
||||
"""Specify to filter the records returned."""
|
||||
filter: IndexFilter! = {}
|
||||
): IndexConnection!
|
||||
commandMenuItems: [CommandMenuItem!]!
|
||||
commandMenuItem(id: UUID!): CommandMenuItem
|
||||
frontComponents: [FrontComponent!]!
|
||||
frontComponent(id: UUID!): FrontComponent
|
||||
findManyAgents: [Agent!]!
|
||||
findOneAgent(input: AgentIdInput!): Agent!
|
||||
billingPortalSession(returnUrlPath: String): BillingSession!
|
||||
@@ -3366,6 +3366,12 @@ type Mutation {
|
||||
createOneLogicFunction(input: CreateLogicFunctionFromSourceInput!): LogicFunction!
|
||||
executeOneLogicFunction(input: ExecuteOneLogicFunctionInput!): LogicFunctionExecutionResult!
|
||||
updateOneLogicFunction(input: UpdateLogicFunctionFromSourceInput!): Boolean!
|
||||
createCommandMenuItem(input: CreateCommandMenuItemInput!): CommandMenuItem!
|
||||
updateCommandMenuItem(input: UpdateCommandMenuItemInput!): CommandMenuItem!
|
||||
deleteCommandMenuItem(id: UUID!): CommandMenuItem!
|
||||
createFrontComponent(input: CreateFrontComponentInput!): FrontComponent!
|
||||
updateFrontComponent(input: UpdateFrontComponentInput!): FrontComponent!
|
||||
deleteFrontComponent(id: UUID!): FrontComponent!
|
||||
createOneObject(input: CreateOneObjectInput!): Object!
|
||||
deleteOneObject(input: DeleteOneObjectInput!): Object!
|
||||
updateOneObject(input: UpdateOneObjectInput!): Object!
|
||||
@@ -3388,12 +3394,6 @@ type Mutation {
|
||||
deleteViewFieldGroup(input: DeleteViewFieldGroupInput!): ViewFieldGroup!
|
||||
destroyViewFieldGroup(input: DestroyViewFieldGroupInput!): ViewFieldGroup!
|
||||
upsertFieldsWidget(input: UpsertFieldsWidgetInput!): View!
|
||||
createCommandMenuItem(input: CreateCommandMenuItemInput!): CommandMenuItem!
|
||||
updateCommandMenuItem(input: UpdateCommandMenuItemInput!): CommandMenuItem!
|
||||
deleteCommandMenuItem(id: UUID!): CommandMenuItem!
|
||||
createFrontComponent(input: CreateFrontComponentInput!): FrontComponent!
|
||||
updateFrontComponent(input: UpdateFrontComponentInput!): FrontComponent!
|
||||
deleteFrontComponent(id: UUID!): FrontComponent!
|
||||
createOneAgent(input: CreateAgentInput!): Agent!
|
||||
updateOneAgent(input: UpdateAgentInput!): Agent!
|
||||
deleteOneAgent(input: AgentIdInput!): Agent!
|
||||
@@ -3719,6 +3719,57 @@ input UpdateLogicFunctionFromSourceInputUpdates {
|
||||
httpRouteTriggerSettings: JSON
|
||||
}
|
||||
|
||||
input CreateCommandMenuItemInput {
|
||||
workflowVersionId: UUID
|
||||
frontComponentId: UUID
|
||||
engineComponentKey: EngineComponentKey
|
||||
label: String!
|
||||
icon: String
|
||||
shortLabel: String
|
||||
position: Float
|
||||
isPinned: Boolean
|
||||
availabilityType: CommandMenuItemAvailabilityType
|
||||
hotKeys: [String!]
|
||||
conditionalAvailabilityExpression: String
|
||||
availabilityObjectMetadataId: UUID
|
||||
}
|
||||
|
||||
input UpdateCommandMenuItemInput {
|
||||
id: UUID!
|
||||
label: String
|
||||
icon: String
|
||||
shortLabel: String
|
||||
position: Float
|
||||
isPinned: Boolean
|
||||
availabilityType: CommandMenuItemAvailabilityType
|
||||
availabilityObjectMetadataId: UUID
|
||||
engineComponentKey: EngineComponentKey
|
||||
hotKeys: [String!]
|
||||
}
|
||||
|
||||
input CreateFrontComponentInput {
|
||||
id: UUID
|
||||
name: String!
|
||||
description: String
|
||||
sourceComponentPath: String!
|
||||
builtComponentPath: String!
|
||||
componentName: String!
|
||||
builtComponentChecksum: String!
|
||||
}
|
||||
|
||||
input UpdateFrontComponentInput {
|
||||
"""The id of the front component to update"""
|
||||
id: UUID!
|
||||
|
||||
"""The front component fields to update"""
|
||||
update: UpdateFrontComponentInputUpdates!
|
||||
}
|
||||
|
||||
input UpdateFrontComponentInputUpdates {
|
||||
name: String
|
||||
description: String
|
||||
}
|
||||
|
||||
input CreateOneObjectInput {
|
||||
"""The object to create"""
|
||||
object: CreateObjectInput!
|
||||
@@ -3934,57 +3985,6 @@ input UpsertFieldsWidgetFieldInput {
|
||||
position: Float!
|
||||
}
|
||||
|
||||
input CreateCommandMenuItemInput {
|
||||
workflowVersionId: UUID
|
||||
frontComponentId: UUID
|
||||
engineComponentKey: EngineComponentKey
|
||||
label: String!
|
||||
icon: String
|
||||
shortLabel: String
|
||||
position: Float
|
||||
isPinned: Boolean
|
||||
availabilityType: CommandMenuItemAvailabilityType
|
||||
hotKeys: [String!]
|
||||
conditionalAvailabilityExpression: String
|
||||
availabilityObjectMetadataId: UUID
|
||||
}
|
||||
|
||||
input UpdateCommandMenuItemInput {
|
||||
id: UUID!
|
||||
label: String
|
||||
icon: String
|
||||
shortLabel: String
|
||||
position: Float
|
||||
isPinned: Boolean
|
||||
availabilityType: CommandMenuItemAvailabilityType
|
||||
availabilityObjectMetadataId: UUID
|
||||
engineComponentKey: EngineComponentKey
|
||||
hotKeys: [String!]
|
||||
}
|
||||
|
||||
input CreateFrontComponentInput {
|
||||
id: UUID
|
||||
name: String!
|
||||
description: String
|
||||
sourceComponentPath: String!
|
||||
builtComponentPath: String!
|
||||
componentName: String!
|
||||
builtComponentChecksum: String!
|
||||
}
|
||||
|
||||
input UpdateFrontComponentInput {
|
||||
"""The id of the front component to update"""
|
||||
id: UUID!
|
||||
|
||||
"""The front component fields to update"""
|
||||
update: UpdateFrontComponentInputUpdates!
|
||||
}
|
||||
|
||||
input UpdateFrontComponentInputUpdates {
|
||||
name: String
|
||||
description: String
|
||||
}
|
||||
|
||||
input CreateAgentInput {
|
||||
name: String
|
||||
label: String!
|
||||
|
||||
@@ -2692,6 +2692,10 @@ export interface Query {
|
||||
findManyLogicFunctions: LogicFunction[]
|
||||
getAvailablePackages: Scalars['JSON']
|
||||
getLogicFunctionSourceCode?: Scalars['String']
|
||||
commandMenuItems: CommandMenuItem[]
|
||||
commandMenuItem?: CommandMenuItem
|
||||
frontComponents: FrontComponent[]
|
||||
frontComponent?: FrontComponent
|
||||
objectRecordCounts: ObjectRecordCount[]
|
||||
object: Object
|
||||
objects: ObjectConnection
|
||||
@@ -2705,10 +2709,6 @@ export interface Query {
|
||||
getViewFieldGroup?: ViewFieldGroup
|
||||
index: Index
|
||||
indexMetadatas: IndexConnection
|
||||
commandMenuItems: CommandMenuItem[]
|
||||
commandMenuItem?: CommandMenuItem
|
||||
frontComponents: FrontComponent[]
|
||||
frontComponent?: FrontComponent
|
||||
findManyAgents: Agent[]
|
||||
findOneAgent: Agent
|
||||
billingPortalSession: BillingSession
|
||||
@@ -2831,6 +2831,12 @@ export interface Mutation {
|
||||
createOneLogicFunction: LogicFunction
|
||||
executeOneLogicFunction: LogicFunctionExecutionResult
|
||||
updateOneLogicFunction: Scalars['Boolean']
|
||||
createCommandMenuItem: CommandMenuItem
|
||||
updateCommandMenuItem: CommandMenuItem
|
||||
deleteCommandMenuItem: CommandMenuItem
|
||||
createFrontComponent: FrontComponent
|
||||
updateFrontComponent: FrontComponent
|
||||
deleteFrontComponent: FrontComponent
|
||||
createOneObject: Object
|
||||
deleteOneObject: Object
|
||||
updateOneObject: Object
|
||||
@@ -2853,12 +2859,6 @@ export interface Mutation {
|
||||
deleteViewFieldGroup: ViewFieldGroup
|
||||
destroyViewFieldGroup: ViewFieldGroup
|
||||
upsertFieldsWidget: View
|
||||
createCommandMenuItem: CommandMenuItem
|
||||
updateCommandMenuItem: CommandMenuItem
|
||||
deleteCommandMenuItem: CommandMenuItem
|
||||
createFrontComponent: FrontComponent
|
||||
updateFrontComponent: FrontComponent
|
||||
deleteFrontComponent: FrontComponent
|
||||
createOneAgent: Agent
|
||||
updateOneAgent: Agent
|
||||
deleteOneAgent: Agent
|
||||
@@ -5852,6 +5852,10 @@ export interface QueryGenqlSelection{
|
||||
findManyLogicFunctions?: LogicFunctionGenqlSelection
|
||||
getAvailablePackages?: { __args: {input: LogicFunctionIdInput} }
|
||||
getLogicFunctionSourceCode?: { __args: {input: LogicFunctionIdInput} }
|
||||
commandMenuItems?: CommandMenuItemGenqlSelection
|
||||
commandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {id: Scalars['UUID']} })
|
||||
frontComponents?: FrontComponentGenqlSelection
|
||||
frontComponent?: (FrontComponentGenqlSelection & { __args: {id: Scalars['UUID']} })
|
||||
objectRecordCounts?: ObjectRecordCountGenqlSelection
|
||||
object?: (ObjectGenqlSelection & { __args: {
|
||||
/** The id of the record to find. */
|
||||
@@ -5877,10 +5881,6 @@ export interface QueryGenqlSelection{
|
||||
paging: CursorPaging,
|
||||
/** Specify to filter the records returned. */
|
||||
filter: IndexFilter} })
|
||||
commandMenuItems?: CommandMenuItemGenqlSelection
|
||||
commandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {id: Scalars['UUID']} })
|
||||
frontComponents?: FrontComponentGenqlSelection
|
||||
frontComponent?: (FrontComponentGenqlSelection & { __args: {id: Scalars['UUID']} })
|
||||
findManyAgents?: AgentGenqlSelection
|
||||
findOneAgent?: (AgentGenqlSelection & { __args: {input: AgentIdInput} })
|
||||
billingPortalSession?: (BillingSessionGenqlSelection & { __args?: {returnUrlPath?: (Scalars['String'] | null)} })
|
||||
@@ -6034,6 +6034,12 @@ export interface MutationGenqlSelection{
|
||||
createOneLogicFunction?: (LogicFunctionGenqlSelection & { __args: {input: CreateLogicFunctionFromSourceInput} })
|
||||
executeOneLogicFunction?: (LogicFunctionExecutionResultGenqlSelection & { __args: {input: ExecuteOneLogicFunctionInput} })
|
||||
updateOneLogicFunction?: { __args: {input: UpdateLogicFunctionFromSourceInput} }
|
||||
createCommandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {input: CreateCommandMenuItemInput} })
|
||||
updateCommandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {input: UpdateCommandMenuItemInput} })
|
||||
deleteCommandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {id: Scalars['UUID']} })
|
||||
createFrontComponent?: (FrontComponentGenqlSelection & { __args: {input: CreateFrontComponentInput} })
|
||||
updateFrontComponent?: (FrontComponentGenqlSelection & { __args: {input: UpdateFrontComponentInput} })
|
||||
deleteFrontComponent?: (FrontComponentGenqlSelection & { __args: {id: Scalars['UUID']} })
|
||||
createOneObject?: (ObjectGenqlSelection & { __args: {input: CreateOneObjectInput} })
|
||||
deleteOneObject?: (ObjectGenqlSelection & { __args: {input: DeleteOneObjectInput} })
|
||||
updateOneObject?: (ObjectGenqlSelection & { __args: {input: UpdateOneObjectInput} })
|
||||
@@ -6056,12 +6062,6 @@ export interface MutationGenqlSelection{
|
||||
deleteViewFieldGroup?: (ViewFieldGroupGenqlSelection & { __args: {input: DeleteViewFieldGroupInput} })
|
||||
destroyViewFieldGroup?: (ViewFieldGroupGenqlSelection & { __args: {input: DestroyViewFieldGroupInput} })
|
||||
upsertFieldsWidget?: (ViewGenqlSelection & { __args: {input: UpsertFieldsWidgetInput} })
|
||||
createCommandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {input: CreateCommandMenuItemInput} })
|
||||
updateCommandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {input: UpdateCommandMenuItemInput} })
|
||||
deleteCommandMenuItem?: (CommandMenuItemGenqlSelection & { __args: {id: Scalars['UUID']} })
|
||||
createFrontComponent?: (FrontComponentGenqlSelection & { __args: {input: CreateFrontComponentInput} })
|
||||
updateFrontComponent?: (FrontComponentGenqlSelection & { __args: {input: UpdateFrontComponentInput} })
|
||||
deleteFrontComponent?: (FrontComponentGenqlSelection & { __args: {id: Scalars['UUID']} })
|
||||
createOneAgent?: (AgentGenqlSelection & { __args: {input: CreateAgentInput} })
|
||||
updateOneAgent?: (AgentGenqlSelection & { __args: {input: UpdateAgentInput} })
|
||||
deleteOneAgent?: (AgentGenqlSelection & { __args: {input: AgentIdInput} })
|
||||
@@ -6263,6 +6263,20 @@ update: UpdateLogicFunctionFromSourceInputUpdates}
|
||||
|
||||
export interface UpdateLogicFunctionFromSourceInputUpdates {name?: (Scalars['String'] | null),description?: (Scalars['String'] | null),timeoutSeconds?: (Scalars['Float'] | null),sourceHandlerCode?: (Scalars['String'] | null),toolInputSchema?: (Scalars['JSON'] | null),handlerName?: (Scalars['String'] | null),sourceHandlerPath?: (Scalars['String'] | null),isTool?: (Scalars['Boolean'] | null),cronTriggerSettings?: (Scalars['JSON'] | null),databaseEventTriggerSettings?: (Scalars['JSON'] | null),httpRouteTriggerSettings?: (Scalars['JSON'] | null)}
|
||||
|
||||
export interface CreateCommandMenuItemInput {workflowVersionId?: (Scalars['UUID'] | null),frontComponentId?: (Scalars['UUID'] | null),engineComponentKey?: (EngineComponentKey | null),label: Scalars['String'],icon?: (Scalars['String'] | null),shortLabel?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isPinned?: (Scalars['Boolean'] | null),availabilityType?: (CommandMenuItemAvailabilityType | null),hotKeys?: (Scalars['String'][] | null),conditionalAvailabilityExpression?: (Scalars['String'] | null),availabilityObjectMetadataId?: (Scalars['UUID'] | null)}
|
||||
|
||||
export interface UpdateCommandMenuItemInput {id: Scalars['UUID'],label?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),shortLabel?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isPinned?: (Scalars['Boolean'] | null),availabilityType?: (CommandMenuItemAvailabilityType | null),availabilityObjectMetadataId?: (Scalars['UUID'] | null),engineComponentKey?: (EngineComponentKey | null),hotKeys?: (Scalars['String'][] | null)}
|
||||
|
||||
export interface CreateFrontComponentInput {id?: (Scalars['UUID'] | null),name: Scalars['String'],description?: (Scalars['String'] | null),sourceComponentPath: Scalars['String'],builtComponentPath: Scalars['String'],componentName: Scalars['String'],builtComponentChecksum: Scalars['String']}
|
||||
|
||||
export interface UpdateFrontComponentInput {
|
||||
/** The id of the front component to update */
|
||||
id: Scalars['UUID'],
|
||||
/** The front component fields to update */
|
||||
update: UpdateFrontComponentInputUpdates}
|
||||
|
||||
export interface UpdateFrontComponentInputUpdates {name?: (Scalars['String'] | null),description?: (Scalars['String'] | null)}
|
||||
|
||||
export interface CreateOneObjectInput {
|
||||
/** The object to create */
|
||||
object: CreateObjectInput}
|
||||
@@ -6351,20 +6365,6 @@ export interface UpsertFieldsWidgetFieldInput {
|
||||
/** The id of the view field */
|
||||
viewFieldId: Scalars['UUID'],isVisible: Scalars['Boolean'],position: Scalars['Float']}
|
||||
|
||||
export interface CreateCommandMenuItemInput {workflowVersionId?: (Scalars['UUID'] | null),frontComponentId?: (Scalars['UUID'] | null),engineComponentKey?: (EngineComponentKey | null),label: Scalars['String'],icon?: (Scalars['String'] | null),shortLabel?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isPinned?: (Scalars['Boolean'] | null),availabilityType?: (CommandMenuItemAvailabilityType | null),hotKeys?: (Scalars['String'][] | null),conditionalAvailabilityExpression?: (Scalars['String'] | null),availabilityObjectMetadataId?: (Scalars['UUID'] | null)}
|
||||
|
||||
export interface UpdateCommandMenuItemInput {id: Scalars['UUID'],label?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),shortLabel?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),isPinned?: (Scalars['Boolean'] | null),availabilityType?: (CommandMenuItemAvailabilityType | null),availabilityObjectMetadataId?: (Scalars['UUID'] | null),engineComponentKey?: (EngineComponentKey | null),hotKeys?: (Scalars['String'][] | null)}
|
||||
|
||||
export interface CreateFrontComponentInput {id?: (Scalars['UUID'] | null),name: Scalars['String'],description?: (Scalars['String'] | null),sourceComponentPath: Scalars['String'],builtComponentPath: Scalars['String'],componentName: Scalars['String'],builtComponentChecksum: Scalars['String']}
|
||||
|
||||
export interface UpdateFrontComponentInput {
|
||||
/** The id of the front component to update */
|
||||
id: Scalars['UUID'],
|
||||
/** The front component fields to update */
|
||||
update: UpdateFrontComponentInputUpdates}
|
||||
|
||||
export interface UpdateFrontComponentInputUpdates {name?: (Scalars['String'] | null),description?: (Scalars['String'] | null)}
|
||||
|
||||
export interface CreateAgentInput {name?: (Scalars['String'] | null),label: Scalars['String'],icon?: (Scalars['String'] | null),description?: (Scalars['String'] | null),prompt: Scalars['String'],modelId: Scalars['String'],roleId?: (Scalars['UUID'] | null),responseFormat?: (Scalars['JSON'] | null),modelConfiguration?: (Scalars['JSON'] | null),evaluationInputs?: (Scalars['String'][] | null)}
|
||||
|
||||
export interface UpdateAgentInput {id: Scalars['UUID'],name?: (Scalars['String'] | null),label?: (Scalars['String'] | null),icon?: (Scalars['String'] | null),description?: (Scalars['String'] | null),prompt?: (Scalars['String'] | null),modelId?: (Scalars['String'] | null),roleId?: (Scalars['UUID'] | null),responseFormat?: (Scalars['JSON'] | null),modelConfiguration?: (Scalars['JSON'] | null),evaluationInputs?: (Scalars['String'][] | null)}
|
||||
|
||||
@@ -6135,6 +6135,30 @@ export default {
|
||||
]
|
||||
}
|
||||
],
|
||||
"commandMenuItems": [
|
||||
224
|
||||
],
|
||||
"commandMenuItem": [
|
||||
224,
|
||||
{
|
||||
"id": [
|
||||
3,
|
||||
"UUID!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"frontComponents": [
|
||||
223
|
||||
],
|
||||
"frontComponent": [
|
||||
223,
|
||||
{
|
||||
"id": [
|
||||
3,
|
||||
"UUID!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"objectRecordCounts": [
|
||||
167
|
||||
],
|
||||
@@ -6256,30 +6280,6 @@ export default {
|
||||
]
|
||||
}
|
||||
],
|
||||
"commandMenuItems": [
|
||||
224
|
||||
],
|
||||
"commandMenuItem": [
|
||||
224,
|
||||
{
|
||||
"id": [
|
||||
3,
|
||||
"UUID!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"frontComponents": [
|
||||
223
|
||||
],
|
||||
"frontComponent": [
|
||||
223,
|
||||
{
|
||||
"id": [
|
||||
3,
|
||||
"UUID!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"findManyAgents": [
|
||||
25
|
||||
],
|
||||
@@ -7324,11 +7324,65 @@ export default {
|
||||
]
|
||||
}
|
||||
],
|
||||
"createCommandMenuItem": [
|
||||
224,
|
||||
{
|
||||
"input": [
|
||||
381,
|
||||
"CreateCommandMenuItemInput!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"updateCommandMenuItem": [
|
||||
224,
|
||||
{
|
||||
"input": [
|
||||
382,
|
||||
"UpdateCommandMenuItemInput!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"deleteCommandMenuItem": [
|
||||
224,
|
||||
{
|
||||
"id": [
|
||||
3,
|
||||
"UUID!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"createFrontComponent": [
|
||||
223,
|
||||
{
|
||||
"input": [
|
||||
383,
|
||||
"CreateFrontComponentInput!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"updateFrontComponent": [
|
||||
223,
|
||||
{
|
||||
"input": [
|
||||
384,
|
||||
"UpdateFrontComponentInput!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"deleteFrontComponent": [
|
||||
223,
|
||||
{
|
||||
"id": [
|
||||
3,
|
||||
"UUID!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"createOneObject": [
|
||||
46,
|
||||
{
|
||||
"input": [
|
||||
381,
|
||||
386,
|
||||
"CreateOneObjectInput!"
|
||||
]
|
||||
}
|
||||
@@ -7337,7 +7391,7 @@ export default {
|
||||
46,
|
||||
{
|
||||
"input": [
|
||||
383,
|
||||
388,
|
||||
"DeleteOneObjectInput!"
|
||||
]
|
||||
}
|
||||
@@ -7346,7 +7400,7 @@ export default {
|
||||
46,
|
||||
{
|
||||
"input": [
|
||||
384,
|
||||
389,
|
||||
"UpdateOneObjectInput!"
|
||||
]
|
||||
}
|
||||
@@ -7355,7 +7409,7 @@ export default {
|
||||
50,
|
||||
{
|
||||
"input": [
|
||||
386,
|
||||
391,
|
||||
"UpdateViewFieldInput!"
|
||||
]
|
||||
}
|
||||
@@ -7364,7 +7418,7 @@ export default {
|
||||
50,
|
||||
{
|
||||
"input": [
|
||||
388,
|
||||
393,
|
||||
"CreateViewFieldInput!"
|
||||
]
|
||||
}
|
||||
@@ -7373,7 +7427,7 @@ export default {
|
||||
50,
|
||||
{
|
||||
"inputs": [
|
||||
388,
|
||||
393,
|
||||
"[CreateViewFieldInput!]!"
|
||||
]
|
||||
}
|
||||
@@ -7382,7 +7436,7 @@ export default {
|
||||
50,
|
||||
{
|
||||
"input": [
|
||||
389,
|
||||
394,
|
||||
"DeleteViewFieldInput!"
|
||||
]
|
||||
}
|
||||
@@ -7391,7 +7445,7 @@ export default {
|
||||
50,
|
||||
{
|
||||
"input": [
|
||||
390,
|
||||
395,
|
||||
"DestroyViewFieldInput!"
|
||||
]
|
||||
}
|
||||
@@ -7400,7 +7454,7 @@ export default {
|
||||
60,
|
||||
{
|
||||
"input": [
|
||||
391,
|
||||
396,
|
||||
"CreateViewInput!"
|
||||
]
|
||||
}
|
||||
@@ -7413,7 +7467,7 @@ export default {
|
||||
"String!"
|
||||
],
|
||||
"input": [
|
||||
392,
|
||||
397,
|
||||
"UpdateViewInput!"
|
||||
]
|
||||
}
|
||||
@@ -7440,7 +7494,7 @@ export default {
|
||||
57,
|
||||
{
|
||||
"input": [
|
||||
393,
|
||||
398,
|
||||
"CreateViewSortInput!"
|
||||
]
|
||||
}
|
||||
@@ -7449,7 +7503,7 @@ export default {
|
||||
57,
|
||||
{
|
||||
"input": [
|
||||
394,
|
||||
399,
|
||||
"UpdateViewSortInput!"
|
||||
]
|
||||
}
|
||||
@@ -7458,7 +7512,7 @@ export default {
|
||||
6,
|
||||
{
|
||||
"input": [
|
||||
396,
|
||||
401,
|
||||
"DeleteViewSortInput!"
|
||||
]
|
||||
}
|
||||
@@ -7467,7 +7521,7 @@ export default {
|
||||
6,
|
||||
{
|
||||
"input": [
|
||||
397,
|
||||
402,
|
||||
"DestroyViewSortInput!"
|
||||
]
|
||||
}
|
||||
@@ -7476,7 +7530,7 @@ export default {
|
||||
59,
|
||||
{
|
||||
"input": [
|
||||
398,
|
||||
403,
|
||||
"UpdateViewFieldGroupInput!"
|
||||
]
|
||||
}
|
||||
@@ -7485,7 +7539,7 @@ export default {
|
||||
59,
|
||||
{
|
||||
"input": [
|
||||
400,
|
||||
405,
|
||||
"CreateViewFieldGroupInput!"
|
||||
]
|
||||
}
|
||||
@@ -7494,7 +7548,7 @@ export default {
|
||||
59,
|
||||
{
|
||||
"inputs": [
|
||||
400,
|
||||
405,
|
||||
"[CreateViewFieldGroupInput!]!"
|
||||
]
|
||||
}
|
||||
@@ -7503,7 +7557,7 @@ export default {
|
||||
59,
|
||||
{
|
||||
"input": [
|
||||
401,
|
||||
406,
|
||||
"DeleteViewFieldGroupInput!"
|
||||
]
|
||||
}
|
||||
@@ -7512,71 +7566,17 @@ export default {
|
||||
59,
|
||||
{
|
||||
"input": [
|
||||
402,
|
||||
407,
|
||||
"DestroyViewFieldGroupInput!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"upsertFieldsWidget": [
|
||||
60,
|
||||
{
|
||||
"input": [
|
||||
403,
|
||||
"UpsertFieldsWidgetInput!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"createCommandMenuItem": [
|
||||
224,
|
||||
{
|
||||
"input": [
|
||||
406,
|
||||
"CreateCommandMenuItemInput!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"updateCommandMenuItem": [
|
||||
224,
|
||||
{
|
||||
"input": [
|
||||
407,
|
||||
"UpdateCommandMenuItemInput!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"deleteCommandMenuItem": [
|
||||
224,
|
||||
{
|
||||
"id": [
|
||||
3,
|
||||
"UUID!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"createFrontComponent": [
|
||||
223,
|
||||
{
|
||||
"input": [
|
||||
408,
|
||||
"CreateFrontComponentInput!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"updateFrontComponent": [
|
||||
223,
|
||||
{
|
||||
"input": [
|
||||
409,
|
||||
"UpdateFrontComponentInput!"
|
||||
]
|
||||
}
|
||||
],
|
||||
"deleteFrontComponent": [
|
||||
223,
|
||||
{
|
||||
"id": [
|
||||
3,
|
||||
"UUID!"
|
||||
"UpsertFieldsWidgetInput!"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -9477,9 +9477,133 @@ export default {
|
||||
1
|
||||
]
|
||||
},
|
||||
"CreateCommandMenuItemInput": {
|
||||
"workflowVersionId": [
|
||||
3
|
||||
],
|
||||
"frontComponentId": [
|
||||
3
|
||||
],
|
||||
"engineComponentKey": [
|
||||
225
|
||||
],
|
||||
"label": [
|
||||
1
|
||||
],
|
||||
"icon": [
|
||||
1
|
||||
],
|
||||
"shortLabel": [
|
||||
1
|
||||
],
|
||||
"position": [
|
||||
11
|
||||
],
|
||||
"isPinned": [
|
||||
6
|
||||
],
|
||||
"availabilityType": [
|
||||
226
|
||||
],
|
||||
"hotKeys": [
|
||||
1
|
||||
],
|
||||
"conditionalAvailabilityExpression": [
|
||||
1
|
||||
],
|
||||
"availabilityObjectMetadataId": [
|
||||
3
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"UpdateCommandMenuItemInput": {
|
||||
"id": [
|
||||
3
|
||||
],
|
||||
"label": [
|
||||
1
|
||||
],
|
||||
"icon": [
|
||||
1
|
||||
],
|
||||
"shortLabel": [
|
||||
1
|
||||
],
|
||||
"position": [
|
||||
11
|
||||
],
|
||||
"isPinned": [
|
||||
6
|
||||
],
|
||||
"availabilityType": [
|
||||
226
|
||||
],
|
||||
"availabilityObjectMetadataId": [
|
||||
3
|
||||
],
|
||||
"engineComponentKey": [
|
||||
225
|
||||
],
|
||||
"hotKeys": [
|
||||
1
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"CreateFrontComponentInput": {
|
||||
"id": [
|
||||
3
|
||||
],
|
||||
"name": [
|
||||
1
|
||||
],
|
||||
"description": [
|
||||
1
|
||||
],
|
||||
"sourceComponentPath": [
|
||||
1
|
||||
],
|
||||
"builtComponentPath": [
|
||||
1
|
||||
],
|
||||
"componentName": [
|
||||
1
|
||||
],
|
||||
"builtComponentChecksum": [
|
||||
1
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"UpdateFrontComponentInput": {
|
||||
"id": [
|
||||
3
|
||||
],
|
||||
"update": [
|
||||
385
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"UpdateFrontComponentInputUpdates": {
|
||||
"name": [
|
||||
1
|
||||
],
|
||||
"description": [
|
||||
1
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"CreateOneObjectInput": {
|
||||
"object": [
|
||||
382
|
||||
387
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -9539,7 +9663,7 @@ export default {
|
||||
},
|
||||
"UpdateOneObjectInput": {
|
||||
"update": [
|
||||
385
|
||||
390
|
||||
],
|
||||
"id": [
|
||||
3
|
||||
@@ -9594,7 +9718,7 @@ export default {
|
||||
3
|
||||
],
|
||||
"update": [
|
||||
387
|
||||
392
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -9793,7 +9917,7 @@ export default {
|
||||
3
|
||||
],
|
||||
"update": [
|
||||
395
|
||||
400
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -9828,7 +9952,7 @@ export default {
|
||||
3
|
||||
],
|
||||
"update": [
|
||||
399
|
||||
404
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -9892,10 +10016,10 @@ export default {
|
||||
3
|
||||
],
|
||||
"groups": [
|
||||
404
|
||||
409
|
||||
],
|
||||
"fields": [
|
||||
405
|
||||
410
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -9915,7 +10039,7 @@ export default {
|
||||
6
|
||||
],
|
||||
"fields": [
|
||||
405
|
||||
410
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
@@ -9935,130 +10059,6 @@ export default {
|
||||
1
|
||||
]
|
||||
},
|
||||
"CreateCommandMenuItemInput": {
|
||||
"workflowVersionId": [
|
||||
3
|
||||
],
|
||||
"frontComponentId": [
|
||||
3
|
||||
],
|
||||
"engineComponentKey": [
|
||||
225
|
||||
],
|
||||
"label": [
|
||||
1
|
||||
],
|
||||
"icon": [
|
||||
1
|
||||
],
|
||||
"shortLabel": [
|
||||
1
|
||||
],
|
||||
"position": [
|
||||
11
|
||||
],
|
||||
"isPinned": [
|
||||
6
|
||||
],
|
||||
"availabilityType": [
|
||||
226
|
||||
],
|
||||
"hotKeys": [
|
||||
1
|
||||
],
|
||||
"conditionalAvailabilityExpression": [
|
||||
1
|
||||
],
|
||||
"availabilityObjectMetadataId": [
|
||||
3
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"UpdateCommandMenuItemInput": {
|
||||
"id": [
|
||||
3
|
||||
],
|
||||
"label": [
|
||||
1
|
||||
],
|
||||
"icon": [
|
||||
1
|
||||
],
|
||||
"shortLabel": [
|
||||
1
|
||||
],
|
||||
"position": [
|
||||
11
|
||||
],
|
||||
"isPinned": [
|
||||
6
|
||||
],
|
||||
"availabilityType": [
|
||||
226
|
||||
],
|
||||
"availabilityObjectMetadataId": [
|
||||
3
|
||||
],
|
||||
"engineComponentKey": [
|
||||
225
|
||||
],
|
||||
"hotKeys": [
|
||||
1
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"CreateFrontComponentInput": {
|
||||
"id": [
|
||||
3
|
||||
],
|
||||
"name": [
|
||||
1
|
||||
],
|
||||
"description": [
|
||||
1
|
||||
],
|
||||
"sourceComponentPath": [
|
||||
1
|
||||
],
|
||||
"builtComponentPath": [
|
||||
1
|
||||
],
|
||||
"componentName": [
|
||||
1
|
||||
],
|
||||
"builtComponentChecksum": [
|
||||
1
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"UpdateFrontComponentInput": {
|
||||
"id": [
|
||||
3
|
||||
],
|
||||
"update": [
|
||||
410
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"UpdateFrontComponentInputUpdates": {
|
||||
"name": [
|
||||
1
|
||||
],
|
||||
"description": [
|
||||
1
|
||||
],
|
||||
"__typename": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"CreateAgentInput": {
|
||||
"name": [
|
||||
1
|
||||
|
||||
@@ -57,6 +57,7 @@ import { prefillCompanies } from 'src/engine/workspace-manager/standard-objects-
|
||||
import { prefillDashboards } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-dashboards';
|
||||
import { prefillOpportunities } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-opportunities';
|
||||
import { prefillPeople } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-people';
|
||||
import { prefillWorkflowCommandMenuItems } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflow-command-menu-items';
|
||||
import { prefillWorkflows } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows';
|
||||
import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service';
|
||||
import { DEFAULT_FEATURE_FLAGS } from 'src/engine/workspace-manager/workspace-migration/constant/default-feature-flags';
|
||||
@@ -698,6 +699,8 @@ export class WorkspaceService extends TypeOrmQueryService<WorkspaceEntity> {
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
await prefillWorkflowCommandMenuItems(queryRunner.manager, workspaceId);
|
||||
|
||||
await prefillOpportunities(queryRunner.manager, schemaName);
|
||||
|
||||
await prefillDashboards(
|
||||
|
||||
+11
-1
@@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type QueryRunner } from 'typeorm';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import {
|
||||
@@ -87,6 +88,7 @@ export class CommandMenuItemService {
|
||||
async create(
|
||||
input: CreateCommandMenuItemInput,
|
||||
workspaceId: string,
|
||||
queryRunner?: QueryRunner,
|
||||
): Promise<CommandMenuItemDTO> {
|
||||
const { flatObjectMetadataMaps, flatFrontComponentMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
@@ -124,6 +126,7 @@ export class CommandMenuItemService {
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
queryRunner,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -153,6 +156,7 @@ export class CommandMenuItemService {
|
||||
async update(
|
||||
input: UpdateCommandMenuItemInput,
|
||||
workspaceId: string,
|
||||
queryRunner?: QueryRunner,
|
||||
): Promise<CommandMenuItemDTO> {
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
@@ -191,6 +195,7 @@ export class CommandMenuItemService {
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
queryRunner,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -217,7 +222,11 @@ export class CommandMenuItemService {
|
||||
);
|
||||
}
|
||||
|
||||
async delete(id: string, workspaceId: string): Promise<CommandMenuItemDTO> {
|
||||
async delete(
|
||||
id: string,
|
||||
workspaceId: string,
|
||||
queryRunner?: QueryRunner,
|
||||
): Promise<CommandMenuItemDTO> {
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
@@ -251,6 +260,7 @@ export class CommandMenuItemService {
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
queryRunner,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ export const seedFeatureFlags = async ({
|
||||
{
|
||||
key: FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED,
|
||||
workspaceId: workspaceId,
|
||||
value: false,
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
key: FeatureFlagKey.IS_DATE_TIME_WHOLE_DAY_FILTER_ENABLED,
|
||||
|
||||
+3
@@ -121,6 +121,7 @@ import {
|
||||
WORKSPACE_MEMBER_DATA_SEED_COLUMNS,
|
||||
} from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant';
|
||||
import { TimelineActivitySeederService } from 'src/engine/workspace-manager/dev-seeder/data/services/timeline-activity-seeder.service';
|
||||
import { prefillWorkflowCommandMenuItems } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflow-command-menu-items';
|
||||
import { prefillWorkflows } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
|
||||
@@ -362,6 +363,8 @@ export class DevSeederDataService {
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
await prefillWorkflowCommandMenuItems(entityManager, workspaceId);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import { type EntityManager } from 'typeorm';
|
||||
|
||||
import { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
|
||||
import { QUICK_LEAD_WORKFLOW_VERSION_ID } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
|
||||
const QUICK_LEAD_COMMAND_MENU_ITEM_UNIVERSAL_IDENTIFIER =
|
||||
'a1b2c3d4-e5f6-7890-abcd-1234567890ab';
|
||||
|
||||
export const prefillWorkflowCommandMenuItems = async (
|
||||
entityManager: EntityManager,
|
||||
workspaceId: string,
|
||||
) => {
|
||||
const applicationRow = await entityManager
|
||||
.createQueryBuilder()
|
||||
.select('app.id')
|
||||
.from('core.application', 'app')
|
||||
.where('app.universalIdentifier = :universalIdentifier', {
|
||||
universalIdentifier: TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
})
|
||||
.andWhere('app.workspaceId = :workspaceId', { workspaceId })
|
||||
.getRawOne();
|
||||
|
||||
if (!applicationRow) {
|
||||
return;
|
||||
}
|
||||
|
||||
await entityManager
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into('core.commandMenuItem', [
|
||||
'workspaceId',
|
||||
'universalIdentifier',
|
||||
'applicationId',
|
||||
'workflowVersionId',
|
||||
'frontComponentId',
|
||||
'engineComponentKey',
|
||||
'label',
|
||||
'icon',
|
||||
'shortLabel',
|
||||
'position',
|
||||
'isPinned',
|
||||
'availabilityType',
|
||||
'conditionalAvailabilityExpression',
|
||||
'availabilityObjectMetadataId',
|
||||
'hotKeys',
|
||||
])
|
||||
.values([
|
||||
{
|
||||
workspaceId,
|
||||
universalIdentifier: QUICK_LEAD_COMMAND_MENU_ITEM_UNIVERSAL_IDENTIFIER,
|
||||
applicationId: applicationRow.app_id,
|
||||
workflowVersionId: QUICK_LEAD_WORKFLOW_VERSION_ID,
|
||||
frontComponentId: null,
|
||||
engineComponentKey: null,
|
||||
label: 'Quick Lead',
|
||||
icon: 'IconUserPlus',
|
||||
shortLabel: 'Quick Lead',
|
||||
position: 100,
|
||||
isPinned: false,
|
||||
availabilityType: CommandMenuItemAvailabilityType.GLOBAL,
|
||||
conditionalAvailabilityExpression: null,
|
||||
availabilityObjectMetadataId: null,
|
||||
hotKeys: null,
|
||||
},
|
||||
])
|
||||
.orIgnore()
|
||||
.execute();
|
||||
};
|
||||
+3
-2
@@ -9,8 +9,9 @@ import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object
|
||||
import { buildObjectIdByNameMaps } from 'src/engine/metadata-modules/flat-object-metadata/utils/build-object-id-by-name-maps.util';
|
||||
import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields';
|
||||
|
||||
const QUICK_LEAD_WORKFLOW_ID = '8b213cac-a68b-4ffe-817a-3ec994e9932d';
|
||||
const QUICK_LEAD_WORKFLOW_VERSION_ID = 'ac67974f-c524-4288-9d88-af8515400b68';
|
||||
export const QUICK_LEAD_WORKFLOW_ID = '8b213cac-a68b-4ffe-817a-3ec994e9932d';
|
||||
export const QUICK_LEAD_WORKFLOW_VERSION_ID =
|
||||
'ac67974f-c524-4288-9d88-af8515400b68';
|
||||
|
||||
export const prefillWorkflows = async (
|
||||
entityManager: EntityManager,
|
||||
|
||||
+4
@@ -5,11 +5,13 @@ import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-m
|
||||
import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata.type';
|
||||
import { prefillCompanies } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-companies';
|
||||
import { prefillPeople } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-people';
|
||||
import { prefillWorkflowCommandMenuItems } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflow-command-menu-items';
|
||||
import { prefillWorkflows } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows';
|
||||
|
||||
export const standardObjectsPrefillData = async (
|
||||
dataSource: DataSource,
|
||||
schemaName: string,
|
||||
workspaceId: string,
|
||||
flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>,
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>,
|
||||
) => {
|
||||
@@ -24,5 +26,7 @@ export const standardObjectsPrefillData = async (
|
||||
flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps,
|
||||
);
|
||||
|
||||
await prefillWorkflowCommandMenuItems(entityManager, workspaceId);
|
||||
});
|
||||
};
|
||||
|
||||
+13
-2
@@ -5,6 +5,7 @@ import {
|
||||
WorkspaceMigrationV2ExceptionCode,
|
||||
} from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type QueryRunner } from 'typeorm';
|
||||
|
||||
import { FlatApplicationCacheMaps } from 'src/engine/core-modules/application/types/flat-application-cache-maps.type';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
@@ -358,12 +359,17 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
public async validateBuildAndRunWorkspaceMigrationFromTo(
|
||||
args: WorkspaceMigrationOrchestratorBuildArgs & {
|
||||
idByUniversalIdentifierByMetadataName?: IdByUniversalIdentifierByMetadataName;
|
||||
queryRunner?: QueryRunner;
|
||||
},
|
||||
): Promise<
|
||||
| WorkspaceMigrationOrchestratorFailedResult
|
||||
| WorkspaceMigrationOrchestratorSuccessfulResult
|
||||
> {
|
||||
const { idByUniversalIdentifierByMetadataName, ...buildArgs } = args;
|
||||
const {
|
||||
idByUniversalIdentifierByMetadataName,
|
||||
queryRunner: externalQueryRunner,
|
||||
...buildArgs
|
||||
} = args;
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationBuildOrchestratorService
|
||||
@@ -396,6 +402,7 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
{
|
||||
workspaceId: args.workspaceId,
|
||||
workspaceMigration,
|
||||
queryRunner: externalQueryRunner,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -416,7 +423,10 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
workspaceId,
|
||||
isSystemBuild = false,
|
||||
applicationUniversalIdentifier,
|
||||
}: ValidateBuildAndRunWorkspaceMigrationFromMatriceArgs): Promise<
|
||||
queryRunner,
|
||||
}: ValidateBuildAndRunWorkspaceMigrationFromMatriceArgs & {
|
||||
queryRunner?: QueryRunner;
|
||||
}): Promise<
|
||||
| WorkspaceMigrationOrchestratorFailedResult
|
||||
| WorkspaceMigrationOrchestratorSuccessfulResult
|
||||
> {
|
||||
@@ -443,6 +453,7 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
dependencyAllFlatEntityMaps,
|
||||
additionalCacheDataMaps,
|
||||
idByUniversalIdentifierByMetadataName,
|
||||
queryRunner,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+21
-8
@@ -3,7 +3,7 @@ import { InjectDataSource } from '@nestjs/typeorm';
|
||||
|
||||
import { type AllMetadataName } from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { DataSource, type QueryRunner } from 'typeorm';
|
||||
|
||||
import { LoggerService } from 'src/engine/core-modules/logger/logger.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
@@ -154,9 +154,11 @@ export class WorkspaceMigrationRunnerService {
|
||||
run = async ({
|
||||
workspaceMigration: { actions, applicationUniversalIdentifier },
|
||||
workspaceId,
|
||||
queryRunner: externalQueryRunner,
|
||||
}: {
|
||||
workspaceMigration: WorkspaceMigration;
|
||||
workspaceId: string;
|
||||
queryRunner?: QueryRunner;
|
||||
}): Promise<{
|
||||
allFlatEntityMaps: AllFlatEntityMaps;
|
||||
metadataEvents: MetadataEvent[];
|
||||
@@ -164,7 +166,10 @@ export class WorkspaceMigrationRunnerService {
|
||||
this.logger.time('Runner', 'Total execution');
|
||||
this.logger.time('Runner', 'Initial cache retrieval');
|
||||
|
||||
const queryRunner = this.coreDataSource.createQueryRunner();
|
||||
const queryRunner =
|
||||
externalQueryRunner ?? this.coreDataSource.createQueryRunner();
|
||||
const isTransactionAlreadyActive = queryRunner.isTransactionActive;
|
||||
|
||||
const actionMetadataNames = [
|
||||
...new Set(actions.flatMap((action) => action.metadataName)),
|
||||
];
|
||||
@@ -214,10 +219,12 @@ export class WorkspaceMigrationRunnerService {
|
||||
|
||||
this.logger.time('Runner', 'Transaction execution');
|
||||
|
||||
try {
|
||||
if (!isTransactionAlreadyActive) {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
}
|
||||
|
||||
try {
|
||||
const allMetadataEvents: MetadataEvent[] = [];
|
||||
|
||||
for (const action of actions) {
|
||||
@@ -243,7 +250,9 @@ export class WorkspaceMigrationRunnerService {
|
||||
allMetadataEvents.push(...metadataEvents);
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
if (!isTransactionAlreadyActive) {
|
||||
await queryRunner.commitTransaction();
|
||||
}
|
||||
|
||||
this.logger.timeEnd('Runner', 'Transaction execution');
|
||||
|
||||
@@ -256,10 +265,12 @@ export class WorkspaceMigrationRunnerService {
|
||||
|
||||
return { allFlatEntityMaps, metadataEvents: allMetadataEvents };
|
||||
} catch (error) {
|
||||
if (queryRunner.isTransactionActive) {
|
||||
await queryRunner.rollbackTransaction().catch((error) =>
|
||||
if (!isTransactionAlreadyActive && queryRunner.isTransactionActive) {
|
||||
await queryRunner.rollbackTransaction().catch((rollbackError) =>
|
||||
// oxlint-disable-next-line no-console
|
||||
console.trace(`Failed to rollback transaction: ${error.message}`),
|
||||
console.trace(
|
||||
`Failed to rollback transaction: ${rollbackError.message}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -288,7 +299,9 @@ export class WorkspaceMigrationRunnerService {
|
||||
code: WorkspaceMigrationRunnerExceptionCode.INTERNAL_SERVER_ERROR,
|
||||
});
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
if (!isTransactionAlreadyActive) {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+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({
|
||||
|
||||
+36
-12
@@ -1,5 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
import {
|
||||
@@ -19,21 +21,32 @@ export class AutomatedTriggerWorkspaceService {
|
||||
type,
|
||||
settings,
|
||||
workspaceId,
|
||||
entityManager,
|
||||
}: {
|
||||
workflowId: string;
|
||||
type: AutomatedTriggerType;
|
||||
settings: AutomatedTriggerSettings;
|
||||
workspaceId: string;
|
||||
entityManager?: WorkspaceEntityManager;
|
||||
}) {
|
||||
const workflowAutomatedTriggerRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkflowAutomatedTriggerWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowAutomatedTrigger',
|
||||
);
|
||||
|
||||
if (isDefined(entityManager)) {
|
||||
await workflowAutomatedTriggerRepository.insert(
|
||||
{ type, settings, workflowId },
|
||||
entityManager,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => {
|
||||
const workflowAutomatedTriggerRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkflowAutomatedTriggerWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowAutomatedTrigger',
|
||||
);
|
||||
|
||||
await workflowAutomatedTriggerRepository.insert({
|
||||
type,
|
||||
settings,
|
||||
@@ -45,19 +58,30 @@ export class AutomatedTriggerWorkspaceService {
|
||||
async deleteAutomatedTrigger({
|
||||
workflowId,
|
||||
workspaceId,
|
||||
entityManager,
|
||||
}: {
|
||||
workflowId: string;
|
||||
workspaceId: string;
|
||||
entityManager?: WorkspaceEntityManager;
|
||||
}) {
|
||||
const workflowAutomatedTriggerRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkflowAutomatedTriggerWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowAutomatedTrigger',
|
||||
);
|
||||
|
||||
if (isDefined(entityManager)) {
|
||||
await workflowAutomatedTriggerRepository.delete(
|
||||
{ workflowId },
|
||||
entityManager,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const authContext = buildSystemAuthContext(workspaceId);
|
||||
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(async () => {
|
||||
const workflowAutomatedTriggerRepository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<WorkflowAutomatedTriggerWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workflowAutomatedTrigger',
|
||||
);
|
||||
|
||||
await workflowAutomatedTriggerRepository.delete({ workflowId });
|
||||
}, authContext);
|
||||
}
|
||||
|
||||
+1
@@ -40,6 +40,7 @@ export type WorkflowManualTrigger = BaseTrigger & {
|
||||
settings: BaseWorkflowTriggerSettings & {
|
||||
objectType?: string;
|
||||
icon?: string;
|
||||
isPinned?: boolean;
|
||||
availability?:
|
||||
| GlobalAvailability
|
||||
| SingleRecordAvailability
|
||||
|
||||
+260
-184
@@ -1,5 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { type ActorMetadata, FeatureFlagKey } from 'twenty-shared/types';
|
||||
@@ -7,6 +6,7 @@ import { type ActorMetadata, FeatureFlagKey } from 'twenty-shared/types';
|
||||
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 { CommandMenuItemAvailabilityType } from 'src/engine/metadata-modules/command-menu-item/enums/command-menu-item-availability-type.enum';
|
||||
import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
@@ -39,6 +39,8 @@ import { assertNever } from 'src/utils/assert';
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowTriggerWorkspaceService {
|
||||
private readonly logger = new Logger(WorkflowTriggerWorkspaceService.name);
|
||||
|
||||
constructor(
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
||||
@@ -183,31 +185,96 @@ export class WorkflowTriggerWorkspaceService {
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const previousPublishedVersionId = workflow.lastPublishedVersionId;
|
||||
|
||||
if (
|
||||
workflow.lastPublishedVersionId &&
|
||||
workflowVersion.id !== workflow.lastPublishedVersionId
|
||||
previousPublishedVersionId &&
|
||||
workflowVersion.id !== previousPublishedVersionId
|
||||
) {
|
||||
await this.performDeactivationSteps(
|
||||
workflow.lastPublishedVersionId,
|
||||
previousPublishedVersionId,
|
||||
workflowVersionRepository,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
await this.upgradeWorkflowVersion(
|
||||
await this.createOrUpdateCommandMenuItem(
|
||||
workflow,
|
||||
workflowVersion.id,
|
||||
workflowRepository,
|
||||
workflowVersionRepository,
|
||||
);
|
||||
|
||||
await this.setActiveVersionStatus(
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
await this.enableTrigger(workflow, workflowVersion, workspaceId);
|
||||
const workspaceDataSource =
|
||||
await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource();
|
||||
|
||||
const queryRunner = workspaceDataSource.createQueryRunner();
|
||||
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
if (workflow.lastPublishedVersionId !== workflowVersion.id) {
|
||||
if (workflow.lastPublishedVersionId) {
|
||||
await workflowVersionRepository.update(
|
||||
{ id: workflow.lastPublishedVersionId },
|
||||
{ status: WorkflowVersionStatus.ARCHIVED },
|
||||
queryRunner.manager,
|
||||
);
|
||||
}
|
||||
|
||||
await workflowRepository.update(
|
||||
{ id: workflow.id },
|
||||
{ lastPublishedVersionId: workflowVersion.id },
|
||||
queryRunner.manager,
|
||||
);
|
||||
}
|
||||
|
||||
const activeWorkflowVersions = await workflowVersionRepository.find(
|
||||
{
|
||||
where: {
|
||||
workflowId: workflowVersion.workflowId,
|
||||
status: WorkflowVersionStatus.ACTIVE,
|
||||
},
|
||||
},
|
||||
queryRunner.manager,
|
||||
);
|
||||
|
||||
if (activeWorkflowVersions.length > 0) {
|
||||
throw new WorkflowTriggerException(
|
||||
'Cannot have more than one active workflow version',
|
||||
WorkflowTriggerExceptionCode.FORBIDDEN,
|
||||
{
|
||||
userFriendlyMessage: msg`Cannot have more than one active workflow version`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await workflowVersionRepository.update(
|
||||
{ id: workflowVersion.id },
|
||||
{ status: WorkflowVersionStatus.ACTIVE },
|
||||
queryRunner.manager,
|
||||
);
|
||||
|
||||
await this.enableAutomatedTrigger(workflowVersion, workspaceId, {
|
||||
entityManager: queryRunner.manager,
|
||||
});
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (error) {
|
||||
if (queryRunner.isTransactionActive) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
}
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
|
||||
await this.emitStatusUpdateEvents(
|
||||
workflowVersion,
|
||||
WorkflowVersionStatus.ACTIVE,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
private async performDeactivationSteps(
|
||||
@@ -228,69 +295,38 @@ export class WorkflowTriggerWorkspaceService {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.setDeactivatedVersionStatus(
|
||||
workflowVersion,
|
||||
workflowVersionRepository,
|
||||
workspaceId,
|
||||
);
|
||||
await this.deleteCommandMenuItem(workflowVersion, workspaceId);
|
||||
|
||||
await this.disableTrigger(workflowVersion, workspaceId);
|
||||
}
|
||||
const workspaceDataSource =
|
||||
await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSource();
|
||||
|
||||
private async setActiveVersionStatus(
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity,
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const activeWorkflowVersions = await workflowVersionRepository.find({
|
||||
where: {
|
||||
workflowId: workflowVersion.workflowId,
|
||||
status: WorkflowVersionStatus.ACTIVE,
|
||||
},
|
||||
});
|
||||
const queryRunner = workspaceDataSource.createQueryRunner();
|
||||
|
||||
if (activeWorkflowVersions.length > 0) {
|
||||
throw new WorkflowTriggerException(
|
||||
'Cannot have more than one active workflow version',
|
||||
WorkflowTriggerExceptionCode.FORBIDDEN,
|
||||
{
|
||||
userFriendlyMessage: msg`Cannot have more than one active workflow version`,
|
||||
},
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
await workflowVersionRepository.update(
|
||||
{ id: workflowVersion.id },
|
||||
{ status: WorkflowVersionStatus.DEACTIVATED },
|
||||
queryRunner.manager,
|
||||
);
|
||||
|
||||
await this.disableAutomatedTrigger(workflowVersion, workspaceId, {
|
||||
entityManager: queryRunner.manager,
|
||||
});
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (error) {
|
||||
if (queryRunner.isTransactionActive) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
}
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
|
||||
await workflowVersionRepository.update(
|
||||
{ id: workflowVersion.id },
|
||||
{ status: WorkflowVersionStatus.ACTIVE },
|
||||
);
|
||||
|
||||
await this.emitStatusUpdateEvents(
|
||||
workflowVersion,
|
||||
WorkflowVersionStatus.ACTIVE,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
private async setDeactivatedVersionStatus(
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity,
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>,
|
||||
workspaceId: string,
|
||||
) {
|
||||
if (workflowVersion.status !== WorkflowVersionStatus.ACTIVE) {
|
||||
throw new WorkflowTriggerException(
|
||||
'Cannot disable non-active workflow version',
|
||||
WorkflowTriggerExceptionCode.FORBIDDEN,
|
||||
{
|
||||
userFriendlyMessage: msg`Cannot disable non-active workflow version`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await workflowVersionRepository.update(
|
||||
{ id: workflowVersion.id },
|
||||
{ status: WorkflowVersionStatus.DEACTIVATED },
|
||||
);
|
||||
|
||||
await this.emitStatusUpdateEvents(
|
||||
workflowVersion,
|
||||
WorkflowVersionStatus.DEACTIVATED,
|
||||
@@ -298,101 +334,161 @@ export class WorkflowTriggerWorkspaceService {
|
||||
);
|
||||
}
|
||||
|
||||
private async upgradeWorkflowVersion(
|
||||
workflow: WorkflowWorkspaceEntity,
|
||||
newPublishedVersionId: string,
|
||||
workflowRepository: WorkspaceRepository<WorkflowWorkspaceEntity>,
|
||||
workflowVersionRepository: WorkspaceRepository<WorkflowVersionWorkspaceEntity>,
|
||||
) {
|
||||
if (workflow.lastPublishedVersionId === newPublishedVersionId) {
|
||||
return;
|
||||
private async resolveManualTriggerAvailability(
|
||||
trigger: WorkflowManualTrigger,
|
||||
workspaceId: string,
|
||||
): Promise<{
|
||||
availabilityType: CommandMenuItemAvailabilityType;
|
||||
availabilityObjectMetadataId: string | undefined;
|
||||
}> {
|
||||
const availability = trigger.settings.availability;
|
||||
|
||||
let availabilityType = CommandMenuItemAvailabilityType.GLOBAL;
|
||||
let availabilityObjectMetadataId: string | undefined;
|
||||
|
||||
if (availability) {
|
||||
switch (availability.type) {
|
||||
case 'GLOBAL':
|
||||
availabilityType = CommandMenuItemAvailabilityType.GLOBAL;
|
||||
break;
|
||||
case 'SINGLE_RECORD':
|
||||
case 'BULK_RECORDS': {
|
||||
availabilityType = CommandMenuItemAvailabilityType.RECORD_SELECTION;
|
||||
|
||||
const { objectIdByNameSingular } =
|
||||
await this.workflowCommonWorkspaceService.getFlatEntityMaps(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const objectId =
|
||||
objectIdByNameSingular[availability.objectNameSingular];
|
||||
|
||||
if (!objectId) {
|
||||
throw new WorkflowTriggerException(
|
||||
`Object metadata not found for object: ${availability.objectNameSingular}`,
|
||||
WorkflowTriggerExceptionCode.INVALID_WORKFLOW_VERSION,
|
||||
);
|
||||
}
|
||||
|
||||
availabilityObjectMetadataId = objectId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (workflow.lastPublishedVersionId) {
|
||||
await workflowVersionRepository.update(
|
||||
{ id: workflow.lastPublishedVersionId },
|
||||
{ status: WorkflowVersionStatus.ARCHIVED },
|
||||
);
|
||||
}
|
||||
|
||||
await workflowRepository.update(
|
||||
{ id: workflow.id },
|
||||
{ lastPublishedVersionId: newPublishedVersionId },
|
||||
);
|
||||
return { availabilityType, availabilityObjectMetadataId };
|
||||
}
|
||||
|
||||
private async enableTrigger(
|
||||
private async createOrUpdateCommandMenuItem(
|
||||
workflow: WorkflowWorkspaceEntity,
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity,
|
||||
workspaceId: string,
|
||||
) {
|
||||
assertWorkflowVersionTriggerIsDefined(workflowVersion);
|
||||
|
||||
if (workflowVersion.trigger.type !== WorkflowTriggerType.MANUAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isCommandMenuItemEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!isCommandMenuItemEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trigger = workflowVersion.trigger as WorkflowManualTrigger;
|
||||
|
||||
const { availabilityType, availabilityObjectMetadataId } =
|
||||
await this.resolveManualTriggerAvailability(trigger, workspaceId);
|
||||
|
||||
const label = isNonEmptyString(workflow.name)
|
||||
? workflow.name
|
||||
: 'Manual Trigger';
|
||||
|
||||
const existingCommandMenuItem =
|
||||
await this.commandMenuItemService.findByWorkflowVersionId(
|
||||
workflowVersion.id,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (existingCommandMenuItem) {
|
||||
await this.commandMenuItemService.update(
|
||||
{
|
||||
id: existingCommandMenuItem.id,
|
||||
label,
|
||||
shortLabel: label,
|
||||
icon: trigger.settings.icon,
|
||||
isPinned: trigger.settings.isPinned,
|
||||
availabilityType,
|
||||
availabilityObjectMetadataId,
|
||||
},
|
||||
workspaceId,
|
||||
);
|
||||
} else {
|
||||
await this.commandMenuItemService.create(
|
||||
{
|
||||
workflowVersionId: workflowVersion.id,
|
||||
label,
|
||||
shortLabel: label,
|
||||
icon: trigger.settings.icon,
|
||||
isPinned: trigger.settings.isPinned,
|
||||
availabilityType,
|
||||
availabilityObjectMetadataId,
|
||||
},
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async deleteCommandMenuItem(
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity,
|
||||
workspaceId: string,
|
||||
) {
|
||||
assertWorkflowVersionTriggerIsDefined(workflowVersion);
|
||||
|
||||
if (workflowVersion.trigger.type !== WorkflowTriggerType.MANUAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isCommandMenuItemEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!isCommandMenuItemEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const existingCommandMenuItem =
|
||||
await this.commandMenuItemService.findByWorkflowVersionId(
|
||||
workflowVersion.id,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (existingCommandMenuItem) {
|
||||
await this.commandMenuItemService.delete(
|
||||
existingCommandMenuItem.id,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async enableAutomatedTrigger(
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity,
|
||||
workspaceId: string,
|
||||
transactionContext?: {
|
||||
entityManager: WorkspaceEntityManager;
|
||||
},
|
||||
) {
|
||||
assertWorkflowVersionTriggerIsDefined(workflowVersion);
|
||||
|
||||
switch (workflowVersion.trigger.type) {
|
||||
case WorkflowTriggerType.MANUAL: {
|
||||
const isCommandMenuItemEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!isCommandMenuItemEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trigger = workflowVersion.trigger as WorkflowManualTrigger;
|
||||
|
||||
const availability = trigger.settings.availability;
|
||||
|
||||
let availabilityType = CommandMenuItemAvailabilityType.GLOBAL;
|
||||
let availabilityObjectMetadataId: string | undefined;
|
||||
|
||||
if (availability) {
|
||||
switch (availability.type) {
|
||||
case 'GLOBAL':
|
||||
availabilityType = CommandMenuItemAvailabilityType.GLOBAL;
|
||||
break;
|
||||
case 'SINGLE_RECORD':
|
||||
case 'BULK_RECORDS': {
|
||||
availabilityType =
|
||||
CommandMenuItemAvailabilityType.RECORD_SELECTION;
|
||||
|
||||
const { objectIdByNameSingular } =
|
||||
await this.workflowCommonWorkspaceService.getFlatEntityMaps(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const objectId =
|
||||
objectIdByNameSingular[availability.objectNameSingular];
|
||||
|
||||
if (!objectId) {
|
||||
throw new WorkflowTriggerException(
|
||||
`Object metadata not found for object: ${availability.objectNameSingular}`,
|
||||
WorkflowTriggerExceptionCode.INVALID_WORKFLOW_VERSION,
|
||||
);
|
||||
}
|
||||
|
||||
availabilityObjectMetadataId = objectId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this.commandMenuItemService.create(
|
||||
{
|
||||
workflowVersionId: workflowVersion.id,
|
||||
label: isNonEmptyString(workflow.name)
|
||||
? workflow.name
|
||||
: 'Manual Trigger',
|
||||
icon: trigger.settings.icon,
|
||||
availabilityType,
|
||||
availabilityObjectMetadataId,
|
||||
},
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
case WorkflowTriggerType.MANUAL:
|
||||
case WorkflowTriggerType.WEBHOOK:
|
||||
return;
|
||||
case WorkflowTriggerType.DATABASE_EVENT: {
|
||||
@@ -404,6 +500,7 @@ export class WorkflowTriggerWorkspaceService {
|
||||
type: AutomatedTriggerType.DATABASE_EVENT,
|
||||
settings,
|
||||
workspaceId,
|
||||
entityManager: transactionContext?.entityManager,
|
||||
});
|
||||
|
||||
return;
|
||||
@@ -416,19 +513,22 @@ export class WorkflowTriggerWorkspaceService {
|
||||
type: AutomatedTriggerType.CRON,
|
||||
settings: { pattern },
|
||||
workspaceId,
|
||||
entityManager: transactionContext?.entityManager,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
default:
|
||||
assertNever(workflowVersion.trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async disableTrigger(
|
||||
private async disableAutomatedTrigger(
|
||||
workflowVersion: WorkflowVersionWorkspaceEntity,
|
||||
workspaceId: string,
|
||||
transactionContext?: {
|
||||
entityManager: WorkspaceEntityManager;
|
||||
},
|
||||
) {
|
||||
assertWorkflowVersionTriggerIsDefined(workflowVersion);
|
||||
|
||||
@@ -438,35 +538,11 @@ export class WorkflowTriggerWorkspaceService {
|
||||
await this.automatedTriggerWorkspaceService.deleteAutomatedTrigger({
|
||||
workflowId: workflowVersion.workflowId,
|
||||
workspaceId,
|
||||
entityManager: transactionContext?.entityManager,
|
||||
});
|
||||
|
||||
return;
|
||||
case WorkflowTriggerType.MANUAL: {
|
||||
const isCommandMenuItemEnabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IS_COMMAND_MENU_ITEM_ENABLED,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!isCommandMenuItemEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const existingCommandMenuItem =
|
||||
await this.commandMenuItemService.findByWorkflowVersionId(
|
||||
workflowVersion.id,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (existingCommandMenuItem) {
|
||||
await this.commandMenuItemService.delete(
|
||||
existingCommandMenuItem.id,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case WorkflowTriggerType.MANUAL:
|
||||
case WorkflowTriggerType.WEBHOOK:
|
||||
return;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user