From 7fe6caf37e7d0474ff8e721976a5d49b950dfdce Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Tue, 23 Apr 2019 07:57:31 -0700 Subject: [PATCH] LP1825896 Store workstations in Hatch when available When Hatch is enabled, use Hatch for storing workstation registration information. If workstations are found in localStorage, they are merged into the collection of workstations stored in hatch and removed from localStorage. Include DB udpate to add workstation setting 'eg.hatch.enable.printing' so that it may live on the server. Signed-off-by: Bill Erickson Signed-off-by: Jason Boyer --- Open-ILS/src/eg2/src/app/core/auth.service.ts | 24 +- .../{share/print => core}/hatch.service.ts | 19 +- .../src/eg2/src/app/core/store.service.ts | 115 ++++++++- .../src/app/share/print/print.component.ts | 14 +- .../workstations/workstations.component.ts | 27 ++- .../src/eg2/src/app/staff/login.component.ts | 12 +- .../src/eg2/src/app/staff/resolver.service.ts | 4 + Open-ILS/src/sql/Pg/950.data.seed-values.sql | 11 + .../upgrade/XXXX.data.hatch-enable-print.sql | 18 ++ .../ui/default/staff/admin/workstation/app.js | 28 ++- Open-ILS/web/js/ui/default/staff/app.js | 4 +- Open-ILS/web/js/ui/default/staff/offline.js | 4 +- .../web/js/ui/default/staff/services/hatch.js | 219 +++++++++++++----- 13 files changed, 388 insertions(+), 111 deletions(-) rename Open-ILS/src/eg2/src/app/{share/print => core}/hatch.service.ts (81%) create mode 100644 Open-ILS/src/sql/Pg/upgrade/XXXX.data.hatch-enable-print.sql diff --git a/Open-ILS/src/eg2/src/app/core/auth.service.ts b/Open-ILS/src/eg2/src/app/core/auth.service.ts index dad2acdb90..a173d6304a 100644 --- a/Open-ILS/src/eg2/src/app/core/auth.service.ts +++ b/Open-ILS/src/eg2/src/app/core/auth.service.ts @@ -286,22 +286,22 @@ export class AuthService { } return new Promise((resolve, reject) => { - const workstations = - this.store.getLocalItem('eg.workstation.all'); + return this.store.getWorkstations().then(workstations => { - if (workstations) { - const ws = workstations.filter( - w => Number(w.id) === Number(this.user().wsid()))[0]; + if (workstations) { + const ws = workstations.filter( + w => Number(w.id) === Number(this.user().wsid()))[0]; - if (ws) { - this.activeUser.workstation = ws.name; - this.workstationState = AuthWsState.VALID; - return resolve(); + if (ws) { + this.activeUser.workstation = ws.name; + this.workstationState = AuthWsState.VALID; + return resolve(); + } } - } - this.workstationState = AuthWsState.NOT_FOUND_LOCAL; - reject(); + this.workstationState = AuthWsState.NOT_FOUND_LOCAL; + reject(); + }); }); } diff --git a/Open-ILS/src/eg2/src/app/share/print/hatch.service.ts b/Open-ILS/src/eg2/src/app/core/hatch.service.ts similarity index 81% rename from Open-ILS/src/eg2/src/app/share/print/hatch.service.ts rename to Open-ILS/src/eg2/src/app/core/hatch.service.ts index bd087b7747..b61ee176e4 100644 --- a/Open-ILS/src/eg2/src/app/share/print/hatch.service.ts +++ b/Open-ILS/src/eg2/src/app/core/hatch.service.ts @@ -22,7 +22,7 @@ export class HatchMessage { } } -@Injectable() +@Injectable({providedIn: 'root'}) export class HatchService { isAvailable: boolean; @@ -44,7 +44,7 @@ export class HatchService { // When the Hatch extension loads, it tacks an attribute onto // the top-level documentElement to indicate it's available. if (!window.document.documentElement.getAttribute('hatch-is-open')) { - console.warn('Could not connect to Hatch'); + console.debug('Could not connect to Hatch'); return this.isAvailable = false; } @@ -105,5 +105,20 @@ export class HatchService { msg.rejector(msg); } } + + getItem(key: string): Promise { + const msg = new HatchMessage({action: 'get', key: key}); + return this.sendRequest(msg).then((m: HatchMessage) => m.response); + } + + setItem(key: string, val: any): Promise { + const msg = new HatchMessage({action: 'set', key: key, content: val}); + return this.sendRequest(msg).then((m: HatchMessage) => m.response); + } + + removeItem(key: string): Promise { + const msg = new HatchMessage({action: 'remove', key: key}); + return this.sendRequest(msg).then((m: HatchMessage) => m.response); + } } diff --git a/Open-ILS/src/eg2/src/app/core/store.service.ts b/Open-ILS/src/eg2/src/app/core/store.service.ts index 46dd6214fe..7b239d2ad2 100644 --- a/Open-ILS/src/eg2/src/app/core/store.service.ts +++ b/Open-ILS/src/eg2/src/app/core/store.service.ts @@ -11,6 +11,10 @@ */ import {Injectable} from '@angular/core'; import {CookieService} from 'ngx-cookie'; +import {HatchService} from './hatch.service'; + +const WS_ALL_KEY = 'eg.workstation.all'; +const WS_DEF_KEY = 'eg.workstation.default'; @Injectable({providedIn: 'root'}) export class StoreService { @@ -30,7 +34,8 @@ export class StoreService { ]; constructor( - private cookieService: CookieService) { + private cookieService: CookieService, + private hatch: HatchService) { } private parseJson(valJson: string): any { @@ -74,6 +79,31 @@ export class StoreService { {path : this.loginSessionBasePath, secure: true}); } + setWorkstations(val: any, isJson?: boolean): Promise { + if (this.hatch.isAvailable) { + return this.hatch.setItem(WS_ALL_KEY, val).then( + ok => { + // When clearing workstations, remove the default. + if (!val || val.length === 0) { + return this.hatch.removeItem(WS_DEF_KEY); + } + } + ); + } else { + return Promise.resolve( + this.setLocalItem(WS_ALL_KEY, val, isJson)); + } + } + + setDefaultWorkstation(val: string, isJson?: boolean): Promise { + if (this.hatch.isAvailable) { + return this.hatch.setItem(WS_DEF_KEY, val); + } else { + return Promise.resolve( + this.setLocalItem(WS_DEF_KEY, val, isJson)); + } + } + getLocalItem(key: string): any { return this.parseJson(window.localStorage.getItem(key)); } @@ -86,6 +116,79 @@ export class StoreService { return this.parseJson(this.cookieService.get(key)); } + getWorkstations(): Promise { + if (this.hatch.isAvailable) { + return this.mergeWorkstations().then(ok => { + this.removeLocalItem(WS_ALL_KEY); + return this.hatch.getItem(WS_ALL_KEY); + }); + } else { + return Promise.resolve(this.getLocalItem(WS_ALL_KEY)); + } + } + + // See if any workstatoins are stored in local storage. If so, also + // see if we have any stored in Hatch. If both, merged workstations + // from localStorage in Hatch storage, skipping any whose name + // collide with a workstation in Hatch. If none exist in Hatch, + // copy the localStorage workstations over wholesale. + mergeWorkstations(): Promise { + const existing = this.getLocalItem(WS_ALL_KEY); + + if (!existing || existing.length === 0) { + return Promise.resolve(); + } + + return this.hatch.getItem(WS_ALL_KEY).then(inHatch => { + + if (!inHatch || inHatch.length === 0) { + // Nothing to merge, copy the data over directly + return this.hatch.setItem('eg.workstation.all', existing); + } + + const addMe: any = []; + existing.forEach(ws => { + const match = inHatch.filter(w => w.name === ws.name)[0]; + if (!match) { + console.log( + 'Migrating workstation from local storage to hatch: ' + + ws.name + ); + addMe.push(ws); + } + }); + inHatch = inHatch.concat(addMe); + return this.hatch.setItem(WS_ALL_KEY, inHatch); + }); + } + + getDefaultWorkstation(): Promise { + if (this.hatch.isAvailable) { + return this.hatch.getItem(WS_DEF_KEY).then(name => { + if (name) { + // We have a default in Hatch, remove any lingering + // value from localStorage. + this.removeLocalItem(WS_DEF_KEY); + return name; + } else { + // Nothing in Hatch, see if we have a localStorage + // value to migrate to Hatch + name = this.getLocalItem(WS_DEF_KEY); + if (name) { + console.debug( + 'Migrating default workstation to Hatch ' + name); + return this.hatch.setItem(WS_DEF_KEY, name) + .then(ok => name); + } else { + return null; + } + } + }); + } else { + return Promise.resolve(this.getLocalItem(WS_DEF_KEY)); + } + } + removeLocalItem(key: string): void { window.localStorage.removeItem(key); } @@ -98,6 +201,16 @@ export class StoreService { this.cookieService.remove(key, {path : this.loginSessionBasePath}); } + removeDefaultWorkstation(val: string, isJson?: boolean): Promise { + if (this.hatch.isAvailable) { + return this.hatch.removeItem(WS_DEF_KEY); + } else { + return Promise.resolve( + this.removeLocalItem(WS_DEF_KEY)); + } + } + + clearLoginSessionItems(): void { this.loginSessionKeys.forEach( key => this.removeLoginSessionItem(key) diff --git a/Open-ILS/src/eg2/src/app/share/print/print.component.ts b/Open-ILS/src/eg2/src/app/share/print/print.component.ts index 20c056721d..0d780f3637 100644 --- a/Open-ILS/src/eg2/src/app/share/print/print.component.ts +++ b/Open-ILS/src/eg2/src/app/share/print/print.component.ts @@ -2,7 +2,7 @@ import {Component, OnInit, TemplateRef, ElementRef, Renderer2} from '@angular/co import {PrintService, PrintRequest} from './print.service'; import {StoreService} from '@eg/core/store.service'; import {ServerStoreService} from '@eg/core/server-store.service'; -import {HatchService, HatchMessage} from './hatch.service'; +import {HatchService, HatchMessage} from '@eg/core/hatch.service'; import {ToastService} from '@eg/share/toast/toast.service'; import {StringService} from '@eg/share/string/string.service'; @@ -26,6 +26,8 @@ export class PrintComponent implements OnInit { printQueue: PrintRequest[]; + useHatch: boolean; + constructor( private renderer: Renderer2, private elm: ElementRef, @@ -45,6 +47,9 @@ export class PrintComponent implements OnInit { this.htmlContainer = this.renderer.selectRootElement('#eg-print-html-container'); + + this.serverStore.getItem('eg.hatch.enable.printing') + .then(use => this.useHatch = use); } handlePrintRequest(printReq: PrintRequest) { @@ -163,7 +168,7 @@ export class PrintComponent implements OnInit { show_dialog: printReq.showDialog }); - if (this.useHatch()) { + if (this.useHatch) { this.printViaHatch(printReq); } else { // Here the needed HTML is already in the page. @@ -171,11 +176,6 @@ export class PrintComponent implements OnInit { } } - useHatch(): boolean { - return this.store.getLocalItem('eg.hatch.enable.printing') - && this.hatch.connect(); - } - printViaHatch(printReq: PrintRequest) { // Send a full HTML document to Hatch diff --git a/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.ts index 457fb94299..afd7940f17 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/workstation/workstations/workstations.component.ts @@ -50,13 +50,18 @@ export class WorkstationsComponent implements OnInit { ) {} ngOnInit() { - this.workstations = this.store.getLocalItem('eg.workstation.all') || []; - this.defaultName = this.store.getLocalItem('eg.workstation.default'); - this.selectedName = this.auth.workstation() || this.defaultName; - const rm = this.route.snapshot.paramMap.get('remove'); - if (rm) { - this.removeSelected(this.removeWorkstation = rm); - } + this.store.getWorkstations() + + .then(wsList => { + this.workstations = wsList || []; + return this.store.getDefaultWorkstation(); + + }).then(def => { + this.defaultName = def; + this.selectedName = this.auth.workstation() || this.defaultName; + const rm = this.route.snapshot.paramMap.get('remove'); + if (rm) { this.removeSelected(this.removeWorkstation = rm); } + }); // TODO: use the org selector limitPerm option this.perm.hasWorkPermAt(['REGISTER_WORKSTATION'], true) @@ -85,7 +90,7 @@ export class WorkstationsComponent implements OnInit { setDefault(): void { if (this.selected()) { this.defaultName = this.selected().name; - this.store.setLocalItem('eg.workstation.default', this.defaultName); + this.store.setDefaultWorkstation(this.defaultName); } } @@ -95,11 +100,11 @@ export class WorkstationsComponent implements OnInit { } this.workstations = this.workstations.filter(w => w.name !== name); - this.store.setLocalItem('eg.workstation.all', this.workstations); + this.store.setWorkstations(this.workstations); if (this.defaultName === name) { this.defaultName = null; - this.store.removeLocalItem('eg.workstation.default'); + this.store.removeWorkstations(); } } @@ -170,7 +175,7 @@ export class WorkstationsComponent implements OnInit { }; this.workstations.push(ws); - this.store.setLocalItem('eg.workstation.all', this.workstations); + this.store.setWorkstations(this.workstations); this.newName = ''; // when registering our first workstation, mark it as the // default and show it as selected in the ws selector. diff --git a/Open-ILS/src/eg2/src/app/staff/login.component.ts b/Open-ILS/src/eg2/src/app/staff/login.component.ts index de889f9fb2..2f2b9312e7 100644 --- a/Open-ILS/src/eg2/src/app/staff/login.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/login.component.ts @@ -36,10 +36,14 @@ export class StaffLoginComponent implements OnInit { // Focus username this.renderer.selectRootElement('#username').focus(); - this.workstations = this.store.getLocalItem('eg.workstation.all'); - this.args.workstation = - this.store.getLocalItem('eg.workstation.default'); - this.applyWorkstation(); + this.store.getWorkstations() + .then(wsList => { + this.workstations = wsList; + return this.store.getDefaultWorkstation(); + }).then(def => { + this.args.workstation = def; + this.applyWorkstation(); + }); } applyWorkstation() { diff --git a/Open-ILS/src/eg2/src/app/staff/resolver.service.ts b/Open-ILS/src/eg2/src/app/staff/resolver.service.ts index 94b469ee67..853bf402f1 100644 --- a/Open-ILS/src/eg2/src/app/staff/resolver.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/resolver.service.ts @@ -9,6 +9,7 @@ import {AuthService, AuthWsState} from '@eg/core/auth.service'; import {PermService} from '@eg/core/perm.service'; import {OrgService} from '@eg/core/org.service'; import {FormatService} from '@eg/core/format.service'; +import {HatchService} from '@eg/core/hatch.service'; const LOGIN_PATH = '/staff/login'; const WS_MANAGE_PATH = '/staff/admin/workstation/workstations/manage'; @@ -26,6 +27,7 @@ export class StaffResolver implements Resolve> { private router: Router, private route: ActivatedRoute, private ngLocation: Location, + private hatch: HatchService, private store: StoreService, private org: OrgService, private net: NetService, @@ -38,6 +40,8 @@ export class StaffResolver implements Resolve> { route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + this.hatch.connect(); + // Staff cookies stay in /$base/staff/ // NOTE: storing session data at '/' so it can be shared by // Angularjs apps. diff --git a/Open-ILS/src/sql/Pg/950.data.seed-values.sql b/Open-ILS/src/sql/Pg/950.data.seed-values.sql index 32dcd0fffa..a49cf02de8 100644 --- a/Open-ILS/src/sql/Pg/950.data.seed-values.sql +++ b/Open-ILS/src/sql/Pg/950.data.seed-values.sql @@ -20297,3 +20297,14 @@ VALUES ( 'Grid Config: circ.patron.xact_details_details_payments', 'cwst', 'label') ); + +INSERT INTO config.workstation_setting_type (name, grp, datatype, label) +VALUES ( + 'eg.hatch.enable.printing', 'gui', 'bool', + oils_i18n_gettext( + 'eg.hatch.enable.printing', + 'Use Hatch for printing', + 'cwst', 'label' + ) +); + diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.hatch-enable-print.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.hatch-enable-print.sql new file mode 100644 index 0000000000..315c85b0d2 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.hatch-enable-print.sql @@ -0,0 +1,18 @@ +BEGIN; + +-- SELECT evergreen.upgrade_deps_block_check('TODO', :eg_version); + +INSERT INTO config.workstation_setting_type (name, grp, datatype, label) +VALUES ( + 'eg.hatch.enable.printing', 'gui', 'bool', + oils_i18n_gettext( + 'eg.hatch.enable.printing', + 'Use Hatch for printing', + 'cwst', 'label' + ) +); + + +COMMIT; + + diff --git a/Open-ILS/web/js/ui/default/staff/admin/workstation/app.js b/Open-ILS/web/js/ui/default/staff/admin/workstation/app.js index d863844e3d..70f87b9f8e 100644 --- a/Open-ILS/web/js/ui/default/staff/admin/workstation/app.js +++ b/Open-ILS/web/js/ui/default/staff/admin/workstation/app.js @@ -72,16 +72,16 @@ function($q , $timeout , $location , egCore , egConfirmDialog) { var service = {}; service.get_all = function() { - return egCore.hatch.getItem('eg.workstation.all') + return egCore.hatch.getWorkstations() .then(function(all) { return all || [] }); } service.get_default = function() { - return egCore.hatch.getItem('eg.workstation.default'); + return egCore.hatch.getDefaultWorkstation(); } service.set_default = function(name) { - return egCore.hatch.setItem('eg.workstation.default', name); + return egCore.hatch.setDefaultWorkstation(name); } service.register_workstation = function(base_name, name, org_id) { @@ -140,7 +140,7 @@ function($q , $timeout , $location , egCore , egConfirmDialog) { return service.get_all() .then(function(all) { all.push(new_ws); - return egCore.hatch.setItem('eg.workstation.all', all) + return egCore.hatch.setWorkstations(all) .then(function() { return new_ws }); }); } @@ -150,13 +150,13 @@ function($q , $timeout , $location , egCore , egConfirmDialog) { service.remove_workstation = function(name) { console.debug('Removing workstation: ' + name); - return egCore.hatch.getItem('eg.workstation.all') + return egCore.hatch.getWorkstations() // remove from list of all workstations .then(function(all) { if (!all) all = []; var keep = all.filter(function(ws) {return ws.name != name}); - return egCore.hatch.setItem('eg.workstation.all', keep) + return egCore.hatch.setWorkstations(keep); }).then(function() { @@ -165,7 +165,7 @@ function($q , $timeout , $location , egCore , egConfirmDialog) { }).then(function(def) { if (def == name) { console.debug('Removing default workstation: ' + name); - return egCore.hatch.removeItem('eg.workstation.default'); + return egCore.hatch.removeDefaultWorkstation(); } }); } @@ -234,8 +234,13 @@ function($scope , egCore) { $scope.setContentType = function(type) { $scope.contentType = type } $scope.setContentType('text/plain'); + var hatchPrinting = false; + egCore.hatch.usePrinting().then(function(answer) { + hatchPrinting = answer; + }); + $scope.useHatchPrinting = function() { - return egCore.hatch.usePrinting(); + return hatchPrinting; } $scope.hatchIsOpen = function() { @@ -959,15 +964,18 @@ function($scope , egCore , ngToast) { var hatch = egCore.hatch; // convenience $scope.hatch_available = hatch.hatchAvailable; - $scope.hatch_printing = hatch.usePrinting(); $scope.hatch_settings = hatch.useSettings(); $scope.hatch_offline = hatch.useOffline(); + hatch.usePrinting().then(function(answer) { + $scope.hatch_printing = answer; + }); + // Apply Hatch settings as changes occur in the UI. $scope.$watch('hatch_printing', function(newval) { if (typeof newval != 'boolean') return; - hatch.setLocalItem('eg.hatch.enable.printing', newval); + hatch.setItem('eg.hatch.enable.printing', newval); }); $scope.$watch('hatch_settings', function(newval) { diff --git a/Open-ILS/web/js/ui/default/staff/app.js b/Open-ILS/web/js/ui/default/staff/app.js index 053f4014a6..e38b76f92d 100644 --- a/Open-ILS/web/js/ui/default/staff/app.js +++ b/Open-ILS/web/js/ui/default/staff/app.js @@ -60,7 +60,7 @@ function($routeProvider , $locationProvider) { // if the user is already logged in, jump to splash page if (egCore.auth.user()) $location.path('/'); - egCore.hatch.getItem('eg.workstation.all') + egCore.hatch.getWorkstations() .then(function(all) { if (all && all.length) { $scope.workstations = all.map(function(a) { return a.name }); @@ -79,7 +79,7 @@ function($routeProvider , $locationProvider) { } } else { // no workstation requested; use the default - egCore.hatch.getItem('eg.workstation.default') + egCore.hatch.getDefaultWorkstation() .then(function(ws) { $scope.args = {workstation : ws} }); diff --git a/Open-ILS/web/js/ui/default/staff/offline.js b/Open-ILS/web/js/ui/default/staff/offline.js index 2a3f820c58..21e10b0dd3 100644 --- a/Open-ILS/web/js/ui/default/staff/offline.js +++ b/Open-ILS/web/js/ui/default/staff/offline.js @@ -360,7 +360,7 @@ function($routeProvider , $locationProvider , $compileProvider) { if (setting !== undefined) $scope.do_check_changed = true; }); - egCore.hatch.getItem('eg.workstation.all') + egCore.hatch.getWorkstations() .then(function(all) { if (all && all.length) { $scope.workstations = all; @@ -381,7 +381,7 @@ function($routeProvider , $locationProvider , $compileProvider) { } } else { // no workstation requested; use the default - egCore.hatch.getItem('eg.workstation.default') + egCore.hatch.getDefaultWorkstation() .then(function(ws) { var ws_obj = all.filter(function(w) { return ws == w.name diff --git a/Open-ILS/web/js/ui/default/staff/services/hatch.js b/Open-ILS/web/js/ui/default/staff/services/hatch.js index 5b6f9235d2..c7e2f39485 100644 --- a/Open-ILS/web/js/ui/default/staff/services/hatch.js +++ b/Open-ILS/web/js/ui/default/staff/services/hatch.js @@ -43,37 +43,12 @@ angular.module('egCoreMod') // which setting types exist for each setting. service.serverSettingSummaries = {}; - /** - * List string prefixes for On-Call storage keys. On-Call keys - * are those that can be set/get/remove'd from localStorage when - * Hatch is not avaialable, even though Hatch is configured as the - * primary storage location for the key in question. On-Call keys - * are those that allow the user to login and perform basic admin - * tasks (like disabling Hatch) even when Hatch is down. - * AKA Browser Staff Run Level 3. - * Note that no attempt is made to synchronize data between Hatch - * and localStorage for On-Call keys. Only one destation is active - * at a time and each maintains its own data separately. - */ - service.onCallPrefixes = ['eg.workstation']; - - // Returns true if the key can be set/get in localStorage even when - // Hatch is not available. - service.keyIsOnCall = function(key) { - var oncall = false; - angular.forEach(service.onCallPrefixes, function(pfx) { - if (key.match(new RegExp('^' + pfx))) - oncall = true; - }); - return oncall; - } - /** * Settings with these prefixes will always live in the browser. */ service.browserOnlyPrefixes = [ - 'eg.workstation', - 'eg.hatch', + 'eg.hatch.enable.settings', // deprecated + 'eg.hatch.enable.offline', // deprecated 'eg.cache', 'current_tag_table_marc21_biblio', 'FFPos', @@ -233,24 +208,169 @@ angular.module('egCoreMod') ); } - // TODO: once Hatch is printing-only, should probably store - // this preference on the server. service.usePrinting = function() { - return service.getLocalItem('eg.hatch.enable.printing'); + return service.getItem('eg.hatch.enable.printing'); } + // DEPRECATED service.useSettings = function() { return service.getLocalItem('eg.hatch.enable.settings'); } + // DEPRECATED service.useOffline = function() { return service.getLocalItem('eg.hatch.enable.offline'); } + service.getWorkstations = function() { + if (service.hatchAvailable) { + return service.mergeWorkstations().then( + function() { + service.removeLocalItem('eg.workstation.all'); + return service.getRemoteItem('eg.workstation.all'); + } + ); + } else { + return $q.when(service.getLocalItem('eg.workstation.all')); + } + } + + // See if any workstations are stored in local storage. If so, also + // see if we have any stored in Hatch. If both, merged workstations + // from localStorage in Hatch storage, skipping any whose name + // collide with a workstation in Hatch. If none exist in Hatch, + // copy the localStorage workstations over wholesale. + service.mergeWorkstations = function() { + var existing = service.getLocalItem('eg.workstation.all'); + + if (!existing || existing.length === 0) { + return $q.when(); + } + + return service.getRemoteItem('eg.workstation.all') + .then(function(inHatch) { + + if (!inHatch || inHatch.length === 0) { + // Nothing to merge, copy the data over directly + console.debug('No workstations in hatch to merge'); + return service.setRemoteItem('eg.workstation.all', existing); + } + + var addMe = []; + existing.forEach(function(ws) { + var match = inHatch.filter( + function(w) {return w.name === ws.name})[0]; + if (!match) { + console.log( + 'Migrating workstation from local storage to hatch: ' + + ws.name + ); + addMe.push(ws); + } + }); + inHatch = inHatch.concat(addMe); + return service.setRemoteItem('eg.workstation.all', inHatch); + }); + } + + service.getDefaultWorkstation = function() { + + if (service.hatchAvailable) { + return service.getRemoteItem('eg.workstation.default') + .then(function(name) { + if (name) { + // We have a default in Hatch, remove any lingering + // value from localStorage. + service.removeLocalItem('eg.workstation.default'); + return name; + } + + name = service.getLocalItem('eg.workstation.default'); + if (name) { + console.log('Migrating default workstation to Hatch ' + name); + return service.setRemoteItem('eg.workstation.default', name) + .then(function() {return name;}); + } + + return null; + }); + } else { + return $q.when(service.getLocalItem('eg.workstation.default')); + } + } + + service.setWorkstations = function(workstations, isJson) { + if (service.hatchAvailable) { + return service.setRemoteItem('eg.workstation.all', workstations); + } else { + return $q.when( + service.setLocalItem('eg.workstation.all', workstations, isJson)); + } + } + + service.setDefaultWorkstation = function(name, isJson) { + if (service.hatchAvailable) { + return service.setRemoteItem('eg.workstation.default', name); + } else { + return $q.when( + service.setLocalItem('eg.workstation.default', name, isJson)); + } + } + + service.removeWorkstations = function() { + if (service.hatchAvailable) { + return service.removeRemoteItem('eg.workstation.all'); + } else { + return $q.when( + service.removeLocalItem('eg.workstation.all')); + } + } + + service.removeDefaultWorkstation = function() { + if (service.hatchAvailable) { + return service.removeRemoteItem('eg.workstation.default'); + } else { + return $q.when( + service.removeLocalItem('eg.workstation.default')); + } + } + + + // Workstation actions always use Hatch when it's available + service.getWorkstationItem = function(key) { + if (service.hatchAvailable) { + return service.getRemoteItem(key); + } else { + return $q.when(service.getLocalItem(key)); + } + } + + service.setWorkstationItem = function(key, value) { + if (service.hatchAvailable) { + return service.setRemoteItem(key, value); + } else { + return $q.when(service.setLocalItem(key, value)); + } + } + + service.removeWorkstationItem = function(key) { + if (service.hatchAvailable) { + return service.removeRemoteItem(key); + } else { + return $q.when(service.removeLocalItem(key)); + } + } + + service.keyIsWorkstation = function(key) { + return Boolean(key.match(/eg.workstation/)); + } + // get the value for a stored item service.getItem = function(key) { - console.debug('getting item: ' + key); + if (service.keyIsWorkstation(key)) { + return service.getWorkstationItem(key); + } if (!service.keyStoredInBrowser(key)) { return service.getServerItem(key); @@ -260,14 +380,7 @@ angular.module('egCoreMod') service.getBrowserItem(key).then( function(val) { deferred.resolve(val); }, - function() { // Hatch error - if (service.keyIsOnCall(key)) { - console.warn("Unable to getItem from Hatch: " + key + - ". Retrieving item from local storage instead"); - deferred.resolve(service.getLocalItem(key)); - } - deferred.reject("Unable to getItem from Hatch: " + key); } ); @@ -388,6 +501,10 @@ angular.module('egCoreMod') */ service.setItem = function(key, value) { + if (service.keyIsWorkstation(key)) { + return service.setWorkstationItem(key, value); + } + if (!service.keyStoredInBrowser(key)) { return service.setServerItem(key, value); } @@ -397,13 +514,6 @@ angular.module('egCoreMod') function(val) {deferred.resolve(val);}, function() { // Hatch error - - if (service.keyIsOnCall(key)) { - console.warn("Unable to setItem in Hatch: " + - key + ". Setting in local storage instead"); - - deferred.resolve(service.setLocalItem(key, value)); - } deferred.reject("Unable to setItem in Hatch: " + key); } ); @@ -627,13 +737,6 @@ angular.module('egCoreMod') if (service.hatchAvailable) return service.appendRemoteItem(key, value); - if (service.keyIsOnCall(key)) { - console.warn("Unable to appendItem in Hatch: " + - key + ". Setting in local storage instead"); - - return $q.when(service.appendLocalItem(key, value)); - } - console.error("Unable to appendItem in Hatch: " + key); return $q.reject(); } @@ -686,6 +789,10 @@ angular.module('egCoreMod') // remove a stored item service.removeItem = function(key) { + if (service.keyIsWorkstation(key)) { + return service.removeWorkstationItem(key); + } + if (!service.keyStoredInBrowser(key)) { return service.removeServerItem(key); } @@ -694,14 +801,6 @@ angular.module('egCoreMod') service.removeBrowserItem(key).then( function(response) {deferred.resolve(response);}, function() { // Hatch error - - if (service.keyIsOnCall(key)) { - console.warn("Unable to removeItem from Hatch: " + key + - ". Removing item from local storage instead"); - - deferred.resolve(service.removeLocalItem(key)); - } - deferred.reject("Unable to removeItem from Hatch: " + key); } ); -- 2.43.2