mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-23 07:46:23 -04:00
Merge pull request #3405 from alphagov/ajax-backoff
Delay AJAX calls if the server is slow to respond
This commit is contained in:
@@ -3,6 +3,12 @@
|
|||||||
|
|
||||||
var queues = {};
|
var queues = {};
|
||||||
var dd = new global.diffDOM();
|
var dd = new global.diffDOM();
|
||||||
|
var interval = 0;
|
||||||
|
|
||||||
|
var calculateBackoff = responseTime => parseInt(Math.max(
|
||||||
|
(250 * Math.sqrt(responseTime)) - 1000,
|
||||||
|
1000
|
||||||
|
));
|
||||||
|
|
||||||
var getRenderer = $component => response => dd.apply(
|
var getRenderer = $component => response => dd.apply(
|
||||||
$component.get(0),
|
$component.get(0),
|
||||||
@@ -19,7 +25,9 @@
|
|||||||
|
|
||||||
var clearQueue = queue => (queue.length = 0);
|
var clearQueue = queue => (queue.length = 0);
|
||||||
|
|
||||||
var poll = function(renderer, resource, queue, interval, form) {
|
var poll = function(renderer, resource, queue, form) {
|
||||||
|
|
||||||
|
let startTime = Date.now();
|
||||||
|
|
||||||
if (document.visibilityState !== "hidden" && queue.push(renderer) === 1) $.ajax(
|
if (document.visibilityState !== "hidden" && queue.push(renderer) === 1) $.ajax(
|
||||||
resource,
|
resource,
|
||||||
@@ -33,6 +41,7 @@
|
|||||||
if (response.stop === 1) {
|
if (response.stop === 1) {
|
||||||
poll = function(){};
|
poll = function(){};
|
||||||
}
|
}
|
||||||
|
interval = calculateBackoff(Date.now() - startTime);
|
||||||
}
|
}
|
||||||
).fail(
|
).fail(
|
||||||
() => poll = function(){}
|
() => poll = function(){}
|
||||||
@@ -49,10 +58,11 @@
|
|||||||
getRenderer($(component)),
|
getRenderer($(component)),
|
||||||
$(component).data('resource'),
|
$(component).data('resource'),
|
||||||
getQueue($(component).data('resource')),
|
getQueue($(component).data('resource')),
|
||||||
($(component).data('interval-seconds') || 1.5) * 1000,
|
|
||||||
$(component).data('form')
|
$(component).data('form')
|
||||||
);
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
global.GOVUK.Modules.UpdateContent.calculateBackoff = calculateBackoff;
|
||||||
|
|
||||||
})(window);
|
})(window);
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
{% macro ajax_block(partials, url, key, interval=5, finished=False, form='') %}
|
{% macro ajax_block(partials, url, key, finished=False, form='') %}
|
||||||
{% if not finished %}
|
{% if not finished %}
|
||||||
<div
|
<div
|
||||||
data-module="update-content"
|
data-module="update-content"
|
||||||
data-resource="{{ url }}"
|
data-resource="{{ url }}"
|
||||||
data-key="{{ key }}"
|
data-key="{{ key }}"
|
||||||
data-interval-seconds="{{ interval }}"
|
|
||||||
data-form="{{ form }}"
|
data-form="{{ form }}"
|
||||||
aria-live="polite"
|
aria-live="polite"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -19,25 +19,25 @@
|
|||||||
{% include 'views/dashboard/write-first-messages.html' %}
|
{% include 'views/dashboard/write-first-messages.html' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{{ ajax_block(partials, updates_url, 'upcoming', interval=20) }}
|
{{ ajax_block(partials, updates_url, 'upcoming') }}
|
||||||
|
|
||||||
<h2 class="heading-medium">
|
<h2 class="heading-medium">
|
||||||
In the last 7 days
|
In the last 7 days
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{{ ajax_block(partials, updates_url, 'inbox', interval=20) }}
|
{{ ajax_block(partials, updates_url, 'inbox') }}
|
||||||
|
|
||||||
{{ ajax_block(partials, updates_url, 'totals', interval=20) }}
|
{{ ajax_block(partials, updates_url, 'totals') }}
|
||||||
{{ show_more(
|
{{ show_more(
|
||||||
url_for('.monthly', service_id=current_service.id),
|
url_for('.monthly', service_id=current_service.id),
|
||||||
'See messages sent per month'
|
'See messages sent per month'
|
||||||
) }}
|
) }}
|
||||||
|
|
||||||
{{ ajax_block(partials, updates_url, 'template-statistics', interval=20) }}
|
{{ ajax_block(partials, updates_url, 'template-statistics') }}
|
||||||
|
|
||||||
{% if current_user.has_permissions('manage_service') %}
|
{% if current_user.has_permissions('manage_service') %}
|
||||||
<h2 class='heading-medium'>This year</h2>
|
<h2 class='heading-medium'>This year</h2>
|
||||||
{{ ajax_block(partials, updates_url, 'usage', interval=20) }}
|
{{ ajax_block(partials, updates_url, 'usage') }}
|
||||||
{{ show_more(
|
{{ show_more(
|
||||||
url_for(".usage", service_id=current_service['id']),
|
url_for(".usage", service_id=current_service['id']),
|
||||||
'See usage'
|
'See usage'
|
||||||
|
|||||||
@@ -31,8 +31,7 @@
|
|||||||
{{ ajax_block(
|
{{ ajax_block(
|
||||||
partials,
|
partials,
|
||||||
url_for('.get_notifications_as_json', service_id=current_service.id, message_type=message_type, status=status),
|
url_for('.get_notifications_as_json', service_id=current_service.id, message_type=message_type, status=status),
|
||||||
'counts',
|
'counts'
|
||||||
interval=20
|
|
||||||
) }}
|
) }}
|
||||||
|
|
||||||
{% call form_wrapper(
|
{% call form_wrapper(
|
||||||
@@ -83,8 +82,7 @@
|
|||||||
partials,
|
partials,
|
||||||
url_for('.get_notifications_as_json', service_id=current_service.id, message_type=message_type, status=status, page=page),
|
url_for('.get_notifications_as_json', service_id=current_service.id, message_type=message_type, status=status, page=page),
|
||||||
'notifications',
|
'notifications',
|
||||||
form='search-form',
|
form='search-form'
|
||||||
interval=20
|
|
||||||
) }}
|
) }}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -96,10 +96,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{% elif template.template_type == 'email' %}
|
{% elif template.template_type == 'email' %}
|
||||||
<div class="js-stick-at-bottom-when-scrolling">
|
<div class="js-stick-at-bottom-when-scrolling">
|
||||||
{{ ajax_block(partials, updates_url, 'status', interval=2, finished=finished) }}
|
{{ ajax_block(partials, updates_url, 'status', finished=finished) }}
|
||||||
</div>
|
</div>
|
||||||
{% elif template.template_type == 'sms' %}
|
{% elif template.template_type == 'sms' %}
|
||||||
{{ ajax_block(partials, updates_url, 'status', interval=2, finished=finished) }}
|
{{ ajax_block(partials, updates_url, 'status', finished=finished) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if current_user.has_permissions('send_messages') and current_user.has_permissions('view_activity') and template.template_type == 'sms' and can_receive_inbound %}
|
{% if current_user.has_permissions('send_messages') and current_user.has_permissions('view_activity') and template.template_type == 'sms' and can_receive_inbound %}
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"gulp-prettyerror": "1.2.1",
|
"gulp-prettyerror": "1.2.1",
|
||||||
"gulp-sass-lint": "1.4.0",
|
"gulp-sass-lint": "1.4.0",
|
||||||
"jest": "24.7.1",
|
"jest": "24.7.1",
|
||||||
|
"jest-date-mock": "^1.0.8",
|
||||||
|
"jest-each": "^25.3.0",
|
||||||
"jshint": "2.10.2",
|
"jshint": "2.10.2",
|
||||||
"jshint-stylish": "2.2.1",
|
"jshint-stylish": "2.2.1",
|
||||||
"rollup-plugin-commonjs": "10.1.0",
|
"rollup-plugin-commonjs": "10.1.0",
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
const each = require('jest-each').default;
|
||||||
|
const jestDateMock = require('jest-date-mock');
|
||||||
|
|
||||||
const helpers = require('./support/helpers.js');
|
const helpers = require('./support/helpers.js');
|
||||||
|
|
||||||
const serviceNumber = '6658542f-0cad-491f-bec8-ab8457700ead';
|
const serviceNumber = '6658542f-0cad-491f-bec8-ab8457700ead';
|
||||||
@@ -18,6 +21,8 @@ beforeAll(() => {
|
|||||||
// set up the object returned from $.ajax so it responds with whatever responseObj is set to
|
// set up the object returned from $.ajax so it responds with whatever responseObj is set to
|
||||||
jqueryAJAXReturnObj = {
|
jqueryAJAXReturnObj = {
|
||||||
done: callback => {
|
done: callback => {
|
||||||
|
// The server takes 1 second to respond
|
||||||
|
jestDateMock.advanceBy(1000);
|
||||||
callback(responseObj);
|
callback(responseObj);
|
||||||
return jqueryAJAXReturnObj;
|
return jqueryAJAXReturnObj;
|
||||||
},
|
},
|
||||||
@@ -100,7 +105,7 @@ describe('Update content', () => {
|
|||||||
jest.clearAllTimers();
|
jest.clearAllTimers();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("It should make requests to the URL specified in the data-resource attribute", () => {
|
test("It should make requests to the URL specified in the data-resource attribute", () => {
|
||||||
|
|
||||||
// start the module
|
// start the module
|
||||||
@@ -136,24 +141,6 @@ describe('Update content', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("If an interval between requests is specified, using the data-interval-seconds attribute, requests should happen at that frequency", () => {
|
|
||||||
|
|
||||||
document.querySelector('[data-module=update-content]').setAttribute('data-interval-seconds', '0.5');
|
|
||||||
|
|
||||||
// start the module
|
|
||||||
window.GOVUK.modules.start();
|
|
||||||
|
|
||||||
expect($.ajax).toHaveBeenCalledTimes(1);
|
|
||||||
|
|
||||||
// units are milliseconds
|
|
||||||
jest.advanceTimersByTime(500);
|
|
||||||
jest.advanceTimersByTime(500);
|
|
||||||
jest.advanceTimersByTime(500);
|
|
||||||
|
|
||||||
expect($.ajax).toHaveBeenCalledTimes(4);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("By default", () => {
|
describe("By default", () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -175,17 +162,36 @@ describe('Update content', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("It should request updates every 1.5 seconds", () => {
|
test("It should request updates with a dynamic interval", () => {
|
||||||
|
|
||||||
|
// First call happens straight away
|
||||||
expect($.ajax).toHaveBeenCalledTimes(1);
|
expect($.ajax).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
// units are milliseconds
|
// It took the server 1000ms to respond to the first call so we
|
||||||
jest.advanceTimersByTime(1500);
|
// will back off – the next call shouldn’t happen in the next 6904ms
|
||||||
|
jest.advanceTimersByTime(6904);
|
||||||
|
expect($.ajax).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
// But it should happen after 6905ms
|
||||||
|
jest.advanceTimersByTime(1);
|
||||||
expect($.ajax).toHaveBeenCalledTimes(2);
|
expect($.ajax).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
each([
|
||||||
|
[1000, 0],
|
||||||
|
[1500, 100],
|
||||||
|
[4590, 500],
|
||||||
|
[6905, 1000],
|
||||||
|
[24000, 10000],
|
||||||
|
]).test('It calculates a delay of %dms if the API responds in %dms', (waitTime, responseTime) => {
|
||||||
|
expect(
|
||||||
|
window.GOVUK.Modules.UpdateContent.calculateBackoff(responseTime)
|
||||||
|
).toBe(
|
||||||
|
waitTime
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("If a form is used as a source for data, referenced in the data-form attribute", () => {
|
describe("If a form is used as a source for data, referenced in the data-form attribute", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user