blob: 809988d2c3a406df04dd7581e941e1072b199e75 [file] [log] [blame]
/**
* @module module/verfiers-detail-sk
* @description <h2><code>verifiers-detail-sk</code></h2>
*
* Displays the details of all verifiers that have run for a
* change+patchset.
*
* @attr {number} change_id - The change ID of the Gerrit change.
*
* @attr {number} patchset_id - The patchset ID of the Gerrit change.
*
*/
import { define } from 'elements-sk/define';
import { html, TemplateResult } from 'lit-html';
import { upgradeProperty } from 'elements-sk/upgradeProperty';
import { diffDate } from 'common-sk/modules/human';
import { ElementSk } from '../../../infra-sk/modules/ElementSk';
import 'elements-sk/error-toast-sk';
import 'elements-sk/icon/folder-icon-sk';
import 'elements-sk/icon/help-icon-sk';
import 'elements-sk/icon/home-icon-sk';
import 'elements-sk/spinner-sk';
import '../../../infra-sk/modules/app-sk';
import '../../../infra-sk/modules/login-sk';
import '../../../infra-sk/modules/theme-chooser-sk';
import { escapeAndLinkify } from '../../../infra-sk/modules/linkify';
import { doImpl } from '../skcq';
import {
GetChangeAttemptsRequest, GetChangeAttemptsResponse, ChangeAttempts, ChangeAttempt, VerifierState,
} from '../json';
const VerifierStateToClassName: Record<VerifierState, string> = {
SUCCESSFUL: 'success',
WAITING: 'waiting',
ABORTED: 'aborted',
FAILURE: 'failed',
};
export class VerifiersDetailSk extends ElementSk {
noData: boolean = true;
updatingData: boolean= true;
changeAttempts: (ChangeAttempts|null) = null;
constructor() {
super(VerifiersDetailSk.template);
}
private static template = (el: VerifiersDetailSk) => html`
<spinner-sk ?active=${el.updatingData}></spinner-sk>
<div ?hidden=${el.noData}>
<h3>SkCQ attempts for <a href="http://skia-review.googlesource.com/c/${el.changeID}/${el.patchsetID}" target=_blank>${el.changeID}/${el.patchsetID}</a></h3>
${el.displayAttempts()}
</div>
${el.displayNotFoundMsg(el.noData, el.updatingData)}
`;
connectedCallback(): void {
super.connectedCallback();
this.fetchAttempts();
this._render();
}
disconnectedCallback(): void {
super.disconnectedCallback();
}
/** @prop change_id The Change ID of the Gerrit change. */
get changeID(): number {
return +this.getAttribute('change_id')!;
}
/** @prop patchset_id The Patchset ID of the Gerrit change. */
get patchsetID(): number {
return +this.getAttribute('patchset_id')!;
}
private displayNotFoundMsg(hidden: boolean, updatingData: boolean): TemplateResult {
if (hidden && !updatingData) {
return html`<h3>No data available for <a href="http://skia-review.googlesource.com/c/${this.changeID}/${this.patchsetID}" target=_blank>${this.changeID}/${this.patchsetID}</a></h3>`;
}
return html``;
}
private displayAttempts(): TemplateResult[] {
if (!this.changeAttempts || !this.changeAttempts!.attempts) {
return [];
}
return this.changeAttempts!.attempts.reverse().map((attempt, index) => html`
<span class="attempt-section ${this.getStateClass(attempt!.overall_status)}">
<table class="attempt-table">
<tr>
<th width=33%>
Attempt #${this.changeAttempts!.attempts!.length - index} ${this.getDryRunText(attempt!)}
</th>
<th width=33%>
${attempt?.overall_status}
</th>
<th width=33%>
Total duration: ${this.getTotalDurationText(attempt!)}
</th>
</tr>
</table>
<br/>
<b>Start time</b> ${new Date(attempt!.start_ts * 1000).toLocaleString()}<br/>
${this.getCommittedText(attempt!)}
${this.displaySubmittableChanges(attempt!)}
<br/>
<b>State of Verifiers</b>
${this.displayVerifiers(attempt!)}
</span>
`);
}
private displayVerifiers(attempt: ChangeAttempt): TemplateResult[] {
if (!attempt.verifiers_statuses || !attempt.verifiers_statuses.length) {
return [];
}
return attempt.verifiers_statuses.map((verifier) => html`
<table class="verifier-table ${this.getStateClass(verifier!.state)}">
<tr>
<td width=33%>
${verifier?.name}
</td>
<td width=33%>
${verifier?.state}
</td>
<td width=33%>
${this.getVerificationDurationText(verifier!.start_ts, verifier!.stop_ts)}
</td>
</tr>
<tr>
<td colspan=3>
${this.displayReason(verifier!.reason)}
</td>
</tr>
</table>
`);
}
private displayReason(reason: string): TemplateResult[] {
const lines = reason.split('\n');
return lines.map((line) => html`
${escapeAndLinkify(line)}<br/>
`);
}
private getStateClass(state: VerifierState): string {
return VerifierStateToClassName[state];
}
private getVerificationDurationText(start: number, stop: number): TemplateResult {
let endTime = Date.now();
let additionalInfo = '(still running)';
if (stop) {
endTime = stop * 1000;
additionalInfo = '';
}
return html`Total duration: ${diffDate(start * 1000, endTime)} ${additionalInfo}<br/>`;
}
private displaySubmittableChanges(attempt: ChangeAttempt): TemplateResult {
if (!attempt.dry_run && attempt.submittable_changes && attempt.submittable_changes.length > 0) {
return html`<b>Changes submitted together</b>
<ul>
${attempt.submittable_changes.map((ch) => html`<li><a href="http://skia-review.googlesource.com/c/${ch}" target=_blank>${ch}</a></li>`)}
</ul>
`;
}
return html``;
}
private getDryRunText(attempt: ChangeAttempt): TemplateResult {
if (attempt.dry_run) {
return html`(Dry-Run)`;
}
return html`(CQ)`;
}
private getTotalDurationText(attempt: ChangeAttempt): TemplateResult {
let endTime = Date.now();
let additionalInfo = '(still running)';
if (attempt.stop_ts) {
endTime = attempt.stop_ts * 1000;
additionalInfo = '';
}
if (attempt.cq_abandoned) {
additionalInfo = '(was abandoned)';
}
return html`${diffDate(attempt?.start_ts as number * 1000, endTime)} ${additionalInfo}`;
}
private getCommittedText(attempt: ChangeAttempt): TemplateResult {
if (attempt.committed_ts) {
const d = new Date(attempt.committed_ts * 1000).toLocaleString();
return html`<b>Patch committed at</b> ${d}<br/>`;
}
return html``;
}
private fetchAttempts() {
if (!this.changeID || !this.patchsetID) {
return;
}
const detail: GetChangeAttemptsRequest = {
change_id: this.changeID,
patchset_id: this.patchsetID,
};
doImpl<GetChangeAttemptsRequest, GetChangeAttemptsResponse>('/_/get_change_attempts', detail, (json: GetChangeAttemptsResponse) => {
this.changeAttempts = json.change_attempts!;
this.noData = !this.changeAttempts || this.changeAttempts.attempts!.length === 0;
this.updatingData = false;
this._render();
});
}
}
define('verifiers-detail-sk', VerifiersDetailSk);