LP#626157 Ang2 experiments
authorBill Erickson <berickxx@gmail.com>
Thu, 30 Nov 2017 12:59:21 +0000 (07:59 -0500)
committerBill Erickson <berickxx@gmail.com>
Mon, 11 Dec 2017 17:39:51 +0000 (12:39 -0500)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
22 files changed:
Open-ILS/webby-src/src/app/base.module.ts
Open-ILS/webby-src/src/app/core/auth.service.ts [deleted file]
Open-ILS/webby-src/src/app/core/auth.ts [new file with mode: 0644]
Open-ILS/webby-src/src/app/core/event.service.ts [deleted file]
Open-ILS/webby-src/src/app/core/event.ts [new file with mode: 0644]
Open-ILS/webby-src/src/app/core/idl.service.ts [deleted file]
Open-ILS/webby-src/src/app/core/idl.ts [new file with mode: 0644]
Open-ILS/webby-src/src/app/core/net.service.ts
Open-ILS/webby-src/src/app/core/org.service.ts [deleted file]
Open-ILS/webby-src/src/app/core/org.ts [new file with mode: 0644]
Open-ILS/webby-src/src/app/core/pcrud.service.ts [deleted file]
Open-ILS/webby-src/src/app/core/pcrud.ts [new file with mode: 0644]
Open-ILS/webby-src/src/app/core/store.service.ts [deleted file]
Open-ILS/webby-src/src/app/core/store.ts [new file with mode: 0644]
Open-ILS/webby-src/src/app/resolver.service.ts
Open-ILS/webby-src/src/app/staff/admin/workstation/workstations.component.ts
Open-ILS/webby-src/src/app/staff/circ/patron/bcsearch/bcsearch.component.ts
Open-ILS/webby-src/src/app/staff/login.component.ts
Open-ILS/webby-src/src/app/staff/resolver.service.ts
Open-ILS/webby-src/src/app/staff/share/org-select.component.html
Open-ILS/webby-src/src/app/staff/share/org-select.component.ts
Open-ILS/webby-src/src/app/staff/staff.component.ts

index 0bd2a07..7f2df7c 100644 (file)
@@ -14,13 +14,13 @@ import {EgBaseRoutingModule} from './routing.module';
 import {WelcomeComponent} from './welcome.component';
 
 // Import and 'provide' globally required services.
-import {EgEventService} from '@eg/core/event.service';
-import {EgStoreService} from '@eg/core/store.service';
-import {EgIdlService} from '@eg/core/idl.service';
+import {EgEventService} from '@eg/core/event';
+import {EgStoreService} from '@eg/core/store';
+import {EgIdlService} from '@eg/core/idl';
 import {EgNetService} from '@eg/core/net.service';
-import {EgAuthService} from '@eg/core/auth.service';
-import {EgPcrudService} from '@eg/core/pcrud.service';
-import {EgOrgService} from '@eg/core/org.service';
+import {EgAuthService} from '@eg/core/auth';
+import {EgPcrudService} from '@eg/core/pcrud';
+import {EgOrgService} from '@eg/core/org';
 
 @NgModule({
   declarations: [
diff --git a/Open-ILS/webby-src/src/app/core/auth.service.ts b/Open-ILS/webby-src/src/app/core/auth.service.ts
deleted file mode 100644 (file)
index 4a956a5..0000000
+++ /dev/null
@@ -1,239 +0,0 @@
-/**
- * 
- */
-import { Injectable, EventEmitter } from '@angular/core';
-import { Observable } from 'rxjs/Rx';
-import { EgNetService } from './net.service';
-import { EgEventService, EgEvent } from './event.service';
-import { EgIdlService, EgIdlObject } from './idl.service';
-import { EgStoreService } from './store.service';
-
-// Models a login instance.
-class EgAuthUser {
-    user:        EgIdlObject;
-    workstation: string; // workstation name
-    token:       string;
-    authtime:    number;
-
-    constructor(token: string, authtime: number, workstation?: string) {
-        this.token = token;
-        this.workstation = workstation;
-        this.authtime = authtime;
-    }
-}
-
-// Params required for calling the login() method.
-interface EgAuthLoginArgs {
-    username: string,
-    password: string,
-    type: string,
-    workstation?: string
-}
-
-export enum EgAuthWsState {
-    PENDING,
-    NOT_USED,
-    NOT_FOUND_SERVER,
-    NOT_FOUND_LOCAL,
-    VALID
-};
-
-@Injectable()
-export class EgAuthService {
-
-    private activeUser: EgAuthUser;
-
-    // opChangeUser refers to the user that has been superseded during
-    // an op-change event.  This use will become the activeUser once
-    // again, when the op-change cycle has completed.
-    private opChangeUser: EgAuthUser;
-
-    workstationState: EgAuthWsState = EgAuthWsState.PENDING;
-
-    redirectUrl: string;
-
-    constructor(
-        private egEvt: EgEventService,
-        private egNet: EgNetService,
-        private egStore: EgStoreService
-    ) {}
-
-    // - Accessor functions alway refer to the active user.
-
-    user(): EgIdlObject { 
-        return this.activeUser.user 
-    };
-
-    // Workstation name.
-    workstation(): string { 
-        return this.activeUser.workstation;
-    };
-
-    token(): string { 
-        return this.activeUser ? this.activeUser.token : null;
-    };
-
-    authtime(): Number { 
-        return this.activeUser.authtime 
-    };
-
-    // NOTE: EgNetService emits an event if the auth session has expired.
-    testAuthToken(): Promise<any> {
-
-        this.activeUser = new EgAuthUser(
-            this.egStore.getLoginSessionItem('eg.auth.token'),
-            this.egStore.getLoginSessionItem('eg.auth.time')
-        );
-
-        if (!this.token()) return Promise.reject('no authtoken');
-
-        return new Promise<any>( (resolve, reject) => {
-            this.egNet.request(
-                'open-ils.auth',
-                'open-ils.auth.session.retrieve', this.token()
-            ).subscribe(
-                user => {
-                    // EgNetService interceps NO_SESSION events.
-                    // We can only get here if the session is valid.
-                    this.activeUser.user = user;
-                    this.sessionPoll();
-                    resolve();
-                },
-                err => { reject(); }
-            );
-        });
-    }
-
-    checkWorkstation(): void {
-        // TODO:
-        // Emits event on invalid workstation.
-    }
-
-    login(args: EgAuthLoginArgs, isOpChange?: boolean): Promise<void> {
-
-        return new Promise<void>((resolve, reject) => {
-            this.egNet.request('open-ils.auth', 'open-ils.auth.login', args)
-            .subscribe(res => {
-                this.handleLoginResponse(args, this.egEvt.parse(res), isOpChange)
-                .then(
-                    ok => resolve(ok),
-                    notOk => reject(notOk)
-                );
-            });
-        });
-    }
-
-    handleLoginResponse(
-        args: EgAuthLoginArgs, evt: EgEvent, isOpChange: boolean): Promise<void> {
-
-        switch (evt.textcode) {
-            case 'SUCCESS':
-                this.handleLoginOk(args, evt, isOpChange);
-                return Promise.resolve();
-
-            case 'WORKSTATION_NOT_FOUND':
-                console.error(`No such workstation "${args.workstation}"`);
-                this.workstationState = EgAuthWsState.NOT_FOUND_SERVER;
-                delete args.workstation;
-                return this.login(args, isOpChange);
-
-            default:
-                console.error(`Login returned unexpected event: ${evt}`);
-                return Promise.reject('login failed');
-        }
-    }
-
-    // Stash the login data
-    handleLoginOk(args: EgAuthLoginArgs, evt: EgEvent, isOpChange: boolean): void {
-
-        if (isOpChange) {
-            this.egStore.setLoginSessionItem('eg.auth.token.oc', this.token());
-            this.egStore.setLoginSessionItem('eg.auth.time.oc', this.authtime());
-            this.opChangeUser = this.activeUser;
-        }
-
-        this.activeUser = new EgAuthUser(
-            evt.payload.authtoken,
-            evt.payload.authtime,
-            args.workstation
-        );
-
-        this.egStore.setLoginSessionItem('eg.auth.token', this.token());
-        this.egStore.setLoginSessionItem('eg.auth.time', this.authtime());
-    }
-
-    undoOpChange(): Promise<any> {
-        if (this.opChangeUser) {
-            this.deleteSession();
-            this.activeUser = this.opChangeUser;
-            this.opChangeUser = null;
-            this.egStore.removeLoginSessionItem('eg.auth.token.oc');                
-            this.egStore.removeLoginSessionItem('eg.auth.time.oc');                 
-            this.egStore.setLoginSessionItem('eg.auth.token', this.token());
-            this.egStore.setLoginSessionItem('eg.auth.time', this.authtime());
-        }
-        return this.testAuthToken();
-    }
-
-    sessionPoll(): void {
-        // TODO
-    }
-
-    // Resolves if login workstation matches a workstation known to this 
-    // browser instance.
-    verifyWorkstation(): Promise<void> {
-        return new Promise((resolve, reject) => {
-
-            if (!this.user()) {
-                this.workstationState = EgAuthWsState.PENDING;
-                reject();
-                return;
-            }
-
-            if (!this.user().wsid()) {
-                this.workstationState = EgAuthWsState.NOT_USED;
-                reject();
-                return;
-            }
-
-            this.egStore.getItem('eg.workstation.all')
-            .then(workstations => {
-                if (!workstations) workstations = [];
-
-                let ws = workstations.filter(
-                    w => {return w.id == this.user().wsid()})[0];
-
-                if (ws) {
-                    this.activeUser.workstation = ws.name;
-                    this.workstationState = EgAuthWsState.VALID;
-                    resolve();
-                } else {
-                    this.workstationState = EgAuthWsState.NOT_FOUND_LOCAL;
-                    reject();
-                }
-            });
-        });
-    }
-
-    deleteSession(): void {
-        if (this.token()) {
-            this.egNet.request(
-                'open-ils.auth',
-                'open-ils.auth.session.delete', this.token())
-            .subscribe(x => console.log('logged out'))
-        }
-    }
-
-    logout(broadcast?: boolean) {
-
-        if (broadcast) {
-            // TODO
-            //this.authChannel.postMessage({action : 'logout'});
-        }
-
-        this.deleteSession();
-        this.egStore.clearLoginSessionItems();                                  
-        this.activeUser = null;
-        this.opChangeUser = null;
-    }
-}
diff --git a/Open-ILS/webby-src/src/app/core/auth.ts b/Open-ILS/webby-src/src/app/core/auth.ts
new file mode 100644 (file)
index 0000000..6d94d80
--- /dev/null
@@ -0,0 +1,239 @@
+/**
+ * 
+ */
+import { Injectable, EventEmitter } from '@angular/core';
+import { Observable } from 'rxjs/Rx';
+import { EgNetService } from './net.service';
+import { EgEventService, EgEvent } from './event';
+import { EgIdlService, EgIdlObject } from './idl';
+import { EgStoreService } from './store';
+
+// Models a login instance.
+class EgAuthUser {
+    user:        EgIdlObject;
+    workstation: string; // workstation name
+    token:       string;
+    authtime:    number;
+
+    constructor(token: string, authtime: number, workstation?: string) {
+        this.token = token;
+        this.workstation = workstation;
+        this.authtime = authtime;
+    }
+}
+
+// Params required for calling the login() method.
+interface EgAuthLoginArgs {
+    username: string,
+    password: string,
+    type: string,
+    workstation?: string
+}
+
+export enum EgAuthWsState {
+    PENDING,
+    NOT_USED,
+    NOT_FOUND_SERVER,
+    NOT_FOUND_LOCAL,
+    VALID
+};
+
+@Injectable()
+export class EgAuthService {
+
+    private activeUser: EgAuthUser;
+
+    // opChangeUser refers to the user that has been superseded during
+    // an op-change event.  This use will become the activeUser once
+    // again, when the op-change cycle has completed.
+    private opChangeUser: EgAuthUser;
+
+    workstationState: EgAuthWsState = EgAuthWsState.PENDING;
+
+    redirectUrl: string;
+
+    constructor(
+        private egEvt: EgEventService,
+        private egNet: EgNetService,
+        private egStore: EgStoreService
+    ) {}
+
+    // - Accessor functions alway refer to the active user.
+
+    user(): EgIdlObject { 
+        return this.activeUser.user 
+    };
+
+    // Workstation name.
+    workstation(): string { 
+        return this.activeUser.workstation;
+    };
+
+    token(): string { 
+        return this.activeUser ? this.activeUser.token : null;
+    };
+
+    authtime(): Number { 
+        return this.activeUser.authtime 
+    };
+
+    // NOTE: EgNetService emits an event if the auth session has expired.
+    testAuthToken(): Promise<any> {
+
+        this.activeUser = new EgAuthUser(
+            this.egStore.getLoginSessionItem('eg.auth.token'),
+            this.egStore.getLoginSessionItem('eg.auth.time')
+        );
+
+        if (!this.token()) return Promise.reject('no authtoken');
+
+        return new Promise<any>( (resolve, reject) => {
+            this.egNet.request(
+                'open-ils.auth',
+                'open-ils.auth.session.retrieve', this.token()
+            ).subscribe(
+                user => {
+                    // EgNetService interceps NO_SESSION events.
+                    // We can only get here if the session is valid.
+                    this.activeUser.user = user;
+                    this.sessionPoll();
+                    resolve();
+                },
+                err => { reject(); }
+            );
+        });
+    }
+
+    checkWorkstation(): void {
+        // TODO:
+        // Emits event on invalid workstation.
+    }
+
+    login(args: EgAuthLoginArgs, isOpChange?: boolean): Promise<void> {
+
+        return new Promise<void>((resolve, reject) => {
+            this.egNet.request('open-ils.auth', 'open-ils.auth.login', args)
+            .subscribe(res => {
+                this.handleLoginResponse(args, this.egEvt.parse(res), isOpChange)
+                .then(
+                    ok => resolve(ok),
+                    notOk => reject(notOk)
+                );
+            });
+        });
+    }
+
+    handleLoginResponse(
+        args: EgAuthLoginArgs, evt: EgEvent, isOpChange: boolean): Promise<void> {
+
+        switch (evt.textcode) {
+            case 'SUCCESS':
+                this.handleLoginOk(args, evt, isOpChange);
+                return Promise.resolve();
+
+            case 'WORKSTATION_NOT_FOUND':
+                console.error(`No such workstation "${args.workstation}"`);
+                this.workstationState = EgAuthWsState.NOT_FOUND_SERVER;
+                delete args.workstation;
+                return this.login(args, isOpChange);
+
+            default:
+                console.error(`Login returned unexpected event: ${evt}`);
+                return Promise.reject('login failed');
+        }
+    }
+
+    // Stash the login data
+    handleLoginOk(args: EgAuthLoginArgs, evt: EgEvent, isOpChange: boolean): void {
+
+        if (isOpChange) {
+            this.egStore.setLoginSessionItem('eg.auth.token.oc', this.token());
+            this.egStore.setLoginSessionItem('eg.auth.time.oc', this.authtime());
+            this.opChangeUser = this.activeUser;
+        }
+
+        this.activeUser = new EgAuthUser(
+            evt.payload.authtoken,
+            evt.payload.authtime,
+            args.workstation
+        );
+
+        this.egStore.setLoginSessionItem('eg.auth.token', this.token());
+        this.egStore.setLoginSessionItem('eg.auth.time', this.authtime());
+    }
+
+    undoOpChange(): Promise<any> {
+        if (this.opChangeUser) {
+            this.deleteSession();
+            this.activeUser = this.opChangeUser;
+            this.opChangeUser = null;
+            this.egStore.removeLoginSessionItem('eg.auth.token.oc');                
+            this.egStore.removeLoginSessionItem('eg.auth.time.oc');                 
+            this.egStore.setLoginSessionItem('eg.auth.token', this.token());
+            this.egStore.setLoginSessionItem('eg.auth.time', this.authtime());
+        }
+        return this.testAuthToken();
+    }
+
+    sessionPoll(): void {
+        // TODO
+    }
+
+    // Resolves if login workstation matches a workstation known to this 
+    // browser instance.
+    verifyWorkstation(): Promise<void> {
+        return new Promise((resolve, reject) => {
+
+            if (!this.user()) {
+                this.workstationState = EgAuthWsState.PENDING;
+                reject();
+                return;
+            }
+
+            if (!this.user().wsid()) {
+                this.workstationState = EgAuthWsState.NOT_USED;
+                reject();
+                return;
+            }
+
+            this.egStore.getItem('eg.workstation.all')
+            .then(workstations => {
+                if (!workstations) workstations = [];
+
+                let ws = workstations.filter(
+                    w => {return w.id == this.user().wsid()})[0];
+
+                if (ws) {
+                    this.activeUser.workstation = ws.name;
+                    this.workstationState = EgAuthWsState.VALID;
+                    resolve();
+                } else {
+                    this.workstationState = EgAuthWsState.NOT_FOUND_LOCAL;
+                    reject();
+                }
+            });
+        });
+    }
+
+    deleteSession(): void {
+        if (this.token()) {
+            this.egNet.request(
+                'open-ils.auth',
+                'open-ils.auth.session.delete', this.token())
+            .subscribe(x => console.log('logged out'))
+        }
+    }
+
+    logout(broadcast?: boolean) {
+
+        if (broadcast) {
+            // TODO
+            //this.authChannel.postMessage({action : 'logout'});
+        }
+
+        this.deleteSession();
+        this.egStore.clearLoginSessionItems();                                  
+        this.activeUser = null;
+        this.opChangeUser = null;
+    }
+}
diff --git a/Open-ILS/webby-src/src/app/core/event.service.ts b/Open-ILS/webby-src/src/app/core/event.service.ts
deleted file mode 100644 (file)
index 3f6afc7..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-import { Injectable } from '@angular/core';
-
-export class EgEvent {
-    code        : Number;
-    textcode    : String;
-    payload     : any;
-    desc        : String;
-    debug       : String;
-    note        : String;
-    servertime  : String;
-    ilsperm     : String;
-    ilspermloc  : Number;
-    success     : Boolean = false;
-
-    toString(): String {
-        let s = `Event: ${this.code}:${this.textcode} -> ${this.desc}`;
-        if (this.ilsperm)
-            s += `  ${this.ilsperm}@${this.ilspermloc}`;
-        if (this.note)
-            s += `\n${this.note}`;
-        return s;
-    }
-}
-
-@Injectable()
-export class EgEventService {
-
-    /**
-     * Returns an EgEvent if 'thing' is an event, null otherwise.
-     */
-    parse(thing: any): EgEvent {
-
-        // All events have a textcode
-        if (thing && typeof thing == 'object' && 'textcode' in thing) {
-
-            let evt = new EgEvent();
-
-            ['textcode','payload','desc','note','servertime','ilsperm']
-                .forEach(field => { evt[field] = thing[field]; });
-
-            evt.debug = thing.stacktrace;
-            evt.code = new Number(thing.code);
-            evt.ilspermloc = new Number(thing.ilspermloc);
-            evt.success = thing.textcode == 'SUCCESS';
-
-            return evt;
-        }
-
-        return null;
-    }
-}
-
-
diff --git a/Open-ILS/webby-src/src/app/core/event.ts b/Open-ILS/webby-src/src/app/core/event.ts
new file mode 100644 (file)
index 0000000..3f6afc7
--- /dev/null
@@ -0,0 +1,53 @@
+import { Injectable } from '@angular/core';
+
+export class EgEvent {
+    code        : Number;
+    textcode    : String;
+    payload     : any;
+    desc        : String;
+    debug       : String;
+    note        : String;
+    servertime  : String;
+    ilsperm     : String;
+    ilspermloc  : Number;
+    success     : Boolean = false;
+
+    toString(): String {
+        let s = `Event: ${this.code}:${this.textcode} -> ${this.desc}`;
+        if (this.ilsperm)
+            s += `  ${this.ilsperm}@${this.ilspermloc}`;
+        if (this.note)
+            s += `\n${this.note}`;
+        return s;
+    }
+}
+
+@Injectable()
+export class EgEventService {
+
+    /**
+     * Returns an EgEvent if 'thing' is an event, null otherwise.
+     */
+    parse(thing: any): EgEvent {
+
+        // All events have a textcode
+        if (thing && typeof thing == 'object' && 'textcode' in thing) {
+
+            let evt = new EgEvent();
+
+            ['textcode','payload','desc','note','servertime','ilsperm']
+                .forEach(field => { evt[field] = thing[field]; });
+
+            evt.debug = thing.stacktrace;
+            evt.code = new Number(thing.code);
+            evt.ilspermloc = new Number(thing.ilspermloc);
+            evt.success = thing.textcode == 'SUCCESS';
+
+            return evt;
+        }
+
+        return null;
+    }
+}
+
+
diff --git a/Open-ILS/webby-src/src/app/core/idl.service.ts b/Open-ILS/webby-src/src/app/core/idl.service.ts
deleted file mode 100644 (file)
index e503ba7..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-import { Injectable } from '@angular/core';
-
-// Added globally by /IDL2js
-declare var _preload_fieldmapper_IDL: Object;
-
-/**
- * NOTE: To achieve full type strictness and avoid compile warnings,
- * we would likely have to pre-compile the IDL down to a .ts file with all 
- * of the IDL class and field definitions.
- */
-
-/**
- * Every IDL object class implements this interface.
- */
-export interface EgIdlObject {
-    a: any[];
-    classname: String;
-    _isfieldmapper: Boolean;
-    // Dynamically appended functions from the IDL.
-    [fields: string]: any;
-}
-
-@Injectable()
-export class EgIdlService {
-
-    classes = {}; // IDL class metadata
-    constructors = {}; // IDL instance generators
-
-    /**
-     * Create a new IDL object instance.
-     */
-    create(cls: string, seed?:any[]): EgIdlObject {
-        if (this.constructors[cls])
-            return new this.constructors[cls](seed);
-        throw new Error(`No such IDL class ${cls}`);
-    }
-
-    parseIdl(): void {
-        let this_ = this;
-        this_.classes = _preload_fieldmapper_IDL;
-
-        /**
-         * Creates the class constructor and getter/setter
-         * methods for each IDL class.
-         */
-        let mkclass = (cls, fields) => {
-            this_.classes[cls].classname = cls;
-
-            // This dance lets us encode each IDL object with the
-            // EgIdlObject interface.  Useful for adding type restrictions
-            // where desired for functions, etc.
-            let generator:any = ((): EgIdlObject => {
-
-                var x:any = function(seed) {
-                    this.a = seed || [];
-                    this.classname = cls;
-                    this._isfieldmapper = true;
-                };
-
-                fields.forEach(function(field, idx) {
-                    x.prototype[field.name] = function(n) {
-                        if (arguments.length==1) this.a[idx] = n;
-                        return this.a[idx];
-                    }
-                });
-
-                return x;
-            });
-
-            this_.constructors[cls] = generator();
-
-            // global class constructors required for JSON_v1.js
-            // TODO: polluting the window namespace w/ every IDL class 
-            // is less than ideal.
-            window[cls] = this_.constructors[cls]; 
-        }
-
-        for (var cls in this_.classes) 
-            mkclass(cls, this_.classes[cls].fields);
-    };
-}
-
diff --git a/Open-ILS/webby-src/src/app/core/idl.ts b/Open-ILS/webby-src/src/app/core/idl.ts
new file mode 100644 (file)
index 0000000..e503ba7
--- /dev/null
@@ -0,0 +1,82 @@
+import { Injectable } from '@angular/core';
+
+// Added globally by /IDL2js
+declare var _preload_fieldmapper_IDL: Object;
+
+/**
+ * NOTE: To achieve full type strictness and avoid compile warnings,
+ * we would likely have to pre-compile the IDL down to a .ts file with all 
+ * of the IDL class and field definitions.
+ */
+
+/**
+ * Every IDL object class implements this interface.
+ */
+export interface EgIdlObject {
+    a: any[];
+    classname: String;
+    _isfieldmapper: Boolean;
+    // Dynamically appended functions from the IDL.
+    [fields: string]: any;
+}
+
+@Injectable()
+export class EgIdlService {
+
+    classes = {}; // IDL class metadata
+    constructors = {}; // IDL instance generators
+
+    /**
+     * Create a new IDL object instance.
+     */
+    create(cls: string, seed?:any[]): EgIdlObject {
+        if (this.constructors[cls])
+            return new this.constructors[cls](seed);
+        throw new Error(`No such IDL class ${cls}`);
+    }
+
+    parseIdl(): void {
+        let this_ = this;
+        this_.classes = _preload_fieldmapper_IDL;
+
+        /**
+         * Creates the class constructor and getter/setter
+         * methods for each IDL class.
+         */
+        let mkclass = (cls, fields) => {
+            this_.classes[cls].classname = cls;
+
+            // This dance lets us encode each IDL object with the
+            // EgIdlObject interface.  Useful for adding type restrictions
+            // where desired for functions, etc.
+            let generator:any = ((): EgIdlObject => {
+
+                var x:any = function(seed) {
+                    this.a = seed || [];
+                    this.classname = cls;
+                    this._isfieldmapper = true;
+                };
+
+                fields.forEach(function(field, idx) {
+                    x.prototype[field.name] = function(n) {
+                        if (arguments.length==1) this.a[idx] = n;
+                        return this.a[idx];
+                    }
+                });
+
+                return x;
+            });
+
+            this_.constructors[cls] = generator();
+
+            // global class constructors required for JSON_v1.js
+            // TODO: polluting the window namespace w/ every IDL class 
+            // is less than ideal.
+            window[cls] = this_.constructors[cls]; 
+        }
+
+        for (var cls in this_.classes) 
+            mkclass(cls, this_.classes[cls].fields);
+    };
+}
+
index b7b3648..0d98d17 100644 (file)
@@ -17,7 +17,7 @@
  */
 import { Injectable, EventEmitter } from '@angular/core';
 import { Observable, Observer } from 'rxjs/Rx';
-import { EgEventService, EgEvent } from './event.service';
+import { EgEventService, EgEvent } from './event';
 
 // Global vars from opensrf.js
 // These are availavble at runtime, but are not exported.
diff --git a/Open-ILS/webby-src/src/app/core/org.service.ts b/Open-ILS/webby-src/src/app/core/org.service.ts
deleted file mode 100644 (file)
index d641555..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-import {Injectable} from '@angular/core';
-import {Observable} from 'rxjs/Rx';
-import {EgIdlObject, EgIdlService} from './idl.service';
-import {EgPcrudService} from './pcrud.service';
-
-type EgOrgNodeOrId = number | EgIdlObject;
-
-@Injectable()
-export class EgOrgService {
-
-    private orgMap = {};
-    private orgList: EgIdlObject[] = [];
-    private orgTree: EgIdlObject; // root node + children
-
-    constructor(
-        private egPcrud: EgPcrudService
-    ) {}
-
-    get(nodeOrId: EgOrgNodeOrId): EgIdlObject {
-        if (typeof nodeOrId == 'object')
-            return nodeOrId;
-        return this.orgMap[nodeOrId];
-    };
-
-    list(): EgIdlObject[] {
-        return this.orgList;
-    };
-
-    tree(): EgIdlObject {
-        return this.orgTree;
-    }
-
-    // get the root OU
-    root(): EgIdlObject {
-        return this.orgList[0];
-    }
-
-    // list of org_unit objects or IDs for ancestors + me
-    ancestors(nodeOrId: EgOrgNodeOrId, asId: Boolean): EgIdlObject[] {
-        let node = this.get(nodeOrId);
-        if (!node) return [];
-        let nodes = [node];
-        while( (node = this.get(node.parent_ou())))
-            nodes.push(node);
-        if (asId) 
-            return nodes.map(function(n){return n.id()});
-        return nodes;
-    };
-
-    // tests that a node can have users
-    canHaveUsers(nodeOrId): Boolean {
-           return this
-            .get(nodeOrId)
-            .ou_type()
-            .can_have_users() == 't';
-    }
-
-    // tests that a node can have volumes
-    canHaveVolumes(nodeOrId): Boolean {
-        return this
-            .get(nodeOrId)
-            .ou_type()
-            .can_have_vols() == 't';
-    }
-
-    // list of org_unit objects  or IDs for me + descendants
-    descendants(nodeOrId: EgOrgNodeOrId, asId: Boolean): EgIdlObject[] {
-        let node = this.get(nodeOrId);
-        if (!node) return [];
-        let nodes = [];
-        function descend(n) {
-            nodes.push(n);
-            n.children().forEach(descend);
-        }
-        descend(node);
-        if (asId) 
-            return nodes.map(function(n){return n.id()});
-        return nodes;
-    }
-
-    // list of org_unit objects or IDs for ancestors + me + descendants
-    fullPath(nodeOrId: EgOrgNodeOrId, asId: Boolean): EgIdlObject[] {
-        let list = this.ancestors(nodeOrId, false).concat(
-          this.descendants(nodeOrId, false).slice(1));
-        if (asId) 
-            return list.map(function(n){return n.id()});
-        return list;
-    }
-
-    sortTree(sortField?: string, node?: EgIdlObject): void {
-        if (!sortField) sortField = 'shortname';
-        if (!node) node = this.orgTree;
-        node.children(
-            node.children.sort((a, b) => {
-                return a[sortField]() < b[sortField]() ? -1 : 1
-            })
-        );
-        node.children.forEach(n => this.sortTree(n));
-    }
-    
-    absorbTree(node?: EgIdlObject): void {
-        if (!node) {
-            node = this.orgTree;
-            this.orgMap = {};
-            this.orgList = [];
-        }
-        this.orgMap[node.id()] = node;
-        this.orgList.push(node);
-        node.children().forEach(c => this.absorbTree(c));
-    }
-
-    /**
-     * Grabs all of the org units from the server, chops them up into
-     * various shapes, then returns an "all done" promise.
-     */
-    fetchOrgs(): Promise<void> {
-        
-        console.log('fetching..');
-        return this.egPcrud.search('aou', {parent_ou : null},
-            {flesh : -1, flesh_fields : {aou : ['children', 'ou_type']}},
-            {anonymous : true}
-        ).toPromise().then(tree => {
-            // ingest tree, etc.
-            this.orgTree = tree;
-            this.absorbTree();
-            console.log('TREE FETCHED: ' + tree);
-        });
-    }
-
-    // NOTE: see ./org-settings.service for settings 
-    // TODO: ^--
-}
diff --git a/Open-ILS/webby-src/src/app/core/org.ts b/Open-ILS/webby-src/src/app/core/org.ts
new file mode 100644 (file)
index 0000000..460b1e3
--- /dev/null
@@ -0,0 +1,132 @@
+import {Injectable} from '@angular/core';
+import {Observable} from 'rxjs/Rx';
+import {EgIdlObject, EgIdlService} from './idl';
+import {EgPcrudService} from './pcrud';
+
+type EgOrgNodeOrId = number | EgIdlObject;
+
+@Injectable()
+export class EgOrgService {
+
+    private orgMap = {};
+    private orgList: EgIdlObject[] = [];
+    private orgTree: EgIdlObject; // root node + children
+
+    constructor(
+        private egPcrud: EgPcrudService
+    ) {}
+
+    get(nodeOrId: EgOrgNodeOrId): EgIdlObject {
+        if (typeof nodeOrId == 'object')
+            return nodeOrId;
+        return this.orgMap[nodeOrId];
+    };
+
+    list(): EgIdlObject[] {
+        return this.orgList;
+    };
+
+    tree(): EgIdlObject {
+        return this.orgTree;
+    }
+
+    // get the root OU
+    root(): EgIdlObject {
+        return this.orgList[0];
+    }
+
+    // list of org_unit objects or IDs for ancestors + me
+    ancestors(nodeOrId: EgOrgNodeOrId, asId: Boolean): EgIdlObject[] {
+        let node = this.get(nodeOrId);
+        if (!node) return [];
+        let nodes = [node];
+        while( (node = this.get(node.parent_ou())))
+            nodes.push(node);
+        if (asId) 
+            return nodes.map(function(n){return n.id()});
+        return nodes;
+    };
+
+    // tests that a node can have users
+    canHaveUsers(nodeOrId): Boolean {
+           return this
+            .get(nodeOrId)
+            .ou_type()
+            .can_have_users() == 't';
+    }
+
+    // tests that a node can have volumes
+    canHaveVolumes(nodeOrId): Boolean {
+        return this
+            .get(nodeOrId)
+            .ou_type()
+            .can_have_vols() == 't';
+    }
+
+    // list of org_unit objects  or IDs for me + descendants
+    descendants(nodeOrId: EgOrgNodeOrId, asId: Boolean): EgIdlObject[] {
+        let node = this.get(nodeOrId);
+        if (!node) return [];
+        let nodes = [];
+        function descend(n) {
+            nodes.push(n);
+            n.children().forEach(descend);
+        }
+        descend(node);
+        if (asId) 
+            return nodes.map(function(n){return n.id()});
+        return nodes;
+    }
+
+    // list of org_unit objects or IDs for ancestors + me + descendants
+    fullPath(nodeOrId: EgOrgNodeOrId, asId: Boolean): EgIdlObject[] {
+        let list = this.ancestors(nodeOrId, false).concat(
+          this.descendants(nodeOrId, false).slice(1));
+        if (asId) 
+            return list.map(function(n){return n.id()});
+        return list;
+    }
+
+    sortTree(sortField?: string, node?: EgIdlObject): void {
+        if (!sortField) sortField = 'shortname';
+        if (!node) node = this.orgTree;
+        node.children(
+            node.children.sort((a, b) => {
+                return a[sortField]() < b[sortField]() ? -1 : 1
+            })
+        );
+        node.children.forEach(n => this.sortTree(n));
+    }
+    
+    absorbTree(node?: EgIdlObject): void {
+        if (!node) {
+            node = this.orgTree;
+            this.orgMap = {};
+            this.orgList = [];
+        }
+        this.orgMap[node.id()] = node;
+        this.orgList.push(node);
+        node.children().forEach(c => this.absorbTree(c));
+    }
+
+    /**
+     * Grabs all of the org units from the server, chops them up into
+     * various shapes, then returns an "all done" promise.
+     */
+    fetchOrgs(): Promise<void> {
+        
+        console.log('fetching..');
+        return this.egPcrud.search('aou', {parent_ou : null},
+            {flesh : -1, flesh_fields : {aou : ['children', 'ou_type']}},
+            {anonymous : true}
+        ).toPromise().then(tree => {
+            // ingest tree, etc.
+            this.orgTree = tree;
+            this.absorbTree();
+            console.log('TREE FETCHED: ' + tree);
+        });
+    }
+
+    // NOTE: see ./org-settings.service for settings 
+    // TODO: ^--
+}
diff --git a/Open-ILS/webby-src/src/app/core/pcrud.service.ts b/Open-ILS/webby-src/src/app/core/pcrud.service.ts
deleted file mode 100644 (file)
index efe1d3e..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-import {Injectable} from '@angular/core';
-import {Observable, Observer} from 'rxjs/Rx';
-//import {toPromise} from 'rxjs/operators';
-import {EgIdlService, EgIdlObject} from './idl.service';
-import {EgNetService, EgNetRequest} from './net.service';
-import {EgAuthService} from './auth.service';
-
-// Used for debugging.
-declare var js2JSON: (jsThing:any) => string;
-declare var OpenSRF: any; // creating sessions
-
-export interface EgPcrudReqOps {
-    authoritative?: boolean;
-    anonymous?: boolean;
-    idlist?: boolean;
-    atomic?: boolean;
-}
-
-// For for documentation purposes.
-type EgPcrudResponse = any;
-
-export class EgPcrudContext {
-
-    static verboseLogging: boolean = true; // 
-    static identGenerator: number = 0; // for debug logging
-
-    private ident: number;
-    private authoritative: boolean;
-    private xactCloseMode: string;
-    private cudIdx: number;
-    private cudAction: string;
-    private cudLast: EgPcrudResponse;
-    private cudList: EgIdlObject[];
-
-    private egIdl: EgIdlService;
-    private egNet: EgNetService;
-    private egAuth: EgAuthService;
-
-    // Tracks nested CUD actions 
-    cudObserver: Observer<EgPcrudResponse>;
-
-    session: any; // OpenSRF.ClientSession
-
-    constructor( // passed in by parent service -- not injected
-        egIdl: EgIdlService,
-        egNet: EgNetService,
-        egAuth: EgAuthService
-    ) {
-        this.egIdl = egIdl;
-        this.egNet = egNet;
-        this.egAuth = egAuth;
-        this.xactCloseMode = 'rollback';
-        this.ident = EgPcrudContext.identGenerator++;
-        this.session = new OpenSRF.ClientSession('open-ils.pcrud');
-    }
-
-    toString(): string {
-        return '[PCRUDContext ' + this.ident + ']';
-    }
-
-    log(msg: string): void {
-        if (EgPcrudContext.verboseLogging)
-            console.debug(this + ': ' + msg);
-    }
-
-    err(msg: string): void {
-        console.error(this + ': ' + msg);
-    }
-
-    token(reqOps?: EgPcrudReqOps): string {
-        return (reqOps && reqOps.anonymous) ?
-            'ANONYMOUS' : this.egAuth.token();
-    }
-
-    connect(): Promise<EgPcrudContext> {
-        this.log('connect');
-        return new Promise( (resolve, reject) => {
-            this.session.connect({
-                onconnect : () => { resolve(this); }
-            });
-        })
-    }
-
-    disconnect(): void {
-        this.log('disconnect');
-        this.session.disconnect();
-    }
-
-    retrieve(fmClass: string, pkey: Number | string, 
-            pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable<EgPcrudResponse> {
-        if (!reqOps) reqOps = {};
-        this.authoritative = reqOps.authoritative || false;
-        return this.dispatch(
-            `open-ils.pcrud.retrieve.${fmClass}`, 
-             [this.token(reqOps), pkey, pcrudOps]);
-    }
-
-    retrieveAll(fmClass: string, pcrudOps?: any, 
-            reqOps?: EgPcrudReqOps): Observable<EgPcrudResponse> {
-        let search = {};
-        search[this.egIdl.classes[fmClass].pkey] = {'!=' : null};
-        return this.search(fmClass, search, pcrudOps, reqOps);
-    }
-
-    search(fmClass: string, search: any, 
-            pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable<EgPcrudResponse> {
-        reqOps = reqOps || {};
-        this.authoritative = reqOps.authoritative || false;
-
-        let returnType = reqOps.idlist ? 'id_list' : 'search';
-        let method = `open-ils.pcrud.${returnType}.${fmClass}`;
-
-        if (reqOps.atomic) method += '.atomic';
-
-        return this.dispatch(method, [this.token(reqOps), search, pcrudOps]);
-    }
-
-    create(list: EgIdlObject[]): Observable<EgPcrudResponse> {
-        return this.cud('create', list)
-    }
-    update(list: EgIdlObject[]): Observable<EgPcrudResponse> {
-        return this.cud('update', list)
-    }
-    remove(list: EgIdlObject[]): Observable<EgPcrudResponse> {
-        return this.cud('delete', list)
-    }
-    autoApply(list: EgIdlObject[]): Observable<EgPcrudResponse> { // RENAMED
-        return this.cud('auto',   list)
-    }
-
-    xactClose(): Observable<EgPcrudResponse> {
-        return this.sendRequest(
-            'open-ils.pcrud.transaction.' + this.xactCloseMode,
-            [this.token()]
-        );
-    };
-
-    xactBegin(): Observable<EgPcrudResponse> {
-        return this.sendRequest(
-            'open-ils.pcrud.transaction.begin', [this.token()]
-        );
-    };
-
-    private dispatch(method: string, params: any[]): Observable<EgPcrudResponse> {
-        if (this.authoritative) {
-            return this.wrapXact(() => {
-                return this.sendRequest(method, params);
-            });
-        } else {
-            return this.sendRequest(method, params)
-        }
-    };
-
-
-    // => connect
-    // => xact_begin 
-    // => action
-    // => xact_close(commit/rollback) 
-    // => disconnect
-    wrapXact(mainFunc: () => Observable<EgPcrudResponse>): Observable<EgPcrudResponse> {
-        let this_ = this;
-
-        return Observable.create(observer => {
-
-            // 1. connect
-            this.connect()
-
-            // 2. start the transaction
-            .then(() => {return this_.xactBegin().toPromise()})
-
-            // 3. execute the main body 
-            .then(() => {
-
-                mainFunc().subscribe(
-                    res => observer.next(res),
-                    err => observer.error(err),
-                    ()  => {
-                        this_.xactClose().toPromise().then(() => {
-                            // 5. disconnect
-                            this_.disconnect();
-                            // 6. all done
-                            observer.complete();
-                        });
-                    }
-                );
-            })
-        });
-    };
-
-    private sendRequest(method: string, 
-            params: any[]): Observable<EgPcrudResponse> {
-
-        this.log(`sendRequest(${method})`);
-
-        return this.egNet.requestCompiled(
-            new EgNetRequest(
-                'open-ils.pcrud', method, params, this.session)
-        );
-    }
-
-    private cud(action: string, 
-        list: EgIdlObject | EgIdlObject[]): Observable<EgPcrudResponse> {
-
-        this.log(`CUD(): ${action}`);
-
-        this.cudIdx = 0;
-        this.cudAction = action;
-        this.xactCloseMode = 'commit';
-
-        if (!Array.isArray(list)) this.cudList = [list];
-
-        let this_ = this;
-
-        return this.wrapXact(() => {
-            return Observable.create(observer => {
-                this_.cudObserver = observer;
-                this_.nextCudRequest();
-            });
-        });
-    }
-
-    /**
-     * Loops through the list of objects to update and sends
-     * them one at a time to the server for processing.  Once
-     * all are done, the cudObserver is resolved.
-     */
-    nextCudRequest(): void {
-        let this_ = this;
-
-        if (this.cudIdx >= this.cudList.length) {
-            this.cudObserver.complete();
-            return;
-        }
-
-        let action = this.cudAction;
-        let fmObj = this.cudList[this.cudIdx++];
-
-        if (action == 'auto') {
-            if (fmObj.ischanged()) action = 'update';
-            if (fmObj.isnew())     action = 'create';
-            if (fmObj.isdeleted()) action = 'delete';
-
-            if (action == 'auto') {
-                // object does not need updating; move along
-                this.nextCudRequest();
-            }
-        }
-
-        this.sendRequest(
-            `open-ils.pcrud.${action}.${fmObj.classname}`,
-            [this.token(), fmObj]
-        ).subscribe(
-            res => this_.cudObserver.next(res),
-            err => this_.cudObserver.error(err),
-            ()  => this_.nextCudRequest()
-        );
-    };
-}
-
-@Injectable()
-export class EgPcrudService {
-
-    constructor(
-        private egIdl: EgIdlService,
-        private egNet: EgNetService,
-        private egAuth: EgAuthService
-    ) {}
-
-    // Pass-thru functions for one-off PCRUD calls
-
-    connect(): Promise<EgPcrudContext> {
-        return this.newContext().connect();
-    }
-
-    newContext(): EgPcrudContext {
-        return new EgPcrudContext(this.egIdl, this.egNet, this.egAuth);
-    }
-
-    retrieve(fmClass: string, pkey: Number | string, 
-        pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable<EgPcrudResponse> {
-        return this.newContext().retrieve(fmClass, pkey, pcrudOps, reqOps);
-    }
-
-    retrieveAll(fmClass: string, pcrudOps?: any, 
-        reqOps?: EgPcrudReqOps): Observable<EgPcrudResponse> {
-        return this.newContext().retrieveAll(fmClass, pcrudOps, reqOps);
-    }
-
-    search(fmClass: string, search: any, 
-        pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable<EgPcrudResponse> {
-        return this.newContext().search(fmClass, search, pcrudOps, reqOps);
-    }
-
-    create(list: EgIdlObject[]): Observable<EgPcrudResponse> {
-        return this.newContext().create(list);
-    }
-
-    update(list: EgIdlObject[]): Observable<EgPcrudResponse> {
-        return this.newContext().update(list);
-    }
-
-    remove(list: EgIdlObject[]): Observable<EgPcrudResponse> {
-        return this.newContext().remove(list);
-    }
-
-    autoApply(list: EgIdlObject[]): Observable<EgPcrudResponse> { 
-        return this.newContext().autoApply(list);
-    }
-}
-
-
diff --git a/Open-ILS/webby-src/src/app/core/pcrud.ts b/Open-ILS/webby-src/src/app/core/pcrud.ts
new file mode 100644 (file)
index 0000000..42a93ef
--- /dev/null
@@ -0,0 +1,311 @@
+import {Injectable} from '@angular/core';
+import {Observable, Observer} from 'rxjs/Rx';
+//import {toPromise} from 'rxjs/operators';
+import {EgIdlService, EgIdlObject} from './idl';
+import {EgNetService, EgNetRequest} from './net.service';
+import {EgAuthService} from './auth';
+
+// Used for debugging.
+declare var js2JSON: (jsThing:any) => string;
+declare var OpenSRF: any; // creating sessions
+
+export interface EgPcrudReqOps {
+    authoritative?: boolean;
+    anonymous?: boolean;
+    idlist?: boolean;
+    atomic?: boolean;
+}
+
+// For for documentation purposes.
+type EgPcrudResponse = any;
+
+export class EgPcrudContext {
+
+    static verboseLogging: boolean = true; // 
+    static identGenerator: number = 0; // for debug logging
+
+    private ident: number;
+    private authoritative: boolean;
+    private xactCloseMode: string;
+    private cudIdx: number;
+    private cudAction: string;
+    private cudLast: EgPcrudResponse;
+    private cudList: EgIdlObject[];
+
+    private egIdl: EgIdlService;
+    private egNet: EgNetService;
+    private egAuth: EgAuthService;
+
+    // Tracks nested CUD actions 
+    cudObserver: Observer<EgPcrudResponse>;
+
+    session: any; // OpenSRF.ClientSession
+
+    constructor( // passed in by parent service -- not injected
+        egIdl: EgIdlService,
+        egNet: EgNetService,
+        egAuth: EgAuthService
+    ) {
+        this.egIdl = egIdl;
+        this.egNet = egNet;
+        this.egAuth = egAuth;
+        this.xactCloseMode = 'rollback';
+        this.ident = EgPcrudContext.identGenerator++;
+        this.session = new OpenSRF.ClientSession('open-ils.pcrud');
+    }
+
+    toString(): string {
+        return '[PCRUDContext ' + this.ident + ']';
+    }
+
+    log(msg: string): void {
+        if (EgPcrudContext.verboseLogging)
+            console.debug(this + ': ' + msg);
+    }
+
+    err(msg: string): void {
+        console.error(this + ': ' + msg);
+    }
+
+    token(reqOps?: EgPcrudReqOps): string {
+        return (reqOps && reqOps.anonymous) ?
+            'ANONYMOUS' : this.egAuth.token();
+    }
+
+    connect(): Promise<EgPcrudContext> {
+        this.log('connect');
+        return new Promise( (resolve, reject) => {
+            this.session.connect({
+                onconnect : () => { resolve(this); }
+            });
+        })
+    }
+
+    disconnect(): void {
+        this.log('disconnect');
+        this.session.disconnect();
+    }
+
+    retrieve(fmClass: string, pkey: Number | string, 
+            pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable<EgPcrudResponse> {
+        if (!reqOps) reqOps = {};
+        this.authoritative = reqOps.authoritative || false;
+        return this.dispatch(
+            `open-ils.pcrud.retrieve.${fmClass}`, 
+             [this.token(reqOps), pkey, pcrudOps]);
+    }
+
+    retrieveAll(fmClass: string, pcrudOps?: any, 
+            reqOps?: EgPcrudReqOps): Observable<EgPcrudResponse> {
+        let search = {};
+        search[this.egIdl.classes[fmClass].pkey] = {'!=' : null};
+        return this.search(fmClass, search, pcrudOps, reqOps);
+    }
+
+    search(fmClass: string, search: any, 
+            pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable<EgPcrudResponse> {
+        reqOps = reqOps || {};
+        this.authoritative = reqOps.authoritative || false;
+
+        let returnType = reqOps.idlist ? 'id_list' : 'search';
+        let method = `open-ils.pcrud.${returnType}.${fmClass}`;
+
+        if (reqOps.atomic) method += '.atomic';
+
+        return this.dispatch(method, [this.token(reqOps), search, pcrudOps]);
+    }
+
+    create(list: EgIdlObject[]): Observable<EgPcrudResponse> {
+        return this.cud('create', list)
+    }
+    update(list: EgIdlObject[]): Observable<EgPcrudResponse> {
+        return this.cud('update', list)
+    }
+    remove(list: EgIdlObject[]): Observable<EgPcrudResponse> {
+        return this.cud('delete', list)
+    }
+    autoApply(list: EgIdlObject[]): Observable<EgPcrudResponse> { // RENAMED
+        return this.cud('auto',   list)
+    }
+
+    xactClose(): Observable<EgPcrudResponse> {
+        return this.sendRequest(
+            'open-ils.pcrud.transaction.' + this.xactCloseMode,
+            [this.token()]
+        );
+    };
+
+    xactBegin(): Observable<EgPcrudResponse> {
+        return this.sendRequest(
+            'open-ils.pcrud.transaction.begin', [this.token()]
+        );
+    };
+
+    private dispatch(method: string, params: any[]): Observable<EgPcrudResponse> {
+        if (this.authoritative) {
+            return this.wrapXact(() => {
+                return this.sendRequest(method, params);
+            });
+        } else {
+            return this.sendRequest(method, params)
+        }
+    };
+
+
+    // => connect
+    // => xact_begin 
+    // => action
+    // => xact_close(commit/rollback) 
+    // => disconnect
+    wrapXact(mainFunc: () => Observable<EgPcrudResponse>): Observable<EgPcrudResponse> {
+        let this_ = this;
+
+        return Observable.create(observer => {
+
+            // 1. connect
+            this.connect()
+
+            // 2. start the transaction
+            .then(() => {return this_.xactBegin().toPromise()})
+
+            // 3. execute the main body 
+            .then(() => {
+
+                mainFunc().subscribe(
+                    res => observer.next(res),
+                    err => observer.error(err),
+                    ()  => {
+                        this_.xactClose().toPromise().then(() => {
+                            // 5. disconnect
+                            this_.disconnect();
+                            // 6. all done
+                            observer.complete();
+                        });
+                    }
+                );
+            })
+        });
+    };
+
+    private sendRequest(method: string, 
+            params: any[]): Observable<EgPcrudResponse> {
+
+        this.log(`sendRequest(${method})`);
+
+        return this.egNet.requestCompiled(
+            new EgNetRequest(
+                'open-ils.pcrud', method, params, this.session)
+        );
+    }
+
+    private cud(action: string, 
+        list: EgIdlObject | EgIdlObject[]): Observable<EgPcrudResponse> {
+
+        this.log(`CUD(): ${action}`);
+
+        this.cudIdx = 0;
+        this.cudAction = action;
+        this.xactCloseMode = 'commit';
+
+        if (!Array.isArray(list)) this.cudList = [list];
+
+        let this_ = this;
+
+        return this.wrapXact(() => {
+            return Observable.create(observer => {
+                this_.cudObserver = observer;
+                this_.nextCudRequest();
+            });
+        });
+    }
+
+    /**
+     * Loops through the list of objects to update and sends
+     * them one at a time to the server for processing.  Once
+     * all are done, the cudObserver is resolved.
+     */
+    nextCudRequest(): void {
+        let this_ = this;
+
+        if (this.cudIdx >= this.cudList.length) {
+            this.cudObserver.complete();
+            return;
+        }
+
+        let action = this.cudAction;
+        let fmObj = this.cudList[this.cudIdx++];
+
+        if (action == 'auto') {
+            if (fmObj.ischanged()) action = 'update';
+            if (fmObj.isnew())     action = 'create';
+            if (fmObj.isdeleted()) action = 'delete';
+
+            if (action == 'auto') {
+                // object does not need updating; move along
+                this.nextCudRequest();
+            }
+        }
+
+        this.sendRequest(
+            `open-ils.pcrud.${action}.${fmObj.classname}`,
+            [this.token(), fmObj]
+        ).subscribe(
+            res => this_.cudObserver.next(res),
+            err => this_.cudObserver.error(err),
+            ()  => this_.nextCudRequest()
+        );
+    };
+}
+
+@Injectable()
+export class EgPcrudService {
+
+    constructor(
+        private egIdl: EgIdlService,
+        private egNet: EgNetService,
+        private egAuth: EgAuthService
+    ) {}
+
+    // Pass-thru functions for one-off PCRUD calls
+
+    connect(): Promise<EgPcrudContext> {
+        return this.newContext().connect();
+    }
+
+    newContext(): EgPcrudContext {
+        return new EgPcrudContext(this.egIdl, this.egNet, this.egAuth);
+    }
+
+    retrieve(fmClass: string, pkey: Number | string, 
+        pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable<EgPcrudResponse> {
+        return this.newContext().retrieve(fmClass, pkey, pcrudOps, reqOps);
+    }
+
+    retrieveAll(fmClass: string, pcrudOps?: any, 
+        reqOps?: EgPcrudReqOps): Observable<EgPcrudResponse> {
+        return this.newContext().retrieveAll(fmClass, pcrudOps, reqOps);
+    }
+
+    search(fmClass: string, search: any, 
+        pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable<EgPcrudResponse> {
+        return this.newContext().search(fmClass, search, pcrudOps, reqOps);
+    }
+
+    create(list: EgIdlObject[]): Observable<EgPcrudResponse> {
+        return this.newContext().create(list);
+    }
+
+    update(list: EgIdlObject[]): Observable<EgPcrudResponse> {
+        return this.newContext().update(list);
+    }
+
+    remove(list: EgIdlObject[]): Observable<EgPcrudResponse> {
+        return this.newContext().remove(list);
+    }
+
+    autoApply(list: EgIdlObject[]): Observable<EgPcrudResponse> { 
+        return this.newContext().autoApply(list);
+    }
+}
+
+
diff --git a/Open-ILS/webby-src/src/app/core/store.service.ts b/Open-ILS/webby-src/src/app/core/store.service.ts
deleted file mode 100644 (file)
index f508f55..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/**
- * Store and retrieve data from various sources.
- */
-import { Injectable } from '@angular/core';
-import { Observable } from 'rxjs/Rx';
-import { CookieService } from 'ngx-cookie';
-
-@Injectable()
-export class EgStoreService {
-    
-    // Base path for cookie-based storage.
-    // Useful for limiting cookies to subsections of the application.
-    loginSessionBasePath: string;
-
-    // Set of keys whose values should disappear at logout.
-    loginSessionKeys: string[] = [
-        'eg.auth.token',
-        'eg.auth.time',
-        'eg.auth.token.oc',
-        'eg.auth.time.oc'
-    ];
-
-    constructor(private cookieService: CookieService) {}
-
-    private parseJson(valJson: string): any {
-        if (valJson == null || valJson == '') return null;
-        try {
-            return JSON.parse(valJson);
-        } catch(E) { 
-            console.error(`Failure to parse JSON: ${E} => ${valJson}`);
-            return null;
-        }
-    }
-
-    /**
-     * Add a an app-local login session key
-     */
-    addLoginSessionKey(key: string): void {
-        this.loginSessionKeys.push(key);
-    }
-
-    setItem(key: string, val: any, isJson?: Boolean): Promise<any> {
-        // TODO: route keys appropriately
-        this.setLocalItem(key, val, false);
-        return Promise.resolve();
-    }
-
-    setLocalItem(key: string, val: any, isJson?: Boolean): void {
-        if (!isJson) val = JSON.stringify(val);
-        window.localStorage.setItem(key, val);
-    }
-
-    setServerItem(key: string, val: any): Promise<any> {
-        return Promise.resolve();
-    }
-
-    setSessionItem(key: string, val: any, isJson?: Boolean): void {
-        if (!isJson) val = JSON.stringify(val);
-        window.sessionStorage.setItem(key, val);
-    }
-
-    setLoginSessionItem(key: string, val: any, isJson?:Boolean): void {
-        if (!isJson) val = JSON.stringify(val);
-        console.log(`storing ses item ${key} : ${val}`);
-        this.cookieService.put(key, val, {path : this.loginSessionBasePath});
-    }
-
-    getItem(key: string): Promise<any> {
-        // TODO: route keys appropriately
-        return Promise.resolve(this.getLocalItem(key));
-    }
-
-    getLocalItem(key: string): any {
-        return this.parseJson(window.localStorage.getItem(key));
-    }
-
-    getServerItem(key: string): Promise<any> {
-        return Promise.resolve();
-    }
-
-    getSessionItem(key: string): any {
-        return this.parseJson(window.sessionStorage.getItem(key));
-    }
-
-    getLoginSessionItem(key: string): any {
-        return this.parseJson(this.cookieService.get(key));
-    }
-
-    removeItem(key: string): Promise<any> {
-        // TODO: route keys appropriately
-        return Promise.resolve(this.removeLocalItem(key));
-    }
-
-    removeLocalItem(key: string): void {
-        window.localStorage.removeItem(key);
-    }
-
-    removeServerItem(key: string): Promise<any> {
-        return Promise.resolve();
-    }
-
-    removeSessionItem(key: string): void {
-        window.sessionStorage.removeItem(key);
-    }
-
-    removeLoginSessionItem(key: string): void {
-        this.cookieService.remove(key, {path : this.loginSessionBasePath});
-    }
-
-    clearLoginSessionItems(): void {
-        this.loginSessionKeys.forEach(
-            key => this.removeLoginSessionItem(key)
-        );
-    }
-}
-
diff --git a/Open-ILS/webby-src/src/app/core/store.ts b/Open-ILS/webby-src/src/app/core/store.ts
new file mode 100644 (file)
index 0000000..f508f55
--- /dev/null
@@ -0,0 +1,116 @@
+/**
+ * Store and retrieve data from various sources.
+ */
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs/Rx';
+import { CookieService } from 'ngx-cookie';
+
+@Injectable()
+export class EgStoreService {
+    
+    // Base path for cookie-based storage.
+    // Useful for limiting cookies to subsections of the application.
+    loginSessionBasePath: string;
+
+    // Set of keys whose values should disappear at logout.
+    loginSessionKeys: string[] = [
+        'eg.auth.token',
+        'eg.auth.time',
+        'eg.auth.token.oc',
+        'eg.auth.time.oc'
+    ];
+
+    constructor(private cookieService: CookieService) {}
+
+    private parseJson(valJson: string): any {
+        if (valJson == null || valJson == '') return null;
+        try {
+            return JSON.parse(valJson);
+        } catch(E) { 
+            console.error(`Failure to parse JSON: ${E} => ${valJson}`);
+            return null;
+        }
+    }
+
+    /**
+     * Add a an app-local login session key
+     */
+    addLoginSessionKey(key: string): void {
+        this.loginSessionKeys.push(key);
+    }
+
+    setItem(key: string, val: any, isJson?: Boolean): Promise<any> {
+        // TODO: route keys appropriately
+        this.setLocalItem(key, val, false);
+        return Promise.resolve();
+    }
+
+    setLocalItem(key: string, val: any, isJson?: Boolean): void {
+        if (!isJson) val = JSON.stringify(val);
+        window.localStorage.setItem(key, val);
+    }
+
+    setServerItem(key: string, val: any): Promise<any> {
+        return Promise.resolve();
+    }
+
+    setSessionItem(key: string, val: any, isJson?: Boolean): void {
+        if (!isJson) val = JSON.stringify(val);
+        window.sessionStorage.setItem(key, val);
+    }
+
+    setLoginSessionItem(key: string, val: any, isJson?:Boolean): void {
+        if (!isJson) val = JSON.stringify(val);
+        console.log(`storing ses item ${key} : ${val}`);
+        this.cookieService.put(key, val, {path : this.loginSessionBasePath});
+    }
+
+    getItem(key: string): Promise<any> {
+        // TODO: route keys appropriately
+        return Promise.resolve(this.getLocalItem(key));
+    }
+
+    getLocalItem(key: string): any {
+        return this.parseJson(window.localStorage.getItem(key));
+    }
+
+    getServerItem(key: string): Promise<any> {
+        return Promise.resolve();
+    }
+
+    getSessionItem(key: string): any {
+        return this.parseJson(window.sessionStorage.getItem(key));
+    }
+
+    getLoginSessionItem(key: string): any {
+        return this.parseJson(this.cookieService.get(key));
+    }
+
+    removeItem(key: string): Promise<any> {
+        // TODO: route keys appropriately
+        return Promise.resolve(this.removeLocalItem(key));
+    }
+
+    removeLocalItem(key: string): void {
+        window.localStorage.removeItem(key);
+    }
+
+    removeServerItem(key: string): Promise<any> {
+        return Promise.resolve();
+    }
+
+    removeSessionItem(key: string): void {
+        window.sessionStorage.removeItem(key);
+    }
+
+    removeLoginSessionItem(key: string): void {
+        this.cookieService.remove(key, {path : this.loginSessionBasePath});
+    }
+
+    clearLoginSessionItems(): void {
+        this.loginSessionKeys.forEach(
+            key => this.removeLoginSessionItem(key)
+        );
+    }
+}
+
index 984e888..c63b2d5 100644 (file)
@@ -1,8 +1,8 @@
 import {Injectable} from '@angular/core';
 import {Router, Resolve, RouterStateSnapshot,
         ActivatedRouteSnapshot} from '@angular/router';
-import {EgIdlService} from '@eg/core/idl.service';
-import {EgOrgService} from '@eg/core/org.service';
+import {EgIdlService} from '@eg/core/idl';
+import {EgOrgService} from '@eg/core/org';
  
 @Injectable()
 export class EgBaseResolver implements Resolve<Promise<void>> {
index 4e212cf..231bcb4 100644 (file)
@@ -1,10 +1,10 @@
 import {Component, OnInit} from '@angular/core';
 import {ActivatedRoute} from '@angular/router';
-import {EgStoreService} from '@eg/core/store.service';
-import {EgIdlObject} from '@eg/core/idl.service';
+import {EgStoreService} from '@eg/core/store';
+import {EgIdlObject} from '@eg/core/idl';
 import {EgNetService} from '@eg/core/net.service';
-import {EgAuthService} from '@eg/core/auth.service';
-import {EgOrgService} from '@eg/core/org.service';
+import {EgAuthService} from '@eg/core/auth';
+import {EgOrgService} from '@eg/core/org';
 
 // Slim version of the WS that's stored in the cache.
 interface Workstation {
@@ -21,7 +21,6 @@ export class EgWorkstationsComponent implements OnInit {
     selectedId: Number;
     workstations: Workstation[] = [];
     isRemoving: boolean = false;
-
     newOwner: EgIdlObject;
     newName: String;
 
@@ -29,12 +28,15 @@ export class EgWorkstationsComponent implements OnInit {
         private route: ActivatedRoute,
         private egNet: EgNetService,
         private egAuth: EgAuthService,
-        private egStore: EgStoreService
+        private egStore: EgStoreService,
+        private egOrg: EgOrgService
     ) {}
 
     ngOnInit() {
         this.egStore.getItem('eg.workstation.all')
         .then(res => this.workstations = res);
+
+        this.newOwner = this.egOrg.root();
     }
 
     selected(): Workstation {
@@ -59,7 +61,8 @@ export class EgWorkstationsComponent implements OnInit {
     }
 
     registerWorkstation(): void {
-        console.log('registering ' + this.newName);
+        console.log(this.newOwner);
+        console.log('registering ' + this.newName + ' : ' + this.newOwner.shortname());
     }
 }
 
index 21bb111..3b2cfe1 100644 (file)
@@ -1,7 +1,7 @@
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 import { EgNetService } from '@eg/core/net.service';
-import { EgAuthService } from '@eg/core/auth.service';
+import { EgAuthService } from '@eg/core/auth';
 
 @Component({
   templateUrl: 'bcsearch.component.html'
index 599562d..3dd9494 100644 (file)
@@ -1,8 +1,8 @@
 import { Component, OnInit, Renderer } from '@angular/core';
 import { Location } from '@angular/common';
 import { Router } from '@angular/router';
-import { EgAuthService, EgAuthWsState } from '@eg/core/auth.service';
-import { EgStoreService } from '@eg/core/store.service'; // TODO: testing
+import { EgAuthService, EgAuthWsState } from '@eg/core/auth';
+import { EgStoreService } from '@eg/core/store'; // TODO: testing
 
 @Component({
   templateUrl : './login.component.html'
index 83dba49..21eddec 100644 (file)
@@ -3,9 +3,9 @@ import { Location } from '@angular/common';
 import { Observable, Observer } from 'rxjs/Rx';
 import { Router, Resolve, RouterStateSnapshot,
          ActivatedRouteSnapshot } from '@angular/router';
-import { EgStoreService } from '@eg/core/store.service';
+import { EgStoreService } from '@eg/core/store';
 import { EgNetService } from '@eg/core/net.service';
-import { EgAuthService } from '@eg/core/auth.service';
+import { EgAuthService } from '@eg/core/auth';
 
 /**
  * Apply configuration, etc. required by all staff components.
index b8fb2d5..50ec19b 100644 (file)
@@ -1,4 +1,9 @@
-
+<style>
+  .org-depth-1 { padding-left:3px; }
+  .org-depth-2 { padding-left:6px; }
+  .org-depth-3 { padding-left:9px; }
+  .org-depth-4 { padding-left:12px; }
+</style>
 <div>
   <input type="text" 
     class="form-control" 
index f111693..4c81aea 100644 (file)
@@ -1,16 +1,16 @@
 import {Component, OnInit, Input} from '@angular/core';
 import {Observable} from 'rxjs/Observable';
 import {map, tap, debounceTime, distinctUntilChanged} from 'rxjs/operators';
-import {EgAuthService} from '@eg/core/auth.service';
-import {EgStoreService} from '@eg/core/store.service';
-import {EgIdlObject} from '@eg/core/idl.service';
+import {EgAuthService} from '@eg/core/auth';
+import {EgStoreService} from '@eg/core/store';
+import {EgOrgService} from '@eg/core/org';
+import {EgIdlObject} from '@eg/core/idl';
 
 @Component({
   selector: 'eg-org-select',
   templateUrl: './org-select.component.html'
 })
 
-
 export class EgOrgSelectComponent implements OnInit {
 
     @Input() placeholder: String;
@@ -26,23 +26,33 @@ export class EgOrgSelectComponent implements OnInit {
 
     constructor(
       private egAuth: EgAuthService,
-      private egStore: EgStoreService 
+      private egStore: EgStoreService,
+      private egOrg: EgOrgService 
     ) {}
     
     ngOnInit() {
     }
 
-    states = ['Alabama', 'Alaska', 'American Samoa', 'Arizona'];
-
     orgFilter = (text$: Observable<string>): Observable<string[]> => {
         return text$
             .debounceTime(100)
             .distinctUntilChanged()
-            .map(term => this.states.filter(
-                v => v.toLowerCase().indexOf(term.toLowerCase()) > -1)
-            );
+            .map(term => {
+                return this.egOrg.list().filter(org => {
+                    let sn = org.shortname().toLowerCase(); 
+                    return sn.indexOf(term.toLowerCase()) > -1
+                }).map(org => {
+                    // The browser won't collapse multiple spaces when
+                    // using a space-ish unicode char instead of regular spaces.
+                    let space = ' '; // U+2007 
+                    let sn = org.shortname();
+                    for (var i = 0; i < org.ou_type().depth(); i++) {
+                        sn = space + sn;
+                    }
+                    return sn;
+                })
+            });
     }
 }
 
 
-
index 6f7ba18..836a92b 100644 (file)
@@ -1,6 +1,6 @@
 import { Component, OnInit } from '@angular/core';
 import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
-import { EgAuthService, EgAuthWsState } from '@eg/core/auth.service';
+import { EgAuthService, EgAuthWsState } from '@eg/core/auth';
 import { EgNetService } from '@eg/core/net.service';
 
 @Component({