Feedback for triggered actions
Change-Id: Ib93f12bbec4813b7231bb6cdfee2c970db4ff69b
This commit is contained in:
@@ -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. */
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user