Feedback for triggered actions
Change-Id: Ib93f12bbec4813b7231bb6cdfee2c970db4ff69b
This commit is contained in:
@@ -247,7 +247,7 @@ export type ActionCallback = (
|
|||||||
checkName: string | undefined,
|
checkName: string | undefined,
|
||||||
/** Identical to 'name' property of Action entity. */
|
/** Identical to 'name' property of Action entity. */
|
||||||
actionName: string
|
actionName: string
|
||||||
) => Promise<ActionResult>;
|
) => Promise<ActionResult> | undefined;
|
||||||
|
|
||||||
export interface ActionResult {
|
export interface ActionResult {
|
||||||
/** An empty errorMessage means success. */
|
/** An empty errorMessage means success. */
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
allActions$,
|
allActions$,
|
||||||
allResults$,
|
allResults$,
|
||||||
allRuns$,
|
allRuns$,
|
||||||
|
someProvidersAreLoading$,
|
||||||
} from '../../services/checks/checks-model';
|
} from '../../services/checks/checks-model';
|
||||||
import './gr-checks-runs';
|
import './gr-checks-runs';
|
||||||
import './gr-checks-results';
|
import './gr-checks-results';
|
||||||
@@ -41,6 +42,10 @@ import {
|
|||||||
import {checkRequiredProperty} from '../../utils/common-util';
|
import {checkRequiredProperty} from '../../utils/common-util';
|
||||||
import {RunSelectedEvent} from './gr-checks-runs';
|
import {RunSelectedEvent} from './gr-checks-runs';
|
||||||
import {ChecksTabState} from '../../types/events';
|
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
|
* The "Checks" tab on the Gerrit change page. Gets its data from plugins that
|
||||||
@@ -64,9 +69,14 @@ export class GrChecksTab extends GrLitElement {
|
|||||||
@property()
|
@property()
|
||||||
changeNum: NumericChangeId | undefined = undefined;
|
changeNum: NumericChangeId | undefined = undefined;
|
||||||
|
|
||||||
|
@property()
|
||||||
|
someProvidersAreLoading = false;
|
||||||
|
|
||||||
@internalProperty()
|
@internalProperty()
|
||||||
selectedRuns: string[] = [];
|
selectedRuns: string[] = [];
|
||||||
|
|
||||||
|
private readonly checksService = appContext.checksService;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.subscribe('runs', allRuns$);
|
this.subscribe('runs', allRuns$);
|
||||||
@@ -74,6 +84,7 @@ export class GrChecksTab extends GrLitElement {
|
|||||||
this.subscribe('results', allResults$);
|
this.subscribe('results', allResults$);
|
||||||
this.subscribe('currentPatchNum', currentPatchNum$);
|
this.subscribe('currentPatchNum', currentPatchNum$);
|
||||||
this.subscribe('changeNum', changeNum$);
|
this.subscribe('changeNum', changeNum$);
|
||||||
|
this.subscribe('someProvidersAreLoading', someProvidersAreLoading$);
|
||||||
|
|
||||||
this.addEventListener('action-triggered', (e: ActionTriggeredEvent) =>
|
this.addEventListener('action-triggered', (e: ActionTriggeredEvent) =>
|
||||||
this.handleActionTriggered(e.detail.action, e.detail.run)
|
this.handleActionTriggered(e.detail.action, e.detail.run)
|
||||||
@@ -131,6 +142,7 @@ export class GrChecksTab extends GrLitElement {
|
|||||||
},
|
},
|
||||||
]}"
|
]}"
|
||||||
></gr-dropdown-list>
|
></gr-dropdown-list>
|
||||||
|
<span ?hidden="${!this.someProvidersAreLoading}">Loading...</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
${this.actions.map(this.renderAction)}
|
${this.actions.map(this.renderAction)}
|
||||||
@@ -170,10 +182,7 @@ export class GrChecksTab extends GrLitElement {
|
|||||||
handleActionTriggered(action: Action, run?: CheckRun) {
|
handleActionTriggered(action: Action, run?: CheckRun) {
|
||||||
if (!this.changeNum) return;
|
if (!this.changeNum) return;
|
||||||
if (!this.currentPatchNum) return;
|
if (!this.currentPatchNum) return;
|
||||||
// TODO(brohlfs): The callback is supposed to be returning a promise.
|
const promise = action.callback(
|
||||||
// A toast should be displayed until the promise completes. And then the
|
|
||||||
// data should be updated.
|
|
||||||
action.callback(
|
|
||||||
this.changeNum,
|
this.changeNum,
|
||||||
this.currentPatchNum as number,
|
this.currentPatchNum as number,
|
||||||
run?.attempt,
|
run?.attempt,
|
||||||
@@ -181,6 +190,25 @@ export class GrChecksTab extends GrLitElement {
|
|||||||
run?.checkName,
|
run?.checkName,
|
||||||
action.name
|
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) {
|
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(
|
export const allResults$ = checksState$.pipe(
|
||||||
map(state => {
|
map(state => {
|
||||||
return Object.values(state)
|
return Object.values(state)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
import {change$, currentPatchNum$} from '../change/change-model';
|
import {change$, currentPatchNum$} from '../change/change-model';
|
||||||
import {
|
import {
|
||||||
updateStateSetLoading,
|
updateStateSetLoading,
|
||||||
|
checkToPluginMap$,
|
||||||
updateStateSetProvider,
|
updateStateSetProvider,
|
||||||
updateStateSetResults,
|
updateStateSetResults,
|
||||||
} from './checks-model';
|
} from './checks-model';
|
||||||
@@ -51,6 +52,12 @@ export class ChecksService {
|
|||||||
|
|
||||||
private changeAndPatchNum$ = change$.pipe(withLatestFrom(currentPatchNum$));
|
private changeAndPatchNum$ = change$.pipe(withLatestFrom(currentPatchNum$));
|
||||||
|
|
||||||
|
private checkToPluginMap = new Map<string, string>();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
checkToPluginMap$.subscribe(x => (this.checkToPluginMap = x));
|
||||||
|
}
|
||||||
|
|
||||||
reload(pluginName: string) {
|
reload(pluginName: string) {
|
||||||
this.reloadSubjects[pluginName].next();
|
this.reloadSubjects[pluginName].next();
|
||||||
}
|
}
|
||||||
@@ -59,6 +66,12 @@ export class ChecksService {
|
|||||||
Object.keys(this.providers).forEach(key => this.reload(key));
|
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(
|
register(
|
||||||
pluginName: string,
|
pluginName: string,
|
||||||
provider: ChecksProvider,
|
provider: ChecksProvider,
|
||||||
|
|||||||
Reference in New Issue
Block a user