Hi,
Please find the attached updated patch.
Feature Details:
- The current tree state as well as the previous will be stored in the
sqlite database.
- The time interval to store the tree state is configurable via preferences
and the default is 30 secs.
-1 can be used to stop the tree saving functionality,
- Jasmine test cases are included.
Thanks,
Khushboo
On Thu, Aug 30, 2018 at 3:34 PM Dave Page <[email protected]> wrote:
> Hi
>
> On Tue, Aug 28, 2018 at 1:40 PM, Khushboo Vashi <
> [email protected]> wrote:
>
>>
>>
>> On Mon, Aug 27, 2018 at 1:19 PM, Akshay Joshi <
>> [email protected]> wrote:
>>
>>> Hi Khushboo
>>>
>>> Patch looks good to me, following are my review comments:
>>>
>>> - Logic doesn't work when we disconnect and re-connect to the
>>> database server. We have duplicated RM #1335.
>>>
>>> I have saved the current state of the tree. so If I have disconnected
>> the server, so that closed state will be considered for that server.
>> If another server(s) is still connected and opened at some node, and then
>> you reload the browser, on connecting/expanding the server it will populate
>> the server nodes.
>>
>> @Dave,
>>
>> Do I need to change this behaviour?
>>
>
> Per our Skype call, let's store and restore both the current tree state
> and the previous state of disconnected servers (as pgAdmin 3 did). So:
>
> - On browser open, the treeview is restored to the previous state, with
> databases reconnected as required.
>
> - On re-connect of a database, the sub-treeview for that database is
> restored to the state it was in prior to disconnection.
>
> Thanks!
>
>>
>>> -
>>> - Instead of -1 we should use 0 to stop the tree saving
>>> functionality. Send request at 0 second doesn't make any sense.
>>> - What if pgAdmin4 opens in two different browsers or two different
>>> tabs with different tree hierarchy in that case it will override the
>>> state
>>> of each other. (This could be an enhancement request).
>>>
>>>
>>> On Mon, Aug 27, 2018 at 11:09 AM, Aditya Toshniwal <
>>> [email protected]> wrote:
>>>
>>>> Hi Khushboo,
>>>>
>>>> Cheers, the patch looks good to me. I have few suggestions:
>>>> 1) Expand the tree -> Go to server properties and rename the server ->
>>>> the tree closes. It should expand to last state. Same applies to other
>>>> nodes which can be renamed.
>>>> 2) It would be nice if we can also save the last selected object. The
>>>> tree expands but the last selected object is not selected.
>>>> 3) In /web/pgadmin/static/js/tree/pgadmin_tree_save_state.js
>>>> import pgAdmin from '../../../static/js/pgadmin'; -- please use alias
>>>> here rather than relative path.
>>>> 4) I would suggest to add 1 example/format JSON of tree state in
>>>> comments in the file so that it will be easier to relate the code with.
>>>>
>>>>
>>>> On Fri, Aug 24, 2018 at 10:08 AM Khushboo Vashi <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi,
>>>>>
>>>>> Please find the attached patch for RM #1253 - Store and reload current
>>>>> location in treeview
>>>>>
>>>>> Feature Details:
>>>>> - The current tree state will be stored in the sqlite database.
>>>>> - The time interval to store the tree state is configurable via
>>>>> preferences and the default is 30 secs.
>>>>> -1 can be used to stop the tree saving functionality,
>>>>> - On window unload the current tree state will be saved.
>>>>> - On Reload, while opening the server, the stored path will be
>>>>> populated.
>>>>> - On closing the node the saved will be updated accordingly.
>>>>> - Jasmine test cases are included.
>>>>>
>>>>>
>>>>> Thanks,
>>>>> Khushboo
>>>>>
>>>>
>>>>
>>>> --
>>>> Thanks and Regards,
>>>> Aditya Toshniwal
>>>> Software Engineer | EnterpriseDB Software Solutions | Pune
>>>> "Don't Complain about Heat, Plant a tree"
>>>>
>>>
>>>
>>>
>>> --
>>> *Akshay Joshi*
>>>
>>> *Sr. Software Architect *
>>>
>>>
>>>
>>> *Phone: +91 20-3058-9517Mobile: +91 976-788-8246*
>>>
>>
>>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
diff --git a/web/pgadmin/browser/register_browser_preferences.py b/web/pgadmin/browser/register_browser_preferences.py
index 2eed500..df30346 100644
--- a/web/pgadmin/browser/register_browser_preferences.py
+++ b/web/pgadmin/browser/register_browser_preferences.py
@@ -28,6 +28,16 @@ def register_browser_preferences(self):
True, category_label=gettext('Display')
)
+ self.preference.register(
+ 'display', 'browser_tree_state_save_interval',
+ gettext("Browser tree state saving interval"), 'integer',
+ 30, category_label=gettext('Display'),
+ help_str=gettext(
+ 'Browser tree state saving interval in seconds.'
+ 'Use -1 to disable the tree saving mechanism.'
+ )
+ )
+
self.table_row_count_threshold = self.preference.register(
'properties', 'table_row_count_threshold',
gettext("Count rows if estimated less than"), 'integer', 2000,
diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index 1c26e0c..6646066 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -585,7 +585,8 @@ class ServerNode(PGChildNodeView):
server_icon_and_background(connected, manager, server),
True,
self.node_type,
- connected=False,
+ connected=connected,
+ user=manager.user_info if connected else None,
server_type='pg' # default server type
)
)
diff --git a/web/pgadmin/browser/static/js/browser.js b/web/pgadmin/browser/static/js/browser.js
index ee02504..f1fd3b2 100644
--- a/web/pgadmin/browser/static/js/browser.js
+++ b/web/pgadmin/browser/static/js/browser.js
@@ -9,7 +9,7 @@ define('pgadmin.browser', [
'pgadmin.browser.error', 'pgadmin.browser.frame',
'pgadmin.browser.node', 'pgadmin.browser.collection',
'sources/codemirror/addon/fold/pgadmin-sqlfoldcode',
- 'pgadmin.browser.keyboard',
+ 'pgadmin.browser.keyboard', 'sources/tree/pgadmin_tree_save_state',
], function(
tree,
gettext, url_for, require, $, _, S, Bootstrap, pgAdmin, Alertify,
@@ -513,9 +513,11 @@ define('pgadmin.browser', [
.done(function() {})
.fail(function() {});
}, 300000);
+
obj.Events.on('pgadmin:browser:tree:add', obj.onAddTreeNode, obj);
obj.Events.on('pgadmin:browser:tree:update', obj.onUpdateTreeNode, obj);
obj.Events.on('pgadmin:browser:tree:refresh', obj.onRefreshTreeNode, obj);
+
},
add_menu_category: function(
@@ -1942,19 +1944,10 @@ define('pgadmin.browser', [
pgAdmin.Browser.editor_shortcut_keys.Tab = 'insertSoftTab';
}
- window.onbeforeunload = function() {
- var e = window.event,
- msg = S(gettext('Are you sure you wish to close the %s browser?')).sprintf(pgBrowser.utils.app_name).value();
-
- // For IE and Firefox prior to version 4
- if (e) {
- e.returnValue = msg;
- }
-
- // For Safari
- return msg;
- };
-
+ $(window).on('beforeunload', function() {
+ if (pgBrowser.get_preference('browser', 'browser_tree_state_save_interval').value !== -1)
+ pgAdmin.Browser.browserTreeState.save_state();
+ });
return pgAdmin.Browser;
});
diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js
index 3394696..f27db63 100644
--- a/web/pgadmin/browser/static/js/node.js
+++ b/web/pgadmin/browser/static/js/node.js
@@ -1,11 +1,11 @@
define('pgadmin.browser.node', [
- 'sources/tree/pgadmin_tree_node',
+ 'sources/tree/pgadmin_tree_node', 'sources/url_for',
'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin',
'pgadmin.browser.menu', 'backbone', 'pgadmin.alertifyjs', 'pgadmin.browser.datamodel',
'backform', 'sources/browser/generate_url', 'sources/utils', 'pgadmin.browser.utils',
'pgadmin.backform',
], function(
- pgadminTreeNode,
+ pgadminTreeNode, url_for,
gettext, $, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform, generateUrl, commonUtils
) {
@@ -849,7 +849,6 @@ define('pgadmin.browser.node', [
}
}
},
-
added: function(item, data, browser) {
var b = browser || pgBrowser,
t = b.tree,
@@ -876,6 +875,8 @@ define('pgadmin.browser.node', [
);
}
+ pgBrowser.Events.trigger('pgadmin:browser:tree:expand-from-previous-tree-state',
+ item);
pgBrowser.Node.callbacks.change_server_background(item, data);
},
// Callback called - when a node is selected in browser tree.
@@ -958,6 +959,14 @@ define('pgadmin.browser.node', [
},
});
},
+ opened: function(item) {
+ pgBrowser.Events.trigger('pgadmin:browser:tree:update-tree-state',
+ item);
+ },
+ closed: function(item) {
+ pgBrowser.Events.trigger('pgadmin:browser:tree:remove-from-tree-state',
+ item);
+ },
},
/**********************************************************************
* A hook (not a callback) to show object properties in given HTML
diff --git a/web/pgadmin/browser/static/js/preferences.js b/web/pgadmin/browser/static/js/preferences.js
index 5ca9e3c..1767d72 100644
--- a/web/pgadmin/browser/static/js/preferences.js
+++ b/web/pgadmin/browser/static/js/preferences.js
@@ -94,6 +94,9 @@ _.extend(pgBrowser, {
modifyAnimation.modifyAlertifyAnimation(self);
}
+ // Initialize Tree saving/reloading
+ pgBrowser.browserTreeState.init();
+
/* Once the cache is loaded after changing the preferences,
* notify the modules of the change
*/
diff --git a/web/pgadmin/preferences/static/js/preferences.js b/web/pgadmin/preferences/static/js/preferences.js
index eeaf4bb..840fc0a 100644
--- a/web/pgadmin/preferences/static/js/preferences.js
+++ b/web/pgadmin/preferences/static/js/preferences.js
@@ -2,6 +2,7 @@ define('pgadmin.preferences', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'pgadmin.alertifyjs', 'sources/pgadmin', 'pgadmin.backform',
'pgadmin.browser', 'sources/modify_animation',
+ 'sources/tree/pgadmin_tree_save_state',
], function(
gettext, url_for, $, _, Backbone, Alertify, pgAdmin, Backform, pgBrowser,
modifyAnimation
diff --git a/web/pgadmin/settings/__init__.py b/web/pgadmin/settings/__init__.py
index 269bfdf..7adf5b4 100644
--- a/web/pgadmin/settings/__init__.py
+++ b/web/pgadmin/settings/__init__.py
@@ -10,13 +10,15 @@
"""Utility functions for storing and retrieving user configuration settings."""
import traceback
+import json
-from flask import Response, request, render_template, url_for
+from flask import Response, request, render_template, url_for, current_app
from flask_babelex import gettext
from flask_login import current_user
from flask_security import login_required
from pgadmin.utils import PgAdminModule
-from pgadmin.utils.ajax import make_json_response, bad_request
+from pgadmin.utils.ajax import make_json_response, bad_request,\
+ success_return, internal_server_error
from pgadmin.utils.menu import MenuItem
from pgadmin.model import db, Setting
@@ -52,7 +54,8 @@ class SettingsModule(PgAdminModule):
list: a list of url endpoints exposed to the client.
"""
return [
- 'settings.store', 'settings.store_bulk', 'settings.reset_layout'
+ 'settings.store', 'settings.store_bulk', 'settings.reset_layout',
+ 'settings.save_tree_state', 'settings.get_tree_state'
]
@@ -145,3 +148,48 @@ def reset_layout():
)
return make_json_response(result=request.form)
+
+
[email protected]("/save_tree_state/", endpoint="save_tree_state",
+ methods=['POST'])
+@login_required
+def save_browser_tree_state():
+ """Save the browser tree state."""
+ data = request.form if request.form else request.data.decode('utf-8')
+ old_data = get_setting('browser_tree_state')
+
+ if old_data and old_data != 'null':
+ if data:
+ data = json.loads(data)
+
+ old_data = json.loads(old_data)
+
+ old_data.update(data)
+ new_data = json.dumps(old_data)
+ else:
+ new_data = data
+
+ try:
+ store_setting('browser_tree_state', new_data)
+ except Exception as e:
+ current_app.logger.exception(e)
+ return internal_server_error(errormsg=str(e))
+
+ return success_return()
+
+
[email protected]("/get_tree_state/", endpoint="get_tree_state",
+ methods=['GET'])
+@login_required
+def get_browser_tree_state():
+ """Get the browser tree state."""
+
+ try:
+ data = get_setting('browser_tree_state')
+ except Exception as e:
+ current_app.logger.exception(e)
+ return internal_server_error(errormsg=str(e))
+
+ return Response(response=data,
+ status=200,
+ mimetype="application/json")
diff --git a/web/pgadmin/static/js/tree/pgadmin_tree_save_state.js b/web/pgadmin/static/js/tree/pgadmin_tree_save_state.js
new file mode 100644
index 0000000..f8efda4
--- /dev/null
+++ b/web/pgadmin/static/js/tree/pgadmin_tree_save_state.js
@@ -0,0 +1,247 @@
+import _ from 'underscore';
+import $ from 'jquery';
+import url_for from 'sources/url_for';
+import gettext from 'sources/gettext';
+import pgAdmin from 'sources/pgadmin';
+
+const pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
+
+
+const browserTreeState = pgBrowser.browserTreeState = pgBrowser.browserTreeState || {};
+
+_.extend(pgBrowser.browserTreeState, {
+
+ // Parent node to start saving / reloading the tree state
+ parent: 'server',
+
+ // The original parent of the browser tree
+ orig_parent: 'server_group',
+
+ // Stored tree state
+ // Sample Object
+ // {1: [
+ // server_group/1,/server/1,/coll-database/1,/database/1,
+ // server_group/1,/server/1,/coll-database/1,/database/2
+ // ]}
+ // Here key is server ID
+
+ stored_state: {},
+
+ // Previous tree state
+ last_state: {},
+
+ // Current tree state
+ current_state: {},
+
+ init: function() {
+
+ const save_tree_state_period = pgBrowser.get_preference('browser', 'browser_tree_state_save_interval');
+
+ if (!_.isUndefined(save_tree_state_period) && save_tree_state_period.value !== -1) {
+ // Save the tree state every 30 seconds
+ setInterval(this.save_state, (save_tree_state_period.value) * 1000);
+
+ // Fetch the tree last state while loading the browser tree
+ this.fetch_state.apply(this);
+
+ pgBrowser.Events.on('pgadmin:browser:tree:expand-from-previous-tree-state',
+ this.expand_from_previous_state, this);
+ pgBrowser.Events.on('pgadmin:browser:tree:remove-from-tree-state',
+ this.remove_from_cache, this);
+ pgBrowser.Events.on('pgadmin:browser:tree:update-tree-state',
+ this.update_cache, this);
+ }
+ },
+ save_state: function() {
+
+ var self = pgBrowser.browserTreeState;
+ if(self.last_state == JSON.stringify(self.current_state))
+ return;
+
+ $.ajax({
+ url: url_for('settings.save_tree_state'),
+ type: 'POST',
+ contentType: 'application/json',
+ data: JSON.stringify(self.current_state),
+ })
+ .done(function() {
+ self.last_state = JSON.stringify(self.current_state);
+ })
+ .fail(function(jqx) {
+ var msg = jqx.responseText;
+ /* Error from the server */
+ if (jqx.status == 417 || jqx.status == 410 || jqx.status == 500) {
+ try {
+ var data = JSON.parse(jqx.responseText);
+ msg = data.errormsg;
+ } catch (e) {
+ console.warn(e.stack || e);
+ }
+ }
+ console.warn(
+ gettext('Error saving the tree state."'), msg);
+ });
+
+ },
+ fetch_state: function() {
+
+ var self = this;
+ $.ajax({
+ url: url_for('settings.get_tree_state'),
+ type: 'GET',
+ dataType: 'json',
+ contentType: 'application/json',
+ })
+ .done(function(res) {
+ self.stored_state = res;
+ })
+ .fail(function(jqx) {
+ var msg = jqx.responseText;
+ /* Error from the server */
+ if (jqx.status == 417 || jqx.status == 410 || jqx.status == 500) {
+ try {
+ var data = JSON.parse(jqx.responseText);
+ msg = data.errormsg;
+ } catch (e) {
+ console.warn(e.stack || e);
+ }
+ }
+ console.warn(
+ gettext('Error fetching the tree state."'), msg);
+ });
+ },
+ update_cache: function(item) {
+ let data = item && pgBrowser.tree.itemData(item),
+ node = data && pgBrowser.Nodes[data._type],
+ treeHierarchy = node.getTreeNodeHierarchy(item),
+ topParent = undefined,
+ pathIDs = pgBrowser.tree.pathId(item),
+ oldPath = pathIDs.join(),
+ path = [],
+ tmpIndex = -1;
+
+ // If no parent so or the server not in tree hierarchy then return
+ if (!pgBrowser.tree.hasParent(item) || !(this.parent in treeHierarchy))
+ return;
+
+ topParent = treeHierarchy[this.parent]['_id'];
+
+ if (pgBrowser.tree.isOpen(item)) {
+ pathIDs.push(data.id);
+ path = pathIDs.join();
+
+ // IF the current path is already saved then return
+ let index = _.find(this.current_state[topParent], function(tData) {
+ return (tData.search(path) !== -1);
+ });
+ if(!_.isUndefined(index))
+ return;
+
+ // Add / Update the current item into the tree path
+ if (!_.isUndefined(this.current_state[topParent])) {
+ tmpIndex = this.current_state[topParent].indexOf(oldPath);
+ } else {
+ this.current_state[topParent] = [];
+ }
+ if (tmpIndex !== -1) {
+ this.current_state[topParent][tmpIndex] = path;
+ }
+ else {
+ this.current_state[topParent].push(path);
+ }
+ }
+ },
+ remove_from_cache: function(item) {
+ let self= this,
+ treeData = self.stored_state || {},
+ data = item && pgBrowser.tree.itemData(item),
+ node = data && pgBrowser.Nodes[data._type],
+ treeHierarchy = node && node.getTreeNodeHierarchy(item);
+
+ if (!pgBrowser.tree.hasParent(item) || !(self.parent in treeHierarchy))
+ return;
+
+ let topParent = treeHierarchy && treeHierarchy[self.parent]['_id'],
+ origParent = treeHierarchy && treeHierarchy[self.orig_parent]['id'];
+
+ if (data._type == self.parent) {
+ treeData[topParent] = self.current_state[topParent];
+ self.save_state();
+ return;
+ }
+
+ if (pgBrowser.tree.isClosed(item)) {
+ let tmpTreeData = self.current_state[topParent];
+ if (!_.isUndefined(tmpTreeData) && !_.isUndefined(tmpTreeData.length)) {
+ let tcnt = 0,
+ tmpItemDataStr = undefined;
+ _.each(tmpTreeData, function(tData) {
+ if (_.isUndefined(tData))
+ return;
+
+ let tmpItemData = tData.split(',');
+
+ if (tmpItemData.indexOf(data.id) !== -1 ) {
+ let index = tmpItemData.indexOf(data.id);
+ tmpItemData.splice(index);
+ tmpItemDataStr = tmpItemData.join();
+
+ if (tmpItemDataStr == origParent)
+ self.current_state[topParent].splice(tData, 1);
+ else
+ self.current_state[topParent][tcnt] = tmpItemDataStr;
+ }
+ tcnt ++;
+ });
+
+ if (tmpItemDataStr !== undefined) {
+ let tmpIndex = _.find(tmpTreeData, function(tData) {
+ return (tData.search(tmpItemDataStr) !== -1);
+ });
+
+ if(tmpIndex && tmpIndex !== tmpTreeData.indexOf(tmpItemDataStr))
+ this.current_state[topParent].splice(tmpIndex, 1);
+ }
+ }
+ }
+ },
+ expand_from_previous_state: function(item) {
+ let self = this,
+ treeData = self.stored_state || {},
+ data = item && pgBrowser.tree.itemData(item),
+ node = data && pgBrowser.Nodes[data._type],
+ treeHierarchy = node && node.getTreeNodeHierarchy(item);
+
+ if (!pgBrowser.tree.hasParent(item) || !(self.parent in treeHierarchy))
+ return;
+
+ // If the server node is open then only we should populate the tree
+ if (data['_type'] == self.parent && (pgBrowser.tree.isOpen(item) === false))
+ return;
+
+ let tmpTreeData = treeData[treeHierarchy[self.parent]['_id']];
+
+ if (!_.isUndefined(tmpTreeData) && !_.isUndefined(tmpTreeData.length)) {
+ _.each(tmpTreeData, function(tData) {
+ if (_.isUndefined(tData))
+ return;
+
+ let tmpItemData = tData.split(',');
+
+ // If the item is in the lastTreeState then open it
+ if (tmpItemData.indexOf(data.id) !== -1 ) {
+ let index = tmpItemData.indexOf(data.id);
+ pgBrowser.tree.toggle(item);
+
+ if (index == (tmpItemData.length - 1 )) {
+ let tIndex = treeData[treeHierarchy[self.parent]['_id']].indexOf(tData);
+ treeData[treeHierarchy[self.parent]['_id']].splice(tIndex, 1);
+ }
+ }
+ });
+ }
+ },
+
+});
+
+export {pgBrowser, browserTreeState};
diff --git a/web/regression/javascript/tree/pgadmin_tree_state_save_spec.js b/web/regression/javascript/tree/pgadmin_tree_state_save_spec.js
new file mode 100644
index 0000000..769b663
--- /dev/null
+++ b/web/regression/javascript/tree/pgadmin_tree_state_save_spec.js
@@ -0,0 +1,240 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+import {pgBrowser, browserTreeState} from '../../../pgadmin/static/js/tree/pgadmin_tree_save_state';
+
+
+describe('#browserTreeState', () => {
+ beforeEach(() => {
+ pgBrowser.Nodes = {
+ server_group: {
+ _type: 'server_group',
+ hasId: true,
+ id: 'server_group/1',
+ _id: 1,
+ getTreeNodeHierarchy: function() { return {
+ server_group: {
+ _type: 'server_group',
+ hasId: true,
+ id: 'server_group/1',
+ _id: 1,
+ },
+ };},
+ },
+ server: {
+ _type: 'server',
+ hasId: true,
+ id: 'server/1',
+ _id: 1,
+ getTreeNodeHierarchy: function() { return {
+ server_group: {
+ _type: 'server_group',
+ hasId: true,
+ id: 'server_group/1',
+ _id: 1,
+ },
+ server: {
+ _type: 'server',
+ hasId: true,
+ id: 'server/1',
+ _id: 1,
+ },
+ };},
+ },
+ coll_database: {
+ _type: 'coll_database',
+ hasId: false,
+ id: 'coll_database/1',
+ _id: 1,
+ getTreeNodeHierarchy: function() { return {
+ server_group: {
+ _type: 'server_group',
+ hasId: true,
+ id: 'server_group/1',
+ _id: 1,
+ },
+ server: {
+ _type: 'server',
+ hasId: true,
+ id: 'server/1',
+ _id: 1,
+ },
+ coll_database: {
+ _type: 'coll_database',
+ hasId: true,
+ id: 'coll_database/1',
+ _id: 1,
+ },
+ };},
+ },
+ database: {
+ _type: 'database',
+ hasId: true,
+ id: 'database/10',
+ _id: 10,
+ getTreeNodeHierarchy: function() { return {
+ server_group: {
+ _type: 'server_group',
+ hasId: true,
+ id: 'server_group/1',
+ _id: 1,
+ },
+ server: {
+ _type: 'server',
+ hasId: true,
+ id: 'server/1',
+ _id: 1,
+ },
+ coll_database: {
+ _type: 'coll_database',
+ hasId: true,
+ id: 'coll_database/1',
+ _id: 1,
+ },
+ database: {
+ _type: 'database',
+ hasId: true,
+ id: 'database/10',
+ _id: 10,
+ },
+ };},
+ },
+ };
+ pgBrowser.tree = jasmine.createSpyObj('tree', ['itemData', 'pathId', 'hasParent', 'isOpen', 'isClosed']);
+ });
+
+ describe('When node is opened tree state is getting updated', () => {
+
+ describe('When tree node server group is opened', () => {
+ let item = {
+ _type: 'server_group',
+ hasId: true,
+ id: 'server_group/1',
+ _id: 1,
+ };
+ beforeEach(() => {
+ pgBrowser.tree.itemData.and.returnValue(item);
+ pgBrowser.tree.pathId.and.returnValue([]);
+ pgBrowser.tree.hasParent.and.returnValue(false);
+ pgBrowser.tree.isOpen.and.returnValue(true);
+ });
+
+ it('The tree current state will be empty', () => {
+ browserTreeState.update_cache(item);
+ expect(browserTreeState.current_state, {});
+ });
+ });
+
+ describe('When server node is opened', () => {
+ let item = {
+ _type: 'server',
+ hasId: true,
+ id: 'server/1',
+ _id: 1,
+ };
+ beforeEach(() => {
+ pgBrowser.tree.itemData.and.returnValue(item);
+ pgBrowser.tree.pathId.and.returnValue(['server_group/1']);
+ pgBrowser.tree.hasParent.and.returnValue(true);
+ pgBrowser.tree.isOpen.and.returnValue(true);
+ });
+
+ it('The tree current state will have server', () => {
+ browserTreeState.update_cache(item);
+ expect(browserTreeState.current_state, {1: ['server_group/1,server/1']});
+ });
+ });
+
+ describe('When coll_database node is opened', () => {
+ let item = {
+ _type: 'coll_database',
+ hasId: true,
+ id: 'coll_database/1',
+ _id: 1,
+ };
+ beforeEach(() => {
+ pgBrowser.tree.itemData.and.returnValue(item);
+ pgBrowser.tree.pathId.and.returnValue(['server_group/1', 'server/1']);
+ pgBrowser.tree.hasParent.and.returnValue(true);
+ pgBrowser.tree.isOpen.and.returnValue(true);
+ });
+
+ it('The tree current state will have coll_database', () => {
+ browserTreeState.update_cache(item);
+ expect(browserTreeState.current_state, {1: ['server_group/1,server/1,coll_database/1']});
+ });
+ });
+
+ describe('When database node is opened', () => {
+ let item = {
+ _type: 'database',
+ hasId: true,
+ id: 'database/10',
+ _id: 10,
+ };
+ beforeEach(() => {
+ pgBrowser.tree.itemData.and.returnValue(item);
+ pgBrowser.tree.pathId.and.returnValue(['server_group/1', 'server/1', 'coll_database/1']);
+ pgBrowser.tree.hasParent.and.returnValue(true);
+ pgBrowser.tree.isOpen.and.returnValue(true);
+ });
+
+ it('The tree current state will have coll_database', () => {
+ browserTreeState.update_cache(item);
+ expect(browserTreeState.current_state, {1: ['server_group/1,server/1,coll_database/1','database/10']});
+ });
+ });
+ });
+
+
+ describe('When node is closed, node should get removed from the tree state cache', () => {
+ describe('When coll_database node is closed, both database and coll_database should be removed', () => {
+ let item = {
+ _type: 'coll_database',
+ hasId: true,
+ id: 'coll_database/1',
+ _id: 1,
+ };
+ beforeEach(() => {
+ pgBrowser.tree.itemData.and.returnValue(item);
+ pgBrowser.tree.pathId.and.returnValue(['server_group/1', 'server/1']);
+ pgBrowser.tree.hasParent.and.returnValue(true);
+ pgBrowser.tree.isOpen.and.returnValue(true);
+ pgBrowser.tree.isClosed.and.returnValue(true);
+ });
+
+ it('The tree current state will remove coll_database and database', () => {
+ browserTreeState.remove_from_cache(item);
+ expect(browserTreeState.current_state, {1: ['server_group/1,server/1']});
+ });
+ });
+
+ describe('When server node is closed, both server and server_group should be removed', () => {
+ let item = {
+ _type: 'server',
+ hasId: true,
+ id: 'server/1',
+ _id: 1,
+ };
+ beforeEach(() => {
+ pgBrowser.tree.itemData.and.returnValue(item);
+ pgBrowser.tree.pathId.and.returnValue(['server_group/1']);
+ pgBrowser.tree.hasParent.and.returnValue(true);
+ pgBrowser.tree.isOpen.and.returnValue(true);
+ pgBrowser.tree.isClosed.and.returnValue(true);
+ });
+
+ it('The tree current state will remove server_group and server', () => {
+ browserTreeState.remove_from_cache(item);
+ expect(browserTreeState.current_state, {1: []});
+ });
+ });
+ });
+
+});