Skip to content

Commit b8f9d79

Browse files
authored
fix: Reinforce api server fetch (#225)
1 parent 5e8708f commit b8f9d79

File tree

1 file changed

+43
-16
lines changed

1 file changed

+43
-16
lines changed

src/lib/api/fetch.ts

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,34 @@ const mcpNameHeader = 'X-mcp';
1010
const jqHeader = 'X-jq';
1111
const contentTypeHeader = 'Content-Type';
1212

13-
// fetchApiServer is a wrapper around fetch that adds the necessary headers for the Crate API or the MCP API server.
13+
/**
14+
* Attempts to parse the response body as JSON. If parsing fails, returns the raw text.
15+
*
16+
* @param {Response} res - The fetch Response object.
17+
* @returns {Promise<unknown>} The parsed JSON object or the raw text if parsing fails.
18+
*/
19+
export const parseJsonOrText = async (res: Response): Promise<unknown> => {
20+
const text = await res.text();
21+
try {
22+
return JSON.parse(text);
23+
} catch {
24+
return text;
25+
}
26+
};
27+
28+
/**
29+
* Wrapper around fetch that adds the necessary headers for the Crate API or the MCP API server.
30+
* Handles authentication, content type, and custom headers for backend routing.
31+
* Redirects to login if the response is unauthorized (401).
32+
*
33+
* @param {string} path - The API endpoint path (appended to `/api/onboarding`).
34+
* @param {ApiConfig} config - The API configuration, including authentication and MCP/Crate context.
35+
* @param {string} [jq] - Optional jq transformation string for the proxy server.
36+
* @param {string} [httpMethod='GET'] - The HTTP method to use (GET, POST, PATCH, etc.).
37+
* @param {BodyInit} [body] - The request body, if applicable.
38+
* @returns {Promise<Response>} The fetch Response object.
39+
* @throws {APIError} Throws an APIError if the response is not ok.
40+
*/
1441
export const fetchApiServer = async (
1542
path: string,
1643
config: ApiConfig,
@@ -31,7 +58,7 @@ export const fetchApiServer = async (
3158
if (jq) headers[jqHeader] = jq;
3259

3360
// If the config object has a mcpConfig, it is assumed that the request is for the MCP API server and the necessary headers are set for the backend to get the OIDC kubeconfig without exposing it to the frontend,
34-
// otherwise, the useCrateClusterHeader is set to true to indicate that the request is for the Crate
61+
// otherwise, the useCrateClusterHeader is set to true to indicate that the request is for the Crate.
3562
if (config.mcpConfig !== undefined) {
3663
headers[projectNameHeader] = config.mcpConfig.projectName;
3764
headers[workspaceNameHeader] = config.mcpConfig.workspaceName;
@@ -48,18 +75,30 @@ export const fetchApiServer = async (
4875

4976
if (!res.ok) {
5077
if (res.status === 401) {
51-
// Unauthorized (token expired), redirect to the login page
78+
// Unauthorized (token expired), redirect to the login page.
5279
sessionStorage.setItem(AUTH_FLOW_SESSION_KEY, 'onboarding');
5380
window.location.replace(`/api/auth/onboarding/login?redirectTo=${encodeURIComponent(getRedirectSuffix())}`);
5481
}
5582
const error = new APIError('An error occurred while fetching the data.', res.status);
56-
error.info = await res.json();
83+
error.info = await parseJsonOrText(res);
5784
throw error;
5885
}
5986

6087
return res;
6188
};
6289

90+
/**
91+
* Calls fetchApiServer and parses the response as JSON.
92+
*
93+
* @template T
94+
* @param {string} path - The API endpoint path (appended to `/api/onboarding`).
95+
* @param {ApiConfig} config - The API configuration, including authentication and MCP/Crate context.
96+
* @param {string} [jq] - Optional jq transformation string for the proxy server.
97+
* @param {string} [httpMethod='GET'] - The HTTP method to use (GET, POST, PATCH, etc.).
98+
* @param {BodyInit} [body] - The request body, if applicable.
99+
* @returns {Promise<T>} The parsed JSON response.
100+
* @throws {APIError} Throws an APIError if the response is not ok.
101+
*/
63102
export const fetchApiServerJson = async <T>(
64103
path: string,
65104
config: ApiConfig,
@@ -71,15 +110,3 @@ export const fetchApiServerJson = async <T>(
71110

72111
return await res.json();
73112
};
74-
75-
// request is of [path, config, jq]
76-
export const fetchApiServerJsonMultiple = (requests: [string | null, ApiConfig, string | undefined][]) => {
77-
return Promise.all(
78-
requests
79-
.filter((r) => r[0] !== null)
80-
.map(([path, config, jq]) =>
81-
// @ts-expect-error path is not null
82-
fetchApiServer(path, config, jq).then((res) => res.json()),
83-
),
84-
);
85-
};

0 commit comments

Comments
 (0)