From 699e255ac8f27569b490f7ebda2f48c8fa08b3bf Mon Sep 17 00:00:00 2001 From: Jeff Maki Date: Wed, 8 Jul 2026 22:41:59 -0400 Subject: [PATCH 1/2] bbox method from Workspaces client to avoid X-Workspace header issues --- services/osm.ts | 12 +++++------- services/workspaces.ts | 18 ++++++++++++++++-- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/services/osm.ts b/services/osm.ts index ca854c3..b6c1fa1 100644 --- a/services/osm.ts +++ b/services/osm.ts @@ -253,13 +253,11 @@ export class OsmApiClient extends BaseHttpClient implements ICancelableClient { } async getWorkspaceBbox(workspaceId: WorkspaceId) { - const response = await this._get(`workspaces/${workspaceId}/bbox.json`); - - if (response.status === 204) { - return undefined - } - - return await response.json(); + // The workspace bbox lives on the v1 tasking API, owned by WorkspacesClient. + // OsmApiClient is constructed before workspacesClient (which depends on it), + // so we resolve the singleton lazily at call time to avoid the import cycle. + const { workspacesClient } = await import('~/services/index'); + return workspacesClient.getWorkspaceBbox(workspaceId); } async getExportBbox(id: number) { diff --git a/services/workspaces.ts b/services/workspaces.ts index f1307f2..5b29586 100644 --- a/services/workspaces.ts +++ b/services/workspaces.ts @@ -119,8 +119,22 @@ export class WorkspacesClient extends BaseHttpClient implements ICancelableClien } } - getWorkspaceBbox(id: WorkspaceId): Promise { - return this.#osmClient.getWorkspaceBbox(id); + async getWorkspaceBbox(id: WorkspaceId): Promise { + const originalBaseUrl = this._baseUrl; + this._baseUrl = this.#newApiUrl; + + try { + const response = await this._send(`workspaces/${id}/bbox`, 'GET'); + + if (response.status === 204) { + return undefined; + } + + return await response.json(); + } + finally { + this._baseUrl = originalBaseUrl; + } } async createWorkspace(workspace: WorkspaceCreation): Promise { From 0184cb163ac2c77f70fbe935648ffa5ea1cd1636 Mon Sep 17 00:00:00 2001 From: Jeff Maki Date: Thu, 9 Jul 2026 10:35:27 -0400 Subject: [PATCH 2/2] Updated bbox tests --- services/workspaces.ts | 1 + test/e2e/create-blank.spec.ts | 4 ++-- test/e2e/export-index.spec.ts | 13 ++++++------ test/e2e/export-tdei.spec.ts | 13 ++++++------ test/unit/services/workspaces.test.ts | 30 +++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/services/workspaces.ts b/services/workspaces.ts index 5b29586..1babd6b 100644 --- a/services/workspaces.ts +++ b/services/workspaces.ts @@ -130,6 +130,7 @@ export class WorkspacesClient extends BaseHttpClient implements ICancelableClien return undefined; } + // The v1 API returns coordinates already in decimal degrees. return await response.json(); } finally { diff --git a/test/e2e/create-blank.spec.ts b/test/e2e/create-blank.spec.ts index 1096f99..f90d0bf 100644 --- a/test/e2e/create-blank.spec.ts +++ b/test/e2e/create-blank.spec.ts @@ -65,8 +65,8 @@ async function stubCreateFlow( route.fulfill({ status: 200, body: '' }) ); - // Dashboard map bbox -> 204 so the map init is skipped in headless. - await page.route('**/osm/api/0.6/**/bbox.json', route => + // Dashboard map bbox (new-API) -> 204 so the map init is skipped in headless. + await page.route('**/workspaces/*/bbox', route => route.fulfill({ status: 204, body: '' }) ); diff --git a/test/e2e/export-index.spec.ts b/test/e2e/export-index.spec.ts index 657fb3c..8759f16 100644 --- a/test/e2e/export-index.spec.ts +++ b/test/e2e/export-index.spec.ts @@ -13,7 +13,7 @@ import { aWorkspace, projectGroups } from '../mocks/fixtures'; // // The Download flow goes through workspacesClient.exportWorkspaceArchive(workspace), // which branches on workspace.type: -// - pathways: osmClient.getWorkspaceData(id) = GET osm/.../workspaces/{id}/bbox.json +// - pathways: osmClient.getWorkspaceData(id) = GET new-api/workspaces/{id}/bbox // then GET osm/.../map.json?bbox=... -> {elements:[]} -> buildPathwaysCsvArchive // (pure client-side zip; NO tdei host, NO polling). // - osw: osm export XML + tdeiClient.convertDataset (4s job polling). @@ -36,15 +36,15 @@ async function stubGetWorkspace(page: import('@playwright/test').Page, overrides }); } -// Stubs the OSM-host calls the pathways download flow makes: -// GET .../workspaces/{id}/bbox.json -> a bbox, then GET .../map.json -> elements. +// Stubs the calls the pathways download flow makes: +// GET new-api/workspaces/{id}/bbox -> a bbox, then GET osm/.../map.json -> elements. // `gate` (if provided) holds the map.json response open so the loading state can // be observed before the archive is built. async function stubPathwaysDownloadOk( page: import('@playwright/test').Page, gate?: Promise ) { - await page.route('**/osm/**/workspaces/1/bbox.json', route => + await page.route('**/workspaces/1/bbox', route => route.fulfill({ json: { min_lon: -122.4, min_lat: 47.6, max_lon: -122.3, max_lat: 47.7 } }) ); await page.route('**/osm/**/map.json**', async (route) => { @@ -151,8 +151,9 @@ test.describe('workspace export landing page', () => { await seedAuthenticatedSession(page); await stubGetWorkspace(page, { type: 'pathways' }); - // Fail the first OSM call in the download flow so exportWorkspaceArchive throws. - await page.route('**/osm/**/workspaces/1/bbox.json', route => + // Fail the first call in the download flow so exportWorkspaceArchive throws. + // The bbox lookup now hits the new API; the map fetch stays on the OSM base. + await page.route('**/workspaces/1/bbox', route => route.fulfill({ status: 500, body: 'boom' }) ); await page.route('**/osm/**/map.json**', route => diff --git a/test/e2e/export-tdei.spec.ts b/test/e2e/export-tdei.spec.ts index 6989451..f78b721 100644 --- a/test/e2e/export-tdei.spec.ts +++ b/test/e2e/export-tdei.spec.ts @@ -21,7 +21,7 @@ import { aWorkspace, PROJECT_GROUP_ID } from '../mocks/fixtures'; // so the service `