summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorGéry Debongnie <ged@odoo.com>2017-03-05 09:32:37 +0100
committerAaron Bohy <aab@odoo.com>2017-04-11 19:44:38 +0200
commit0df9968433389e0d504276cd711473c2eef937ae (patch)
treeb2de8031109344e4053cdb2141017a3da6e18b36 /addons/web/static/src
parent00a172b40d0fd7edbf7cd7190b2e9db6345e51b5 (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')
-rw-r--r--addons/web/static/src/js/core/rpc.js273
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);
+ },
+};
+
+});