Skip to content

Commit 563bec1

Browse files
committed
chore: migrated trace-route query from axios to fetch
1 parent c465b29 commit 563bec1

3 files changed

Lines changed: 92 additions & 75 deletions

File tree

src/components/trace-route/trace-route.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { useTraceRouteQuery } from '@/hooks/use-trace-route-query';
22
import { useCommonStore, type Profile } from '@/stores/common-store';
33
import { useTraceRouteStore } from '@/stores/trace-route-store';
4-
import axios from 'axios';
54
import { useEffect, useRef, useState } from 'react';
65
import { toast } from 'sonner';
76

@@ -122,6 +121,7 @@ export const TraceRouteControl = () => {
122121
}, [activeRouteIndex, traceRouteResults.data]);
123122

124123
useEffect(() => {
124+
isMountedRef.current = true;
125125
return () => {
126126
isMountedRef.current = false;
127127
if (loadingTimeoutRef.current !== null) {
@@ -227,8 +227,12 @@ export const TraceRouteControl = () => {
227227
return;
228228
}
229229
clearTraceRoute();
230-
if (axios.isAxiosError(error) && error.response) {
231-
const payload = (error.response.data ?? {}) as TraceRouteErrorPayload;
230+
const payload =
231+
typeof error === 'object' && error !== null && 'payload' in error
232+
? ((error as { payload?: TraceRouteErrorPayload }).payload ?? {})
233+
: null;
234+
235+
if (payload) {
232236
const statusText = payload.status ?? 'Trace route failed';
233237
let errorMsg =
234238
payload.error ??

src/hooks/use-trace-route-query.spec.ts

Lines changed: 60 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { beforeEach, describe, expect, it, vi } from 'vitest';
2-
import axios from 'axios';
32
import { useTraceRouteQuery } from './use-trace-route-query';
43
import { parseGpxToLatLng } from '@/utils/parse-gpx';
54
import {
@@ -8,12 +7,6 @@ import {
87
showValhallaWarnings,
98
} from '@/utils/valhalla';
109

11-
vi.mock('axios', () => ({
12-
default: {
13-
post: vi.fn(),
14-
},
15-
}));
16-
1710
vi.mock('@/utils/parse-gpx', () => ({
1811
parseGpxToLatLng: vi.fn(),
1912
}));
@@ -94,10 +87,17 @@ const createRouteResponse = () => ({
9487
],
9588
});
9689

90+
const getLastFetchBody = () => {
91+
const call = vi.mocked(fetch).mock.calls.at(-1);
92+
const init = call?.[1] as RequestInit | undefined;
93+
return JSON.parse((init?.body as string) ?? '{}');
94+
};
95+
9796
describe('useTraceRouteQuery', () => {
9897
beforeEach(() => {
9998
vi.clearAllMocks();
10099
vi.mocked(getValhallaUrl).mockReturnValue('http://mock-valhalla');
100+
vi.stubGlobal('fetch', vi.fn());
101101
});
102102

103103
it('should throw when both polyline and file are missing', async () => {
@@ -110,17 +110,21 @@ describe('useTraceRouteQuery', () => {
110110

111111
it('should post encoded polyline request and parse geometry', async () => {
112112
const response = createRouteResponse();
113-
vi.mocked(axios.post).mockResolvedValue({ data: response });
113+
vi.mocked(fetch).mockResolvedValue({
114+
ok: true,
115+
json: vi.fn().mockResolvedValue(response),
116+
} as unknown as Response);
114117

115118
const { traceRoute } = useTraceRouteQuery({
116119
polyline: ' abc\n123 ',
117120
});
118121

119122
const result = await traceRoute();
120123

121-
expect(axios.post).toHaveBeenCalledWith(
122-
'http://mock-valhalla/trace_route',
123-
{
124+
expect(fetch).toHaveBeenCalledWith('http://mock-valhalla/trace_route', {
125+
method: 'POST',
126+
headers: { 'Content-Type': 'application/json' },
127+
body: JSON.stringify({
124128
encoded_polyline: 'abc123',
125129
shape_match: 'map_snap',
126130
costing: 'auto',
@@ -130,9 +134,8 @@ describe('useTraceRouteQuery', () => {
130134
interpolation_distance: 10,
131135
breakage_distance: 50,
132136
},
133-
},
134-
{ headers: { 'Content-Type': 'application/json' } }
135-
);
137+
}),
138+
});
136139

137140
expect(parseDirectionsGeometry).toHaveBeenCalledTimes(2);
138141
expect(showValhallaWarnings).toHaveBeenCalledWith(response.trip.warnings);
@@ -150,14 +153,18 @@ describe('useTraceRouteQuery', () => {
150153
]);
151154

152155
const response = createRouteResponse();
153-
vi.mocked(axios.post).mockResolvedValue({ data: response });
156+
vi.mocked(fetch).mockResolvedValue({
157+
ok: true,
158+
json: vi.fn().mockResolvedValue(response),
159+
} as unknown as Response);
154160

155161
const { traceRoute } = useTraceRouteQuery({ fileText: '<gpx>...</gpx>' });
156162
await traceRoute();
157163

158-
expect(axios.post).toHaveBeenCalledWith(
159-
'http://mock-valhalla/trace_route',
160-
{
164+
expect(fetch).toHaveBeenCalledWith('http://mock-valhalla/trace_route', {
165+
method: 'POST',
166+
headers: { 'Content-Type': 'application/json' },
167+
body: JSON.stringify({
161168
shape: [
162169
{ lat: 52.5, lon: 13.4, type: 'break' },
163170
{ lat: 52.6, lon: 13.5 },
@@ -171,14 +178,16 @@ describe('useTraceRouteQuery', () => {
171178
interpolation_distance: 10,
172179
breakage_distance: 50,
173180
},
174-
},
175-
{ headers: { 'Content-Type': 'application/json' } }
176-
);
181+
}),
182+
});
177183
});
178184

179185
it('should map accuracy and radius to valhalla trace options', async () => {
180186
const response = createRouteResponse();
181-
vi.mocked(axios.post).mockResolvedValue({ data: response });
187+
vi.mocked(fetch).mockResolvedValue({
188+
ok: true,
189+
json: vi.fn().mockResolvedValue(response),
190+
} as unknown as Response);
182191

183192
const { traceRoute } = useTraceRouteQuery({
184193
polyline: 'abc123',
@@ -192,23 +201,20 @@ describe('useTraceRouteQuery', () => {
192201

193202
await traceRoute();
194203

195-
expect(axios.post).toHaveBeenCalledWith(
196-
'http://mock-valhalla/trace_route',
197-
expect.objectContaining({
198-
trace_options: {
199-
gps_accuracy: 7,
200-
search_radius: 60,
201-
interpolation_distance: 15,
202-
breakage_distance: 75,
203-
},
204-
}),
205-
{ headers: { 'Content-Type': 'application/json' } }
206-
);
204+
expect(getLastFetchBody().trace_options).toEqual({
205+
gps_accuracy: 7,
206+
search_radius: 60,
207+
interpolation_distance: 15,
208+
breakage_distance: 75,
209+
});
207210
});
208211

209212
it('should prefer explicit gps_accuracy/search_radius over aliases', async () => {
210213
const response = createRouteResponse();
211-
vi.mocked(axios.post).mockResolvedValue({ data: response });
214+
vi.mocked(fetch).mockResolvedValue({
215+
ok: true,
216+
json: vi.fn().mockResolvedValue(response),
217+
} as unknown as Response);
212218

213219
const { traceRoute } = useTraceRouteQuery({
214220
polyline: 'abc123',
@@ -222,21 +228,18 @@ describe('useTraceRouteQuery', () => {
222228

223229
await traceRoute();
224230

225-
expect(axios.post).toHaveBeenCalledWith(
226-
'http://mock-valhalla/trace_route',
227-
expect.objectContaining({
228-
trace_options: expect.objectContaining({
229-
gps_accuracy: 4,
230-
search_radius: 25,
231-
}),
232-
}),
233-
{ headers: { 'Content-Type': 'application/json' } }
234-
);
231+
expect(getLastFetchBody().trace_options).toMatchObject({
232+
gps_accuracy: 4,
233+
search_radius: 25,
234+
});
235235
});
236236

237237
it('should fallback to defaults for invalid numeric trace options', async () => {
238238
const response = createRouteResponse();
239-
vi.mocked(axios.post).mockResolvedValue({ data: response });
239+
vi.mocked(fetch).mockResolvedValue({
240+
ok: true,
241+
json: vi.fn().mockResolvedValue(response),
242+
} as unknown as Response);
240243

241244
const { traceRoute } = useTraceRouteQuery({
242245
polyline: 'abc123',
@@ -250,23 +253,20 @@ describe('useTraceRouteQuery', () => {
250253

251254
await traceRoute();
252255

253-
expect(axios.post).toHaveBeenCalledWith(
254-
'http://mock-valhalla/trace_route',
255-
expect.objectContaining({
256-
trace_options: {
257-
gps_accuracy: 5,
258-
search_radius: 50,
259-
interpolation_distance: 10,
260-
breakage_distance: 50,
261-
},
262-
}),
263-
{ headers: { 'Content-Type': 'application/json' } }
264-
);
256+
expect(getLastFetchBody().trace_options).toEqual({
257+
gps_accuracy: 5,
258+
search_radius: 50,
259+
interpolation_distance: 10,
260+
breakage_distance: 50,
261+
});
265262
});
266263

267264
it("should map 'car' costing to valhalla 'auto'", async () => {
268265
const response = createRouteResponse();
269-
vi.mocked(axios.post).mockResolvedValue({ data: response });
266+
vi.mocked(fetch).mockResolvedValue({
267+
ok: true,
268+
json: vi.fn().mockResolvedValue(response),
269+
} as unknown as Response);
270270

271271
const { traceRoute } = useTraceRouteQuery({
272272
polyline: 'abc123',
@@ -275,13 +275,7 @@ describe('useTraceRouteQuery', () => {
275275

276276
await traceRoute();
277277

278-
expect(axios.post).toHaveBeenCalledWith(
279-
'http://mock-valhalla/trace_route',
280-
expect.objectContaining({
281-
costing: 'auto',
282-
}),
283-
{ headers: { 'Content-Type': 'application/json' } }
284-
);
278+
expect(getLastFetchBody().costing).toBe('auto');
285279
});
286280

287281
it('should throw when GPX file has fewer than 2 points', async () => {

src/hooks/use-trace-route-query.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
parseDirectionsGeometry,
1010
showValhallaWarnings,
1111
} from '@/utils/valhalla';
12-
import axios from 'axios';
1312

1413
type Shape = {
1514
lat: number;
@@ -35,6 +34,12 @@ interface TraceOptions {
3534
interpolation_distance?: number;
3635
}
3736

37+
type TraceRouteErrorPayload = {
38+
status?: string;
39+
error?: string;
40+
error_code?: number;
41+
};
42+
3843
export const useTraceRouteQuery = ({
3944
polyline,
4045
fileText,
@@ -107,11 +112,25 @@ export const useTraceRouteQuery = ({
107112
throw new Error('GPX must contain at least 2 points.');
108113
}
109114

110-
const { data } = await axios.post<ValhallaRouteResponse>(
111-
`${getValhallaUrl()}/trace_route`,
112-
valhallaRequest.json,
113-
{ headers: { 'Content-Type': 'application/json' } }
114-
);
115+
const response = await fetch(`${getValhallaUrl()}/trace_route`, {
116+
method: 'POST',
117+
headers: { 'Content-Type': 'application/json' },
118+
body: JSON.stringify(valhallaRequest.json),
119+
});
120+
121+
if (!response.ok) {
122+
const errorData =
123+
((await response.json().catch(() => ({}))) as TraceRouteErrorPayload) ??
124+
{};
125+
const error = new Error(
126+
errorData.error || `Trace route failed (${response.status})`
127+
) as Error & { payload?: TraceRouteErrorPayload; status?: number };
128+
error.payload = errorData;
129+
error.status = response.status;
130+
throw error;
131+
}
132+
133+
const data: ValhallaRouteResponse = await response.json();
115134

116135
(data as ParsedDirectionsGeometry).decodedGeometry =
117136
parseDirectionsGeometry(data);

0 commit comments

Comments
 (0)