debian-mirror-gitlab/app/assets/javascripts/lib/utils/poll.js

130 lines
3.3 KiB
JavaScript
Raw Normal View History

2018-11-18 11:00:15 +05:30
import httpStatusCodes, { successCodes } from './http_status';
2018-03-17 18:26:18 +05:30
import { normalizeHeaders } from './common_utils';
2017-08-17 22:00:37 +05:30
/**
* Polling utility for handling realtime updates.
2018-03-17 18:26:18 +05:30
* Requirements: Promise based HTTP client
*
* Service for promise based http client and method need to be provided as props
2017-08-17 22:00:37 +05:30
*
* @example
* new Poll({
* resource: resource,
* method: 'name',
* data: {page: 1, scope: 'all'}, // optional
* successCallback: () => {},
* errorCallback: () => {},
* notificationCallback: () => {}, // optional
* }).makeRequest();
*
* Usage in pipelines table with visibility lib:
*
* const poll = new Poll({
* resource: this.service,
* method: 'getPipelines',
* data: { page: pageNumber, scope },
* successCallback: this.successCallback,
* errorCallback: this.errorCallback,
* notificationCallback: this.updateLoading,
* });
*
* if (!Visibility.hidden()) {
* poll.makeRequest();
* }
*
* Visibility.change(() => {
* if (!Visibility.hidden()) {
* poll.restart();
* } else {
* poll.stop();
* }
2018-11-18 11:00:15 +05:30
* });
2017-08-17 22:00:37 +05:30
*
* 1. Checks for response and headers before start polling
* 2. Interval is provided by `Poll-Interval` header.
* 3. If `Poll-Interval` is -1, we stop polling
* 4. If HTTP response is 200, we poll.
* 5. If HTTP response is different from 200, we stop polling.
*
*/
export default class Poll {
constructor(options = {}) {
this.options = options;
this.options.data = options.data || {};
2018-11-18 11:00:15 +05:30
this.options.notificationCallback =
options.notificationCallback || function notificationCallback() {};
2017-08-17 22:00:37 +05:30
this.intervalHeader = 'POLL-INTERVAL';
this.timeoutID = null;
this.canPoll = true;
}
checkConditions(response) {
2018-03-17 18:26:18 +05:30
const headers = normalizeHeaders(response.headers);
2017-08-17 22:00:37 +05:30
const pollInterval = parseInt(headers[this.intervalHeader], 10);
2018-11-18 11:00:15 +05:30
if (pollInterval > 0 && successCodes.indexOf(response.status) !== -1 && this.canPoll) {
2019-05-18 00:54:41 +05:30
if (this.timeoutID) {
clearTimeout(this.timeoutID);
}
2017-08-17 22:00:37 +05:30
this.timeoutID = setTimeout(() => {
this.makeRequest();
}, pollInterval);
}
this.options.successCallback(response);
}
makeRequest() {
const { resource, method, data, errorCallback, notificationCallback } = this.options;
// It's called everytime a new request is made. Useful to update the status.
notificationCallback(true);
return resource[method](data)
2018-11-18 11:00:15 +05:30
.then(response => {
2017-08-17 22:00:37 +05:30
this.checkConditions(response);
notificationCallback(false);
})
2018-11-18 11:00:15 +05:30
.catch(error => {
2017-08-17 22:00:37 +05:30
notificationCallback(false);
2017-09-10 17:25:29 +05:30
if (error.status === httpStatusCodes.ABORTED) {
return;
}
2017-08-17 22:00:37 +05:30
errorCallback(error);
});
}
/**
* Stops the polling recursive chain
* and guarantees if the timeout is already running it won't make another request by
* cancelling the previously established timeout.
*/
stop() {
this.canPoll = false;
clearTimeout(this.timeoutID);
}
/**
2019-05-18 00:54:41 +05:30
* Enables polling after it has been stopped
2017-08-17 22:00:37 +05:30
*/
2019-05-18 00:54:41 +05:30
enable(options) {
2018-03-17 18:26:18 +05:30
if (options && options.data) {
this.options.data = options.data;
}
2017-08-17 22:00:37 +05:30
this.canPoll = true;
2019-05-18 00:54:41 +05:30
if (options && options.response) {
this.checkConditions(options.response);
}
}
/**
* Restarts polling after it has been stopped and makes a request
*/
restart(options) {
this.enable(options);
2017-08-17 22:00:37 +05:30
this.makeRequest();
}
}