fix: Show latest crawl logs for failed workflows (#2694)
Shows "Logs" tab for failed workflows, and links directly to logs when clicking a failed workflow in the workflow list.
This commit is contained in:
parent
5c78a57cbb
commit
0a68485c07
@ -244,7 +244,7 @@ export class WorkflowListItem extends BtrixElement {
|
|||||||
}
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
const href = `/orgs/${this.orgSlugState}/workflows/${this.workflow?.id}/${WorkflowTab.LatestCrawl}`;
|
const href = `/orgs/${this.orgSlugState}/workflows/${this.workflow?.id}/${this.workflow?.lastCrawlState === "failed" ? WorkflowTab.Logs : WorkflowTab.LatestCrawl}`;
|
||||||
this.navigate.to(href);
|
this.navigate.to(href);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -28,12 +28,13 @@ import { pageNav, type Breadcrumb } from "@/layouts/pageHeader";
|
|||||||
import { WorkflowTab } from "@/routes";
|
import { WorkflowTab } from "@/routes";
|
||||||
import { deleteConfirmation, noData, notApplicable } from "@/strings/ui";
|
import { deleteConfirmation, noData, notApplicable } from "@/strings/ui";
|
||||||
import type { APIPaginatedList, APIPaginationQuery } from "@/types/api";
|
import type { APIPaginatedList, APIPaginationQuery } from "@/types/api";
|
||||||
import { FAILED_STATES, type CrawlState } from "@/types/crawlState";
|
import { type CrawlState } from "@/types/crawlState";
|
||||||
import { isApiError } from "@/utils/api";
|
import { isApiError } from "@/utils/api";
|
||||||
import {
|
import {
|
||||||
DEFAULT_MAX_SCALE,
|
DEFAULT_MAX_SCALE,
|
||||||
inactiveCrawlStates,
|
inactiveCrawlStates,
|
||||||
isActive,
|
isActive,
|
||||||
|
isSkipped,
|
||||||
isSuccessfullyFinished,
|
isSuccessfullyFinished,
|
||||||
} from "@/utils/crawler";
|
} from "@/utils/crawler";
|
||||||
import { humanizeSchedule } from "@/utils/cron";
|
import { humanizeSchedule } from "@/utils/cron";
|
||||||
@ -328,10 +329,12 @@ export class WorkflowDetail extends BtrixElement {
|
|||||||
return this.workflow?.isCrawlRunning && !this.isPaused;
|
return this.workflow?.isCrawlRunning && !this.isPaused;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workflow is for a crawl that has failed or canceled
|
private get isSkippedOrCanceled() {
|
||||||
private get isUnsuccessfullyFinished() {
|
if (!this.workflow?.lastCrawlState) return null;
|
||||||
return (FAILED_STATES as readonly string[]).includes(
|
|
||||||
this.workflow?.lastCrawlState || "",
|
return (
|
||||||
|
this.workflow.lastCrawlState === "canceled" ||
|
||||||
|
isSkipped({ state: this.workflow.lastCrawlState })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -678,6 +681,10 @@ export class WorkflowDetail extends BtrixElement {
|
|||||||
const logTotals = this.logTotalsTask.value;
|
const logTotals = this.logTotalsTask.value;
|
||||||
const authToken = this.authState?.headers.Authorization.split(" ")[1];
|
const authToken = this.authState?.headers.Authorization.split(" ")[1];
|
||||||
const disableDownload = this.isRunning;
|
const disableDownload = this.isRunning;
|
||||||
|
const disableReplay = !latestCrawl.fileSize;
|
||||||
|
const disableLogs = !(logTotals?.errors || logTotals?.behaviors);
|
||||||
|
const replayHref = `/api/orgs/${this.orgId}/all-crawls/${latestCrawlId}/download?auth_bearer=${authToken}`;
|
||||||
|
const replayFilename = `browsertrix-${latestCrawlId}.wacz`;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<btrix-copy-button
|
<btrix-copy-button
|
||||||
@ -698,13 +705,13 @@ export class WorkflowDetail extends BtrixElement {
|
|||||||
content="${msg("Download Item as WACZ")} (${this.localize.bytes(
|
content="${msg("Download Item as WACZ")} (${this.localize.bytes(
|
||||||
latestCrawl.fileSize || 0,
|
latestCrawl.fileSize || 0,
|
||||||
)})"
|
)})"
|
||||||
?disabled=${!latestCrawl.fileSize}
|
?disabled=${disableReplay}
|
||||||
>
|
>
|
||||||
<sl-button
|
<sl-button
|
||||||
size="small"
|
size="small"
|
||||||
href=${`/api/orgs/${this.orgId}/all-crawls/${latestCrawlId}/download?auth_bearer=${authToken}`}
|
href=${replayHref}
|
||||||
download=${`browsertrix-${latestCrawlId}.wacz`}
|
download=${replayFilename}
|
||||||
?disabled=${disableDownload || !latestCrawl.fileSize}
|
?disabled=${disableDownload || disableReplay}
|
||||||
>
|
>
|
||||||
<sl-icon name="cloud-download" slot="prefix"></sl-icon>
|
<sl-icon name="cloud-download" slot="prefix"></sl-icon>
|
||||||
${msg("Download")}
|
${msg("Download")}
|
||||||
@ -715,7 +722,7 @@ export class WorkflowDetail extends BtrixElement {
|
|||||||
slot="trigger"
|
slot="trigger"
|
||||||
size="small"
|
size="small"
|
||||||
caret
|
caret
|
||||||
?disabled=${disableDownload}
|
?disabled=${disableReplay && disableLogs}
|
||||||
>
|
>
|
||||||
<sl-visually-hidden
|
<sl-visually-hidden
|
||||||
>${msg("Download options")}</sl-visually-hidden
|
>${msg("Download options")}</sl-visually-hidden
|
||||||
@ -723,9 +730,9 @@ export class WorkflowDetail extends BtrixElement {
|
|||||||
</sl-button>
|
</sl-button>
|
||||||
<sl-menu>
|
<sl-menu>
|
||||||
<btrix-menu-item-link
|
<btrix-menu-item-link
|
||||||
href=${`/api/orgs/${this.orgId}/all-crawls/${this.lastCrawlId}/download?auth_bearer=${authToken}`}
|
href=${replayHref}
|
||||||
?disabled=${!latestCrawl.fileSize}
|
?disabled=${disableDownload || disableReplay}
|
||||||
download
|
download=${replayFilename}
|
||||||
>
|
>
|
||||||
<sl-icon name="cloud-download" slot="prefix"></sl-icon>
|
<sl-icon name="cloud-download" slot="prefix"></sl-icon>
|
||||||
${msg("Item")}
|
${msg("Item")}
|
||||||
@ -741,7 +748,7 @@ export class WorkflowDetail extends BtrixElement {
|
|||||||
</btrix-menu-item-link>
|
</btrix-menu-item-link>
|
||||||
<btrix-menu-item-link
|
<btrix-menu-item-link
|
||||||
href=${`/api/orgs/${this.orgId}/crawls/${this.lastCrawlId}/logs?auth_bearer=${authToken}`}
|
href=${`/api/orgs/${this.orgId}/crawls/${this.lastCrawlId}/logs?auth_bearer=${authToken}`}
|
||||||
?disabled=${!(logTotals?.errors || logTotals?.behaviors)}
|
?disabled=${disableLogs}
|
||||||
download
|
download
|
||||||
>
|
>
|
||||||
<sl-icon
|
<sl-icon
|
||||||
@ -1427,7 +1434,7 @@ export class WorkflowDetail extends BtrixElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly renderLatestCrawl = () => {
|
private readonly renderLatestCrawl = () => {
|
||||||
if (!this.lastCrawlId || this.isUnsuccessfullyFinished) {
|
if (!this.lastCrawlId || this.isSkippedOrCanceled) {
|
||||||
return this.renderInactiveCrawlMessage();
|
return this.renderInactiveCrawlMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1722,6 +1729,10 @@ export class WorkflowDetail extends BtrixElement {
|
|||||||
</span>`;
|
</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isSuccessfullyFinished({ state: workflow.lastCrawlState })) {
|
||||||
|
return notApplicable;
|
||||||
|
}
|
||||||
|
|
||||||
return html`<div class="inline-flex items-center gap-2">
|
return html`<div class="inline-flex items-center gap-2">
|
||||||
${latestCrawl.reviewStatus || !this.isCrawler
|
${latestCrawl.reviewStatus || !this.isCrawler
|
||||||
? html`<btrix-qa-review-status
|
? html`<btrix-qa-review-status
|
||||||
@ -1861,13 +1872,47 @@ export class WorkflowDetail extends BtrixElement {
|
|||||||
let message = msg("This workflow hasn’t been run yet.");
|
let message = msg("This workflow hasn’t been run yet.");
|
||||||
|
|
||||||
if (this.lastCrawlId) {
|
if (this.lastCrawlId) {
|
||||||
if (this.workflow.lastCrawlState === "canceled") {
|
switch (this.workflow.lastCrawlState) {
|
||||||
message = msg("This crawl can’t be replayed since it was canceled.");
|
case "canceled":
|
||||||
} else {
|
message = msg("This crawl can’t be replayed since it was canceled.");
|
||||||
message = msg("Replay is not available for this crawl.");
|
break;
|
||||||
|
case "failed":
|
||||||
|
message = msg("This crawl can’t be replayed because it failed.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
message = msg("Replay is not available for this crawl.");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const actionButton = (workflow: Workflow) => {
|
||||||
|
if (!workflow.lastCrawlId) return;
|
||||||
|
|
||||||
|
if (workflow.lastCrawlState === "failed") {
|
||||||
|
return html`<div class="mt-4">
|
||||||
|
<sl-button
|
||||||
|
size="small"
|
||||||
|
href="${this.basePath}/logs"
|
||||||
|
@click=${this.navigate.link}
|
||||||
|
>
|
||||||
|
${msg("View Error Logs")}
|
||||||
|
<sl-icon slot="prefix" name="terminal-fill"></sl-icon>
|
||||||
|
</sl-button>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`<div class="mt-4">
|
||||||
|
<sl-button
|
||||||
|
size="small"
|
||||||
|
href="${this.basePath}/crawls/${workflow.lastCrawlId}"
|
||||||
|
@click=${this.navigate.link}
|
||||||
|
>
|
||||||
|
${msg("View Crawl Details")}
|
||||||
|
<sl-icon slot="suffix" name="arrow-right"></sl-icon>
|
||||||
|
</sl-button>
|
||||||
|
</div>`;
|
||||||
|
};
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<section
|
<section
|
||||||
class="flex h-56 min-h-max flex-col items-center justify-center rounded-lg border p-4"
|
class="flex h-56 min-h-max flex-col items-center justify-center rounded-lg border p-4"
|
||||||
@ -1878,20 +1923,7 @@ export class WorkflowDetail extends BtrixElement {
|
|||||||
this.isCrawler && !this.lastCrawlId,
|
this.isCrawler && !this.lastCrawlId,
|
||||||
() => html`<div class="mt-4">${this.renderRunNowButton()}</div>`,
|
() => html`<div class="mt-4">${this.renderRunNowButton()}</div>`,
|
||||||
)}
|
)}
|
||||||
${when(
|
${when(this.workflow, actionButton)}
|
||||||
this.lastCrawlId,
|
|
||||||
(id) =>
|
|
||||||
html`<div class="mt-4">
|
|
||||||
<sl-button
|
|
||||||
size="small"
|
|
||||||
href="${this.basePath}/crawls/${id}"
|
|
||||||
@click=${this.navigate.link}
|
|
||||||
>
|
|
||||||
${msg("View Crawl Details")}
|
|
||||||
<sl-icon slot="suffix" name="arrow-right"></sl-icon>
|
|
||||||
</sl-button>
|
|
||||||
</div>`,
|
|
||||||
)}
|
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -33,11 +33,15 @@ export function isActive({ state }: Partial<Crawl | QARun>) {
|
|||||||
return (activeCrawlStates as readonly (typeof state)[]).includes(state);
|
return (activeCrawlStates as readonly (typeof state)[]).includes(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isSuccessfullyFinished({ state }: { state: string }) {
|
export function isSuccessfullyFinished({ state }: { state: string | null }) {
|
||||||
return state && (SUCCESSFUL_STATES as readonly string[]).includes(state);
|
return state && (SUCCESSFUL_STATES as readonly string[]).includes(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNotFailed({ state }: { state: string }) {
|
export function isSkipped({ state }: { state: string | null }) {
|
||||||
|
return state?.startsWith("skipped");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNotFailed({ state }: { state: string | null }) {
|
||||||
return (
|
return (
|
||||||
state && !(FAILED_STATES as readonly string[]).some((str) => str === state)
|
state && !(FAILED_STATES as readonly string[]).some((str) => str === state)
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user