diff options
author | Fatih Acet <acetfatih@gmail.com> | 2017-03-23 11:41:05 +0000 |
---|---|---|
committer | Fatih Acet <acetfatih@gmail.com> | 2017-03-23 11:41:05 +0000 |
commit | dc058e153c3c91a0debe4277f17797ad34ea9f04 (patch) | |
tree | bd6dfec404f32fb8b31fe88f6d6d51afc3bee40b | |
parent | 1bb1a75a2b2876bcba15ce8d542e3604595d84d7 (diff) | |
parent | 45b2c63fdf2d7d35aa5c4e6adba96d597baf1628 (diff) | |
download | gitlab-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.js | 62 | ||||
-rw-r--r-- | changelogs/unreleased/29575-polling.yml | 4 | ||||
-rw-r--r-- | spec/javascripts/lib/utils/poll_spec.js | 127 |
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); + }); +}); |