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