From 036496cb924394ae1254cf135f38218f40788942 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 00:58:46 -0700 Subject: [PATCH] [Discover] Display Cache Time and Clear Cache Button (#8214) (#8421) * initial commit for cache time and clearing cache * Changeset file for PR #8214 created/updated * updating UI to address some comments * addressing comments, adding tests * removing dynamic default message, following i18n best practice --------- (cherry picked from commit a7f3e9d3f2b6e9c4cf935ac305ad564a02122116) Signed-off-by: Sean Li Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> --- changelogs/fragments/8214.yml | 2 + src/plugins/data/common/storage/storage.ts | 4 + .../dataset_service/dataset_service.test.ts | 71 +++++++++------ .../dataset_service/dataset_service.ts | 13 +++ .../ui/dataset_selector/dataset_explorer.tsx | 91 ++++++++++++++----- 5 files changed, 130 insertions(+), 51 deletions(-) create mode 100644 changelogs/fragments/8214.yml diff --git a/changelogs/fragments/8214.yml b/changelogs/fragments/8214.yml new file mode 100644 index 000000000000..ce9d1ee6ca32 --- /dev/null +++ b/changelogs/fragments/8214.yml @@ -0,0 +1,2 @@ +feat: +- Add last updated time and cache refresh button to Discover Advanced Dataset Selector ([#8214](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8214)) \ No newline at end of file diff --git a/src/plugins/data/common/storage/storage.ts b/src/plugins/data/common/storage/storage.ts index 8ec5f6a13b8d..752e0b29c92e 100644 --- a/src/plugins/data/common/storage/storage.ts +++ b/src/plugins/data/common/storage/storage.ts @@ -61,6 +61,10 @@ export class DataStorage { if (ourKey != null) ours.push(ourKey); }); } + + clear(): void { + this.engine.clear(); + } } export function createStorage(deps: { engine: IStorageEngine; prefix: string }) { diff --git a/src/plugins/data/public/query/query_string/dataset_service/dataset_service.test.ts b/src/plugins/data/public/query/query_string/dataset_service/dataset_service.test.ts index f72fd0ea5b46..02b9eb0759fc 100644 --- a/src/plugins/data/public/query/query_string/dataset_service/dataset_service.test.ts +++ b/src/plugins/data/public/query/query_string/dataset_service/dataset_service.test.ts @@ -23,17 +23,26 @@ describe('DatasetService', () => { service = new DatasetService(uiSettings, sessionStorage); }); - test('registerType and getType', () => { - const mockType = { - id: 'test-type', - title: 'Test Type', - meta: { icon: { type: 'test' } }, - toDataset: jest.fn(), - fetch: jest.fn(), - fetchFields: jest.fn(), - supportedLanguages: jest.fn(), - }; + const mockResult = { + id: 'test-structure', + title: 'Test Structure', + type: 'test-type', + children: [{ id: 'child1', title: 'Child 1', type: 'test-type' }], + }; + + const mockPath: DataStructure[] = [{ id: 'root', title: 'Root', type: 'root' }]; + + const mockType = { + id: 'test-type', + title: 'Test Type', + meta: { icon: { type: 'test' } }, + toDataset: jest.fn(), + fetch: jest.fn().mockResolvedValue(mockResult), + fetchFields: jest.fn(), + supportedLanguages: jest.fn(), + }; + test('registerType and getType', () => { service.registerType(mockType); expect(service.getType('test-type')).toBe(mockType); }); @@ -52,25 +61,9 @@ describe('DatasetService', () => { }); test('fetchOptions caches and returns data structures', async () => { - const mockType = { - id: 'test-type', - title: 'Test Type', - meta: { icon: { type: 'test' } }, - toDataset: jest.fn(), - fetch: jest.fn().mockResolvedValue({ - id: 'test-structure', - title: 'Test Structure', - type: 'test-type', - children: [{ id: 'child1', title: 'Child 1', type: 'test-type' }], - }), - fetchFields: jest.fn(), - supportedLanguages: jest.fn(), - }; - service.registerType(mockType); - const path: DataStructure[] = [{ id: 'root', title: 'Root', type: 'root' }]; - const result = await service.fetchOptions(mockDataPluginServices, path, 'test-type'); + const result = await service.fetchOptions(mockDataPluginServices, mockPath, 'test-type'); expect(result).toEqual({ id: 'test-structure', @@ -79,8 +72,30 @@ describe('DatasetService', () => { children: [{ id: 'child1', title: 'Child 1', type: 'test-type' }], }); - const cachedResult = await service.fetchOptions(mockDataPluginServices, path, 'test-type'); + const cachedResult = await service.fetchOptions(mockDataPluginServices, mockPath, 'test-type'); expect(cachedResult).toEqual(result); expect(mockType.fetch).toHaveBeenCalledTimes(2); }); + + test('clear cache', async () => { + service.registerType(mockType); + + await service.fetchOptions(mockDataPluginServices, mockPath, 'test-type'); + expect(sessionStorage.keys().length === 1); + + service.clearCache(); + expect(sessionStorage.keys().length === 0); + }); + + test('caching object correctly sets last cache time', async () => { + service.registerType(mockType); + + const time = Date.now(); + + Date.now = jest.fn(() => time); + + await service.fetchOptions(mockDataPluginServices, mockPath, 'test-type'); + + expect(service.getLastCacheTime()).toEqual(time); + }); }); diff --git a/src/plugins/data/public/query/query_string/dataset_service/dataset_service.ts b/src/plugins/data/public/query/query_string/dataset_service/dataset_service.ts index 2f9a0884442f..36faead38308 100644 --- a/src/plugins/data/public/query/query_string/dataset_service/dataset_service.ts +++ b/src/plugins/data/public/query/query_string/dataset_service/dataset_service.ts @@ -138,6 +138,7 @@ export class DatasetService { } private cacheDataStructure(dataType: string, dataStructure: DataStructure) { + this.setLastCacheTime(Date.now()); const cachedDataStructure: CachedDataStructure = { id: dataStructure.id, title: dataStructure.title, @@ -164,6 +165,18 @@ export class DatasetService { }); } + public clearCache(): void { + this.sessionStorage.clear(); + } + + public getLastCacheTime(): number | undefined { + return Number(this.sessionStorage.get('lastCacheTime')) || undefined; + } + + private setLastCacheTime(time: number): void { + this.sessionStorage.set('lastCacheTime', time); + } + private async fetchDefaultDataset(): Promise { const defaultIndexPatternId = this.uiSettings.get('defaultIndex'); if (!defaultIndexPatternId) { diff --git a/src/plugins/data/public/ui/dataset_selector/dataset_explorer.tsx b/src/plugins/data/public/ui/dataset_selector/dataset_explorer.tsx index f393884bc599..dbcf7fe5acfc 100644 --- a/src/plugins/data/public/ui/dataset_selector/dataset_explorer.tsx +++ b/src/plugins/data/public/ui/dataset_selector/dataset_explorer.tsx @@ -7,6 +7,8 @@ import React, { useState } from 'react'; import { EuiButton, EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, EuiIcon, EuiLink, EuiModalBody, @@ -19,6 +21,7 @@ import { EuiToolTip, } from '@elastic/eui'; import { FormattedMessage } from '@osd/i18n/react'; +import moment from 'moment'; import { BaseDataset, DATA_STRUCTURE_META_TYPES, DataStructure } from '../../../common'; import { QueryStringContract } from '../../query'; import { IDataPluginServices } from '../../types'; @@ -38,6 +41,7 @@ export const DatasetExplorer = ({ onNext: (dataset: BaseDataset) => void; onCancel: () => void; }) => { + const uiSettings = services.uiSettings; const [explorerDataset, setExplorerDataset] = useState(undefined); const [loading, setLoading] = useState(false); @@ -68,31 +72,72 @@ export const DatasetExplorer = ({ return ( <> - -

- -

- -

- - + + + +

- -

- - +

+ +

+ + + + +

+
+
+ + {queryString.getDatasetService().getLastCacheTime() && ( + + + + + + + + + { + queryString.getDatasetService().clearCache(); + onCancel(); + }} + size="xs" + iconSide="left" + iconType="refresh" + iconGap="s" + flush="both" + > + + + + + + )} +