From 3105f5ce6dcc84ea959c0639a976ded08b2b8eee Mon Sep 17 00:00:00 2001
From: Pablo Machado
Date: Wed, 6 Apr 2022 12:50:32 +0200
Subject: [PATCH 1/8] Fix alerts and external alerts filters on Hots and Users
pages (#129451)
---
.../use_lens_attributes.test.tsx | 10 +++++-----
.../use_lens_attributes.tsx | 10 +++++++---
.../components/visualization_actions/utils.ts | 2 +-
.../public/hosts/pages/hosts.tsx | 16 ++++++++--------
.../public/hosts/pages/hosts_tabs.tsx | 9 +++++++--
.../pages/navigation/alerts_query_tab_body.tsx | 7 ++-----
.../hosts/pages/navigation/sessions_tab_body.tsx | 7 ++-----
.../public/hosts/pages/types.ts | 2 ++
.../overview/components/event_counts/index.tsx | 4 ++--
.../public/users/pages/details/details_tabs.tsx | 6 ++----
.../public/users/pages/details/helpers.ts | 2 +-
.../public/users/pages/types.ts | 2 ++
.../public/users/pages/users.tsx | 9 ++++++++-
.../public/users/pages/users_tabs.tsx | 9 +++++++--
14 files changed, 56 insertions(+), 39 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx
index 72257a76d43c8..426afa6233d49 100644
--- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.test.tsx
@@ -18,7 +18,7 @@ import {
} from '../../mock';
import { getExternalAlertLensAttributes } from './lens_attributes/common/external_alert';
import { useLensAttributes } from './use_lens_attributes';
-import { filterHostExternalAlertData, getHostDetailsPageFilter, getIndexFilters } from './utils';
+import { hostNameExistsFilter, getHostDetailsPageFilter, getIndexFilters } from './utils';
import { createStore, State } from '../../store';
jest.mock('../../containers/sourcerer', () => ({
@@ -80,7 +80,7 @@ describe('useLensAttributes', () => {
store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
});
- it('should should add query', () => {
+ it('should add query', () => {
const wrapper = ({ children }: { children: React.ReactElement }) => (
{children}
);
@@ -96,7 +96,7 @@ describe('useLensAttributes', () => {
expect(result?.current?.state.query).toEqual({ query: 'host.name: *', language: 'kql' });
});
- it('should should add filters', () => {
+ it('should add filters', () => {
const wrapper = ({ children }: { children: React.ReactElement }) => (
{children}
);
@@ -113,12 +113,12 @@ describe('useLensAttributes', () => {
...getExternalAlertLensAttributes().state.filters,
...filterFromSearchBar,
...getHostDetailsPageFilter('mockHost'),
- ...filterHostExternalAlertData,
+ ...hostNameExistsFilter,
...getIndexFilters(['auditbeat-*']),
]);
});
- it('should should add data view id to references', () => {
+ it('should add data view id to references', () => {
const wrapper = ({ children }: { children: React.ReactElement }) => (
{children}
);
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx
index 123cff112456c..042a606a6571c 100644
--- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_lens_attributes.tsx
@@ -7,6 +7,7 @@
import { useMemo } from 'react';
import { SecurityPageName } from '../../../../common/constants';
+import { HostsTableType } from '../../../hosts/store/model';
import { NetworkRouteType } from '../../../network/pages/navigation/types';
import { useSourcererDataView } from '../../containers/sourcerer';
import { useDeepEqualSelector } from '../../hooks/use_selector';
@@ -16,7 +17,7 @@ import { LensAttributes, GetLensAttributes } from './types';
import {
getHostDetailsPageFilter,
filterNetworkExternalAlertData,
- filterHostExternalAlertData,
+ hostNameExistsFilter,
getIndexFilters,
} from './utils';
@@ -40,8 +41,11 @@ export const useLensAttributes = ({
const [{ detailName, pageName, tabName }] = useRouteSpy();
const tabsFilters = useMemo(() => {
- if (pageName === SecurityPageName.hosts && tabName === 'externalAlerts') {
- return filterHostExternalAlertData;
+ if (
+ pageName === SecurityPageName.hosts &&
+ (tabName === HostsTableType.alerts || tabName === HostsTableType.events)
+ ) {
+ return hostNameExistsFilter;
}
if (pageName === SecurityPageName.network && tabName === NetworkRouteType.alerts) {
diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/utils.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/utils.ts
index 9a6df2ab6ab99..94e09f0d95c83 100644
--- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/utils.ts
+++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/utils.ts
@@ -30,7 +30,7 @@ export const getHostDetailsPageFilter = (hostName?: string): Filter[] =>
]
: [];
-export const filterHostExternalAlertData: Filter[] = [
+export const hostNameExistsFilter: Filter[] = [
{
query: {
bool: {
diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx
index 8d2255655b685..859e20607a4f7 100644
--- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx
+++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx
@@ -11,8 +11,8 @@ import { noop } from 'lodash/fp';
import React, { useCallback, useMemo, useRef } from 'react';
import { useDispatch } from 'react-redux';
import { useParams } from 'react-router-dom';
+import { Filter } from '@kbn/es-query';
import { isTab } from '../../../../timelines/public';
-
import { SecurityPageName } from '../../app/types';
import { UpdateDateRange } from '../../common/components/charts/common';
import { FiltersGlobal } from '../../common/components/filters_global';
@@ -54,9 +54,10 @@ import { useDeepEqualSelector, useShallowEqualSelector } from '../../common/hook
import { useInvalidFilterQuery } from '../../common/hooks/use_invalid_filter_query';
import { ID } from '../containers/hosts';
import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';
-import { filterHostExternalAlertData } from '../../common/components/visualization_actions/utils';
+
import { LandingPageComponent } from '../../common/components/landing_page';
import { Loader } from '../../common/components/loader';
+import { hostNameExistsFilter } from '../../common/components/visualization_actions/utils';
/**
* Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space.
@@ -99,17 +100,15 @@ const HostsComponent = () => {
const capabilities = useMlCapabilities();
const { uiSettings } = useKibana().services;
const { tabName } = useParams<{ tabName: string }>();
- const tabsFilters = React.useMemo(() => {
- if (tabName === HostsTableType.alerts) {
- return filters.length > 0
- ? [...filters, ...filterHostExternalAlertData]
- : filterHostExternalAlertData;
+ const tabsFilters: Filter[] = React.useMemo(() => {
+ if (tabName === HostsTableType.alerts || tabName === HostsTableType.events) {
+ return filters.length > 0 ? [...filters, ...hostNameExistsFilter] : hostNameExistsFilter;
}
if (tabName === HostsTableType.risk) {
const severityFilter = generateSeverityFilter(severitySelection);
- return [...severityFilter, ...filterHostExternalAlertData, ...filters];
+ return [...severityFilter, ...hostNameExistsFilter, ...filters];
}
return filters;
}, [severitySelection, tabName, filters]);
@@ -242,6 +241,7 @@ const HostsComponent = () => {
setQuery={setQuery}
from={from}
type={hostsModel.HostsType.page}
+ pageFilters={tabsFilters}
/>
diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx
index ed2a1ec983b20..af619db5e9fa0 100644
--- a/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx
+++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx
@@ -33,6 +33,7 @@ export const HostsTabs = memo(
deleteQuery,
docValueFields,
filterQuery,
+ pageFilters,
from,
indexNames,
isInitializing,
@@ -99,10 +100,14 @@ export const HostsTabs = memo(
-
+
-
+
diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx
index 548520676184a..d19e22b4406ba 100644
--- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx
+++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx
@@ -9,16 +9,13 @@ import React, { useMemo } from 'react';
import { TimelineId } from '../../../../common/types/timeline';
import { AlertsView } from '../../../common/components/alerts_viewer';
-import { filterHostExternalAlertData } from '../../../common/components/visualization_actions/utils';
+import { hostNameExistsFilter } from '../../../common/components/visualization_actions/utils';
import { AlertsComponentQueryProps } from './types';
export const HostAlertsQueryTabBody = React.memo((alertsProps: AlertsComponentQueryProps) => {
const { pageFilters, ...rest } = alertsProps;
const hostPageFilters = useMemo(
- () =>
- pageFilters != null
- ? [...filterHostExternalAlertData, ...pageFilters]
- : filterHostExternalAlertData,
+ () => (pageFilters != null ? [...hostNameExistsFilter, ...pageFilters] : hostNameExistsFilter),
[pageFilters]
);
diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/sessions_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/sessions_tab_body.tsx
index 0ff47a104ca21..4ad98f84eb854 100644
--- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/sessions_tab_body.tsx
+++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/sessions_tab_body.tsx
@@ -8,16 +8,13 @@
import React, { useMemo } from 'react';
import { TimelineId } from '../../../../common/types/timeline';
import { SessionsView } from '../../../common/components/sessions_viewer';
-import { filterHostExternalAlertData } from '../../../common/components/visualization_actions/utils';
+import { hostNameExistsFilter } from '../../../common/components/visualization_actions/utils';
import { AlertsComponentQueryProps } from './types';
export const SessionsTabBody = React.memo((alertsProps: AlertsComponentQueryProps) => {
const { pageFilters, filterQuery, ...rest } = alertsProps;
const hostPageFilters = useMemo(
- () =>
- pageFilters != null
- ? [...filterHostExternalAlertData, ...pageFilters]
- : filterHostExternalAlertData,
+ () => (pageFilters != null ? [...hostNameExistsFilter, ...pageFilters] : hostNameExistsFilter),
[pageFilters]
);
diff --git a/x-pack/plugins/security_solution/public/hosts/pages/types.ts b/x-pack/plugins/security_solution/public/hosts/pages/types.ts
index c7fd743ee5e44..83c23834cc13b 100644
--- a/x-pack/plugins/security_solution/public/hosts/pages/types.ts
+++ b/x-pack/plugins/security_solution/public/hosts/pages/types.ts
@@ -7,6 +7,7 @@
import { ActionCreator } from 'typescript-fsa';
+import { Filter } from '@kbn/es-query';
import { hostsModel } from '../store';
import { GlobalTimeArgs } from '../../common/containers/use_global_time';
import { InputsModelId } from '../../common/store/inputs/constants';
@@ -18,6 +19,7 @@ export const hostDetailsPagePath = `${HOSTS_PATH}/:detailName`;
export type HostsTabsProps = GlobalTimeArgs & {
docValueFields: DocValueFields[];
filterQuery: string;
+ pageFilters?: Filter[];
indexNames: string[];
type: hostsModel.HostsType;
setAbsoluteRangeDatePicker: ActionCreator<{
diff --git a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx
index efc5a61d227c0..114813ea50a4f 100644
--- a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx
@@ -19,7 +19,7 @@ import { getEsQueryConfig } from '../../../../../../../src/plugins/data/common';
import { GlobalTimeArgs } from '../../../common/containers/use_global_time';
import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query';
import {
- filterHostExternalAlertData,
+ hostNameExistsFilter,
filterNetworkExternalAlertData,
} from '../../../common/components/visualization_actions/utils';
@@ -51,7 +51,7 @@ const EventCountsComponent: React.FC = ({
config: getEsQueryConfig(uiSettings),
indexPattern,
queries: [query],
- filters: [...filters, ...filterHostExternalAlertData],
+ filters: [...filters, ...hostNameExistsFilter],
}),
[filters, indexPattern, query, uiSettings]
);
diff --git a/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx b/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx
index 25ada310b74b7..e23cac75b6cdf 100644
--- a/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx
+++ b/x-pack/plugins/security_solution/public/users/pages/details/details_tabs.tsx
@@ -19,7 +19,7 @@ import { usersDetailsPagePath } from '../constants';
import { TimelineId } from '../../../../common/types';
import { EventsQueryTabBody } from '../../../common/components/events_tab/events_query_tab_body';
import { AlertsView } from '../../../common/components/alerts_viewer';
-import { filterUserExternalAlertData } from './helpers';
+import { userNameExistsFilter } from './helpers';
export const UsersDetailsTabs = React.memo(
({
@@ -64,9 +64,7 @@ export const UsersDetailsTabs = React.memo(
const alertsPageFilters = useMemo(
() =>
- pageFilters != null
- ? [...filterUserExternalAlertData, ...pageFilters]
- : filterUserExternalAlertData,
+ pageFilters != null ? [...userNameExistsFilter, ...pageFilters] : userNameExistsFilter,
[pageFilters]
);
diff --git a/x-pack/plugins/security_solution/public/users/pages/details/helpers.ts b/x-pack/plugins/security_solution/public/users/pages/details/helpers.ts
index daa02df2fb9ca..bca7ff5c6c336 100644
--- a/x-pack/plugins/security_solution/public/users/pages/details/helpers.ts
+++ b/x-pack/plugins/security_solution/public/users/pages/details/helpers.ts
@@ -31,7 +31,7 @@ export const getUsersDetailsPageFilters = (userName: string): Filter[] => [
},
];
-export const filterUserExternalAlertData: Filter[] = [
+export const userNameExistsFilter: Filter[] = [
{
query: {
bool: {
diff --git a/x-pack/plugins/security_solution/public/users/pages/types.ts b/x-pack/plugins/security_solution/public/users/pages/types.ts
index 12b6fd0b8749c..787f5def1a18c 100644
--- a/x-pack/plugins/security_solution/public/users/pages/types.ts
+++ b/x-pack/plugins/security_solution/public/users/pages/types.ts
@@ -6,6 +6,7 @@
*/
import { ActionCreator } from 'typescript-fsa';
+import { Filter } from '@kbn/es-query';
import { GlobalTimeArgs } from '../../common/containers/use_global_time';
import { usersModel } from '../../users/store';
@@ -15,6 +16,7 @@ import { InputsModelId } from '../../common/store/inputs/constants';
export type UsersTabsProps = GlobalTimeArgs & {
docValueFields: DocValueFields[];
filterQuery: string;
+ pageFilters?: Filter[];
indexNames: string[];
type: usersModel.UsersType;
setAbsoluteRangeDatePicker: ActionCreator<{
diff --git a/x-pack/plugins/security_solution/public/users/pages/users.tsx b/x-pack/plugins/security_solution/public/users/pages/users.tsx
index ae5b485142f9c..046cf5ead65b7 100644
--- a/x-pack/plugins/security_solution/public/users/pages/users.tsx
+++ b/x-pack/plugins/security_solution/public/users/pages/users.tsx
@@ -11,6 +11,7 @@ import { noop } from 'lodash/fp';
import React, { useCallback, useMemo, useRef } from 'react';
import { useDispatch } from 'react-redux';
import { useParams } from 'react-router-dom';
+import { Filter } from '@kbn/es-query';
import { isTab } from '../../../../timelines/public';
import { SecurityPageName } from '../../app/types';
import { FiltersGlobal } from '../../common/components/filters_global';
@@ -49,6 +50,7 @@ import { hasMlUserPermissions } from '../../../common/machine_learning/has_ml_us
import { useMlCapabilities } from '../../common/components/ml/hooks/use_ml_capabilities';
import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';
import { LandingPageComponent } from '../../common/components/landing_page';
+import { userNameExistsFilter } from './details/helpers';
const ID = 'UsersQueryId';
@@ -86,7 +88,11 @@ const UsersComponent = () => {
const { uiSettings } = useKibana().services;
const { tabName } = useParams<{ tabName: string }>();
- const tabsFilters = React.useMemo(() => {
+ const tabsFilters: Filter[] = React.useMemo(() => {
+ if (tabName === UsersTableType.alerts || tabName === UsersTableType.events) {
+ return filters.length > 0 ? [...filters, ...userNameExistsFilter] : userNameExistsFilter;
+ }
+
if (tabName === UsersTableType.risk) {
const severityFilter = generateSeverityFilter(severitySelection);
@@ -216,6 +222,7 @@ const UsersComponent = () => {
setQuery={setQuery}
to={to}
type={usersModel.UsersType.page}
+ pageFilters={tabsFilters}
/>
diff --git a/x-pack/plugins/security_solution/public/users/pages/users_tabs.tsx b/x-pack/plugins/security_solution/public/users/pages/users_tabs.tsx
index c0e3fb3e4ab18..fb2cecee75ea6 100644
--- a/x-pack/plugins/security_solution/public/users/pages/users_tabs.tsx
+++ b/x-pack/plugins/security_solution/public/users/pages/users_tabs.tsx
@@ -27,6 +27,7 @@ export const UsersTabs = memo(
({
deleteQuery,
filterQuery,
+ pageFilters,
from,
indexNames,
isInitializing,
@@ -90,13 +91,17 @@ export const UsersTabs = memo(
-
+
From 11722fb920a50df45ced2c6d6d3db97201556947 Mon Sep 17 00:00:00 2001
From: Joe Reuter
Date: Wed, 6 Apr 2022 13:31:11 +0200
Subject: [PATCH 2/8] [Lens] Make suggestions depend on active data (#129326)
---
.../editor_frame/editor_frame.test.tsx | 34 ++++++-------------
.../editor_frame/state_helpers.ts | 19 +----------
.../editor_frame/suggestion_panel.tsx | 25 +++++++++++---
.../lens/public/state_management/selectors.ts | 5 ++-
x-pack/plugins/lens/public/types.ts | 9 +----
5 files changed, 36 insertions(+), 56 deletions(-)
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx
index a54161863ed24..faf36a3b519c5 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx
@@ -408,8 +408,7 @@ describe('editor_frame', () => {
setDatasourceState(updatedState);
});
- // validation requires to calls this getConfiguration API
- expect(mockVisualization.getConfiguration).toHaveBeenCalledTimes(6);
+ expect(mockVisualization.getConfiguration).toHaveBeenCalledTimes(2);
expect(mockVisualization.getConfiguration).toHaveBeenLastCalledWith(
expect.objectContaining({
state: updatedState,
@@ -487,8 +486,7 @@ describe('editor_frame', () => {
setDatasourceState({});
});
- // validation requires to calls this getConfiguration API
- expect(mockVisualization.getConfiguration).toHaveBeenCalledTimes(6);
+ expect(mockVisualization.getConfiguration).toHaveBeenCalledTimes(2);
expect(mockVisualization.getConfiguration).toHaveBeenLastCalledWith(
expect.objectContaining({
frame: expect.objectContaining({
@@ -847,8 +845,7 @@ describe('editor_frame', () => {
instance.find('[data-test-subj="lnsSuggestion"]').at(2).simulate('click');
});
- // validation requires to calls this getConfiguration API
- expect(mockVisualization.getConfiguration).toHaveBeenCalledTimes(6);
+ expect(mockVisualization.getConfiguration).toHaveBeenCalledTimes(2);
expect(mockVisualization.getConfiguration).toHaveBeenLastCalledWith(
expect.objectContaining({
state: suggestionVisState,
@@ -928,7 +925,7 @@ describe('editor_frame', () => {
},
{
score: 0.6,
- state: {},
+ state: suggestionVisState,
title: 'Suggestion2',
previewIcon: 'empty',
},
@@ -939,7 +936,7 @@ describe('editor_frame', () => {
getSuggestions: () => [
{
score: 0.8,
- state: suggestionVisState,
+ state: {},
title: 'Suggestion3',
previewIcon: 'empty',
},
@@ -980,6 +977,8 @@ describe('editor_frame', () => {
})
).instance;
+ instance.update();
+
act(() => {
instance.find('[data-test-subj="mockVisA"]').find(DragDrop).prop('onDrop')!(
{
@@ -992,7 +991,7 @@ describe('editor_frame', () => {
);
});
- expect(mockVisualization2.getConfiguration).toHaveBeenCalledWith(
+ expect(mockVisualization.getConfiguration).toHaveBeenCalledWith(
expect.objectContaining({
state: suggestionVisState,
})
@@ -1035,20 +1034,8 @@ describe('editor_frame', () => {
visualizationMap: {
testVis: {
...mockVisualization,
- getSuggestions: () => [
- {
- score: 0.2,
- state: {},
- title: 'Suggestion1',
- previewIcon: 'empty',
- },
- {
- score: 0.6,
- state: {},
- title: 'Suggestion2',
- previewIcon: 'empty',
- },
- ],
+ // do not return suggestions for the currently active vis, otherwise it will be chosen
+ getSuggestions: () => [],
},
testVis2: {
...mockVisualization2,
@@ -1079,6 +1066,7 @@ describe('editor_frame', () => {
} as EditorFrameProps;
instance = (await mountWithProvider()).instance;
+ instance.update();
act(() => {
instance.find(DragDrop).filter('[dataTestSubj="lnsWorkspace"]').prop('onDrop')!(
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts
index 40db06285d0b6..95626b7657e59 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts
@@ -15,7 +15,6 @@ import {
FramePublicAPI,
InitializationOptions,
Visualization,
- VisualizationDimensionGroupConfig,
VisualizationMap,
VisualizeEditorContext,
} from '../../types';
@@ -197,24 +196,8 @@ export const validateDatasourceAndVisualization = (
currentVisualizationState: unknown | undefined,
frameAPI: Pick
): ErrorMessage[] | undefined => {
- const layersGroups = currentVisualizationState
- ? currentVisualization
- ?.getLayerIds(currentVisualizationState)
- .reduce>((memo, layerId) => {
- const groups = currentVisualization?.getConfiguration({
- frame: frameAPI,
- layerId,
- state: currentVisualizationState,
- }).groups;
- if (groups) {
- memo[layerId] = groups;
- }
- return memo;
- }, {})
- : undefined;
-
const datasourceValidationErrors = currentDatasourceState
- ? currentDataSource?.getErrorMessages(currentDatasourceState, layersGroups)
+ ? currentDataSource?.getErrorMessages(currentDatasourceState)
: undefined;
const visualizationValidationErrors = currentVisualizationState
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx
index 0257dafa9ccc8..69de606a77ca7 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx
@@ -41,7 +41,11 @@ import {
} from '../../../../../../src/plugins/expressions/public';
import { prependDatasourceExpression } from './expression_helpers';
import { trackUiEvent, trackSuggestionEvent } from '../../lens_ui_telemetry';
-import { getMissingIndexPattern, validateDatasourceAndVisualization } from './state_helpers';
+import {
+ getMissingIndexPattern,
+ validateDatasourceAndVisualization,
+ getDatasourceLayers,
+} from './state_helpers';
import {
rollbackSuggestion,
selectExecutionContextSearch,
@@ -226,16 +230,28 @@ export function SuggestionPanel({
visualizationId,
visualizationState: suggestionVisualizationState,
datasourceState: suggestionDatasourceState,
- datasourceId: suggetionDatasourceId,
+ datasourceId: suggestionDatasourceId,
}) => {
return (
!hide &&
validateDatasourceAndVisualization(
- suggetionDatasourceId ? datasourceMap[suggetionDatasourceId] : null,
+ suggestionDatasourceId ? datasourceMap[suggestionDatasourceId] : null,
suggestionDatasourceState,
visualizationMap[visualizationId],
suggestionVisualizationState,
- frame
+ {
+ datasourceLayers: getDatasourceLayers(
+ suggestionDatasourceId
+ ? {
+ [suggestionDatasourceId]: {
+ isLoading: true,
+ state: suggestionDatasourceState,
+ },
+ }
+ : {},
+ datasourceMap
+ ),
+ }
) == null
);
}
@@ -284,6 +300,7 @@ export function SuggestionPanel({
activeDatasourceId,
datasourceMap,
visualizationMap,
+ frame.activeData,
]);
const context: ExecutionContextSearch = useLensSelector(selectExecutionContextSearch);
diff --git a/x-pack/plugins/lens/public/state_management/selectors.ts b/x-pack/plugins/lens/public/state_management/selectors.ts
index aab72c3239e8a..351496ca3e37b 100644
--- a/x-pack/plugins/lens/public/state_management/selectors.ts
+++ b/x-pack/plugins/lens/public/state_management/selectors.ts
@@ -6,9 +6,8 @@
*/
import { createSelector } from '@reduxjs/toolkit';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { SavedObjectReference } from 'kibana/server';
import { FilterManager } from 'src/plugins/data/public';
+import { SavedObjectReference } from 'kibana/public';
import { LensState } from './types';
import { Datasource, DatasourceMap, VisualizationMap } from '../types';
import { getDatasourceLayers } from '../editor_frame_service/editor_frame';
@@ -160,7 +159,7 @@ export const selectDatasourceLayers = createSelector(
export const selectFramePublicAPI = createSelector(
[
- selectDatasourceStates,
+ selectCurrentDatasourceStates,
selectActiveData,
selectInjectedDependencies as SelectInjectedDependenciesFunction,
],
diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts
index cfa23320dc561..a46493de668ba 100644
--- a/x-pack/plugins/lens/public/types.ts
+++ b/x-pack/plugins/lens/public/types.ts
@@ -296,14 +296,7 @@ export interface Datasource {
) => Array>;
getPublicAPI: (props: PublicAPIProps) => DatasourcePublicAPI;
- getErrorMessages: (
- state: T,
- layersGroups?: Record,
- dateRange?: {
- fromDate: string;
- toDate: string;
- }
- ) =>
+ getErrorMessages: (state: T) =>
| Array<{
shortMessage: string;
longMessage: React.ReactNode;
From 39cf5d57f1f8be53fafc8dd1cec8f0ff16275d7c Mon Sep 17 00:00:00 2001
From: Shahzad
Date: Wed, 6 Apr 2022 13:43:33 +0200
Subject: [PATCH 3/8] [Monitor management] Update to use new add monitor
endpoint (#129447)
Co-authored-by: Abdul Zahid
---
.../monitor_management/monitor_types.ts | 30 ++++-----
.../hooks/use_inline_errors.test.tsx | 2 +-
.../hooks/use_inline_errors_count.test.tsx | 2 +-
.../hooks/use_locations.test.tsx | 1 +
.../monitor_list/invalid_monitors.tsx | 1 +
.../monitor_list/monitor_async_error.tsx | 19 ++++--
.../monitor_list/monitor_list.test.tsx | 1 +
.../public/lib/__mocks__/uptime_store.mock.ts | 1 +
.../synthetics_service/service_api_client.ts | 5 +-
.../synthetics_service/synthetics_service.ts | 67 +++++++++++--------
.../synthetics_service/add_monitor.ts | 16 ++---
.../uptime/rest/monitor_states_real_data.ts | 8 ++-
12 files changed, 91 insertions(+), 62 deletions(-)
diff --git a/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_types.ts b/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_types.ts
index 872ccdbb71ec8..1e8b89ce065fa 100644
--- a/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_types.ts
+++ b/x-pack/plugins/uptime/common/runtime_types/monitor_management/monitor_types.ts
@@ -306,23 +306,19 @@ export type EncryptedSyntheticsMonitorWithId = t.TypeOf<
typeof EncryptedSyntheticsMonitorWithIdCodec
>;
-export const MonitorManagementListResultCodec = t.intersection([
- t.type({
- monitors: t.array(
- t.interface({
- id: t.string,
- attributes: EncryptedSyntheticsMonitorCodec,
- updated_at: t.string,
- })
- ),
- page: t.number,
- perPage: t.number,
- total: t.union([t.number, t.null]),
- }),
- t.partial({
- syncErrors: ServiceLocationErrors,
- }),
-]);
+export const MonitorManagementListResultCodec = t.type({
+ monitors: t.array(
+ t.interface({
+ id: t.string,
+ attributes: EncryptedSyntheticsMonitorCodec,
+ updated_at: t.string,
+ })
+ ),
+ page: t.number,
+ perPage: t.number,
+ total: t.union([t.number, t.null]),
+ syncErrors: t.union([ServiceLocationErrors, t.null]),
+});
export type MonitorManagementListResult = t.TypeOf;
diff --git a/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_inline_errors.test.tsx b/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_inline_errors.test.tsx
index c0c1145e5cc2f..649b009687e33 100644
--- a/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_inline_errors.test.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_inline_errors.test.tsx
@@ -70,7 +70,7 @@ describe('useInlineErrors', function () {
{
error: { monitorList: null, serviceLocations: null, enablement: null },
enablement: null,
- list: { monitors: [], page: 1, perPage: 10, total: null },
+ list: { monitors: [], page: 1, perPage: 10, total: null, syncErrors: null },
loading: { monitorList: false, serviceLocations: false, enablement: false },
locations: [],
syntheticsService: {
diff --git a/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_inline_errors_count.test.tsx b/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_inline_errors_count.test.tsx
index c56ca40c59aad..e973e3dd1a7f1 100644
--- a/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_inline_errors_count.test.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_inline_errors_count.test.tsx
@@ -68,7 +68,7 @@ describe('useInlineErrorsCount', function () {
'heartbeat-8*,heartbeat-7*,synthetics-*',
{
error: { monitorList: null, serviceLocations: null, enablement: null },
- list: { monitors: [], page: 1, perPage: 10, total: null },
+ list: { monitors: [], page: 1, perPage: 10, total: null, syncErrors: null },
enablement: null,
loading: { monitorList: false, serviceLocations: false, enablement: false },
locations: [],
diff --git a/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_locations.test.tsx b/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_locations.test.tsx
index fdb1c57712020..46b8981b74a0f 100644
--- a/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_locations.test.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor_management/hooks/use_locations.test.tsx
@@ -40,6 +40,7 @@ describe('useExpViewTimeRange', function () {
page: 1,
total: 0,
monitors: [],
+ syncErrors: null,
},
locations: [],
enablement: null,
diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/invalid_monitors.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/invalid_monitors.tsx
index 87a6cf2dc1d2b..342b9c6547b1b 100644
--- a/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/invalid_monitors.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/invalid_monitors.tsx
@@ -48,6 +48,7 @@ export const InvalidMonitors = ({
page: pageState.pageIndex,
perPage: pageState.pageSize,
total: invalidTotal ?? 0,
+ syncErrors: null,
},
enablement: null,
error: { monitorList: null, serviceLocations: null, enablement: null },
diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_async_error.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_async_error.tsx
index c9e9dba2027a4..48aeaf3648a0b 100644
--- a/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_async_error.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_async_error.tsx
@@ -36,11 +36,15 @@ export const MonitorAsyncError = () => {
/>
- {Object.values(syncErrors).map((e) => {
+ {Object.values(syncErrors ?? {}).map((e) => {
return (
- - {`${
- locations.find((location) => location.id === e.locationId)?.label
- } - ${STATUS_LABEL}: ${e.error.status}; ${REASON_LABEL}: ${e.error.reason}.`}
+ -
+ {`${
+ locations.find((location) => location.id === e.locationId)?.label
+ } - ${STATUS_LABEL}: ${e.error?.status ?? NOT_AVAILABLE_LABEL}; ${REASON_LABEL}: ${
+ e.error?.reason ?? NOT_AVAILABLE_LABEL
+ }`}
+
);
})}
@@ -67,6 +71,13 @@ const STATUS_LABEL = i18n.translate(
}
);
+const NOT_AVAILABLE_LABEL = i18n.translate(
+ 'xpack.uptime.monitorManagement.monitorSync.failure.notAvailable',
+ {
+ defaultMessage: 'Not available',
+ }
+);
+
const DISMISS_LABEL = i18n.translate(
'xpack.uptime.monitorManagement.monitorSync.failure.dismissLabel',
{
diff --git a/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_list.test.tsx b/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_list.test.tsx
index 8d0bd67616576..40eb185a65f0c 100644
--- a/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_list.test.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor_management/monitor_list/monitor_list.test.tsx
@@ -48,6 +48,7 @@ describe('', () => {
page: 1,
total: 6,
monitors,
+ syncErrors: null,
},
locations: [],
enablement: null,
diff --git a/x-pack/plugins/uptime/public/lib/__mocks__/uptime_store.mock.ts b/x-pack/plugins/uptime/public/lib/__mocks__/uptime_store.mock.ts
index 1bb86877f9861..0286e884a68d7 100644
--- a/x-pack/plugins/uptime/public/lib/__mocks__/uptime_store.mock.ts
+++ b/x-pack/plugins/uptime/public/lib/__mocks__/uptime_store.mock.ts
@@ -69,6 +69,7 @@ export const mockState: AppState = {
perPage: 10,
total: null,
monitors: [],
+ syncErrors: null,
},
locations: [],
loading: {
diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts
index 68d4ebd385f07..3397dcad94e95 100644
--- a/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts
+++ b/x-pack/plugins/uptime/server/lib/synthetics_service/service_api_client.ts
@@ -74,7 +74,7 @@ export class ServiceAPIClient {
}
async put(data: ServiceData) {
- return this.callAPI('POST', data);
+ return this.callAPI('PUT', data);
}
async delete(data: ServiceData) {
@@ -170,6 +170,9 @@ export class ServiceAPIClient {
catchError((err) => {
pushErrors.push({ locationId: id, error: err.response?.data });
this.logger.error(err);
+ if (err.response?.data?.reason) {
+ this.logger.error(err.response?.data?.reason);
+ }
// we don't want to throw an unhandled exception here
return of(true);
})
diff --git a/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts b/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts
index 24630cd0ec738..c6fa280f2f163 100644
--- a/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts
+++ b/x-pack/plugins/uptime/server/lib/synthetics_service/synthetics_service.ts
@@ -45,6 +45,11 @@ const SYNTHETICS_SERVICE_SYNC_MONITORS_TASK_TYPE =
const SYNTHETICS_SERVICE_SYNC_MONITORS_TASK_ID = 'UPTIME:SyntheticsService:sync-task';
const SYNTHETICS_SERVICE_SYNC_INTERVAL_DEFAULT = '5m';
+type SyntheticsConfig = SyntheticsMonitorWithId & {
+ fields_under_root?: boolean;
+ fields?: { config_id: string; run_once?: boolean; test_run_id?: string };
+};
+
export class SyntheticsService {
private logger: Logger;
private readonly server: UptimeServerSetup;
@@ -218,14 +223,32 @@ export class SyntheticsService {
};
}
- async pushConfigs(
- configs?: Array<
- SyntheticsMonitorWithId & {
- fields_under_root?: boolean;
- fields?: { config_id: string };
- }
- >
- ) {
+ async addConfig(config: SyntheticsConfig) {
+ const monitors = this.formatConfigs([config]);
+
+ this.apiKey = await this.getApiKey();
+
+ if (!this.apiKey) {
+ return null;
+ }
+
+ const data = {
+ monitors,
+ output: await this.getOutput(this.apiKey),
+ };
+
+ this.logger.debug(`1 monitor will be pushed to synthetics service.`);
+
+ try {
+ this.syncErrors = await this.apiClient.post(data);
+ return this.syncErrors;
+ } catch (e) {
+ this.logger.error(e);
+ throw e;
+ }
+ }
+
+ async pushConfigs(configs?: SyntheticsConfig[]) {
const monitors = this.formatConfigs(configs || (await this.getMonitorConfigs()));
if (monitors.length === 0) {
this.logger.debug('No monitor found which can be pushed to service.');
@@ -246,21 +269,15 @@ export class SyntheticsService {
this.logger.debug(`${monitors.length} monitors will be pushed to synthetics service.`);
try {
- return await this.apiClient.post(data);
+ this.syncErrors = await this.apiClient.put(data);
+ return this.syncErrors;
} catch (e) {
this.logger.error(e);
throw e;
}
}
- async runOnceConfigs(
- configs?: Array<
- SyntheticsMonitorWithId & {
- fields_under_root?: boolean;
- fields?: { run_once: boolean; config_id: string };
- }
- >
- ) {
+ async runOnceConfigs(configs?: SyntheticsConfig[]) {
const monitors = this.formatConfigs(configs || (await this.getMonitorConfigs()));
if (monitors.length === 0) {
return;
@@ -284,15 +301,7 @@ export class SyntheticsService {
}
}
- async triggerConfigs(
- request?: KibanaRequest,
- configs?: Array<
- SyntheticsMonitorWithId & {
- fields_under_root?: boolean;
- fields?: { config_id: string; test_run_id: string };
- }
- >
- ) {
+ async triggerConfigs(request?: KibanaRequest, configs?: SyntheticsConfig[]) {
const monitors = this.formatConfigs(configs || (await this.getMonitorConfigs()));
if (monitors.length === 0) {
return;
@@ -328,7 +337,11 @@ export class SyntheticsService {
monitors: this.formatConfigs(configs),
output: await this.getOutput(this.apiKey),
};
- return await this.apiClient.delete(data);
+ const result = await this.apiClient.delete(data);
+ if (this.syncErrors && this.syncErrors?.length > 0) {
+ this.syncErrors = await this.pushConfigs();
+ }
+ return result;
}
async deleteAllConfigs() {
diff --git a/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts b/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts
index 19bc5050ddfcc..521dae85e85db 100644
--- a/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts
+++ b/x-pack/plugins/uptime/server/rest_api/synthetics_service/add_monitor.ts
@@ -45,16 +45,14 @@ export const addSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({
const { syntheticsService } = server;
- const errors = await syntheticsService.pushConfigs([
- {
- ...monitor,
- id: newMonitor.id,
- fields: {
- config_id: newMonitor.id,
- },
- fields_under_root: true,
+ const errors = await syntheticsService.addConfig({
+ ...monitor,
+ id: newMonitor.id,
+ fields: {
+ config_id: newMonitor.id,
},
- ]);
+ fields_under_root: true,
+ });
sendTelemetryEvents(
server.logger,
diff --git a/x-pack/test/api_integration/apis/uptime/rest/monitor_states_real_data.ts b/x-pack/test/api_integration/apis/uptime/rest/monitor_states_real_data.ts
index 909a485057f85..68d17cf3a4dd2 100644
--- a/x-pack/test/api_integration/apis/uptime/rest/monitor_states_real_data.ts
+++ b/x-pack/test/api_integration/apis/uptime/rest/monitor_states_real_data.ts
@@ -8,7 +8,10 @@
import expect from '@kbn/expect';
import { isRight } from 'fp-ts/lib/Either';
import { FtrProviderContext } from '../../../ftr_provider_context';
-import { MonitorSummariesResultType } from '../../../../../plugins/uptime/common/runtime_types';
+import {
+ MonitorSummariesResult,
+ MonitorSummariesResultType,
+} from '../../../../../plugins/uptime/common/runtime_types';
import { API_URLS } from '../../../../../plugins/uptime/common/constants';
interface ExpectedMonitorStatesPage {
@@ -40,7 +43,8 @@ const checkMonitorStatesResponse = ({
const decoded = MonitorSummariesResultType.decode(response);
expect(isRight(decoded)).to.be.ok();
if (isRight(decoded)) {
- const { summaries, prevPagePagination, nextPagePagination } = decoded.right;
+ const { summaries, prevPagePagination, nextPagePagination } =
+ decoded.right as MonitorSummariesResult;
expect(summaries).to.have.length(size);
expect(summaries?.map((s) => s.monitor_id)).to.eql(statesIds);
expect(
From 8af56aaf8181b4639f0ee169f8189790975f12a4 Mon Sep 17 00:00:00 2001
From: Matthew Kime
Date: Wed, 6 Apr 2022 07:24:59 -0500
Subject: [PATCH 4/8] reset selection after data view delete (#129552)
---
.../components/index_pattern_table/index_pattern_table.tsx | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/plugins/data_view_management/public/components/index_pattern_table/index_pattern_table.tsx b/src/plugins/data_view_management/public/components/index_pattern_table/index_pattern_table.tsx
index 2de06c20c4040..578a0b198bbe8 100644
--- a/src/plugins/data_view_management/public/components/index_pattern_table/index_pattern_table.tsx
+++ b/src/plugins/data_view_management/public/components/index_pattern_table/index_pattern_table.tsx
@@ -94,7 +94,10 @@ export const IndexPatternTable = ({
dataViews,
overlays,
uiSettings,
- onDelete: () => loadDataViews(),
+ onDelete: () => {
+ setSelectedItems([]);
+ loadDataViews();
+ },
});
if (selectedItems.length === 0) {
return;
From 1a48388cbd90e5f3170e4b1e6cf256790c908641 Mon Sep 17 00:00:00 2001
From: Andrew Tate
Date: Wed, 6 Apr 2022 08:02:08 -0500
Subject: [PATCH 5/8] [Lens] apply pinned filters to Lens (#129503)
---
.../init_middleware/load_initial.ts | 2 +-
.../public/state_management/load_initial.test.tsx | 13 +++++++++++--
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts
index c9009ab395e7c..e25c57ac129c9 100644
--- a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts
+++ b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts
@@ -186,7 +186,7 @@ export function loadInitial(
setState({
isSaveable: true,
sharingSavedObjectProps,
- filters,
+ filters: data.query.filterManager.getFilters(),
query: doc.state.query,
searchSessionId:
dashboardFeatureFlag.allowByValueEmbeddables &&
diff --git a/x-pack/plugins/lens/public/state_management/load_initial.test.tsx b/x-pack/plugins/lens/public/state_management/load_initial.test.tsx
index cc57a44fc21ca..9ae27a9c0073e 100644
--- a/x-pack/plugins/lens/public/state_management/load_initial.test.tsx
+++ b/x-pack/plugins/lens/public/state_management/load_initial.test.tsx
@@ -17,6 +17,7 @@ import { Location, History } from 'history';
import { act } from 'react-dom/test-utils';
import { LensEmbeddableInput } from '../embeddable';
import { loadInitial } from './lens_slice';
+import { Filter } from '@kbn/es-query';
const history = {
location: {
@@ -214,9 +215,16 @@ describe('Initializing the store', () => {
});
it('loads a document and uses query and filters if initial input is provided', async () => {
- const { store, deps } = await makeLensStore({ preloadedState });
+ const { store, deps } = makeLensStore({ preloadedState });
+
+ const mockFilters = 'some filters from the filter manager' as unknown as Filter[];
+
+ jest
+ .spyOn(deps.lensServices.data.query.filterManager, 'getFilters')
+ .mockReturnValue(mockFilters);
+
await act(async () => {
- await store.dispatch(loadInitial(defaultProps));
+ store.dispatch(loadInitial(defaultProps));
});
expect(deps.lensServices.attributeService.unwrapAttributes).toHaveBeenCalledWith({
@@ -233,6 +241,7 @@ describe('Initializing the store', () => {
query: 'kuery',
isLoading: false,
activeDatasourceId: 'testDatasource',
+ filters: mockFilters,
}),
});
});
From 9606cf17d1be171cd5418de8ac457955068eaaf7 Mon Sep 17 00:00:00 2001
From: Nicolas Chaulet
Date: Wed, 6 Apr 2022 09:43:22 -0400
Subject: [PATCH 6/8] [Fleet] Do not enable view agent dashboard for agent
policy without monitoring (#129509)
---
.../components/agent_dashboard_link.test.tsx | 47 +++++++++++++++++--
.../components/agent_dashboard_link.tsx | 27 +++++++++--
.../agents/agent_details_page/index.tsx | 2 +-
3 files changed, 68 insertions(+), 8 deletions(-)
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.test.tsx
index ef9e1251c40e2..3fc27cfbc9263 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.test.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.test.tsx
@@ -8,7 +8,7 @@
import React from 'react';
import { createFleetTestRendererMock } from '../../../../../../mock';
-import type { Agent } from '../../../../types';
+import type { Agent, AgentPolicy } from '../../../../types';
import { useGetPackageInfoByKey } from '../../../../../../hooks/use_request/epm';
import { AgentDashboardLink } from './agent_dashboard_link';
@@ -26,7 +26,7 @@ jest.mock('../../../../../../hooks/use_fleet_status', () => ({
jest.mock('../../../../../../hooks/use_request/epm');
describe('AgentDashboardLink', () => {
- it('should enable the button if elastic_agent package is installed', async () => {
+ it('should enable the button if elastic_agent package is installed and policy has monitoring enabled', async () => {
mockedUseGetPackageInfoByKey.mockReturnValue({
isLoading: false,
data: {
@@ -44,6 +44,11 @@ describe('AgentDashboardLink', () => {
id: 'agent-id-123',
} as unknown as Agent
}
+ agentPolicy={
+ {
+ monitoring_enabled: ['logs', 'metrics'],
+ } as unknown as AgentPolicy
+ }
/>
);
@@ -51,7 +56,7 @@ describe('AgentDashboardLink', () => {
expect(result.getByRole('link').hasAttribute('href')).toBeTruthy();
});
- it('should not enable the button if elastic_agent package is installed', async () => {
+ it('should not enable the button if elastic_agent package is not installed and policy has monitoring enabled', async () => {
mockedUseGetPackageInfoByKey.mockReturnValue({
isLoading: false,
data: {
@@ -69,6 +74,42 @@ describe('AgentDashboardLink', () => {
id: 'agent-id-123',
} as unknown as Agent
}
+ agentPolicy={
+ {
+ monitoring_enabled: ['logs', 'metrics'],
+ } as unknown as AgentPolicy
+ }
+ />
+ );
+
+ expect(result.queryByRole('link')).toBeNull();
+ expect(result.queryByRole('button')).not.toBeNull();
+ expect(result.getByRole('button').hasAttribute('disabled')).toBeTruthy();
+ });
+
+ it('should not enable the button if elastic_agent package is installed and policy do not have monitoring enabled', async () => {
+ mockedUseGetPackageInfoByKey.mockReturnValue({
+ isLoading: false,
+ data: {
+ item: {
+ status: 'installed',
+ },
+ },
+ } as ReturnType);
+ const testRenderer = createFleetTestRendererMock();
+
+ const result = testRenderer.render(
+
);
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.tsx
index 9449e5370b7cd..191fcea481bb5 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.tsx
@@ -10,7 +10,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { EuiButton, EuiToolTip } from '@elastic/eui';
import { useGetPackageInfoByKey, useKibanaLink } from '../../../../hooks';
-import type { Agent } from '../../../../types';
+import type { Agent, AgentPolicy } from '../../../../types';
import {
FLEET_ELASTIC_AGENT_PACKAGE,
FLEET_ELASTIC_AGENT_DETAILS_DASHBOARD_ID,
@@ -34,10 +34,14 @@ function useAgentDashboardLink(agent: Agent) {
export const AgentDashboardLink: React.FunctionComponent<{
agent: Agent;
-}> = ({ agent }) => {
+ agentPolicy?: AgentPolicy;
+}> = ({ agent, agentPolicy }) => {
const { isInstalled, link, isLoading } = useAgentDashboardLink(agent);
- const buttonArgs = !isInstalled || isLoading ? { disabled: true } : { href: link };
+ const isLogAndMetricsEnabled = agentPolicy?.monitoring_enabled?.length ?? 0 > 0;
+
+ const buttonArgs =
+ !isInstalled || isLoading || !isLogAndMetricsEnabled ? { disabled: true } : { href: link };
const button = (
@@ -48,12 +52,27 @@ export const AgentDashboardLink: React.FunctionComponent<{
);
+ if (!isLogAndMetricsEnabled) {
+ return (
+
+ }
+ >
+ {button}
+
+ );
+ }
+
if (!isInstalled) {
return (
}
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/index.tsx
index 91c84f1be4713..5654f4a18d1d3 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/index.tsx
@@ -124,7 +124,7 @@ export const AgentDetailsPage: React.FunctionComponent = () => {
)}
-
+
>
From 4a7084e0035cab583b5d1ad6130af6376eaa13ec Mon Sep 17 00:00:00 2001
From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com>
Date: Wed, 6 Apr 2022 10:28:38 -0400
Subject: [PATCH 7/8] [Security Solution][Endpoint] Fix Host Isolation
Exceptions summary card showing in Fleet with Basic license (#129408)
* Enable use of `useEndpointPrivileges()` from component rendered in fleet's context
* Fix Host Isolation exceptions showing up even if user does not have access
---
.../security_solution_start_dependencies.tsx | 28 ++++++++
.../endpoint/use_endpoint_privileges.ts | 9 ++-
.../index.tsx | 72 ++++++++++---------
.../endpoint_policy_edit_extension.tsx | 5 +-
.../with_security_context.tsx | 12 +++-
5 files changed, 85 insertions(+), 41 deletions(-)
create mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/security_solution_start_dependencies.tsx
diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/security_solution_start_dependencies.tsx b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/security_solution_start_dependencies.tsx
new file mode 100644
index 0000000000000..26298b0e13c58
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/security_solution_start_dependencies.tsx
@@ -0,0 +1,28 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useContext } from 'react';
+import { StartPlugins } from '../../../../types';
+
+/**
+ * For use with the Fleet UI extensions, where `useKibana().services.**` does not return the services
+ * provided to the Security Solution plugin.
+ */
+export const SecuritySolutionStartDependenciesContext = React.createContext<
+ undefined | Pick
+>(undefined);
+
+/**
+ * Hook used in `useEndpointPrivileges()` when that hook is being invoked from outside of
+ * security solution, as is the case with UI extensions that are rendered within the Fleet
+ * pages.
+ */
+export const useSecuritySolutionStartDependencies = ():
+ | undefined
+ | Pick => {
+ return useContext(SecuritySolutionStartDependenciesContext);
+};
diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts
index e6c7b9a5d0e95..dcd602290d85c 100644
--- a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts
+++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts
@@ -18,6 +18,7 @@ import {
getEndpointAuthzInitialState,
} from '../../../../../common/endpoint/service/authz';
import { FleetAuthz } from '../../../../../../fleet/common';
+import { useSecuritySolutionStartDependencies } from './security_solution_start_dependencies';
/**
* Retrieve the endpoint privileges for the current user.
@@ -27,7 +28,11 @@ import { FleetAuthz } from '../../../../../../fleet/common';
*/
export const useEndpointPrivileges = (): Immutable => {
const user = useCurrentUser();
- const fleetServices = useKibana().services.fleet;
+ const fleetServicesFromUseKibana = useKibana().services.fleet;
+ // The `fleetServicesFromPluginStart` will be defined when this hooks called from a component
+ // that is being rendered under the Fleet context (UI extensions). The `fleetServicesFromUseKibana`
+ // above will be `undefined` in this case.
+ const fleetServicesFromPluginStart = useSecuritySolutionStartDependencies()?.fleet;
const isMounted = useRef(true);
const licenseService = useLicense();
const [fleetCheckDone, setFleetCheckDone] = useState(false);
@@ -35,6 +40,8 @@ export const useEndpointPrivileges = (): Immutable => {
const [userRolesCheckDone, setUserRolesCheckDone] = useState(false);
const [userRoles, setUserRoles] = useState>([]);
+ const fleetServices = fleetServicesFromUseKibana ?? fleetServicesFromPluginStart;
+
const privileges = useMemo(() => {
const privilegeList: EndpointPrivileges = Object.freeze({
loading: !fleetCheckDone || !userRolesCheckDone || !user,
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/index.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/index.tsx
index 0da28d6ed7d1b..b6b91d8d64e4f 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/index.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/index.tsx
@@ -11,7 +11,6 @@ import { EuiSpacer } from '@elastic/eui';
import React, { memo, useMemo } from 'react';
import { useHttp } from '../../../../../../common/lib/kibana/hooks';
import { PackageCustomExtensionComponentProps } from '../../../../../../../../fleet/public';
-import { ReactQueryClientProvider } from '../../../../../../common/containers/query_client/query_client_provider';
import { FleetArtifactsCard } from './components/fleet_artifacts_card';
import {
getBlocklistsListPath,
@@ -23,6 +22,7 @@ import { TrustedAppsApiClient } from '../../../../trusted_apps/service/trusted_a
import { EventFiltersApiClient } from '../../../../event_filters/service/event_filters_api_client';
import { HostIsolationExceptionsApiClient } from '../../../../host_isolation_exceptions/host_isolation_exceptions_api_client';
import { BlocklistsApiClient } from '../../../../blocklist/services';
+import { useCanSeeHostIsolationExceptionsMenu } from '../../../../host_isolation_exceptions/view/hooks';
export const TRUSTED_APPS_LABELS = {
artifactsSummaryApiError: (error: string) =>
@@ -96,6 +96,8 @@ export const BLOCKLISTS_LABELS = {
export const EndpointPackageCustomExtension = memo(
(props) => {
const http = useHttp();
+ const canSeeHostIsolationExceptions = useCanSeeHostIsolationExceptionsMenu();
+
const trustedAppsApiClientInstance = useMemo(
() => TrustedAppsApiClient.getInstance(http),
[http]
@@ -115,39 +117,41 @@ export const EndpointPackageCustomExtension = memo
-
-
-
-
-
-
-
-
-
+
+
+
+ {canSeeHostIsolationExceptions && (
+ <>
+
+
+ >
+ )}
+
+
);
}
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_edit_extension.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_edit_extension.tsx
index 690cb2dc734bd..24aad4e9ed686 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_edit_extension.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_edit_extension.tsx
@@ -31,7 +31,6 @@ import {
policyDetailsForUpdate,
} from '../../store/policy_details/selectors';
-import { ReactQueryClientProvider } from '../../../../../common/containers/query_client/query_client_provider';
import { useUserPrivileges } from '../../../../../common/components/user_privileges';
import { FleetIntegrationArtifactsCard } from './endpoint_package_custom_extension/components/fleet_integration_artifacts_card';
import { BlocklistsApiClient } from '../../../blocklist/services';
@@ -137,10 +136,10 @@ export const TRUSTED_APPS_LABELS = {
export const EndpointPolicyEditExtension = memo(
({ policy, onChange }) => {
return (
-
+ <>
-
+ >
);
}
);
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context.tsx
index 5153fbb73ba71..06a099ed119ab 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/with_security_context.tsx
@@ -15,6 +15,8 @@ import { managementReducer } from '../../../../store/reducer';
import { managementMiddlewareFactory } from '../../../../store/middleware';
import { appReducer } from '../../../../../common/store/app';
import { ExperimentalFeaturesService } from '../../../../../common/experimental_features_service';
+import { SecuritySolutionStartDependenciesContext } from '../../../../../common/components/user_privileges/endpoint/security_solution_start_dependencies';
+import { ReactQueryClientProvider } from '../../../../../common/containers/query_client/query_client_provider';
type ComposeType = typeof compose;
declare global {
@@ -69,9 +71,13 @@ export const withSecurityContext = ({
return (
-
-
-
+
+
+
+
+
+
+
);
});
From d2be8b5231d22e0d0e7e8e4b66c3a5ea0b3ccf74 Mon Sep 17 00:00:00 2001
From: Max Kovalev
Date: Wed, 6 Apr 2022 18:20:56 +0300
Subject: [PATCH 8/8] [Maps] replace src/plugins/kibana_react
ExitFullScreenButton with src/plugins/shared_ux ExitFullScreenButton
(#128051)
* #125786 - replacing of usage exitFullScreen button from kibana_react to sharedUX plugin
* 125786 - changing the way of using SharedUX plugin in maps due to CI failure
* 125786 - correcting time values in tests
* 125786 - updating reference screenshots; reverting test
* 125786 - changed data subject name for full screen button test
* 125786 - updated test and baseline screenshots
* 125786 - updated screenshots
* 125786 - updated screenshots
* 125786 - running test for creating screenshoots on CI side
* 125786 - news screenshots; fix for ally test; revert sample_data.js
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
x-pack/plugins/maps/kibana.json | 3 +-
.../map_container/map_container.tsx | 8 ++----
x-pack/plugins/maps/public/kibana_services.ts | 1 +
x-pack/plugins/maps/public/plugin.ts | 2 ++
x-pack/plugins/maps/public/render_app.tsx | 26 ++++++++++--------
x-pack/plugins/maps/tsconfig.json | 1 +
x-pack/test/accessibility/apps/maps.ts | 2 +-
.../test/functional/apps/maps/sample_data.js | 9 ++++--
.../test/functional/page_objects/gis_page.ts | 10 +++----
.../screenshots/baseline/ecommerce_map.png | Bin 68442 -> 50514 bytes
.../screenshots/baseline/flights_map.png | Bin 104337 -> 47234 bytes
.../screenshots/baseline/web_logs_map.png | Bin 134984 -> 122717 bytes
12 files changed, 36 insertions(+), 26 deletions(-)
diff --git a/x-pack/plugins/maps/kibana.json b/x-pack/plugins/maps/kibana.json
index 5a2d1410c8ebc..a19f160127eb3 100644
--- a/x-pack/plugins/maps/kibana.json
+++ b/x-pack/plugins/maps/kibana.json
@@ -25,7 +25,8 @@
"mapsEms",
"savedObjects",
"share",
- "presentationUtil"
+ "presentationUtil",
+ "sharedUX"
],
"optionalPlugins": [
"cloud",
diff --git a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx
index 581460f318583..6a4162f65a7fb 100644
--- a/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx
+++ b/x-pack/plugins/maps/public/connected_components/map_container/map_container.tsx
@@ -15,14 +15,14 @@ import { Filter } from '@kbn/es-query';
import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public';
import { Observable } from 'rxjs';
import moment from 'moment';
+import { ExitFullScreenButton } from '@kbn/shared-ux-components';
import { MBMap } from '../mb_map';
import { RightSideControls } from '../right_side_controls';
import { Timeslider } from '../timeslider';
import { ToolbarOverlay } from '../toolbar_overlay';
import { EditLayerPanel } from '../edit_layer_panel';
import { AddLayerPanel } from '../add_layer_panel';
-import { ExitFullScreenButton } from '../../../../../../src/plugins/kibana_react/public';
-import { getCoreChrome, getData } from '../../kibana_services';
+import { getData } from '../../kibana_services';
import { RawValue } from '../../../common/constants';
import { FLYOUT_STATE } from '../../reducers/ui';
import { MapSettings } from '../../reducers/map';
@@ -207,9 +207,7 @@ export class MapContainer extends Component {
let exitFullScreenButton;
if (isFullScreen) {
- exitFullScreenButton = (
-
- );
+ exitFullScreenButton = ;
}
const shareAttributes = this.props.isSharable
? {
diff --git a/x-pack/plugins/maps/public/kibana_services.ts b/x-pack/plugins/maps/public/kibana_services.ts
index 8d7492829c40f..f2345d2102a12 100644
--- a/x-pack/plugins/maps/public/kibana_services.ts
+++ b/x-pack/plugins/maps/public/kibana_services.ts
@@ -65,6 +65,7 @@ export const getSecurityService = () => pluginsStart.security;
export const getSpacesApi = () => pluginsStart.spaces;
export const getTheme = () => coreStart.theme;
export const getUsageCollection = () => pluginsStart.usageCollection;
+export const getSharedUXPluginContext = () => pluginsStart.sharedUX;
// xpack.maps.* kibana.yml settings from this plugin
let mapAppConfig: MapsConfigType;
diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts
index 2aa661f2c4668..2ab91462cfe7e 100644
--- a/x-pack/plugins/maps/public/plugin.ts
+++ b/x-pack/plugins/maps/public/plugin.ts
@@ -79,6 +79,7 @@ import type { CloudSetup } from '../../cloud/public';
import type { LensPublicSetup } from '../../lens/public';
import { setupLensChoroplethChart } from './lens';
+import { SharedUXPluginStart } from '../../../../src/plugins/shared_ux/public';
export interface MapsPluginSetupDependencies {
cloud?: CloudSetup;
@@ -114,6 +115,7 @@ export interface MapsPluginStartDependencies {
spaces?: SpacesPluginStart;
mapsEms: MapsEmsPluginPublicStart;
usageCollection?: UsageCollectionSetup;
+ sharedUX: SharedUXPluginStart;
}
/**
diff --git a/x-pack/plugins/maps/public/render_app.tsx b/x-pack/plugins/maps/public/render_app.tsx
index aa5e1ee29833d..8a3ea87905512 100644
--- a/x-pack/plugins/maps/public/render_app.tsx
+++ b/x-pack/plugins/maps/public/render_app.tsx
@@ -10,6 +10,7 @@ import { render, unmountComponentAtNode } from 'react-dom';
import { Router, Switch, Route, Redirect, RouteComponentProps } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import type { AppMountParameters } from 'kibana/public';
+import { SharedUxServicesProvider } from '@kbn/shared-ux-services';
import { KibanaThemeProvider } from '../../../../src/plugins/kibana_react/public';
import {
getCoreChrome,
@@ -18,6 +19,7 @@ import {
getToasts,
getEmbeddableService,
getDocLinks,
+ getSharedUXPluginContext,
} from './kibana_services';
import {
createKbnUrlStateStorage,
@@ -94,17 +96,19 @@ export async function renderApp(
}
return (
-
+
+
+
);
}
diff --git a/x-pack/plugins/maps/tsconfig.json b/x-pack/plugins/maps/tsconfig.json
index 1c8951a6cae3a..5d5f4223fab9a 100644
--- a/x-pack/plugins/maps/tsconfig.json
+++ b/x-pack/plugins/maps/tsconfig.json
@@ -32,6 +32,7 @@
{ "path": "../../../src/plugins/usage_collection/tsconfig.json" },
{ "path": "../../../src/plugins/kibana_react/tsconfig.json" },
{ "path": "../../../src/plugins/kibana_utils/tsconfig.json" },
+ { "path": "../../../src/plugins/shared_ux/tsconfig.json" },
{ "path": "../cloud/tsconfig.json" },
{ "path": "../features/tsconfig.json" },
{ "path": "../lens/tsconfig.json" },
diff --git a/x-pack/test/accessibility/apps/maps.ts b/x-pack/test/accessibility/apps/maps.ts
index d38cf44f39fba..c5b824c330829 100644
--- a/x-pack/test/accessibility/apps/maps.ts
+++ b/x-pack/test/accessibility/apps/maps.ts
@@ -82,7 +82,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('displays exit full screen logo button', async () => {
- await testSubjects.click('exitFullScreenModeLogo');
+ await testSubjects.click('exitFullScreenModeButton');
await a11y.testAppSnapshot();
});
diff --git a/x-pack/test/functional/apps/maps/sample_data.js b/x-pack/test/functional/apps/maps/sample_data.js
index 5c0c4af46e2a9..35733e8d75ffb 100644
--- a/x-pack/test/functional/apps/maps/sample_data.js
+++ b/x-pack/test/functional/apps/maps/sample_data.js
@@ -91,9 +91,12 @@ export default function ({ getPageObjects, getService, updateBaselines }) {
[UI_SETTINGS.TIMEPICKER_QUICK_RANGES]: SAMPLE_DATA_RANGE,
});
//running the rest of the tests with limited roles
- await security.testUser.setRoles(['global_maps_all', 'kibana_sample_read'], {
- skipBrowserRefresh: true,
- });
+ await security.testUser.setRoles(
+ ['global_maps_all', 'geoall_data_writer', 'kibana_sample_read'],
+ {
+ skipBrowserRefresh: true,
+ }
+ );
});
after(async () => {
diff --git a/x-pack/test/functional/page_objects/gis_page.ts b/x-pack/test/functional/page_objects/gis_page.ts
index 33f28795277af..909a96706575b 100644
--- a/x-pack/test/functional/page_objects/gis_page.ts
+++ b/x-pack/test/functional/page_objects/gis_page.ts
@@ -64,7 +64,7 @@ export class GisPageObject extends FtrService {
this.log.debug(`enterFullScreen`);
await this.testSubjects.click('mapsFullScreenMode');
await this.retry.try(async () => {
- await this.testSubjects.exists('exitFullScreenModeLogo');
+ await this.testSubjects.exists('exitFullScreenModeButton');
});
await this.waitForLayersToLoad();
}
@@ -72,9 +72,9 @@ export class GisPageObject extends FtrService {
// TODO combine with dashboard full screen into a service
async existFullScreen() {
this.log.debug(`existFullScreen`);
- const isFullScreen = await this.testSubjects.exists('exitFullScreenModeLogo');
+ const isFullScreen = await this.testSubjects.exists('exitFullScreenModeButton');
if (isFullScreen) {
- await this.testSubjects.click('exitFullScreenModeLogo');
+ await this.testSubjects.click('exitFullScreenModeButton');
}
}
@@ -544,11 +544,11 @@ export class GisPageObject extends FtrService {
}
async exitFullScreenLogoButtonExists() {
- return await this.testSubjects.exists('exitFullScreenModeLogo');
+ return await this.testSubjects.exists('exitFullScreenModeButton');
}
async getExitFullScreenLogoButton() {
- return await this.testSubjects.find('exitFullScreenModeLogo');
+ return await this.testSubjects.find('exitFullScreenModeButton');
}
async clickExitFullScreenTextButton() {
diff --git a/x-pack/test/functional/screenshots/baseline/ecommerce_map.png b/x-pack/test/functional/screenshots/baseline/ecommerce_map.png
index 8b0e308b7ecb5feddf9f8b9ed75c0cf09267e3c7..df13f7d563bfe8052e4b6e07df8b06225756cab3 100644
GIT binary patch
literal 50514
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