feat(workbench): integrate runtime data and map tooling
This commit is contained in:
@@ -122,6 +122,20 @@ describe("Agent API client sessions", () => {
|
||||
expect(headers.get("Content-Type")).toBe("application/json");
|
||||
});
|
||||
|
||||
it("sends the authorized project context on Agent requests", async () => {
|
||||
const fetchMock = vi.fn(async (_input: RequestInfo | URL, _init?: RequestInit) =>
|
||||
new Response(JSON.stringify({ sessions: [] }), { status: 200 })
|
||||
);
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
const getProjectId = vi.fn().mockResolvedValue("project-117");
|
||||
|
||||
await createAgentApiClient("http://agent.local", { getProjectId }).listSessions();
|
||||
|
||||
expect(getProjectId).toHaveBeenCalledOnce();
|
||||
const headers = new Headers(fetchMock.mock.calls[0]?.[1]?.headers);
|
||||
expect(headers.get("X-Project-Id")).toBe("project-117");
|
||||
});
|
||||
|
||||
it("streams session events from the backend SSE endpoint", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
|
||||
@@ -115,6 +115,7 @@ export type AgentApiClient = {
|
||||
|
||||
export type AgentApiClientOptions = {
|
||||
getAccessToken?: AccessTokenProvider;
|
||||
getProjectId?: () => Promise<string>;
|
||||
};
|
||||
|
||||
const AGENT_API_BASE_URLS = [env.TJWATER_AGENT_API_BASE_URL.replace(/\/$/, "")];
|
||||
@@ -137,7 +138,8 @@ export function createAgentApiClient(
|
||||
setActiveBaseUrl,
|
||||
path,
|
||||
init,
|
||||
options.getAccessToken
|
||||
options.getAccessToken,
|
||||
options.getProjectId
|
||||
);
|
||||
const requestJson = async <T,>(path: string, init?: RequestInit) => {
|
||||
const response = await request(path, init);
|
||||
@@ -298,18 +300,19 @@ async function fetchWithFallback(
|
||||
setActiveBaseUrl: (baseUrl: string) => void,
|
||||
path: string,
|
||||
init?: RequestInit,
|
||||
getAccessToken?: AccessTokenProvider
|
||||
getAccessToken?: AccessTokenProvider,
|
||||
getProjectId?: () => Promise<string>
|
||||
) {
|
||||
const orderedBaseUrls = [activeBaseUrl, ...baseUrls.filter((item) => item !== activeBaseUrl)];
|
||||
let lastError: unknown;
|
||||
let lastResponse: Response | null = null;
|
||||
const accessToken = await getAccessToken?.();
|
||||
const requestInit = accessToken
|
||||
? {
|
||||
...init,
|
||||
headers: withBearerToken(init?.headers, accessToken)
|
||||
}
|
||||
: init;
|
||||
const projectId = await getProjectId?.();
|
||||
const hasContextHeaders = Boolean(accessToken || projectId);
|
||||
const headers = hasContextHeaders ? new Headers(init?.headers) : undefined;
|
||||
if (accessToken) headers?.set("Authorization", `Bearer ${accessToken}`);
|
||||
if (projectId) headers?.set("X-Project-Id", projectId);
|
||||
const requestInit = hasContextHeaders ? { ...init, headers } : init;
|
||||
|
||||
for (const baseUrl of orderedBaseUrls) {
|
||||
try {
|
||||
@@ -337,12 +340,6 @@ async function fetchWithFallback(
|
||||
throw lastError instanceof Error ? lastError : new Error("Agent API unavailable");
|
||||
}
|
||||
|
||||
function withBearerToken(headersInit: HeadersInit | undefined, accessToken: string) {
|
||||
const headers = new Headers(headersInit);
|
||||
headers.set("Authorization", `Bearer ${accessToken}`);
|
||||
return headers;
|
||||
}
|
||||
|
||||
function shouldFallbackOnHttpStatus(status: number) {
|
||||
return status === 404 || status === 405 || status === 502 || status === 503 || status === 504;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user