diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/index.js b/x-pack/plugins/maps/public/connected_components/right_side_controls/attribution_control/index.ts
similarity index 64%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/index.js
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/attribution_control/index.ts
index 32e93465c3c489b..9c1dfee6e011141 100644
--- a/x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/index.js
+++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/attribution_control/index.ts
@@ -6,20 +6,17 @@
*/
import { connect } from 'react-redux';
-import { AttributionControl } from './view';
+import { AttributionControl } from './attribution_control';
import { getLayerList } from '../../../selectors/map_selectors';
import { getIsFullScreen } from '../../../selectors/ui_selectors';
+import { MapStoreState } from '../../../reducers/store';
-function mapStateToProps(state = {}) {
+function mapStateToProps(state: MapStoreState) {
return {
layerList: getLayerList(state),
isFullScreen: getIsFullScreen(state),
};
}
-function mapDispatchToProps() {
- return {};
-}
-
-const connectedViewControl = connect(mapStateToProps, mapDispatchToProps)(AttributionControl);
-export { connectedViewControl as AttributionControl };
+const connected = connect(mapStateToProps, {})(AttributionControl);
+export { connected as AttributionControl };
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/index.js b/x-pack/plugins/maps/public/connected_components/right_side_controls/index.ts
similarity index 60%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/index.js
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/index.ts
index d1f003ae4bc3d87..8b77726e5514dee 100644
--- a/x-pack/plugins/maps/public/connected_components/widget_overlay/index.js
+++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/index.ts
@@ -6,15 +6,15 @@
*/
import { connect } from 'react-redux';
-import { WidgetOverlay } from './widget_overlay';
-
+import { RightSideControls } from './right_side_controls';
import { getMapSettings } from '../../selectors/map_selectors';
+import { MapStoreState } from '../../reducers/store';
-function mapStateToProps(state = {}) {
+function mapStateToProps(state: MapStoreState) {
return {
settings: getMapSettings(state),
};
}
-const connectedWidgetOverlay = connect(mapStateToProps, null)(WidgetOverlay);
-export { connectedWidgetOverlay as WidgetOverlay };
+const connected = connect(mapStateToProps, {})(RightSideControls);
+export { connected as RightSideControls };
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/__snapshots__/layer_control.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/__snapshots__/layer_control.test.tsx.snap
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/__snapshots__/layer_control.test.tsx.snap
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/__snapshots__/layer_control.test.tsx.snap
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/_index.scss
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/_index.scss
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/_layer_control.scss b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/_layer_control.scss
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/_layer_control.scss
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/_layer_control.scss
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/index.ts b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/index.ts
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/index.ts
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/index.ts
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_control.test.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_control.test.tsx
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_control.test.tsx
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_control.test.tsx
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_control.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_control.tsx
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_control.tsx
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_control.tsx
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/__snapshots__/layer_toc.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/__snapshots__/layer_toc.test.tsx.snap
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/__snapshots__/layer_toc.test.tsx.snap
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/__snapshots__/layer_toc.test.tsx.snap
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/index.ts b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/index.ts
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/index.ts
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/index.ts
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/layer_toc.test.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/layer_toc.test.tsx
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/layer_toc.test.tsx
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/layer_toc.test.tsx
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/layer_toc.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/layer_toc.tsx
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/layer_toc.tsx
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/layer_toc.tsx
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/_toc_entry.scss b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/_toc_entry.scss
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/_toc_entry.scss
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/_toc_entry.scss
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/action_labels.ts b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/action_labels.ts
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/action_labels.ts
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/action_labels.ts
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.ts b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/index.ts
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.ts
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/index.ts
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry.test.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.test.tsx
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry.test.tsx
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.test.tsx
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.tsx
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry.tsx
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.tsx
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/index.ts b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/index.ts
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/index.ts
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/index.ts
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_button/index.ts b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_button/index.ts
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_button/index.ts
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_button/index.ts
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_button/toc_entry_button.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_button/toc_entry_button.tsx
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_button/toc_entry_button.tsx
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_button/toc_entry_button.tsx
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/_view_control.scss b/x-pack/plugins/maps/public/connected_components/right_side_controls/mouse_coordinates_control/_mouse_coordinates_control.scss
similarity index 100%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/_view_control.scss
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/mouse_coordinates_control/_mouse_coordinates_control.scss
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/index.js b/x-pack/plugins/maps/public/connected_components/right_side_controls/mouse_coordinates_control/index.ts
similarity index 61%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/index.js
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/mouse_coordinates_control/index.ts
index a3a7865b61cb654..fa094dd0d6b7ff1 100644
--- a/x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/index.js
+++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/mouse_coordinates_control/index.ts
@@ -6,15 +6,16 @@
*/
import { connect } from 'react-redux';
-import { ViewControl } from './view_control';
+import { MouseCoordinatesControl } from './mouse_coordinates_control';
import { getMouseCoordinates, getMapZoom } from '../../../selectors/map_selectors';
+import { MapStoreState } from '../../../reducers/store';
-function mapStateToProps(state = {}) {
+function mapStateToProps(state: MapStoreState) {
return {
mouseCoordinates: getMouseCoordinates(state),
zoom: getMapZoom(state),
};
}
-const connectedViewControl = connect(mapStateToProps, null)(ViewControl);
-export { connectedViewControl as ViewControl };
+const connected = connect(mapStateToProps, {})(MouseCoordinatesControl);
+export { connected as MouseCoordinatesControl };
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/view_control.js b/x-pack/plugins/maps/public/connected_components/right_side_controls/mouse_coordinates_control/mouse_coordinates_control.tsx
similarity index 87%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/view_control.js
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/mouse_coordinates_control/mouse_coordinates_control.tsx
index 409c6fd5ca44cbc..32c9f2f58ecf2e8 100644
--- a/x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/view_control.js
+++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/mouse_coordinates_control/mouse_coordinates_control.tsx
@@ -8,10 +8,18 @@
import _ from 'lodash';
import React, { Fragment } from 'react';
import { EuiText } from '@elastic/eui';
-import { DECIMAL_DEGREES_PRECISION } from '../../../../common/constants';
import { FormattedMessage } from '@kbn/i18n/react';
+import { DECIMAL_DEGREES_PRECISION } from '../../../../common/constants';
+
+export interface Props {
+ mouseCoordinates?: {
+ lat: number;
+ lon: number;
+ };
+ zoom: number;
+}
-export function ViewControl({ mouseCoordinates, zoom }) {
+export function MouseCoordinatesControl({ mouseCoordinates, zoom }: Props) {
let latLon;
if (mouseCoordinates) {
latLon = (
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/widget_overlay.js b/x-pack/plugins/maps/public/connected_components/right_side_controls/right_side_controls.tsx
similarity index 71%
rename from x-pack/plugins/maps/public/connected_components/widget_overlay/widget_overlay.js
rename to x-pack/plugins/maps/public/connected_components/right_side_controls/right_side_controls.tsx
index f7a362c79dcc87e..12f283597f42adf 100644
--- a/x-pack/plugins/maps/public/connected_components/widget_overlay/widget_overlay.js
+++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/right_side_controls.tsx
@@ -8,10 +8,15 @@
import React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { LayerControl } from './layer_control';
-import { ViewControl } from './view_control';
+import { MouseCoordinatesControl } from './mouse_coordinates_control';
import { AttributionControl } from './attribution_control';
+import { MapSettings } from '../../reducers/map';
-export function WidgetOverlay({ settings }) {
+export interface Props {
+ settings: MapSettings;
+}
+
+export function RightSideControls({ settings }: Props) {
return (
{!settings.hideLayerControl && }
- {!settings.hideViewControl && }
+
+ {!settings.hideViewControl && }
+
diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.js b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.js
index ce78ff0f4862575..7b8b3bdcceb4bc4 100644
--- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.js
+++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table.js
@@ -152,7 +152,10 @@ export class AnomaliesTableInternal extends Component {
const result = {
pageIndex: page && page.index !== undefined ? page.index : tableState.pageIndex,
pageSize: page && page.size !== undefined ? page.size : tableState.pageSize,
- sortField: sort && sort.field !== undefined ? sort.field : tableState.sortField,
+ sortField:
+ sort && sort.field !== undefined && typeof sort.field === 'string'
+ ? sort.field
+ : tableState.sortField,
sortDirection:
sort && sort.direction !== undefined ? sort.direction : tableState.sortDirection,
};
diff --git a/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts b/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts
index 6d70566af1a6462..935f44a657f7189 100644
--- a/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts
+++ b/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts
@@ -39,6 +39,7 @@ import { CombinedJob } from '../../../../common/types/anomaly_detection_jobs';
import { InfluencersFilterQuery } from '../../../../common/types/es_client';
import { ExplorerChartsData } from '../explorer_charts/explorer_charts_container_service';
import { mlJobService } from '../../services/job_service';
+import { TimeBucketsInterval } from '../../util/time_buckets';
// Memoize the data fetching methods.
// wrapWithLastRefreshArg() wraps any given function and preprends a `lastRefresh` argument
@@ -75,7 +76,7 @@ export interface LoadExplorerDataConfig {
noInfluencersConfigured: boolean;
selectedCells: AppStateSelectedCells | undefined;
selectedJobs: ExplorerJob[];
- swimlaneBucketInterval: any;
+ swimlaneBucketInterval: TimeBucketsInterval;
swimlaneLimit: number;
tableInterval: string;
tableSeverity: number;
diff --git a/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/state.ts b/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/state.ts
index faab658740a7068..2365e4e46890265 100644
--- a/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/state.ts
+++ b/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/state.ts
@@ -5,7 +5,6 @@
* 2.0.
*/
-import { Duration } from 'moment';
import { ML_RESULTS_INDEX_PATTERN } from '../../../../../common/constants/index_patterns';
import { Dictionary } from '../../../../../common/types/common';
@@ -25,6 +24,7 @@ import {
import { AnnotationsTable } from '../../../../../common/types/annotations';
import { SWIM_LANE_DEFAULT_PAGE_SIZE } from '../../explorer_constants';
import { InfluencersFilterQuery } from '../../../../../common/types/es_client';
+import { TimeBucketsInterval } from '../../../util/time_buckets';
export interface ExplorerState {
overallAnnotations: AnnotationsTable;
@@ -46,7 +46,7 @@ export interface ExplorerState {
queryString: string;
selectedCells: AppStateSelectedCells | undefined;
selectedJobs: ExplorerJob[] | null;
- swimlaneBucketInterval: Duration | undefined;
+ swimlaneBucketInterval: TimeBucketsInterval | undefined;
swimlaneContainerWidth: number;
tableData: AnomaliesTableData;
tableQueryString: string;
diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.ts
index 5995224ef325489..21d8413f1a70400 100644
--- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.ts
+++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.ts
@@ -106,6 +106,8 @@ function findFieldsInQuery(obj: object) {
if (isPopulatedObject(val)) {
fields.push(key);
fields.push(...findFieldsInQuery(val));
+ } else if (typeof val === 'string') {
+ fields.push(val);
} else {
fields.push(key);
}
diff --git a/x-pack/plugins/ml/public/application/services/anomaly_timeline_service.ts b/x-pack/plugins/ml/public/application/services/anomaly_timeline_service.ts
index 1521f62ac588d96..54d9626edf26c0e 100644
--- a/x-pack/plugins/ml/public/application/services/anomaly_timeline_service.ts
+++ b/x-pack/plugins/ml/public/application/services/anomaly_timeline_service.ts
@@ -11,7 +11,12 @@ import {
TimeRange,
UI_SETTINGS,
} from '../../../../../../src/plugins/data/public';
-import { getBoundsRoundedToInterval, TimeBuckets, TimeRangeBounds } from '../util/time_buckets';
+import {
+ getBoundsRoundedToInterval,
+ TimeBuckets,
+ TimeBucketsInterval,
+ TimeRangeBounds,
+} from '../util/time_buckets';
import {
ExplorerJob,
OverallSwimlaneData,
@@ -92,9 +97,10 @@ export class AnomalyTimelineService {
*/
public async loadOverallData(
selectedJobs: ExplorerJob[],
- chartWidth: number
+ chartWidth?: number,
+ bucketInterval?: TimeBucketsInterval
): Promise {
- const interval = this.getSwimlaneBucketInterval(selectedJobs, chartWidth);
+ const interval = bucketInterval ?? this.getSwimlaneBucketInterval(selectedJobs, chartWidth!);
if (!selectedJobs || !selectedJobs.length) {
throw new Error('Explorer jobs collection is required');
@@ -129,9 +135,6 @@ export class AnomalyTimelineService {
interval.asSeconds()
);
- // eslint-disable-next-line no-console
- console.log('Explorer overall swim lane data set:', overallSwimlaneData);
-
return overallSwimlaneData;
}
@@ -156,8 +159,9 @@ export class AnomalyTimelineService {
swimlaneLimit: number,
perPage: number,
fromPage: number,
- swimlaneContainerWidth: number,
- influencersFilterQuery?: any
+ swimlaneContainerWidth?: number,
+ influencersFilterQuery?: any,
+ bucketInterval?: TimeBucketsInterval
): Promise {
const timefilterBounds = this.getTimeBounds();
@@ -165,10 +169,8 @@ export class AnomalyTimelineService {
throw new Error('timeRangeSelectorEnabled has to be enabled');
}
- const swimlaneBucketInterval = this.getSwimlaneBucketInterval(
- selectedJobs,
- swimlaneContainerWidth
- );
+ const swimlaneBucketInterval =
+ bucketInterval ?? this.getSwimlaneBucketInterval(selectedJobs, swimlaneContainerWidth!);
const searchBounds = getBoundsRoundedToInterval(
timefilterBounds,
@@ -222,8 +224,6 @@ export class AnomalyTimelineService {
viewBySwimlaneFieldName,
swimlaneBucketInterval.asSeconds()
);
- // eslint-disable-next-line no-console
- console.log('Explorer view by swim lane data set:', viewBySwimlaneData);
return viewBySwimlaneData;
}
diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js
index 06a0f7e17e16494..8e5bf249ae2831d 100644
--- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js
+++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js
@@ -26,9 +26,11 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
+ EuiIcon,
EuiSpacer,
EuiPanel,
EuiTitle,
+ EuiToolTip,
EuiAccordion,
EuiBadge,
} from '@elastic/eui';
@@ -1259,9 +1261,21 @@ export class TimeSeriesExplorer extends React.Component {
+
+ {i18n.translate('xpack.ml.timeSeriesExplorer.intervalLabel', {
+ defaultMessage: 'Interval',
+ })}
+
+
+
+ }
>
diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.ts
index 703851f3fe9b610..b5f149af205e3dd 100644
--- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.ts
+++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.ts
@@ -14,13 +14,11 @@ import { MlStartDependencies } from '../../plugin';
import { UI_SETTINGS } from '../../../../../../src/plugins/data/public';
import {
AppStateSelectedCells,
- ExplorerJob,
getSelectionInfluencers,
getSelectionJobIds,
getSelectionTimeRange,
} from '../../application/explorer/explorer_utils';
import { OVERALL_LABEL, SWIMLANE_TYPE } from '../../application/explorer/explorer_constants';
-import { parseInterval } from '../../../common/util/parse_interval';
import {
AnomalyChartsEmbeddableInput,
AnomalyChartsEmbeddableOutput,
@@ -76,8 +74,8 @@ export function useAnomalyChartsInputResolver(
.pipe(
tap(setIsLoading.bind(null, true)),
debounceTime(FETCH_RESULTS_DEBOUNCE_MS),
- switchMap(([jobs, input, embeddableContainerWidth, severityValue]) => {
- if (!jobs) {
+ switchMap(([explorerJobs, input, embeddableContainerWidth, severityValue]) => {
+ if (!explorerJobs) {
// couldn't load the list of jobs
return of(undefined);
}
@@ -88,15 +86,6 @@ export function useAnomalyChartsInputResolver(
anomalyExplorerService.setTimeRange(timeRangeInput);
- const explorerJobs: ExplorerJob[] = jobs.map((job) => {
- const bucketSpan = parseInterval(job.analysis_config.bucket_span);
- return {
- id: job.job_id,
- selected: true,
- bucketSpanSeconds: bucketSpan!.asSeconds(),
- };
- });
-
let influencersFilterQuery: InfluencersFilterQuery;
try {
influencersFilterQuery = processFilters(filters, query);
diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.test.ts b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.test.ts
index 4d2e2406376e270..01b1e3acf7f958c 100644
--- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.test.ts
+++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.test.ts
@@ -55,6 +55,11 @@ describe('useSwimlaneInputResolver', () => {
points: [],
})
),
+ getSwimlaneBucketInterval: jest.fn(() => {
+ return {
+ asSeconds: jest.fn(() => 900),
+ };
+ }),
},
anomalyDetectorService: {
getJobs$: jest.fn((jobId: string[]) => {
diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.ts b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.ts
index 4574c7e859c08c2..8b0c89bbd16b725 100644
--- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.ts
+++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.ts
@@ -12,6 +12,8 @@ import {
debounceTime,
distinctUntilChanged,
map,
+ pluck,
+ shareReplay,
skipWhile,
startWith,
switchMap,
@@ -27,8 +29,7 @@ import {
SwimlaneType,
} from '../../application/explorer/explorer_constants';
import { UI_SETTINGS } from '../../../../../../src/plugins/data/public';
-import { ExplorerJob, OverallSwimlaneData } from '../../application/explorer/explorer_utils';
-import { parseInterval } from '../../../common/util/parse_interval';
+import { OverallSwimlaneData } from '../../application/explorer/explorer_utils';
import { isViewBySwimLaneData } from '../../application/explorer/swimlane_container';
import { ViewMode } from '../../../../../../src/plugins/embeddable/public';
import {
@@ -43,7 +44,7 @@ import { getJobsObservable } from '../common/get_jobs_observable';
const FETCH_RESULTS_DEBOUNCE_MS = 500;
export function useSwimlaneInputResolver(
- embeddableInput: Observable,
+ embeddableInput$: Observable,
onInputChange: (output: Partial) => void,
refresh: Observable,
services: [CoreStart, MlStartDependencies, AnomalySwimlaneServices],
@@ -67,6 +68,30 @@ export function useSwimlaneInputResolver(
const [isLoading, setIsLoading] = useState(false);
const chartWidth$ = useMemo(() => new Subject(), []);
+
+ const selectedJobs$ = useMemo(() => {
+ return getJobsObservable(embeddableInput$, anomalyDetectorService, setError).pipe(
+ shareReplay(1)
+ );
+ }, []);
+
+ const bucketInterval$ = useMemo(() => {
+ return combineLatest([
+ selectedJobs$,
+ chartWidth$,
+ embeddableInput$.pipe(pluck('timeRange')),
+ ]).pipe(
+ skipWhile(([jobs, width]) => !Array.isArray(jobs) || !width),
+ tap(([, , timeRange]) => {
+ anomalyTimelineService.setTimeRange(timeRange);
+ }),
+ map(([jobs, width]) => anomalyTimelineService.getSwimlaneBucketInterval(jobs!, width)),
+ distinctUntilChanged((prev, curr) => {
+ return prev.asSeconds() === curr.asSeconds();
+ })
+ );
+ }, []);
+
const fromPage$ = useMemo(() => new Subject(), []);
const perPage$ = useMemo(() => new Subject(), []);
@@ -81,9 +106,9 @@ export function useSwimlaneInputResolver(
useEffect(() => {
const subscription = combineLatest([
- getJobsObservable(embeddableInput, anomalyDetectorService, setError),
- embeddableInput,
- chartWidth$.pipe(skipWhile((v) => !v)),
+ selectedJobs$,
+ embeddableInput$,
+ bucketInterval$,
fromPage$,
perPage$.pipe(
startWith(undefined),
@@ -97,8 +122,8 @@ export function useSwimlaneInputResolver(
.pipe(
tap(setIsLoading.bind(null, true)),
debounceTime(FETCH_RESULTS_DEBOUNCE_MS),
- switchMap(([jobs, input, swimlaneContainerWidth, fromPageInput, perPageFromState]) => {
- if (!jobs) {
+ switchMap(([explorerJobs, input, bucketInterval, fromPageInput, perPageFromState]) => {
+ if (!explorerJobs) {
// couldn't load the list of jobs
return of(undefined);
}
@@ -107,27 +132,15 @@ export function useSwimlaneInputResolver(
viewBy,
swimlaneType: swimlaneTypeInput,
perPage: perPageInput,
- timeRange,
filters,
query,
viewMode,
} = input;
- anomalyTimelineService.setTimeRange(timeRange);
-
if (!swimlaneType) {
setSwimlaneType(swimlaneTypeInput);
}
- const explorerJobs: ExplorerJob[] = jobs.map((job) => {
- const bucketSpan = parseInterval(job.analysis_config.bucket_span);
- return {
- id: job.job_id,
- selected: true,
- bucketSpanSeconds: bucketSpan!.asSeconds(),
- };
- });
-
let appliedFilters: any;
try {
appliedFilters = processFilters(filters, query, CONTROLLED_BY_SWIM_LANE_FILTER);
@@ -138,7 +151,7 @@ export function useSwimlaneInputResolver(
}
return from(
- anomalyTimelineService.loadOverallData(explorerJobs, swimlaneContainerWidth)
+ anomalyTimelineService.loadOverallData(explorerJobs, undefined, bucketInterval)
).pipe(
switchMap((overallSwimlaneData) => {
const { earliest, latest } = overallSwimlaneData;
@@ -165,8 +178,9 @@ export function useSwimlaneInputResolver(
: ANOMALY_SWIM_LANE_HARD_LIMIT,
perPageFromState ?? perPageInput ?? SWIM_LANE_DEFAULT_PAGE_SIZE,
fromPageInput,
- swimlaneContainerWidth,
- appliedFilters
+ undefined,
+ appliedFilters,
+ bucketInterval
)
).pipe(
map((viewBySwimlaneData) => {
diff --git a/x-pack/plugins/ml/public/embeddables/common/get_jobs_observable.ts b/x-pack/plugins/ml/public/embeddables/common/get_jobs_observable.ts
index 6bdec30340b764e..451eb95b4f801f4 100644
--- a/x-pack/plugins/ml/public/embeddables/common/get_jobs_observable.ts
+++ b/x-pack/plugins/ml/public/embeddables/common/get_jobs_observable.ts
@@ -6,10 +6,12 @@
*/
import { Observable, of } from 'rxjs';
-import { catchError, distinctUntilChanged, pluck, switchMap } from 'rxjs/operators';
+import { catchError, distinctUntilChanged, map, pluck, switchMap } from 'rxjs/operators';
import { isEqual } from 'lodash';
import { AnomalyChartsEmbeddableInput, AnomalySwimlaneEmbeddableInput } from '../types';
import { AnomalyDetectorService } from '../../application/services/anomaly_detector_service';
+import { ExplorerJob } from '../../application/explorer/explorer_utils';
+import { parseInterval } from '../../../common/util/parse_interval';
export function getJobsObservable(
embeddableInput: Observable,
@@ -20,6 +22,17 @@ export function getJobsObservable(
pluck('jobIds'),
distinctUntilChanged(isEqual),
switchMap((jobsIds) => anomalyDetectorService.getJobs$(jobsIds)),
+ map((jobs) => {
+ const explorerJobs: ExplorerJob[] = jobs.map((job) => {
+ const bucketSpan = parseInterval(job.analysis_config.bucket_span);
+ return {
+ id: job.job_id,
+ selected: true,
+ bucketSpanSeconds: bucketSpan!.asSeconds(),
+ };
+ });
+ return explorerJobs;
+ }),
catchError((e) => {
setErrorHandler(e.body ?? e);
return of(undefined);
diff --git a/x-pack/plugins/observability/public/components/app/section/metrics/metric_with_sparkline.tsx b/x-pack/plugins/observability/public/components/app/section/metrics/metric_with_sparkline.tsx
index 3cb61f85d57f0e1..828038fd754361a 100644
--- a/x-pack/plugins/observability/public/components/app/section/metrics/metric_with_sparkline.tsx
+++ b/x-pack/plugins/observability/public/components/app/section/metrics/metric_with_sparkline.tsx
@@ -6,7 +6,7 @@
*/
import { Chart, Settings, AreaSeries } from '@elastic/charts';
-import { EuiIcon, EuiTextColor } from '@elastic/eui';
+import { EuiFlexItem, EuiFlexGroup, EuiIcon, EuiTextColor } from '@elastic/eui';
import React, { useContext } from 'react';
import {
EUI_CHARTS_THEME_DARK,
@@ -43,19 +43,22 @@ export function MetricWithSparkline({ id, formatter, value, timeseries, color }:
);
}
return (
- <>
-
-
-
-
-
- {formatter(value)}
- >
+
+
+
+
+
+
+
+
+ {formatter(value)}
+
+
);
}
diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx
index 530b8dee3a4d20e..8d3060792857e35 100644
--- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx
+++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/filter_expanded.test.tsx
@@ -8,12 +8,13 @@
import React from 'react';
import { fireEvent, screen } from '@testing-library/react';
import { FilterExpanded } from './filter_expanded';
-import { mockUrlStorage, mockUseValuesList, render } from '../../rtl_helpers';
+import { mockAppIndexPattern, mockUrlStorage, mockUseValuesList, render } from '../../rtl_helpers';
import { USER_AGENT_NAME } from '../../configurations/constants/elasticsearch_fieldnames';
describe('FilterExpanded', function () {
it('should render properly', async function () {
mockUrlStorage({ filters: [{ field: USER_AGENT_NAME, values: ['Chrome'] }] });
+ mockAppIndexPattern();
render(
field === fd);
- const displayValues = (values || []).filter((opt) =>
- opt.toLowerCase().includes(value.toLowerCase())
- );
+ const displayValues = values.filter((opt) => opt.toLowerCase().includes(value.toLowerCase()));
return (
@@ -60,50 +56,70 @@ export function FilterExpanded({ seriesId, field, label, goBack, nestedField, is
{
setValue(evt.target.value);
}}
+ placeholder={i18n.translate('xpack.observability.filters.expanded.search', {
+ defaultMessage: 'Search for {label}',
+ values: { label },
+ })}
/>
- {loading && (
-
-
-
- )}
- {displayValues.map((opt) => (
-
-
- {isNegated !== false && (
+
+ {displayValues.map((opt) => (
+
+
+ {isNegated !== false && (
+
+ )}
- )}
-
-
-
-
- ))}
+
+
+
+ ))}
+
);
}
+const ListWrapper = euiStyled.div`
+ height: 400px;
+ overflow-y: auto;
+ &::-webkit-scrollbar {
+ height: ${({ theme }) => theme.eui.euiScrollBar};
+ width: ${({ theme }) => theme.eui.euiScrollBar};
+ }
+ &::-webkit-scrollbar-thumb {
+ background-clip: content-box;
+ background-color: ${({ theme }) => rgba(theme.eui.euiColorDarkShade, 0.5)};
+ border: ${({ theme }) => theme.eui.euiScrollBarCorner} solid transparent;
+ }
+ &::-webkit-scrollbar-corner,
+ &::-webkit-scrollbar-track {
+ background-color: transparent;
+ }
+`;
+
const Wrapper = styled.div`
- max-width: 400px;
+ width: 400px;
`;
diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx
index 88cb53826341938..2d82aca658ec3a4 100644
--- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx
+++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/series_filter.tsx
@@ -119,7 +119,7 @@ export function SeriesFilter({ series, isNew, seriesId, defaultFilters = [] }: P
button={button}
isOpen={isPopoverVisible}
closePopover={closePopover}
- anchorPosition="leftCenter"
+ anchorPosition={isNew ? 'leftCenter' : 'rightCenter'}
>
{!selectedField ? mainPanel : childPanel}
diff --git a/x-pack/plugins/observability/public/hooks/use_es_search.ts b/x-pack/plugins/observability/public/hooks/use_es_search.ts
new file mode 100644
index 000000000000000..b6ee4a63823b1d0
--- /dev/null
+++ b/x-pack/plugins/observability/public/hooks/use_es_search.ts
@@ -0,0 +1,49 @@
+/*
+ * 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 { estypes } from '@elastic/elasticsearch';
+import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
+import { ESSearchResponse } from '../../../../../typings/elasticsearch';
+import { useKibana } from '../../../../../src/plugins/kibana_react/public';
+import { isCompleteResponse } from '../../../../../src/plugins/data/common';
+import { useFetcher } from './use_fetcher';
+
+export const useEsSearch = (
+ params: TParams,
+ fnDeps: any[]
+) => {
+ const {
+ services: { data },
+ } = useKibana<{ data: DataPublicPluginStart }>();
+
+ const { data: response = {}, loading } = useFetcher(() => {
+ return new Promise((resolve) => {
+ const search$ = data.search
+ .search({
+ params,
+ })
+ .subscribe({
+ next: (result) => {
+ if (isCompleteResponse(result)) {
+ // Final result
+ resolve(result);
+ search$.unsubscribe();
+ }
+ },
+ });
+ });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [...fnDeps]);
+
+ const { rawResponse } = response as any;
+
+ return { data: rawResponse as ESSearchResponse, loading };
+};
+
+export function createEsParams(params: T): T {
+ return params;
+}
diff --git a/x-pack/plugins/observability/public/hooks/use_values_list.ts b/x-pack/plugins/observability/public/hooks/use_values_list.ts
index e17f515ed6cb9eb..147a66f3d505ec7 100644
--- a/x-pack/plugins/observability/public/hooks/use_values_list.ts
+++ b/x-pack/plugins/observability/public/hooks/use_values_list.ts
@@ -5,11 +5,12 @@
* 2.0.
*/
+import { capitalize, merge } from 'lodash';
+import { useEffect, useState } from 'react';
+import { useDebounce } from 'react-use';
import { IndexPattern } from '../../../../../src/plugins/data/common';
-import { useKibana } from '../../../../../src/plugins/kibana_react/public';
-import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
-import { useFetcher } from './use_fetcher';
import { ESFilter } from '../../../../../typings/elasticsearch';
+import { createEsParams, useEsSearch } from './use_es_search';
export interface Props {
sourceField: string;
@@ -17,6 +18,7 @@ export interface Props {
indexPattern: IndexPattern;
filters?: ESFilter[];
time?: { from: string; to: string };
+ keepHistory?: boolean;
}
export const useValuesList = ({
@@ -25,38 +27,83 @@ export const useValuesList = ({
query = '',
filters,
time,
+ keepHistory,
}: Props): { values: string[]; loading?: boolean } => {
- const {
- services: { data },
- } = useKibana<{ data: DataPublicPluginStart }>();
+ const [debouncedQuery, setDebounceQuery] = useState(query);
+ const [values, setValues] = useState([]);
const { from, to } = time ?? {};
- const { data: values, loading } = useFetcher(() => {
- if (!sourceField || !indexPattern) {
- return [];
+ let includeClause = '';
+
+ if (query) {
+ if (query[0].toLowerCase() === query[0]) {
+ // if first letter is lowercase we also add the capitalize option
+ includeClause = `(${query}|${capitalize(query)}).*`;
+ } else {
+ // otherwise we add lowercase option prefix
+ includeClause = `(${query}|${query.toLowerCase()}).*`;
}
- return data.autocomplete.getValueSuggestions({
- indexPattern,
- query: query || '',
- useTimeRange: !(from && to),
- field: indexPattern.getFieldByName(sourceField)!,
- boolFilter:
- from && to
- ? [
- ...(filters || []),
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- },
- },
- },
- ]
- : filters || [],
- });
- }, [query, sourceField, data.autocomplete, indexPattern, from, to, filters]);
-
- return { values: values as string[], loading };
+ }
+
+ useDebounce(
+ () => {
+ setDebounceQuery(query);
+ },
+ 350,
+ [query]
+ );
+
+ const { data, loading } = useEsSearch(
+ createEsParams({
+ index: indexPattern.title,
+ body: {
+ query: {
+ bool: {
+ filter: [
+ ...(filters ?? []),
+ ...(from && to
+ ? [
+ {
+ range: {
+ '@timestamp': {
+ gte: from,
+ lte: to,
+ },
+ },
+ },
+ ]
+ : []),
+ ],
+ },
+ },
+ size: 0,
+ aggs: {
+ values: {
+ terms: {
+ field: sourceField,
+ size: 100,
+ ...(query ? { include: includeClause } : {}),
+ },
+ },
+ },
+ },
+ }),
+ [debouncedQuery, from, to]
+ );
+
+ useEffect(() => {
+ const newValues =
+ data?.aggregations?.values.buckets.map(({ key: value }) => value as string) ?? [];
+
+ if (keepHistory) {
+ setValues((prevState) => {
+ return merge(newValues, prevState);
+ });
+ } else {
+ setValues(newValues);
+ }
+ }, [data, keepHistory, loading]);
+
+ return { values, loading };
};
diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/repository_add.helpers.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/repository_add.helpers.ts
index e53c19f00d1b9c0..b369b20c122ebe7 100644
--- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/repository_add.helpers.ts
+++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/repository_add.helpers.ts
@@ -71,6 +71,13 @@ type TestSubjects =
| 'compressToggle'
| 'fsRepositoryType'
| 'locationInput'
+ | 'clientInput'
+ | 'containerInput'
+ | 'basePathInput'
+ | 'bucketInput'
+ | 'pathInput'
+ | 'uriInput'
+ | 'bufferSizeInput'
| 'maxRestoreBytesInput'
| 'maxSnapshotBytesInput'
| 'nameInput'
diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts
index 9864b18c4b8cb7a..85d438fc5f3ae1f 100644
--- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts
+++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts
@@ -193,7 +193,14 @@ describe('', () => {
});
describe('form payload & api errors', () => {
- const repository = getRepository();
+ const fsRepository = getRepository({
+ settings: {
+ chunkSize: '10mb',
+ location: '/tmp/es-backups',
+ maxSnapshotBytesPerSec: '1g',
+ maxRestoreBytesPerSec: '1g',
+ },
+ });
beforeEach(async () => {
httpRequestsMockHelpers.setLoadRepositoryTypesResponse(repositoryTypes);
@@ -202,33 +209,237 @@ describe('', () => {
});
describe('not source only', () => {
- beforeEach(() => {
+ test('should send the correct payload for FS repository', async () => {
+ const { form, actions, component } = testBed;
+
// Fill step 1 required fields and go to step 2
- testBed.form.setInputValue('nameInput', repository.name);
- testBed.actions.selectRepositoryType(repository.type);
- testBed.actions.clickNextButton();
+ form.setInputValue('nameInput', fsRepository.name);
+ actions.selectRepositoryType(fsRepository.type);
+ actions.clickNextButton();
+
+ // Fill step 2
+ form.setInputValue('locationInput', fsRepository.settings.location);
+ form.toggleEuiSwitch('compressToggle');
+ form.setInputValue('chunkSizeInput', fsRepository.settings.chunkSize);
+ form.setInputValue('maxSnapshotBytesInput', fsRepository.settings.maxSnapshotBytesPerSec);
+ form.setInputValue('maxRestoreBytesInput', fsRepository.settings.maxRestoreBytesPerSec);
+ form.toggleEuiSwitch('readOnlyToggle');
+
+ await act(async () => {
+ actions.clickSubmitButton();
+ });
+
+ component.update();
+
+ const latestRequest = server.requests[server.requests.length - 1];
+
+ expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual({
+ name: fsRepository.name,
+ type: fsRepository.type,
+ settings: {
+ ...fsRepository.settings,
+ compress: true,
+ readonly: true,
+ },
+ });
});
- test('should send the correct payload', async () => {
- const { form, actions } = testBed;
+ test('should send the correct payload for Azure repository', async () => {
+ const azureRepository = getRepository({
+ type: 'azure',
+ settings: {
+ chunkSize: '10mb',
+ maxSnapshotBytesPerSec: '1g',
+ maxRestoreBytesPerSec: '1g',
+ client: 'client',
+ container: 'container',
+ basePath: 'path',
+ },
+ });
+
+ const { form, actions, component } = testBed;
+
+ // Fill step 1 required fields and go to step 2
+ form.setInputValue('nameInput', azureRepository.name);
+ actions.selectRepositoryType(azureRepository.type);
+ actions.clickNextButton();
// Fill step 2
- form.setInputValue('locationInput', repository.settings.location);
+ form.setInputValue('clientInput', azureRepository.settings.client);
+ form.setInputValue('containerInput', azureRepository.settings.container);
+ form.setInputValue('basePathInput', azureRepository.settings.basePath);
form.toggleEuiSwitch('compressToggle');
+ form.setInputValue('chunkSizeInput', azureRepository.settings.chunkSize);
+ form.setInputValue(
+ 'maxSnapshotBytesInput',
+ azureRepository.settings.maxSnapshotBytesPerSec
+ );
+ form.setInputValue('maxRestoreBytesInput', azureRepository.settings.maxRestoreBytesPerSec);
+ form.toggleEuiSwitch('readOnlyToggle');
await act(async () => {
actions.clickSubmitButton();
- await nextTick();
});
+ component.update();
+
const latestRequest = server.requests[server.requests.length - 1];
expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual({
- name: repository.name,
- type: repository.type,
+ name: azureRepository.name,
+ type: azureRepository.type,
settings: {
- location: repository.settings.location,
- compress: true,
+ ...azureRepository.settings,
+ compress: false,
+ readonly: true,
+ },
+ });
+ });
+
+ test('should send the correct payload for GCS repository', async () => {
+ const gcsRepository = getRepository({
+ type: 'gcs',
+ settings: {
+ chunkSize: '10mb',
+ maxSnapshotBytesPerSec: '1g',
+ maxRestoreBytesPerSec: '1g',
+ client: 'test_client',
+ bucket: 'test_bucket',
+ basePath: 'test_path',
+ },
+ });
+
+ const { form, actions, component } = testBed;
+
+ // Fill step 1 required fields and go to step 2
+ form.setInputValue('nameInput', gcsRepository.name);
+ actions.selectRepositoryType(gcsRepository.type);
+ actions.clickNextButton();
+
+ // Fill step 2
+ form.setInputValue('clientInput', gcsRepository.settings.client);
+ form.setInputValue('bucketInput', gcsRepository.settings.bucket);
+ form.setInputValue('basePathInput', gcsRepository.settings.basePath);
+ form.toggleEuiSwitch('compressToggle');
+ form.setInputValue('chunkSizeInput', gcsRepository.settings.chunkSize);
+ form.setInputValue('maxSnapshotBytesInput', gcsRepository.settings.maxSnapshotBytesPerSec);
+ form.setInputValue('maxRestoreBytesInput', gcsRepository.settings.maxRestoreBytesPerSec);
+ form.toggleEuiSwitch('readOnlyToggle');
+
+ await act(async () => {
+ actions.clickSubmitButton();
+ });
+
+ component.update();
+
+ const latestRequest = server.requests[server.requests.length - 1];
+
+ expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual({
+ name: gcsRepository.name,
+ type: gcsRepository.type,
+ settings: {
+ ...gcsRepository.settings,
+ compress: false,
+ readonly: true,
+ },
+ });
+ });
+
+ test('should send the correct payload for HDFS repository', async () => {
+ const hdfsRepository = getRepository({
+ type: 'hdfs',
+ settings: {
+ uri: 'uri',
+ path: 'test_path',
+ chunkSize: '10mb',
+ maxSnapshotBytesPerSec: '1g',
+ maxRestoreBytesPerSec: '1g',
+ },
+ });
+
+ const { form, actions, component } = testBed;
+
+ // Fill step 1 required fields and go to step 2
+ form.setInputValue('nameInput', hdfsRepository.name);
+ actions.selectRepositoryType(hdfsRepository.type);
+ actions.clickNextButton();
+
+ // Fill step 2
+ form.setInputValue('uriInput', hdfsRepository.settings.uri);
+ form.setInputValue('pathInput', hdfsRepository.settings.path);
+ form.toggleEuiSwitch('compressToggle');
+ form.setInputValue('chunkSizeInput', hdfsRepository.settings.chunkSize);
+ form.setInputValue('maxSnapshotBytesInput', hdfsRepository.settings.maxSnapshotBytesPerSec);
+ form.setInputValue('maxRestoreBytesInput', hdfsRepository.settings.maxRestoreBytesPerSec);
+ form.toggleEuiSwitch('readOnlyToggle');
+
+ await act(async () => {
+ actions.clickSubmitButton();
+ });
+
+ component.update();
+
+ const latestRequest = server.requests[server.requests.length - 1];
+
+ expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual({
+ name: hdfsRepository.name,
+ type: hdfsRepository.type,
+ settings: {
+ ...hdfsRepository.settings,
+ uri: `hdfs://${hdfsRepository.settings.uri}`,
+ compress: false,
+ readonly: true,
+ },
+ });
+ });
+
+ test('should send the correct payload for S3 repository', async () => {
+ const { form, actions, component } = testBed;
+
+ const s3Repository = getRepository({
+ type: 's3',
+ settings: {
+ bucket: 'test_bucket',
+ client: 'test_client',
+ basePath: 'test_path',
+ bufferSize: '1g',
+ chunkSize: '10mb',
+ maxSnapshotBytesPerSec: '1g',
+ maxRestoreBytesPerSec: '1g',
+ },
+ });
+
+ // Fill step 1 required fields and go to step 2
+ form.setInputValue('nameInput', s3Repository.name);
+ actions.selectRepositoryType(s3Repository.type);
+ actions.clickNextButton();
+
+ // Fill step 2
+ form.setInputValue('bucketInput', s3Repository.settings.bucket);
+ form.setInputValue('clientInput', s3Repository.settings.client);
+ form.setInputValue('basePathInput', s3Repository.settings.basePath);
+ form.setInputValue('bufferSizeInput', s3Repository.settings.bufferSize);
+ form.toggleEuiSwitch('compressToggle');
+ form.setInputValue('chunkSizeInput', s3Repository.settings.chunkSize);
+ form.setInputValue('maxSnapshotBytesInput', s3Repository.settings.maxSnapshotBytesPerSec);
+ form.setInputValue('maxRestoreBytesInput', s3Repository.settings.maxRestoreBytesPerSec);
+ form.toggleEuiSwitch('readOnlyToggle');
+
+ await act(async () => {
+ actions.clickSubmitButton();
+ });
+
+ component.update();
+
+ const latestRequest = server.requests[server.requests.length - 1];
+
+ expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual({
+ name: s3Repository.name,
+ type: s3Repository.type,
+ settings: {
+ ...s3Repository.settings,
+ compress: false,
+ readonly: true,
},
});
});
@@ -236,7 +447,13 @@ describe('', () => {
test('should surface the API errors from the "save" HTTP request', async () => {
const { component, form, actions, find, exists } = testBed;
- form.setInputValue('locationInput', repository.settings.location);
+ // Fill step 1 required fields and go to step 2
+ form.setInputValue('nameInput', fsRepository.name);
+ actions.selectRepositoryType(fsRepository.type);
+ actions.clickNextButton();
+
+ // Fill step 2
+ form.setInputValue('locationInput', fsRepository.settings.location);
form.toggleEuiSwitch('compressToggle');
const error = {
@@ -249,10 +466,10 @@ describe('', () => {
await act(async () => {
actions.clickSubmitButton();
- await nextTick();
- component.update();
});
+ component.update();
+
expect(exists('saveRepositoryApiError')).toBe(true);
expect(find('saveRepositoryApiError').text()).toContain(error.message);
});
@@ -261,31 +478,32 @@ describe('', () => {
describe('source only', () => {
beforeEach(() => {
// Fill step 1 required fields and go to step 2
- testBed.form.setInputValue('nameInput', repository.name);
- testBed.actions.selectRepositoryType(repository.type);
+ testBed.form.setInputValue('nameInput', fsRepository.name);
+ testBed.actions.selectRepositoryType(fsRepository.type);
testBed.form.toggleEuiSwitch('sourceOnlyToggle'); // toggle source
testBed.actions.clickNextButton();
});
test('should send the correct payload', async () => {
- const { form, actions } = testBed;
+ const { form, actions, component } = testBed;
// Fill step 2
- form.setInputValue('locationInput', repository.settings.location);
+ form.setInputValue('locationInput', fsRepository.settings.location);
await act(async () => {
actions.clickSubmitButton();
- await nextTick();
});
+ component.update();
+
const latestRequest = server.requests[server.requests.length - 1];
expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual({
- name: repository.name,
+ name: fsRepository.name,
type: 'source',
settings: {
- delegateType: repository.type,
- location: repository.settings.location,
+ delegateType: fsRepository.type,
+ location: fsRepository.settings.location,
},
});
});
diff --git a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/azure_settings.tsx b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/azure_settings.tsx
index adbbe81176bdedf..b2657d0bfc0fb97 100644
--- a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/azure_settings.tsx
+++ b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/azure_settings.tsx
@@ -17,7 +17,7 @@ import {
} from '@elastic/eui';
import { AzureRepository, Repository } from '../../../../../common/types';
import { RepositorySettingsValidation } from '../../../services/validation';
-import { textService } from '../../../services/text';
+import { ChunkSizeField, MaxSnapshotsField, MaxRestoreField } from './common';
interface Props {
repository: AzureRepository;
@@ -53,6 +53,12 @@ export const AzureSettings: React.FunctionComponent = ({
text: option,
}));
+ const updateSettings = (name: string, value: string) => {
+ updateRepositorySettings({
+ [name]: value,
+ });
+ };
+
return (
{/* Client field */}
@@ -232,139 +238,28 @@ export const AzureSettings: React.FunctionComponent = ({
{/* Chunk size field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.chunkSize)}
- error={settingErrors.chunkSize}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- chunkSize: e.target.value,
- });
- }}
- data-test-subj="chunkSizeInput"
- />
-
-
+
{/* Max snapshot bytes field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.maxSnapshotBytesPerSec)}
- error={settingErrors.maxSnapshotBytesPerSec}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- maxSnapshotBytesPerSec: e.target.value,
- });
- }}
- data-test-subj="maxSnapshotBytesInput"
- />
-
-
+
{/* Max restore bytes field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.maxRestoreBytesPerSec)}
- error={settingErrors.maxRestoreBytesPerSec}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- maxRestoreBytesPerSec: e.target.value,
- });
- }}
- data-test-subj="maxRestoreBytesInput"
- />
-
-
+
{/* Location mode field */}
void;
+ error: RepositorySettingsValidation['chunkSize'];
+}
+
+export const ChunkSizeField: React.FunctionComponent = ({
+ isInvalid,
+ error,
+ defaultValue,
+ updateSettings,
+}) => {
+ return (
+
+
+
+
+
+ }
+ description={
+
+ }
+ fullWidth
+ >
+
+ }
+ fullWidth
+ isInvalid={isInvalid}
+ error={error}
+ helpText={
+ 1g,
+ example2: 10mb,
+ example3: 5k,
+ example4: 1024B,
+ }}
+ />
+ }
+ >
+ updateSettings('chunkSize', e.target.value)}
+ data-test-subj="chunkSizeInput"
+ />
+
+
+ );
+};
diff --git a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/common/index.ts b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/common/index.ts
new file mode 100644
index 000000000000000..173e13b1b6e1770
--- /dev/null
+++ b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/common/index.ts
@@ -0,0 +1,10 @@
+/*
+ * 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.
+ */
+
+export { ChunkSizeField } from './chunk_size';
+export { MaxRestoreField } from './max_restore';
+export { MaxSnapshotsField } from './max_snapshots';
diff --git a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/common/max_restore.tsx b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/common/max_restore.tsx
new file mode 100644
index 000000000000000..281fe26d5b9d34d
--- /dev/null
+++ b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/common/max_restore.tsx
@@ -0,0 +1,78 @@
+/*
+ * 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 from 'react';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiDescribedFormGroup, EuiFieldText, EuiFormRow, EuiTitle, EuiCode } from '@elastic/eui';
+import { RepositorySettingsValidation } from '../../../../services/validation';
+
+interface Props {
+ isInvalid: boolean;
+ defaultValue: string;
+ updateSettings: (name: string, value: string) => void;
+ error: RepositorySettingsValidation['maxRestoreBytesPerSec'];
+}
+
+export const MaxRestoreField: React.FunctionComponent = ({
+ isInvalid,
+ error,
+ defaultValue,
+ updateSettings,
+}) => {
+ return (
+
+
+
+
+
+ }
+ description={
+
+ }
+ fullWidth
+ >
+
+ }
+ fullWidth
+ isInvalid={isInvalid}
+ error={error}
+ helpText={
+ 1g,
+ example2: 10mb,
+ example3: 5k,
+ example4: 1024B,
+ }}
+ />
+ }
+ >
+ updateSettings('maxRestoreBytesPerSec', e.target.value)}
+ data-test-subj="maxRestoreBytesInput"
+ />
+
+
+ );
+};
diff --git a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/common/max_snapshots.tsx b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/common/max_snapshots.tsx
new file mode 100644
index 000000000000000..85b9153c711b9c8
--- /dev/null
+++ b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/common/max_snapshots.tsx
@@ -0,0 +1,79 @@
+/*
+ * 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 from 'react';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiDescribedFormGroup, EuiFieldText, EuiFormRow, EuiTitle, EuiCode } from '@elastic/eui';
+import { RepositorySettingsValidation } from '../../../../services/validation';
+
+interface Props {
+ isInvalid: boolean;
+ defaultValue: string;
+ updateSettings: (name: string, value: string) => void;
+ error: RepositorySettingsValidation['maxSnapshotBytesPerSec'];
+}
+
+export const MaxSnapshotsField: React.FunctionComponent = ({
+ isInvalid,
+ error,
+ defaultValue,
+ updateSettings,
+}) => {
+ return (
+
+
+
+
+
+ }
+ description={
+
+ }
+ fullWidth
+ >
+
+ }
+ fullWidth
+ isInvalid={isInvalid}
+ error={error}
+ helpText={
+ 1g,
+ example2: 10mb,
+ example3: 5k,
+ example4: 1024B,
+ defaultSize: 40mb,
+ }}
+ />
+ }
+ >
+ updateSettings('maxSnapshotBytesPerSec', e.target.value)}
+ data-test-subj="maxSnapshotBytesInput"
+ />
+
+
+ );
+};
diff --git a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/fs_settings.tsx b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/fs_settings.tsx
index 2635cabfa1ef636..af3e6e82312624a 100644
--- a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/fs_settings.tsx
+++ b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/fs_settings.tsx
@@ -17,7 +17,7 @@ import {
} from '@elastic/eui';
import { FSRepository, Repository } from '../../../../../common/types';
import { RepositorySettingsValidation } from '../../../services/validation';
-import { textService } from '../../../services/text';
+import { ChunkSizeField, MaxRestoreField, MaxSnapshotsField } from './common';
interface Props {
repository: FSRepository;
@@ -44,6 +44,11 @@ export const FSSettings: React.FunctionComponent = ({
},
} = repository;
const hasErrors: boolean = Boolean(Object.keys(settingErrors).length);
+ const updateSettings = (name: string, value: string) => {
+ updateRepositorySettings({
+ [name]: value,
+ });
+ };
return (
@@ -141,139 +146,28 @@ export const FSSettings: React.FunctionComponent = ({
{/* Chunk size field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.chunkSize)}
- error={settingErrors.chunkSize}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- chunkSize: e.target.value,
- });
- }}
- data-test-subj="chunkSizeInput"
- />
-
-
+
{/* Max snapshot bytes field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.maxSnapshotBytesPerSec)}
- error={settingErrors.maxSnapshotBytesPerSec}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- maxSnapshotBytesPerSec: e.target.value,
- });
- }}
- data-test-subj="maxSnapshotBytesInput"
- />
-
-
+
{/* Max restore bytes field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.maxRestoreBytesPerSec)}
- error={settingErrors.maxRestoreBytesPerSec}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- maxRestoreBytesPerSec: e.target.value,
- });
- }}
- data-test-subj="maxRestoreBytesInput"
- />
-
-
+
{/* Readonly field */}
= ({
} = repository;
const hasErrors: boolean = Boolean(Object.keys(settingErrors).length);
+ const updateSettings = (name: string, value: string) => {
+ updateRepositorySettings({
+ [name]: value,
+ });
+ };
+
return (
{/* Client field */}
@@ -220,139 +226,28 @@ export const GCSSettings: React.FunctionComponent = ({
{/* Chunk size field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.chunkSize)}
- error={settingErrors.chunkSize}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- chunkSize: e.target.value,
- });
- }}
- data-test-subj="chunkSizeInput"
- />
-
-
+
{/* Max snapshot bytes field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.maxSnapshotBytesPerSec)}
- error={settingErrors.maxSnapshotBytesPerSec}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- maxSnapshotBytesPerSec: e.target.value,
- });
- }}
- data-test-subj="maxSnapshotBytesInput"
- />
-
-
+
{/* Max restore bytes field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.maxRestoreBytesPerSec)}
- error={settingErrors.maxRestoreBytesPerSec}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- maxRestoreBytesPerSec: e.target.value,
- });
- }}
- data-test-subj="maxRestoreBytesInput"
- />
-
-
+
{/* Readonly field */}
;
@@ -54,6 +54,11 @@ export const HDFSSettings: React.FunctionComponent = ({
},
} = repository;
const hasErrors: boolean = Boolean(Object.keys(settingErrors).length);
+ const updateSettings = (settingName: string, value: string) => {
+ updateRepositorySettings({
+ [settingName]: value,
+ });
+ };
const [additionalConf, setAdditionalConf] = useState(JSON.stringify(rest, null, 2));
const [isConfInvalid, setIsConfInvalid] = useState(false);
@@ -244,49 +249,12 @@ export const HDFSSettings: React.FunctionComponent = ({
{/* Chunk size field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.chunkSize)}
- error={settingErrors.chunkSize}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- chunkSize: e.target.value,
- });
- }}
- data-test-subj="chunkSizeInput"
- />
-
-
+
{/* Security principal field */}
= ({
{/* Max snapshot bytes field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.maxSnapshotBytesPerSec)}
- error={settingErrors.maxSnapshotBytesPerSec}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- maxSnapshotBytesPerSec: e.target.value,
- });
- }}
- data-test-subj="maxSnapshotBytesInput"
- />
-
-
+
{/* Max restore bytes field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.maxRestoreBytesPerSec)}
- error={settingErrors.maxRestoreBytesPerSec}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- maxRestoreBytesPerSec: e.target.value,
- });
- }}
- data-test-subj="maxRestoreBytesInput"
- />
-
-
+
{/* Readonly field */}
= ({
text: option,
}));
const hasErrors: boolean = Boolean(Object.keys(settingErrors).length);
+ const updateSettings = (name: string, value: string) => {
+ updateRepositorySettings({
+ [name]: value,
+ });
+ };
const storageClassOptions = ['standard', 'reduced_redundancy', 'standard_ia'].map((option) => ({
value: option,
@@ -249,49 +255,12 @@ export const S3Settings: React.FunctionComponent = ({
{/* Chunk size field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.chunkSize)}
- error={settingErrors.chunkSize}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- chunkSize: e.target.value,
- });
- }}
- data-test-subj="chunkSizeInput"
- />
-
-
+
{/* Server side encryption field */}
= ({
fullWidth
isInvalid={Boolean(hasErrors && settingErrors.bufferSize)}
error={settingErrors.bufferSize}
- helpText={textService.getSizeNotationHelpText()}
+ helpText={
+ 1g,
+ example2: 10mb,
+ example3: 5k,
+ example4: 1024B,
+ defaultSize: 100mb,
+ defaultPercentage: 5%,
+ }}
+ />
+ }
>
= ({
{/* Max snapshot bytes field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.maxSnapshotBytesPerSec)}
- error={settingErrors.maxSnapshotBytesPerSec}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- maxSnapshotBytesPerSec: e.target.value,
- });
- }}
- data-test-subj="maxSnapshotBytesInput"
- />
-
-
+
{/* Max restore bytes field */}
-
-
-
-
-
- }
- description={
-
- }
- fullWidth
- >
-
- }
- fullWidth
- isInvalid={Boolean(hasErrors && settingErrors.maxRestoreBytesPerSec)}
- error={settingErrors.maxRestoreBytesPerSec}
- helpText={textService.getSizeNotationHelpText()}
- >
- {
- updateRepositorySettings({
- maxRestoreBytesPerSec: e.target.value,
- });
- }}
- data-test-subj="maxRestoreBytesInput"
- />
-
-
+
{/* Readonly field */}
0 ? groupResult.metrics[0] : null;
+ const value = metric && metric.length === 2 ? metric[1] : null;
+
+ if (!value) {
+ logger.debug(
+ `alert ${ID}:${alertId} "${name}": no metrics found for group ${instanceId}} from groupResult ${JSON.stringify(
+ groupResult
+ )}`
+ );
+ continue;
+ }
+
const met = compareFn(value, params.threshold);
if (!met) continue;
diff --git a/x-pack/plugins/task_manager/server/routes/health.test.ts b/x-pack/plugins/task_manager/server/routes/health.test.ts
index dd7ed69aaf27f96..0a9671d9ac37e99 100644
--- a/x-pack/plugins/task_manager/server/routes/health.test.ts
+++ b/x-pack/plugins/task_manager/server/routes/health.test.ts
@@ -155,31 +155,6 @@ describe('healthRoute', () => {
expect(await serviceStatus).toMatchObject({
level: ServiceStatusLevels.unavailable,
summary: 'Task Manager is unavailable',
- meta: {
- status: 'error',
- ...summarizeMonitoringStats(
- mockHealthStats({
- last_update: expect.any(String),
- stats: {
- configuration: {
- timestamp: expect.any(String),
- },
- workload: {
- timestamp: expect.any(String),
- },
- runtime: {
- timestamp: expect.any(String),
- value: {
- polling: {
- last_successful_poll: expect.any(String),
- },
- },
- },
- },
- }),
- getTaskManagerConfig({})
- ),
- },
});
});
diff --git a/x-pack/plugins/task_manager/server/routes/health.ts b/x-pack/plugins/task_manager/server/routes/health.ts
index 589443b62ea4277..cc2f6c6630e56d8 100644
--- a/x-pack/plugins/task_manager/server/routes/health.ts
+++ b/x-pack/plugins/task_manager/server/routes/health.ts
@@ -34,13 +34,22 @@ const LEVEL_SUMMARY = {
[ServiceStatusLevels.unavailable.toString()]: 'Task Manager is unavailable',
};
+/**
+ * We enforce a `meta` of `never` because this meta gets duplicated into *every dependant plugin*, and
+ * this will then get logged out when logging is set to Verbose.
+ * We used to pass in the the entire MonitoredHealth into this `meta` field, but this means that the
+ * whole MonitoredHealth JSON (which can be quite big) was duplicated dozens of times and when we
+ * try to view logs in Discover, it fails to render as this JSON was often dozens of levels deep.
+ */
+type TaskManagerServiceStatus = ServiceStatus;
+
export function healthRoute(
router: IRouter,
monitoringStats$: Observable,
logger: Logger,
taskManagerId: string,
config: TaskManagerConfig
-): Observable {
+): Observable {
// if "hot" health stats are any more stale than monitored_stats_required_freshness (pollInterval +1s buffer by default)
// consider the system unhealthy
const requiredHotStatsFreshness: number = config.monitored_stats_required_freshness;
@@ -67,7 +76,7 @@ export function healthRoute(
return { id: taskManagerId, timestamp, status: healthStatus, ...summarizedStats };
}
- const serviceStatus$: Subject = new Subject();
+ const serviceStatus$: Subject = new Subject();
/* keep track of last health summary, as we'll return that to the next call to _health */
let lastMonitoredStats: MonitoringStats | null = null;
@@ -110,7 +119,7 @@ export function healthRoute(
export function withServiceStatus(
monitoredHealth: MonitoredHealth
-): [MonitoredHealth, ServiceStatus] {
+): [MonitoredHealth, TaskManagerServiceStatus] {
const level =
monitoredHealth.status === HealthStatus.OK
? ServiceStatusLevels.available
@@ -122,7 +131,6 @@ export function withServiceStatus(
{
level,
summary: LEVEL_SUMMARY[level.toString()],
- meta: monitoredHealth,
},
];
}
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index bde4e24ecd96442..23235182c9978c2 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -5653,7 +5653,6 @@
"xpack.apm.transactionsTable.nameColumnLabel": "名前",
"xpack.apm.transactionsTable.notFoundLabel": "トランザクションが見つかりませんでした。",
"xpack.apm.transactionsTable.throughputColumnLabel": "スループット",
- "xpack.apm.transactionTypeSelectLabel": "型",
"xpack.apm.tutorial.apmServer.title": "APM Server",
"xpack.apm.tutorial.elasticCloud.textPre": "APM Server を有効にするには、[the Elastic Cloud console] (https://cloud.elastic.co/deployments?q={cloudId}) に移動し、展開設定で APM を有効にします。有効になったら、このページを更新してください。",
"xpack.apm.tutorial.elasticCloudInstructions.title": "APM エージェント",
@@ -20806,9 +20805,6 @@
"xpack.snapshotRestore.repositoryForm.typeAzure.basePathDescription": "レポジトリデータへのコンテナーパスです。",
"xpack.snapshotRestore.repositoryForm.typeAzure.basePathLabel": "ベースパス",
"xpack.snapshotRestore.repositoryForm.typeAzure.basePathTitle": "ベースパス",
- "xpack.snapshotRestore.repositoryForm.typeAzure.chunkSizeDescription": "スナップショットの作成時にファイルを小さなユニットに分けます。",
- "xpack.snapshotRestore.repositoryForm.typeAzure.chunkSizeLabel": "チャンクサイズ",
- "xpack.snapshotRestore.repositoryForm.typeAzure.chunkSizeTitle": "チャンクサイズ",
"xpack.snapshotRestore.repositoryForm.typeAzure.clientDescription": "Azure クライアントの名前です。",
"xpack.snapshotRestore.repositoryForm.typeAzure.clientLabel": "クライアント",
"xpack.snapshotRestore.repositoryForm.typeAzure.clientTitle": "クライアント",
@@ -20821,29 +20817,14 @@
"xpack.snapshotRestore.repositoryForm.typeAzure.locationModeDescription": "プライマリまたはセカンダリの場所です。セカンダリの場合、読み込み専用が true です。",
"xpack.snapshotRestore.repositoryForm.typeAzure.locationModeLabel": "位置情報モード",
"xpack.snapshotRestore.repositoryForm.typeAzure.locationModeTitle": "位置情報モード",
- "xpack.snapshotRestore.repositoryForm.typeAzure.maxRestoreBytesDescription": "各ノードのスナップショットの復元レートです。",
- "xpack.snapshotRestore.repositoryForm.typeAzure.maxRestoreBytesLabel": "1 秒間の最高復元バイト数",
- "xpack.snapshotRestore.repositoryForm.typeAzure.maxRestoreBytesTitle": "1 秒間の最高復元バイト数",
- "xpack.snapshotRestore.repositoryForm.typeAzure.maxSnapshotBytesDescription": "各ノードのスナップショットの作成レートです。",
- "xpack.snapshotRestore.repositoryForm.typeAzure.maxSnapshotBytesLabel": "1 秒間の最高スナップショットバイト数",
- "xpack.snapshotRestore.repositoryForm.typeAzure.maxSnapshotBytesTitle": "1 秒間の最高スナップショットバイト数",
"xpack.snapshotRestore.repositoryForm.typeAzure.readonlyDescription": "このレポジトリへの書き込みアクセスがあるクラスターは 1 つだけでなければなりません。他のクラスターはすべて読み込み専用にする必要があります。",
"xpack.snapshotRestore.repositoryForm.typeAzure.readonlyLabel": "読み込み専用レポジトリ",
"xpack.snapshotRestore.repositoryForm.typeAzure.readonlyTitle": "読み込み専用",
- "xpack.snapshotRestore.repositoryForm.typeFS.chunkSizeDescription": "スナップショットの作成時にファイルを小さなユニットに分けます。",
- "xpack.snapshotRestore.repositoryForm.typeFS.chunkSizeLabel": "チャンクサイズ",
- "xpack.snapshotRestore.repositoryForm.typeFS.chunkSizeTitle": "チャンクサイズ",
"xpack.snapshotRestore.repositoryForm.typeFS.compressDescription": "スナップショット用にインデックスのマッピングと設定ファイルを圧縮します。データファイルは圧縮されません。",
"xpack.snapshotRestore.repositoryForm.typeFS.compressLabel": "スナップショットを圧縮",
"xpack.snapshotRestore.repositoryForm.typeFS.compressTitle": "スナップショットの圧縮",
"xpack.snapshotRestore.repositoryForm.typeFS.locationLabel": "場所 (必須) ",
"xpack.snapshotRestore.repositoryForm.typeFS.locationTitle": "ファイルシステムの場所",
- "xpack.snapshotRestore.repositoryForm.typeFS.maxRestoreBytesDescription": "各ノードのスナップショットの復元レートです。",
- "xpack.snapshotRestore.repositoryForm.typeFS.maxRestoreBytesLabel": "1 秒間の最高復元バイト数",
- "xpack.snapshotRestore.repositoryForm.typeFS.maxRestoreBytesTitle": "1 秒間の最高復元バイト数",
- "xpack.snapshotRestore.repositoryForm.typeFS.maxSnapshotBytesDescription": "各ノードのスナップショットの作成レートです。",
- "xpack.snapshotRestore.repositoryForm.typeFS.maxSnapshotBytesLabel": "1 秒間の最高スナップショットバイト数",
- "xpack.snapshotRestore.repositoryForm.typeFS.maxSnapshotBytesTitle": "1 秒間の最高スナップショットバイト数",
"xpack.snapshotRestore.repositoryForm.typeFS.readonlyDescription": "このレポジトリへの書き込みアクセスがあるクラスターは 1 つだけでなければなりません。他のクラスターはすべて読み込み専用にする必要があります。",
"xpack.snapshotRestore.repositoryForm.typeFS.readonlyLabel": "読み込み専用レポジトリ",
"xpack.snapshotRestore.repositoryForm.typeFS.readonlyTitle": "読み込み専用",
@@ -20853,27 +20834,15 @@
"xpack.snapshotRestore.repositoryForm.typeGCS.bucketDescription": "スナップショットに使用する Google Cloud Storage バケットの名前です。",
"xpack.snapshotRestore.repositoryForm.typeGCS.bucketLabel": "バケット (必須) ",
"xpack.snapshotRestore.repositoryForm.typeGCS.bucketTitle": "バケット",
- "xpack.snapshotRestore.repositoryForm.typeGCS.chunkSizeDescription": "スナップショットの作成時にファイルを小さなユニットに分けます。",
- "xpack.snapshotRestore.repositoryForm.typeGCS.chunkSizeLabel": "チャンクサイズ",
- "xpack.snapshotRestore.repositoryForm.typeGCS.chunkSizeTitle": "チャンクサイズ",
"xpack.snapshotRestore.repositoryForm.typeGCS.clientDescription": "Google Cloud Storage クライアントの名前です。",
"xpack.snapshotRestore.repositoryForm.typeGCS.clientLabel": "クライアント",
"xpack.snapshotRestore.repositoryForm.typeGCS.clientTitle": "クライアント",
"xpack.snapshotRestore.repositoryForm.typeGCS.compressDescription": "スナップショット用にインデックスのマッピングと設定ファイルを圧縮します。データファイルは圧縮されません。",
"xpack.snapshotRestore.repositoryForm.typeGCS.compressLabel": "スナップショットを圧縮",
"xpack.snapshotRestore.repositoryForm.typeGCS.compressTitle": "スナップショットを圧縮",
- "xpack.snapshotRestore.repositoryForm.typeGCS.maxRestoreBytesDescription": "各ノードのスナップショットの復元レートです。",
- "xpack.snapshotRestore.repositoryForm.typeGCS.maxRestoreBytesLabel": "1 秒間の最高復元バイト数",
- "xpack.snapshotRestore.repositoryForm.typeGCS.maxRestoreBytesTitle": "1 秒間の最高復元バイト数",
- "xpack.snapshotRestore.repositoryForm.typeGCS.maxSnapshotBytesDescription": "各ノードのスナップショットの作成レートです。",
- "xpack.snapshotRestore.repositoryForm.typeGCS.maxSnapshotBytesLabel": "1 秒間の最高スナップショットバイト数",
- "xpack.snapshotRestore.repositoryForm.typeGCS.maxSnapshotBytesTitle": "1 秒間の最高スナップショットバイト数",
"xpack.snapshotRestore.repositoryForm.typeGCS.readonlyDescription": "このレポジトリへの書き込みアクセスがあるクラスターは 1 つだけでなければなりません。他のクラスターはすべて読み込み専用にする必要があります。",
"xpack.snapshotRestore.repositoryForm.typeGCS.readonlyLabel": "読み込み専用レポジトリ",
"xpack.snapshotRestore.repositoryForm.typeGCS.readonlyTitle": "読み込み専用",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.chunkSizeDescription": "スナップショットの作成時にファイルを小さなユニットに分けます。",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.chunkSizeLabel": "チャンクサイズ",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.chunkSizeTitle": "チャンクサイズ",
"xpack.snapshotRestore.repositoryForm.typeHDFS.compressDescription": "スナップショット用にインデックスのマッピングと設定ファイルを圧縮します。データファイルは圧縮されません。",
"xpack.snapshotRestore.repositoryForm.typeHDFS.compressLabel": "スナップショットを圧縮",
"xpack.snapshotRestore.repositoryForm.typeHDFS.compressTitle": "スナップショットの圧縮",
@@ -20886,12 +20855,6 @@
"xpack.snapshotRestore.repositoryForm.typeHDFS.loadDefaultsDescription": "デフォルトの Hadoop 構成を読み込みます。",
"xpack.snapshotRestore.repositoryForm.typeHDFS.loadDefaultsLabel": "デフォルトを読み込む",
"xpack.snapshotRestore.repositoryForm.typeHDFS.loadDefaultsTitle": "デフォルトを読み込む",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.maxRestoreBytesDescription": "各ノードのスナップショットの復元レートです。",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.maxRestoreBytesLabel": "1 秒間の最高復元バイト数",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.maxRestoreBytesTitle": "1 秒間の最高復元バイト数",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.maxSnapshotBytesDescription": "各ノードのスナップショットの作成レートです。",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.maxSnapshotBytesLabel": "1 秒間の最高スナップショットバイト数",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.maxSnapshotBytesTitle": "1 秒間の最高スナップショットバイト数",
"xpack.snapshotRestore.repositoryForm.typeHDFS.pathDescription": "データが保管されているファイルへのパスです。",
"xpack.snapshotRestore.repositoryForm.typeHDFS.pathLabel": "パス (必須) ",
"xpack.snapshotRestore.repositoryForm.typeHDFS.pathTitle": "パス",
@@ -20922,21 +20885,12 @@
"xpack.snapshotRestore.repositoryForm.typeS3.cannedAclDescription": "新しい S3 バケットとオブジェクトに追加する canned ACL です。",
"xpack.snapshotRestore.repositoryForm.typeS3.cannedAclLabel": "Canned ACL",
"xpack.snapshotRestore.repositoryForm.typeS3.cannedAclTitle": "Canned ACL",
- "xpack.snapshotRestore.repositoryForm.typeS3.chunkSizeDescription": "スナップショットの作成時にファイルを小さなユニットに分けます。",
- "xpack.snapshotRestore.repositoryForm.typeS3.chunkSizeLabel": "チャンクサイズ",
- "xpack.snapshotRestore.repositoryForm.typeS3.chunkSizeTitle": "チャンクサイズ",
"xpack.snapshotRestore.repositoryForm.typeS3.clientDescription": "AWS S3 クライアントの名前です。",
"xpack.snapshotRestore.repositoryForm.typeS3.clientLabel": "クライアント",
"xpack.snapshotRestore.repositoryForm.typeS3.clientTitle": "クライアント",
"xpack.snapshotRestore.repositoryForm.typeS3.compressDescription": "スナップショット用にインデックスのマッピングと設定ファイルを圧縮します。データファイルは圧縮されません。",
"xpack.snapshotRestore.repositoryForm.typeS3.compressLabel": "スナップショットを圧縮",
"xpack.snapshotRestore.repositoryForm.typeS3.compressTitle": "スナップショットの圧縮",
- "xpack.snapshotRestore.repositoryForm.typeS3.maxRestoreBytesDescription": "各ノードのスナップショットの復元レートです。",
- "xpack.snapshotRestore.repositoryForm.typeS3.maxRestoreBytesLabel": "1 秒間の最高復元バイト数",
- "xpack.snapshotRestore.repositoryForm.typeS3.maxRestoreBytesTitle": "1 秒間の最高復元バイト数",
- "xpack.snapshotRestore.repositoryForm.typeS3.maxSnapshotBytesDescription": "各ノードのスナップショットの作成レートです。",
- "xpack.snapshotRestore.repositoryForm.typeS3.maxSnapshotBytesLabel": "1 秒間の最高スナップショットバイト数",
- "xpack.snapshotRestore.repositoryForm.typeS3.maxSnapshotBytesTitle": "1 秒間の最高スナップショットバイト数",
"xpack.snapshotRestore.repositoryForm.typeS3.readonlyDescription": "このレポジトリへの書き込みアクセスがあるクラスターは 1 つだけでなければなりません。他のクラスターはすべて読み込み専用にする必要があります。",
"xpack.snapshotRestore.repositoryForm.typeS3.readonlyLabel": "読み込み専用レポジトリ",
"xpack.snapshotRestore.repositoryForm.typeS3.readonlyTitle": "読み込み専用",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 43d537ad560cde7..a32e206e8ef5c5e 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -5692,7 +5692,6 @@
"xpack.apm.transactionsTable.nameColumnLabel": "名称",
"xpack.apm.transactionsTable.notFoundLabel": "未找到任何事务。",
"xpack.apm.transactionsTable.throughputColumnLabel": "吞吐量",
- "xpack.apm.transactionTypeSelectLabel": "类型",
"xpack.apm.tutorial.apmServer.title": "APM Server",
"xpack.apm.tutorial.elasticCloud.textPre": "要启用 APM Server,请前往 [Elastic Cloud 控制台](https://cloud.elastic.co/deployments?q={cloudId}) 并在部署设置中启用 APM。启用后,请刷新此页面。",
"xpack.apm.tutorial.elasticCloudInstructions.title": "APM 代理",
@@ -21143,9 +21142,6 @@
"xpack.snapshotRestore.repositoryForm.typeAzure.basePathDescription": "存储库数据的容器路径。",
"xpack.snapshotRestore.repositoryForm.typeAzure.basePathLabel": "基路径",
"xpack.snapshotRestore.repositoryForm.typeAzure.basePathTitle": "基路径",
- "xpack.snapshotRestore.repositoryForm.typeAzure.chunkSizeDescription": "拍取快照时,将文件拆分成更小的单位。",
- "xpack.snapshotRestore.repositoryForm.typeAzure.chunkSizeLabel": "块大小",
- "xpack.snapshotRestore.repositoryForm.typeAzure.chunkSizeTitle": "块大小",
"xpack.snapshotRestore.repositoryForm.typeAzure.clientDescription": "Azure 客户端的名称。",
"xpack.snapshotRestore.repositoryForm.typeAzure.clientLabel": "客户端",
"xpack.snapshotRestore.repositoryForm.typeAzure.clientTitle": "客户端",
@@ -21158,29 +21154,14 @@
"xpack.snapshotRestore.repositoryForm.typeAzure.locationModeDescription": "主要位置或辅助位置。如果为辅助位置,则只读为 true。",
"xpack.snapshotRestore.repositoryForm.typeAzure.locationModeLabel": "位置模式",
"xpack.snapshotRestore.repositoryForm.typeAzure.locationModeTitle": "位置模式",
- "xpack.snapshotRestore.repositoryForm.typeAzure.maxRestoreBytesDescription": "每个节点的快照还原速率。",
- "xpack.snapshotRestore.repositoryForm.typeAzure.maxRestoreBytesLabel": "每秒最大还原字节数",
- "xpack.snapshotRestore.repositoryForm.typeAzure.maxRestoreBytesTitle": "每秒最大还原字节数",
- "xpack.snapshotRestore.repositoryForm.typeAzure.maxSnapshotBytesDescription": "为每个节点创建快照的速率。",
- "xpack.snapshotRestore.repositoryForm.typeAzure.maxSnapshotBytesLabel": "每秒最大快照字节数",
- "xpack.snapshotRestore.repositoryForm.typeAzure.maxSnapshotBytesTitle": "每秒最大快照字节数",
"xpack.snapshotRestore.repositoryForm.typeAzure.readonlyDescription": "仅一个集群应对此存储库有写权限。所有其他集群应为只读。",
"xpack.snapshotRestore.repositoryForm.typeAzure.readonlyLabel": "只读存储库",
"xpack.snapshotRestore.repositoryForm.typeAzure.readonlyTitle": "只读",
- "xpack.snapshotRestore.repositoryForm.typeFS.chunkSizeDescription": "拍取快照时,将文件拆分成更小的单位。",
- "xpack.snapshotRestore.repositoryForm.typeFS.chunkSizeLabel": "块大小",
- "xpack.snapshotRestore.repositoryForm.typeFS.chunkSizeTitle": "块大小",
"xpack.snapshotRestore.repositoryForm.typeFS.compressDescription": "压缩快照的索引映射和设置文件。数据文件未压缩。",
"xpack.snapshotRestore.repositoryForm.typeFS.compressLabel": "压缩快照",
"xpack.snapshotRestore.repositoryForm.typeFS.compressTitle": "快照压缩",
"xpack.snapshotRestore.repositoryForm.typeFS.locationLabel": "位置 (必填) ",
"xpack.snapshotRestore.repositoryForm.typeFS.locationTitle": "文件系统位置",
- "xpack.snapshotRestore.repositoryForm.typeFS.maxRestoreBytesDescription": "每个节点的快照还原速率。",
- "xpack.snapshotRestore.repositoryForm.typeFS.maxRestoreBytesLabel": "每秒最大还原字节数",
- "xpack.snapshotRestore.repositoryForm.typeFS.maxRestoreBytesTitle": "每秒最大还原字节数",
- "xpack.snapshotRestore.repositoryForm.typeFS.maxSnapshotBytesDescription": "为每个节点创建快照的速率。",
- "xpack.snapshotRestore.repositoryForm.typeFS.maxSnapshotBytesLabel": "每秒最大快照字节数",
- "xpack.snapshotRestore.repositoryForm.typeFS.maxSnapshotBytesTitle": "每秒最大快照字节数",
"xpack.snapshotRestore.repositoryForm.typeFS.readonlyDescription": "仅一个集群应对此存储库有写权限。所有其他集群应为只读。",
"xpack.snapshotRestore.repositoryForm.typeFS.readonlyLabel": "只读存储库",
"xpack.snapshotRestore.repositoryForm.typeFS.readonlyTitle": "只读",
@@ -21190,27 +21171,15 @@
"xpack.snapshotRestore.repositoryForm.typeGCS.bucketDescription": "要用于快照的 Google Cloud Storage 存储桶的名称。",
"xpack.snapshotRestore.repositoryForm.typeGCS.bucketLabel": "存储桶 (必填) ",
"xpack.snapshotRestore.repositoryForm.typeGCS.bucketTitle": "存储桶",
- "xpack.snapshotRestore.repositoryForm.typeGCS.chunkSizeDescription": "拍取快照时,将文件拆分成更小的单位。",
- "xpack.snapshotRestore.repositoryForm.typeGCS.chunkSizeLabel": "块大小",
- "xpack.snapshotRestore.repositoryForm.typeGCS.chunkSizeTitle": "块大小",
"xpack.snapshotRestore.repositoryForm.typeGCS.clientDescription": "Google Cloud Storage 客户端的名称。",
"xpack.snapshotRestore.repositoryForm.typeGCS.clientLabel": "客户端",
"xpack.snapshotRestore.repositoryForm.typeGCS.clientTitle": "客户端",
"xpack.snapshotRestore.repositoryForm.typeGCS.compressDescription": "压缩快照的索引映射和设置文件。数据文件未压缩。",
"xpack.snapshotRestore.repositoryForm.typeGCS.compressLabel": "压缩快照",
"xpack.snapshotRestore.repositoryForm.typeGCS.compressTitle": "压缩快照",
- "xpack.snapshotRestore.repositoryForm.typeGCS.maxRestoreBytesDescription": "每个节点的快照还原速率。",
- "xpack.snapshotRestore.repositoryForm.typeGCS.maxRestoreBytesLabel": "每秒最大还原字节数",
- "xpack.snapshotRestore.repositoryForm.typeGCS.maxRestoreBytesTitle": "每秒最大还原字节数",
- "xpack.snapshotRestore.repositoryForm.typeGCS.maxSnapshotBytesDescription": "为每个节点创建快照的速率。",
- "xpack.snapshotRestore.repositoryForm.typeGCS.maxSnapshotBytesLabel": "每秒最大快照字节数",
- "xpack.snapshotRestore.repositoryForm.typeGCS.maxSnapshotBytesTitle": "每秒最大快照字节数",
"xpack.snapshotRestore.repositoryForm.typeGCS.readonlyDescription": "仅一个集群应对此存储库有写权限。所有其他集群应为只读。",
"xpack.snapshotRestore.repositoryForm.typeGCS.readonlyLabel": "只读存储库",
"xpack.snapshotRestore.repositoryForm.typeGCS.readonlyTitle": "只读",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.chunkSizeDescription": "拍取快照时,将文件拆分成更小的单位。",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.chunkSizeLabel": "块大小",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.chunkSizeTitle": "块大小",
"xpack.snapshotRestore.repositoryForm.typeHDFS.compressDescription": "压缩快照的索引映射和设置文件。数据文件未压缩。",
"xpack.snapshotRestore.repositoryForm.typeHDFS.compressLabel": "压缩快照",
"xpack.snapshotRestore.repositoryForm.typeHDFS.compressTitle": "快照压缩",
@@ -21223,12 +21192,6 @@
"xpack.snapshotRestore.repositoryForm.typeHDFS.loadDefaultsDescription": "加载默认的 Hadoop 配置。",
"xpack.snapshotRestore.repositoryForm.typeHDFS.loadDefaultsLabel": "加载默认值",
"xpack.snapshotRestore.repositoryForm.typeHDFS.loadDefaultsTitle": "加载默认值",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.maxRestoreBytesDescription": "每个节点的快照还原速率。",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.maxRestoreBytesLabel": "每秒最大还原字节数",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.maxRestoreBytesTitle": "每秒最大还原字节数",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.maxSnapshotBytesDescription": "为每个节点创建快照的速率。",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.maxSnapshotBytesLabel": "每秒最大快照字节数",
- "xpack.snapshotRestore.repositoryForm.typeHDFS.maxSnapshotBytesTitle": "每秒最大快照字节数",
"xpack.snapshotRestore.repositoryForm.typeHDFS.pathDescription": "存储数据的文件路径。",
"xpack.snapshotRestore.repositoryForm.typeHDFS.pathLabel": "路径 (必填) ",
"xpack.snapshotRestore.repositoryForm.typeHDFS.pathTitle": "路径",
@@ -21259,21 +21222,12 @@
"xpack.snapshotRestore.repositoryForm.typeS3.cannedAclDescription": "要添加到新 S3 存储库和对象的标准 ACL。",
"xpack.snapshotRestore.repositoryForm.typeS3.cannedAclLabel": "标准 ACL",
"xpack.snapshotRestore.repositoryForm.typeS3.cannedAclTitle": "标准 ACL",
- "xpack.snapshotRestore.repositoryForm.typeS3.chunkSizeDescription": "拍取快照时,将文件拆分成更小的单位。",
- "xpack.snapshotRestore.repositoryForm.typeS3.chunkSizeLabel": "块大小",
- "xpack.snapshotRestore.repositoryForm.typeS3.chunkSizeTitle": "块大小",
"xpack.snapshotRestore.repositoryForm.typeS3.clientDescription": "AWS S3 客户端的名称。",
"xpack.snapshotRestore.repositoryForm.typeS3.clientLabel": "客户端",
"xpack.snapshotRestore.repositoryForm.typeS3.clientTitle": "客户端",
"xpack.snapshotRestore.repositoryForm.typeS3.compressDescription": "压缩快照的索引映射和设置文件。数据文件未压缩。",
"xpack.snapshotRestore.repositoryForm.typeS3.compressLabel": "压缩快照",
"xpack.snapshotRestore.repositoryForm.typeS3.compressTitle": "快照压缩",
- "xpack.snapshotRestore.repositoryForm.typeS3.maxRestoreBytesDescription": "每个节点的快照还原速率。",
- "xpack.snapshotRestore.repositoryForm.typeS3.maxRestoreBytesLabel": "每秒最大还原字节数",
- "xpack.snapshotRestore.repositoryForm.typeS3.maxRestoreBytesTitle": "每秒最大还原字节数",
- "xpack.snapshotRestore.repositoryForm.typeS3.maxSnapshotBytesDescription": "为每个节点创建快照的速率。",
- "xpack.snapshotRestore.repositoryForm.typeS3.maxSnapshotBytesLabel": "每秒最大快照字节数",
- "xpack.snapshotRestore.repositoryForm.typeS3.maxSnapshotBytesTitle": "每秒最大快照字节数",
"xpack.snapshotRestore.repositoryForm.typeS3.readonlyDescription": "仅一个集群应对此存储库有写权限。所有其他集群应为只读。",
"xpack.snapshotRestore.repositoryForm.typeS3.readonlyLabel": "只读存储库",
"xpack.snapshotRestore.repositoryForm.typeS3.readonlyTitle": "只读",
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts
index 679bc3d53c40da3..38d65b923b37491 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts
@@ -99,10 +99,10 @@ describe('Jira API', () => {
test('should call get issue types API', async () => {
const abortCtrl = new AbortController();
http.post.mockResolvedValueOnce(issueTypesResponse);
- const res = await getIssueTypes({ http, signal: abortCtrl.signal, connectorId: 'test' });
+ const res = await getIssueTypes({ http, signal: abortCtrl.signal, connectorId: 'te/st' });
expect(res).toEqual(issueTypesResponse);
- expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', {
+ expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', {
body: '{"params":{"subAction":"issueTypes","subActionParams":{}}}',
signal: abortCtrl.signal,
});
@@ -116,12 +116,12 @@ describe('Jira API', () => {
const res = await getFieldsByIssueType({
http,
signal: abortCtrl.signal,
- connectorId: 'test',
+ connectorId: 'te/st',
id: '10006',
});
expect(res).toEqual(fieldsResponse);
- expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', {
+ expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', {
body: '{"params":{"subAction":"fieldsByIssueType","subActionParams":{"id":"10006"}}}',
signal: abortCtrl.signal,
});
@@ -135,12 +135,12 @@ describe('Jira API', () => {
const res = await getIssues({
http,
signal: abortCtrl.signal,
- connectorId: 'test',
+ connectorId: 'te/st',
title: 'test issue',
});
expect(res).toEqual(issuesResponse);
- expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', {
+ expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', {
body: '{"params":{"subAction":"issues","subActionParams":{"title":"test issue"}}}',
signal: abortCtrl.signal,
});
@@ -154,12 +154,12 @@ describe('Jira API', () => {
const res = await getIssue({
http,
signal: abortCtrl.signal,
- connectorId: 'test',
+ connectorId: 'te/st',
id: 'RJ-107',
});
expect(res).toEqual(issuesResponse);
- expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', {
+ expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', {
body: '{"params":{"subAction":"issue","subActionParams":{"id":"RJ-107"}}}',
signal: abortCtrl.signal,
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts
index 46ea9dea3aa56cc..83e126ea9d2f6db 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts
@@ -17,12 +17,15 @@ export async function getIssueTypes({
signal: AbortSignal;
connectorId: string;
}): Promise> {
- return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, {
- body: JSON.stringify({
- params: { subAction: 'issueTypes', subActionParams: {} },
- }),
- signal,
- });
+ return await http.post(
+ `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`,
+ {
+ body: JSON.stringify({
+ params: { subAction: 'issueTypes', subActionParams: {} },
+ }),
+ signal,
+ }
+ );
}
export async function getFieldsByIssueType({
@@ -36,12 +39,15 @@ export async function getFieldsByIssueType({
connectorId: string;
id: string;
}): Promise> {
- return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, {
- body: JSON.stringify({
- params: { subAction: 'fieldsByIssueType', subActionParams: { id } },
- }),
- signal,
- });
+ return await http.post(
+ `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`,
+ {
+ body: JSON.stringify({
+ params: { subAction: 'fieldsByIssueType', subActionParams: { id } },
+ }),
+ signal,
+ }
+ );
}
export async function getIssues({
@@ -55,12 +61,15 @@ export async function getIssues({
connectorId: string;
title: string;
}): Promise> {
- return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, {
- body: JSON.stringify({
- params: { subAction: 'issues', subActionParams: { title } },
- }),
- signal,
- });
+ return await http.post(
+ `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`,
+ {
+ body: JSON.stringify({
+ params: { subAction: 'issues', subActionParams: { title } },
+ }),
+ signal,
+ }
+ );
}
export async function getIssue({
@@ -74,10 +83,13 @@ export async function getIssue({
connectorId: string;
id: string;
}): Promise> {
- return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, {
- body: JSON.stringify({
- params: { subAction: 'issue', subActionParams: { id } },
- }),
- signal,
- });
+ return await http.post(
+ `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`,
+ {
+ body: JSON.stringify({
+ params: { subAction: 'issue', subActionParams: { id } },
+ }),
+ signal,
+ }
+ );
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.test.ts
index 01208f93405d254..0d4bf9148a92ff0 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.test.ts
@@ -32,7 +32,7 @@ const incidentTypesResponse = {
{ id: 16, name: 'TBD / Unknown' },
{ id: 15, name: 'Vendor / 3rd party error' },
],
- actionId: 'test',
+ actionId: 'te/st',
};
const severityResponse = {
@@ -42,7 +42,7 @@ const severityResponse = {
{ id: 5, name: 'Medium' },
{ id: 6, name: 'High' },
],
- actionId: 'test',
+ actionId: 'te/st',
};
describe('Resilient API', () => {
@@ -57,11 +57,11 @@ describe('Resilient API', () => {
const res = await getIncidentTypes({
http,
signal: abortCtrl.signal,
- connectorId: 'test',
+ connectorId: 'te/st',
});
expect(res).toEqual(incidentTypesResponse);
- expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', {
+ expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', {
body: '{"params":{"subAction":"incidentTypes","subActionParams":{}}}',
signal: abortCtrl.signal,
});
@@ -75,11 +75,11 @@ describe('Resilient API', () => {
const res = await getSeverity({
http,
signal: abortCtrl.signal,
- connectorId: 'test',
+ connectorId: 'te/st',
});
expect(res).toEqual(severityResponse);
- expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', {
+ expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', {
body: '{"params":{"subAction":"severity","subActionParams":{}}}',
signal: abortCtrl.signal,
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.ts
index 8ea3c3c63e50f0f..6bd9c43105cf0e2 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/api.ts
@@ -17,12 +17,15 @@ export async function getIncidentTypes({
signal: AbortSignal;
connectorId: string;
}): Promise> {
- return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, {
- body: JSON.stringify({
- params: { subAction: 'incidentTypes', subActionParams: {} },
- }),
- signal,
- });
+ return await http.post(
+ `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`,
+ {
+ body: JSON.stringify({
+ params: { subAction: 'incidentTypes', subActionParams: {} },
+ }),
+ signal,
+ }
+ );
}
export async function getSeverity({
@@ -34,10 +37,13 @@ export async function getSeverity({
signal: AbortSignal;
connectorId: string;
}): Promise> {
- return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, {
- body: JSON.stringify({
- params: { subAction: 'severity', subActionParams: {} },
- }),
- signal,
- });
+ return await http.post(
+ `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`,
+ {
+ body: JSON.stringify({
+ params: { subAction: 'severity', subActionParams: {} },
+ }),
+ signal,
+ }
+ );
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.test.ts
index 5c814bbfd64505f..ba820efc8111fe3 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.test.ts
@@ -56,12 +56,12 @@ describe('ServiceNow API', () => {
const res = await getChoices({
http,
signal: abortCtrl.signal,
- connectorId: 'test',
+ connectorId: 'te/st',
fields: ['priority'],
});
expect(res).toEqual(choicesResponse);
- expect(http.post).toHaveBeenCalledWith('/api/actions/connector/test/_execute', {
+ expect(http.post).toHaveBeenCalledWith('/api/actions/connector/te%2Fst/_execute', {
body: '{"params":{"subAction":"getChoices","subActionParams":{"fields":["priority"]}}}',
signal: abortCtrl.signal,
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.ts
index bb9091559128545..62347580e75ca13 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/api.ts
@@ -19,10 +19,13 @@ export async function getChoices({
connectorId: string;
fields: string[];
}): Promise> {
- return await http.post(`${BASE_ACTION_API_PATH}/connector/${connectorId}/_execute`, {
- body: JSON.stringify({
- params: { subAction: 'getChoices', subActionParams: { fields } },
- }),
- signal,
- });
+ return await http.post(
+ `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(connectorId)}/_execute`,
+ {
+ body: JSON.stringify({
+ params: { subAction: 'getChoices', subActionParams: { fields } },
+ }),
+ signal,
+ }
+ );
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.test.ts
index bb00c8c30e4edef..ba4c62471555b4e 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.test.ts
@@ -14,7 +14,7 @@ beforeEach(() => jest.resetAllMocks());
describe('deleteActions', () => {
test('should call delete API per action', async () => {
- const ids = ['1', '2', '3'];
+ const ids = ['1', '2', '/'];
const result = await deleteActions({ ids, http });
expect(result).toEqual({ errors: [], successes: [undefined, undefined, undefined] });
@@ -27,7 +27,7 @@ describe('deleteActions', () => {
"/api/actions/connector/2",
],
Array [
- "/api/actions/connector/3",
+ "/api/actions/connector/%2F",
],
]
`);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.ts
index c9c25db676a06dd..868e5390045ccd8 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/delete.ts
@@ -16,7 +16,9 @@ export async function deleteActions({
}): Promise<{ successes: string[]; errors: string[] }> {
const successes: string[] = [];
const errors: string[] = [];
- await Promise.all(ids.map((id) => http.delete(`${BASE_ACTION_API_PATH}/connector/${id}`))).then(
+ await Promise.all(
+ ids.map((id) => http.delete(`${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(id)}`))
+ ).then(
function (fulfilled) {
successes.push(...fulfilled);
},
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.test.ts
index 60cd3132aa756b9..2b0cdcb2ca69b4a 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.test.ts
@@ -14,7 +14,7 @@ beforeEach(() => jest.resetAllMocks());
describe('executeAction', () => {
test('should call execute API', async () => {
- const id = '123';
+ const id = '12/3';
const params = {
stringParams: 'someString',
numericParams: 123,
@@ -32,7 +32,7 @@ describe('executeAction', () => {
});
expect(http.post.mock.calls[0]).toMatchInlineSnapshot(`
Array [
- "/api/actions/connector/123/_execute",
+ "/api/actions/connector/12%2F3/_execute",
Object {
"body": "{\\"params\\":{\\"stringParams\\":\\"someString\\",\\"numericParams\\":123}}",
},
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts
index 638ceddb5652fb5..d97ad7d5962b74a 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/execute.ts
@@ -31,8 +31,11 @@ export async function executeAction({
http: HttpSetup;
params: Record;
}): Promise> {
- const res = await http.post(`${BASE_ACTION_API_PATH}/connector/${id}/_execute`, {
- body: JSON.stringify({ params }),
- });
+ const res = await http.post(
+ `${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(id)}/_execute`,
+ {
+ body: JSON.stringify({ params }),
+ }
+ );
return rewriteBodyRes(res);
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.test.ts
index 29e7a1e4bed3d09..3cee8d225b001e2 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.test.ts
@@ -15,9 +15,9 @@ beforeEach(() => jest.resetAllMocks());
describe('updateActionConnector', () => {
test('should call the update API', async () => {
- const id = '123';
+ const id = '12/3';
const apiResponse = {
- connector_type_id: 'test',
+ connector_type_id: 'te/st',
is_preconfigured: false,
name: 'My test',
config: {},
@@ -27,7 +27,7 @@ describe('updateActionConnector', () => {
http.put.mockResolvedValueOnce(apiResponse);
const connector: ActionConnectorWithoutId<{}, {}> = {
- actionTypeId: 'test',
+ actionTypeId: 'te/st',
isPreconfigured: false,
name: 'My test',
config: {},
@@ -39,7 +39,7 @@ describe('updateActionConnector', () => {
expect(result).toEqual(resolvedValue);
expect(http.put.mock.calls[0]).toMatchInlineSnapshot(`
Array [
- "/api/actions/connector/123",
+ "/api/actions/connector/12%2F3",
Object {
"body": "{\\"name\\":\\"My test\\",\\"config\\":{},\\"secrets\\":{}}",
},
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts
index 18b8871ce25d1ce..1bc0cefc2723b2e 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/update.ts
@@ -30,7 +30,7 @@ export async function updateActionConnector({
connector: Pick;
id: string;
}): Promise {
- const res = await http.put(`${BASE_ACTION_API_PATH}/connector/${id}`, {
+ const res = await http.put(`${BASE_ACTION_API_PATH}/connector/${encodeURIComponent(id)}`, {
body: JSON.stringify({
name: connector.name,
config: connector.config,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.test.ts
index e94da81d0f5d510..c7b987f2b04bdfb 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.test.ts
@@ -18,7 +18,7 @@ describe('loadAlertInstanceSummary', () => {
consumer: 'alerts',
enabled: true,
errorMessages: [],
- id: 'test',
+ id: 'te/st',
lastRun: '2021-04-01T22:18:27.609Z',
muteAll: false,
name: 'test',
@@ -35,7 +35,7 @@ describe('loadAlertInstanceSummary', () => {
consumer: 'alerts',
enabled: true,
error_messages: [],
- id: 'test',
+ id: 'te/st',
last_run: '2021-04-01T22:18:27.609Z',
mute_all: false,
name: 'test',
@@ -47,11 +47,11 @@ describe('loadAlertInstanceSummary', () => {
throttle: null,
});
- const result = await loadAlertInstanceSummary({ http, alertId: 'test' });
+ const result = await loadAlertInstanceSummary({ http, alertId: 'te/st' });
expect(result).toEqual(resolvedValue);
expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
Array [
- "/internal/alerting/rule/test/_alert_summary",
+ "/internal/alerting/rule/te%2Fst/_alert_summary",
]
`);
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.ts
index e37c0640ec1c8f4..cb924db74cea557 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/alert_summary.ts
@@ -36,6 +36,8 @@ export async function loadAlertInstanceSummary({
http: HttpSetup;
alertId: string;
}): Promise {
- const res = await http.get(`${INTERNAL_BASE_ALERTING_API_PATH}/rule/${alertId}/_alert_summary`);
+ const res = await http.get(
+ `${INTERNAL_BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(alertId)}/_alert_summary`
+ );
return rewriteBodyRes(res);
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.test.ts
index b279e4c0237d967..11e5f4763e775e3 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.test.ts
@@ -12,7 +12,7 @@ const http = httpServiceMock.createStartContract();
describe('deleteAlerts', () => {
test('should call delete API for each alert', async () => {
- const ids = ['1', '2', '3'];
+ const ids = ['1', '2', '/'];
const result = await deleteAlerts({ http, ids });
expect(result).toEqual({ errors: [], successes: [undefined, undefined, undefined] });
expect(http.delete.mock.calls).toMatchInlineSnapshot(`
@@ -24,7 +24,7 @@ describe('deleteAlerts', () => {
"/api/alerting/rule/2",
],
Array [
- "/api/alerting/rule/3",
+ "/api/alerting/rule/%2F",
],
]
`);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.ts
index 870d5a409c3dda3..b853e722e6fc366 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/delete.ts
@@ -16,7 +16,9 @@ export async function deleteAlerts({
}): Promise<{ successes: string[]; errors: string[] }> {
const successes: string[] = [];
const errors: string[] = [];
- await Promise.all(ids.map((id) => http.delete(`${BASE_ALERTING_API_PATH}/rule/${id}`))).then(
+ await Promise.all(
+ ids.map((id) => http.delete(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}`))
+ ).then(
function (fulfilled) {
successes.push(...fulfilled);
},
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.test.ts
index 90d1cd13096e848..4323816221c6ed6 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.test.ts
@@ -13,12 +13,12 @@ beforeEach(() => jest.resetAllMocks());
describe('disableAlert', () => {
test('should call disable alert API', async () => {
- const result = await disableAlert({ http, id: '1' });
+ const result = await disableAlert({ http, id: '1/' });
expect(result).toEqual(undefined);
expect(http.post.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
- "/api/alerting/rule/1/_disable",
+ "/api/alerting/rule/1%2F/_disable",
],
]
`);
@@ -27,7 +27,7 @@ describe('disableAlert', () => {
describe('disableAlerts', () => {
test('should call disable alert API per alert', async () => {
- const ids = ['1', '2', '3'];
+ const ids = ['1', '2', '/'];
const result = await disableAlerts({ http, ids });
expect(result).toEqual(undefined);
expect(http.post.mock.calls).toMatchInlineSnapshot(`
@@ -39,7 +39,7 @@ describe('disableAlerts', () => {
"/api/alerting/rule/2/_disable",
],
Array [
- "/api/alerting/rule/3/_disable",
+ "/api/alerting/rule/%2F/_disable",
],
]
`);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.ts
index cc0939fbebfbded..758e66644b34e79 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/disable.ts
@@ -8,7 +8,7 @@ import { HttpSetup } from 'kibana/public';
import { BASE_ALERTING_API_PATH } from '../../constants';
export async function disableAlert({ id, http }: { id: string; http: HttpSetup }): Promise {
- await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/_disable`);
+ await http.post(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/_disable`);
}
export async function disableAlerts({
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.test.ts
index ef65e8b605cba4a..3a54a0772664b87 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.test.ts
@@ -13,12 +13,12 @@ beforeEach(() => jest.resetAllMocks());
describe('enableAlert', () => {
test('should call enable alert API', async () => {
- const result = await enableAlert({ http, id: '1' });
+ const result = await enableAlert({ http, id: '1/' });
expect(result).toEqual(undefined);
expect(http.post.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
- "/api/alerting/rule/1/_enable",
+ "/api/alerting/rule/1%2F/_enable",
],
]
`);
@@ -27,7 +27,7 @@ describe('enableAlert', () => {
describe('enableAlerts', () => {
test('should call enable alert API per alert', async () => {
- const ids = ['1', '2', '3'];
+ const ids = ['1', '2', '/'];
const result = await enableAlerts({ http, ids });
expect(result).toEqual(undefined);
expect(http.post.mock.calls).toMatchInlineSnapshot(`
@@ -39,7 +39,7 @@ describe('enableAlerts', () => {
"/api/alerting/rule/2/_enable",
],
Array [
- "/api/alerting/rule/3/_enable",
+ "/api/alerting/rule/%2F/_enable",
],
]
`);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.ts
index 3c16ffaec6223fd..4bb3e3d45fcaea0 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/enable.ts
@@ -8,7 +8,7 @@ import { HttpSetup } from 'kibana/public';
import { BASE_ALERTING_API_PATH } from '../../constants';
export async function enableAlert({ id, http }: { id: string; http: HttpSetup }): Promise {
- await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/_enable`);
+ await http.post(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/_enable`);
}
export async function enableAlerts({
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.test.ts
index f2d8337eb4091ce..5c71f6433f2b9ab 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.test.ts
@@ -13,9 +13,10 @@ const http = httpServiceMock.createStartContract();
describe('loadAlert', () => {
test('should call get API with base parameters', async () => {
- const alertId = uuid.v4();
+ const alertId = `${uuid.v4()}/`;
+ const alertIdEncoded = encodeURIComponent(alertId);
const resolvedValue = {
- id: '1',
+ id: '1/',
params: {
aggType: 'count',
termSize: 5,
@@ -56,7 +57,7 @@ describe('loadAlert', () => {
http.get.mockResolvedValueOnce(resolvedValue);
expect(await loadAlert({ http, alertId })).toEqual({
- id: '1',
+ id: '1/',
params: {
aggType: 'count',
termSize: 5,
@@ -94,6 +95,6 @@ describe('loadAlert', () => {
},
],
});
- expect(http.get).toHaveBeenCalledWith(`/api/alerting/rule/${alertId}`);
+ expect(http.get).toHaveBeenCalledWith(`/api/alerting/rule/${alertIdEncoded}`);
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.ts
index 2e4cbc9b50c51b8..9fa882c02fa228f 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/get_rule.ts
@@ -16,6 +16,6 @@ export async function loadAlert({
http: HttpSetup;
alertId: string;
}): Promise {
- const res = await http.get(`${BASE_ALERTING_API_PATH}/rule/${alertId}`);
+ const res = await http.get(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(alertId)}`);
return transformAlert(res);
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.test.ts
index 75143dd6b7f8570..804096dbafac895 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.test.ts
@@ -13,12 +13,12 @@ beforeEach(() => jest.resetAllMocks());
describe('muteAlert', () => {
test('should call mute alert API', async () => {
- const result = await muteAlert({ http, id: '1' });
+ const result = await muteAlert({ http, id: '1/' });
expect(result).toEqual(undefined);
expect(http.post.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
- "/api/alerting/rule/1/_mute_all",
+ "/api/alerting/rule/1%2F/_mute_all",
],
]
`);
@@ -27,7 +27,7 @@ describe('muteAlert', () => {
describe('muteAlerts', () => {
test('should call mute alert API per alert', async () => {
- const ids = ['1', '2', '3'];
+ const ids = ['1', '2', '/'];
const result = await muteAlerts({ http, ids });
expect(result).toEqual(undefined);
expect(http.post.mock.calls).toMatchInlineSnapshot(`
@@ -39,7 +39,7 @@ describe('muteAlerts', () => {
"/api/alerting/rule/2/_mute_all",
],
Array [
- "/api/alerting/rule/3/_mute_all",
+ "/api/alerting/rule/%2F/_mute_all",
],
]
`);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.ts
index 22a96d7a11ff38f..888cdfa92c8f5e6 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute.ts
@@ -8,7 +8,7 @@ import { HttpSetup } from 'kibana/public';
import { BASE_ALERTING_API_PATH } from '../../constants';
export async function muteAlert({ id, http }: { id: string; http: HttpSetup }): Promise {
- await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/_mute_all`);
+ await http.post(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/_mute_all`);
}
export async function muteAlerts({ ids, http }: { ids: string[]; http: HttpSetup }): Promise {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.test.ts
index 4365cce42c8c3eb..384bc65754b0334 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.test.ts
@@ -12,12 +12,12 @@ const http = httpServiceMock.createStartContract();
describe('muteAlertInstance', () => {
test('should call mute instance alert API', async () => {
- const result = await muteAlertInstance({ http, id: '1', instanceId: '123' });
+ const result = await muteAlertInstance({ http, id: '1/', instanceId: '12/3' });
expect(result).toEqual(undefined);
expect(http.post.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
- "/api/alerting/rule/1/alert/123/_mute",
+ "/api/alerting/rule/1%2F/alert/12%2F3/_mute",
],
]
`);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.ts
index 0bb05010cfa3c59..05f2417db947220 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/mute_alert.ts
@@ -16,5 +16,9 @@ export async function muteAlertInstance({
instanceId: string;
http: HttpSetup;
}): Promise {
- await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/alert/${instanceId}/_mute`);
+ await http.post(
+ `${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/alert/${encodeURIComponent(
+ instanceId
+ )}/_mute`
+ );
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.test.ts
index 68a6feeb65e1e74..dfaceffcf8f00a3 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.test.ts
@@ -13,7 +13,7 @@ beforeEach(() => jest.resetAllMocks());
describe('unmuteAlerts', () => {
test('should call unmute alert API per alert', async () => {
- const ids = ['1', '2', '3'];
+ const ids = ['1', '2', '/'];
const result = await unmuteAlerts({ http, ids });
expect(result).toEqual(undefined);
expect(http.post.mock.calls).toMatchInlineSnapshot(`
@@ -25,7 +25,7 @@ describe('unmuteAlerts', () => {
"/api/alerting/rule/2/_unmute_all",
],
Array [
- "/api/alerting/rule/3/_unmute_all",
+ "/api/alerting/rule/%2F/_unmute_all",
],
]
`);
@@ -34,12 +34,12 @@ describe('unmuteAlerts', () => {
describe('unmuteAlert', () => {
test('should call unmute alert API', async () => {
- const result = await unmuteAlert({ http, id: '1' });
+ const result = await unmuteAlert({ http, id: '1/' });
expect(result).toEqual(undefined);
expect(http.post.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
- "/api/alerting/rule/1/_unmute_all",
+ "/api/alerting/rule/1%2F/_unmute_all",
],
]
`);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.ts
index c65be6a670a897c..bd2139f05264513 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute.ts
@@ -8,7 +8,7 @@ import { HttpSetup } from 'kibana/public';
import { BASE_ALERTING_API_PATH } from '../../constants';
export async function unmuteAlert({ id, http }: { id: string; http: HttpSetup }): Promise {
- await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/_unmute_all`);
+ await http.post(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/_unmute_all`);
}
export async function unmuteAlerts({
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.test.ts
index c0131cbab0ebf1d..d95c95158b0b7e6 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.test.ts
@@ -12,12 +12,12 @@ const http = httpServiceMock.createStartContract();
describe('unmuteAlertInstance', () => {
test('should call mute instance alert API', async () => {
- const result = await unmuteAlertInstance({ http, id: '1', instanceId: '123' });
+ const result = await unmuteAlertInstance({ http, id: '1/', instanceId: '12/3' });
expect(result).toEqual(undefined);
expect(http.post.mock.calls).toMatchInlineSnapshot(`
Array [
Array [
- "/api/alerting/rule/1/alert/123/_unmute",
+ "/api/alerting/rule/1%2F/alert/12%2F3/_unmute",
],
]
`);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.ts
index 60d2cca72b85e60..2e37aa2c0ee295f 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/unmute_alert.ts
@@ -16,5 +16,9 @@ export async function unmuteAlertInstance({
instanceId: string;
http: HttpSetup;
}): Promise {
- await http.post(`${BASE_ALERTING_API_PATH}/rule/${id}/alert/${instanceId}/_unmute`);
+ await http.post(
+ `${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}/alert/${encodeURIComponent(
+ instanceId
+ )}/_unmute`
+ );
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.test.ts
index 745a94b8d1134b1..3a6059248a3b0b2 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.test.ts
@@ -32,7 +32,7 @@ describe('updateAlert', () => {
};
const resolvedValue: Alert = {
...alertToUpdate,
- id: '123',
+ id: '12/3',
enabled: true,
alertTypeId: 'test',
createdBy: null,
@@ -46,11 +46,11 @@ describe('updateAlert', () => {
};
http.put.mockResolvedValueOnce(resolvedValue);
- const result = await updateAlert({ http, id: '123', alert: alertToUpdate });
+ const result = await updateAlert({ http, id: '12/3', alert: alertToUpdate });
expect(result).toEqual(resolvedValue);
expect(http.put.mock.calls[0]).toMatchInlineSnapshot(`
Array [
- "/api/alerting/rule/123",
+ "/api/alerting/rule/12%2F3",
Object {
"body": "{\\"throttle\\":\\"1m\\",\\"name\\":\\"test\\",\\"tags\\":[\\"foo\\"],\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"params\\":{},\\"notify_when\\":\\"onThrottleInterval\\",\\"actions\\":[]}",
},
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.ts
index 44b9306949f8103..930c0c2fb21a08a 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/update.ts
@@ -41,7 +41,7 @@ export async function updateAlert({
>;
id: string;
}): Promise {
- const res = await http.put(`${BASE_ALERTING_API_PATH}/rule/${id}`, {
+ const res = await http.put(`${BASE_ALERTING_API_PATH}/rule/${encodeURIComponent(id)}`, {
body: JSON.stringify(
rewriteBodyRequest(
pick(alert, ['throttle', 'name', 'tags', 'schedule', 'params', 'actions', 'notifyWhen'])
diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts
index 86d18d98fa0e125..37f6219cf30a561 100644
--- a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.test.ts
@@ -7,8 +7,14 @@
// test error conditions of calling timeSeriesQuery - postive results tested in FT
+import type { estypes } from '@elastic/elasticsearch';
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
-import { TimeSeriesQueryParameters, TimeSeriesQuery, timeSeriesQuery } from './time_series_query';
+import {
+ TimeSeriesQueryParameters,
+ TimeSeriesQuery,
+ timeSeriesQuery,
+ getResultFromEs,
+} from './time_series_query';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { elasticsearchClientMock } from '../../../../../../src/core/server/elasticsearch/client/mocks';
@@ -53,3 +59,135 @@ describe('timeSeriesQuery', () => {
);
});
});
+
+describe('getResultFromEs', () => {
+ it('correctly parses time series results for count aggregation', () => {
+ expect(
+ getResultFromEs(true, false, {
+ took: 0,
+ timed_out: false,
+ _shards: { total: 1, successful: 1, skipped: 0, failed: 0 },
+ hits: { total: { value: 0, relation: 'eq' }, hits: [] },
+ aggregations: {
+ dateAgg: {
+ buckets: [
+ {
+ key: '2021-04-22T15:14:31.075Z-2021-04-22T15:19:31.075Z',
+ from: 1619104471075,
+ from_as_string: '2021-04-22T15:14:31.075Z',
+ to: 1619104771075,
+ to_as_string: '2021-04-22T15:19:31.075Z',
+ doc_count: 0,
+ },
+ ],
+ },
+ },
+ } as estypes.SearchResponse)
+ ).toEqual({
+ results: [
+ {
+ group: 'all documents',
+ metrics: [['2021-04-22T15:19:31.075Z', 0]],
+ },
+ ],
+ });
+ });
+
+ it('correctly parses time series results with no aggregation data for count aggregation', () => {
+ // this could happen with cross cluster searches when cluster permissions are incorrect
+ // the query completes but doesn't return any aggregations
+ expect(
+ getResultFromEs(true, false, {
+ took: 0,
+ timed_out: false,
+ _shards: { total: 0, successful: 0, skipped: 0, failed: 0 },
+ _clusters: { total: 1, successful: 1, skipped: 0 },
+ hits: { total: { value: 0, relation: 'eq' }, hits: [] },
+ } as estypes.SearchResponse)
+ ).toEqual({
+ results: [],
+ });
+ });
+
+ it('correctly parses time series results for group aggregation', () => {
+ expect(
+ getResultFromEs(false, true, {
+ took: 1,
+ timed_out: false,
+ _shards: { total: 1, successful: 1, skipped: 0, failed: 0 },
+ hits: { total: { value: 298, relation: 'eq' }, hits: [] },
+ aggregations: {
+ groupAgg: {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
+ buckets: [
+ {
+ key: 'host-2',
+ doc_count: 149,
+ sortValueAgg: { value: 0.5000000018251423 },
+ dateAgg: {
+ buckets: [
+ {
+ key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z',
+ from: 1619104723191,
+ from_as_string: '2021-04-22T15:18:43.191Z',
+ to: 1619105023191,
+ to_as_string: '2021-04-22T15:23:43.191Z',
+ doc_count: 149,
+ metricAgg: { value: 0.5000000018251423 },
+ },
+ ],
+ },
+ },
+ {
+ key: 'host-1',
+ doc_count: 149,
+ sortValueAgg: { value: 0.5000000011000857 },
+ dateAgg: {
+ buckets: [
+ {
+ key: '2021-04-22T15:18:43.191Z-2021-04-22T15:23:43.191Z',
+ from: 1619104723191,
+ from_as_string: '2021-04-22T15:18:43.191Z',
+ to: 1619105023191,
+ to_as_string: '2021-04-22T15:23:43.191Z',
+ doc_count: 149,
+ metricAgg: { value: 0.5000000011000857 },
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ } as estypes.SearchResponse)
+ ).toEqual({
+ results: [
+ {
+ group: 'host-2',
+ metrics: [['2021-04-22T15:23:43.191Z', 0.5000000018251423]],
+ },
+ {
+ group: 'host-1',
+ metrics: [['2021-04-22T15:23:43.191Z', 0.5000000011000857]],
+ },
+ ],
+ });
+ });
+
+ it('correctly parses time series results with no aggregation data for group aggregation', () => {
+ // this could happen with cross cluster searches when cluster permissions are incorrect
+ // the query completes but doesn't return any aggregations
+ expect(
+ getResultFromEs(false, true, {
+ took: 0,
+ timed_out: false,
+ _shards: { total: 0, successful: 0, skipped: 0, failed: 0 },
+ _clusters: { total: 1, successful: 1, skipped: 0 },
+ hits: { total: { value: 0, relation: 'eq' }, hits: [] },
+ } as estypes.SearchResponse)
+ ).toEqual({
+ results: [],
+ });
+ });
+});
diff --git a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts
index ad044f4570ea3d4..a2ba8d43c9c60ca 100644
--- a/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts
+++ b/x-pack/plugins/triggers_actions_ui/server/data/lib/time_series_query.ts
@@ -147,7 +147,7 @@ export async function timeSeriesQuery(
return getResultFromEs(isCountAgg, isGroupAgg, esResult);
}
-function getResultFromEs(
+export function getResultFromEs(
isCountAgg: boolean,
isGroupAgg: boolean,
esResult: estypes.SearchResponse
@@ -155,8 +155,8 @@ function getResultFromEs(
const aggregations = esResult?.aggregations || {};
// add a fake 'all documents' group aggregation, if a group aggregation wasn't used
- if (!isGroupAgg) {
- const dateAgg = aggregations.dateAgg || {};
+ if (!isGroupAgg && aggregations.dateAgg) {
+ const dateAgg = aggregations.dateAgg;
aggregations.groupAgg = {
buckets: [{ key: 'all documents', dateAgg }],
diff --git a/x-pack/plugins/uptime/public/components/overview/query_bar/query_bar.tsx b/x-pack/plugins/uptime/public/components/overview/query_bar/query_bar.tsx
index 54e2789dc666f7a..0543e5868bb9ec5 100644
--- a/x-pack/plugins/uptime/public/components/overview/query_bar/query_bar.tsx
+++ b/x-pack/plugins/uptime/public/components/overview/query_bar/query_bar.tsx
@@ -36,7 +36,7 @@ export const QueryBar = () => {
const { query, setQuery } = useQueryBar();
- const { index_pattern: indexPattern } = useIndexPattern(query.language ?? SyntaxType.text);
+ const { index_pattern: indexPattern } = useIndexPattern();
const [inputVal, setInputVal] = useState(query.query);
diff --git a/x-pack/plugins/uptime/public/components/overview/query_bar/use_index_pattern.ts b/x-pack/plugins/uptime/public/components/overview/query_bar/use_index_pattern.ts
index ab10afb5b231e52..b0e567c40ed73ce 100644
--- a/x-pack/plugins/uptime/public/components/overview/query_bar/use_index_pattern.ts
+++ b/x-pack/plugins/uptime/public/components/overview/query_bar/use_index_pattern.ts
@@ -9,18 +9,17 @@ import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getIndexPattern } from '../../../state/actions';
import { selectIndexPattern } from '../../../state/selectors';
-import { SyntaxType } from './use_query_bar';
-export const useIndexPattern = (queryLanguage?: string) => {
+export const useIndexPattern = () => {
const dispatch = useDispatch();
const indexPattern = useSelector(selectIndexPattern);
useEffect(() => {
// we only use index pattern for kql queries
- if (!indexPattern.index_pattern && (!queryLanguage || queryLanguage === SyntaxType.kuery)) {
+ if (!indexPattern.index_pattern) {
dispatch(getIndexPattern());
}
- }, [indexPattern.index_pattern, dispatch, queryLanguage]);
+ }, [indexPattern.index_pattern, dispatch]);
return indexPattern;
};
diff --git a/x-pack/plugins/uptime/public/components/overview/query_bar/use_query_bar.ts b/x-pack/plugins/uptime/public/components/overview/query_bar/use_query_bar.ts
index 9e3691497eab6c7..0d8a2ee17994af4 100644
--- a/x-pack/plugins/uptime/public/components/overview/query_bar/use_query_bar.ts
+++ b/x-pack/plugins/uptime/public/components/overview/query_bar/use_query_bar.ts
@@ -44,7 +44,7 @@ export const useQueryBar = () => {
}
);
- const { index_pattern: indexPattern } = useIndexPattern(query.language);
+ const { index_pattern: indexPattern } = useIndexPattern();
const updateUrlParams = useUrlParams()[1];
diff --git a/x-pack/plugins/uptime/public/pages/settings.test.tsx b/x-pack/plugins/uptime/public/pages/settings.test.tsx
index 95fed208f6b0a89..e0b7b70ad46fbe1 100644
--- a/x-pack/plugins/uptime/public/pages/settings.test.tsx
+++ b/x-pack/plugins/uptime/public/pages/settings.test.tsx
@@ -13,7 +13,8 @@ import { act } from 'react-dom/test-utils';
import * as alertApi from '../state/api/alerts';
describe('settings', () => {
- describe('form', () => {
+ // FLAKY: https://github.com/elastic/kibana/issues/97067
+ describe.skip('form', () => {
beforeAll(() => {
jest.spyOn(alertApi, 'fetchActionTypes').mockImplementation(async () => [
{
diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/evaluate.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/evaluate.ts
new file mode 100644
index 000000000000000..e1fa889d20daf23
--- /dev/null
+++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/evaluate.ts
@@ -0,0 +1,188 @@
+/*
+ * 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 expect from '@kbn/expect';
+import { FtrProviderContext } from '../../../ftr_provider_context';
+import { USER } from '../../../../functional/services/ml/security_common';
+import { DataFrameAnalyticsConfig } from '../../../../../plugins/ml/public/application/data_frame_analytics/common';
+import { DeepPartial } from '../../../../../plugins/ml/common/types/common';
+import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api';
+
+export default ({ getService }: FtrProviderContext) => {
+ const esArchiver = getService('esArchiver');
+ const supertest = getService('supertestWithoutAuth');
+ const ml = getService('ml');
+
+ const currentTime = `${Date.now()}`;
+ const generateDestinationIndex = (analyticsId: string) => `user-${analyticsId}`;
+ const jobEval: any = {
+ regression: {
+ index: generateDestinationIndex(`regression_${currentTime}`),
+ evaluation: {
+ regression: {
+ actual_field: 'stab',
+ predicted_field: 'ml.stab_prediction',
+ metrics: {
+ r_squared: {},
+ mse: {},
+ msle: {},
+ huber: {},
+ },
+ },
+ },
+ },
+ classification: {
+ index: generateDestinationIndex(`classification_${currentTime}`),
+ evaluation: {
+ classification: {
+ actual_field: 'y',
+ predicted_field: 'ml.y_prediction',
+ metrics: { multiclass_confusion_matrix: {}, accuracy: {}, recall: {} },
+ },
+ },
+ },
+ };
+ const jobAnalysis: any = {
+ classification: {
+ source: {
+ index: ['ft_bank_marketing'],
+ query: {
+ match_all: {},
+ },
+ },
+ analysis: {
+ classification: {
+ dependent_variable: 'y',
+ training_percent: 20,
+ },
+ },
+ },
+ regression: {
+ source: {
+ index: ['ft_egs_regression'],
+ query: {
+ match_all: {},
+ },
+ },
+ analysis: {
+ regression: {
+ dependent_variable: 'stab',
+ training_percent: 20,
+ },
+ },
+ },
+ };
+
+ interface TestConfig {
+ jobType: string;
+ config: DeepPartial;
+ eval: any;
+ }
+
+ const testJobConfigs: TestConfig[] = ['regression', 'classification'].map((jobType, idx) => {
+ const analyticsId = `${jobType}_${currentTime}`;
+ return {
+ jobType,
+ config: {
+ id: analyticsId,
+ description: `Testing ${jobType} evaluation`,
+ dest: {
+ index: generateDestinationIndex(analyticsId),
+ results_field: 'ml',
+ },
+ analyzed_fields: {
+ includes: [],
+ excludes: [],
+ },
+ model_memory_limit: '60mb',
+ ...jobAnalysis[jobType],
+ },
+ eval: jobEval[jobType],
+ };
+ });
+
+ async function createJobs(mockJobConfigs: TestConfig[]) {
+ for (const jobConfig of mockJobConfigs) {
+ await ml.api.createAndRunDFAJob(jobConfig.config as DataFrameAnalyticsConfig);
+ }
+ }
+
+ describe('POST data_frame/_evaluate', () => {
+ before(async () => {
+ await esArchiver.loadIfNeeded('ml/bm_classification');
+ await esArchiver.loadIfNeeded('ml/egs_regression');
+ await ml.testResources.setKibanaTimeZoneToUTC();
+ await createJobs(testJobConfigs);
+ });
+
+ after(async () => {
+ await ml.api.cleanMlIndices();
+ });
+
+ testJobConfigs.forEach((testConfig) => {
+ describe(`EvaluateDataFrameAnalytics ${testConfig.jobType}`, async () => {
+ it(`should evaluate ${testConfig.jobType} analytics job`, async () => {
+ const { body } = await supertest
+ .post(`/api/ml/data_frame/_evaluate`)
+ .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER))
+ .set(COMMON_REQUEST_HEADERS)
+ .send(testConfig.eval)
+ .expect(200);
+
+ if (testConfig.jobType === 'classification') {
+ const { classification } = body;
+ expect(body).to.have.property('classification');
+ expect(classification).to.have.property('recall');
+ expect(classification).to.have.property('accuracy');
+ expect(classification).to.have.property('multiclass_confusion_matrix');
+ } else {
+ const { regression } = body;
+ expect(body).to.have.property('regression');
+ expect(regression).to.have.property('mse');
+ expect(regression).to.have.property('msle');
+ expect(regression).to.have.property('r_squared');
+ }
+ });
+
+ it(`should evaluate ${testConfig.jobType} job for the user with only view permission`, async () => {
+ const { body } = await supertest
+ .post(`/api/ml/data_frame/_evaluate`)
+ .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER))
+ .set(COMMON_REQUEST_HEADERS)
+ .send(testConfig.eval)
+ .expect(200);
+
+ if (testConfig.jobType === 'classification') {
+ const { classification } = body;
+ expect(body).to.have.property('classification');
+ expect(classification).to.have.property('recall');
+ expect(classification).to.have.property('accuracy');
+ expect(classification).to.have.property('multiclass_confusion_matrix');
+ } else {
+ const { regression } = body;
+ expect(body).to.have.property('regression');
+ expect(regression).to.have.property('mse');
+ expect(regression).to.have.property('msle');
+ expect(regression).to.have.property('r_squared');
+ }
+ });
+
+ it(`should not allow unauthorized user to evaluate ${testConfig.jobType} job`, async () => {
+ const { body } = await supertest
+ .post(`/api/ml/data_frame/_evaluate`)
+ .auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED))
+ .set(COMMON_REQUEST_HEADERS)
+ .send(testConfig.eval)
+ .expect(403);
+
+ expect(body.error).to.eql('Forbidden');
+ expect(body.message).to.eql('Forbidden');
+ });
+ });
+ });
+ });
+};
diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/index.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/index.ts
index 3fdefe2c4bbc241..21ff8f2cc64c16a 100644
--- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/index.ts
+++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/index.ts
@@ -20,6 +20,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./get_spaces'));
loadTestFile(require.resolve('./update_spaces'));
loadTestFile(require.resolve('./delete_spaces'));
+ loadTestFile(require.resolve('./evaluate'));
loadTestFile(require.resolve('./explain'));
});
}
diff --git a/x-pack/test/functional/apps/dashboard/drilldowns/index.ts b/x-pack/test/functional/apps/dashboard/drilldowns/index.ts
index a88389f2498d586..fa24a4ba6a19ee8 100644
--- a/x-pack/test/functional/apps/dashboard/drilldowns/index.ts
+++ b/x-pack/test/functional/apps/dashboard/drilldowns/index.ts
@@ -26,6 +26,8 @@ export default function ({ loadTestFile, getService }: FtrProviderContext) {
loadTestFile(require.resolve('./dashboard_to_dashboard_drilldown'));
loadTestFile(require.resolve('./dashboard_to_url_drilldown'));
+ // Requires xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled
+ // setting set in kibana.yml to work (not enabled by default)
loadTestFile(require.resolve('./explore_data_panel_action'));
// Disabled for now as it requires xpack.discoverEnhanced.actions.exploreDataInChart.enabled
diff --git a/x-pack/test/functional/apps/lens/dashboard.ts b/x-pack/test/functional/apps/lens/dashboard.ts
index 1490abb320ca64e..9998f1dd4cdcb88 100644
--- a/x-pack/test/functional/apps/lens/dashboard.ts
+++ b/x-pack/test/functional/apps/lens/dashboard.ts
@@ -86,6 +86,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(hasIpFilter).to.be(true);
});
+ // Requires xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled
+ // setting set in kibana.yml to work (not enabled by default)
it('should be able to drill down to discover', async () => {
await PageObjects.common.navigateToApp('dashboard');
await PageObjects.dashboard.clickNewDashboard();
diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/anomaly_explorer.ts b/x-pack/test/functional/apps/ml/anomaly_detection/anomaly_explorer.ts
index 66d45c801b81ad4..c713343b3e38041 100644
--- a/x-pack/test/functional/apps/ml/anomaly_detection/anomaly_explorer.ts
+++ b/x-pack/test/functional/apps/ml/anomaly_detection/anomaly_explorer.ts
@@ -324,6 +324,17 @@ export default function ({ getService }: FtrProviderContext) {
await ml.anomalyExplorer.assertAnomalyExplorerChartsCount(0);
});
+ it('allows to change the anomalies table pagination', async () => {
+ await ml.testExecution.logTestStep('displays the anomalies table with default config');
+ await ml.anomaliesTable.assertTableExists();
+ await ml.anomaliesTable.assertRowsNumberPerPage(25);
+ await ml.anomaliesTable.assertTableRowsCount(25);
+
+ await ml.testExecution.logTestStep('updates table pagination');
+ await ml.anomaliesTable.setRowsNumberPerPage(10);
+ await ml.anomaliesTable.assertTableRowsCount(10);
+ });
+
it('adds swim lane embeddable to a dashboard', async () => {
// should be the last step because it navigates away from the Anomaly Explorer page
await ml.testExecution.logTestStep(
diff --git a/x-pack/test/functional/apps/monitoring/index.js b/x-pack/test/functional/apps/monitoring/index.js
index d595400f3e335fe..37d5d2083c4b1d2 100644
--- a/x-pack/test/functional/apps/monitoring/index.js
+++ b/x-pack/test/functional/apps/monitoring/index.js
@@ -15,32 +15,34 @@ export default function ({ loadTestFile }) {
loadTestFile(require.resolve('./cluster/overview'));
// loadTestFile(require.resolve('./cluster/license'));
+ // NOTE: All _mb tests skipped because of various failures: https://github.com/elastic/kibana/issues/98239
+
loadTestFile(require.resolve('./elasticsearch/overview'));
- loadTestFile(require.resolve('./elasticsearch/overview_mb'));
+ // loadTestFile(require.resolve('./elasticsearch/overview_mb'));
loadTestFile(require.resolve('./elasticsearch/nodes'));
- loadTestFile(require.resolve('./elasticsearch/nodes_mb'));
+ // loadTestFile(require.resolve('./elasticsearch/nodes_mb'));
loadTestFile(require.resolve('./elasticsearch/node_detail'));
- loadTestFile(require.resolve('./elasticsearch/node_detail_mb'));
+ // loadTestFile(require.resolve('./elasticsearch/node_detail_mb'));
loadTestFile(require.resolve('./elasticsearch/indices'));
- loadTestFile(require.resolve('./elasticsearch/indices_mb'));
+ // loadTestFile(require.resolve('./elasticsearch/indices_mb'));
loadTestFile(require.resolve('./elasticsearch/index_detail'));
- loadTestFile(require.resolve('./elasticsearch/index_detail_mb'));
+ // loadTestFile(require.resolve('./elasticsearch/index_detail_mb'));
loadTestFile(require.resolve('./elasticsearch/shards'));
// loadTestFile(require.resolve('./elasticsearch/shard_activity'));
loadTestFile(require.resolve('./kibana/overview'));
- loadTestFile(require.resolve('./kibana/overview_mb'));
+ // loadTestFile(require.resolve('./kibana/overview_mb'));
loadTestFile(require.resolve('./kibana/instances'));
- loadTestFile(require.resolve('./kibana/instances_mb'));
+ // loadTestFile(require.resolve('./kibana/instances_mb'));
loadTestFile(require.resolve('./kibana/instance'));
- loadTestFile(require.resolve('./kibana/instance_mb'));
+ // loadTestFile(require.resolve('./kibana/instance_mb'));
// loadTestFile(require.resolve('./logstash/overview'));
// loadTestFile(require.resolve('./logstash/nodes'));
// loadTestFile(require.resolve('./logstash/node'));
loadTestFile(require.resolve('./logstash/pipelines'));
- loadTestFile(require.resolve('./logstash/pipelines_mb'));
+ // loadTestFile(require.resolve('./logstash/pipelines_mb'));
loadTestFile(require.resolve('./beats/cluster'));
loadTestFile(require.resolve('./beats/overview'));
@@ -51,6 +53,6 @@ export default function ({ loadTestFile }) {
loadTestFile(require.resolve('./enable_monitoring'));
loadTestFile(require.resolve('./setup/metricbeat_migration'));
- loadTestFile(require.resolve('./setup/metricbeat_migration_mb'));
+ // loadTestFile(require.resolve('./setup/metricbeat_migration_mb'));
});
}
diff --git a/x-pack/test/functional/apps/monitoring/kibana/instances_mb.js b/x-pack/test/functional/apps/monitoring/kibana/instances_mb.js
index e46b1d161e68a9a..3317513f8157d4b 100644
--- a/x-pack/test/functional/apps/monitoring/kibana/instances_mb.js
+++ b/x-pack/test/functional/apps/monitoring/kibana/instances_mb.js
@@ -13,7 +13,8 @@ export default function ({ getService, getPageObjects }) {
const instances = getService('monitoringKibanaInstances');
const kibanaClusterSummaryStatus = getService('monitoringKibanaSummaryStatus');
- describe('Kibana instances listing mb', () => {
+ // Failing: See https://github.com/elastic/kibana/issues/98190
+ describe.skip('Kibana instances listing mb', () => {
const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects);
before(async () => {
diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js
index 0b22ab920287c96..f171e247472f104 100644
--- a/x-pack/test/functional/config.js
+++ b/x-pack/test/functional/config.js
@@ -90,6 +90,7 @@ export default async function ({ readConfigFile }) {
'--usageCollection.maximumWaitTimeForAllCollectorsInS=1',
'--xpack.security.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', // server restarts should not invalidate active sessions
'--xpack.encryptedSavedObjects.encryptionKey="DkdXazszSCYexXqz4YktBGHCRkV6hyNK"',
+ '--xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled=true',
'--timelion.ui.enabled=true',
'--savedObjects.maxImportPayloadBytes=10485760', // for OSS test management/_import_objects
],
diff --git a/x-pack/test/functional/services/ml/anomalies_table.ts b/x-pack/test/functional/services/ml/anomalies_table.ts
index 30bb3e67bc862d5..52dfaa1a70855d8 100644
--- a/x-pack/test/functional/services/ml/anomalies_table.ts
+++ b/x-pack/test/functional/services/ml/anomalies_table.ts
@@ -22,6 +22,10 @@ export function MachineLearningAnomaliesTableProvider({ getService }: FtrProvide
return await testSubjects.findAll('mlAnomaliesTable > ~mlAnomaliesListRow');
},
+ /**
+ * Asserts the number of rows rendered in a table
+ * @param expectedCount
+ */
async assertTableRowsCount(expectedCount: number) {
const actualCount = (await this.getTableRows()).length;
expect(actualCount).to.eql(
@@ -118,5 +122,32 @@ export function MachineLearningAnomaliesTableProvider({ getService }: FtrProvide
}' (got '${isEnabled ? 'enabled' : 'disabled'}')`
);
},
+
+ /**
+ * Asserts selected number of rows per page on the pagination control.
+ * @param rowsNumber
+ */
+ async assertRowsNumberPerPage(rowsNumber: 10 | 25 | 100) {
+ const textContent = await testSubjects.getVisibleText(
+ 'mlAnomaliesTable > tablePaginationPopoverButton'
+ );
+ expect(textContent).to.be(`Rows per page: ${rowsNumber}`);
+ },
+
+ async ensurePagePopupOpen() {
+ await retry.tryForTime(5000, async () => {
+ const isOpen = await testSubjects.exists('tablePagination-10-rows');
+ if (!isOpen) {
+ await testSubjects.click('mlAnomaliesTable > tablePaginationPopoverButton');
+ await testSubjects.existOrFail('tablePagination-10-rows');
+ }
+ });
+ },
+
+ async setRowsNumberPerPage(rowsNumber: 10 | 25 | 100) {
+ await this.ensurePagePopupOpen();
+ await testSubjects.click(`tablePagination-${rowsNumber}-rows`);
+ await this.assertRowsNumberPerPage(rowsNumber);
+ },
};
}
diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts
index 7b760dfb8b6a196..cbb1d2729e74c1f 100644
--- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts
+++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts
@@ -50,12 +50,25 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
return createdAction;
}
+ async function muteAlert(alertId: string) {
+ const { body: alert } = await supertest
+ .post(`/api/alerting/rule/${alertId}/_mute_all`)
+ .set('kbn-xsrf', 'foo');
+ return alert;
+ }
+
+ async function disableAlert(alertId: string) {
+ const { body: alert } = await supertest
+ .post(`/api/alerting/rule/${alertId}/_disable`)
+ .set('kbn-xsrf', 'foo');
+ return alert;
+ }
+
async function refreshAlertsList() {
await testSubjects.click('rulesTab');
}
- // FLAKY: https://github.com/elastic/kibana/issues/95591
- describe.skip('alerts list', function () {
+ describe('alerts list', function () {
before(async () => {
await pageObjects.common.navigateToApp('triggersActions');
await testSubjects.click('rulesTab');
@@ -138,25 +151,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
it('should re-enable single alert', async () => {
const createdAlert = await createAlert();
+ await disableAlert(createdAlert.id);
await refreshAlertsList();
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
await testSubjects.click('collapsedItemActions');
await pageObjects.triggersActionsUI.toggleSwitch('disableSwitch');
-
- await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
- createdAlert.name,
- 'disableSwitch',
- 'true'
- );
-
- await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
-
- await testSubjects.click('collapsedItemActions');
-
- await pageObjects.triggersActionsUI.toggleSwitch('disableSwitch');
-
await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
createdAlert.name,
'disableSwitch',
@@ -172,7 +173,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await testSubjects.click('collapsedItemActions');
await pageObjects.triggersActionsUI.toggleSwitch('muteSwitch');
-
await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
createdAlert.name,
'muteSwitch',
@@ -182,25 +182,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
it('should unmute single alert', async () => {
const createdAlert = await createAlert();
+ await muteAlert(createdAlert.id);
await refreshAlertsList();
- await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
-
- await testSubjects.click('collapsedItemActions');
-
- await pageObjects.triggersActionsUI.toggleSwitch('muteSwitch');
-
- await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
- createdAlert.name,
- 'muteSwitch',
- 'true'
- );
await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);
await testSubjects.click('collapsedItemActions');
await pageObjects.triggersActionsUI.toggleSwitch('muteSwitch');
-
await pageObjects.triggersActionsUI.ensureRuleActionToggleApplied(
createdAlert.name,
'muteSwitch',
diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
index 97f8b3f61dc892a..b38b605bc1b6788 100644
--- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
+++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts
@@ -93,14 +93,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
async function getAlertInstanceSummary(alertId: string) {
const { body: summary } = await supertest
- .get(`/internal/alerting/rule/${alertId}/_alert_summary`)
+ .get(`/internal/alerting/rule/${encodeURIComponent(alertId)}/_alert_summary`)
.expect(200);
return summary;
}
async function muteAlertInstance(alertId: string, alertInstanceId: string) {
const { body: response } = await supertest
- .post(`/api/alerting/rule/${alertId}/alert/${alertInstanceId}/_mute`)
+ .post(
+ `/api/alerting/rule/${encodeURIComponent(alertId)}/alert/${encodeURIComponent(
+ alertInstanceId
+ )}/_mute`
+ )
.set('kbn-xsrf', 'foo')
.expect(204);
@@ -640,17 +644,17 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
it('renders the muted inactive alert instances', async () => {
// mute an alert instance that doesn't exist
- await muteAlertInstance(alert.id, 'eu-east');
+ await muteAlertInstance(alert.id, 'eu/east');
// refresh to see alert
await browser.refresh();
const instancesList: any[] = await pageObjects.alertDetailsUI.getAlertInstancesList();
expect(
- instancesList.filter((alertInstance) => alertInstance.instance === 'eu-east')
+ instancesList.filter((alertInstance) => alertInstance.instance === 'eu/east')
).to.eql([
{
- instance: 'eu-east',
+ instance: 'eu/east',
status: 'OK',
start: '',
duration: '',
@@ -693,14 +697,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
it('allows the user unmute an inactive instance', async () => {
- log.debug(`Ensuring eu-east is muted`);
- await pageObjects.alertDetailsUI.ensureAlertInstanceMute('eu-east', true);
+ log.debug(`Ensuring eu/east is muted`);
+ await pageObjects.alertDetailsUI.ensureAlertInstanceMute('eu/east', true);
- log.debug(`Unmuting eu-east`);
- await pageObjects.alertDetailsUI.clickAlertInstanceMuteButton('eu-east');
+ log.debug(`Unmuting eu/east`);
+ await pageObjects.alertDetailsUI.clickAlertInstanceMuteButton('eu/east');
- log.debug(`Ensuring eu-east is removed from list`);
- await pageObjects.alertDetailsUI.ensureAlertInstanceExistance('eu-east', false);
+ log.debug(`Ensuring eu/east is removed from list`);
+ await pageObjects.alertDetailsUI.ensureAlertInstanceExistance('eu/east', false);
});
});
diff --git a/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts b/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts
index 5fa442e289037ea..8eeabf1f5d670dd 100644
--- a/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts
+++ b/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts
@@ -146,13 +146,7 @@ export function TriggersActionsPageProvider({ getService }: FtrProviderContext)
},
async toggleSwitch(testSubject: string) {
const switchBtn = await testSubjects.find(testSubject);
- const valueBefore = await switchBtn.getAttribute('aria-checked');
await switchBtn.click();
- await retry.try(async () => {
- const switchBtnAfter = await testSubjects.find(testSubject);
- const valueAfter = await switchBtnAfter.getAttribute('aria-checked');
- expect(valueAfter).not.to.eql(valueBefore);
- });
},
async clickCreateAlertButton() {
const createBtn = await find.byCssSelector(
@@ -194,9 +188,10 @@ export function TriggersActionsPageProvider({ getService }: FtrProviderContext)
switchName: string,
shouldBeCheckedAsString: string
) {
- await retry.try(async () => {
+ await retry.tryForTime(30000, async () => {
await this.searchAlerts(ruleName);
await testSubjects.click('collapsedItemActions');
+
const switchControl = await testSubjects.find(switchName);
const isChecked = await switchControl.getAttribute('aria-checked');
expect(isChecked).to.eql(shouldBeCheckedAsString);
diff --git a/yarn.lock b/yarn.lock
index ed20146e4fa5f14..3ed199a6a3c4f7a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1359,10 +1359,10 @@
dependencies:
object-hash "^1.3.0"
-"@elastic/charts@29.0.0":
- version "29.0.0"
- resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-29.0.0.tgz#6f4ea5bba2caab9700e900fc0bb72685306d1184"
- integrity sha512-df8fYiwOWzO7boIBXMsiWY9oHw5//WZJ2MogJ/38pZeDMRHwjIvQCzj1NL641ijFlFBfWwPSmPur9vbF5xTjbg==
+"@elastic/charts@29.1.0":
+ version "29.1.0"
+ resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-29.1.0.tgz#2850aa30d5e00aa8a1ab4974ea36f3c960a8e457"
+ integrity sha512-/nHT8niLtvSwX3dyEeIQWXEEZrB3xgjLIdlnqZhQXEdHqDQnxlehOMsTqWWws7jS/5uRq/sg+8N2z1xEb+odDw==
dependencies:
"@popperjs/core" "^2.4.0"
chroma-js "^2.1.0"
@@ -1550,7 +1550,7 @@
"@types/node-jose" "1.1.0"
node-jose "1.1.0"
-"@elastic/safer-lodash-set@link:packages/elastic-safer-lodash-set":
+"@elastic/safer-lodash-set@link:bazel-bin/packages/elastic-safer-lodash-set/npm_module":
version "0.0.0"
uid ""
@@ -2585,7 +2585,7 @@
version "0.0.0"
uid ""
-"@kbn/babel-code-parser@link:packages/kbn-babel-code-parser":
+"@kbn/babel-code-parser@link:bazel-bin/packages/kbn-babel-code-parser/npm_module":
version "0.0.0"
uid ""