Fix workflow activation optimistic rendering in Cmd+K (#10109)

Close
https://discord.com/channels/1130383047699738754/1334441759484149793

Using refetch queries was not working for certain use cases.
To find manual active workflows in cmd+k, we use a query with specific
filters that was complicated to refetch.

Finally I will update the cache manually. It was not properly updated
before because the json value of the version trigger was stringified
without spaces. So the entity was not found in apollo cache.
This commit is contained in:
Thomas Trompette
2025-02-10 17:46:36 +01:00
committed by GitHub
parent d52c7ffd73
commit b5736a28fd
10 changed files with 127 additions and 47 deletions

View File

@ -162,7 +162,6 @@ describe('useDeactivateWorkflowSingleRecordAction', () => {
expect(deactivateWorkflowVersionMock).toHaveBeenCalledWith({
workflowVersionId: activeWorkflowMock.currentVersion.id,
workflowId: activeWorkflowMock.id,
});
});
});

View File

@ -23,7 +23,6 @@ export const useDeactivateWorkflowSingleRecordAction: ActionHookWithoutObjectMet
deactivateWorkflowVersion({
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
workflowId: workflowWithCurrentVersion.id,
});
};

View File

@ -7,7 +7,7 @@ import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/s
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useAllActiveWorkflowVersions } from '@/workflow/hooks/useAllActiveWorkflowVersions';
import { useActiveWorkflowVersionsWithManualTrigger } from '@/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger';
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
import { msg } from '@lingui/core/macro';
@ -38,10 +38,10 @@ export const useWorkflowRunRecordActions = ({
recordStoreFamilyState(selectedRecordId),
);
const { records: activeWorkflowVersions } = useAllActiveWorkflowVersions({
objectMetadataItem,
triggerType: 'MANUAL',
});
const { records: activeWorkflowVersions } =
useActiveWorkflowVersionsWithManualTrigger({
objectMetadataItem,
});
const { runWorkflowVersion } = useRunWorkflowVersion();

View File

@ -2,7 +2,7 @@ import {
ActionMenuEntryScope,
ActionMenuEntryType,
} from '@/action-menu/types/ActionMenuEntry';
import { useAllActiveWorkflowVersions } from '@/workflow/hooks/useAllActiveWorkflowVersions';
import { useActiveWorkflowVersionsWithManualTrigger } from '@/workflow/hooks/useActiveWorkflowVersionsWithManualTrigger';
import { useRunWorkflowVersion } from '@/workflow/hooks/useRunWorkflowVersion';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { msg } from '@lingui/core/macro';
@ -16,9 +16,8 @@ export const useRunWorkflowActions = () => {
FeatureFlagKey.IsWorkflowEnabled,
);
const { records: activeWorkflowVersions } = useAllActiveWorkflowVersions({
triggerType: 'MANUAL',
});
const { records: activeWorkflowVersions } =
useActiveWorkflowVersionsWithManualTrigger({});
const { runWorkflowVersion } = useRunWorkflowVersion();

View File

@ -10,9 +10,9 @@ export const isMatchingRawJsonFilter = ({
switch (true) {
case rawJsonFilter.like !== undefined: {
const regexPattern = rawJsonFilter.like.replace(/%/g, '.*');
const regexCaseInsensitive = new RegExp(`^${regexPattern}$`, 'i');
const regexCaseInsensitive = new RegExp(`^${regexPattern}$`, 'is');
const stringValue = JSON.stringify(value);
const stringValue = JSON.stringify(value, null, 1);
return regexCaseInsensitive.test(stringValue);
}

View File

@ -124,7 +124,6 @@ export const RecordShowPageWorkflowHeader = ({
return deactivateWorkflowVersion({
workflowVersionId: workflowWithCurrentVersion.currentVersion.id,
workflowId: workflowWithCurrentVersion.id,
});
}}
/>

View File

@ -128,7 +128,6 @@ export const RecordShowPageWorkflowVersionHeader = ({
onClick={() => {
return deactivateWorkflowVersion({
workflowVersionId: workflowVersion.id,
workflowId: workflowVersion.workflowId,
});
}}
/>

View File

@ -1,8 +1,12 @@
import { useApolloClient, useMutation } from '@apollo/client';
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
import { modifyRecordFromCache } from '@/object-record/cache/utils/modifyRecordFromCache';
import { ACTIVATE_WORKFLOW_VERSION } from '@/workflow/graphql/mutations/activateWorkflowVersion';
import { WorkflowVersion } from '@/workflow/types/Workflow';
import { isDefined } from 'twenty-shared';
import {
ActivateWorkflowVersionMutation,
ActivateWorkflowVersionMutationVariables,
@ -17,8 +21,8 @@ export const useActivateWorkflowVersion = () => {
client: apolloClient,
});
const { findManyRecordsQuery: findManyWorkflowVersionsQuery } =
useFindManyRecordsQuery({
const { objectMetadataItem: objectMetadataItemWorkflowVersion } =
useObjectMetadataItem({
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
});
@ -33,14 +37,72 @@ export const useActivateWorkflowVersion = () => {
variables: {
workflowVersionId,
},
refetchQueries: [
{
query: findManyWorkflowVersionsQuery,
variables: {
workflowId,
update: () => {
modifyRecordFromCache({
cache: apolloClient.cache,
recordId: workflowVersionId,
objectMetadataItem: objectMetadataItemWorkflowVersion,
fieldModifiers: {
status: () => 'ACTIVE',
},
},
],
});
const cacheSnapshot = apolloClient.cache.extract();
const allWorkflowVersions: Array<WorkflowVersion> = Object.values(
cacheSnapshot,
).filter(
(item) =>
item.__typename === 'WorkflowVersion' &&
item.workflowId === workflowId,
);
const previousActiveWorkflowVersions = allWorkflowVersions.filter(
(version) =>
version.status === 'ACTIVE' && version.id !== workflowVersionId,
);
const newlyActiveWorkflowVersion = allWorkflowVersions.find(
(version) => version.id === workflowVersionId,
);
if (isDefined(newlyActiveWorkflowVersion)) {
triggerUpdateRecordOptimisticEffect({
cache: apolloClient.cache,
objectMetadataItem: objectMetadataItemWorkflowVersion,
currentRecord: newlyActiveWorkflowVersion,
updatedRecord: {
...newlyActiveWorkflowVersion,
status: 'ACTIVE',
},
objectMetadataItems: [objectMetadataItemWorkflowVersion],
});
}
for (const workflowVersion of previousActiveWorkflowVersions) {
apolloClient.cache.modify({
id: apolloClient.cache.identify(workflowVersion),
fields: {
status: () => {
return workflowVersion.id !== workflowVersionId &&
workflowVersion.status === 'ACTIVE'
? 'ARCHIVED'
: workflowVersion.status;
},
},
});
triggerUpdateRecordOptimisticEffect({
cache: apolloClient.cache,
objectMetadataItem: objectMetadataItemWorkflowVersion,
currentRecord: workflowVersion,
updatedRecord: {
...workflowVersion,
status: 'ARCHIVED',
},
objectMetadataItems: [objectMetadataItemWorkflowVersion],
});
}
},
});
};

View File

@ -3,19 +3,13 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import {
Workflow,
WorkflowTriggerType,
WorkflowVersion,
} from '@/workflow/types/Workflow';
import { Workflow, WorkflowVersion } from '@/workflow/types/Workflow';
import { isDefined } from 'twenty-shared';
export const useAllActiveWorkflowVersions = ({
export const useActiveWorkflowVersionsWithManualTrigger = ({
objectMetadataItem,
triggerType,
}: {
objectMetadataItem?: ObjectMetadataItem;
triggerType: WorkflowTriggerType;
}) => {
const filters = [
{
@ -25,7 +19,7 @@ export const useAllActiveWorkflowVersions = ({
},
{
trigger: {
like: `%"type": "${triggerType}"%`,
like: `%"type": "MANUAL"%`,
},
},
];
@ -63,7 +57,8 @@ export const useAllActiveWorkflowVersions = ({
return {
records: records.filter(
(record) =>
record.trigger?.type !== 'CRON' &&
record.status === 'ACTIVE' &&
record.trigger?.type === 'MANUAL' &&
!isDefined(record.trigger?.settings.objectType),
),
};

View File

@ -1,8 +1,12 @@
import { useApolloClient, useMutation } from '@apollo/client';
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery';
import { modifyRecordFromCache } from '@/object-record/cache/utils/modifyRecordFromCache';
import { DEACTIVATE_WORKFLOW_VERSION } from '@/workflow/graphql/mutations/deactivateWorkflowVersion';
import { WorkflowVersion } from '@/workflow/types/Workflow';
import { isDefined } from 'twenty-shared';
import {
DeactivateWorkflowVersionMutation,
DeactivateWorkflowVersionMutationVariables,
@ -17,30 +21,54 @@ export const useDeactivateWorkflowVersion = () => {
client: apolloClient,
});
const { findManyRecordsQuery: findManyWorkflowVersionsQuery } =
useFindManyRecordsQuery({
const { objectMetadataItem: objectMetadataItemWorkflowVersion } =
useObjectMetadataItem({
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
});
const deactivateWorkflowVersion = async ({
workflowVersionId,
workflowId,
}: {
workflowVersionId: string;
workflowId: string;
}) => {
await mutate({
variables: {
workflowVersionId,
},
refetchQueries: [
{
query: findManyWorkflowVersionsQuery,
variables: {
workflowId,
update: () => {
modifyRecordFromCache({
cache: apolloClient.cache,
recordId: workflowVersionId,
objectMetadataItem: objectMetadataItemWorkflowVersion,
fieldModifiers: {
status: () => 'DEACTIVATED',
},
},
],
});
const cacheSnapshot = apolloClient.cache.extract();
const workflowVersion: WorkflowVersion | undefined = Object.values(
cacheSnapshot,
).find(
(item) =>
item.__typename === 'WorkflowVersion' &&
item.id === workflowVersionId,
);
if (!isDefined(workflowVersion)) {
return;
}
triggerUpdateRecordOptimisticEffect({
cache: apolloClient.cache,
objectMetadataItem: objectMetadataItemWorkflowVersion,
currentRecord: workflowVersion,
updatedRecord: {
...workflowVersion,
status: 'DEACTIVATED',
},
objectMetadataItems: [objectMetadataItemWorkflowVersion],
});
},
});
};