Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Saved Searches] Add support for saved searches by value #146849

Merged
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
dc0c67f
[Discover] Initial implementation of saved searches by value
davismcphee Dec 2, 2022
0340368
Finish initial implementation of saved searches by value
davismcphee Jun 16, 2023
ac280fa
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Jun 16, 2023
3da5211
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Jun 16, 2023
09f942c
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Jun 16, 2023
33aaa6a
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Jun 16, 2023
9a66a71
Change savedSearch.embeddable to savedSearch.byValue
davismcphee Jun 16, 2023
9e4b4ed
Add support for description and cleanup embeddable and embeeddable fa…
davismcphee Jun 16, 2023
7c1479c
More saved search by value cleanup
davismcphee Jun 16, 2023
7dc1e4c
Resolving merge conflicts
davismcphee Jul 14, 2023
32b4445
Fix broken TS types
davismcphee Jul 19, 2023
9f454df
Fixing Jest tests
davismcphee Jul 19, 2023
c18662f
Finish fixing Jest tests
davismcphee Jul 20, 2023
5a4cefc
Add support for Discover edit/open actions for by value saved search …
davismcphee Jul 20, 2023
5c65c1f
Add support for inject/extract
davismcphee Jul 20, 2023
bece30b
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Jul 20, 2023
5154bca
Add tests for new by value functionality
davismcphee Jul 21, 2023
b9b455a
Remove unused services from functional test file
davismcphee Jul 24, 2023
1bf6fe3
Resolve merge conflicts
davismcphee Jul 26, 2023
7a7edeb
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Jul 26, 2023
a55c7ca
Fix type in src/plugins/discover/server/embeddable/search_embeddable_…
davismcphee Jul 27, 2023
37242bb
Merge branch 'main' into enhancement-saved-searches-by-value
stratoula Jul 27, 2023
87ffe57
Export SavedSearchUnwrapMetaInfo
davismcphee Jul 27, 2023
584257a
Merge branch 'main' into enhancement-saved-searches-by-value
davismcphee Jul 27, 2023
2464c63
Change from toSpec to toMinimalSpec when creating saved search embedd…
davismcphee Jul 27, 2023
9c78dc2
Merge branch 'main' into enhancement-saved-searches-by-value
davismcphee Jul 31, 2023
f9ee121
Remove isDestroyed from saved search embeddable and replaced with des…
davismcphee Aug 1, 2023
52160a6
Update Dashboard copy to change 'Visualization Library' to 'library'
davismcphee Aug 1, 2023
e087529
Merge branch 'main' into enhancement-saved-searches-by-value
davismcphee Aug 1, 2023
361d410
Update Dashboard Jest test snapshot
davismcphee Aug 1, 2023
44f3621
Merge branch 'main' into enhancement-saved-searches-by-value
davismcphee Aug 1, 2023
86dc808
Merge branch 'main' into enhancement-saved-searches-by-value
davismcphee Aug 1, 2023
13cf1fa
Fix saved search embeddable edit link base path
davismcphee Aug 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const dashboardAddToLibraryActionStrings = {
}),
getSuccessMessage: (panelTitle: string) =>
i18n.translate('dashboard.panel.addToLibrary.successMessage', {
defaultMessage: `Panel {panelTitle} was added to the visualize library`,
defaultMessage: `Panel {panelTitle} was added to the library`,
values: { panelTitle },
}),
};
Expand Down Expand Up @@ -91,15 +91,15 @@ export const dashboardUnlinkFromLibraryActionStrings = {
}),
getSuccessMessage: (panelTitle: string) =>
i18n.translate('dashboard.panel.unlinkFromLibrary.successMessage', {
defaultMessage: `Panel {panelTitle} is no longer connected to the visualize library`,
defaultMessage: `Panel {panelTitle} is no longer connected to the library`,
values: { panelTitle },
}),
};

export const dashboardLibraryNotificationStrings = {
getDisplayName: () =>
i18n.translate('dashboard.panel.LibraryNotification', {
defaultMessage: 'Visualize Library Notification',
defaultMessage: 'Library Notification',
}),
getTooltip: () =>
i18n.translate('dashboard.panel.libraryNotification.toolTip', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const emptyScreenStrings = {
}),
getEditModeSubtitle: () =>
i18n.translate('dashboard.emptyScreen.editModeSubtitle', {
defaultMessage: 'Create a visualization of your data, or add one from the Visualize Library.',
defaultMessage: 'Create a visualization of your data, or add one from the library.',
}),
getAddFromLibraryButtonTitle: () =>
i18n.translate('dashboard.emptyScreen.addFromLibrary', {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions src/plugins/discover/common/embeddable/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export { inject, extract } from './search_inject_extract';
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { extract, inject } from './search_inject_extract';

describe('search inject extract', () => {
describe('inject', () => {
it('should not inject references if state does not have attributes', () => {
const state = { type: 'type', id: 'id' };
const injectedReferences = [{ name: 'name', type: 'type', id: 'id' }];
expect(inject(state, injectedReferences)).toEqual(state);
});

it('should inject references if state has references with the same name', () => {
const state = {
type: 'type',
id: 'id',
attributes: {
references: [{ name: 'name', type: 'type', id: '1' }],
},
};
const injectedReferences = [{ name: 'name', type: 'type', id: '2' }];
expect(inject(state, injectedReferences)).toEqual({
...state,
attributes: {
...state.attributes,
references: injectedReferences,
},
});
});

it('should clear references if state has no references with the same name', () => {
const state = {
type: 'type',
id: 'id',
attributes: {
references: [{ name: 'name', type: 'type', id: '1' }],
},
};
const injectedReferences = [{ name: 'other', type: 'type', id: '2' }];
expect(inject(state, injectedReferences)).toEqual({
...state,
attributes: {
...state.attributes,
references: [],
},
});
});
});

describe('extract', () => {
it('should not extract references if state does not have attributes', () => {
const state = { type: 'type', id: 'id' };
expect(extract(state)).toEqual({ state, references: [] });
});

it('should extract references if state has references', () => {
const state = {
type: 'type',
id: 'id',
attributes: {
references: [{ name: 'name', type: 'type', id: '1' }],
},
};
expect(extract(state)).toEqual({
state,
references: [{ name: 'name', type: 'type', id: '1' }],
});
});
});
});
52 changes: 52 additions & 0 deletions src/plugins/discover/common/embeddable/search_inject_extract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { SavedObjectReference } from '@kbn/core-saved-objects-server';
import type { EmbeddableStateWithType } from '@kbn/embeddable-plugin/common';
import type { SearchByValueInput } from '@kbn/saved-search-plugin/public';

export const inject = (
state: EmbeddableStateWithType,
injectedReferences: SavedObjectReference[]
): EmbeddableStateWithType => {
if (hasAttributes(state)) {
// Filter out references that are not in the state
// https://github.com/elastic/kibana/pull/119079
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ThomThomson It seems like we have to use the same approach to filtering references as the Lens inject function due to the same issue outlined in #119079. When we have a by value saved search using a temporary data view, the saved search has no references which seems to result in the Dashboard inject code passing in all dashboard references instead:

const filteredReferences = references
.filter((reference) => reference.name.indexOf(prefix) === 0)
.map((reference) => ({ ...reference, name: reference.name.replace(prefix, '') }));
const panelReferences = filteredReferences.length === 0 ? references : filteredReferences;

Let me know if this makes sense to you or if I'm missing something here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah of course, that makes a lot of sense!

const references = state.attributes.references
.map((stateRef) =>
injectedReferences.find((injectedRef) => injectedRef.name === stateRef.name)
)
.filter(Boolean);

state = {
...state,
attributes: {
...state.attributes,
references,
},
} as EmbeddableStateWithType;
}

return state;
};

export const extract = (
state: EmbeddableStateWithType
): { state: EmbeddableStateWithType; references: SavedObjectReference[] } => {
let references: SavedObjectReference[] = [];

if (hasAttributes(state)) {
references = state.attributes.references;
}

return { state, references };
};

const hasAttributes = (
state: EmbeddableStateWithType
): state is EmbeddableStateWithType & SearchByValueInput => 'attributes' in state;
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { savedSearchMock } from '../__mocks__/saved_search';
import { getDiscoverLocatorParams } from './get_discover_locator_params';
import type { SearchInput } from './types';

describe('getDiscoverLocatorParams', () => {
it('should return saved search id if input has savedObjectId', () => {
const input = { savedObjectId: 'savedObjectId' } as SearchInput;
expect(getDiscoverLocatorParams({ input, savedSearch: savedSearchMock })).toEqual({
savedSearchId: 'savedObjectId',
});
});

it('should return Discover params if input has no savedObjectId', () => {
const input = {} as SearchInput;
expect(getDiscoverLocatorParams({ input, savedSearch: savedSearchMock })).toEqual({
dataViewId: savedSearchMock.searchSource.getField('index')?.id,
dataViewSpec: savedSearchMock.searchSource.getField('index')?.toMinimalSpec(),
timeRange: savedSearchMock.timeRange,
refreshInterval: savedSearchMock.refreshInterval,
filters: savedSearchMock.searchSource.getField('filter'),
query: savedSearchMock.searchSource.getField('query'),
columns: savedSearchMock.columns,
sort: savedSearchMock.sort,
viewMode: savedSearchMock.viewMode,
hideAggregatedPreview: savedSearchMock.hideAggregatedPreview,
breakdownField: savedSearchMock.breakdownField,
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { Filter } from '@kbn/es-query';
import type { SavedSearch } from '@kbn/saved-search-plugin/common';
import type { SearchByReferenceInput } from '@kbn/saved-search-plugin/public';
import type { DiscoverAppLocatorParams } from '../../common';
import type { SearchInput } from './types';

export const getDiscoverLocatorParams = ({
input,
savedSearch,
}: {
input: SearchInput;
savedSearch: SavedSearch;
}) => {
const dataView = savedSearch.searchSource.getField('index');
const savedObjectId = (input as SearchByReferenceInput).savedObjectId;
const locatorParams: DiscoverAppLocatorParams = savedObjectId
? { savedSearchId: savedObjectId }
: {
dataViewId: dataView?.id,
dataViewSpec: dataView?.toMinimalSpec(),
timeRange: savedSearch.timeRange,
refreshInterval: savedSearch.refreshInterval,
filters: savedSearch.searchSource.getField('filter') as Filter[],
query: savedSearch.searchSource.getField('query'),
columns: savedSearch.columns,
sort: savedSearch.sort,
viewMode: savedSearch.viewMode,
hideAggregatedPreview: savedSearch.hideAggregatedPreview,
breakdownField: savedSearch.breakdownField,
};

return locatorParams;
};
Loading