Skip to content

Commit beba3e7

Browse files
feat: Hook to fetch & filter Failover History of a domain (#1075)
Add hook for fetching and filtering failover history Add helper function to check if a ClusterFailover matches a given cluster attribute Add some types/constants for domain page failovers Signed-off-by: Adhitya Mamallan <[email protected]>
1 parent bfb8d93 commit beba3e7

File tree

9 files changed

+612
-0
lines changed

9 files changed

+612
-0
lines changed

src/route-handlers/list-failover-history/list-failover-history.types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type z } from 'zod';
22

3+
import { type FailoverEvent as FailoverEventProto } from '@/__generated__/proto-ts/uber/cadence/api/v1/FailoverEvent';
34
import { type ListFailoverHistoryResponse as ListFailoverHistoryResponseProto } from '@/__generated__/proto-ts/uber/cadence/api/v1/ListFailoverHistoryResponse';
45
import { type DefaultMiddlewaresContext } from '@/utils/route-handlers-middleware';
56

@@ -20,4 +21,6 @@ export type ListFailoverHistoryRequestQueryParams = z.input<
2021

2122
export type ListFailoverHistoryResponse = ListFailoverHistoryResponseProto;
2223

24+
export type FailoverEvent = FailoverEventProto;
25+
2326
export type Context = DefaultMiddlewaresContext;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { type FailoverEvent } from '@/route-handlers/list-failover-history/list-failover-history.types';
2+
3+
export const PRIMARY_CLUSTER_SCOPE = 'primary';
4+
5+
export const FAILOVER_TYPE_LABEL_MAP: Record<
6+
FailoverEvent['failoverType'],
7+
string
8+
> = {
9+
FAILOVER_TYPE_INVALID: 'Invalid',
10+
FAILOVER_TYPE_FORCE: 'Force',
11+
FAILOVER_TYPE_GRACEFUL: 'Graceful',
12+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { type FailoverEvent } from '@/route-handlers/list-failover-history/list-failover-history.types';
2+
3+
export type ClusterFailover = FailoverEvent['clusterFailovers'][number];
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import { PRIMARY_CLUSTER_SCOPE } from '../../domain-page-failovers/domain-page-failovers.constants';
2+
import { type ClusterFailover } from '../../domain-page-failovers/domain-page-failovers.types';
3+
import clusterFailoverMatchesAttribute from '../cluster-failover-matches-attribute';
4+
5+
describe(clusterFailoverMatchesAttribute.name, () => {
6+
it('should return true when clusterAttribute is null and scope is PRIMARY_CLUSTER_SCOPE', () => {
7+
const clusterFailover: ClusterFailover = {
8+
fromCluster: {
9+
activeClusterName: 'cluster1',
10+
failoverVersion: '1',
11+
},
12+
toCluster: {
13+
activeClusterName: 'cluster2',
14+
failoverVersion: '2',
15+
},
16+
clusterAttribute: null,
17+
};
18+
19+
expect(
20+
clusterFailoverMatchesAttribute(clusterFailover, PRIMARY_CLUSTER_SCOPE)
21+
).toBe(true);
22+
});
23+
24+
it('should return false when clusterAttribute is null and scope is not PRIMARY_CLUSTER_SCOPE', () => {
25+
const clusterFailover: ClusterFailover = {
26+
fromCluster: {
27+
activeClusterName: 'cluster1',
28+
failoverVersion: '1',
29+
},
30+
toCluster: {
31+
activeClusterName: 'cluster2',
32+
failoverVersion: '2',
33+
},
34+
clusterAttribute: null,
35+
};
36+
37+
expect(
38+
clusterFailoverMatchesAttribute(clusterFailover, 'other-scope')
39+
).toBe(false);
40+
});
41+
42+
it('should return false when clusterAttribute is null and scope is undefined', () => {
43+
const clusterFailover: ClusterFailover = {
44+
fromCluster: {
45+
activeClusterName: 'cluster1',
46+
failoverVersion: '1',
47+
},
48+
toCluster: {
49+
activeClusterName: 'cluster2',
50+
failoverVersion: '2',
51+
},
52+
clusterAttribute: null,
53+
};
54+
55+
expect(clusterFailoverMatchesAttribute(clusterFailover)).toBe(false);
56+
});
57+
58+
it('should return true when clusterAttribute scope matches and no value is provided', () => {
59+
const clusterFailover: ClusterFailover = {
60+
fromCluster: {
61+
activeClusterName: 'cluster1',
62+
failoverVersion: '1',
63+
},
64+
toCluster: {
65+
activeClusterName: 'cluster2',
66+
failoverVersion: '2',
67+
},
68+
clusterAttribute: {
69+
scope: 'city',
70+
name: 'new_york',
71+
},
72+
};
73+
74+
expect(clusterFailoverMatchesAttribute(clusterFailover, 'city')).toBe(true);
75+
});
76+
77+
it('should return false when clusterAttribute scope does not match and no value is provided', () => {
78+
const clusterFailover: ClusterFailover = {
79+
fromCluster: {
80+
activeClusterName: 'cluster1',
81+
failoverVersion: '1',
82+
},
83+
toCluster: {
84+
activeClusterName: 'cluster2',
85+
failoverVersion: '2',
86+
},
87+
clusterAttribute: {
88+
scope: 'city',
89+
name: 'new_york',
90+
},
91+
};
92+
93+
expect(
94+
clusterFailoverMatchesAttribute(clusterFailover, 'other-scope')
95+
).toBe(false);
96+
});
97+
98+
it('should return true when clusterAttribute scope and name both match', () => {
99+
const clusterFailover: ClusterFailover = {
100+
fromCluster: {
101+
activeClusterName: 'cluster1',
102+
failoverVersion: '1',
103+
},
104+
toCluster: {
105+
activeClusterName: 'cluster2',
106+
failoverVersion: '2',
107+
},
108+
clusterAttribute: {
109+
scope: 'city',
110+
name: 'new_york',
111+
},
112+
};
113+
114+
expect(
115+
clusterFailoverMatchesAttribute(clusterFailover, 'city', 'new_york')
116+
).toBe(true);
117+
});
118+
119+
it('should return false when clusterAttribute scope matches but name does not match', () => {
120+
const clusterFailover: ClusterFailover = {
121+
fromCluster: {
122+
activeClusterName: 'cluster1',
123+
failoverVersion: '1',
124+
},
125+
toCluster: {
126+
activeClusterName: 'cluster2',
127+
failoverVersion: '2',
128+
},
129+
clusterAttribute: {
130+
scope: 'city',
131+
name: 'new_york',
132+
},
133+
};
134+
135+
expect(
136+
clusterFailoverMatchesAttribute(clusterFailover, 'city', 'san_francisco')
137+
).toBe(false);
138+
});
139+
140+
it('should return false when clusterAttribute scope does not match even if name matches', () => {
141+
const clusterFailover: ClusterFailover = {
142+
fromCluster: {
143+
activeClusterName: 'cluster1',
144+
failoverVersion: '1',
145+
},
146+
toCluster: {
147+
activeClusterName: 'cluster2',
148+
failoverVersion: '2',
149+
},
150+
clusterAttribute: {
151+
scope: 'city',
152+
name: 'new_york',
153+
},
154+
};
155+
156+
expect(
157+
clusterFailoverMatchesAttribute(
158+
clusterFailover,
159+
'other-scope',
160+
'new_york'
161+
)
162+
).toBe(false);
163+
});
164+
165+
it('should return false when clusterAttribute scope does not match and value is provided', () => {
166+
const clusterFailover: ClusterFailover = {
167+
fromCluster: {
168+
activeClusterName: 'cluster1',
169+
failoverVersion: '1',
170+
},
171+
toCluster: {
172+
activeClusterName: 'cluster2',
173+
failoverVersion: '2',
174+
},
175+
clusterAttribute: {
176+
scope: 'city',
177+
name: 'new_york',
178+
},
179+
};
180+
181+
expect(
182+
clusterFailoverMatchesAttribute(
183+
clusterFailover,
184+
'other-scope',
185+
'san_francisco'
186+
)
187+
).toBe(false);
188+
});
189+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { PRIMARY_CLUSTER_SCOPE } from '../domain-page-failovers/domain-page-failovers.constants';
2+
import { type ClusterFailover } from '../domain-page-failovers/domain-page-failovers.types';
3+
4+
export default function clusterFailoverMatchesAttribute(
5+
clusterFailover: ClusterFailover,
6+
scope?: string,
7+
value?: string
8+
) {
9+
const attribute = clusterFailover.clusterAttribute;
10+
if (attribute === null) return scope === PRIMARY_CLUSTER_SCOPE;
11+
12+
const scopeMatches = attribute.scope === scope;
13+
if (!value) return scopeMatches;
14+
15+
return scopeMatches && attribute.name === value;
16+
}

0 commit comments

Comments
 (0)