Improve format of crawl template config error from server (#281)
* better display of api errors, such as fields missing or invalid urls, addresses #280
This commit is contained in:
parent
301b05ff4e
commit
9606d59c3d
@ -8,6 +8,7 @@ import LiteElement, { html } from "../../utils/LiteElement";
|
||||
import { ScheduleInterval, humanizeNextDate } from "../../utils/cron";
|
||||
import type { CrawlConfig, Profile } from "./types";
|
||||
import { getUTCSchedule } from "../../utils/cron";
|
||||
import { TemplateResult } from "lit";
|
||||
|
||||
type NewCrawlTemplate = {
|
||||
id?: string;
|
||||
@ -89,7 +90,7 @@ export class CrawlTemplatesNew extends LiteElement {
|
||||
private browserProfileId?: string | null;
|
||||
|
||||
@state()
|
||||
private serverError?: string;
|
||||
private serverError?: TemplateResult | string;
|
||||
|
||||
private get formattededNextCrawlDate() {
|
||||
const utcSchedule = this.getUTCSchedule();
|
||||
@ -536,7 +537,13 @@ export class CrawlTemplatesNew extends LiteElement {
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e?.isApiError) {
|
||||
this.serverError = e?.message;
|
||||
const isConfigError = ({ loc }: any) =>
|
||||
loc.some((v: string) => v === "config");
|
||||
if (e.details && e.details.some(isConfigError)) {
|
||||
this.serverError = this.formatConfigServerError(e.details);
|
||||
} else {
|
||||
this.serverError = e.message;
|
||||
}
|
||||
} else {
|
||||
this.serverError = msg("Something unexpected went wrong");
|
||||
}
|
||||
@ -545,6 +552,34 @@ export class CrawlTemplatesNew extends LiteElement {
|
||||
this.isSubmitting = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format `config` related API error returned from server
|
||||
*/
|
||||
private formatConfigServerError(details: any): TemplateResult {
|
||||
const detailsWithoutDictError = details.filter(
|
||||
({ type }: any) => type !== "type_error.dict"
|
||||
);
|
||||
|
||||
const renderDetail = ({ loc, msg: detailMsg }: any) => html`
|
||||
<li>
|
||||
${loc.some((v: string) => v === "seeds") &&
|
||||
typeof loc[loc.length - 1] === "number"
|
||||
? msg(str`Seed URL ${loc[loc.length - 1] + 1}: `)
|
||||
: `${loc[loc.length - 1]}: `}
|
||||
${detailMsg}
|
||||
</li>
|
||||
`;
|
||||
|
||||
return html`
|
||||
${msg(
|
||||
"Couldn't save crawl template. Please fix the following crawl configuration issues:"
|
||||
)}
|
||||
<ul class="list-disc w-fit mx-auto">
|
||||
${detailsWithoutDictError.map(renderDetail)}
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
|
||||
private getUTCSchedule(): string {
|
||||
if (!this.scheduleInterval) {
|
||||
return "";
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { msg } from "@lit/localize";
|
||||
|
||||
import type { Auth } from "../utils/AuthService";
|
||||
import { APIError } from "./api";
|
||||
@ -121,28 +122,29 @@ export default class LiteElement extends LitElement {
|
||||
this.dispatchEvent(new CustomEvent("need-login"));
|
||||
}
|
||||
|
||||
let errorMessage: string;
|
||||
let detail;
|
||||
let errorMessage: string = msg("Unknown API error");
|
||||
|
||||
try {
|
||||
const detail = (await resp.json()).detail;
|
||||
detail = (await resp.json()).detail;
|
||||
|
||||
if (typeof detail === "string") {
|
||||
errorMessage = detail;
|
||||
} else {
|
||||
// TODO return client error details
|
||||
const fieldDetail = detail[detail.length - 1];
|
||||
} else if (Array.isArray(detail) && detail.length) {
|
||||
const fieldDetail = detail[0];
|
||||
const { loc, msg } = fieldDetail;
|
||||
|
||||
errorMessage = `${loc[loc.length - 1]} ${msg}`;
|
||||
const fieldName = loc
|
||||
.filter((v: any) => v !== "body" && typeof v === "string")
|
||||
.join(" ");
|
||||
errorMessage = `${fieldName} ${msg}`;
|
||||
}
|
||||
} catch {
|
||||
errorMessage = "Unknown API error";
|
||||
}
|
||||
} catch {}
|
||||
|
||||
// TODO client error details
|
||||
throw new APIError({
|
||||
message: errorMessage,
|
||||
status: resp.status,
|
||||
details: detail,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,30 @@
|
||||
type StatusCode = number;
|
||||
type Detail = {
|
||||
loc: any[];
|
||||
msg: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export class APIError extends Error {
|
||||
statusCode: number;
|
||||
statusCode: StatusCode;
|
||||
details: Detail[] | null;
|
||||
|
||||
get isApiError() {
|
||||
return true;
|
||||
}
|
||||
|
||||
constructor({ message, status }: { message: string; status: number }) {
|
||||
constructor({
|
||||
message,
|
||||
status,
|
||||
details,
|
||||
}: {
|
||||
message: string;
|
||||
status: StatusCode;
|
||||
details?: Detail[];
|
||||
}) {
|
||||
super(message);
|
||||
|
||||
this.statusCode = status;
|
||||
this.details = details || null;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user