diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index 4f23dc42c050..d0bc5412bc07 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -226,9 +226,9 @@ # Set the value of this setting to true to enables the experimental multiple data source # support feature. Use with caution. -#data_source.enabled: false +data_source.enabled: true # Set the value of these settings to custermize crypto materials to encryption saved credentials # in data sources. -#data_source.encryption.wrappingKeyName: 'changeme' -#data_source.encryption.wrappingKeyNamespace: 'changeme' -#data_source.encryption.wrappingKey: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] +data_source.encryption.wrappingKeyName: 'changeme' +data_source.encryption.wrappingKeyNamespace: 'changeme' +data_source.encryption.wrappingKey: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] diff --git a/package.json b/package.json index e87881fdb400..94bd51e8625f 100644 --- a/package.json +++ b/package.json @@ -129,6 +129,7 @@ "@hapi/podium": "^4.1.3", "@hapi/vision": "^6.1.0", "@hapi/wreck": "^17.1.0", + "@opensearch-dashboards-test/opensearch-dashboards-test-library": "^1.0.3", "@opensearch-project/opensearch": "^1.1.0", "@osd/ace": "1.0.0", "@osd/analytics": "1.0.0", @@ -151,6 +152,7 @@ "angular-elastic": "^2.5.1", "angular-sanitize": "^1.8.0", "bluebird": "3.5.5", + "caniuse-lite": "^1.0.30001397", "chalk": "^4.1.0", "chokidar": "^3.4.2", "color": "1.0.3", @@ -212,6 +214,7 @@ "type-detect": "^4.0.8", "uuid": "3.3.2", "whatwg-fetch": "^3.0.0", + "yarn": "^1.22.19", "yauzl": "^2.10.0" }, "devDependencies": { @@ -461,4 +464,4 @@ "node": "14.20.0", "yarn": "^1.21.1" } -} \ No newline at end of file +} diff --git a/src/plugins/data/common/search/search_source/fetch/get_search_params.ts b/src/plugins/data/common/search/search_source/fetch/get_search_params.ts index a25d6e530bad..5dc865d2729a 100644 --- a/src/plugins/data/common/search/search_source/fetch/get_search_params.ts +++ b/src/plugins/data/common/search/search_source/fetch/get_search_params.ts @@ -59,6 +59,12 @@ export function getSearchParamsFromRequest( const { getConfig } = dependencies; const searchParams = getSearchParams(getConfig); + if (searchRequest.body.pit) { + delete searchParams.preference; + return { + body: searchRequest.body, ...searchParams, + }; + } return { index: searchRequest.index.title || searchRequest.index, body: searchRequest.body, diff --git a/src/plugins/data/server/index_patterns/index_patterns_api_client.ts b/src/plugins/data/server/index_patterns/index_patterns_api_client.ts index ebfadf00b943..8501adee7b73 100644 --- a/src/plugins/data/server/index_patterns/index_patterns_api_client.ts +++ b/src/plugins/data/server/index_patterns/index_patterns_api_client.ts @@ -28,7 +28,7 @@ * under the License. */ -import { GetFieldsOptions, IIndexPatternsApiClient } from '../../common/index_patterns/types'; +import { GetFieldsOptions, IIndexPatternsApiClient } from '../../common'; export class IndexPatternsApiServer implements IIndexPatternsApiClient { async getFieldsForTimePattern(options: GetFieldsOptions = {}) { diff --git a/src/plugins/data/server/search/opensearch_search/get_default_search_params.ts b/src/plugins/data/server/search/opensearch_search/get_default_search_params.ts index d7cbd48a6507..fc85d23a8068 100644 --- a/src/plugins/data/server/search/opensearch_search/get_default_search_params.ts +++ b/src/plugins/data/server/search/opensearch_search/get_default_search_params.ts @@ -49,7 +49,7 @@ export async function getDefaultSearchParams(uiSettingsClient: IUiSettingsClient maxConcurrentShardRequests: maxConcurrentShardRequests > 0 ? maxConcurrentShardRequests : undefined, ignoreThrottled, - ignoreUnavailable: true, // Don't fail if the index/indices don't exist + ignoreUnavailable: false, // Don't fail if the index/indices don't exist trackTotalHits: true, }; } diff --git a/src/plugins/data/server/search/opensearch_search/opensearch_search_strategy.ts b/src/plugins/data/server/search/opensearch_search/opensearch_search_strategy.ts index 4ccc8db05728..aea4da4554e9 100644 --- a/src/plugins/data/server/search/opensearch_search/opensearch_search_strategy.ts +++ b/src/plugins/data/server/search/opensearch_search/opensearch_search_strategy.ts @@ -63,13 +63,14 @@ export const opensearchSearchStrategyProvider = ( // ignoreThrottled is not supported in OSS const { ignoreThrottled, ...defaultParams } = await getDefaultSearchParams(uiSettingsClient); - const params = toSnakeCase({ ...defaultParams, ...getShardTimeout(config), ...request.params, }); - + if (params.body.pit) { + delete params.ignore_unavailable; + } try { const client = await decideClient(context, request); const promise = shimAbortSignal(client.search(params), options?.abortSignal); diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index de244e3c44b6..816ccd30bb62 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -37,7 +37,7 @@ import dateMath from '@elastic/datemath'; import { i18n } from '@osd/i18n'; import { getState, splitState } from './discover_state'; -import { RequestAdapter } from '../../../../inspector/public'; +import { RequestAdapter } from '../../../../inspector'; import { opensearchFilters, indexPatterns as indexPatternsUtils, @@ -77,6 +77,7 @@ const { toastNotifications, uiSettings: config, visualizations, + savedObjectsClient, } = getServices(); import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../helpers/breadcrumbs'; @@ -102,6 +103,33 @@ const fetchStatuses = { const app = getAngularModule(); +export async function getpits(savedObjectsClient) { + return ( + savedObjectsClient + .find({ + type: 'point-in-time', + perPage: 10000, + }) + .then((response) => + response.savedObjects + .map((pattern) => { + return { + ...pattern, + }; + }) + .sort((a, b) => { + if (a.sort < b.sort) { + return -1; + } else if (a.sort > b.sort) { + return 1; + } else { + return 0; + } + }) + ) || [] + ); +} + app.config(($routeProvider) => { const defaults = { requireDefaultIndex: true, @@ -129,14 +157,14 @@ app.config(($routeProvider) => { template: indexTemplateLegacy, reloadOnSearch: false, resolve: { - savedObjects: function ($route, Promise) { + savedObjects: async function ($route, Promise) { const history = getHistory(); const savedSearchId = $route.current.params.id; - return data.indexPatterns.ensureDefaultIndexPattern(history).then(() => { + return await data.indexPatterns.ensureDefaultIndexPattern(history).then(async () => { const { appStateContainer } = getState({ history }); - const { index } = appStateContainer.getState(); - return Promise.props({ - ip: indexPatterns.getCache().then((indexPatternList) => { + const { index, pitid } = appStateContainer.getState(); + return await Promise.props({ + ip: await indexPatterns.getCache().then(async (indexPatternList) => { /** * 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 @@ -154,9 +182,14 @@ app.config(($routeProvider) => { stateValFound: !!index && id === index, }); }), - savedSearch: getServices() + pit: await Promise.props({ + list: await getpits(savedObjectsClient, history), + pitid: pitid, + }), + savedSearch: await getServices() .getSavedSearchById(savedSearchId) .then((savedSearch) => { + // console.log("Invoking this to make a internal call"); if (savedSearchId) { chrome.recentlyAccessed.add( savedSearch.getFullPath(), @@ -164,6 +197,7 @@ app.config(($routeProvider) => { savedSearchId ); } + console.log(savedSearch); return savedSearch; }) .catch( @@ -208,7 +242,10 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise let inspectorRequest; const savedSearch = $route.current.locals.savedObjects.savedSearch; $scope.searchSource = savedSearch.searchSource; + // console.log("This is the search store",$scope.searchSource); $scope.indexPattern = resolveIndexPatternLoading(); + $scope.selectedPointInTime = $route.current.locals.savedObjects.pit.pitid || undefined; + // console.log($scope.indexPattern); //used for functional testing $scope.fetchCounter = 0; @@ -249,7 +286,6 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise // sync initial app filters from state to filterManager filterManager.setAppFilters(_.cloneDeep(appStateContainer.getState().filters)); data.query.queryString.setQuery(appStateContainer.getState().query); - const stopSyncingQueryAppStateWithStateContainer = connectToQueryState( data.query, appStateContainer, @@ -294,20 +330,49 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise }); $scope.setIndexPattern = async (id) => { - const nextIndexPattern = await indexPatterns.get(id); - if (nextIndexPattern) { - const nextAppState = getSwitchIndexPatternAppState( - $scope.indexPattern, - nextIndexPattern, - $scope.state.columns, - $scope.state.sort, - config.get(MODIFY_COLUMNS_ON_SWITCH) - ); - await replaceUrlAppState(nextAppState); - $route.reload(); + try { + const nextIndexPattern = await indexPatterns.get(id); + if (nextIndexPattern) { + const nextAppState = getSwitchIndexPatternAppState( + $scope.indexPattern, + nextIndexPattern, + $scope.state.columns, + $scope.state.sort, + config.get(MODIFY_COLUMNS_ON_SWITCH) + ); + await replaceUrlAppState(nextAppState); + $route.reload(); + } + } catch (e) { + const nextIndexPattern = await indexPatterns.get(id); + if (nextIndexPattern) { + const nextAppState = getSwitchIndexPatternAppState( + $scope.indexPattern, + nextIndexPattern, + $scope.state.columns, + $scope.state.sort, + config.get(MODIFY_COLUMNS_ON_SWITCH) + ); + await replaceUrlAppState(nextAppState); + $route.reload(); } - }; + } + $scope.setPointInTime = async (id) => { + console.log('This is teh pit list', $route.current.locals.savedObjects.pit.list); + const pitList = await $route.current.locals.savedObjects.pit.list; + const nextPit = pitList.find((pit) => pit.id === id); + console.log(nextPit); + const nextAppState = getSwitchIndexPatternAppState( + $scope.indexPattern, + nextPit, + $scope.state.columns, + $scope.state.sort, + config.get(MODIFY_COLUMNS_ON_SWITCH) + ); + await replaceUrlAppState(nextAppState); + await $route.reload(); + }; // update data source when filters update subscriptions.add( subscribeWithScope( @@ -613,6 +678,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise ? savedSearch.columns : config.get(DEFAULT_COLUMNS_SETTING).slice(), index: $scope.indexPattern.id, + pit: $scope.selectedPointInTime, interval: 'auto', filters: _.cloneDeep($scope.searchSource.getOwnField('filter')), }; @@ -627,6 +693,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise timefield: getTimeField(), savedSearch: savedSearch, indexPatternList: $route.current.locals.savedObjects.ip.list, + pointInTimeList: $route.current.locals.savedObjects.pit.list, config: config, fixedScroll: createFixedScroll($scope, $timeout), setHeaderActionMenu: getHeaderActionMenuMounter(), @@ -804,27 +871,66 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise // Abort any in-progress requests before fetching again if (abortController) abortController.abort(); abortController = new AbortController(); + // if selected Point in time is not null that means the user has selected a PIT saved object + if ($scope.selectedPointInTime != null) { + $scope + .updateDataSource() + .then(setupVisualization) + .then(async function () { + $scope.fetchStatus = fetchStatuses.LOADING; + logInspectorRequest(); + // eslint-disable-next-line camelcase + // Since search source does not support pit, this is an work around to make the fetch call + const search_source_local = _.cloneDeep($scope.searchSource); + // search_source_local = $scope.searchSource; + console.log('This is the local search source'); + const pit_object = { + pit: { + id: $scope.selectedPointInTime, + keep_alive: '1m', + }, + }; + const pit_json = JSON.parse(JSON.stringify(pit_object)); + // searchRequest.params.body = { ...searchRequest.params.body, ...pit_json }; + search_source_local.fields = { ...search_source_local.fields, ...pit_json }; + delete search_source_local.fields.index; + return await search_source_local.fetch({ + abortSignal: abortController.signal, + }); + }) + .then(onResults) + .catch((error) => { + // If the request was aborted then no need to surface this error in the UI + if (error instanceof Error && error.name === 'AbortError') return; - $scope - .updateDataSource() - .then(setupVisualization) - .then(function () { - $scope.fetchStatus = fetchStatuses.LOADING; - logInspectorRequest(); - return $scope.searchSource.fetch({ - abortSignal: abortController.signal, + $scope.fetchStatus = fetchStatuses.NO_RESULTS; + $scope.rows = []; + + data.search.showError(error); }); - }) - .then(onResults) - .catch((error) => { - // If the request was aborted then no need to surface this error in the UI - if (error instanceof Error && error.name === 'AbortError') return; + } else { + // this is the default call for fetching the index pattern results + $scope + .updateDataSource() + .then(setupVisualization) + .then(async function () { + $scope.fetchStatus = fetchStatuses.LOADING; + logInspectorRequest(); + return await $scope.searchSource.fetch({ + abortSignal: abortController.signal, + }); + }) + .then(onResults) + .catch((error) => { + // If the request was aborted then no need to surface this error in the UI + if (error instanceof Error && error.name === 'AbortError') return; - $scope.fetchStatus = fetchStatuses.NO_RESULTS; - $scope.rows = []; + $scope.fetchStatus = fetchStatuses.NO_RESULTS; + $scope.rows = []; - data.search.showError(error); - }); + data.search.showError(error); + }); + } }; $scope.handleRefresh = function (_payload, isUpdate) { @@ -876,6 +982,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise } function onResults(resp) { + console.log('response after fetching the results', resp); inspectorRequest.stats(getResponseInspectorStats(resp, $scope.searchSource)).ok({ json: resp }); if (getTimeField()) { @@ -890,7 +997,9 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise $scope.hits = resp.hits.total; $scope.rows = resp.hits.hits; - + debugger; + console.log($scope.hits); + console.log($scope.rows); // if we haven't counted yet, reset the counts const counts = ($scope.fieldCounts = $scope.fieldCounts || {}); diff --git a/src/plugins/discover/public/application/angular/discover_legacy.html b/src/plugins/discover/public/application/angular/discover_legacy.html index 8582f71c0cb8..f4bbb30aff2e 100644 --- a/src/plugins/discover/public/application/angular/discover_legacy.html +++ b/src/plugins/discover/public/application/angular/discover_legacy.html @@ -23,6 +23,8 @@ saved-search="savedSearch" search-source="searchSource" set-index-pattern="setIndexPattern" + set-point-in-time="setPointInTime" + selected-point-in-time = "selectedPointInTime" show-save-query="showSaveQuery" state="state" time-filter-update-handler="timefilterUpdateHandler" diff --git a/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts b/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts index 09cc33964862..2b4b6e6e66a6 100644 --- a/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts +++ b/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts @@ -55,6 +55,8 @@ export function createDiscoverLegacyDirective(reactDirective: any) { ['savedSearch', { watchDepth: 'reference' }], ['searchSource', { watchDepth: 'reference' }], ['setIndexPattern', { watchDepth: 'reference' }], + ['setPointInTime', { watchDepth: 'reference' }], + ['selectedPointInTime', { watchDepth: 'reference' }], ['showSaveQuery', { watchDepth: 'reference' }], ['state', { watchDepth: 'reference' }], ['timefilterUpdateHandler', { watchDepth: 'reference' }], diff --git a/src/plugins/discover/public/application/components/discover_legacy.tsx b/src/plugins/discover/public/application/components/discover_legacy.tsx index 3e6e96fc6124..eed06bd333ab 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.tsx +++ b/src/plugins/discover/public/application/components/discover_legacy.tsx @@ -81,6 +81,7 @@ export interface DiscoverLegacyProps { savedSearch: SavedSearch; config: IUiSettingsClient; indexPatternList: Array>; + pointInTimeList: any; timefield: string; sampleSize: number; fixedScroll: (el: HTMLElement) => void; @@ -99,6 +100,8 @@ export interface DiscoverLegacyProps { updateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void; updateSavedQueryId: (savedQueryId?: string) => void; vis?: Vis; + selectedPointInTime: any; + setPointInTime: (id: string) => void; } export function DiscoverLegacy({ @@ -130,16 +133,24 @@ export function DiscoverLegacy({ updateQuery, updateSavedQueryId, vis, + selectedPointInTime, + setPointInTime, }: DiscoverLegacyProps) { const [isSidebarClosed, setIsSidebarClosed] = useState(false); const { TopNavMenu } = getServices().navigation.ui; - const { savedSearch, indexPatternList } = opts; + const { savedSearch, indexPatternList, pointInTimeList } = opts; const bucketAggConfig = vis?.data?.aggs?.aggs[1]; const bucketInterval = bucketAggConfig && search.aggs.isDateHistogramBucketAggConfig(bucketAggConfig) ? bucketAggConfig.buckets?.getInterval() : undefined; const [fixedScrollEl, setFixedScrollEl] = useState(); + // The selected Point in time here is the id of the saved object, We will be passing the complete object later + if (selectedPointInTime != null) { + selectedPointInTime = pointInTimeList.find( + (pattern: { id: any }) => pattern.id === selectedPointInTime + ); + } useEffect(() => (fixedScrollEl ? opts.fixedScroll(fixedScrollEl) : undefined), [ fixedScrollEl, @@ -195,11 +206,14 @@ export function DiscoverLegacy({ fieldCounts={fieldCounts} hits={rows} indexPatternList={indexPatternList} + pointInTimeList={pointInTimeList} onAddField={addColumn} onAddFilter={onAddFilter} onRemoveField={onRemoveColumn} selectedIndexPattern={searchSource && searchSource.getField('index')} setIndexPattern={setIndexPattern} + selectedPointInTime={selectedPointInTime} // currently search source do not support pit hence passing it in props + setPointInTime={setPointInTime} /> )} diff --git a/src/plugins/discover/public/application/components/sidebar/change_indexpattern.tsx b/src/plugins/discover/public/application/components/sidebar/change_indexpattern.tsx index 553031f06721..9806ef012cf3 100644 --- a/src/plugins/discover/public/application/components/sidebar/change_indexpattern.tsx +++ b/src/plugins/discover/public/application/components/sidebar/change_indexpattern.tsx @@ -29,16 +29,21 @@ */ import { i18n } from '@osd/i18n'; -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { EuiButtonEmpty, EuiPopover, EuiPopoverTitle, EuiSelectable, EuiButtonEmptyProps, + EuiSpacer, + EuiFlexGroup, + EuiFlexItem, + EuiSelectableOption, } from '@elastic/eui'; import { EuiSelectableProps } from '@elastic/eui/src/components/selectable/selectable'; -import { IndexPatternRef } from './types'; +import { IndexPatternRef, PointInTimeRef } from './types'; +import { ChangePatternFilter } from './change_pattern_filter'; export type ChangeIndexPatternTriggerProps = EuiButtonEmptyProps & { label: string; @@ -47,18 +52,22 @@ export type ChangeIndexPatternTriggerProps = EuiButtonEmptyProps & { export function ChangeIndexPattern({ indexPatternRefs, + pointInTimeRefs, indexPatternId, - onChangeIndexPattern, + onChangePattern, trigger, selectableProps, }: { trigger: ChangeIndexPatternTriggerProps; indexPatternRefs: IndexPatternRef[]; - onChangeIndexPattern: (newId: string) => void; + pointInTimeRefs: PointInTimeRef[]; + onChangePattern: (newId: string) => void; indexPatternId?: string; selectableProps?: EuiSelectableProps; }) { const [isPopoverOpen, setPopoverIsOpen] = useState(false); + const [isIndexPatternSelected, setIndexPatternIsSelected] = useState(true); + const [isPointInTimeSelected, setPointInTimeIsSelected] = useState(true); const createTrigger = function () { const { label, title, ...rest } = trigger; @@ -78,6 +87,63 @@ export function ChangeIndexPattern({ ); }; + interface OptionData { + value?: string | number; + references?: any; + } + const indexPatternOptions = (isSelected: boolean) => { + return isSelected + ? [ + { + label: 'index-pattern', + isGroupLabel: true, + }, + ...indexPatternRefs.map( + ({ title, id }): EuiSelectableOption => ({ + label: title, + key: id, + searchableLabel: title, + value: id, + checked: id === indexPatternId ? 'on' : undefined, + }) + ), + ] + : []; + }; + + const pointInTimeOptions = (isSelected: boolean) => { + return isSelected + ? [ + { + label: 'point-in-time', + isGroupLabel: true, + }, + ...pointInTimeRefs.map( + ({ title, id, references }): EuiSelectableOption => ({ + label: title, + key: title, + searchableLabel: title, + value: id, + references, + checked: id === indexPatternId ? 'on' : undefined, + }) + ), + ] + : []; + }; + + const [options, setOptions] = useState>>([ + ...indexPatternOptions(isIndexPatternSelected), + ...pointInTimeOptions(isPointInTimeSelected), + ]); + + useEffect(() => { + setOptions([ + ...indexPatternOptions(isIndexPatternSelected), + ...pointInTimeOptions(isPointInTimeSelected), + ]); + }, [isIndexPatternSelected, isPointInTimeSelected]); + return ( -
+
{i18n.translate('discover.fieldChooser.indexPattern.changeIndexPatternTitle', { defaultMessage: 'Change index pattern', })} + + ({ - label: title, - key: id, - value: id, - checked: id === indexPatternId ? 'on' : undefined, - }))} - onChange={(choices) => { + options={options} + onChange={(choices: Array>) => { const choice = (choices.find(({ checked }) => checked) as unknown) as { value: string; }; - onChangeIndexPattern(choice.value); + onChangePattern(choice.value); setPopoverIsOpen(false); }} searchProps={{ @@ -120,7 +183,14 @@ export function ChangeIndexPattern({ > {(list, search) => ( <> - {search} + + {search} + + + {list} )} diff --git a/src/plugins/discover/public/application/components/sidebar/change_pattern_filter.tsx b/src/plugins/discover/public/application/components/sidebar/change_pattern_filter.tsx new file mode 100644 index 000000000000..1c3732681273 --- /dev/null +++ b/src/plugins/discover/public/application/components/sidebar/change_pattern_filter.tsx @@ -0,0 +1,80 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiButton, + EuiFlexItem, + EuiPopover, + EuiSelectable, + EuiSelectableOption, +} from '@elastic/eui'; +import React, { useState } from 'react'; + +interface ChangePatternDeps { + setIndexPatternIsSelected: (value: boolean) => void; + setPointInTimeIsSelected: (value: boolean) => void; +} + +export function ChangePatternFilter({ + setIndexPatternIsSelected, + setPointInTimeIsSelected, +}: ChangePatternDeps) { + const [options, setOptions] = useState([ + { label: 'point-in-time', checked: 'on' }, + { label: 'index-pattern', checked: 'on' }, + ]); + + const [isPopoverOpen, setPopoverIsOpen] = useState(false); + + return ( + + setPopoverIsOpen(!isPopoverOpen)} + size="s" + > + Filter + + } + isOpen={isPopoverOpen} + closePopover={() => setPopoverIsOpen(false)} + > + { + choices.find((option) => { + if (option.label == 'point-in-time') { + if (option.checked) { + setPointInTimeIsSelected(true); + } else { + setPointInTimeIsSelected(false); + } + } + + if (option.label == 'index-pattern') { + if (option.checked) { + setIndexPatternIsSelected(true); + } else { + setIndexPatternIsSelected(false); + } + } + }); + setOptions(choices); + }} + > + {(list) =>
{list}
} +
+
+
+ ); +} diff --git a/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.tsx b/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.tsx index 18e874aecb2d..6af2e10814bb 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_index_pattern.tsx @@ -33,21 +33,27 @@ import { SavedObject } from 'opensearch-dashboards/public'; import { IIndexPattern, IndexPatternAttributes } from 'src/plugins/data/public'; import { I18nProvider } from '@osd/i18n/react'; -import { IndexPatternRef } from './types'; +import {IndexPatternRef, PointInTimeRef} from './types'; import { ChangeIndexPattern } from './change_indexpattern'; +import { + PointInTime +} from "../../../../../../../plugins/my_plugin_name/components/point_in_time_flyout/point_in_time_flyout"; export interface DiscoverIndexPatternProps { /** * list of available index patterns, if length > 1, component offers a "change" link */ indexPatternList: Array>; + pointInTimeList: Array>; /** * currently selected index pattern, due to angular issues it's undefined at first rendering */ + selectedPointInTime: any; selectedIndexPattern: IIndexPattern; /** * triggered when user selects a new index pattern */ setIndexPattern: (id: string) => void; + setPointInTime: (id: string) => void; } /** @@ -55,23 +61,40 @@ export interface DiscoverIndexPatternProps { */ export function DiscoverIndexPattern({ indexPatternList, + pointInTimeList, selectedIndexPattern, + selectedPointInTime, setIndexPattern, + setPointInTime, }: DiscoverIndexPatternProps) { const options: IndexPatternRef[] = (indexPatternList || []).map((entity) => ({ id: entity.id, title: entity.attributes!.title, + type: entity.type, })); - const { id: selectedId, title: selectedTitle } = selectedIndexPattern || {}; - + // eslint-disable-next-line @typescript-eslint/naming-convention + const point_in_time_options: PointInTimeRef[] = (pointInTimeList || []).map((entity) => ({ + id: entity.id, + title: entity.attributes!.name, + type: entity.type, + references: entity.references, + })); + // TODO: will have a title attribute in the saved object of Point in time + if (selectedPointInTime) { + selectedPointInTime = { ...selectedPointInTime, title: selectedPointInTime?.attributes?.name }; + } + const selectedPattern = selectedPointInTime || selectedIndexPattern || {}; + const { id: selectedId, title: selectedTitle } = selectedPattern; const [selected, setSelected] = useState({ id: selectedId, title: selectedTitle || '', }); useEffect(() => { - const { id, title } = selectedIndexPattern; + const { id, title } = selectedPattern; setSelected({ id, title }); - }, [selectedIndexPattern]); + }, [selectedIndexPattern]) + + if (!selectedId) { return null; } @@ -88,12 +111,20 @@ export function DiscoverIndexPattern({ }} indexPatternId={selected.id} indexPatternRefs={options} - onChangeIndexPattern={(id) => { + pointInTimeRefs={point_in_time_options} + onChangePattern={(id) => { const indexPattern = options.find((pattern) => pattern.id === id); if (indexPattern) { setIndexPattern(id); setSelected(indexPattern); } + const pointInTime = point_in_time_options.find((pattern) => pattern.id === id); + if (pointInTime) { + setPointInTime(id); + const PitId = pointInTimeList[0].attributes.id; + const title = pointInTimeList[0].attributes.name; + setSelected({ id: PitId, title }); + } }} /> diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx index f957b93a4cc4..b0c64b04a51e 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx @@ -46,6 +46,9 @@ import { getDetails } from './lib/get_details'; import { getDefaultFieldFilter, setFieldFilterProp } from './lib/field_filter'; import { getIndexPatternFieldList } from './lib/get_index_pattern_field_list'; import { getServices } from '../../../opensearch_dashboards_services'; +import { + PointInTime +} from "../../../../../../../plugins/my_plugin_name/components/point_in_time_flyout/point_in_time_flyout"; export interface DiscoverSidebarProps { /** @@ -64,6 +67,7 @@ export interface DiscoverSidebarProps { * List of available index patterns */ indexPatternList: Array>; + pointInTimeList: Array>; /** * Callback function when selecting a field */ @@ -85,6 +89,8 @@ export interface DiscoverSidebarProps { * Callback function to select another index pattern */ setIndexPattern: (id: string) => void; + selectedPointInTime: any; + setPointInTime: (id: string) => void; } export function DiscoverSidebar({ @@ -92,11 +98,14 @@ export function DiscoverSidebar({ fieldCounts, hits, indexPatternList, + pointInTimeList, onAddField, onAddFilter, onRemoveField, selectedIndexPattern, setIndexPattern, + setPointInTime, + selectedPointInTime, }: DiscoverSidebarProps) { const [showFields, setShowFields] = useState(false); const [fields, setFields] = useState(null); @@ -117,7 +126,9 @@ export function DiscoverSidebar({ ); const getDetailsByField = useCallback( - (ipField: IndexPatternField) => getDetails(ipField, hits, columns, selectedIndexPattern), + (ipField: IndexPatternField) => { + return getDetails(ipField, hits, columns, selectedIndexPattern); + }, [hits, columns, selectedIndexPattern] ); @@ -164,6 +175,9 @@ export function DiscoverSidebar({ selectedIndexPattern={selectedIndexPattern} setIndexPattern={setIndexPattern} indexPatternList={sortBy(indexPatternList, (o) => o.attributes.title)} + pointInTimeList={pointInTimeList} + setPointInTime={setPointInTime} + selectedPointInTime={selectedPointInTime} />
diff --git a/src/plugins/discover/public/application/components/sidebar/types.ts b/src/plugins/discover/public/application/components/sidebar/types.ts index b254057b0de0..405f2466bdaa 100644 --- a/src/plugins/discover/public/application/components/sidebar/types.ts +++ b/src/plugins/discover/public/application/components/sidebar/types.ts @@ -31,6 +31,14 @@ export interface IndexPatternRef { id: string; title: string; + type: string; +} + +export interface PointInTimeRef { + id: string; + title: string; + type: string; + references?: any; } export interface FieldDetails { diff --git a/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.ts b/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.ts index 51835910a402..386bfaa4323d 100644 --- a/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.ts +++ b/src/plugins/discover/public/application/helpers/get_switch_index_pattern_app_state.ts @@ -36,23 +36,38 @@ import { IndexPattern } from '../../opensearch_dashboards_services'; * Helper function to remove or adapt the currently selected columns/sort to be valid with the next * index pattern, returns a new state object */ + +export interface PointInTime { + attributes: any; + id: string; +} export function getSwitchIndexPatternAppState( - currentIndexPattern: IndexPattern, - nextIndexPattern: IndexPattern, + currentIndexPattern: IndexPattern | PointInTime, + nextIndexPattern: IndexPattern | PointInTime, currentColumns: string[], currentSort: SortPairArr[], modifyColumns: boolean = true ) { - const nextColumns = modifyColumns - ? currentColumns.filter( - (column) => + if (nextIndexPattern.attributes) { + return { + pitid: nextIndexPattern.id, + index: null, + }; + } else { + const nextColumns = modifyColumns + ? currentColumns.filter( + (column) => { nextIndexPattern.fields.getByName(column) || !currentIndexPattern.fields.getByName(column) + } + ) - : currentColumns; - const nextSort = getSortArray(currentSort, nextIndexPattern); - return { - index: nextIndexPattern.id, - columns: nextColumns.length ? nextColumns : ['_source'], - sort: nextSort, - }; + : currentColumns; + const nextSort = getSortArray(currentSort, nextIndexPattern); + return { + index: nextIndexPattern.id, + pitid: null, + columns: nextColumns.length ? nextColumns : ['_source'], + sort: nextSort, + }; + } } diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index 3fdafcff0c40..7ca5044502c7 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -38,6 +38,7 @@ import { ToastsStart, IUiSettingsClient, PluginInitializerContext, + SavedObjectsClientContract, } from 'opensearch-dashboards/public'; import { FilterManager, @@ -82,6 +83,7 @@ export interface DiscoverServices { getEmbeddableInjector: any; uiSettings: IUiSettingsClient; visualizations: VisualizationsStart; + savedObjectsClient: SavedObjectsClientContract; } export async function buildServices( @@ -125,5 +127,6 @@ export async function buildServices( toastNotifications: core.notifications.toasts, uiSettings: core.uiSettings, visualizations: plugins.visualizations, + savedObjectsClient: core.savedObjects.client, }; } diff --git a/src/plugins/index_pattern_management/opensearch_dashboards.json b/src/plugins/index_pattern_management/opensearch_dashboards.json index 1efd05cb1a49..19d2d8985960 100644 --- a/src/plugins/index_pattern_management/opensearch_dashboards.json +++ b/src/plugins/index_pattern_management/opensearch_dashboards.json @@ -1,4 +1,4 @@ -{ + { "id": "indexPatternManagement", "version": "opensearchDashboards", "server": true, diff --git a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx index 8013c05aad79..6c76c591ff4d 100644 --- a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx +++ b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx @@ -38,7 +38,7 @@ import { CoreSetup } from 'src/core/public'; import { ManagementAppMountParams } from '../../../management/public'; import { StartDependencies, SavedObjectsManagementPluginStart } from '../plugin'; import { ISavedObjectsManagementServiceRegistry } from '../services'; -import { getAllowedTypes } from './../lib'; +import { getAllowedTypes } from '../lib'; interface MountParams { core: CoreSetup; diff --git a/yarn.lock b/yarn.lock index 2c8df6850b10..65d69a0865b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2505,6 +2505,11 @@ dependencies: "@octokit/openapi-types" "^11.2.0" +"@opensearch-dashboards-test/opensearch-dashboards-test-library@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@opensearch-dashboards-test/opensearch-dashboards-test-library/-/opensearch-dashboards-test-library-1.0.3.tgz#41f4f48776a4a16e2a2956070a51a706e47bfde1" + integrity sha512-fHYyj7TEeuc+AXiIlYduahwRnzH8ddz9hnJiLc8429A4mVKg+pdrlxFkup3lvH+5U6JgVxTpbO8J/XKAt6U7/A== + "@opensearch-project/opensearch@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@opensearch-project/opensearch/-/opensearch-1.1.0.tgz#8b3c8b4cbcea01755ba092d2997bf0b4ca7f22f7" @@ -5581,6 +5586,11 @@ caniuse-lite@^1.0.30001317: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001397.tgz" integrity sha512-SW9N2TbCdLf0eiNDRrrQXx2sOkaakNZbCjgNpPyMJJbiOrU5QzMIrXOVMRM1myBXTD5iTkdrtU/EguCrBocHlA== +caniuse-lite@^1.0.30001397: + version "1.0.30001399" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001399.tgz#1bf994ca375d7f33f8d01ce03b7d5139e8587873" + integrity sha512-4vQ90tMKS+FkvuVWS5/QY1+d805ODxZiKFzsU8o/RsVJz49ZSRR8EjykLJbqhzdPgadbX6wB538wOzle3JniRA== + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -19279,6 +19289,11 @@ yargs@~16.0.3: y18n "^5.0.1" yargs-parser "^20.0.0" +yarn@^1.22.19: + version "1.22.19" + resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.19.tgz#4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447" + integrity sha512-/0V5q0WbslqnwP91tirOvldvYISzaqhClxzyUKXYxs07yUILIs5jx/k6CFe8bvKSkds5w+eiOqta39Wk3WxdcQ== + yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"