]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/PermaCrud.js
Even more PermaCrud error handling cleanup, this time in .apply()
[working/Evergreen.git] / Open-ILS / web / js / dojo / openils / PermaCrud.js
1 /* ---------------------------------------------------------------------------
2  * Copyright (C) 2008  Equinox Software, Inc
3  * Mike Rylander <miker@esilibrary.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * ---------------------------------------------------------------------------
15  */
16
17 if(!dojo._hasResource["openils.PermaCrud"]) {
18
19     dojo._hasResource["openils.PermaCrud"] = true;
20     dojo.provide("openils.PermaCrud");
21     dojo.require("fieldmapper.Fieldmapper");
22     dojo.require("openils.User");
23
24     dojo.declare('openils.PermaCrud', null, {
25
26         session : null,
27         authtoken : null,
28         connnected : false,
29         authoritative : false,
30
31         constructor : function ( kwargs ) {
32             kwargs = kwargs || {};
33
34             this.authtoken = kwargs.authtoken;
35             this.authoritative = kwargs.authoritative;
36
37             this.session =
38                 kwargs.session ||
39                 new OpenSRF.ClientSession('open-ils.pcrud');
40
41             if (
42                 this.session &&
43                 this.session.state == OSRF_APP_SESSION_CONNECTED
44             ) this.connected = true;
45         },
46
47         auth : function (token) {
48             if (token) this.authtoken = token;
49             return this.authtoken || openils.User.authtoken;
50         },
51
52         connect : function ( onerror ) {
53             if (!this.connected && !this.session.connect()) {
54                 this.connected = false;
55                 if (onerror) onerror(this.session);
56                 return false;
57             }
58             this.connected = true;
59             return true;
60         },
61
62         disconnect : function ( onerror ) {
63             this.connected = false;
64             return true;
65             // disconnect returns nothing, which is null, which is not true, cause the following to always run ... arg.
66             if (!this.session.disconnect()) {
67                 if (onerror) onerror(this.session);
68                 return false;
69             }
70         },
71
72         _session_request : function ( args /* hash */, commitOnComplete /* set to true, else no */ ) {
73
74             var me = this;
75             var endstyle = 'rollback';
76             var aopts = dojo.mixin({}, args);
77             args = aopts;
78             if (commitOnComplete) endstyle = 'commit';
79
80             if (me.authoritative) {
81                 if (!me.connected) me.connect();
82                 if (args.timeout && !args.oncomplete && !args.onresponse) { // pure sync call
83                     args.oncomplete = function (r) {
84                         me.session.request('open-ils.pcrud.transaction.' + endstyle, me.auth());
85                         me.session.disconnect();
86                         me.disconnect();
87                     };
88                 } else if (args.oncomplete) { // there's an oncomplete, fire that, and then end the transaction
89                     var orig_oncomplete = args.oncomplete;
90                     args.oncomplete = function (r) {
91                         var ret;
92                         try {
93                             ret = orig_oncomplete(r);
94                         } finally {
95                             me.session.request('open-ils.pcrud.transaction.' + endstyle, me.auth());
96                             me.session.disconnect();
97                             me.disconnect();
98                         }
99                         return ret;
100                     };
101                 }
102                 me.session.request('open-ils.pcrud.transaction.begin', me.auth());
103             }
104             return me.session.request( args );
105         },
106
107         retrieve : function ( fm_class /* Fieldmapper class hint */, id /* Fieldmapper object primary key value */,  opts /* Option hash */) {
108             if(!opts) opts = {};
109             var req_hash = dojo.mixin(
110                 opts, 
111                 { method : 'open-ils.pcrud.retrieve.' + fm_class,
112                   params : [ this.auth(), id ]
113                 }
114             );
115
116             if (!opts.async && !opts.timeout) req_hash.timeout = 10;
117
118             var _pcrud = this;
119             var req = this._session_request( req_hash );
120
121             if (!req.onerror)
122                 req.onerror = function (r) { throw js2JSON(r); };
123
124             // if it's an async call and the user does not care about 
125             // the responses, pull them off the network and discard them
126             if (!req_hash.timeout && !req.oncomplete)
127                 req.oncomplete = function (r) { while(r.recv()){}; };
128
129             req.send();
130
131             // for synchronous calls with no handlers, return the first received value
132             if (req_hash.timeout && !opts.oncomplete && !opts.onresponse) {
133                 var resp = req.recv();
134                 if(resp) return resp.content();
135                 return null;
136             }
137
138             return req;
139         },
140
141         retrieveAll : function ( fm_class /* Fieldmapper class hint */, opts /* Option hash */) {
142             var pkey = fieldmapper[fm_class].Identifier;
143
144             if(!opts) opts = {};
145             var order_by = {};
146             if (opts.order_by) order_by.order_by = opts.order_by;
147             if (opts.select) order_by.select = opts.select;
148             if (opts.limit) order_by.limit = opts.limit;
149             if (opts.offset) order_by.offset = opts.offset;
150             if (opts.join) order_by.join = opts.join;
151             
152             var method = 'open-ils.pcrud.search.' + fm_class;
153             if(!opts.streaming) method += '.atomic';
154
155             var search = {};
156             search[pkey] = { '!=' : null };
157
158             var req_hash = dojo.mixin(
159                 opts, 
160                 { method : method,
161                   params : [ this.auth(), search, order_by ]
162                 }
163             );
164
165             if (!opts.async && !opts.timeout) req_hash.timeout = 10;
166
167             var _pcrud = this;
168             var req = this._session_request( req_hash );
169
170             if (!req.onerror)
171                 req.onerror = function (r) { throw js2JSON(r); };
172             
173             // if it's an async call and the user does not care about 
174             // the responses, pull them off the network and discard them
175             if (!req_hash.timeout && !req.oncomplete)
176                 req.oncomplete = function (r) { while(r.recv()){}; };
177
178             req.send();
179
180             // for synchronous calls with no handlers, return the first received value
181             if (req_hash.timeout && !opts.oncomplete && !opts.onresponse) {
182                 var resp = req.recv();
183                 if(resp) return resp.content();
184                 return null;
185             }
186
187             return req;
188         },
189
190         search : function ( fm_class /* Fieldmapper class hint */, search /* Fieldmapper query object */, opts /* Option hash */) {
191             var return_type = 'search';
192             if(!opts) opts = {};
193             var order_by = {};
194             if (opts.order_by) order_by.order_by = opts.order_by;
195             if (opts.select) order_by.select = opts.select;
196             if (opts.limit) order_by.limit = opts.limit;
197             if (opts.offset) order_by.offset = opts.offset;
198             if (opts.join) order_by.join = opts.join;
199             if (opts.id_list) return_type = 'id_list';
200
201             var method = 'open-ils.pcrud.' + return_type + '.' + fm_class;
202             if(!opts.streaming) method += '.atomic';
203
204             var req_hash = dojo.mixin(
205                 opts, 
206                 { method : method,
207                   params : [ this.auth(), search, order_by ]
208                 }
209             );
210
211             if (!opts.async && !opts.timeout) req_hash.timeout = 10;
212
213             var _pcrud = this;
214             var req = this._session_request( req_hash );
215
216             if (!req.onerror)
217                 req.onerror = function (r) { throw js2JSON(r); };
218
219             // if it's an async call and the user does not care about 
220             // the responses, pull them off the network and discard them
221             if (!req_hash.timeout && !req.oncomplete)
222                 req.oncomplete = function (r) { while(r.recv()){}; };
223
224             req.send();
225
226             // for synchronous calls with no handlers, return the first received value
227             if (req_hash.timeout && !opts.oncomplete && !opts.onresponse) {
228                 var resp = req.recv();
229                 if(resp) return resp.content();
230                 return null;
231             }
232
233             return req;
234         },
235
236         _CUD : function ( method /* 'create' or 'update' or 'delete' */, list /* Fieldmapper object */, opts /* Option hash */) {
237             if(!opts) opts = {};
238
239             if (dojo.isArray(list)) {
240                 if (list.classname) list = [ list ];
241             } else {
242                 list = [ list ];
243             }
244
245             if (!this.connected) this.connect();
246
247             var _pcrud = this;
248             var _return_list = [];
249
250             function _CUD_recursive ( obj_list, pos, final_complete, final_error ) {
251                 var obj = obj_list[pos];
252                 var req_hash = {
253                     method : 'open-ils.pcrud.' + method + '.' + obj.classname,
254                     params : [ _pcrud.auth(), obj ],
255                     onerror : final_error || function (r) { _pcrud.disconnect(); throw '_CUD: Error creating, deleting or updating ' + js2JSON(obj); }
256                 };
257
258                 var req = _pcrud.session.request( req_hash );
259                 req._final_complete = final_complete;
260                 req._final_error = final_error;
261
262                 if (++pos == obj_list.length) {
263                     req.oncomplete = function (r) {
264                         var res = r.recv();
265
266                         if ( res && res.content() ) {
267                             _return_list.push( res.content() );
268                             _pcrud.session.request({
269                                 method : 'open-ils.pcrud.transaction.commit',
270                                 timeout : 10,
271                                 params : [ _pcrud.auth() ],
272                                 onerror : function (r) {
273                                     _pcrud.disconnect();
274                                     if (req._final_error) req._final_error(r)
275                                     else throw 'Transaction commit error';
276                                 },      
277                                 oncomplete : function (r) {
278                                     var res = r.recv();
279                                     if ( res && res.content() ) {
280                                         if(req._final_complete)
281                                             req._final_complete(req, _return_list);
282                                         _pcrud.disconnect();
283                                     } else {
284                                         _pcrud.disconnect();
285                                         if (req._final_error) req._final_error(r)
286                                         else throw 'Transaction commit error';
287                                     }
288                                 },
289                             }).send();
290                         } else {
291                             _pcrud.disconnect();
292                             if (req._final_error) req._final_error(r)
293                             else throw '_CUD: Error creating, deleting or updating ' + js2JSON(obj);
294                         }
295                     };
296
297                     req.onerror = function (r) {
298                         _pcrud.disconnect();
299                         if (req._final_error) req._final_error(r);
300                         else throw '_CUD: Error creating, deleting or updating ' + js2JSON(obj);
301                     };
302
303                 } else {
304                     req._pos = pos;
305                     req._obj_list = obj_list;
306                     req.oncomplete = function (r) {
307                         var res = r.recv();
308                         if ( res && res.content() ) {
309                             _return_list.push( res.content() );
310                             _CUD_recursive( r._obj_list, r._pos, req._final_complete, req._final_error );
311                         } else {
312                             _pcrud.disconnect();
313                             if (req._final_error) req._final_error(r);
314                             else throw '_CUD: Error creating, deleting or updating ' + js2JSON(obj);
315                         }
316                     };
317                     req.onerror = function (r) {
318                         _pcrud.disconnect();
319                         if (req._final_error) req._final_error(r);
320                         throw '_CUD: Error creating, deleting or updating ' + js2JSON(obj);
321                     };
322                 }
323
324                 req.send();
325             }
326
327             var f_complete = opts.oncomplete;
328             var f_error = opts.onerror;
329
330             this.session.request({
331                 method : 'open-ils.pcrud.transaction.begin',
332                 timeout : 10,
333                 params : [ _pcrud.auth() ],
334                 onerror : function (r) {
335                     _pcrud.disconnect();
336                     throw 'Transaction begin error';
337                 },      
338                 oncomplete : function (r) {
339                     var res = r.recv();
340                     if ( res && res.content() ) {
341                         _CUD_recursive( list, 0, f_complete, f_error );
342                     } else {
343                         _pcrud.disconnect();
344                         throw 'Transaction begin error';
345                     }
346                 },
347             }).send();
348
349             return _return_list;
350
351         },
352
353         create : function ( list, opts ) {
354             return this._CUD( 'create', list, opts );
355         },
356
357         update : function ( list, opts ) {
358             var id_list = this._CUD( 'update', list, opts );
359             var obj_list = [];
360
361             for (var idx = 0; idx < id_list.length; idx++) {
362                 obj_list.push(
363                     this.retrieve( list[idx].classname, id_list[idx] )
364                 );
365             }
366
367             return obj_list;
368         },
369
370         /* 
371          * 'delete' is a reserved keyword in JavaScript and can't be used
372          * in browsers like IE or Chrome, so we define a safe synonym
373      * NOTE: delete() is now removed -- use eliminate instead
374
375         delete : function ( list, opts ) {
376             return this._CUD( 'delete', list, opts );
377         },
378
379          */
380         eliminate: function ( list, opts ) {
381             return this._CUD( 'delete', list, opts );
382         },
383
384         apply : function ( list, opts ) {
385             this._auto_CUD( list, opts );
386         },
387
388         _auto_CUD : function ( list /* Fieldmapper object */, opts /* Option hash */) {
389
390             if(!opts) opts = {};
391
392             if (dojo.isArray(list)) {
393                 if (list.classname) list = [ list ];
394             } else {
395                 list = [ list ];
396             }
397
398             if (!this.connected) this.connect();
399
400             var _pcrud = this;
401             var _return_list = [];
402
403             function _auto_CUD_recursive ( obj_list, pos, final_complete, final_error ) {
404                 var obj = obj_list[pos];
405
406                 var method;
407                 if (obj.ischanged()) method = 'update';
408                 if (obj.isnew())     method = 'create';
409                 if (obj.isdeleted()) method = 'delete';
410                 if (!method) {
411                     return _auto_CUD_recursive(obj_list, pos+1, final_complete, final_error);
412                 }
413
414                 var req_hash = {
415                     method : 'open-ils.pcrud.' + method + '.' + obj.classname,
416                     timeout : 10,
417                     params : [ _pcrud.auth(), obj ],
418                     onerror : final_error || function (r) { _pcrud.disconnect(); throw '_auto_CUD: Error creating, deleting or updating ' + js2JSON(obj); }
419                 };
420
421                 var req = _pcrud.session.request( req_hash );
422                 req._final_complete = final_complete;
423                 req._final_error = final_error;
424
425                 if (++pos == obj_list.length) {
426                     req.oncomplete = function (r) {
427                         var res = r.recv();
428
429                         if ( res && res.content() ) {
430                             _return_list.push( res.content() );
431                             _pcrud.session.request({
432                                 method : 'open-ils.pcrud.transaction.commit',
433                                 timeout : 10,
434                                 params : [ _pcrud.auth() ],
435                                 onerror : function (r) {
436                                     _pcrud.disconnect();
437                                     throw 'Transaction commit error';
438                                 },      
439                                 oncomplete : function (r) {
440                                     var res = r.recv();
441                                     if ( res && res.content() ) {
442                                         if (req._final_complete) 
443                                             req._final_complete(req, _return_list);
444                                         _pcrud.disconnect();
445                                     } else {
446                                         _pcrud.disconnect();
447                                         if (req._final_error) req._final_error(r);
448                                         else throw 'Transaction commit error';
449                                     }
450                                 },
451                             }).send();
452                         } else {
453                             _pcrud.disconnect();
454                             if (req._final_error) req._final_error(r)
455                             else throw '_auto_CUD: Error creating, deleting or updating ' + js2JSON(obj);
456                         }
457                     };
458
459                     req.onerror = function (r) {
460                         _pcrud.disconnect();
461                         if (req._final_error) req._final_error(r);
462                     };
463
464                 } else {
465                     req._pos = pos;
466                     req._obj_list = obj_list;
467                     req.oncomplete = function (r) {
468                         var res = r.recv();
469                         if ( res && res.content() ) {
470                             _return_list.push( res.content() );
471                             _auto_CUD_recursive( r._obj_list, r._pos, req._final_complete, req._final_error );
472                         } else {
473                             _pcrud.disconnect();
474                             if (req._final_error) req._final_error(r);
475                             else throw '_auto_CUD: Error creating, deleting or updating ' + js2JSON(obj);
476                         }
477                     };
478                 }
479
480                 req.send();
481             }
482
483             var f_complete = opts.oncomplete;
484             var f_error = opts.onerror;
485
486             this.session.request({
487                 method : 'open-ils.pcrud.transaction.begin',
488                 timeout : 10,
489                 params : [ _pcrud.auth() ],
490                 onerror : function (r) {
491                     _pcrud.disconnect();
492                     throw 'Transaction begin error';
493                 },      
494                 oncomplete : function (r) {
495                     var res = r.recv();
496                     if ( res && res.content() ) {
497                         _auto_CUD_recursive( list, 0, f_complete, f_error );
498                     } else {
499                         _pcrud.disconnect();
500                         throw 'Transaction begin error';
501                     }
502                 },
503             }).send();
504
505             return _return_list;
506         }
507
508     });
509 }
510
511