Feedback for triggered actions

Change-Id: Ib93f12bbec4813b7231bb6cdfee2c970db4ff69b
This commit is contained in:
Ben Rohlfs
2021-02-25 14:45:15 +01:00
parent e4894feac1
commit 068c743835
4 changed files with 58 additions and 5 deletions

View File

@@ -247,7 +247,7 @@ export type ActionCallback = (
checkName: string | undefined,
/** Identical to 'name' property of Action entity. */
actionName: string
) => Promise<ActionResult>;
) => Promise<ActionResult> | undefined;
export interface ActionResult {
/** An empty errorMessage means success. */

View File

@@ -28,6 +28,7 @@ import {
allActions$,
allResults$,
allRuns$,
someProvidersAreLoading$,
} from '../../services/checks/checks-model';
import './gr-checks-runs';
import './gr-checks-results';
@@ -41,6 +42,10 @@ import {
import {checkRequiredProperty} from '../../utils/common-util';
import {RunSelectedEvent} from './gr-checks-runs';
import {ChecksTabState} from '../../types/events';
import {fireAlert} from '../../utils/event-util';
import {appContext} from '../../services/app-context';
import {from, timer} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
/**
* The "Checks" tab on the Gerrit change page. Gets its data from plugins that
@@ -64,9 +69,14 @@ export class GrChecksTab extends GrLitElement {
@property()
changeNum: NumericChangeId | undefined = undefined;
@property()
someProvidersAreLoading = false;
@internalProperty()
selectedRuns: string[] = [];
private readonly checksService = appContext.checksService;
constructor() {
super();
this.subscribe('runs', allRuns$);
@@ -74,6 +84,7 @@ export class GrChecksTab extends GrLitElement {
this.subscribe('results', allResults$);
this.subscribe('currentPatchNum', currentPatchNum$);
this.subscribe('changeNum', changeNum$);
this.subscribe('someProvidersAreLoading', someProvidersAreLoading$);
this.addEventListener('action-triggered', (e: ActionTriggeredEvent) =>
this.handleActionTriggered(e.detail.action, e.detail.run)
@@ -131,6 +142,7 @@ export class GrChecksTab extends GrLitElement {
},
]}"
></gr-dropdown-list>
<span ?hidden="${!this.someProvidersAreLoading}">Loading...</span>
</div>
<div class="right">
${this.actions.map(this.renderAction)}
@@ -170,10 +182,7 @@ export class GrChecksTab extends GrLitElement {
handleActionTriggered(action: Action, run?: CheckRun) {
if (!this.changeNum) return;
if (!this.currentPatchNum) return;
// TODO(brohlfs): The callback is supposed to be returning a promise.
// A toast should be displayed until the promise completes. And then the
// data should be updated.
action.callback(
const promise = action.callback(
this.changeNum,
this.currentPatchNum as number,
run?.attempt,
@@ -181,6 +190,25 @@ export class GrChecksTab extends GrLitElement {
run?.checkName,
action.name
);
// Plugins *should* return a promise, but you never know ...
if (promise?.then) {
const prefix = `Triggering action '${action.name}'`;
fireAlert(this, `${prefix} ...`);
from(promise)
// If the action takes longer than 5 seconds, then most likely the
// user is either not interested or the result not relevant anymore.
.pipe(takeUntil(timer(5000)))
.subscribe(result => {
if (result.errorMessage) {
fireAlert(this, `${prefix} failed with ${result.errorMessage}.`);
} else {
fireAlert(this, `${prefix} successful.`);
this.checksService.reloadForCheck(run?.checkName);
}
});
} else {
fireAlert(this, `Action '${action.name}' triggered.`);
}
}
handleRunSelected(e: RunSelectedEvent) {

View File

@@ -87,6 +87,18 @@ export const allRuns$ = checksState$.pipe(
})
);
export const checkToPluginMap$ = checksState$.pipe(
map(state => {
const map = new Map<string, string>();
for (const [pluginName, providerState] of Object.entries(state)) {
for (const run of providerState.runs) {
map.set(run.checkName, pluginName);
}
}
return map;
})
);
export const allResults$ = checksState$.pipe(
map(state => {
return Object.values(state)

View File

@@ -32,6 +32,7 @@ import {
import {change$, currentPatchNum$} from '../change/change-model';
import {
updateStateSetLoading,
checkToPluginMap$,
updateStateSetProvider,
updateStateSetResults,
} from './checks-model';
@@ -51,6 +52,12 @@ export class ChecksService {
private changeAndPatchNum$ = change$.pipe(withLatestFrom(currentPatchNum$));
private checkToPluginMap = new Map<string, string>();
constructor() {
checkToPluginMap$.subscribe(x => (this.checkToPluginMap = x));
}
reload(pluginName: string) {
this.reloadSubjects[pluginName].next();
}
@@ -59,6 +66,12 @@ export class ChecksService {
Object.keys(this.providers).forEach(key => this.reload(key));
}
reloadForCheck(checkName?: string) {
if (!checkName) return;
const plugin = this.checkToPluginMap.get(checkName);
if (plugin) this.reload(plugin);
}
register(
pluginName: string,
provider: ChecksProvider,