mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
* Added promise with cancel * Fixed imports * Fixed an async unit test
This commit is contained in:
parent
a43c19ee82
commit
f4bd4b5ec2
4 changed files with 111 additions and 15 deletions
70
x-pack/plugins/monitoring/common/cancel_promise.ts
Normal file
70
x-pack/plugins/monitoring/common/cancel_promise.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export enum Status {
|
||||
Canceled,
|
||||
Failed,
|
||||
Resolved,
|
||||
Awaiting,
|
||||
Idle,
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple [PromiseWithCancel] factory
|
||||
*/
|
||||
export class PromiseWithCancel {
|
||||
private _promise: Promise<any>;
|
||||
private _status: Status = Status.Idle;
|
||||
|
||||
/**
|
||||
* @param {Promise} promise Promise you want to cancel / track
|
||||
*/
|
||||
constructor(promise: Promise<any>) {
|
||||
this._promise = promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the promise in any state
|
||||
*/
|
||||
public cancel = (): void => {
|
||||
this._status = Status.Canceled;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns status based on [Status]
|
||||
*/
|
||||
public status = (): Status => {
|
||||
return this._status;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns promise passed in [constructor]
|
||||
* This sets the state to Status.Awaiting
|
||||
*/
|
||||
public promise = (): Promise<any> => {
|
||||
if (this._status === Status.Canceled) {
|
||||
throw Error('Getting a canceled promise is not allowed');
|
||||
} else if (this._status !== Status.Idle) {
|
||||
return this._promise;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._status = Status.Awaiting;
|
||||
return this._promise
|
||||
.then(response => {
|
||||
if (this._status !== Status.Canceled) {
|
||||
this._status = Status.Resolved;
|
||||
return resolve(response);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (this._status !== Status.Canceled) {
|
||||
this._status = Status.Failed;
|
||||
return reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
|
@ -8,6 +8,7 @@ import { spy, stub } from 'sinon';
|
|||
import expect from '@kbn/expect';
|
||||
import { MonitoringViewBaseController } from '../';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { PromiseWithCancel, Status } from '../../../common/cancel_promise';
|
||||
|
||||
/*
|
||||
* Mostly copied from base_table_controller test, with modifications
|
||||
|
@ -20,6 +21,7 @@ describe('MonitoringViewBaseController', function () {
|
|||
let opts;
|
||||
let titleService;
|
||||
let executorService;
|
||||
const httpCall = (ms) => new Promise((resolve) => setTimeout(() => resolve(), ms));
|
||||
|
||||
before(() => {
|
||||
titleService = spy();
|
||||
|
@ -36,7 +38,8 @@ describe('MonitoringViewBaseController', function () {
|
|||
|
||||
$scope = {
|
||||
cluster: { cluster_uuid: 'foo' },
|
||||
$on: stub()
|
||||
$on: stub(),
|
||||
$apply: stub()
|
||||
};
|
||||
|
||||
opts = {
|
||||
|
@ -73,17 +76,15 @@ describe('MonitoringViewBaseController', function () {
|
|||
let counter = 0;
|
||||
const opts = {
|
||||
title: 'testo',
|
||||
getPageData: () => Promise.resolve(++counter),
|
||||
getPageData: (ms) => httpCall(ms),
|
||||
$injector,
|
||||
$scope
|
||||
};
|
||||
|
||||
const ctrl = new MonitoringViewBaseController(opts);
|
||||
Promise.all([
|
||||
ctrl.updateData(),
|
||||
ctrl.updateData(),
|
||||
]).then(() => {
|
||||
expect(counter).to.be(1);
|
||||
ctrl.updateData(30).then(() => ++counter);
|
||||
ctrl.updateData(60).then(() => {
|
||||
expect(counter).to.be(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -140,6 +141,29 @@ describe('MonitoringViewBaseController', function () {
|
|||
expect(timefilter.isTimeRangeSelectorEnabled).to.be(false);
|
||||
expect(timefilter.isAutoRefreshSelectorEnabled).to.be(false);
|
||||
});
|
||||
|
||||
it('disables timepicker and auto refresh', (done) => {
|
||||
opts = {
|
||||
title: 'test',
|
||||
getPageData: () => httpCall(60),
|
||||
$injector,
|
||||
$scope
|
||||
};
|
||||
|
||||
ctrl = new MonitoringViewBaseController({ ...opts });
|
||||
ctrl.updateDataPromise = new PromiseWithCancel(httpCall(50));
|
||||
|
||||
let shouldBeFalse = false;
|
||||
ctrl.updateDataPromise.promise().then(() => (shouldBeFalse = true));
|
||||
|
||||
const lastUpdateDataPromise = ctrl.updateDataPromise;
|
||||
|
||||
ctrl.updateData().then(() => {
|
||||
expect(shouldBeFalse).to.be(false);
|
||||
expect(lastUpdateDataPromise.status()).to.be(Status.Canceled);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ import { getPageData } from '../lib/get_page_data';
|
|||
import { PageLoading } from 'plugins/monitoring/components';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { PromiseWithCancel } from '../../common/cancel_promise';
|
||||
|
||||
/**
|
||||
* Class to manage common instantiation behaviors in a view controller
|
||||
|
@ -96,23 +97,21 @@ export class MonitoringViewBaseController {
|
|||
timefilter.enableAutoRefreshSelector();
|
||||
}
|
||||
|
||||
this.updateDataPromise = null;
|
||||
this.updateData = () => {
|
||||
if (this.updateDataPromise) {
|
||||
// Do not sent another request if one is inflight
|
||||
// See https://github.com/elastic/kibana/issues/24082
|
||||
return this.updateDataPromise;
|
||||
this.updateDataPromise.cancel();
|
||||
this.updateDataPromise = null;
|
||||
}
|
||||
const _api = apiUrlFn ? apiUrlFn() : api;
|
||||
return this.updateDataPromise = _getPageData($injector, _api)
|
||||
.then(pageData => {
|
||||
this.updateDataPromise = new PromiseWithCancel(_getPageData($injector, _api));
|
||||
return this.updateDataPromise.promise().then((pageData) => {
|
||||
$scope.$apply(() => {
|
||||
this._isDataInitialized = true; // render will replace loading screen with the react component
|
||||
$scope.pageData = this.data = pageData; // update the view's data with the fetch result
|
||||
this.updateDataPromise = null;
|
||||
})
|
||||
.catch(() => {
|
||||
this.updateDataPromise = null;
|
||||
});
|
||||
});
|
||||
};
|
||||
this.updateData();
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
],
|
||||
"test_utils/*": [
|
||||
"x-pack/test_utils/*"
|
||||
],
|
||||
"monitoring/common/*": [
|
||||
"x-pack/monitoring/common/*"
|
||||
]
|
||||
},
|
||||
"types": [
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue