diff options
| author | Géry Debongnie <ged@odoo.com> | 2017-03-05 09:32:37 +0100 |
|---|---|---|
| committer | Aaron Bohy <aab@odoo.com> | 2017-04-11 19:44:38 +0200 |
| commit | 0df9968433389e0d504276cd711473c2eef937ae (patch) | |
| tree | b2de8031109344e4053cdb2141017a3da6e18b36 /addons/web/static/src/js | |
| parent | 00a172b40d0fd7edbf7cd7190b2e9db6345e51b5 (diff) | |
[ADD] web: introduce new RPC framework
With this commit, we introduce a new way to perform a RPC. During the
history of the Odoo web client, many ways to do a rpc have been
introduced, for various reasons. Let us mention the Model class (to
perform a call on a specific method from a model, the web.ajax module
(low level primitive), the dataset.call method, the this.rpc method (was
removed at some point), the session.rpc method and maybe some others.
With this commit, we reintroduce the this.rpc method, with a
'mid-level' API: not really high level (not automatic domain/context
calculations), but still higher level than direct web.ajax calls.
This new method is intended to replace all other RPCs in Odoo web
client. Of course, web.ajax will still be available, but it is
discouraged to use it directly. Using the rpc method means that the
rpcs are done through an event bubbling up, which means that the call is
testable.
Diffstat (limited to 'addons/web/static/src/js')
| -rw-r--r-- | addons/web/static/src/js/core/rpc.js | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/addons/web/static/src/js/core/rpc.js b/addons/web/static/src/js/core/rpc.js new file mode 100644 index 00000000000..76d416ec110 --- /dev/null +++ b/addons/web/static/src/js/core/rpc.js @@ -0,0 +1,273 @@ +odoo.define('web.rpc', function (require) { +"use strict"; + +var Class = require('web.Class'); + +var BaseRPCBuilder = Class.extend({ + /** + * @param {Widget} parent the rpc will go through this widget + * @param {Object} params + * @param {string} [params.model] + * @param {string} [params.method] + * @param {string} [params.route] + */ + init: function (parent, params) { + this.parent = parent; + this._route = params.route; + if (!this._route) { + this._route = '/web/dataset/call_kw/' + params.model + '/' + params.method; + } + this._model = params.model; + this._method = params.method; + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @param {Array} args + * @returns {BaseRPCBuilder} + */ + args: function (args) { + if (!(args instanceof Array)) { + throw new Error("Arguments should be an array"); + } + this._args = args; + return this; + }, + /** + * @param {Object} options + * @return {Deferred<*>} + */ + exec: function (options) { + if (options && options.callback) { + return options.callback(this._getRoute(), this._getArgs(), options.callbackOptions); + } + return this.parent.call('ajax', 'rpc', this._getRoute(), this._getArgs(), options || {}); + }, + /** + * @param {Object} kwargs + * @returns {BaseRPCBuilder} + */ + kwargs: function (kwargs) { + this._kwargs = kwargs; + return this; + }, + /** + * @param {Object} context + * @returns {BaseRPCBuilder} + */ + withContext: function (context) { + this._context = context; + return this; + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + * @returns {Object} + */ + _getArgs: function () { + var kwargs = _.extend({}, this._kwargs); + if (this._context) { + kwargs.context = this._context; + } + return { + method: this._method, + model: this._model, + args: this._args || [], + kwargs: kwargs, + }; + }, + /** + * @private + * @returns {string} + */ + _getRoute: function () { + return this._route; + }, +}); + +var SearchRPCBuilder = BaseRPCBuilder.extend({ + init: function () { + this._super.apply(this, arguments); + this._route = '/web/dataset/search_read'; + this._orderBy = false; + this._domain = []; + this._fields = false; + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @param {Object[]} orderBy + * @returns {BaseRPCBuilder} + */ + orderBy: function (orderBy) { + // todo: serialize properly this + this._orderBy = this._serializeSort(orderBy); + return this; + }, + /** + * @param {Object} context + * @returns {BaseRPCBuilder} + */ + withContext: function (context) { + this._context = context; + return this; + }, + /** + * @param {Object} domain + * @returns {BaseRPCBuilder} + */ + withDomain: function (domain) { + this._domain = domain; + return this; + }, + /** + * @param {string[]} fields + * @returns {BaseRPCBuilder} + */ + withFields: function (fields) { + this._fields = fields; + return this; + }, + /** + * @param {Object} limit + * @returns {BaseRPCBuilder} + */ + withLimit: function (limit) { + this._limit = limit; + return this; + }, + /** + * @param {Object} offset + * @returns {BaseRPCBuilder} + */ + withOffset: function (offset) { + this._offset = offset; + return this; + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + * @private + * @returns {Object} + */ + _getArgs: function () { + return { + context: this._context || {}, + domain: this._domain, + fields: this._fields, + limit: this._limit, + model: this._model, + sort: this._orderBy + }; + }, + /** + * Helper method, generates a string to describe a ordered by sequence for + * SQL. + * + * For example, [{name: 'foo'}, {name: 'bar', asc: false}] will + * be converted into 'foo ASC, bar DESC' + * + * @param {Object[]} orderBy list of objects {name:..., [asc: ...]} + * @returns {string} + */ + _serializeSort: function (orderBy) { + return _.map(orderBy, function (order) { + return order.name + (order.asc !== false ? ' ASC' : ' DESC'); + }).join(', '); + }, +}); + +var ReadGroupRPCBuilder = SearchRPCBuilder.extend({ + init: function () { + this._super.apply(this, arguments); + this._route = '/web/dataset/call_kw/' + this._model + '/read_group'; + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * We need to make sure that arguments are properly set. + * @override + * @returns {Deferred<*>} + */ + exec: function () { + if (!this._groupBy) { + throw new Error("read_group must have a group_by argument"); + } + return this._super.apply(this, arguments); + }, + /** + * @param {string[]} groupBy + * @returns {BaseRPCBuilder} + */ + groupBy: function (groupBy) { + // todo: serialize properly this + this._groupBy = groupBy; + return this; + }, + /** + * @param {boolean} lazy + * @returns {BaseRPCBuilder} + */ + lazy: function (lazy) { + // todo: serialize properly this + this._lazy = lazy; + return this; + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + * @private + * @returns {Object} + */ + _getArgs: function () { + var kwargs = _.extend({}, this._kwargs, { + context: this._context || {}, + domain: this._domain || [], + fields: this._fields, + groupby: this._groupBy, + lazy: !!this._lazy, + orderby: this._orderBy, + }); + return { + args: this._args || [], + kwargs: kwargs, + method: 'read_group', + model: this._model, + }; + }, +}); + +return { + builders: { + default: BaseRPCBuilder, + search_read: SearchRPCBuilder, + read_group: ReadGroupRPCBuilder, + }, + query: function (params) { + var Builder = this.builders[params.method] || this.builders.default; + return new Builder(params.parent, params); + }, +}; + +}); |
