Improved: show details of TypeError using Obsidian API.

This commit is contained in:
vorotamoroz
2025-03-24 12:04:58 +09:00
parent 24f31ed19e
commit 2a1d71da5c
@@ -1,5 +1,5 @@
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts"; import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
import { LOG_LEVEL_DEBUG, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger"; import { LOG_LEVEL_DEBUG, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "octagonal-wheels/common/logger";
import { Notice, requestUrl, type RequestUrlParam, type RequestUrlResponse } from "../../deps.ts"; import { Notice, requestUrl, type RequestUrlParam, type RequestUrlResponse } from "../../deps.ts";
import { type EntryDoc, type FilePathWithPrefix } from "../../lib/src/common/types.ts"; import { type EntryDoc, type FilePathWithPrefix } from "../../lib/src/common/types.ts";
import { getPathFromTFile } from "../../common/utils.ts"; import { getPathFromTFile } from "../../common/utils.ts";
@@ -47,6 +47,58 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
return !this.last_successful_post; return !this.last_successful_post;
} }
async fetchByAPI(
url: string,
localURL: string,
method: string,
authHeader: string,
opts?: RequestInit
): Promise<Response> {
const body = opts?.body as string;
const transformedHeaders = { ...(opts?.headers as Record<string, string>) };
if (authHeader != "") transformedHeaders["authorization"] = authHeader;
delete transformedHeaders["host"];
delete transformedHeaders["Host"];
delete transformedHeaders["content-length"];
delete transformedHeaders["Content-Length"];
const requestParam: RequestUrlParam = {
url,
method: opts?.method,
body: body,
headers: transformedHeaders,
contentType: "application/json",
// contentType: opts.headers,
};
const size = body ? ` (${body.length})` : "";
try {
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
const r = await fetchByAPI(requestParam);
if (method == "POST" || method == "PUT") {
this.last_successful_post = r.status - (r.status % 100) == 200;
} else {
this.last_successful_post = true;
}
this._log(`HTTP:${method}${size} to:${localURL} -> ${r.status}`, LOG_LEVEL_DEBUG);
return new Response(r.arrayBuffer, {
headers: r.headers,
status: r.status,
statusText: `${r.status}`,
});
} catch (ex) {
this._log(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL_VERBOSE);
// limit only in bulk_docs.
if (url.toString().indexOf("_bulk_docs") !== -1) {
this.last_successful_post = false;
}
this._log(ex);
throw ex;
} finally {
this.plugin.responseCount.value = this.plugin.responseCount.value + 1;
}
}
async $$connectRemoteCouchDB( async $$connectRemoteCouchDB(
uri: string, uri: string,
auth: { username: string; password: string }, auth: { username: string; password: string },
@@ -87,88 +139,74 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
} }
size = ` (${opts_length})`; size = ` (${opts_length})`;
} }
if (!disableRequestURI && typeof url == "string" && typeof (opts?.body ?? "") == "string") { try {
const body = opts?.body as string; if (!disableRequestURI && typeof url == "string" && typeof (opts?.body ?? "") == "string") {
return await this.fetchByAPI(url, localURL, method, authHeader, opts);
const transformedHeaders = { ...(opts?.headers as Record<string, string>) }; }
if (authHeader != "") transformedHeaders["authorization"] = authHeader; // --> native Fetch API.
delete transformedHeaders["host"];
delete transformedHeaders["Host"];
delete transformedHeaders["content-length"];
delete transformedHeaders["Content-Length"];
const requestParam: RequestUrlParam = {
url,
method: opts?.method,
body: body,
headers: transformedHeaders,
contentType: "application/json",
// contentType: opts.headers,
};
try { try {
if (this.settings.enableDebugTools) {
// Issue #407
(opts!.headers as Headers).append("ngrok-skip-browser-warning", "123");
}
this.plugin.requestCount.value = this.plugin.requestCount.value + 1; this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
const r = await fetchByAPI(requestParam); const response: Response = await fetch(url, opts);
if (method == "POST" || method == "PUT") { if (method == "POST" || method == "PUT") {
this.last_successful_post = r.status - (r.status % 100) == 200; this.last_successful_post = response.ok;
} else { } else {
this.last_successful_post = true; this.last_successful_post = true;
} }
this._log(`HTTP:${method}${size} to:${localURL} -> ${r.status}`, LOG_LEVEL_DEBUG); this._log(`HTTP:${method}${size} to:${localURL} -> ${response.status}`, LOG_LEVEL_DEBUG);
if (Math.floor(response.status / 100) !== 2) {
return new Response(r.arrayBuffer, { if (response.status == 404) {
headers: r.headers, if (method === "GET" && localURL.indexOf("/_local/") === -1) {
status: r.status, this._log(
statusText: `${r.status}`, `Just checkpoint or some server information has been missing. The 404 error shown above is not an error.`,
}); LOG_LEVEL_VERBOSE
} catch (ex) { );
this._log(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL_VERBOSE); }
// limit only in bulk_docs. } else {
if (url.toString().indexOf("_bulk_docs") !== -1) { const r = response.clone();
this.last_successful_post = false; this._log(
} `The request may have failed. The reason sent by the server: ${r.status}: ${r.statusText}`,
this._log(ex); LOG_LEVEL_NOTICE
throw ex; );
} finally { try {
this.plugin.responseCount.value = this.plugin.responseCount.value + 1; const result = await r.text();
} this._log(result, LOG_LEVEL_VERBOSE);
} } catch (_) {
this._log("Cloud not fetch response body", LOG_LEVEL_VERBOSE);
try { this._log(_, LOG_LEVEL_VERBOSE);
if (this.settings.enableDebugTools) { }
// Issue #407
(opts!.headers as Headers).append("ngrok-skip-browser-warning", "123");
}
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
const response: Response = await fetch(url, opts);
if (method == "POST" || method == "PUT") {
this.last_successful_post = response.ok;
} else {
this.last_successful_post = true;
}
this._log(`HTTP:${method}${size} to:${localURL} -> ${response.status}`, LOG_LEVEL_DEBUG);
if (Math.floor(response.status / 100) !== 2) {
if (method != "GET" && localURL.indexOf("/_local/") === -1 && !localURL.endsWith("/")) {
const r = response.clone();
this._log(
`The request may have failed. The reason sent by the server: ${r.status}: ${r.statusText}`
);
try {
this._log(await (await r.blob()).text(), LOG_LEVEL_VERBOSE);
} catch (_) {
this._log("Cloud not parse response", LOG_LEVEL_VERBOSE);
this._log(_, LOG_LEVEL_VERBOSE);
} }
} else {
this._log(
`Just checkpoint or some server information has been missing. The 404 error shown above is not an error.`,
LOG_LEVEL_VERBOSE
);
} }
return response;
} catch (ex) {
if (ex instanceof TypeError) {
this._log(
"Failed to fetch by native fetch API. Trying to fetch by API to get more information."
);
const resp2 = await this.fetchByAPI(url.toString(), localURL, method, authHeader, opts);
if (resp2.status / 100 == 2) {
this._log(
"The request was successful by API. But the native fetch API failed! Please check CORS settings on the remote database!. While this condition, you cannot enable LiveSync",
LOG_LEVEL_NOTICE
);
return resp2;
}
const r2 = resp2.clone();
const msg = await r2.text();
this._log(`Failed to fetch by API. ${resp2.status}: ${msg}`, LOG_LEVEL_NOTICE);
return resp2;
}
throw ex;
} }
return response; } catch (ex: any) {
} catch (ex) {
this._log(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL_VERBOSE); this._log(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL_VERBOSE);
const msg = ex instanceof Error ? `${ex?.name}:${ex?.message}` : ex?.toString();
this._log(`Failed to fetch: ${msg}`, LOG_LEVEL_NOTICE);
this._log(ex, LOG_LEVEL_VERBOSE);
// limit only in bulk_docs. // limit only in bulk_docs.
if (url.toString().indexOf("_bulk_docs") !== -1) { if (url.toString().indexOf("_bulk_docs") !== -1) {
this.last_successful_post = false; this.last_successful_post = false;
@@ -178,6 +216,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
} finally { } finally {
this.plugin.responseCount.value = this.plugin.responseCount.value + 1; this.plugin.responseCount.value = this.plugin.responseCount.value + 1;
} }
// return await fetch(url, opts); // return await fetch(url, opts);
}, },
}; };
@@ -195,11 +234,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
const info = await db.info(); const info = await db.info();
return { db: db, info: info }; return { db: db, info: info };
} catch (ex: any) { } catch (ex: any) {
let msg = `${ex?.name}:${ex?.message}`; const msg = `${ex?.name}:${ex?.message}`;
if (ex?.name == "TypeError" && ex?.message == "Failed to fetch") {
msg +=
"\n**Note** This error caused by many reasons. The only sure thing is you didn't touch the server.\nTo check details, open inspector.";
}
this._log(ex, LOG_LEVEL_VERBOSE); this._log(ex, LOG_LEVEL_VERBOSE);
return msg; return msg;
} }