Skip to content

Commit

Permalink
Merge pull request #8273 from spalger/backport/4.6/smallerState
Browse files Browse the repository at this point in the history
Backport smaller state representation to 4.6
  • Loading branch information
epixa authored Oct 11, 2016
2 parents 332abe8 + 7fbd389 commit 2a44afd
Show file tree
Hide file tree
Showing 27 changed files with 1,554 additions and 199 deletions.
21 changes: 14 additions & 7 deletions src/plugins/kibana/public/discover/controllers/discover.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ define(function (require) {
const moment = require('moment');
const ConfigTemplate = require('ui/ConfigTemplate');
const getSort = require('ui/doc_table/lib/get_sort');
const rison = require('ui/utils/rison');

const dateMath = require('ui/utils/dateMath');
const stateMonitorFactory = require('ui/state_management/state_monitor_factory');
const StateProvider = require('ui/state_management/state');

require('ui/doc_table');
require('ui/visualize');
Expand All @@ -33,18 +33,25 @@ define(function (require) {
template: require('plugins/kibana/discover/index.html'),
reloadOnSearch: false,
resolve: {
ip: function (Promise, courier, config, $location) {
ip: function (Promise, courier, config, $location, Private) {
const State = Private(StateProvider);
return courier.indexPatterns.getIds()
.then(function (list) {
const stateRison = $location.search()._a;

let state;
try { state = rison.decode(stateRison); }
catch (e) { state = {}; }
/**
* In making the indexPattern modifiable it was placed in appState. Unfortunately,
* the load order of AppState conflicts with the load order of many other things
* so in order to get the name of the index we should use, and to switch to the
* default if necessary, we parse the appState with a temporary State object and
* then destroy it immediatly after we're done
*
* @type {State}
*/
const state = new State('_a', {});

const specified = !!state.index;
const exists = _.contains(list, state.index);
const id = exists ? state.index : config.get('defaultIndex');
state.destroy();

return Promise.props({
list: list,
Expand Down
101 changes: 101 additions & 0 deletions src/testUtils/__tests__/stub_browser_storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import expect from 'expect.js';

import StubBrowserStorage from '../stub_browser_storage';

describe('StubBrowserStorage', () => {
describe('#getItem() / #setItem()', () => {
it('stores items as strings', () => {
const store = new StubBrowserStorage();
store.setItem(1, 1);
expect(store.getItem(1)).to.be('1');
});

it('stores keys as strings', () => {
const store = new StubBrowserStorage();
store.setItem(1, 1);
expect(store.key(0)).to.be('1');
});

it('returns null for missing keys', () => {
const store = new StubBrowserStorage();
expect(store.getItem('unknown key')).to.be(null);
});
});

describe('#length', () => {
it('reports the number of items stored', () => {
const store = new StubBrowserStorage();
store.setItem(1, 1);
store.setItem(2, 2);
store.setItem(3, 3);
store.setItem(4, 4);
expect(store).to.have.length(4);
});

it('does not trip on items getting reset', () => {
const store = new StubBrowserStorage();
store.setItem(1, 1);
store.setItem(1, 2);
expect(store).to.have.length(1);
});
});

describe('#key()', () => {
it('returns the key as a specific index', () => {
const store = new StubBrowserStorage();
store.setItem(1, 2);
expect(store.key(0)).to.be('1');
expect(store.key(1)).to.be(undefined);
});
});

describe('#setStubbedSizeLimit', () => {
it('allows limiting the storage size', () => {
const store = new StubBrowserStorage();
store.setStubbedSizeLimit(10);
store.setItem('abc', 'def'); // store size is 6, key.length + val.length
expect(() => {
store.setItem('ghi', 'jkl');
}).throwError(/quota/);
});

it('allows defining the limit as infinity', () => {
const store = new StubBrowserStorage();
store.setStubbedSizeLimit(Infinity);
store.setItem('abc', 'def');
store.setItem('ghi', 'jkl'); // unlike the previous test, this doesn't throw
});

it('throws an error if the limit is below the current size', () => {
const store = new StubBrowserStorage();
store.setItem('key', 'val');
expect(() => {
store.setStubbedSizeLimit(5);
}).throwError(Error);
});

it('respects removed items', () => {
const store = new StubBrowserStorage();
store.setStubbedSizeLimit(10);
store.setItem('abc', 'def');
store.removeItem('abc');
store.setItem('ghi', 'jkl'); // unlike the previous test, this doesn't throw
});
});

describe('#getStubbedSizeLimit', () => {
it('returns the size limit', () => {
const store = new StubBrowserStorage();
store.setStubbedSizeLimit(10);
expect(store.getStubbedSizeLimit()).to.equal(10);
});
});

describe('#getStubbedSize', () => {
it('returns the size', () => {
const store = new StubBrowserStorage();
store.setItem(1, 1);
expect(store.getStubbedSize()).to.equal(2);
});
});
});
109 changes: 109 additions & 0 deletions src/testUtils/stub_browser_storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
export default class StubBrowserStorage {
constructor() {
this._keys = [];
this._values = [];
this._size = 0;
this._sizeLimit = 5000000; // 5mb, minimum browser storage size
}

// -----------------------------------------------------------------------------------------------
// Browser-specific methods.
// -----------------------------------------------------------------------------------------------

get length() {
return this._keys.length;
}

key(i) {
return this._keys[i];
}

getItem(key) {
key = String(key);

const i = this._keys.indexOf(key);
if (i === -1) return null;
return this._values[i];
}

setItem(key, value) {
key = String(key);
value = String(value);
const sizeOfAddition = this._getSizeOfAddition(key, value);
this._updateSize(sizeOfAddition);

const i = this._keys.indexOf(key);
if (i === -1) {
this._keys.push(key);
this._values.push(value);
} else {
this._values[i] = value;
}
}

removeItem(key) {
key = String(key);
const sizeOfRemoval = this._getSizeOfRemoval(key);
this._updateSize(sizeOfRemoval);

const i = this._keys.indexOf(key);
if (i === -1) return;
this._keys.splice(i, 1);
this._values.splice(i, 1);
}

// -----------------------------------------------------------------------------------------------
// Test-specific methods.
// -----------------------------------------------------------------------------------------------

getStubbedKeys() {
return this._keys.slice();
}

getStubbedValues() {
return this._values.slice();
}

setStubbedSizeLimit(sizeLimit) {
// We can't reconcile a size limit with the "stored" items, if the stored items size exceeds it.
if (sizeLimit < this._size) {
throw new Error(`You can't set a size limit smaller than the current size.`);
}

this._sizeLimit = sizeLimit;
}

getStubbedSizeLimit() {
return this._sizeLimit;
}

getStubbedSize() {
return this._size;
}

_getSizeOfAddition(key, value) {
const i = this._keys.indexOf(key);
if (i === -1) {
return key.length + value.length;
}
// Return difference of what's been stored, and what *will* be stored.
return value.length - this._values[i].length;
}

_getSizeOfRemoval(key) {
const i = this._keys.indexOf(key);
if (i === -1) {
return 0;
}
// Return negative value.
return -(key.length + this._values[i].length);
}

_updateSize(delta) {
if (this._size + delta > this._sizeLimit) {
throw new Error('something about quota exceeded, browsers are not consistent here');
}

this._size += delta;
}
}
6 changes: 3 additions & 3 deletions src/ui/public/chrome/__tests__/Tab.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const Tab = require('../Tab');
const expect = require('expect.js');
const TabFakeStore = require('./_TabFakeStore');
const StubBrowserStorage = require('testUtils/stub_browser_storage');

describe('Chrome Tab', function () {
describe('construction', function () {
Expand Down Expand Up @@ -87,7 +87,7 @@ describe('Chrome Tab', function () {
});

it('discovers the lastUrl', function () {
const lastUrlStore = new TabFakeStore();
const lastUrlStore = new StubBrowserStorage();
const tab = new Tab({ id: 'foo', lastUrlStore });
expect(tab.lastUrl).to.not.equal('bar');

Expand All @@ -102,7 +102,7 @@ describe('Chrome Tab', function () {

describe('#setLastUrl()', function () {
it('updates the lastUrl and storage value if passed a lastUrlStore', function () {
const lastUrlStore = new TabFakeStore();
const lastUrlStore = new StubBrowserStorage();
const tab = new Tab({ id: 'foo', lastUrlStore });

expect(tab.lastUrl).to.not.equal('foo');
Expand Down
4 changes: 2 additions & 2 deletions src/ui/public/chrome/__tests__/TabCollection.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const expect = require('expect.js');
const { indexBy, random } = require('lodash');

const TabFakeStore = require('./_TabFakeStore');
const StubBrowserStorage = require('testUtils/stub_browser_storage');
const TabCollection = require('../TabCollection');
const Tab = require('../Tab');

Expand Down Expand Up @@ -54,7 +54,7 @@ describe('Chrome TabCollection', function () {

describe('#consumeRouteUpdate()', function () {
it('updates the active tab', function () {
const store = new TabFakeStore();
const store = new StubBrowserStorage();
const baseUrl = `http://localhost:${random(1000, 9999)}`;
const tabs = new TabCollection({ store, defaults: { baseUrl } });
tabs.set([
Expand Down
9 changes: 0 additions & 9 deletions src/ui/public/chrome/__tests__/_TabFakeStore.js

This file was deleted.

10 changes: 5 additions & 5 deletions src/ui/public/chrome/api/__tests__/apps.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const expect = require('expect.js');

const setup = require('../apps');
const TabFakeStore = require('../../__tests__/_TabFakeStore');
const StubBrowserStorage = require('testUtils/stub_browser_storage');

describe('Chrome API :: apps', function () {
describe('#get/setShowAppsLink()', function () {
Expand Down Expand Up @@ -147,13 +147,13 @@ describe('Chrome API :: apps', function () {
describe('#get/setLastUrlFor()', function () {
it('reads/writes last url from storage', function () {
const chrome = {};
const store = new TabFakeStore();
const store = new StubBrowserStorage();
setup(chrome, { appUrlStore: store });
expect(chrome.getLastUrlFor('app')).to.equal(undefined);
expect(chrome.getLastUrlFor('app')).to.equal(null);
chrome.setLastUrlFor('app', 'url');
expect(chrome.getLastUrlFor('app')).to.equal('url');
expect(store.getKeys().length).to.equal(1);
expect(store.getValues().shift()).to.equal('url');
expect(store.getStubbedKeys().length).to.equal(1);
expect(store.getStubbedValues().shift()).to.equal('url');
});
});
});
Expand Down
20 changes: 15 additions & 5 deletions src/ui/public/chrome/api/angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { format as formatUrl, parse as parseUrl } from 'url';
import Notifier from 'ui/notify/notifier';
import { UrlOverflowServiceProvider } from '../../error_url_overflow';

const URL_LIMIT_WARN_WITHIN = 150;
const URL_LIMIT_WARN_WITHIN = 1000;

module.exports = function (chrome, internals) {

Expand Down Expand Up @@ -35,10 +35,20 @@ module.exports = function (chrome, internals) {

try {
if (urlOverflow.check($location.absUrl()) <= URL_LIMIT_WARN_WITHIN) {
notify.warning(`
The URL has gotten big and may cause Kibana
to stop working. Please simplify the data on screen.
`);
notify.directive({
template: `
<p>
The URL has gotten big and may cause Kibana
to stop working. Please either enable the
<code>state:storeInSessionStorage</code>
option in the <a href="#/management/kibana/settings">advanced
settings</a> or simplify the onscreen visuals.
</p>
`
}, {
type: 'error',
actions: [{ text: 'close' }]
});
}
} catch (e) {
const { host, path, search, protocol } = parseUrl(window.location.href);
Expand Down
Loading

0 comments on commit 2a44afd

Please sign in to comment.