Skip to content

Commit 2cbb579

Browse files
authored
Merge branch 'master' into feature/14118/replace-usage-of-custom-hooks-with-use-config-value
2 parents f2c345e + ee287c0 commit 2cbb579

14 files changed

Lines changed: 287 additions & 25 deletions

File tree

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ CADENCE_CLUSTERS_NAMES=cluster0,cluster1
3636

3737
Feature flags control various UI features and functionality in `cadence-web`. These can be configured using environment variables.
3838

39-
| Feature | Description | Environment Variable Configuration | Minimum Cadence server Version |
40-
|---------|-------------|---------------------|------------------------|
41-
| Extended Domain Information | Enhanced domain metadata display and help menu UI | `CADENCE_EXTENDED_DOMAIN_INFO_METADATA_ENABLED=true` | N/A |
42-
| Workflow Diagnostics | Workflow diagnostics APIs and UI for debugging workflows | `CADENCE_WORKFLOW_DIAGNOSTICS_ENABLED=true` | 1.2.13+ (1.3.1+ for full functionality) |
39+
| Feature | Description | Environment Variable Configuration | Minimum Cadence server Version |
40+
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | --------------------------------------- |
41+
| Extended Domain Information | Enhanced domain metadata display and help menu UI | `CADENCE_EXTENDED_DOMAIN_INFO_METADATA_ENABLED=true` | N/A |
42+
| Workflow Diagnostics | Workflow diagnostics APIs and UI for debugging workflows | `CADENCE_WORKFLOW_DIAGNOSTICS_ENABLED=true` | 1.2.13+ (1.3.1+ for full functionality) |
43+
| Enable Default Archival Search | Shows the default workflow search input + query input for archival workflows page. Default search is disabled by default as it doesn't work well with S3 implementation | `CADENCE_ARCHIVAL_DEFAULT_SEARCH_ENABLED=true` | N/A |
44+
4345

4446
**Note:** For advanced customization, feature flags can be modified through resolvers in the dynamic config system ([`src/config/dynamic/resolvers`](src/config/dynamic/resolvers)).
4547

src/config/dynamic/dynamic.config.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
ConfigSyncResolverDefinition,
77
} from '../../utils/config/config.types';
88

9+
import archivalDefaultSearchEnabled from './resolvers/archival-default-search-enabled';
910
import clusters from './resolvers/clusters';
1011
import clustersPublic from './resolvers/clusters-public';
1112
import { type PublicClustersConfigs } from './resolvers/clusters-public.types';
@@ -51,6 +52,12 @@ const dynamicConfigs: {
5152
'request',
5253
true
5354
>;
55+
ARCHIVAL_DEFAULT_SEARCH_ENABLED: ConfigAsyncResolverDefinition<
56+
undefined,
57+
boolean,
58+
'request',
59+
true
60+
>;
5461
} = {
5562
CADENCE_WEB_PORT: {
5663
env: 'CADENCE_WEB_PORT',
@@ -85,6 +92,11 @@ const dynamicConfigs: {
8592
evaluateOn: 'request',
8693
isPublic: true,
8794
},
95+
ARCHIVAL_DEFAULT_SEARCH_ENABLED: {
96+
resolver: archivalDefaultSearchEnabled,
97+
evaluateOn: 'request',
98+
isPublic: true,
99+
},
88100
} as const;
89101

90102
export default dynamicConfigs;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default async function archivalDefaultSearchEnabled(): Promise<boolean> {
2+
const enabled =
3+
process.env.CADENCE_ARCHIVAL_DEFAULT_SEARCH_ENABLED === 'true';
4+
5+
return enabled;
6+
}

src/config/dynamic/resolvers/schemas/resolver-schemas.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ const resolverSchemas: ResolverSchemas = {
5454
args: z.undefined(),
5555
returnType: z.boolean(),
5656
},
57+
ARCHIVAL_DEFAULT_SEARCH_ENABLED: {
58+
args: z.undefined(),
59+
returnType: z.boolean(),
60+
},
5761
};
5862

5963
export default resolverSchemas;

src/utils/config/__fixtures__/resolved-config-values.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,6 @@ const mockResolvedConfigValues: LoadedConfigResolvedValues = {
3939
issues: false,
4040
},
4141
WORKFLOW_DIAGNOSTICS_ENABLED: false,
42+
ARCHIVAL_DEFAULT_SEARCH_ENABLED: false,
4243
};
4344
export default mockResolvedConfigValues;
Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { render, screen } from '@/test-utils/rtl';
22

3+
import * as useListWorkflowsModule from '@/views/shared/hooks/use-list-workflows';
4+
import { type WorkflowsHeaderInputType } from '@/views/shared/workflows-header/workflows-header.types';
5+
36
import { mockDomainPageQueryParamsValues } from '../../../domain-page/__fixtures__/domain-page-query-params';
7+
import * as useArchivalInputTypeModule from '../../hooks/use-archival-input-type';
48
import DomainWorkflowsArchivalHeader from '../domain-workflows-archival-header';
59

610
jest.useFakeTimers().setSystemTime(new Date('2023-05-25'));
@@ -11,7 +15,14 @@ jest.mock('@/hooks/use-page-query-params/use-page-query-params', () =>
1115
);
1216

1317
jest.mock('@/views/shared/workflows-header/workflows-header', () =>
14-
jest.fn(() => <div>Workflows Header</div>)
18+
jest.fn(({ forceQueryInputOnly }) => (
19+
<div>
20+
<div>Workflows Header</div>
21+
<div>
22+
Force query input only: {forceQueryInputOnly ? 'true' : 'false'}
23+
</div>
24+
</div>
25+
))
1526
);
1627

1728
jest.mock('@/views/shared/hooks/use-list-workflows', () =>
@@ -20,18 +31,59 @@ jest.mock('@/views/shared/hooks/use-list-workflows', () =>
2031
isFetching: false,
2132
}))
2233
);
34+
jest.mock('../../hooks/use-archival-input-type');
2335

2436
describe(DomainWorkflowsArchivalHeader.name, () => {
25-
it('renders workflows header', async () => {
26-
render(
27-
<DomainWorkflowsArchivalHeader
28-
domain="mock_domain"
29-
cluster="mock_cluster"
30-
timeRangeStart="mock-time-range-start"
31-
timeRangeEnd="mock-time-range-end"
32-
/>
33-
);
34-
37+
it('renders workflows header with query input only', async () => {
38+
setup({});
3539
expect(screen.getByText('Workflows Header')).toBeInTheDocument();
3640
});
41+
42+
it('should pass correct props to workflows header', async () => {
43+
setup({
44+
forceQueryInputOnly: false,
45+
});
46+
expect(
47+
screen.getByText('Force query input only: false')
48+
).toBeInTheDocument();
49+
});
50+
51+
it('should pass inputType to useListWorkflows', async () => {
52+
setup({
53+
inputType: 'query',
54+
});
55+
const useListWorkflowsSpy = jest.spyOn(useListWorkflowsModule, 'default');
56+
expect(useListWorkflowsSpy).toHaveBeenCalledWith(
57+
expect.objectContaining({
58+
inputType: 'query',
59+
})
60+
);
61+
});
3762
});
63+
64+
const setup = ({
65+
forceQueryInputOnly = true,
66+
inputType = 'query',
67+
}: {
68+
forceQueryInputOnly?: boolean;
69+
inputType?: WorkflowsHeaderInputType;
70+
}) => {
71+
const useArchivalInputTypeSpy = jest.spyOn(
72+
useArchivalInputTypeModule,
73+
'default'
74+
);
75+
76+
useArchivalInputTypeSpy.mockReturnValue({
77+
forceQueryInputOnly,
78+
inputType,
79+
});
80+
81+
return render(
82+
<DomainWorkflowsArchivalHeader
83+
domain="mock_domain"
84+
cluster="mock_cluster"
85+
timeRangeStart="mock-time-range-start"
86+
timeRangeEnd="mock-time-range-end"
87+
/>
88+
);
89+
};

src/views/domain-workflows-archival/domain-workflows-archival-header/domain-workflows-archival-header.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import WorkflowsHeader from '@/views/shared/workflows-header/workflows-header';
77

88
import domainWorkflowsArchivalFiltersConfig from '../config/domain-workflows-archival-filters.config';
99
import DOMAIN_WORKFLOWS_ARCHIVAL_PAGE_SIZE from '../config/domain-workflows-archival-page-size.config';
10+
import useArchivalInputType from '../hooks/use-archival-input-type';
1011

1112
import { type Props } from './domain-workflows-archival-header.types';
1213

@@ -17,13 +18,14 @@ export default function DomainWorkflowsArchivalHeader({
1718
timeRangeEnd,
1819
}: Props) {
1920
const [queryParams] = usePageQueryParams(domainPageQueryParamsConfig);
21+
const { forceQueryInputOnly, inputType } = useArchivalInputType();
2022

2123
const { refetch, isFetching } = useListWorkflows({
2224
domain,
2325
cluster,
2426
pageSize: DOMAIN_WORKFLOWS_ARCHIVAL_PAGE_SIZE,
2527
listType: 'archived',
26-
inputType: queryParams.inputTypeArchival,
28+
inputType,
2729
search: queryParams.searchArchival,
2830
statuses: queryParams.statusesArchival,
2931
timeRangeStart,
@@ -43,6 +45,7 @@ export default function DomainWorkflowsArchivalHeader({
4345
refetchQuery={refetch}
4446
isQueryRunning={isFetching}
4547
expandFiltersByDefault={true}
48+
showQueryInputOnly={forceQueryInputOnly}
4649
/>
4750
);
4851
}

src/views/domain-workflows-archival/domain-workflows-archival-table/__tests__/domain-workflows-archival-table.test.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ jest.mock('@/hooks/use-page-query-params/use-page-query-params', () =>
5858
jest.fn(() => [mockDomainPageQueryParamsValues, mockSetQueryParams])
5959
);
6060

61+
jest.mock('../../hooks/use-archival-input-type', () =>
62+
jest.fn(() => ({
63+
forceQueryInputOnly: false,
64+
inputType: mockDomainPageQueryParamsValues.inputTypeArchival,
65+
}))
66+
);
67+
6168
describe(DomainWorkflowsArchivalTable.name, () => {
6269
beforeEach(() => {
6370
jest.clearAllMocks();

src/views/domain-workflows-archival/domain-workflows-archival-table/domain-workflows-archival-table.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import useListWorkflows from '@/views/shared/hooks/use-list-workflows';
1111
import WorkflowsTable from '@/views/shared/workflows-table/workflows-table';
1212

1313
import DOMAIN_WORKFLOWS_ARCHIVAL_PAGE_SIZE from '../config/domain-workflows-archival-page-size.config';
14+
import useArchivalInputType from '../hooks/use-archival-input-type';
1415

1516
import { type Props } from './domain-workflows-archival-table.types';
1617
import getArchivalErrorPanelProps from './helpers/get-archival-error-panel-props';
@@ -25,6 +26,8 @@ export default function DomainWorkflowsArchivalTable({
2526
domainPageQueryParamsConfig
2627
);
2728

29+
const { inputType } = useArchivalInputType();
30+
2831
const {
2932
workflows,
3033
error,
@@ -38,7 +41,7 @@ export default function DomainWorkflowsArchivalTable({
3841
cluster,
3942
listType: 'archived',
4043
pageSize: DOMAIN_WORKFLOWS_ARCHIVAL_PAGE_SIZE,
41-
inputType: queryParams.inputTypeArchival,
44+
inputType,
4245
search: queryParams.searchArchival,
4346
statuses: queryParams.statusesArchival,
4447
timeRangeStart,
@@ -57,7 +60,7 @@ export default function DomainWorkflowsArchivalTable({
5760
<PanelSection>
5861
<ErrorPanel
5962
{...getArchivalErrorPanelProps({
60-
inputType: queryParams.inputTypeArchival,
63+
inputType,
6164
error,
6265
queryString: queryParams.queryArchival,
6366
})}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import React, { Suspense } from 'react';
2+
3+
import { renderHook, waitFor } from '@/test-utils/rtl';
4+
5+
import { type UseSuspenseConfigValueResult } from '@/hooks/use-config-value/use-config-value.types';
6+
import useSuspenseConfigValue from '@/hooks/use-config-value/use-suspense-config-value';
7+
import usePageQueryParams from '@/hooks/use-page-query-params/use-page-query-params';
8+
9+
import useArchivalInputType from '../use-archival-input-type';
10+
11+
jest.mock('@/hooks/use-page-query-params/use-page-query-params');
12+
jest.mock('@/hooks/use-config-value/use-suspense-config-value');
13+
14+
const mockUsePageQueryParams = usePageQueryParams as jest.MockedFunction<any>;
15+
const mockUseSuspenseConfigValue =
16+
useSuspenseConfigValue as jest.MockedFunction<any>;
17+
18+
describe(useArchivalInputType.name, () => {
19+
it('should return forceQueryInputOnly as false when archival default search is enabled', async () => {
20+
const { result } = setup({ archivalDefaultSearchEnabled: true });
21+
22+
await waitFor(() => {
23+
expect(result.current.forceQueryInputOnly).toBe(false);
24+
});
25+
});
26+
27+
it('should return forceQueryInputOnly as true when archival default search is disabled', async () => {
28+
const { result } = setup({ archivalDefaultSearchEnabled: false });
29+
30+
await waitFor(() => {
31+
expect(result.current.forceQueryInputOnly).toBe(true);
32+
});
33+
});
34+
35+
it('should return inputType from queryParams.inputTypeArchival when archival default search is enabled', async () => {
36+
const { result } = setup({
37+
archivalDefaultSearchEnabled: true,
38+
inputTypeArchivalQueryParamValue: 'search',
39+
});
40+
41+
await waitFor(() => {
42+
expect(result.current.inputType).toBe('search');
43+
});
44+
45+
const { result: result2 } = setup({
46+
archivalDefaultSearchEnabled: true,
47+
inputTypeArchivalQueryParamValue: 'query',
48+
});
49+
50+
await waitFor(() => {
51+
expect(result2.current.inputType).toBe('query');
52+
});
53+
});
54+
55+
it('should always return inputType as query when archival default search is disabled', async () => {
56+
const { result } = setup({
57+
archivalDefaultSearchEnabled: false,
58+
inputTypeArchivalQueryParamValue: 'search',
59+
});
60+
61+
await waitFor(() => {
62+
expect(result.current.inputType).toBe('query');
63+
});
64+
});
65+
66+
it('should ignore queryParams.inputTypeArchivalQueryParamValue when archival default search is disabled', async () => {
67+
const { result } = setup({
68+
archivalDefaultSearchEnabled: false,
69+
inputTypeArchivalQueryParamValue: 'query',
70+
});
71+
72+
await waitFor(() => {
73+
expect(result.current.inputType).toBe('query');
74+
});
75+
});
76+
77+
it('should return an object with forceQueryInputOnly and inputType properties', async () => {
78+
const { result } = setup({
79+
archivalDefaultSearchEnabled: true,
80+
inputTypeArchivalQueryParamValue: 'search',
81+
});
82+
83+
await waitFor(() => {
84+
expect(result.current).toHaveProperty('forceQueryInputOnly');
85+
expect(result.current).toHaveProperty('inputType');
86+
expect(typeof result.current.forceQueryInputOnly).toBe('boolean');
87+
expect(['search', 'query']).toContain(result.current.inputType);
88+
});
89+
});
90+
});
91+
92+
function setup({
93+
archivalDefaultSearchEnabled = true,
94+
inputTypeArchivalQueryParamValue = 'search',
95+
}: {
96+
archivalDefaultSearchEnabled?: boolean;
97+
inputTypeArchivalQueryParamValue?: 'search' | 'query';
98+
}) {
99+
mockUsePageQueryParams.mockReturnValue([
100+
{
101+
inputTypeArchival: inputTypeArchivalQueryParamValue,
102+
},
103+
jest.fn(),
104+
]);
105+
106+
mockUseSuspenseConfigValue.mockReturnValue({
107+
data: archivalDefaultSearchEnabled,
108+
} satisfies Pick<
109+
UseSuspenseConfigValueResult<'ARCHIVAL_DEFAULT_SEARCH_ENABLED'>,
110+
'data'
111+
>);
112+
113+
const { result } = renderHook(() => useArchivalInputType(), undefined, {
114+
wrapper: ({ children }: { children: React.ReactNode }) => (
115+
<Suspense>{children}</Suspense>
116+
),
117+
});
118+
119+
return { result };
120+
}

0 commit comments

Comments
 (0)