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';
7 // Externally defined. Used here for debugging.
8 declare var js2JSON: (jsThing: any) => string;
9 declare var OpenSRF: any; // creating sessions
11 interface PcrudReqOps {
12 authoritative?: boolean;
18 // For for documentation purposes.
19 type PcrudResponse = any;
21 export class PcrudContext {
23 static verboseLogging = true; //
24 static identGenerator = 0; // for debug logging
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[];
34 private idl: IdlService;
35 private net: NetService;
36 private auth: AuthService;
38 // Tracks nested CUD actions
39 cudObserver: Observer<PcrudResponse>;
41 session: any; // OpenSRF.ClientSession
43 constructor( // passed in by parent service -- not injected
51 this.xactCloseMode = 'rollback';
52 this.ident = PcrudContext.identGenerator++;
53 this.session = new OpenSRF.ClientSession('open-ils.pcrud');
57 return '[PCRUDContext ' + this.ident + ']';
60 log(msg: string): void {
61 if (PcrudContext.verboseLogging) {
62 console.debug(this + ': ' + msg);
66 err(msg: string): void {
67 console.error(this + ': ' + msg);
70 token(reqOps?: PcrudReqOps): string {
71 return (reqOps && reqOps.anonymous) ?
72 'ANONYMOUS' : this.auth.token();
75 connect(): Promise<PcrudContext> {
77 return new Promise( (resolve, reject) => {
78 this.session.connect({
79 onconnect : () => { resolve(this); }
85 this.log('disconnect');
86 this.session.disconnect();
89 retrieve(fmClass: string, pkey: Number | string,
90 pcrudOps?: any, reqOps?: PcrudReqOps): Observable<PcrudResponse> {
91 reqOps = reqOps || {};
92 this.authoritative = reqOps.authoritative || false;
94 `open-ils.pcrud.retrieve.${fmClass}`,
95 [this.token(reqOps), pkey, pcrudOps]);
98 retrieveAll(fmClass: string, pcrudOps?: any,
99 reqOps?: PcrudReqOps): Observable<PcrudResponse> {
101 search[this.idl.classes[fmClass].pkey] = {'!=' : null};
102 return this.search(fmClass, search, pcrudOps, reqOps);
105 search(fmClass: string, search: any,
106 pcrudOps?: any, reqOps?: PcrudReqOps): Observable<PcrudResponse> {
107 reqOps = reqOps || {};
108 this.authoritative = reqOps.authoritative || false;
110 const returnType = reqOps.idlist ? 'id_list' : 'search';
111 let method = `open-ils.pcrud.${returnType}.${fmClass}`;
113 if (reqOps.atomic) { method += '.atomic'; }
115 return this.dispatch(method, [this.token(reqOps), search, pcrudOps]);
118 create(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
119 return this.cud('create', list);
121 update(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
122 return this.cud('update', list);
124 remove(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
125 return this.cud('delete', list);
127 autoApply(list: IdlObject | IdlObject[]): Observable<PcrudResponse> { // RENAMED
128 return this.cud('auto', list);
131 xactClose(): Observable<PcrudResponse> {
132 return this.sendRequest(
133 'open-ils.pcrud.transaction.' + this.xactCloseMode,
138 xactBegin(): Observable<PcrudResponse> {
139 return this.sendRequest(
140 'open-ils.pcrud.transaction.begin', [this.token()]
144 private dispatch(method: string, params: any[]): Observable<PcrudResponse> {
145 if (this.authoritative) {
146 return this.wrapXact(() => {
147 return this.sendRequest(method, params);
150 return this.sendRequest(method, params);
158 // => xact_close(commit/rollback)
160 wrapXact(mainFunc: () => Observable<PcrudResponse>): Observable<PcrudResponse> {
161 return Observable.create(observer => {
166 // 2. start the transaction
167 .then(() => this.xactBegin().toPromise())
169 // 3. execute the main body
172 mainFunc().subscribe(
173 res => observer.next(res),
174 err => observer.error(err),
176 this.xactClose().toPromise().then(() => {
188 private sendRequest(method: string,
189 params: any[]): Observable<PcrudResponse> {
191 // this.log(`sendRequest(${method})`);
193 return this.net.requestCompiled(
195 'open-ils.pcrud', method, params, this.session)
199 private cud(action: string,
200 list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
201 this.cudList = [].concat(list); // value or array
203 this.log(`CUD(): ${action}`);
206 this.cudAction = action;
207 this.xactCloseMode = 'commit';
209 return this.wrapXact(() => {
210 return Observable.create(observer => {
211 this.cudObserver = observer;
212 this.nextCudRequest();
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.
222 nextCudRequest(): void {
223 if (this.cudIdx >= this.cudList.length) {
224 this.cudObserver.complete();
228 let action = this.cudAction;
229 const fmObj = this.cudList[this.cudIdx++];
231 if (action === 'auto') {
232 if (fmObj.ischanged()) { action = 'update'; }
233 if (fmObj.isnew()) { action = 'create'; }
234 if (fmObj.isdeleted()) { action = 'delete'; }
236 if (action === 'auto') {
237 // object does not need updating; move along
238 this.nextCudRequest();
243 `open-ils.pcrud.${action}.${fmObj.classname}`,
244 [this.token(), fmObj]
246 res => this.cudObserver.next(res),
247 err => this.cudObserver.error(err),
248 () => this.nextCudRequest()
253 @Injectable({providedIn: 'root'})
254 export class PcrudService {
257 private idl: IdlService,
258 private net: NetService,
259 private auth: AuthService
262 // Pass-thru functions for one-off PCRUD calls
264 connect(): Promise<PcrudContext> {
265 return this.newContext().connect();
268 newContext(): PcrudContext {
269 return new PcrudContext(this.idl, this.net, this.auth);
272 retrieve(fmClass: string, pkey: Number | string,
273 pcrudOps?: any, reqOps?: PcrudReqOps): Observable<PcrudResponse> {
274 return this.newContext().retrieve(fmClass, pkey, pcrudOps, reqOps);
277 retrieveAll(fmClass: string, pcrudOps?: any,
278 reqOps?: PcrudReqOps): Observable<PcrudResponse> {
279 return this.newContext().retrieveAll(fmClass, pcrudOps, reqOps);
282 search(fmClass: string, search: any,
283 pcrudOps?: any, reqOps?: PcrudReqOps): Observable<PcrudResponse> {
284 return this.newContext().search(fmClass, search, pcrudOps, reqOps);
287 create(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
288 return this.newContext().create(list);
291 update(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
292 return this.newContext().update(list);
295 remove(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
296 return this.newContext().remove(list);
299 autoApply(list: IdlObject | IdlObject[]): Observable<PcrudResponse> {
300 return this.newContext().autoApply(list);