[FIX] web: ajax must return a promise with abort method
Remove part in service_mixins who hide this error. Queries must be able to be canceled for better performance. Also in the case of longpolling, if the request is not canceled, wait for a server message or the maximum time (30 sec) to add or delete channels.
This commit is contained in:
@@ -692,6 +692,9 @@ QUnit.module('account', {
|
||||
}],
|
||||
payment_aml_ids: [], new_aml_dicts: []}]],
|
||||
"Should call process_bank_statement_line with ids");
|
||||
var def = $.Deferred();
|
||||
def.abort = function () {};
|
||||
event.data.callback(def);
|
||||
});
|
||||
|
||||
// click on reconcile button
|
||||
@@ -734,6 +737,9 @@ QUnit.module('account', {
|
||||
analytic_tag_ids: [[6, null, []]]
|
||||
}]}]],
|
||||
"Should call process_bank_statement_line with ids");
|
||||
var def = $.Deferred();
|
||||
def.abort = function () {};
|
||||
event.data.callback(def);
|
||||
});
|
||||
|
||||
// click on validate button
|
||||
@@ -787,6 +793,9 @@ QUnit.module('account', {
|
||||
name: 'SAJ/2014/002 and SAJ/2014/003 : Open balance'
|
||||
}]}]],
|
||||
"Should call process_bank_statement_line with ids");
|
||||
var def = $.Deferred();
|
||||
def.abort = function () {};
|
||||
event.data.callback(def);
|
||||
});
|
||||
|
||||
// click on validate button
|
||||
|
||||
@@ -38,7 +38,7 @@ function genericJsonRpc (fct_name, params, settings, fct) {
|
||||
});
|
||||
// FIXME: jsonp?
|
||||
deferred.abort = function () {
|
||||
deferred.reject('communication', $.Event(), 'abort', 'abort');
|
||||
deferred.reject({message: "XmlHttpRequestError abort"}, $.Event('abort'));
|
||||
if (xhr.abort) {
|
||||
xhr.abort();
|
||||
}
|
||||
|
||||
@@ -125,8 +125,14 @@ var ServicesMixin = {
|
||||
*/
|
||||
_rpc: function (params, options) {
|
||||
var query = rpc.buildQuery(params);
|
||||
var def = this.call('ajax', 'rpc', query.route, query.params, options);
|
||||
return def ? def.promise() : $.Deferred().promise();
|
||||
var def = this.call('ajax', 'rpc', query.route, query.params, options, this);
|
||||
if (!def && this.isDestroyed()) {
|
||||
def = $.Deferred();
|
||||
def.abort = function () {};
|
||||
}
|
||||
var promise = def.promise();
|
||||
promise.abort = def.abort.bind(def);
|
||||
return promise;
|
||||
},
|
||||
loadFieldView: function (dataset, view_id, view_type, options) {
|
||||
return this.loadViews(dataset.model, dataset.get_context().eval(), [[view_id, view_type]], options).then(function (result) {
|
||||
|
||||
@@ -344,9 +344,11 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
||||
}
|
||||
};
|
||||
|
||||
return deferred.then(function () {
|
||||
var promise = deferred.then(function () {
|
||||
if (aborted) {
|
||||
return $.Deferred().reject('communication', $.Event(), 'abort', 'abort');
|
||||
var def = $.Deferred().reject({message: "XmlHttpRequestError abort"}, $.Event('abort'));
|
||||
def.abort = function () {};
|
||||
return def;
|
||||
}
|
||||
// TODO: remove
|
||||
if (! _.isString(url)) {
|
||||
@@ -374,6 +376,9 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
|
||||
xhrDef = fct(url, "call", params, options);
|
||||
return xhrDef;
|
||||
});
|
||||
|
||||
promise.abort = deferred.abort;
|
||||
return promise;
|
||||
},
|
||||
url: function (path, params) {
|
||||
params = _.extend(params || {});
|
||||
|
||||
@@ -8,17 +8,20 @@ var session = require('web.session');
|
||||
var AjaxService = AbstractService.extend({
|
||||
name: 'ajax',
|
||||
rpc: function (route, args, options, target) {
|
||||
return $.Deferred(function (def) {
|
||||
session.rpc(route, args, options).then(function () {
|
||||
if (!target.isDestroyed()) {
|
||||
def.resolve.apply(def, arguments);
|
||||
}
|
||||
}, function () {
|
||||
if (!target.isDestroyed()) {
|
||||
def.reject.apply(def, arguments);
|
||||
}
|
||||
});
|
||||
}).promise();
|
||||
var def = $.Deferred();
|
||||
var promise = def.promise();
|
||||
var xhrDef = session.rpc(route, args, options);
|
||||
promise.abort = xhrDef.abort.bind(xhrDef);
|
||||
xhrDef.then(function () {
|
||||
if (!target.isDestroyed()) {
|
||||
def.resolve.apply(def, arguments);
|
||||
}
|
||||
}).fail(function () {
|
||||
if (!target.isDestroyed()) {
|
||||
def.reject.apply(def, arguments);
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
odoo.define('web.widget_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AjaxService = require('web.AjaxService');
|
||||
var concurrency = require('web.concurrency');
|
||||
var core = require('web.core');
|
||||
var QWeb = require('web.QWeb');
|
||||
var Widget = require('web.Widget');
|
||||
var testUtils = require('web.test_utils');
|
||||
|
||||
QUnit.module('core', {}, function () {
|
||||
|
||||
@@ -377,16 +379,47 @@ QUnit.module('core', {}, function () {
|
||||
});
|
||||
|
||||
QUnit.test("calling _rpc on destroyed widgets", function (assert) {
|
||||
assert.expect(1);
|
||||
assert.expect(3);
|
||||
|
||||
var def;
|
||||
var parent = new Widget();
|
||||
testUtils.addMockEnvironment(parent, {
|
||||
session: {
|
||||
rpc: function () {
|
||||
def = $.Deferred();
|
||||
def.abort = def.reject;
|
||||
return def;
|
||||
},
|
||||
},
|
||||
services: [AjaxService],
|
||||
});
|
||||
var widget = new Widget(parent);
|
||||
|
||||
widget._rpc({route: '/a/route'}).then(function () {
|
||||
assert.ok(true, "The ajax call should be resolve");
|
||||
});
|
||||
def.resolve();
|
||||
def = null;
|
||||
|
||||
var widget = new Widget();
|
||||
widget.destroy();
|
||||
widget._rpc({route: '/a/route'}).always(function () {
|
||||
throw Error("Calling _rpc on a destroyed widget should return a " +
|
||||
"deferred that is never resolved nor rejected");
|
||||
});
|
||||
widget.destroy();
|
||||
def.resolve();
|
||||
def = null;
|
||||
|
||||
widget._rpc({route: '/a/route'}).always(function () {
|
||||
throw Error("Calling _rpc on a destroyed widget should return a " +
|
||||
"deferred that is never resolved nor rejected");
|
||||
});
|
||||
assert.ok(!def,
|
||||
"The trigger_up is not performed and the call returns a deferred "+
|
||||
"never resolved nor rejected");
|
||||
|
||||
assert.ok(true,
|
||||
"there should be no crash when calling _rpc on a destroyed widget");
|
||||
parent.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('start is not called when widget is destroyed', function (assert) {
|
||||
|
||||
@@ -110,7 +110,18 @@ var MockServer = Class.extend({
|
||||
console.log('%c[rpc] request ' + route, 'color: blue; font-weight: bold;', args);
|
||||
args = JSON.parse(JSON.stringify(args));
|
||||
}
|
||||
return this._performRpc(route, args).then(function (result) {
|
||||
var def = this._performRpc(route, args);
|
||||
|
||||
var abort = def.abort || def.reject;
|
||||
if (abort) {
|
||||
abort = abort.bind(def);
|
||||
} else {
|
||||
abort = function () {
|
||||
throw new Error("Can't abort this request");
|
||||
};
|
||||
}
|
||||
|
||||
def = def.then(function (result) {
|
||||
var resultString = JSON.stringify(result || false);
|
||||
if (debug) {
|
||||
console.log('%c[rpc] response' + route, 'color: blue; font-weight: bold;', JSON.parse(resultString));
|
||||
@@ -123,6 +134,10 @@ var MockServer = Class.extend({
|
||||
}
|
||||
return $.Deferred().reject(errorString, event || $.Event());
|
||||
});
|
||||
|
||||
var promise = def.promise();
|
||||
promise.abort = abort;
|
||||
return promise;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -407,15 +407,15 @@ function addMockEnvironment(widget, params) {
|
||||
var services = {ajax: null}; // mocked ajax service already loaded
|
||||
intercept(widget, 'call_service', function (ev) {
|
||||
var args, result;
|
||||
if (ev.data.service === 'ajax') {
|
||||
if (services[ev.data.service]) {
|
||||
var service = services[ev.data.service];
|
||||
args = (ev.data.args || []);
|
||||
result = service[ev.data.method].apply(service, args);
|
||||
} else if (ev.data.service === 'ajax') {
|
||||
// ajax service is already mocked by the server
|
||||
var route = ev.data.args[0];
|
||||
args = ev.data.args[1];
|
||||
result = mockServer.performRpc(route, args);
|
||||
} else if (services[ev.data.service]) {
|
||||
var service = services[ev.data.service];
|
||||
args = (ev.data.args || []);
|
||||
result = service[ev.data.method].apply(service, args);
|
||||
}
|
||||
ev.data.callback(result);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user