@baqhub/sdk
Version:
The official JavaScript SDK for the BAQ federated app platform.
239 lines (238 loc) • 7.26 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Http = exports.RequestFailedError = exports.RequestError = void 0;
exports.setHttpApi = setHttpApi;
const tslib_1 = require("tslib");
const memoize_js_1 = tslib_1.__importDefault(require("lodash/memoize.js"));
const pickBy_js_1 = tslib_1.__importDefault(require("lodash/pickBy.js"));
const snakeCase_js_1 = tslib_1.__importDefault(require("lodash/snakeCase.js"));
const constants_js_1 = require("../constants.js");
const async_js_1 = require("../helpers/async.js");
const customError_js_1 = require("../helpers/customError.js");
const string_js_1 = require("../helpers/string.js");
const type_js_1 = require("../helpers/type.js");
const uuid_js_1 = require("../helpers/uuid.js");
const httpMethod_js_1 = require("../model/core/httpMethod.js");
const eventSource_js_1 = require("./eventSource.js");
const getClientId = (0, memoize_js_1.default)(() => uuid_js_1.Uuid.new());
let api = {
fetch: (...args) => fetch(...args),
eventSourceFetch: (...args) => fetch(...args),
};
function setHttpApi(newApi) {
api = newApi;
}
class RequestError extends customError_js_1.CustomError {
options;
status;
headers;
constructor(options, status, headers) {
super("Request error.");
this.options = options;
this.status = status;
this.headers = headers;
}
}
exports.RequestError = RequestError;
class RequestFailedError extends customError_js_1.CustomError {
baseError;
constructor(baseError) {
super("Request failed.");
this.baseError = baseError;
}
}
exports.RequestFailedError = RequestFailedError;
function isError(error, statuses) {
if (error instanceof RequestFailedError && !statuses) {
return true;
}
if (!(error instanceof RequestError)) {
return false;
}
if (!statuses) {
return true;
}
return statuses.includes(error.status);
}
async function httpHead(url, options) {
const response = await sendAsync({
...options,
method: httpMethod_js_1.HttpMethod.HEAD,
url,
});
return response.headers;
}
async function httpGet(url, options) {
const response = await sendAsync({
...options,
method: httpMethod_js_1.HttpMethod.GET,
url,
});
const json = await response.json();
return [response.headers, json];
}
async function httpDownload(url, options) {
const response = await sendAsync({
...options,
headers: {
Accept: "*/*",
...options?.headers,
},
method: httpMethod_js_1.HttpMethod.GET,
url,
});
const blob = await response.blob();
return [response.headers, blob];
}
async function httpPost(body, url, options = {}) {
const headers = {
"Content-Type": "application/json; charset=utf-8",
...options.headers,
};
const response = await sendAsync({
...options,
headers,
method: httpMethod_js_1.HttpMethod.POST,
url,
body,
});
const json = await response.json();
return [response.headers, json];
}
async function httpPut(body, url, options = {}) {
const headers = {
"Content-Type": "application/json; charset=utf-8",
...options.headers,
};
const response = await sendAsync({
...options,
headers,
method: httpMethod_js_1.HttpMethod.PUT,
url,
body,
});
const json = await response.json();
return [response.headers, json];
}
async function httpPatch(body, url, options = {}) {
const headers = {
"Content-Type": "application/json; charset=utf-8",
...options.headers,
};
const response = await sendAsync({
...options,
headers,
method: httpMethod_js_1.HttpMethod.PATCH,
url,
body,
});
const json = await response.json();
return [response.headers, json];
}
async function httpDelete(url, options) {
return sendAsync({
...options,
method: httpMethod_js_1.HttpMethod.DELETE,
url,
});
}
async function httpDeleteBody(body, url, options = {}) {
const headers = {
"Content-Type": "application/json; charset=utf-8",
...options.headers,
};
const response = await sendAsync({
...options,
headers,
method: httpMethod_js_1.HttpMethod.DELETE,
url,
body,
});
const json = await response.json();
return [response.headers, json];
}
function buildUrl(url, query) {
const queryArray = Object.entries(query || {})
.map(([key, value]) => value ? [(0, snakeCase_js_1.default)(key), value] : undefined)
.filter(type_js_1.isDefined);
return url + string_js_1.Str.buildQuery(queryArray);
}
function conformBody(body) {
if (body instanceof File) {
return body;
}
if (body instanceof Blob) {
return body;
}
if (body) {
return JSON.stringify(body);
}
return null;
}
async function sendAsync(options) {
const { method, url, body } = options;
const fullUrl = buildUrl(url, options.query);
const initialHeaders = {
Accept: "application/json",
...options.headers,
[constants_js_1.Constants.clientIdHeader]: getClientId(),
};
const headers = {
...initialHeaders,
Authorization: options.authorizationBuilder?.(method, fullUrl, initialHeaders),
};
try {
const response = await api.fetch(fullUrl, {
method,
headers: (0, pickBy_js_1.default)(headers, type_js_1.isDefined),
body: conformBody(body),
signal: options.signal,
});
if (!response.ok) {
throw new RequestError(options, response.status, response.headers);
}
return response;
}
catch (error) {
// If this request was aborted, throw a custom error.
if (options.signal?.aborted) {
throw new async_js_1.AbortedError();
}
if (error instanceof RequestError) {
throw error;
}
// Otherwise, bubble up.
throw new RequestFailedError(error);
}
}
function httpEventSource(onMessage, url, options = {}) {
const buildHeaders = lastEventId => {
const initialHeaders = (0, pickBy_js_1.default)({
...options.headers,
[constants_js_1.Constants.clientIdHeader]: getClientId(),
[constants_js_1.Constants.lastEventIdHeader]: lastEventId,
}, type_js_1.isDefined);
return (0, pickBy_js_1.default)({
...initialHeaders,
Authorization: options.authorizationBuilder?.(httpMethod_js_1.HttpMethod.GET, url, initialHeaders),
}, type_js_1.isDefined);
};
(0, eventSource_js_1.fetchEventSource)(api.eventSourceFetch, buildUrl(url, options.query), {
buildHeaders,
onMessage: onMessage,
openWhenHidden: true,
signal: options.signal,
});
}
exports.Http = {
isError,
head: httpHead,
get: httpGet,
post: httpPost,
put: httpPut,
patch: httpPatch,
delete: httpDelete,
deleteBody: httpDeleteBody,
download: httpDownload,
eventSource: httpEventSource,
};