LP#1379824 Make PermaCrud.js disconnect() actually disconnect
[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             // session.disconnect() does not return any indication of success,
64             // so we must simply assume it worked
65             this.session.disconnect();
66             this.connected = false;
67             return true;
68         },
69
70         _session_request : function ( args /* hash */, commitOnComplete /* set to true, else no */ ) {
71
72             var me = this;
73             var endstyle = 'rollback';
74             var aopts = dojo.mixin({}, args);
75             args = aopts;
76             if (commitOnComplete) endstyle = 'commit';
77
78             if (me.authoritative) {
79                 if (!me.connected) me.connect();
80                 if (args.timeout && !args.oncomplete && !args.onresponse) { // pure sync call
81                     args.oncomplete = function (r) {
82                         me.session.request('open-ils.pcrud.transaction.' + endstyle, me.auth());
83                         me.disconnect();
84                     };
85                 } else if (args.oncomplete) { // there's an oncomplete, fire that, and then end the transaction
86                     var orig_oncomplete = args.oncomplete;
87                     args.oncomplete = function (r) {
88                         var ret;
89                         try {
90                             ret = orig_oncomplete(r);
91                         } finally {
92                             me.session.request('open-ils.pcrud.transaction.' + endstyle, me.auth());
93                             me.disconnect();
94                         }
95                         return ret;
96                     };
97                 }
98                 me.session.request('open-ils.pcrud.transaction.begin', me.auth());
99             }
100             return me.session.request( args );
101         },
102
103         retrieve : function ( fm_class /* Fieldmapper class hint */, id /* Fieldmapper object primary key value */,  opts /* Option hash */) {
104             if(!opts) opts = {};
105             var ffj = {};
106             if (opts.join) ffj.join = opts.join;
107             if (opts.flesh) ffj.flesh = opts.flesh;
108             if (opts.flesh_fields) ffj.flesh_fields = opts.flesh_fields;
109             var req_hash = dojo.mixin(
110                 opts, 
111                 { method : 'open-ils.pcrud.retrieve.' + fm_class,
112                   params : [ this.auth(), id, ffj ]
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             if (opts.flesh) order_by.flesh = opts.flesh;
152             if (opts.flesh_fields) order_by.flesh_fields = opts.flesh_fields;
153             
154             var method = 'open-ils.pcrud.search.' + fm_class;
155             if(!opts.streaming) method += '.atomic';
156
157             var search = {};
158             search[pkey] = { '!=' : null };
159
160             var req_hash = dojo.mixin(
161                 opts, 
162                 { method : method,
163                   params : [ this.auth(), search, order_by ]
164                 }
165             );
166
167             if (!opts.async && !opts.timeout) req_hash.timeout = 10;
168
169             var _pcrud = this;
170             var req = this._session_request( req_hash );
171
172             if (!req.onerror)
173                 req.onerror = function (r) { throw js2JSON(r); };
174             
175             // if it's an async call and the user does not care about 
176             // the responses, pull them off the network and discard them
177             if (!req_hash.timeout && !req.oncomplete)
178                 req.oncomplete = function (r) { while(r.recv()){}; };
179
180             req.send();
181
182             // for synchronous calls with no handlers, return the first received value
183             if (req_hash.timeout && !opts.oncomplete && !opts.onresponse) {
184                 var resp = req.recv();
185                 if(resp) return resp.content();
186                 return null;
187             }
188
189             return req;
190         },
191
192         search : function ( fm_class /* Fieldmapper class hint */, search /* Fieldmapper query object */, opts /* Option hash */) {
193             var return_type = 'search';
194             if(!opts) opts = {};
195             var order_by = {};
196             if (opts.order_by) order_by.order_by = opts.order_by;
197             if (opts.select) order_by.select = opts.select;
198             if (opts.limit) order_by.limit = opts.limit;
199             if (opts.offset) order_by.offset = opts.offset;
200             if (opts.join) order_by.join = opts.join;
201             if (opts.flesh) order_by.flesh = opts.flesh;
202             if (opts.flesh_fields) order_by.flesh_fields = opts.flesh_fields;
203             if (opts.id_list) return_type = 'id_list';
204
205             var method = 'open-ils.pcrud.' + return_type + '.' + fm_class;
206             if(!opts.streaming) method += '.atomic';
207
208             var req_hash = dojo.mixin(
209                 opts, 
210                 { method : method,
211                   params : [ this.auth(), search, order_by ]
212                 }
213             );
214
215             if (!opts.async && !opts.timeout) req_hash.timeout = 10;
216
217             var _pcrud = this;
218             var req = this._session_request( req_hash );
219
220             if (!req.onerror)
221                 req.onerror = function (r) { throw js2JSON(r); };
222
223             // if it's an async call and the user does not care about 
224             // the responses, pull them off the network and discard them
225             if (!req_hash.timeout && !req.oncomplete)
226                 req.oncomplete = function (r) { while(r.recv()){}; };
227
228             req.send();
229
230             // for synchronous calls with no handlers, return the first received value
231             if (req_hash.timeout && !opts.oncomplete && !opts.onresponse) {
232                 var resp = req.recv();
233                 if(resp) return resp.content();
234                 return null;
235             }
236
237             return req;
238         },
239
240         _CUD : function ( method /* 'create' or 'update' or 'delete' */, list /* Fieldmapper object */, opts /* Option hash */) {
241             if(!opts) opts = {};
242
243             if (dojo.isArray(list)) {
244                 if (list.classname) list = [ list ];
245             } else {
246                 list = [ list ];
247             }
248
249             if (!this.connected) this.connect();
250
251             var _pcrud = this;
252             var _return_list = [];
253
254             function _CUD_recursive ( obj_list, pos, final_complete, final_error ) {
255                 var obj = obj_list[pos];
256                 var req_hash = {
257                     method : 'open-ils.pcrud.' + method + '.' + obj.classname,
258                     params : [ _pcrud.auth(), obj ],
259                     onerror : final_error || function (r) { _pcrud.disconnect(); throw '_CUD: Error creating, deleting or updating ' + js2JSON(obj); }
260                 };
261
262                 var req = _pcrud.session.request( req_hash );
263                 req._final_complete = final_complete;
264                 req._final_error = final_error;
265
266                 if (++pos == obj_list.length) {
267                     req.oncomplete = function (r) {
268                         var res = r.recv();
269
270                         if ( res && res.content() ) {
271                             _return_list.push( res.content() );
272                             _pcrud.session.request({
273                                 method : 'open-ils.pcrud.transaction.commit',
274                                 timeout : 10,
275                                 params : [ _pcrud.auth() ],
276                                 onerror : function (r) {
277                                     _pcrud.disconnect();
278                                     if (req._final_error) req._final_error(r)
279                                     else throw 'Transaction commit error';
280                                 },      
281                                 oncomplete : function (r) {
282                                     var res = r.recv();
283                                     if ( res && res.content() ) {
284                                         if(req._final_complete)
285                                             req._final_complete(req, _return_list);
286                                         _pcrud.disconnect();
287                                     } else {
288                                         _pcrud.disconnect();
289                                         if (req._final_error) req._final_error(r)
290                                         else throw 'Transaction commit error';
291                                     }
292                                 },
293                             }).send();
294                         } else {
295                             _pcrud.disconnect();
296                             if (req._final_error) req._final_error(r)
297                             else throw '_CUD: Error creating, deleting or updating ' + js2JSON(obj);
298                         }
299                     };
300
301                     req.onerror = function (r) {
302                         _pcrud.disconnect();
303                         if (req._final_error) req._final_error(r);
304                         else throw '_CUD: Error creating, deleting or updating ' + js2JSON(obj);
305                     };
306
307                 } else {
308                     req._pos = pos;
309                     req._obj_list = obj_list;
310                     req.oncomplete = function (r) {
311                         var res = r.recv();
312                         if ( res && res.content() ) {
313                             _return_list.push( res.content() );
314                             _CUD_recursive( r._obj_list, r._pos, req._final_complete, req._final_error );
315                         } else {
316                             _pcrud.disconnect();
317                             if (req._final_error) req._final_error(r);
318                             else throw '_CUD: Error creating, deleting or updating ' + js2JSON(obj);
319                         }
320                     };
321                     req.onerror = function (r) {
322                         _pcrud.disconnect();
323                         if (req._final_error) req._final_error(r);
324                         throw '_CUD: Error creating, deleting or updating ' + js2JSON(obj);
325                     };
326                 }
327
328                 req.send();
329             }
330
331             var f_complete = opts.oncomplete;
332             var f_error = opts.onerror;
333
334             this.session.request({
335                 method : 'open-ils.pcrud.transaction.begin',
336                 timeout : 10,
337                 params : [ _pcrud.auth() ],
338                 onerror : function (r) {
339                     _pcrud.disconnect();
340                     throw 'Transaction begin error';
341                 },      
342                 oncomplete : function (r) {
343                     var res = r.recv();
344                     if ( res && res.content() ) {
345                         _CUD_recursive( list, 0, f_complete, f_error );
346                     } else {
347                         _pcrud.disconnect();
348                         throw 'Transaction begin error';
349                     }
350                 },
351             }).send();
352
353             return _return_list;
354
355         },
356
357         create : function ( list, opts ) {
358             return this._CUD( 'create', list, opts );
359         },
360
361         update : function ( list, opts ) {
362             var id_list = this._CUD( 'update', list, opts );
363             var obj_list = [];
364
365             for (var idx = 0; idx < id_list.length; idx++) {
366                 obj_list.push(
367                     this.retrieve( list[idx].classname, id_list[idx] )
368                 );
369             }
370
371             return obj_list;
372         },
373
374         /* 
375          * 'delete' is a reserved keyword in JavaScript and can't be used
376          * in browsers like IE or Chrome, so we define a safe synonym
377      * NOTE: delete() is now removed -- use eliminate instead
378
379         delete : function ( list, opts ) {
380             return this._CUD( 'delete', list, opts );
381         },
382
383          */
384         eliminate: function ( list, opts ) {
385             return this._CUD( 'delete', list, opts );
386         },
387
388         apply : function ( list, opts ) {
389             this._auto_CUD( list, opts );
390         },
391
392         _auto_CUD : function ( list /* Fieldmapper object */, opts /* Option hash */) {
393
394             if(!opts) opts = {};
395
396             if (dojo.isArray(list)) {
397                 if (list.classname) list = [ list ];
398             } else {
399                 list = [ list ];
400             }
401
402             if (!this.connected) this.connect();
403
404             var _pcrud = this;
405             var _return_list = [];
406
407             function _auto_CUD_recursive ( obj_list, pos, final_complete, final_error ) {
408                 var obj = obj_list[pos];
409
410                 var method;
411                 if (obj.ischanged()) method = 'update';
412                 if (obj.isnew())     method = 'create';
413                 if (obj.isdeleted()) method = 'delete';
414                 if (!method) {
415                     return _auto_CUD_recursive(obj_list, pos+1, final_complete, final_error);
416                 }
417
418                 var req_hash = {
419                     method : 'open-ils.pcrud.' + method + '.' + obj.classname,
420                     timeout : 10,
421                     params : [ _pcrud.auth(), obj ],
422                     onerror : final_error || function (r) { _pcrud.disconnect(); throw '_auto_CUD: Error creating, deleting or updating ' + js2JSON(obj); }
423                 };
424
425                 var req = _pcrud.session.request( req_hash );
426                 req._final_complete = final_complete;
427                 req._final_error = final_error;
428
429                 if (++pos == obj_list.length) {
430                     req.oncomplete = function (r) {
431                         var res = r.recv();
432
433                         if ( res && res.content() ) {
434                             _return_list.push( res.content() );
435                             _pcrud.session.request({
436                                 method : 'open-ils.pcrud.transaction.commit',
437                                 timeout : 10,
438                                 params : [ _pcrud.auth() ],
439                                 onerror : function (r) {
440                                     _pcrud.disconnect();
441                                     throw 'Transaction commit error';
442                                 },      
443                                 oncomplete : function (r) {
444                                     var res = r.recv();
445                                     if ( res && res.content() ) {
446                                         if (req._final_complete) 
447                                             req._final_complete(req, _return_list);
448                                         _pcrud.disconnect();
449                                     } else {
450                                         _pcrud.disconnect();
451                                         if (req._final_error) req._final_error(r);
452                                         else throw 'Transaction commit error';
453                                     }
454                                 },
455                             }).send();
456                         } else {
457                             _pcrud.disconnect();
458                             if (req._final_error) req._final_error(r)
459                             else throw '_auto_CUD: Error creating, deleting or updating ' + js2JSON(obj);
460                         }
461                     };
462
463                     req.onerror = function (r) {
464                         _pcrud.disconnect();
465                         if (req._final_error) req._final_error(r);
466                     };
467
468                 } else {
469                     req._pos = pos;
470                     req._obj_list = obj_list;
471                     req.oncomplete = function (r) {
472                         var res = r.recv();
473                         if ( res && res.content() ) {
474                             _return_list.push( res.content() );
475                             _auto_CUD_recursive( r._obj_list, r._pos, req._final_complete, req._final_error );
476                         } else {
477                             _pcrud.disconnect();
478                             if (req._final_error) req._final_error(r);
479                             else throw '_auto_CUD: Error creating, deleting or updating ' + js2JSON(obj);
480                         }
481                     };
482                 }
483
484                 req.send();
485             }
486
487             var f_complete = opts.oncomplete;
488             var f_error = opts.onerror;
489
490             this.session.request({
491                 method : 'open-ils.pcrud.transaction.begin',
492                 timeout : 10,
493                 params : [ _pcrud.auth() ],
494                 onerror : function (r) {
495                     _pcrud.disconnect();
496                     throw 'Transaction begin error';
497                 },      
498                 oncomplete : function (r) {
499                     var res = r.recv();
500                     if ( res && res.content() ) {
501                         _auto_CUD_recursive( list, 0, f_complete, f_error );
502                     } else {
503                         _pcrud.disconnect();
504                         throw 'Transaction begin error';
505                     }
506                 },
507             }).send();
508
509             return _return_list;
510         }
511
512     });
513 }
514
515