LP#1750894 Cascade settings metadata
authorBill Erickson <berickxx@gmail.com>
Wed, 23 May 2018 21:01:03 +0000 (17:01 -0400)
committerBill Erickson <berickxx@gmail.com>
Tue, 29 May 2018 14:13:36 +0000 (10:13 -0400)
Return info about the supported setting types with each setting value
lookup in the cascade settings lookup func.  Return that info to the
browser.  This will make it possible for the browser to determine what
setting types are available e.g. if it needs to fall back to local
storage or expose an org-setting valuea application option.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/lib/OpenILS/Application/Actor/Settings.pm
Open-ILS/src/sql/Pg/upgrade/XXXX.schema.workstation-settings.sql
Open-ILS/web/js/ui/default/staff/services/hatch.js

index bb9f9c0..2848be8 100644 (file)
@@ -12403,6 +12403,20 @@ SELECT  usr,
                </links>
        </class>
 
+       <class id="acss" 
+               controller="open-ils.cstore" 
+               oils_obj:fieldmapper="actor::cascade_setting_summary"
+               oils_persist:virtual="true" 
+               reporter:label="Cascade Setting Summary">
+       <fields>
+               <field reporter:label="Setting Name" name="name" reporter:datatype="text"/>
+               <field reporter:label="Value" name="value" reporter:datatype="text"/>
+               <field reporter:label="Has Org Setting" name="has_org_setting" reporter:datatype="bool"/>
+               <field reporter:label="Has User Setting" name="has_user_setting" reporter:datatype="bool"/>
+               <field reporter:label="Has Workstation Setting" name="has_workstation_setting" reporter:datatype="bool"/>
+       </fields>
+       </class>
+
        <!-- ********************************************************************************************************************* -->
 </IDL>
 
index c3a4913..d76aedb 100644 (file)
@@ -72,22 +72,21 @@ sub retrieve_settings {
     my $ses = OpenSRF::AppSession->create('open-ils.cstore');
     my $req = $ses->request('open-ils.cstore.json_query', {
         from => [
-            'actor.get_setting_batch', 
+            'actor.get_cascade_setting_batch', 
             $settings_str, $aou_id, $user_id, $ws_id
         ]
     });
 
     # The DB responds in setting-name order.
     while (my $resp = $req->recv) {
-        my $json = $resp->content->{'actor.get_setting_batch'};
-        $client->respond(
-            {$settings->[0] => OpenSRF::Utils::JSON->JSON2perl($json)})
-            if defined $json;
-        shift @$settings;
+        my $sumhash = $resp->content;
+        my $summary = Fieldmapper::actor::cascade_setting_summary->new;
+        $summary->$_($sumhash->{$_}) for keys %$sumhash;
+        $summary->value(OpenSRF::Utils::JSON->JSON2perl($summary->value));
+        $client->respond($summary);
     }
 
     $ses->kill_me;
-
     return undef;
 }
 
index ddb927c..85b3b0d 100644 (file)
@@ -1,5 +1,15 @@
 BEGIN;
 
+/*
+
+CREATE TYPE actor.cascade_setting_summary AS (
+    name TEXT,
+    value JSON,
+    has_org_setting BOOLEAN,
+    has_user_setting BOOLEAN,
+    has_workstation_setting BOOLEAN
+);
+
 -- SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
 
 CREATE TABLE config.workstation_setting_type (
@@ -66,41 +76,88 @@ CREATE CONSTRAINT TRIGGER check_setting_is_usr_or_ws
   AFTER INSERT OR UPDATE ON config.workstation_setting_type
   FOR EACH ROW EXECUTE PROCEDURE config.setting_is_user_or_ws();
 
-CREATE OR REPLACE FUNCTION actor.get_setting(setting_name TEXT, 
-    org_id INT, user_id INT, workstation_id INT) RETURNS JSON AS
+  */
+
+CREATE OR REPLACE FUNCTION actor.get_cascade_setting(
+    setting_name TEXT, org_id INT, user_id INT, workstation_id INT) 
+    RETURNS actor.cascade_setting_summary AS
 $FUNC$
 DECLARE
     setting_value JSON;
+    summary actor.cascade_setting_summary;
     org_setting_type config.org_unit_setting_type%ROWTYPE;
 BEGIN
 
+    summary.name := setting_name;
+
+    -- Collect the org setting type status first in case we exit early.
+    -- The existance of an org setting type is not considered
+    -- privileged information.
+    SELECT INTO org_setting_type * 
+        FROM config.org_unit_setting_type WHERE name = setting_name;
+    IF FOUND THEN
+        summary.has_org_setting := TRUE;
+    ELSE
+        summary.has_org_setting := FALSE;
+    END IF;
+
     -- User and workstation settings have the same priority.
     -- Start with user settings since that's the simplest code path.
+    -- The workstation_id is ignored if no user_id is provided.
     IF user_id IS NOT NULL THEN
-        SELECT INTO setting_value value 
-            FROM actor.usr_setting
+
+        SELECT INTO summary.value value FROM actor.usr_setting
             WHERE usr = user_id AND name = setting_name;
-        IF FOUND THEN
-            RETURN setting_value;
-        END IF;
-    END IF;
 
-    -- No user setting value found.  Next try workstation.
-    IF workstation_id IS NOT NULL THEN
+        IF FOUND THEN
+            -- if we have a value, we have a setting type
+            summary.has_user_setting := TRUE;
 
-        SELECT INTO setting_value value 
-            FROM actor.workstation_setting         
-            WHERE workstation = workstation_id AND name = setting_name;
+            IF workstation_id THEN
+                -- Only inform the caller about the workstation
+                -- setting type disposition when a workstation id is
+                -- provided.  Otherwise, it's NULL to indicate UNKNOWN.
+                summary.has_workstation_setting := FALSE;
+            END IF;
 
-        IF FOUND THEN
-            RETURN setting_value;
+            RETURN summary;
         END IF;
 
-        -- Workstation setting not found.  However, since we have a
-        -- workstation let's use its owning_lib if necessary.
-        IF org_id IS NULL THEN
-            SELECT INTO org_id owning_lib 
-                FROM actor.workstation WHERE id = workstation_id;
+        -- no user setting value, but a setting type may exist
+        SELECT INTO summary.has_user_setting EXISTS (
+            SELECT TRUE FROM config.usr_setting_type 
+            WHERE name = setting_name
+        );
+
+        IF workstation_id IS NOT NULL THEN 
+
+            IF NOT summary.has_user_setting THEN
+                -- A workstation setting type may only exist when a user
+                -- setting type does not.
+
+                SELECT INTO summary.value value 
+                    FROM actor.workstation_setting         
+                    WHERE workstation = workstation_id AND name = setting_name;
+
+                IF FOUND THEN
+                    -- if we have a value, we have a setting type
+                    summary.has_workstation_setting := TRUE;
+                    RETURN summary;
+                END IF;
+
+                -- no value, but a setting type may exist
+                SELECT INTO summary.has_workstation_setting EXISTS (
+                    SELECT TRUE FROM config.workstation_setting_type 
+                    WHERE name = setting_name
+                );
+            END IF;
+
+            -- Finally make use of the workstation to determine the org
+            -- unit if none is provided.
+            IF org_id IS NULL AND summary.has_org_setting THEN
+                SELECT INTO org_id owning_lib 
+                    FROM actor.workstation WHERE id = workstation_id;
+            END IF;
         END IF;
     END IF;
 
@@ -108,23 +165,25 @@ BEGIN
     -- First see if we have any data that needs protecting, then 
     -- check the permission if needed.
 
+    IF NOT summary.has_org_setting THEN
+        RETURN summary;
+    END IF;
+
+    -- avoid putting the value into the summary until we confirm
+    -- the value should be visible to the caller.
     SELECT INTO setting_value value 
         FROM actor.org_unit_ancestor_setting(setting_name, org_id);
 
     IF NOT FOUND THEN
         -- No value found -- perm check is irrelevant.
-        RETURN NULL;
+        RETURN summary;
     END IF;
 
-    -- Check view permissions if necessary.
-    SELECT INTO org_setting_type * 
-        FROM config.org_unit_setting_type WHERE name = setting_name;
-
     IF org_setting_type.view_perm IS NOT NULL THEN
 
         IF user_id IS NULL THEN
             RAISE NOTICE 'Perm check required but no user_id provided';
-            RETURN NULL;
+            RETURN summary;
         END IF;
 
         IF NOT permission.usr_has_perm(
@@ -133,27 +192,31 @@ BEGIN
         THEN
             RAISE NOTICE 'Perm check failed for user % on %',
                 user_id, org_setting_type.view_perm;
-            RETURN NULL;
+            RETURN summary;
         END IF;
     END IF;
 
     -- Perm check succeeded or was not necessary.
-    RETURN setting_value;
+    summary.value := setting_value;
+    RETURN summary;
 END;
 $FUNC$ LANGUAGE PLPGSQL;
 
 
-CREATE OR REPLACE FUNCTION actor.get_setting_batch(setting_names TEXT[], 
-    org_id INT, user_id INT, workstation_id INT) RETURNS SETOF JSON AS
+CREATE OR REPLACE FUNCTION actor.get_cascade_setting_batch(
+    setting_names TEXT[], org_id INT, user_id INT, workstation_id INT) 
+    RETURNS SETOF actor.cascade_setting_summary AS
 $FUNC$
 -- Returns a row per setting matching the setting name order.  If no 
 -- value is applied, NULL is returned to retain name-response ordering.
 DECLARE
     setting_name TEXT;
+    summary actor.cascade_setting_summary;
 BEGIN
     FOREACH setting_name IN ARRAY setting_names LOOP
-        RETURN NEXT * FROM actor.get_setting(
+        SELECT INTO summary * FROM actor.get_cascade_setting(
             setting_Name, org_id, user_id, workstation_id);
+        RETURN NEXT summary;
     END LOOP;
 END;
 $FUNC$ LANGUAGE PLPGSQL;
index e9d4568..c360b79 100644 (file)
@@ -361,8 +361,9 @@ angular.module('egCoreMod')
     }
 
     service.getServerItem = function(key) {
-        if (service.keyCache[key] != undefined)
+        if (key in service.keyCache) {
             return $q.when(service.keyCache[key])
+        }
 
         if (!service.auth) service.auth = $injector.get('egAuth');
         if (!service.auth.token()) return $q.when(null);
@@ -372,7 +373,10 @@ angular.module('egCoreMod')
             'open-ils.actor.settings.retrieve.atomic',
             [key], service.auth.token()
         ).then(function(settings) {
-            service.keyCache[key] = settings[0] ? settings[0][key] : null;
+            var val = settings[0].value();
+            // The server returns null for undefined settings.
+            // Treat as undefined for backwards compat.
+            service.keyCache[key] = (val === null) ? undefined : val;
             return service.keyCache[key];
         });
     }
@@ -464,20 +468,15 @@ angular.module('egCoreMod')
             'open-ils.actor.settings.retrieve',
             keys, service.auth.token()
         ).then(
-            function() {
-                // cache a value of null for any settings that have
-                // no server values applied
-                angular.forEach(keys, function(key) {
-                    if (Object.keys(foundValues).indexOf(key) == -1) 
-                        service.keyCache[key] = foundValues[key] = null;
-                });
-                return foundValues;
-            }, 
-            null,
+            function() { return foundValues; }, 
+            function() {},
             function(setting) {
-                // when streaming we receive one setting per response
-                var key = Object.keys(setting)[0];
-                service.keyCache[key] = foundValues[key] = setting[key];
+                var val = setting.value();
+                // The server returns null for undefined settings.
+                // Treat as undefined for backwards compat.
+                service.keyCache[setting.name()] = 
+                    foundValues[setting.name()] = 
+                    (val === null) ? undefined : val;
             }
         );
     }