@ -326,6 +326,7 @@ export type ClientConfig = {
|
|||||||
api: ApiConfig;
|
api: ApiConfig;
|
||||||
authProviders: AuthProviders;
|
authProviders: AuthProviders;
|
||||||
billing: Billing;
|
billing: Billing;
|
||||||
|
calendarBookingPageId?: Maybe<Scalars['String']['output']>;
|
||||||
canManageFeatureFlags: Scalars['Boolean']['output'];
|
canManageFeatureFlags: Scalars['Boolean']['output'];
|
||||||
captcha: Captcha;
|
captcha: Captcha;
|
||||||
chromeExtensionId?: Maybe<Scalars['String']['output']>;
|
chromeExtensionId?: Maybe<Scalars['String']['output']>;
|
||||||
@ -658,8 +659,7 @@ export enum FeatureFlagKey {
|
|||||||
IS_PERMISSIONS_V2_ENABLED = 'IS_PERMISSIONS_V2_ENABLED',
|
IS_PERMISSIONS_V2_ENABLED = 'IS_PERMISSIONS_V2_ENABLED',
|
||||||
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
|
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
|
||||||
IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED',
|
IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED',
|
||||||
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED',
|
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED'
|
||||||
IS_WORKFLOW_ENABLED = 'IS_WORKFLOW_ENABLED'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Field = {
|
export type Field = {
|
||||||
@ -1001,6 +1001,7 @@ export type Mutation = {
|
|||||||
signUp: AvailableWorkspacesAndAccessTokensOutput;
|
signUp: AvailableWorkspacesAndAccessTokensOutput;
|
||||||
signUpInNewWorkspace: SignUpOutput;
|
signUpInNewWorkspace: SignUpOutput;
|
||||||
signUpInWorkspace: SignUpOutput;
|
signUpInWorkspace: SignUpOutput;
|
||||||
|
skipBookOnboardingStep: OnboardingStepSuccess;
|
||||||
skipSyncEmailOnboardingStep: OnboardingStepSuccess;
|
skipSyncEmailOnboardingStep: OnboardingStepSuccess;
|
||||||
submitFormStep: Scalars['Boolean']['output'];
|
submitFormStep: Scalars['Boolean']['output'];
|
||||||
switchToEnterprisePlan: BillingUpdateOutput;
|
switchToEnterprisePlan: BillingUpdateOutput;
|
||||||
@ -1574,6 +1575,7 @@ export type OnDbEventInput = {
|
|||||||
|
|
||||||
/** Onboarding status */
|
/** Onboarding status */
|
||||||
export enum OnboardingStatus {
|
export enum OnboardingStatus {
|
||||||
|
BOOK_ONBOARDING = 'BOOK_ONBOARDING',
|
||||||
COMPLETED = 'COMPLETED',
|
COMPLETED = 'COMPLETED',
|
||||||
INVITE_TEAM = 'INVITE_TEAM',
|
INVITE_TEAM = 'INVITE_TEAM',
|
||||||
PLAN_REQUIRED = 'PLAN_REQUIRED',
|
PLAN_REQUIRED = 'PLAN_REQUIRED',
|
||||||
@ -1945,6 +1947,8 @@ export type Role = {
|
|||||||
export type RunWorkflowVersionInput = {
|
export type RunWorkflowVersionInput = {
|
||||||
/** Execution result in JSON format */
|
/** Execution result in JSON format */
|
||||||
payload?: InputMaybe<Scalars['JSON']['input']>;
|
payload?: InputMaybe<Scalars['JSON']['input']>;
|
||||||
|
/** Workflow run ID */
|
||||||
|
workflowRunId?: InputMaybe<Scalars['String']['input']>;
|
||||||
/** Workflow version ID */
|
/** Workflow version ID */
|
||||||
workflowVersionId: Scalars['String']['input'];
|
workflowVersionId: Scalars['String']['input'];
|
||||||
};
|
};
|
||||||
|
|||||||
@ -615,8 +615,7 @@ export enum FeatureFlagKey {
|
|||||||
IS_PERMISSIONS_V2_ENABLED = 'IS_PERMISSIONS_V2_ENABLED',
|
IS_PERMISSIONS_V2_ENABLED = 'IS_PERMISSIONS_V2_ENABLED',
|
||||||
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
|
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
|
||||||
IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED',
|
IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED',
|
||||||
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED',
|
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED'
|
||||||
IS_WORKFLOW_ENABLED = 'IS_WORKFLOW_ENABLED'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Field = {
|
export type Field = {
|
||||||
|
|||||||
@ -413,13 +413,11 @@ export const DEFAULT_RECORD_ACTIONS_CONFIG: Record<
|
|||||||
shouldBeRegistered: ({
|
shouldBeRegistered: ({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
viewType,
|
viewType,
|
||||||
isWorkflowEnabled,
|
|
||||||
getTargetObjectReadPermission,
|
getTargetObjectReadPermission,
|
||||||
}) =>
|
}) =>
|
||||||
getTargetObjectReadPermission(CoreObjectNameSingular.Workflow) === true &&
|
getTargetObjectReadPermission(CoreObjectNameSingular.Workflow) === true &&
|
||||||
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Workflow ||
|
(objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Workflow ||
|
||||||
viewType === ActionViewType.SHOW_PAGE) &&
|
viewType === ActionViewType.SHOW_PAGE),
|
||||||
isWorkflowEnabled,
|
|
||||||
availableOn: [
|
availableOn: [
|
||||||
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
ActionViewType.INDEX_PAGE_NO_SELECTION,
|
||||||
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
ActionViewType.INDEX_PAGE_SINGLE_RECORD_SELECTION,
|
||||||
|
|||||||
@ -4,18 +4,12 @@ import { ActionType } from '@/action-menu/actions/types/ActionType';
|
|||||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||||
import { useActiveWorkflowVersionsWithManualTrigger } from '@/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger';
|
import { useActiveWorkflowVersionsWithManualTrigger } from '@/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger';
|
||||||
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
|
||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
|
||||||
import { msg } from '@lingui/core/macro';
|
import { msg } from '@lingui/core/macro';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||||
import { IconSettingsAutomation } from 'twenty-ui/display';
|
import { IconSettingsAutomation } from 'twenty-ui/display';
|
||||||
import { FeatureFlagKey } from '~/generated/graphql';
|
|
||||||
|
|
||||||
export const useRunWorkflowRecordAgnosticActions = () => {
|
export const useRunWorkflowRecordAgnosticActions = () => {
|
||||||
const isWorkflowEnabled = useIsFeatureEnabled(
|
|
||||||
FeatureFlagKey.IS_WORKFLOW_ENABLED,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { actionMenuType } = useContext(ActionMenuContext);
|
const { actionMenuType } = useContext(ActionMenuContext);
|
||||||
|
|
||||||
const { records: activeWorkflowVersions } =
|
const { records: activeWorkflowVersions } =
|
||||||
@ -27,10 +21,6 @@ export const useRunWorkflowRecordAgnosticActions = () => {
|
|||||||
|
|
||||||
const { runWorkflowVersion } = useRunWorkflowVersion();
|
const { runWorkflowVersion } = useRunWorkflowVersion();
|
||||||
|
|
||||||
if (!isWorkflowEnabled) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return activeWorkflowVersions
|
return activeWorkflowVersions
|
||||||
.map((activeWorkflowVersion, index) => {
|
.map((activeWorkflowVersion, index) => {
|
||||||
if (!isDefined(activeWorkflowVersion.workflow)) {
|
if (!isDefined(activeWorkflowVersion.workflow)) {
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
|
|||||||
export type ShouldBeRegisteredFunctionParams = {
|
export type ShouldBeRegisteredFunctionParams = {
|
||||||
objectMetadataItem?: ObjectMetadataItem;
|
objectMetadataItem?: ObjectMetadataItem;
|
||||||
objectPermissions: ObjectPermissions;
|
objectPermissions: ObjectPermissions;
|
||||||
isWorkflowEnabled: boolean;
|
|
||||||
recordFilters?: RecordFilter[];
|
recordFilters?: RecordFilter[];
|
||||||
isShowPage?: boolean;
|
isShowPage?: boolean;
|
||||||
isSoftDeleteFilterActive?: boolean;
|
isSoftDeleteFilterActive?: boolean;
|
||||||
|
|||||||
@ -6,10 +6,8 @@ import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context
|
|||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
|
||||||
|
|
||||||
export const ActionMenuContextProvider = ({
|
export const ActionMenuContextProvider = ({
|
||||||
children,
|
children,
|
||||||
@ -19,10 +17,6 @@ export const ActionMenuContextProvider = ({
|
|||||||
}: Omit<ActionMenuContextType, 'actions'> & {
|
}: Omit<ActionMenuContextType, 'actions'> & {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
const isWorkflowEnabled = useIsFeatureEnabled(
|
|
||||||
FeatureFlagKey.IS_WORKFLOW_ENABLED,
|
|
||||||
);
|
|
||||||
|
|
||||||
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValueV2(
|
const contextStoreCurrentObjectMetadataItemId = useRecoilComponentValueV2(
|
||||||
contextStoreCurrentObjectMetadataItemIdComponentState,
|
contextStoreCurrentObjectMetadataItemIdComponentState,
|
||||||
);
|
);
|
||||||
@ -39,7 +33,7 @@ export const ActionMenuContextProvider = ({
|
|||||||
objectMetadataItem?.nameSingular === CoreObjectNameSingular.WorkflowRun ||
|
objectMetadataItem?.nameSingular === CoreObjectNameSingular.WorkflowRun ||
|
||||||
objectMetadataItem?.nameSingular === CoreObjectNameSingular.WorkflowVersion;
|
objectMetadataItem?.nameSingular === CoreObjectNameSingular.WorkflowVersion;
|
||||||
|
|
||||||
if (isWorkflowEnabled && isDefined(objectMetadataItem) && isWorkflowObject) {
|
if (isDefined(objectMetadataItem) && isWorkflowObject) {
|
||||||
return (
|
return (
|
||||||
<ActionMenuContextProviderWorkflowObjects
|
<ActionMenuContextProviderWorkflowObjects
|
||||||
isInRightDrawer={isInRightDrawer}
|
isInRightDrawer={isInRightDrawer}
|
||||||
@ -53,7 +47,6 @@ export const ActionMenuContextProvider = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isWorkflowEnabled &&
|
|
||||||
isDefined(objectMetadataItem) &&
|
isDefined(objectMetadataItem) &&
|
||||||
(actionMenuType === 'command-menu' ||
|
(actionMenuType === 'command-menu' ||
|
||||||
actionMenuType === 'command-menu-show-page-action-menu-dropdown')
|
actionMenuType === 'command-menu-show-page-action-menu-dropdown')
|
||||||
|
|||||||
@ -13,10 +13,8 @@ import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPe
|
|||||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||||
import { isSoftDeleteFilterActiveComponentState } from '@/object-record/record-table/states/isSoftDeleteFilterActiveComponentState';
|
import { isSoftDeleteFilterActiveComponentState } from '@/object-record/record-table/states/isSoftDeleteFilterActiveComponentState';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
|
||||||
|
|
||||||
export const useShouldActionBeRegisteredParams = ({
|
export const useShouldActionBeRegisteredParams = ({
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
@ -61,10 +59,6 @@ export const useShouldActionBeRegisteredParams = ({
|
|||||||
useRecoilComponentValueV2(contextStoreCurrentViewTypeComponentState) ===
|
useRecoilComponentValueV2(contextStoreCurrentViewTypeComponentState) ===
|
||||||
ContextStoreViewType.ShowPage;
|
ContextStoreViewType.ShowPage;
|
||||||
|
|
||||||
const isWorkflowEnabled = useIsFeatureEnabled(
|
|
||||||
FeatureFlagKey.IS_WORKFLOW_ENABLED,
|
|
||||||
);
|
|
||||||
|
|
||||||
const numberOfSelectedRecords = useRecoilComponentValueV2(
|
const numberOfSelectedRecords = useRecoilComponentValueV2(
|
||||||
contextStoreNumberOfSelectedRecordsComponentState,
|
contextStoreNumberOfSelectedRecordsComponentState,
|
||||||
);
|
);
|
||||||
@ -101,7 +95,6 @@ export const useShouldActionBeRegisteredParams = ({
|
|||||||
isSoftDeleteFilterActive,
|
isSoftDeleteFilterActive,
|
||||||
isShowPage,
|
isShowPage,
|
||||||
selectedRecord,
|
selectedRecord,
|
||||||
isWorkflowEnabled,
|
|
||||||
numberOfSelectedRecords,
|
numberOfSelectedRecords,
|
||||||
viewType: viewType ?? undefined,
|
viewType: viewType ?? undefined,
|
||||||
getTargetObjectReadPermission: getObjectReadPermission,
|
getTargetObjectReadPermission: getObjectReadPermission,
|
||||||
|
|||||||
@ -1,43 +1,22 @@
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
import { isWorkflowRelatedObjectMetadata } from '@/object-metadata/utils/isWorkflowRelatedObjectMetadata';
|
import { useMemo } from 'react';
|
||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
|
||||||
|
|
||||||
export const useFilteredObjectMetadataItems = () => {
|
export const useFilteredObjectMetadataItems = () => {
|
||||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||||
|
|
||||||
const isWorkflowEnabled = useIsFeatureEnabled(
|
|
||||||
FeatureFlagKey.IS_WORKFLOW_ENABLED,
|
|
||||||
);
|
|
||||||
|
|
||||||
const isWorkflowToBeFiltered = useCallback(
|
|
||||||
(nameSingular: string) => {
|
|
||||||
return (
|
|
||||||
!isWorkflowEnabled && isWorkflowRelatedObjectMetadata(nameSingular)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[isWorkflowEnabled],
|
|
||||||
);
|
|
||||||
|
|
||||||
const activeNonSystemObjectMetadataItems = useMemo(
|
const activeNonSystemObjectMetadataItems = useMemo(
|
||||||
() =>
|
() =>
|
||||||
objectMetadataItems.filter(
|
objectMetadataItems.filter(
|
||||||
({ isActive, isSystem, nameSingular }) =>
|
({ isActive, isSystem }) => isActive && !isSystem,
|
||||||
isActive && !isSystem && !isWorkflowToBeFiltered(nameSingular),
|
|
||||||
),
|
),
|
||||||
[isWorkflowToBeFiltered, objectMetadataItems],
|
[objectMetadataItems],
|
||||||
);
|
);
|
||||||
|
|
||||||
const activeObjectMetadataItems = useMemo(
|
const activeObjectMetadataItems = useMemo(
|
||||||
() =>
|
() => objectMetadataItems.filter(({ isActive }) => isActive),
|
||||||
objectMetadataItems.filter(
|
[objectMetadataItems],
|
||||||
({ isActive, nameSingular }) =>
|
|
||||||
isActive && !isWorkflowToBeFiltered(nameSingular),
|
|
||||||
),
|
|
||||||
[isWorkflowToBeFiltered, objectMetadataItems],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const alphaSortedActiveNonSystemObjectMetadataItems =
|
const alphaSortedActiveNonSystemObjectMetadataItems =
|
||||||
|
|||||||
@ -4,10 +4,7 @@ import { ObjectMetadataItemNotFoundError } from '@/object-metadata/errors/Object
|
|||||||
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
|
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
|
||||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||||
|
|
||||||
import { isWorkflowRelatedObjectMetadata } from '@/object-metadata/utils/isWorkflowRelatedObjectMetadata';
|
|
||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
|
||||||
import { ObjectMetadataItemIdentifier } from '../types/ObjectMetadataItemIdentifier';
|
import { ObjectMetadataItemIdentifier } from '../types/ObjectMetadataItemIdentifier';
|
||||||
|
|
||||||
export const useObjectMetadataItem = ({
|
export const useObjectMetadataItem = ({
|
||||||
@ -20,21 +17,8 @@ export const useObjectMetadataItem = ({
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const isWorkflowEnabled = useIsFeatureEnabled(
|
|
||||||
FeatureFlagKey.IS_WORKFLOW_ENABLED,
|
|
||||||
);
|
|
||||||
|
|
||||||
const isWorkflowToBeFiltered =
|
|
||||||
!isWorkflowEnabled && isWorkflowRelatedObjectMetadata(objectNameSingular);
|
|
||||||
|
|
||||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||||
|
|
||||||
if (isWorkflowToBeFiltered) {
|
|
||||||
throw new Error(
|
|
||||||
'Workflow is not enabled. If you want to use it, please enable it in the lab.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isDefined(objectMetadataItem)) {
|
if (!isDefined(objectMetadataItem)) {
|
||||||
throw new ObjectMetadataItemNotFoundError(
|
throw new ObjectMetadataItemNotFoundError(
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
|
|||||||
@ -20,7 +20,6 @@ import {
|
|||||||
IconSettings,
|
IconSettings,
|
||||||
} from 'twenty-ui/display';
|
} from 'twenty-ui/display';
|
||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
import { FeatureFlagKey } from '~/generated/graphql';
|
|
||||||
|
|
||||||
export const useRecordShowContainerTabs = (
|
export const useRecordShowContainerTabs = (
|
||||||
loading: boolean,
|
loading: boolean,
|
||||||
@ -155,7 +154,7 @@ export const useRecordShowContainerTabs = (
|
|||||||
ifMobile: false,
|
ifMobile: false,
|
||||||
ifDesktop: false,
|
ifDesktop: false,
|
||||||
ifInRightDrawer: false,
|
ifInRightDrawer: false,
|
||||||
ifFeaturesDisabled: [FeatureFlagKey.IS_WORKFLOW_ENABLED],
|
ifFeaturesDisabled: [],
|
||||||
ifRequiredObjectsInactive: [],
|
ifRequiredObjectsInactive: [],
|
||||||
ifRelationsMissing: [],
|
ifRelationsMissing: [],
|
||||||
},
|
},
|
||||||
@ -178,7 +177,7 @@ export const useRecordShowContainerTabs = (
|
|||||||
ifMobile: false,
|
ifMobile: false,
|
||||||
ifDesktop: false,
|
ifDesktop: false,
|
||||||
ifInRightDrawer: false,
|
ifInRightDrawer: false,
|
||||||
ifFeaturesDisabled: [FeatureFlagKey.IS_WORKFLOW_ENABLED],
|
ifFeaturesDisabled: [],
|
||||||
ifRequiredObjectsInactive: [],
|
ifRequiredObjectsInactive: [],
|
||||||
ifRelationsMissing: [],
|
ifRelationsMissing: [],
|
||||||
},
|
},
|
||||||
@ -200,7 +199,7 @@ export const useRecordShowContainerTabs = (
|
|||||||
ifMobile: false,
|
ifMobile: false,
|
||||||
ifDesktop: false,
|
ifDesktop: false,
|
||||||
ifInRightDrawer: false,
|
ifInRightDrawer: false,
|
||||||
ifFeaturesDisabled: [FeatureFlagKey.IS_WORKFLOW_ENABLED],
|
ifFeaturesDisabled: [],
|
||||||
ifRequiredObjectsInactive: [],
|
ifRequiredObjectsInactive: [],
|
||||||
ifRelationsMissing: [],
|
ifRelationsMissing: [],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { ObjectMetadataItemsQuery } from '~/generated-metadata/graphql';
|
|||||||
// This file is not designed to be manually edited.
|
// This file is not designed to be manually edited.
|
||||||
// It's an extract from the dev seeded environment metadata call
|
// It's an extract from the dev seeded environment metadata call
|
||||||
// TODO: automate the generation of this file
|
// TODO: automate the generation of this file
|
||||||
// ⚠️ WARNING ⚠️: Be sure to activate the workflow feature flag (IsWorkflowEnabled) before updating that mock.
|
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
|
||||||
|
|||||||
@ -68,10 +68,6 @@ export const mockCurrentWorkspace: Workspace = {
|
|||||||
key: FeatureFlagKey.IS_POSTGRESQL_INTEGRATION_ENABLED,
|
key: FeatureFlagKey.IS_POSTGRESQL_INTEGRATION_ENABLED,
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: FeatureFlagKey.IS_WORKFLOW_ENABLED,
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
createdAt: '2023-04-26T10:23:42.33625+00:00',
|
createdAt: '2023-04-26T10:23:42.33625+00:00',
|
||||||
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
||||||
|
|||||||
@ -7,19 +7,11 @@ type FeatureFlagMetadata = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type PublicFeatureFlag = {
|
export type PublicFeatureFlag = {
|
||||||
key: Extract<FeatureFlagKey, FeatureFlagKey.IS_WORKFLOW_ENABLED>;
|
key: FeatureFlagKey;
|
||||||
metadata: FeatureFlagMetadata;
|
metadata: FeatureFlagMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PUBLIC_FEATURE_FLAGS: PublicFeatureFlag[] = [
|
export const PUBLIC_FEATURE_FLAGS: PublicFeatureFlag[] = [
|
||||||
{
|
|
||||||
key: FeatureFlagKey.IS_WORKFLOW_ENABLED,
|
|
||||||
metadata: {
|
|
||||||
label: 'Workflows',
|
|
||||||
description: 'Create custom workflows to automate your work.',
|
|
||||||
imagePath: 'https://twenty.com/images/lab/is-workflow-enabled.png',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
...(process.env.CLOUDFLARE_API_KEY
|
...(process.env.CLOUDFLARE_API_KEY
|
||||||
? [
|
? [
|
||||||
// {
|
// {
|
||||||
|
|||||||
@ -2,7 +2,6 @@ export enum FeatureFlagKey {
|
|||||||
IS_AIRTABLE_INTEGRATION_ENABLED = 'IS_AIRTABLE_INTEGRATION_ENABLED',
|
IS_AIRTABLE_INTEGRATION_ENABLED = 'IS_AIRTABLE_INTEGRATION_ENABLED',
|
||||||
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
|
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
|
||||||
IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED',
|
IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED',
|
||||||
IS_WORKFLOW_ENABLED = 'IS_WORKFLOW_ENABLED',
|
|
||||||
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED',
|
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED',
|
||||||
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
|
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
|
||||||
IS_PERMISSIONS_V2_ENABLED = 'IS_PERMISSIONS_V2_ENABLED',
|
IS_PERMISSIONS_V2_ENABLED = 'IS_PERMISSIONS_V2_ENABLED',
|
||||||
|
|||||||
@ -41,7 +41,7 @@ describe('FeatureFlagService', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const workspaceId = 'workspace-id';
|
const workspaceId = 'workspace-id';
|
||||||
const featureFlag = FeatureFlagKey.IS_WORKFLOW_ENABLED;
|
const featureFlag = FeatureFlagKey.IS_AI_ENABLED;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
@ -130,12 +130,10 @@ describe('FeatureFlagService', () => {
|
|||||||
// Prepare
|
// Prepare
|
||||||
mockWorkspaceFeatureFlagsMapCacheService.getWorkspaceFeatureFlagsMap.mockResolvedValue(
|
mockWorkspaceFeatureFlagsMapCacheService.getWorkspaceFeatureFlagsMap.mockResolvedValue(
|
||||||
{
|
{
|
||||||
[FeatureFlagKey.IS_WORKFLOW_ENABLED]: true,
|
|
||||||
[FeatureFlagKey.IS_AI_ENABLED]: false,
|
[FeatureFlagKey.IS_AI_ENABLED]: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const mockFeatureFlags = [
|
const mockFeatureFlags = [
|
||||||
{ key: FeatureFlagKey.IS_WORKFLOW_ENABLED, value: true },
|
|
||||||
{ key: FeatureFlagKey.IS_AI_ENABLED, value: false },
|
{ key: FeatureFlagKey.IS_AI_ENABLED, value: false },
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -154,7 +152,6 @@ describe('FeatureFlagService', () => {
|
|||||||
it('should return a map of feature flags for a workspace', async () => {
|
it('should return a map of feature flags for a workspace', async () => {
|
||||||
// Prepare
|
// Prepare
|
||||||
const mockFeatureFlags = [
|
const mockFeatureFlags = [
|
||||||
{ key: FeatureFlagKey.IS_WORKFLOW_ENABLED, value: true, workspaceId },
|
|
||||||
{ key: FeatureFlagKey.IS_AI_ENABLED, value: false, workspaceId },
|
{ key: FeatureFlagKey.IS_AI_ENABLED, value: false, workspaceId },
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -165,7 +162,6 @@ describe('FeatureFlagService', () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
[FeatureFlagKey.IS_WORKFLOW_ENABLED]: true,
|
|
||||||
[FeatureFlagKey.IS_AI_ENABLED]: false,
|
[FeatureFlagKey.IS_AI_ENABLED]: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -174,10 +170,7 @@ describe('FeatureFlagService', () => {
|
|||||||
describe('enableFeatureFlags', () => {
|
describe('enableFeatureFlags', () => {
|
||||||
it('should enable multiple feature flags for a workspace', async () => {
|
it('should enable multiple feature flags for a workspace', async () => {
|
||||||
// Prepare
|
// Prepare
|
||||||
const keys = [
|
const keys = [FeatureFlagKey.IS_AI_ENABLED];
|
||||||
FeatureFlagKey.IS_WORKFLOW_ENABLED,
|
|
||||||
FeatureFlagKey.IS_AI_ENABLED,
|
|
||||||
];
|
|
||||||
|
|
||||||
mockFeatureFlagRepository.upsert.mockResolvedValue({});
|
mockFeatureFlagRepository.upsert.mockResolvedValue({});
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ describe('featureFlagValidator', () => {
|
|||||||
it('should not throw error if featureFlagKey is valid', () => {
|
it('should not throw error if featureFlagKey is valid', () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
featureFlagValidator.assertIsFeatureFlagKey(
|
featureFlagValidator.assertIsFeatureFlagKey(
|
||||||
'IS_WORKFLOW_ENABLED',
|
'IS_AI_ENABLED',
|
||||||
new CustomException('Error', 'Error'),
|
new CustomException('Error', 'Error'),
|
||||||
),
|
),
|
||||||
).not.toThrow();
|
).not.toThrow();
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { OnModuleDestroy } from '@nestjs/common';
|
import { OnModuleDestroy } from '@nestjs/common';
|
||||||
|
|
||||||
import { JobsOptions, MetricsTime, Queue, QueueOptions, Worker } from 'bullmq';
|
import { JobsOptions, MetricsTime, Queue, QueueOptions, Worker } from 'bullmq';
|
||||||
import { v4 } from 'uuid';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
QueueCronJobOptions,
|
QueueCronJobOptions,
|
||||||
|
|||||||
@ -1042,7 +1042,7 @@ export class ConfigVariables {
|
|||||||
type: ConfigVariableType.NUMBER,
|
type: ConfigVariableType.NUMBER,
|
||||||
})
|
})
|
||||||
@CastToPositiveNumber()
|
@CastToPositiveNumber()
|
||||||
WORKFLOW_EXEC_THROTTLE_LIMIT = 100;
|
WORKFLOW_EXEC_THROTTLE_LIMIT = 10;
|
||||||
|
|
||||||
@ConfigVariablesMetadata({
|
@ConfigVariablesMetadata({
|
||||||
group: ConfigVariablesGroup.RateLimiting,
|
group: ConfigVariablesGroup.RateLimiting,
|
||||||
|
|||||||
@ -10,7 +10,6 @@ export class ServerlessFunctionException extends CustomException {
|
|||||||
export enum ServerlessFunctionExceptionCode {
|
export enum ServerlessFunctionExceptionCode {
|
||||||
SERVERLESS_FUNCTION_NOT_FOUND = 'SERVERLESS_FUNCTION_NOT_FOUND',
|
SERVERLESS_FUNCTION_NOT_FOUND = 'SERVERLESS_FUNCTION_NOT_FOUND',
|
||||||
SERVERLESS_FUNCTION_VERSION_NOT_FOUND = 'SERVERLESS_FUNCTION_VERSION_NOT_FOUND',
|
SERVERLESS_FUNCTION_VERSION_NOT_FOUND = 'SERVERLESS_FUNCTION_VERSION_NOT_FOUND',
|
||||||
FEATURE_FLAG_INVALID = 'FEATURE_FLAG_INVALID',
|
|
||||||
SERVERLESS_FUNCTION_ALREADY_EXIST = 'SERVERLESS_FUNCTION_ALREADY_EXIST',
|
SERVERLESS_FUNCTION_ALREADY_EXIST = 'SERVERLESS_FUNCTION_ALREADY_EXIST',
|
||||||
SERVERLESS_FUNCTION_NOT_READY = 'SERVERLESS_FUNCTION_NOT_READY',
|
SERVERLESS_FUNCTION_NOT_READY = 'SERVERLESS_FUNCTION_NOT_READY',
|
||||||
SERVERLESS_FUNCTION_BUILDING = 'SERVERLESS_FUNCTION_BUILDING',
|
SERVERLESS_FUNCTION_BUILDING = 'SERVERLESS_FUNCTION_BUILDING',
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { InjectRepository } from '@nestjs/typeorm';
|
|||||||
import graphqlTypeJson from 'graphql-type-json';
|
import graphqlTypeJson from 'graphql-type-json';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
|
||||||
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||||
@ -19,10 +18,6 @@ import { ServerlessFunctionIdInput } from 'src/engine/metadata-modules/serverles
|
|||||||
import { ServerlessFunctionDTO } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto';
|
import { ServerlessFunctionDTO } from 'src/engine/metadata-modules/serverless-function/dtos/serverless-function.dto';
|
||||||
import { UpdateServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/update-serverless-function.input';
|
import { UpdateServerlessFunctionInput } from 'src/engine/metadata-modules/serverless-function/dtos/update-serverless-function.input';
|
||||||
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
|
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
|
||||||
import {
|
|
||||||
ServerlessFunctionException,
|
|
||||||
ServerlessFunctionExceptionCode,
|
|
||||||
} from 'src/engine/metadata-modules/serverless-function/serverless-function.exception';
|
|
||||||
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
|
||||||
import { serverlessFunctionGraphQLApiExceptionHandler } from 'src/engine/metadata-modules/serverless-function/utils/serverless-function-graphql-api-exception-handler.utils';
|
import { serverlessFunctionGraphQLApiExceptionHandler } from 'src/engine/metadata-modules/serverless-function/utils/serverless-function-graphql-api-exception-handler.utils';
|
||||||
|
|
||||||
@ -37,29 +32,12 @@ export class ServerlessFunctionResolver {
|
|||||||
private readonly serverlessFunctionRepository: Repository<ServerlessFunctionEntity>,
|
private readonly serverlessFunctionRepository: Repository<ServerlessFunctionEntity>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async checkFeatureFlag(workspaceId: string) {
|
|
||||||
const isWorkflowEnabled = await this.featureFlagRepository.findOneBy({
|
|
||||||
workspaceId,
|
|
||||||
key: FeatureFlagKey.IS_WORKFLOW_ENABLED,
|
|
||||||
value: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isWorkflowEnabled) {
|
|
||||||
throw new ServerlessFunctionException(
|
|
||||||
`IS_WORKFLOW_ENABLED feature flag is not set to true for this workspace`,
|
|
||||||
ServerlessFunctionExceptionCode.FEATURE_FLAG_INVALID,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Query(() => ServerlessFunctionDTO)
|
@Query(() => ServerlessFunctionDTO)
|
||||||
async findOneServerlessFunction(
|
async findOneServerlessFunction(
|
||||||
@Args('input') { id }: ServerlessFunctionIdInput,
|
@Args('input') { id }: ServerlessFunctionIdInput,
|
||||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await this.checkFeatureFlag(workspaceId);
|
|
||||||
|
|
||||||
return await this.serverlessFunctionRepository.findOneOrFail({
|
return await this.serverlessFunctionRepository.findOneOrFail({
|
||||||
where: {
|
where: {
|
||||||
id,
|
id,
|
||||||
@ -76,8 +54,6 @@ export class ServerlessFunctionResolver {
|
|||||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await this.checkFeatureFlag(workspaceId);
|
|
||||||
|
|
||||||
return await this.serverlessFunctionService.findManyServerlessFunctions({
|
return await this.serverlessFunctionService.findManyServerlessFunctions({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
});
|
});
|
||||||
@ -87,13 +63,8 @@ export class ServerlessFunctionResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Query(() => graphqlTypeJson)
|
@Query(() => graphqlTypeJson)
|
||||||
async getAvailablePackages(
|
async getAvailablePackages(@Args('input') { id }: ServerlessFunctionIdInput) {
|
||||||
@Args('input') { id }: ServerlessFunctionIdInput,
|
|
||||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
await this.checkFeatureFlag(workspaceId);
|
|
||||||
|
|
||||||
return await this.serverlessFunctionService.getAvailablePackages(id);
|
return await this.serverlessFunctionService.getAvailablePackages(id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
serverlessFunctionGraphQLApiExceptionHandler(error);
|
serverlessFunctionGraphQLApiExceptionHandler(error);
|
||||||
@ -106,8 +77,6 @@ export class ServerlessFunctionResolver {
|
|||||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await this.checkFeatureFlag(workspaceId);
|
|
||||||
|
|
||||||
return await this.serverlessFunctionService.getServerlessFunctionSourceCode(
|
return await this.serverlessFunctionService.getServerlessFunctionSourceCode(
|
||||||
workspaceId,
|
workspaceId,
|
||||||
input.id,
|
input.id,
|
||||||
@ -124,8 +93,6 @@ export class ServerlessFunctionResolver {
|
|||||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await this.checkFeatureFlag(workspaceId);
|
|
||||||
|
|
||||||
return await this.serverlessFunctionService.deleteOneServerlessFunction({
|
return await this.serverlessFunctionService.deleteOneServerlessFunction({
|
||||||
id: input.id,
|
id: input.id,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
@ -142,8 +109,6 @@ export class ServerlessFunctionResolver {
|
|||||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await this.checkFeatureFlag(workspaceId);
|
|
||||||
|
|
||||||
return await this.serverlessFunctionService.updateOneServerlessFunction(
|
return await this.serverlessFunctionService.updateOneServerlessFunction(
|
||||||
input,
|
input,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
@ -160,8 +125,6 @@ export class ServerlessFunctionResolver {
|
|||||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await this.checkFeatureFlag(workspaceId);
|
|
||||||
|
|
||||||
return await this.serverlessFunctionService.createOneServerlessFunction(
|
return await this.serverlessFunctionService.createOneServerlessFunction(
|
||||||
input,
|
input,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
@ -177,7 +140,6 @@ export class ServerlessFunctionResolver {
|
|||||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await this.checkFeatureFlag(workspaceId);
|
|
||||||
const { id, payload, version } = input;
|
const { id, payload, version } = input;
|
||||||
|
|
||||||
return await this.serverlessFunctionService.executeOneServerlessFunction(
|
return await this.serverlessFunctionService.executeOneServerlessFunction(
|
||||||
@ -197,7 +159,6 @@ export class ServerlessFunctionResolver {
|
|||||||
@AuthWorkspace() { id: workspaceId }: Workspace,
|
@AuthWorkspace() { id: workspaceId }: Workspace,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await this.checkFeatureFlag(workspaceId);
|
|
||||||
const { id } = input;
|
const { id } = input;
|
||||||
|
|
||||||
return await this.serverlessFunctionService.publishOneServerlessFunction(
|
return await this.serverlessFunctionService.publishOneServerlessFunction(
|
||||||
|
|||||||
@ -19,7 +19,6 @@ export const serverlessFunctionGraphQLApiExceptionHandler = (error: any) => {
|
|||||||
throw new ConflictError(error.message);
|
throw new ConflictError(error.message);
|
||||||
case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_NOT_READY:
|
case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_NOT_READY:
|
||||||
case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_BUILDING:
|
case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_BUILDING:
|
||||||
case ServerlessFunctionExceptionCode.FEATURE_FLAG_INVALID:
|
|
||||||
case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_EXECUTION_LIMIT_REACHED:
|
case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_EXECUTION_LIMIT_REACHED:
|
||||||
throw new ForbiddenError(error.message);
|
throw new ForbiddenError(error.message);
|
||||||
case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_CODE_UNCHANGED:
|
case ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_CODE_UNCHANGED:
|
||||||
|
|||||||
@ -30,11 +30,6 @@ export const seedFeatureFlags = async (
|
|||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
value: true,
|
value: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: FeatureFlagKey.IS_WORKFLOW_ENABLED,
|
|
||||||
workspaceId: workspaceId,
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: FeatureFlagKey.IS_UNIQUE_INDEXES_ENABLED,
|
key: FeatureFlagKey.IS_UNIQUE_INDEXES_ENABLED,
|
||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { AutomatedTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service';
|
|
||||||
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
|
|
||||||
import { DatabaseEventTriggerListener } from 'src/modules/workflow/workflow-trigger/automated-trigger/listeners/database-event-trigger.listener';
|
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
||||||
|
import { AutomatedTriggerWorkspaceService } from 'src/modules/workflow/workflow-trigger/automated-trigger/automated-trigger.workspace-service';
|
||||||
import { CronTriggerCronCommand } from 'src/modules/workflow/workflow-trigger/automated-trigger/crons/commands/cron-trigger.cron.command';
|
import { CronTriggerCronCommand } from 'src/modules/workflow/workflow-trigger/automated-trigger/crons/commands/cron-trigger.cron.command';
|
||||||
import { CronTriggerCronJob } from 'src/modules/workflow/workflow-trigger/automated-trigger/crons/jobs/cron-trigger.cron.job';
|
import { CronTriggerCronJob } from 'src/modules/workflow/workflow-trigger/automated-trigger/crons/jobs/cron-trigger.cron.job';
|
||||||
import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module';
|
import { DatabaseEventTriggerListener } from 'src/modules/workflow/workflow-trigger/automated-trigger/listeners/database-event-trigger.listener';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
FeatureFlagModule,
|
|
||||||
TypeOrmModule.forFeature([Workspace], 'core'),
|
TypeOrmModule.forFeature([Workspace], 'core'),
|
||||||
WorkflowCommonModule,
|
WorkflowCommonModule,
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
|
||||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||||
import { AutomatedTriggerType } from 'src/modules/workflow/common/standard-objects/workflow-automated-trigger.workspace-entity';
|
import { AutomatedTriggerType } from 'src/modules/workflow/common/standard-objects/workflow-automated-trigger.workspace-entity';
|
||||||
@ -12,7 +11,6 @@ describe('DatabaseEventTriggerListener', () => {
|
|||||||
let listener: DatabaseEventTriggerListener;
|
let listener: DatabaseEventTriggerListener;
|
||||||
let twentyORMGlobalManager: jest.Mocked<TwentyORMGlobalManager>;
|
let twentyORMGlobalManager: jest.Mocked<TwentyORMGlobalManager>;
|
||||||
let messageQueueService: jest.Mocked<MessageQueueService>;
|
let messageQueueService: jest.Mocked<MessageQueueService>;
|
||||||
let featureFlagService: jest.Mocked<FeatureFlagService>;
|
|
||||||
|
|
||||||
const mockRepository = {
|
const mockRepository = {
|
||||||
find: jest.fn(),
|
find: jest.fn(),
|
||||||
@ -27,10 +25,6 @@ describe('DatabaseEventTriggerListener', () => {
|
|||||||
add: jest.fn(),
|
add: jest.fn(),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
featureFlagService = {
|
|
||||||
isFeatureEnabled: jest.fn().mockResolvedValue(true),
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
DatabaseEventTriggerListener,
|
DatabaseEventTriggerListener,
|
||||||
@ -42,10 +36,6 @@ describe('DatabaseEventTriggerListener', () => {
|
|||||||
provide: MessageQueueService,
|
provide: MessageQueueService,
|
||||||
useValue: messageQueueService,
|
useValue: messageQueueService,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
provide: FeatureFlagService,
|
|
||||||
useValue: featureFlagService,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: 'MESSAGE_QUEUE_workflow-queue',
|
provide: 'MESSAGE_QUEUE_workflow-queue',
|
||||||
useValue: messageQueueService,
|
useValue: messageQueueService,
|
||||||
@ -303,14 +293,6 @@ describe('DatabaseEventTriggerListener', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore events when feature flag is disabled', async () => {
|
|
||||||
featureFlagService.isFeatureEnabled.mockResolvedValueOnce(false);
|
|
||||||
|
|
||||||
await listener.handleObjectRecordUpdateEvent(mockPayload);
|
|
||||||
|
|
||||||
expect(messageQueueService.add).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle multiple events in a batch', async () => {
|
it('should handle multiple events in a batch', async () => {
|
||||||
const batchPayload = {
|
const batchPayload = {
|
||||||
...mockPayload,
|
...mockPayload,
|
||||||
|
|||||||
@ -10,8 +10,6 @@ import { ObjectRecordDeleteEvent } from 'src/engine/core-modules/event-emitter/t
|
|||||||
import { ObjectRecordDestroyEvent } from 'src/engine/core-modules/event-emitter/types/object-record-destroy.event';
|
import { ObjectRecordDestroyEvent } from 'src/engine/core-modules/event-emitter/types/object-record-destroy.event';
|
||||||
import { ObjectRecordNonDestructiveEvent } from 'src/engine/core-modules/event-emitter/types/object-record-non-destructive-event';
|
import { ObjectRecordNonDestructiveEvent } from 'src/engine/core-modules/event-emitter/types/object-record-non-destructive-event';
|
||||||
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
|
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
|
||||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
|
||||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
|
||||||
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
|
||||||
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
|
||||||
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
|
||||||
@ -36,7 +34,6 @@ export class DatabaseEventTriggerListener {
|
|||||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||||
@InjectMessageQueue(MessageQueue.workflowQueue)
|
@InjectMessageQueue(MessageQueue.workflowQueue)
|
||||||
private readonly messageQueueService: MessageQueueService,
|
private readonly messageQueueService: MessageQueueService,
|
||||||
private readonly isFeatureFlagEnabledService: FeatureFlagService,
|
|
||||||
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
private readonly workflowCommonWorkspaceService: WorkflowCommonWorkspaceService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -231,13 +228,7 @@ export class DatabaseEventTriggerListener {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isWorkflowEnabled =
|
return false;
|
||||||
await this.isFeatureFlagEnabledService.isFeatureEnabled(
|
|
||||||
FeatureFlagKey.IS_WORKFLOW_ENABLED,
|
|
||||||
workspaceId,
|
|
||||||
);
|
|
||||||
|
|
||||||
return !isWorkflowEnabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleEvent({
|
private async handleEvent({
|
||||||
|
|||||||
Reference in New Issue
Block a user