LP#626157 Ang2 experiments / service
authorBill Erickson <berickxx@gmail.com>
Sun, 19 Nov 2017 15:42:42 +0000 (10:42 -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>
Open-ILS/webby-src/README.adoc [new file with mode: 0644]
Open-ILS/webby-src/src/app/core/README
Open-ILS/webby-src/src/app/core/eg-event.ts [new file with mode: 0644]
Open-ILS/webby-src/src/app/core/eg-net-request.ts [new file with mode: 0644]
Open-ILS/webby-src/src/app/core/eg-net.service.ts [new file with mode: 0644]
Open-ILS/webby-src/src/app/core/types/eg-event.ts [deleted file]

diff --git a/Open-ILS/webby-src/README.adoc b/Open-ILS/webby-src/README.adoc
new file mode 100644 (file)
index 0000000..fd58af9
--- /dev/null
@@ -0,0 +1,17 @@
+= EG Angular2 App =
+
+=== Apache Configuration ===
+
+[source,conf]
+---------------------------------------------------------------------
+<Directory "/openils/var/web/webby">
+    FallbackResource /webby/index.html
+</Directory>
+---------------------------------------------------------------------
+
+=== Transpile + Deploy in --watch mode for Dev ===
+
+[source,sh]
+---------------------------------------------------------------------
+ng build --deploy-url /webby/ --base-href /webby/ --output-path  ../web/webby/  --watch 
+---------------------------------------------------------------------
index 3195b3c..217411d 100644 (file)
@@ -1 +1,5 @@
 Core types (classes) and Angular services used by all modules.
+
+Class files are named $classname.ts
+
+Service files are named $servicename.service.ts
diff --git a/Open-ILS/webby-src/src/app/core/eg-event.ts b/Open-ILS/webby-src/src/app/core/eg-event.ts
new file mode 100644 (file)
index 0000000..1561fed
--- /dev/null
@@ -0,0 +1,47 @@
+
+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;   
+    }
+
+    /**
+     * Returns an EgEvent if 'thing' is an event, null otherwise.
+     */
+    public static 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/eg-net-request.ts b/Open-ILS/webby-src/src/app/core/eg-net-request.ts
new file mode 100644 (file)
index 0000000..428d59a
--- /dev/null
@@ -0,0 +1,20 @@
+import { EgEvent } from './eg-event';
+
+public class EgNetRequest {
+    service    : String;
+    method     : String;
+    params     : any[];
+    observer   : Observer<any>;
+    superseded : Boolean = false;
+
+    // Last EgEvent encountered by this request.
+    // Most callers will not need to import EgEvent since the parsed
+    // event will be available here.
+    evt: EgEvent;
+
+    constructor(service: String, method: String, params: any[]) {
+        this.service = service;
+        this.method = method;
+        this.params = params;
+    }
+}
diff --git a/Open-ILS/webby-src/src/app/core/eg-net.service.ts b/Open-ILS/webby-src/src/app/core/eg-net.service.ts
new file mode 100644 (file)
index 0000000..c272c67
--- /dev/null
@@ -0,0 +1,117 @@
+/**
+ * 
+ * constructor(private egNet : EgNetService) {
+ *   ...
+ *   egNet.request(service, method, param1 [, param2, ...])
+ *     .subscribe(
+ *       (res) => console.log('received one resopnse: ' + res),
+ *       (err) => console.error('recived request error: ' + err),
+ *       ()    => console.log('request complete')
+ *     )
+ *   );
+ *   ...
+ * }
+ *
+ * Each response is relayed via Observable onNext().  The interface is 
+ * the same for streaming and atomic requests.
+ */
+import { Injectable } from '@angular/core';
+import { Observable, Observer, EventEmitter } from 'rxjs/Rx';
+import { EgNetRequest } from './eg-net-request';
+import { EgEvent } from './eg-event';
+
+// Global vars from opensrf.js
+// These are availavble at runtime, but are not exported.
+declare var OpenSRF, OSRF_TRANSPORT_TYPE_WS;
+
+@Injectable()
+export class EgNetService {
+
+    public permFailed$: EventEmitter<EgNetRequest>;
+    public authExpired$: EventEmitter<EgNetRequest>;
+
+    // If true, permission failures are emitted via permFailed$ 
+    // and the active request is marked as superseded.
+    public permFailedHasHandler: Boolean = false;
+
+    constructor() { 
+        this.permFailed$ = new EventEmitter<EgNetRequest>();
+        this.authExpired$ = new EventEmitter<EgNetRequest>();
+    }
+
+    // Variadic params version
+    request(service: String, method: String, ...params: any[]): Observable<any> {
+        return this.request(service, method, params);
+    }
+
+    // Array params version
+    request(service: String, method: String, params: any[]): Observable<any> {
+        var request = new EgNetRequest(service, method, params);
+
+        return Observable.create(
+            observer => {
+                request.observer = observer;
+                this.sendRequest(request);
+            }
+        );
+    }
+
+    private sendRequest(request: EgNetRequest): void {
+        OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_WS;
+
+        new OpenSRF.ClientSession(request.service).request({
+            async  : true,
+            method : request.method,
+            params : request.params,
+            oncomplete : function() {
+                // A superseded request will be complete()'ed by the 
+                // superseder at a later time.
+                if (!request.superseded)
+                    request.observer.complete();
+            },
+            onresponse : function(r) {
+                this.dispatchResponse(request, r.recv().content());
+            },
+            onerror : function(msg) {
+                let msg = `${request.method} failed! See server logs.`;
+                console.error(msg);
+                request.observer.error(msg);
+            },
+            onmethoderror : function(req, statCode, statMsg) { 
+                let msg = 
+                    `${request.method} failed! stat=${statCode} msg=${statMsg}`;
+                console.error(msg);
+                request.observer.error(msg);
+            }
+
+        }).send();
+    }
+
+    // Relay response object to the caller for typical/successful responses.  
+    // Applies special handling to response events that require global attention.
+    private dispatchResponse = function(request, response) {
+        request.evt = EgEvent.parse(response);
+
+        if (request.evt) {
+            switch(request.evt.textcode) {
+
+                case 'NO_SESSION':
+                    console.debug(`EgNet emitting event: ${request.evt}`);
+                    request.observer.error(request.evt.toString());
+                    this.authExpired$.emit(request);
+                    return;
+
+                case 'PERM_FAILURE':
+                    if (permFailedHasHandler) {
+                        console.debug(`EgNet emitting event: ${request.evt}`);
+                        request.superseded = true;
+                        this.permFailed$.emit(request);
+                        return;
+                    }
+            } 
+        }
+
+        // Pass the response to the caller.
+        request.observer.next(response);
+    };
+}
diff --git a/Open-ILS/webby-src/src/app/core/types/eg-event.ts b/Open-ILS/webby-src/src/app/core/types/eg-event.ts
deleted file mode 100644 (file)
index 1561fed..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-
-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;   
-    }
-
-    /**
-     * Returns an EgEvent if 'thing' is an event, null otherwise.
-     */
-    public static 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;
-    }
-}
-