]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/core/pcrud.service.ts
LP#1801984 Upgrading Angular 6 to Angular 7
[Evergreen.git] / Open-ILS / src / eg2 / src / app / core / pcrud.service.ts
1 import {Injectable} from '@angular/core';
2 import {Observable, Observer} from 'rxjs';
3 import {IdlService, IdlObject} from './idl.service';
4 import {NetService, NetRequest} from './net.service';
5 import {AuthService} from './auth.service';
6
7 // Externally defined.  Used here for debugging.
8 declare var js2JSON: (jsThing: any) => string;
9 declare var OpenSRF: any; // creating sessions
10
11 interface PcrudReqOps {
12     authoritative?: boolean;
13     anonymous?: boolean;
14     idlist?: boolean;
15     atomic?: boolean;
16 }
17
18 // For for documentation purposes.
19 type PcrudResponse = any;
20
21 export class PcrudContext {
22
23     static verboseLogging = true; //
24     static identGenerator = 0; // for debug logging
25
26     private ident: number;
27     private authoritative: boolean;
28     private xactCloseMode: string;
29     private cudIdx: number;
30     private cudAction: string;
31     private cudLast: PcrudResponse;
32     private cudList: IdlObject[];
33
34     private idl: IdlService;
35     private net: NetService;
36     private auth: AuthService;
37
38     // Tracks nested CUD actions
39     cudObserver: Observer<PcrudResponse>;
40
41     session: any; // OpenSRF.ClientSession
42
43     constructor( // passed in by parent service -- not injected
44         egIdl: IdlService,
45         egNet: NetService,
46         egAuth: AuthService
47     ) {
48         this.idl = egIdl;
49         this.net = egNet;
50         this.auth = egAuth;
51         this.xactCloseMode = 'rollback';
52         this.ident = PcrudContext.identGenerator++;
53         this.session = new OpenSRF.ClientSession('open-ils.pcrud');
54     }
55
56     toString(): string {
57         return '[PCRUDContext ' + this.ident + ']';
58     }
59
60     log(msg: string): void {
61         if (PcrudContext.verboseLogging) {
62             console.debug(this + ': ' + msg);
63         }
64     }
65
66     err(msg: string): void {
67         console.error(this + ': ' + msg);
68     }
69
70     token(reqOps?: PcrudReqOps): string {
71         return (reqOps && reqOps.anonymous) ?
72             'ANONYMOUS' : this.auth.token();
73     }
74
75     connect(): Promise<PcrudContext> {
76         this.log('connect');
77         return new Promise( (resolve, reject) => {
78             this.session.connect({
79                 onconnect : () => { resolve(this); }
80             });
81         });
82     }
83
84     disconnect(): void {
85         this.log('disconnect');
86         this.session.disconnect();
87     }
88
89     retrieve(fmClass: string, pkey: Number | string,
90             pcrudOps?: any, reqOps?: PcrudReqOps): Observable<PcrudResponse> {
91         reqOps = reqOps || {};
92         this.authoritative = reqOps.authoritative || false;
93         return this.dispatch(
94             `open-ils.pcrud.retrieve.${fmClass}`,
95              [this.token(reqOps), pkey, pcrudOps]);
96     }
97
98     retrieveAll(fmClass: string, pcrudOps?: any,
99             reqOps?: PcrudReqOps): Observable<PcrudResponse> {
100         const search = {};
101         search[this.idl.classes[fmClass].pkey] = {'!=' : null};
102         return this.search(fmClass, search, pcrudOps, reqOps);
103     }
104
105     search(fmClass: string, search: any,
106             pcrudOps?: any, reqOps?: PcrudReqOps): Observable<PcrudResponse> {
107         reqOps = reqOps || {};
108         this.authoritative = reqOps.authoritative || false;
109
110         const returnType = reqOps.idlist ? 'id_list' : 'search';
111         let method = `open-ils.pcrud.${returnType}.${fmClass}`;
112
113         if (reqOps.atomic) { method += '.atomic'; }
114
115         return this.dispatch(method, [this.token(reqOps), search, pcrudOps]);
116     }
117
118     create(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
119         return this.cud('create', list);
120     }
121     update(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
122         return this.cud('update', list);
123     }
124     remove(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
125         return this.cud('delete', list);
126     }
127     autoApply(list: IdlObject | IdlObject[]): Observable<PcrudResponse> { // RENAMED
128         return this.cud('auto',   list);
129     }
130
131     xactClose(): Observable<PcrudResponse> {
132         return this.sendRequest(
133             'open-ils.pcrud.transaction.' + this.xactCloseMode,
134             [this.token()]
135         );
136     }
137
138     xactBegin(): Observable<PcrudResponse> {
139         return this.sendRequest(
140             'open-ils.pcrud.transaction.begin', [this.token()]
141         );
142     }
143
144     private dispatch(method: string, params: any[]): Observable<PcrudResponse> {
145         if (this.authoritative) {
146             return this.wrapXact(() => {
147                 return this.sendRequest(method, params);
148             });
149         } else {
150             return this.sendRequest(method, params);
151         }
152     }
153
154
155     // => connect
156     // => xact_begin
157     // => action
158     // => xact_close(commit/rollback)
159     // => disconnect
160     wrapXact(mainFunc: () => Observable<PcrudResponse>): Observable<PcrudResponse> {
161         return Observable.create(observer => {
162
163             // 1. connect
164             this.connect()
165
166             // 2. start the transaction
167             .then(() => this.xactBegin().toPromise())
168
169             // 3. execute the main body
170             .then(() => {
171
172                 mainFunc().subscribe(
173                     res => observer.next(res),
174                     err => observer.error(err),
175                     ()  => {
176                         this.xactClose().toPromise().then(() => {
177                             // 5. disconnect
178                             this.disconnect();
179                             // 6. all done
180                             observer.complete();
181                         });
182                     }
183                 );
184             });
185         });
186     }
187
188     private sendRequest(method: string,
189             params: any[]): Observable<PcrudResponse> {
190
191         // this.log(`sendRequest(${method})`);
192
193         return this.net.requestCompiled(
194             new NetRequest(
195                 'open-ils.pcrud', method, params, this.session)
196         );
197     }
198
199     private cud(action: string,
200         list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
201         this.cudList = [].concat(list); // value or array
202
203         this.log(`CUD(): ${action}`);
204
205         this.cudIdx = 0;
206         this.cudAction = action;
207         this.xactCloseMode = 'commit';
208
209         return this.wrapXact(() => {
210             return Observable.create(observer => {
211                 this.cudObserver = observer;
212                 this.nextCudRequest();
213             });
214         });
215     }
216
217     /**
218      * Loops through the list of objects to update and sends
219      * them one at a time to the server for processing.  Once
220      * all are done, the cudObserver is resolved.
221      */
222     nextCudRequest(): void {
223         if (this.cudIdx >= this.cudList.length) {
224             this.cudObserver.complete();
225             return;
226         }
227
228         let action = this.cudAction;
229         const fmObj = this.cudList[this.cudIdx++];
230
231         if (action === 'auto') {
232             if (fmObj.ischanged()) { action = 'update'; }
233             if (fmObj.isnew())     { action = 'create'; }
234             if (fmObj.isdeleted()) { action = 'delete'; }
235
236             if (action === 'auto') {
237                 // object does not need updating; move along
238                 this.nextCudRequest();
239             }
240         }
241
242         this.sendRequest(
243             `open-ils.pcrud.${action}.${fmObj.classname}`,
244             [this.token(), fmObj]
245         ).subscribe(
246             res => this.cudObserver.next(res),
247             err => this.cudObserver.error(err),
248             ()  => this.nextCudRequest()
249         );
250     }
251 }
252
253 @Injectable({providedIn: 'root'})
254 export class PcrudService {
255
256     constructor(
257         private idl: IdlService,
258         private net: NetService,
259         private auth: AuthService
260     ) {}
261
262     // Pass-thru functions for one-off PCRUD calls
263
264     connect(): Promise<PcrudContext> {
265         return this.newContext().connect();
266     }
267
268     newContext(): PcrudContext {
269         return new PcrudContext(this.idl, this.net, this.auth);
270     }
271
272     retrieve(fmClass: string, pkey: Number | string,
273         pcrudOps?: any, reqOps?: PcrudReqOps): Observable<PcrudResponse> {
274         return this.newContext().retrieve(fmClass, pkey, pcrudOps, reqOps);
275     }
276
277     retrieveAll(fmClass: string, pcrudOps?: any,
278         reqOps?: PcrudReqOps): Observable<PcrudResponse> {
279         return this.newContext().retrieveAll(fmClass, pcrudOps, reqOps);
280     }
281
282     search(fmClass: string, search: any,
283         pcrudOps?: any, reqOps?: PcrudReqOps): Observable<PcrudResponse> {
284         return this.newContext().search(fmClass, search, pcrudOps, reqOps);
285     }
286
287     create(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
288         return this.newContext().create(list);
289     }
290
291     update(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
292         return this.newContext().update(list);
293     }
294
295     remove(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
296         return this.newContext().remove(list);
297     }
298
299     autoApply(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
300         return this.newContext().autoApply(list);
301     }
302 }
303
304