Skip to content

Commit a8cb2ec

Browse files
Show Active Cluster Selection Policy on Workflow Summary page (cadence-workflow#1006)
* Add Active Cluster Selection Strategy, and strategy-specific fields to Workflow Summary page * Fix type issues with formatted start event, by creating a new fixture using type casting
1 parent 985ca92 commit a8cb2ec

File tree

7 files changed

+174
-25
lines changed

7 files changed

+174
-25
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import formatWorkflowHistoryEvent from '@/utils/data-formatters/format-workflow-history-event';
2+
import { startWorkflowExecutionEvent } from '@/views/workflow-history/__fixtures__/workflow-history-single-events';
3+
4+
import { type FormattedFirstHistoryEvent } from '../workflow-summary-details/workflow-summary-details.types';
5+
6+
export const mockFormattedFirstEvent = formatWorkflowHistoryEvent(
7+
startWorkflowExecutionEvent
8+
) as FormattedFirstHistoryEvent;

src/views/workflow-summary/config/workflow-summary-details.config.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import WorkflowStatusTag from '@/views/shared/workflow-status-tag/workflow-statu
66
import getWorkflowStatusTagProps from '@/views/workflow-page/helpers/get-workflow-status-tag-props';
77

88
import WorkflowEventDetailsExecutionLink from '../../shared/workflow-event-details-execution-link/workflow-event-details-execution-link';
9+
import getActiveClusterSelectionPolicy from '../workflow-summary-details/helpers/get-active-cluster-selection-policy';
10+
import { ACTIVE_CLUSTER_SELECTION_STRATEGY_LABEL_MAP } from '../workflow-summary-details/workflow-summary-details.constants';
911
import { type WorkflowSummaryDetailsConfig } from '../workflow-summary-details/workflow-summary-details.types';
1012

1113
const workflowSummaryDetailsConfig: WorkflowSummaryDetailsConfig[] = [
@@ -144,6 +146,69 @@ const workflowSummaryDetailsConfig: WorkflowSummaryDetailsConfig[] = [
144146
return !(runId && workflowId && domain && decodedPageUrlParams.cluster);
145147
},
146148
},
149+
{
150+
key: 'activeClusterSelectionStrategy',
151+
getLabel: () => 'Cluster Strategy',
152+
valueComponent: (args) => {
153+
const policy = getActiveClusterSelectionPolicy(args);
154+
155+
return policy
156+
? ACTIVE_CLUSTER_SELECTION_STRATEGY_LABEL_MAP[policy.strategy]
157+
: null;
158+
},
159+
hide: (args) => getActiveClusterSelectionPolicy(args) === null,
160+
},
161+
{
162+
key: 'stickyRegion',
163+
getLabel: () => 'Sticky Region',
164+
valueComponent: (args) => {
165+
const policy = getActiveClusterSelectionPolicy(args);
166+
167+
if (
168+
policy?.strategy !== 'ACTIVE_CLUSTER_SELECTION_STRATEGY_REGION_STICKY'
169+
)
170+
return null;
171+
172+
return policy.activeClusterStickyRegionConfig?.stickyRegion;
173+
},
174+
hide: (args) =>
175+
getActiveClusterSelectionPolicy(args)?.strategy !==
176+
'ACTIVE_CLUSTER_SELECTION_STRATEGY_REGION_STICKY',
177+
},
178+
{
179+
key: 'externalEntityType',
180+
getLabel: () => 'Entity Type',
181+
valueComponent: (args) => {
182+
const policy = getActiveClusterSelectionPolicy(args);
183+
184+
if (
185+
policy?.strategy !== 'ACTIVE_CLUSTER_SELECTION_STRATEGY_EXTERNAL_ENTITY'
186+
)
187+
return null;
188+
189+
return policy.activeClusterExternalEntityConfig?.externalEntityType;
190+
},
191+
hide: (args) =>
192+
getActiveClusterSelectionPolicy(args)?.strategy !==
193+
'ACTIVE_CLUSTER_SELECTION_STRATEGY_EXTERNAL_ENTITY',
194+
},
195+
{
196+
key: 'externalEntityKey',
197+
getLabel: () => 'Entity Key',
198+
valueComponent: (args) => {
199+
const policy = getActiveClusterSelectionPolicy(args);
200+
201+
if (
202+
policy?.strategy !== 'ACTIVE_CLUSTER_SELECTION_STRATEGY_EXTERNAL_ENTITY'
203+
)
204+
return null;
205+
206+
return policy.activeClusterExternalEntityConfig?.externalEntityKey;
207+
},
208+
hide: (args) =>
209+
getActiveClusterSelectionPolicy(args)?.strategy !==
210+
'ACTIVE_CLUSTER_SELECTION_STRATEGY_EXTERNAL_ENTITY',
211+
},
147212
];
148213

149214
export default workflowSummaryDetailsConfig;

src/views/workflow-summary/workflow-summary-details/__tests__/workflow-summary-details.test.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import { render, screen } from '@/test-utils/rtl';
44

55
import { type DescribeWorkflowResponse } from '@/route-handlers/describe-workflow/describe-workflow.types';
66
import formatWorkflowHistoryEvent from '@/utils/data-formatters/format-workflow-history-event';
7-
import { type FormattedHistoryEventForType } from '@/utils/data-formatters/schema/format-history-event-schema';
87
import {
98
completeWorkflowExecutionEvent,
109
startWorkflowExecutionEvent,
1110
} from '@/views/workflow-history/__fixtures__/workflow-history-single-events';
1211

12+
import { mockFormattedFirstEvent } from '../../__fixtures__/formatted-first-history-event';
1313
import WorkflowSummaryDetails from '../workflow-summary-details';
1414
import {
1515
type WorkflowSummaryDetailsConfig,
@@ -57,10 +57,6 @@ const mockWorkflowDetails: DescribeWorkflowResponse = {
5757
};
5858

5959
describe('WorkflowSummaryDetails', () => {
60-
// TODO @assem.hafez enhance typing for formattedFirstHistoryEvent
61-
//@ts-expect-error - TS is complaining about the type of formattedFirstHistoryEvent
62-
const formattedFirstHistoryEvent: FormattedHistoryEventForType<'WorkflowExecutionStarted'> =
63-
formatWorkflowHistoryEvent(startWorkflowExecutionEvent);
6460
const formattedCloseHistoryEvent = formatWorkflowHistoryEvent(
6561
completeWorkflowExecutionEvent
6662
);
@@ -69,7 +65,7 @@ describe('WorkflowSummaryDetails', () => {
6965
<WorkflowSummaryDetails
7066
firstHistoryEvent={startWorkflowExecutionEvent}
7167
closeHistoryEvent={completeWorkflowExecutionEvent}
72-
formattedFirstHistoryEvent={formattedFirstHistoryEvent}
68+
formattedFirstHistoryEvent={mockFormattedFirstEvent}
7369
formattedCloseHistoryEvent={formattedCloseHistoryEvent}
7470
workflowDetails={mockWorkflowDetails}
7571
decodedPageUrlParams={params}
@@ -85,7 +81,7 @@ describe('WorkflowSummaryDetails', () => {
8581
<WorkflowSummaryDetails
8682
firstHistoryEvent={startWorkflowExecutionEvent}
8783
closeHistoryEvent={completeWorkflowExecutionEvent}
88-
formattedFirstHistoryEvent={formattedFirstHistoryEvent}
84+
formattedFirstHistoryEvent={mockFormattedFirstEvent}
8985
formattedCloseHistoryEvent={formattedCloseHistoryEvent}
9086
workflowDetails={mockWorkflowDetails}
9187
decodedPageUrlParams={params}
@@ -103,7 +99,7 @@ describe('WorkflowSummaryDetails', () => {
10399
<WorkflowSummaryDetails
104100
firstHistoryEvent={startWorkflowExecutionEvent}
105101
closeHistoryEvent={completeWorkflowExecutionEvent}
106-
formattedFirstHistoryEvent={formattedFirstHistoryEvent}
102+
formattedFirstHistoryEvent={mockFormattedFirstEvent}
107103
formattedCloseHistoryEvent={formattedCloseHistoryEvent}
108104
workflowDetails={mockWorkflowDetails}
109105
decodedPageUrlParams={params}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { type ActiveClusterSelectionPolicy } from '@/__generated__/proto-ts/uber/cadence/api/v1/ActiveClusterSelectionPolicy';
2+
import { mockDescribeWorkflowResponse } from '@/views/workflow-page/__fixtures__/describe-workflow-response';
3+
import { mockFormattedFirstEvent } from '@/views/workflow-summary/__fixtures__/formatted-first-history-event';
4+
5+
import getActiveClusterSelectionPolicy from '../get-active-cluster-selection-policy';
6+
7+
const mockRegionStickyPolicy: ActiveClusterSelectionPolicy = {
8+
strategyConfig: 'activeClusterStickyRegionConfig',
9+
strategy: 'ACTIVE_CLUSTER_SELECTION_STRATEGY_REGION_STICKY',
10+
activeClusterStickyRegionConfig: {
11+
stickyRegion: 'us-west-1',
12+
},
13+
};
14+
15+
const mockExternalEntityPolicy: ActiveClusterSelectionPolicy = {
16+
strategyConfig: 'activeClusterExternalEntityConfig',
17+
strategy: 'ACTIVE_CLUSTER_SELECTION_STRATEGY_EXTERNAL_ENTITY',
18+
activeClusterExternalEntityConfig: {
19+
externalEntityType: 'customer',
20+
externalEntityKey: 'customer-123',
21+
},
22+
};
23+
24+
describe(getActiveClusterSelectionPolicy.name, () => {
25+
it('returns policy from workflowDetails when available', () => {
26+
const result = getActiveClusterSelectionPolicy({
27+
workflowDetails: {
28+
...mockDescribeWorkflowResponse,
29+
workflowExecutionInfo: {
30+
...mockDescribeWorkflowResponse.workflowExecutionInfo,
31+
activeClusterSelectionPolicy: mockRegionStickyPolicy,
32+
},
33+
},
34+
formattedFirstEvent: mockFormattedFirstEvent,
35+
});
36+
37+
expect(result).toEqual(mockRegionStickyPolicy);
38+
});
39+
40+
it('returns policy from formattedFirstEvent when workflowDetails has no policy', () => {
41+
const formattedFirstEvent = Object.assign({}, mockFormattedFirstEvent);
42+
formattedFirstEvent.activeClusterSelectionPolicy = mockExternalEntityPolicy;
43+
44+
const result = getActiveClusterSelectionPolicy({
45+
workflowDetails: mockDescribeWorkflowResponse,
46+
formattedFirstEvent,
47+
});
48+
49+
expect(result).toEqual(mockExternalEntityPolicy);
50+
});
51+
52+
it('returns null when no policy is available in either source', () => {
53+
const result = getActiveClusterSelectionPolicy({
54+
workflowDetails: mockDescribeWorkflowResponse,
55+
formattedFirstEvent: mockFormattedFirstEvent,
56+
});
57+
58+
expect(result).toBeNull();
59+
});
60+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { type ActiveClusterSelectionPolicy } from '@/__generated__/proto-ts/uber/cadence/api/v1/ActiveClusterSelectionPolicy';
2+
3+
import { type WorkflowSummaryFieldArgs } from '../workflow-summary-details.types';
4+
5+
export default function getActiveClusterSelectionPolicy({
6+
workflowDetails,
7+
formattedFirstEvent,
8+
}: Pick<
9+
WorkflowSummaryFieldArgs,
10+
'workflowDetails' | 'formattedFirstEvent'
11+
>): ActiveClusterSelectionPolicy | null {
12+
const policy =
13+
workflowDetails.workflowExecutionInfo?.activeClusterSelectionPolicy ||
14+
formattedFirstEvent?.activeClusterSelectionPolicy;
15+
16+
return policy ?? null;
17+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { type ActiveClusterSelectionStrategy } from '@/__generated__/proto-ts/uber/cadence/api/v1/ActiveClusterSelectionStrategy';
2+
3+
export const ACTIVE_CLUSTER_SELECTION_STRATEGY_LABEL_MAP = {
4+
ACTIVE_CLUSTER_SELECTION_STRATEGY_INVALID: 'Invalid',
5+
ACTIVE_CLUSTER_SELECTION_STRATEGY_REGION_STICKY: 'Region Sticky',
6+
ACTIVE_CLUSTER_SELECTION_STRATEGY_EXTERNAL_ENTITY: 'External Entity',
7+
} as const satisfies Record<ActiveClusterSelectionStrategy, string>;

src/views/workflow-summary/workflow-summary-details/workflow-summary-details.types.ts

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import {
66
} from '@/utils/data-formatters/schema/format-history-event-schema';
77
import type { WorkflowPageTabContentProps } from '@/views/workflow-page/workflow-page-tab-content/workflow-page-tab-content.types';
88

9-
type FormattedFirstHistoryEvent =
9+
export type FormattedFirstHistoryEvent =
1010
FormattedHistoryEventForType<'WorkflowExecutionStarted'> | null;
11+
1112
export type Props = {
1213
firstHistoryEvent: HistoryEvent;
1314
closeHistoryEvent: HistoryEvent | null;
@@ -21,23 +22,18 @@ export type WorkflowSummaryDetailsComponent =
2122
| keyof JSX.IntrinsicElements
2223
| React.JSXElementConstructor<any>;
2324

25+
export type WorkflowSummaryFieldArgs = {
26+
firstEvent: HistoryEvent;
27+
closeEvent: HistoryEvent | null;
28+
formattedFirstEvent: FormattedFirstHistoryEvent;
29+
formattedCloseEvent: FormattedHistoryEvent | null;
30+
workflowDetails: DescribeWorkflowResponse;
31+
decodedPageUrlParams: Props['decodedPageUrlParams'];
32+
};
33+
2434
export type WorkflowSummaryDetailsConfig = {
2535
key: string;
2636
getLabel: () => string;
27-
valueComponent: React.ComponentType<{
28-
firstEvent: HistoryEvent;
29-
closeEvent: HistoryEvent | null;
30-
formattedFirstEvent: FormattedFirstHistoryEvent;
31-
formattedCloseEvent: FormattedHistoryEvent | null;
32-
workflowDetails: DescribeWorkflowResponse;
33-
decodedPageUrlParams: Props['decodedPageUrlParams'];
34-
}>;
35-
hide?: (args: {
36-
firstEvent: HistoryEvent;
37-
closeEvent: HistoryEvent | null;
38-
formattedFirstEvent: FormattedFirstHistoryEvent;
39-
formattedCloseEvent: FormattedHistoryEvent | null;
40-
workflowDetails: DescribeWorkflowResponse;
41-
decodedPageUrlParams: Props['decodedPageUrlParams'];
42-
}) => boolean;
37+
valueComponent: React.ComponentType<WorkflowSummaryFieldArgs>;
38+
hide?: (args: WorkflowSummaryFieldArgs) => boolean;
4339
};

0 commit comments

Comments
 (0)