Skip to content

Commit 2381da5

Browse files
committed
fix(pr)
1 parent 017fd14 commit 2381da5

File tree

2 files changed

+150
-41
lines changed

2 files changed

+150
-41
lines changed

static/gsApp/hooks/useMetricDetectorLimit.spec.tsx

Lines changed: 128 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ describe('useMetricDetectorLimit', () => {
4444
SubscriptionStore.init();
4545
});
4646

47-
it('returns no limits when feature flag is disabled', () => {
47+
it('handles feature flag is disabled', () => {
4848
const wrapper = createWrapper(mockOrganizationWithoutFeature);
49-
MockApiClient.addMockResponse({
49+
const detectorsRequest = MockApiClient.addMockResponse({
5050
url: '/organizations/org-slug/detectors/',
51+
headers: {'X-Hits': '5'},
5152
body: [],
5253
});
5354

@@ -60,23 +61,43 @@ describe('useMetricDetectorLimit', () => {
6061
isLoading: false,
6162
isError: false,
6263
});
64+
65+
expect(detectorsRequest).not.toHaveBeenCalled();
6366
});
6467

65-
it('handles no subscription data (defaults to 0)', () => {
68+
it('handles no subscription data', async () => {
6669
const wrapper = createWrapper(mockOrganization);
6770

6871
SubscriptionStore.set(mockOrganization.slug, null as any);
6972
const detectorsRequest = MockApiClient.addMockResponse({
7073
url: '/organizations/org-slug/detectors/',
74+
headers: {'X-Hits': '2'},
7175
body: [],
7276
});
7377

7478
const {result} = renderHook(() => useMetricDetectorLimit(), {wrapper});
7579

76-
expect(result.current.detectorLimit).toBe(0);
77-
expect(result.current.isLoading).toBe(true);
80+
await waitFor(() => {
81+
expect(result.current?.isLoading).toBe(false);
82+
});
7883

79-
expect(detectorsRequest).toHaveBeenCalled();
84+
expect(result.current).toEqual({
85+
hasReachedLimit: false,
86+
detectorLimit: -1,
87+
detectorCount: 2,
88+
isLoading: false,
89+
isError: false,
90+
});
91+
92+
expect(detectorsRequest).toHaveBeenCalledWith(
93+
'/organizations/org-slug/detectors/',
94+
expect.objectContaining({
95+
query: expect.objectContaining({
96+
query: 'type:metric',
97+
per_page: 0,
98+
}),
99+
})
100+
);
80101
});
81102

82103
it('handles detectors count is below limit', async () => {
@@ -96,11 +117,8 @@ describe('useMetricDetectorLimit', () => {
96117

97118
const detectorsRequest = MockApiClient.addMockResponse({
98119
url: '/organizations/org-slug/detectors/',
99-
body: [
100-
{id: '1', name: 'Detector 1'},
101-
{id: '2', name: 'Detector 2'},
102-
{id: '3', name: 'Detector 3'},
103-
],
120+
headers: {'X-Hits': '3'},
121+
body: [],
104122
});
105123

106124
const {result} = renderHook(() => useMetricDetectorLimit(), {wrapper});
@@ -117,7 +135,15 @@ describe('useMetricDetectorLimit', () => {
117135
isError: false,
118136
});
119137

120-
expect(detectorsRequest).toHaveBeenCalledTimes(1);
138+
expect(detectorsRequest).toHaveBeenCalledWith(
139+
'/organizations/org-slug/detectors/',
140+
expect.objectContaining({
141+
query: expect.objectContaining({
142+
query: 'type:metric',
143+
per_page: 0,
144+
}),
145+
})
146+
);
121147
});
122148

123149
it('handles detectors count equals limit', async () => {
@@ -137,11 +163,44 @@ describe('useMetricDetectorLimit', () => {
137163

138164
MockApiClient.addMockResponse({
139165
url: '/organizations/org-slug/detectors/',
140-
body: [
141-
{id: '1', name: 'Detector 1'},
142-
{id: '2', name: 'Detector 2'},
143-
{id: '3', name: 'Detector 3'},
144-
],
166+
headers: {'X-Hits': '3'},
167+
body: [],
168+
});
169+
170+
const {result} = renderHook(() => useMetricDetectorLimit(), {wrapper});
171+
172+
await waitFor(() => {
173+
expect(result.current.isLoading).toBe(false);
174+
});
175+
176+
expect(result.current).toEqual({
177+
hasReachedLimit: true,
178+
detectorLimit: 3,
179+
detectorCount: 3,
180+
isLoading: false,
181+
isError: false,
182+
});
183+
});
184+
185+
it('handles detector limit is -1', async () => {
186+
const wrapper = createWrapper(mockOrganization);
187+
188+
const subscription = SubscriptionFixture({
189+
organization: mockOrganization,
190+
planDetails: {
191+
...SubscriptionFixture({
192+
organization: mockOrganization,
193+
}).planDetails,
194+
metricDetectorLimit: -1,
195+
},
196+
});
197+
198+
SubscriptionStore.set(mockOrganization.slug, subscription);
199+
200+
const detectorsRequest = MockApiClient.addMockResponse({
201+
url: '/organizations/org-slug/detectors/',
202+
headers: {'X-Hits': '10'},
203+
body: [],
145204
});
146205

147206
const {result} = renderHook(() => useMetricDetectorLimit(), {wrapper});
@@ -150,11 +209,15 @@ describe('useMetricDetectorLimit', () => {
150209
expect(result.current.isLoading).toBe(false);
151210
});
152211

153-
expect(result.current.hasReachedLimit).toBe(true);
154-
expect(result.current.detectorLimit).toBe(3);
155-
expect(result.current.detectorCount).toBe(3);
156-
expect(result.current.isLoading).toBe(false);
157-
expect(result.current.isError).toBe(false);
212+
expect(result.current).toEqual({
213+
hasReachedLimit: false,
214+
detectorLimit: -1,
215+
detectorCount: 10,
216+
isLoading: false,
217+
isError: false,
218+
});
219+
220+
expect(detectorsRequest).toHaveBeenCalled();
158221
});
159222

160223
it('handles detectors API error gracefully', async () => {
@@ -183,10 +246,48 @@ describe('useMetricDetectorLimit', () => {
183246
expect(result.current.isLoading).toBe(false);
184247
});
185248

186-
expect(result.current.isError).toBe(true);
187-
expect(result.current.detectorLimit).toBe(20);
188-
expect(result.current.detectorCount).toBe(0);
189-
expect(result.current.hasReachedLimit).toBe(false);
190-
expect(result.current.isLoading).toBe(false);
249+
expect(result.current).toEqual({
250+
hasReachedLimit: false,
251+
detectorLimit: 20,
252+
detectorCount: -1,
253+
isLoading: false,
254+
isError: true,
255+
});
256+
});
257+
258+
it('handles missing X-Hits header as error', async () => {
259+
const wrapper = createWrapper(mockOrganization);
260+
261+
const subscription = SubscriptionFixture({
262+
organization: mockOrganization,
263+
planDetails: {
264+
...SubscriptionFixture({
265+
organization: mockOrganization,
266+
}).planDetails,
267+
metricDetectorLimit: 5,
268+
},
269+
});
270+
271+
SubscriptionStore.set(mockOrganization.slug, subscription);
272+
273+
MockApiClient.addMockResponse({
274+
url: '/organizations/org-slug/detectors/',
275+
body: [],
276+
// No X-Hits header
277+
});
278+
279+
const {result} = renderHook(() => useMetricDetectorLimit(), {wrapper});
280+
281+
await waitFor(() => {
282+
expect(result.current.isLoading).toBe(false);
283+
});
284+
285+
expect(result.current).toEqual({
286+
hasReachedLimit: false,
287+
detectorLimit: 5,
288+
detectorCount: -1,
289+
isLoading: false,
290+
isError: true,
291+
});
191292
});
192293
});

static/gsApp/hooks/useMetricDetectorLimit.tsx

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,51 @@ import {useDetectorsQuery} from 'sentry/views/detectors/hooks/index';
33

44
import useSubscription from 'getsentry/hooks/useSubscription';
55

6-
interface MetricDetectorLimitResponse {
6+
type MetricDetectorLimitResponse = {
77
detectorCount: number;
88
detectorLimit: number;
99
hasReachedLimit: boolean;
1010
isError: boolean;
1111
isLoading: boolean;
12-
}
12+
};
1313

1414
const UNLIMITED_QUOTA = -1;
15+
const ERROR_COUNT = -1;
1516

1617
export function useMetricDetectorLimit(): MetricDetectorLimitResponse {
1718
const organization = useOrganization();
1819
const subscription = useSubscription();
19-
const {data: detectors, isLoading, isError} = useDetectorsQuery();
20-
21-
const detectorLimit = subscription?.planDetails?.metricDetectorLimit ?? 0;
22-
23-
if (
24-
!organization.features.includes('workflow-engine-metric-detector-limit') ||
25-
detectorLimit === UNLIMITED_QUOTA
26-
) {
20+
const has_flag = organization.features.includes(
21+
'workflow-engine-metric-detector-limit'
22+
);
23+
24+
const {isLoading, isError, getResponseHeader} = useDetectorsQuery(
25+
{
26+
query: 'type:metric',
27+
limit: 0,
28+
},
29+
{enabled: has_flag}
30+
);
31+
32+
const hits = getResponseHeader?.('X-Hits');
33+
const detectorCount = hits ? parseInt(hits, 10) : ERROR_COUNT;
34+
const detectorLimit = subscription?.planDetails?.metricDetectorLimit ?? UNLIMITED_QUOTA;
35+
36+
if (!has_flag || detectorLimit === UNLIMITED_QUOTA) {
2737
return {
2838
hasReachedLimit: false,
2939
detectorLimit: UNLIMITED_QUOTA,
30-
detectorCount: -1,
40+
detectorCount,
3141
isLoading: false,
3242
isError: false,
3343
};
3444
}
3545

36-
const detectorCount = detectors?.length || 0;
37-
3846
return {
3947
detectorCount,
4048
detectorLimit,
4149
hasReachedLimit: detectorCount >= detectorLimit,
4250
isLoading,
43-
isError,
51+
isError: isError || detectorCount === ERROR_COUNT,
4452
};
4553
}

0 commit comments

Comments
 (0)