Files
notesnook/packages/core/utils/http.js

199 lines
5.0 KiB
JavaScript
Raw Normal View History

/*
This file is part of the Notesnook project (https://notesnook.com/)
2023-01-16 13:44:52 +05:00
Copyright (C) 2023 Streetwriters (Private) Limited
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2022-08-30 16:13:11 +05:00
import { EV, EVENTS } from "../common";
import { logger } from "../logger";
2022-01-18 12:00:57 +05:00
import { getServerNameFromHost } from "./constants";
import { extractHostname } from "./hostname";
2020-12-16 12:06:25 +05:00
function get(url, token) {
return request(url, token, "GET");
}
function deleteRequest(url, token) {
return request(url, token, "DELETE");
}
function patch(url, data, token) {
return bodyRequest(url, data, token, "PATCH");
2020-12-16 12:06:25 +05:00
}
patch.json = function (url, data, token) {
return bodyRequest(url, data, token, "PATCH", "application/json");
};
2020-12-16 12:06:25 +05:00
function post(url, data, token) {
return bodyRequest(url, data, token, "POST");
2020-12-16 12:06:25 +05:00
}
post.json = function (url, data, token) {
return bodyRequest(url, data, token, "POST", "application/json");
2020-12-16 12:06:25 +05:00
};
export default {
get,
post,
delete: deleteRequest,
patch
};
function transformer(data, type) {
2020-12-23 11:28:38 +05:00
if (!data) return;
if (type === "application/json") return JSON.stringify(data);
2020-12-16 12:06:25 +05:00
else {
return Object.entries(data)
2022-03-11 22:49:24 +05:00
.map(([key, value]) =>
value ? `${encodeURIComponent(key)}=${encodeURIComponent(value)}` : ""
2020-12-16 12:06:25 +05:00
)
.join("&");
}
}
/**
*
* @param {Response} response
* @returns
*/
2020-12-16 12:06:25 +05:00
async function handleResponse(response) {
try {
const contentType = response.headers.get("content-type");
if (contentType && contentType.includes("application/json")) {
const json = await response.json();
if (response.ok) {
return json;
}
throw new RequestError(errorTransformer(json));
} else {
if (response.status === 429)
throw new Error("You are being rate limited.");
if (response.ok) return await response.text();
else if (response.status === 401) {
EV.publish(EVENTS.userUnauthorized, response.url);
throw new Error("Unauthorized.");
} else
throw new Error(
`Request failed with status code: ${response.status} ${response.statusText}.`
);
2020-12-16 12:06:25 +05:00
}
} catch (e) {
logger.error(e, "Error while sending request:", { url: response.url });
throw e;
2020-12-16 12:06:25 +05:00
}
}
async function request(url, token, method) {
return handleResponse(
2022-01-18 12:00:57 +05:00
await fetchWrapped(url, {
2020-12-16 12:06:25 +05:00
method,
headers: getAuthorizationHeader(token)
2020-12-16 12:06:25 +05:00
})
);
}
async function bodyRequest(
url,
data,
token,
method,
contentType = "application/x-www-form-urlencoded"
) {
2020-12-16 12:06:25 +05:00
return handleResponse(
2022-01-18 12:00:57 +05:00
await fetchWrapped(url, {
2020-12-16 12:06:25 +05:00
method,
body: transformer(data, contentType),
2020-12-16 12:06:25 +05:00
headers: {
2020-12-16 13:23:14 +05:00
...getAuthorizationHeader(token),
"Content-Type": contentType
}
2020-12-16 12:06:25 +05:00
})
);
}
2020-12-16 13:23:14 +05:00
function getAuthorizationHeader(token) {
return token ? { Authorization: "Bearer " + token } : {};
}
export function errorTransformer(errorJson) {
2022-03-11 22:49:24 +05:00
let errorMessage = "Unknown error.";
let errorCode = "unknown";
if (!errorJson.error && !errorJson.errors && !errorJson.error_description)
2022-03-11 22:49:24 +05:00
return { description: errorMessage, code: errorCode, data: {} };
const { error, error_description, errors, data } = errorJson;
if (errors) {
2022-03-11 22:49:24 +05:00
errorMessage = errors.join("\n");
}
outer: switch (error) {
case "invalid_grant": {
switch (error_description) {
case "invalid_username_or_password":
2022-03-11 22:49:24 +05:00
errorMessage = "Username or password incorrect.";
errorCode = error_description;
break outer;
default:
2022-03-11 22:49:24 +05:00
errorMessage = error_description || error;
errorCode = error || "invalid_grant";
break outer;
}
}
default:
errorMessage = error_description || error || "An unknown error occurred.";
2022-03-11 22:49:24 +05:00
errorCode = error;
break;
}
2022-03-11 22:49:24 +05:00
return {
description: errorMessage,
code: errorCode,
data: data ? JSON.parse(data) : undefined
2022-03-11 22:49:24 +05:00
};
}
2022-01-18 12:00:57 +05:00
/**
*
* @param {RequestInfo} input
* @param {RequestInit} init
*/
async function fetchWrapped(input, init) {
try {
const response = await fetch(input, init);
return response;
} catch (e) {
const host = extractHostname(input);
const serverName = getServerNameFromHost(host);
if (serverName)
throw new Error(
`${serverName} is not responding. Please check your internet connection. If the problem persists, feel free email us at support@streetwriters.co. (Reference error: ${e.message})`
);
throw e;
}
}
2022-03-21 10:23:35 +05:00
export class RequestError extends Error {
2022-03-11 22:49:24 +05:00
constructor(error) {
super(error.description);
this.code = error.code;
this.data = error.data;
}
}