87 lines
2.3 KiB
TypeScript
87 lines
2.3 KiB
TypeScript
import { createBasicAuthHeader } from "./config.js";
|
|
import type { AtlassianConfig, FetchLike } from "./types.js";
|
|
|
|
export type HttpMethod = "GET" | "POST" | "PUT";
|
|
|
|
export function createJsonHeaders(config: AtlassianConfig, includeJsonBody: boolean) {
|
|
const headers: Array<[string, string]> = [
|
|
["Accept", "application/json"],
|
|
["Authorization", createBasicAuthHeader(config)],
|
|
];
|
|
|
|
if (includeJsonBody) {
|
|
headers.push(["Content-Type", "application/json"]);
|
|
}
|
|
|
|
return headers;
|
|
}
|
|
|
|
export async function parseResponse(response: Response) {
|
|
if (response.status === 204) {
|
|
return null;
|
|
}
|
|
|
|
const contentType = response.headers.get("content-type") ?? "";
|
|
|
|
if (contentType.includes("application/json")) {
|
|
try {
|
|
return await response.json();
|
|
} catch {
|
|
throw new Error("Malformed JSON response from Atlassian API");
|
|
}
|
|
}
|
|
|
|
return response.text();
|
|
}
|
|
|
|
export function createStatusError(errorPrefix: string, response: Response) {
|
|
const base = `${errorPrefix}: ${response.status} ${response.statusText}`;
|
|
|
|
switch (response.status) {
|
|
case 401:
|
|
return new Error(`${base} - check ATLASSIAN_EMAIL and ATLASSIAN_API_TOKEN`);
|
|
case 403:
|
|
return new Error(`${base} - verify product permissions for this account`);
|
|
case 404:
|
|
return new Error(`${base} - verify the resource identifier or API path`);
|
|
case 429:
|
|
return new Error(`${base} - retry later or reduce request rate`);
|
|
default:
|
|
return new Error(base);
|
|
}
|
|
}
|
|
|
|
export async function sendJsonRequest(options: {
|
|
config: AtlassianConfig;
|
|
fetchImpl?: FetchLike;
|
|
url: string;
|
|
method: HttpMethod;
|
|
body?: unknown;
|
|
errorPrefix: string;
|
|
handleResponseError?: (response: Response) => Error | undefined;
|
|
}) {
|
|
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
|
|
if (!fetchImpl) {
|
|
throw new Error("Fetch API is not available in this runtime");
|
|
}
|
|
|
|
const response = await fetchImpl(options.url, {
|
|
method: options.method,
|
|
headers: createJsonHeaders(options.config, options.body !== undefined),
|
|
...(options.body === undefined ? {} : { body: JSON.stringify(options.body) }),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const customError = options.handleResponseError?.(response);
|
|
|
|
if (customError) {
|
|
throw customError;
|
|
}
|
|
|
|
throw createStatusError(options.errorPrefix, response);
|
|
}
|
|
|
|
return parseResponse(response);
|
|
}
|