[FIX] web: checks rpc responses in web.ajax

Before this commit, load_action and load_views were never resolved
in case of internet connection loss.
It's because the call is triggered by web.rpc direcly who uses
ajax.rpc without any response check.

Now the rpc response is checked in web.ajax.
Thereby all responses are handled whether they come from session.rpc
or rpc.query.
Plus, it was not the responsibility of the session to check the
rpc responses.

Steps to reproduce the issue:
(1) load_action:
- Go to the app switcher for the first time
- Force disconnected from the network
-> No notification appears and the user aren't notify
(2) load_views (mobile only):
- Go to Sales -> Quotation
(In this case, the first call is 'load_views' because we need to force
to load some views like kanban view)
- Force disconnected from the network
-> No notification appears and the user aren't notify

Note: It's now important to notify the user about connection loss
because a recent improvement in mobile app replaces the 'retry screen'
and lets the webclient handle connection issues by itself.
The single odoo mobile app is supposed to work with all databases
from 10.0.
This commit is contained in:
Adrien Dieudonne
2018-06-19 20:11:22 +02:00
parent d3e2cf5585
commit fc5d030ef2
4 changed files with 85 additions and 52 deletions
@@ -170,7 +170,7 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
core.bus.on('connection_restored', this, this._onConnectionRestored);
// crash manager integration
session.on('error', crash_manager, crash_manager.rpc_error);
core.bus.on('rpc_error', crash_manager, crash_manager.rpc_error);
window.onerror = function (message, file, line, col, error) {
// Scripts injected in DOM (eg: google API's js files) won't return a clean error on window.onerror.
// The browser will just give you a 'Script error.' as message and nothing else for security issue.
+3 -3
View File
@@ -26,9 +26,9 @@ var Loading = Widget.extend({
this._super(parent);
this.count = 0;
this.blocked_ui = false;
session.on("request", this, this.request_call);
session.on("response", this, this.response_call);
session.on("response_failed", this, this.response_call);
core.bus.on('rpc_request', this, this.request_call);
core.bus.on("rpc_response", this, this.response_call);
core.bus.on("rpc_response_failed", this, this.response_call);
},
destroy: function() {
this.on_rpc_event(-this.count);
+57 -5
View File
@@ -5,7 +5,15 @@ var core = require('web.core');
var utils = require('web.utils');
var time = require('web.time');
function genericJsonRpc (fct_name, params, fct) {
function genericJsonRpc (fct_name, params, settings, fct) {
var shadow = settings.shadow || false;
delete settings.shadow;
if (!shadow) {
core.bus.trigger('rpc_request');
}
var deferred = $.Deferred();
var data = {
jsonrpc: "2.0",
method: fct_name,
@@ -29,12 +37,56 @@ function genericJsonRpc (fct_name, params, fct) {
return def.reject.apply(def, ["communication"].concat(_.toArray(arguments)));
});
// FIXME: jsonp?
result.abort = function () { if (xhr.abort) xhr.abort(); };
return result;
deferred.abort = function () {
deferred.reject('communication', $.Event(), 'abort', 'abort');
if (xhr.abort) {
xhr.abort();
}
};
result.then(function (result) {
if (!shadow) {
core.bus.trigger('rpc_response');
}
deferred.resolve(result);
}, function (type, error, textStatus, errorThrown) {
if (type === "server") {
if (!shadow) {
core.bus.trigger('rpc_response');
}
if (error.code === 100) {
core.bus.trigger('invalidate_session');
}
deferred.reject(error, $.Event());
} else {
if (!shadow) {
core.bus.trigger('rpc_response_failed');
}
var nerror = {
code: -32098,
message: "XmlHttpRequestError " + errorThrown,
data: {
type: "xhr"+textStatus,
debug: error.responseText,
objects: [error, errorThrown]
},
};
deferred.reject(nerror, $.Event());
}
});
deferred.fail(function () { // Allow deferred user to disable rpc_error call in fail
deferred.fail(function (error, event) {
if (!event.isDefaultPrevented()) {
core.bus.trigger('rpc_error', error, event);
}
});
});
return deferred;
}
function jsonRpc(url, fct_name, params, settings) {
return genericJsonRpc(fct_name, params, function(data) {
settings = settings || {};
return genericJsonRpc(fct_name, params, settings, function(data) {
return $.ajax(url, _.extend({}, settings, {
url: url,
dataType: 'json',
@@ -47,7 +99,7 @@ function jsonRpc(url, fct_name, params, settings) {
function jsonpRpc(url, fct_name, params, settings) {
settings = settings || {};
return genericJsonRpc(fct_name, params, function(data) {
return genericJsonRpc(fct_name, params, settings, function(data) {
var payload_str = JSON.stringify(data, time.date_to_utc);
var payload_url = $.param({r:payload_str});
var force2step = settings.force2step || false;
+24 -43
View File
@@ -49,6 +49,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
this.qweb_mutex = new concurrency.Mutex();
this.currencies = {};
this._groups_def = {};
core.bus.on('invalidate_session', this, this._onInvalidateSession);
},
setup: function (origin, options) {
// must be able to customize server
@@ -327,31 +328,31 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
rpc: function (url, params, options) {
var self = this;
options = _.clone(options || {});
var shadow = options.shadow || false;
options.headers = _.extend({}, options.headers);
if (odoo.debug) {
options.headers["X-Debug-Mode"] = $.deparam($.param.querystring()).debug;
}
delete options.shadow;
var deferred = $.Deferred();
var deferred = self.check_session_id();
var aborted = false;
var xhrDef;
deferred.abort = function () {
aborted = true;
if (xhrDef) {
xhrDef.abort();
} else {
aborted = true;
}
};
self.check_session_id().then(function () {
return deferred.then(function () {
if (aborted) {
return deferred.reject('communication', $.Event(), 'abort', 'abort');
return $.Deferred().reject('communication', $.Event(), 'abort', 'abort');
}
// TODO: remove
if (! _.isString(url)) {
_.extend(options, url);
url = url.url;
}
// TODO correct handling of timeouts
if (! shadow)
self.trigger('request');
var fct;
if (self.origin_server) {
fct = ajax.jsonRpc;
@@ -370,40 +371,9 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
url = self.url(url, null);
options.session_id = self.session_id || '';
}
var p = fct(url, "call", params, options);
deferred.abort = p.abort.bind(p); // Allow to abort the rpc call
p = p.then(function (result) {
if (! shadow)
self.trigger('response');
return deferred.resolve(result);
}, function (type, error, textStatus, errorThrown) {
if (type === "server") {
if (! shadow)
self.trigger('response');
if (error.code === 100) {
self.uid = false;
}
return deferred.reject(error, $.Event());
} else {
if (! shadow)
self.trigger('response_failed');
var nerror = {
code: -32098,
message: "XmlHttpRequestError " + errorThrown,
data: {type: "xhr"+textStatus, debug: error.responseText, objects: [error, errorThrown] }
};
return deferred.reject(nerror, $.Event());
}
});
return deferred.fail(function () { // Allow deferred user to disable rpc_error call in fail
deferred.fail(function (error, event) {
if (!event.isDefaultPrevented()) {
self.trigger('error', error, event);
}
});
});
xhrDef = fct(url, "call", params, options);
return xhrDef;
});
return deferred;
},
url: function (path, params) {
params = _.extend(params || {});
@@ -428,6 +398,17 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
getTZOffset: function (date) {
return -new Date(date).getTimezoneOffset();
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
*/
_onInvalidateSession: function () {
this.uid = false;
},
});
return Session;