Skip to content

Commit 1899338

Browse files
authored
develop->staging (#84)
2 parents da98a1f + 0184cb1 commit 1899338

6 files changed

Lines changed: 68 additions & 23 deletions

File tree

services/osm.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,11 @@ export class OsmApiClient extends BaseHttpClient implements ICancelableClient {
253253
}
254254

255255
async getWorkspaceBbox(workspaceId: WorkspaceId) {
256-
const response = await this._get(`workspaces/${workspaceId}/bbox.json`);
257-
258-
if (response.status === 204) {
259-
return undefined
260-
}
261-
262-
return await response.json();
256+
// The workspace bbox lives on the v1 tasking API, owned by WorkspacesClient.
257+
// OsmApiClient is constructed before workspacesClient (which depends on it),
258+
// so we resolve the singleton lazily at call time to avoid the import cycle.
259+
const { workspacesClient } = await import('~/services/index');
260+
return workspacesClient.getWorkspaceBbox(workspaceId);
263261
}
264262

265263
async getExportBbox(id: number) {

services/workspaces.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,23 @@ export class WorkspacesClient extends BaseHttpClient implements ICancelableClien
119119
}
120120
}
121121

122-
getWorkspaceBbox(id: WorkspaceId): Promise<BoundingBox> {
123-
return this.#osmClient.getWorkspaceBbox(id);
122+
async getWorkspaceBbox(id: WorkspaceId): Promise<BoundingBox | undefined> {
123+
const originalBaseUrl = this._baseUrl;
124+
this._baseUrl = this.#newApiUrl;
125+
126+
try {
127+
const response = await this._send(`workspaces/${id}/bbox`, 'GET');
128+
129+
if (response.status === 204) {
130+
return undefined;
131+
}
132+
133+
// The v1 API returns coordinates already in decimal degrees.
134+
return await response.json();
135+
}
136+
finally {
137+
this._baseUrl = originalBaseUrl;
138+
}
124139
}
125140

126141
async createWorkspace(workspace: WorkspaceCreation): Promise<WorkspaceId> {

test/e2e/create-blank.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ async function stubCreateFlow(
6565
route.fulfill({ status: 200, body: '' })
6666
);
6767

68-
// Dashboard map bbox -> 204 so the map init is skipped in headless.
69-
await page.route('**/osm/api/0.6/**/bbox.json', route =>
68+
// Dashboard map bbox (new-API) -> 204 so the map init is skipped in headless.
69+
await page.route('**/workspaces/*/bbox', route =>
7070
route.fulfill({ status: 204, body: '' })
7171
);
7272

test/e2e/export-index.spec.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { aWorkspace, projectGroups } from '../mocks/fixtures';
1313
//
1414
// The Download flow goes through workspacesClient.exportWorkspaceArchive(workspace),
1515
// which branches on workspace.type:
16-
// - pathways: osmClient.getWorkspaceData(id) = GET osm/.../workspaces/{id}/bbox.json
16+
// - pathways: osmClient.getWorkspaceData(id) = GET new-api/workspaces/{id}/bbox
1717
// then GET osm/.../map.json?bbox=... -> {elements:[]} -> buildPathwaysCsvArchive
1818
// (pure client-side zip; NO tdei host, NO polling).
1919
// - osw: osm export XML + tdeiClient.convertDataset (4s job polling).
@@ -36,15 +36,15 @@ async function stubGetWorkspace(page: import('@playwright/test').Page, overrides
3636
});
3737
}
3838

39-
// Stubs the OSM-host calls the pathways download flow makes:
40-
// GET .../workspaces/{id}/bbox.json -> a bbox, then GET .../map.json -> elements.
39+
// Stubs the calls the pathways download flow makes:
40+
// GET new-api/workspaces/{id}/bbox -> a bbox, then GET osm/.../map.json -> elements.
4141
// `gate` (if provided) holds the map.json response open so the loading state can
4242
// be observed before the archive is built.
4343
async function stubPathwaysDownloadOk(
4444
page: import('@playwright/test').Page,
4545
gate?: Promise<void>
4646
) {
47-
await page.route('**/osm/**/workspaces/1/bbox.json', route =>
47+
await page.route('**/workspaces/1/bbox', route =>
4848
route.fulfill({ json: { min_lon: -122.4, min_lat: 47.6, max_lon: -122.3, max_lat: 47.7 } })
4949
);
5050
await page.route('**/osm/**/map.json**', async (route) => {
@@ -151,8 +151,9 @@ test.describe('workspace export landing page', () => {
151151
await seedAuthenticatedSession(page);
152152
await stubGetWorkspace(page, { type: 'pathways' });
153153

154-
// Fail the first OSM call in the download flow so exportWorkspaceArchive throws.
155-
await page.route('**/osm/**/workspaces/1/bbox.json', route =>
154+
// Fail the first call in the download flow so exportWorkspaceArchive throws.
155+
// The bbox lookup now hits the new API; the map fetch stays on the OSM base.
156+
await page.route('**/workspaces/1/bbox', route =>
156157
route.fulfill({ status: 500, body: 'boom' })
157158
);
158159
await page.route('**/osm/**/map.json**', route =>

test/e2e/export-tdei.spec.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { aWorkspace, PROJECT_GROUP_ID } from '../mocks/fixtures';
2121
// so the service `<option>`s shown must match that simulated response.
2222
//
2323
// The upload flow (services/export/tdei.ts, osw branch) runs:
24-
// OSM GET workspaces/{id}/bbox.json (dataset_area not in metadata)
24+
// API GET workspaces/{id}/bbox (dataset_area not in metadata)
2525
// OSM GET map?bbox=... (export workspace xml)
2626
// TDEI POST osw/convert -> jobId; poll TDEI GET jobs?job_id=... until COMPLETED;
2727
// TDEI GET job/download/{jobId}
@@ -155,9 +155,10 @@ test('submitting with valid values shows a loading state', async ({ page }) => {
155155
await stubPageLoad(page, [eligibleProjectGroup]);
156156
await stubServices(page);
157157

158-
// OSM bbox + xml export so the flow reaches the convert step. The OSM API base
159-
// is http://api.test/osm/api/0.6/, so the globs must include api/0.6/.
160-
await page.route('**/osm/api/0.6/workspaces/1/bbox.json', route =>
158+
// Workspace bbox (new-API, decimal degrees) + OSM xml export so the flow reaches
159+
// the convert step. The OSM map base is http://api.test/osm/api/0.6/, so those
160+
// globs must include api/0.6/; the bbox now lives on the new API.
161+
await page.route('**/workspaces/1/bbox', route =>
161162
route.fulfill({ json: { min_lat: 47.6, min_lon: -122.34, max_lat: 47.62, max_lon: -122.32 } })
162163
);
163164
await page.route('**/osm/api/0.6/map?**', route =>
@@ -184,7 +185,7 @@ test('a duplicate dataset version shows an error and lets the user change the ve
184185
await stubPageLoad(page, [eligibleProjectGroup]);
185186
await stubServices(page);
186187

187-
await page.route('**/osm/api/0.6/workspaces/1/bbox.json', route =>
188+
await page.route('**/workspaces/1/bbox', route =>
188189
route.fulfill({ json: { min_lat: 47.6, min_lon: -122.34, max_lat: 47.62, max_lon: -122.32 } })
189190
);
190191
await page.route('**/osm/api/0.6/map?**', route =>
@@ -245,7 +246,7 @@ test('an API error shows an error message and a Try again button that resets the
245246
await stubPageLoad(page, [eligibleProjectGroup]);
246247
await stubServices(page);
247248

248-
await page.route('**/osm/api/0.6/workspaces/1/bbox.json', route =>
249+
await page.route('**/workspaces/1/bbox', route =>
249250
route.fulfill({ json: { min_lat: 47.6, min_lon: -122.34, max_lat: 47.62, max_lon: -122.32 } })
250251
);
251252
await page.route('**/osm/api/0.6/map?**', route =>

test/unit/services/workspaces.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,33 @@ describe('WorkspacesClient.getMyWorkspaces', () => {
6767
await expect(makeClient().getMyWorkspaces()).rejects.toBeInstanceOf(WorkspacesClientError);
6868
});
6969
});
70+
71+
describe('WorkspacesClient.getWorkspaceBbox', () => {
72+
// Use a DISTINCT new-API base so the test proves the call base-swaps onto the
73+
// v1 (tasking) API — if it used the legacy base, the new-API stub wouldn't
74+
// match and the fetch would fail.
75+
const NEW_API_BASE = 'http://new-api.test/';
76+
77+
function makeBboxClient() {
78+
return new WorkspacesClient(TEST_API_BASE, NEW_API_BASE, tdeiClient, osmClient);
79+
}
80+
81+
it('fetches the bbox from the v1 endpoint and returns it unchanged (backend sends decimal degrees)', async () => {
82+
// Regression: coordinates come pre-scaled from the backend, so the client
83+
// must NOT re-scale them — bbox in === bbox out.
84+
const bbox = { min_lat: 47.6, min_lon: -122.34, max_lat: 47.62, max_lon: -122.32 };
85+
server.use(
86+
http.get(`${NEW_API_BASE}workspaces/1/bbox`, () => HttpResponse.json(bbox))
87+
);
88+
89+
await expect(makeBboxClient().getWorkspaceBbox(1)).resolves.toEqual(bbox);
90+
});
91+
92+
it('returns undefined when the workspace has no extent (204)', async () => {
93+
server.use(
94+
http.get(`${NEW_API_BASE}workspaces/1/bbox`, () => new HttpResponse(null, { status: 204 }))
95+
);
96+
97+
await expect(makeBboxClient().getWorkspaceBbox(1)).resolves.toBeUndefined();
98+
});
99+
});

0 commit comments

Comments
 (0)