Skip to content

Commit 042ea5b

Browse files
authored
feat: Update useSerialLogs to accept URL params (#5674)
1 parent 9755ea1 commit 042ea5b

4 files changed

Lines changed: 341 additions & 4 deletions

File tree

static/js/publisher/hooks/__tests__/useSerialLogs.test.tsx

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,168 @@ describe("useSerialLogs", () => {
9090
});
9191
});
9292

93+
test("returns serial logs data with pageSize param", async () => {
94+
server.use(
95+
http.get(
96+
"/api/store/test-brand-id/models/test-model/serial-log",
97+
({ request }) => {
98+
const url = new URL(request.url);
99+
100+
expect(url.searchParams.get("page-size")).toBe("10");
101+
return HttpResponse.json({
102+
data: serialLogsResponse,
103+
success: true,
104+
});
105+
},
106+
),
107+
);
108+
109+
const { result } = renderHook(
110+
() =>
111+
useSerialLogs("test-brand-id", "test-model", {
112+
pageSize: 10,
113+
}),
114+
{
115+
wrapper: createWrapper(),
116+
},
117+
);
118+
119+
await waitFor(() => {
120+
expect(result.current.isSuccess).toBe(true);
121+
});
122+
123+
expect(result.current.data).toEqual({
124+
data: serialLogsResponse,
125+
success: true,
126+
});
127+
});
128+
129+
test("returns serial logs data with startTime and endTime params", async () => {
130+
server.use(
131+
http.get(
132+
"/api/store/test-brand-id/models/test-model/serial-log",
133+
({ request }) => {
134+
const url = new URL(request.url);
135+
expect(url.searchParams.get("start-time")).toBe(
136+
"2026-03-24T04:00:23.875000",
137+
);
138+
expect(url.searchParams.get("end-time")).toBe(
139+
"2026-03-28T04:00:23.875000",
140+
);
141+
return HttpResponse.json({
142+
data: serialLogsResponse,
143+
success: true,
144+
});
145+
},
146+
),
147+
);
148+
149+
const { result } = renderHook(
150+
() =>
151+
useSerialLogs("test-brand-id", "test-model", {
152+
interval: {
153+
startTime: "2026-03-24T04:00:23.875000",
154+
endTime: "2026-03-28T04:00:23.875000",
155+
},
156+
}),
157+
{
158+
wrapper: createWrapper(),
159+
},
160+
);
161+
162+
await waitFor(() => {
163+
expect(result.current.isSuccess).toBe(true);
164+
});
165+
166+
expect(result.current.data).toEqual({
167+
data: serialLogsResponse,
168+
success: true,
169+
});
170+
});
171+
172+
test("returns serial logs data with nextPage param", async () => {
173+
server.use(
174+
http.get(
175+
"/api/store/test-brand-id/models/test-model/serial-log",
176+
({ request }) => {
177+
const url = new URL(request.url);
178+
179+
expect(url.searchParams.get("next-page")).toBe("nextpagecursor");
180+
return HttpResponse.json({
181+
data: serialLogsResponse,
182+
success: true,
183+
});
184+
},
185+
),
186+
);
187+
188+
const { result } = renderHook(
189+
() =>
190+
useSerialLogs("test-brand-id", "test-model", {
191+
nextPage: "nextpagecursor",
192+
}),
193+
{
194+
wrapper: createWrapper(),
195+
},
196+
);
197+
198+
await waitFor(() => {
199+
expect(result.current.isSuccess).toBe(true);
200+
});
201+
202+
expect(result.current.data).toEqual({
203+
data: serialLogsResponse,
204+
success: true,
205+
});
206+
});
207+
208+
test("returns serial logs data with startTime, endTime, pageSize and nextPage params", async () => {
209+
server.use(
210+
http.get(
211+
"/api/store/test-brand-id/models/test-model/serial-log",
212+
({ request }) => {
213+
const url = new URL(request.url);
214+
expect(url.searchParams.get("start-time")).toBe(
215+
"2026-03-24T04:00:23.875000",
216+
);
217+
expect(url.searchParams.get("end-time")).toBe(
218+
"2026-03-28T04:00:23.875000",
219+
);
220+
expect(url.searchParams.get("page-size")).toBe("10");
221+
expect(url.searchParams.get("next-page")).toBe("nextpagecursor");
222+
return HttpResponse.json({
223+
data: serialLogsResponse,
224+
success: true,
225+
});
226+
},
227+
),
228+
);
229+
230+
const { result } = renderHook(
231+
() =>
232+
useSerialLogs("test-brand-id", "test-model", {
233+
interval: {
234+
startTime: "2026-03-24T04:00:23.875000",
235+
endTime: "2026-03-28T04:00:23.875000",
236+
},
237+
pageSize: 10,
238+
nextPage: "nextpagecursor",
239+
}),
240+
{
241+
wrapper: createWrapper(),
242+
},
243+
);
244+
245+
await waitFor(() => {
246+
expect(result.current.isSuccess).toBe(true);
247+
});
248+
249+
expect(result.current.data).toEqual({
250+
data: serialLogsResponse,
251+
success: true,
252+
});
253+
});
254+
93255
test("returns error if request fails", async () => {
94256
const { result } = renderHook(
95257
() => useSerialLogs("test-brand-id-fail", "test-model"),

static/js/publisher/hooks/useSerialLogs.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,41 @@ import type { SerialLogResponse, ApiResponse } from "../types/shared";
44
const useSerialLogs = (
55
brandId: string | undefined,
66
modelId: string | undefined,
7+
urlSearchParams?: {
8+
pageSize?: number;
9+
nextPage?: string;
10+
interval?: {
11+
startTime: string;
12+
endTime: string;
13+
};
14+
},
715
): UseQueryResult<ApiResponse<SerialLogResponse>, Error> => {
16+
const url = new URL(
17+
`/api/store/${brandId}/models/${modelId}/serial-log`,
18+
window.location.origin,
19+
);
20+
21+
if (urlSearchParams) {
22+
const { interval, pageSize, nextPage } = urlSearchParams;
23+
24+
if (interval) {
25+
url.searchParams.set("start-time", interval.startTime);
26+
url.searchParams.set("end-time", interval.endTime);
27+
}
28+
29+
if (pageSize) {
30+
url.searchParams.set("page-size", pageSize.toString());
31+
}
32+
33+
if (nextPage) {
34+
url.searchParams.set("next-page", nextPage);
35+
}
36+
}
37+
838
return useQuery<ApiResponse<SerialLogResponse>, Error>({
9-
queryKey: ["serials", brandId, modelId],
39+
queryKey: ["serials", brandId, modelId, urlSearchParams],
1040
queryFn: async () => {
11-
const response = await fetch(
12-
`/api/store/${brandId}/models/${modelId}/serial-log`,
13-
);
41+
const response = await fetch(url.toString());
1442
const responseData = await response.json();
1543

1644
return responseData;

tests/endpoints/tests_models.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,3 +427,142 @@ def test_get_serial_log_general_error(self, mock_get_serial_log):
427427
self.assertEqual(response.status_code, 500)
428428
self.assertFalse(data["success"])
429429
self.assertEqual(data["message"], "Internal server error")
430+
431+
@patch(
432+
"canonicalwebteam.store_api.publishergw.PublisherGW"
433+
+ ".get_store_model_serial_logs"
434+
)
435+
def test_get_serial_log_with_time_range(self, mock_get_serial_log):
436+
mock_serial_log = []
437+
mock_get_serial_log.return_value = mock_serial_log
438+
response = self.client.get(
439+
"/api/store/1/models/test-model/serial-log"
440+
"?start-time=2026-06-01T00:00:00Z"
441+
"&end-time=2026-06-30T23:59:59Z"
442+
)
443+
data = response.json
444+
445+
self.assertEqual(response.status_code, 200)
446+
self.assertTrue(data["success"])
447+
self.assertEqual(data["data"], [])
448+
449+
mock_get_serial_log.assert_called_once()
450+
# Arguments are passed positionally:
451+
# (session, store_id, model_name, start_time, end_time, page_size)
452+
self.assertEqual(
453+
mock_get_serial_log.call_args.args[3],
454+
"2026-06-01T00:00:00Z",
455+
)
456+
self.assertEqual(
457+
mock_get_serial_log.call_args.args[4],
458+
"2026-06-30T23:59:59Z",
459+
)
460+
461+
@patch(
462+
"canonicalwebteam.store_api.publishergw.PublisherGW"
463+
+ ".get_store_model_serial_logs"
464+
)
465+
def test_get_serial_log_with_page_size(self, mock_get_serial_log):
466+
mock_serial_log = [
467+
{
468+
"brand-id": "test-brand-id",
469+
"created-at": "2026-03-23T04:00:23.875000",
470+
"model-name": "test-model",
471+
"serial": "test-serial",
472+
}
473+
]
474+
mock_get_serial_log.return_value = mock_serial_log
475+
response = self.client.get(
476+
"/api/store/1/models/test-model/serial-log?page-size=50"
477+
)
478+
data = response.json
479+
480+
self.assertEqual(response.status_code, 200)
481+
self.assertTrue(data["success"])
482+
self.assertEqual(data["data"], mock_serial_log)
483+
484+
mock_get_serial_log.assert_called_once()
485+
# Arguments are passed positionally:
486+
# session, store_id, model_name, start_time,
487+
# end_time, page_size, cursor
488+
self.assertEqual(
489+
mock_get_serial_log.call_args.args[5],
490+
"50",
491+
)
492+
493+
@patch(
494+
"canonicalwebteam.store_api.publishergw.PublisherGW"
495+
+ ".get_store_model_serial_logs"
496+
)
497+
def test_get_serial_log_with_next_page(self, mock_get_serial_log):
498+
mock_serial_log = [
499+
{
500+
"brand-id": "test-brand-id",
501+
"created-at": "2026-03-23T04:00:23.875000",
502+
"model-name": "test-model",
503+
"serial": "test-serial",
504+
}
505+
]
506+
mock_get_serial_log.return_value = mock_serial_log
507+
response = self.client.get(
508+
"/api/store/1/models/test-model/serial-log?next-page=nextpage"
509+
)
510+
data = response.json
511+
512+
self.assertEqual(response.status_code, 200)
513+
self.assertTrue(data["success"])
514+
self.assertEqual(data["data"], mock_serial_log)
515+
516+
mock_get_serial_log.assert_called_once()
517+
# Arguments are passed positionally:
518+
# session, store_id, model_name, start_time,
519+
# end_time, page_size, cursor
520+
self.assertEqual(
521+
mock_get_serial_log.call_args.args[6],
522+
"nextpage",
523+
)
524+
525+
@patch(
526+
"canonicalwebteam.store_api.publishergw.PublisherGW"
527+
+ ".get_store_model_serial_logs"
528+
)
529+
def test_get_serial_log_with_all_parameters(self, mock_get_serial_log):
530+
mock_serial_log = [
531+
{
532+
"brand-id": "test-brand-id",
533+
"created-at": "2026-03-23T04:00:23.875000",
534+
"model-name": "test-model",
535+
"serial": "test-serial",
536+
}
537+
]
538+
mock_get_serial_log.return_value = mock_serial_log
539+
response = self.client.get(
540+
"/api/store/1/models/test-model/serial-log"
541+
"?start-time=2026-01-01T00:00:00Z"
542+
"&end-time=2026-12-31T23:59:59Z"
543+
"&page-size=25"
544+
"&next-page=nextpage"
545+
)
546+
data = response.json
547+
548+
self.assertEqual(response.status_code, 200)
549+
self.assertTrue(data["success"])
550+
self.assertEqual(data["data"], mock_serial_log)
551+
552+
mock_get_serial_log.assert_called_once()
553+
# Arguments are passed positionally:
554+
# session, store_id, model_name, start_time,
555+
# end_time, page_size, cursor
556+
self.assertEqual(
557+
mock_get_serial_log.call_args.args[3],
558+
"2026-01-01T00:00:00Z",
559+
)
560+
self.assertEqual(
561+
mock_get_serial_log.call_args.args[4],
562+
"2026-12-31T23:59:59Z",
563+
)
564+
self.assertEqual(
565+
mock_get_serial_log.call_args.args[5],
566+
"25",
567+
)
568+
self.assertEqual(mock_get_serial_log.call_args.args[6], "nextpage")

webapp/endpoints/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,13 +353,21 @@ def create_remodel_allowlist(store_id: str):
353353
@login_required
354354
@exchange_required
355355
def get_serial_log(store_id: str, model_name: str):
356+
start_time = flask.request.args.get("start-time")
357+
end_time = flask.request.args.get("end-time")
358+
page_size = flask.request.args.get("page-size")
359+
cursor = flask.request.args.get("next-page")
356360
res = {}
357361

358362
try:
359363
logs = publisher_gateway.get_store_model_serial_logs(
360364
flask.session,
361365
store_id,
362366
model_name,
367+
start_time,
368+
end_time,
369+
page_size,
370+
cursor,
363371
)
364372
res["data"] = logs
365373
res["success"] = True

0 commit comments

Comments
 (0)