summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFatih Acet <acetfatih@gmail.com>2017-03-23 11:41:05 +0000
committerFatih Acet <acetfatih@gmail.com>2017-03-23 11:41:05 +0000
commitdc058e153c3c91a0debe4277f17797ad34ea9f04 (patch)
treebd6dfec404f32fb8b31fe88f6d6d51afc3bee40b
parent1bb1a75a2b2876bcba15ce8d542e3604595d84d7 (diff)
parent45b2c63fdf2d7d35aa5c4e6adba96d597baf1628 (diff)
downloadgitlab-ce-dc058e153c3c91a0debe4277f17797ad34ea9f04.tar.gz
Merge branch '29575-polling' into 'master'
Poll with vue resource get function Closes #29575 See merge request !10057
-rw-r--r--app/assets/javascripts/lib/utils/poll.js62
-rw-r--r--changelogs/unreleased/29575-polling.yml4
-rw-r--r--spec/javascripts/lib/utils/poll_spec.js127
3 files changed, 193 insertions, 0 deletions
diff --git a/app/assets/javascripts/lib/utils/poll.js b/app/assets/javascripts/lib/utils/poll.js
new file mode 100644
index 00000000000..938cf9912a8
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/poll.js
@@ -0,0 +1,62 @@
+import httpStatusCodes from './http_status';
+
+/**
+ * Polling utility for handling realtime updates.
+ * Service for vue resouce and method need to be provided as props
+ *
+ * @example
+ * new poll({
+ * resource: resource,
+ * method: 'name',
+ * data: {page: 1, scope: 'all'},
+ * successCallback: () => {},
+ * errorCallback: () => {},
+ * }).makeRequest();
+ *
+ * this.service = new BoardsService(endpoint);
+ * new poll({
+ * resource: this.service,
+ * method: 'get',
+ * data: {page: 1, scope: 'all'},
+ * successCallback: () => {},
+ * errorCallback: () => {},
+ * }).makeRequest();
+ *
+ *
+ * 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 || {};
+
+ this.intervalHeader = 'POLL-INTERVAL';
+ }
+
+ checkConditions(response) {
+ const headers = gl.utils.normalizeHeaders(response.headers);
+ const pollInterval = headers[this.intervalHeader];
+
+ if (pollInterval > 0 && response.status === httpStatusCodes.OK) {
+ this.options.successCallback(response);
+ setTimeout(() => {
+ this.makeRequest();
+ }, pollInterval);
+ } else {
+ this.options.successCallback(response);
+ }
+ }
+
+ makeRequest() {
+ const { resource, method, data, errorCallback } = this.options;
+
+ return resource[method](data)
+ .then(response => this.checkConditions(response))
+ .catch(error => errorCallback(error));
+ }
+}
diff --git a/changelogs/unreleased/29575-polling.yml b/changelogs/unreleased/29575-polling.yml
new file mode 100644
index 00000000000..75016afd455
--- /dev/null
+++ b/changelogs/unreleased/29575-polling.yml
@@ -0,0 +1,4 @@
+---
+title: Adds polling utility function for vue resource
+merge_request:
+author:
diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js
new file mode 100644
index 00000000000..05bc6bfd74b
--- /dev/null
+++ b/spec/javascripts/lib/utils/poll_spec.js
@@ -0,0 +1,127 @@
+import Vue from 'vue';
+import VueResource from 'vue-resource';
+import Poll from '~/lib/utils/poll';
+
+Vue.use(VueResource);
+
+class ServiceMock {
+ constructor(endpoint) {
+ this.service = Vue.resource(endpoint);
+ }
+
+ fetch() {
+ return this.service.get();
+ }
+}
+
+describe('Poll', () => {
+ let callbacks;
+
+ beforeEach(() => {
+ callbacks = {
+ success: () => {},
+ error: () => {},
+ };
+
+ spyOn(callbacks, 'success');
+ spyOn(callbacks, 'error');
+ });
+
+ it('calls the success callback when no header for interval is provided', (done) => {
+ const successInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200 }));
+ };
+
+ Vue.http.interceptors.push(successInterceptor);
+
+ new Poll({
+ resource: new ServiceMock('endpoint'),
+ method: 'fetch',
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
+ done();
+ }, 0);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, successInterceptor);
+ });
+
+ it('calls the error callback whe the http request returns an error', (done) => {
+ const errorInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 500 }));
+ };
+
+ Vue.http.interceptors.push(errorInterceptor);
+
+ new Poll({
+ resource: new ServiceMock('endpoint'),
+ method: 'fetch',
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(callbacks.success).not.toHaveBeenCalled();
+ expect(callbacks.error).toHaveBeenCalled();
+ done();
+ }, 0);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, errorInterceptor);
+ });
+
+ it('should call the success callback when the interval header is -1', (done) => {
+ const intervalInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200, headers: { 'poll-interval': -1 } }));
+ };
+
+ Vue.http.interceptors.push(intervalInterceptor);
+
+ new Poll({
+ resource: new ServiceMock('endpoint'),
+ method: 'fetch',
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
+ done();
+ }, 0);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, intervalInterceptor);
+ });
+
+ it('starts polling when http status is 200 and interval header is provided', (done) => {
+ const pollInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200, headers: { 'poll-interval': 2 } }));
+ };
+
+ Vue.http.interceptors.push(pollInterceptor);
+
+ const service = new ServiceMock('endpoint');
+ spyOn(service, 'fetch').and.callThrough();
+
+ new Poll({
+ resource: service,
+ method: 'fetch',
+ data: { page: 1 },
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(service.fetch.calls.count()).toEqual(2);
+ expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
+ done();
+ }, 5);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+ });
+});