├── .bowerrc
├── .editorconfig
├── .gitignore
├── .jshintrc
├── .travis.yml
├── README.md
├── bower.json
├── dist
├── odoo.js
└── odoo.min.js
├── gulp
└── build.js
├── gulpfile.js
├── package.json
└── src
├── app
└── app.js
└── components
└── odoo
├── jsonRpc-service.js
└── jsonRpc.spec.js
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | bower_components/
3 | .sass-cache/
4 | .tmp/
5 | *.swp
6 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "camelcase": true,
6 | "curly": true,
7 | "eqeqeq": true,
8 | "immed": true,
9 | "indent": 2,
10 | "latedef": true,
11 | "newcap": true,
12 | "noarg": true,
13 | "quotmark": "single",
14 | "regexp": true,
15 | "undef": true,
16 | "unused": true,
17 | "strict": true,
18 | "trailing": true,
19 | "smarttabs": true,
20 | "white": true,
21 | "validthis": true,
22 | "globals": {
23 | "angular": false,
24 | // Angular Mocks
25 | "inject": false,
26 | // JASMINE
27 | "describe": false,
28 | "it": false,
29 | "before": false,
30 | "beforeEach": false,
31 | "after": false,
32 | "afterEach": false,
33 | "expect": false
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | script: gulp
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### angular-odoo
2 |
3 | Call Odoo webservices from AngularJS
4 |
5 |
6 | Why
7 | ===
8 |
9 | Odoo is not HTTP friendly : every request shoud be POST, session_id should be added in the body, there some other stuff which should be added in each request...
10 |
11 | This module gives you an abstraction and friendly methods in order to communicate with Odoo.
12 |
13 |
14 | Requirements
15 | ===
16 |
17 | * OpenERP 7 or Odoo 8
18 | * Angular > 1.4
19 |
20 |
21 | Install
22 | ===
23 |
24 | Prefered method:
25 |
26 | bower install angular-odoo
27 |
28 | Alternative :
29 |
30 | Download dist/odoo.js or dist/odoo.min.js
31 |
32 | Include
33 | ===
34 |
35 | Add the script to your page :
36 |
37 | ```html
38 |
39 | ```
40 |
41 | Add the module __odoo__ to your applicaiton:
42 | ```js
43 | angular.module('yourApplication', ['odoo']);
44 | ```
45 |
46 | Use in your services
47 | ===
48 |
49 | Add __jsonRpc__ as a dependency.
50 |
51 | ```js
52 | angular.module('loginCtrl', ['$scope', 'jsonRpc', function($scope, jsonRpc) {
53 |
54 | jsonRpc.getDbList().then(function (result) {
55 | //get databases list
56 | $scope.dbs = result;
57 | });
58 |
59 | $scope.login = function(creds) {
60 | jsonRpc.login(creds.db, creds.username, creds.password).then(function () {
61 | //login successfull redirect here
62 | }, function(reason) {
63 | //display error
64 | });
65 | };
66 | }]);
67 |
68 | ```
69 |
70 |
71 | High level functions :
72 |
73 | * login
74 | * isLoggedIn
75 | * logout
76 | * searchRead
77 | * getSessionInfo
78 | * getServerInfo
79 | * getDbList
80 | * syncDataImport
81 | * syncImportObject
82 | * call
83 |
84 |
85 | Please read src/components/odoo/jsonRPC-service.js for code and detailled documentation.
86 |
87 |
88 | At [Akretion](http://akretion.com), we write Angular / Ionic applications and use this lib in all our devs when Odoo is the backend.
89 |
90 |
91 | Tests
92 | ===
93 |
94 | There is some tests in jsonRpc.spec.js
95 |
96 |
97 | Contributors
98 | ===
99 |
100 | * [Hparfr](https://github.com/hparfr)
101 | * [Sebastienbeau](https://github.com/sebastienbeau)
102 | * [Guewen](https://github.com/guewen)
103 | * [FranzPoize](https://github.com/FranzPoize)
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "odoo",
3 | "version": "0.1.3",
4 | "dependencies": {
5 | "angular": "~1.4.3"
6 | },
7 | "devDependencies": {
8 | "angular-mocks": "~1.4.3",
9 | "jasmine": "~2.2.1"
10 | },
11 | "main": "dist/odoo.js"
12 | }
13 |
--------------------------------------------------------------------------------
/dist/odoo.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | angular.module('odoo', []);
3 |
4 | 'use strict';
5 | angular.module('odoo').provider('jsonRpc', function jsonRpcProvider() {
6 |
7 | this.odooRpc = {
8 | odoo_server: "",
9 | uniq_id_counter: 0,
10 | context: {'lang': 'fr_FR'},
11 | shouldManageSessionId: false, //try without first
12 | errorInterceptors: []
13 | };
14 |
15 | var preflightPromise = null;
16 |
17 | this.$get = ["$http", "$q", "$timeout", function($http, $q, $timeout) {
18 |
19 | var odooRpc = this.odooRpc;
20 |
21 | /**
22 | * login
23 | * update cookie (session_id) in both cases
24 | * @return promise
25 | * resolve promise if credentials ok
26 | * reject promise if credentials ko (with {title: wrong_login})
27 | * reject promise in other cases (http issues, server error)
28 | */
29 | odooRpc.login = function(db, login, password) {
30 | var params = {
31 | db : db,
32 | login : login,
33 | password : password
34 | };
35 |
36 | return odooRpc.sendRequest('/web/session/authenticate', params).then(function(result) {
37 | if (!result.uid) {
38 | cookies.delete_sessionId();
39 | return $q.reject({
40 | title: 'wrong_login',
41 | message:"Username and password don't match",
42 | fullTrace: result
43 | });
44 | }
45 | odooRpc.context = result.user_context;
46 | cookies.set_sessionId(result.session_id);
47 | return result;
48 | });
49 | };
50 |
51 | /**
52 | * check if logged in or not
53 | * @param force
54 | * if false -> check the cookies and return boolean
55 | * if true -> check with the server if still connected return promise
56 | * @return boolean || promise
57 | *
58 | */
59 | odooRpc.isLoggedIn = function (force) {
60 | if (!force)
61 | return cookies.get_sessionId().length > 0;
62 |
63 | return odooRpc.getSessionInfo().then(function (result) {
64 | cookies.set_sessionId(result.session_id);
65 | return !!(result.uid);
66 | });
67 | };
68 |
69 | /**
70 | * logout (delete cookie)
71 | * @param force
72 | * if true try to connect with falsy ids
73 | * @return null || promise
74 | */
75 | odooRpc.logout = function (force) {
76 | cookies.delete_sessionId();
77 | if (force)
78 | return odooRpc.getSessionInfo().then(function (r) { //get db from sessionInfo
79 | if (r.db)
80 | return odooRpc.login(r.db, '', '');
81 | });
82 | return $q.when();
83 | };
84 |
85 | odooRpc.searchRead = function(model, domain, fields) {
86 | var params = {
87 | model: model,
88 | domain: domain,
89 | fields: fields
90 | }
91 | return odooRpc.sendRequest('/web/dataset/search_read', params);
92 | };
93 |
94 | odooRpc.getSessionInfo = function(model, method, args, kwargs) {
95 | return odooRpc.sendRequest('/web/session/get_session_info', {});
96 | };
97 |
98 | odooRpc.getServerInfo = function(model, method, args, kwargs) {
99 | return odooRpc.sendRequest('/web/webclient/version_info', {});
100 | };
101 |
102 | odooRpc.getDbList = function() {
103 | return odooRpc.sendRequest('/web/database/get_list', {});
104 | };
105 | odooRpc.syncDataImport = function(model, func_key, base_domain, filter_domain, limit, object) {
106 | return odooRpc.call(model, 'get_sync_data', [
107 | func_key, object.timekey, base_domain, filter_domain, limit
108 | ], {}).then(function(result) {
109 | //if (object.timekey === result.timekey) TODO: add mutlidomain before uncomment
110 | // return; //no change since last run
111 | object.timekey = result.timekey;
112 |
113 | angular.forEach(result.remove_ids, function(id) {
114 | delete object.data[id];
115 | });
116 |
117 | if (Object.keys(result.data).length) {
118 | angular.merge(object.data, result.data); ///merge deeply old with new
119 | return odooRpc.syncDataImport(model, func_key, base_domain, filter_domain, limit, object);
120 | }
121 | });
122 | };
123 |
124 | odooRpc.syncImportObject = function(params) {
125 | /* params = {
126 | model: 'odoo.model',
127 | func_key: 'my_function_key',
128 | domain: [],
129 | limit: 50,
130 | interval: 5000,
131 | }
132 |
133 | When an error happens, the sync cycle is interrupted.
134 |
135 | An optional parameter 'onErrorRetry' can be specified. If its value is
136 | true, then the sync cycle will continue on the next interval even when
137 | errors occur. For a more fine-grained control over the retries,
138 | 'onErrorRetry' could also be a function, taking the error as argument.
139 | It should call 'nextSync()' on the synchronized object's API to delay
140 | the next sync iteration.
141 |
142 | Example:
143 |
144 | params = {
145 | ...
146 | onErrorRetry: function(sync, err) {
147 | if(shouldRetry(err)) {
148 | sync.nextSync();
149 | }
150 | }
151 | }
152 |
153 | return a synchronized object where you can access
154 | to the data using object.data
155 | */
156 | var stop = false;
157 | var watchers = [];
158 | var object = {
159 | data: {},
160 | timekey: null,
161 | stopCallback: function () {
162 | stop = true;
163 | },
164 | watch: function(fun) {
165 | watchers.push(fun);
166 | },
167 | nextSync: nextSync
168 | };
169 |
170 | function nextSync(interval) {
171 | if(!stop) {
172 | $timeout(sync, interval || params.interval);
173 | }
174 | }
175 |
176 | function runWatchers(data) {
177 | watchers.forEach(function (fun) {
178 | fun(object);
179 | });
180 | }
181 |
182 | var errorCallback = null;
183 | if(angular.isFunction(params.onErrorRetry)) {
184 | errorCallback = function(err) { params.onErrorRetry(object, err); };
185 | } else if(params.onErrorRetry) {
186 | errorCallback = function(err) { nextSync(); };
187 | }
188 |
189 | function sync() {
190 |
191 | odooRpc.syncDataImport(
192 | params.model,
193 | params.func_key,
194 | params.base_domain,
195 | params.filter_domain,
196 | params.limit,
197 | object)
198 | .then(nextSync)
199 | .then(runWatchers)
200 | .catch(errorCallback);
201 | }
202 | sync();
203 |
204 | return object;
205 | };
206 |
207 | odooRpc.call = function(model, method, args, kwargs) {
208 |
209 | kwargs = kwargs || {};
210 | kwargs.context = kwargs.context || {};
211 | angular.extend(kwargs.context, odooRpc.context);
212 |
213 | var params = {
214 | model: model,
215 | method: method,
216 | args: args,
217 | kwargs: kwargs,
218 | };
219 | return odooRpc.sendRequest('/web/dataset/call_kw', params);
220 | };
221 |
222 |
223 | /**
224 | * base function
225 | */
226 | odooRpc.sendRequest = function(url, params) {
227 |
228 | /** (internal) build request for $http
229 | * keep track of uniq_id_counter
230 | * add session_id in the request (for Odoo v7 only)
231 | */
232 | function buildRequest(url, params) {
233 | odooRpc.uniq_id_counter += 1;
234 | if (odooRpc.shouldManageSessionId)
235 | params.session_id = cookies.get_sessionId();
236 |
237 | var json_data = {
238 | jsonrpc: '2.0',
239 | method: 'call',
240 | params: params, //payload
241 | };
242 | var headers = {
243 | 'Content-Type': 'application/json',
244 | 'X-Openerp-Session-Id': cookies.get_sessionId()
245 | }
246 | return {
247 | 'method' : 'POST',
248 | 'url' : odooRpc.odoo_server + url,
249 | 'data' : JSON.stringify(json_data),
250 | 'headers': headers,
251 | 'id': ("r" + odooRpc.uniq_id_counter),
252 | };
253 | }
254 |
255 | /** (internal) Odoo do some error handling and doesn't care
256 | * about HTTP response code
257 | * catch errors codes here and reject
258 | * @param response $http promise
259 | * @return promise
260 | * if no error : response.data ($http.config & header stripped)
261 | * if error : reject with a custom errorObj
262 | */
263 | function handleOdooErrors(response) {
264 | if (!response.data.error)
265 | return response.data;
266 |
267 | var error = response.data.error;
268 | var errorObj = {
269 | title: '',
270 | message:'',
271 | fullTrace: error
272 | };
273 |
274 | if (error.code === 200 && error.message === "Odoo Server Error" && error.data.name === "werkzeug.exceptions.NotFound") {
275 | errorObj.title = 'page_not_found';
276 | errorObj.message = 'HTTP Error';
277 | } else if ( (error.code === 100 && error.message === "Odoo Session Expired") || //v8
278 | (error.code === 300 && error.message === "OpenERP WebClient Error" && error.data.debug.match("SessionExpiredException")) //v7
279 | ) {
280 | errorObj.title ='session_expired';
281 | cookies.delete_sessionId();
282 | } else if ( (error.message === "Odoo Server Error" && /FATAL: database "(.+)" does not exist/.test(error.data.message))) {
283 | errorObj.title = "database_not_found";
284 | errorObj.message = error.data.message;
285 | } else if ( (error.data.name === "openerp.exceptions.AccessError")) {
286 | errorObj.title = 'AccessError';
287 | errorObj.message = error.data.message;
288 | } else {
289 | var split = ("" + error.data.fault_code).split('\n')[0].split(' -- ');
290 | if (split.length > 1) {
291 | error.type = split.shift();
292 | error.data.fault_code = error.data.fault_code.substr(error.type.length + 4);
293 | }
294 |
295 | if (error.code === 200 && error.type) {
296 | errorObj.title = error.type;
297 | errorObj.message = error.data.fault_code.replace(/\n/g, "
");
298 | } else {
299 | errorObj.title = error.message;
300 | errorObj.message = error.data.debug.replace(/\n/g, "
");
301 | }
302 | }
303 | odooRpc.errorInterceptors.forEach(function (i) {
304 | i(errorObj);
305 | });
306 | return $q.reject(errorObj)
307 | }
308 |
309 | /**
310 | * (internal)
311 | * catch HTTP response code (not handled by Odoo ie Error 500, 404)
312 | * @params $http rejected promise
313 | * @return promise
314 | */
315 | function handleHttpErrors(reason) {
316 | var errorObj = {title:'http', fullTrace: reason, message:'HTTP Error'};
317 | odooRpc.errorInterceptors.forEach(function (i) {
318 | i(errorObj);
319 | });
320 | return $q.reject(errorObj);
321 | }
322 |
323 | /**
324 | * (internal) wrapper around $http for handling errors and build request
325 | */
326 | function http(url, params) {
327 | var req = buildRequest(url, params);
328 | return $http(req).then(handleOdooErrors, handleHttpErrors);
329 | }
330 |
331 | /** (internal) determine if session_id shoud be managed by this lib
332 | * more info:
333 | * in v7 session_id is returned by the server in the payload
334 | * and it should be added in each request's paylaod.
335 | * it's
336 | *
337 | * in v8 session_id is set as a cookie by the server
338 | * therefor the browser send it on each request automatically
339 | *
340 | * in both case, we keep session_id as a cookie to be compliant with other odoo web clients
341 | *
342 | */
343 | function preflight() {
344 | //preflightPromise is a kind of cache and is set only if the request succeed
345 | return preflightPromise || http('/web/webclient/version_info', {}).then(function (reason) {
346 | odooRpc.shouldManageSessionId = (reason.result.server_serie < "8"); //server_serie is string like "7.01"
347 | preflightPromise = $q.when(); //runonce
348 | });
349 | }
350 |
351 | return preflight().then(function () {
352 | return http(url, params).then(function(response) {
353 | var subRequests = [];
354 | if (response.result.type === "ir.actions.act_proxy") {
355 | angular.forEach(response.result.action_list, function(action) {
356 | subRequests.push($http.post(action['url'], action['params']));
357 | });
358 | return $q.all(subRequests);
359 | } else
360 | return response.result;
361 | });
362 | });
363 | };
364 |
365 | return odooRpc;
366 | }];
367 |
368 | var cookies = (function() {
369 | var session_id; //cookies doesn't work with Android Default Browser / Ionic
370 | return {
371 | delete_sessionId: function() {
372 | session_id = null;
373 | document.cookie = 'session_id=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
374 | },
375 | get_sessionId: function () {
376 | return document.cookie.split('; ')
377 | .filter(function (x) { return x.indexOf('session_id') === 0; })
378 | .map(function (x) { return x.split('=')[1]; })
379 | .pop() || session_id || "";
380 | },
381 | set_sessionId: function (val) {
382 | document.cookie = 'session_id=' + val;
383 | session_id = val;
384 | }
385 | };
386 | }());
387 | });
388 |
389 |
--------------------------------------------------------------------------------
/dist/odoo.min.js:
--------------------------------------------------------------------------------
1 | "use strict";angular.module("odoo",[]);
2 | "use strict";angular.module("odoo").provider("jsonRpc",function(){this.odooRpc={odoo_server:"",uniq_id_counter:0,context:{lang:"fr_FR"},shouldManageSessionId:!1,errorInterceptors:[]};var e=null;this.$get=["$http","$q","$timeout",function(n,o,s){var r=this.odooRpc;return r.login=function(e,n,s){var i={db:e,login:n,password:s};return r.sendRequest("/web/session/authenticate",i).then(function(e){return e.uid?(r.context=e.user_context,t.set_sessionId(e.session_id),e):(t.delete_sessionId(),o.reject({title:"wrong_login",message:"Username and password don't match",fullTrace:e}))})},r.isLoggedIn=function(e){return e?r.getSessionInfo().then(function(e){return t.set_sessionId(e.session_id),!!e.uid}):t.get_sessionId().length>0},r.logout=function(e){return t.delete_sessionId(),e?r.getSessionInfo().then(function(e){return e.db?r.login(e.db,"",""):void 0}):o.when()},r.searchRead=function(e,t,n){var o={model:e,domain:t,fields:n};return r.sendRequest("/web/dataset/search_read",o)},r.getSessionInfo=function(){return r.sendRequest("/web/session/get_session_info",{})},r.getServerInfo=function(){return r.sendRequest("/web/webclient/version_info",{})},r.getDbList=function(){return r.sendRequest("/web/database/get_list",{})},r.syncDataImport=function(e,t,n,o,s,i){return r.call(e,"get_sync_data",[t,i.timekey,n,o,s],{}).then(function(a){return i.timekey=a.timekey,angular.forEach(a.remove_ids,function(e){delete i.data[e]}),Object.keys(a.data).length?(angular.merge(i.data,a.data),r.syncDataImport(e,t,n,o,s,i)):void 0})},r.syncImportObject=function(e){function t(t){i||s(o,t||e.interval)}function n(){a.forEach(function(e){e(u)})}function o(){r.syncDataImport(e.model,e.func_key,e.base_domain,e.filter_domain,e.limit,u).then(t).then(n).catch(c)}var i=!1,a=[],u={data:{},timekey:null,stopCallback:function(){i=!0},watch:function(e){a.push(e)},nextSync:t},c=null;return angular.isFunction(e.onErrorRetry)?c=function(t){e.onErrorRetry(u,t)}:e.onErrorRetry&&(c=function(){t()}),o(),u},r.call=function(e,t,n,o){o=o||{},o.context=o.context||{},angular.extend(o.context,r.context);var s={model:e,method:t,args:n,kwargs:o};return r.sendRequest("/web/dataset/call_kw",s)},r.sendRequest=function(s,i){function a(e,n){r.uniq_id_counter+=1,r.shouldManageSessionId&&(n.session_id=t.get_sessionId());var o={jsonrpc:"2.0",method:"call",params:n},s={"Content-Type":"application/json","X-Openerp-Session-Id":t.get_sessionId()};return{method:"POST",url:r.odoo_server+e,data:JSON.stringify(o),headers:s,id:"r"+r.uniq_id_counter}}function u(e){if(!e.data.error)return e.data;var n=e.data.error,s={title:"",message:"",fullTrace:n};if(200===n.code&&"Odoo Server Error"===n.message&&"werkzeug.exceptions.NotFound"===n.data.name)s.title="page_not_found",s.message="HTTP Error";else if(100===n.code&&"Odoo Session Expired"===n.message||300===n.code&&"OpenERP WebClient Error"===n.message&&n.data.debug.match("SessionExpiredException"))s.title="session_expired",t.delete_sessionId();else if("Odoo Server Error"===n.message&&/FATAL: database "(.+)" does not exist/.test(n.data.message))s.title="database_not_found",s.message=n.data.message;else if("openerp.exceptions.AccessError"===n.data.name)s.title="AccessError",s.message=n.data.message;else{var i=(""+n.data.fault_code).split("\n")[0].split(" -- ");i.length>1&&(n.type=i.shift(),n.data.fault_code=n.data.fault_code.substr(n.type.length+4)),200===n.code&&n.type?(s.title=n.type,s.message=n.data.fault_code.replace(/\n/g,"
")):(s.title=n.message,s.message=n.data.debug.replace(/\n/g,"
"))}return r.errorInterceptors.forEach(function(e){e(s)}),o.reject(s)}function c(e){var t={title:"http",fullTrace:e,message:"HTTP Error"};return r.errorInterceptors.forEach(function(e){e(t)}),o.reject(t)}function d(e,t){var o=a(e,t);return n(o).then(u,c)}function l(){return e||d("/web/webclient/version_info",{}).then(function(t){r.shouldManageSessionId=t.result.server_serie<"8",e=o.when()})}return l().then(function(){return d(s,i).then(function(e){var t=[];return"ir.actions.act_proxy"===e.result.type?(angular.forEach(e.result.action_list,function(e){t.push(n.post(e.url,e.params))}),o.all(t)):e.result})})},r}];var t=function(){var e;return{delete_sessionId:function(){e=null,document.cookie="session_id=; expires=Thu, 01 Jan 1970 00:00:00 GMT"},get_sessionId:function(){return document.cookie.split("; ").filter(function(e){return 0===e.indexOf("session_id")}).map(function(e){return e.split("=")[1]}).pop()||e||""},set_sessionId:function(t){document.cookie="session_id="+t,e=t}}}()});
--------------------------------------------------------------------------------
/gulp/build.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var gulp = require('gulp');
4 | var bump = require('gulp-bump');
5 | var git = require('gulp-git');
6 | var paths = gulp.paths;
7 |
8 | var $ = require('gulp-load-plugins')({
9 | pattern: ['gulp-*', 'main-bower-files', 'uglify-save-license', 'del']
10 | });
11 |
12 | gulp.task('build-lib', [], function () {
13 | return gulp.src([
14 | paths.src + '/app/app.js',
15 | paths.src + '/components/**/*.js'
16 | ])
17 | .pipe($.ignore.exclude('*.spec.js'))
18 | .pipe($.ngAnnotate())
19 | .pipe($.concat('odoo.js'))
20 | .pipe(gulp.dest(paths.dist + '/'))
21 | .pipe($.size({ title: paths.dist + '/', showFiles: true }));
22 | });
23 |
24 | gulp.task('build-lib-min', [], function () {
25 | return gulp.src([
26 | paths.src + '/app/app.js',
27 | paths.src + '/components/**/*.js'
28 | ])
29 | .pipe($.ignore.exclude('*.spec.js'))
30 | .pipe($.ngAnnotate())
31 | .pipe($.uglify({preserveComments:$.uglifySaveLicense}))
32 | .pipe($.concat('odoo.min.js'))
33 | .pipe(gulp.dest(paths.dist + '/'))
34 | .pipe($.size({ title: paths.dist + '/', showFiles: true }));
35 | });
36 |
37 | gulp.task('bump', function() {
38 | gulp.src(['./bower.json', './package.json'])
39 | .pipe(bump())
40 | .pipe(git.add())
41 | .pipe(gulp.dest('./'));
42 | });
43 |
44 | gulp.task('tag', function() {
45 | var pkg = require('../package.json');
46 | var message = 'Release ' + pkg.version;
47 | return gulp.src('./')
48 | .pipe(git.add())
49 | .pipe(git.commit(message))
50 | .on('end', function (e) {
51 | console.log('on end', e);
52 | git.tag(pkg.version, message)
53 | });
54 | });
55 |
56 | gulp.task('clean', function (done) {
57 | $.del([paths.dist + '/', paths.tmp + '/'], done);
58 | });
59 |
60 | gulp.task('build', ['build-lib', 'build-lib-min']);
61 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var gulp = require('gulp');
4 |
5 | gulp.paths = {
6 | src: 'src',
7 | dist: 'dist',
8 | tmp: '.tmp',
9 | e2e: 'e2e'
10 | };
11 |
12 | require('require-dir')('./gulp');
13 |
14 | gulp.task('default', ['clean'], function () {
15 | gulp.start('build');
16 | });
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "odoo",
3 | "version": "0.1.3",
4 | "dependencies": {
5 | "gulp-bump": "^0.3.1",
6 | "gulp-git": "^1.2.0"
7 | },
8 | "devDependencies": {
9 | "gulp": "~3.8.10",
10 | "gulp-concat": "~2.5.2",
11 | "del": "~0.1.3",
12 | "gulp-size": "~1.1.0",
13 | "require-dir": "~0.1.0",
14 | "gulp-load-plugins": "~0.7.1",
15 | "gulp-ng-annotate": "~0.3.6",
16 | "gulp-uglify": "~1.0.1",
17 | "uglify-save-license": "~0.4.1",
18 | "gulp-ignore": "~1.2.1"
19 | },
20 | "engines": {
21 | "node": ">=0.10.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/app/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | angular.module('odoo', []);
3 |
--------------------------------------------------------------------------------
/src/components/odoo/jsonRpc-service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | angular.module('odoo').provider('jsonRpc', function jsonRpcProvider() {
3 |
4 | this.odooRpc = {
5 | odoo_server: "",
6 | uniq_id_counter: 0,
7 | odooVersion: '',
8 | context: {'lang': 'fr_FR'},
9 | shouldManageSessionId: false, //try without first for v7
10 | errorInterceptors: []
11 | };
12 |
13 | var preflightPromise = null;
14 |
15 | this.$get = function($http, $q, $timeout) {
16 |
17 | var odooRpc = this.odooRpc;
18 |
19 | /**
20 | * login
21 | * update cookie (session_id) in both cases
22 | * @return promise
23 | * resolve promise if credentials ok
24 | * reject promise if credentials ko (with {title: wrong_login})
25 | * reject promise in other cases (http issues, server error)
26 | */
27 | odooRpc.login = function(db, login, password) {
28 | var params = {
29 | db : db,
30 | login : login,
31 | password : password
32 | };
33 |
34 | return odooRpc.sendRequest('/web/session/authenticate', params).then(function(result) {
35 | if (!result.uid) {
36 | cookies.delete_sessionId();
37 | return $q.reject({
38 | title: 'wrong_login',
39 | message:"Username and password don't match",
40 | fullTrace: result
41 | });
42 | }
43 | odooRpc.context = result.user_context;
44 | cookies.set_sessionId(result.session_id);
45 | return result;
46 | });
47 | };
48 |
49 | /**
50 | * check if logged in or not
51 | * @param force
52 | * if false -> check the cookies and return boolean
53 | * if true -> check with the server if still connected return promise
54 | * @return boolean || promise
55 | *
56 | */
57 | odooRpc.isLoggedIn = function (force) {
58 | if (!force)
59 | return cookies.get_sessionId().length > 0;
60 |
61 | return odooRpc.getSessionInfo().then(function (result) {
62 | cookies.set_sessionId(result.session_id);
63 | return !!(result.uid);
64 | });
65 | };
66 |
67 | /**
68 | * logout (delete cookie)
69 | * @param force
70 | * if true try to connect with falsy ids
71 | * @return null || promise
72 | */
73 | odooRpc.logout = function (force) {
74 | cookies.delete_sessionId();
75 | if (force)
76 | return odooRpc.getSessionInfo().then(function (r) { //get db from sessionInfo
77 | if (r.db)
78 | return odooRpc.login(r.db, '', '');
79 | });
80 | return $q.when();
81 | };
82 |
83 | odooRpc.searchRead = function(model, domain, fields) {
84 | var params = {
85 | model: model,
86 | domain: domain,
87 | fields: fields
88 | }
89 | return odooRpc.sendRequest('/web/dataset/search_read', params);
90 | };
91 |
92 | odooRpc.getSessionInfo = function(model, method, args, kwargs) {
93 | return odooRpc.sendRequest('/web/session/get_session_info', {});
94 | };
95 |
96 | odooRpc.getServerInfo = function(model, method, args, kwargs) {
97 | return odooRpc.sendRequest('/web/webclient/version_info', {});
98 | };
99 |
100 | odooRpc.getDbList = function() {
101 | if (odooRpc.odooVersion[0] == "7" || odooRpc.odooVersion[0] == "8")
102 | return odooRpc.sendRequest('/web/database/get_list', {});
103 | return odooRpc.callJson('db', 'list', {});
104 | };
105 | odooRpc.syncDataImport = function(model, func_key, base_domain, filter_domain, limit, object) {
106 | return odooRpc.call(model, 'get_sync_data', [
107 | func_key, object.timekey, base_domain, filter_domain, limit
108 | ], {}).then(function(result) {
109 | //if (object.timekey === result.timekey) TODO: add mutlidomain before uncomment
110 | // return; //no change since last run
111 | object.timekey = result.timekey;
112 |
113 | angular.forEach(result.remove_ids, function(id) {
114 | delete object.data[id];
115 | });
116 |
117 | if (Object.keys(result.data).length) {
118 | angular.merge(object.data, result.data); ///merge deeply old with new
119 | return odooRpc.syncDataImport(model, func_key, base_domain, filter_domain, limit, object);
120 | }
121 | });
122 | };
123 |
124 | odooRpc.syncImportObject = function(params) {
125 | /* params = {
126 | model: 'odoo.model',
127 | func_key: 'my_function_key',
128 | domain: [],
129 | limit: 50,
130 | interval: 5000,
131 | }
132 |
133 | When an error happens, the sync cycle is interrupted.
134 |
135 | An optional parameter 'onErrorRetry' can be specified. If its value is
136 | true, then the sync cycle will continue on the next interval even when
137 | errors occur. For a more fine-grained control over the retries,
138 | 'onErrorRetry' could also be a function, taking the error as argument.
139 | It should call 'nextSync()' on the synchronized object's API to delay
140 | the next sync iteration.
141 |
142 | Example:
143 |
144 | params = {
145 | ...
146 | onErrorRetry: function(sync, err) {
147 | if(shouldRetry(err)) {
148 | sync.nextSync();
149 | }
150 | }
151 | }
152 |
153 | return a synchronized object where you can access
154 | to the data using object.data
155 | */
156 | var stop = false;
157 | var watchers = [];
158 | var object = {
159 | data: {},
160 | timekey: null,
161 | stopCallback: function () {
162 | stop = true;
163 | },
164 | watch: function(fun) {
165 | watchers.push(fun);
166 | },
167 | nextSync: nextSync
168 | };
169 |
170 | function nextSync(interval) {
171 | if(!stop) {
172 | $timeout(sync, interval || params.interval);
173 | }
174 | }
175 |
176 | function runWatchers(data) {
177 | watchers.forEach(function (fun) {
178 | fun(object);
179 | });
180 | }
181 |
182 | var errorCallback = null;
183 | if(angular.isFunction(params.onErrorRetry)) {
184 | errorCallback = function(err) { params.onErrorRetry(object, err); };
185 | } else if(params.onErrorRetry) {
186 | errorCallback = function(err) { nextSync(); };
187 | }
188 |
189 | function sync() {
190 |
191 | odooRpc.syncDataImport(
192 | params.model,
193 | params.func_key,
194 | params.base_domain,
195 | params.filter_domain,
196 | params.limit,
197 | object)
198 | .then(nextSync)
199 | .then(runWatchers)
200 | .catch(errorCallback);
201 | }
202 | sync();
203 |
204 | return object;
205 | };
206 |
207 | odooRpc.call = function(model, method, args, kwargs) {
208 |
209 | kwargs = kwargs || {};
210 | kwargs.context = kwargs.context || {};
211 | angular.extend(kwargs.context, odooRpc.context);
212 |
213 | var params = {
214 | model: model,
215 | method: method,
216 | args: args,
217 | kwargs: kwargs,
218 | };
219 | return odooRpc.sendRequest('/web/dataset/call_kw', params);
220 | };
221 |
222 | odooRpc.callJson = function(service, method, args) {
223 | var params = {
224 | service: service,
225 | method: method,
226 | args: args,
227 | };
228 | return odooRpc.sendRequest('/jsonrpc', params);
229 | }
230 |
231 | /**
232 | * base function
233 | */
234 | odooRpc.sendRequest = function(url, params) {
235 |
236 | /** (internal) build request for $http
237 | * keep track of uniq_id_counter
238 | * add session_id in the request (for Odoo v7 only)
239 | */
240 | function buildRequest(url, params) {
241 | odooRpc.uniq_id_counter += 1;
242 | if (odooRpc.shouldManageSessionId)
243 | params.session_id = cookies.get_sessionId();
244 |
245 | var json_data = {
246 | jsonrpc: '2.0',
247 | method: 'call',
248 | params: params, //payload
249 | };
250 | var headers = {
251 | 'Content-Type': 'application/json',
252 | 'X-Openerp-Session-Id': cookies.get_sessionId()
253 | }
254 | return {
255 | 'method' : 'POST',
256 | 'url' : odooRpc.odoo_server + url,
257 | 'data' : JSON.stringify(json_data),
258 | 'headers': headers,
259 | 'id': ("r" + odooRpc.uniq_id_counter),
260 | };
261 | }
262 |
263 | /** (internal) Odoo do some error handling and doesn't care
264 | * about HTTP response code
265 | * catch errors codes here and reject
266 | * @param response $http promise
267 | * @return promise
268 | * if no error : response.data ($http.config & header stripped)
269 | * if error : reject with a custom errorObj
270 | */
271 | function handleOdooErrors(response) {
272 | if (!response.data.error)
273 | return response.data;
274 |
275 | var error = response.data.error;
276 | var errorObj = {
277 | title: '',
278 | message:'',
279 | fullTrace: error
280 | };
281 |
282 | if (error.code === 200 && error.message === "Odoo Server Error" && error.data.name === "werkzeug.exceptions.NotFound") {
283 | errorObj.title = 'page_not_found';
284 | errorObj.message = 'HTTP Error';
285 | } else if ( (error.code === 100 && error.message === "Odoo Session Expired") || //v8
286 | (error.code === 300 && error.message === "OpenERP WebClient Error" && error.data.debug.match("SessionExpiredException")) //v7
287 | ) {
288 | errorObj.title ='session_expired';
289 | cookies.delete_sessionId();
290 | } else if ( (error.message === "Odoo Server Error" && /FATAL: database "(.+)" does not exist/.test(error.data.message))) {
291 | errorObj.title = "database_not_found";
292 | errorObj.message = error.data.message;
293 | } else if ( (error.data.name === "openerp.exceptions.AccessError")) {
294 | errorObj.title = 'AccessError';
295 | errorObj.message = error.data.message;
296 | } else {
297 | var split = ("" + error.data.fault_code).split('\n')[0].split(' -- ');
298 | if (split.length > 1) {
299 | error.type = split.shift();
300 | error.data.fault_code = error.data.fault_code.substr(error.type.length + 4);
301 | }
302 |
303 | if (error.code === 200 && error.type) {
304 | errorObj.title = error.type;
305 | errorObj.message = error.data.fault_code.replace(/\n/g, "
");
306 | } else {
307 | errorObj.title = error.message;
308 | errorObj.message = error.data.debug.replace(/\n/g, "
");
309 | }
310 | }
311 | odooRpc.errorInterceptors.forEach(function (i) {
312 | i(errorObj);
313 | });
314 | return $q.reject(errorObj)
315 | }
316 |
317 | /**
318 | * (internal)
319 | * catch HTTP response code (not handled by Odoo ie Error 500, 404)
320 | * @params $http rejected promise
321 | * @return promise
322 | */
323 | function handleHttpErrors(reason) {
324 | var errorObj = {title:'http', fullTrace: reason, message:'HTTP Error'};
325 | odooRpc.errorInterceptors.forEach(function (i) {
326 | i(errorObj);
327 | });
328 | return $q.reject(errorObj);
329 | }
330 |
331 | /**
332 | * (internal) wrapper around $http for handling errors and build request
333 | */
334 | function http(url, params) {
335 | var req = buildRequest(url, params);
336 | return $http(req).then(handleOdooErrors, handleHttpErrors);
337 | }
338 |
339 | /** (internal) determine if session_id shoud be managed by this lib
340 | * more info:
341 | * in v7 session_id is returned by the server in the payload
342 | * and it should be added in each request's paylaod.
343 | * it's
344 | *
345 | * in v8 session_id is set as a cookie by the server
346 | * therefor the browser send it on each request automatically
347 | *
348 | * in both case, we keep session_id as a cookie to be compliant with other odoo web clients
349 | *
350 | */
351 | function preflight() {
352 | //preflightPromise is a kind of cache and is set only if the request succeed
353 | return preflightPromise || http('/web/webclient/version_info', {}).then(function (reason) {
354 | odooRpc.odooVersion = reason.result.server_serie;
355 | odooRpc.shouldManageSessionId = (odooRpc.odooVersion[0] == "7"); //server_serie is string like "7.01"
356 | preflightPromise = $q.when(); //runonce
357 | });
358 | }
359 |
360 | return preflight().then(function () {
361 | return http(url, params).then(function(response) {
362 | var subRequests = [];
363 | if (response.result.type === "ir.actions.act_proxy") {
364 | angular.forEach(response.result.action_list, function(action) {
365 | subRequests.push($http.post(action['url'], action['params']));
366 | });
367 | return $q.all(subRequests);
368 | } else
369 | return response.result;
370 | });
371 | });
372 | };
373 |
374 | return odooRpc;
375 | };
376 |
377 | var cookies = (function() {
378 | var session_id; //cookies doesn't work with Android Default Browser / Ionic
379 | return {
380 | delete_sessionId: function() {
381 | session_id = null;
382 | document.cookie = 'session_id=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
383 | },
384 | get_sessionId: function () {
385 | return document.cookie.split('; ')
386 | .filter(function (x) { return x.indexOf('session_id') === 0; })
387 | .map(function (x) { return x.split('=')[1]; })
388 | .pop() || session_id || "";
389 | },
390 | set_sessionId: function (val) {
391 | document.cookie = 'session_id=' + val;
392 | session_id = val;
393 | }
394 | };
395 | }());
396 | });
397 |
398 |
--------------------------------------------------------------------------------
/src/components/odoo/jsonRpc.spec.js:
--------------------------------------------------------------------------------
1 | describe("jsonRpc tests", function() {
2 |
3 | var $httpBackend;
4 | var jsonRpc;
5 |
6 | beforeEach(module('odoo'));
7 |
8 | beforeEach(inject(function(_jsonRpc_) {
9 | jsonRpc = _jsonRpc_;
10 | }));
11 |
12 | beforeEach(inject(function(_$httpBackend_) {
13 | $httpBackend = _$httpBackend_
14 | }));
15 |
16 | afterEach(function() {
17 | $httpBackend.verifyNoOutstandingExpectation();
18 | $httpBackend.verifyNoOutstandingRequest();
19 | });
20 | function fail(a) {
21 | expect(true).toBe(false);
22 | }
23 | function success() {
24 | expect(true).toBe(true);
25 | }
26 |
27 | function set_version_info7() {
28 | $httpBackend.whenPOST('/web/webclient/version_info').respond({
29 | jsonrpc:"2.0",
30 | id: null,
31 | result: {"server_serie": "7.0", "server_version_info": [7, 0, 0, "final", 0], "server_version": "7.0", "protocol_version": 1}
32 | });
33 | }
34 | function set_version_info8() {
35 | $httpBackend.whenPOST('/web/webclient/version_info').respond({
36 | jsonrpc:"2.0",
37 | id: null,
38 | result: {"server_serie": "8.0", "server_version_info": [8, 0, 0, "final", 0], "server_version": "8.0", "protocol_version": 1}
39 | });
40 | }
41 |
42 | describe("session expiration", function () {
43 | function success(reason) {
44 | expect(reason.title).toEqual("session_expired");
45 | }
46 | it("session expired v7", function () {
47 | set_version_info7();
48 |
49 | $httpBackend.whenPOST('/web/session/get_session_info').respond ({
50 | jsonrpc:"2.0",
51 | id: null,
52 | error: {
53 | message: "OpenERP WebClient Error",
54 | code: 300,
55 | data : {
56 | debug: "Client Traceback (most recent call last): File \"/workspace/parts/odoo/addons/web/http.py\", line 204, in dispatch response[\"result\"] = method(self, **self.params) File \"/workspace/parts/odoo/addons/web/controllers/main.py\", line 1133, in call_kw return self._call_kw(req, model, method, args, kwargs) File \"/workspace/parts/odoo/addons/web/controllers/main.py\", line 1125, in _call_kw return getattr(req.session.model(model), method)(*args, **kwargs) File \"/workspace/parts/odoo/addons/web/session.py\", line 158, in model raise SessionExpiredException(\"Session expired\")SessionExpiredException: Session expired",
57 | type: "client_exception"
58 | }
59 | }
60 | });
61 | jsonRpc.sendRequest('/web/session/get_session_info', {}).then(fail, success);;
62 | $httpBackend.flush();
63 | });
64 |
65 | it("session expired v8", function () {
66 | set_version_info8();
67 |
68 | $httpBackend.whenPOST('/web/session/get_session_info').respond({
69 | jsonrpc:"2.0",
70 | id: null,
71 | error: {
72 | message: "Odoo Session Expired",
73 | code: 100
74 | },
75 | data: {
76 | debug: "Traceback (most recent call last): File \"/workspace/parts/odoo/openerp/http.py\", line 530, in _handle_exception return super(JsonRequest, self)._handle_exception(exception) File \"/workspace/parts/odoo/openerp/addons/base/ir/ir_http.py\", line 160, in _dispatch auth_method = self._authenticate(func.routing[\"auth\"]) File \"/workspace/parts/odoo/openerp/addons/base/ir/ir_http.py\", line 93, in _authenticate getattr(self, \"_auth_method_%s\" % auth_method)() File \"/workspace/parts/odoo/openerp/addons/base/ir/ir_http.py\", line 70, in _auth_method_user raise http.SessionExpiredException(\"Session expired\")SessionExpiredException: Session expired",
77 | message: "Session expired",
78 | name:"openerp.http.SessionExpiredException",
79 | arguments: ["Session expired"]
80 | }
81 | });
82 | jsonRpc.sendRequest('/web/session/get_session_info', {}).then(fail, success);;
83 | $httpBackend.flush();
84 | });
85 | });
86 |
87 | describe("login fail (wrong credentials)", function () {
88 | function success(reason) {
89 | expect(reason.title).toEqual("wrong_login");
90 | }
91 | it("should reject wrong login v7", function () {
92 | set_version_info7();
93 |
94 | $httpBackend.whenPOST('/web/session/authenticate').respond({
95 | jsonrpc:"2.0",
96 | id: null,
97 | result: { username: "admin", user_context: {}, uid: false, db: "db", company_id: null, session_id: "7a97f880c0374c02507b09e478cffb5be4df2ef8" }
98 | });
99 | jsonRpc.login().then(fail, success);
100 | $httpBackend.flush();
101 | });
102 |
103 | it("wrong login v8", function () {
104 | set_version_info8();
105 | $httpBackend.whenPOST('/web/session/authenticate').respond ({
106 | jsonrpc:"2.0",
107 | id: null,
108 | result: { username: "admin", user_context: {}, uid: false, db: "db", company_id: null, session_id: "5699c4cd07f37e9fa8eaf5b63af8545020f25278" }
109 | });
110 | jsonRpc.login().then(fail, success);;
111 | $httpBackend.flush();
112 | });
113 | });
114 |
115 | describe("server issue on login", function () {
116 | function success(reason) {
117 | expect(reason.message).toEqual("HTTP Error");
118 | }
119 | it("should handle 404", function () {
120 | set_version_info7();
121 | $httpBackend.whenPOST('/web/session/authenticate').respond(404, "Not found");
122 | jsonRpc.login().then(fail, success);
123 | $httpBackend.flush();
124 | });
125 | it("should handle odoo 404", function () {
126 | set_version_info8();
127 | $httpBackend.whenPOST('/web/session/authenticate').respond({
128 | jsonrpc: "2.0",
129 | id: null,
130 | error: {message: "Odoo Server Error", code: 200, data: {debug: "Traceback (most recent call last):\n File \"/workspace/parts/odoo/openerp/http.py\", line 530, in _handle_exception\n return super(JsonRequest, self)._handle_exception(exception)\n File \"/workspace/parts/odoo/openerp/addons/base/ir/ir_http.py\", line 153, in _dispatch\n rule, arguments = self._find_handler(return_rule=True)\n File \"/workspace/parts/odoo/openerp/addons/base/ir/ir_http.py\", line 65, in _find_handler\n return self.routing_map().bind_to_environ(request.httprequest.environ).match(return_rule=return_rule)\n File \"/home/ubuntu/.voodoo/shared/eggs/Werkzeug-0.10.4-py2.7.egg/werkzeug/routing.py\", line 1483, in match\n raise NotFound()\nNotFound: 404: Not Found\n", message: "", name: "werkzeug.exceptions.NotFound", arguments: []}}});
131 | jsonRpc.login().then(fail, success);
132 | $httpBackend.flush();
133 | });
134 | it("should handle 500", function () {
135 | set_version_info7();
136 | $httpBackend.whenPOST('/web/session/authenticate').respond(500, "Server error");
137 | jsonRpc.login().then(fail, success);
138 | $httpBackend.flush();
139 | });
140 | });
141 |
142 |
143 | describe("wrong db on login", function () {
144 | function success(reason) {
145 | console.log('message', reason);
146 | expect(reason.title).toEqual("database_not_found");
147 | }/*
148 | it("should work with v7", function () {
149 | to be written
150 | });*/
151 | it("should work with v8", function () {
152 | set_version_info8();
153 | $httpBackend.whenPOST('/web/session/authenticate').respond({
154 | jsonrpc: "2.0",
155 | id: null,
156 | error: {message: "Odoo Server Error", code: 200, data: {debug: "\"Traceback (most recent call last): File \"/workspace/parts/odoo/openerp/http.py\", line 537, in _handle_exception return super(JsonRequest, self)._handle_exception(exception) File \"/workspace/parts/odoo/openerp/http.py\", line 574, in dispatch result = self._call_function(**self.params) File \"/workspace/parts/odoo/openerp/http.py\", line 310, in _call_function return checked_call(self.db, *args, **kwargs) File \"/workspace/parts/odoo/openerp/service/model.py\", line 113, in wrapper return f(dbname, *args, **kwargs) File \"/workspace/parts/odoo/openerp/http.py\", line 307, in checked_call return self.endpoint(*a, **kw) File \"/workspace/parts/odoo/openerp/http.py\", line 803, in __call__ return self.method(*args, **kw) File \"/workspace/parts/odoo/openerp/http.py\", line 403, in response_wrap response = f(*args, **kw) File \"/workspace/parts/odoo/addons/web/controllers/main.py\", line 796, in authenticate request.session.authenticate(db, login, password) File \"/workspace/parts/odoo/openerp/http.py\", line 956, in authenticate uid = dispatch_rpc('common', 'authenticate', [db, login, password, env]) File \"/workspace/parts/odoo/openerp/http.py\", line 115, in dispatch_rpc result = dispatch(method, params) File \"/workspace/parts/odoo/openerp/service/common.py\", line 26, in dispatch return fn(*params) File \"/workspace/parts/odoo/openerp/service/common.py\", line 37, in exp_authenticate res_users = openerp.registry(db)['res.users'] File \"/workspace/parts/odoo/openerp/__init__.py\", line 68, in registry return modules.registry.RegistryManager.get(database_name) File \"/workspace/parts/odoo/openerp/modules/registry.py\", line 339, in get update_module) File \"/workspace/parts/odoo/openerp/modules/registry.py\", line 356, in new registry = Registry(db_name) File \"/workspace/parts/odoo/openerp/modules/registry.py\", line 81, in __init__ cr = self.cursor() File \"/workspace/parts/odoo/openerp/modules/registry.py\", line 272, in cursor return self._db.cursor() File \"/workspace/parts/odoo/openerp/sql_db.py\", line 575, in cursor return Cursor(self.__pool, self.dbname, self.dsn, serialized=serialized) File \"/workspace/parts/odoo/openerp/sql_db.py\", line 181, in __init__ self._cnx = pool.borrow(dsn) File \"/workspace/parts/odoo/openerp/sql_db.py\", line 464, in _locked return fun(self, *args, **kwargs) File \"/workspace/parts/odoo/openerp/sql_db.py\", line 526, in borrow result = psycopg2.connect(dsn=dsn, connection_factory=PsycoConnection) File \"shared/eggs/psycopg2-2.6.1-py2.7-linux-x86_64.egg/psycopg2/__init__.py\", line 164, in connect conn = _connect(dsn, connection_factory=connection_factory, async=async)OperationalError: FATAL: database \"db\" does not exist", message: "FATAL: database \"db\" does not exist", name: "psycopg2.OperationalError", arguments: ["FATAL: database \"db\" does not exist"]}}});
157 | jsonRpc.login().then(fail, success);
158 | $httpBackend.flush();
159 | });
160 | });
161 |
162 |
163 | describe("login succeed", function () {
164 | function success(result) {
165 | expect(result.uid).toEqual(1);
166 | expect(result.username).toEqual('admin');
167 | }
168 | it("should login with v7", function () {
169 | set_version_info7();
170 | $httpBackend.whenPOST('/web/session/authenticate').respond({
171 | jsonrpc:"2.0",
172 | id: null,
173 | result: { username: "admin", user_context: { lang:"en_US", tz:"Europe/Brussels", uid:1}, uid: 1, db: "db", company_id: null, session_id: "5699c4cd07f37e9fa8eaf5b63af8545020f25278" }
174 | }, {
175 | 'Set-Cookie': 'sid=e3a14bd882a848187e0611bbc51712a25db0fec7; Path=/'
176 | });
177 | jsonRpc.login().then(success, fail);;
178 | $httpBackend.flush();
179 | });
180 |
181 | it("should login with v8", function () {
182 | set_version_info8();
183 | $httpBackend.whenPOST('/web/session/authenticate').respond({
184 | jsonrpc:"2.0",
185 | id: null,
186 | result: { username: "admin", user_context: { lang:"en_US", tz:"Europe/Brussels", uid:1}, uid: 1, db: "db", session_id: "5699c4cd07f37e9fa8eaf5b63af8545020f25278" }
187 | },{
188 | 'Set-Cookie': 'session_id=7a97f880c0374c02507b09e478cffb5be4df2ef8; Expires=Thu, 23-Jul-2015 10:36:21 GMT; Max-Age=7776000; Path=/'
189 | });
190 | jsonRpc.login().then(success, fail);
191 | $httpBackend.flush();
192 | });
193 | });
194 |
195 | describe("v7/v8 specific", function () {
196 | var session_idToken = null;
197 | function success(result) {
198 | expect(result.uid).toEqual(1);
199 | }
200 |
201 | beforeEach(function () {
202 | session_idToken = "sendMePlease"+ Math.random();
203 |
204 | $httpBackend.whenPOST('/web/session/authenticate').respond({
205 | jsonrpc:"2.0",
206 | id: null,
207 | result: { username: "admin", user_context: { lang:"en_US", tz:"Europe/Brussels", uid:1}, uid: 1, db: "db", company_id: null, session_id: session_idToken }
208 | });
209 | });
210 |
211 | it("should take care of putting session_id in request.body — v7", function () {
212 | set_version_info7();
213 |
214 | $httpBackend.whenPOST('/web/session/get_session_info',
215 | '{"jsonrpc":"2.0","method":"call","params":{"session_id":"'+ session_idToken +'"}}'
216 | ).respond({
217 | jsonrpc:"2.0",
218 | id: null,
219 | result: { username: "admin", user_context: { lang:"en_US", tz:"Europe/Brussels", uid:1}, uid: 1, db: "db", company_id: null, session_id: session_idToken }
220 | });
221 | jsonRpc.login().then(function (a) {
222 | jsonRpc.sendRequest('/web/session/get_session_info', {}).then(success, fail);
223 | });
224 |
225 | $httpBackend.flush();
226 | });
227 |
228 |
229 | it("should take care of NOT putting session_id in request.body - v8", function () {
230 | set_version_info8();
231 |
232 | $httpBackend.whenPOST('/web/session/get_session_info',
233 | '{"jsonrpc":"2.0","method":"call","params":{}}' //session is_shouldn't be retransmitted
234 | ).respond({
235 | jsonrpc:"2.0",
236 | id: null,
237 | result: { username: "admin", user_context: { lang:"en_US", tz:"Europe/Brussels", uid:1}, uid: 1, db: "db", session_id: session_idToken }
238 | });
239 |
240 | jsonRpc.login().then(function () {
241 | jsonRpc.sendRequest('/web/session/get_session_info', {}).then(success, fail);
242 | });
243 | $httpBackend.flush();
244 | });
245 | });
246 |
247 | describe("isLoggedIn ? ", function () {
248 | it("isLoggedIn with v7", function () {
249 | return success();
250 | //continue here
251 | set_version_info7();
252 | $httpBackend.whenPOST('/web/session/authenticate').respond({
253 | jsonrpc:"2.0",
254 | id: null,
255 | result: { username: "admin", user_context: { lang:"en_US", tz:"Europe/Brussels", uid:1}, uid: 1, db: "db", company_id: null, session_id: "5699c4cd07f37e9fa8eaf5b63af8545020f25278" }
256 | }, {
257 | 'Set-Cookie': 'sid=e3a14bd882a848187e0611bbc51712a25db0fec7; Path=/'
258 | });
259 |
260 | $httpBackend.whenPOST('/web/session/get_session_info',
261 | '{"jsonrpc":"2.0","method":"call","params":{"session_id":"'+ session_idToken +'"}}'
262 | ).respond({
263 | jsonrpc:"2.0",
264 | id: null,
265 | result: { username: "admin", user_context: { lang:"en_US", tz:"Europe/Brussels", uid:1}, uid: 1, db: "db", company_id: null, session_id: session_idToken }
266 | });
267 |
268 | jsonRpc.isLoggedIn().then(function (r) {
269 | expect(r).toBe(false); //ensure not connected
270 | }, fail)
271 | .then(function () {
272 | return jsonRpc.login('db','admin','password');
273 | }) //login
274 | .then(jsonRpc.isLoggedIn).then(function (r) {
275 | expect(r).toBe(true); //ensure connected
276 | }, fail);
277 |
278 | $httpBackend.flush();
279 | });
280 | });
281 | });
--------------------------------------------------------------------------------