Răsfoiți Sursa

[frontend] Consolidate localStorage logic in storageUtils

Johan Ahlen 5 ani în urmă
părinte
comite
198bcf8d8d
65 a modificat fișierele cu 435 adăugiri și 451 ștergeri
  1. 4 1
      apps/beeswax/src/beeswax/templates/create_database.mako
  2. 4 1
      apps/beeswax/src/beeswax/templates/create_table_manually.mako
  3. 5 7
      apps/beeswax/src/beeswax/templates/execute.mako
  4. 4 2
      apps/beeswax/src/beeswax/templates/import_wizard_choose_delimiter.mako
  5. 4 1
      apps/beeswax/src/beeswax/templates/import_wizard_choose_file.mako
  6. 4 2
      apps/beeswax/src/beeswax/templates/import_wizard_define_columns.mako
  7. 2 2
      apps/filebrowser/src/filebrowser/templates/listdir_components.mako
  8. 5 1
      apps/jobbrowser/src/jobbrowser/templates/job_browser.mako
  9. 2 2
      apps/oozie/src/oozie/templates/editor2/workflow_editor.mako
  10. 22 89
      desktop/core/src/desktop/js/api/apiHelper.js
  11. 5 4
      desktop/core/src/desktop/js/apps/notebook/editorViewModel.js
  12. 3 3
      desktop/core/src/desktop/js/apps/notebook/notebook.js
  13. 8 9
      desktop/core/src/desktop/js/apps/notebook/snippet.js
  14. 26 30
      desktop/core/src/desktop/js/apps/notebook2/components/aceEditor/AceEditor.vue
  15. 3 4
      desktop/core/src/desktop/js/apps/notebook2/components/aceEditor/AceLocationHandler.ts
  16. 5 4
      desktop/core/src/desktop/js/apps/notebook2/editorViewModel.js
  17. 9 10
      desktop/core/src/desktop/js/apps/notebook2/snippet.js
  18. 2 2
      desktop/core/src/desktop/js/apps/tableBrowser/metastoreDatabase.js
  19. 4 4
      desktop/core/src/desktop/js/apps/tableBrowser/metastoreNamespace.js
  20. 3 4
      desktop/core/src/desktop/js/apps/tableBrowser/metastoreSource.js
  21. 10 10
      desktop/core/src/desktop/js/apps/tableBrowser/metastoreViewModel.js
  22. 3 2
      desktop/core/src/desktop/js/components/SqlText.vue
  23. 1 1
      desktop/core/src/desktop/js/jquery/plugins/jquery.filechooser.js
  24. 2 1
      desktop/core/src/desktop/js/jquery/plugins/jquery.hdfs.autocomplete.js
  25. 1 1
      desktop/core/src/desktop/js/jquery/plugins/jquery.hiveautocomplete.js
  26. 8 11
      desktop/core/src/desktop/js/ko/bindings/ace/aceLocationHandler.js
  27. 18 27
      desktop/core/src/desktop/js/ko/bindings/ace/ko.aceEditor.js
  28. 1 1
      desktop/core/src/desktop/js/ko/bindings/ace/ko.aceResizer.js
  29. 3 3
      desktop/core/src/desktop/js/ko/bindings/ko.assistVerticalResizer.js
  30. 1 1
      desktop/core/src/desktop/js/ko/bindings/ko.highlight.js
  31. 1 1
      desktop/core/src/desktop/js/ko/bindings/ko.logResizer.js
  32. 1 1
      desktop/core/src/desktop/js/ko/bindings/ko.readOnlyAce.js
  33. 1 1
      desktop/core/src/desktop/js/ko/bindings/ko.splitDraggable.js
  34. 1 1
      desktop/core/src/desktop/js/ko/bindings/ko.splitFlexDraggable.js
  35. 10 13
      desktop/core/src/desktop/js/ko/components/assist/assistDbNamespace.js
  36. 4 6
      desktop/core/src/desktop/js/ko/components/assist/assistDbSource.js
  37. 4 10
      desktop/core/src/desktop/js/ko/components/assist/assistInnerPanel.js
  38. 21 20
      desktop/core/src/desktop/js/ko/components/assist/ko.assistDbPanel.js
  39. 9 17
      desktop/core/src/desktop/js/ko/components/assist/ko.assistDocumentsPanel.js
  40. 8 9
      desktop/core/src/desktop/js/ko/components/assist/ko.assistFunctionsPanel.js
  41. 3 7
      desktop/core/src/desktop/js/ko/components/assist/ko.assistGitPanel.js
  42. 3 7
      desktop/core/src/desktop/js/ko/components/assist/ko.assistHBasePanel.js
  43. 3 3
      desktop/core/src/desktop/js/ko/components/assist/ko.assistPanel.js
  44. 9 9
      desktop/core/src/desktop/js/ko/components/assist/ko.assistStoragePanel.js
  45. 13 13
      desktop/core/src/desktop/js/ko/components/assist/ko.rightAssistPanel.js
  46. 7 7
      desktop/core/src/desktop/js/ko/components/contextPopover/ko.contextPopover.js
  47. 2 2
      desktop/core/src/desktop/js/ko/components/contextPopover/resizeHelper.js
  48. 10 24
      desktop/core/src/desktop/js/ko/components/ko.contextSelector.js
  49. 9 10
      desktop/core/src/desktop/js/ko/components/ko.performanceGraph.js
  50. 4 4
      desktop/core/src/desktop/js/ko/components/ko.sidebar.js
  51. 2 1
      desktop/core/src/desktop/js/ko/components/simpleAceEditor/ko.simpleAceEditor.js
  52. 5 5
      desktop/core/src/desktop/js/sidePanelViewModel.js
  53. 2 2
      desktop/core/src/desktop/js/topNavViewModel.js
  54. 0 5
      desktop/core/src/desktop/js/utils/hdfsAutocompleter.js
  55. 3 2
      desktop/core/src/desktop/js/utils/hdfsAutocompleter.test.js
  56. 11 22
      desktop/core/src/desktop/js/utils/hueUtils.ts
  57. 90 0
      desktop/core/src/desktop/js/utils/storageUtils.ts
  58. 4 1
      desktop/core/src/desktop/static/desktop/js/home2.vm.js
  59. 4 1
      desktop/core/src/desktop/templates/assist_m.mako
  60. 2 2
      desktop/core/src/desktop/templates/sql_syntax_dropdown.mako
  61. 1 1
      desktop/libs/dashboard/src/dashboard/templates/common_search.mako
  62. 4 1
      desktop/libs/indexer/src/indexer/templates/importer.mako
  63. 4 1
      desktop/libs/indexer/src/indexer/templates/indexer.mako
  64. 4 1
      desktop/libs/indexer/src/indexer/templates/indexes.mako
  65. 4 1
      desktop/libs/indexer/src/indexer/templates/topics.mako

+ 4 - 1
apps/beeswax/src/beeswax/templates/create_database.mako

@@ -222,7 +222,10 @@ ${layout.metastore_menubar()}
 
       self.assistAvailable = ko.observable(true);
       self.isLeftPanelVisible = ko.observable();
-      self.apiHelper.withLocalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
+      window.hueUtils.withLocalStorage('assist.assist_panel_visible', self.isLeftPanelVisible, true);
+      self.isLeftPanelVisible.subscribe(function () {
+        huePubSub.publish('assist.forceRender');
+      });
 
       huePubSub.subscribe("assist.table.selected", function (entry) {
         location.href = '/metastore/table/' + entry.path[0] + '/' + entry.name + '?connector_id=' + entry.getConnector().id + '&namespace=' + entry.namespace.id;

+ 4 - 1
apps/beeswax/src/beeswax/templates/create_table_manually.mako

@@ -542,7 +542,10 @@ ${ layout.metastore_menubar() }
       self.apiHelper = window.apiHelper;
       self.assistAvailable = ko.observable(true);
       self.isLeftPanelVisible = ko.observable();
-      self.apiHelper.withLocalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
+      window.hueUtils.withLocalStorage('assist.assist_panel_visible', self.isLeftPanelVisible, true);
+      self.isLeftPanelVisible.subscribe(function () {
+        huePubSub.publish('assist.forceRender');
+      });
 
       huePubSub.subscribe("assist.table.selected", function (entry) {
         location.href = '/metastore/table/' + entry.path[0] + '/' + entry.name + '?connector_id=' + entry.getConnector().id + '&namespace=' + entry.namespace.id;

+ 5 - 7
apps/beeswax/src/beeswax/templates/execute.mako

@@ -1170,8 +1170,6 @@ var autocompleter = new AceAutocompleteWrapper({
   timeout: AUTOCOMPLETE_TIMEOUT
 });
 
-var localStorageUserPrefix = apiHelper.getlocalStorageUserPrefix(snippetType);
-
 var truncateOutput = function (obj) {
   //default to 20 characters (column output displays first 21 chars so we need to consider the length of both column name and type
   var chars = obj.chars || 20,
@@ -1709,9 +1707,9 @@ $(document).ready(function () {
   });
 
   % if not (design and design.id) and not ( query_history and query_history.id ):
-    if (hueUtils.hueLocalStorage(localStorageUserPrefix + "${app_name}_temp_query") != null && hueUtils.hueLocalStorage(localStorageUserPrefix + "${app_name}_temp_query") != "") {
+    if (hueUtils.hueLocalStorage("${app_name}_temp_query") != null && hueUtils.hueLocalStorage("${app_name}_temp_query") != "") {
       viewModel.queryEditorBlank(true);
-      codeMirror.setValue(hueUtils.hueLocalStorage(localStorageUserPrefix + "${app_name}_temp_query"));
+      codeMirror.setValue(hueUtils.hueLocalStorage("${app_name}_temp_query"));
     }
   % endif
 
@@ -2089,7 +2087,7 @@ function addResults(viewModel, dataTable, startRow, nextRow) {
 
 function resultsTable(e, data) {
   $("#results .dataTables_wrapper").animate({opacity: '1'}, 50);
-  hueUtils.hueLocalStorage(localStorageUserPrefix + "${app_name}_temp_query", null);
+  hueUtils.hueLocalStorage("${app_name}_temp_query", null);
   if (viewModel.design.results.columns().length > 0) {
     if (!dataTable) {
       if (viewModel.design.results.columns().length < 500) {
@@ -2369,7 +2367,7 @@ function tryCancelQuery() {
 }
 
 function createNewQuery() {
-  hueUtils.hueLocalStorage(localStorageUserPrefix + "${app_name}_temp_query", null);
+  hueUtils.hueLocalStorage("${app_name}_temp_query", null);
   location.href="${ url(app_name + ':execute_query') }";
 }
 
@@ -2765,7 +2763,7 @@ function cacheQueryTextEvents() {
     if (typeof codeMirror != "undefined") {
       codeMirror.on("change", function () {
         $(".query").val(codeMirror.getValue());
-        hueUtils.hueLocalStorage(localStorageUserPrefix + "${app_name}_temp_query", codeMirror.getValue());
+        hueUtils.hueLocalStorage("${app_name}_temp_query", codeMirror.getValue());
       });
       window.clearInterval(_waitForCodemirrorInit);
     }

+ 4 - 2
apps/beeswax/src/beeswax/templates/import_wizard_choose_delimiter.mako

@@ -182,8 +182,10 @@ ${ layout.metastore_menubar() }
       self.apiHelper = window.apiHelper;
       self.assistAvailable = ko.observable(true);
       self.isLeftPanelVisible = ko.observable();
-      self.apiHelper.withLocalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
-
+      window.hueUtils.withLocalStorage('assist.assist_panel_visible', self.isLeftPanelVisible, true);
+      self.isLeftPanelVisible.subscribe(function () {
+        huePubSub.publish('assist.forceRender');
+      });
 
       huePubSub.subscribe("assist.table.selected", function (entry) {
         location.href = '/metastore/table/' + entry.path[0] + '/' + entry.name + '?connector_id=' + entry.getConnector().id + '&namespace=' + entry.namespace.id;

+ 4 - 1
apps/beeswax/src/beeswax/templates/import_wizard_choose_file.mako

@@ -189,7 +189,10 @@ ${ layout.metastore_menubar() }
       self.apiHelper = window.apiHelper;
       self.assistAvailable = ko.observable(true);
       self.isLeftPanelVisible = ko.observable();
-      self.apiHelper.withLocalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
+      window.hueUtils.withLocalStorage('assist.assist_panel_visible', self.isLeftPanelVisible, true);
+      self.isLeftPanelVisible.subscribe(function () {
+        huePubSub.publish('assist.forceRender');
+      });
 
 
       huePubSub.subscribe("assist.table.selected", function (entry) {

+ 4 - 2
apps/beeswax/src/beeswax/templates/import_wizard_define_columns.mako

@@ -217,8 +217,10 @@ ${ layout.metastore_menubar() }
       self.apiHelper = window.apiHelper;
       self.assistAvailable = ko.observable(true);
       self.isLeftPanelVisible = ko.observable();
-      self.apiHelper.withLocalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
-
+      window.hueUtils.withLocalStorage('assist.assist_panel_visible', self.isLeftPanelVisible, true);
+      self.isLeftPanelVisible.subscribe(function () {
+        huePubSub.publish('assist.forceRender');
+      });
 
       huePubSub.subscribe("assist.table.selected", function (entry) {
         location.href = '/metastore/table/' + entry.path[0] + '/' + entry.name + '?connector_id=' + entry.getConnector().id + '&namespace=' + entry.namespace.id;

+ 2 - 2
apps/filebrowser/src/filebrowser/templates/listdir_components.mako

@@ -929,7 +929,7 @@ from filebrowser.conf import ENABLE_EXTRACT_UPLOADED_ARCHIVE
 
       self.page = ko.observable(new Page(page));
       self.recordsPerPageChoices = ["15", "30", "45", "60", "100", "200", "1000"],
-      self.recordsPerPage = ko.observable(apiHelper.getFromLocalStorage('fb', 'records_per_page', 45));
+      self.recordsPerPage = ko.observable(hueUtils.hueLocalStorage('fb.records_per_page') || 45);
       self.targetPageNum = ko.observable(1);
       self.targetPath = ko.observable("${current_request_path | n,unicode }");
       self.sortBy = ko.observable("name");
@@ -1263,7 +1263,7 @@ from filebrowser.conf import ENABLE_EXTRACT_UPLOADED_ARCHIVE
       };
 
       self.recordsPerPage.subscribe(function (newValue) {
-        apiHelper.setInLocalStorage('fb', 'records_per_page', newValue)
+        window.hueUtils.hueLocalStorage('fb.records_per_page', newValue)
         self.retrieveData();
       });
 

+ 5 - 1
apps/jobbrowser/src/jobbrowser/templates/job_browser.mako

@@ -3884,7 +3884,11 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       self.apiHelper = window.apiHelper;
       self.assistAvailable = ko.observable(true);
       self.isLeftPanelVisible = ko.observable();
-      self.apiHelper.withLocalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
+      window.hueUtils.withLocalStorage('assist.assist_panel_visible', self.isLeftPanelVisible, true);
+      self.isLeftPanelVisible.subscribe(function () {
+        huePubSub.publish('assist.forceRender');
+      });
+
       self.appConfig = ko.observable();
       self.clusterType = ko.observable();
       self.isMini = ko.observable(false);

+ 2 - 2
apps/oozie/src/oozie/templates/editor2/workflow_editor.mako

@@ -632,7 +632,7 @@ ${ utils.submit_popup_event() }
 
 
   % if ENABLE_DOCUMENT_ACTION.get():
-  var defaultSection = apiHelper.getFromLocalStorage('oozie', 'draggable_section', 'documents');
+  var defaultSection = hueUtils.hueLocalStorage('oozie.draggable_section') || 'documents';
   % else:
   var defaultSection = 'actions';
   % endif
@@ -887,7 +887,7 @@ ${ utils.submit_popup_event() }
     }, 'oozie');
 
     huePubSub.subscribe('oozie.draggable.section.change', function(val){
-      apiHelper.setInLocalStorage('oozie', 'draggable_section', val);
+      window.hueUtils.hueLocalStorage('oozie.draggable_section', val);
     });
 
     $(document).on("click", ".widget-main-section", function (e) {

+ 22 - 89
desktop/core/src/desktop/js/api/apiHelper.js

@@ -24,12 +24,13 @@ import {
   simplePost,
   successResponseIsError
 } from './apiUtils';
+import * as URLS from './urls';
 import apiQueueManager from 'api/apiQueueManager';
 import CancellableJqPromise from 'api/cancellableJqPromise';
 import hueDebug from 'utils/hueDebug';
 import huePubSub from 'utils/huePubSub';
-import hueUtils, { hueLocalStorage } from 'utils/hueUtils';
-import * as URLS from './urls';
+import hueUtils from 'utils/hueUtils';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 export const LINK_SHARING_PERMS = {
   READ: 'read',
@@ -62,19 +63,19 @@ class ApiHelper {
     this.cancelActiveRequest = cancelActiveRequest; // TODO: Remove when job_browser.mako is in webpack
 
     huePubSub.subscribe('assist.clear.git.cache', () => {
-      hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'git' }), {});
+      setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'git' }), {});
     });
 
     huePubSub.subscribe('assist.clear.collections.cache', () => {
-      hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'collections' }), {});
+      setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'collections' }), {});
     });
 
     huePubSub.subscribe('assist.clear.hbase.cache', () => {
-      hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'hbase' }), {});
+      setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'hbase' }), {});
     });
 
     huePubSub.subscribe('assist.clear.document.cache', () => {
-      hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'document' }), {});
+      setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'document' }), {});
     });
 
     const clearAllCaches = () => {
@@ -86,14 +87,14 @@ class ApiHelper {
         sourceType: 'impala',
         clearAll: true
       });
-      hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'hdfs' }), {});
-      hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'adls' }), {});
-      hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'abfs' }), {});
-      hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'git' }), {});
-      hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: 's3' }), {});
-      hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'collections' }), {});
-      hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'hbase' }), {});
-      hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'document' }), {});
+      setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'hdfs' }), {});
+      setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'adls' }), {});
+      setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'abfs' }), {});
+      setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'git' }), {});
+      setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 's3' }), {});
+      setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'collections' }), {});
+      setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'hbase' }), {});
+      setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'document' }), {});
     };
 
     huePubSub.subscribe('assist.clear.all.caches', clearAllCaches);
@@ -107,7 +108,7 @@ class ApiHelper {
   }
 
   clearStorageCache(sourceType) {
-    hueLocalStorage(this.getAssistCacheIdentifier({ sourceType: sourceType }), {});
+    setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: sourceType }), {});
   }
 
   hasExpired(timestamp, cacheType) {
@@ -132,7 +133,7 @@ class ApiHelper {
    */
   fetchCached(options) {
     const cacheIdentifier = this.getAssistCacheIdentifier(options);
-    const cachedData = hueLocalStorage(cacheIdentifier) || {};
+    const cachedData = getFromLocalStorage(cacheIdentifier) || {};
     const cachedId = options.hash ? options.url + options.hash : options.url;
 
     if (
@@ -149,7 +150,7 @@ class ApiHelper {
           data: data
         };
         try {
-          hueLocalStorage(cacheIdentifier, cachedData);
+          setInLocalStorage(cacheIdentifier, cachedData);
         } catch (e) {}
       });
     } else {
@@ -161,14 +162,6 @@ class ApiHelper {
     }
   }
 
-  /**
-   * @param {string} sourceType
-   * @returns {string}
-   */
-  getlocalStorageUserPrefix(sourceType) {
-    return sourceType + '_' + window.LOGGED_USERNAME + '_' + window.location.hostname;
-  }
-
   /**
    * @param {object} options
    * @param {string} options.sourceType
@@ -176,67 +169,7 @@ class ApiHelper {
    * @returns {string}
    */
   getAssistCacheIdentifier(options) {
-    return (
-      'hue.assist.' +
-      (options.cacheType || 'default') +
-      '.' +
-      this.getlocalStorageUserPrefix(options.sourceType)
-    );
-  }
-
-  /**
-   *
-   * @param {string} owner - 'assist', 'viewModelA' etc.
-   * @param {string} id
-   * @param {*} [value] - Optional, undefined and null will remove the value
-   */
-  setInLocalStorage(owner, id, value) {
-    try {
-      const cachedData =
-        hueLocalStorage('hue.user.settings.' + this.getlocalStorageUserPrefix(owner)) || {};
-      if (typeof value !== 'undefined' && value !== null) {
-        cachedData[id] = value;
-        hueLocalStorage('hue.user.settings.' + this.getlocalStorageUserPrefix(owner), cachedData);
-      } else if (cachedData[id]) {
-        delete cachedData[id];
-        hueLocalStorage('hue.user.settings.' + this.getlocalStorageUserPrefix(owner), cachedData);
-      }
-    } catch (e) {}
-  }
-
-  /**
-   *
-   * @param {string} owner - 'assist', 'viewModelA' etc.
-   * @param {string} id
-   * @param {*} [defaultValue]
-   * @returns {*}
-   */
-  getFromLocalStorage(owner, id, defaultValue) {
-    const cachedData =
-      hueLocalStorage('hue.user.settings.' + this.getlocalStorageUserPrefix(owner)) || {};
-    return typeof cachedData[id] !== 'undefined' ? cachedData[id] : defaultValue;
-  }
-
-  /**
-   * @param {string} owner - 'assist', 'viewModelA' etc.
-   * @param {string} id
-   * @param {Observable} observable
-   * @param {*} [defaultValue] - Optional default value to use if not in total storage initially
-   */
-  withLocalStorage(owner, id, observable, defaultValue, noInit) {
-    const cachedValue = this.getFromLocalStorage(owner, id, defaultValue);
-
-    if (!noInit && cachedValue !== 'undefined') {
-      observable(cachedValue);
-    }
-
-    observable.subscribe(newValue => {
-      if (owner === 'assist' && id === 'assist_panel_visible') {
-        huePubSub.publish('assist.forceRender');
-      }
-      this.setInLocalStorage(owner, id, newValue);
-    });
-    return observable;
+    return 'hue.assist.' + (options.cacheType || 'default') + '.' + options.sourceType;
   }
 
   /**
@@ -1258,7 +1191,7 @@ class ApiHelper {
   clearDbCache(options) {
     const cacheIdentifier = this.getAssistCacheIdentifier(options);
     if (options.clearAll) {
-      hueLocalStorage(cacheIdentifier, {});
+      setInLocalStorage(cacheIdentifier, {});
     } else {
       let url = URLS.AUTOCOMPLETE_API_PREFIX;
       if (options.databaseName) {
@@ -1270,9 +1203,9 @@ class ApiHelper {
       if (options.fields) {
         url += options.fields.length > 0 ? '/' + options.fields.join('/') : '';
       }
-      const cachedData = hueLocalStorage(cacheIdentifier) || {};
+      const cachedData = getFromLocalStorage(cacheIdentifier) || {};
       delete cachedData[url];
-      hueLocalStorage(cacheIdentifier, cachedData);
+      getFromLocalStorage(cacheIdentifier, cachedData);
     }
   }
 

+ 5 - 4
desktop/core/src/desktop/js/apps/notebook/editorViewModel.js

@@ -30,6 +30,7 @@ import {
   GET_ACTIVE_SNIPPET_CONNECTOR_EVENT
 } from 'apps/notebook2/events';
 import { CONFIG_REFRESHED_EVENT, findEditorConnector, getLastKnownConfig } from 'utils/hueConfig';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 class EditorViewModel {
   constructor(editor_id, notebooks, options, CoordinatorEditorViewModel, RunningCoordinatorModel) {
@@ -281,21 +282,21 @@ class EditorViewModel {
     self.assistWithoutStorage = ko.observable(false);
 
     self.isLeftPanelVisible = ko.observable(
-      apiHelper.getFromLocalStorage('assist', 'assist_panel_visible', true)
+      getFromLocalStorage('assist.assist_panel_visible', true)
     );
     self.isLeftPanelVisible.subscribe(val => {
       if (!self.assistWithoutStorage()) {
-        apiHelper.setInLocalStorage('assist', 'assist_panel_visible', val);
+        setInLocalStorage('assist.assist_panel_visible', val);
       }
     });
 
     self.isRightPanelAvailable = ko.observable(options.assistAvailable && HAS_OPTIMIZER);
     self.isRightPanelVisible = ko.observable(
-      apiHelper.getFromLocalStorage('assist', 'right_assist_panel_visible', true)
+      getFromLocalStorage('assist.right_assist_panel_visible', true)
     );
     self.isRightPanelVisible.subscribe(val => {
       if (!self.assistWithoutStorage()) {
-        apiHelper.setInLocalStorage('assist', 'right_assist_panel_visible', val);
+        setInLocalStorage('assist.right_assist_panel_visible', val);
       }
     });
 

+ 3 - 3
desktop/core/src/desktop/js/apps/notebook/notebook.js

@@ -30,6 +30,7 @@ import {
   ASSIST_IS_DB_PANEL_READY_EVENT,
   ASSIST_SET_DATABASE_EVENT
 } from 'ko/components/assist/events';
+import { getFromLocalStorage } from 'utils/storageUtils';
 
 const NOTEBOOK_MAPPING = {
   ignore: [
@@ -912,9 +913,8 @@ class Notebook {
       () => {
         if (self.type().indexOf('query') === 0) {
           const whenDatabaseAvailable = function (snippet) {
-            const lastSelectedDb = apiHelper.getFromLocalStorage(
-              'assist_' + snippet.type() + '_' + snippet.namespace().id,
-              'lastSelectedDb'
+            const lastSelectedDb = getFromLocalStorage(
+              'assist_' + snippet.type() + '_' + snippet.namespace().id + '.lastSelectedDb'
             );
 
             huePubSub.publish(ASSIST_SET_DATABASE_EVENT, {

+ 8 - 9
desktop/core/src/desktop/js/apps/notebook/snippet.js

@@ -24,7 +24,8 @@ import apiHelper from 'api/apiHelper';
 import dataCatalog from 'catalog/dataCatalog';
 import hueAnalytics from 'utils/hueAnalytics';
 import huePubSub from 'utils/huePubSub';
-import hueUtils, { hueLocalStorage } from 'utils/hueUtils';
+import hueUtils from 'utils/hueUtils';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 import Result from 'apps/notebook/result';
 import Session from 'apps/notebook/session';
 import sqlStatementsParser from 'parse/sqlStatementsParser';
@@ -345,7 +346,7 @@ class Snippet {
 
     self.database.subscribe(newValue => {
       if (newValue !== null) {
-        apiHelper.setInLocalStorage('editor', 'last.selected.database', newValue);
+        setInLocalStorage('editor.last.selected.database', newValue);
         if (previousDatabase !== null && previousDatabase !== newValue) {
           huePubSub.publish(REFRESH_STATEMENT_LOCATIONS_EVENT, self.id());
         }
@@ -1021,8 +1022,8 @@ class Snippet {
         : false
     );
     let defaultShowLogs = true;
-    if (vm.editorMode() && hueLocalStorage('hue.editor.showLogs')) {
-      defaultShowLogs = hueLocalStorage('hue.editor.showLogs');
+    if (vm.editorMode() && getFromLocalStorage('hue.editor.showLogs')) {
+      defaultShowLogs = getFromLocalStorage('hue.editor.showLogs');
     }
     self.showLogs = ko.observable(
       typeof snippet.showLogs !== 'undefined' && snippet.showLogs != null
@@ -1192,7 +1193,7 @@ class Snippet {
         self.getLogs();
       }
       if (vm.editorMode()) {
-        hueLocalStorage('hue.editor.showLogs', val);
+        setInLocalStorage('hue.editor.showLogs', val);
       }
     });
 
@@ -1455,12 +1456,10 @@ class Snippet {
     });
     self.compatibilityTargetPlatform = ko.observable(COMPATIBILITY_TARGET_PLATFORMS[self.type()]);
 
-    self.showOptimizer = ko.observable(
-      apiHelper.getFromLocalStorage('editor', 'show.optimizer', false)
-    );
+    self.showOptimizer = ko.observable(getFromLocalStorage('editor.show.optimizer', false));
     self.showOptimizer.subscribe(newValue => {
       if (newValue !== null) {
-        apiHelper.setInLocalStorage('editor', 'show.optimizer', newValue);
+        setInLocalStorage('editor.show.optimizer', newValue);
       }
     });
 

+ 26 - 30
desktop/core/src/desktop/js/apps/notebook2/components/aceEditor/AceEditor.vue

@@ -24,31 +24,31 @@
 </template>
 
 <script lang="ts">
-  import { CURSOR_POSITION_CHANGED_EVENT } from 'ko/bindings/ace/aceLocationHandler';
-  import { INSERT_AT_CURSOR_EVENT } from 'ko/bindings/ace/ko.aceEditor';
-  import { IdentifierChainEntry, ParsedLocation } from 'parse/types';
-  import huePubSub from 'utils/huePubSub';
-  import AceAutocomplete from './autocomplete/AceAutocomplete.vue';
   import $ from 'jquery';
-  import { EditorInterpreter } from 'types/config';
   import Vue from 'vue';
   import Component from 'vue-class-component';
   import { Prop } from 'vue-property-decorator';
   import { wrap } from 'vue/webComponentWrapper';
+  import ace, { getAceMode } from 'ext/aceHelper';
   import { Ace } from 'ext/ace';
 
-  import apiHelper from 'api/apiHelper';
-  import AceGutterHandler from 'ko/bindings/ace/aceGutterHandler';
-  import { hueWindow } from 'types/types';
-  import ace, { getAceMode } from 'ext/aceHelper';
+  import AceAutocomplete from './autocomplete/AceAutocomplete.vue';
 
+  import AceLocationHandler from './AceLocationHandler';
   import Executor from 'apps/notebook2/execution/executor';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
-  import AceLocationHandler from './AceLocationHandler';
+  import { INSERT_AT_CURSOR_EVENT } from 'ko/bindings/ace/ko.aceEditor';
+  import AceGutterHandler from 'ko/bindings/ace/aceGutterHandler';
+  import { CURSOR_POSITION_CHANGED_EVENT } from 'ko/bindings/ace/aceLocationHandler';
+  import { IdentifierChainEntry, ParsedLocation } from 'parse/types';
+  import { EditorInterpreter } from 'types/config';
+  import { hueWindow } from 'types/types';
+  import huePubSub from 'utils/huePubSub';
   import { defer } from 'utils/hueUtils';
   import I18n from 'utils/i18n';
+  import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
-  //taken from https://www.cs.tut.fi/~jkorpela/chars/spaces.html
+  // Taken from https://www.cs.tut.fi/~jkorpela/chars/spaces.html
   const UNICODES_TO_REMOVE = /[\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u202F\u205F\u3000\uFEFF]/gi;
 
   const removeUnicodes = (value: string) => value.replace(UNICODES_TO_REMOVE, ' ');
@@ -112,9 +112,8 @@
       editor.session.setMode(getAceMode(this.executor.connector().dialect));
 
       if ((<hueWindow>window).ENABLE_SQL_SYNTAX_CHECK && window.Worker) {
-        let errorHighlightingEnabled = apiHelper.getFromLocalStorage(
-          'hue.ace',
-          'errorHighlightingEnabled',
+        let errorHighlightingEnabled = getFromLocalStorage(
+          'hue.ace.errorHighlightingEnabled',
           true
         );
 
@@ -124,7 +123,7 @@
 
         editor.customMenuOptions.setErrorHighlighting = (enabled: boolean) => {
           errorHighlightingEnabled = enabled;
-          apiHelper.setInLocalStorage('hue.ace', 'errorHighlightingEnabled', enabled);
+          setInLocalStorage('hue.ace.errorHighlightingEnabled', enabled);
           if (this.aceLocationHandler) {
             if (enabled) {
               this.aceLocationHandler.attachSqlSyntaxWorker();
@@ -135,7 +134,7 @@
         };
         editor.customMenuOptions.getErrorHighlighting = () => errorHighlightingEnabled;
         editor.customMenuOptions.setClearIgnoredSyntaxChecks = () => {
-          apiHelper.setInLocalStorage('hue.syntax.checker', 'suppressedRules', {});
+          setInLocalStorage('hue.syntax.checker.suppressedRules', {});
           $('#setClearIgnoredSyntaxChecks')
             .hide()
             .before('<div style="margin-top:5px;float:right;">done</div>');
@@ -421,7 +420,7 @@
     }
 
     addCustomAceConfigOptions(editor: Ace.Editor): void {
-      let darkThemeEnabled = apiHelper.getFromLocalStorage('ace', 'dark.theme.enabled', false);
+      let darkThemeEnabled = getFromLocalStorage('ace.dark.theme.enabled', false);
 
       editor.setTheme(darkThemeEnabled ? 'ace/theme/hue_dark' : 'ace/theme/hue');
 
@@ -434,13 +433,13 @@
       editor.customMenuOptions = {
         setEnableDarkTheme: (enabled: boolean): void => {
           darkThemeEnabled = enabled;
-          apiHelper.setInLocalStorage('ace', 'dark.theme.enabled', darkThemeEnabled);
+          setInLocalStorage('ace.dark.theme.enabled', darkThemeEnabled);
           editor.setTheme(darkThemeEnabled ? 'ace/theme/hue_dark' : 'ace/theme/hue');
         },
         getEnableDarkTheme: () => darkThemeEnabled,
         setEnableAutocompleter: (enabled: boolean): void => {
           editor.setOption('enableBasicAutocompletion', enabled);
-          apiHelper.setInLocalStorage('hue.ace', 'enableBasicAutocompletion', enabled);
+          setInLocalStorage('hue.ace.enableBasicAutocompletion', enabled);
           const $enableLiveAutocompletionChecked = $('#setEnableLiveAutocompletion:checked');
           const $setEnableLiveAutocompletion = $('#setEnableLiveAutocompletion');
           if (enabled && $enableLiveAutocompletionChecked.length === 0) {
@@ -452,7 +451,7 @@
         getEnableAutocompleter: () => editor.getOption('enableBasicAutocompletion'),
         setEnableLiveAutocompletion: (enabled: boolean): void => {
           editor.setOption('enableLiveAutocompletion', enabled);
-          apiHelper.setInLocalStorage('hue.ace', 'enableLiveAutocompletion', enabled);
+          setInLocalStorage('hue.ace.enableLiveAutocompletion', enabled);
           if (enabled && $('#setEnableAutocompleter:checked').length === 0) {
             $('#setEnableAutocompleter').trigger('click');
           }
@@ -463,7 +462,7 @@
             size += 'px';
           }
           editor.setOption('fontSize', size);
-          apiHelper.setInLocalStorage('hue.ace', 'fontSize', size);
+          setInLocalStorage('hue.ace.fontSize', size);
         },
         getFontSize: (): string => {
           let size = <string>editor.getOption('fontSize');
@@ -476,22 +475,19 @@
     }
 
     configureEditorOptions(editor: Ace.Editor): void {
-      const enableBasicAutocompletion = apiHelper.getFromLocalStorage(
-        'hue.ace',
-        'enableBasicAutocompletion',
+      const enableBasicAutocompletion = getFromLocalStorage(
+        'hue.ace.enableBasicAutocompletion',
         true
       );
 
       const enableLiveAutocompletion =
-        enableBasicAutocompletion &&
-        apiHelper.getFromLocalStorage('hue.ace', 'enableLiveAutocompletion', true);
+        enableBasicAutocompletion && getFromLocalStorage('hue.ace.enableLiveAutocompletion', true);
 
       const editorOptions: Ace.Options = {
         enableBasicAutocompletion,
         enableLiveAutocompletion,
-        fontSize: apiHelper.getFromLocalStorage(
-          'hue.ace',
-          'fontSize',
+        fontSize: getFromLocalStorage(
+          'hue.ace.fontSize',
           navigator.platform && navigator.platform.toLowerCase().indexOf('linux') > -1
             ? '14px'
             : '12px'

+ 3 - 4
desktop/core/src/desktop/js/apps/notebook2/components/aceEditor/AceLocationHandler.ts

@@ -17,7 +17,6 @@
 import { Ace } from 'ext/ace';
 import ace from 'ext/aceHelper';
 
-import apiHelper from 'api/apiHelper';
 import Executor from 'apps/notebook2/execution/executor';
 import DataCatalogEntry from 'catalog/dataCatalogEntry';
 import SubscriptionTracker, { Disposable } from 'components/utils/SubscriptionTracker';
@@ -44,6 +43,7 @@ import {
   POST_TO_LOCATION_WORKER_EVENT,
   POST_TO_SYNTAX_WORKER_EVENT
 } from 'sql/sqlWorkerHandler';
+import { getFromLocalStorage } from 'utils/storageUtils';
 
 export interface ActiveStatementChangedEvent {
   id: string;
@@ -825,9 +825,8 @@ export default class AceLocationHandler implements Disposable {
         return;
       }
 
-      const suppressedRules = apiHelper.getFromLocalStorage(
-        'hue.syntax.checker',
-        'suppressedRules',
+      const suppressedRules = getFromLocalStorage<{ [key: string]: boolean }>(
+        'hue.syntax.checker.suppressedRules',
         {}
       );
       if (

+ 5 - 4
desktop/core/src/desktop/js/apps/notebook2/editorViewModel.js

@@ -34,6 +34,7 @@ import {
   findEditorConnector,
   getLastKnownConfig
 } from 'utils/hueConfig';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 class EditorViewModel {
   constructor(editorId, notebooks, options, CoordinatorEditorViewModel, RunningCoordinatorModel) {
@@ -145,21 +146,21 @@ class EditorViewModel {
     this.assistWithoutStorage = ko.observable(false);
 
     this.isLeftPanelVisible = ko.observable(
-      apiHelper.getFromLocalStorage('assist', 'assist_panel_visible', true)
+      getFromLocalStorage('assist.assist_panel_visible', true)
     );
     this.isLeftPanelVisible.subscribe(val => {
       if (!this.assistWithoutStorage()) {
-        apiHelper.setInLocalStorage('assist', 'assist_panel_visible', val);
+        setInLocalStorage('assist.assist_panel_visible', val);
       }
     });
 
     this.isRightPanelAvailable = ko.observable(options.assistAvailable && HAS_OPTIMIZER);
     this.isRightPanelVisible = ko.observable(
-      apiHelper.getFromLocalStorage('assist', 'right_assist_panel_visible', true)
+      getFromLocalStorage('assist.right_assist_panel_visible', true)
     );
     this.isRightPanelVisible.subscribe(val => {
       if (!this.assistWithoutStorage()) {
-        apiHelper.setInLocalStorage('assist', 'right_assist_panel_visible', val);
+        setInLocalStorage('assist.right_assist_panel_visible', val);
       }
     });
 

+ 9 - 10
desktop/core/src/desktop/js/apps/notebook2/snippet.js

@@ -34,7 +34,8 @@ import apiHelper from 'api/apiHelper';
 import Executor from 'apps/notebook2/execution/executor';
 import hueAnalytics from 'utils/hueAnalytics';
 import huePubSub from 'utils/huePubSub';
-import hueUtils, { defer, hueLocalStorage } from 'utils/hueUtils';
+import { defer, UUID } from 'utils/hueUtils';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 import sessionManager from 'apps/notebook2/execution/sessionManager';
 import SqlExecutable from 'apps/notebook2/execution/sqlExecutable';
 import { HIDE_FIXED_HEADERS_EVENT, REDRAW_FIXED_HEADERS_EVENT } from 'apps/notebook2/events';
@@ -200,7 +201,7 @@ export default class Snippet {
     this.parentVm = vm;
     this.parentNotebook = notebook;
 
-    this.id = ko.observable(snippetRaw.id || hueUtils.UUID());
+    this.id = ko.observable(snippetRaw.id || UUID());
     this.name = ko.observable(snippetRaw.name || '');
 
     this.connector = ko.observable();
@@ -290,7 +291,7 @@ export default class Snippet {
 
     this.database.subscribe(newValue => {
       if (newValue !== null) {
-        apiHelper.setInLocalStorage('editor', 'last.selected.database', newValue);
+        setInLocalStorage('editor.last.selected.database', newValue);
         if (previousDatabase !== null && previousDatabase !== newValue) {
           huePubSub.publish(REFRESH_STATEMENT_LOCATIONS_EVENT, this.id());
         }
@@ -488,8 +489,8 @@ export default class Snippet {
     });
 
     let defaultShowLogs = true;
-    if (this.parentVm.editorMode() && hueLocalStorage('hue.editor.showLogs')) {
-      defaultShowLogs = hueLocalStorage('hue.editor.showLogs');
+    if (this.parentVm.editorMode() && getFromLocalStorage('hue.editor.showLogs')) {
+      defaultShowLogs = getFromLocalStorage('hue.editor.showLogs');
     }
     this.showLogs = ko.observable(snippetRaw.showLogs || defaultShowLogs);
     this.jobs = ko.observableArray(snippetRaw.jobs || []);
@@ -500,7 +501,7 @@ export default class Snippet {
     this.showLogs.subscribe(val => {
       huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
       if (this.parentVm.editorMode()) {
-        hueLocalStorage('hue.editor.showLogs', val);
+        setInLocalStorage('hue.editor.showLogs', val);
       }
     });
 
@@ -556,12 +557,10 @@ export default class Snippet {
       COMPATIBILITY_TARGET_PLATFORMS[this.dialect()]
     );
 
-    this.showOptimizer = ko.observable(
-      apiHelper.getFromLocalStorage('editor', 'show.optimizer', false)
-    );
+    this.showOptimizer = ko.observable(getFromLocalStorage('editor.show.optimizer', false));
     this.showOptimizer.subscribe(newValue => {
       if (newValue !== null) {
-        apiHelper.setInLocalStorage('editor', 'show.optimizer', newValue);
+        setInLocalStorage('editor.show.optimizer', newValue);
       }
     });
 

+ 2 - 2
desktop/core/src/desktop/js/apps/tableBrowser/metastoreDatabase.js

@@ -17,9 +17,9 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import huePubSub from 'utils/huePubSub';
 import MetastoreTable from 'apps/tableBrowser/metastoreTable';
+import { setInLocalStorage } from 'utils/storageUtils';
 
 class MetastoreDatabase {
   /**
@@ -179,7 +179,7 @@ class MetastoreDatabase {
         this.loadingAnalysis(false);
       });
 
-    apiHelper.setInLocalStorage('metastore', 'last.selected.database', this.name);
+    setInLocalStorage('metastore.last.selected.database', this.name);
   }
 
   setTableByName(tableName, callback) {

+ 4 - 4
desktop/core/src/desktop/js/apps/tableBrowser/metastoreNamespace.js

@@ -17,10 +17,10 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
+import MetastoreDatabase from 'apps/tableBrowser/metastoreDatabase';
 import dataCatalog from 'catalog/dataCatalog';
 import huePubSub from 'utils/huePubSub';
-import MetastoreDatabase from 'apps/tableBrowser/metastoreDatabase';
+import { getFromLocalStorage } from 'utils/storageUtils';
 
 class MetastoreNamespace {
   constructor(options) {
@@ -143,8 +143,8 @@ class MetastoreNamespace {
     const whenLoaded = clearCacheOnMissing => {
       if (!databaseName) {
         databaseName =
-          apiHelper.getFromLocalStorage('editor', 'last.selected.database') ||
-          apiHelper.getFromLocalStorage('metastore', 'last.selected.database') ||
+          getFromLocalStorage('editor.last.selected.database') ||
+          getFromLocalStorage('metastore.last.selected.database') ||
           'default';
         clearCacheOnMissing = false;
       }

+ 3 - 4
desktop/core/src/desktop/js/apps/tableBrowser/metastoreSource.js

@@ -17,7 +17,6 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import contextCatalog, { NAMESPACES_REFRESHED_EVENT } from 'catalog/contextCatalog';
 import huePubSub from 'utils/huePubSub';
 import MetastoreNamespace from 'apps/tableBrowser/metastoreNamespace';
@@ -27,6 +26,7 @@ import {
   ASSIST_SET_DATABASE_EVENT
 } from 'ko/components/assist/events';
 import { findEditorConnector } from 'utils/hueConfig';
+import { getFromLocalStorage } from 'utils/storageUtils';
 
 class MetastoreSource {
   constructor(options) {
@@ -63,9 +63,8 @@ class MetastoreSource {
 
     huePubSub.subscribe(ASSIST_DB_PANEL_IS_READY_EVENT, () => {
       this.lastLoadNamespacesDeferred.done(() => {
-        let lastSelectedDb = apiHelper.getFromLocalStorage(
-          'assist_' + this.type + '_' + this.namespace.id,
-          'lastSelectedDb'
+        let lastSelectedDb = getFromLocalStorage(
+          'assist_' + this.type + '_' + this.namespace.id + '.lastSelectedDb'
         );
         if (!lastSelectedDb && lastSelectedDb !== '') {
           lastSelectedDb = 'default';

+ 10 - 10
desktop/core/src/desktop/js/apps/tableBrowser/metastoreViewModel.js

@@ -17,13 +17,13 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
-import huePubSub from 'utils/huePubSub';
-import hueUtils from 'utils/hueUtils';
+import * as propsMappers from './propsMappers';
 import MetastoreSource from 'apps/tableBrowser/metastoreSource';
 import dataCatalog from 'catalog/dataCatalog';
-import * as propsMappers from './propsMappers';
 import { findEditorConnector, GET_KNOWN_CONFIG_EVENT } from 'utils/hueConfig';
+import huePubSub from 'utils/huePubSub';
+import hueUtils from 'utils/hueUtils';
+import { getFromLocalStorage, withLocalStorage } from 'utils/storageUtils';
 
 class MetastoreViewModel {
   /**
@@ -41,7 +41,10 @@ class MetastoreViewModel {
     this.partitionsLimit = options.partitionsLimit;
     this.assistAvailable = ko.observable(true);
     this.isLeftPanelVisible = ko.observable();
-    apiHelper.withLocalStorage('assist', 'assist_panel_visible', this.isLeftPanelVisible, true);
+    withLocalStorage('assist.assist_panel_visible', this.isLeftPanelVisible, true);
+    this.isLeftPanelVisible.subscribe(() => {
+      huePubSub.publish('assist.forceRender');
+    });
     this.optimizerEnabled = ko.observable(options.optimizerEnabled || false);
     this.navigatorEnabled = ko.observable(options.navigatorEnabled || false);
     this.appConfig = ko.observable();
@@ -404,11 +407,8 @@ class MetastoreViewModel {
         }
       }
 
-      if (
-        !namespaceId &&
-        apiHelper.getFromLocalStorage('contextSelector', 'lastSelectedNamespace')
-      ) {
-        namespaceId = apiHelper.getFromLocalStorage('contextSelector', 'lastSelectedNamespace').id;
+      if (!namespaceId && getFromLocalStorage('contextSelector.lastSelectedNamespace')) {
+        namespaceId = getFromLocalStorage('contextSelector.lastSelectedNamespace').id;
       }
 
       this.source().lastLoadNamespacesDeferred.done(() => {

+ 3 - 2
desktop/core/src/desktop/js/components/SqlText.vue

@@ -23,13 +23,14 @@
 </template>
 
 <script lang="ts">
-  import { formatSql } from 'apps/notebook2/apiUtils';
   import $ from 'jquery';
-  import { hueLocalStorage } from 'utils/hueUtils';
   import Vue from 'vue';
   import Component from 'vue-class-component';
   import { Prop, Watch } from 'vue-property-decorator';
 
+  import { formatSql } from 'apps/notebook2/apiUtils';
+  import { hueLocalStorage } from 'utils/storageUtils';
+
   interface Ace {
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     require: (module: string) => any;

+ 1 - 1
desktop/core/src/desktop/js/jquery/plugins/jquery.filechooser.js

@@ -19,8 +19,8 @@ import * as ko from 'knockout';
 import qq from 'ext/fileuploader.custom';
 
 import huePubSub from 'utils/huePubSub';
-import { hueLocalStorage } from 'utils/hueUtils';
 import I18n from 'utils/i18n';
+import { hueLocalStorage } from 'utils/storageUtils';
 
 /*
  * jHue fileChooser plugin

+ 2 - 1
desktop/core/src/desktop/js/jquery/plugins/jquery.hdfs.autocomplete.js

@@ -15,7 +15,8 @@
 // limitations under the License.
 
 import $ from 'jquery';
-import { escapeOutput, hueLocalStorage } from 'utils/hueUtils';
+import { escapeOutput } from 'utils/hueUtils';
+import { hueLocalStorage } from 'utils/storageUtils';
 
 /*
  * jHue HDFS autocomplete plugin

+ 1 - 1
desktop/core/src/desktop/js/jquery/plugins/jquery.hiveautocomplete.js

@@ -18,7 +18,7 @@ import $ from 'jquery';
 
 import contextCatalog from 'catalog/contextCatalog';
 import dataCatalog from 'catalog/dataCatalog';
-import { hueLocalStorage } from 'utils/hueUtils';
+import { hueLocalStorage } from 'utils/storageUtils';
 
 /*
  * jHue generic autocomplete plugin

+ 8 - 11
desktop/core/src/desktop/js/ko/bindings/ace/aceLocationHandler.js

@@ -17,21 +17,22 @@
 import $ from 'jquery';
 import ace from 'ext/aceHelper';
 
-import AssistStorageEntry from 'ko/components/assist/assistStorageEntry';
+import { DIALECT } from 'apps/notebook2/snippet';
 import dataCatalog from 'catalog/dataCatalog';
-import hueDebug from 'utils/hueDebug';
-import huePubSub from 'utils/huePubSub';
-import I18n from 'utils/i18n';
+import AssistStorageEntry from 'ko/components/assist/assistStorageEntry';
 import sqlStatementsParser from 'parse/sqlStatementsParser';
 import sqlUtils from 'sql/sqlUtils';
-import stringDistance from 'sql/stringDistance';
-import { DIALECT } from 'apps/notebook2/snippet';
 import {
   POST_FROM_LOCATION_WORKER_EVENT,
   POST_FROM_SYNTAX_WORKER_EVENT,
   POST_TO_LOCATION_WORKER_EVENT,
   POST_TO_SYNTAX_WORKER_EVENT
 } from 'sql/sqlWorkerHandler';
+import stringDistance from 'sql/stringDistance';
+import hueDebug from 'utils/hueDebug';
+import huePubSub from 'utils/huePubSub';
+import I18n from 'utils/i18n';
+import { getFromLocalStorage } from 'utils/storageUtils';
 
 // TODO: depends on Ace, sqlStatementsParser
 
@@ -785,11 +786,7 @@ class AceLocationHandler {
         return;
       }
 
-      const suppressedRules = window.apiHelper.getFromLocalStorage(
-        'hue.syntax.checker',
-        'suppressedRules',
-        {}
-      );
+      const suppressedRules = getFromLocalStorage('hue.syntax.checker.suppressedRules', {});
       if (
         e.data.syntaxError &&
         e.data.syntaxError.ruleId &&

+ 18 - 27
desktop/core/src/desktop/js/ko/bindings/ace/ko.aceEditor.js

@@ -18,16 +18,14 @@ import $ from 'jquery';
 import * as ko from 'knockout';
 import ace from 'ext/aceHelper';
 
-import apiHelper from 'api/apiHelper';
+import AceLocationHandlerV2 from 'apps/notebook2/components/aceEditor/AceLocationHandler';
 import AceLocationHandler, {
   REFRESH_STATEMENT_LOCATIONS_EVENT
 } from 'ko/bindings/ace/aceLocationHandler';
-
-import AceLocationHandlerV2 from 'apps/notebook2/components/aceEditor/AceLocationHandler';
-
-import huePubSub from 'utils/huePubSub';
 import AceGutterHandler from 'ko/bindings/ace/aceGutterHandler';
 import { registerBinding } from 'ko/bindings/bindingUtils';
+import huePubSub from 'utils/huePubSub';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 export const NAME = 'aceEditor';
 export const INSERT_AT_CURSOR_EVENT = 'editor.insert.at.cursor';
@@ -96,9 +94,8 @@ registerBinding(NAME, {
 
     editor.session.setMode(snippet.getAceMode());
     editor.setOptions({
-      fontSize: apiHelper.getFromLocalStorage(
-        'hue.ace',
-        'fontSize',
+      fontSize: getFromLocalStorage(
+        'hue.ace.fontSize',
         navigator.platform && navigator.platform.toLowerCase().indexOf('linux') > -1
           ? '14px'
           : '12px'
@@ -152,7 +149,7 @@ registerBinding(NAME, {
       aceErrorsSub.dispose();
     });
 
-    let darkThemeEnabled = apiHelper.getFromLocalStorage('ace', 'dark.theme.enabled', false);
+    let darkThemeEnabled = getFromLocalStorage('ace.dark.theme.enabled', false);
     editor.setTheme(darkThemeEnabled ? 'ace/theme/hue_dark' : 'ace/theme/hue');
 
     const editorOptions = {
@@ -174,7 +171,7 @@ registerBinding(NAME, {
     editor.customMenuOptions = {
       setEnableDarkTheme: function (enabled) {
         darkThemeEnabled = enabled;
-        apiHelper.setInLocalStorage('ace', 'dark.theme.enabled', darkThemeEnabled);
+        setInLocalStorage('ace.dark.theme.enabled', darkThemeEnabled);
         editor.setTheme(darkThemeEnabled ? 'ace/theme/hue_dark' : 'ace/theme/hue');
       },
       getEnableDarkTheme: function () {
@@ -182,7 +179,7 @@ registerBinding(NAME, {
       },
       setEnableAutocompleter: function (enabled) {
         editor.setOption('enableBasicAutocompletion', enabled);
-        apiHelper.setInLocalStorage('hue.ace', 'enableBasicAutocompletion', enabled);
+        setInLocalStorage('hue.ace.enableBasicAutocompletion', enabled);
         const $enableLiveAutocompletionChecked = $('#setEnableLiveAutocompletion:checked');
         const $setEnableLiveAutocompletion = $('#setEnableLiveAutocompletion');
         if (enabled && $enableLiveAutocompletionChecked.length === 0) {
@@ -196,7 +193,7 @@ registerBinding(NAME, {
       },
       setEnableLiveAutocompletion: function (enabled) {
         editor.setOption('enableLiveAutocompletion', enabled);
-        apiHelper.setInLocalStorage('hue.ace', 'enableLiveAutocompletion', enabled);
+        setInLocalStorage('hue.ace.enableLiveAutocompletion', enabled);
         if (enabled && $('#setEnableAutocompleter:checked').length === 0) {
           $('#setEnableAutocompleter').trigger('click');
         }
@@ -209,7 +206,7 @@ registerBinding(NAME, {
           size += 'px';
         }
         editor.setOption('fontSize', size);
-        apiHelper.setInLocalStorage('hue.ace', 'fontSize', size);
+        setInLocalStorage('hue.ace.fontSize', size);
       },
       getFontSize: function () {
         let size = editor.getOption('fontSize');
@@ -221,11 +218,7 @@ registerBinding(NAME, {
     };
 
     if (window.ENABLE_SQL_SYNTAX_CHECK && window.Worker) {
-      let errorHighlightingEnabled = apiHelper.getFromLocalStorage(
-        'hue.ace',
-        'errorHighlightingEnabled',
-        true
-      );
+      let errorHighlightingEnabled = getFromLocalStorage('hue.ace.errorHighlightingEnabled', true);
 
       if (errorHighlightingEnabled) {
         aceLocationHandler.attachSqlSyntaxWorker();
@@ -233,7 +226,7 @@ registerBinding(NAME, {
 
       editor.customMenuOptions.setErrorHighlighting = function (enabled) {
         errorHighlightingEnabled = enabled;
-        apiHelper.setInLocalStorage('hue.ace', 'errorHighlightingEnabled', enabled);
+        setInLocalStorage('hue.ace.errorHighlightingEnabled', enabled);
         if (enabled) {
           aceLocationHandler.attachSqlSyntaxWorker();
         } else {
@@ -244,7 +237,7 @@ registerBinding(NAME, {
         return errorHighlightingEnabled;
       };
       editor.customMenuOptions.setClearIgnoredSyntaxChecks = function () {
-        apiHelper.setInLocalStorage('hue.syntax.checker', 'suppressedRules', {});
+        setInLocalStorage('hue.syntax.checker.suppressedRules', {});
         $('#setClearIgnoredSyntaxChecks')
           .hide()
           .before('<div style="margin-top:5px;float:right;">done</div>');
@@ -256,15 +249,13 @@ registerBinding(NAME, {
 
     $.extend(editorOptions, aceOptions);
 
-    editorOptions['enableBasicAutocompletion'] = apiHelper.getFromLocalStorage(
-      'hue.ace',
-      'enableBasicAutocompletion',
+    editorOptions['enableBasicAutocompletion'] = getFromLocalStorage(
+      'hue.ace.enableBasicAutocompletion',
       true
     );
     if (editorOptions['enableBasicAutocompletion']) {
-      editorOptions['enableLiveAutocompletion'] = apiHelper.getFromLocalStorage(
-        'hue.ace',
-        'enableLiveAutocompletion',
+      editorOptions['enableLiveAutocompletion'] = getFromLocalStorage(
+        'hue.ace.enableLiveAutocompletion',
         true
       );
     }
@@ -500,7 +491,7 @@ registerBinding(NAME, {
       bindKey: { win: 'Ctrl-Alt-t', mac: 'Command-Alt-t' },
       exec: function () {
         darkThemeEnabled = !darkThemeEnabled;
-        apiHelper.setInLocalStorage('ace', 'dark.theme.enabled', darkThemeEnabled);
+        setInLocalStorage('ace.dark.theme.enabled', darkThemeEnabled);
         editor.setTheme(darkThemeEnabled ? 'ace/theme/hue_dark' : 'ace/theme/hue');
       }
     });

+ 1 - 1
desktop/core/src/desktop/js/ko/bindings/ace/ko.aceResizer.js

@@ -18,7 +18,7 @@ import $ from 'jquery';
 import * as ko from 'knockout';
 
 import huePubSub from 'utils/huePubSub';
-import { hueLocalStorage } from 'utils/hueUtils';
+import { hueLocalStorage } from 'utils/storageUtils';
 
 ko.bindingHandlers.aceResizer = {
   init: function (element, valueAccessor) {

+ 3 - 3
desktop/core/src/desktop/js/ko/bindings/ko.assistVerticalResizer.js

@@ -17,8 +17,8 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import huePubSub from 'utils/huePubSub';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 ko.bindingHandlers.assistVerticalResizer = {
   init: function (element, valueAccessor) {
@@ -85,7 +85,7 @@ ko.bindingHandlers.assistVerticalResizer = {
       return;
     }
 
-    const panelRatios = apiHelper.getFromLocalStorage('assist', 'innerPanelRatios', {});
+    const panelRatios = getFromLocalStorage('assist.innerPanelRatios', {});
 
     let totalRatios = 0;
     $.each($allPanels, (idx, panel) => {
@@ -253,7 +253,7 @@ ko.bindingHandlers.assistVerticalResizer = {
             panelRatios[panelDefinitions()[idx].type] =
               $(panel).outerHeight(true) / totalHeightForPanels;
           });
-          apiHelper.setInLocalStorage('assist', 'innerPanelRatios', panelRatios);
+          setInLocalStorage('assist.innerPanelRatios', panelRatios);
         }
       });
     });

+ 1 - 1
desktop/core/src/desktop/js/ko/bindings/ko.highlight.js

@@ -16,7 +16,7 @@
 
 import $ from 'jquery';
 import * as ko from 'knockout';
-import { hueLocalStorage } from 'utils/hueUtils';
+import { hueLocalStorage } from 'utils/storageUtils';
 
 // TODO: Depends on Ace
 

+ 1 - 1
desktop/core/src/desktop/js/ko/bindings/ko.logResizer.js

@@ -16,7 +16,7 @@
 
 import $ from 'jquery';
 import * as ko from 'knockout';
-import { hueLocalStorage } from 'utils/hueUtils';
+import { hueLocalStorage } from 'utils/storageUtils';
 
 ko.bindingHandlers.logResizer = {
   init: function (element, valueAccessor) {

+ 1 - 1
desktop/core/src/desktop/js/ko/bindings/ko.readOnlyAce.js

@@ -16,7 +16,7 @@
 
 import $ from 'jquery';
 import * as ko from 'knockout';
-import { hueLocalStorage } from 'utils/hueUtils';
+import { hueLocalStorage } from 'utils/storageUtils';
 
 // TODO: Depends on Ace
 

+ 1 - 1
desktop/core/src/desktop/js/ko/bindings/ko.splitDraggable.js

@@ -18,7 +18,7 @@ import $ from 'jquery';
 import * as ko from 'knockout';
 
 import huePubSub from 'utils/huePubSub';
-import { hueLocalStorage } from 'utils/hueUtils';
+import { hueLocalStorage } from 'utils/storageUtils';
 
 ko.bindingHandlers.splitDraggable = {
   init: function (element, valueAccessor) {

+ 1 - 1
desktop/core/src/desktop/js/ko/bindings/ko.splitFlexDraggable.js

@@ -18,7 +18,7 @@ import $ from 'jquery';
 import * as ko from 'knockout';
 
 import huePubSub from 'utils/huePubSub';
-import { hueLocalStorage } from 'utils/hueUtils';
+import { hueLocalStorage } from 'utils/storageUtils';
 
 ko.bindingHandlers.splitFlexDraggable = {
   init: function (element, valueAccessor) {

+ 10 - 13
desktop/core/src/desktop/js/ko/components/assist/assistDbNamespace.js

@@ -17,11 +17,11 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
-import AssistDbEntry from 'ko/components/assist/assistDbEntry';
+import { ASSIST_ACTIVE_DB_CHANGED_EVENT } from './events';
 import dataCatalog from 'catalog/dataCatalog';
+import AssistDbEntry from 'ko/components/assist/assistDbEntry';
 import huePubSub from 'utils/huePubSub';
-import { ASSIST_ACTIVE_DB_CHANGED_EVENT } from './events';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 class AssistDbNamespace {
   /**
@@ -156,18 +156,16 @@ class AssistDbNamespace {
         }
         if (!self.navigationSettings.rightAssist) {
           const entry = self.selectedDatabase().catalogEntry;
-          apiHelper.setInLocalStorage(
-            'assist_' + entry.getConnector().id + '_' + entry.namespace.id,
-            'lastSelectedDb',
+          setInLocalStorage(
+            'assist_' + entry.getConnector().id + '_' + entry.namespace.id + '.lastSelectedDb',
             entry.name
           );
           huePubSub.publish('assist.database.set', entry);
         }
       } else {
-        apiHelper.setInLocalStorage(
-          'assist_' + self.connector.id + '_' + self.namespace.id,
-          'lastSelectedDb',
-          ''
+        setInLocalStorage(
+          'assist_' + self.connector.id + '_' + self.namespace.id + '.lastSelectedDb',
+          null
         );
       }
     };
@@ -189,9 +187,8 @@ class AssistDbNamespace {
         self.selectedDatabaseChanged();
         return;
       }
-      let lastSelectedDb = apiHelper.getFromLocalStorage(
-        'assist_' + self.sourceType + '_' + self.namespace.id,
-        'lastSelectedDb'
+      let lastSelectedDb = getFromLocalStorage(
+        'assist_' + self.sourceType + '_' + self.namespace.id + '.lastSelectedDb'
       );
 
       if (!lastSelectedDb && lastSelectedDb !== '') {

+ 4 - 6
desktop/core/src/desktop/js/ko/components/assist/assistDbSource.js

@@ -17,10 +17,10 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
-import AssistDbNamespace from 'ko/components/assist/assistDbNamespace';
 import contextCatalog, { NAMESPACES_REFRESHED_EVENT } from 'catalog/contextCatalog';
+import AssistDbNamespace from 'ko/components/assist/assistDbNamespace';
 import huePubSub from 'utils/huePubSub';
+import { getFromLocalStorage } from 'utils/storageUtils';
 
 class AssistDbSource {
   /**
@@ -46,11 +46,9 @@ class AssistDbSource {
     self.nonSqlType = options.nonSqlType;
     self.navigationSettings = options.navigationSettings;
     self.initialNamespace =
-      options.initialNamespace ||
-      apiHelper.getFromLocalStorage('contextSelector', 'lastSelectedNamespace');
+      options.initialNamespace || getFromLocalStorage('contextSelector.lastSelectedNamespace');
     self.initialCompute =
-      options.initialCompute ||
-      apiHelper.getFromLocalStorage('contextSelector', 'lastSelectedCompute');
+      options.initialCompute || getFromLocalStorage('contextSelector.lastSelectedCompute');
 
     self.selectedNamespace = ko.observable();
     self.namespaces = ko.observableArray();

+ 4 - 10
desktop/core/src/desktop/js/ko/components/assist/assistInnerPanel.js

@@ -16,7 +16,7 @@
 
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
+import { withLocalStorage } from 'utils/storageUtils';
 
 class AssistInnerPanel {
   /**
@@ -25,7 +25,7 @@ class AssistInnerPanel {
    * @param {number} options.minHeight
    * @param {string} options.icon
    * @param {boolean} [options.rightAlignIcon] - Default false
-   * @param {boolean} options.visible
+   * @param {boolean} [options.visible]
    * @param {Object} panelData - component data
    * @constructor
    */
@@ -39,14 +39,8 @@ class AssistInnerPanel {
     self.rightAlignIcon = !!options.rightAlignIcon;
     self.iconSvg = options.iconSvg;
 
-    self.visible = ko.observable(options.visible || true);
-    apiHelper.withLocalStorage(
-      'assist',
-      'showingPanel_' + self.type,
-      self.visible,
-      false,
-      options.visible
-    );
+    self.visible = ko.observable(!!options.visible);
+    withLocalStorage('assist.showingPanel_' + self.type, self.visible, false, options.visible);
 
     self.templateName =
       'assist-' +

+ 21 - 20
desktop/core/src/desktop/js/ko/components/assist/ko.assistDbPanel.js

@@ -17,18 +17,6 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
-import AssistDbSource from 'ko/components/assist/assistDbSource';
-import componentUtils from 'ko/components/componentUtils';
-import dataCatalog from 'catalog/dataCatalog';
-import huePubSub from 'utils/huePubSub';
-import I18n from 'utils/i18n';
-import {
-  CONFIG_REFRESHED_EVENT,
-  filterEditorConnectors,
-  findDashboardConnector,
-  findEditorConnector
-} from 'utils/hueConfig';
 import {
   ASSIST_DB_HIGHLIGHT_EVENT,
   ASSIST_DB_PANEL_IS_READY_EVENT,
@@ -41,8 +29,19 @@ import {
   ASSIST_SHOW_SQL_EVENT,
   SHOW_LEFT_ASSIST_EVENT
 } from './events';
-
 import { ASSIST_KEY_COMPONENT } from './ko.assistKey';
+import dataCatalog from 'catalog/dataCatalog';
+import AssistDbSource from 'ko/components/assist/assistDbSource';
+import componentUtils from 'ko/components/componentUtils';
+import huePubSub from 'utils/huePubSub';
+import {
+  CONFIG_REFRESHED_EVENT,
+  filterEditorConnectors,
+  findDashboardConnector,
+  findEditorConnector
+} from 'utils/hueConfig';
+import I18n from 'utils/i18n';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 const ASSIST_TABLE_TEMPLATES = `
   <script type="text/html" id="assist-no-database-entries">
@@ -675,12 +674,12 @@ class AssistDbPanel {
                     assistDbSource.selectedNamespace().selectedDatabase().catalogEntry
                   );
                 } else {
-                  let lastSelectedDb = apiHelper.getFromLocalStorage(
+                  let lastSelectedDb = getFromLocalStorage(
                     'assist_' +
                       connector.id +
                       '_' +
-                      assistDbSource.selectedNamespace().namespace.id,
-                    'lastSelectedDb'
+                      assistDbSource.selectedNamespace().namespace.id +
+                      '.lastSelectedDb'
                   );
                   if (!lastSelectedDb && lastSelectedDb !== '') {
                     lastSelectedDb = 'default';
@@ -732,9 +731,9 @@ class AssistDbPanel {
           if (newSource.namespaces().length === 0) {
             newSource.loadNamespaces();
           }
-          apiHelper.setInLocalStorage('assist', 'lastSelectedSource', newSource.sourceType);
+          setInLocalStorage('assist.lastSelectedSource', newSource.sourceType);
         } else {
-          apiHelper.setInLocalStorage('assist', 'lastSelectedSource');
+          setInLocalStorage('assist.lastSelectedSource', null);
         }
       });
     }
@@ -855,8 +854,10 @@ class AssistDbPanel {
 
       if (sources.indexOf(this.selectedSource()) === -1) {
         if (sources.length) {
-          const storageSourceType = apiHelper.getFromLocalStorage('assist', 'lastSelectedSource');
-          this.selectedSource(this.sourceIndex[storageSourceType] || sources[0]);
+          const storageSourceType = getFromLocalStorage('assist.lastSelectedSource');
+          this.selectedSource(
+            (storageSourceType && this.sourceIndex[storageSourceType]) || sources[0]
+          );
         } else {
           this.selectedSource(undefined);
         }

+ 9 - 17
desktop/core/src/desktop/js/ko/components/assist/ko.assistDocumentsPanel.js

@@ -17,14 +17,14 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
-import componentUtils from 'ko/components/componentUtils';
+import { ASSIST_DOC_HIGHLIGHT_EVENT, ASSIST_SHOW_DOC_EVENT } from './events';
+import { DOCUMENT_TYPES } from 'doc/docSupport';
 import HueFileEntry from 'doc/hueFileEntry';
+import componentUtils from 'ko/components/componentUtils';
+import { CONFIG_REFRESHED_EVENT, getLastKnownConfig } from 'utils/hueConfig';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
-import { DOCUMENT_TYPES } from 'doc/docSupport';
-import { ASSIST_DOC_HIGHLIGHT_EVENT, ASSIST_SHOW_DOC_EVENT } from './events';
-import { CONFIG_REFRESHED_EVENT, getLastKnownConfig } from 'utils/hueConfig';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 export const REFRESH_DOC_ASSIST_EVENT = 'assist.document.refresh';
 
@@ -253,7 +253,7 @@ class AssistDocumentsPanel {
 
     self.highlightTypeFilter = ko.observable(false);
 
-    const lastOpenedUuid = apiHelper.getFromLocalStorage('assist', 'last.opened.assist.doc.uuid');
+    const lastOpenedUuid = getFromLocalStorage('assist.last.opened.assist.doc.uuid');
 
     if (lastOpenedUuid) {
       self.activeEntry(
@@ -283,20 +283,12 @@ class AssistDocumentsPanel {
             newEntry.definition() &&
             newEntry.definition().uuid
           ) {
-            apiHelper.setInLocalStorage(
-              'assist',
-              'last.opened.assist.doc.uuid',
-              newEntry.definition().uuid
-            );
+            setInLocalStorage('assist.last.opened.assist.doc.uuid', newEntry.definition().uuid);
           }
           loadedSub.dispose();
         });
       } else if (!newEntry.hasErrors() && newEntry.definition() && newEntry.definition().uuid) {
-        apiHelper.setInLocalStorage(
-          'assist',
-          'last.opened.assist.doc.uuid',
-          newEntry.definition().uuid
-        );
+        setInLocalStorage('assist.last.opened.assist.doc.uuid', newEntry.definition().uuid);
       }
     });
 
@@ -396,7 +388,7 @@ class AssistDocumentsPanel {
       (self.activeEntry().definition() &&
         (self.activeEntry().definition().path !== '/' || self.activeEntry().definition().uuid))
     ) {
-      apiHelper.setInLocalStorage('assist', 'last.opened.assist.doc.uuid', null);
+      setInLocalStorage('assist.last.opened.assist.doc.uuid', null);
       self.activeEntry(
         new HueFileEntry({
           activeEntry: self.activeEntry,

+ 8 - 9
desktop/core/src/desktop/js/ko/components/assist/ko.assistFunctionsPanel.js

@@ -16,11 +16,8 @@
 
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import componentUtils from 'ko/components/componentUtils';
-import huePubSub from 'utils/huePubSub';
-import I18n from 'utils/i18n';
-import { CONFIG_REFRESHED_EVENT, filterEditorConnectors } from 'utils/hueConfig';
+import { HUE_DROP_DOWN_COMPONENT } from 'ko/components/ko.dropDown';
 import {
   CLEAR_UDF_CACHE_EVENT,
   DESCRIBE_UDF_EVENT,
@@ -28,7 +25,10 @@ import {
   hasUdfCategories,
   UDF_DESCRIBED_EVENT
 } from 'sql/reference/sqlReferenceRepository';
-import { HUE_DROP_DOWN_COMPONENT } from 'ko/components/ko.dropDown';
+import { CONFIG_REFRESHED_EVENT, filterEditorConnectors } from 'utils/hueConfig';
+import huePubSub from 'utils/huePubSub';
+import I18n from 'utils/i18n';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 export const NAME = 'assist-functions-panel';
 // prettier-ignore
@@ -268,9 +268,8 @@ class AssistFunctionsPanel {
 
     this.activeConnectorUdfs.subscribe(async activeConnectorUdfs => {
       await activeConnectorUdfs.init();
-      apiHelper.setInLocalStorage(
-        'assist',
-        'function.panel.active.connector.type',
+      setInLocalStorage(
+        'assist.function.panel.active.connector.type',
         activeConnectorUdfs.connector.type
       );
     });
@@ -293,7 +292,7 @@ class AssistFunctionsPanel {
     const configUpdated = () => {
       const lastActiveConnectorType =
         (this.activeConnector() && this.activeConnector().type) ||
-        apiHelper.getFromLocalStorage('assist', 'function.panel.active.connector.type');
+        getFromLocalStorage('assist.function.panel.active.connector.type');
 
       const availableConnectorUdfs = filterEditorConnectors(hasUdfCategories).map(
         connector => new ConnectorUdfCategories(connector)

+ 3 - 7
desktop/core/src/desktop/js/ko/components/assist/ko.assistGitPanel.js

@@ -16,11 +16,11 @@
 
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import AssistGitEntry from 'ko/components/assist/assistGitEntry';
 import componentUtils from 'ko/components/componentUtils';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 const TEMPLATE = `
   <script type="text/html" id="git-details-title">
@@ -95,11 +95,7 @@ class AssistGitPanel {
 
     self.selectedGitEntry = ko.observable();
     self.reload = function () {
-      const lastKnownPath = apiHelper.getFromLocalStorage(
-        'assist',
-        'currentGitPath',
-        window.USER_HOME_DIR
-      );
+      const lastKnownPath = getFromLocalStorage('assist.currentGitPath', window.USER_HOME_DIR);
       const parts = lastKnownPath.split('/');
       parts.shift();
 
@@ -119,7 +115,7 @@ class AssistGitPanel {
 
     huePubSub.subscribe('assist.selectGitEntry', entry => {
       self.selectedGitEntry(entry);
-      apiHelper.setInLocalStorage('assist', 'currentGitPath', entry.path);
+      setInLocalStorage('assist.currentGitPath', entry.path);
     });
 
     huePubSub.subscribe('assist.git.refresh', () => {

+ 3 - 7
desktop/core/src/desktop/js/ko/components/assist/ko.assistHBasePanel.js

@@ -16,11 +16,11 @@
 
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import AssistHBaseEntry from 'ko/components/assist/assistHBaseEntry';
 import componentUtils from 'ko/components/componentUtils';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 const TEMPLATE = `
   <script type="text/html" id="hbase-context-items">
@@ -117,11 +117,7 @@ class AssistHBasePanel {
     self.reload = () => {
       self.selectedHBaseEntry(root);
       root.loadEntries(() => {
-        const lastOpenendPath = apiHelper.getFromLocalStorage(
-          'assist',
-          'last.opened.hbase.entry',
-          null
-        );
+        const lastOpenendPath = getFromLocalStorage('assist.last.opened.hbase.entry');
         if (lastOpenendPath) {
           root.entries().every(entry => {
             if (entry.path === lastOpenendPath) {
@@ -136,7 +132,7 @@ class AssistHBasePanel {
 
     self.selectedHBaseEntry.subscribe(newEntry => {
       if (newEntry !== root || (newEntry === root && newEntry.loaded)) {
-        apiHelper.setInLocalStorage('assist', 'last.opened.hbase.entry', newEntry.path);
+        setInLocalStorage('assist.last.opened.hbase.entry', newEntry.path);
       }
     });
 

+ 3 - 3
desktop/core/src/desktop/js/ko/components/assist/ko.assistPanel.js

@@ -17,7 +17,6 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import AssistInnerPanel from 'ko/components/assist/assistInnerPanel';
 import componentUtils from 'ko/components/componentUtils';
 import huePubSub from 'utils/huePubSub';
@@ -29,6 +28,7 @@ import {
   ASSIST_SHOW_SQL_EVENT,
   SHOW_LEFT_ASSIST_EVENT
 } from './events';
+import { withLocalStorage } from 'utils/storageUtils';
 
 const TEMPLATE = `
   <script type="text/html" id="assist-panel-inner-header">
@@ -68,7 +68,7 @@ class AssistPanel {
    * @param {Object} params
    * @param {string} params.user
    * @param {boolean} params.onlySql - For the old query editors
-   * @param {string[]} params.visibleAssistPanels - Panels that will initially be shown regardless of total storage
+   * @param {string[]} params.visibleAssistPanels - Panels that will initially be shown regardless of localStorage
    * @param {Object} params.sql
    * @param {Object} params.sql.navigationSettings - enable/disable the links
    * @param {boolean} params.sql.navigationSettings.openItem - Example: true
@@ -92,7 +92,7 @@ class AssistPanel {
     self.visiblePanel = ko.observable();
 
     self.lastOpenPanelType = ko.observable();
-    apiHelper.withLocalStorage('assist', 'last.open.panel', self.lastOpenPanelType);
+    withLocalStorage('assist.last.open.panel', self.lastOpenPanelType);
 
     // TODO: Support dynamic config changes
     huePubSub.publish(GET_KNOWN_CONFIG_EVENT, clusterConfig => {

+ 9 - 9
desktop/core/src/desktop/js/ko/components/assist/ko.assistStoragePanel.js

@@ -16,12 +16,13 @@
 
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import AssistStorageEntry from './assistStorageEntry';
+import apiHelper from 'api/apiHelper';
 import componentUtils from 'ko/components/componentUtils';
+import { getRootFilePath } from 'utils/hueConfig';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
-import { getRootFilePath } from 'utils/hueConfig';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 // prettier-ignore
 const TEMPLATE = `
@@ -184,7 +185,7 @@ class AssistStoragePanel {
   constructor(options) {
     this.sources = ko.observableArray(options.sources);
 
-    const lastSourceType = apiHelper.getFromLocalStorage('assist', 'lastStorageSource', 'hdfs');
+    const lastSourceType = getFromLocalStorage('assist.lastStorageSource', 'hdfs');
 
     let foundLastSource = this.sources().find(source => source.type === lastSourceType);
 
@@ -202,7 +203,7 @@ class AssistStoragePanel {
     this.activeSource.subscribe(newValue => {
       if (newValue) {
         this.rootPath = getRootFilePath(this.activeSource());
-        apiHelper.setInLocalStorage('assist', 'lastStorageSource', newValue.type);
+        setInLocalStorage('assist.lastStorageSource', newValue.type);
         this.selectedStorageEntry(undefined);
         this.reload();
       }
@@ -210,7 +211,7 @@ class AssistStoragePanel {
 
     huePubSub.subscribe('assist.selectStorageEntry', entry => {
       this.selectedStorageEntry(entry);
-      apiHelper.setInLocalStorage('assist', 'currentStoragePath_' + entry.source.type, entry.path);
+      setInLocalStorage('assist.currentStoragePath_' + entry.source.type, entry.path);
     });
 
     huePubSub.subscribe('assist.storage.refresh', () => {
@@ -224,7 +225,7 @@ class AssistStoragePanel {
           ? '/'
           : window.USER_HOME_DIR;
       this.loadPath(path);
-      apiHelper.setInLocalStorage('assist', 'currentStoragePath_' + this.activeSource().type, path);
+      setInLocalStorage('assist.currentStoragePath_' + this.activeSource().type, path);
     });
 
     this.init();
@@ -258,9 +259,8 @@ class AssistStoragePanel {
 
   reload() {
     this.loadPath(
-      apiHelper.getFromLocalStorage(
-        'assist',
-        'currentStoragePath_' + this.activeSource().type,
+      getFromLocalStorage(
+        'assist.currentStoragePath_' + this.activeSource().type,
         this.activeSource().type === 'hdfs' ? window.USER_HOME_DIR : '/'
       )
     );

+ 13 - 13
desktop/core/src/desktop/js/ko/components/assist/ko.rightAssistPanel.js

@@ -16,16 +16,16 @@
 
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
-import componentUtils from 'ko/components/componentUtils';
-import huePubSub from 'utils/huePubSub';
-import I18n from 'utils/i18n';
-import { ACTIVE_SNIPPET_CONNECTOR_CHANGED_EVENT } from 'apps/notebook2/events';
 import {
   ASSIST_LANG_REF_PANEL_SHOW_TOPIC_EVENT,
   ASSIST_LANG_REF_SHOW_TOPIC_EVENT,
   SHOW_RIGHT_ASSIST_EVENT
 } from './events';
+import { ACTIVE_SNIPPET_CONNECTOR_CHANGED_EVENT } from 'apps/notebook2/events';
+import componentUtils from 'ko/components/componentUtils';
+import huePubSub from 'utils/huePubSub';
+import I18n from 'utils/i18n';
+import { withLocalStorage } from 'utils/storageUtils';
 
 const EDITOR_ASSISTANT_TAB = 'editorAssistant';
 const DASHBOARD_ASSISTANT_TAB = 'dashboardAssistant';
@@ -132,16 +132,16 @@ class RightAssistPanel {
     );
     this.schedulesTabAvailable = ko.observable(false);
 
-    this.lastActiveTabEditor = apiHelper.withLocalStorage(
-      'assist',
-      'last.open.right.panel',
-      ko.observable(),
+    this.lastActiveTabEditor = ko.observable();
+    withLocalStorage(
+      'assist.last.open.right.panel',
+      this.lastActiveTabEditor,
       EDITOR_ASSISTANT_TAB
     );
-    this.lastActiveTabDashboard = apiHelper.withLocalStorage(
-      'assist',
-      'last.open.right.panel.dashboard',
-      ko.observable(),
+    this.lastActiveTabDashboard = ko.observable();
+    withLocalStorage(
+      'assist.last.open.right.panel.dashboard',
+      this.lastActiveTabDashboard,
       DASHBOARD_ASSISTANT_TAB
     );
 

+ 7 - 7
desktop/core/src/desktop/js/ko/components/contextPopover/ko.contextPopover.js

@@ -17,24 +17,24 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import AsteriskContextTabs from './asteriskContextTabs';
 import CollectionContextTabs from './collectionContextTabs';
-import contextCatalog from 'catalog/contextCatalog';
-import dataCatalog from 'catalog/dataCatalog';
 import DataCatalogContext from './dataCatalogContext';
 import DocumentContext, { DOCUMENT_CONTEXT_TEMPLATE } from './documentContext';
 import FunctionContextTabs, { FUNCTION_CONTEXT_TEMPLATE } from './functionContext';
-import huePubSub from 'utils/huePubSub';
-import I18n from 'utils/i18n';
+import { DOCUMENT_CONTEXT_FOOTER } from './ko.documentContextFooter';
 import LangRefContext from './langRefContext';
 import PartitionContext, { PARTITION_CONTEXT_TEMPLATE } from './partitionContext';
 import ResizeHelper from './resizeHelper';
 import StorageContext from './storageContext';
+import contextCatalog from 'catalog/contextCatalog';
+import dataCatalog from 'catalog/dataCatalog';
 import { ASSIST_KEY_COMPONENT } from 'ko/components/assist/ko.assistKey';
 import componentUtils from 'ko/components/componentUtils';
 import { findEditorConnector, GET_KNOWN_CONFIG_EVENT } from 'utils/hueConfig';
-import { DOCUMENT_CONTEXT_FOOTER } from './ko.documentContextFooter';
+import huePubSub from 'utils/huePubSub';
+import I18n from 'utils/i18n';
+import { getFromLocalStorage } from 'utils/storageUtils';
 
 export const CONTEXT_POPOVER_CLASS = 'hue-popover';
 export const HIDE_CONTEXT_POPOVER_EVENT = 'context.popover.hide';
@@ -569,7 +569,7 @@ class ContextPopoverViewModel {
     self.left = ko.observable(0);
     self.top = ko.observable(0);
 
-    const popoverSize = apiHelper.getFromLocalStorage('assist', 'popover.size', {
+    const popoverSize = getFromLocalStorage('assist.popover.size', {
       width: 450,
       height: 400
     });

+ 2 - 2
desktop/core/src/desktop/js/ko/components/contextPopover/resizeHelper.js

@@ -16,8 +16,8 @@
 
 import $ from 'jquery';
 
-import apiHelper from 'api/apiHelper';
 import huePubSub from 'utils/huePubSub';
+import { setInLocalStorage } from 'utils/storageUtils';
 
 const HALF_SIZE_LIMIT_X = 130;
 const HALF_SIZE_LIMIT_Y = 100;
@@ -68,7 +68,7 @@ class ResizeHelper {
     initOriginalValues(0);
 
     self.saveSize = function () {
-      apiHelper.setInLocalStorage('assist', 'popover.size', {
+      setInLocalStorage('assist.popover.size', {
         width: $('.hue-popover').width(),
         height: $('.hue-popover').height()
       });

+ 10 - 24
desktop/core/src/desktop/js/ko/components/ko.contextSelector.js

@@ -17,7 +17,7 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
+import { ASSIST_SET_DATABASE_EVENT } from './assist/events';
 import componentUtils from './componentUtils';
 import contextCatalog, {
   CONTEXT_CATALOG_REFRESHED_EVENT,
@@ -26,7 +26,7 @@ import contextCatalog, {
 import dataCatalog from 'catalog/dataCatalog';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
-import { ASSIST_SET_DATABASE_EVENT } from './assist/events';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 export const CONTEXT_SELECTOR_COMPONENT = 'hue-context-selector';
 
@@ -193,7 +193,7 @@ const HueContextSelector = function (params) {
       if (params[type.onSelect]) {
         params[type.onSelect](selectedVal, previousVal);
       }
-      apiHelper.setInLocalStorage('contextSelector', type.localStorageId, selectedVal);
+      setInLocalStorage('contextSelector.' + type.localStorageId, selectedVal);
 
       if (selectedVal && type === TYPES_INDEX.compute) {
         self.setMatchingNamespace(selectedVal);
@@ -274,11 +274,7 @@ HueContextSelector.prototype.setMatchingNamespace = function (compute) {
         const found = self[TYPES_INDEX.namespace.available]().some(namespace => {
           if (compute.namespace === namespace.id) {
             self[TYPES_INDEX.namespace.name](namespace);
-            apiHelper.setInLocalStorage(
-              'contextSelector',
-              TYPES_INDEX.namespace.localStorageId,
-              namespace
-            );
+            setInLocalStorage('contextSelector.' + TYPES_INDEX.namespace.localStorageId, namespace);
             return true;
           }
         });
@@ -306,11 +302,7 @@ HueContextSelector.prototype.setMatchingCompute = function (namespace) {
         const found = self[TYPES_INDEX.compute.available]().some(compute => {
           if (namespace.id === compute.namespace) {
             self[TYPES_INDEX.compute.name](compute);
-            apiHelper.setInLocalStorage(
-              'contextSelector',
-              TYPES_INDEX.compute.localStorageId,
-              namespace
-            );
+            setInLocalStorage('contextSelector.' + TYPES_INDEX.compute.localStorageId, namespace);
             return true;
           }
         });
@@ -358,14 +350,8 @@ HueContextSelector.prototype.reload = function (type) {
           });
         }
 
-        if (
-          !self[type.name]() &&
-          apiHelper.getFromLocalStorage('contextSelector', type.localStorageId)
-        ) {
-          const lastSelected = apiHelper.getFromLocalStorage(
-            'contextSelector',
-            type.localStorageId
-          );
+        if (!self[type.name]() && getFromLocalStorage('contextSelector.' + type.localStorageId)) {
+          const lastSelected = getFromLocalStorage('contextSelector.' + type.localStorageId);
           const found = available.some(other => {
             if (other.id === lastSelected.id) {
               self[type.name](other);
@@ -441,12 +427,12 @@ HueContextSelector.prototype.reloadDatabases = function () {
                   self.availableDatabases([]);
                 })
                 .always(() => {
-                  let lastSelectedDb = apiHelper.getFromLocalStorage(
+                  let lastSelectedDb = getFromLocalStorage(
                     'assist_' +
                       ko.unwrap(self.connector).id +
                       '_' +
-                      self[TYPES_INDEX.namespace.name]().id,
-                    'lastSelectedDb'
+                      self[TYPES_INDEX.namespace.name]().id +
+                      '.lastSelectedDb'
                   );
 
                   const updateAssist = lastSelectedDb !== '';

+ 9 - 10
desktop/core/src/desktop/js/ko/components/ko.performanceGraph.js

@@ -17,9 +17,10 @@
 import * as d3 from 'd3';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import componentUtils from './componentUtils';
+import apiHelper from 'api/apiHelper';
 import I18n from 'utils/i18n';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 export const NAME = 'performance-graph';
 
@@ -74,9 +75,7 @@ class PerformanceGraph {
     ];
 
     self.selectedGranularity = ko.observable(
-      self.availableGranularities[
-        apiHelper.getFromLocalStorage('warehouses', 'performanceGraphGranularity', 0)
-      ]
+      self.availableGranularities[getFromLocalStorage('warehouses.performanceGraphGranularity', 0)]
     );
     self.clusterName = ko.observable(params.clusterName);
 
@@ -101,7 +100,7 @@ class PerformanceGraph {
         self.selectedGranularity.subscribe(granularity => {
           for (let i = 0; i < self.availableGranularities.length; i++) {
             if (granularity === self.availableGranularities[i]) {
-              apiHelper.setInLocalStorage('warehouses', 'performanceGraphGranularity', i);
+              setInLocalStorage('warehouses.performanceGraphGranularity', i);
               break;
             }
           }
@@ -335,7 +334,7 @@ class PerformanceGraph {
         tooltip: options.tooltip,
         toggle: function () {
           enabled = !enabled;
-          apiHelper.setInLocalStorage('warehouses', options.id + 'GraphEnabled', enabled);
+          setInLocalStorage('warehouses.' + options.id + 'GraphEnabled', enabled);
           mainGroup
             .select('.highlight-point-' + options.id)
             .style('display', enabled ? null : 'none');
@@ -389,7 +388,7 @@ class PerformanceGraph {
       createLineGraph({
         id: 'queries',
         label: I18n('Queries'),
-        enabled: apiHelper.getFromLocalStorage('warehouses', 'queriesGraphEnabled', true),
+        enabled: getFromLocalStorage('warehouses.queriesGraphEnabled', true),
         color: '#A9DBF1',
         subLineColor: '#DCDCDC',
         area: true,
@@ -418,7 +417,7 @@ class PerformanceGraph {
       }),
       createLineGraph({
         id: 'cpu',
-        enabled: apiHelper.getFromLocalStorage('warehouses', 'cpuGraphEnabled', false),
+        enabled: getFromLocalStorage('warehouses.cpuGraphEnabled', false),
         label: I18n('CPU'),
         color: '#654C94',
         subLine: true,
@@ -434,7 +433,7 @@ class PerformanceGraph {
       }),
       createLineGraph({
         id: 'memory',
-        enabled: apiHelper.getFromLocalStorage('warehouses', 'memoryGraphEnabled', false),
+        enabled: getFromLocalStorage('warehouses.memoryGraphEnabled', false),
         label: I18n('Memory'),
         color: '#83C1B9',
         subLine: true,
@@ -450,7 +449,7 @@ class PerformanceGraph {
       }),
       createLineGraph({
         id: 'io',
-        enabled: apiHelper.getFromLocalStorage('warehouses', 'ioGraphEnabled', false),
+        enabled: getFromLocalStorage('warehouses.ioGraphEnabled', false),
         label: I18n('IO'),
         color: '#D4965E',
         subLine: true,

+ 4 - 4
desktop/core/src/desktop/js/ko/components/ko.sidebar.js

@@ -17,12 +17,12 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
+import { ASSIST_ACTIVE_DB_CHANGED_EVENT, ASSIST_SET_DATABASE_EVENT } from './assist/events';
 import componentUtils from 'ko/components/componentUtils';
+import { GET_KNOWN_CONFIG_EVENT, CONFIG_REFRESHED_EVENT } from 'utils/hueConfig';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
-import { GET_KNOWN_CONFIG_EVENT, CONFIG_REFRESHED_EVENT } from 'utils/hueConfig';
-import { ASSIST_ACTIVE_DB_CHANGED_EVENT, ASSIST_SET_DATABASE_EVENT } from './assist/events';
+import { withLocalStorage } from 'utils/storageUtils';
 
 export const NAME = 'hue-sidebar';
 
@@ -267,7 +267,7 @@ class Sidebar {
       }
     });
 
-    apiHelper.withLocalStorage('hue.sidebar', 'collabse', this.collapsed, true);
+    withLocalStorage('hue.sidebar.collapse', this.collapsed, true);
 
     this.items = ko.observableArray();
 

+ 2 - 1
desktop/core/src/desktop/js/ko/components/simpleAceEditor/ko.simpleAceEditor.js

@@ -20,8 +20,9 @@ import ace from 'ext/aceHelper';
 
 import AceLocationHandler from 'ko/bindings/ace/aceLocationHandler';
 import componentUtils from 'ko/components/componentUtils';
-import { hueLocalStorage, UUID } from 'utils/hueUtils';
+import { UUID } from 'utils/hueUtils';
 import huePubSub from 'utils/huePubSub';
+import { hueLocalStorage } from 'utils/storageUtils';
 import SolrFormulaAutocompleter from './solrFormulaAutocompleter';
 import SolrQueryAutocompleter from './solrQueryAutocompleter';
 import SqlAutocompleter from 'sql/sqlAutocompleter';

+ 5 - 5
desktop/core/src/desktop/js/sidePanelViewModel.js

@@ -17,22 +17,22 @@
 import $ from 'jquery';
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import hueAnalytics from 'utils/hueAnalytics';
 import huePubSub from 'utils/huePubSub';
 import { ACTIVE_SNIPPET_CONNECTOR_CHANGED_EVENT } from 'apps/notebook2/events';
 import { SHOW_LEFT_ASSIST_EVENT, SHOW_RIGHT_ASSIST_EVENT } from 'ko/components/assist/events';
+import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 
 class SidePanelViewModel {
   constructor() {
     const self = this;
     self.assistWithoutStorage = ko.observable(false);
     self.leftAssistVisible = ko.observable(
-      apiHelper.getFromLocalStorage('assist', 'left_assist_panel_visible', true)
+      getFromLocalStorage('assist.left_assist_panel_visible', true)
     );
     self.leftAssistVisible.subscribe(val => {
       if (!self.assistWithoutStorage()) {
-        apiHelper.setInLocalStorage('assist', 'left_assist_panel_visible', val);
+        setInLocalStorage('assist.left_assist_panel_visible', val);
       }
       hueAnalytics.convert('hue', 'leftAssistVisible/' + val);
       window.setTimeout(() => {
@@ -42,11 +42,11 @@ class SidePanelViewModel {
     });
 
     self.rightAssistVisible = ko.observable(
-      apiHelper.getFromLocalStorage('assist', 'right_assist_panel_visible', true)
+      getFromLocalStorage('assist.right_assist_panel_visible', true)
     );
     self.rightAssistVisible.subscribe(val => {
       if (!self.assistWithoutStorage()) {
-        apiHelper.setInLocalStorage('assist', 'right_assist_panel_visible', val);
+        setInLocalStorage('assist.right_assist_panel_visible', val);
       }
       hueAnalytics.convert('hue', 'rightAssistVisible/' + val);
       window.setTimeout(() => {

+ 2 - 2
desktop/core/src/desktop/js/topNavViewModel.js

@@ -16,7 +16,6 @@
 
 import * as ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
 import huePubSub from 'utils/huePubSub';
 import {
   CONFIG_REFRESHED_EVENT,
@@ -24,6 +23,7 @@ import {
   GET_KNOWN_CONFIG_EVENT,
   REFRESH_CONFIG_EVENT
 } from 'utils/hueConfig';
+import { withLocalStorage } from 'utils/storageUtils';
 
 class TopNavViewModel {
   constructor(onePageViewModel) {
@@ -32,7 +32,7 @@ class TopNavViewModel {
 
     // TODO: Drop. Just for PoC
     self.pocClusterMode = ko.observable();
-    apiHelper.withLocalStorage('topNav', 'multiCluster', self.pocClusterMode, 'dw');
+    withLocalStorage('topNav.multiCluster', self.pocClusterMode, 'dw');
     huePubSub.subscribe('set.multi.cluster.mode', self.pocClusterMode);
 
     self.hasJobBrowser = ko.observable(window.HAS_JOB_BROWSER);

+ 0 - 5
desktop/core/src/desktop/js/utils/hdfsAutocompleter.js

@@ -34,11 +34,6 @@ class HdfsAutocompleter {
     self.timeout = options.timeout;
   }
 
-  getlocalStorageUserPrefix() {
-    const self = this;
-    return self.user;
-  }
-
   hasExpired(timestamp) {
     return new Date().getTime() - timestamp > TIME_TO_LIVE_IN_MILLIS;
   }

+ 3 - 2
desktop/core/src/desktop/js/utils/hdfsAutocompleter.test.js

@@ -16,7 +16,7 @@
 
 import $ from 'jquery';
 import * as ko from 'knockout';
-import * as hueUtilsMock from './hueUtils';
+import * as storageUtilsMock from './storageUtils';
 
 import HdfsAutocompleter from './hdfsAutocompleter';
 describe('hdfsAutocompleter.js', () => {
@@ -38,7 +38,8 @@ describe('hdfsAutocompleter.js', () => {
   };
 
   beforeAll(() => {
-    jest.spyOn(hueUtilsMock, 'hueLocalStorage').mockImplementation(() => null);
+    jest.spyOn(storageUtilsMock, 'getFromLocalStorage').mockImplementation(() => null);
+    jest.spyOn(storageUtilsMock, 'setInLocalStorage').mockImplementation(() => null);
 
     jest.spyOn($, 'ajax').mockImplementation(options => {
       const firstUrlPart = options.url.split('?')[0];

+ 11 - 22
desktop/core/src/desktop/js/utils/hueUtils.ts

@@ -17,6 +17,7 @@
 import $ from 'jquery';
 import sanitizeHtml from 'sanitize-html';
 import { hueWindow } from 'types/types';
+import { hueLocalStorage, withLocalStorage } from './storageUtils';
 
 export const bootstrapRatios = {
   span3(): number {
@@ -584,27 +585,6 @@ export const defer = async (callback: () => void): Promise<void> => {
   }
 };
 
-export const hueLocalStorage = <T = string>(key: string, value?: T): T | null => {
-  if (window.localStorage) {
-    if (typeof value !== 'undefined') {
-      if (value === null) {
-        window.localStorage.removeItem(key);
-      } else {
-        window.localStorage.setItem(key, JSON.stringify(value));
-      }
-      return value;
-    }
-    const storedValue = window.localStorage.getItem(key);
-    if (storedValue && storedValue.length) {
-      try {
-        return JSON.parse(storedValue);
-      } catch (e) {}
-      return <T>(<unknown>storedValue);
-    }
-  }
-  return null;
-};
-
 export default {
   bootstrapRatios,
   changeURL,
@@ -621,6 +601,10 @@ export default {
   getStyleFromCSSClass,
   goFullScreen,
   highlight,
+  /**
+   * Exposed here for legacy code using global window.hueUtils, use utils/storageUtils instead.
+   * @deprecated
+   */
   hueLocalStorage,
   html2text,
   htmlEncode,
@@ -636,5 +620,10 @@ export default {
   UUID,
   waitForObservable,
   waitForRendered,
-  waitForVariable
+  waitForVariable,
+  /**
+   * Exposed here for legacy code using global window.hueUtils, use utils/storageUtils instead.
+   * @deprecated
+   */
+  withLocalStorage
 };

+ 90 - 0
desktop/core/src/desktop/js/utils/storageUtils.ts

@@ -0,0 +1,90 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { hueWindow } from 'types/types';
+
+type LocalStorageGet = {
+  <T = string>(key: string): T | null;
+  <T = string>(key: string, defaultValue: T): T;
+};
+export const getFromLocalStorage: LocalStorageGet = <T>(key: string, defaultValue?: T) => {
+  const defaultOrNull = typeof defaultValue !== 'undefined' ? defaultValue : null;
+  if (!window.localStorage) {
+    return defaultOrNull;
+  }
+
+  const userKey = (<hueWindow>window).LOGGED_USERNAME
+    ? `${(<hueWindow>window).LOGGED_USERNAME}.${key}`
+    : key;
+  const storedValue = window.localStorage.getItem(userKey);
+  if (storedValue === null) {
+    return defaultOrNull;
+  }
+
+  if (storedValue && storedValue.length) {
+    try {
+      return JSON.parse(storedValue);
+    } catch (e) {}
+    return <T>(<unknown>storedValue);
+  }
+  return defaultOrNull;
+};
+
+export const setInLocalStorage = (key: string, value: unknown): void => {
+  if (!window.localStorage) {
+    return;
+  }
+  const userKey = (<hueWindow>window).LOGGED_USERNAME
+    ? `${(<hueWindow>window).LOGGED_USERNAME}.${key}`
+    : key;
+  if (typeof value === 'undefined' || value === null) {
+    window.localStorage.removeItem(userKey);
+  } else {
+    let jsonString: string | undefined = undefined;
+    try {
+      jsonString = JSON.stringify(value);
+    } catch (e) {}
+    try {
+      window.localStorage.setItem(userKey, jsonString || String(value));
+    } catch (e) {}
+  }
+};
+
+export const hueLocalStorage = <T = string>(key: string, value?: T): T | null => {
+  if (typeof value !== 'undefined') {
+    setInLocalStorage(key, value);
+    return value;
+  }
+  return getFromLocalStorage(key);
+};
+
+export const withLocalStorage = <T = string>(
+  key: string,
+  observable: KnockoutObservable<T>,
+  defaultValue?: T,
+  noInit = false
+): KnockoutSubscription => {
+  const cachedValue = getFromLocalStorage<T>(key);
+  if (!noInit && cachedValue !== null) {
+    observable(cachedValue);
+  } else if (defaultValue) {
+    observable(defaultValue);
+  }
+
+  return observable.subscribe(newValue => {
+    setInLocalStorage(key, newValue);
+  });
+};

+ 4 - 1
desktop/core/src/desktop/static/desktop/js/home2.vm.js

@@ -31,7 +31,10 @@ var HomeViewModel = (function () {
     self.apiHelper = window.apiHelper;
     self.isLeftPanelVisible = ko.observable(false);
     // Uncomment to enable the assist panel
-    // self.apiHelper.withLocalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
+    // window.hueUtils.withLocalStorage('assist.assist_panel_visible', self.isLeftPanelVisible, true);
+    // self.isLeftPanelVisible.subscribe(function () {
+    //   huePubSub.publish('assist.forceRender');
+    // });
 
     self.sharingEnabled = ko.observable(false);
 

+ 4 - 1
desktop/core/src/desktop/templates/assist_m.mako

@@ -62,7 +62,10 @@ ${ commonheader_m(_('Assist'), 'assist', user, request) | n,unicode }
       self.apiHelper = window.apiHelper;
       self.assistAvailable = ko.observable(true);
       self.isLeftPanelVisible = ko.observable();
-      self.apiHelper.withLocalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
+      window.hueUtils.withLocalStorage('assist.assist_panel_visible', self.isLeftPanelVisible, true);
+      self.isLeftPanelVisible.subscribe(function () {
+        huePubSub.publish('assist.forceRender');
+      });
 
       self.sqlSourceTypes = [];
       self.availableLanguages = [];

+ 2 - 2
desktop/core/src/desktop/templates/sql_syntax_dropdown.mako

@@ -66,9 +66,9 @@ from django.utils.translation import ugettext as _
 
         var selectedSub = self.selected.subscribe(function (newValue) {
           if (typeof newValue.suppressRule !== 'undefined') {
-            var suppressedRules = window.apiHelper.getFromLocalStorage('hue.syntax.checker', 'suppressedRules', {});
+            var suppressedRules = window.hueUtils.hueLocalStorage('hue.syntax.checker.suppressedRules') || {};
             suppressedRules[newValue.suppressRule] = true;
-            window.apiHelper.setInLocalStorage('hue.syntax.checker', 'suppressedRules', suppressedRules);
+            window.hueUtils.hueLocalStorage('hue.syntax.checker.suppressedRules', suppressedRules);
             huePubSub.publish('editor.refresh.statement.locations', params.editorId);
           } else {
             params.editor.session.replace(params.range, newValue);

+ 1 - 1
desktop/libs/dashboard/src/dashboard/templates/common_search.mako

@@ -5042,7 +5042,7 @@ $(document).ready(function () {
   huePubSub.subscribe('app.dom.unload', function (app) {
     if (app === 'dashboard') {
       %if is_report:
-      if (window.apiHelper.getFromLocalStorage('assist', 'right_assist_panel_visible', false)) {
+      if (window.hueUtils.hueLocalStorage('assist.right_assist_panel_visible')) {
         huePubSub.publish('right.assist.show');
       }
       %endif

+ 4 - 1
desktop/libs/indexer/src/indexer/templates/importer.mako

@@ -3061,7 +3061,10 @@ ${ commonheader(_("Importer"), "indexer", user, request, "60px") | n,unicode }
 
       self.assistAvailable = ko.observable(true);
       self.isLeftPanelVisible = ko.observable();
-      self.apiHelper.withLocalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
+      window.hueUtils.withLocalStorage('assist.assist_panel_visible', self.isLeftPanelVisible, true);
+      self.isLeftPanelVisible.subscribe(function () {
+        huePubSub.publish('assist.forceRender');
+      });
       self.loadDefaultField = loadDefaultField;
 
       self.createWizard = new CreateWizard(self);

+ 4 - 1
desktop/libs/indexer/src/indexer/templates/indexer.mako

@@ -923,7 +923,10 @@ ${ commonheader(_("Solr Indexes"), "search", user, request, "60px") | n,unicode
       self.apiHelper = window.apiHelper;
       self.assistAvailable = ko.observable(true);
       self.isLeftPanelVisible = ko.observable();
-      self.apiHelper.withLocalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
+      window.hueUtils.withLocalStorage('assist.assist_panel_visible', self.isLeftPanelVisible, true);
+      self.isLeftPanelVisible.subscribe(function () {
+        huePubSub.publish('assist.forceRender');
+      });
 
       // wizard related
       self.wizardEnabled = ko.observable(false);

+ 4 - 1
desktop/libs/indexer/src/indexer/templates/indexes.mako

@@ -654,7 +654,10 @@ ${ commonheader(_("Index Browser"), "search", user, request, "60px") | n,unicode
       self.assistAvailable = ko.observable(true);
       self.apiHelper = window.apiHelper;
       self.isLeftPanelVisible = ko.observable();
-      self.apiHelper.withLocalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
+      window.hueUtils.withLocalStorage('assist.assist_panel_visible', self.isLeftPanelVisible, true);
+      self.isLeftPanelVisible.subscribe(function () {
+        huePubSub.publish('assist.forceRender');
+      });
 
       self.section = ko.observable('list-indexes');
       self.tab = ko.observable('');

+ 4 - 1
desktop/libs/indexer/src/indexer/templates/topics.mako

@@ -585,7 +585,10 @@ ${ commonheader(_("Streams Browser"), "search", user, request, "60px") | n,unico
       self.assistAvailable = ko.observable(true);
       self.apiHelper = window.apiHelper;
       self.isLeftPanelVisible = ko.observable();
-      self.apiHelper.withLocalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
+      window.hueUtils.withLocalStorage('assist.assist_panel_visible', self.isLeftPanelVisible, true);
+      self.isLeftPanelVisible.subscribe(function () {
+        huePubSub.publish('assist.forceRender');
+      });
 
       self.section = ko.observable('list-indexes');
       self.tab = ko.observable('');