{
private readonly embeddableFactories: EmbeddableFactoryRegistry = new Map();
private readonly enhancements: EnhancementsRegistry = new Map();
public setup(core: CoreSetup) {
+ const commonContract = {
+ getEmbeddableFactory: this.getEmbeddableFactory,
+ getEnhancement: this.getEnhancement,
+ };
return {
registerEmbeddableFactory: this.registerEmbeddableFactory,
registerEnhancement: this.registerEnhancement,
+ telemetry: getTelemetryFunction(commonContract),
+ extract: getExtractFunction(commonContract),
+ inject: getInjectFunction(commonContract),
+ migrate: getMigrateFunction(commonContract),
};
}
diff --git a/src/plugins/embeddable/server/server.api.md b/src/plugins/embeddable/server/server.api.md
index 87f7d76cffaa85..d3921ab11457c8 100644
--- a/src/plugins/embeddable/server/server.api.md
+++ b/src/plugins/embeddable/server/server.api.md
@@ -18,12 +18,11 @@ export interface EmbeddableRegistryDefinition {
// (undocumented)
registerEmbeddableFactory: (factory: EmbeddableRegistryDefinition) => void;
// (undocumented)
diff --git a/x-pack/plugins/apm/common/runtime_types/to_number_rt/index.ts b/x-pack/plugins/apm/common/runtime_types/to_number_rt/index.ts
new file mode 100644
index 00000000000000..0fe8181c114052
--- /dev/null
+++ b/x-pack/plugins/apm/common/runtime_types/to_number_rt/index.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import * as t from 'io-ts';
+
+export const toNumberRt = new t.Type(
+ 'ToNumber',
+ t.any.is,
+ (input, context) => {
+ const number = Number(input);
+ return !isNaN(number) ? t.success(number) : t.failure(input, context);
+ },
+ t.identity
+);
diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/ServiceListMetric.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/ServiceListMetric.tsx
index c94c94d4a0b72c..716fed7775f7b4 100644
--- a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/ServiceListMetric.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/ServiceListMetric.tsx
@@ -3,15 +3,9 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-
-import { EuiFlexItem } from '@elastic/eui';
-import { EuiFlexGroup } from '@elastic/eui';
-
import React from 'react';
-import { useTheme } from '../../../../hooks/useTheme';
import { useUrlParams } from '../../../../hooks/useUrlParams';
-import { getEmptySeries } from '../../../shared/charts/CustomPlot/getEmptySeries';
-import { SparkPlot } from '../../../shared/charts/SparkPlot';
+import { SparkPlotWithValueLabel } from '../../../shared/charts/spark_plot/spark_plot_with_value_label';
export function ServiceListMetric({
color,
@@ -22,28 +16,17 @@ export function ServiceListMetric({
series?: Array<{ x: number; y: number | null }>;
valueLabel: React.ReactNode;
}) {
- const theme = useTheme();
-
const {
urlParams: { start, end },
} = useUrlParams();
- const colorValue = theme.eui[color];
-
return (
-
-
-
-
-
- {valueLabel}
-
-
+
);
}
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx
index 016ee3daf6b510..ee77157fe4eb36 100644
--- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx
@@ -12,9 +12,10 @@ import { useTrackPageview } from '../../../../../observability/public';
import { isRumAgentName } from '../../../../common/agent_name';
import { ChartsSyncContextProvider } from '../../../context/charts_sync_context';
import { TransactionErrorRateChart } from '../../shared/charts/transaction_error_rate_chart';
-import { ErrorOverviewLink } from '../../shared/Links/apm/ErrorOverviewLink';
import { ServiceMapLink } from '../../shared/Links/apm/ServiceMapLink';
import { TransactionOverviewLink } from '../../shared/Links/apm/TransactionOverviewLink';
+import { ServiceOverviewErrorsTable } from './service_overview_errors_table';
+import { TableLinkFlexItem } from './table_link_flex_item';
const rowHeight = 310;
const latencyChartRowHeight = 230;
@@ -27,12 +28,6 @@ const LatencyChartRow = styled(EuiFlexItem)`
height: ${latencyChartRowHeight}px;
`;
-const TableLinkFlexItem = styled(EuiFlexItem)`
- & > a {
- text-align: right;
- }
-`;
-
interface ServiceOverviewProps {
agentName?: string;
serviceName: string;
@@ -130,30 +125,7 @@ export function ServiceOverview({
)}
-
-
-
-
- {i18n.translate(
- 'xpack.apm.serviceOverview.errorsTableTitle',
- {
- defaultMessage: 'Errors',
- }
- )}
-
-
-
-
-
- {i18n.translate(
- 'xpack.apm.serviceOverview.errorsTableLinkText',
- {
- defaultMessage: 'View errors',
- }
- )}
-
-
-
+
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/fetch_wrapper.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/fetch_wrapper.tsx
new file mode 100644
index 00000000000000..4c8d368811a0c1
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/fetch_wrapper.tsx
@@ -0,0 +1,30 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { FETCH_STATUS } from '../../../../hooks/useFetcher';
+import { ErrorStatePrompt } from '../../../shared/ErrorStatePrompt';
+import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt';
+
+export function FetchWrapper({
+ hasData,
+ status,
+ children,
+}: {
+ hasData: boolean;
+ status: FETCH_STATUS;
+ children: React.ReactNode;
+}) {
+ if (status === FETCH_STATUS.FAILURE) {
+ return ;
+ }
+
+ if (!hasData && status !== FETCH_STATUS.SUCCESS) {
+ return ;
+ }
+
+ return <>{children}>;
+}
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx
new file mode 100644
index 00000000000000..a5a002cf3aca49
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx
@@ -0,0 +1,266 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React, { useState } from 'react';
+import { EuiTitle } from '@elastic/eui';
+import { EuiFlexItem } from '@elastic/eui';
+import { EuiFlexGroup } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { EuiBasicTable } from '@elastic/eui';
+import { EuiBasicTableColumn } from '@elastic/eui';
+import styled from 'styled-components';
+import { EuiToolTip } from '@elastic/eui';
+import { asInteger } from '../../../../../common/utils/formatters';
+import { FETCH_STATUS, useFetcher } from '../../../../hooks/useFetcher';
+import { useUrlParams } from '../../../../hooks/useUrlParams';
+import { ErrorOverviewLink } from '../../../shared/Links/apm/ErrorOverviewLink';
+import { TableLinkFlexItem } from '../table_link_flex_item';
+import { SparkPlotWithValueLabel } from '../../../shared/charts/spark_plot/spark_plot_with_value_label';
+import { callApmApi } from '../../../../services/rest/createCallApmApi';
+import { TimestampTooltip } from '../../../shared/TimestampTooltip';
+import { ErrorDetailLink } from '../../../shared/Links/apm/ErrorDetailLink';
+import { px, truncate, unit } from '../../../../style/variables';
+import { FetchWrapper } from './fetch_wrapper';
+
+interface Props {
+ serviceName: string;
+}
+
+interface ErrorGroupItem {
+ name: string;
+ last_seen: number;
+ group_id: string;
+ occurrences: {
+ value: number;
+ timeseries: Array<{ x: number; y: number }> | null;
+ };
+}
+
+type SortDirection = 'asc' | 'desc';
+type SortField = 'name' | 'last_seen' | 'occurrences';
+
+const PAGE_SIZE = 5;
+const DEFAULT_SORT = {
+ direction: 'desc' as const,
+ field: 'occurrences' as const,
+};
+
+const ErrorDetailLinkWrapper = styled.div`
+ width: 100%;
+ .euiToolTipAnchor {
+ width: 100% !important;
+ }
+`;
+
+const StyledErrorDetailLink = styled(ErrorDetailLink)`
+ display: block;
+ ${truncate('100%')}
+`;
+
+export function ServiceOverviewErrorsTable({ serviceName }: Props) {
+ const {
+ urlParams: { start, end },
+ uiFilters,
+ } = useUrlParams();
+
+ const [tableOptions, setTableOptions] = useState<{
+ pageIndex: number;
+ sort: {
+ direction: SortDirection;
+ field: SortField;
+ };
+ }>({
+ pageIndex: 0,
+ sort: DEFAULT_SORT,
+ });
+
+ const columns: Array> = [
+ {
+ field: 'name',
+ name: i18n.translate('xpack.apm.serviceOverview.errorsTableColumnName', {
+ defaultMessage: 'Name',
+ }),
+ render: (_, { name, group_id: errorGroupId }) => {
+ return (
+
+
+
+ {name}
+
+
+
+ );
+ },
+ },
+ {
+ field: 'last_seen',
+ name: i18n.translate(
+ 'xpack.apm.serviceOverview.errorsTableColumnLastSeen',
+ {
+ defaultMessage: 'Last seen',
+ }
+ ),
+ render: (_, { last_seen: lastSeen }) => {
+ return ;
+ },
+ width: px(unit * 8),
+ },
+ {
+ field: 'occurrences',
+ name: i18n.translate(
+ 'xpack.apm.serviceOverview.errorsTableColumnOccurrences',
+ {
+ defaultMessage: 'Occurrences',
+ }
+ ),
+ width: px(unit * 12),
+ render: (_, { occurrences }) => {
+ return (
+
+ );
+ },
+ },
+ ];
+
+ const {
+ data = {
+ totalItemCount: 0,
+ items: [],
+ tableOptions: {
+ pageIndex: 0,
+ sort: DEFAULT_SORT,
+ },
+ },
+ status,
+ } = useFetcher(() => {
+ if (!start || !end) {
+ return;
+ }
+
+ return callApmApi({
+ pathname: '/api/apm/services/{serviceName}/error_groups',
+ params: {
+ path: { serviceName },
+ query: {
+ start,
+ end,
+ uiFilters: JSON.stringify(uiFilters),
+ size: PAGE_SIZE,
+ numBuckets: 20,
+ pageIndex: tableOptions.pageIndex,
+ sortField: tableOptions.sort.field,
+ sortDirection: tableOptions.sort.direction,
+ },
+ },
+ }).then((response) => {
+ return {
+ items: response.error_groups,
+ totalItemCount: response.total_error_groups,
+ tableOptions: {
+ pageIndex: tableOptions.pageIndex,
+ sort: {
+ field: tableOptions.sort.field,
+ direction: tableOptions.sort.direction,
+ },
+ },
+ };
+ });
+ }, [
+ start,
+ end,
+ serviceName,
+ uiFilters,
+ tableOptions.pageIndex,
+ tableOptions.sort.field,
+ tableOptions.sort.direction,
+ ]);
+
+ const {
+ items,
+ totalItemCount,
+ tableOptions: { pageIndex, sort },
+ } = data;
+
+ return (
+
+
+
+
+
+
+ {i18n.translate('xpack.apm.serviceOverview.errorsTableTitle', {
+ defaultMessage: 'Errors',
+ })}
+
+
+
+
+
+ {i18n.translate('xpack.apm.serviceOverview.errorsTableLinkText', {
+ defaultMessage: 'View errors',
+ })}
+
+
+
+
+
+
+ {
+ setTableOptions({
+ pageIndex: newTableOptions.page?.index ?? 0,
+ sort: newTableOptions.sort
+ ? {
+ field: newTableOptions.sort.field as SortField,
+ direction: newTableOptions.sort.direction,
+ }
+ : DEFAULT_SORT,
+ });
+ }}
+ sorting={{
+ enableAllColumns: true,
+ sort: {
+ direction: sort.direction,
+ field: sort.field,
+ },
+ }}
+ />
+
+
+
+ );
+}
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/table_link_flex_item.tsx b/x-pack/plugins/apm/public/components/app/service_overview/table_link_flex_item.tsx
new file mode 100644
index 00000000000000..35df003af380d4
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/app/service_overview/table_link_flex_item.tsx
@@ -0,0 +1,14 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { EuiFlexItem } from '@elastic/eui';
+import styled from 'styled-components';
+
+export const TableLinkFlexItem = styled(EuiFlexItem)`
+ & > a {
+ text-align: right;
+ }
+`;
diff --git a/x-pack/plugins/apm/public/components/shared/charts/SparkPlot/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/spark_plot/index.tsx
similarity index 100%
rename from x-pack/plugins/apm/public/components/shared/charts/SparkPlot/index.tsx
rename to x-pack/plugins/apm/public/components/shared/charts/spark_plot/index.tsx
diff --git a/x-pack/plugins/apm/public/components/shared/charts/spark_plot/spark_plot_with_value_label/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/spark_plot/spark_plot_with_value_label/index.tsx
new file mode 100644
index 00000000000000..e2bb42fddb33bb
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/shared/charts/spark_plot/spark_plot_with_value_label/index.tsx
@@ -0,0 +1,57 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { EuiFlexItem } from '@elastic/eui';
+import { EuiFlexGroup } from '@elastic/eui';
+
+import React from 'react';
+import { useTheme } from '../../../../../hooks/useTheme';
+import { getEmptySeries } from '../../CustomPlot/getEmptySeries';
+import { SparkPlot } from '../';
+
+type Color =
+ | 'euiColorVis0'
+ | 'euiColorVis1'
+ | 'euiColorVis2'
+ | 'euiColorVis3'
+ | 'euiColorVis4'
+ | 'euiColorVis5'
+ | 'euiColorVis6'
+ | 'euiColorVis7'
+ | 'euiColorVis8'
+ | 'euiColorVis9';
+
+export function SparkPlotWithValueLabel({
+ start,
+ end,
+ color,
+ series,
+ valueLabel,
+}: {
+ start: number;
+ end: number;
+ color: Color;
+ series?: Array<{ x: number; y: number | null }>;
+ valueLabel: React.ReactNode;
+}) {
+ const theme = useTheme();
+
+ const colorValue = theme.eui[color];
+
+ return (
+
+
+
+
+
+ {valueLabel}
+
+
+ );
+}
diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
index d734a1395fc5e4..97c03924538c80 100644
--- a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
+++ b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
@@ -16,6 +16,7 @@ import {
} from '../../../common/elasticsearch_fieldnames';
import { getErrorGroupsProjection } from '../../projections/errors';
import { mergeProjection } from '../../projections/util/merge_projection';
+import { getErrorName } from '../helpers/get_error_name';
import { Setup, SetupTimeRange } from '../helpers/setup_request';
export type ErrorGroupListAPIResponse = PromiseReturnType<
@@ -93,8 +94,7 @@ export async function getErrorGroups({
// this is an exception rather than the rule so the ES type does not account for this.
const hits = (resp.aggregations?.error_groups.buckets || []).map((bucket) => {
const source = bucket.sample.hits.hits[0]._source;
- const message =
- source.error.log?.message || source.error.exception?.[0]?.message;
+ const message = getErrorName(source);
return {
message,
diff --git a/x-pack/plugins/apm/server/lib/helpers/get_error_name.ts b/x-pack/plugins/apm/server/lib/helpers/get_error_name.ts
new file mode 100644
index 00000000000000..dbc69592a4f8e8
--- /dev/null
+++ b/x-pack/plugins/apm/server/lib/helpers/get_error_name.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { APMError } from '../../../typings/es_schemas/ui/apm_error';
+
+export function getErrorName({ error }: APMError) {
+ return error.log?.message || error.exception?.[0]?.message;
+}
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts
new file mode 100644
index 00000000000000..99d978116180b7
--- /dev/null
+++ b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/index.ts
@@ -0,0 +1,177 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { ValuesType } from 'utility-types';
+import { orderBy } from 'lodash';
+import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n';
+import { PromiseReturnType } from '../../../../../observability/typings/common';
+import { rangeFilter } from '../../../../common/utils/range_filter';
+import { ProcessorEvent } from '../../../../common/processor_event';
+import {
+ ERROR_EXC_MESSAGE,
+ ERROR_GROUP_ID,
+ ERROR_LOG_MESSAGE,
+ SERVICE_NAME,
+} from '../../../../common/elasticsearch_fieldnames';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
+import { getBucketSize } from '../../helpers/get_bucket_size';
+import { getErrorName } from '../../helpers/get_error_name';
+
+export type ServiceErrorGroupItem = ValuesType<
+ PromiseReturnType
+>;
+
+export async function getServiceErrorGroups({
+ serviceName,
+ setup,
+ size,
+ numBuckets,
+ pageIndex,
+ sortDirection,
+ sortField,
+}: {
+ serviceName: string;
+ setup: Setup & SetupTimeRange;
+ size: number;
+ pageIndex: number;
+ numBuckets: number;
+ sortDirection: 'asc' | 'desc';
+ sortField: 'name' | 'last_seen' | 'occurrences';
+}) {
+ const { apmEventClient, start, end, esFilter } = setup;
+
+ const { intervalString } = getBucketSize(start, end, numBuckets);
+
+ const response = await apmEventClient.search({
+ apm: {
+ events: [ProcessorEvent.error],
+ },
+ body: {
+ size: 0,
+ query: {
+ bool: {
+ filter: [
+ { term: { [SERVICE_NAME]: serviceName } },
+ { range: rangeFilter(start, end) },
+ ...esFilter,
+ ],
+ },
+ },
+ aggs: {
+ error_groups: {
+ terms: {
+ field: ERROR_GROUP_ID,
+ size: 500,
+ order: {
+ _count: 'desc',
+ },
+ },
+ aggs: {
+ sample: {
+ top_hits: {
+ size: 1,
+ _source: [ERROR_LOG_MESSAGE, ERROR_EXC_MESSAGE, '@timestamp'],
+ sort: {
+ '@timestamp': 'desc',
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ });
+
+ const errorGroups =
+ response.aggregations?.error_groups.buckets.map((bucket) => ({
+ group_id: bucket.key as string,
+ name:
+ getErrorName(bucket.sample.hits.hits[0]._source) ?? NOT_AVAILABLE_LABEL,
+ last_seen: new Date(
+ bucket.sample.hits.hits[0]?._source['@timestamp']
+ ).getTime(),
+ occurrences: {
+ value: bucket.doc_count,
+ },
+ })) ?? [];
+
+ // Sort error groups first, and only get timeseries for data in view.
+ // This is to limit the possibility of creating too many buckets.
+
+ const sortedAndSlicedErrorGroups = orderBy(
+ errorGroups,
+ (group) => {
+ if (sortField === 'occurrences') {
+ return group.occurrences.value;
+ }
+ return group[sortField];
+ },
+ [sortDirection]
+ ).slice(pageIndex * size, pageIndex * size + size);
+
+ const sortedErrorGroupIds = sortedAndSlicedErrorGroups.map(
+ (group) => group.group_id
+ );
+
+ const timeseriesResponse = await apmEventClient.search({
+ apm: {
+ events: [ProcessorEvent.error],
+ },
+ body: {
+ size: 0,
+ query: {
+ bool: {
+ filter: [
+ { terms: { [ERROR_GROUP_ID]: sortedErrorGroupIds } },
+ { term: { [SERVICE_NAME]: serviceName } },
+ { range: rangeFilter(start, end) },
+ ...esFilter,
+ ],
+ },
+ },
+ aggs: {
+ error_groups: {
+ terms: {
+ field: ERROR_GROUP_ID,
+ size,
+ },
+ aggs: {
+ timeseries: {
+ date_histogram: {
+ field: '@timestamp',
+ fixed_interval: intervalString,
+ min_doc_count: 0,
+ extended_bounds: {
+ min: start,
+ max: end,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ });
+
+ return {
+ total_error_groups: errorGroups.length,
+ is_aggregation_accurate:
+ (response.aggregations?.error_groups.sum_other_doc_count ?? 0) === 0,
+ error_groups: sortedAndSlicedErrorGroups.map((errorGroup) => ({
+ ...errorGroup,
+ occurrences: {
+ ...errorGroup.occurrences,
+ timeseries:
+ timeseriesResponse.aggregations?.error_groups.buckets
+ .find((bucket) => bucket.key === errorGroup.group_id)
+ ?.timeseries.buckets.map((dateBucket) => ({
+ x: dateBucket.key,
+ y: dateBucket.doc_count,
+ })) ?? null,
+ },
+ })),
+ };
+}
diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts
index 2fbe404a70d829..34551c35ee2342 100644
--- a/x-pack/plugins/apm/server/routes/create_apm_api.ts
+++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts
@@ -21,6 +21,7 @@ import {
serviceNodeMetadataRoute,
serviceAnnotationsRoute,
serviceAnnotationsCreateRoute,
+ serviceErrorGroupsRoute,
} from './services';
import {
agentConfigurationRoute,
@@ -115,6 +116,7 @@ const createApmApi = () => {
.add(serviceNodeMetadataRoute)
.add(serviceAnnotationsRoute)
.add(serviceAnnotationsCreateRoute)
+ .add(serviceErrorGroupsRoute)
// Agent configuration
.add(getSingleAgentConfigurationRoute)
diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts
index 590b6c49d71bf8..ada1674d4555db 100644
--- a/x-pack/plugins/apm/server/routes/services.ts
+++ b/x-pack/plugins/apm/server/routes/services.ts
@@ -17,6 +17,8 @@ import { uiFiltersRt, rangeRt } from './default_api_types';
import { getServiceAnnotations } from '../lib/services/annotations';
import { dateAsStringRt } from '../../common/runtime_types/date_as_string_rt';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
+import { getServiceErrorGroups } from '../lib/services/get_service_error_groups';
+import { toNumberRt } from '../../common/runtime_types/to_number_rt';
export const servicesRoute = createRoute(() => ({
path: '/api/apm/services',
@@ -195,3 +197,45 @@ export const serviceAnnotationsCreateRoute = createRoute(() => ({
});
},
}));
+
+export const serviceErrorGroupsRoute = createRoute(() => ({
+ path: '/api/apm/services/{serviceName}/error_groups',
+ params: {
+ path: t.type({
+ serviceName: t.string,
+ }),
+ query: t.intersection([
+ rangeRt,
+ uiFiltersRt,
+ t.type({
+ size: toNumberRt,
+ numBuckets: toNumberRt,
+ pageIndex: toNumberRt,
+ sortDirection: t.union([t.literal('asc'), t.literal('desc')]),
+ sortField: t.union([
+ t.literal('last_seen'),
+ t.literal('occurrences'),
+ t.literal('name'),
+ ]),
+ }),
+ ]),
+ },
+ handler: async ({ context, request }) => {
+ const setup = await setupRequest(context, request);
+
+ const {
+ path: { serviceName },
+ query: { size, numBuckets, pageIndex, sortDirection, sortField },
+ } = context.params;
+
+ return getServiceErrorGroups({
+ serviceName,
+ setup,
+ size,
+ numBuckets,
+ pageIndex,
+ sortDirection,
+ sortField,
+ });
+ },
+}));
diff --git a/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/constants.ts b/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/constants.ts
new file mode 100644
index 00000000000000..922ec36619a4b9
--- /dev/null
+++ b/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/constants.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+/**
+ * NOTE: DO NOT CHANGE THIS STRING WITHOUT CAREFUL CONSIDERATOIN, BECAUSE IT IS
+ * STORED IN SAVED OBJECTS.
+ *
+ * Also temporary dashboard drilldown migration code inside embeddable plugin relies on it
+ * x-pack/plugins/embeddable_enhanced/public/embeddables/embeddable_action_storage.ts
+ */
+export const EMBEDDABLE_TO_DASHBOARD_DRILLDOWN = 'DASHBOARD_TO_DASHBOARD_DRILLDOWN';
diff --git a/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/dashboard_drilldown_persistable_state.test.ts b/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/dashboard_drilldown_persistable_state.test.ts
new file mode 100644
index 00000000000000..dd890b2463226b
--- /dev/null
+++ b/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/dashboard_drilldown_persistable_state.test.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { createExtract, createInject } from './dashboard_drilldown_persistable_state';
+import { SerializedEvent } from '../../../../ui_actions_enhanced/common';
+
+const drilldownId = 'test_id';
+const extract = createExtract({ drilldownId });
+const inject = createInject({ drilldownId });
+
+const state: SerializedEvent = {
+ eventId: 'event_id',
+ triggers: [],
+ action: {
+ factoryId: drilldownId,
+ name: 'name',
+ config: {
+ dashboardId: 'dashboardId_1',
+ },
+ },
+};
+
+test('should extract and injected dashboard reference', () => {
+ const { state: extractedState, references } = extract(state);
+ expect(extractedState).not.toEqual(state);
+ expect(extractedState.action.config.dashboardId).toBeUndefined();
+ expect(references).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "id": "dashboardId_1",
+ "name": "drilldown:test_id:event_id:dashboardId",
+ "type": "dashboard",
+ },
+ ]
+ `);
+
+ let injectedState = inject(extractedState, references);
+ expect(injectedState).toEqual(state);
+
+ references[0].id = 'dashboardId_2';
+
+ injectedState = inject(extractedState, references);
+ expect(injectedState).not.toEqual(extractedState);
+ expect(injectedState.action.config.dashboardId).toBe('dashboardId_2');
+});
diff --git a/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/dashboard_drilldown_persistable_state.ts b/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/dashboard_drilldown_persistable_state.ts
new file mode 100644
index 00000000000000..bd972723c649b2
--- /dev/null
+++ b/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/dashboard_drilldown_persistable_state.ts
@@ -0,0 +1,75 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { SavedObjectReference } from '../../../../../../src/core/types';
+import { PersistableStateService } from '../../../../../../src/plugins/kibana_utils/common';
+import { SerializedAction, SerializedEvent } from '../../../../ui_actions_enhanced/common';
+import { DrilldownConfig } from './types';
+
+type DashboardDrilldownPersistableState = PersistableStateService;
+
+const generateRefName = (state: SerializedEvent, id: string) =>
+ `drilldown:${id}:${state.eventId}:dashboardId`;
+
+const injectDashboardId = (state: SerializedEvent, dashboardId: string): SerializedEvent => {
+ return {
+ ...state,
+ action: {
+ ...state.action,
+ config: {
+ ...state.action.config,
+ dashboardId,
+ },
+ },
+ };
+};
+
+export const createInject = ({
+ drilldownId,
+}: {
+ drilldownId: string;
+}): DashboardDrilldownPersistableState['inject'] => {
+ return (state: SerializedEvent, references: SavedObjectReference[]) => {
+ const action = state.action as SerializedAction;
+ const refName = generateRefName(state, drilldownId);
+ const ref = references.find((r) => r.name === refName);
+ if (!ref) return state;
+ if (ref.id && ref.id === action.config.dashboardId) return state;
+ return injectDashboardId(state, ref.id);
+ };
+};
+
+export const createExtract = ({
+ drilldownId,
+}: {
+ drilldownId: string;
+}): DashboardDrilldownPersistableState['extract'] => {
+ return (state: SerializedEvent) => {
+ const action = state.action as SerializedAction;
+ const references: SavedObjectReference[] = action.config.dashboardId
+ ? [
+ {
+ name: generateRefName(state, drilldownId),
+ type: 'dashboard',
+ id: action.config.dashboardId,
+ },
+ ]
+ : [];
+
+ const { dashboardId, ...restOfConfig } = action.config;
+
+ return {
+ state: {
+ ...state,
+ action: ({
+ ...state.action,
+ config: restOfConfig,
+ } as unknown) as SerializedAction,
+ },
+ references,
+ };
+ };
+};
diff --git a/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/index.ts b/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/index.ts
new file mode 100644
index 00000000000000..f6a757ad7a1801
--- /dev/null
+++ b/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/index.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { createExtract, createInject } from './dashboard_drilldown_persistable_state';
+export { EMBEDDABLE_TO_DASHBOARD_DRILLDOWN } from './constants';
+export { DrilldownConfig } from './types';
diff --git a/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/types.ts b/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/types.ts
new file mode 100644
index 00000000000000..3be2a9739837ed
--- /dev/null
+++ b/x-pack/plugins/dashboard_enhanced/common/drilldowns/dashboard_drilldown/types.ts
@@ -0,0 +1,12 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
+export type DrilldownConfig = {
+ dashboardId?: string;
+ useCurrentFilters: boolean;
+ useCurrentDateRange: boolean;
+};
diff --git a/x-pack/plugins/dashboard_enhanced/common/drilldowns/index.ts b/x-pack/plugins/dashboard_enhanced/common/drilldowns/index.ts
new file mode 100644
index 00000000000000..76c9abbd4bfbe3
--- /dev/null
+++ b/x-pack/plugins/dashboard_enhanced/common/drilldowns/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export * from './dashboard_drilldown';
diff --git a/x-pack/plugins/dashboard_enhanced/common/index.ts b/x-pack/plugins/dashboard_enhanced/common/index.ts
new file mode 100644
index 00000000000000..8cc3e129065311
--- /dev/null
+++ b/x-pack/plugins/dashboard_enhanced/common/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export * from './drilldowns';
diff --git a/x-pack/plugins/dashboard_enhanced/kibana.json b/x-pack/plugins/dashboard_enhanced/kibana.json
index f79a69c9f4aba5..b24c0b6983f407 100644
--- a/x-pack/plugins/dashboard_enhanced/kibana.json
+++ b/x-pack/plugins/dashboard_enhanced/kibana.json
@@ -1,7 +1,7 @@
{
"id": "dashboardEnhanced",
"version": "kibana",
- "server": false,
+ "server": true,
"ui": true,
"requiredPlugins": ["data", "uiActionsEnhanced", "embeddable", "dashboard", "share"],
"configPath": ["xpack", "dashboardEnhanced"],
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/abstract_dashboard_drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/abstract_dashboard_drilldown.tsx
index b098d66619814e..451254efd96485 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/abstract_dashboard_drilldown.tsx
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/abstract_dashboard_drilldown.tsx
@@ -9,19 +9,19 @@ import { DataPublicPluginStart } from 'src/plugins/data/public';
import { DashboardStart } from 'src/plugins/dashboard/public';
import { reactToUiComponent } from '../../../../../../../src/plugins/kibana_react/public';
import {
- TriggerId,
TriggerContextMapping,
+ TriggerId,
} from '../../../../../../../src/plugins/ui_actions/public';
import { CollectConfigContainer } from './components';
import {
- UiActionsEnhancedDrilldownDefinition as Drilldown,
- UiActionsEnhancedBaseActionFactoryContext as BaseActionFactoryContext,
AdvancedUiActionsStart,
+ UiActionsEnhancedBaseActionFactoryContext as BaseActionFactoryContext,
+ UiActionsEnhancedDrilldownDefinition as Drilldown,
} from '../../../../../ui_actions_enhanced/public';
import { txtGoToDashboard } from './i18n';
import {
- StartServicesGetter,
CollectConfigProps,
+ StartServicesGetter,
} from '../../../../../../../src/plugins/kibana_utils/public';
import { KibanaURL } from '../../../../../../../src/plugins/share/public';
import { Config } from './types';
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/types.ts b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/types.ts
index 330a501a78d391..7f5137812ee324 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/types.ts
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/abstract_dashboard_drilldown/types.ts
@@ -6,12 +6,8 @@
import { UiActionsEnhancedBaseActionFactoryContext } from '../../../../../ui_actions_enhanced/public';
import { APPLY_FILTER_TRIGGER } from '../../../../../../../src/plugins/ui_actions/public';
+import { DrilldownConfig } from '../../../../common';
-// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
-export type Config = {
- dashboardId?: string;
- useCurrentFilters: boolean;
- useCurrentDateRange: boolean;
-};
+export type Config = DrilldownConfig;
export type FactoryContext = UiActionsEnhancedBaseActionFactoryContext;
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/embeddable_to_dashboard_drilldown/embeddable_to_dashboard_drilldown.test.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/embeddable_to_dashboard_drilldown/embeddable_to_dashboard_drilldown.test.tsx
index f6de2ba931c58d..5bfb175ea0d00c 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/embeddable_to_dashboard_drilldown/embeddable_to_dashboard_drilldown.test.tsx
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/embeddable_to_dashboard_drilldown/embeddable_to_dashboard_drilldown.test.tsx
@@ -65,6 +65,12 @@ test('getHref is defined', () => {
expect(drilldown.getHref).toBeDefined();
});
+test('inject/extract are defined', () => {
+ const drilldown = new EmbeddableToDashboardDrilldown({} as any);
+ expect(drilldown.extract).toBeDefined();
+ expect(drilldown.inject).toBeDefined();
+});
+
describe('.execute() & getHref', () => {
/**
* A convenience test setup helper
diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/embeddable_to_dashboard_drilldown/embeddable_to_dashboard_drilldown.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/embeddable_to_dashboard_drilldown/embeddable_to_dashboard_drilldown.tsx
index 25bc93ad38b368..921c2aed00624a 100644
--- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/embeddable_to_dashboard_drilldown/embeddable_to_dashboard_drilldown.tsx
+++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/embeddable_to_dashboard_drilldown/embeddable_to_dashboard_drilldown.tsx
@@ -22,6 +22,7 @@ import {
} from '../abstract_dashboard_drilldown';
import { KibanaURL } from '../../../../../../../src/plugins/share/public';
import { EMBEDDABLE_TO_DASHBOARD_DRILLDOWN } from './constants';
+import { createExtract, createInject } from '../../../../common';
type Trigger = typeof APPLY_FILTER_TRIGGER;
type Context = TriggerContextMapping[Trigger];
@@ -80,4 +81,8 @@ export class EmbeddableToDashboardDrilldown extends AbstractDashboardDrilldown {
+ constructor(protected readonly context: PluginInitializerContext) {}
+
+ public setup(core: CoreSetup, plugins: SetupDependencies): SetupContract {
+ plugins.uiActionsEnhanced.registerActionFactory({
+ id: EMBEDDABLE_TO_DASHBOARD_DRILLDOWN,
+ inject: createInject({ drilldownId: EMBEDDABLE_TO_DASHBOARD_DRILLDOWN }),
+ extract: createExtract({ drilldownId: EMBEDDABLE_TO_DASHBOARD_DRILLDOWN }),
+ });
+
+ return {};
+ }
+
+ public start(core: CoreStart, plugins: StartDependencies): StartContract {
+ return {};
+ }
+
+ public stop() {}
+}
diff --git a/x-pack/plugins/enterprise_search/common/version.ts b/x-pack/plugins/enterprise_search/common/version.ts
index e29ad8a9f866b9..c23b05f7cdb3d8 100644
--- a/x-pack/plugins/enterprise_search/common/version.ts
+++ b/x-pack/plugins/enterprise_search/common/version.ts
@@ -8,4 +8,4 @@ import { SemVer } from 'semver';
import pkg from '../../../../package.json';
export const CURRENT_VERSION = new SemVer(pkg.version as string);
-export const CURRENT_MAJOR_VERSION = CURRENT_VERSION.major;
+export const CURRENT_MAJOR_VERSION = `${CURRENT_VERSION.major}.${CURRENT_VERSION.minor}`;
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/constants.ts
index ea4906ec08946b..2b96e3322cd555 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/constants.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/constants.ts
@@ -5,6 +5,7 @@
*/
import { i18n } from '@kbn/i18n';
+import { DOCS_PREFIX } from '../../routes';
export const CREDENTIALS_TITLE = i18n.translate(
'xpack.enterpriseSearch.appSearch.credentials.title',
@@ -100,4 +101,4 @@ export const TOKEN_TYPE_INFO = [
export const FLYOUT_ARIA_LABEL_ID = 'credentialsFlyoutTitle';
-export const DOCS_HREF = 'https://www.elastic.co/guide/en/app-search/current/authentication.html';
+export const DOCS_HREF = `${DOCS_PREFIX}/authentication.html`;
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_panel.tsx
index 23572074b3c692..3297f0df4d7bd2 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_panel.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_panel.tsx
@@ -10,6 +10,8 @@ import { i18n } from '@kbn/i18n';
import { EuiLink, EuiSpacer, EuiSwitch, EuiText, EuiTextColor, EuiTitle } from '@elastic/eui';
import { useActions, useValues } from 'kea';
+import { DOCS_PREFIX } from '../../../routes';
+
import { LogRetentionLogic } from './log_retention_logic';
import { AnalyticsLogRetentionMessage, ApiLogRetentionMessage } from './messaging';
import { LogRetentionOptions } from './types';
@@ -41,10 +43,7 @@ export const LogRetentionPanel: React.FC = () => {
{i18n.translate('xpack.enterpriseSearch.appSearch.settings.logRetention.description', {
defaultMessage: 'Manage the default write settings for API Logs and Analytics.',
})}{' '}
-
+
{i18n.translate('xpack.enterpriseSearch.appSearch.settings.logRetention.learnMore', {
defaultMessage: 'Learn more about retention settings.',
})}
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx
index 60d7f6951a478c..b3faa73dfaed6a 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx
@@ -13,14 +13,15 @@ import { APP_SEARCH_PLUGIN } from '../../../../../common/constants';
import { SetupGuide as SetupGuideLayout } from '../../../shared/setup_guide';
import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome';
import { SendAppSearchTelemetry as SendTelemetry } from '../../../shared/telemetry';
+import { DOCS_PREFIX } from '../../routes';
import GettingStarted from './assets/getting_started.png';
export const SetupGuide: React.FC = () => (
= {};
const MANIFEST_NAME = 'manifest.yml';
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts
index c5c9e8ac2c01ba..b6988f64843d09 100644
--- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts
@@ -11,8 +11,7 @@ import {
ElasticsearchAssetType,
InstallablePackage,
} from '../../../../types';
-import { ArchiveEntry } from '../../registry';
-import { getAsset, getPathParts } from '../../archive';
+import { ArchiveEntry, getAsset, getPathParts } from '../../archive';
import { CallESAsCurrentUser } from '../../../../types';
import { saveInstalledEsRefs } from '../../packages/install';
import { getInstallationObject } from '../../packages';
diff --git a/x-pack/plugins/fleet/server/services/epm/packages/assets.ts b/x-pack/plugins/fleet/server/services/epm/packages/assets.ts
index 50d8f2f4d2fb22..80e1cbba6484b6 100644
--- a/x-pack/plugins/fleet/server/services/epm/packages/assets.ts
+++ b/x-pack/plugins/fleet/server/services/epm/packages/assets.ts
@@ -6,7 +6,7 @@
import { InstallablePackage } from '../../../types';
import * as Registry from '../registry';
-import { getArchiveFilelist, getAsset } from '../archive';
+import { ArchiveEntry, getArchiveFilelist, getAsset } from '../archive';
// paths from RegistryPackage are routes to the assets on EPR
// e.g. `/package/nginx/1.2.0/data_stream/access/fields/fields.yml`
@@ -51,14 +51,14 @@ export async function getAssetsData(
packageInfo: InstallablePackage,
filter = (path: string): boolean => true,
datasetName?: string
-): Promise {
+): Promise {
// TODO: Needs to be called to fill the cache but should not be required
await Registry.ensureCachedArchiveInfo(packageInfo.name, packageInfo.version, 'registry');
// Gather all asset data
const assets = getAssets(packageInfo, filter, datasetName);
- const entries: Registry.ArchiveEntry[] = assets.map((path) => {
+ const entries: ArchiveEntry[] = assets.map((path) => {
const buffer = getAsset(path);
return { path, buffer };
diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.test.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.test.ts
index 1208ffdaefe4af..aea28b5d56ab9d 100644
--- a/x-pack/plugins/fleet/server/services/epm/registry/index.test.ts
+++ b/x-pack/plugins/fleet/server/services/epm/registry/index.test.ts
@@ -5,9 +5,8 @@
*/
import { AssetParts } from '../../../types';
-import { getPathParts } from '../archive';
-import { getBufferExtractor, splitPkgKey } from './index';
-import { untarBuffer, unzipBuffer } from './extract';
+import { getBufferExtractor, getPathParts, untarBuffer, unzipBuffer } from '../archive';
+import { splitPkgKey } from './index';
const testPaths = [
{
diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts
index c35e91bdf580bd..aef1bb75619cdf 100644
--- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts
+++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts
@@ -24,13 +24,11 @@ import {
unpackArchiveToCache,
} from '../archive';
import { fetchUrl, getResponse, getResponseStream } from './requests';
-import { streamToBuffer } from './streams';
+import { streamToBuffer } from '../streams';
import { getRegistryUrl } from './registry_url';
import { appContextService } from '../..';
import { PackageNotFoundError, PackageCacheError } from '../../../errors';
-export { ArchiveEntry, getBufferExtractor } from './extract';
-
export interface SearchParams {
category?: CategoryId;
experimental?: boolean;
diff --git a/x-pack/plugins/fleet/server/services/epm/registry/requests.ts b/x-pack/plugins/fleet/server/services/epm/registry/requests.ts
index 2b9c3495657903..c8d158c8afaaaa 100644
--- a/x-pack/plugins/fleet/server/services/epm/registry/requests.ts
+++ b/x-pack/plugins/fleet/server/services/epm/registry/requests.ts
@@ -6,7 +6,7 @@
import fetch, { FetchError, Response, RequestInit } from 'node-fetch';
import pRetry from 'p-retry';
-import { streamToString } from './streams';
+import { streamToString } from '../streams';
import { appContextService } from '../../app_context';
import { RegistryError, RegistryConnectionError, RegistryResponseError } from '../../../errors';
import { getProxyAgent, getRegistryProxyUrl } from './proxy';
diff --git a/x-pack/plugins/fleet/server/services/epm/registry/streams.ts b/x-pack/plugins/fleet/server/services/epm/streams.ts
similarity index 100%
rename from x-pack/plugins/fleet/server/services/epm/registry/streams.ts
rename to x-pack/plugins/fleet/server/services/epm/streams.ts
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts b/x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts
index 3666528f431667..1f337298a03adb 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts
@@ -116,9 +116,18 @@ export const formatColumn: ExpressionFunctionDefinition<
});
}
if (parentFormatParams) {
- const innerParams = (col.meta.params?.params as Record) ?? {};
+ // if original format is already a nested one, we are just replacing the wrapper params
+ // otherwise wrapping it inside parentFormatId/parentFormatParams
+ const isNested = isNestedFormat(col.meta.params);
+ const innerParams = isNested
+ ? col.meta.params?.params
+ : { id: col.meta.params?.id, params: col.meta.params?.params };
+
+ const formatId = isNested ? col.meta.params?.id : parentFormatId;
+
return withParams(col, {
...col.meta.params,
+ id: formatId,
params: {
...innerParams,
...parentFormatParams,
@@ -132,6 +141,11 @@ export const formatColumn: ExpressionFunctionDefinition<
},
};
+function isNestedFormat(params: DatatableColumn['meta']['params']) {
+ // if there is a nested params object with an id, it's a nested format
+ return !!params?.params?.id;
+}
+
function withParams(col: DatatableColumn, params: Record) {
return { ...col, meta: { ...col.meta, params } };
}
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts
index 92280b0fb6ce6f..793f3387e707d3 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts
@@ -38,8 +38,10 @@ export class IndexPatternDatasource {
renameColumns,
formatColumn,
getTimeScaleFunction,
+ getSuffixFormatter,
} = await import('../async_services');
return core.getStartServices().then(([coreStart, { data }]) => {
+ data.fieldFormats.register([getSuffixFormatter(data.fieldFormats.deserialize)]);
expressions.registerFunction(getTimeScaleFunction(data));
expressions.registerFunction(renameColumns);
expressions.registerFunction(formatColumn);
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx
index e37c31559cd0ca..94f240058d6189 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx
@@ -78,6 +78,7 @@ export function columnToOperation(column: IndexPatternColumn, uniqueLabel?: stri
export * from './rename_columns';
export * from './format_column';
export * from './time_scale';
+export * from './suffix_formatter';
export function getIndexPatternDatasource({
core,
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts
new file mode 100644
index 00000000000000..ef1739e4424fa3
--- /dev/null
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { FormatFactory } from '../types';
+import { getSuffixFormatter } from './suffix_formatter';
+
+describe('suffix formatter', () => {
+ it('should call nested formatter and apply suffix', () => {
+ const convertMock = jest.fn((x) => x);
+ const formatFactory = jest.fn(() => ({ convert: convertMock }));
+ const SuffixFormatter = getSuffixFormatter((formatFactory as unknown) as FormatFactory);
+ const nestedParams = { abc: 123 };
+ const formatterInstance = new SuffixFormatter({
+ unit: 'h',
+ id: 'nestedFormatter',
+ params: nestedParams,
+ });
+
+ const result = formatterInstance.convert(12345);
+
+ expect(result).toEqual('12345/h');
+ expect(convertMock).toHaveBeenCalledWith(12345);
+ expect(formatFactory).toHaveBeenCalledWith({ id: 'nestedFormatter', params: nestedParams });
+ });
+});
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts b/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts
new file mode 100644
index 00000000000000..5594976738efee
--- /dev/null
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts
@@ -0,0 +1,51 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+import { FieldFormat, KBN_FIELD_TYPES } from '../../../../../src/plugins/data/public';
+import { FormatFactory } from '../types';
+import { TimeScaleUnit } from './time_scale';
+
+const unitSuffixes: Record = {
+ s: i18n.translate('xpack.lens.fieldFormats.suffix.s', { defaultMessage: '/h' }),
+ m: i18n.translate('xpack.lens.fieldFormats.suffix.m', { defaultMessage: '/m' }),
+ h: i18n.translate('xpack.lens.fieldFormats.suffix.h', { defaultMessage: '/h' }),
+ d: i18n.translate('xpack.lens.fieldFormats.suffix.d', { defaultMessage: '/d' }),
+};
+
+export function getSuffixFormatter(formatFactory: FormatFactory) {
+ return class SuffixFormatter extends FieldFormat {
+ static id = 'suffix';
+ static title = i18n.translate('xpack.lens.fieldFormats.suffix.title', {
+ defaultMessage: 'Suffix',
+ });
+ static fieldType = KBN_FIELD_TYPES.NUMBER;
+ allowsNumericalAggregations = true;
+
+ getParamDefaults() {
+ return {
+ unit: undefined,
+ nestedParams: {},
+ };
+ }
+
+ textConvert = (val: unknown) => {
+ const unit = this.param('unit') as TimeScaleUnit | undefined;
+ const suffix = unit ? unitSuffixes[unit] : undefined;
+ const nestedFormatter = this.param('id');
+ const nestedParams = this.param('params');
+
+ const formattedValue = formatFactory({ id: nestedFormatter, params: nestedParams }).convert(
+ val
+ );
+
+ if (suffix) {
+ return `${formattedValue}${suffix}`;
+ }
+ return formattedValue;
+ };
+ };
+}
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.ts b/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.ts
index 7a4e8f6bc0638a..06ff8058b1d09e 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/time_scale.ts
@@ -11,7 +11,7 @@ import { DataPublicPluginStart } from 'src/plugins/data/public';
import { search } from '../../../../../src/plugins/data/public';
import { buildResultColumns } from '../../../../../src/plugins/expressions/common';
-type TimeScaleUnit = 's' | 'm' | 'h' | 'd';
+export type TimeScaleUnit = 's' | 'm' | 'h' | 'd';
export interface TimeScaleArgs {
dateColumnId: string;
diff --git a/x-pack/plugins/reporting/common/constants.ts b/x-pack/plugins/reporting/common/constants.ts
index 07a239494da239..16e40bab65a46e 100644
--- a/x-pack/plugins/reporting/common/constants.ts
+++ b/x-pack/plugins/reporting/common/constants.ts
@@ -11,13 +11,6 @@ export const BROWSER_TYPE = 'chromium';
export const JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY =
'xpack.reporting.jobCompletionNotifications';
-export const API_BASE_URL = '/api/reporting'; // "Generation URL" from share menu
-export const API_BASE_URL_V1 = '/api/reporting/v1'; //
-export const API_BASE_GENERATE_V1 = `${API_BASE_URL_V1}/generate`;
-export const API_LIST_URL = '/api/reporting/jobs';
-export const API_GENERATE_IMMEDIATE = `${API_BASE_URL_V1}/generate/immediate/csv/saved-object`;
-export const API_DIAGNOSE_URL = `${API_BASE_URL}/diagnose`;
-
export const CONTENT_TYPE_CSV = 'text/csv';
export const CSV_REPORTING_ACTION = 'downloadCsvReport';
export const CSV_BOM_CHARS = '\ufeff';
@@ -57,15 +50,49 @@ export const UI_SETTINGS_CUSTOM_PDF_LOGO = 'xpackReporting:customPdfLogo';
export const UI_SETTINGS_CSV_SEPARATOR = 'csv:separator';
export const UI_SETTINGS_CSV_QUOTE_VALUES = 'csv:quoteValues';
+export const LAYOUT_TYPES = {
+ PRESERVE_LAYOUT: 'preserve_layout',
+ PRINT: 'print',
+};
+
+// Export Type Definitions
+export const CSV_REPORT_TYPE = 'CSV';
+export const PDF_REPORT_TYPE = 'printablePdf';
+export const PNG_REPORT_TYPE = 'PNG';
+
export const PDF_JOB_TYPE = 'printable_pdf';
export const PNG_JOB_TYPE = 'PNG';
export const CSV_JOB_TYPE = 'csv';
export const CSV_FROM_SAVEDOBJECT_JOB_TYPE = 'csv_from_savedobject';
export const USES_HEADLESS_JOB_TYPES = [PDF_JOB_TYPE, PNG_JOB_TYPE];
+// Licenses
export const LICENSE_TYPE_TRIAL = 'trial';
export const LICENSE_TYPE_BASIC = 'basic';
export const LICENSE_TYPE_STANDARD = 'standard';
export const LICENSE_TYPE_GOLD = 'gold';
export const LICENSE_TYPE_PLATINUM = 'platinum';
export const LICENSE_TYPE_ENTERPRISE = 'enterprise';
+
+// Routes
+export const API_BASE_URL = '/api/reporting'; // "Generation URL" from share menu
+export const API_BASE_GENERATE = `${API_BASE_URL}/generate`;
+export const API_LIST_URL = `${API_BASE_URL}/jobs`;
+export const API_DIAGNOSE_URL = `${API_BASE_URL}/diagnose`;
+
+// hacky endpoint
+export const API_BASE_URL_V1 = '/api/reporting/v1'; //
+export const API_GENERATE_IMMEDIATE = `${API_BASE_URL_V1}/generate/immediate/csv/saved-object`;
+
+// Management UI route
+export const REPORTING_MANAGEMENT_HOME = '/app/management/insightsAndAlerting/reporting';
+
+// Statuses
+export enum JOB_STATUSES {
+ PENDING = 'pending',
+ PROCESSING = 'processing',
+ COMPLETED = 'completed',
+ FAILED = 'failed',
+ CANCELLED = 'cancelled',
+ WARNINGS = 'completed_with_warnings',
+}
diff --git a/x-pack/plugins/reporting/common/index.ts b/x-pack/plugins/reporting/common/index.ts
index cda8934fc8bf62..0be6ab66827746 100644
--- a/x-pack/plugins/reporting/common/index.ts
+++ b/x-pack/plugins/reporting/common/index.ts
@@ -4,5 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { LayoutSelectorDictionary } from './types';
+
export { CancellationToken } from './cancellation_token';
export { Poller } from './poller';
+
+export const getDefaultLayoutSelectors = (): LayoutSelectorDictionary => ({
+ screenshot: '[data-shared-items-container]',
+ renderComplete: '[data-shared-item]',
+ itemsCountAttribute: 'data-shared-items-count',
+ timefilterDurationAttribute: 'data-shared-timefilter-duration',
+});
diff --git a/x-pack/plugins/reporting/common/poller.ts b/x-pack/plugins/reporting/common/poller.ts
index 2127a876f4a271..017dbac13e29b5 100644
--- a/x-pack/plugins/reporting/common/poller.ts
+++ b/x-pack/plugins/reporting/common/poller.ts
@@ -5,7 +5,16 @@
*/
import _ from 'lodash';
-import { PollerOptions } from './types';
+
+interface PollerOptions {
+ functionToPoll: () => Promise;
+ pollFrequencyInMillis: number;
+ trailing?: boolean;
+ continuePollingOnError?: boolean;
+ pollFrequencyErrorMultiplier?: number;
+ successFunction?: (...args: any) => any;
+ errorFunction?: (error: Error) => any;
+}
// @TODO Maybe move to observables someday
export class Poller {
diff --git a/x-pack/plugins/reporting/common/types.ts b/x-pack/plugins/reporting/common/types.ts
index 24c126bfe0571f..abd0bee7fb6ea2 100644
--- a/x-pack/plugins/reporting/common/types.ts
+++ b/x-pack/plugins/reporting/common/types.ts
@@ -4,15 +4,94 @@
* you may not use this file except in compliance with the Elastic License.
*/
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-export { ReportingConfigType } from '../server/config';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { LayoutParams } from '../server/lib/layouts';
-export { LayoutParams };
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-export { ReportDocument, ReportSource } from '../server/lib/store/report';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-export { BaseParams } from '../server/types';
+export interface PageSizeParams {
+ pageMarginTop: number;
+ pageMarginBottom: number;
+ pageMarginWidth: number;
+ tableBorderWidth: number;
+ headingHeight: number;
+ subheadingHeight: number;
+}
+
+export interface LayoutSelectorDictionary {
+ screenshot: string;
+ renderComplete: string;
+ itemsCountAttribute: string;
+ timefilterDurationAttribute: string;
+}
+
+export interface PdfImageSize {
+ width: number;
+ height?: number;
+}
+
+export interface Size {
+ width: number;
+ height: number;
+}
+
+export interface LayoutParams {
+ id: string;
+ dimensions?: Size;
+ selectors?: LayoutSelectorDictionary;
+}
+
+export interface ReportDocumentHead {
+ _id: string;
+ _index: string;
+ _seq_no: unknown;
+ _primary_term: unknown;
+}
+
+export interface TaskRunResult {
+ content_type: string | null;
+ content: string | null;
+ csv_contains_formulas?: boolean;
+ size: number;
+ max_size_reached?: boolean;
+ warnings?: string[];
+}
+
+export interface ReportSource {
+ jobtype: string;
+ kibana_name: string;
+ kibana_id: string;
+ created_by: string | false;
+ payload: {
+ headers: string; // encrypted headers
+ browserTimezone?: string; // may use timezone from advanced settings
+ objectType: string;
+ title: string;
+ layout?: LayoutParams;
+ };
+ meta: { objectType: string; layout?: string };
+ browser_type: string;
+ max_attempts: number;
+ timeout: number;
+
+ status: JobStatus;
+ attempts: number;
+ output: TaskRunResult | null;
+ started_at?: string;
+ completed_at?: string;
+ created_at: string;
+ priority?: number;
+ process_expiration?: string;
+}
+
+/*
+ * The document created by Reporting to store in the .reporting index
+ */
+export interface ReportDocument extends ReportDocumentHead {
+ _source: ReportSource;
+}
+
+export interface BaseParams {
+ browserTimezone?: string; // browserTimezone is optional: it is not in old POST URLs that were generated prior to being added to this interface
+ layout?: LayoutParams;
+ objectType: string;
+ title: string;
+}
export type JobId = string;
export type JobStatus =
@@ -59,18 +138,28 @@ export interface ReportApiJSON {
status: string;
}
-export interface PollerOptions {
- functionToPoll: () => Promise;
- pollFrequencyInMillis: number;
- trailing?: boolean;
- continuePollingOnError?: boolean;
- pollFrequencyErrorMultiplier?: number;
- successFunction?: (...args: any) => any;
- errorFunction?: (error: Error) => any;
-}
-
export interface LicenseCheckResults {
enableLinks: boolean;
showLinks: boolean;
message: string;
}
+
+export interface JobSummary {
+ id: JobId;
+ status: JobStatus;
+ title: string;
+ jobtype: string;
+ maxSizeReached?: boolean;
+ csvContainsFormulas?: boolean;
+}
+
+export interface JobSummarySet {
+ completed: JobSummary[];
+ failed: JobSummary[];
+}
+
+type DownloadLink = string;
+export type DownloadReportFn = (jobId: JobId) => DownloadLink;
+
+type ManagementLink = string;
+export type ManagementLinkFn = () => ManagementLink;
diff --git a/x-pack/plugins/reporting/constants.ts b/x-pack/plugins/reporting/constants.ts
deleted file mode 100644
index 772c52dde4a152..00000000000000
--- a/x-pack/plugins/reporting/constants.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export const JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY =
- 'xpack.reporting.jobCompletionNotifications';
-
-// Routes
-export const API_BASE_URL = '/api/reporting';
-export const API_LIST_URL = `${API_BASE_URL}/jobs`;
-export const API_BASE_GENERATE = `${API_BASE_URL}/generate`;
-export const API_GENERATE_IMMEDIATE = `${API_BASE_URL}/v1/generate/immediate/csv/saved-object`;
-export const REPORTING_MANAGEMENT_HOME = '/app/management/insightsAndAlerting/reporting';
-
-// Statuses
-export const JOB_STATUS_FAILED = 'failed';
-export const JOB_STATUS_COMPLETED = 'completed';
-export const JOB_STATUS_WARNINGS = 'completed_with_warnings';
-
-export enum JobStatuses {
- PENDING = 'pending',
- PROCESSING = 'processing',
- COMPLETED = 'completed',
- FAILED = 'failed',
- CANCELLED = 'cancelled',
- WARNINGS = 'completed_with_warnings',
-}
-
-// Types
-export const PDF_JOB_TYPE = 'printable_pdf';
-export const PNG_JOB_TYPE = 'PNG';
-export const CSV_JOB_TYPE = 'csv';
-export const CSV_FROM_SAVEDOBJECT_JOB_TYPE = 'csv_from_savedobject';
-export const USES_HEADLESS_JOB_TYPES = [PDF_JOB_TYPE, PNG_JOB_TYPE];
-
-// Actions
-export const CSV_REPORTING_ACTION = 'downloadCsvReport';
diff --git a/x-pack/plugins/reporting/public/components/buttons/report_download_button.tsx b/x-pack/plugins/reporting/public/components/buttons/report_download_button.tsx
index 6c13264ebcb1fc..4bd86d15949e85 100644
--- a/x-pack/plugins/reporting/public/components/buttons/report_download_button.tsx
+++ b/x-pack/plugins/reporting/public/components/buttons/report_download_button.tsx
@@ -6,7 +6,7 @@
import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import React, { FunctionComponent } from 'react';
-import { JobStatuses } from '../../../constants';
+import { JOB_STATUSES } from '../../../common/constants';
import { Job as ListingJob, Props as ListingProps } from '../report_listing';
type Props = { record: ListingJob } & ListingProps;
@@ -14,7 +14,7 @@ type Props = { record: ListingJob } & ListingProps;
export const ReportDownloadButton: FunctionComponent = (props: Props) => {
const { record, apiClient, intl } = props;
- if (record.status !== JobStatuses.COMPLETED && record.status !== JobStatuses.WARNINGS) {
+ if (record.status !== JOB_STATUSES.COMPLETED && record.status !== JOB_STATUSES.WARNINGS) {
return null;
}
diff --git a/x-pack/plugins/reporting/public/components/buttons/report_error_button.tsx b/x-pack/plugins/reporting/public/components/buttons/report_error_button.tsx
index 4eee86cd79ce72..2864802f843f45 100644
--- a/x-pack/plugins/reporting/public/components/buttons/report_error_button.tsx
+++ b/x-pack/plugins/reporting/public/components/buttons/report_error_button.tsx
@@ -7,7 +7,7 @@
import { EuiButtonIcon, EuiCallOut, EuiPopover } from '@elastic/eui';
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
import React, { Component } from 'react';
-import { JobStatuses } from '../../../constants';
+import { JOB_STATUSES } from '../../../common/constants';
import { JobContent, ReportingAPIClient } from '../../lib/reporting_api_client';
import { Job as ListingJob } from '../report_listing';
@@ -43,7 +43,7 @@ class ReportErrorButtonUi extends Component {
public render() {
const { record, intl } = this.props;
- if (record.status !== JobStatuses.FAILED) {
+ if (record.status !== JOB_STATUSES.FAILED) {
return null;
}
diff --git a/x-pack/plugins/reporting/public/components/buttons/report_info_button.tsx b/x-pack/plugins/reporting/public/components/buttons/report_info_button.tsx
index 068cb7d44b0a16..0e249f156f5874 100644
--- a/x-pack/plugins/reporting/public/components/buttons/report_info_button.tsx
+++ b/x-pack/plugins/reporting/public/components/buttons/report_info_button.tsx
@@ -17,8 +17,8 @@ import {
} from '@elastic/eui';
import { get } from 'lodash';
import React, { Component, Fragment } from 'react';
+import { USES_HEADLESS_JOB_TYPES } from '../../../common/constants';
import { ReportApiJSON } from '../../../common/types';
-import { USES_HEADLESS_JOB_TYPES } from '../../../constants';
import { ReportingAPIClient } from '../../lib/reporting_api_client';
interface Props {
diff --git a/x-pack/plugins/reporting/public/components/index.ts b/x-pack/plugins/reporting/public/components/index.ts
index 354ef189704ad0..370e90c8d2d08f 100644
--- a/x-pack/plugins/reporting/public/components/index.ts
+++ b/x-pack/plugins/reporting/public/components/index.ts
@@ -9,3 +9,4 @@ export { getFailureToast } from './job_failure';
export { getWarningFormulasToast } from './job_warning_formulas';
export { getWarningMaxSizeToast } from './job_warning_max_size';
export { getGeneralErrorToast } from './general_error';
+export { ScreenCapturePanelContent } from './screen_capture_panel_content';
diff --git a/x-pack/plugins/reporting/public/components/job_download_button.tsx b/x-pack/plugins/reporting/public/components/job_download_button.tsx
index 8cf3ce8644add1..7dff2cafa047b2 100644
--- a/x-pack/plugins/reporting/public/components/job_download_button.tsx
+++ b/x-pack/plugins/reporting/public/components/job_download_button.tsx
@@ -7,8 +7,7 @@
import { EuiButton } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React from 'react';
-import { JobSummary } from '../';
-import { JobId } from '../../common/types';
+import { JobId, JobSummary } from '../../common/types';
interface Props {
getUrl: (jobId: JobId) => string;
diff --git a/x-pack/plugins/reporting/public/components/job_failure.tsx b/x-pack/plugins/reporting/public/components/job_failure.tsx
index 8d8f32f692343d..e9c3a448cfe412 100644
--- a/x-pack/plugins/reporting/public/components/job_failure.tsx
+++ b/x-pack/plugins/reporting/public/components/job_failure.tsx
@@ -9,8 +9,8 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Fragment } from 'react';
import { ToastInput } from 'src/core/public';
-import { JobSummary, ManagementLinkFn } from '../';
import { toMountPoint } from '../../../../../src/plugins/kibana_react/public';
+import { JobSummary, ManagementLinkFn } from '../../common/types';
export const getFailureToast = (
errorText: string,
diff --git a/x-pack/plugins/reporting/public/components/job_success.tsx b/x-pack/plugins/reporting/public/components/job_success.tsx
index 05cf2c4c5784a6..f03914b2be2f2d 100644
--- a/x-pack/plugins/reporting/public/components/job_success.tsx
+++ b/x-pack/plugins/reporting/public/components/job_success.tsx
@@ -7,9 +7,8 @@
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Fragment } from 'react';
import { ToastInput } from 'src/core/public';
-import { JobSummary } from '../';
import { toMountPoint } from '../../../../../src/plugins/kibana_react/public';
-import { JobId } from '../../common/types';
+import { JobId, JobSummary } from '../../common/types';
import { DownloadButton } from './job_download_button';
import { ReportLink } from './report_link';
diff --git a/x-pack/plugins/reporting/public/components/job_warning_formulas.tsx b/x-pack/plugins/reporting/public/components/job_warning_formulas.tsx
index 8cccc94e98dcda..338c718a060c1e 100644
--- a/x-pack/plugins/reporting/public/components/job_warning_formulas.tsx
+++ b/x-pack/plugins/reporting/public/components/job_warning_formulas.tsx
@@ -7,9 +7,8 @@
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Fragment } from 'react';
import { ToastInput } from 'src/core/public';
-import { JobSummary } from '../';
import { toMountPoint } from '../../../../../src/plugins/kibana_react/public';
-import { JobId } from '../../common/types';
+import { JobId, JobSummary } from '../../common/types';
import { DownloadButton } from './job_download_button';
import { ReportLink } from './report_link';
diff --git a/x-pack/plugins/reporting/public/components/job_warning_max_size.tsx b/x-pack/plugins/reporting/public/components/job_warning_max_size.tsx
index c350eef0e5a547..cab743e2006df0 100644
--- a/x-pack/plugins/reporting/public/components/job_warning_max_size.tsx
+++ b/x-pack/plugins/reporting/public/components/job_warning_max_size.tsx
@@ -7,9 +7,8 @@
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Fragment } from 'react';
import { ToastInput } from 'src/core/public';
-import { JobSummary } from '../';
import { toMountPoint } from '../../../../../src/plugins/kibana_react/public';
-import { JobId } from '../../common/types';
+import { JobId, JobSummary } from '../../common/types';
import { DownloadButton } from './job_download_button';
import { ReportLink } from './report_link';
diff --git a/x-pack/plugins/reporting/public/components/report_listing.tsx b/x-pack/plugins/reporting/public/components/report_listing.tsx
index a512b1305b8e03..ac6d03a407c289 100644
--- a/x-pack/plugins/reporting/public/components/report_listing.tsx
+++ b/x-pack/plugins/reporting/public/components/report_listing.tsx
@@ -22,9 +22,9 @@ import { Component, default as React, Fragment } from 'react';
import { Subscription } from 'rxjs';
import { ApplicationStart, ToastsSetup } from 'src/core/public';
import { ILicense, LicensingPluginSetup } from '../../../licensing/public';
+import { JOB_STATUSES as JobStatuses } from '../../common/constants';
import { Poller } from '../../common/poller';
import { durationToNumber } from '../../common/schema_utils';
-import { JobStatuses } from '../../constants';
import { checkLicense } from '../lib/license_check';
import { JobQueueEntry, ReportingAPIClient } from '../lib/reporting_api_client';
import { ClientConfigType } from '../plugin';
diff --git a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx
index 18895f9e623eb9..7f48b5d9101baa 100644
--- a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx
+++ b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx
@@ -10,6 +10,7 @@ import React, { Component, ReactElement } from 'react';
import { ToastsSetup } from 'src/core/public';
import url from 'url';
import { toMountPoint } from '../../../../../src/plugins/kibana_react/public';
+import { CSV_REPORT_TYPE, PDF_REPORT_TYPE, PNG_REPORT_TYPE } from '../../common/constants';
import { BaseParams } from '../../common/types';
import { ReportingAPIClient } from '../lib/reporting_api_client';
@@ -165,12 +166,12 @@ class ReportingPanelContentUi extends Component {
private prettyPrintReportingType = () => {
switch (this.props.reportType) {
- case 'printablePdf':
+ case PDF_REPORT_TYPE:
return 'PDF';
case 'csv':
- return 'CSV';
+ return CSV_REPORT_TYPE;
case 'png':
- return 'PNG';
+ return PNG_REPORT_TYPE;
default:
return this.props.reportType;
}
diff --git a/x-pack/plugins/reporting/public/index.ts b/x-pack/plugins/reporting/public/index.ts
index 251fd14ee4d57c..f15a5ca4817571 100644
--- a/x-pack/plugins/reporting/public/index.ts
+++ b/x-pack/plugins/reporting/public/index.ts
@@ -5,33 +5,21 @@
*/
import { PluginInitializerContext } from 'src/core/public';
-import { ReportingPublicPlugin } from './plugin';
+import { ScreenCapturePanelContent } from './components/screen_capture_panel_content';
import * as jobCompletionNotifications from './lib/job_completion_notifications';
-import { JobId, JobStatus } from '../common/types';
+import { ReportingAPIClient } from './lib/reporting_api_client';
+import { ReportingPublicPlugin } from './plugin';
-export function plugin(initializerContext: PluginInitializerContext) {
- return new ReportingPublicPlugin(initializerContext);
+export interface ReportingSetup {
+ components: {
+ ScreenCapturePanel: typeof ScreenCapturePanelContent;
+ };
}
-export { ReportingPublicPlugin as Plugin };
-export { jobCompletionNotifications };
+export type ReportingStart = ReportingSetup;
-export interface JobSummary {
- id: JobId;
- status: JobStatus;
- title: string;
- jobtype: string;
- maxSizeReached?: boolean;
- csvContainsFormulas?: boolean;
-}
+export { ReportingAPIClient, ReportingPublicPlugin as Plugin, jobCompletionNotifications };
-export interface JobSummarySet {
- completed: JobSummary[];
- failed: JobSummary[];
+export function plugin(initializerContext: PluginInitializerContext) {
+ return new ReportingPublicPlugin(initializerContext);
}
-
-type DownloadLink = string;
-export type DownloadReportFn = (jobId: JobId) => DownloadLink;
-
-type ManagementLink = string;
-export type ManagementLinkFn = () => ManagementLink;
diff --git a/x-pack/plugins/reporting/public/lib/job_completion_notifications.ts b/x-pack/plugins/reporting/public/lib/job_completion_notifications.ts
index 06694361b757d5..39a7c9f84b8e5b 100644
--- a/x-pack/plugins/reporting/public/lib/job_completion_notifications.ts
+++ b/x-pack/plugins/reporting/public/lib/job_completion_notifications.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY } from '../../constants';
+import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY } from '../../common/constants';
type JobId = string;
diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client.ts
index 2853caaaaa1b51..71b57d0c0124e3 100644
--- a/x-pack/plugins/reporting/public/lib/reporting_api_client.ts
+++ b/x-pack/plugins/reporting/public/lib/reporting_api_client.ts
@@ -7,14 +7,20 @@
import { stringify } from 'query-string';
import rison from 'rison-node';
import { HttpSetup } from 'src/core/public';
-import { DownloadReportFn, ManagementLinkFn } from '../';
-import { JobId, ReportApiJSON, ReportDocument, ReportSource } from '../../common/types';
import {
API_BASE_GENERATE,
API_BASE_URL,
API_LIST_URL,
REPORTING_MANAGEMENT_HOME,
-} from '../../constants';
+} from '../../common/constants';
+import {
+ DownloadReportFn,
+ JobId,
+ ManagementLinkFn,
+ ReportApiJSON,
+ ReportDocument,
+ ReportSource,
+} from '../../common/types';
import { add } from './job_completion_notifications';
export interface JobQueueEntry {
diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts
index f91517e4397f93..31d324bd77159b 100644
--- a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts
+++ b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts
@@ -6,8 +6,7 @@
import sinon, { stub } from 'sinon';
import { NotificationsStart } from 'src/core/public';
-import { JobSummary } from '../';
-import { ReportDocument } from '../../common/types';
+import { JobSummary, ReportDocument } from '../../common/types';
import { ReportingAPIClient } from './reporting_api_client';
import { ReportingNotifierStreamHandler } from './stream_handler';
diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.ts b/x-pack/plugins/reporting/public/lib/stream_handler.ts
index d97c0a7a2d11ef..4b2305b60c413c 100644
--- a/x-pack/plugins/reporting/public/lib/stream_handler.ts
+++ b/x-pack/plugins/reporting/public/lib/stream_handler.ts
@@ -8,14 +8,8 @@ import { i18n } from '@kbn/i18n';
import * as Rx from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { NotificationsSetup } from 'src/core/public';
-import { JobSummarySet, JobSummary } from '../';
-import { JobId, ReportDocument } from '../../common/types';
-import {
- JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY,
- JOB_STATUS_COMPLETED,
- JOB_STATUS_FAILED,
- JOB_STATUS_WARNINGS,
-} from '../../constants';
+import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY, JOB_STATUSES } from '../../common/constants';
+import { JobId, JobSummary, JobSummarySet, ReportDocument } from '../../common/types';
import {
getFailureToast,
getGeneralErrorToast,
@@ -107,9 +101,9 @@ export class ReportingNotifierStreamHandler {
_source: { status: jobStatus },
} = job;
if (storedJobs.includes(jobId)) {
- if (jobStatus === JOB_STATUS_COMPLETED || jobStatus === JOB_STATUS_WARNINGS) {
+ if (jobStatus === JOB_STATUSES.COMPLETED || jobStatus === JOB_STATUSES.WARNINGS) {
completedJobs.push(getReportStatus(job));
- } else if (jobStatus === JOB_STATUS_FAILED) {
+ } else if (jobStatus === JOB_STATUSES.FAILED) {
failedJobs.push(getReportStatus(job));
} else {
pending.push(jobId);
diff --git a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx
index 1e3f7e34bebdb1..9a4832b114e40d 100644
--- a/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx
+++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx
@@ -9,20 +9,18 @@ import _ from 'lodash';
import moment from 'moment-timezone';
import { CoreSetup } from 'src/core/public';
import {
- UiActionsActionDefinition as ActionDefinition,
+ ISearchEmbeddable,
+ SEARCH_EMBEDDABLE_TYPE,
+} from '../../../../../src/plugins/discover/public';
+import { IEmbeddable, ViewMode } from '../../../../../src/plugins/embeddable/public';
+import {
IncompatibleActionError,
+ UiActionsActionDefinition as ActionDefinition,
} from '../../../../../src/plugins/ui_actions/public';
import { LicensingPluginSetup } from '../../../licensing/public';
+import { API_GENERATE_IMMEDIATE, CSV_REPORTING_ACTION } from '../../common/constants';
import { checkLicense } from '../lib/license_check';
-import { ViewMode, IEmbeddable } from '../../../../../src/plugins/embeddable/public';
-import {
- ISearchEmbeddable,
- SEARCH_EMBEDDABLE_TYPE,
-} from '../../../../../src/plugins/discover/public';
-
-import { API_GENERATE_IMMEDIATE, CSV_REPORTING_ACTION } from '../../constants';
-
function isSavedSearchEmbeddable(
embeddable: IEmbeddable | ISearchEmbeddable
): embeddable is ISearchEmbeddable {
diff --git a/x-pack/plugins/reporting/public/plugin.ts b/x-pack/plugins/reporting/public/plugin.ts
index 33f4fd4abf72cb..52362b4c68734f 100644
--- a/x-pack/plugins/reporting/public/plugin.ts
+++ b/x-pack/plugins/reporting/public/plugin.ts
@@ -24,11 +24,14 @@ import {
import { ManagementSetup, ManagementStart } from '../../../../src/plugins/management/public';
import { SharePluginSetup, SharePluginStart } from '../../../../src/plugins/share/public';
import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public';
+import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY } from '../common/constants';
import { durationToNumber } from '../common/schema_utils';
-import { JobId, ReportingConfigType } from '../common/types';
-import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY } from '../constants';
-import { JobSummarySet } from './';
-import { getGeneralErrorToast } from './components';
+import { JobId, JobSummarySet } from '../common/types';
+import { ReportingSetup, ReportingStart } from './';
+import {
+ getGeneralErrorToast,
+ ScreenCapturePanelContent as ScreenCapturePanel,
+} from './components';
import { ReportingAPIClient } from './lib/reporting_api_client';
import { ReportingNotifierStreamHandler as StreamHandler } from './lib/stream_handler';
import { GetCsvReportPanelAction } from './panel_actions/get_csv_panel_action';
@@ -36,7 +39,12 @@ import { csvReportingProvider } from './share_context_menu/register_csv_reportin
import { reportingPDFPNGProvider } from './share_context_menu/register_pdf_png_reporting';
export interface ClientConfigType {
- poll: ReportingConfigType['poll'];
+ poll: {
+ jobsRefresh: {
+ interval: number;
+ intervalErrorMultiplier: number;
+ };
+ };
}
function getStored(): JobId[] {
@@ -75,8 +83,13 @@ export interface ReportingPublicPluginStartDendencies {
export class ReportingPublicPlugin
implements
- Plugin {
- private config: ClientConfigType;
+ Plugin<
+ ReportingSetup,
+ ReportingStart,
+ ReportingPublicPluginSetupDendencies,
+ ReportingPublicPluginStartDendencies
+ > {
+ private readonly contract: ReportingStart = { components: { ScreenCapturePanel } };
private readonly stop$ = new Rx.ReplaySubject(1);
private readonly title = i18n.translate('xpack.reporting.management.reportingTitle', {
defaultMessage: 'Reporting',
@@ -84,6 +97,7 @@ export class ReportingPublicPlugin
private readonly breadcrumbText = i18n.translate('xpack.reporting.breadcrumb', {
defaultMessage: 'Reporting',
});
+ private config: ClientConfigType;
constructor(initializerContext: PluginInitializerContext) {
this.config = initializerContext.config.get();
@@ -149,6 +163,8 @@ export class ReportingPublicPlugin
uiSettings,
})
);
+
+ return this.contract;
}
public start(core: CoreStart) {
@@ -166,6 +182,8 @@ export class ReportingPublicPlugin
catchError((err) => handleError(notifications, err))
)
.subscribe();
+
+ return this.contract;
}
public stop() {
diff --git a/x-pack/plugins/reporting/server/export_types/csv/create_job.ts b/x-pack/plugins/reporting/server/export_types/csv/create_job.ts
index 5b98a198b7d1a6..43243d265e926d 100644
--- a/x-pack/plugins/reporting/server/export_types/csv/create_job.ts
+++ b/x-pack/plugins/reporting/server/export_types/csv/create_job.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { CSV_JOB_TYPE } from '../../../constants';
+import { CSV_JOB_TYPE } from '../../../common/constants';
import { cryptoFactory } from '../../lib';
import { CreateJobFn, CreateJobFnFactory } from '../../types';
import { IndexPatternSavedObject, JobParamsCSV, TaskPayloadCSV } from './types';
diff --git a/x-pack/plugins/reporting/server/export_types/csv/index.ts b/x-pack/plugins/reporting/server/export_types/csv/index.ts
index e66cfef18c6e26..f7b7ff5709fe69 100644
--- a/x-pack/plugins/reporting/server/export_types/csv/index.ts
+++ b/x-pack/plugins/reporting/server/export_types/csv/index.ts
@@ -5,6 +5,7 @@
*/
import {
+ CSV_JOB_TYPE as jobType,
LICENSE_TYPE_BASIC,
LICENSE_TYPE_ENTERPRISE,
LICENSE_TYPE_GOLD,
@@ -12,7 +13,6 @@ import {
LICENSE_TYPE_STANDARD,
LICENSE_TYPE_TRIAL,
} from '../../../common/constants';
-import { CSV_JOB_TYPE as jobType } from '../../../constants';
import { CreateJobFn, ExportTypeDefinition, RunTaskFn } from '../../types';
import { createJobFnFactory } from './create_job';
import { runTaskFnFactory } from './execute_job';
diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts
index abe9fbf3e39506..2c163aeb57a644 100644
--- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts
+++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts
@@ -5,6 +5,7 @@
*/
import {
+ CSV_FROM_SAVEDOBJECT_JOB_TYPE,
LICENSE_TYPE_BASIC,
LICENSE_TYPE_ENTERPRISE,
LICENSE_TYPE_GOLD,
@@ -12,7 +13,6 @@ import {
LICENSE_TYPE_STANDARD,
LICENSE_TYPE_TRIAL,
} from '../../../common/constants';
-import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../constants';
import { ExportTypeDefinition } from '../../types';
import { createJobFnFactory, ImmediateCreateJobFn } from './create_job';
import { ImmediateExecuteFn, runTaskFnFactory } from './execute_job';
diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/metadata.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/metadata.ts
index a0fd8a29fdcc45..fda360103a1153 100644
--- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/metadata.ts
+++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/metadata.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../constants';
+import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants';
export const metadata = {
id: CSV_FROM_SAVEDOBJECT_JOB_TYPE,
diff --git a/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts b/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts
index b1fcdbe05fd67f..010b6f431db7ed 100644
--- a/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts
+++ b/x-pack/plugins/reporting/server/export_types/png/create_job/index.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { PNG_JOB_TYPE } from '../../../../constants';
+import { PNG_JOB_TYPE } from '../../../../common/constants';
import { cryptoFactory } from '../../../lib';
import { CreateJobFn, CreateJobFnFactory } from '../../../types';
import { validateUrls } from '../../common';
diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts
index dcd33a0fc8d530..a529cb864b6f73 100644
--- a/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts
+++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/create_job/index.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { PDF_JOB_TYPE } from '../../../../constants';
+import { PDF_JOB_TYPE } from '../../../../common/constants';
import { cryptoFactory } from '../../../lib';
import { CreateJobFn, CreateJobFnFactory } from '../../../types';
import { validateUrls } from '../../common';
diff --git a/x-pack/plugins/reporting/server/lib/layouts/create_layout.ts b/x-pack/plugins/reporting/server/lib/layouts/create_layout.ts
index e69b8d61dec0d2..c90f67b81317e8 100644
--- a/x-pack/plugins/reporting/server/lib/layouts/create_layout.ts
+++ b/x-pack/plugins/reporting/server/lib/layouts/create_layout.ts
@@ -4,8 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { LAYOUT_TYPES } from '../../../common/constants';
import { CaptureConfig } from '../../types';
-import { LayoutInstance, LayoutParams, LayoutTypes } from './';
+import { LayoutInstance, LayoutParams } from './';
import { PreserveLayout } from './preserve_layout';
import { PrintLayout } from './print_layout';
@@ -13,7 +14,7 @@ export function createLayout(
captureConfig: CaptureConfig,
layoutParams?: LayoutParams
): LayoutInstance {
- if (layoutParams && layoutParams.dimensions && layoutParams.id === LayoutTypes.PRESERVE_LAYOUT) {
+ if (layoutParams && layoutParams.dimensions && layoutParams.id === LAYOUT_TYPES.PRESERVE_LAYOUT) {
return new PreserveLayout(layoutParams.dimensions);
}
diff --git a/x-pack/plugins/reporting/server/lib/layouts/index.ts b/x-pack/plugins/reporting/server/lib/layouts/index.ts
index c091339a605823..8bfe79aeb8a216 100644
--- a/x-pack/plugins/reporting/server/lib/layouts/index.ts
+++ b/x-pack/plugins/reporting/server/lib/layouts/index.ts
@@ -4,59 +4,23 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { HeadlessChromiumDriver } from '../../browsers';
import { LevelLogger } from '../';
+import { LayoutSelectorDictionary, Size } from '../../../common/types';
+import { HeadlessChromiumDriver } from '../../browsers';
import { Layout } from './layout';
+export {
+ LayoutParams,
+ LayoutSelectorDictionary,
+ PageSizeParams,
+ PdfImageSize,
+ Size,
+} from '../../../common/types';
export { createLayout } from './create_layout';
export { Layout } from './layout';
export { PreserveLayout } from './preserve_layout';
export { PrintLayout } from './print_layout';
-export const LayoutTypes = {
- PRESERVE_LAYOUT: 'preserve_layout',
- PRINT: 'print',
-};
-
-export const getDefaultLayoutSelectors = (): LayoutSelectorDictionary => ({
- screenshot: '[data-shared-items-container]',
- renderComplete: '[data-shared-item]',
- itemsCountAttribute: 'data-shared-items-count',
- timefilterDurationAttribute: 'data-shared-timefilter-duration',
-});
-
-export interface PageSizeParams {
- pageMarginTop: number;
- pageMarginBottom: number;
- pageMarginWidth: number;
- tableBorderWidth: number;
- headingHeight: number;
- subheadingHeight: number;
-}
-
-export interface LayoutSelectorDictionary {
- screenshot: string;
- renderComplete: string;
- itemsCountAttribute: string;
- timefilterDurationAttribute: string;
-}
-
-export interface PdfImageSize {
- width: number;
- height?: number;
-}
-
-export interface Size {
- width: number;
- height: number;
-}
-
-export interface LayoutParams {
- id: string;
- dimensions?: Size;
- selectors?: LayoutSelectorDictionary;
-}
-
interface LayoutSelectors {
// Fields that are not part of Layout: the instances
// independently implement these fields on their own
diff --git a/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts b/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts
index faddaae64ce5dd..549e898d8a13ea 100644
--- a/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts
+++ b/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts
@@ -6,15 +6,10 @@
import path from 'path';
import { CustomPageSize } from 'pdfmake/interfaces';
-import {
- getDefaultLayoutSelectors,
- Layout,
- LayoutInstance,
- LayoutSelectorDictionary,
- LayoutTypes,
- PageSizeParams,
- Size,
-} from './';
+import { getDefaultLayoutSelectors } from '../../../common';
+import { LAYOUT_TYPES } from '../../../common/constants';
+import { LayoutSelectorDictionary, PageSizeParams, Size } from '../../../common/types';
+import { Layout, LayoutInstance } from './';
// We use a zoom of two to bump up the resolution of the screenshot a bit.
const ZOOM: number = 2;
@@ -28,7 +23,7 @@ export class PreserveLayout extends Layout implements LayoutInstance {
private readonly scaledWidth: number;
constructor(size: Size, layoutSelectors?: LayoutSelectorDictionary) {
- super(LayoutTypes.PRESERVE_LAYOUT);
+ super(LAYOUT_TYPES.PRESERVE_LAYOUT);
this.height = size.height;
this.width = size.width;
this.scaledHeight = size.height * ZOOM;
diff --git a/x-pack/plugins/reporting/server/lib/layouts/print_layout.ts b/x-pack/plugins/reporting/server/lib/layouts/print_layout.ts
index e979cdeeb71fec..8db1fa7ff63478 100644
--- a/x-pack/plugins/reporting/server/lib/layouts/print_layout.ts
+++ b/x-pack/plugins/reporting/server/lib/layouts/print_layout.ts
@@ -8,16 +8,12 @@ import path from 'path';
import { PageOrientation, PredefinedPageSize } from 'pdfmake/interfaces';
import { EvaluateFn, SerializableOrJSHandle } from 'puppeteer';
import { LevelLogger } from '../';
+import { getDefaultLayoutSelectors } from '../../../common';
+import { LAYOUT_TYPES } from '../../../common/constants';
+import { LayoutSelectorDictionary, Size } from '../../../common/types';
import { HeadlessChromiumDriver } from '../../browsers';
import { CaptureConfig } from '../../types';
-import {
- getDefaultLayoutSelectors,
- LayoutInstance,
- LayoutSelectorDictionary,
- LayoutTypes,
- Size,
-} from './';
-import { Layout } from './layout';
+import { Layout, LayoutInstance } from './';
export class PrintLayout extends Layout implements LayoutInstance {
public readonly selectors: LayoutSelectorDictionary = {
@@ -28,7 +24,7 @@ export class PrintLayout extends Layout implements LayoutInstance {
private captureConfig: CaptureConfig;
constructor(captureConfig: CaptureConfig) {
- super(LayoutTypes.PRINT);
+ super(LAYOUT_TYPES.PRINT);
this.captureConfig = captureConfig;
}
diff --git a/x-pack/plugins/reporting/server/lib/store/index.ts b/x-pack/plugins/reporting/server/lib/store/index.ts
index a48f2661203236..17f0fb5bf03893 100644
--- a/x-pack/plugins/reporting/server/lib/store/index.ts
+++ b/x-pack/plugins/reporting/server/lib/store/index.ts
@@ -4,5 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export { Report, ReportDocument } from './report';
+export { ReportDocument } from '../../../common/types';
+export { Report } from './report';
export { ReportingStore } from './store';
diff --git a/x-pack/plugins/reporting/server/lib/store/report.ts b/x-pack/plugins/reporting/server/lib/store/report.ts
index d82b90f4025ed2..2e4473ef8f2ea6 100644
--- a/x-pack/plugins/reporting/server/lib/store/report.ts
+++ b/x-pack/plugins/reporting/server/lib/store/report.ts
@@ -7,51 +7,8 @@
import moment from 'moment';
// @ts-ignore no module definition
import Puid from 'puid';
-import { JobStatus, ReportApiJSON } from '../../../common/types';
-import { JobStatuses } from '../../../constants';
-import { LayoutParams } from '../layouts';
-import { TaskRunResult } from '../tasks';
-
-interface ReportDocumentHead {
- _id: string;
- _index: string;
- _seq_no: unknown;
- _primary_term: unknown;
-}
-
-/*
- * The document created by Reporting to store in the .reporting index
- */
-export interface ReportDocument extends ReportDocumentHead {
- _source: ReportSource;
-}
-
-export interface ReportSource {
- jobtype: string;
- kibana_name: string;
- kibana_id: string;
- created_by: string | false;
- payload: {
- headers: string; // encrypted headers
- browserTimezone?: string; // may use timezone from advanced settings
- objectType: string;
- title: string;
- layout?: LayoutParams;
- };
- meta: { objectType: string; layout?: string };
- browser_type: string;
- max_attempts: number;
- timeout: number;
-
- status: JobStatus;
- attempts: number;
- output: TaskRunResult | null;
- started_at?: string;
- completed_at?: string;
- created_at: string;
- priority?: number;
- process_expiration?: string;
-}
+import { JOB_STATUSES } from '../../../common/constants';
+import { ReportApiJSON, ReportDocumentHead, ReportSource } from '../../../common/types';
const puid = new Puid();
@@ -107,7 +64,7 @@ export class Report implements Partial {
this.browser_type = opts.browser_type;
this.priority = opts.priority;
- this.status = opts.status || JobStatuses.PENDING;
+ this.status = opts.status || JOB_STATUSES.PENDING;
this.output = opts.output || null;
}
@@ -175,3 +132,5 @@ export class Report implements Partial {
};
}
}
+
+export { ReportApiJSON, ReportSource };
diff --git a/x-pack/plugins/reporting/server/lib/tasks/index.ts b/x-pack/plugins/reporting/server/lib/tasks/index.ts
index 0dd9945985bfb5..c866c81c9793c0 100644
--- a/x-pack/plugins/reporting/server/lib/tasks/index.ts
+++ b/x-pack/plugins/reporting/server/lib/tasks/index.ts
@@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { ReportSource, TaskRunResult } from '../../../common/types';
import { BasePayload } from '../../types';
-import { ReportSource } from '../store/report';
/*
* The document created by Reporting to store as task parameters for Task
@@ -22,11 +22,4 @@ export interface ReportTaskParams {
meta: ReportSource['meta'];
}
-export interface TaskRunResult {
- content_type: string | null;
- content: string | null;
- csv_contains_formulas?: boolean;
- size: number;
- max_size_reached?: boolean;
- warnings?: string[];
-}
+export { TaskRunResult };
diff --git a/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts b/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts
index 400fbb16f54dcb..6ac5875acd34c6 100644
--- a/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts
+++ b/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts
@@ -7,7 +7,6 @@
import { schema } from '@kbn/config-schema';
import { KibanaRequest } from 'src/core/server';
import { ReportingCore } from '../';
-import { API_BASE_GENERATE_V1 } from '../../common/constants';
import { createJobFnFactory } from '../export_types/csv_from_savedobject/create_job';
import { runTaskFnFactory } from '../export_types/csv_from_savedobject/execute_job';
import {
@@ -20,6 +19,9 @@ import { authorizedUserPreRoutingFactory } from './lib/authorized_user_pre_routi
import { getJobParamsFromRequest } from './lib/get_job_params_from_request';
import { HandlerErrorFunction } from './types';
+const API_BASE_URL_V1 = '/api/reporting/v1';
+const API_BASE_GENERATE_V1 = `${API_BASE_URL_V1}/generate`;
+
export type CsvFromSavedObjectRequest = KibanaRequest<
JobParamsPanelCsv,
unknown,
diff --git a/x-pack/plugins/reporting/server/test_helpers/create_mock_layoutinstance.ts b/x-pack/plugins/reporting/server/test_helpers/create_mock_layoutinstance.ts
index c9dbbda9fd68de..12a3ac5c762c74 100644
--- a/x-pack/plugins/reporting/server/test_helpers/create_mock_layoutinstance.ts
+++ b/x-pack/plugins/reporting/server/test_helpers/create_mock_layoutinstance.ts
@@ -4,12 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { createLayout, LayoutInstance, LayoutTypes } from '../lib/layouts';
+import { LAYOUT_TYPES } from '../../common/constants';
+import { createLayout, LayoutInstance } from '../lib/layouts';
import { CaptureConfig } from '../types';
export const createMockLayoutInstance = (captureConfig: CaptureConfig) => {
const mockLayout = createLayout(captureConfig, {
- id: LayoutTypes.PRESERVE_LAYOUT,
+ id: LAYOUT_TYPES.PRESERVE_LAYOUT,
dimensions: { height: 100, width: 100 },
}) as LayoutInstance;
mockLayout.selectors = {
diff --git a/x-pack/plugins/reporting/server/types.ts b/x-pack/plugins/reporting/server/types.ts
index eb046a3eab0752..8cd26df032f641 100644
--- a/x-pack/plugins/reporting/server/types.ts
+++ b/x-pack/plugins/reporting/server/types.ts
@@ -8,15 +8,15 @@ import { KibanaRequest, RequestHandlerContext } from 'src/core/server';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { DataPluginStart } from 'src/plugins/data/server/plugin';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
-import { SpacesPluginSetup } from '../../spaces/server';
-import { CancellationToken } from '../../../plugins/reporting/common';
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
import { LicensingPluginSetup } from '../../licensing/server';
import { AuthenticatedUser, SecurityPluginSetup } from '../../security/server';
+import { SpacesPluginSetup } from '../../spaces/server';
+import { CancellationToken } from '../common';
+import { BaseParams } from '../common/types';
import { ReportingConfigType } from './config';
import { ReportingCore } from './core';
import { LevelLogger } from './lib';
-import { LayoutParams } from './lib/layouts';
import { ReportTaskParams, TaskRunResult } from './lib/tasks';
/*
@@ -47,12 +47,7 @@ export type ReportingUser = { username: AuthenticatedUser['username'] } | false;
export type CaptureConfig = ReportingConfigType['capture'];
export type ScrollConfig = ReportingConfigType['csv']['scroll'];
-export interface BaseParams {
- browserTimezone?: string; // browserTimezone is optional: it is not in old POST URLs that were generated prior to being added to this interface
- layout?: LayoutParams;
- objectType: string;
- title: string;
-}
+export { BaseParams };
// base params decorated with encrypted headers that come into runJob functions
export interface BasePayload extends BaseParams {
diff --git a/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts b/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts
index 3250e048edad21..890def5b63d4a7 100644
--- a/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts
@@ -33,6 +33,9 @@ export const factory = (): PolicyConfig => {
logging: {
file: 'info',
},
+ antivirus_registration: {
+ enabled: false,
+ },
},
mac: {
events: {
diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts
index 1d64578a6a7f11..673d04c856935f 100644
--- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts
@@ -8,6 +8,7 @@ import { ApplicationStart } from 'kibana/public';
import { NewPackagePolicy, PackagePolicy } from '../../../../fleet/common';
import { ManifestSchema } from '../schema/manifest';
+export * from './os';
export * from './trusted_apps';
/**
@@ -880,6 +881,9 @@ export interface PolicyConfig {
enabled: boolean;
};
};
+ antivirus_registration: {
+ enabled: boolean;
+ };
};
mac: {
advanced?: {};
@@ -919,7 +923,10 @@ export interface UIPolicyConfig {
/**
* Windows-specific policy configuration that is supported via the UI
*/
- windows: Pick;
+ windows: Pick<
+ PolicyConfig['windows'],
+ 'events' | 'malware' | 'popup' | 'antivirus_registration' | 'advanced'
+ >;
/**
* Mac-specific policy configuration that is supported via the UI
*/
diff --git a/x-pack/plugins/security_solution/common/endpoint/types/os.ts b/x-pack/plugins/security_solution/common/endpoint/types/os.ts
new file mode 100644
index 00000000000000..b9afbd63ecd54a
--- /dev/null
+++ b/x-pack/plugins/security_solution/common/endpoint/types/os.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;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export type Linux = 'linux';
+export type MacOS = 'macos';
+export type Windows = 'windows';
+export type OperatingSystem = Linux | MacOS | Windows;
diff --git a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts
index 3568136dd0e7b9..79d66443bc8f13 100644
--- a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts
@@ -11,6 +11,7 @@ import {
GetTrustedAppsRequestSchema,
PostTrustedAppCreateRequestSchema,
} from '../schema/trusted_apps';
+import { Linux, MacOS, Windows } from './os';
/** API request params for deleting Trusted App entry */
export type DeleteTrustedAppsRequestParams = TypeOf;
@@ -51,11 +52,11 @@ export type NewTrustedApp = {
description?: string;
} & (
| {
- os: 'linux' | 'macos';
+ os: Linux | MacOS;
entries: MacosLinuxConditionEntry[];
}
| {
- os: 'windows';
+ os: Windows;
entries: WindowsConditionEntry[];
}
);
diff --git a/x-pack/plugins/security_solution/public/management/common/translations.ts b/x-pack/plugins/security_solution/public/management/common/translations.ts
index d24eb1bd315fa7..415658c1fd6afd 100644
--- a/x-pack/plugins/security_solution/public/management/common/translations.ts
+++ b/x-pack/plugins/security_solution/public/management/common/translations.ts
@@ -6,6 +6,8 @@
import { i18n } from '@kbn/i18n';
+import { OperatingSystem } from '../../../common/endpoint/types';
+
export const ENDPOINTS_TAB = i18n.translate('xpack.securitySolution.endpointsTab', {
defaultMessage: 'Endpoints',
});
@@ -21,3 +23,15 @@ export const TRUSTED_APPS_TAB = i18n.translate('xpack.securitySolution.trustedAp
export const BETA_BADGE_LABEL = i18n.translate('xpack.securitySolution.administration.list.beta', {
defaultMessage: 'Beta',
});
+
+export const OS_TITLES: Readonly<{ [K in OperatingSystem]: string }> = {
+ windows: i18n.translate('xpack.securitySolution.administration.os.windows', {
+ defaultMessage: 'Windows',
+ }),
+ macos: i18n.translate('xpack.securitySolution.administration.os.macos', {
+ defaultMessage: 'Mac',
+ }),
+ linux: i18n.translate('xpack.securitySolution.administration.os.linux', {
+ defaultMessage: 'Linux',
+ }),
+};
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action.ts
index f5a219bce4a6bd..bda408cd00e75e 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/action.ts
@@ -31,6 +31,13 @@ interface UserChangedPolicyConfig {
};
}
+interface UserChangedAntivirusRegistration {
+ type: 'userChangedAntivirusRegistration';
+ payload: {
+ enabled: boolean;
+ };
+}
+
interface ServerReturnedPolicyDetailsAgentSummaryData {
type: 'serverReturnedPolicyDetailsAgentSummaryData';
payload: {
@@ -62,4 +69,5 @@ export type PolicyDetailsAction =
| ServerReturnedPolicyDetailsUpdateFailure
| ServerReturnedUpdatedPolicyDetailsData
| ServerFailedToReturnPolicyDetailsData
- | UserChangedPolicyConfig;
+ | UserChangedPolicyConfig
+ | UserChangedAntivirusRegistration;
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.test.ts
index 89ba05547f4479..69c2afbd019607 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.test.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.test.ts
@@ -245,6 +245,9 @@ describe('policy details: ', () => {
},
},
logging: { file: 'info' },
+ antivirus_registration: {
+ enabled: false,
+ },
},
mac: {
events: { process: true, file: true, network: true },
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts
index 43a6ad2c585b43..bcdc7ba2089c64 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts
@@ -4,11 +4,33 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { fullPolicy, isOnPolicyDetailsPage } from './selectors';
-import { Immutable, PolicyConfig, UIPolicyConfig } from '../../../../../../common/endpoint/types';
+import {
+ Immutable,
+ PolicyConfig,
+ UIPolicyConfig,
+ PolicyData,
+} from '../../../../../../common/endpoint/types';
import { ImmutableReducer } from '../../../../../common/store';
import { AppAction } from '../../../../../common/store/actions';
import { PolicyDetailsState } from '../../types';
+const updatePolicyConfigInPolicyData = (
+ policyData: Immutable,
+ policyConfig: Immutable
+) => ({
+ ...policyData,
+ inputs: policyData.inputs.map((input) => ({
+ ...input,
+ config: input.config && {
+ ...input.config,
+ policy: {
+ ...input.config.policy,
+ value: policyConfig,
+ },
+ },
+ })),
+});
+
/**
* Return a fresh copy of initial state, since we mutate state in the reducer.
*/
@@ -126,5 +148,26 @@ export const policyDetailsReducer: ImmutableReducer UIPolicyConfig = createSel
events: windows.events,
malware: windows.malware,
popup: windows.popup,
+ antivirus_registration: windows.antivirus_registration,
},
mac: {
advanced: mac.advanced,
@@ -122,6 +123,10 @@ export const policyConfig: (s: PolicyDetailsState) => UIPolicyConfig = createSel
}
);
+export const isAntivirusRegistrationEnabled = createSelector(policyConfig, (uiPolicyConfig) => {
+ return uiPolicyConfig.windows.antivirus_registration.enabled;
+});
+
/** Returns the total number of possible windows eventing configurations */
export const totalWindowsEvents = (state: PolicyDetailsState): number => {
const config = policyConfig(state);
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts
index 152caff3714b0a..3926ad2220e35d 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts
@@ -76,68 +76,6 @@ export interface PolicyListUrlSearchParams {
page_size: number;
}
-/**
- * Endpoint Policy configuration
- */
-export interface PolicyConfig {
- windows: {
- events: {
- dll_and_driver_load: boolean;
- dns: boolean;
- file: boolean;
- network: boolean;
- process: boolean;
- registry: boolean;
- security: boolean;
- };
- malware: MalwareFields;
- logging: {
- stdout: string;
- file: string;
- };
- advanced: PolicyConfigAdvancedOptions;
- };
- mac: {
- events: {
- file: boolean;
- process: boolean;
- network: boolean;
- };
- malware: MalwareFields;
- logging: {
- stdout: string;
- file: string;
- };
- advanced: PolicyConfigAdvancedOptions;
- };
- linux: {
- events: {
- file: boolean;
- process: boolean;
- network: boolean;
- };
- logging: {
- stdout: string;
- file: string;
- };
- advanced: PolicyConfigAdvancedOptions;
- };
-}
-
-interface PolicyConfigAdvancedOptions {
- elasticsearch: {
- indices: {
- control: string;
- event: string;
- logging: string;
- };
- kernel: {
- connect: boolean;
- process: boolean;
- };
- };
-}
-
export enum OS {
windows = 'windows',
mac = 'mac',
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.stories.tsx
new file mode 100644
index 00000000000000..4f288af393b7c0
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.stories.tsx
@@ -0,0 +1,60 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+import React from 'react';
+import { ThemeProvider } from 'styled-components';
+import { storiesOf, addDecorator } from '@storybook/react';
+import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
+import { EuiCheckbox, EuiSpacer, EuiSwitch, EuiText } from '@elastic/eui';
+
+import { ConfigForm } from '.';
+
+addDecorator((storyFn) => (
+ ({ eui: euiLightVars, darkMode: false })}>{storyFn()}
+));
+
+storiesOf('PolicyDetails/ConfigForm', module)
+ .add('One OS', () => {
+ return (
+
+ {'Some content'}
+
+ );
+ })
+ .add('Multiple OSs', () => {
+ return (
+
+ {'Some content'}
+
+ );
+ })
+ .add('Complex content', () => {
+ return (
+
+
+ {'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore ' +
+ 'et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut ' +
+ 'aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum ' +
+ 'dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia ' +
+ 'deserunt mollit anim id est laborum.'}
+
+
+ {}} />
+
+ {}} />
+ {}} />
+ {}} />
+
+ );
+ })
+ .add('Right corner content', () => {
+ const toggle = {}} />;
+
+ return (
+
+ {'Some content'}
+
+ );
+ });
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.tsx
new file mode 100644
index 00000000000000..30c35de9b907f3
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/components/config_form/index.tsx
@@ -0,0 +1,84 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FC, ReactNode, memo } from 'react';
+import { i18n } from '@kbn/i18n';
+import {
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiTitle,
+ EuiHorizontalRule,
+ EuiText,
+ EuiShowFor,
+ EuiPanel,
+} from '@elastic/eui';
+
+import { OperatingSystem } from '../../../../../../../common/endpoint/types';
+import { OS_TITLES } from '../../../../../common/translations';
+
+const TITLES = {
+ type: i18n.translate('xpack.securitySolution.endpoint.policyDetailType', {
+ defaultMessage: 'Type',
+ }),
+ os: i18n.translate('xpack.securitySolution.endpoint.policyDetailOS', {
+ defaultMessage: 'Operating System',
+ }),
+};
+
+interface ConfigFormProps {
+ /**
+ * A subtitle for this component.
+ **/
+ type: string;
+ /**
+ * Types of supported operating systems.
+ */
+ supportedOss: OperatingSystem[];
+ dataTestSubj?: string;
+ /** React Node to be put on the right corner of the card */
+ rightCorner?: ReactNode;
+}
+
+export const ConfigFormHeading: FC = memo(({ children }) => (
+
+ {children}
+
+));
+
+ConfigFormHeading.displayName = 'ConfigFormHeading';
+
+export const ConfigForm: FC = memo(
+ ({ type, supportedOss, dataTestSubj, rightCorner, children }) => (
+
+
+
+ {TITLES.type}
+ {type}
+
+
+ {TITLES.os}
+ {supportedOss.map((os) => OS_TITLES[os]).join(', ')}
+
+
+
+
+ {rightCorner}
+
+
+
+
+ {rightCorner}
+
+
+
+
+
+ {children}
+
+ )
+);
+
+ConfigForm.displayName = 'ConfigForm';
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx
index 8fc5de48f36db2..9c11bc6f5a4d13 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx
@@ -36,6 +36,7 @@ import { AgentsSummary } from './agents_summary';
import { VerticalDivider } from './vertical_divider';
import { WindowsEvents, MacEvents, LinuxEvents } from './policy_forms/events';
import { MalwareProtections } from './policy_forms/protections/malware';
+import { AntivirusRegistrationForm } from './policy_forms/antivirus_registration';
import { useToasts } from '../../../../common/lib/kibana';
import { AppAction } from '../../../../common/store/actions';
import { SpyRoute } from '../../../../common/utils/route/spy_routes';
@@ -251,6 +252,8 @@ export const PolicyDetails = React.memo(() => {
+
+
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/antivirus_registration/index.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/antivirus_registration/index.tsx
new file mode 100644
index 00000000000000..8d1ac29c8ce1e7
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/antivirus_registration/index.tsx
@@ -0,0 +1,64 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { memo, useCallback } from 'react';
+import { useDispatch } from 'react-redux';
+import { i18n } from '@kbn/i18n';
+import { EuiSpacer, EuiSwitch, EuiText } from '@elastic/eui';
+
+import { isAntivirusRegistrationEnabled } from '../../../store/policy_details/selectors';
+import { usePolicyDetailsSelector } from '../../policy_hooks';
+import { ConfigForm } from '../../components/config_form';
+
+export const AntivirusRegistrationForm = memo(() => {
+ const antivirusRegistrationEnabled = usePolicyDetailsSelector(isAntivirusRegistrationEnabled);
+ const dispatch = useDispatch();
+
+ const handleSwitchChange = useCallback(
+ (event) =>
+ dispatch({
+ type: 'userChangedAntivirusRegistration',
+ payload: {
+ enabled: event.target.checked,
+ },
+ }),
+ [dispatch]
+ );
+
+ return (
+
+
+ {i18n.translate(
+ 'xpack.securitySolution.endpoint.policy.details.antivirusRegistration.explanation',
+ {
+ defaultMessage: 'Switch the toggle to on to register Elastic anti-virus',
+ }
+ )}
+
+
+
+
+ );
+});
+
+AntivirusRegistrationForm.displayName = 'AntivirusRegistrationForm';
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/config_form.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/config_form.tsx
deleted file mode 100644
index 8e3c4138efb36e..00000000000000
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/config_form.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React, { useMemo } from 'react';
-import {
- EuiCard,
- EuiFlexGroup,
- EuiFlexItem,
- EuiTitle,
- EuiHorizontalRule,
- EuiText,
-} from '@elastic/eui';
-import { FormattedMessage } from '@kbn/i18n/react';
-import styled from 'styled-components';
-
-const PolicyDetailCard = styled.div`
- .policyDetailTitleOS {
- flex-grow: 2;
- }
- .policyDetailTitleFlexItem {
- margin: 0;
- }
-`;
-export const ConfigForm: React.FC<{
- /**
- * A subtitle for this component.
- **/
- type: string;
- /**
- * Types of supported operating systems.
- */
- supportedOss: React.ReactNode;
- children: React.ReactNode;
- dataTestSubj: string;
- /** React Node to be put on the right corner of the card */
- rightCorner: React.ReactNode;
-}> = React.memo(({ type, supportedOss, children, dataTestSubj, rightCorner }) => {
- const typeTitle = useMemo(() => {
- return (
-
-
-
-
-
-
-
-
-
-
- {type}
-
-
-
-
-
-
-
-
-
-
-
- {supportedOss}
-
-
- {rightCorner}
-
- );
- }, [rightCorner, supportedOss, type]);
-
- return (
-
-
-
- {children}
-
-
- );
-});
-
-ConfigForm.displayName = 'ConfigForm';
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/linux.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/linux.tsx
index 66126adb7a4e1d..b43f93f1a1e2be 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/linux.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/linux.tsx
@@ -6,15 +6,19 @@
import React, { useMemo } from 'react';
import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
-import { EuiTitle, EuiText, EuiSpacer } from '@elastic/eui';
+import { EuiText, EuiSpacer } from '@elastic/eui';
import { EventsCheckbox } from './checkbox';
import { OS } from '../../../types';
import { usePolicyDetailsSelector } from '../../policy_hooks';
import { selectedLinuxEvents, totalLinuxEvents } from '../../../store/policy_details/selectors';
-import { ConfigForm } from '../config_form';
+import { ConfigForm, ConfigFormHeading } from '../../components/config_form';
import { getIn, setIn } from '../../../models/policy_details_config';
import { UIPolicyConfig } from '../../../../../../../common/endpoint/types';
+import {
+ COLLECTIONS_ENABLED_MESSAGE,
+ EVENTS_FORM_TYPE_LABEL,
+ EVENTS_HEADING,
+} from './translations';
export const LinuxEvents = React.memo(() => {
const selected = usePolicyDetailsSelector(selectedLinuxEvents);
@@ -59,14 +63,7 @@ export const LinuxEvents = React.memo(() => {
];
return (
<>
-
-
-
-
-
+ {EVENTS_HEADING}
{items.map((item, index) => {
return (
@@ -85,28 +82,16 @@ export const LinuxEvents = React.memo(() => {
);
}, []);
- const collectionsEnabled = useMemo(() => {
- return (
-
-
-
- );
- }, [selected, total]);
-
return (
+ {COLLECTIONS_ENABLED_MESSAGE(selected, total)}
+
+ }
>
{checkboxes}
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/mac.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/mac.tsx
index dc70fc0ba0f4fd..fbbe50fbec1b03 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/mac.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/mac.tsx
@@ -6,15 +6,19 @@
import React, { useMemo } from 'react';
import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
-import { EuiTitle, EuiText, EuiSpacer } from '@elastic/eui';
+import { EuiText, EuiSpacer } from '@elastic/eui';
import { EventsCheckbox } from './checkbox';
import { OS } from '../../../types';
import { usePolicyDetailsSelector } from '../../policy_hooks';
import { selectedMacEvents, totalMacEvents } from '../../../store/policy_details/selectors';
-import { ConfigForm } from '../config_form';
+import { ConfigForm, ConfigFormHeading } from '../../components/config_form';
import { getIn, setIn } from '../../../models/policy_details_config';
import { UIPolicyConfig } from '../../../../../../../common/endpoint/types';
+import {
+ COLLECTIONS_ENABLED_MESSAGE,
+ EVENTS_FORM_TYPE_LABEL,
+ EVENTS_HEADING,
+} from './translations';
export const MacEvents = React.memo(() => {
const selected = usePolicyDetailsSelector(selectedMacEvents);
@@ -59,14 +63,7 @@ export const MacEvents = React.memo(() => {
];
return (
<>
-
-
-
-
-
+ {EVENTS_HEADING}
{items.map((item, index) => {
return (
@@ -85,28 +82,16 @@ export const MacEvents = React.memo(() => {
);
}, []);
- const collectionsEnabled = useMemo(() => {
- return (
-
-
-
- );
- }, [selected, total]);
-
return (
+ {COLLECTIONS_ENABLED_MESSAGE(selected, total)}
+
+ }
>
{checkboxes}
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/translations.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/translations.ts
new file mode 100644
index 00000000000000..3b48b7969a8ced
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/translations.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const EVENTS_HEADING = i18n.translate(
+ 'xpack.securitySolution.endpoint.policyDetailsConfig.eventingEvents',
+ {
+ defaultMessage: 'Events',
+ }
+);
+
+export const EVENTS_FORM_TYPE_LABEL = i18n.translate(
+ 'xpack.securitySolution.endpoint.policy.details.eventCollection',
+ {
+ defaultMessage: 'Event Collection',
+ }
+);
+
+export const COLLECTIONS_ENABLED_MESSAGE = (selected: number, total: number) => {
+ return i18n.translate('xpack.securitySolution.endpoint.policy.details.eventCollectionsEnabled', {
+ defaultMessage: '{selected} / {total} event collections enabled',
+ values: { selected, total },
+ });
+};
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/windows.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/windows.tsx
index 5acdf67922a3a6..f7b1a8e901ed2c 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/windows.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/events/windows.tsx
@@ -6,15 +6,19 @@
import React, { useMemo } from 'react';
import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
-import { EuiTitle, EuiText, EuiSpacer } from '@elastic/eui';
+import { EuiText, EuiSpacer } from '@elastic/eui';
import { EventsCheckbox } from './checkbox';
import { OS } from '../../../types';
import { usePolicyDetailsSelector } from '../../policy_hooks';
import { selectedWindowsEvents, totalWindowsEvents } from '../../../store/policy_details/selectors';
-import { ConfigForm } from '../config_form';
+import { ConfigForm, ConfigFormHeading } from '../../components/config_form';
import { setIn, getIn } from '../../../models/policy_details_config';
import { UIPolicyConfig, Immutable } from '../../../../../../../common/endpoint/types';
+import {
+ COLLECTIONS_ENABLED_MESSAGE,
+ EVENTS_FORM_TYPE_LABEL,
+ EVENTS_HEADING,
+} from './translations';
export const WindowsEvents = React.memo(() => {
const selected = usePolicyDetailsSelector(selectedWindowsEvents);
@@ -99,14 +103,7 @@ export const WindowsEvents = React.memo(() => {
];
return (
<>
-
-
-
-
-
+ {EVENTS_HEADING}
{items.map((item, index) => {
return (
@@ -125,28 +122,16 @@ export const WindowsEvents = React.memo(() => {
);
}, []);
- const collectionsEnabled = useMemo(() => {
- return (
-
-
-
- );
- }, [selected, total]);
-
return (
+ {COLLECTIONS_ENABLED_MESSAGE(selected, total)}
+
+ }
>
{checkboxes}
diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/malware.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/malware.tsx
index b61dee52697370..7259b2ec19ee2a 100644
--- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/malware.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/malware.tsx
@@ -7,10 +7,11 @@
import React, { useCallback, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiRadio,
EuiSwitch,
- EuiTitle,
EuiText,
EuiSpacer,
EuiTextArea,
@@ -18,15 +19,13 @@ import {
EuiCallOut,
EuiCheckbox,
} from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
import { cloneDeep } from 'lodash';
import { APP_ID } from '../../../../../../../common/constants';
import { SecurityPageName } from '../../../../../../app/types';
import { Immutable, ProtectionModes } from '../../../../../../../common/endpoint/types';
import { OS, MalwareProtectionOSes } from '../../../types';
-import { ConfigForm } from '../config_form';
+import { ConfigForm, ConfigFormHeading } from '../../components/config_form';
import { policyConfig } from '../../../store/policy_details/selectors';
import { usePolicyDetailsSelector } from '../../policy_hooks';
import { LinkToApp } from '../../../../../../common/components/endpoint/link_to_app';
@@ -200,14 +199,12 @@ export const MalwareProtections = React.memo(() => {
const radioButtons = useMemo(() => {
return (
<>
-
-
-
-
-
+
+
+
{radios.map((radio) => {
@@ -221,14 +218,12 @@ export const MalwareProtections = React.memo(() => {
})}
-
-
-
-
-
+
+
+
{
type={i18n.translate('xpack.securitySolution.endpoint.policy.details.malware', {
defaultMessage: 'Malware',
})}
- supportedOss={i18n.translate('xpack.securitySolution.endpoint.policy.details.windowsAndMac', {
- defaultMessage: 'Windows, Mac',
- })}
+ supportedOss={['windows', 'macos']}
dataTestSubj="malwareProtectionsForm"
rightCorner={protectionSwitch}
>
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx
index 211fc9ec3371e1..4bac9164e1d621 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx
@@ -118,7 +118,7 @@ describe('When showing the Trusted App Create Form', () => {
'.euiSuperSelect__listbox button.euiSuperSelect__item'
)
).map((button) => button.textContent);
- expect(options).toEqual(['Mac OS', 'Windows', 'Linux']);
+ expect(options).toEqual(['Mac', 'Windows', 'Linux']);
});
it('should show Description as optional', () => {
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/__snapshots__/index.test.tsx.snap
index a94e6287a4f58b..a47558257420c5 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/__snapshots__/index.test.tsx.snap
@@ -17,7 +17,7 @@ exports[`trusted_app_card TrustedAppCard should render correctly 1`] = `
value={
}
/>
@@ -112,7 +112,7 @@ exports[`trusted_app_card TrustedAppCard should trim long texts 1`] = `
value={
}
/>
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap
index c82b9cac8ab1fc..6d45059099f8d3 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap
@@ -412,7 +412,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
class="euiToolTipAnchor"
>
- Mac OS
+ Mac
@@ -1168,7 +1168,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
class="euiToolTipAnchor"
>
- Mac OS
+ Mac
@@ -1924,7 +1924,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
class="euiToolTipAnchor"
>
- Mac OS
+ Mac
@@ -3222,7 +3222,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
class="euiToolTipAnchor"
>
- Mac OS
+ Mac
@@ -3978,7 +3978,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
class="euiToolTipAnchor"
>
- Mac OS
+ Mac
@@ -4734,7 +4734,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
class="euiToolTipAnchor"
>
- Mac OS
+ Mac
@@ -5990,7 +5990,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
class="euiToolTipAnchor"
>
- Mac OS
+ Mac
@@ -6746,7 +6746,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
class="euiToolTipAnchor"
>
- Mac OS
+ Mac
@@ -7502,7 +7502,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
class="euiToolTipAnchor"
>
- Mac OS
+ Mac
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/__snapshots__/index.test.tsx.snap
index 2797c433b8236c..d0459871d48811 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/__snapshots__/index.test.tsx.snap
@@ -1061,7 +1061,7 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -1448,7 +1448,7 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -1835,7 +1835,7 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -2222,7 +2222,7 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -2609,7 +2609,7 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -2996,7 +2996,7 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -3383,7 +3383,7 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -4001,7 +4001,7 @@ exports[`TrustedAppsList renders correctly when loaded data 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -4388,7 +4388,7 @@ exports[`TrustedAppsList renders correctly when loaded data 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -4775,7 +4775,7 @@ exports[`TrustedAppsList renders correctly when loaded data 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -5162,7 +5162,7 @@ exports[`TrustedAppsList renders correctly when loaded data 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -5549,7 +5549,7 @@ exports[`TrustedAppsList renders correctly when loaded data 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -5936,7 +5936,7 @@ exports[`TrustedAppsList renders correctly when loaded data 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -6323,7 +6323,7 @@ exports[`TrustedAppsList renders correctly when loaded data 1`] = `
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -7099,7 +7099,7 @@ exports[`TrustedAppsList renders correctly when loading data for the second time
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -7486,7 +7486,7 @@ exports[`TrustedAppsList renders correctly when loading data for the second time
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -7873,7 +7873,7 @@ exports[`TrustedAppsList renders correctly when loading data for the second time
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -8260,7 +8260,7 @@ exports[`TrustedAppsList renders correctly when loading data for the second time
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -8647,7 +8647,7 @@ exports[`TrustedAppsList renders correctly when loading data for the second time
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -9034,7 +9034,7 @@ exports[`TrustedAppsList renders correctly when loading data for the second time
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -9421,7 +9421,7 @@ exports[`TrustedAppsList renders correctly when loading data for the second time
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -10039,7 +10039,7 @@ exports[`TrustedAppsList renders correctly when new page and page size set (not
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -10426,7 +10426,7 @@ exports[`TrustedAppsList renders correctly when new page and page size set (not
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -10813,7 +10813,7 @@ exports[`TrustedAppsList renders correctly when new page and page size set (not
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -11200,7 +11200,7 @@ exports[`TrustedAppsList renders correctly when new page and page size set (not
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -11587,7 +11587,7 @@ exports[`TrustedAppsList renders correctly when new page and page size set (not
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -11974,7 +11974,7 @@ exports[`TrustedAppsList renders correctly when new page and page size set (not
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
@@ -12361,7 +12361,7 @@ exports[`TrustedAppsList renders correctly when new page and page size set (not
class="euiToolTipAnchor eui-textTruncate"
>
- Mac OS
+ Mac
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts
index b2f62c2f1da4eb..4c2b3f0e59ccb4 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts
@@ -11,23 +11,13 @@ import {
WindowsConditionEntry,
} from '../../../../../common/endpoint/types';
+export { OS_TITLES } from '../../../common/translations';
+
export const ABOUT_TRUSTED_APPS = i18n.translate('xpack.securitySolution.trustedapps.aboutInfo', {
defaultMessage:
'Add a trusted application to improve performance or alleviate conflicts with other applications running on your hosts. Trusted applications will be applied to hosts running Endpoint Security.',
});
-export const OS_TITLES: Readonly<{ [K in TrustedApp['os']]: string }> = {
- windows: i18n.translate('xpack.securitySolution.trustedapps.os.windows', {
- defaultMessage: 'Windows',
- }),
- macos: i18n.translate('xpack.securitySolution.trustedapps.os.macos', {
- defaultMessage: 'Mac OS',
- }),
- linux: i18n.translate('xpack.securitySolution.trustedapps.os.linux', {
- defaultMessage: 'Linux',
- }),
-};
-
type Entry = MacosLinuxConditionEntry | WindowsConditionEntry;
export const CONDITION_FIELD_TITLE: { [K in Entry['field']]: string } = {
diff --git a/x-pack/plugins/task_manager/server/monitoring/workload_statistics.test.ts b/x-pack/plugins/task_manager/server/monitoring/workload_statistics.test.ts
index d1c5256c81c63d..c2e62b6e1898b7 100644
--- a/x-pack/plugins/task_manager/server/monitoring/workload_statistics.test.ts
+++ b/x-pack/plugins/task_manager/server/monitoring/workload_statistics.test.ts
@@ -46,9 +46,13 @@ describe('Workload Statistics Aggregator', () => {
aggregations: {
taskType: {
buckets: [],
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
},
schedule: {
buckets: [],
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
},
idleTasks: {
doc_count: 0,
@@ -158,6 +162,8 @@ describe('Workload Statistics Aggregator', () => {
},
aggregations: {
schedule: {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
buckets: [
{
key: '3600s',
@@ -174,11 +180,15 @@ describe('Workload Statistics Aggregator', () => {
],
},
taskType: {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
buckets: [
{
key: 'actions_telemetry',
doc_count: 2,
status: {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
buckets: [
{
key: 'idle',
@@ -191,6 +201,8 @@ describe('Workload Statistics Aggregator', () => {
key: 'alerting_telemetry',
doc_count: 1,
status: {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
buckets: [
{
key: 'idle',
@@ -203,6 +215,8 @@ describe('Workload Statistics Aggregator', () => {
key: 'session_cleanup',
doc_count: 1,
status: {
+ doc_count_error_upper_bound: 0,
+ sum_other_doc_count: 0,
buckets: [
{
key: 'idle',
@@ -608,6 +622,7 @@ describe('padBuckets', () => {
key: 1601668047000,
doc_count: 1,
interval: {
+ doc_count_error_upper_bound: 0,
sum_other_doc_count: 0,
buckets: [],
},
@@ -617,6 +632,7 @@ describe('padBuckets', () => {
key: 1601668050000,
doc_count: 1,
interval: {
+ doc_count_error_upper_bound: 0,
sum_other_doc_count: 0,
buckets: [],
},
@@ -626,6 +642,7 @@ describe('padBuckets', () => {
key: 1601668053000,
doc_count: 0,
interval: {
+ doc_count_error_upper_bound: 0,
sum_other_doc_count: 0,
buckets: [],
},
@@ -635,6 +652,7 @@ describe('padBuckets', () => {
key: 1601668056000,
doc_count: 0,
interval: {
+ doc_count_error_upper_bound: 0,
sum_other_doc_count: 0,
buckets: [],
},
@@ -644,6 +662,7 @@ describe('padBuckets', () => {
key: 1601668059000,
doc_count: 0,
interval: {
+ doc_count_error_upper_bound: 0,
sum_other_doc_count: 0,
buckets: [],
},
@@ -653,6 +672,7 @@ describe('padBuckets', () => {
key: 1601668062000,
doc_count: 1,
interval: {
+ doc_count_error_upper_bound: 0,
sum_other_doc_count: 0,
buckets: [],
},
@@ -678,13 +698,13 @@ describe('padBuckets', () => {
key_as_string: '2020-10-02T20:40:09.000Z',
key: 1601671209000,
doc_count: 1,
- interval: { buckets: [] },
+ interval: { buckets: [], sum_other_doc_count: 0, doc_count_error_upper_bound: 0 },
},
{
key_as_string: '2020-10-02T20:40:12.000Z',
key: 1601671212000,
doc_count: 1,
- interval: { buckets: [] },
+ interval: { buckets: [], sum_other_doc_count: 0, doc_count_error_upper_bound: 0 },
},
],
},
@@ -707,13 +727,13 @@ describe('padBuckets', () => {
key_as_string: '2020-10-02T20:40:09.000Z',
key: 1601671209000,
doc_count: 1,
- interval: { buckets: [] },
+ interval: { buckets: [], sum_other_doc_count: 0, doc_count_error_upper_bound: 0 },
},
{
key_as_string: '2020-10-02T20:40:12.000Z',
key: 1601671212000,
doc_count: 1,
- interval: { buckets: [] },
+ interval: { buckets: [], sum_other_doc_count: 0, doc_count_error_upper_bound: 0 },
},
],
},
@@ -796,7 +816,7 @@ function mockHistogram(
key_as_string: key.toISOString(),
key: key.getTime(),
doc_count: count,
- interval: { buckets: [] },
+ interval: { buckets: [], doc_count_error_upper_bound: 0, sum_other_doc_count: 0 },
});
}
return histogramBuckets;
@@ -806,6 +826,8 @@ function mockHistogram(
key: number;
doc_count: number;
interval: {
+ doc_count_error_upper_bound: number;
+ sum_other_doc_count: number;
buckets: Array<{
key: string;
doc_count: number;
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 1892174550a1f6..238b3dccc698a4 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -17498,8 +17498,6 @@
"xpack.securitySolution.endpoint.policy.details.detectionRulesMessage": "{detectionRulesLink}を表示します。事前構築済みルールは、[検出ルール]ページで「Elastic」というタグが付けられています。",
"xpack.securitySolution.endpoint.policy.details.eventCollection": "イベント収集",
"xpack.securitySolution.endpoint.policy.details.eventCollectionsEnabled": "{selected} / {total}件のイベント収集が有効です",
- "xpack.securitySolution.endpoint.policy.details.linux": "Linux",
- "xpack.securitySolution.endpoint.policy.details.mac": "Mac",
"xpack.securitySolution.endpoint.policy.details.malware": "マルウェア",
"xpack.securitySolution.endpoint.policy.details.malwareProtectionsEnabled": "マルウェア保護{mode, select, true {有効} false {無効}}",
"xpack.securitySolution.endpoint.policy.details.prevent": "防御",
@@ -17514,8 +17512,6 @@
"xpack.securitySolution.endpoint.policy.details.updateErrorTitle": "失敗しました。",
"xpack.securitySolution.endpoint.policy.details.updateSuccessMessage": "統合{name}が更新されました。",
"xpack.securitySolution.endpoint.policy.details.updateSuccessTitle": "成功!",
- "xpack.securitySolution.endpoint.policy.details.windows": "Windows",
- "xpack.securitySolution.endpoint.policy.details.windowsAndMac": "Windows、Mac",
"xpack.securitySolution.endpoint.policyDetailOS": "オペレーティングシステム",
"xpack.securitySolution.endpoint.policyDetails.agentsSummary.errorTitle": "エラー",
"xpack.securitySolution.endpoint.policyDetails.agentsSummary.offlineTitle": "オフライン",
@@ -18466,9 +18462,6 @@
"xpack.securitySolution.trustedapps.logicalConditionBuilder.group.andOperator": "AND",
"xpack.securitySolution.trustedapps.logicalConditionBuilder.noEntries": "条件が定義されていません",
"xpack.securitySolution.trustedapps.noResults": "項目が見つかりません",
- "xpack.securitySolution.trustedapps.os.linux": "Linux",
- "xpack.securitySolution.trustedapps.os.macos": "Mac OS",
- "xpack.securitySolution.trustedapps.os.windows": "Windows",
"xpack.securitySolution.trustedapps.trustedapp.createdAt": "作成日",
"xpack.securitySolution.trustedapps.trustedapp.createdBy": "作成者",
"xpack.securitySolution.trustedapps.trustedapp.description": "説明",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 65b7f4dfe9f279..48654a5ec5ff43 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -17516,8 +17516,6 @@
"xpack.securitySolution.endpoint.policy.details.detectionRulesMessage": "请查看{detectionRulesLink}。在“检测规则”页面上,预置规则标记有“Elastic”。",
"xpack.securitySolution.endpoint.policy.details.eventCollection": "事件收集",
"xpack.securitySolution.endpoint.policy.details.eventCollectionsEnabled": "{selected} / {total} 事件收集已启用",
- "xpack.securitySolution.endpoint.policy.details.linux": "Linux",
- "xpack.securitySolution.endpoint.policy.details.mac": "Mac",
"xpack.securitySolution.endpoint.policy.details.malware": "恶意软件",
"xpack.securitySolution.endpoint.policy.details.malwareProtectionsEnabled": "恶意软件防护{mode, select, true {已启用} false {已禁用}}",
"xpack.securitySolution.endpoint.policy.details.prevent": "防御",
@@ -17533,8 +17531,6 @@
"xpack.securitySolution.endpoint.policy.details.updateErrorTitle": "失败!",
"xpack.securitySolution.endpoint.policy.details.updateSuccessMessage": "集成 {name} 已更新。",
"xpack.securitySolution.endpoint.policy.details.updateSuccessTitle": "成功!",
- "xpack.securitySolution.endpoint.policy.details.windows": "Windows",
- "xpack.securitySolution.endpoint.policy.details.windowsAndMac": "Windows、Mac",
"xpack.securitySolution.endpoint.policyDetailOS": "操作系统",
"xpack.securitySolution.endpoint.policyDetails.agentsSummary.errorTitle": "错误",
"xpack.securitySolution.endpoint.policyDetails.agentsSummary.offlineTitle": "脱机",
@@ -18485,9 +18481,6 @@
"xpack.securitySolution.trustedapps.logicalConditionBuilder.group.andOperator": "AND",
"xpack.securitySolution.trustedapps.logicalConditionBuilder.noEntries": "未定义条件",
"xpack.securitySolution.trustedapps.noResults": "找不到项目",
- "xpack.securitySolution.trustedapps.os.linux": "Linux",
- "xpack.securitySolution.trustedapps.os.macos": "Mac OS",
- "xpack.securitySolution.trustedapps.os.windows": "Windows",
"xpack.securitySolution.trustedapps.trustedapp.createdAt": "创建日期",
"xpack.securitySolution.trustedapps.trustedapp.createdBy": "创建者",
"xpack.securitySolution.trustedapps.trustedapp.description": "描述",
diff --git a/x-pack/plugins/ui_actions_enhanced/common/index.ts b/x-pack/plugins/ui_actions_enhanced/common/index.ts
new file mode 100644
index 00000000000000..9f4141dbcae7df
--- /dev/null
+++ b/x-pack/plugins/ui_actions_enhanced/common/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export * from './types';
diff --git a/x-pack/plugins/ui_actions_enhanced/server/dynamic_action_enhancement.ts b/x-pack/plugins/ui_actions_enhanced/server/dynamic_action_enhancement.ts
index b3664362009149..ade78c31211ab2 100644
--- a/x-pack/plugins/ui_actions_enhanced/server/dynamic_action_enhancement.ts
+++ b/x-pack/plugins/ui_actions_enhanced/server/dynamic_action_enhancement.ts
@@ -7,11 +7,11 @@
import { EnhancementRegistryDefinition } from '../../../../src/plugins/embeddable/server';
import { SavedObjectReference } from '../../../../src/core/types';
import { DynamicActionsState, SerializedEvent } from './types';
-import { AdvancedUiActionsPublicPlugin } from './plugin';
+import { AdvancedUiActionsServerPlugin } from './plugin';
import { SerializableState } from '../../../../src/plugins/kibana_utils/common';
export const dynamicActionEnhancement = (
- uiActionsEnhanced: AdvancedUiActionsPublicPlugin
+ uiActionsEnhanced: AdvancedUiActionsServerPlugin
): EnhancementRegistryDefinition => {
return {
id: 'dynamicActions',
diff --git a/x-pack/plugins/ui_actions_enhanced/server/index.ts b/x-pack/plugins/ui_actions_enhanced/server/index.ts
index 5419c4135796df..e1363be35e2e9a 100644
--- a/x-pack/plugins/ui_actions_enhanced/server/index.ts
+++ b/x-pack/plugins/ui_actions_enhanced/server/index.ts
@@ -4,13 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { AdvancedUiActionsPublicPlugin } from './plugin';
+import { AdvancedUiActionsServerPlugin } from './plugin';
export function plugin() {
- return new AdvancedUiActionsPublicPlugin();
+ return new AdvancedUiActionsServerPlugin();
}
-export { AdvancedUiActionsPublicPlugin as Plugin };
+export { AdvancedUiActionsServerPlugin as Plugin };
export {
SetupContract as AdvancedUiActionsSetup,
StartContract as AdvancedUiActionsStart,
diff --git a/x-pack/plugins/ui_actions_enhanced/server/plugin.ts b/x-pack/plugins/ui_actions_enhanced/server/plugin.ts
index d6d18848be4de4..718304018730de 100644
--- a/x-pack/plugins/ui_actions_enhanced/server/plugin.ts
+++ b/x-pack/plugins/ui_actions_enhanced/server/plugin.ts
@@ -16,7 +16,7 @@ import {
} from './types';
export interface SetupContract {
- registerActionFactory: any;
+ registerActionFactory: (definition: ActionFactoryDefinition) => void;
}
export type StartContract = void;
@@ -25,7 +25,7 @@ interface SetupDependencies {
embeddable: EmbeddableSetup; // Embeddable are needed because they register basic triggers/actions.
}
-export class AdvancedUiActionsPublicPlugin
+export class AdvancedUiActionsServerPlugin
implements Plugin {
protected readonly actionFactories: ActionFactoryRegistry = new Map();
diff --git a/x-pack/test/apm_api_integration/basic/tests/index.ts b/x-pack/test/apm_api_integration/basic/tests/index.ts
index df3e60d79aca57..39dd721c7067e9 100644
--- a/x-pack/test/apm_api_integration/basic/tests/index.ts
+++ b/x-pack/test/apm_api_integration/basic/tests/index.ts
@@ -25,6 +25,10 @@ export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderCont
loadTestFile(require.resolve('./services/transaction_types'));
});
+ describe('Service overview', function () {
+ loadTestFile(require.resolve('./service_overview/error_groups'));
+ });
+
describe('Settings', function () {
loadTestFile(require.resolve('./settings/custom_link'));
loadTestFile(require.resolve('./settings/agent_configuration'));
diff --git a/x-pack/test/apm_api_integration/basic/tests/service_overview/error_groups.ts b/x-pack/test/apm_api_integration/basic/tests/service_overview/error_groups.ts
new file mode 100644
index 00000000000000..b699a30d40418c
--- /dev/null
+++ b/x-pack/test/apm_api_integration/basic/tests/service_overview/error_groups.ts
@@ -0,0 +1,220 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+import qs from 'querystring';
+import { pick, uniqBy } from 'lodash';
+import { expectSnapshot } from '../../../common/match_snapshot';
+import { FtrProviderContext } from '../../../../common/ftr_provider_context';
+import archives from '../../../common/archives_metadata';
+
+export default function ApiTest({ getService }: FtrProviderContext) {
+ const supertest = getService('supertest');
+ const esArchiver = getService('esArchiver');
+
+ const archiveName = 'apm_8.0.0';
+ const { start, end } = archives[archiveName];
+
+ describe('Service overview error groups', () => {
+ describe('when data is not loaded', () => {
+ it('handles the empty state', async () => {
+ const response = await supertest.get(
+ `/api/apm/services/opbeans-java/error_groups?${qs.stringify({
+ start,
+ end,
+ uiFilters: '{}',
+ size: 5,
+ numBuckets: 20,
+ pageIndex: 0,
+ sortDirection: 'desc',
+ sortField: 'occurrences',
+ })}`
+ );
+
+ expect(response.status).to.be(200);
+ expect(response.body).to.eql({
+ total_error_groups: 0,
+ error_groups: [],
+ is_aggregation_accurate: true,
+ });
+ });
+ });
+
+ describe('when data is loaded', () => {
+ before(() => esArchiver.load(archiveName));
+ after(() => esArchiver.unload(archiveName));
+
+ it('returns the correct data', async () => {
+ const response = await supertest.get(
+ `/api/apm/services/opbeans-java/error_groups?${qs.stringify({
+ start,
+ end,
+ uiFilters: '{}',
+ size: 5,
+ numBuckets: 20,
+ pageIndex: 0,
+ sortDirection: 'desc',
+ sortField: 'occurrences',
+ })}`
+ );
+
+ expect(response.status).to.be(200);
+
+ expectSnapshot(response.body.total_error_groups).toMatchInline(`5`);
+
+ expectSnapshot(response.body.error_groups.map((group: any) => group.name)).toMatchInline(`
+ Array [
+ "Could not write JSON: Null return value from advice does not match primitive return type for: public abstract double co.elastic.apm.opbeans.repositories.Numbers.getCost(); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Null return value from advice does not match primitive return type for: public abstract double co.elastic.apm.opbeans.repositories.Numbers.getCost() (through reference chain: co.elastic.apm.opbeans.repositories.Stats[\\"numbers\\"]->com.sun.proxy.$Proxy133[\\"cost\\"])",
+ "java.io.IOException: Connection reset by peer",
+ "Connection reset by peer",
+ "Could not write JSON: Unable to find co.elastic.apm.opbeans.model.Customer with id 6617; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unable to find co.elastic.apm.opbeans.model.Customer with id 6617 (through reference chain: co.elastic.apm.opbeans.model.Customer_$$_jvst369_3[\\"email\\"])",
+ "Request method 'POST' not supported",
+ ]
+ `);
+
+ expectSnapshot(response.body.error_groups.map((group: any) => group.occurrences.value))
+ .toMatchInline(`
+ Array [
+ 8,
+ 2,
+ 1,
+ 1,
+ 1,
+ ]
+ `);
+
+ const firstItem = response.body.error_groups[0];
+
+ expectSnapshot(pick(firstItem, 'group_id', 'last_seen', 'name', 'occurrences.value'))
+ .toMatchInline(`
+ Object {
+ "group_id": "051f95eabf120ebe2f8b0399fe3e54c5",
+ "last_seen": 1601391561523,
+ "name": "Could not write JSON: Null return value from advice does not match primitive return type for: public abstract double co.elastic.apm.opbeans.repositories.Numbers.getCost(); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Null return value from advice does not match primitive return type for: public abstract double co.elastic.apm.opbeans.repositories.Numbers.getCost() (through reference chain: co.elastic.apm.opbeans.repositories.Stats[\\"numbers\\"]->com.sun.proxy.$Proxy133[\\"cost\\"])",
+ "occurrences": Object {
+ "value": 8,
+ },
+ }
+ `);
+
+ expectSnapshot(
+ firstItem.occurrences.timeseries.filter(({ y }: any) => y > 0).length
+ ).toMatchInline(`7`);
+ });
+
+ it('sorts items in the correct order', async () => {
+ const descendingResponse = await supertest.get(
+ `/api/apm/services/opbeans-java/error_groups?${qs.stringify({
+ start,
+ end,
+ uiFilters: '{}',
+ size: 5,
+ numBuckets: 20,
+ pageIndex: 0,
+ sortDirection: 'desc',
+ sortField: 'occurrences',
+ })}`
+ );
+
+ expect(descendingResponse.status).to.be(200);
+
+ const descendingOccurrences = descendingResponse.body.error_groups.map(
+ (item: any) => item.occurrences.value
+ );
+
+ expect(descendingOccurrences).to.eql(descendingOccurrences.concat().sort().reverse());
+
+ const ascendingResponse = await supertest.get(
+ `/api/apm/services/opbeans-java/error_groups?${qs.stringify({
+ start,
+ end,
+ uiFilters: '{}',
+ size: 5,
+ numBuckets: 20,
+ pageIndex: 0,
+ sortDirection: 'desc',
+ sortField: 'occurrences',
+ })}`
+ );
+
+ const ascendingOccurrences = ascendingResponse.body.error_groups.map(
+ (item: any) => item.occurrences.value
+ );
+
+ expect(ascendingOccurrences).to.eql(ascendingOccurrences.concat().sort().reverse());
+ });
+
+ it('sorts items by the correct field', async () => {
+ const response = await supertest.get(
+ `/api/apm/services/opbeans-java/error_groups?${qs.stringify({
+ start,
+ end,
+ uiFilters: '{}',
+ size: 5,
+ numBuckets: 20,
+ pageIndex: 0,
+ sortDirection: 'desc',
+ sortField: 'last_seen',
+ })}`
+ );
+
+ expect(response.status).to.be(200);
+
+ const dates = response.body.error_groups.map((group: any) => group.last_seen);
+
+ expect(dates).to.eql(dates.concat().sort().reverse());
+ });
+
+ it('paginates through the items', async () => {
+ const size = 1;
+
+ const firstPage = await supertest.get(
+ `/api/apm/services/opbeans-java/error_groups?${qs.stringify({
+ start,
+ end,
+ uiFilters: '{}',
+ size,
+ numBuckets: 20,
+ pageIndex: 0,
+ sortDirection: 'desc',
+ sortField: 'occurrences',
+ })}`
+ );
+
+ expect(firstPage.status).to.eql(200);
+
+ const totalItems = firstPage.body.total_error_groups;
+
+ const pages = Math.floor(totalItems / size);
+
+ const items = await new Array(pages)
+ .fill(undefined)
+ .reduce(async (prevItemsPromise, _, pageIndex) => {
+ const prevItems = await prevItemsPromise;
+
+ const thisPage = await supertest.get(
+ `/api/apm/services/opbeans-java/error_groups?${qs.stringify({
+ start,
+ end,
+ uiFilters: '{}',
+ size,
+ numBuckets: 20,
+ pageIndex,
+ sortDirection: 'desc',
+ sortField: 'occurrences',
+ })}`
+ );
+
+ return prevItems.concat(thisPage.body.error_groups);
+ }, Promise.resolve([]));
+
+ expect(items.length).to.eql(totalItems);
+
+ expect(uniqBy(items, 'group_id').length).to.eql(totalItems);
+ });
+ });
+ });
+}
diff --git a/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_dashboard_drilldown.ts b/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_dashboard_drilldown.ts
index 43b88915b69d90..9326f7e240e3e4 100644
--- a/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_dashboard_drilldown.ts
+++ b/x-pack/test/functional/apps/dashboard/drilldowns/dashboard_to_dashboard_drilldown.ts
@@ -14,7 +14,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const dashboardPanelActions = getService('dashboardPanelActions');
const dashboardDrilldownPanelActions = getService('dashboardDrilldownPanelActions');
const dashboardDrilldownsManage = getService('dashboardDrilldownsManage');
- const PageObjects = getPageObjects(['dashboard', 'common', 'header', 'timePicker']);
+ const PageObjects = getPageObjects([
+ 'dashboard',
+ 'common',
+ 'header',
+ 'timePicker',
+ 'settings',
+ 'copySavedObjectsToSpace',
+ ]);
const pieChart = getService('pieChart');
const log = getService('log');
const browser = getService('browser');
@@ -22,120 +29,188 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const filterBar = getService('filterBar');
const security = getService('security');
+ const spaces = getService('spaces');
describe('Dashboard to dashboard drilldown', function () {
- before(async () => {
- log.debug('Dashboard Drilldowns:initTests');
- await security.testUser.setRoles(['test_logstash_reader', 'global_dashboard_all']);
- await PageObjects.common.navigateToApp('dashboard');
- await PageObjects.dashboard.preserveCrossAppState();
- });
-
- after(async () => {
- await security.testUser.restoreDefaults();
- });
-
- it('should create dashboard to dashboard drilldown, use it, and then delete it', async () => {
- await PageObjects.dashboard.gotoDashboardEditMode(
- dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME
- );
-
- // create drilldown
- await dashboardPanelActions.openContextMenu();
- await dashboardDrilldownPanelActions.expectExistsCreateDrilldownAction();
- await dashboardDrilldownPanelActions.clickCreateDrilldown();
- await dashboardDrilldownsManage.expectsCreateDrilldownFlyoutOpen();
- await dashboardDrilldownsManage.fillInDashboardToDashboardDrilldownWizard({
- drilldownName: DRILLDOWN_TO_AREA_CHART_NAME,
- destinationDashboardTitle: dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME,
+ describe('Create & use drilldowns', () => {
+ before(async () => {
+ log.debug('Dashboard Drilldowns:initTests');
+ await security.testUser.setRoles(['test_logstash_reader', 'global_dashboard_all']);
+ await PageObjects.common.navigateToApp('dashboard');
+ await PageObjects.dashboard.preserveCrossAppState();
});
- await dashboardDrilldownsManage.saveChanges();
- await dashboardDrilldownsManage.expectsCreateDrilldownFlyoutClose();
-
- // check that drilldown notification badge is shown
- expect(await PageObjects.dashboard.getPanelDrilldownCount()).to.be(1);
-
- // save dashboard, navigate to view mode
- await PageObjects.dashboard.saveDashboard(
- dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME,
- {
- saveAsNew: false,
- waitDialogIsClosed: true,
- }
- );
-
- // trigger drilldown action by clicking on a pie and picking drilldown action by it's name
- await pieChart.clickOnPieSlice('40,000');
- await dashboardDrilldownPanelActions.expectMultipleActionsMenuOpened();
-
- const href = await dashboardDrilldownPanelActions.getActionHrefByText(
- DRILLDOWN_TO_AREA_CHART_NAME
- );
- expect(typeof href).to.be('string'); // checking that action has a href
- const dashboardIdFromHref = PageObjects.dashboard.getDashboardIdFromUrl(href);
-
- await navigateWithinDashboard(async () => {
- await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_AREA_CHART_NAME);
- });
- // checking that href is at least pointing to the same dashboard that we are navigated to by regular click
- expect(dashboardIdFromHref).to.be(await PageObjects.dashboard.getDashboardIdFromCurrentUrl());
-
- // check that we drilled-down with filter from pie chart
- expect(await filterBar.getFilterCount()).to.be(1);
-
- const originalTimeRangeDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
- // brush area chart and drilldown back to pie chat dashboard
- await brushAreaChart();
- await dashboardDrilldownPanelActions.expectMultipleActionsMenuOpened();
-
- await navigateWithinDashboard(async () => {
- await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_PIE_CHART_NAME);
+ after(async () => {
+ await security.testUser.restoreDefaults();
});
- // because filters are preserved during navigation, we expect that only one slice is displayed (filter is still applied)
- expect(await filterBar.getFilterCount()).to.be(1);
- await pieChart.expectPieSliceCount(1);
-
- // check that new time range duration was applied
- const newTimeRangeDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
- expect(newTimeRangeDurationHours).to.be.lessThan(originalTimeRangeDurationHours);
-
- // delete drilldown
- await PageObjects.dashboard.switchToEditMode();
- await dashboardPanelActions.openContextMenu();
- await dashboardDrilldownPanelActions.expectExistsManageDrilldownsAction();
- await dashboardDrilldownPanelActions.clickManageDrilldowns();
- await dashboardDrilldownsManage.expectsManageDrilldownsFlyoutOpen();
-
- await dashboardDrilldownsManage.deleteDrilldownsByTitles([DRILLDOWN_TO_AREA_CHART_NAME]);
- await dashboardDrilldownsManage.closeFlyout();
+ it('should create dashboard to dashboard drilldown, use it, and then delete it', async () => {
+ await PageObjects.dashboard.gotoDashboardEditMode(
+ dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME
+ );
+
+ // create drilldown
+ await dashboardPanelActions.openContextMenu();
+ await dashboardDrilldownPanelActions.expectExistsCreateDrilldownAction();
+ await dashboardDrilldownPanelActions.clickCreateDrilldown();
+ await dashboardDrilldownsManage.expectsCreateDrilldownFlyoutOpen();
+ await dashboardDrilldownsManage.fillInDashboardToDashboardDrilldownWizard({
+ drilldownName: DRILLDOWN_TO_AREA_CHART_NAME,
+ destinationDashboardTitle: dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME,
+ });
+ await dashboardDrilldownsManage.saveChanges();
+ await dashboardDrilldownsManage.expectsCreateDrilldownFlyoutClose();
+
+ // check that drilldown notification badge is shown
+ expect(await PageObjects.dashboard.getPanelDrilldownCount()).to.be(1);
+
+ // save dashboard, navigate to view mode
+ await PageObjects.dashboard.saveDashboard(
+ dashboardDrilldownsManage.DASHBOARD_WITH_PIE_CHART_NAME,
+ {
+ saveAsNew: false,
+ waitDialogIsClosed: true,
+ }
+ );
+
+ // trigger drilldown action by clicking on a pie and picking drilldown action by it's name
+ await pieChart.clickOnPieSlice('40,000');
+ await dashboardDrilldownPanelActions.expectMultipleActionsMenuOpened();
+
+ const href = await dashboardDrilldownPanelActions.getActionHrefByText(
+ DRILLDOWN_TO_AREA_CHART_NAME
+ );
+ expect(typeof href).to.be('string'); // checking that action has a href
+ const dashboardIdFromHref = PageObjects.dashboard.getDashboardIdFromUrl(href);
+
+ await navigateWithinDashboard(async () => {
+ await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_AREA_CHART_NAME);
+ });
+ // checking that href is at least pointing to the same dashboard that we are navigated to by regular click
+ expect(dashboardIdFromHref).to.be(
+ await PageObjects.dashboard.getDashboardIdFromCurrentUrl()
+ );
+
+ // check that we drilled-down with filter from pie chart
+ expect(await filterBar.getFilterCount()).to.be(1);
+
+ const originalTimeRangeDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
+
+ // brush area chart and drilldown back to pie chat dashboard
+ await brushAreaChart();
+ await dashboardDrilldownPanelActions.expectMultipleActionsMenuOpened();
+
+ await navigateWithinDashboard(async () => {
+ await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_PIE_CHART_NAME);
+ });
+
+ // because filters are preserved during navigation, we expect that only one slice is displayed (filter is still applied)
+ expect(await filterBar.getFilterCount()).to.be(1);
+ await pieChart.expectPieSliceCount(1);
+
+ // check that new time range duration was applied
+ const newTimeRangeDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
+ expect(newTimeRangeDurationHours).to.be.lessThan(originalTimeRangeDurationHours);
+
+ // delete drilldown
+ await PageObjects.dashboard.switchToEditMode();
+ await dashboardPanelActions.openContextMenu();
+ await dashboardDrilldownPanelActions.expectExistsManageDrilldownsAction();
+ await dashboardDrilldownPanelActions.clickManageDrilldowns();
+ await dashboardDrilldownsManage.expectsManageDrilldownsFlyoutOpen();
+
+ await dashboardDrilldownsManage.deleteDrilldownsByTitles([DRILLDOWN_TO_AREA_CHART_NAME]);
+ await dashboardDrilldownsManage.closeFlyout();
+
+ // check that drilldown notification badge is shown
+ expect(await PageObjects.dashboard.getPanelDrilldownCount()).to.be(0);
+ });
- // check that drilldown notification badge is shown
- expect(await PageObjects.dashboard.getPanelDrilldownCount()).to.be(0);
+ it('browser back/forward navigation works after drilldown navigation', async () => {
+ await PageObjects.dashboard.loadSavedDashboard(
+ dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME
+ );
+ const originalTimeRangeDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
+ await brushAreaChart();
+ await dashboardDrilldownPanelActions.expectMultipleActionsMenuOpened();
+ await navigateWithinDashboard(async () => {
+ await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_PIE_CHART_NAME);
+ });
+ // check that new time range duration was applied
+ const newTimeRangeDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
+ expect(newTimeRangeDurationHours).to.be.lessThan(originalTimeRangeDurationHours);
+
+ await navigateWithinDashboard(async () => {
+ await browser.goBack();
+ });
+
+ expect(await PageObjects.timePicker.getTimeDurationInHours()).to.be(
+ originalTimeRangeDurationHours
+ );
+ });
});
- it('browser back/forward navigation works after drilldown navigation', async () => {
- await PageObjects.dashboard.loadSavedDashboard(
- dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME
- );
- const originalTimeRangeDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
- await brushAreaChart();
- await dashboardDrilldownPanelActions.expectMultipleActionsMenuOpened();
- await navigateWithinDashboard(async () => {
- await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_PIE_CHART_NAME);
+ describe('Copy to space', () => {
+ const destinationSpaceId = 'custom_space';
+ before(async () => {
+ await spaces.create({
+ id: destinationSpaceId,
+ name: 'custom_space',
+ disabledFeatures: [],
+ });
+ await PageObjects.settings.navigateTo();
+ await PageObjects.settings.clickKibanaSavedObjects();
});
- // check that new time range duration was applied
- const newTimeRangeDurationHours = await PageObjects.timePicker.getTimeDurationInHours();
- expect(newTimeRangeDurationHours).to.be.lessThan(originalTimeRangeDurationHours);
- await navigateWithinDashboard(async () => {
- await browser.goBack();
+ after(async () => {
+ await spaces.delete(destinationSpaceId);
});
- expect(await PageObjects.timePicker.getTimeDurationInHours()).to.be(
- originalTimeRangeDurationHours
- );
+ it('Dashboards linked by a drilldown are both copied to a space', async () => {
+ await PageObjects.copySavedObjectsToSpace.openCopyToSpaceFlyoutForObject(
+ dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME
+ );
+ await PageObjects.copySavedObjectsToSpace.setupForm({
+ destinationSpaceId,
+ });
+ await PageObjects.copySavedObjectsToSpace.startCopy();
+
+ // Wait for successful copy
+ await testSubjects.waitForDeleted(`cts-summary-indicator-loading-${destinationSpaceId}`);
+ await testSubjects.existOrFail(`cts-summary-indicator-success-${destinationSpaceId}`);
+
+ const summaryCounts = await PageObjects.copySavedObjectsToSpace.getSummaryCounts();
+
+ expect(summaryCounts).to.eql({
+ success: 5, // 2 dashboards (linked by a drilldown) + 2 visualizations + 1 index pattern
+ pending: 0,
+ skipped: 0,
+ errors: 0,
+ });
+
+ await PageObjects.copySavedObjectsToSpace.finishCopy();
+
+ // Actually use copied dashboards in a new space:
+
+ await PageObjects.common.navigateToApp('dashboard', {
+ basePath: `/s/${destinationSpaceId}`,
+ });
+ await PageObjects.dashboard.preserveCrossAppState();
+ await PageObjects.dashboard.loadSavedDashboard(
+ dashboardDrilldownsManage.DASHBOARD_WITH_AREA_CHART_NAME
+ );
+ await PageObjects.header.waitUntilLoadingHasFinished();
+ await PageObjects.dashboard.waitForRenderComplete();
+
+ // brush area chart and drilldown back to pie chat dashboard
+ await brushAreaChart();
+ await dashboardDrilldownPanelActions.expectMultipleActionsMenuOpened();
+
+ await navigateWithinDashboard(async () => {
+ await dashboardDrilldownPanelActions.clickActionByText(DRILLDOWN_TO_PIE_CHART_NAME);
+ });
+ await pieChart.expectPieSliceCount(10);
+ });
});
});
diff --git a/x-pack/test/functional/es_archives/reporting/hugedata/data.json.gz b/x-pack/test/functional/es_archives/reporting/hugedata/data.json.gz
index c524379640df7e..c616730ff35b64 100644
Binary files a/x-pack/test/functional/es_archives/reporting/hugedata/data.json.gz and b/x-pack/test/functional/es_archives/reporting/hugedata/data.json.gz differ
diff --git a/x-pack/test/functional/es_archives/spaces/copy_saved_objects/data.json b/x-pack/test/functional/es_archives/spaces/copy_saved_objects/data.json
index 3434e1f80a7cee..552142d3b190ae 100644
--- a/x-pack/test/functional/es_archives/spaces/copy_saved_objects/data.json
+++ b/x-pack/test/functional/es_archives/spaces/copy_saved_objects/data.json
@@ -126,7 +126,7 @@
"title": "Dashboard Foo",
"hits": 0,
"description": "",
- "panelsJSON": "[{}]",
+ "panelsJSON": "[]",
"optionsJSON": "{}",
"version": 1,
"timeRestore": false,
@@ -156,7 +156,7 @@
"title": "Dashboard Bar",
"hits": 0,
"description": "",
- "panelsJSON": "[{}]",
+ "panelsJSON": "[]",
"optionsJSON": "{}",
"version": 1,
"timeRestore": false,
diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/dashboard/sample_dashboard.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/dashboard/sample_dashboard.json
index ef08d693242104..7f416c26cc9aae 100644
--- a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/dashboard/sample_dashboard.json
+++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/dashboard/sample_dashboard.json
@@ -11,6 +11,12 @@
"title": "[Logs Sample] Overview ECS",
"version": 1
},
+ "references": [
+ { "id": "sample_visualization", "name": "panel_0", "type": "visualization" },
+ { "id": "sample_search", "name": "panel_1", "type": "search" },
+ { "id": "sample_search", "name": "panel_2", "type": "search" },
+ { "id": "sample_visualization", "name": "panel_3", "type": "visualization" }
+ ],
"id": "sample_dashboard",
"type": "dashboard"
-}
\ No newline at end of file
+}
diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/dashboard/sample_dashboard2.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/dashboard/sample_dashboard2.json
index 7ea63c5d444ba9..c99506fec3cf54 100644
--- a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/dashboard/sample_dashboard2.json
+++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/dashboard/sample_dashboard2.json
@@ -11,6 +11,12 @@
"title": "[Logs Sample2] Overview ECS",
"version": 1
},
+ "references": [
+ { "id": "sample_visualization", "name": "panel_0", "type": "visualization" },
+ { "id": "sample_search", "name": "panel_1", "type": "search" },
+ { "id": "sample_search", "name": "panel_2", "type": "search" },
+ { "id": "sample_visualization", "name": "panel_3", "type": "visualization" }
+ ],
"id": "sample_dashboard2",
"type": "dashboard"
-}
\ No newline at end of file
+}
diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/dashboard/sample_dashboard.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/dashboard/sample_dashboard.json
index ef08d693242104..4513c07f277867 100644
--- a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/dashboard/sample_dashboard.json
+++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/dashboard/sample_dashboard.json
@@ -11,6 +11,12 @@
"title": "[Logs Sample] Overview ECS",
"version": 1
},
+ "references": [
+ { "id": "sample_visualization", "name": "panel_0", "type": "visualization" },
+ { "id": "sample_search2", "name": "panel_1", "type": "search" },
+ { "id": "sample_search2", "name": "panel_2", "type": "search" },
+ { "id": "sample_visualization", "name": "panel_3", "type": "visualization" }
+ ],
"id": "sample_dashboard",
"type": "dashboard"
-}
\ No newline at end of file
+}
diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts
index 15c26a1b9374d6..f032416d2e7bb7 100644
--- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts
+++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts
@@ -221,6 +221,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
message: 'Elastic Security { action } { filename }',
},
},
+ antivirus_registration: {
+ enabled: false,
+ },
},
},
streams: [],
diff --git a/x-pack/test/security_solution_endpoint/config.ts b/x-pack/test/security_solution_endpoint/config.ts
index 9499c235a5f0d5..f3cb4a5812a5cb 100644
--- a/x-pack/test/security_solution_endpoint/config.ts
+++ b/x-pack/test/security_solution_endpoint/config.ts
@@ -43,5 +43,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
...getRegistryUrlAsArray(),
],
},
+ layout: {
+ fixedHeaderHeight: 200,
+ },
};
}
diff --git a/x-pack/typings/elasticsearch/aggregations.d.ts b/x-pack/typings/elasticsearch/aggregations.d.ts
index 29c78e93831751..bc9ed447c8717f 100644
--- a/x-pack/typings/elasticsearch/aggregations.d.ts
+++ b/x-pack/typings/elasticsearch/aggregations.d.ts
@@ -204,6 +204,8 @@ type SubAggregationResponseOf<
interface AggregationResponsePart {
terms: {
+ doc_count_error_upper_bound: number;
+ sum_other_doc_count: number;
buckets: Array<
{
doc_count: number;