From 1178adde4ea4788fff92980e05d06e433533b598 Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Thu, 18 Jun 2020 15:09:42 +0200 Subject: [PATCH 1/3] index pattern saved in local storage --- .../indexpattern_datasource/indexpattern.tsx | 3 +++ .../public/indexpattern_datasource/loader.ts | 26 ++++++++++++++++++- .../plugins/lens/public/settings_storage.tsx | 15 +++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/lens/public/settings_storage.tsx diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 9c4f6c9b590ce..a98f63cf9b360 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -125,6 +125,7 @@ export function getIndexPatternDatasource({ state, savedObjectsClient: await savedObjectsClient, defaultIndexPatternId: core.uiSettings.get('defaultIndex'), + storage, }); }, @@ -207,6 +208,7 @@ export function getIndexPatternDatasource({ setState, savedObjectsClient, onError: onIndexPatternLoadError, + storage, }); }} data={data} @@ -290,6 +292,7 @@ export function getIndexPatternDatasource({ layerId: props.layerId, onError: onIndexPatternLoadError, replaceIfPossible: true, + storage, }); }} {...props} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index c34f4c1d23148..0c9be223b43e4 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -5,6 +5,7 @@ */ import _ from 'lodash'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { SavedObjectsClientContract, SavedObjectAttributes, HttpSetup } from 'kibana/public'; import { SimpleSavedObject } from 'kibana/public'; import { StateSetter } from '../types'; @@ -24,6 +25,7 @@ import { IFieldType, IndexPatternTypeMeta, } from '../../../../../src/plugins/data/public'; +import { readFromStorage, writeToStorage } from '../../settings_storage'; interface SavedIndexPatternAttributes extends SavedObjectAttributes { title: string; @@ -68,22 +70,38 @@ export async function loadIndexPatterns({ ); } +const getLastUsedIndexPatternId = ( + storage: IStorageWrapper, + indexPatternRefs: IndexPatternRef[] +) => { + const indexPattern = readFromStorage(storage, 'indexPattern'); + return indexPattern && indexPatternRefs.find((i) => i.id === indexPattern)?.id; +}; + +const setLastUsedIndexPatternId = (storage: IStorageWrapper, value: string) => { + writeToStorage(storage, 'indexPattern', value); +}; + export async function loadInitialState({ state, savedObjectsClient, defaultIndexPatternId, + storage, }: { state?: IndexPatternPersistedState; savedObjectsClient: SavedObjectsClient; defaultIndexPatternId?: string; + storage: IStorageWrapper; }): Promise { const indexPatternRefs = await loadIndexPatternRefs(savedObjectsClient); + const lastUsedIndexPatternId = getLastUsedIndexPatternId(storage, indexPatternRefs); + const requiredPatterns = _.unique( state ? Object.values(state.layers) .map((l) => l.indexPatternId) .concat(state.currentIndexPatternId) - : [defaultIndexPatternId || indexPatternRefs[0].id] + : [lastUsedIndexPatternId || defaultIndexPatternId || indexPatternRefs[0].id] ); const currentIndexPatternId = requiredPatterns[0]; @@ -120,12 +138,14 @@ export async function changeIndexPattern({ state, setState, onError, + storage, }: { id: string; savedObjectsClient: SavedObjectsClient; state: IndexPatternPrivateState; setState: SetState; onError: ErrorHandler; + storage: IStorageWrapper; }) { try { const indexPatterns = await loadIndexPatterns({ @@ -145,6 +165,7 @@ export async function changeIndexPattern({ }, currentIndexPatternId: id, })); + setLastUsedIndexPatternId(storage, id); } catch (err) { onError(err); } @@ -158,6 +179,7 @@ export async function changeLayerIndexPattern({ setState, onError, replaceIfPossible, + storage, }: { indexPatternId: string; layerId: string; @@ -166,6 +188,7 @@ export async function changeLayerIndexPattern({ setState: SetState; onError: ErrorHandler; replaceIfPossible?: boolean; + storage: IStorageWrapper; }) { try { const indexPatterns = await loadIndexPatterns({ @@ -186,6 +209,7 @@ export async function changeLayerIndexPattern({ }, currentIndexPatternId: replaceIfPossible ? indexPatternId : s.currentIndexPatternId, })); + setLastUsedIndexPatternId(storage, indexPatternId); } catch (err) { onError(err); } diff --git a/x-pack/plugins/lens/public/settings_storage.tsx b/x-pack/plugins/lens/public/settings_storage.tsx new file mode 100644 index 0000000000000..899941df1c532 --- /dev/null +++ b/x-pack/plugins/lens/public/settings_storage.tsx @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +const STORAGE_KEY = 'lens-settings'; + +export const readFromStorage = (storage: IStorageWrapper, key: string) => { + const data = storage.get(STORAGE_KEY); + return data && data[key]; +}; +export const writeToStorage = (storage: IStorageWrapper, key: string, value: string) => { + storage.set(STORAGE_KEY, { [key]: value }); +}; From f1c6ec144a1f1ead6900ccf5c8b5ebd3159d1ac4 Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Thu, 25 Jun 2020 22:17:54 +0200 Subject: [PATCH 2/3] tests added --- .../indexpattern_datasource/loader.test.ts | 74 +++++++++++++++++++ .../public/indexpattern_datasource/loader.ts | 6 +- .../plugins/lens/public/settings_storage.tsx | 2 + 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts index b54ad3651471d..8f67717c4e48f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts @@ -5,6 +5,7 @@ */ import { SavedObjectsClientContract } from 'kibana/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import _ from 'lodash'; import { loadInitialState, @@ -18,6 +19,17 @@ import { documentField } from './document_field'; jest.mock('./operations'); +const createMockStorage = (lastData: Record) => { + return { + get: jest.fn().mockImplementation(() => lastData), + set: jest.fn().mockImplementation((key, value) => { + lastData[key] = value; + }), + remove: jest.fn(), + clear: jest.fn(), + }; +}; + const sampleIndexPatterns = { a: { id: 'a', @@ -271,6 +283,7 @@ describe('loader', () => { it('should load a default state', async () => { const state = await loadInitialState({ savedObjectsClient: mockClient(), + storage: createMockStorage(), }); expect(state).toMatchObject({ @@ -287,10 +300,51 @@ describe('loader', () => { }); }); + it('should load a default state when lastUsedIndexPatternId is not found in indexPatternRefs', async () => { + const state = await loadInitialState({ + savedObjectsClient: mockClient(), + storage: createMockStorage({ indexPatternId: 'c' }), + }); + + expect(state).toMatchObject({ + currentIndexPatternId: 'a', + indexPatternRefs: [ + { id: 'a', title: sampleIndexPatterns.a.title }, + { id: 'b', title: sampleIndexPatterns.b.title }, + ], + indexPatterns: { + a: sampleIndexPatterns.a, + }, + layers: {}, + showEmptyFields: false, + }); + }); + + it('should load lastUsedIndexPatternId if in localStorage', async () => { + const state = await loadInitialState({ + savedObjectsClient: mockClient(), + storage: createMockStorage({ indexPatternId: 'b' }), + }); + + expect(state).toMatchObject({ + currentIndexPatternId: 'b', + indexPatternRefs: [ + { id: 'a', title: sampleIndexPatterns.a.title }, + { id: 'b', title: sampleIndexPatterns.b.title }, + ], + indexPatterns: { + b: sampleIndexPatterns.b, + }, + layers: {}, + showEmptyFields: false, + }); + }); + it('should use the default index pattern id, if provided', async () => { const state = await loadInitialState({ defaultIndexPatternId: 'b', savedObjectsClient: mockClient(), + storage: createMockStorage(), }); expect(state).toMatchObject({ @@ -339,6 +393,7 @@ describe('loader', () => { const state = await loadInitialState({ state: savedState, savedObjectsClient: mockClient(), + storage: createMockStorage({ indexPatternId: 'a' }), }); expect(state).toMatchObject({ @@ -367,6 +422,7 @@ describe('loader', () => { layers: {}, showEmptyFields: true, }; + const storage = createMockStorage({ indexPatternId: 'b' }); await changeIndexPattern({ state, @@ -374,6 +430,7 @@ describe('loader', () => { id: 'a', savedObjectsClient: mockClient(), onError: jest.fn(), + storage, }); expect(setState).toHaveBeenCalledTimes(1); @@ -383,6 +440,9 @@ describe('loader', () => { a: sampleIndexPatterns.a, }, }); + expect(storage.set).toHaveBeenCalledWith('lens-settings', { + indexPatternId: 'a', + }); }); it('handles errors', async () => { @@ -398,6 +458,8 @@ describe('loader', () => { showEmptyFields: true, }; + const storage = createMockStorage({ indexPatternId: 'b' }); + await changeIndexPattern({ state, setState, @@ -409,9 +471,11 @@ describe('loader', () => { }), }, onError, + storage, }); expect(setState).not.toHaveBeenCalled(); + expect(storage.set).not.toHaveBeenCalled(); expect(onError).toHaveBeenCalledWith(err); }); }); @@ -452,6 +516,8 @@ describe('loader', () => { showEmptyFields: true, }; + const storage = createMockStorage({ indexPatternId: 'a' }); + await changeLayerIndexPattern({ state, setState, @@ -459,6 +525,7 @@ describe('loader', () => { layerId: 'l1', savedObjectsClient: mockClient(), onError: jest.fn(), + storage, }); expect(setState).toHaveBeenCalledTimes(1); @@ -492,6 +559,9 @@ describe('loader', () => { }, }, }); + expect(storage.set).toHaveBeenCalledWith('lens-settings', { + indexPatternId: 'b', + }); }); it('handles errors', async () => { @@ -515,6 +585,8 @@ describe('loader', () => { showEmptyFields: true, }; + const storage = createMockStorage({ indexPatternId: 'b' }); + await changeLayerIndexPattern({ state, setState, @@ -527,9 +599,11 @@ describe('loader', () => { }), }, onError, + storage, }); expect(setState).not.toHaveBeenCalled(); + expect(storage.set).not.toHaveBeenCalled(); expect(onError).toHaveBeenCalledWith(err); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index 0c9be223b43e4..f2608cd5423e4 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -25,7 +25,7 @@ import { IFieldType, IndexPatternTypeMeta, } from '../../../../../src/plugins/data/public'; -import { readFromStorage, writeToStorage } from '../../settings_storage'; +import { readFromStorage, writeToStorage } from '../settings_storage'; interface SavedIndexPatternAttributes extends SavedObjectAttributes { title: string; @@ -74,12 +74,12 @@ const getLastUsedIndexPatternId = ( storage: IStorageWrapper, indexPatternRefs: IndexPatternRef[] ) => { - const indexPattern = readFromStorage(storage, 'indexPattern'); + const indexPattern = readFromStorage(storage, 'indexPatternId'); return indexPattern && indexPatternRefs.find((i) => i.id === indexPattern)?.id; }; const setLastUsedIndexPatternId = (storage: IStorageWrapper, value: string) => { - writeToStorage(storage, 'indexPattern', value); + writeToStorage(storage, 'indexPatternId', value); }; export async function loadInitialState({ diff --git a/x-pack/plugins/lens/public/settings_storage.tsx b/x-pack/plugins/lens/public/settings_storage.tsx index 899941df1c532..58e014512edab 100644 --- a/x-pack/plugins/lens/public/settings_storage.tsx +++ b/x-pack/plugins/lens/public/settings_storage.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; + const STORAGE_KEY = 'lens-settings'; export const readFromStorage = (storage: IStorageWrapper, key: string) => { From 3b71699d8bda8aca1d792041c286f86dce31072f Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Thu, 25 Jun 2020 23:52:09 +0200 Subject: [PATCH 3/3] fix tests and types. set index to sync --- .../indexpattern_datasource/loader.test.ts | 32 +++++++++++++------ .../public/indexpattern_datasource/loader.ts | 3 +- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts index 8f67717c4e48f..55fd8a6d936d3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts @@ -5,7 +5,6 @@ */ import { SavedObjectsClientContract } from 'kibana/public'; -import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import _ from 'lodash'; import { loadInitialState, @@ -19,12 +18,10 @@ import { documentField } from './document_field'; jest.mock('./operations'); -const createMockStorage = (lastData: Record) => { +const createMockStorage = (lastData?: Record) => { return { get: jest.fn().mockImplementation(() => lastData), - set: jest.fn().mockImplementation((key, value) => { - lastData[key] = value; - }), + set: jest.fn(), remove: jest.fn(), clear: jest.fn(), }; @@ -281,9 +278,10 @@ describe('loader', () => { describe('loadInitialState', () => { it('should load a default state', async () => { + const storage = createMockStorage(); const state = await loadInitialState({ savedObjectsClient: mockClient(), - storage: createMockStorage(), + storage, }); expect(state).toMatchObject({ @@ -298,12 +296,16 @@ describe('loader', () => { layers: {}, showEmptyFields: false, }); + expect(storage.set).toHaveBeenCalledWith('lens-settings', { + indexPatternId: 'a', + }); }); it('should load a default state when lastUsedIndexPatternId is not found in indexPatternRefs', async () => { + const storage = createMockStorage({ indexPatternId: 'c' }); const state = await loadInitialState({ savedObjectsClient: mockClient(), - storage: createMockStorage({ indexPatternId: 'c' }), + storage, }); expect(state).toMatchObject({ @@ -318,6 +320,9 @@ describe('loader', () => { layers: {}, showEmptyFields: false, }); + expect(storage.set).toHaveBeenCalledWith('lens-settings', { + indexPatternId: 'a', + }); }); it('should load lastUsedIndexPatternId if in localStorage', async () => { @@ -341,10 +346,11 @@ describe('loader', () => { }); it('should use the default index pattern id, if provided', async () => { + const storage = createMockStorage(); const state = await loadInitialState({ defaultIndexPatternId: 'b', savedObjectsClient: mockClient(), - storage: createMockStorage(), + storage, }); expect(state).toMatchObject({ @@ -359,6 +365,9 @@ describe('loader', () => { layers: {}, showEmptyFields: false, }); + expect(storage.set).toHaveBeenCalledWith('lens-settings', { + indexPatternId: 'b', + }); }); it('should initialize from saved state', async () => { @@ -390,10 +399,11 @@ describe('loader', () => { }, }, }; + const storage = createMockStorage({ indexPatternId: 'a' }); const state = await loadInitialState({ state: savedState, savedObjectsClient: mockClient(), - storage: createMockStorage({ indexPatternId: 'a' }), + storage, }); expect(state).toMatchObject({ @@ -408,6 +418,10 @@ describe('loader', () => { layers: savedState.layers, showEmptyFields: false, }); + + expect(storage.set).toHaveBeenCalledWith('lens-settings', { + indexPatternId: 'b', + }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index f2608cd5423e4..ca52ffe73a871 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -105,12 +105,13 @@ export async function loadInitialState({ ); const currentIndexPatternId = requiredPatterns[0]; + setLastUsedIndexPatternId(storage, currentIndexPatternId); + const indexPatterns = await loadIndexPatterns({ savedObjectsClient, cache: {}, patterns: requiredPatterns, }); - if (state) { return { ...state,