diff options
| author | Alexandre Kühn <aku@odoo.com> | 2018-02-12 10:08:39 +0100 |
|---|---|---|
| committer | Alexandre Kühn <aku@odoo.com> | 2018-02-12 16:09:38 +0100 |
| commit | 066eaaeafad845a7b616a535e8827a5b3ae0164a (patch) | |
| tree | 6fb69b6dd636b801080cfd7596c97df22c67f599 /addons/web/static | |
| parent | 0de5c1f076d7e7d0e5361e08c810c727bd41d9e0 (diff) | |
[FIX] mail,web: JS mail and services tracebacks
Fix over this commit: https://github.com/odoo/odoo/commit/02ec09cb1c3e2d7bc7968f40c18f2208d7f3f498
In mobile, when discuss is installed, there is a traceback.
This is a consequence on an issue with JS services, where some of them
are not registered in the service provider.
For instance, the webclient (the service provider) was instantiated before
chat_manager (a service), so it was not aware of this service.
To solve this issue, service providers now listen on newly registered services.
Also improving deployment of JS services by not relying on topological sort.
Diffstat (limited to 'addons/web/static')
| -rw-r--r-- | addons/web/static/src/js/core/abstract_service.js | 9 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/registry.js | 14 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/service_mixins.js | 98 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/utils.js | 31 | ||||
| -rw-r--r-- | addons/web/static/src/js/services/ajax_service.js | 3 | ||||
| -rw-r--r-- | addons/web/static/src/js/services/core.js | 1 | ||||
| -rw-r--r-- | addons/web/static/src/js/services/local_storage_service.js | 3 | ||||
| -rw-r--r-- | addons/web/static/src/js/services/report_service.js | 3 | ||||
| -rw-r--r-- | addons/web/static/tests/helpers/test_utils.js | 45 |
9 files changed, 102 insertions, 105 deletions
diff --git a/addons/web/static/src/js/core/abstract_service.js b/addons/web/static/src/js/core/abstract_service.js index cbd6c1322ca..14efef09b39 100644 --- a/addons/web/static/src/js/core/abstract_service.js +++ b/addons/web/static/src/js/core/abstract_service.js @@ -6,7 +6,6 @@ var Mixins = require('web.mixins'); var ServicesMixin = require('web.ServicesMixin'); var AbstractService = Class.extend(Mixins.EventDispatcherMixin, ServicesMixin, { - Services: [], dependencies: [], name: null, init: function (parent) { @@ -15,13 +14,5 @@ var AbstractService = Class.extend(Mixins.EventDispatcherMixin, ServicesMixin, { }, }); -var realExtend = AbstractService.extend; - -AbstractService.extend = function () { - var Service = realExtend.apply(this, arguments); - AbstractService.prototype.Services.push(Service); - return Service; -}; - return AbstractService; }); diff --git a/addons/web/static/src/js/core/registry.js b/addons/web/static/src/js/core/registry.js index d70c09e4ae0..f925b0ea30a 100644 --- a/addons/web/static/src/js/core/registry.js +++ b/addons/web/static/src/js/core/registry.js @@ -25,6 +25,7 @@ var Registry = Class.extend({ */ init: function (mapping) { this.map = Object.create(mapping || null); + this.listeners = []; // listening callbacks on newly added items. }, //-------------------------------------------------------------------------- @@ -34,12 +35,17 @@ var Registry = Class.extend({ /** * Add a key (and a value) to the registry. * + * Notify the listeners on newly added item in the registry. + * * @param {string} key * @param {any} value * @returns {Registry} can be used to chain add calls. */ add: function (key, value) { this.map[key] = value; + _.each(this.listeners, function (callback) { + callback(key, value); + }); return this; }, /** @@ -89,6 +95,14 @@ var Registry = Class.extend({ } return null; }, + /** + * Register a callback to execute when items are added to the registry. + * + * @param {function} callback function with parameters (key, value). + */ + onAdd: function (callback) { + this.listeners.push(callback); + }, }); return Registry; diff --git a/addons/web/static/src/js/core/service_mixins.js b/addons/web/static/src/js/core/service_mixins.js index d5abf727704..052e2a6a3d9 100644 --- a/addons/web/static/src/js/core/service_mixins.js +++ b/addons/web/static/src/js/core/service_mixins.js @@ -1,58 +1,86 @@ odoo.define('web.ServiceProviderMixin', function (require) { "use strict"; -var AbstractService = require('web.AbstractService'); -var utils = require('web.utils'); +var core = require('web.core'); var ServiceProviderMixin = { - services: {}, - init: function () { + services: {}, // dict containing deployed service instances + UndeployedServices: {}, // dict containing classes of undeployed services + /** + * @override + */ + init: function (parent) { + var self = this; // to properly instantiate services with this as parent, this mixin // assumes that it is used along the EventDispatcherMixin, and that // EventDispatchedMixin's init is called first - var self = this; - // as EventDispatcherMixin's init is already called, this handler has to - // be bound manually + // as EventDispatcherMixin's init is already called, this handler has + // to be bound manually this.on('call_service', this, this._call_service.bind(this)); - var sortedServices = this._sortServices(AbstractService.prototype.Services); - _.each(sortedServices, function (Service) { - var service = new Service(self); - self.services[service.name] = service; + // add already registered services from the service registry + _.each(core.serviceRegistry.map, function (Service, serviceName) { + self.UndeployedServices[serviceName] = Service; }); + this._deployServices(); + + // listen on newly added services + core.serviceRegistry.onAdd(function (serviceName, Service) { + self.UndeployedServices[serviceName] = Service; + self._deployServices(); + }); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + */ + _deployServices: function () { + var self = this; + var done = false; + while (!done) { + var Service = _.find(this.UndeployedServices, function (Service) { + // no missing dependency + return !_.some(Service.prototype.dependencies, function (depName) { + return !self.services[depName]; + }); + }); + if (Service) { + this.services[Service.prototype.name] = new Service(this); + delete this.UndeployedServices[Service.prototype.name]; + } else { + done = true; + } + } }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Call the 'service', using data from the 'event' that + * has triggered the service call. + * + * For the ajax service, the arguments are extended with + * the target so that it can call back the caller. + * + * @private + * @param {OdooEvent} event + */ _call_service: function (event) { - var service = this.services[event.data.service]; var args = event.data.args || []; - // ajax service uses an extra 'target' argument for rpc if (event.data.service === 'ajax' && event.data.method === 'rpc') { + // ajax service uses an extra 'target' argument for rpc args = args.concat(event.target); } + var service = this.services[event.data.service]; var result = service[event.data.method].apply(service, args); event.data.callback(result); }, - _sortServices: function (services) { - var nodes = {}; - // Create nodes (services) - _.each(services, function (Service) { - nodes[Service.prototype.name] = Service.prototype.dependencies; - }); - var sorted; - try { - sorted = utils.topologicalSort(nodes); - } catch (err) { - console.warn('topologicalSort Error:', err.message); - sorted = nodes; - } - // Sort services based on sorted - // Note: we convert sorted to an object key=>index for efficiency - var sortedObj = _.invert(_.object(_.pairs(sorted))); - sorted = _.sortBy(services, function (Service) { - return sortedObj[Service.prototype.name]; - }); - - return sorted; - }, }; return ServiceProviderMixin; diff --git a/addons/web/static/src/js/core/utils.js b/addons/web/static/src/js/core/utils.js index 0b4af722203..1bdd9e70a69 100644 --- a/addons/web/static/src/js/core/utils.js +++ b/addons/web/static/src/js/core/utils.js @@ -301,37 +301,6 @@ var utils = { return fn(mod, Math.floor(x)); }, /** - * Topological sort of nodes - * - * @param {Object} nodes where key is name of node, and value is list of - * names of dependent nodes. - * @param {string[]} nodes[name] list of named dependencies of node having - * 'name' as its name. - * @return {string[]} topological sort of nodes by their names. - * @throws {Error} if there is at least one circular - * dependency between nodes - */ - topologicalSort: function (nodes) { - var sorted = []; - var unresolved = nodes; - while (!_.isEmpty(unresolved)) { - // Get all nodes that have no dependency - var resolvable = _.pick(unresolved, function (dependencies, name) { - return _.isEmpty(_.difference(dependencies, sorted)); - }); - if (_.isEmpty(resolvable)) { - throw new Error("Circular dependency detected"); - } - // Append them to sorted - sorted = _.union(sorted, _.keys(resolvable)); - // Remove these nodes from unresolved - _.each(resolvable, function (dependencies, name) { - unresolved = _.omit(unresolved, name); - }); - } - return sorted; - }, - /** * performs a half up rounding with a fixed amount of decimals, correcting for float loss of precision * See the corresponding float_round() in server/tools/float_utils.py for more info * @param {Number} value the value to be rounded diff --git a/addons/web/static/src/js/services/ajax_service.js b/addons/web/static/src/js/services/ajax_service.js index 480549347d4..3446f651a59 100644 --- a/addons/web/static/src/js/services/ajax_service.js +++ b/addons/web/static/src/js/services/ajax_service.js @@ -2,6 +2,7 @@ odoo.define('web.AjaxService', function (require) { "use strict"; var AbstractService = require('web.AbstractService'); +var core = require('web.core'); var session = require('web.session'); var AjaxService = AbstractService.extend({ @@ -21,6 +22,8 @@ var AjaxService = AbstractService.extend({ }, }); +core.serviceRegistry.add('ajax', AjaxService); + return AjaxService; }); diff --git a/addons/web/static/src/js/services/core.js b/addons/web/static/src/js/services/core.js index 73071a2a65d..522d6b07302 100644 --- a/addons/web/static/src/js/services/core.js +++ b/addons/web/static/src/js/services/core.js @@ -41,6 +41,7 @@ return { crash_registry: new Registry(), search_filters_registry: new Registry(), search_widgets_registry: new Registry(), + serviceRegistry: new Registry(), /** * @type {String} */ diff --git a/addons/web/static/src/js/services/local_storage_service.js b/addons/web/static/src/js/services/local_storage_service.js index a4527026238..7a8c803a0f9 100644 --- a/addons/web/static/src/js/services/local_storage_service.js +++ b/addons/web/static/src/js/services/local_storage_service.js @@ -2,6 +2,7 @@ odoo.define('web.LocalStorageService', function (require) { 'use strict'; var AbstractService = require('web.AbstractService'); +var core = require('web.core'); var localStorage = require('web.local_storage'); var LocalStorageService = AbstractService.extend({ @@ -20,6 +21,8 @@ var LocalStorageService = AbstractService.extend({ } }); +core.serviceRegistry.add('local_storage', LocalStorageService); + return LocalStorageService; }); diff --git a/addons/web/static/src/js/services/report_service.js b/addons/web/static/src/js/services/report_service.js index a2bb8759a24..cfd62c0499f 100644 --- a/addons/web/static/src/js/services/report_service.js +++ b/addons/web/static/src/js/services/report_service.js @@ -6,6 +6,7 @@ odoo.define('web.ReportService', function (require) { */ var AbstractService = require('web.AbstractService'); +var core = require('web.core'); var ReportService = AbstractService.extend({ name: 'report', @@ -28,6 +29,8 @@ var ReportService = AbstractService.extend({ }, }); +core.serviceRegistry.add('report', ReportService); + return ReportService; }); diff --git a/addons/web/static/tests/helpers/test_utils.js b/addons/web/static/tests/helpers/test_utils.js index 6b2c09d2631..7070716ed27 100644 --- a/addons/web/static/tests/helpers/test_utils.js +++ b/addons/web/static/tests/helpers/test_utils.js @@ -19,7 +19,6 @@ var core = require('web.core'); var dom = require('web.dom'); var session = require('web.session'); var MockServer = require('web.MockServer'); -var utils = require('web.utils'); var Widget = require('web.Widget'); var DebouncedField = basic_fields.DebouncedField; @@ -405,7 +404,7 @@ function addMockEnvironment(widget, params) { // Dispatch service calls // Note: some services could call other services at init, // Which is why we have to init services after that - var services = {}; + var services = {ajax: null}; // mocked ajax service already loaded intercept(widget, 'call_service', function (ev) { var args, result; if (ev.data.service === 'ajax') { @@ -420,36 +419,22 @@ function addMockEnvironment(widget, params) { } ev.data.callback(result); }); - // Instantiate services - // Note: ensure topological sort of services based on their dependencies - var sortServices = function (services) { - // Create nodes (services), with ajax already loaded - var nodes = { ajax: [] }; - _.each(services, function (Service) { - nodes[Service.prototype.name] = Service.prototype.dependencies; + + // Deploy services + var done = false; + while (!done) { + var index = _.findIndex(params.services, function (Service) { + return !_.some(Service.prototype.dependencies, function (depName) { + return !_.has(services, depName); + }); }); - var sorted; - try { - sorted = utils.topologicalSort(nodes); - } catch (err) { - console.warn('topologicalSort Error:', err.message); - sorted = nodes; + if (index !== -1) { + var Service = params.services.splice(index, 1)[0]; + services[Service.prototype.name] = new Service(widget); + } else { + done = true; } - // Remove ajax from sorted - sorted = _.without(sorted, 'ajax'); - // Sort services based on sorted - // Note: we convert sorted to an object key=>index for efficiency - var sortedObj = _.invert(_.object(_.pairs(sorted))); - sorted = _.sortBy(services, function (Service) { - return sortedObj[Service.prototype.name]; - }); - return sorted; - }; - var sortedServices = sortServices(params.services); - _.each(sortedServices, function (Service) { - var service = new Service(widget); - services[service.name] = service; - }); + } intercept(widget, 'load_action', function (event) { mockServer.performRpc('/web/action/load', { |
