1 import {Injectable} from '@angular/core';
2 import {Observable} from 'rxjs/Observable';
3 import {Observer} from 'rxjs/Observer';
4 import {IdlService, IdlObject} from './idl.service';
5 import {NetService, NetRequest} from './net.service';
6 import {AuthService} from './auth.service';
8 // Externally defined. Used here for debugging.
9 declare var js2JSON: (jsThing: any) => string;
10 declare var OpenSRF: any; // creating sessions
12 interface PcrudReqOps {
13 authoritative?: boolean;
19 // For for documentation purposes.
20 type PcrudResponse = any;
22 export class PcrudContext {
24 static verboseLogging = true; //
25 static identGenerator = 0; // for debug logging
27 private ident: number;
28 private authoritative: boolean;
29 private xactCloseMode: string;
30 private cudIdx: number;
31 private cudAction: string;
32 private cudLast: PcrudResponse;
33 private cudList: IdlObject[];
35 private idl: IdlService;
36 private net: NetService;
37 private auth: AuthService;
39 // Tracks nested CUD actions
40 cudObserver: Observer<PcrudResponse>;
42 session: any; // OpenSRF.ClientSession
44 constructor( // passed in by parent service -- not injected
52 this.xactCloseMode = 'rollback';
53 this.ident = PcrudContext.identGenerator++;
54 this.session = new OpenSRF.ClientSession('open-ils.pcrud');
58 return '[PCRUDContext ' + this.ident + ']';
61 log(msg: string): void {
62 if (PcrudContext.verboseLogging) {
63 console.debug(this + ': ' + msg);
67 err(msg: string): void {
68 console.error(this + ': ' + msg);
71 token(reqOps?: PcrudReqOps): string {
72 return (reqOps && reqOps.anonymous) ?
73 'ANONYMOUS' : this.auth.token();
76 connect(): Promise<PcrudContext> {
78 return new Promise( (resolve, reject) => {
79 this.session.connect({
80 onconnect : () => { resolve(this); }
86 this.log('disconnect');
87 this.session.disconnect();
90 retrieve(fmClass: string, pkey: Number | string,
91 pcrudOps?: any, reqOps?: PcrudReqOps): Observable<PcrudResponse> {
92 reqOps = reqOps || {};
93 this.authoritative = reqOps.authoritative || false;
95 `open-ils.pcrud.retrieve.${fmClass}`,
96 [this.token(reqOps), pkey, pcrudOps]);
99 retrieveAll(fmClass: string, pcrudOps?: any,
100 reqOps?: PcrudReqOps): Observable<PcrudResponse> {
102 search[this.idl.classes[fmClass].pkey] = {'!=' : null};
103 return this.search(fmClass, search, pcrudOps, reqOps);
106 search(fmClass: string, search: any,
107 pcrudOps?: any, reqOps?: PcrudReqOps): Observable<PcrudResponse> {
108 reqOps = reqOps || {};
109 this.authoritative = reqOps.authoritative || false;
111 const returnType = reqOps.idlist ? 'id_list' : 'search';
112 let method = `open-ils.pcrud.${returnType}.${fmClass}`;
114 if (reqOps.atomic) { method += '.atomic'; }
116 return this.dispatch(method, [this.token(reqOps), search, pcrudOps]);
119 create(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
120 return this.cud('create', list);
122 update(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
123 return this.cud('update', list);
125 remove(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
126 return this.cud('delete', list);
128 autoApply(list: IdlObject | IdlObject[]): Observable<PcrudResponse> { // RENAMED
129 return this.cud('auto', list);
132 xactClose(): Observable<PcrudResponse> {
133 return this.sendRequest(
134 'open-ils.pcrud.transaction.' + this.xactCloseMode,
139 xactBegin(): Observable<PcrudResponse> {
140 return this.sendRequest(
141 'open-ils.pcrud.transaction.begin', [this.token()]
145 private dispatch(method: string, params: any[]): Observable<PcrudResponse> {
146 if (this.authoritative) {
147 return this.wrapXact(() => {
148 return this.sendRequest(method, params);
151 return this.sendRequest(method, params);
159 // => xact_close(commit/rollback)
161 wrapXact(mainFunc: () => Observable<PcrudResponse>): Observable<PcrudResponse> {
162 return Observable.create(observer => {
167 // 2. start the transaction
168 .then(() => this.xactBegin().toPromise())
170 // 3. execute the main body
173 mainFunc().subscribe(
174 res => observer.next(res),
175 err => observer.error(err),
177 this.xactClose().toPromise().then(() => {
189 private sendRequest(method: string,
190 params: any[]): Observable<PcrudResponse> {
192 // this.log(`sendRequest(${method})`);
194 return this.net.requestCompiled(
196 'open-ils.pcrud', method, params, this.session)
200 private cud(action: string,
201 list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
202 this.cudList = [].concat(list); // value or array
204 this.log(`CUD(): ${action}`);
207 this.cudAction = action;
208 this.xactCloseMode = 'commit';
210 return this.wrapXact(() => {
211 return Observable.create(observer => {
212 this.cudObserver = observer;
213 this.nextCudRequest();
219 * Loops through the list of objects to update and sends
220 * them one at a time to the server for processing. Once
221 * all are done, the cudObserver is resolved.
223 nextCudRequest(): void {
224 if (this.cudIdx >= this.cudList.length) {
225 this.cudObserver.complete();
229 let action = this.cudAction;
230 const fmObj = this.cudList[this.cudIdx++];
232 if (action === 'auto') {
233 if (fmObj.ischanged()) { action = 'update'; }
234 if (fmObj.isnew()) { action = 'create'; }
235 if (fmObj.isdeleted()) { action = 'delete'; }
237 if (action === 'auto') {
238 // object does not need updating; move along
239 this.nextCudRequest();
244 `open-ils.pcrud.${action}.${fmObj.classname}`,
245 [this.token(), fmObj]
247 res => this.cudObserver.next(res),
248 err => this.cudObserver.error(err),
249 () => this.nextCudRequest()
254 @Injectable({providedIn: 'root'})
255 export class PcrudService {
258 private idl: IdlService,
259 private net: NetService,
260 private auth: AuthService
263 // Pass-thru functions for one-off PCRUD calls
265 connect(): Promise<PcrudContext> {
266 return this.newContext().connect();
269 newContext(): PcrudContext {
270 return new PcrudContext(this.idl, this.net, this.auth);
273 retrieve(fmClass: string, pkey: Number | string,
274 pcrudOps?: any, reqOps?: PcrudReqOps): Observable<PcrudResponse> {
275 return this.newContext().retrieve(fmClass, pkey, pcrudOps, reqOps);
278 retrieveAll(fmClass: string, pcrudOps?: any,
279 reqOps?: PcrudReqOps): Observable<PcrudResponse> {
280 return this.newContext().retrieveAll(fmClass, pcrudOps, reqOps);
283 search(fmClass: string, search: any,
284 pcrudOps?: any, reqOps?: PcrudReqOps): Observable<PcrudResponse> {
285 return this.newContext().search(fmClass, search, pcrudOps, reqOps);
288 create(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
289 return this.newContext().create(list);
292 update(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
293 return this.newContext().update(list);
296 remove(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
297 return this.newContext().remove(list);
300 autoApply(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
301 return this.newContext().autoApply(list);