= ({
pageSideBar={pageSideBar}
pageSideBarProps={{
paddingSize: solutionNav ? 'none' : 'l',
- ...rest.pageSideBarProps,
- className: classNames(sideBarClasses, rest.pageSideBarProps?.className),
+ ...pageSideBarProps,
+ className: classNames(sideBarClasses, pageSideBarProps?.className),
}}
{...rest}
>
diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx
index da01f9d44879b..82757e9a8e357 100644
--- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx
+++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx
@@ -449,7 +449,9 @@ export const getTopNavConfig = (
onSave={onSave}
options={tagOptions}
getAppNameFromId={stateTransfer.getAppNameFromId}
- objectType={'visualization'}
+ objectType={i18n.translate('visualize.topNavMenu.saveVisualizationObjectType', {
+ defaultMessage: 'visualization',
+ })}
onClose={() => {}}
originatingApp={originatingApp}
returnToOriginSwitchLabel={
@@ -475,7 +477,9 @@ export const getTopNavConfig = (
canSaveByReference={Boolean(visualizeCapabilities.save)}
onSave={onSave}
tagOptions={tagOptions}
- objectType={'visualization'}
+ objectType={i18n.translate('visualize.topNavMenu.saveVisualizationObjectType', {
+ defaultMessage: 'visualization',
+ })}
onClose={() => {}}
/>
);
diff --git a/test/functional/apps/context/_context_navigation.js b/test/functional/apps/context/_context_navigation.js
index 2efc145b12561..7f72d44c50ea0 100644
--- a/test/functional/apps/context/_context_navigation.js
+++ b/test/functional/apps/context/_context_navigation.js
@@ -21,8 +21,7 @@ export default function ({ getService, getPageObjects }) {
const PageObjects = getPageObjects(['common', 'context', 'discover', 'timePicker']);
const kibanaServer = getService('kibanaServer');
- // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104364
- describe.skip('discover - context - back navigation', function contextSize() {
+ describe('discover - context - back navigation', function contextSize() {
before(async function () {
await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings();
await kibanaServer.uiSettings.update({ 'doc_table:legacy': true });
diff --git a/test/functional/apps/context/_discover_navigation.js b/test/functional/apps/context/_discover_navigation.js
index 6a2298ba48cb4..a09be8b35ba8f 100644
--- a/test/functional/apps/context/_discover_navigation.js
+++ b/test/functional/apps/context/_discover_navigation.js
@@ -32,8 +32,7 @@ export default function ({ getService, getPageObjects }) {
const browser = getService('browser');
const kibanaServer = getService('kibanaServer');
- // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104413
- describe.skip('context link in discover', () => {
+ describe('context link in discover', () => {
before(async () => {
await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings();
await kibanaServer.uiSettings.update({
diff --git a/test/functional/apps/dashboard/saved_search_embeddable.ts b/test/functional/apps/dashboard/saved_search_embeddable.ts
index 33d015a4c6019..5bcec338aad1e 100644
--- a/test/functional/apps/dashboard/saved_search_embeddable.ts
+++ b/test/functional/apps/dashboard/saved_search_embeddable.ts
@@ -17,8 +17,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'timePicker', 'discover']);
- // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104365
- describe.skip('dashboard saved search embeddable', () => {
+ describe('dashboard saved search embeddable', () => {
before(async () => {
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/dashboard/current/data');
diff --git a/test/functional/apps/dashboard/view_edit.ts b/test/functional/apps/dashboard/view_edit.ts
index 1ca70112c3d1e..b29b07f9df4e4 100644
--- a/test/functional/apps/dashboard/view_edit.ts
+++ b/test/functional/apps/dashboard/view_edit.ts
@@ -19,8 +19,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const dashboardName = 'dashboard with filter';
const filterBar = getService('filterBar');
- // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104467
- describe.skip('dashboard view edit mode', function viewEditModeTests() {
+ describe('dashboard view edit mode', function viewEditModeTests() {
before(async () => {
await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana');
await kibanaServer.uiSettings.replace({
diff --git a/test/functional/apps/discover/_date_nested.ts b/test/functional/apps/discover/_date_nested.ts
new file mode 100644
index 0000000000000..8297d84832ff6
--- /dev/null
+++ b/test/functional/apps/discover/_date_nested.ts
@@ -0,0 +1,35 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function ({ getService, getPageObjects }: FtrProviderContext) {
+ const esArchiver = getService('esArchiver');
+ const testSubjects = getService('testSubjects');
+ const PageObjects = getPageObjects(['common', 'timePicker', 'discover']);
+ const security = getService('security');
+
+ describe('timefield is a date in a nested field', function () {
+ before(async function () {
+ await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/date_nested');
+ await security.testUser.setRoles(['kibana_admin', 'kibana_date_nested']);
+ await PageObjects.common.navigateToApp('discover');
+ });
+
+ after(async function unloadMakelogs() {
+ await security.testUser.restoreDefaults();
+ await esArchiver.unload('test/functional/fixtures/es_archiver/date_nested');
+ });
+
+ it('should show an error message', async function () {
+ await PageObjects.discover.selectIndexPattern('date-nested');
+ await PageObjects.discover.waitUntilSearchingHasFinished();
+ await testSubjects.existOrFail('discoverNoResultsError');
+ });
+ });
+}
diff --git a/test/functional/apps/discover/_discover.ts b/test/functional/apps/discover/_discover.ts
index 245b895d75b3a..bb75b4441f880 100644
--- a/test/functional/apps/discover/_discover.ts
+++ b/test/functional/apps/discover/_discover.ts
@@ -38,8 +38,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.timePicker.setDefaultAbsoluteRange();
});
- // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104409
- describe.skip('query', function () {
+ describe('query', function () {
const queryName1 = 'Query # 1';
it('should show correct time range string by timepicker', async function () {
diff --git a/test/functional/apps/discover/_field_data.ts b/test/functional/apps/discover/_field_data.ts
index ec9f9cf65e0fa..338d17ba31ff4 100644
--- a/test/functional/apps/discover/_field_data.ts
+++ b/test/functional/apps/discover/_field_data.ts
@@ -33,7 +33,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings();
await PageObjects.common.navigateToApp('discover');
});
-
describe('field data', function () {
it('search php should show the correct hit count', async function () {
const expectedHitCount = '445';
diff --git a/test/functional/apps/discover/_saved_queries.ts b/test/functional/apps/discover/_saved_queries.ts
index 29073c5fe4ebb..20f2cab907d9b 100644
--- a/test/functional/apps/discover/_saved_queries.ts
+++ b/test/functional/apps/discover/_saved_queries.ts
@@ -40,8 +40,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.timePicker.setDefaultAbsoluteRange();
});
- // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104366
- describe.skip('saved query management component functionality', function () {
+ describe('saved query management component functionality', function () {
before(async function () {
// set up a query with filters and a time filter
log.debug('set up a query with filters to save');
diff --git a/test/functional/apps/discover/index.ts b/test/functional/apps/discover/index.ts
index a17bf53e7f478..3a18a55fe138b 100644
--- a/test/functional/apps/discover/index.ts
+++ b/test/functional/apps/discover/index.ts
@@ -12,8 +12,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const browser = getService('browser');
- // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/104466
- describe.skip('discover app', function () {
+ describe('discover app', function () {
this.tags('ciGroup6');
before(function () {
@@ -51,5 +50,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./_indexpattern_with_unmapped_fields'));
loadTestFile(require.resolve('./_runtime_fields_editor'));
loadTestFile(require.resolve('./_huge_fields'));
+ loadTestFile(require.resolve('./_date_nested'));
});
}
diff --git a/test/functional/config.js b/test/functional/config.js
index c2c856517c58e..1c0c519f21e4c 100644
--- a/test/functional/config.js
+++ b/test/functional/config.js
@@ -247,6 +247,21 @@ export default async function ({ readConfigFile }) {
},
kibana: [],
},
+
+ kibana_date_nested: {
+ elasticsearch: {
+ cluster: [],
+ indices: [
+ {
+ names: ['date-nested'],
+ privileges: ['read', 'view_index_metadata'],
+ field_security: { grant: ['*'], except: [] },
+ },
+ ],
+ run_as: [],
+ },
+ kibana: [],
+ },
kibana_message_with_newline: {
elasticsearch: {
cluster: [],
diff --git a/test/functional/fixtures/es_archiver/date_nested/data.json b/test/functional/fixtures/es_archiver/date_nested/data.json
new file mode 100644
index 0000000000000..0bdb3fc510a63
--- /dev/null
+++ b/test/functional/fixtures/es_archiver/date_nested/data.json
@@ -0,0 +1,30 @@
+{
+ "type": "doc",
+ "value": {
+ "id": "index-pattern:date-nested",
+ "index": ".kibana",
+ "source": {
+ "index-pattern": {
+ "fields":"[]",
+ "timeFieldName": "@timestamp",
+ "title": "date-nested"
+ },
+ "type": "index-pattern"
+ }
+ }
+}
+
+
+{
+ "type": "doc",
+ "value": {
+ "id": "date-nested-1",
+ "index": "date-nested",
+ "source": {
+ "message" : "test",
+ "nested": {
+ "timestamp": "2021-06-30T12:00:00.123Z"
+ }
+ }
+ }
+}
diff --git a/test/functional/fixtures/es_archiver/date_nested/mappings.json b/test/functional/fixtures/es_archiver/date_nested/mappings.json
new file mode 100644
index 0000000000000..f30e5863f4f8b
--- /dev/null
+++ b/test/functional/fixtures/es_archiver/date_nested/mappings.json
@@ -0,0 +1,22 @@
+{
+ "type": "index",
+ "value": {
+ "index": "date-nested",
+ "mappings": {
+ "properties": {
+ "message": {
+ "type": "text"
+ },
+ "nested": {
+ "type": "nested"
+ }
+ }
+ },
+ "settings": {
+ "index": {
+ "number_of_replicas": "0",
+ "number_of_shards": "1"
+ }
+ }
+ }
+}
diff --git a/tsconfig.json b/tsconfig.json
index f6df8fcbb6406..082325306e379 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -76,7 +76,6 @@
{ "path": "./x-pack/plugins/canvas/tsconfig.json" },
{ "path": "./x-pack/plugins/cases/tsconfig.json" },
{ "path": "./x-pack/plugins/cloud/tsconfig.json" },
- { "path": "./x-pack/plugins/console_extensions/tsconfig.json" },
{ "path": "./x-pack/plugins/data_enhanced/tsconfig.json" },
{ "path": "./x-pack/plugins/dashboard_mode/tsconfig.json" },
{ "path": "./x-pack/plugins/discover_enhanced/tsconfig.json" },
diff --git a/tsconfig.refs.json b/tsconfig.refs.json
index e08b50cc055c1..bbaf18d29ae2a 100644
--- a/tsconfig.refs.json
+++ b/tsconfig.refs.json
@@ -63,7 +63,6 @@
{ "path": "./x-pack/plugins/canvas/tsconfig.json" },
{ "path": "./x-pack/plugins/cases/tsconfig.json" },
{ "path": "./x-pack/plugins/cloud/tsconfig.json" },
- { "path": "./x-pack/plugins/console_extensions/tsconfig.json" },
{ "path": "./x-pack/plugins/dashboard_enhanced/tsconfig.json" },
{ "path": "./x-pack/plugins/data_enhanced/tsconfig.json" },
{ "path": "./x-pack/plugins/dashboard_mode/tsconfig.json" },
diff --git a/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/index.tsx
index 1ca7f46a0b26f..32c93e43175df 100644
--- a/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/Settings/agent_configurations/index.tsx
@@ -18,7 +18,6 @@ import { i18n } from '@kbn/i18n';
import { isEmpty } from 'lodash';
import React from 'react';
import { useLocation } from 'react-router-dom';
-import { useTrackPageview } from '../../../../../../observability/public';
import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context';
import { useFetcher } from '../../../../hooks/use_fetcher';
import { createAgentConfigurationHref } from '../../../shared/Links/apm/agentConfigurationLinks';
@@ -34,9 +33,6 @@ export function AgentConfigurations() {
{ preservePreviousData: false, showToastOnError: false }
);
- useTrackPageview({ app: 'apm', path: 'agent_configuration' });
- useTrackPageview({ app: 'apm', path: 'agent_configuration', delay: 15000 });
-
const hasConfigurations = !isEmpty(data.configurations);
return (
diff --git a/x-pack/plugins/apm/public/components/app/Settings/schema/confirm_switch_modal.tsx b/x-pack/plugins/apm/public/components/app/Settings/schema/confirm_switch_modal.tsx
index 04817aaf84f3e..5bb93c251777f 100644
--- a/x-pack/plugins/apm/public/components/app/Settings/schema/confirm_switch_modal.tsx
+++ b/x-pack/plugins/apm/public/components/app/Settings/schema/confirm_switch_modal.tsx
@@ -15,6 +15,7 @@ import {
htmlIdGenerator,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
+import { useUiTracker } from '../../../../../../observability/public';
import { ElasticDocsLink } from '../../../shared/Links/ElasticDocsLink';
interface Props {
@@ -27,6 +28,7 @@ export function ConfirmSwitchModal({
onCancel,
unsupportedConfigs,
}: Props) {
+ const trackApmEvent = useUiTracker({ app: 'apm' });
const [isConfirmChecked, setIsConfirmChecked] = useState(false);
const hasUnsupportedConfigs = !!unsupportedConfigs.length;
return (
@@ -48,7 +50,12 @@ export function ConfirmSwitchModal({
}
)}
defaultFocusedButton="confirm"
- onConfirm={onConfirm}
+ onConfirm={() => {
+ trackApmEvent({
+ metric: 'confirm_data_stream_switch',
+ });
+ onConfirm();
+ }}
confirmButtonDisabled={!isConfirmChecked}
>
diff --git a/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx
index 5a67ce28e9e1a..0c95648a1cefc 100644
--- a/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx
@@ -142,9 +142,7 @@ async function createCloudApmPackagePolicy(
) {
updateLocalStorage(FETCH_STATUS.LOADING);
try {
- const {
- cloud_apm_package_policy: cloudApmPackagePolicy,
- } = await callApmApi({
+ const { cloudApmPackagePolicy } = await callApmApi({
endpoint: 'POST /api/apm/fleet/cloud_apm_package_policy',
signal: null,
});
diff --git a/x-pack/plugins/apm/public/components/app/correlations/index.tsx b/x-pack/plugins/apm/public/components/app/correlations/index.tsx
index 2b32ece14e5cd..9ad5088bb0bcf 100644
--- a/x-pack/plugins/apm/public/components/app/correlations/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/correlations/index.tsx
@@ -104,16 +104,7 @@ export function Correlations() {
width: '20%',
});
}
- if (urlParams.transactionName) {
- properties.push({
- label: i18n.translate('xpack.apm.correlations.transactionLabel', {
- defaultMessage: 'Transaction',
- }),
- fieldName: TRANSACTION_NAME,
- val: urlParams.transactionName,
- width: '20%',
- });
- }
+
if (urlParams.environment) {
properties.push({
label: i18n.translate('xpack.apm.correlations.environmentLabel', {
@@ -125,6 +116,17 @@ export function Correlations() {
});
}
+ if (urlParams.transactionName) {
+ properties.push({
+ label: i18n.translate('xpack.apm.correlations.transactionLabel', {
+ defaultMessage: 'Transaction',
+ }),
+ fieldName: TRANSACTION_NAME,
+ val: urlParams.transactionName,
+ width: '20%',
+ });
+ }
+
return properties;
}, [serviceName, urlParams.environment, urlParams.transactionName]);
diff --git a/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations.tsx
index f9536353747ee..03fab3e788639 100644
--- a/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations.tsx
+++ b/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations.tsx
@@ -36,6 +36,7 @@ import { useCorrelations } from './use_correlations';
import { push } from '../../shared/Links/url_helpers';
import { useUiTracker } from '../../../../../observability/public';
import { asPreciseDecimal } from '../../../../common/utils/formatters';
+import { LatencyCorrelationsHelpPopover } from './ml_latency_correlations_help_popover';
const DEFAULT_PERCENTILE_THRESHOLD = 95;
const isErrorMessage = (arg: unknown): arg is Error => {
@@ -60,17 +61,16 @@ export function MlLatencyCorrelations({ onClose }: Props) {
} = useApmPluginContext();
const { serviceName } = useParams<{ serviceName: string }>();
- const { urlParams } = useUrlParams();
-
- const fetchOptions = useMemo(
- () => ({
- ...{
- serviceName,
- ...urlParams,
- },
- }),
- [serviceName, urlParams]
- );
+ const {
+ urlParams: {
+ environment,
+ kuery,
+ transactionName,
+ transactionType,
+ start,
+ end,
+ },
+ } = useUrlParams();
const {
error,
@@ -84,7 +84,15 @@ export function MlLatencyCorrelations({ onClose }: Props) {
} = useCorrelations({
index: 'apm-*',
...{
- ...fetchOptions,
+ ...{
+ environment,
+ kuery,
+ serviceName,
+ transactionName,
+ transactionType,
+ start,
+ end,
+ },
percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD,
},
});
@@ -151,7 +159,7 @@ export function MlLatencyCorrelations({ onClose }: Props) {
'xpack.apm.correlations.latencyCorrelations.correlationsTable.correlationColumnDescription',
{
defaultMessage:
- 'The impact of a field on the latency of the service, ranging from 0 to 1.',
+ 'The correlation score [0-1] of an attribute; the greater the score, the more an attribute increases latency.',
}
)}
>
@@ -263,20 +271,6 @@ export function MlLatencyCorrelations({ onClose }: Props) {
return (
<>
-
-
- {i18n.translate(
- 'xpack.apm.correlations.latencyCorrelations.description',
- {
- defaultMessage:
- 'What is slowing down my service? Correlations will help discover a slower performance in a particular cohort of your data.',
- }
- )}
-
-
-
-
-
{!isRunning && (
@@ -320,6 +314,9 @@ export function MlLatencyCorrelations({ onClose }: Props) {
+
+
+
@@ -332,8 +329,7 @@ export function MlLatencyCorrelations({ onClose }: Props) {
{
defaultMessage: 'Latency distribution for {name}',
values: {
- name:
- fetchOptions.transactionName ?? fetchOptions.serviceName,
+ name: transactionName ?? serviceName,
},
}
)}
diff --git a/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations_help_popover.tsx b/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations_help_popover.tsx
new file mode 100644
index 0000000000000..1f9a41c1139cd
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/app/correlations/ml_latency_correlations_help_popover.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
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useState } from 'react';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { HelpPopover, HelpPopoverButton } from '../help_popover/help_popover';
+
+export function LatencyCorrelationsHelpPopover() {
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
+
+ return (
+ {
+ setIsPopoverOpen(!isPopoverOpen);
+ }}
+ />
+ }
+ closePopover={() => setIsPopoverOpen(false)}
+ isOpen={isPopoverOpen}
+ title={i18n.translate('xpack.apm.correlations.latencyPopoverTitle', {
+ defaultMessage: 'Latency correlations',
+ })}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/x-pack/plugins/apm/public/components/app/correlations/use_correlations.ts b/x-pack/plugins/apm/public/components/app/correlations/use_correlations.ts
index 8c874571d23db..2baeb63fa4a23 100644
--- a/x-pack/plugins/apm/public/components/app/correlations/use_correlations.ts
+++ b/x-pack/plugins/apm/public/components/app/correlations/use_correlations.ts
@@ -36,6 +36,7 @@ interface RawResponse {
took: number;
values: SearchServiceValue[];
overallHistogram: HistogramItem[];
+ log: string[];
}
export const useCorrelations = (params: CorrelationsOptions) => {
diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx
index 344393d42506f..225186cab12c8 100644
--- a/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx
@@ -17,7 +17,6 @@ import {
import { i18n } from '@kbn/i18n';
import React from 'react';
import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
-import { useTrackPageview } from '../../../../../observability/public';
import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { useErrorGroupDistributionFetcher } from '../../../hooks/use_error_group_distribution_fetcher';
@@ -128,9 +127,6 @@ export function ErrorGroupDetails({
groupId,
});
- useTrackPageview({ app: 'apm', path: 'error_group_details' });
- useTrackPageview({ app: 'apm', path: 'error_group_details', delay: 15000 });
-
if (!errorGroupData || !errorDistributionData) {
return ;
}
diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx
index 4c622758e6c8b..8d8d0cb9c107c 100644
--- a/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx
@@ -14,7 +14,6 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
-import { useTrackPageview } from '../../../../../observability/public';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { useErrorGroupDistributionFetcher } from '../../../hooks/use_error_group_distribution_fetcher';
import { useFetcher } from '../../../hooks/use_fetcher';
@@ -60,12 +59,6 @@ export function ErrorGroupOverview({ serviceName }: ErrorGroupOverviewProps) {
[environment, kuery, serviceName, start, end, sortField, sortDirection]
);
- useTrackPageview({
- app: 'apm',
- path: 'error_group_overview',
- });
- useTrackPageview({ app: 'apm', path: 'error_group_overview', delay: 15000 });
-
if (!errorDistributionData || !errorGroupListData) {
return null;
}
diff --git a/x-pack/plugins/apm/public/components/app/help_popover/help_popover.tsx b/x-pack/plugins/apm/public/components/app/help_popover/help_popover.tsx
new file mode 100644
index 0000000000000..def310f1d8140
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/app/help_popover/help_popover.tsx
@@ -0,0 +1,72 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { ReactNode } from 'react';
+import { i18n } from '@kbn/i18n';
+import {
+ EuiButtonIcon,
+ EuiLinkButtonProps,
+ EuiPopover,
+ EuiPopoverProps,
+ EuiPopoverTitle,
+ EuiText,
+} from '@elastic/eui';
+import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
+
+const PopoverContent = euiStyled(EuiText)`
+ max-width: 480px;
+ max-height: 40vh;
+`;
+
+export function HelpPopoverButton({
+ onClick,
+}: {
+ onClick: EuiLinkButtonProps['onClick'];
+}) {
+ return (
+
+ );
+}
+
+export function HelpPopover({
+ anchorPosition,
+ button,
+ children,
+ closePopover,
+ isOpen,
+ title,
+}: {
+ anchorPosition?: EuiPopoverProps['anchorPosition'];
+ button: EuiPopoverProps['button'];
+ children: ReactNode;
+ closePopover: EuiPopoverProps['closePopover'];
+ isOpen: EuiPopoverProps['isOpen'];
+ title?: string;
+}) {
+ return (
+
+ {title && {title}}
+
+ {children}
+
+ );
+}
diff --git a/x-pack/plugins/console_extensions/server/config.ts b/x-pack/plugins/apm/public/components/app/help_popover/index.tsx
similarity index 56%
rename from x-pack/plugins/console_extensions/server/config.ts
rename to x-pack/plugins/apm/public/components/app/help_popover/index.tsx
index 15b06bf93ffbe..b1d53722c7bb5 100644
--- a/x-pack/plugins/console_extensions/server/config.ts
+++ b/x-pack/plugins/apm/public/components/app/help_popover/index.tsx
@@ -5,10 +5,4 @@
* 2.0.
*/
-import { schema, TypeOf } from '@kbn/config-schema';
-
-export type ConfigType = TypeOf;
-
-export const config = schema.object({
- enabled: schema.boolean({ defaultValue: true }),
-});
+export { HelpPopoverButton, HelpPopover } from './help_popover';
diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx
index cac94885511c1..ef0a5f2df0434 100644
--- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx
@@ -9,7 +9,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useEffect } from 'react';
import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public';
-import { useTrackPageview } from '../../../../../observability/public';
import { useAnomalyDetectionJobsContext } from '../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context';
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
@@ -92,13 +91,6 @@ export function ServiceInventory() {
const { core } = useApmPluginContext();
const { servicesData, servicesStatus } = useServicesFetcher();
- // The page is called "service inventory" to avoid confusion with the
- // "service overview", but this is tracked in some dashboards because it's the
- // initial landing page for APM, so it stays as "services_overview" (plural.)
- // for backward compatibility.
- useTrackPageview({ app: 'apm', path: 'services_overview' });
- useTrackPageview({ app: 'apm', path: 'services_overview', delay: 15000 });
-
const {
anomalyDetectionJobsData,
anomalyDetectionJobsStatus,
diff --git a/x-pack/plugins/apm/public/components/app/service_map/index.tsx b/x-pack/plugins/apm/public/components/app/service_map/index.tsx
index 582eafe7553af..22adb10512d7a 100644
--- a/x-pack/plugins/apm/public/components/app/service_map/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/service_map/index.tsx
@@ -13,7 +13,6 @@ import {
} from '@elastic/eui';
import React, { PropsWithChildren, ReactNode } from 'react';
import { isActivePlatinumLicense } from '../../../../common/license_check';
-import { useTrackPageview } from '../../../../../observability/public';
import {
invalidLicenseMessage,
SERVICE_MAP_TIMEOUT_ERROR,
@@ -106,9 +105,6 @@ export function ServiceMap({
const PADDING_BOTTOM = 24;
const heightWithPadding = height - PADDING_BOTTOM;
- useTrackPageview({ app: 'apm', path: 'service_map' });
- useTrackPageview({ app: 'apm', path: 'service_map', delay: 15000 });
-
if (!license) {
return null;
}
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 fce543b05c6c3..374b2d59ea347 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
@@ -7,7 +7,6 @@
import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
import React from 'react';
-import { useTrackPageview } from '../../../../../observability/public';
import { isRumAgentName, isIosAgentName } from '../../../../common/agent_name';
import { AnnotationsContextProvider } from '../../../context/annotations/annotations_context';
import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context';
@@ -35,9 +34,6 @@ interface ServiceOverviewProps {
export function ServiceOverview({ serviceName }: ServiceOverviewProps) {
const { agentName } = useApmServiceContext();
- useTrackPageview({ app: 'apm', path: 'service_overview' });
- useTrackPageview({ app: 'apm', path: 'service_overview', delay: 15000 });
-
// The default EuiFlexGroup breaks at 768, but we want to break at 992, so we
// observe the window width and set the flex directions of rows accordingly
const { isMedium } = useBreakPoints();
diff --git a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx
index d280b36a603ba..ccb5fea72432c 100644
--- a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx
@@ -6,7 +6,6 @@
*/
import React from 'react';
-import { useTrackPageview } from '../../../../../observability/public';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher';
import { APIReturnType } from '../../../services/rest/createCallApmApi';
@@ -43,9 +42,6 @@ export function TraceOverview() {
[environment, kuery, start, end]
);
- useTrackPageview({ app: 'apm', path: 'traces_overview' });
- useTrackPageview({ app: 'apm', path: 'traces_overview', delay: 15000 });
-
return (
<>
diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx
index 1e13e224a511a..40f50e768e76e 100644
--- a/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx
@@ -9,7 +9,6 @@ import { EuiHorizontalRule, EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui';
import { flatten, isEmpty } from 'lodash';
import React from 'react';
import { useHistory } from 'react-router-dom';
-import { useTrackPageview } from '../../../../../observability/public';
import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { FETCH_STATUS } from '../../../hooks/use_fetcher';
@@ -41,9 +40,6 @@ export function TransactionDetails() {
} = useWaterfallFetcher();
const { transactionName } = urlParams;
- useTrackPageview({ app: 'apm', path: 'transaction_details' });
- useTrackPageview({ app: 'apm', path: 'transaction_details', delay: 15000 });
-
const selectedSample = flatten(
distributionData.buckets.map((bucket) => bucket.samples)
).find(
diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx
index 041c12822357c..2435e5fc5a1f6 100644
--- a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx
+++ b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx
@@ -17,7 +17,6 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { Location } from 'history';
import React from 'react';
import { useLocation } from 'react-router-dom';
-import { useTrackPageview } from '../../../../../observability/public';
import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context';
import { IUrlParams } from '../../../context/url_params_context/types';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
@@ -62,8 +61,6 @@ export function TransactionOverview({ serviceName }: TransactionOverviewProps) {
// redirect to first transaction type
useRedirect(getRedirectLocation({ location, transactionType, urlParams }));
- useTrackPageview({ app: 'apm', path: 'transaction_overview' });
- useTrackPageview({ app: 'apm', path: 'transaction_overview', delay: 15000 });
const {
transactionListData,
transactionListStatus,
diff --git a/x-pack/plugins/apm/public/components/routing/app_root.tsx b/x-pack/plugins/apm/public/components/routing/app_root.tsx
index 8fc59a01eeca0..a924c1f31cbef 100644
--- a/x-pack/plugins/apm/public/components/routing/app_root.tsx
+++ b/x-pack/plugins/apm/public/components/routing/app_root.tsx
@@ -9,7 +9,7 @@ import { ApmRoute } from '@elastic/apm-rum-react';
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
import React from 'react';
-import { Route, Router, Switch } from 'react-router-dom';
+import { Route, RouteComponentProps, Router, Switch } from 'react-router-dom';
import { DefaultTheme, ThemeProvider } from 'styled-components';
import { APP_WRAPPER_CLASS } from '../../../../../../src/core/public';
import {
@@ -17,20 +17,21 @@ import {
RedirectAppLinks,
useUiSetting$,
} from '../../../../../../src/plugins/kibana_react/public';
+import { HeaderMenuPortal } from '../../../../observability/public';
import { ScrollToTopOnPathChange } from '../../components/app/Main/ScrollToTopOnPathChange';
+import { AnomalyDetectionJobsContextProvider } from '../../context/anomaly_detection_jobs/anomaly_detection_jobs_context';
import {
ApmPluginContext,
ApmPluginContextValue,
} from '../../context/apm_plugin/apm_plugin_context';
+import { useApmPluginContext } from '../../context/apm_plugin/use_apm_plugin_context';
import { LicenseProvider } from '../../context/license/license_context';
import { UrlParamsProvider } from '../../context/url_params_context/url_params_context';
import { useApmBreadcrumbs } from '../../hooks/use_apm_breadcrumbs';
import { ApmPluginStartDeps } from '../../plugin';
-import { HeaderMenuPortal } from '../../../../observability/public';
import { ApmHeaderActionMenu } from '../shared/apm_header_action_menu';
-import { useApmPluginContext } from '../../context/apm_plugin/use_apm_plugin_context';
-import { AnomalyDetectionJobsContextProvider } from '../../context/anomaly_detection_jobs/anomaly_detection_jobs_context';
import { apmRouteConfig } from './apm_route_config';
+import { TelemetryWrapper } from './telemetry_wrapper';
export function ApmAppRoot({
apmPluginContextValue,
@@ -62,9 +63,18 @@ export function ApmAppRoot({
- {apmRouteConfig.map((route, i) => (
-
- ))}
+ {apmRouteConfig.map((route, i) => {
+ const { component, render, ...rest } = route;
+ return (
+ {
+ return TelemetryWrapper({ route, props });
+ }}
+ />
+ );
+ })}
diff --git a/x-pack/plugins/apm/public/components/routing/telemetry_wrapper.tsx b/x-pack/plugins/apm/public/components/routing/telemetry_wrapper.tsx
new file mode 100644
index 0000000000000..fc3fc6a338d18
--- /dev/null
+++ b/x-pack/plugins/apm/public/components/routing/telemetry_wrapper.tsx
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import React from 'react';
+import { RouteComponentProps } from 'react-router-dom';
+import { useTrackPageview } from '../../../../observability/public';
+import { APMRouteDefinition } from '../../application/routes';
+import { redirectTo } from './redirect_to';
+
+export function TelemetryWrapper({
+ route,
+ props,
+}: {
+ route: APMRouteDefinition;
+ props: RouteComponentProps;
+}) {
+ const { component, render, path } = route;
+ const pathAsString = path as string;
+
+ useTrackPageview({ app: 'apm', path: pathAsString });
+ useTrackPageview({ app: 'apm', path: pathAsString, delay: 15000 });
+
+ if (component) {
+ return React.createElement(component, props);
+ }
+ if (render) {
+ return <>{render(props)}>;
+ }
+ return <>{redirectTo('/')}>;
+}
diff --git a/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts b/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts
index 1366503ea1428..679f33707b5b5 100644
--- a/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts
+++ b/x-pack/plugins/apm/server/lib/alerts/test_utils/index.ts
@@ -70,8 +70,14 @@ export const createRuleTypeMocks = () => {
executor: async ({ params }: { params: Record }) => {
return alertExecutor({
services,
- rule: { consumer: APM_SERVER_FEATURE_ID },
params,
+ rule: {
+ consumer: APM_SERVER_FEATURE_ID,
+ name: 'name',
+ producer: 'producer',
+ ruleTypeId: 'ruleTypeId',
+ ruleTypeName: 'ruleTypeName',
+ },
startedAt: new Date(),
});
},
diff --git a/x-pack/plugins/apm/server/lib/fleet/sync_agent_configs_to_apm_package_policies.ts b/x-pack/plugins/apm/server/lib/fleet/sync_agent_configs_to_apm_package_policies.ts
index 4294c5b82cd63..1365ddc28ddb2 100644
--- a/x-pack/plugins/apm/server/lib/fleet/sync_agent_configs_to_apm_package_policies.ts
+++ b/x-pack/plugins/apm/server/lib/fleet/sync_agent_configs_to_apm_package_policies.ts
@@ -10,6 +10,7 @@ import {
CoreStart,
SavedObjectsClientContract,
} from 'kibana/server';
+import { TelemetryUsageCounter } from '../../routes/typings';
import { APMPluginStartDependencies } from '../../types';
import { getInternalSavedObjectsClient } from '../helpers/get_internal_saved_objects_client';
import { Setup } from '../helpers/setup_request';
@@ -21,11 +22,19 @@ export async function syncAgentConfigsToApmPackagePolicies({
core,
fleetPluginStart,
setup,
+ telemetryUsageCounter,
}: {
core: { setup: CoreSetup; start: () => Promise };
fleetPluginStart: NonNullable;
setup: Setup;
+ telemetryUsageCounter?: TelemetryUsageCounter;
}) {
+ if (telemetryUsageCounter) {
+ telemetryUsageCounter.incrementCounter({
+ counterName: 'sync_agent_config_to_apm_package_policies',
+ counterType: 'success',
+ });
+ }
const coreStart = await core.start();
const esClient = coreStart.elasticsearch.client.asInternalUser;
const [
diff --git a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts
index a5340c1220b44..ef869a0ed6cfa 100644
--- a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts
+++ b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.test.ts
@@ -13,6 +13,11 @@ import { APMConfig } from '../..';
function getMockSavedObjectsClient() {
return ({
+ get: jest.fn(() => ({
+ attributes: {
+ title: 'apm-*',
+ },
+ })),
create: jest.fn(),
} as unknown) as InternalSavedObjectsClient;
}
@@ -22,14 +27,12 @@ describe('createStaticIndexPattern', () => {
const setup = {} as Setup;
const savedObjectsClient = getMockSavedObjectsClient();
- await createStaticIndexPattern(
+ await createStaticIndexPattern({
setup,
- {
- 'xpack.apm.autocreateApmIndexPattern': false,
- } as APMConfig,
+ config: { 'xpack.apm.autocreateApmIndexPattern': false } as APMConfig,
savedObjectsClient,
- 'default'
- );
+ spaceId: 'default',
+ });
expect(savedObjectsClient.create).not.toHaveBeenCalled();
});
@@ -43,14 +46,12 @@ describe('createStaticIndexPattern', () => {
const savedObjectsClient = getMockSavedObjectsClient();
- await createStaticIndexPattern(
+ await createStaticIndexPattern({
setup,
- {
- 'xpack.apm.autocreateApmIndexPattern': true,
- } as APMConfig,
+ config: { 'xpack.apm.autocreateApmIndexPattern': true } as APMConfig,
savedObjectsClient,
- 'default'
- );
+ spaceId: 'default',
+ });
expect(savedObjectsClient.create).not.toHaveBeenCalled();
});
@@ -64,15 +65,73 @@ describe('createStaticIndexPattern', () => {
const savedObjectsClient = getMockSavedObjectsClient();
- await createStaticIndexPattern(
+ await createStaticIndexPattern({
setup,
- {
+ config: { 'xpack.apm.autocreateApmIndexPattern': true } as APMConfig,
+ savedObjectsClient,
+ spaceId: 'default',
+ });
+
+ expect(savedObjectsClient.create).toHaveBeenCalled();
+ });
+
+ it(`should upgrade an index pattern if 'apm_oss.indexPattern' does not match title`, async () => {
+ const setup = {} as Setup;
+
+ // does have APM data
+ jest
+ .spyOn(HistoricalAgentData, 'hasHistoricalAgentData')
+ .mockResolvedValue(true);
+
+ const savedObjectsClient = getMockSavedObjectsClient();
+ const apmIndexPatternTitle = 'traces-apm*,logs-apm*,metrics-apm*,apm-*';
+
+ await createStaticIndexPattern({
+ setup,
+ config: {
'xpack.apm.autocreateApmIndexPattern': true,
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ 'apm_oss.indexPattern': apmIndexPatternTitle,
} as APMConfig,
savedObjectsClient,
- 'default'
+ spaceId: 'default',
+ });
+
+ expect(savedObjectsClient.get).toHaveBeenCalled();
+ expect(savedObjectsClient.create).toHaveBeenCalled();
+ // @ts-ignore
+ expect(savedObjectsClient.create.mock.calls[0][1].title).toBe(
+ apmIndexPatternTitle
);
+ // @ts-ignore
+ expect(savedObjectsClient.create.mock.calls[0][2].overwrite).toBe(true);
+ });
+
+ it(`should not upgrade an index pattern if 'apm_oss.indexPattern' already match existing title`, async () => {
+ const setup = {} as Setup;
+
+ // does have APM data
+ jest
+ .spyOn(HistoricalAgentData, 'hasHistoricalAgentData')
+ .mockResolvedValue(true);
+
+ const savedObjectsClient = getMockSavedObjectsClient();
+ const apmIndexPatternTitle = 'apm-*';
+
+ await createStaticIndexPattern({
+ setup,
+ config: {
+ 'xpack.apm.autocreateApmIndexPattern': true,
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ 'apm_oss.indexPattern': apmIndexPatternTitle,
+ } as APMConfig,
+ savedObjectsClient,
+ spaceId: 'default',
+ });
+ expect(savedObjectsClient.get).toHaveBeenCalled();
expect(savedObjectsClient.create).toHaveBeenCalled();
+ // @ts-ignore
+ expect(savedObjectsClient.create.mock.calls[0][2].overwrite).toBe(false);
});
});
diff --git a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts
index a2944d6241d2d..5dbee59b4ce86 100644
--- a/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts
+++ b/x-pack/plugins/apm/server/lib/index_pattern/create_static_index_pattern.ts
@@ -15,13 +15,23 @@ import { InternalSavedObjectsClient } from '../helpers/get_internal_saved_object
import { withApmSpan } from '../../utils/with_apm_span';
import { getApmIndexPatternTitle } from './get_apm_index_pattern_title';
-export async function createStaticIndexPattern(
- setup: Setup,
- config: APMRouteHandlerResources['config'],
- savedObjectsClient: InternalSavedObjectsClient,
- spaceId: string | undefined,
- overwrite = false
-): Promise {
+type ApmIndexPatternAttributes = typeof apmIndexPattern.attributes & {
+ title: string;
+};
+
+export async function createStaticIndexPattern({
+ setup,
+ config,
+ savedObjectsClient,
+ spaceId,
+ overwrite = false,
+}: {
+ setup: Setup;
+ config: APMRouteHandlerResources['config'];
+ savedObjectsClient: InternalSavedObjectsClient;
+ spaceId?: string;
+ overwrite?: boolean;
+}): Promise {
return withApmSpan('create_static_index_pattern', async () => {
// don't autocreate APM index pattern if it's been disabled via the config
if (!config['xpack.apm.autocreateApmIndexPattern']) {
@@ -35,8 +45,31 @@ export async function createStaticIndexPattern(
return false;
}
+ const apmIndexPatternTitle = getApmIndexPatternTitle(config);
+
+ if (!overwrite) {
+ try {
+ const {
+ attributes: { title: existingApmIndexPatternTitle },
+ }: {
+ attributes: ApmIndexPatternAttributes;
+ } = await savedObjectsClient.get(
+ 'index-pattern',
+ APM_STATIC_INDEX_PATTERN_ID
+ );
+ // if the existing index pattern does not matches the new one, force an update
+ if (existingApmIndexPatternTitle !== apmIndexPatternTitle) {
+ overwrite = true;
+ }
+ } catch (e) {
+ // if the index pattern (saved object) is not found, then we can continue with creation
+ if (!SavedObjectsErrorHelpers.isNotFoundError(e)) {
+ throw e;
+ }
+ }
+ }
+
try {
- const apmIndexPatternTitle = getApmIndexPatternTitle(config);
await withApmSpan('create_index_pattern_saved_object', () =>
savedObjectsClient.create(
'index-pattern',
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/async_search_service.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/async_search_service.ts
index 7a511fc60fd06..155cb1f4615bd 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/async_search_service.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/async_search_service.ts
@@ -11,7 +11,7 @@ import { fetchTransactionDurationFieldCandidates } from './query_field_candidate
import { fetchTransactionDurationFieldValuePairs } from './query_field_value_pairs';
import { fetchTransactionDurationPercentiles } from './query_percentiles';
import { fetchTransactionDurationCorrelation } from './query_correlation';
-import { fetchTransactionDurationHistogramRangesteps } from './query_histogram_rangesteps';
+import { fetchTransactionDurationHistogramRangeSteps } from './query_histogram_range_steps';
import { fetchTransactionDurationRanges, HistogramItem } from './query_ranges';
import type {
AsyncSearchProviderProgress,
@@ -24,6 +24,8 @@ import { fetchTransactionDurationFractions } from './query_fractions';
const CORRELATION_THRESHOLD = 0.3;
const KS_TEST_THRESHOLD = 0.1;
+const currentTimeAsString = () => new Date().toISOString();
+
export const asyncSearchServiceProvider = (
esClient: ElasticsearchClient,
params: SearchServiceParams
@@ -31,6 +33,9 @@ export const asyncSearchServiceProvider = (
let isCancelled = false;
let isRunning = true;
let error: Error;
+ const log: string[] = [];
+ const logMessage = (message: string) =>
+ log.push(`${currentTimeAsString()}: ${message}`);
const progress: AsyncSearchProviderProgress = {
started: Date.now(),
@@ -53,13 +58,17 @@ export const asyncSearchServiceProvider = (
let percentileThresholdValue: number;
const cancel = () => {
+ logMessage(`Service cancelled.`);
isCancelled = true;
};
const fetchCorrelations = async () => {
try {
// 95th percentile to be displayed as a marker in the log log chart
- const percentileThreshold = await fetchTransactionDurationPercentiles(
+ const {
+ totalDocs,
+ percentiles: percentileThreshold,
+ } = await fetchTransactionDurationPercentiles(
esClient,
params,
params.percentileThreshold ? [params.percentileThreshold] : undefined
@@ -67,12 +76,32 @@ export const asyncSearchServiceProvider = (
percentileThresholdValue =
percentileThreshold[`${params.percentileThreshold}.0`];
- const histogramRangeSteps = await fetchTransactionDurationHistogramRangesteps(
+ logMessage(
+ `Fetched ${params.percentileThreshold}th percentile value of ${percentileThresholdValue} based on ${totalDocs} documents.`
+ );
+
+ // finish early if we weren't able to identify the percentileThresholdValue.
+ if (percentileThresholdValue === undefined) {
+ logMessage(
+ `Abort service since percentileThresholdValue could not be determined.`
+ );
+ progress.loadedHistogramStepsize = 1;
+ progress.loadedOverallHistogram = 1;
+ progress.loadedFieldCanditates = 1;
+ progress.loadedFieldValuePairs = 1;
+ progress.loadedHistograms = 1;
+ isRunning = false;
+ return;
+ }
+
+ const histogramRangeSteps = await fetchTransactionDurationHistogramRangeSteps(
esClient,
params
);
progress.loadedHistogramStepsize = 1;
+ logMessage(`Loaded histogram range steps.`);
+
if (isCancelled) {
isRunning = false;
return;
@@ -86,6 +115,8 @@ export const asyncSearchServiceProvider = (
progress.loadedOverallHistogram = 1;
overallHistogram = overallLogHistogramChartData;
+ logMessage(`Loaded overall histogram chart data.`);
+
if (isCancelled) {
isRunning = false;
return;
@@ -93,13 +124,13 @@ export const asyncSearchServiceProvider = (
// Create an array of ranges [2, 4, 6, ..., 98]
const percents = Array.from(range(2, 100, 2));
- const percentilesRecords = await fetchTransactionDurationPercentiles(
- esClient,
- params,
- percents
- );
+ const {
+ percentiles: percentilesRecords,
+ } = await fetchTransactionDurationPercentiles(esClient, params, percents);
const percentiles = Object.values(percentilesRecords);
+ logMessage(`Loaded percentiles.`);
+
if (isCancelled) {
isRunning = false;
return;
@@ -110,6 +141,8 @@ export const asyncSearchServiceProvider = (
params
);
+ logMessage(`Identified ${fieldCandidates.length} fieldCandidates.`);
+
progress.loadedFieldCanditates = 1;
const fieldValuePairs = await fetchTransactionDurationFieldValuePairs(
@@ -119,6 +152,8 @@ export const asyncSearchServiceProvider = (
progress
);
+ logMessage(`Identified ${fieldValuePairs.length} fieldValuePairs.`);
+
if (isCancelled) {
isRunning = false;
return;
@@ -133,6 +168,8 @@ export const asyncSearchServiceProvider = (
totalDocCount,
} = await fetchTransactionDurationFractions(esClient, params, ranges);
+ logMessage(`Loaded fractions and totalDocCount of ${totalDocCount}.`);
+
async function* fetchTransactionDurationHistograms() {
for (const item of shuffle(fieldValuePairs)) {
if (item === undefined || isCancelled) {
@@ -185,7 +222,11 @@ export const asyncSearchServiceProvider = (
yield undefined;
}
} catch (e) {
- error = e;
+ // don't fail the whole process for individual correlation queries, just add the error to the internal log.
+ logMessage(
+ `Failed to fetch correlation/kstest for '${item.field}/${item.value}'`
+ );
+ yield undefined;
}
}
}
@@ -199,10 +240,14 @@ export const asyncSearchServiceProvider = (
progress.loadedHistograms = loadedHistograms / fieldValuePairs.length;
}
- isRunning = false;
+ logMessage(
+ `Identified ${values.length} significant correlations out of ${fieldValuePairs.length} field/value pairs.`
+ );
} catch (e) {
error = e;
}
+
+ isRunning = false;
};
fetchCorrelations();
@@ -212,6 +257,7 @@ export const asyncSearchServiceProvider = (
return {
error,
+ log,
isRunning,
loaded: Math.round(progress.getOverallProgress() * 100),
overallHistogram,
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.test.ts
index 12e897ab3eec9..016355b3a6415 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.test.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.test.ts
@@ -10,10 +10,23 @@ import { getQueryWithParams } from './get_query_with_params';
describe('correlations', () => {
describe('getQueryWithParams', () => {
it('returns the most basic query filtering on processor.event=transaction', () => {
- const query = getQueryWithParams({ params: { index: 'apm-*' } });
+ const query = getQueryWithParams({
+ params: { index: 'apm-*', start: '2020', end: '2021' },
+ });
expect(query).toEqual({
bool: {
- filter: [{ term: { 'processor.event': 'transaction' } }],
+ filter: [
+ { term: { 'processor.event': 'transaction' } },
+ {
+ range: {
+ '@timestamp': {
+ format: 'epoch_millis',
+ gte: 1577836800000,
+ lte: 1609459200000,
+ },
+ },
+ },
+ ],
},
});
});
@@ -24,8 +37,8 @@ describe('correlations', () => {
index: 'apm-*',
serviceName: 'actualServiceName',
transactionName: 'actualTransactionName',
- start: '01-01-2021',
- end: '31-01-2021',
+ start: '2020',
+ end: '2021',
environment: 'dev',
percentileThresholdValue: 75,
},
@@ -33,22 +46,17 @@ describe('correlations', () => {
expect(query).toEqual({
bool: {
filter: [
- { term: { 'processor.event': 'transaction' } },
- {
- term: {
- 'service.name': 'actualServiceName',
- },
- },
{
term: {
- 'transaction.name': 'actualTransactionName',
+ 'processor.event': 'transaction',
},
},
{
range: {
'@timestamp': {
- gte: '01-01-2021',
- lte: '31-01-2021',
+ format: 'epoch_millis',
+ gte: 1577836800000,
+ lte: 1609459200000,
},
},
},
@@ -57,6 +65,16 @@ describe('correlations', () => {
'service.environment': 'dev',
},
},
+ {
+ term: {
+ 'service.name': 'actualServiceName',
+ },
+ },
+ {
+ term: {
+ 'transaction.name': 'actualTransactionName',
+ },
+ },
{
range: {
'transaction.duration.us': {
@@ -71,7 +89,7 @@ describe('correlations', () => {
it('returns a query considering a custom field/value pair', () => {
const query = getQueryWithParams({
- params: { index: 'apm-*' },
+ params: { index: 'apm-*', start: '2020', end: '2021' },
fieldName: 'actualFieldName',
fieldValue: 'actualFieldValue',
});
@@ -79,6 +97,15 @@ describe('correlations', () => {
bool: {
filter: [
{ term: { 'processor.event': 'transaction' } },
+ {
+ range: {
+ '@timestamp': {
+ format: 'epoch_millis',
+ gte: 1577836800000,
+ lte: 1609459200000,
+ },
+ },
+ },
{
term: {
actualFieldName: 'actualFieldValue',
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.ts
index 08ba4b23fec35..e0ddfc1b053b5 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/get_query_with_params.ts
@@ -5,16 +5,19 @@
* 2.0.
*/
+import { pipe } from 'fp-ts/lib/pipeable';
+import { getOrElse } from 'fp-ts/lib/Either';
+import { failure } from 'io-ts/lib/PathReporter';
+import * as t from 'io-ts';
+
import type { estypes } from '@elastic/elasticsearch';
-import {
- PROCESSOR_EVENT,
- SERVICE_NAME,
- TRANSACTION_DURATION,
- TRANSACTION_NAME,
-} from '../../../../common/elasticsearch_fieldnames';
+import { TRANSACTION_DURATION } from '../../../../common/elasticsearch_fieldnames';
import type { SearchServiceParams } from '../../../../common/search_strategies/correlations/types';
-import { environmentQuery as getEnvironmentQuery } from '../../../utils/queries';
-import { ProcessorEvent } from '../../../../common/processor_event';
+import { rangeRt } from '../../../routes/default_api_types';
+
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
+
+import { getCorrelationsFilters } from '../../correlations/get_filters';
const getPercentileThresholdValueQuery = (
percentileThresholdValue: number | undefined
@@ -39,26 +42,6 @@ export const getTermsQuery = (
return fieldName && fieldValue ? [{ term: { [fieldName]: fieldValue } }] : [];
};
-const getRangeQuery = (
- start?: string,
- end?: string
-): estypes.QueryDslQueryContainer[] => {
- if (start === undefined && end === undefined) {
- return [];
- }
-
- return [
- {
- range: {
- '@timestamp': {
- ...(start !== undefined ? { gte: start } : {}),
- ...(end !== undefined ? { lte: end } : {}),
- },
- },
- },
- ];
-};
-
interface QueryParams {
params: SearchServiceParams;
fieldName?: string;
@@ -71,21 +54,37 @@ export const getQueryWithParams = ({
}: QueryParams) => {
const {
environment,
+ kuery,
serviceName,
start,
end,
percentileThresholdValue,
+ transactionType,
transactionName,
} = params;
+
+ // converts string based start/end to epochmillis
+ const setup = pipe(
+ rangeRt.decode({ start, end }),
+ getOrElse((errors) => {
+ throw new Error(failure(errors).join('\n'));
+ })
+ ) as Setup & SetupTimeRange;
+
+ const filters = getCorrelationsFilters({
+ setup,
+ environment,
+ kuery,
+ serviceName,
+ transactionType,
+ transactionName,
+ });
+
return {
bool: {
filter: [
- ...getTermsQuery(PROCESSOR_EVENT, ProcessorEvent.transaction),
- ...getTermsQuery(SERVICE_NAME, serviceName),
- ...getTermsQuery(TRANSACTION_NAME, transactionName),
+ ...filters,
...getTermsQuery(fieldName, fieldValue),
- ...getRangeQuery(start, end),
- ...getEnvironmentQuery(environment),
...getPercentileThresholdValueQuery(percentileThresholdValue),
] as estypes.QueryDslQueryContainer[],
},
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_correlation.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_correlation.test.ts
index 24741ebaa2dae..678328dce1a19 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_correlation.test.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_correlation.test.ts
@@ -15,7 +15,7 @@ import {
BucketCorrelation,
} from './query_correlation';
-const params = { index: 'apm-*' };
+const params = { index: 'apm-*', start: '2020', end: '2021' };
const expectations = [1, 3, 5];
const ranges = [{ to: 1 }, { from: 1, to: 3 }, { from: 3, to: 5 }, { from: 5 }];
const fractions = [1, 2, 4, 5];
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_candidates.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_candidates.test.ts
index 89bdd4280d324..8929b31b3ecb1 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_candidates.test.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_candidates.test.ts
@@ -16,7 +16,7 @@ import {
shouldBeExcluded,
} from './query_field_candidates';
-const params = { index: 'apm-*' };
+const params = { index: 'apm-*', start: '2020', end: '2021' };
describe('query_field_candidates', () => {
describe('shouldBeExcluded', () => {
@@ -61,6 +61,15 @@ describe('query_field_candidates', () => {
'processor.event': 'transaction',
},
},
+ {
+ range: {
+ '@timestamp': {
+ format: 'epoch_millis',
+ gte: 1577836800000,
+ lte: 1609459200000,
+ },
+ },
+ },
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_value_pairs.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_value_pairs.test.ts
index ea5a1f55bc924..7ffbc5208e41e 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_value_pairs.test.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_field_value_pairs.test.ts
@@ -16,7 +16,7 @@ import {
getTermsAggRequest,
} from './query_field_value_pairs';
-const params = { index: 'apm-*' };
+const params = { index: 'apm-*', start: '2020', end: '2021' };
describe('query_field_value_pairs', () => {
describe('getTermsAggRequest', () => {
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_fractions.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_fractions.test.ts
index 6052841d277c3..3e7d4a52e4de2 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_fractions.test.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_fractions.test.ts
@@ -14,7 +14,7 @@ import {
getTransactionDurationRangesRequest,
} from './query_fractions';
-const params = { index: 'apm-*' };
+const params = { index: 'apm-*', start: '2020', end: '2021' };
const ranges = [{ to: 1 }, { from: 1, to: 3 }, { from: 3, to: 5 }, { from: 5 }];
describe('query_fractions', () => {
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram.test.ts
index 2be9446352260..ace9177947960 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram.test.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram.test.ts
@@ -14,7 +14,7 @@ import {
getTransactionDurationHistogramRequest,
} from './query_histogram';
-const params = { index: 'apm-*' };
+const params = { index: 'apm-*', start: '2020', end: '2021' };
const interval = 100;
describe('query_histogram', () => {
@@ -40,6 +40,15 @@ describe('query_histogram', () => {
'processor.event': 'transaction',
},
},
+ {
+ range: {
+ '@timestamp': {
+ format: 'epoch_millis',
+ gte: 1577836800000,
+ lte: 1609459200000,
+ },
+ },
+ },
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_interval.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_interval.test.ts
index 9ed529ccabddb..ebd78f1248510 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_interval.test.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_interval.test.ts
@@ -14,7 +14,7 @@ import {
getHistogramIntervalRequest,
} from './query_histogram_interval';
-const params = { index: 'apm-*' };
+const params = { index: 'apm-*', start: '2020', end: '2021' };
describe('query_histogram_interval', () => {
describe('getHistogramIntervalRequest', () => {
@@ -43,6 +43,15 @@ describe('query_histogram_interval', () => {
'processor.event': 'transaction',
},
},
+ {
+ range: {
+ '@timestamp': {
+ format: 'epoch_millis',
+ gte: 1577836800000,
+ lte: 1609459200000,
+ },
+ },
+ },
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_rangesteps.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_range_steps.test.ts
similarity index 77%
rename from x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_rangesteps.test.ts
rename to x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_range_steps.test.ts
index bb366ea29fed4..76aab1cd979c9 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_rangesteps.test.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_range_steps.test.ts
@@ -10,13 +10,13 @@ import type { estypes } from '@elastic/elasticsearch';
import type { ElasticsearchClient } from 'src/core/server';
import {
- fetchTransactionDurationHistogramRangesteps,
+ fetchTransactionDurationHistogramRangeSteps,
getHistogramIntervalRequest,
-} from './query_histogram_rangesteps';
+} from './query_histogram_range_steps';
-const params = { index: 'apm-*' };
+const params = { index: 'apm-*', start: '2020', end: '2021' };
-describe('query_histogram_rangesteps', () => {
+describe('query_histogram_range_steps', () => {
describe('getHistogramIntervalRequest', () => {
it('returns the request body for the histogram interval request', () => {
const req = getHistogramIntervalRequest(params);
@@ -43,6 +43,15 @@ describe('query_histogram_rangesteps', () => {
'processor.event': 'transaction',
},
},
+ {
+ range: {
+ '@timestamp': {
+ format: 'epoch_millis',
+ gte: 1577836800000,
+ lte: 1609459200000,
+ },
+ },
+ },
],
},
},
@@ -53,13 +62,14 @@ describe('query_histogram_rangesteps', () => {
});
});
- describe('fetchTransactionDurationHistogramRangesteps', () => {
+ describe('fetchTransactionDurationHistogramRangeSteps', () => {
it('fetches the range steps for the log histogram', async () => {
const esClientSearchMock = jest.fn((req: estypes.SearchRequest): {
body: estypes.SearchResponse;
} => {
return {
body: ({
+ hits: { total: { value: 10 } },
aggregations: {
transaction_duration_max: {
value: 10000,
@@ -76,7 +86,7 @@ describe('query_histogram_rangesteps', () => {
search: esClientSearchMock,
} as unknown) as ElasticsearchClient;
- const resp = await fetchTransactionDurationHistogramRangesteps(
+ const resp = await fetchTransactionDurationHistogramRangeSteps(
esClientMock,
params
);
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_rangesteps.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_range_steps.ts
similarity index 83%
rename from x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_rangesteps.ts
rename to x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_range_steps.ts
index e537165ca53f3..6ee5dd6bcdf83 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_rangesteps.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_histogram_range_steps.ts
@@ -23,6 +23,14 @@ import type { SearchServiceParams } from '../../../../common/search_strategies/c
import { getQueryWithParams } from './get_query_with_params';
+const getHistogramRangeSteps = (min: number, max: number, steps: number) => {
+ // A d3 based scale function as a helper to get equally distributed bins on a log scale.
+ const logFn = scaleLog().domain([min, max]).range([1, steps]);
+ return [...Array(steps).keys()]
+ .map(logFn.invert)
+ .map((d) => (isNaN(d) ? 0 : d));
+};
+
export const getHistogramIntervalRequest = (
params: SearchServiceParams
): estypes.SearchRequest => ({
@@ -37,19 +45,24 @@ export const getHistogramIntervalRequest = (
},
});
-export const fetchTransactionDurationHistogramRangesteps = async (
+export const fetchTransactionDurationHistogramRangeSteps = async (
esClient: ElasticsearchClient,
params: SearchServiceParams
): Promise => {
+ const steps = 100;
+
const resp = await esClient.search(getHistogramIntervalRequest(params));
+ if ((resp.body.hits.total as estypes.SearchTotalHits).value === 0) {
+ return getHistogramRangeSteps(0, 1, 100);
+ }
+
if (resp.body.aggregations === undefined) {
throw new Error(
- 'fetchTransactionDurationHistogramInterval failed, did not return aggregations.'
+ 'fetchTransactionDurationHistogramRangeSteps failed, did not return aggregations.'
);
}
- const steps = 100;
const min = (resp.body.aggregations
.transaction_duration_min as estypes.AggregationsValueAggregate).value;
const max =
@@ -57,9 +70,5 @@ export const fetchTransactionDurationHistogramRangesteps = async (
.transaction_duration_max as estypes.AggregationsValueAggregate).value *
2;
- // A d3 based scale function as a helper to get equally distributed bins on a log scale.
- const logFn = scaleLog().domain([min, max]).range([1, steps]);
- return [...Array(steps).keys()]
- .map(logFn.invert)
- .map((d) => (isNaN(d) ? 0 : d));
+ return getHistogramRangeSteps(min, max, steps);
};
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.test.ts
index 0c319aee0fb2b..f0d01a4849f9f 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.test.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.test.ts
@@ -14,7 +14,7 @@ import {
getTransactionDurationPercentilesRequest,
} from './query_percentiles';
-const params = { index: 'apm-*' };
+const params = { index: 'apm-*', start: '2020', end: '2021' };
describe('query_percentiles', () => {
describe('getTransactionDurationPercentilesRequest', () => {
@@ -41,10 +41,20 @@ describe('query_percentiles', () => {
'processor.event': 'transaction',
},
},
+ {
+ range: {
+ '@timestamp': {
+ format: 'epoch_millis',
+ gte: 1577836800000,
+ lte: 1609459200000,
+ },
+ },
+ },
],
},
},
size: 0,
+ track_total_hits: true,
},
index: params.index,
});
@@ -53,6 +63,7 @@ describe('query_percentiles', () => {
describe('fetchTransactionDurationPercentiles', () => {
it('fetches the percentiles', async () => {
+ const totalDocs = 10;
const percentilesValues = {
'1.0': 5.0,
'5.0': 25.0,
@@ -68,6 +79,7 @@ describe('query_percentiles', () => {
} => {
return {
body: ({
+ hits: { total: { value: totalDocs } },
aggregations: {
transaction_duration_percentiles: {
values: percentilesValues,
@@ -86,7 +98,7 @@ describe('query_percentiles', () => {
params
);
- expect(resp).toEqual(percentilesValues);
+ expect(resp).toEqual({ percentiles: percentilesValues, totalDocs });
expect(esClientSearchMock).toHaveBeenCalledTimes(1);
});
});
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.ts
index 18dcefb59a11a..c80f5d836c0ef 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_percentiles.ts
@@ -38,6 +38,7 @@ export const getTransactionDurationPercentilesRequest = (
return {
index: params.index,
body: {
+ track_total_hits: true,
query,
size: 0,
aggs: {
@@ -61,7 +62,7 @@ export const fetchTransactionDurationPercentiles = async (
percents?: number[],
fieldName?: string,
fieldValue?: string
-): Promise> => {
+): Promise<{ totalDocs: number; percentiles: Record }> => {
const resp = await esClient.search(
getTransactionDurationPercentilesRequest(
params,
@@ -71,14 +72,22 @@ export const fetchTransactionDurationPercentiles = async (
)
);
+ // return early with no results if the search didn't return any documents
+ if ((resp.body.hits.total as estypes.SearchTotalHits).value === 0) {
+ return { totalDocs: 0, percentiles: {} };
+ }
+
if (resp.body.aggregations === undefined) {
throw new Error(
'fetchTransactionDurationPercentiles failed, did not return aggregations.'
);
}
- return (
- (resp.body.aggregations
- .transaction_duration_percentiles as estypes.AggregationsTDigestPercentilesAggregate)
- .values ?? {}
- );
+
+ return {
+ totalDocs: (resp.body.hits.total as estypes.SearchTotalHits).value,
+ percentiles:
+ (resp.body.aggregations
+ .transaction_duration_percentiles as estypes.AggregationsTDigestPercentilesAggregate)
+ .values ?? {},
+ };
};
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_ranges.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_ranges.test.ts
index 9451928e47ded..7d18efc360563 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_ranges.test.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/query_ranges.test.ts
@@ -14,7 +14,7 @@ import {
getTransactionDurationRangesRequest,
} from './query_ranges';
-const params = { index: 'apm-*' };
+const params = { index: 'apm-*', start: '2020', end: '2021' };
const rangeSteps = [1, 3, 5];
describe('query_ranges', () => {
@@ -59,6 +59,15 @@ describe('query_ranges', () => {
'processor.event': 'transaction',
},
},
+ {
+ range: {
+ '@timestamp': {
+ format: 'epoch_millis',
+ gte: 1577836800000,
+ lte: 1609459200000,
+ },
+ },
+ },
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.test.ts
index 6d4bfcdde9994..09775cb2eb034 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.test.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.test.ts
@@ -122,6 +122,8 @@ describe('APM Correlations search strategy', () => {
} as unknown) as SearchStrategyDependencies;
params = {
index: 'apm-*',
+ start: '2020',
+ end: '2021',
};
});
@@ -154,10 +156,22 @@ describe('APM Correlations search strategy', () => {
},
query: {
bool: {
- filter: [{ term: { 'processor.event': 'transaction' } }],
+ filter: [
+ { term: { 'processor.event': 'transaction' } },
+ {
+ range: {
+ '@timestamp': {
+ format: 'epoch_millis',
+ gte: 1577836800000,
+ lte: 1609459200000,
+ },
+ },
+ },
+ ],
},
},
size: 0,
+ track_total_hits: true,
})
);
});
@@ -167,11 +181,17 @@ describe('APM Correlations search strategy', () => {
it('retrieves the current request', async () => {
const searchStrategy = await apmCorrelationsSearchStrategyProvider();
const response = await searchStrategy
- .search({ id: 'my-search-id', params }, {}, mockDeps)
+ .search({ params }, {}, mockDeps)
.toPromise();
- expect(response).toEqual(
- expect.objectContaining({ id: 'my-search-id' })
+ const searchStrategyId = response.id;
+
+ const response2 = await searchStrategy
+ .search({ id: searchStrategyId, params }, {}, mockDeps)
+ .toPromise();
+
+ expect(response2).toEqual(
+ expect.objectContaining({ id: searchStrategyId })
);
});
});
@@ -226,7 +246,7 @@ describe('APM Correlations search strategy', () => {
expect(response2.id).toEqual(response1.id);
expect(response2).toEqual(
- expect.objectContaining({ loaded: 10, isRunning: false })
+ expect.objectContaining({ loaded: 100, isRunning: false })
);
});
});
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.ts
index d6b4e0e7094b3..8f2e6913c0d06 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/search_strategy.ts
@@ -41,14 +41,40 @@ export const apmCorrelationsSearchStrategyProvider = (): ISearchStrategy<
throw new Error('Invalid request parameters.');
}
- const id = request.id ?? uuid();
+ // The function to fetch the current state of the async search service.
+ // This will be either an existing service for a follow up fetch or a new one for new requests.
+ let getAsyncSearchServiceState: ReturnType<
+ typeof asyncSearchServiceProvider
+ >;
+
+ // If the request includes an ID, we require that the async search service already exists
+ // otherwise we throw an error. The client should never poll a service that's been cancelled or finished.
+ // This also avoids instantiating async search services when the service gets called with random IDs.
+ if (typeof request.id === 'string') {
+ const existingGetAsyncSearchServiceState = asyncSearchServiceMap.get(
+ request.id
+ );
- const getAsyncSearchServiceState =
- asyncSearchServiceMap.get(id) ??
- asyncSearchServiceProvider(deps.esClient.asCurrentUser, request.params);
+ if (typeof existingGetAsyncSearchServiceState === 'undefined') {
+ throw new Error(
+ `AsyncSearchService with ID '${request.id}' does not exist.`
+ );
+ }
+
+ getAsyncSearchServiceState = existingGetAsyncSearchServiceState;
+ } else {
+ getAsyncSearchServiceState = asyncSearchServiceProvider(
+ deps.esClient.asCurrentUser,
+ request.params
+ );
+ }
+
+ // Reuse the request's id or create a new one.
+ const id = request.id ?? uuid();
const {
error,
+ log,
isRunning,
loaded,
started,
@@ -76,6 +102,7 @@ export const apmCorrelationsSearchStrategyProvider = (): ISearchStrategy<
isRunning,
isPartial: isRunning,
rawResponse: {
+ log,
took,
values,
percentileThresholdValue,
diff --git a/x-pack/plugins/apm/server/lib/search_strategies/correlations/utils/aggregation_utils.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/correlations/utils/aggregation_utils.test.ts
index 63de0a59d4894..4313ad58ecbc0 100644
--- a/x-pack/plugins/apm/server/lib/search_strategies/correlations/utils/aggregation_utils.test.ts
+++ b/x-pack/plugins/apm/server/lib/search_strategies/correlations/utils/aggregation_utils.test.ts
@@ -14,6 +14,7 @@ describe('aggregation utils', () => {
expect(expectations).toEqual([0, 0.5, 1]);
expect(ranges).toEqual([{ to: 0 }, { from: 0, to: 1 }, { from: 1 }]);
});
+
it('returns expectations and ranges based on given percentiles #2', async () => {
const { expectations, ranges } = computeExpectationsAndRanges([1, 3, 5]);
expect(expectations).toEqual([1, 2, 4, 5]);
@@ -24,6 +25,7 @@ describe('aggregation utils', () => {
{ from: 5 },
]);
});
+
it('returns expectations and ranges with adjusted fractions', async () => {
const { expectations, ranges } = computeExpectationsAndRanges([
1,
@@ -45,5 +47,97 @@ describe('aggregation utils', () => {
{ from: 5 },
]);
});
+
+ // TODO identify these results derived from the array of percentiles are usable with the ES correlation aggregation
+ it('returns expectation and ranges adjusted when percentiles have equal values', async () => {
+ const { expectations, ranges } = computeExpectationsAndRanges([
+ 5000,
+ 5000,
+ 3090428,
+ 3090428,
+ 3090428,
+ 3618812,
+ 3618812,
+ 3618812,
+ 3618812,
+ 3696636,
+ 3696636,
+ 3696636,
+ 3696636,
+ 3696636,
+ 3696636,
+ ]);
+ expect(expectations).toEqual([
+ 5000,
+ 1856256.7999999998,
+ 3392361.714285714,
+ 3665506.4,
+ 3696636,
+ ]);
+ expect(ranges).toEqual([
+ {
+ to: 5000,
+ },
+ {
+ from: 5000,
+ to: 5000,
+ },
+ {
+ from: 5000,
+ to: 3090428,
+ },
+ {
+ from: 3090428,
+ to: 3090428,
+ },
+ {
+ from: 3090428,
+ to: 3090428,
+ },
+ {
+ from: 3090428,
+ to: 3618812,
+ },
+ {
+ from: 3618812,
+ to: 3618812,
+ },
+ {
+ from: 3618812,
+ to: 3618812,
+ },
+ {
+ from: 3618812,
+ to: 3618812,
+ },
+ {
+ from: 3618812,
+ to: 3696636,
+ },
+ {
+ from: 3696636,
+ to: 3696636,
+ },
+ {
+ from: 3696636,
+ to: 3696636,
+ },
+ {
+ from: 3696636,
+ to: 3696636,
+ },
+ {
+ from: 3696636,
+ to: 3696636,
+ },
+ {
+ from: 3696636,
+ to: 3696636,
+ },
+ {
+ from: 3696636,
+ },
+ ]);
+ });
});
});
diff --git a/x-pack/plugins/apm/server/routes/fleet.ts b/x-pack/plugins/apm/server/routes/fleet.ts
index b760014d6af89..66843f4f0df4d 100644
--- a/x-pack/plugins/apm/server/routes/fleet.ts
+++ b/x-pack/plugins/apm/server/routes/fleet.ts
@@ -25,8 +25,6 @@ import { createCloudApmPackgePolicy } from '../lib/fleet/create_cloud_apm_packag
import { getUnsupportedApmServerSchema } from '../lib/fleet/get_unsupported_apm_server_schema';
import { isSuperuser } from '../lib/fleet/is_superuser';
import { getInternalSavedObjectsClient } from '../lib/helpers/get_internal_saved_objects_client';
-import { setupRequest } from '../lib/helpers/setup_request';
-import { createStaticIndexPattern } from '../lib/index_pattern/create_static_index_pattern';
const hasFleetDataRoute = createApmServerRoute({
endpoint: 'GET /api/apm/fleet/has_data',
@@ -156,7 +154,7 @@ const createCloudApmPackagePolicyRoute = createApmServerRoute({
endpoint: 'POST /api/apm/fleet/cloud_apm_package_policy',
options: { tags: ['access:apm', 'access:apm_write'] },
handler: async (resources) => {
- const { plugins, context, config, request, logger, core } = resources;
+ const { plugins, context, config, request, logger } = resources;
const cloudApmMigrationEnabled =
config['xpack.apm.agent.migrations.enabled'];
if (!plugins.fleet || !plugins.security) {
@@ -174,7 +172,7 @@ const createCloudApmPackagePolicyRoute = createApmServerRoute({
throw Boom.forbidden(CLOUD_SUPERUSER_REQUIRED_MESSAGE);
}
- const cloudApmAackagePolicy = await createCloudApmPackgePolicy({
+ const cloudApmPackagePolicy = await createCloudApmPackgePolicy({
cloudPluginSetup,
fleetPluginStart,
savedObjectsClient,
@@ -182,25 +180,7 @@ const createCloudApmPackagePolicyRoute = createApmServerRoute({
logger,
});
- const [setup, internalSavedObjectsClient] = await Promise.all([
- setupRequest(resources),
- core
- .start()
- .then(({ savedObjects }) => savedObjects.createInternalRepository()),
- ]);
-
- const spaceId = plugins.spaces?.setup.spacesService.getSpaceId(request);
-
- // force update the index pattern title with data streams
- await createStaticIndexPattern(
- setup,
- config,
- internalSavedObjectsClient,
- spaceId,
- true
- );
-
- return { cloud_apm_package_policy: cloudApmAackagePolicy };
+ return { cloudApmPackagePolicy };
},
});
diff --git a/x-pack/plugins/apm/server/routes/index_pattern.ts b/x-pack/plugins/apm/server/routes/index_pattern.ts
index aa70cde4f96ae..190baf3bbc270 100644
--- a/x-pack/plugins/apm/server/routes/index_pattern.ts
+++ b/x-pack/plugins/apm/server/routes/index_pattern.ts
@@ -32,12 +32,12 @@ const staticIndexPatternRoute = createApmServerRoute({
const spaceId = spaces?.setup.spacesService.getSpaceId(request);
- const didCreateIndexPattern = await createStaticIndexPattern(
+ const didCreateIndexPattern = await createStaticIndexPattern({
setup,
config,
savedObjectsClient,
- spaceId
- );
+ spaceId,
+ });
return { created: didCreateIndexPattern };
},
diff --git a/x-pack/plugins/apm/server/routes/register_routes/index.ts b/x-pack/plugins/apm/server/routes/register_routes/index.ts
index 8e6070de722be..16e77f59f4d02 100644
--- a/x-pack/plugins/apm/server/routes/register_routes/index.ts
+++ b/x-pack/plugins/apm/server/routes/register_routes/index.ts
@@ -18,9 +18,12 @@ import {
routeValidationObject,
} from '@kbn/server-route-repository';
import { mergeRt, jsonRt } from '@kbn/io-ts-utils';
-import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/server';
import { pickKeys } from '../../../common/utils/pick_keys';
-import { APMRouteHandlerResources, InspectResponse } from '../typings';
+import {
+ APMRouteHandlerResources,
+ InspectResponse,
+ TelemetryUsageCounter,
+} from '../typings';
import type { ApmPluginRequestHandlerContext } from '../typings';
const inspectRt = t.exact(
@@ -56,9 +59,7 @@ export function registerRoutes({
repository: ServerRouteRepository;
config: APMRouteHandlerResources['config'];
ruleDataClient: APMRouteHandlerResources['ruleDataClient'];
- telemetryUsageCounter?: ReturnType<
- UsageCollectionSetup['createUsageCounter']
- >;
+ telemetryUsageCounter?: TelemetryUsageCounter;
}) {
const routes = repository.getRoutes();
@@ -104,6 +105,7 @@ export function registerRoutes({
logger,
core,
plugins,
+ telemetryUsageCounter,
params: merge(
{
query: {
diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts
index 05eec47893793..f50770cb5ded7 100644
--- a/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts
+++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration.ts
@@ -79,7 +79,7 @@ const deleteAgentConfigurationRoute = createApmServerRoute({
}),
handler: async (resources) => {
const setup = await setupRequest(resources);
- const { params, logger, core } = resources;
+ const { params, logger, core, telemetryUsageCounter } = resources;
const { service } = params.body;
@@ -106,6 +106,7 @@ const deleteAgentConfigurationRoute = createApmServerRoute({
core,
fleetPluginStart: await resources.plugins.fleet.start(),
setup,
+ telemetryUsageCounter,
});
logger.info(
`Updated Fleet integration policy for APM to remove the deleted agent configuration.`
@@ -128,7 +129,7 @@ const createOrUpdateAgentConfigurationRoute = createApmServerRoute({
]),
handler: async (resources) => {
const setup = await setupRequest(resources);
- const { params, logger, core } = resources;
+ const { params, logger, core, telemetryUsageCounter } = resources;
const { body, query } = params;
// if the config already exists, it is fetched and updated
@@ -162,6 +163,7 @@ const createOrUpdateAgentConfigurationRoute = createApmServerRoute({
core,
fleetPluginStart: await resources.plugins.fleet.start(),
setup,
+ telemetryUsageCounter,
});
logger.info(
`Saved latest agent settings to Fleet integration policy for APM.`
diff --git a/x-pack/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts
index 56a5950c27367..4279cfd84328c 100644
--- a/x-pack/plugins/apm/server/routes/typings.ts
+++ b/x-pack/plugins/apm/server/routes/typings.ts
@@ -18,6 +18,7 @@ import type { RacApiRequestHandlerContext } from '../../../rule_registry/server'
import { LicensingApiRequestHandlerContext } from '../../../licensing/server';
import { APMConfig } from '..';
import { APMPluginDependencies } from '../types';
+import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/server';
export interface ApmPluginRequestHandlerContext extends RequestHandlerContext {
licensing: LicensingApiRequestHandlerContext;
@@ -47,6 +48,10 @@ export interface APMRouteCreateOptions {
};
}
+export type TelemetryUsageCounter = ReturnType<
+ UsageCollectionSetup['createUsageCounter']
+>;
+
export interface APMRouteHandlerResources {
request: KibanaRequest;
context: ApmPluginRequestHandlerContext;
@@ -68,4 +73,5 @@ export interface APMRouteHandlerResources {
};
};
ruleDataClient: RuleDataClient;
+ telemetryUsageCounter?: TelemetryUsageCounter;
}
diff --git a/x-pack/plugins/cases/docs/README.md b/x-pack/plugins/cases/docs/README.md
index 85482d98dc509..16e9311958763 100644
--- a/x-pack/plugins/cases/docs/README.md
+++ b/x-pack/plugins/cases/docs/README.md
@@ -19,7 +19,7 @@ yarn global add typedoc typedoc-plugin-markdown
```bash
cd x-pack/plugins/cases/docs
-npx typedoc --options cases_client_typedoc.json
+npx typedoc --gitRemote upstream --options cases_client_typedoc.json
```
After running the above commands the files in the `server` directory will be updated to match the new tsdocs.
diff --git a/x-pack/plugins/cases/docs/cases_client/classes/client.casesclient.md b/x-pack/plugins/cases/docs/cases_client/classes/client.casesclient.md
index bd07a44a2bfdf..b3bef7b36b571 100644
--- a/x-pack/plugins/cases/docs/cases_client/classes/client.casesclient.md
+++ b/x-pack/plugins/cases/docs/cases_client/classes/client.casesclient.md
@@ -45,7 +45,7 @@ Client wrapper that contains accessor methods for individual entities within the
**Returns:** [*CasesClient*](client.casesclient.md)
-Defined in: [client.ts:28](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L28)
+Defined in: [client.ts:28](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L28)
## Properties
@@ -53,7 +53,7 @@ Defined in: [client.ts:28](https://github.com/jonathan-buttner/kibana/blob/b65ed
• `Private` `Readonly` **\_attachments**: [*AttachmentsSubClient*](../interfaces/attachments_client.attachmentssubclient.md)
-Defined in: [client.ts:24](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L24)
+Defined in: [client.ts:24](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L24)
___
@@ -61,7 +61,7 @@ ___
• `Private` `Readonly` **\_cases**: [*CasesSubClient*](../interfaces/cases_client.casessubclient.md)
-Defined in: [client.ts:23](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L23)
+Defined in: [client.ts:23](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L23)
___
@@ -69,7 +69,7 @@ ___
• `Private` `Readonly` **\_casesClientInternal**: *CasesClientInternal*
-Defined in: [client.ts:22](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L22)
+Defined in: [client.ts:22](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L22)
___
@@ -77,7 +77,7 @@ ___
• `Private` `Readonly` **\_configure**: [*ConfigureSubClient*](../interfaces/configure_client.configuresubclient.md)
-Defined in: [client.ts:27](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L27)
+Defined in: [client.ts:27](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L27)
___
@@ -85,7 +85,7 @@ ___
• `Private` `Readonly` **\_stats**: [*StatsSubClient*](../interfaces/stats_client.statssubclient.md)
-Defined in: [client.ts:28](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L28)
+Defined in: [client.ts:28](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L28)
___
@@ -93,7 +93,7 @@ ___
• `Private` `Readonly` **\_subCases**: [*SubCasesClient*](../interfaces/sub_cases_client.subcasesclient.md)
-Defined in: [client.ts:26](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L26)
+Defined in: [client.ts:26](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L26)
___
@@ -101,7 +101,7 @@ ___
• `Private` `Readonly` **\_userActions**: [*UserActionsSubClient*](../interfaces/user_actions_client.useractionssubclient.md)
-Defined in: [client.ts:25](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L25)
+Defined in: [client.ts:25](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L25)
## Accessors
@@ -113,7 +113,7 @@ Retrieves an interface for interacting with attachments (comments) entities.
**Returns:** [*AttachmentsSubClient*](../interfaces/attachments_client.attachmentssubclient.md)
-Defined in: [client.ts:50](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L50)
+Defined in: [client.ts:50](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L50)
___
@@ -125,7 +125,7 @@ Retrieves an interface for interacting with cases entities.
**Returns:** [*CasesSubClient*](../interfaces/cases_client.casessubclient.md)
-Defined in: [client.ts:43](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L43)
+Defined in: [client.ts:43](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L43)
___
@@ -137,7 +137,7 @@ Retrieves an interface for interacting with the configuration of external connec
**Returns:** [*ConfigureSubClient*](../interfaces/configure_client.configuresubclient.md)
-Defined in: [client.ts:76](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L76)
+Defined in: [client.ts:76](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L76)
___
@@ -149,7 +149,7 @@ Retrieves an interface for retrieving statistics related to the cases entities.
**Returns:** [*StatsSubClient*](../interfaces/stats_client.statssubclient.md)
-Defined in: [client.ts:83](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L83)
+Defined in: [client.ts:83](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L83)
___
@@ -163,7 +163,7 @@ Currently this functionality is disabled and will throw an error if this functio
**Returns:** [*SubCasesClient*](../interfaces/sub_cases_client.subcasesclient.md)
-Defined in: [client.ts:66](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L66)
+Defined in: [client.ts:66](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L66)
___
@@ -175,4 +175,4 @@ Retrieves an interface for interacting with the user actions associated with the
**Returns:** [*UserActionsSubClient*](../interfaces/user_actions_client.useractionssubclient.md)
-Defined in: [client.ts:57](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/client.ts#L57)
+Defined in: [client.ts:57](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/client.ts#L57)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_add.addargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_add.addargs.md
index f8f7babd15b90..0fafb377bcc41 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_add.addargs.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_add.addargs.md
@@ -21,14 +21,14 @@ The arguments needed for creating a new attachment to a case.
The case ID that this attachment will be associated with
-Defined in: [attachments/add.ts:305](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/add.ts#L305)
+Defined in: [attachments/add.ts:305](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/add.ts#L305)
___
### comment
-• **comment**: { `comment`: *string* ; `owner`: *string* ; `type`: user } \| { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert }
+• **comment**: { `comment`: *string* ; `owner`: *string* ; `type`: user } \| { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } \| { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions }
The attachment values.
-Defined in: [attachments/add.ts:309](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/add.ts#L309)
+Defined in: [attachments/add.ts:309](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/add.ts#L309)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_client.attachmentssubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_client.attachmentssubclient.md
index 57141796f6f67..ff9744583cfaf 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_client.attachmentssubclient.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_client.attachmentssubclient.md
@@ -35,7 +35,7 @@ Adds an attachment to a case.
**Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\>
-Defined in: [attachments/client.ts:35](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L35)
+Defined in: [attachments/client.ts:35](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L35)
___
@@ -53,7 +53,7 @@ Deletes a single attachment for a specific case.
**Returns:** *Promise*
-Defined in: [attachments/client.ts:43](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L43)
+Defined in: [attachments/client.ts:43](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L43)
___
@@ -71,7 +71,7 @@ Deletes all attachments associated with a single case.
**Returns:** *Promise*
-Defined in: [attachments/client.ts:39](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L39)
+Defined in: [attachments/client.ts:39](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L39)
___
@@ -89,13 +89,13 @@ Retrieves all comments matching the search criteria.
**Returns:** *Promise*<[*ICommentsResponse*](typedoc_interfaces.icommentsresponse.md)\>
-Defined in: [attachments/client.ts:47](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L47)
+Defined in: [attachments/client.ts:47](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L47)
___
### get
-▸ **get**(`getArgs`: [*GetArgs*](attachments_get.getargs.md)): *Promise*<{ `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }\>
+▸ **get**(`getArgs`: [*GetArgs*](attachments_get.getargs.md)): *Promise*<{ `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }\>
Retrieves a single attachment for a case.
@@ -105,9 +105,9 @@ Retrieves a single attachment for a case.
| :------ | :------ |
| `getArgs` | [*GetArgs*](attachments_get.getargs.md) |
-**Returns:** *Promise*<{ `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }\>
+**Returns:** *Promise*<{ `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }\>
-Defined in: [attachments/client.ts:59](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L59)
+Defined in: [attachments/client.ts:59](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L59)
___
@@ -125,7 +125,7 @@ Gets all attachments for a single case.
**Returns:** *Promise*<[*IAllCommentsResponse*](typedoc_interfaces.iallcommentsresponse.md)\>
-Defined in: [attachments/client.ts:55](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L55)
+Defined in: [attachments/client.ts:55](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L55)
___
@@ -143,7 +143,7 @@ Retrieves all alerts attach to a case given a single case ID
**Returns:** *Promise*<{ `attached_at`: *string* ; `id`: *string* ; `index`: *string* }[]\>
-Defined in: [attachments/client.ts:51](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L51)
+Defined in: [attachments/client.ts:51](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L51)
___
@@ -163,4 +163,4 @@ The request must include all fields for the attachment. Even the fields that are
**Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\>
-Defined in: [attachments/client.ts:65](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/client.ts#L65)
+Defined in: [attachments/client.ts:65](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/client.ts#L65)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteallargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteallargs.md
index d134c92e282a3..39c72e81a9935 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteallargs.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteallargs.md
@@ -21,7 +21,7 @@ Parameters for deleting all comments of a case or sub case.
The case ID to delete all attachments for
-Defined in: [attachments/delete.ts:31](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/delete.ts#L31)
+Defined in: [attachments/delete.ts:31](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/delete.ts#L31)
___
@@ -31,4 +31,4 @@ ___
If specified the caseID will be ignored and this value will be used to find a sub case for deleting all the attachments
-Defined in: [attachments/delete.ts:35](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/delete.ts#L35)
+Defined in: [attachments/delete.ts:35](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/delete.ts#L35)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteargs.md
index a1c177bad8a09..fb7e61fa1521f 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteargs.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_delete.deleteargs.md
@@ -22,7 +22,7 @@ Parameters for deleting a single attachment of a case or sub case.
The attachment ID to delete
-Defined in: [attachments/delete.ts:49](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/delete.ts#L49)
+Defined in: [attachments/delete.ts:49](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/delete.ts#L49)
___
@@ -32,7 +32,7 @@ ___
The case ID to delete an attachment from
-Defined in: [attachments/delete.ts:45](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/delete.ts#L45)
+Defined in: [attachments/delete.ts:45](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/delete.ts#L45)
___
@@ -42,4 +42,4 @@ ___
If specified the caseID will be ignored and this value will be used to find a sub case for deleting the attachment
-Defined in: [attachments/delete.ts:53](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/delete.ts#L53)
+Defined in: [attachments/delete.ts:53](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/delete.ts#L53)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.findargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.findargs.md
index dcd4deb28b687..826a05f5865ab 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.findargs.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.findargs.md
@@ -21,7 +21,7 @@ Parameters for finding attachments of a case
The case ID for finding associated attachments
-Defined in: [attachments/get.ts:47](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L47)
+Defined in: [attachments/get.ts:47](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L47)
___
@@ -48,4 +48,4 @@ Optional parameters for filtering the returned attachments
| `sortOrder` | *undefined* \| ``"desc"`` \| ``"asc"`` |
| `subCaseId` | *undefined* \| *string* |
-Defined in: [attachments/get.ts:51](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L51)
+Defined in: [attachments/get.ts:51](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L51)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallalertsattachtocase.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallalertsattachtocase.md
index d935823054b03..abeeaca19b23e 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallalertsattachtocase.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallalertsattachtocase.md
@@ -18,4 +18,4 @@
The ID of the case to retrieve the alerts from
-Defined in: [attachments/get.ts:87](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L87)
+Defined in: [attachments/get.ts:87](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L87)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallargs.md
index 9577e89b46074..9ea29437d5c2c 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallargs.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getallargs.md
@@ -22,7 +22,7 @@ Parameters for retrieving all attachments of a case
The case ID to retrieve all attachments for
-Defined in: [attachments/get.ts:61](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L61)
+Defined in: [attachments/get.ts:61](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L61)
___
@@ -32,7 +32,7 @@ ___
Optionally include the attachments associated with a sub case
-Defined in: [attachments/get.ts:65](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L65)
+Defined in: [attachments/get.ts:65](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L65)
___
@@ -42,4 +42,4 @@ ___
If included the case ID will be ignored and the attachments will be retrieved from the specified ID of the sub case
-Defined in: [attachments/get.ts:69](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L69)
+Defined in: [attachments/get.ts:69](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L69)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getargs.md
index 5530ad8bd936e..e46d83d795f48 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getargs.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_get.getargs.md
@@ -19,7 +19,7 @@
The ID of the attachment to retrieve
-Defined in: [attachments/get.ts:80](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L80)
+Defined in: [attachments/get.ts:80](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L80)
___
@@ -29,4 +29,4 @@ ___
The ID of the case to retrieve an attachment from
-Defined in: [attachments/get.ts:76](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/get.ts#L76)
+Defined in: [attachments/get.ts:76](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/get.ts#L76)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_update.updateargs.md b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_update.updateargs.md
index ce586a6bfdfbd..23d7c88c9c864 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_update.updateargs.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/attachments_update.updateargs.md
@@ -22,7 +22,7 @@ Parameters for updating a single attachment
The ID of the case that is associated with this attachment
-Defined in: [attachments/update.ts:32](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/update.ts#L32)
+Defined in: [attachments/update.ts:32](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/update.ts#L32)
___
@@ -32,14 +32,14 @@ ___
The ID of a sub case, if specified a sub case will be searched for to perform the attachment update instead of on a case
-Defined in: [attachments/update.ts:40](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/update.ts#L40)
+Defined in: [attachments/update.ts:40](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/update.ts#L40)
___
### updateRequest
-• **updateRequest**: { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `id`: *string* ; `version`: *string* }
+• **updateRequest**: { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `id`: *string* ; `version`: *string* }
The full attachment request with the fields updated with appropriate values
-Defined in: [attachments/update.ts:36](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/attachments/update.ts#L36)
+Defined in: [attachments/update.ts:36](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/attachments/update.ts#L36)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_client.casessubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_client.casessubclient.md
index 52cf2fbaf1ef1..45285066b4608 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_client.casessubclient.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_client.casessubclient.md
@@ -36,7 +36,7 @@ Creates a case.
**Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\>
-Defined in: [cases/client.ts:49](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L49)
+Defined in: [cases/client.ts:49](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L49)
___
@@ -56,7 +56,7 @@ Delete a case and all its comments.
**Returns:** *Promise*
-Defined in: [cases/client.ts:73](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L73)
+Defined in: [cases/client.ts:73](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L73)
___
@@ -76,7 +76,7 @@ If the `owner` field is left empty then all the cases that the user has access t
**Returns:** *Promise*<[*ICasesFindResponse*](typedoc_interfaces.icasesfindresponse.md)\>
-Defined in: [cases/client.ts:55](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L55)
+Defined in: [cases/client.ts:55](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L55)
___
@@ -94,7 +94,7 @@ Retrieves a single case with the specified ID.
**Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\>
-Defined in: [cases/client.ts:59](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L59)
+Defined in: [cases/client.ts:59](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L59)
___
@@ -112,7 +112,7 @@ Retrieves the cases ID and title that have the requested alert attached to them
**Returns:** *Promise*<{ `id`: *string* ; `title`: *string* }[]\>
-Defined in: [cases/client.ts:85](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L85)
+Defined in: [cases/client.ts:85](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L85)
___
@@ -131,7 +131,7 @@ Retrieves all the reporters across all accessible cases.
**Returns:** *Promise*<{ `email`: *undefined* \| ``null`` \| *string* ; `full_name`: *undefined* \| ``null`` \| *string* ; `username`: *undefined* \| ``null`` \| *string* }[]\>
-Defined in: [cases/client.ts:81](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L81)
+Defined in: [cases/client.ts:81](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L81)
___
@@ -150,7 +150,7 @@ Retrieves all the tags across all cases the user making the request has access t
**Returns:** *Promise*
-Defined in: [cases/client.ts:77](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L77)
+Defined in: [cases/client.ts:77](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L77)
___
@@ -168,7 +168,7 @@ Pushes a specific case to an external system.
**Returns:** *Promise*<[*ICaseResponse*](typedoc_interfaces.icaseresponse.md)\>
-Defined in: [cases/client.ts:63](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L63)
+Defined in: [cases/client.ts:63](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L63)
___
@@ -186,4 +186,4 @@ Update the specified cases with the passed in values.
**Returns:** *Promise*<[*ICasesResponse*](typedoc_interfaces.icasesresponse.md)\>
-Defined in: [cases/client.ts:67](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/client.ts#L67)
+Defined in: [cases/client.ts:67](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/client.ts#L67)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.casesbyalertidparams.md b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.casesbyalertidparams.md
index 4992ed035721b..257269ca64566 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.casesbyalertidparams.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.casesbyalertidparams.md
@@ -21,7 +21,7 @@ Parameters for finding cases IDs using an alert ID
The alert ID to search for
-Defined in: [cases/get.ts:44](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L44)
+Defined in: [cases/get.ts:44](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L44)
___
@@ -37,4 +37,4 @@ The filtering options when searching for associated cases.
| :------ | :------ |
| `owner` | *undefined* \| *string* \| *string*[] |
-Defined in: [cases/get.ts:48](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L48)
+Defined in: [cases/get.ts:48](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L48)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.getparams.md b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.getparams.md
index a4dfc7301e543..16cc952746818 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.getparams.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_get.getparams.md
@@ -22,7 +22,7 @@ The parameters for retrieving a case
Case ID
-Defined in: [cases/get.ts:145](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L145)
+Defined in: [cases/get.ts:145](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L145)
___
@@ -32,7 +32,7 @@ ___
Whether to include the attachments for a case in the response
-Defined in: [cases/get.ts:149](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L149)
+Defined in: [cases/get.ts:149](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L149)
___
@@ -42,4 +42,4 @@ ___
Whether to include the attachments for all children of a case in the response
-Defined in: [cases/get.ts:153](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L153)
+Defined in: [cases/get.ts:153](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L153)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_push.pushparams.md b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_push.pushparams.md
index 0ed510700af8a..3aa6ee77941a8 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/cases_push.pushparams.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/cases_push.pushparams.md
@@ -21,7 +21,7 @@ Parameters for pushing a case to an external system
The ID of a case
-Defined in: [cases/push.ts:53](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/push.ts#L53)
+Defined in: [cases/push.ts:53](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/push.ts#L53)
___
@@ -31,4 +31,4 @@ ___
The ID of an external system to push to
-Defined in: [cases/push.ts:57](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/push.ts#L57)
+Defined in: [cases/push.ts:57](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/push.ts#L57)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/configure_client.configuresubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/configure_client.configuresubclient.md
index 98a6c3a2fcbbf..f94e7d6c7f49e 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/configure_client.configuresubclient.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/configure_client.configuresubclient.md
@@ -31,7 +31,7 @@ Creates a configuration if one does not already exist. If one exists it is delet
**Returns:** *Promise*<[*ICasesConfigureResponse*](typedoc_interfaces.icasesconfigureresponse.md)\>
-Defined in: [configure/client.ts:98](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/configure/client.ts#L98)
+Defined in: [configure/client.ts:98](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/configure/client.ts#L98)
___
@@ -50,7 +50,7 @@ Retrieves the external connector configuration for a particular case owner.
**Returns:** *Promise*<{} \| [*ICasesConfigureResponse*](typedoc_interfaces.icasesconfigureresponse.md)\>
-Defined in: [configure/client.ts:80](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/configure/client.ts#L80)
+Defined in: [configure/client.ts:80](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/configure/client.ts#L80)
___
@@ -62,7 +62,7 @@ Retrieves the valid external connectors supported by the cases plugin.
**Returns:** *Promise*
-Defined in: [configure/client.ts:84](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/configure/client.ts#L84)
+Defined in: [configure/client.ts:84](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/configure/client.ts#L84)
___
@@ -81,4 +81,4 @@ Updates a particular configuration with new values.
**Returns:** *Promise*<[*ICasesConfigureResponse*](typedoc_interfaces.icasesconfigureresponse.md)\>
-Defined in: [configure/client.ts:91](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/configure/client.ts#L91)
+Defined in: [configure/client.ts:91](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/configure/client.ts#L91)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/stats_client.statssubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/stats_client.statssubclient.md
index cc0f30055597d..9bbf3cdaf015f 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/stats_client.statssubclient.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/stats_client.statssubclient.md
@@ -29,4 +29,4 @@ Retrieves the total number of open, closed, and in-progress cases.
**Returns:** *Promise*<{ `count_closed_cases`: *number* ; `count_in_progress_cases`: *number* ; `count_open_cases`: *number* }\>
-Defined in: [stats/client.ts:34](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/stats/client.ts#L34)
+Defined in: [stats/client.ts:34](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/stats/client.ts#L34)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/sub_cases_client.subcasesclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/sub_cases_client.subcasesclient.md
index 5c0369709c0f0..e3ca5a756f8a7 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/sub_cases_client.subcasesclient.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/sub_cases_client.subcasesclient.md
@@ -31,7 +31,7 @@ Deletes the specified entities and their attachments.
**Returns:** *Promise*
-Defined in: [sub_cases/client.ts:68](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/sub_cases/client.ts#L68)
+Defined in: [sub_cases/client.ts:68](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/sub_cases/client.ts#L68)
___
@@ -49,7 +49,7 @@ Retrieves the sub cases matching the search criteria.
**Returns:** *Promise*<[*ISubCasesFindResponse*](typedoc_interfaces.isubcasesfindresponse.md)\>
-Defined in: [sub_cases/client.ts:72](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/sub_cases/client.ts#L72)
+Defined in: [sub_cases/client.ts:72](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/sub_cases/client.ts#L72)
___
@@ -67,7 +67,7 @@ Retrieves a single sub case.
**Returns:** *Promise*<[*ISubCaseResponse*](typedoc_interfaces.isubcaseresponse.md)\>
-Defined in: [sub_cases/client.ts:76](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/sub_cases/client.ts#L76)
+Defined in: [sub_cases/client.ts:76](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/sub_cases/client.ts#L76)
___
@@ -86,4 +86,4 @@ Updates the specified sub cases to the new values included in the request.
**Returns:** *Promise*<[*ISubCasesResponse*](typedoc_interfaces.isubcasesresponse.md)\>
-Defined in: [sub_cases/client.ts:80](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/sub_cases/client.ts#L80)
+Defined in: [sub_cases/client.ts:80](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/sub_cases/client.ts#L80)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasepostrequest.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasepostrequest.md
index 70533a15fe616..5f9189ea41e48 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasepostrequest.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasepostrequest.md
@@ -29,7 +29,7 @@ the docs are huge.
### connector
-• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none }
+• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { caseId: string \| null; } ; `type`: swimlane }
Inherited from: CasePostRequest.connector
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icaseresponse.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icaseresponse.md
index 5db55e5552473..dc591a508844b 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icaseresponse.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icaseresponse.md
@@ -57,7 +57,7 @@ ___
### comments
-• **comments**: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[]
+• **comments**: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[]
Inherited from: CaseResponse.comments
@@ -65,7 +65,7 @@ ___
### connector
-• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none }
+• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { caseId: string \| null; } ; `type`: swimlane }
Inherited from: CaseResponse.connector
@@ -159,7 +159,7 @@ ___
### subCases
-• **subCases**: *undefined* \| { `status`: CaseStatuses } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { `comments`: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] }[]
+• **subCases**: *undefined* \| { `status`: CaseStatuses } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { `comments`: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] }[]
Inherited from: CaseResponse.subCases
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurepatch.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurepatch.md
index 3854fda03fb6a..9ab5341a2dbc6 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurepatch.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurepatch.md
@@ -30,7 +30,7 @@ ___
### connector
-• **connector**: *undefined* \| { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none }
+• **connector**: *undefined* \| { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { caseId: string \| null; } ; `type`: swimlane }
Inherited from: CasesConfigurePatch.connector
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurerequest.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurerequest.md
index 548e1a5c48f58..0b1c11ac548a6 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurerequest.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigurerequest.md
@@ -30,7 +30,7 @@ ___
### connector
-• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none }
+• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { caseId: string \| null; } ; `type`: swimlane }
Inherited from: CasesConfigureRequest.connector
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigureresponse.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigureresponse.md
index c493a4c6c0f0c..42c7378431c1b 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigureresponse.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesconfigureresponse.md
@@ -38,7 +38,7 @@ ___
### connector
-• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none }
+• **connector**: { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { caseId: string \| null; } ; `type`: swimlane }
Inherited from: CasesConfigureResponse.connector
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesfindresponse.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesfindresponse.md
index 9be5fd5743a8e..06e14d219cde0 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesfindresponse.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasesfindresponse.md
@@ -26,7 +26,7 @@
### cases
-• **cases**: { `connector`: { id: string; name: string; } & { type: ConnectorTypes.jira; fields: { issueType: string \| null; priority: string \| null; parent: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.resilient; fields: { incidentTypes: string[] \| null; severityCode: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.serviceNowITSM; fields: { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.serviceNowSIR; fields: { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.none; fields: null; } ; `description`: *string* ; `owner`: *string* ; `settings`: { syncAlerts: boolean; } ; `status`: CaseStatuses ; `tags`: *string*[] ; `title`: *string* ; `type`: CaseType } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `external_service`: ``null`` \| { connector\_id: string; connector\_name: string; external\_id: string; external\_title: string; external\_url: string; } & { pushed\_at: string; pushed\_by: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; }; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { `comments`: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] ; `subCaseIds`: *undefined* \| *string*[] ; `subCases`: *undefined* \| { `status`: CaseStatuses } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { comments?: ((({ comment: string; type: CommentType.user; owner: string; } & { associationType: AssociationType; created\_at: string; created\_by: { email: string \| null \| undefined; full\_name: string \| ... 1 more ... \| undefined; username: string \| ... 1 more ... \| undefined; }; ... 4 more ...; updated\_by: { ...; } ...[] }[]
+• **cases**: { `connector`: { id: string; name: string; } & { type: ConnectorTypes.jira; fields: { issueType: string \| null; priority: string \| null; parent: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.none; fields: null; } & { id: string; name: string; } & { type: ConnectorTypes.resilient; fields: { incidentTypes: string[] \| null; severityCode: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.serviceNowITSM; fields: { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.serviceNowSIR; fields: { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } \| null; } & { id: string; name: string; } & { type: ConnectorTypes.swimlane; fields: { caseId: string \| null; } \| null; } ; `description`: *string* ; `owner`: *string* ; `settings`: { syncAlerts: boolean; } ; `status`: CaseStatuses ; `tags`: *string*[] ; `title`: *string* ; `type`: CaseType } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `external_service`: ``null`` \| { connector\_id: string; connector\_name: string; external\_id: string; external\_title: string; external\_url: string; } & { pushed\_at: string; pushed\_by: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; }; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { `comments`: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] ; `subCaseIds`: *undefined* \| *string*[] ; `subCases`: *undefined* \| { `status`: CaseStatuses } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { comments?: ((({ comment: string; type: CommentType.user; owner: string; } & { associationType: AssociationType; created\_at: string; created\_by: { email: string \| null \| undefined; full\_name: string \| ... 1 more ... \| undefined; username: string \| ... 1 more ... \| undefined; }; ... 4 more ...; updated\_by: { ...; } ...[] }[]
Inherited from: CasesFindResponse.cases
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasespatchrequest.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasespatchrequest.md
index bfdb3b7315e55..d4747a1836cc4 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasespatchrequest.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icasespatchrequest.md
@@ -20,6 +20,6 @@
### cases
-• **cases**: { `connector`: *undefined* \| { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } ; `description`: *undefined* \| *string* ; `owner`: *undefined* \| *string* ; `settings`: *undefined* \| { `syncAlerts`: *boolean* } ; `status`: *undefined* \| open \| *any*[*any*] \| closed ; `tags`: *undefined* \| *string*[] ; `title`: *undefined* \| *string* ; `type`: *undefined* \| collection \| individual } & { `id`: *string* ; `version`: *string* }[]
+• **cases**: { `connector`: *undefined* \| { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { issueType: string \| null; priority: string \| null; parent: string \| null; } ; `type`: jira } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` ; `type`: none } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { incidentTypes: string[] \| null; severityCode: string \| null; } ; `type`: resilient } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { impact: string \| null; severity: string \| null; urgency: string \| null; category: string \| null; subcategory: string \| null; } ; `type`: serviceNowITSM } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { category: string \| null; destIp: boolean \| null; malwareHash: boolean \| null; malwareUrl: boolean \| null; priority: string \| null; sourceIp: boolean \| null; subcategory: string \| null; } ; `type`: serviceNowSIR } & { `id`: *string* ; `name`: *string* } & { `fields`: ``null`` \| { caseId: string \| null; } ; `type`: swimlane } ; `description`: *undefined* \| *string* ; `owner`: *undefined* \| *string* ; `settings`: *undefined* \| { `syncAlerts`: *boolean* } ; `status`: *undefined* \| open \| *any*[*any*] \| closed ; `tags`: *undefined* \| *string*[] ; `title`: *undefined* \| *string* ; `type`: *undefined* \| collection \| individual } & { `id`: *string* ; `version`: *string* }[]
Inherited from: CasesPatchRequest.cases
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icommentsresponse.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icommentsresponse.md
index d34480b2c633c..4a720e2c6d9be 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icommentsresponse.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.icommentsresponse.md
@@ -23,7 +23,7 @@
### comments
-• **comments**: { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[]
+• **comments**: { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[]
Inherited from: CommentsResponse.comments
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcaseresponse.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcaseresponse.md
index b33b280d2e753..a6bf610f86349 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcaseresponse.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcaseresponse.md
@@ -48,7 +48,7 @@ ___
### comments
-• **comments**: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[]
+• **comments**: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[]
Inherited from: SubCaseResponse.comments
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcasesfindresponse.md b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcasesfindresponse.md
index 35d63126f608a..61fb60a54db00 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcasesfindresponse.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/typedoc_interfaces.isubcasesfindresponse.md
@@ -66,7 +66,7 @@ ___
### subCases
-• **subCases**: { `status`: CaseStatuses } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { `comments`: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] }[]
+• **subCases**: { `status`: CaseStatuses } & { `closed_at`: ``null`` \| *string* ; `closed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `created_at`: *string* ; `created_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `totalAlerts`: *number* ; `totalComment`: *number* ; `version`: *string* } & { `comments`: *undefined* \| { `comment`: *string* ; `owner`: *string* ; `type`: user } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `alertId`: *string* \| *string*[] ; `index`: *string* \| *string*[] ; `owner`: *string* ; `rule`: { id: string \| null; name: string \| null; } ; `type`: alert \| generatedAlert } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* } & { `actions`: { targets: { hostname: string; endpointId: string; }[]; type: string; } ; `comment`: *string* ; `owner`: *string* ; `type`: actions } & { `associationType`: AssociationType ; `created_at`: *string* ; `created_by`: { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `owner`: *string* ; `pushed_at`: ``null`` \| *string* ; `pushed_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } ; `updated_at`: ``null`` \| *string* ; `updated_by`: ``null`` \| { email: string \| null \| undefined; full\_name: string \| null \| undefined; username: string \| null \| undefined; } } & { `id`: *string* ; `version`: *string* }[] }[]
Inherited from: SubCasesFindResponse.subCases
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionget.md b/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionget.md
index 5f0cc89239fd8..1cbebef379dbd 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionget.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionget.md
@@ -21,7 +21,7 @@ Parameters for retrieving user actions for a particular case
The ID of the case
-Defined in: [user_actions/client.ts:19](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/user_actions/client.ts#L19)
+Defined in: [user_actions/client.ts:19](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/user_actions/client.ts#L19)
___
@@ -31,4 +31,4 @@ ___
If specified then a sub case will be used for finding all the user actions
-Defined in: [user_actions/client.ts:23](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/user_actions/client.ts#L23)
+Defined in: [user_actions/client.ts:23](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/user_actions/client.ts#L23)
diff --git a/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionssubclient.md b/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionssubclient.md
index df2641adf5a8c..065f20b4cefcb 100644
--- a/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionssubclient.md
+++ b/x-pack/plugins/cases/docs/cases_client/interfaces/user_actions_client.useractionssubclient.md
@@ -28,4 +28,4 @@ Retrieves all user actions for a particular case.
**Returns:** *Promise*<[*ICaseUserActionsResponse*](typedoc_interfaces.icaseuseractionsresponse.md)\>
-Defined in: [user_actions/client.ts:33](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/user_actions/client.ts#L33)
+Defined in: [user_actions/client.ts:33](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/user_actions/client.ts#L33)
diff --git a/x-pack/plugins/cases/docs/cases_client/modules/cases_get.md b/x-pack/plugins/cases/docs/cases_client/modules/cases_get.md
index d4ca13501294a..4c165866cec47 100644
--- a/x-pack/plugins/cases/docs/cases_client/modules/cases_get.md
+++ b/x-pack/plugins/cases/docs/cases_client/modules/cases_get.md
@@ -31,7 +31,7 @@ Retrieves the reporters from all the cases.
**Returns:** *Promise*
-Defined in: [cases/get.ts:290](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L290)
+Defined in: [cases/get.ts:289](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L289)
___
@@ -50,4 +50,4 @@ Retrieves the tags from all the cases.
**Returns:** *Promise*
-Defined in: [cases/get.ts:240](https://github.com/jonathan-buttner/kibana/blob/b65ed845242/x-pack/plugins/cases/server/client/cases/get.ts#L240)
+Defined in: [cases/get.ts:239](https://github.com/elastic/kibana/blob/a80791aa4cc/x-pack/plugins/cases/server/client/cases/get.ts#L239)
diff --git a/x-pack/plugins/cases/public/components/__mock__/form.ts b/x-pack/plugins/cases/public/components/__mock__/form.ts
index 6d3e8353e630a..aa40ea0421b4c 100644
--- a/x-pack/plugins/cases/public/components/__mock__/form.ts
+++ b/x-pack/plugins/cases/public/components/__mock__/form.ts
@@ -23,6 +23,7 @@ export const mockFormHook = {
setFieldErrors: jest.fn(),
getFields: jest.fn(),
getFormData: jest.fn(),
+ getFieldDefaultValue: jest.fn(),
/* Returns a list of all errors in the form */
getErrors: jest.fn(),
reset: jest.fn(),
@@ -33,7 +34,6 @@ export const mockFormHook = {
__validateFields: jest.fn(),
__updateFormDataAt: jest.fn(),
__readFieldConfigFromSchema: jest.fn(),
- __getFieldDefaultValue: jest.fn(),
};
export const getFormMock = (sampleData: any) => ({
diff --git a/x-pack/plugins/console_extensions/README.md b/x-pack/plugins/console_extensions/README.md
deleted file mode 100644
index 49d83d2888d6b..0000000000000
--- a/x-pack/plugins/console_extensions/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Console extensions
-
-This plugin provides autocomplete definitions of licensed APIs to the OSS Console plugin.
\ No newline at end of file
diff --git a/x-pack/plugins/console_extensions/kibana.json b/x-pack/plugins/console_extensions/kibana.json
deleted file mode 100644
index 9411523d3f6dd..0000000000000
--- a/x-pack/plugins/console_extensions/kibana.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "id": "consoleExtensions",
- "version": "1.0.0",
- "kibanaVersion": "kibana",
- "requiredPlugins": ["console"],
- "server": true,
- "ui": false
-}
diff --git a/x-pack/plugins/console_extensions/server/index.ts b/x-pack/plugins/console_extensions/server/index.ts
deleted file mode 100644
index a03111a487090..0000000000000
--- a/x-pack/plugins/console_extensions/server/index.ts
+++ /dev/null
@@ -1,17 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { PluginInitializerContext, PluginConfigDescriptor } from 'kibana/server';
-
-import { config as configSchema, ConfigType } from './config';
-import { ConsoleExtensionsServerPlugin } from './plugin';
-
-export const plugin = (ctx: PluginInitializerContext) => new ConsoleExtensionsServerPlugin(ctx);
-
-export const config: PluginConfigDescriptor = {
- schema: configSchema,
-};
diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/js/ingest.ts b/x-pack/plugins/console_extensions/server/lib/spec_definitions/js/ingest.ts
deleted file mode 100644
index 36ebfa589b823..0000000000000
--- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/js/ingest.ts
+++ /dev/null
@@ -1,70 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-// NOTE: This is copy-pasted from es_6_0/ingest.js in OSS Console.
-const commonPipelineParams = {
- on_failure: [],
- ignore_failure: {
- __one_of: [false, true],
- },
- if: '',
- tag: '',
-};
-
-// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/enrich-processor.html
-const enrichProcessorDefinition = {
- enrich: {
- __template: {
- policy_name: '',
- field: '',
- target_field: '',
- },
- policy_name: '',
- field: '',
- target_field: '',
- ignore_missing: {
- __one_of: [false, true],
- },
- override: {
- __one_of: [true, false],
- },
- max_matches: 1,
- shape_relation: 'INTERSECTS',
- ...commonPipelineParams,
- },
-};
-
-// Based on https://www.elastic.co/guide/en/elasticsearch/reference/master/inference-processor.html
-const inferenceProcessorDefinition = {
- inference: {
- __template: {
- model_id: '',
- inference_config: {},
- field_mappings: {},
- },
- target_field: '',
- model_id: '',
- field_mappings: {
- __template: {},
- },
- inference_config: {
- regression: {
- __template: {},
- results_field: '',
- },
- classification: {
- __template: {},
- results_field: '',
- num_top_classes: 2,
- top_classes_results_field: '',
- },
- },
- ...commonPipelineParams,
- },
-};
-
-export const processors = [enrichProcessorDefinition, inferenceProcessorDefinition];
diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_expired_data.json b/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_expired_data.json
deleted file mode 100644
index 4afa9e323b030..0000000000000
--- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.delete_expired_data.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "ml.delete_expired_data": {
- "methods": [
- "DELETE"
- ],
- "patterns": [
- "_ml/_delete_expired_data"
- ]
- }
-}
diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.info.json b/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.info.json
deleted file mode 100644
index 51b571776ead9..0000000000000
--- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.info.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "ml.info": {
- "methods": [
- "GET"
- ],
- "patterns": [
- "_ml/info"
- ]
- }
-}
diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_filter.json b/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_filter.json
deleted file mode 100644
index 6d57c433d71f4..0000000000000
--- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_filter.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "ml.put_filter": {
- "methods": [
- "PUT"
- ],
- "patterns": [
- "_ml/filters/{filter_id}"
- ]
- }
-}
diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_trained_model.json b/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_trained_model.json
deleted file mode 100644
index 27d0393be6086..0000000000000
--- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/ml.put_trained_model.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "ml.put_trained_model": {
- "methods": [
- "PUT"
- ],
- "patterns": [
- "_ml/inference/{model_id}"
- ],
- "documentation": "TODO"
- }
-}
diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.delete_job.json b/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.delete_job.json
deleted file mode 100644
index 8ecf617751a51..0000000000000
--- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.delete_job.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "rollup.delete_job": {
- "methods": [
- "DELETE"
- ],
- "patterns": [
- "_rollup/job/{id}"
- ]
- }
-}
diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.put_job.json b/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.put_job.json
deleted file mode 100644
index 7734fd54a1ab1..0000000000000
--- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated/rollup.put_job.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "rollup.put_job": {
- "methods": [
- "PUT"
- ],
- "patterns": [
- "_rollup/job/{id}"
- ]
- }
-}
diff --git a/x-pack/plugins/console_extensions/server/plugin.ts b/x-pack/plugins/console_extensions/server/plugin.ts
deleted file mode 100644
index 9ea3f314296ee..0000000000000
--- a/x-pack/plugins/console_extensions/server/plugin.ts
+++ /dev/null
@@ -1,40 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { join } from 'path';
-import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from 'kibana/server';
-
-import { ConsoleSetup, ConsoleStart } from '../../../../src/plugins/console/server';
-
-import { processors } from './lib/spec_definitions/js';
-
-interface SetupDependencies {
- console: ConsoleSetup;
-}
-
-interface StartDependencies {
- console: ConsoleStart;
-}
-
-const CONSOLE_XPACK_JSON_SPEC_PATH = join(__dirname, 'lib/spec_definitions/json');
-
-export class ConsoleExtensionsServerPlugin implements Plugin {
- log: Logger;
- constructor(private readonly ctx: PluginInitializerContext) {
- this.log = this.ctx.logger.get();
- }
-
- setup(core: CoreSetup, { console: { addExtensionSpecFilePath } }: SetupDependencies) {
- addExtensionSpecFilePath(CONSOLE_XPACK_JSON_SPEC_PATH);
- this.log.debug(`Added extension path to ${CONSOLE_XPACK_JSON_SPEC_PATH}...`);
- }
-
- start(core: CoreStart, { console: { addProcessorDefinition } }: StartDependencies) {
- processors.forEach((processor) => addProcessorDefinition(processor));
- this.log.debug('Added processor definition extensions.');
- }
-}
diff --git a/x-pack/plugins/console_extensions/tsconfig.json b/x-pack/plugins/console_extensions/tsconfig.json
deleted file mode 100644
index 5ad28f230a0bb..0000000000000
--- a/x-pack/plugins/console_extensions/tsconfig.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "extends": "../../../tsconfig.base.json",
- "compilerOptions": {
- "composite": true,
- "outDir": "./target/types",
- "emitDeclarationOnly": true,
- "declaration": true,
- "declarationMap": true
- },
- "include": [
- "server/**/*"
- ],
- "references": [
- { "path": "../../../src/core/tsconfig.json" },
- { "path": "../../../src/plugins/console/tsconfig.json" }
- ]
-}
diff --git a/x-pack/plugins/data_enhanced/server/search/session/check_non_persiseted_sessions.ts b/x-pack/plugins/data_enhanced/server/search/session/check_non_persiseted_sessions.ts
index 8c75ce91cac6a..2115ce85eeb27 100644
--- a/x-pack/plugins/data_enhanced/server/search/session/check_non_persiseted_sessions.ts
+++ b/x-pack/plugins/data_enhanced/server/search/session/check_non_persiseted_sessions.ts
@@ -59,7 +59,11 @@ function checkNonPersistedSessionsPage(
`${SEARCH_SESSIONS_CLEANUP_TASK_TYPE} Found ${nonPersistedSearchSessions.total} sessions, processing ${nonPersistedSearchSessions.saved_objects.length}`
);
- const updatedSessions = await getAllSessionsStatusUpdates(deps, nonPersistedSearchSessions);
+ const updatedSessions = await getAllSessionsStatusUpdates(
+ deps,
+ config,
+ nonPersistedSearchSessions
+ );
const deletedSessionIds: string[] = [];
await Promise.all(
diff --git a/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts b/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts
index 0d51e97952275..3e89383c16d5e 100644
--- a/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts
+++ b/x-pack/plugins/data_enhanced/server/search/session/check_persisted_sessions.ts
@@ -36,7 +36,11 @@ function checkPersistedSessionsPage(
`${SEARCH_SESSIONS_TASK_TYPE} Found ${persistedSearchSessions.total} sessions, processing ${persistedSearchSessions.saved_objects.length}`
);
- const updatedSessions = await getAllSessionsStatusUpdates(deps, persistedSearchSessions);
+ const updatedSessions = await getAllSessionsStatusUpdates(
+ deps,
+ config,
+ persistedSearchSessions
+ );
await bulkUpdateSessions(deps, updatedSessions);
return persistedSearchSessions;
diff --git a/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts b/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts
index e261c324f440f..61d1635dabe1b 100644
--- a/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts
+++ b/x-pack/plugins/data_enhanced/server/search/session/expire_persisted_sessions.ts
@@ -36,7 +36,7 @@ function checkSessionExpirationPage(
`${SEARCH_SESSIONS_EXPIRE_TASK_TYPE} Found ${searchSessions.total} sessions, processing ${searchSessions.saved_objects.length}`
);
- const updatedSessions = await getAllSessionsStatusUpdates(deps, searchSessions);
+ const updatedSessions = await getAllSessionsStatusUpdates(deps, config, searchSessions);
await bulkUpdateSessions(deps, updatedSessions);
return searchSessions;
diff --git a/x-pack/plugins/data_enhanced/server/search/session/get_session_status.test.ts b/x-pack/plugins/data_enhanced/server/search/session/get_session_status.test.ts
index fc86e75297393..c3946e5af16fa 100644
--- a/x-pack/plugins/data_enhanced/server/search/session/get_session_status.test.ts
+++ b/x-pack/plugins/data_enhanced/server/search/session/get_session_status.test.ts
@@ -5,16 +5,21 @@
* 2.0.
*/
-import { SearchStatus } from './types';
+import { SearchSessionsConfig, SearchStatus } from './types';
import { getSessionStatus } from './get_session_status';
import { SearchSessionStatus } from '../../../../../../src/plugins/data/common';
+import moment from 'moment';
describe('getSessionStatus', () => {
+ const mockConfig = ({
+ notTouchedInProgressTimeout: moment.duration(1, 'm'),
+ } as unknown) as SearchSessionsConfig;
test("returns an in_progress status if there's nothing inside the session", () => {
const session: any = {
idMapping: {},
+ touched: moment(),
};
- expect(getSessionStatus(session)).toBe(SearchSessionStatus.IN_PROGRESS);
+ expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.IN_PROGRESS);
});
test("returns an error status if there's at least one error", () => {
@@ -25,7 +30,25 @@ describe('getSessionStatus', () => {
c: { status: SearchStatus.COMPLETE },
},
};
- expect(getSessionStatus(session)).toBe(SearchSessionStatus.ERROR);
+ expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.ERROR);
+ });
+
+ test('expires a empty session after a minute', () => {
+ const session: any = {
+ idMapping: {},
+ touched: moment().subtract(2, 'm'),
+ };
+ expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.EXPIRED);
+ });
+
+ test('doesnt expire a full session after a minute', () => {
+ const session: any = {
+ idMapping: {
+ a: { status: SearchStatus.IN_PROGRESS },
+ },
+ touched: moment().subtract(2, 'm'),
+ };
+ expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.IN_PROGRESS);
});
test('returns a complete status if all are complete', () => {
@@ -36,7 +59,7 @@ describe('getSessionStatus', () => {
c: { status: SearchStatus.COMPLETE },
},
};
- expect(getSessionStatus(session)).toBe(SearchSessionStatus.COMPLETE);
+ expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.COMPLETE);
});
test('returns a running status if some are still running', () => {
@@ -47,6 +70,6 @@ describe('getSessionStatus', () => {
c: { status: SearchStatus.IN_PROGRESS },
},
};
- expect(getSessionStatus(session)).toBe(SearchSessionStatus.IN_PROGRESS);
+ expect(getSessionStatus(session, mockConfig)).toBe(SearchSessionStatus.IN_PROGRESS);
});
});
diff --git a/x-pack/plugins/data_enhanced/server/search/session/get_session_status.ts b/x-pack/plugins/data_enhanced/server/search/session/get_session_status.ts
index 23e02eedc0004..e7ae52b6c88ae 100644
--- a/x-pack/plugins/data_enhanced/server/search/session/get_session_status.ts
+++ b/x-pack/plugins/data_enhanced/server/search/session/get_session_status.ts
@@ -5,16 +5,28 @@
* 2.0.
*/
+import moment from 'moment';
import {
SearchSessionSavedObjectAttributes,
SearchSessionStatus,
} from '../../../../../../src/plugins/data/common/';
-import { SearchStatus } from './types';
+import { SearchSessionsConfig, SearchStatus } from './types';
-export function getSessionStatus(session: SearchSessionSavedObjectAttributes): SearchSessionStatus {
+export function getSessionStatus(
+ session: SearchSessionSavedObjectAttributes,
+ config: SearchSessionsConfig
+): SearchSessionStatus {
const searchStatuses = Object.values(session.idMapping);
+ const curTime = moment();
if (searchStatuses.some((item) => item.status === SearchStatus.ERROR)) {
return SearchSessionStatus.ERROR;
+ } else if (
+ searchStatuses.length === 0 &&
+ curTime.diff(moment(session.touched), 'ms') >
+ moment.duration(config.notTouchedInProgressTimeout).asMilliseconds()
+ ) {
+ // Expire empty sessions that weren't touched for a minute
+ return SearchSessionStatus.EXPIRED;
} else if (
searchStatuses.length > 0 &&
searchStatuses.every((item) => item.status === SearchStatus.COMPLETE)
diff --git a/x-pack/plugins/data_enhanced/server/search/session/update_session_status.test.ts b/x-pack/plugins/data_enhanced/server/search/session/update_session_status.test.ts
index 485a30fd54951..d9e3fa6f8cab3 100644
--- a/x-pack/plugins/data_enhanced/server/search/session/update_session_status.test.ts
+++ b/x-pack/plugins/data_enhanced/server/search/session/update_session_status.test.ts
@@ -21,6 +21,7 @@ import {
describe('bulkUpdateSessions', () => {
let mockClient: any;
+ const mockConfig: any = {};
let savedObjectsClient: jest.Mocked;
const mockLogger: any = {
debug: jest.fn(),
@@ -66,6 +67,7 @@ describe('bulkUpdateSessions', () => {
client: mockClient,
logger: mockLogger,
},
+ mockConfig,
so
);
@@ -105,6 +107,7 @@ describe('bulkUpdateSessions', () => {
client: mockClient,
logger: mockLogger,
},
+ mockConfig,
so
);
@@ -139,6 +142,7 @@ describe('bulkUpdateSessions', () => {
client: mockClient,
logger: mockLogger,
},
+ mockConfig,
so
);
@@ -176,6 +180,7 @@ describe('bulkUpdateSessions', () => {
client: mockClient,
logger: mockLogger,
},
+ mockConfig,
so
);
@@ -219,6 +224,7 @@ describe('bulkUpdateSessions', () => {
client: mockClient,
logger: mockLogger,
},
+ mockConfig,
so
);
diff --git a/x-pack/plugins/data_enhanced/server/search/session/update_session_status.ts b/x-pack/plugins/data_enhanced/server/search/session/update_session_status.ts
index 1c484467bef63..4758e7cb22684 100644
--- a/x-pack/plugins/data_enhanced/server/search/session/update_session_status.ts
+++ b/x-pack/plugins/data_enhanced/server/search/session/update_session_status.ts
@@ -13,11 +13,17 @@ import {
} from '../../../../../../src/plugins/data/common';
import { getSearchStatus } from './get_search_status';
import { getSessionStatus } from './get_session_status';
-import { CheckSearchSessionsDeps, SearchSessionsResponse, SearchStatus } from './types';
+import {
+ CheckSearchSessionsDeps,
+ SearchSessionsConfig,
+ SearchSessionsResponse,
+ SearchStatus,
+} from './types';
import { isSearchSessionExpired } from './utils';
export async function updateSessionStatus(
{ logger, client }: CheckSearchSessionsDeps,
+ config: SearchSessionsConfig,
session: SavedObjectsFindResult
) {
let sessionUpdated = false;
@@ -61,7 +67,7 @@ export async function updateSessionStatus(
// And only then derive the session's status
const sessionStatus = isExpired
? SearchSessionStatus.EXPIRED
- : getSessionStatus(session.attributes);
+ : getSessionStatus(session.attributes, config);
if (sessionStatus !== session.attributes.status) {
const now = new Date().toISOString();
session.attributes.status = sessionStatus;
@@ -79,13 +85,14 @@ export async function updateSessionStatus(
export async function getAllSessionsStatusUpdates(
deps: CheckSearchSessionsDeps,
+ config: SearchSessionsConfig,
searchSessions: SearchSessionsResponse
) {
const updatedSessions = new Array>();
await Promise.all(
searchSessions.saved_objects.map(async (session) => {
- const updated = await updateSessionStatus(deps, session);
+ const updated = await updateSessionStatus(deps, config, session);
if (updated) {
updatedSessions.push(session);
diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx
index ec55eddec2a79..f45c4a89d006c 100644
--- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx
+++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_view.tsx
@@ -68,6 +68,7 @@ import { TimeBuckets } from '../../services/time_buckets';
import { extractSearchData } from '../../utils/saved_search_utils';
import { DataVisualizerIndexPatternManagement } from '../index_pattern_management';
import { ResultLink } from '../../../common/components/results_links';
+import { extractErrorProperties } from '../../utils/error_utils';
interface DataVisualizerPageState {
overallStats: OverallStats;
@@ -371,9 +372,16 @@ export const IndexDataVisualizerView: FC = (dataVi
earliest,
latest
);
+ // Because load overall stats perform queries in batches
+ // there could be multiple errors
+ if (Array.isArray(allStats.errors) && allStats.errors.length > 0) {
+ allStats.errors.forEach((err: any) => {
+ dataLoader.displayError(extractErrorProperties(err));
+ });
+ }
setOverallStats(allStats);
} catch (err) {
- dataLoader.displayError(err);
+ dataLoader.displayError(err.body ?? err);
}
}
diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts
index 4a3c971cc57cd..1b92eaddd1343 100644
--- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts
+++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/data_loader/data_loader.ts
@@ -109,17 +109,17 @@ export class DataLoader {
'The request may have timed out. Try using a smaller sample size or narrowing the time range.',
values: {
index: this._indexPattern.title,
- message: err.message,
+ message: err.error ?? err.message,
},
}),
});
} else {
this._toastNotifications.addError(err, {
- title: i18n.translate('xpack.dataVisualizer.index.errorLoadingDataMessage.', {
- defaultMessage: 'Error loading data in index {index}. {message}',
+ title: i18n.translate('xpack.dataVisualizer.index.errorLoadingDataMessage', {
+ defaultMessage: 'Error loading data in index {index}. {message}.',
values: {
index: this._indexPattern.title,
- message: err.message,
+ message: err.error ?? err.message,
},
}),
});
diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts
new file mode 100644
index 0000000000000..9bb36496a149e
--- /dev/null
+++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts
@@ -0,0 +1,184 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { HttpFetchError } from 'kibana/public';
+import Boom from '@hapi/boom';
+import { isPopulatedObject } from '../../../../common/utils/object_utils';
+
+export interface WrappedError {
+ body: {
+ attributes: {
+ body: EsErrorBody;
+ };
+ message: Boom.Boom;
+ };
+ statusCode: number;
+}
+
+export interface EsErrorRootCause {
+ type: string;
+ reason: string;
+ caused_by?: EsErrorRootCause;
+ script?: string;
+}
+
+export interface EsErrorBody {
+ error: {
+ root_cause?: EsErrorRootCause[];
+ caused_by?: EsErrorRootCause;
+ type: string;
+ reason: string;
+ };
+ status: number;
+}
+
+export interface DVResponseError {
+ statusCode: number;
+ error: string;
+ message: string;
+ attributes?: {
+ body: EsErrorBody;
+ };
+}
+
+export interface ErrorMessage {
+ message: string;
+}
+
+export interface DVErrorObject {
+ causedBy?: string;
+ message: string;
+ statusCode?: number;
+ fullError?: EsErrorBody;
+}
+
+export interface DVHttpFetchError extends HttpFetchError {
+ body: T;
+}
+
+export type ErrorType =
+ | WrappedError
+ | DVHttpFetchError
+ | EsErrorBody
+ | Boom.Boom
+ | string
+ | undefined;
+
+export function isEsErrorBody(error: any): error is EsErrorBody {
+ return error && error.error?.reason !== undefined;
+}
+
+export function isErrorString(error: any): error is string {
+ return typeof error === 'string';
+}
+
+export function isErrorMessage(error: any): error is ErrorMessage {
+ return error && error.message !== undefined && typeof error.message === 'string';
+}
+
+export function isDVResponseError(error: any): error is DVResponseError {
+ return typeof error.body === 'object' && 'message' in error.body;
+}
+
+export function isBoomError(error: any): error is Boom.Boom {
+ return error.isBoom === true;
+}
+
+export function isWrappedError(error: any): error is WrappedError {
+ return error && isBoomError(error.body?.message) === true;
+}
+
+export const extractErrorProperties = (error: ErrorType): DVErrorObject => {
+ // extract properties of the error object from within the response error
+ // coming from Kibana, Elasticsearch, and our own DV messages
+
+ // some responses contain raw es errors as part of a bulk response
+ // e.g. if some jobs fail the action in a bulk request
+
+ if (isEsErrorBody(error)) {
+ return {
+ message: error.error.reason,
+ statusCode: error.status,
+ fullError: error,
+ };
+ }
+
+ if (isErrorString(error)) {
+ return {
+ message: error,
+ };
+ }
+ if (isWrappedError(error)) {
+ return error.body.message?.output?.payload;
+ }
+
+ if (isBoomError(error)) {
+ return {
+ message: error.output.payload.message,
+ statusCode: error.output.payload.statusCode,
+ };
+ }
+
+ if (error?.body === undefined && !error?.message) {
+ return {
+ message: '',
+ };
+ }
+
+ if (typeof error.body === 'string') {
+ return {
+ message: error.body,
+ };
+ }
+
+ if (isDVResponseError(error)) {
+ if (
+ typeof error.body.attributes === 'object' &&
+ typeof error.body.attributes.body?.error?.reason === 'string'
+ ) {
+ const errObj: DVErrorObject = {
+ message: error.body.attributes.body.error.reason,
+ statusCode: error.body.statusCode,
+ fullError: error.body.attributes.body,
+ };
+ if (
+ typeof error.body.attributes.body.error.caused_by === 'object' &&
+ (typeof error.body.attributes.body.error.caused_by?.reason === 'string' ||
+ typeof error.body.attributes.body.error.caused_by?.caused_by?.reason === 'string')
+ ) {
+ errObj.causedBy =
+ error.body.attributes.body.error.caused_by?.caused_by?.reason ||
+ error.body.attributes.body.error.caused_by?.reason;
+ }
+ if (
+ Array.isArray(error.body.attributes.body.error.root_cause) &&
+ typeof error.body.attributes.body.error.root_cause[0] === 'object' &&
+ isPopulatedObject(error.body.attributes.body.error.root_cause[0], ['script'])
+ ) {
+ errObj.causedBy = error.body.attributes.body.error.root_cause[0].script;
+ errObj.message += `: '${error.body.attributes.body.error.root_cause[0].script}'`;
+ }
+ return errObj;
+ } else {
+ return {
+ message: error.body.message,
+ statusCode: error.body.statusCode,
+ };
+ }
+ }
+
+ if (isErrorMessage(error)) {
+ return {
+ message: error.message,
+ };
+ }
+
+ // If all else fail return an empty message instead of JSON.stringify
+ return {
+ message: '',
+ };
+};
diff --git a/x-pack/plugins/data_visualizer/server/models/data_visualizer/data_visualizer.ts b/x-pack/plugins/data_visualizer/server/models/data_visualizer/data_visualizer.ts
index 27c09c889deb7..155cf09ebb8db 100644
--- a/x-pack/plugins/data_visualizer/server/models/data_visualizer/data_visualizer.ts
+++ b/x-pack/plugins/data_visualizer/server/models/data_visualizer/data_visualizer.ts
@@ -31,6 +31,7 @@ import {
getNumericFieldsStats,
getStringFieldsStats,
} from './get_fields_stats';
+import { wrapError } from '../../utils/error_wrapper';
export class DataVisualizer {
private _client: IScopedClusterClient;
@@ -60,6 +61,7 @@ export class DataVisualizer {
aggregatableNotExistsFields: [] as FieldData[],
nonAggregatableExistsFields: [] as FieldData[],
nonAggregatableNotExistsFields: [] as FieldData[],
+ errors: [] as any[],
};
// To avoid checking for the existence of too many aggregatable fields in one request,
@@ -76,49 +78,61 @@ export class DataVisualizer {
await Promise.all(
batches.map(async (fields) => {
- const batchStats = await this.checkAggregatableFieldsExist(
- indexPatternTitle,
- query,
- fields,
- samplerShardSize,
- timeFieldName,
- earliestMs,
- latestMs,
- undefined,
- runtimeMappings
- );
+ try {
+ const batchStats = await this.checkAggregatableFieldsExist(
+ indexPatternTitle,
+ query,
+ fields,
+ samplerShardSize,
+ timeFieldName,
+ earliestMs,
+ latestMs,
+ undefined,
+ runtimeMappings
+ );
- // Total count will be returned with each batch of fields. Just overwrite.
- stats.totalCount = batchStats.totalCount;
+ // Total count will be returned with each batch of fields. Just overwrite.
+ stats.totalCount = batchStats.totalCount;
- // Add to the lists of fields which do and do not exist.
- stats.aggregatableExistsFields.push(...batchStats.aggregatableExistsFields);
- stats.aggregatableNotExistsFields.push(...batchStats.aggregatableNotExistsFields);
+ // Add to the lists of fields which do and do not exist.
+ stats.aggregatableExistsFields.push(...batchStats.aggregatableExistsFields);
+ stats.aggregatableNotExistsFields.push(...batchStats.aggregatableNotExistsFields);
+ } catch (e) {
+ // If index not found, no need to proceed with other batches
+ if (e.statusCode === 404) {
+ throw e;
+ }
+ stats.errors.push(wrapError(e));
+ }
})
);
await Promise.all(
nonAggregatableFields.map(async (field) => {
- const existsInDocs = await this.checkNonAggregatableFieldExists(
- indexPatternTitle,
- query,
- field,
- timeFieldName,
- earliestMs,
- latestMs,
- runtimeMappings
- );
+ try {
+ const existsInDocs = await this.checkNonAggregatableFieldExists(
+ indexPatternTitle,
+ query,
+ field,
+ timeFieldName,
+ earliestMs,
+ latestMs,
+ runtimeMappings
+ );
- const fieldData: FieldData = {
- fieldName: field,
- existsInDocs,
- stats: {},
- };
+ const fieldData: FieldData = {
+ fieldName: field,
+ existsInDocs,
+ stats: {},
+ };
- if (existsInDocs === true) {
- stats.nonAggregatableExistsFields.push(fieldData);
- } else {
- stats.nonAggregatableNotExistsFields.push(fieldData);
+ if (existsInDocs === true) {
+ stats.nonAggregatableExistsFields.push(fieldData);
+ } else {
+ stats.nonAggregatableNotExistsFields.push(fieldData);
+ }
+ } catch (e) {
+ stats.errors.push(wrapError(e));
}
})
);
diff --git a/x-pack/plugins/data_visualizer/server/plugin.ts b/x-pack/plugins/data_visualizer/server/plugin.ts
index 4ae695b05b81f..9db580959b116 100644
--- a/x-pack/plugins/data_visualizer/server/plugin.ts
+++ b/x-pack/plugins/data_visualizer/server/plugin.ts
@@ -12,7 +12,7 @@ import { dataVisualizerRoutes } from './routes';
export class DataVisualizerPlugin implements Plugin {
constructor() {}
- async setup(coreSetup: CoreSetup, plugins: SetupDeps) {
+ setup(coreSetup: CoreSetup, plugins: SetupDeps) {
dataVisualizerRoutes(coreSetup);
}
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.tsx
index 1e6e8ec6c5e49..4c22a234e731c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/account_header/account_header.tsx
@@ -68,11 +68,11 @@ export const AccountHeader: React.FC = () => {
return (
-
+
{WORKPLACE_SEARCH_TITLE}
-
+
{ACCOUNT_NAV.SOURCES}
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts
index fcaa847c47f3e..09ba41f81d76a 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.test.ts
@@ -20,8 +20,14 @@ jest.mock('../../../../app_logic', () => ({
}));
import { AppLogic } from '../../../../app_logic';
-import { ADD_GITHUB_PATH, SOURCES_PATH, getSourcesPath } from '../../../../routes';
+import {
+ ADD_GITHUB_PATH,
+ SOURCES_PATH,
+ PERSONAL_SOURCES_PATH,
+ getSourcesPath,
+} from '../../../../routes';
import { CustomSource } from '../../../../types';
+import { PERSONAL_DASHBOARD_SOURCE_ERROR } from '../../constants';
import { SourcesLogic } from '../../sources_logic';
import {
@@ -36,7 +42,7 @@ describe('AddSourceLogic', () => {
const { mount } = new LogicMounter(AddSourceLogic);
const { http } = mockHttpValues;
const { navigateToUrl } = mockKibanaValues;
- const { clearFlashMessages, flashAPIErrors } = mockFlashMessageHelpers;
+ const { clearFlashMessages, flashAPIErrors, setErrorMessage } = mockFlashMessageHelpers;
const defaultValues = {
addSourceCurrentStep: AddSourceSteps.ConfigIntroStep,
@@ -353,6 +359,33 @@ describe('AddSourceLogic', () => {
expect(navigateToUrl).toHaveBeenCalledWith(`${ADD_GITHUB_PATH}/configure${queryString}`);
});
+ describe('Github error edge case', () => {
+ const getGithubQueryString = (context: 'organization' | 'account') =>
+ `?error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application.&error_uri=https%3A%2F%2Fdocs.github.com%2Fapps%2Fmanaging-oauth-apps%2Ftroubleshooting-authorization-request-errors%2F%23redirect-uri-mismatch&state=%7B%22action%22%3A%22create%22%2C%22context%22%3A%22${context}%22%2C%22service_type%22%3A%22github%22%2C%22csrf_token%22%3A%22TOKEN%3D%3D%22%2C%22index_permissions%22%3Afalse%7D`;
+
+ it('handles "organization" redirect and displays error', () => {
+ const githubQueryString = getGithubQueryString('organization');
+ AddSourceLogic.actions.saveSourceParams(githubQueryString);
+
+ expect(navigateToUrl).toHaveBeenCalledWith('/');
+ expect(setErrorMessage).toHaveBeenCalledWith(
+ 'The redirect_uri MUST match the registered callback URL for this application.'
+ );
+ });
+
+ it('handles "account" redirect and displays error', () => {
+ const githubQueryString = getGithubQueryString('account');
+ AddSourceLogic.actions.saveSourceParams(githubQueryString);
+
+ expect(navigateToUrl).toHaveBeenCalledWith(PERSONAL_SOURCES_PATH);
+ expect(setErrorMessage).toHaveBeenCalledWith(
+ PERSONAL_DASHBOARD_SOURCE_ERROR(
+ 'The redirect_uri MUST match the registered callback URL for this application.'
+ )
+ );
+ });
+ });
+
it('handles error', async () => {
http.get.mockReturnValue(Promise.reject('this is an error'));
diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts
index 0bd37aed81c32..81e27f07293dc 100644
--- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/add_source/add_source_logic.ts
@@ -16,14 +16,21 @@ import {
flashAPIErrors,
setSuccessMessage,
clearFlashMessages,
+ setErrorMessage,
} from '../../../../../shared/flash_messages';
import { HttpLogic } from '../../../../../shared/http';
import { KibanaLogic } from '../../../../../shared/kibana';
import { parseQueryParams } from '../../../../../shared/query_params';
import { AppLogic } from '../../../../app_logic';
import { CUSTOM_SERVICE_TYPE, WORKPLACE_SEARCH_URL_PREFIX } from '../../../../constants';
-import { SOURCES_PATH, ADD_GITHUB_PATH, getSourcesPath } from '../../../../routes';
+import {
+ SOURCES_PATH,
+ ADD_GITHUB_PATH,
+ PERSONAL_SOURCES_PATH,
+ getSourcesPath,
+} from '../../../../routes';
import { CustomSource } from '../../../../types';
+import { PERSONAL_DASHBOARD_SOURCE_ERROR } from '../../constants';
import { staticSourceData } from '../../source_data';
import { SourcesLogic } from '../../sources_logic';
@@ -50,6 +57,8 @@ export interface OauthParams {
state: string;
session_state: string;
oauth_verifier?: string;
+ error?: string;
+ error_description?: string;
}
export interface AddSourceActions {
@@ -501,6 +510,22 @@ export const AddSourceLogic = kea
+ i18n.translate('xpack.enterpriseSearch.workplaceSearch.personalDashboardSourceError', {
+ defaultMessage:
+ 'Could not connect the source, reach out to your admin for help. Error message: {error}',
+ values: { error },
+ });
diff --git a/x-pack/plugins/file_upload/server/plugin.ts b/x-pack/plugins/file_upload/server/plugin.ts
index c729afec92f94..bd5eebe372a75 100644
--- a/x-pack/plugins/file_upload/server/plugin.ts
+++ b/x-pack/plugins/file_upload/server/plugin.ts
@@ -21,7 +21,7 @@ export class FileUploadPlugin implements Plugin {
this._logger = initializerContext.logger.get();
}
- async setup(coreSetup: CoreSetup, plugins: SetupDeps) {
+ setup(coreSetup: CoreSetup, plugins: SetupDeps) {
fileUploadRoutes(coreSetup, this._logger);
setupCapabilities(coreSetup);
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_cloud_instructions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_cloud_instructions.tsx
index 3b9d297f37df2..88590ce3ce504 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_cloud_instructions.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_cloud_instructions.tsx
@@ -8,7 +8,6 @@
import React, { useEffect } from 'react';
import {
EuiButton,
- EuiPanel,
EuiLink,
EuiEmptyPrompt,
EuiFlexItem,
@@ -39,62 +38,54 @@ export const CloudInstructions: React.FC<{ deploymentUrl: string }> = ({ deploym
return (
<>
-
-
-
-
- }
- body={
+
-
-
- ),
- }}
+ id="xpack.fleet.fleetServerSetup.cloudSetupTitle"
+ defaultMessage="Enable APM & Fleet"
/>
- }
- actions={
- <>
-
-
-
- >
- }
- />
-
+
+ }
+ body={
+
+
+
+ ),
+ }}
+ />
+ }
+ actions={
+ <>
+
+
+
+ >
+ }
+ />
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx
index 0fc3821d2e3f7..1d43f90b80def 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/fleet_server_on_prem_instructions.tsx
@@ -10,7 +10,6 @@ import {
EuiButton,
EuiFlexGroup,
EuiFlexItem,
- EuiPanel,
EuiSpacer,
EuiText,
EuiLink,
@@ -737,9 +736,8 @@ export const OnPremInstructions: React.FC = () => {
}, [notifications.toasts]);
return (
-
-
-
+ <>
+
{
: CompleteStep(),
]}
/>
-
+ >
);
};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/es_requirements_page.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/es_requirements_page.tsx
index b4e6f1007536f..9c5ec12645c1d 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/es_requirements_page.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/es_requirements_page.tsx
@@ -72,7 +72,8 @@ export const MissingESRequirementsPage: React.FunctionComponent<{
elasticsearch.yml }}
/>
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx
index c79263093abeb..28332961f37a2 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/fleet_server_requirement_page.tsx
@@ -21,7 +21,6 @@ const FlexItemWithMinWidth = styled(EuiFlexItem)`
const ContentWrapper = styled(EuiFlexGroup)`
height: 100%;
margin: 0 auto;
- max-width: 800px;
`;
export const FleetServerRequirementPage = () => {
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_unenroll_modal/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_unenroll_modal/index.tsx
index 0b13fcc9c72be..fae88fcda4bc8 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_unenroll_modal/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_unenroll_modal/index.tsx
@@ -137,7 +137,7 @@ export const AgentUnenrollAgentModal: React.FunctionComponent = ({
diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/markdown_renderers.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/markdown_renderers.tsx
index cbc2f7b5f7888..2cbdfe3671c4e 100644
--- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/markdown_renderers.tsx
+++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/markdown_renderers.tsx
@@ -24,6 +24,12 @@ const REL_NOFOLLOW = 'nofollow';
/** prevents the browser from sending the current address as referrer via the Referer HTTP header */
const REL_NOREFERRER = 'noreferrer';
+// Maps deprecated code block languages to supported ones in prism.js
+const CODE_LANGUAGE_OVERRIDES: Record = {
+ $json: 'json',
+ $yml: 'yml',
+};
+
export const markdownRenderers = {
root: ({ children }: { children: React.ReactNode[] }) => (
{children}
@@ -60,8 +66,17 @@ export const markdownRenderers = {
),
code: ({ language, value }: { language: string; value: string }) => {
- // Old packages are using `$json`, which is not valid any more with the move to prism.js
- const parsedLang = language === '$json' ? 'json' : language;
+ let parsedLang = language;
+
+ // Some integrations export code block content that includes language tags that have since
+ // been removed or deprecated in `prism.js`, the upstream depedency that handles syntax highlighting
+ // in EuiCodeBlock components
+ const languageOverride = CODE_LANGUAGE_OVERRIDES[language];
+
+ if (languageOverride) {
+ parsedLang = languageOverride;
+ }
+
return (
{value}
diff --git a/x-pack/plugins/console_extensions/server/lib/spec_definitions/js/index.ts b/x-pack/plugins/infra/common/alerting/logs/log_threshold/index.ts
similarity index 82%
rename from x-pack/plugins/console_extensions/server/lib/spec_definitions/js/index.ts
rename to x-pack/plugins/infra/common/alerting/logs/log_threshold/index.ts
index ef5a474df32d5..3f4cbc82c405c 100644
--- a/x-pack/plugins/console_extensions/server/lib/spec_definitions/js/index.ts
+++ b/x-pack/plugins/infra/common/alerting/logs/log_threshold/index.ts
@@ -5,4 +5,5 @@
* 2.0.
*/
-export { processors } from './ingest';
+export * from './rule_data';
+export * from './types';
diff --git a/x-pack/plugins/infra/common/alerting/logs/log_threshold/rule_data.ts b/x-pack/plugins/infra/common/alerting/logs/log_threshold/rule_data.ts
new file mode 100644
index 0000000000000..dd60739289756
--- /dev/null
+++ b/x-pack/plugins/infra/common/alerting/logs/log_threshold/rule_data.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { jsonRt } from '@kbn/io-ts-utils/target/json_rt';
+import * as rt from 'io-ts';
+import { alertParamsRT as logThresholdAlertParamsRT } from './types';
+
+export const serializedParamsKey = 'serialized_params';
+
+export const logThresholdRuleDataNamespace = 'log_threshold_rule';
+export const logThresholdRuleDataSerializedParamsKey = `${logThresholdRuleDataNamespace}.${serializedParamsKey}` as const;
+
+export const logThresholdRuleDataRT = rt.type({
+ [logThresholdRuleDataSerializedParamsKey]: rt.array(jsonRt.pipe(logThresholdAlertParamsRT)),
+});
diff --git a/x-pack/plugins/infra/common/constants.ts b/x-pack/plugins/infra/common/constants.ts
index 9362293fce82f..1c3aa550f2f62 100644
--- a/x-pack/plugins/infra/common/constants.ts
+++ b/x-pack/plugins/infra/common/constants.ts
@@ -11,3 +11,8 @@ export const LOGS_INDEX_PATTERN = 'logs-*,filebeat-*,kibana_sample_data_logs*';
export const TIMESTAMP_FIELD = '@timestamp';
export const METRICS_APP = 'metrics';
export const LOGS_APP = 'logs';
+
+export const METRICS_FEATURE_ID = 'infrastructure';
+export const LOGS_FEATURE_ID = 'logs';
+
+export type InfraFeatureId = typeof METRICS_FEATURE_ID | typeof LOGS_FEATURE_ID;
diff --git a/x-pack/plugins/infra/kibana.json b/x-pack/plugins/infra/kibana.json
index ec1b11c90f7a3..981036114282e 100644
--- a/x-pack/plugins/infra/kibana.json
+++ b/x-pack/plugins/infra/kibana.json
@@ -12,7 +12,8 @@
"visTypeTimeseries",
"alerting",
"triggersActionsUi",
- "observability"
+ "observability",
+ "ruleRegistry"
],
"optionalPlugins": ["ml", "home", "embeddable"],
"server": true,
diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/index.ts b/x-pack/plugins/infra/public/alerting/log_threshold/index.ts
index 5bd64de2f3ac2..0f2746b446927 100644
--- a/x-pack/plugins/infra/public/alerting/log_threshold/index.ts
+++ b/x-pack/plugins/infra/public/alerting/log_threshold/index.ts
@@ -5,5 +5,5 @@
* 2.0.
*/
-export { getAlertType } from './log_threshold_alert_type';
+export * from './log_threshold_alert_type';
export { AlertDropdown } from './components/alert_dropdown';
diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts b/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts
index 2c8a6a7ea286a..44097fd005cc7 100644
--- a/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts
+++ b/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts
@@ -7,14 +7,15 @@
import { i18n } from '@kbn/i18n';
import React from 'react';
-import { AlertTypeModel } from '../../../../triggers_actions_ui/public';
+import { ObservabilityRuleTypeModel } from '../../../../observability/public';
import {
LOG_DOCUMENT_COUNT_ALERT_TYPE_ID,
PartialAlertParams,
} from '../../../common/alerting/logs/log_threshold/types';
+import { formatReason } from './rule_data_formatters';
import { validateExpression } from './validation';
-export function getAlertType(): AlertTypeModel {
+export function createLogThresholdAlertType(): ObservabilityRuleTypeModel {
return {
id: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID,
description: i18n.translate('xpack.infra.logs.alertFlyout.alertDescription', {
@@ -33,5 +34,6 @@ export function getAlertType(): AlertTypeModel {
}
),
requiresAppContext: false,
+ format: formatReason,
};
}
diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/rule_data_formatters.ts b/x-pack/plugins/infra/public/alerting/log_threshold/rule_data_formatters.ts
new file mode 100644
index 0000000000000..6ca081ffbc5ef
--- /dev/null
+++ b/x-pack/plugins/infra/public/alerting/log_threshold/rule_data_formatters.ts
@@ -0,0 +1,87 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+import {
+ ALERT_EVALUATION_THRESHOLD,
+ ALERT_EVALUATION_VALUE,
+ ALERT_ID,
+ ALERT_START,
+} from '@kbn/rule-data-utils';
+import { modifyUrl } from '@kbn/std';
+import { fold } from 'fp-ts/lib/Either';
+import { pipe } from 'fp-ts/lib/function';
+import { ObservabilityRuleTypeFormatter } from '../../../../observability/public';
+import {
+ ComparatorToi18nMap,
+ logThresholdRuleDataRT,
+ logThresholdRuleDataSerializedParamsKey,
+ ratioAlertParamsRT,
+} from '../../../common/alerting/logs/log_threshold';
+
+export const formatReason: ObservabilityRuleTypeFormatter = ({ fields }) => {
+ const reason = pipe(
+ logThresholdRuleDataRT.decode(fields),
+ fold(
+ () =>
+ i18n.translate('xpack.infra.logs.alerting.threshold.unknownReasonDescription', {
+ defaultMessage: 'unknown reason',
+ }),
+ (logThresholdRuleData) => {
+ const params = logThresholdRuleData[logThresholdRuleDataSerializedParamsKey][0];
+
+ const actualCount = fields[ALERT_EVALUATION_VALUE];
+ const groupName = fields[ALERT_ID];
+ const isGrouped = (params.groupBy?.length ?? 0) > 0;
+ const isRatio = ratioAlertParamsRT.is(params);
+ const thresholdCount = fields[ALERT_EVALUATION_THRESHOLD];
+ const translatedComparator = ComparatorToi18nMap[params.count.comparator];
+
+ if (isRatio) {
+ return i18n.translate('xpack.infra.logs.alerting.threshold.ratioAlertReasonDescription', {
+ defaultMessage:
+ '{isGrouped, select, true{{groupName}: } false{}}The log entries ratio is {actualCount} ({translatedComparator} {thresholdCount}).',
+ values: {
+ actualCount,
+ translatedComparator,
+ groupName,
+ isGrouped,
+ thresholdCount,
+ },
+ });
+ } else {
+ return i18n.translate('xpack.infra.logs.alerting.threshold.countAlertReasonDescription', {
+ defaultMessage:
+ '{isGrouped, select, true{{groupName}: } false{}}{actualCount, plural, one {{actualCount} log entry} other {{actualCount} log entries} } ({translatedComparator} {thresholdCount}) match the configured conditions.',
+ values: {
+ actualCount,
+ translatedComparator,
+ groupName,
+ isGrouped,
+ thresholdCount,
+ },
+ });
+ }
+ }
+ )
+ );
+
+ const alertStartDate = fields[ALERT_START];
+ const timestamp = alertStartDate != null ? new Date(alertStartDate).valueOf() : null;
+ const link = modifyUrl('/app/logs/link-to/default/logs', ({ query, ...otherUrlParts }) => ({
+ ...otherUrlParts,
+ query: {
+ ...query,
+ ...(timestamp != null ? { time: `${timestamp}` } : {}),
+ },
+ }));
+
+ return {
+ reason,
+ link, // TODO: refactor to URL generators
+ };
+};
diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts
index fd599aed5f890..d5951d9ec9915 100644
--- a/x-pack/plugins/infra/public/plugin.ts
+++ b/x-pack/plugins/infra/public/plugin.ts
@@ -10,21 +10,21 @@ import { AppMountParameters, PluginInitializerContext } from 'kibana/public';
import { from } from 'rxjs';
import { map } from 'rxjs/operators';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
-import { createMetricThresholdAlertType } from './alerting/metric_threshold';
import { createInventoryMetricAlertType } from './alerting/inventory';
-import { getAlertType as getLogsAlertType } from './alerting/log_threshold';
+import { createLogThresholdAlertType } from './alerting/log_threshold';
+import { createMetricThresholdAlertType } from './alerting/metric_threshold';
+import { LOG_STREAM_EMBEDDABLE } from './components/log_stream/log_stream_embeddable';
+import { LogStreamEmbeddableFactoryDefinition } from './components/log_stream/log_stream_embeddable_factory';
+import { createMetricsFetchData, createMetricsHasData } from './metrics_overview_fetchers';
import { registerFeatures } from './register_feature';
import {
- InfraClientSetupDeps,
- InfraClientStartDeps,
InfraClientCoreSetup,
InfraClientCoreStart,
InfraClientPluginClass,
+ InfraClientSetupDeps,
+ InfraClientStartDeps,
} from './types';
import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './utils/logs_overview_fetchers';
-import { createMetricsHasData, createMetricsFetchData } from './metrics_overview_fetchers';
-import { LOG_STREAM_EMBEDDABLE } from './components/log_stream/log_stream_embeddable';
-import { LogStreamEmbeddableFactoryDefinition } from './components/log_stream/log_stream_embeddable_factory';
export class Plugin implements InfraClientPluginClass {
constructor(_context: PluginInitializerContext) {}
@@ -35,7 +35,9 @@ export class Plugin implements InfraClientPluginClass {
}
pluginsSetup.triggersActionsUi.alertTypeRegistry.register(createInventoryMetricAlertType());
- pluginsSetup.triggersActionsUi.alertTypeRegistry.register(getLogsAlertType());
+ pluginsSetup.observability.observabilityRuleTypeRegistry.register(
+ createLogThresholdAlertType()
+ );
pluginsSetup.triggersActionsUi.alertTypeRegistry.register(createMetricThresholdAlertType());
pluginsSetup.observability.dashboard.register({
diff --git a/x-pack/plugins/infra/server/features.ts b/x-pack/plugins/infra/server/features.ts
index 91f82e82b33cd..fe0570c4950f8 100644
--- a/x-pack/plugins/infra/server/features.ts
+++ b/x-pack/plugins/infra/server/features.ts
@@ -10,9 +10,10 @@ import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../common/alerting/logs/log_th
import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from './lib/alerting/inventory_metric_threshold/types';
import { METRIC_THRESHOLD_ALERT_TYPE_ID } from './lib/alerting/metric_threshold/types';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server';
+import { LOGS_FEATURE_ID, METRICS_FEATURE_ID } from '../common/constants';
export const METRICS_FEATURE = {
- id: 'infrastructure',
+ id: METRICS_FEATURE_ID,
name: i18n.translate('xpack.infra.featureRegistry.linkInfrastructureTitle', {
defaultMessage: 'Metrics',
}),
@@ -71,7 +72,7 @@ export const METRICS_FEATURE = {
};
export const LOGS_FEATURE = {
- id: 'logs',
+ id: LOGS_FEATURE_ID,
name: i18n.translate('xpack.infra.featureRegistry.linkLogsTitle', {
defaultMessage: 'Logs',
}),
diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts
index 1657d41d0b793..0ec071b97d7cf 100644
--- a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts
+++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts
@@ -20,6 +20,7 @@ import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../pl
import { SpacesPluginSetup } from '../../../../../../plugins/spaces/server';
import { PluginSetupContract as AlertingPluginContract } from '../../../../../alerting/server';
import { MlPluginSetup } from '../../../../../ml/server';
+import { RuleRegistryPluginSetupContract } from '../../../../../rule_registry/server';
export interface InfraServerPluginSetupDeps {
data: DataPluginSetup;
@@ -29,6 +30,7 @@ export interface InfraServerPluginSetupDeps {
visTypeTimeseries: VisTypeTimeseriesSetup;
features: FeaturesPluginSetup;
alerting: AlertingPluginContract;
+ ruleRegistry: RuleRegistryPluginSetupContract;
ml?: MlPluginSetup;
}
diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts
index f9d0b5575abfc..241931e610af0 100644
--- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts
+++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts
@@ -5,59 +5,64 @@
* 2.0.
*/
+import { estypes } from '@elastic/elasticsearch';
import { i18n } from '@kbn/i18n';
+import { ALERT_EVALUATION_THRESHOLD, ALERT_EVALUATION_VALUE } from '@kbn/rule-data-utils';
import { ElasticsearchClient } from 'kibana/server';
-import { estypes } from '@elastic/elasticsearch';
import {
- AlertExecutorOptions,
- AlertServices,
+ ActionGroup,
+ ActionGroupIdsOf,
AlertInstance,
- AlertTypeParams,
- AlertTypeState,
AlertInstanceContext,
AlertInstanceState,
- ActionGroup,
- ActionGroupIdsOf,
+ AlertTypeState,
} from '../../../../../alerting/server';
import {
+ logThresholdRuleDataRT,
+ logThresholdRuleDataSerializedParamsKey,
+} from '../../../../common/alerting/logs/log_threshold';
+import {
+ AlertParams,
+ alertParamsRT,
AlertStates,
Comparator,
- AlertParams,
+ CountAlertParams,
+ CountCriteria,
Criterion,
- GroupedSearchQueryResponseRT,
- UngroupedSearchQueryResponseRT,
- UngroupedSearchQueryResponse,
+ getDenominator,
+ getNumerator,
GroupedSearchQueryResponse,
- alertParamsRT,
- isRatioAlertParams,
+ GroupedSearchQueryResponseRT,
hasGroupBy,
- getNumerator,
- getDenominator,
- CountCriteria,
- CountAlertParams,
- RatioAlertParams,
- isOptimizedGroupedSearchQueryResponse,
isOptimizableGroupedThreshold,
+ isOptimizedGroupedSearchQueryResponse,
+ isRatioAlertParams,
+ RatioAlertParams,
+ UngroupedSearchQueryResponse,
+ UngroupedSearchQueryResponseRT,
} from '../../../../common/alerting/logs/log_threshold/types';
-import { InfraBackendLibs } from '../../infra_types';
-import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds';
+import { resolveLogSourceConfiguration } from '../../../../common/log_sources';
import { decodeOrThrow } from '../../../../common/runtime_types';
+import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds';
+import { InfraBackendLibs } from '../../infra_types';
import { UNGROUPED_FACTORY_KEY } from '../common/utils';
-import { resolveLogSourceConfiguration } from '../../../../common/log_sources';
-type LogThresholdActionGroups = ActionGroupIdsOf;
-type LogThresholdAlertServices = AlertServices<
- AlertInstanceState,
- AlertInstanceContext,
- LogThresholdActionGroups
->;
-type LogThresholdAlertExecutorOptions = AlertExecutorOptions<
- AlertTypeParams,
- AlertTypeState,
- AlertInstanceState,
- AlertInstanceContext,
+export type LogThresholdActionGroups = ActionGroupIdsOf;
+export type LogThresholdAlertTypeParams = AlertParams;
+export type LogThresholdAlertTypeState = AlertTypeState; // no specific state used
+export type LogThresholdAlertInstanceState = AlertInstanceState; // no specific state used
+export type LogThresholdAlertInstanceContext = AlertInstanceContext; // no specific instance context used
+
+type LogThresholdAlertInstance = AlertInstance<
+ LogThresholdAlertInstanceState,
+ LogThresholdAlertInstanceContext,
LogThresholdActionGroups
>;
+type LogThresholdAlertInstanceFactory = (
+ id: string,
+ threshold: number,
+ value: number
+) => LogThresholdAlertInstance;
const COMPOSITE_GROUP_SIZE = 2000;
@@ -75,9 +80,26 @@ const checkValueAgainstComparatorMap: {
// With forks for group_by vs ungrouped, and ratio vs non-ratio.
export const createLogThresholdExecutor = (libs: InfraBackendLibs) =>
- async function ({ services, params }: LogThresholdAlertExecutorOptions) {
- const { alertInstanceFactory, savedObjectsClient, scopedClusterClient } = services;
+ libs.logsRules.createLifecycleRuleExecutor<
+ LogThresholdAlertTypeParams,
+ LogThresholdAlertTypeState,
+ LogThresholdAlertInstanceState,
+ LogThresholdAlertInstanceContext,
+ LogThresholdActionGroups
+ >(async ({ services, params }) => {
+ const { alertWithLifecycle, savedObjectsClient, scopedClusterClient } = services;
const { sources } = libs;
+ const alertInstanceFactory: LogThresholdAlertInstanceFactory = (id, threshold, value) =>
+ alertWithLifecycle({
+ id,
+ fields: {
+ [ALERT_EVALUATION_THRESHOLD]: threshold,
+ [ALERT_EVALUATION_VALUE]: value,
+ ...logThresholdRuleDataRT.encode({
+ [logThresholdRuleDataSerializedParamsKey]: [params],
+ }),
+ },
+ });
const sourceConfiguration = await sources.getSourceConfiguration(savedObjectsClient, 'default');
const { indices, timestampField, runtimeMappings } = await resolveLogSourceConfiguration(
@@ -113,7 +135,7 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) =>
} catch (e) {
throw new Error(e);
}
- };
+ });
async function executeAlert(
alertParams: CountAlertParams,
@@ -121,7 +143,7 @@ async function executeAlert(
indexPattern: string,
runtimeMappings: estypes.MappingRuntimeFields,
esClient: ElasticsearchClient,
- alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory']
+ alertInstanceFactory: LogThresholdAlertInstanceFactory
) {
const query = getESQuery(alertParams, timestampField, indexPattern, runtimeMappings);
@@ -152,7 +174,7 @@ async function executeRatioAlert(
indexPattern: string,
runtimeMappings: estypes.MappingRuntimeFields,
esClient: ElasticsearchClient,
- alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory']
+ alertInstanceFactory: LogThresholdAlertInstanceFactory
) {
// Ratio alert params are separated out into two standard sets of alert params
const numeratorParams: AlertParams = {
@@ -214,14 +236,14 @@ const getESQuery = (
export const processUngroupedResults = (
results: UngroupedSearchQueryResponse,
params: CountAlertParams,
- alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'],
+ alertInstanceFactory: LogThresholdAlertInstanceFactory,
alertInstaceUpdater: AlertInstanceUpdater
) => {
const { count, criteria } = params;
const documentCount = results.hits.total.value;
if (checkValueAgainstComparatorMap[count.comparator](documentCount, count.value)) {
- const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY);
+ const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY, count.value, documentCount);
alertInstaceUpdater(alertInstance, AlertStates.ALERT, [
{
actionGroup: FIRED_ACTIONS.id,
@@ -240,7 +262,7 @@ export const processUngroupedRatioResults = (
numeratorResults: UngroupedSearchQueryResponse,
denominatorResults: UngroupedSearchQueryResponse,
params: RatioAlertParams,
- alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'],
+ alertInstanceFactory: LogThresholdAlertInstanceFactory,
alertInstaceUpdater: AlertInstanceUpdater
) => {
const { count, criteria } = params;
@@ -250,7 +272,7 @@ export const processUngroupedRatioResults = (
const ratio = getRatio(numeratorCount, denominatorCount);
if (ratio !== undefined && checkValueAgainstComparatorMap[count.comparator](ratio, count.value)) {
- const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY);
+ const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY, count.value, ratio);
alertInstaceUpdater(alertInstance, AlertStates.ALERT, [
{
actionGroup: FIRED_ACTIONS.id,
@@ -308,7 +330,7 @@ const getReducedGroupByResults = (
export const processGroupByResults = (
results: GroupedSearchQueryResponse['aggregations']['groups']['buckets'],
params: CountAlertParams,
- alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'],
+ alertInstanceFactory: LogThresholdAlertInstanceFactory,
alertInstaceUpdater: AlertInstanceUpdater
) => {
const { count, criteria } = params;
@@ -319,7 +341,7 @@ export const processGroupByResults = (
const documentCount = group.documentCount;
if (checkValueAgainstComparatorMap[count.comparator](documentCount, count.value)) {
- const alertInstance = alertInstanceFactory(group.name);
+ const alertInstance = alertInstanceFactory(group.name, count.value, documentCount);
alertInstaceUpdater(alertInstance, AlertStates.ALERT, [
{
actionGroup: FIRED_ACTIONS.id,
@@ -339,7 +361,7 @@ export const processGroupByRatioResults = (
numeratorResults: GroupedSearchQueryResponse['aggregations']['groups']['buckets'],
denominatorResults: GroupedSearchQueryResponse['aggregations']['groups']['buckets'],
params: RatioAlertParams,
- alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'],
+ alertInstanceFactory: LogThresholdAlertInstanceFactory,
alertInstaceUpdater: AlertInstanceUpdater
) => {
const { count, criteria } = params;
@@ -360,7 +382,7 @@ export const processGroupByRatioResults = (
ratio !== undefined &&
checkValueAgainstComparatorMap[count.comparator](ratio, count.value)
) {
- const alertInstance = alertInstanceFactory(numeratorGroup.name);
+ const alertInstance = alertInstanceFactory(numeratorGroup.name, count.value, ratio);
alertInstaceUpdater(alertInstance, AlertStates.ALERT, [
{
actionGroup: FIRED_ACTIONS.id,
diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts
index 62d92d0487ff7..3d0bac3dd2bf5 100644
--- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts
+++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts
@@ -6,14 +6,7 @@
*/
import { i18n } from '@kbn/i18n';
-import {
- PluginSetupContract,
- AlertTypeParams,
- AlertTypeState,
- AlertInstanceContext,
- AlertInstanceState,
- ActionGroupIdsOf,
-} from '../../../../../alerting/server';
+import { PluginSetupContract } from '../../../../../alerting/server';
import { createLogThresholdExecutor, FIRED_ACTIONS } from './log_threshold_executor';
import {
LOG_DOCUMENT_COUNT_ALERT_TYPE_ID,
@@ -88,13 +81,7 @@ export async function registerLogThresholdAlertType(
);
}
- alertingPlugin.registerType<
- AlertTypeParams,
- AlertTypeState,
- AlertInstanceState,
- AlertInstanceContext,
- ActionGroupIdsOf
- >({
+ alertingPlugin.registerType({
id: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID,
name: i18n.translate('xpack.infra.logs.alertName', {
defaultMessage: 'Log threshold',
diff --git a/x-pack/plugins/infra/server/lib/infra_types.ts b/x-pack/plugins/infra/server/lib/infra_types.ts
index 0c57ff2e05847..332a2e499977b 100644
--- a/x-pack/plugins/infra/server/lib/infra_types.ts
+++ b/x-pack/plugins/infra/server/lib/infra_types.ts
@@ -5,15 +5,16 @@
* 2.0.
*/
+import { handleEsError } from '../../../../../src/plugins/es_ui_shared/server';
+import { InfraConfig } from '../plugin';
+import { GetLogQueryFields } from '../services/log_queries/get_log_query_fields';
+import { RulesServiceSetup } from '../services/rules';
+import { KibanaFramework } from './adapters/framework/kibana_framework_adapter';
import { InfraFieldsDomain } from './domains/fields_domain';
import { InfraLogEntriesDomain } from './domains/log_entries_domain';
import { InfraMetricsDomain } from './domains/metrics_domain';
import { InfraSources } from './sources';
import { InfraSourceStatus } from './source_status';
-import { InfraConfig } from '../plugin';
-import { KibanaFramework } from './adapters/framework/kibana_framework_adapter';
-import { GetLogQueryFields } from '../services/log_queries/get_log_query_fields';
-import { handleEsError } from '../../../../../src/plugins/es_ui_shared/server';
export interface InfraDomainLibs {
fields: InfraFieldsDomain;
@@ -28,4 +29,6 @@ export interface InfraBackendLibs extends InfraDomainLibs {
sourceStatus: InfraSourceStatus;
getLogQueryFields: GetLogQueryFields;
handleEsError: typeof handleEsError;
+ logsRules: RulesServiceSetup;
+ metricsRules: RulesServiceSetup;
}
diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts
index 7c5666049bd60..de445affc178e 100644
--- a/x-pack/plugins/infra/server/plugin.ts
+++ b/x-pack/plugins/infra/server/plugin.ts
@@ -8,7 +8,9 @@
import { Server } from '@hapi/hapi';
import { schema, TypeOf } from '@kbn/config-schema';
import { i18n } from '@kbn/i18n';
+import { Logger } from '@kbn/logging';
import { CoreSetup, PluginInitializerContext, Plugin } from 'src/core/server';
+import { LOGS_FEATURE_ID, METRICS_FEATURE_ID } from '../common/constants';
import { InfraStaticSourceConfiguration } from '../common/source_configuration/source_configuration';
import { inventoryViewSavedObjectType } from '../common/saved_objects/inventory_view';
import { metricsExplorerViewSavedObjectType } from '../common/saved_objects/metrics_explorer_view';
@@ -32,6 +34,7 @@ import { InfraPluginRequestHandlerContext } from './types';
import { UsageCollector } from './usage/usage_collector';
import { createGetLogQueryFields } from './services/log_queries/get_log_query_fields';
import { handleEsError } from '../../../../src/plugins/es_ui_shared/server';
+import { RulesService } from './services/rules';
export const config = {
schema: schema.object({
@@ -82,9 +85,25 @@ export interface InfraPluginSetup {
export class InfraServerPlugin implements Plugin {
public config: InfraConfig;
public libs: InfraBackendLibs | undefined;
+ public logger: Logger;
+
+ private logsRules: RulesService;
+ private metricsRules: RulesService;
constructor(context: PluginInitializerContext) {
this.config = context.config.get();
+ this.logger = context.logger.get();
+
+ this.logsRules = new RulesService(
+ LOGS_FEATURE_ID,
+ 'observability.logs',
+ this.logger.get('logsRules')
+ );
+ this.metricsRules = new RulesService(
+ METRICS_FEATURE_ID,
+ 'observability.metrics',
+ this.logger.get('metricsRules')
+ );
}
setup(core: CoreSetup, plugins: InfraServerPluginSetupDeps) {
@@ -126,6 +145,8 @@ export class InfraServerPlugin implements Plugin {
...domainLibs,
getLogQueryFields: createGetLogQueryFields(sources, framework),
handleEsError,
+ logsRules: this.logsRules.setup(core, plugins),
+ metricsRules: this.metricsRules.setup(core, plugins),
};
plugins.features.registerKibanaFeature(METRICS_FEATURE);
diff --git a/x-pack/plugins/infra/server/services/rules/index.ts b/x-pack/plugins/infra/server/services/rules/index.ts
new file mode 100644
index 0000000000000..eaa3d0da493e5
--- /dev/null
+++ b/x-pack/plugins/infra/server/services/rules/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
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export * from './rules_service';
+export * from './types';
diff --git a/x-pack/plugins/infra/server/services/rules/rule_data_client.ts b/x-pack/plugins/infra/server/services/rules/rule_data_client.ts
new file mode 100644
index 0000000000000..d693be40f10d0
--- /dev/null
+++ b/x-pack/plugins/infra/server/services/rules/rule_data_client.ts
@@ -0,0 +1,87 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { once } from 'lodash';
+import { CoreSetup, Logger } from 'src/core/server';
+import { TECHNICAL_COMPONENT_TEMPLATE_NAME } from '../../../../rule_registry/common/assets';
+import { RuleRegistryPluginSetupContract } from '../../../../rule_registry/server';
+import { logThresholdRuleDataNamespace } from '../../../common/alerting/logs/log_threshold';
+import type { InfraFeatureId } from '../../../common/constants';
+import { RuleRegistrationContext, RulesServiceStartDeps } from './types';
+
+export const createRuleDataClient = ({
+ ownerFeatureId,
+ registrationContext,
+ getStartServices,
+ logger,
+ ruleDataService,
+}: {
+ ownerFeatureId: InfraFeatureId;
+ registrationContext: RuleRegistrationContext;
+ getStartServices: CoreSetup['getStartServices'];
+ logger: Logger;
+ ruleDataService: RuleRegistryPluginSetupContract['ruleDataService'];
+}) => {
+ const initializeRuleDataTemplates = once(async () => {
+ const componentTemplateName = ruleDataService.getFullAssetName(
+ `${registrationContext}-mappings`
+ );
+
+ const indexNamePattern = ruleDataService.getFullAssetName(`${registrationContext}*`);
+
+ if (!ruleDataService.isWriteEnabled()) {
+ return;
+ }
+
+ await ruleDataService.createOrUpdateComponentTemplate({
+ name: componentTemplateName,
+ body: {
+ template: {
+ settings: {
+ number_of_shards: 1,
+ },
+ mappings: {
+ properties: {
+ [logThresholdRuleDataNamespace]: {
+ properties: {
+ serialized_params: {
+ type: 'keyword',
+ index: false,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ });
+
+ await ruleDataService.createOrUpdateIndexTemplate({
+ name: ruleDataService.getFullAssetName(registrationContext),
+ body: {
+ index_patterns: [indexNamePattern],
+ composed_of: [
+ ruleDataService.getFullAssetName(TECHNICAL_COMPONENT_TEMPLATE_NAME),
+ componentTemplateName,
+ ],
+ },
+ });
+
+ await ruleDataService.updateIndexMappingsMatchingPattern(indexNamePattern);
+ });
+
+ // initialize eagerly
+ const initializeRuleDataTemplatesPromise = initializeRuleDataTemplates().catch((err) => {
+ logger.error(err);
+ });
+
+ return ruleDataService.getRuleDataClient(
+ ownerFeatureId,
+ ruleDataService.getFullAssetName(registrationContext),
+ () => initializeRuleDataTemplatesPromise
+ );
+};
diff --git a/x-pack/plugins/infra/server/services/rules/rules_service.ts b/x-pack/plugins/infra/server/services/rules/rules_service.ts
new file mode 100644
index 0000000000000..9341fc59d75b8
--- /dev/null
+++ b/x-pack/plugins/infra/server/services/rules/rules_service.ts
@@ -0,0 +1,50 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { CoreSetup, Logger } from 'src/core/server';
+import { createLifecycleExecutor } from '../../../../rule_registry/server';
+import { InfraFeatureId } from '../../../common/constants';
+import { createRuleDataClient } from './rule_data_client';
+import {
+ RuleRegistrationContext,
+ RulesServiceSetup,
+ RulesServiceSetupDeps,
+ RulesServiceStart,
+ RulesServiceStartDeps,
+} from './types';
+
+export class RulesService {
+ constructor(
+ public readonly ownerFeatureId: InfraFeatureId,
+ public readonly registrationContext: RuleRegistrationContext,
+ private readonly logger: Logger
+ ) {}
+
+ public setup(
+ core: CoreSetup,
+ setupDeps: RulesServiceSetupDeps
+ ): RulesServiceSetup {
+ const ruleDataClient = createRuleDataClient({
+ getStartServices: core.getStartServices,
+ logger: this.logger,
+ ownerFeatureId: this.ownerFeatureId,
+ registrationContext: this.registrationContext,
+ ruleDataService: setupDeps.ruleRegistry.ruleDataService,
+ });
+
+ const createLifecycleRuleExecutor = createLifecycleExecutor(this.logger, ruleDataClient);
+
+ return {
+ createLifecycleRuleExecutor,
+ ruleDataClient,
+ };
+ }
+
+ public start(_startDeps: RulesServiceStartDeps): RulesServiceStart {
+ return {};
+ }
+}
diff --git a/x-pack/plugins/infra/server/services/rules/types.ts b/x-pack/plugins/infra/server/services/rules/types.ts
new file mode 100644
index 0000000000000..b67b79ee5d3c2
--- /dev/null
+++ b/x-pack/plugins/infra/server/services/rules/types.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { PluginSetupContract as AlertingPluginSetup } from '../../../../alerting/server';
+import {
+ createLifecycleExecutor,
+ RuleDataClient,
+ RuleRegistryPluginSetupContract,
+} from '../../../../rule_registry/server';
+
+type LifecycleRuleExecutorCreator = ReturnType;
+
+export interface RulesServiceSetupDeps {
+ alerting: AlertingPluginSetup;
+ ruleRegistry: RuleRegistryPluginSetupContract;
+}
+
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface RulesServiceStartDeps {}
+
+export interface RulesServiceSetup {
+ createLifecycleRuleExecutor: LifecycleRuleExecutorCreator;
+ ruleDataClient: RuleDataClient;
+}
+
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface RulesServiceStart {}
+
+export type RuleRegistrationContext = 'observability.logs' | 'observability.metrics';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/network_direction.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/network_direction.test.tsx
new file mode 100644
index 0000000000000..7a4c55d6f5e02
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/network_direction.test.tsx
@@ -0,0 +1,180 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { act } from 'react-dom/test-utils';
+import { setup, SetupResult, getProcessorValue } from './processor.helpers';
+
+// Default parameter values automatically added to the network direction processor when saved
+const defaultNetworkDirectionParameters = {
+ if: undefined,
+ tag: undefined,
+ source_ip: undefined,
+ description: undefined,
+ target_field: undefined,
+ ignore_missing: undefined,
+ ignore_failure: undefined,
+ destination_ip: undefined,
+ internal_networks: undefined,
+ internal_networks_field: undefined,
+};
+
+const NETWORK_DIRECTION_TYPE = 'network_direction';
+
+describe('Processor: Network Direction', () => {
+ let onUpdate: jest.Mock;
+ let testBed: SetupResult;
+
+ beforeAll(() => {
+ jest.useFakeTimers();
+ });
+
+ afterAll(() => {
+ jest.useRealTimers();
+ });
+
+ beforeEach(async () => {
+ onUpdate = jest.fn();
+
+ await act(async () => {
+ testBed = await setup({
+ value: {
+ processors: [],
+ },
+ onFlyoutOpen: jest.fn(),
+ onUpdate,
+ });
+ });
+
+ testBed.component.update();
+
+ // Open flyout to add new processor
+ testBed.actions.addProcessor();
+ // Add type (the other fields are not visible until a type is selected)
+ await testBed.actions.addProcessorType(NETWORK_DIRECTION_TYPE);
+ });
+
+ test('prevents form submission if internal_network field is not provided', async () => {
+ const {
+ actions: { saveNewProcessor },
+ form,
+ } = testBed;
+
+ // Click submit button with only the type defined
+ await saveNewProcessor();
+
+ // Expect form error as "field" is required parameter
+ expect(form.getErrorsMessages()).toEqual(['A field value is required.']);
+ });
+
+ test('saves with default parameter values', async () => {
+ const {
+ actions: { saveNewProcessor },
+ find,
+ component,
+ } = testBed;
+
+ // Add "networkDirectionField" value (required)
+ await act(async () => {
+ find('networkDirectionField.input').simulate('change', [{ label: 'loopback' }]);
+ });
+ component.update();
+
+ // Save the field
+ await saveNewProcessor();
+
+ const processors = getProcessorValue(onUpdate, NETWORK_DIRECTION_TYPE);
+ expect(processors[0][NETWORK_DIRECTION_TYPE]).toEqual({
+ ...defaultNetworkDirectionParameters,
+ internal_networks: ['loopback'],
+ });
+ });
+
+ test('allows to set internal_networks_field', async () => {
+ const {
+ actions: { saveNewProcessor },
+ form,
+ find,
+ } = testBed;
+
+ find('toggleCustomField').simulate('click');
+
+ form.setInputValue('networkDirectionField.input', 'internal_networks_field');
+
+ // Save the field with new changes
+ await saveNewProcessor();
+
+ const processors = getProcessorValue(onUpdate, NETWORK_DIRECTION_TYPE);
+ expect(processors[0][NETWORK_DIRECTION_TYPE]).toEqual({
+ ...defaultNetworkDirectionParameters,
+ internal_networks_field: 'internal_networks_field',
+ });
+ });
+
+ test('allows to set just internal_networks_field or internal_networks', async () => {
+ const {
+ actions: { saveNewProcessor },
+ form,
+ find,
+ component,
+ } = testBed;
+
+ // Set internal_networks field
+ await act(async () => {
+ find('networkDirectionField.input').simulate('change', [{ label: 'loopback' }]);
+ });
+ component.update();
+
+ // Toggle to internal_networks_field and set a random value
+ find('toggleCustomField').simulate('click');
+ form.setInputValue('networkDirectionField.input', 'internal_networks_field');
+
+ // Save the field with new changes
+ await saveNewProcessor();
+
+ const processors = getProcessorValue(onUpdate, NETWORK_DIRECTION_TYPE);
+ expect(processors[0][NETWORK_DIRECTION_TYPE]).toEqual({
+ ...defaultNetworkDirectionParameters,
+ internal_networks_field: 'internal_networks_field',
+ });
+ });
+
+ test('allows optional parameters to be set', async () => {
+ const {
+ actions: { saveNewProcessor },
+ form,
+ find,
+ component,
+ } = testBed;
+
+ // Add "networkDirectionField" value (required)
+ await act(async () => {
+ find('networkDirectionField.input').simulate('change', [{ label: 'loopback' }]);
+ });
+ component.update();
+
+ // Set optional parameteres
+ form.toggleEuiSwitch('ignoreMissingSwitch.input');
+ form.toggleEuiSwitch('ignoreFailureSwitch.input');
+ form.setInputValue('sourceIpField.input', 'source.ip');
+ form.setInputValue('targetField.input', 'target_field');
+ form.setInputValue('destinationIpField.input', 'destination.ip');
+
+ // Save the field with new changes
+ await saveNewProcessor();
+
+ const processors = getProcessorValue(onUpdate, NETWORK_DIRECTION_TYPE);
+ expect(processors[0][NETWORK_DIRECTION_TYPE]).toEqual({
+ ...defaultNetworkDirectionParameters,
+ ignore_failure: true,
+ ignore_missing: false,
+ source_ip: 'source.ip',
+ target_field: 'target_field',
+ destination_ip: 'destination.ip',
+ internal_networks: ['loopback'],
+ });
+ });
+});
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx
index 24e1ddce008ea..e4024e4ec67f4 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx
@@ -171,6 +171,10 @@ type TestSubject =
| 'regexFileField.input'
| 'valueFieldInput'
| 'mediaTypeSelectorField'
+ | 'networkDirectionField.input'
+ | 'sourceIpField.input'
+ | 'destinationIpField.input'
+ | 'toggleCustomField'
| 'ignoreEmptyField.input'
| 'overrideField.input'
| 'fieldsValueField.input'
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts
index 5e3e5f82478bd..f5eb1ab3ec59b 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/index.ts
@@ -28,6 +28,7 @@ export { Join } from './join';
export { Json } from './json';
export { Kv } from './kv';
export { Lowercase } from './lowercase';
+export { NetworkDirection } from './network_direction';
export { Pipeline } from './pipeline';
export { RegisteredDomain } from './registered_domain';
export { Remove } from './remove';
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/network_direction.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/network_direction.tsx
new file mode 100644
index 0000000000000..2026a77bc6566
--- /dev/null
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/network_direction.tsx
@@ -0,0 +1,242 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { FunctionComponent, useState, useCallback, useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
+import { isEmpty } from 'lodash';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiButtonEmpty, EuiCode } from '@elastic/eui';
+
+import {
+ FIELD_TYPES,
+ UseField,
+ useFormContext,
+ Field,
+ FieldHook,
+ FieldConfig,
+ SerializerFunc,
+} from '../../../../../../shared_imports';
+import { FieldsConfig, from, to } from './shared';
+import { TargetField } from './common_fields/target_field';
+import { IgnoreMissingField } from './common_fields/ignore_missing_field';
+
+interface InternalNetworkTypes {
+ internal_networks: string[];
+ internal_networks_field: string;
+}
+
+type InternalNetworkFields = {
+ [K in keyof InternalNetworkTypes]: FieldHook;
+};
+
+const internalNetworkValues: string[] = [
+ 'loopback',
+ 'unicast',
+ 'global_unicast',
+ 'multicast',
+ 'interface_local_multicast',
+ 'link_local_unicast',
+ 'link_local_multicast',
+ 'link_local_multicast',
+ 'private',
+ 'public',
+ 'unspecified',
+];
+
+const fieldsConfig: FieldsConfig = {
+ /* Optional fields config */
+ source_ip: {
+ type: FIELD_TYPES.TEXT,
+ serializer: from.emptyStringToUndefined,
+ label: i18n.translate('xpack.ingestPipelines.pipelineEditor.networkDirection.sourceIpLabel', {
+ defaultMessage: 'Source IP (optional)',
+ }),
+ helpText: (
+ {'source.ip'},
+ }}
+ />
+ ),
+ },
+ destination_ip: {
+ type: FIELD_TYPES.TEXT,
+ serializer: from.emptyStringToUndefined,
+ label: i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.networkDirection.destinationIpLabel',
+ {
+ defaultMessage: 'Destination IP (optional)',
+ }
+ ),
+ helpText: (
+ {'destination.ip'},
+ }}
+ />
+ ),
+ },
+};
+
+const getInternalNetworkConfig: (
+ toggleCustom: () => void
+) => Record<
+ keyof InternalNetworkFields,
+ {
+ path: string;
+ config?: FieldConfig;
+ euiFieldProps?: Record;
+ labelAppend: JSX.Element;
+ }
+> = (toggleCustom: () => void) => ({
+ internal_networks: {
+ path: 'fields.internal_networks',
+ euiFieldProps: {
+ noSuggestions: false,
+ options: internalNetworkValues.map((label) => ({ label })),
+ },
+ config: {
+ type: FIELD_TYPES.COMBO_BOX,
+ deserializer: to.arrayOfStrings,
+ serializer: from.optionalArrayOfStrings,
+ fieldsToValidateOnChange: ['fields.internal_networks', 'fields.internal_networks_field'],
+ validations: [
+ {
+ validator: ({ value, path, formData }) => {
+ if (isEmpty(value) && isEmpty(formData['fields.internal_networks_field'])) {
+ return { path, message: 'A field value is required.' };
+ }
+ },
+ },
+ ],
+ label: i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.networkDirection.internalNetworksLabel',
+ {
+ defaultMessage: 'Internal networks',
+ }
+ ),
+ helpText: (
+
+ ),
+ },
+ labelAppend: (
+
+ {i18n.translate('xpack.ingestPipelines.pipelineEditor.internalNetworkCustomLabel', {
+ defaultMessage: 'Use custom field',
+ })}
+
+ ),
+ key: 'preset',
+ },
+ internal_networks_field: {
+ path: 'fields.internal_networks_field',
+ config: {
+ type: FIELD_TYPES.TEXT,
+ serializer: from.emptyStringToUndefined,
+ fieldsToValidateOnChange: ['fields.internal_networks', 'fields.internal_networks_field'],
+ validations: [
+ {
+ validator: ({ value, path, formData }) => {
+ if (isEmpty(value) && isEmpty(formData['fields.internal_networks'])) {
+ return { path, message: 'A field value is required.' };
+ }
+ },
+ },
+ ],
+ label: i18n.translate(
+ 'xpack.ingestPipelines.pipelineEditor.networkDirection.internalNetworksFieldLabel',
+ {
+ defaultMessage: 'Internal networks field',
+ }
+ ),
+ helpText: (
+ {'internal_networks'},
+ }}
+ />
+ ),
+ },
+ labelAppend: (
+
+ {i18n.translate('xpack.ingestPipelines.pipelineEditor.internalNetworkPredefinedLabel', {
+ defaultMessage: 'Use preset field',
+ })}
+
+ ),
+ key: 'custom',
+ },
+});
+
+export const NetworkDirection: FunctionComponent = () => {
+ const { getFieldDefaultValue } = useFormContext();
+ const isInternalNetowrksFieldDefined =
+ getFieldDefaultValue('fields.internal_networks_field') !== undefined;
+ const [isCustom, setIsCustom] = useState(isInternalNetowrksFieldDefined);
+
+ const toggleCustom = useCallback(() => {
+ setIsCustom((prev) => !prev);
+ }, []);
+
+ const internalNetworkFieldProps = useMemo(
+ () =>
+ isCustom
+ ? getInternalNetworkConfig(toggleCustom).internal_networks_field
+ : getInternalNetworkConfig(toggleCustom).internal_networks,
+ [isCustom, toggleCustom]
+ );
+
+ return (
+ <>
+
+
+
+
+ {'network.direction'},
+ }}
+ />
+ }
+ />
+
+
+
+ }
+ />
+ >
+ );
+};
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx
index 983fb0ea67bb0..e6ca465bf1a02 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx
@@ -34,6 +34,7 @@ import {
Json,
Kv,
Lowercase,
+ NetworkDirection,
Pipeline,
RegisteredDomain,
Remove,
@@ -517,6 +518,23 @@ export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = {
},
}),
},
+ network_direction: {
+ FieldsComponent: NetworkDirection,
+ docLinkPath: '/network-direction-processor.html',
+ label: i18n.translate('xpack.ingestPipelines.processors.label.networkDirection', {
+ defaultMessage: 'Network Direction',
+ }),
+ typeDescription: i18n.translate(
+ 'xpack.ingestPipelines.processors.description.networkDirection',
+ {
+ defaultMessage: 'Calculates the network direction given a source IP address.',
+ }
+ ),
+ getDefaultDescription: () =>
+ i18n.translate('xpack.ingestPipelines.processors.defaultDescription.networkDirection', {
+ defaultMessage: 'Calculates the network direction given a source IP address.',
+ }),
+ },
pipeline: {
FieldsComponent: Pipeline,
docLinkPath: '/pipeline-processor.html',
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/context/processors_context.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/context/processors_context.tsx
index 0c43297e811d3..ddf996de7805c 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/context/processors_context.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/context/processors_context.tsx
@@ -151,7 +151,13 @@ export const PipelineProcessorsContextProvider: FunctionComponent = ({
break;
case 'managingProcessor':
// These are the option names we get back from our UI
- const knownOptionNames = Object.keys(processorTypeAndOptions.options);
+ const knownOptionNames = [
+ ...Object.keys(processorTypeAndOptions.options),
+ // We manually add fields that we **don't** want to be treated as "unknownOptions"
+ 'internal_networks',
+ 'internal_networks_field',
+ ];
+
// The processor that we are updating may have options configured the UI does not know about
const unknownOptions = omit(mode.arg.processor.options, knownOptionNames);
// In order to keep the options we don't get back from our UI, we merge the known and unknown options
diff --git a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts
index 8ed57221a1395..29be11430bf64 100644
--- a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts
+++ b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts
@@ -43,6 +43,7 @@ export {
ArrayItem,
FormHook,
useFormContext,
+ UseMultiFields,
FormDataProvider,
OnFormUpdateArg,
FieldConfig,
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx
index 19d91c1006cf0..936f1e477057d 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx
@@ -35,7 +35,7 @@ const operationDefinitionMap: Record = {
}),
} as unknown) as GenericOperationDefinition,
terms: { input: 'field' } as GenericOperationDefinition,
- sum: { input: 'field' } as GenericOperationDefinition,
+ sum: { input: 'field', filterable: true } as GenericOperationDefinition,
last_value: { input: 'field' } as GenericOperationDefinition,
max: { input: 'field' } as GenericOperationDefinition,
count: ({
@@ -928,6 +928,63 @@ invalid: "
).toEqual(['The operation average does not accept any parameter']);
});
+ it('returns an error if first argument type is passed multiple times', () => {
+ const formulas = [
+ 'average(bytes, bytes)',
+ "sum(bytes, kql='category.keyword: *', bytes)",
+ 'moving_average(average(bytes), average(bytes))',
+ "moving_average(average(bytes), kql='category.keyword: *', average(bytes))",
+ 'moving_average(average(bytes, bytes), count())',
+ 'moving_average(moving_average(average(bytes, bytes), count(), count()))',
+ ];
+ for (const formula of formulas) {
+ expect(
+ formulaOperation.getErrorMessage!(
+ getNewLayerWithFormula(formula),
+ 'col1',
+ indexPattern,
+ operationDefinitionMap
+ )
+ ).toEqual(
+ expect.arrayContaining([
+ expect.stringMatching(
+ /The operation (moving_average|average|sum) in the Formula requires a single (field|metric), found:/
+ ),
+ ])
+ );
+ }
+ });
+
+ it('returns an error if a function received an argument of the wrong argument type in any position', () => {
+ const formulas = [
+ 'average(bytes, count())',
+ "sum(bytes, kql='category.keyword: *', count(), count())",
+ 'average(bytes, bytes + 1)',
+ 'average(count(), bytes)',
+ 'moving_average(average(bytes), bytes)',
+ 'moving_average(bytes, bytes)',
+ 'moving_average(average(bytes), window=7, bytes)',
+ 'moving_average(window=7, bytes)',
+ "moving_average(kql='category.keyword: *', bytes)",
+ ];
+ for (const formula of formulas) {
+ expect(
+ formulaOperation.getErrorMessage!(
+ getNewLayerWithFormula(formula),
+ 'col1',
+ indexPattern,
+ operationDefinitionMap
+ )
+ ).toEqual(
+ expect.arrayContaining([
+ expect.stringMatching(
+ /The operation (moving_average|average|sum) in the Formula does not support (metric|field) parameters, found:/
+ ),
+ ])
+ );
+ }
+ });
+
it('returns an error if the parameter passed to an operation is of the wrong type', () => {
expect(
formulaOperation.getErrorMessage!(
@@ -1087,6 +1144,14 @@ invalid: "
)
).toEqual([`The first argument for ${fn} should be a field name. Found no field`]);
}
+ expect(
+ formulaOperation.getErrorMessage!(
+ getNewLayerWithFormula(`sum(kql='category.keyword: *')`),
+ 'col1',
+ indexPattern,
+ operationDefinitionMap
+ )
+ ).toEqual([`The first argument for sum should be a field name. Found category.keyword: *`]);
});
it("returns a clear error when there's a missing function for a fullReference operation", () => {
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/util.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/util.ts
index 445df21a6067e..6d0a585db048f 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/util.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/util.ts
@@ -455,7 +455,7 @@ Example: Calculate area based on side length
},
};
-export function isMathNode(node: TinymathAST) {
+export function isMathNode(node: TinymathAST | string) {
return isObject(node) && node.type === 'function' && tinymathFunctions[node.name];
}
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts
index 5b7a9beaa4e32..d65ef5ada8b37 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts
@@ -7,7 +7,7 @@
import { isObject, partition } from 'lodash';
import { i18n } from '@kbn/i18n';
-import { parse, TinymathLocation } from '@kbn/tinymath';
+import { parse, TinymathLocation, TinymathVariable } from '@kbn/tinymath';
import type { TinymathAST, TinymathFunction, TinymathNamedArgument } from '@kbn/tinymath';
import { esKuery, esQuery } from '../../../../../../../../src/plugins/data/public';
import {
@@ -63,6 +63,19 @@ interface ValidationErrors {
message: string;
type: {};
};
+ tooManyFirstArguments: {
+ message: string;
+ type: {
+ operation: string;
+ type: string;
+ text: string;
+ supported?: number;
+ };
+ };
+ wrongArgument: {
+ message: string;
+ type: { operation: string; text: string; type: string };
+ };
}
type ErrorTypes = keyof ValidationErrors;
@@ -276,6 +289,25 @@ function getMessageFromId({
defaultMessage: 'Use only one of kql= or lucene=, not both',
});
break;
+ case 'tooManyFirstArguments':
+ message = i18n.translate('xpack.lens.indexPattern.formulaOperationTooManyFirstArguments', {
+ defaultMessage:
+ 'The operation {operation} in the Formula requires a {supported, plural, one {single} other {supported}} {type}, found: {text}',
+ values: {
+ operation: out.operation,
+ text: out.text,
+ type: out.type,
+ supported: out.supported || 1,
+ },
+ });
+ break;
+ case 'wrongArgument':
+ message = i18n.translate('xpack.lens.indexPattern.formulaOperationwrongArgument', {
+ defaultMessage:
+ 'The operation {operation} in the Formula does not support {type} parameters, found: {text}',
+ values: { operation: out.operation, text: out.text, type: out.type },
+ });
+ break;
// case 'mathRequiresFunction':
// message = i18n.translate('xpack.lens.indexPattern.formulaMathRequiresFunctionLabel', {
// defaultMessage; 'The function {name} requires an Elasticsearch function',
@@ -531,14 +563,16 @@ function runFullASTValidation(
} else {
if (nodeOperation.input === 'field') {
if (shouldHaveFieldArgument(node)) {
- if (!isFirstArgumentValidType(firstArg, 'variable')) {
+ if (!isArgumentValidType(firstArg, 'variable')) {
if (isMathNode(firstArg)) {
errors.push(
getMessageFromId({
messageId: 'wrongFirstArgument',
values: {
operation: node.name,
- type: 'field',
+ type: i18n.translate('xpack.lens.indexPattern.formulaFieldValue', {
+ defaultMessage: 'field',
+ }),
argument: `math operation`,
},
locations: node.location ? [node.location] : [],
@@ -550,7 +584,9 @@ function runFullASTValidation(
messageId: 'wrongFirstArgument',
values: {
operation: node.name,
- type: 'field',
+ type: i18n.translate('xpack.lens.indexPattern.formulaFieldValue', {
+ defaultMessage: 'field',
+ }),
argument:
getValueOrName(firstArg) ||
i18n.translate('xpack.lens.indexPattern.formulaNoFieldForOperation', {
@@ -561,6 +597,25 @@ function runFullASTValidation(
})
);
}
+ } else {
+ // If the first argument is valid proceed with the other arguments validation
+ const fieldErrors = validateFieldArguments(node, variables, {
+ isFieldOperation: true,
+ firstArg,
+ });
+ if (fieldErrors.length) {
+ errors.push(...fieldErrors);
+ }
+ }
+ const functionErrors = validateFunctionArguments(node, functions, 0, {
+ isFieldOperation: true,
+ type: i18n.translate('xpack.lens.indexPattern.formulaFieldValue', {
+ defaultMessage: 'field',
+ }),
+ firstArgValidation: false,
+ });
+ if (functionErrors.length) {
+ errors.push(...functionErrors);
}
} else {
// Named arguments only
@@ -602,16 +657,20 @@ function runFullASTValidation(
if (nodeOperation.input === 'fullReference') {
// What about fn(7 + 1)? We may want to allow that
// In general this should be handled down the Esaggs route rather than here
- if (
- !isFirstArgumentValidType(firstArg, 'function') ||
- (isMathNode(firstArg) && validateMathNodes(firstArg, missingVariablesSet).length)
- ) {
+ const isFirstArgumentNotValid = Boolean(
+ !isArgumentValidType(firstArg, 'function') ||
+ (isMathNode(firstArg) && validateMathNodes(firstArg, missingVariablesSet).length)
+ );
+ // First field has a special handling
+ if (isFirstArgumentNotValid) {
errors.push(
getMessageFromId({
messageId: 'wrongFirstArgument',
values: {
operation: node.name,
- type: 'operation',
+ type: i18n.translate('xpack.lens.indexPattern.formulaOperationValue', {
+ defaultMessage: 'operation',
+ }),
argument:
getValueOrName(firstArg) ||
i18n.translate('xpack.lens.indexPattern.formulaNoOperation', {
@@ -622,6 +681,21 @@ function runFullASTValidation(
})
);
}
+ // Check for multiple function passed
+ const requiredFunctions = nodeOperation.requiredReferences
+ ? nodeOperation.requiredReferences.length
+ : 1;
+ const functionErrors = validateFunctionArguments(node, functions, requiredFunctions, {
+ isFieldOperation: false,
+ firstArgValidation: isFirstArgumentNotValid,
+ type: i18n.translate('xpack.lens.indexPattern.formulaMetricValue', {
+ defaultMessage: 'metric',
+ }),
+ });
+ if (functionErrors.length) {
+ errors.push(...functionErrors);
+ }
+
if (!canHaveParams(nodeOperation) && namedArguments.length) {
errors.push(
getMessageFromId({
@@ -633,6 +707,14 @@ function runFullASTValidation(
})
);
} else {
+ // check for fields passed at any position
+ const fieldErrors = validateFieldArguments(node, variables, {
+ isFieldOperation: false,
+ firstArg,
+ });
+ if (fieldErrors.length) {
+ errors.push(...fieldErrors);
+ }
const argumentsErrors = validateNameArguments(
node,
nodeOperation,
@@ -736,7 +818,7 @@ export function hasFunctionFieldArgument(type: string) {
return !['count'].includes(type);
}
-export function isFirstArgumentValidType(arg: TinymathAST, type: TinymathNodeTypes['type']) {
+export function isArgumentValidType(arg: TinymathAST | string, type: TinymathNodeTypes['type']) {
return isObject(arg) && arg.type === type;
}
@@ -812,3 +894,109 @@ export function validateMathNodes(root: TinymathAST, missingVariableSet: Set,
+ { isFieldOperation, firstArg }: { isFieldOperation: boolean; firstArg: TinymathAST }
+) {
+ const fields = variables.filter(
+ (arg) => isArgumentValidType(arg, 'variable') && !isMathNode(arg)
+ );
+ const errors = [];
+ if (isFieldOperation && (fields.length > 1 || (fields.length === 1 && fields[0] !== firstArg))) {
+ errors.push(
+ getMessageFromId({
+ messageId: 'tooManyFirstArguments',
+ values: {
+ operation: node.name,
+ type: i18n.translate('xpack.lens.indexPattern.formulaFieldValue', {
+ defaultMessage: 'field',
+ }),
+ supported: 1,
+ text: (fields as TinymathVariable[]).map(({ text }) => text).join(', '),
+ },
+ locations: node.location ? [node.location] : [],
+ })
+ );
+ }
+ if (!isFieldOperation && fields.length) {
+ errors.push(
+ getMessageFromId({
+ messageId: 'wrongArgument',
+ values: {
+ operation: node.name,
+ text: (fields as TinymathVariable[]).map(({ text }) => text).join(', '),
+ type: i18n.translate('xpack.lens.indexPattern.formulaFieldValue', {
+ defaultMessage: 'field',
+ }),
+ },
+ locations: node.location ? [node.location] : [],
+ })
+ );
+ }
+ return errors;
+}
+
+function validateFunctionArguments(
+ node: TinymathFunction,
+ functions: TinymathFunction[],
+ requiredFunctions: number = 0,
+ {
+ isFieldOperation,
+ firstArgValidation,
+ type,
+ }: { isFieldOperation: boolean; firstArgValidation: boolean; type: string }
+) {
+ const errors = [];
+ // For math operation let the native operation run its own validation
+ const [esOperations, mathOperations] = partition(functions, (arg) => !isMathNode(arg));
+ if (esOperations.length > requiredFunctions) {
+ if (isFieldOperation) {
+ errors.push(
+ getMessageFromId({
+ messageId: 'wrongArgument',
+ values: {
+ operation: node.name,
+ text: (esOperations as TinymathFunction[]).map(({ text }) => text).join(', '),
+ type: i18n.translate('xpack.lens.indexPattern.formulaMetricValue', {
+ defaultMessage: 'metric',
+ }),
+ },
+ locations: node.location ? [node.location] : [],
+ })
+ );
+ } else {
+ errors.push(
+ getMessageFromId({
+ messageId: 'tooManyFirstArguments',
+ values: {
+ operation: node.name,
+ type,
+ supported: requiredFunctions,
+ text: (esOperations as TinymathFunction[]).map(({ text }) => text).join(', '),
+ },
+ locations: node.location ? [node.location] : [],
+ })
+ );
+ }
+ }
+ // full reference operation have another way to handle math operations
+ if (
+ isFieldOperation &&
+ ((!firstArgValidation && mathOperations.length) || mathOperations.length > 1)
+ ) {
+ errors.push(
+ getMessageFromId({
+ messageId: 'wrongArgument',
+ values: {
+ operation: node.name,
+ type,
+ text: (mathOperations as TinymathFunction[]).map(({ text }) => text).join(', '),
+ },
+ locations: node.location ? [node.location] : [],
+ })
+ );
+ }
+ return errors;
+}
diff --git a/x-pack/plugins/lens/server/routes/field_stats.ts b/x-pack/plugins/lens/server/routes/field_stats.ts
index 12d3ef3f4a95e..7103e395eabdc 100644
--- a/x-pack/plugins/lens/server/routes/field_stats.ts
+++ b/x-pack/plugins/lens/server/routes/field_stats.ts
@@ -8,7 +8,7 @@ import { errors, estypes } from '@elastic/elasticsearch';
import DateMath from '@elastic/datemath';
import { schema } from '@kbn/config-schema';
import { CoreSetup } from 'src/core/server';
-import { IFieldType } from 'src/plugins/data/common';
+import type { IndexPatternField } from 'src/plugins/data/common';
import { SavedObjectNotFound } from '../../../../../src/plugins/kibana_utils/common';
import { ESSearchResponse } from '../../../../../src/core/types/elasticsearch';
import { FieldStatsResponse, BASE_API_URL } from '../../common';
@@ -79,6 +79,14 @@ export async function initFieldsRoute(setup: CoreSetup) {
},
};
+ const runtimeMappings = indexPattern.fields
+ .filter((f) => f.runtimeField)
+ .reduce((acc, f) => {
+ if (!f.runtimeField) return acc;
+ acc[f.name] = f.runtimeField;
+ return acc;
+ }, {} as Record);
+
const search = async (aggs: Record) => {
const { body: result } = await requestClient.search({
index: indexPattern.title,
@@ -86,7 +94,7 @@ export async function initFieldsRoute(setup: CoreSetup) {
body: {
query,
aggs,
- runtime_mappings: field.runtimeField ? { [fieldName]: field.runtimeField } : {},
+ runtime_mappings: runtimeMappings,
},
size: 0,
});
@@ -138,7 +146,7 @@ export async function getNumberHistogram(
aggSearchWithBody: (
aggs: Record
) => Promise,
- field: IFieldType,
+ field: IndexPatternField,
useTopHits = true
): Promise {
const fieldRef = getFieldRef(field);
@@ -247,7 +255,7 @@ export async function getNumberHistogram(
export async function getStringSamples(
aggSearchWithBody: (aggs: Record) => unknown,
- field: IFieldType,
+ field: IndexPatternField,
size = 10
): Promise {
const fieldRef = getFieldRef(field);
@@ -287,7 +295,7 @@ export async function getStringSamples(
// This one is not sampled so that it returns the full date range
export async function getDateHistogram(
aggSearchWithBody: (aggs: Record) => unknown,
- field: IFieldType,
+ field: IndexPatternField,
range: { fromDate: string; toDate: string }
): Promise {
const fromDate = DateMath.parse(range.fromDate);
@@ -329,7 +337,7 @@ export async function getDateHistogram(
};
}
-function getFieldRef(field: IFieldType) {
+function getFieldRef(field: IndexPatternField) {
return field.scripted
? {
script: {
diff --git a/x-pack/plugins/lists/public/exceptions/components/autocomplete/field.tsx b/x-pack/plugins/lists/public/exceptions/components/autocomplete/field.tsx
index b3a5e36f12e40..47527914e71ff 100644
--- a/x-pack/plugins/lists/public/exceptions/components/autocomplete/field.tsx
+++ b/x-pack/plugins/lists/public/exceptions/components/autocomplete/field.tsx
@@ -28,6 +28,13 @@ interface OperatorProps {
selectedField: IFieldType | undefined;
}
+/**
+ * There is a copy within:
+ * x-pack/plugins/security_solution/public/common/components/autocomplete/field.tsx
+ *
+ * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378
+ * NOTE: This has deviated from the copy and will have to be reconciled.
+ */
export const FieldComponent: React.FC = ({
fieldInputWidth,
fieldTypeFilter = [],
diff --git a/x-pack/plugins/lists/public/exceptions/components/autocomplete/field_value_match.tsx b/x-pack/plugins/lists/public/exceptions/components/autocomplete/field_value_match.tsx
index c1776280842c6..8dbe8f223ae5b 100644
--- a/x-pack/plugins/lists/public/exceptions/components/autocomplete/field_value_match.tsx
+++ b/x-pack/plugins/lists/public/exceptions/components/autocomplete/field_value_match.tsx
@@ -47,6 +47,11 @@ interface AutocompleteFieldMatchProps {
onError?: (arg: boolean) => void;
}
+/**
+ * There is a copy of this within:
+ * x-pack/plugins/security_solution/public/common/components/autocomplete/field_value_match.tsx
+ * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378
+ */
export const AutocompleteFieldMatchComponent: React.FC = ({
placeholder,
rowLabel,
diff --git a/x-pack/plugins/lists/public/exceptions/components/autocomplete/helpers.ts b/x-pack/plugins/lists/public/exceptions/components/autocomplete/helpers.ts
index 965214815eedf..975416e272227 100644
--- a/x-pack/plugins/lists/public/exceptions/components/autocomplete/helpers.ts
+++ b/x-pack/plugins/lists/public/exceptions/components/autocomplete/helpers.ts
@@ -10,6 +10,7 @@ import { EuiComboBoxOptionOption } from '@elastic/eui';
import type { ListSchema, Type } from '@kbn/securitysolution-io-ts-list-types';
import {
EXCEPTION_OPERATORS,
+ OperatorOption,
doesNotExistOperator,
existsOperator,
isNotOperator,
@@ -18,7 +19,7 @@ import {
import { IFieldType } from '../../../../../../../src/plugins/data/common';
-import { GetGenericComboBoxPropsReturn, OperatorOption } from './types';
+import { GetGenericComboBoxPropsReturn } from './types';
import * as i18n from './translations';
/**
@@ -72,6 +73,10 @@ export const checkEmptyValue = (
/**
* Very basic validation for values
+ * There is a copy within:
+ * x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.ts
+ *
+ * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378
*
* @param param the value being checked
* @param field the selected field
@@ -109,7 +114,10 @@ export const paramIsValid = (
/**
* Determines the options, selected values and option labels for EUI combo box
+ * There is a copy within:
+ * x-pack/plugins/security_solution/public/common/components/autocomplete/helpers.ts
*
+ * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378
* @param options options user can select from
* @param selectedOptions user selection if any
* @param getLabel helper function to know which property to use for labels
diff --git a/x-pack/plugins/lists/public/exceptions/components/autocomplete/hooks/use_field_value_autocomplete.ts b/x-pack/plugins/lists/public/exceptions/components/autocomplete/hooks/use_field_value_autocomplete.ts
index 674bb5e5537d9..63d3925d6d64d 100644
--- a/x-pack/plugins/lists/public/exceptions/components/autocomplete/hooks/use_field_value_autocomplete.ts
+++ b/x-pack/plugins/lists/public/exceptions/components/autocomplete/hooks/use_field_value_autocomplete.ts
@@ -33,7 +33,10 @@ export interface UseFieldValueAutocompleteProps {
}
/**
* Hook for using the field value autocomplete service
+ * There is a copy within:
+ * x-pack/plugins/security_solution/public/common/components/autocomplete/hooks/use_field_value_autocomplete.ts
*
+ * TODO: This should be in its own packaged and not copied, https://github.com/elastic/kibana/issues/105378
*/
export const useFieldValueAutocomplete = ({
selectedField,
diff --git a/x-pack/plugins/lists/public/exceptions/components/autocomplete/operator.tsx b/x-pack/plugins/lists/public/exceptions/components/autocomplete/operator.tsx
index 7fc221c5a097c..0d2fe5bd664be 100644
--- a/x-pack/plugins/lists/public/exceptions/components/autocomplete/operator.tsx
+++ b/x-pack/plugins/lists/public/exceptions/components/autocomplete/operator.tsx
@@ -7,11 +7,12 @@
import React, { useCallback, useMemo } from 'react';
import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
+import { OperatorOption } from '@kbn/securitysolution-list-utils';
import { IFieldType } from '../../../../../../../src/plugins/data/common';
import { getGenericComboBoxProps, getOperators } from './helpers';
-import { GetGenericComboBoxPropsReturn, OperatorOption } from './types';
+import { GetGenericComboBoxPropsReturn } from './types';
const AS_PLAIN_TEXT = { asPlainText: true };
diff --git a/x-pack/plugins/lists/public/exceptions/components/autocomplete/types.ts b/x-pack/plugins/lists/public/exceptions/components/autocomplete/types.ts
index 76d5b7758007b..07f1903fb70e1 100644
--- a/x-pack/plugins/lists/public/exceptions/components/autocomplete/types.ts
+++ b/x-pack/plugins/lists/public/exceptions/components/autocomplete/types.ts
@@ -6,20 +6,9 @@
*/
import { EuiComboBoxOptionOption } from '@elastic/eui';
-import type {
- ListOperatorEnum as OperatorEnum,
- ListOperatorTypeEnum as OperatorTypeEnum,
-} from '@kbn/securitysolution-io-ts-list-types';
export interface GetGenericComboBoxPropsReturn {
comboOptions: EuiComboBoxOptionOption[];
labels: string[];
selectedComboOptions: EuiComboBoxOptionOption[];
}
-
-export interface OperatorOption {
- message: string;
- value: string;
- operator: OperatorEnum;
- type: OperatorTypeEnum;
-}
diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx
index 7daef8467dd1a..c54da89766d76 100644
--- a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx
+++ b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx
@@ -18,6 +18,7 @@ import {
BuilderEntry,
EXCEPTION_OPERATORS_ONLY_LISTS,
FormattedBuilderEntry,
+ OperatorOption,
getEntryOnFieldChange,
getEntryOnListChange,
getEntryOnMatchAnyChange,
@@ -32,7 +33,6 @@ import { IFieldType, IIndexPattern } from '../../../../../../../src/plugins/data
import { HttpStart } from '../../../../../../../src/core/public';
import { FieldComponent } from '../autocomplete/field';
import { OperatorComponent } from '../autocomplete/operator';
-import { OperatorOption } from '../autocomplete/types';
import { AutocompleteFieldExistsComponent } from '../autocomplete/field_value_exists';
import { AutocompleteFieldMatchComponent } from '../autocomplete/field_value_match';
import { AutocompleteFieldMatchAnyComponent } from '../autocomplete/field_value_match_any';
diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts b/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts
index 212db40f3168c..afeac2d1bf4de 100644
--- a/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts
+++ b/x-pack/plugins/lists/public/exceptions/components/builder/helpers.test.ts
@@ -24,6 +24,7 @@ import {
EmptyEntry,
ExceptionsBuilderExceptionItem,
FormattedBuilderEntry,
+ OperatorOption,
doesNotExistOperator,
existsOperator,
filterExceptionItems,
@@ -64,7 +65,6 @@ import { getEntryNestedMock } from '../../../../common/schemas/types/entry_neste
import { getEntryMatchMock } from '../../../../common/schemas/types/entry_match.mock';
import { getEntryMatchAnyMock } from '../../../../common/schemas/types/entry_match_any.mock';
import { getListResponseMock } from '../../../../common/schemas/response/list_schema.mock';
-import { OperatorOption } from '../autocomplete/types';
import { getEntryListMock } from '../../../../common/schemas/types/entry_list.mock';
// TODO: ALL THESE TESTS SHOULD BE MOVED TO @kbn/securitysolution-list-utils for its helper. The only reason why they're here is due to missing other packages we hae to create or missing things from kbn packages such as mocks from kibana core
diff --git a/x-pack/plugins/maps/public/actions/data_request_actions.ts b/x-pack/plugins/maps/public/actions/data_request_actions.ts
index 47350474eef04..50df95a52c4d4 100644
--- a/x-pack/plugins/maps/public/actions/data_request_actions.ts
+++ b/x-pack/plugins/maps/public/actions/data_request_actions.ts
@@ -55,6 +55,7 @@ export type DataRequestContext = {
startLoading(dataId: string, requestToken: symbol, requestMeta?: DataMeta): void;
stopLoading(dataId: string, requestToken: symbol, data: object, resultsMeta?: DataMeta): void;
onLoadError(dataId: string, requestToken: symbol, errorMessage: string): void;
+ onJoinError(errorMessage: string): void;
updateSourceData(newData: unknown): void;
isRequestStillActive(dataId: string, requestToken: symbol): boolean;
registerCancelCallback(requestToken: symbol, callback: () => void): void;
@@ -121,6 +122,8 @@ function getDataRequestContext(
dispatch(endDataLoad(layerId, dataId, requestToken, data, meta)),
onLoadError: (dataId: string, requestToken: symbol, errorMessage: string) =>
dispatch(onDataLoadError(layerId, dataId, requestToken, errorMessage)),
+ onJoinError: (errorMessage: string) =>
+ dispatch(setLayerDataLoadErrorStatus(layerId, errorMessage)),
updateSourceData: (newData: object) => {
dispatch(updateSourceDataRequest(layerId, newData));
},
@@ -193,13 +196,11 @@ export function syncDataForLayerId(layerId: string | null) {
}
function setLayerDataLoadErrorStatus(layerId: string, errorMessage: string | null) {
- return (dispatch: Dispatch) => {
- dispatch({
- type: SET_LAYER_ERROR_STATUS,
- isInErrorState: errorMessage !== null,
- layerId,
- errorMessage,
- });
+ return {
+ type: SET_LAYER_ERROR_STATUS,
+ isInErrorState: errorMessage !== null,
+ layerId,
+ errorMessage,
};
}
diff --git a/x-pack/plugins/maps/public/classes/joins/inner_join.ts b/x-pack/plugins/maps/public/classes/joins/inner_join.ts
index 988690233d484..9f6fec576e9e2 100644
--- a/x-pack/plugins/maps/public/classes/joins/inner_join.ts
+++ b/x-pack/plugins/maps/public/classes/joins/inner_join.ts
@@ -118,17 +118,23 @@ export class InnerJoin {
});
}
- const joinKey = feature.properties[this._leftField.getName()];
- const coercedKey =
- typeof joinKey === 'undefined' || joinKey === null ? null : joinKey.toString();
- if (coercedKey !== null && propertiesMap.has(coercedKey)) {
- Object.assign(feature.properties, propertiesMap.get(coercedKey));
+ const joinKey = this.getJoinKey(feature);
+ if (joinKey !== null && propertiesMap.has(joinKey)) {
+ Object.assign(feature.properties, propertiesMap.get(joinKey));
return true;
} else {
return false;
}
}
+ getJoinKey(feature: Feature): string | null {
+ const joinKey =
+ feature.properties && this._leftField
+ ? feature.properties[this._leftField.getName()]
+ : undefined;
+ return joinKey === undefined || joinKey === null ? null : joinKey.toString();
+ }
+
getRightJoinSource(): ITermJoinSource {
if (!this._rightSource) {
throw new Error('Cannot get rightSource from InnerJoin with incomplete config');
diff --git a/x-pack/plugins/maps/public/classes/layers/__fixtures__/mock_sync_context.ts b/x-pack/plugins/maps/public/classes/layers/__fixtures__/mock_sync_context.ts
index dd1367605376d..16aca6760c4d5 100644
--- a/x-pack/plugins/maps/public/classes/layers/__fixtures__/mock_sync_context.ts
+++ b/x-pack/plugins/maps/public/classes/layers/__fixtures__/mock_sync_context.ts
@@ -16,6 +16,7 @@ export class MockSyncContext implements DataRequestContext {
registerCancelCallback: (requestToken: symbol, callback: () => void) => void;
startLoading: (dataId: string, requestToken: symbol, meta: DataMeta) => void;
stopLoading: (dataId: string, requestToken: symbol, data: object, meta: DataMeta) => void;
+ onJoinError: (errorMessage: string) => void;
updateSourceData: (newData: unknown) => void;
forceRefresh: boolean;
@@ -38,6 +39,7 @@ export class MockSyncContext implements DataRequestContext {
this.registerCancelCallback = sinon.spy();
this.startLoading = sinon.spy();
this.stopLoading = sinon.spy();
+ this.onJoinError = sinon.spy();
this.updateSourceData = sinon.spy();
this.forceRefresh = false;
}
diff --git a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx
index 024c2308df6c6..87747d915af4a 100644
--- a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx
+++ b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx
@@ -106,7 +106,7 @@ export class ClientFileCreateSourceEditor extends Component {
applyGlobalTime: true,
id: '12345',
indexPatternId: 'apm_static_index_pattern_id',
- indexPatternTitle: 'apm-*',
+ indexPatternTitle: 'traces-apm*,logs-apm*,metrics-apm*,apm-*',
metrics: [
{
field: 'transaction.duration.us',
diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts
index adf6f1d7f270d..0b57afb38d585 100644
--- a/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts
+++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/observability/create_layer_descriptor.ts
@@ -39,7 +39,7 @@ import { getDefaultDynamicProperties } from '../../../styles/vector/vector_style
// redefining APM constant to avoid making maps app depend on APM plugin
export const APM_INDEX_PATTERN_ID = 'apm_static_index_pattern_id';
-export const APM_INDEX_PATTERN_TITLE = 'apm-*';
+export const APM_INDEX_PATTERN_TITLE = 'traces-apm*,logs-apm*,metrics-apm*,apm-*';
const defaultDynamicProperties = getDefaultDynamicProperties();
diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts
index 9c6e72fc11d3a..a3a3e8b20f678 100644
--- a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts
+++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.test.ts
@@ -706,4 +706,343 @@ describe('createLayerDescriptor', () => {
},
]);
});
+
+ test('apm data stream', () => {
+ expect(createSecurityLayerDescriptors('id', 'traces-apm-opbean-node')).toEqual([
+ {
+ __dataRequests: [],
+ alpha: 0.75,
+ id: '12345',
+ includeInFitToBounds: true,
+ joins: [],
+ label: 'traces-apm-opbean-node | Source Point',
+ maxZoom: 24,
+ minZoom: 0,
+ sourceDescriptor: {
+ applyGlobalQuery: true,
+ applyGlobalTime: true,
+ filterByMapBounds: true,
+ geoField: 'client.geo.location',
+ id: '12345',
+ indexPatternId: 'id',
+ scalingType: 'TOP_HITS',
+ sortField: '',
+ sortOrder: 'desc',
+ tooltipProperties: [
+ 'host.name',
+ 'client.ip',
+ 'client.domain',
+ 'client.geo.country_iso_code',
+ 'client.as.organization.name',
+ ],
+ topHitsSize: 1,
+ topHitsSplitField: 'client.ip',
+ type: 'ES_SEARCH',
+ },
+ style: {
+ isTimeAware: true,
+ properties: {
+ fillColor: {
+ options: {
+ color: '#6092C0',
+ },
+ type: 'STATIC',
+ },
+ icon: {
+ options: {
+ value: 'home',
+ },
+ type: 'STATIC',
+ },
+ iconOrientation: {
+ options: {
+ orientation: 0,
+ },
+ type: 'STATIC',
+ },
+ iconSize: {
+ options: {
+ size: 8,
+ },
+ type: 'STATIC',
+ },
+ labelBorderColor: {
+ options: {
+ color: '#FFFFFF',
+ },
+ type: 'STATIC',
+ },
+ labelBorderSize: {
+ options: {
+ size: 'SMALL',
+ },
+ },
+ labelColor: {
+ options: {
+ color: '#000000',
+ },
+ type: 'STATIC',
+ },
+ labelSize: {
+ options: {
+ size: 14,
+ },
+ type: 'STATIC',
+ },
+ labelText: {
+ options: {
+ value: '',
+ },
+ type: 'STATIC',
+ },
+ lineColor: {
+ options: {
+ color: '#FFFFFF',
+ },
+ type: 'STATIC',
+ },
+ lineWidth: {
+ options: {
+ size: 2,
+ },
+ type: 'STATIC',
+ },
+ symbolizeAs: {
+ options: {
+ value: 'icon',
+ },
+ },
+ },
+ type: 'VECTOR',
+ },
+ type: 'VECTOR',
+ visible: true,
+ },
+ {
+ __dataRequests: [],
+ alpha: 0.75,
+ id: '12345',
+ includeInFitToBounds: true,
+ joins: [],
+ label: 'traces-apm-opbean-node | Destination point',
+ maxZoom: 24,
+ minZoom: 0,
+ sourceDescriptor: {
+ applyGlobalQuery: true,
+ applyGlobalTime: true,
+ filterByMapBounds: true,
+ geoField: 'server.geo.location',
+ id: '12345',
+ indexPatternId: 'id',
+ scalingType: 'TOP_HITS',
+ sortField: '',
+ sortOrder: 'desc',
+ tooltipProperties: [
+ 'host.name',
+ 'server.ip',
+ 'server.domain',
+ 'server.geo.country_iso_code',
+ 'server.as.organization.name',
+ ],
+ topHitsSize: 1,
+ topHitsSplitField: 'server.ip',
+ type: 'ES_SEARCH',
+ },
+ style: {
+ isTimeAware: true,
+ properties: {
+ fillColor: {
+ options: {
+ color: '#D36086',
+ },
+ type: 'STATIC',
+ },
+ icon: {
+ options: {
+ value: 'marker',
+ },
+ type: 'STATIC',
+ },
+ iconOrientation: {
+ options: {
+ orientation: 0,
+ },
+ type: 'STATIC',
+ },
+ iconSize: {
+ options: {
+ size: 8,
+ },
+ type: 'STATIC',
+ },
+ labelBorderColor: {
+ options: {
+ color: '#FFFFFF',
+ },
+ type: 'STATIC',
+ },
+ labelBorderSize: {
+ options: {
+ size: 'SMALL',
+ },
+ },
+ labelColor: {
+ options: {
+ color: '#000000',
+ },
+ type: 'STATIC',
+ },
+ labelSize: {
+ options: {
+ size: 14,
+ },
+ type: 'STATIC',
+ },
+ labelText: {
+ options: {
+ value: '',
+ },
+ type: 'STATIC',
+ },
+ lineColor: {
+ options: {
+ color: '#FFFFFF',
+ },
+ type: 'STATIC',
+ },
+ lineWidth: {
+ options: {
+ size: 2,
+ },
+ type: 'STATIC',
+ },
+ symbolizeAs: {
+ options: {
+ value: 'icon',
+ },
+ },
+ },
+ type: 'VECTOR',
+ },
+ type: 'VECTOR',
+ visible: true,
+ },
+ {
+ __dataRequests: [],
+ alpha: 0.75,
+ id: '12345',
+ includeInFitToBounds: true,
+ joins: [],
+ label: 'traces-apm-opbean-node | Line',
+ maxZoom: 24,
+ minZoom: 0,
+ sourceDescriptor: {
+ applyGlobalQuery: true,
+ applyGlobalTime: true,
+ destGeoField: 'server.geo.location',
+ id: '12345',
+ indexPatternId: 'id',
+ metrics: [
+ {
+ field: 'client.bytes',
+ type: 'sum',
+ },
+ {
+ field: 'server.bytes',
+ type: 'sum',
+ },
+ ],
+ sourceGeoField: 'client.geo.location',
+ type: 'ES_PEW_PEW',
+ },
+ style: {
+ isTimeAware: true,
+ properties: {
+ fillColor: {
+ options: {
+ color: '#54B399',
+ },
+ type: 'STATIC',
+ },
+ icon: {
+ options: {
+ value: 'marker',
+ },
+ type: 'STATIC',
+ },
+ iconOrientation: {
+ options: {
+ orientation: 0,
+ },
+ type: 'STATIC',
+ },
+ iconSize: {
+ options: {
+ size: 6,
+ },
+ type: 'STATIC',
+ },
+ labelBorderColor: {
+ options: {
+ color: '#FFFFFF',
+ },
+ type: 'STATIC',
+ },
+ labelBorderSize: {
+ options: {
+ size: 'SMALL',
+ },
+ },
+ labelColor: {
+ options: {
+ color: '#000000',
+ },
+ type: 'STATIC',
+ },
+ labelSize: {
+ options: {
+ size: 14,
+ },
+ type: 'STATIC',
+ },
+ labelText: {
+ options: {
+ value: '',
+ },
+ type: 'STATIC',
+ },
+ lineColor: {
+ options: {
+ color: '#6092C0',
+ },
+ type: 'STATIC',
+ },
+ lineWidth: {
+ options: {
+ field: {
+ name: 'doc_count',
+ origin: 'source',
+ },
+ fieldMetaOptions: {
+ isEnabled: true,
+ sigma: 3,
+ },
+ maxSize: 8,
+ minSize: 1,
+ },
+ type: 'DYNAMIC',
+ },
+ symbolizeAs: {
+ options: {
+ value: 'circle',
+ },
+ },
+ },
+ type: 'VECTOR',
+ },
+ type: 'VECTOR',
+ visible: true,
+ },
+ ]);
+ });
});
diff --git a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts
index b2283196a41dd..8a40ba63bed0d 100644
--- a/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts
+++ b/x-pack/plugins/maps/public/classes/layers/solution_layers/security/create_layer_descriptors.ts
@@ -35,7 +35,11 @@ const defaultDynamicProperties = getDefaultDynamicProperties();
const euiVisColorPalette = euiPaletteColorBlind();
function isApmIndex(indexPatternTitle: string) {
- return minimatch(indexPatternTitle, APM_INDEX_PATTERN_TITLE);
+ return APM_INDEX_PATTERN_TITLE.split(',')
+ .map((pattern) => {
+ return minimatch(indexPatternTitle, pattern);
+ })
+ .some(Boolean);
}
function getSourceField(indexPatternTitle: string) {
diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx
index 880a47d0981cb..6277411fa053a 100644
--- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx
+++ b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx
@@ -95,13 +95,13 @@ export class TiledVectorLayer extends VectorLayer {
? i18n.translate('xpack.maps.tiles.resultsTrimmedMsg', {
defaultMessage: `Results limited to {count} documents.`,
values: {
- count: totalFeaturesCount,
+ count: totalFeaturesCount.toLocaleString(),
},
})
: i18n.translate('xpack.maps.tiles.resultsCompleteMsg', {
defaultMessage: `Found {count} documents.`,
values: {
- count: totalFeaturesCount,
+ count: totalFeaturesCount.toLocaleString(),
},
}),
areResultsTrimmed: isIncomplete,
diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.test.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.test.ts
new file mode 100644
index 0000000000000..aad1b693be79b
--- /dev/null
+++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.test.ts
@@ -0,0 +1,285 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import sinon from 'sinon';
+import _ from 'lodash';
+import { FeatureCollection } from 'geojson';
+import { ESTermSourceDescriptor } from '../../../../common/descriptor_types';
+import {
+ AGG_TYPE,
+ FEATURE_VISIBLE_PROPERTY_NAME,
+ SOURCE_TYPES,
+} from '../../../../common/constants';
+import { performInnerJoins } from './perform_inner_joins';
+import { InnerJoin } from '../../joins/inner_join';
+import { IVectorSource } from '../../sources/vector_source';
+import { IField } from '../../fields/field';
+
+const LEFT_FIELD = 'leftKey';
+const COUNT_PROPERTY_NAME = '__kbnjoin__count__d3625663-5b34-4d50-a784-0d743f676a0c';
+const featureCollection = {
+ type: 'FeatureCollection',
+ features: [
+ {
+ type: 'Feature',
+ properties: {
+ [FEATURE_VISIBLE_PROPERTY_NAME]: false,
+ [LEFT_FIELD]: 'alpha',
+ },
+ geometry: {
+ type: 'Point',
+ coordinates: [-112, 46],
+ },
+ },
+ {
+ type: 'Feature',
+ properties: {
+ [COUNT_PROPERTY_NAME]: 20,
+ [FEATURE_VISIBLE_PROPERTY_NAME]: true,
+ [LEFT_FIELD]: 'bravo',
+ },
+ geometry: {
+ type: 'Point',
+ coordinates: [-100, 40],
+ },
+ },
+ ],
+};
+
+const joinDescriptor = {
+ leftField: LEFT_FIELD,
+ right: {
+ applyGlobalQuery: true,
+ applyGlobalTime: true,
+ id: 'd3625663-5b34-4d50-a784-0d743f676a0c',
+ indexPatternId: 'myIndexPattern',
+ metrics: [
+ {
+ type: AGG_TYPE.COUNT,
+ },
+ ],
+ term: 'rightKey',
+ type: SOURCE_TYPES.ES_TERM_SOURCE,
+ } as ESTermSourceDescriptor,
+};
+const mockVectorSource = ({
+ getInspectorAdapters: () => {
+ return undefined;
+ },
+ createField: () => {
+ return {
+ getName: () => {
+ return LEFT_FIELD;
+ },
+ } as IField;
+ },
+} as unknown) as IVectorSource;
+const innerJoin = new InnerJoin(joinDescriptor, mockVectorSource);
+const propertiesMap = new Map>();
+propertiesMap.set('alpha', { [COUNT_PROPERTY_NAME]: 1 });
+
+test('should skip join when no state has changed', () => {
+ const updateSourceData = sinon.spy();
+ const onJoinError = sinon.spy();
+
+ performInnerJoins(
+ {
+ refreshed: false,
+ featureCollection: _.cloneDeep(featureCollection) as FeatureCollection,
+ },
+ [
+ {
+ dataHasChanged: false,
+ join: innerJoin,
+ },
+ ],
+ updateSourceData,
+ onJoinError
+ );
+
+ expect(updateSourceData.notCalled);
+ expect(onJoinError.notCalled);
+});
+
+test('should perform join when features change', () => {
+ const updateSourceData = sinon.spy();
+ const onJoinError = sinon.spy();
+
+ performInnerJoins(
+ {
+ refreshed: true,
+ featureCollection: _.cloneDeep(featureCollection) as FeatureCollection,
+ },
+ [
+ {
+ dataHasChanged: false,
+ join: innerJoin,
+ },
+ ],
+ updateSourceData,
+ onJoinError
+ );
+
+ expect(updateSourceData.calledOnce);
+ expect(onJoinError.notCalled);
+});
+
+test('should perform join when join state changes', () => {
+ const updateSourceData = sinon.spy();
+ const onJoinError = sinon.spy();
+
+ performInnerJoins(
+ {
+ refreshed: false,
+ featureCollection: _.cloneDeep(featureCollection) as FeatureCollection,
+ },
+ [
+ {
+ dataHasChanged: true,
+ join: innerJoin,
+ },
+ ],
+ updateSourceData,
+ onJoinError
+ );
+
+ expect(updateSourceData.calledOnce);
+ expect(onJoinError.notCalled);
+});
+
+test('should call updateSourceData with feature collection with updated feature visibility and join properties', () => {
+ const updateSourceData = sinon.spy();
+ const onJoinError = sinon.spy();
+
+ performInnerJoins(
+ {
+ refreshed: true,
+ featureCollection: _.cloneDeep(featureCollection) as FeatureCollection,
+ },
+ [
+ {
+ dataHasChanged: false,
+ join: innerJoin,
+ propertiesMap,
+ },
+ ],
+ updateSourceData,
+ onJoinError
+ );
+
+ const firstCallArgs = updateSourceData.args[0];
+ const updateSourceDataFeatureCollection = firstCallArgs[0];
+ expect(updateSourceDataFeatureCollection).toEqual({
+ type: 'FeatureCollection',
+ features: [
+ {
+ type: 'Feature',
+ properties: {
+ [COUNT_PROPERTY_NAME]: 1,
+ [FEATURE_VISIBLE_PROPERTY_NAME]: true,
+ [LEFT_FIELD]: 'alpha',
+ },
+ geometry: {
+ type: 'Point',
+ coordinates: [-112, 46],
+ },
+ },
+ {
+ type: 'Feature',
+ properties: {
+ [FEATURE_VISIBLE_PROPERTY_NAME]: false,
+ [LEFT_FIELD]: 'bravo',
+ },
+ geometry: {
+ type: 'Point',
+ coordinates: [-100, 40],
+ },
+ },
+ ],
+ });
+ expect(onJoinError.notCalled);
+});
+
+test('should call updateSourceData when no results returned from terms aggregation', () => {
+ const updateSourceData = sinon.spy();
+ const onJoinError = sinon.spy();
+
+ performInnerJoins(
+ {
+ refreshed: false,
+ featureCollection: _.cloneDeep(featureCollection) as FeatureCollection,
+ },
+ [
+ {
+ dataHasChanged: true,
+ join: innerJoin,
+ },
+ ],
+ updateSourceData,
+ onJoinError
+ );
+
+ const firstCallArgs = updateSourceData.args[0];
+ const updateSourceDataFeatureCollection = firstCallArgs[0];
+ expect(updateSourceDataFeatureCollection).toEqual({
+ type: 'FeatureCollection',
+ features: [
+ {
+ type: 'Feature',
+ properties: {
+ [FEATURE_VISIBLE_PROPERTY_NAME]: false,
+ [LEFT_FIELD]: 'alpha',
+ },
+ geometry: {
+ type: 'Point',
+ coordinates: [-112, 46],
+ },
+ },
+ {
+ type: 'Feature',
+ properties: {
+ [COUNT_PROPERTY_NAME]: 20,
+ [FEATURE_VISIBLE_PROPERTY_NAME]: false,
+ [LEFT_FIELD]: 'bravo',
+ },
+ geometry: {
+ type: 'Point',
+ coordinates: [-100, 40],
+ },
+ },
+ ],
+ });
+ expect(onJoinError.notCalled);
+});
+
+test('should call onJoinError when there are no matching features', () => {
+ const updateSourceData = sinon.spy();
+ const onJoinError = sinon.spy();
+
+ // instead of returning military alphabet like "alpha" or "bravo", mismatched key returns numbers, like '1'
+ const propertiesMapFromMismatchedKey = new Map>();
+ propertiesMapFromMismatchedKey.set('1', { [COUNT_PROPERTY_NAME]: 1 });
+
+ performInnerJoins(
+ {
+ refreshed: false,
+ featureCollection: _.cloneDeep(featureCollection) as FeatureCollection,
+ },
+ [
+ {
+ dataHasChanged: true,
+ join: innerJoin,
+ propertiesMap: propertiesMapFromMismatchedKey,
+ },
+ ],
+ updateSourceData,
+ onJoinError
+ );
+
+ expect(updateSourceData.notCalled);
+ expect(onJoinError.calledOnce);
+});
diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.ts
new file mode 100644
index 0000000000000..23c6527d3e818
--- /dev/null
+++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/perform_inner_joins.ts
@@ -0,0 +1,134 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FeatureCollection } from 'geojson';
+import { i18n } from '@kbn/i18n';
+import { FEATURE_VISIBLE_PROPERTY_NAME } from '../../../../common/constants';
+import { DataRequestContext } from '../../../actions';
+import { InnerJoin } from '../../joins/inner_join';
+import { PropertiesMap } from '../../../../common/elasticsearch_util';
+
+interface SourceResult {
+ refreshed: boolean;
+ featureCollection: FeatureCollection;
+}
+
+export interface JoinState {
+ dataHasChanged: boolean;
+ join: InnerJoin;
+ propertiesMap?: PropertiesMap;
+}
+
+export function performInnerJoins(
+ sourceResult: SourceResult,
+ joinStates: JoinState[],
+ updateSourceData: DataRequestContext['updateSourceData'],
+ onJoinError: DataRequestContext['onJoinError']
+) {
+ // should update the store if
+ // -- source result was refreshed
+ // -- any of the join configurations changed (joinState changed)
+ // -- visibility of any of the features has changed
+
+ let shouldUpdateStore =
+ sourceResult.refreshed || joinStates.some((joinState) => joinState.dataHasChanged);
+
+ if (!shouldUpdateStore) {
+ return;
+ }
+
+ const joinStatuses = joinStates.map((joinState) => {
+ return {
+ joinedWithAtLeastOneFeature: false,
+ keys: [] as string[],
+ joinState,
+ };
+ });
+
+ for (let i = 0; i < sourceResult.featureCollection.features.length; i++) {
+ const feature = sourceResult.featureCollection.features[i];
+ if (!feature.properties) {
+ feature.properties = {};
+ }
+ const oldVisbility = feature.properties[FEATURE_VISIBLE_PROPERTY_NAME];
+ let isFeatureVisible = true;
+ for (let j = 0; j < joinStates.length; j++) {
+ const joinState = joinStates[j];
+ const innerJoin = joinState.join;
+ const joinStatus = joinStatuses[j];
+ const joinKey = innerJoin.getJoinKey(feature);
+ if (joinKey !== null) {
+ joinStatus.keys.push(joinKey);
+ }
+ const canJoinOnCurrent = joinState.propertiesMap
+ ? innerJoin.joinPropertiesToFeature(feature, joinState.propertiesMap)
+ : false;
+ if (canJoinOnCurrent && !joinStatus.joinedWithAtLeastOneFeature) {
+ joinStatus.joinedWithAtLeastOneFeature = true;
+ }
+ isFeatureVisible = isFeatureVisible && canJoinOnCurrent;
+ }
+
+ if (oldVisbility !== isFeatureVisible) {
+ shouldUpdateStore = true;
+ }
+
+ feature.properties[FEATURE_VISIBLE_PROPERTY_NAME] = isFeatureVisible;
+ }
+
+ if (shouldUpdateStore) {
+ updateSourceData({ ...sourceResult.featureCollection });
+ }
+
+ const joinStatusesWithoutAnyMatches = joinStatuses.filter((joinStatus) => {
+ return (
+ !joinStatus.joinedWithAtLeastOneFeature && joinStatus.joinState.propertiesMap !== undefined
+ );
+ });
+
+ if (joinStatusesWithoutAnyMatches.length) {
+ function prettyPrintArray(array: unknown[]) {
+ return array.length <= 5
+ ? array.join(',')
+ : array.slice(0, 5).join(',') +
+ i18n.translate('xpack.maps.vectorLayer.joinError.firstTenMsg', {
+ defaultMessage: ` (5 of {total})`,
+ values: { total: array.length },
+ });
+ }
+
+ const joinStatus = joinStatusesWithoutAnyMatches[0];
+ const leftFieldName = joinStatus.joinState.join.getLeftField().getName();
+ const reason =
+ joinStatus.keys.length === 0
+ ? i18n.translate('xpack.maps.vectorLayer.joinError.noLeftFieldValuesMsg', {
+ defaultMessage: `Left field: '{leftFieldName}', does not provide any values.`,
+ values: { leftFieldName },
+ })
+ : i18n.translate('xpack.maps.vectorLayer.joinError.noMatchesMsg', {
+ defaultMessage: `Left field does not match right field. Left field: '{leftFieldName}' has values { leftFieldValues }. Right field: '{rightFieldName}' has values: { rightFieldValues }.`,
+ values: {
+ leftFieldName,
+ leftFieldValues: prettyPrintArray(joinStatus.keys),
+ rightFieldName: joinStatus.joinState.join
+ .getRightJoinSource()
+ .getTermField()
+ .getName(),
+ rightFieldValues: prettyPrintArray(
+ Array.from(joinStatus.joinState.propertiesMap!.keys())
+ ),
+ },
+ });
+
+ onJoinError(
+ i18n.translate('xpack.maps.vectorLayer.joinErrorMsg', {
+ defaultMessage: `Unable to perform term join. {reason}`,
+ values: { reason },
+ })
+ );
+ }
+}
diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx
index 959064b3daab2..74a0ea1e63882 100644
--- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx
+++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx
@@ -69,17 +69,7 @@ import { IESSource } from '../../sources/es_source';
import { PropertiesMap } from '../../../../common/elasticsearch_util';
import { ITermJoinSource } from '../../sources/term_join_source';
import { addGeoJsonMbSource, getVectorSourceBounds, syncVectorSource } from './utils';
-
-interface SourceResult {
- refreshed: boolean;
- featureCollection?: FeatureCollection;
-}
-
-interface JoinState {
- dataHasChanged: boolean;
- join: InnerJoin;
- propertiesMap?: PropertiesMap;
-}
+import { JoinState, performInnerJoins } from './perform_inner_joins';
export interface VectorLayerArguments {
source: IVectorSource;
@@ -435,51 +425,6 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
};
}
- _performInnerJoins(
- sourceResult: SourceResult,
- joinStates: JoinState[],
- updateSourceData: DataRequestContext['updateSourceData']
- ) {
- // should update the store if
- // -- source result was refreshed
- // -- any of the join configurations changed (joinState changed)
- // -- visibility of any of the features has changed
-
- let shouldUpdateStore =
- sourceResult.refreshed || joinStates.some((joinState) => joinState.dataHasChanged);
-
- if (!shouldUpdateStore) {
- return;
- }
-
- for (let i = 0; i < sourceResult.featureCollection!.features.length; i++) {
- const feature = sourceResult.featureCollection!.features[i];
- if (!feature.properties) {
- feature.properties = {};
- }
- const oldVisbility = feature.properties[FEATURE_VISIBLE_PROPERTY_NAME];
- let isFeatureVisible = true;
- for (let j = 0; j < joinStates.length; j++) {
- const joinState = joinStates[j];
- const innerJoin = joinState.join;
- const canJoinOnCurrent = joinState.propertiesMap
- ? innerJoin.joinPropertiesToFeature(feature, joinState.propertiesMap)
- : false;
- isFeatureVisible = isFeatureVisible && canJoinOnCurrent;
- }
-
- if (oldVisbility !== isFeatureVisible) {
- shouldUpdateStore = true;
- }
-
- feature.properties[FEATURE_VISIBLE_PROPERTY_NAME] = isFeatureVisible;
- }
-
- if (shouldUpdateStore) {
- updateSourceData({ ...sourceResult.featureCollection });
- }
- }
-
async _syncSourceStyleMeta(
syncContext: DataRequestContext,
source: IVectorSource,
@@ -714,7 +659,12 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
}
const joinStates = await this._syncJoins(syncContext, style);
- this._performInnerJoins(sourceResult, joinStates, syncContext.updateSourceData);
+ performInnerJoins(
+ sourceResult,
+ joinStates,
+ syncContext.updateSourceData,
+ syncContext.onJoinError
+ );
} catch (error) {
if (!(error instanceof DataRequestAbortError)) {
throw error;
diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.test.ts
index 6d3171312eaec..0c15afff6b051 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.test.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.test.ts
@@ -51,7 +51,7 @@ describe('getSourceTooltipContent', () => {
sourceDataRequest
);
expect(areResultsTrimmed).toBe(true);
- expect(tooltipContent).toBe('Results limited to first 1000 tracks of ~5000.');
+ expect(tooltipContent).toBe('Results limited to first 1,000 tracks of ~5,000.');
});
it('Should show results trimmed icon and message when tracks are trimmed', () => {
@@ -90,7 +90,7 @@ describe('getSourceTooltipContent', () => {
);
expect(areResultsTrimmed).toBe(true);
expect(tooltipContent).toBe(
- 'Results limited to first 1000 tracks of ~5000. 10 of 1000 tracks are incomplete.'
+ 'Results limited to first 1,000 tracks of ~5,000. 10 of 1,000 tracks are incomplete.'
);
});
});
diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx
index 460c1228e50a8..82be83dad43f7 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx
@@ -326,21 +326,21 @@ export class ESGeoLineSource extends AbstractESAggSource {
? i18n.translate('xpack.maps.esGeoLine.areEntitiesTrimmedMsg', {
defaultMessage: `Results limited to first {entityCount} tracks of ~{totalEntities}.`,
values: {
- entityCount: meta.entityCount,
- totalEntities: meta.totalEntities,
+ entityCount: meta.entityCount.toLocaleString(),
+ totalEntities: meta.totalEntities.toLocaleString(),
},
})
: i18n.translate('xpack.maps.esGeoLine.tracksCountMsg', {
defaultMessage: `Found {entityCount} tracks.`,
- values: { entityCount: meta.entityCount },
+ values: { entityCount: meta.entityCount.toLocaleString() },
});
const tracksTrimmedMsg =
meta.numTrimmedTracks > 0
? i18n.translate('xpack.maps.esGeoLine.tracksTrimmedMsg', {
defaultMessage: `{numTrimmedTracks} of {entityCount} tracks are incomplete.`,
values: {
- entityCount: meta.entityCount,
- numTrimmedTracks: meta.numTrimmedTracks,
+ entityCount: meta.entityCount.toLocaleString(),
+ numTrimmedTracks: meta.numTrimmedTracks.toLocaleString(),
},
})
: undefined;
diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx
index 343c366b548f6..55eed588b8840 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx
@@ -620,17 +620,17 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
? i18n.translate('xpack.maps.esSearch.topHitsResultsTrimmedMsg', {
defaultMessage: `Results limited to first {entityCount} entities of ~{totalEntities}.`,
values: {
- entityCount: meta.entityCount,
- totalEntities: meta.totalEntities,
+ entityCount: meta.entityCount?.toLocaleString(),
+ totalEntities: meta.totalEntities?.toLocaleString(),
},
})
: i18n.translate('xpack.maps.esSearch.topHitsEntitiesCountMsg', {
defaultMessage: `Found {entityCount} entities.`,
- values: { entityCount: meta.entityCount },
+ values: { entityCount: meta.entityCount?.toLocaleString() },
});
const docsPerEntityMsg = i18n.translate('xpack.maps.esSearch.topHitsSizeMsg', {
defaultMessage: `Showing top {topHitsSize} documents per entity.`,
- values: { topHitsSize: this._descriptor.topHitsSize },
+ values: { topHitsSize: this._descriptor.topHitsSize?.toLocaleString() },
});
return {
@@ -645,7 +645,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
return {
tooltipContent: i18n.translate('xpack.maps.esSearch.resultsTrimmedMsg', {
defaultMessage: `Results limited to first {count} documents.`,
- values: { count: meta.resultsCount },
+ values: { count: meta.resultsCount?.toLocaleString() },
}),
areResultsTrimmed: true,
};
@@ -654,7 +654,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
return {
tooltipContent: i18n.translate('xpack.maps.esSearch.featureCountMsg', {
defaultMessage: `Found {count} documents.`,
- values: { count: meta.resultsCount },
+ values: { count: meta.resultsCount?.toLocaleString() },
}),
areResultsTrimmed: false,
};
diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/util/__snapshots__/scaling_form.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_search_source/util/__snapshots__/scaling_form.test.tsx.snap
index 03f2594f287ea..99ce13ce326d6 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_search_source/util/__snapshots__/scaling_form.test.tsx.snap
+++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/util/__snapshots__/scaling_form.test.tsx.snap
@@ -28,7 +28,7 @@ exports[`scaling form should disable clusters option when clustering is not supp
@@ -114,14 +114,14 @@ exports[`scaling form should render 1`] = `
{
state = {
- maxResultWindow: DEFAULT_MAX_RESULT_WINDOW,
+ maxResultWindow: DEFAULT_MAX_RESULT_WINDOW.toLocaleString(),
};
_isMounted = false;
@@ -61,7 +61,7 @@ export class ScalingForm extends Component {
const indexPattern = await getIndexPatternService().get(this.props.indexPatternId);
const { maxResultWindow } = await loadIndexSettings(indexPattern!.title);
if (this._isMounted) {
- this.setState({ maxResultWindow });
+ this.setState({ maxResultWindow: maxResultWindow.toLocaleString() });
}
} catch (err) {
return;
@@ -90,7 +90,7 @@ export class ScalingForm extends Component {
{
,
+ "name": "Edit layer settings",
+ "onClick": [Function],
+ "toolTipContent": null,
+ },
],
"title": "Layer actions",
},
diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx
index 83b4d2c2a756b..2a3186f00d7ce 100644
--- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx
+++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx
@@ -158,6 +158,17 @@ export class TOCEntryActionsPopover extends Component {
},
},
];
+ actionItems.push({
+ disabled: this.props.isEditButtonDisabled,
+ name: EDIT_LAYER_SETTINGS_LABEL,
+ icon: ,
+ 'data-test-subj': 'layerSettingsButton',
+ toolTipContent: null,
+ onClick: () => {
+ this._closePopover();
+ this.props.openLayerSettings();
+ },
+ });
if (!this.props.isReadOnly) {
if (this.state.supportsFeatureEditing) {
@@ -186,17 +197,6 @@ export class TOCEntryActionsPopover extends Component {
},
});
}
- actionItems.push({
- disabled: this.props.isEditButtonDisabled,
- name: EDIT_LAYER_SETTINGS_LABEL,
- icon: ,
- 'data-test-subj': 'layerSettingsButton',
- toolTipContent: null,
- onClick: () => {
- this._closePopover();
- this.props.openLayerSettings();
- },
- });
actionItems.push({
name: i18n.translate('xpack.maps.layerTocActions.cloneLayerTitle', {
defaultMessage: 'Clone layer',
diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_button/toc_entry_button.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_button/toc_entry_button.tsx
index 41c2992c77d88..ffad34454bb61 100644
--- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_button/toc_entry_button.tsx
+++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_button/toc_entry_button.tsx
@@ -116,7 +116,7 @@ export class TOCEntryButton extends Component {
footnotes.push({
icon: ,
message: i18n.translate('xpack.maps.layer.isUsingSearchMsg', {
- defaultMessage: 'Results narrowed by search bar',
+ defaultMessage: 'Results narrowed by query and filters',
}),
});
}
diff --git a/x-pack/plugins/ml/common/constants/messages.test.ts b/x-pack/plugins/ml/common/constants/messages.test.ts
index 1141eea2c176d..59fc50757b674 100644
--- a/x-pack/plugins/ml/common/constants/messages.test.ts
+++ b/x-pack/plugins/ml/common/constants/messages.test.ts
@@ -35,7 +35,7 @@ describe('Constants: Messages parseMessages()', () => {
status: 'success',
text: 'Presence of detector functions validated in all detectors.',
url:
- 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#detectors',
+ 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-detectors',
},
{
bucketSpan: '15m',
@@ -44,7 +44,7 @@ describe('Constants: Messages parseMessages()', () => {
status: 'success',
text: 'Format of "15m" is valid and passed validation checks.',
url:
- 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#bucket-span',
+ 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-bucket-span',
},
{
heading: 'Time range',
@@ -58,7 +58,7 @@ describe('Constants: Messages parseMessages()', () => {
status: 'success',
text: 'Valid and within the estimated model memory limit.',
url:
- 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#model-memory-limits',
+ 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-model-memory-limits',
},
]);
});
@@ -79,7 +79,7 @@ describe('Constants: Messages parseMessages()', () => {
status: 'success',
text: 'Presence of detector functions validated in all detectors.',
url:
- 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#detectors',
+ 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-detectors',
},
{
bucketSpan: '15m',
@@ -116,7 +116,7 @@ describe('Constants: Messages parseMessages()', () => {
status: 'success',
text: 'Presence of detector functions validated in all detectors.',
url:
- 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#detectors',
+ 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-detectors',
},
{
id: 'cardinality_model_plot_high',
@@ -131,7 +131,7 @@ describe('Constants: Messages parseMessages()', () => {
text:
'Cardinality of partition_field "order_id" is above 1000 and might result in high memory usage.',
url:
- 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#cardinality',
+ 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-cardinality',
},
{
heading: 'Bucket span',
@@ -140,7 +140,7 @@ describe('Constants: Messages parseMessages()', () => {
text:
'Bucket span is 1 day or more. Be aware that days are considered as UTC days, not local days.',
url:
- 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#bucket-span',
+ 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-bucket-span',
},
{
bucketSpanCompareFactor: 25,
@@ -156,7 +156,7 @@ describe('Constants: Messages parseMessages()', () => {
status: 'success',
text: 'Influencer configuration passed the validation checks.',
url:
- 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-influencers.html',
+ 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-influencers',
},
{
id: 'half_estimated_mml_greater_than_mml',
@@ -165,7 +165,7 @@ describe('Constants: Messages parseMessages()', () => {
text:
'The specified model memory limit is less than half of the estimated model memory limit and will likely hit the hard limit.',
url:
- 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/create-jobs.html#model-memory-limits',
+ 'https://www.elastic.co/guide/en/machine-learning/mocked-test-branch/ml-ad-finding-anomalies.html#ml-ad-model-memory-limits',
},
{
id: 'missing_summary_count_field_name',
diff --git a/x-pack/plugins/ml/common/types/anomalies.ts b/x-pack/plugins/ml/common/types/anomalies.ts
index e84035aa50c8f..2bf717067f712 100644
--- a/x-pack/plugins/ml/common/types/anomalies.ts
+++ b/x-pack/plugins/ml/common/types/anomalies.ts
@@ -12,6 +12,8 @@ export interface Influencer {
influencer_field_values: string[];
}
+export type MLAnomalyDoc = AnomalyRecordDoc;
+
export interface AnomalyRecordDoc {
[key: string]: any;
job_id: string;
diff --git a/x-pack/plugins/ml/public/application/components/help_popover/help_popover.tsx b/x-pack/plugins/ml/public/application/components/help_popover/help_popover.tsx
index 8cd6a3fbd1138..95c66d58dbb75 100644
--- a/x-pack/plugins/ml/public/application/components/help_popover/help_popover.tsx
+++ b/x-pack/plugins/ml/public/application/components/help_popover/help_popover.tsx
@@ -6,6 +6,7 @@
*/
import React, { ReactNode } from 'react';
+import { i18n } from '@kbn/i18n';
import {
EuiButtonIcon,
EuiLinkButtonProps,
@@ -22,6 +23,9 @@ export const HelpPopoverButton = ({ onClick }: { onClick: EuiLinkButtonProps['on
className="mlHelpPopover__buttonIcon"
size="s"
iconType="help"
+ aria-label={i18n.translate('xpack.ml.helpPopover.ariaLabel', {
+ defaultMessage: 'Help',
+ })}
onClick={onClick}
/>
);
diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx
index 6dd4e6c14589b..f282b2fde2b3a 100644
--- a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx
+++ b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx
@@ -42,6 +42,7 @@ import { Anomaly } from '../../../jobs/new_job/common/results_loader/results_loa
import { parseInterval } from '../../../../../common/util/parse_interval';
import { CreateCalendar, CalendarEvent } from './create_calendar';
import { timeFormatter } from '../../../../../common/util/date_utils';
+import { toastNotificationServiceProvider } from '../../../services/toast_notification_service';
interface Props {
snapshot: ModelSnapshot;
@@ -139,6 +140,10 @@ export const RevertModelSnapshotFlyout: FC = ({
})
);
refresh();
+ })
+ .catch((error) => {
+ const { displayErrorToast } = toastNotificationServiceProvider(toasts);
+ displayErrorToast(error);
});
hideRevertModal();
closeFlyout();
diff --git a/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts b/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts
index 621ce44204730..45afab0cce4fd 100644
--- a/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts
+++ b/x-pack/plugins/ml/public/application/explorer/actions/load_explorer_data.ts
@@ -223,7 +223,9 @@ const loadExplorerDataProvider = (
swimlaneLimit,
viewByPerPage,
viewByFromPage,
- swimlaneContainerWidth
+ swimlaneContainerWidth,
+ selectionInfluencers,
+ influencersFilterQuery
)
: Promise.resolve([]),
}).pipe(
diff --git a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx
index 6f5ae5e17590a..57e051e1b8417 100644
--- a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx
+++ b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx
@@ -95,7 +95,6 @@ function getInitSearchInputState({
interface ExplorerQueryBarProps {
filterActive: boolean;
- filterIconTriggeredQuery: string;
filterPlaceHolder: string;
indexPattern: IIndexPattern;
queryString?: string;
@@ -104,7 +103,6 @@ interface ExplorerQueryBarProps {
export const ExplorerQueryBar: FC = ({
filterActive,
- filterIconTriggeredQuery,
filterPlaceHolder,
indexPattern,
queryString,
@@ -116,14 +114,12 @@ export const ExplorerQueryBar: FC = ({
);
const [errorMessage, setErrorMessage] = useState(undefined);
- useEffect(() => {
- if (filterIconTriggeredQuery !== undefined) {
- setSearchInput({
- language: searchInput.language,
- query: filterIconTriggeredQuery,
- });
- }
- }, [filterIconTriggeredQuery]);
+ useEffect(
+ function updateSearchInputFromFilter() {
+ setSearchInput(getInitSearchInputState({ filterActive, queryString }));
+ },
+ [filterActive, queryString]
+ );
const searchChangeHandler = (query: Query) => {
if (searchInput.language !== query.language) {
@@ -131,6 +127,7 @@ export const ExplorerQueryBar: FC = ({
}
setSearchInput(query);
};
+
const applyInfluencersFilterQuery = (query: Query) => {
try {
const { clearSettings, settings } = getKqlQueryValues({
diff --git a/x-pack/plugins/ml/public/application/explorer/explorer.js b/x-pack/plugins/ml/public/application/explorer/explorer.js
index 31058b62af7fe..81474c212d265 100644
--- a/x-pack/plugins/ml/public/application/explorer/explorer.js
+++ b/x-pack/plugins/ml/public/application/explorer/explorer.js
@@ -86,7 +86,6 @@ const ExplorerPage = ({
filterPlaceHolder,
indexPattern,
queryString,
- filterIconTriggeredQuery,
updateLanguage,
}) => (
@@ -121,7 +120,6 @@ const ExplorerPage = ({
filterPlaceHolder={filterPlaceHolder}
indexPattern={indexPattern}
queryString={queryString}
- filterIconTriggeredQuery={filterIconTriggeredQuery}
updateLanguage={updateLanguage}
/>
@@ -151,7 +149,7 @@ export class ExplorerUI extends React.Component {
selectedJobsRunning: PropTypes.bool.isRequired,
};
- state = { filterIconTriggeredQuery: undefined, language: DEFAULT_QUERY_LANG };
+ state = { language: DEFAULT_QUERY_LANG };
htmlIdGen = htmlIdGenerator();
componentDidMount() {
@@ -200,8 +198,6 @@ export class ExplorerUI extends React.Component {
}
}
- this.setState({ filterIconTriggeredQuery: `${newQueryString}` });
-
try {
const { clearSettings, settings } = getKqlQueryValues({
inputString: `${newQueryString}`,
@@ -327,7 +323,6 @@ export class ExplorerUI extends React.Component {
influencers={influencers}
filterActive={filterActive}
filterPlaceHolder={filterPlaceHolder}
- filterIconTriggeredQuery={this.state.filterIconTriggeredQuery}
indexPattern={indexPattern}
queryString={queryString}
updateLanguage={this.updateLanguage}
diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.d.ts b/x-pack/plugins/ml/public/application/explorer/explorer_utils.d.ts
index ebab308b86027..e8abe9e45c09a 100644
--- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.d.ts
+++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.d.ts
@@ -181,15 +181,6 @@ declare interface LoadOverallDataResponse {
overallSwimlaneData: OverallSwimlaneData;
}
-export declare const loadViewByTopFieldValuesForSelectedTime: (
- earliestMs: number,
- latestMs: number,
- selectedJobs: ExplorerJob[],
- viewBySwimlaneFieldName: string,
- swimlaneLimit: number,
- noInfluencersConfigured: boolean
-) => Promise;
-
export declare interface FilterData {
influencersFilterQuery: InfluencersFilterQuery;
filterActive: boolean;
diff --git a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx
index 82f8a90fafb7d..86ec2014c8339 100644
--- a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx
+++ b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx
@@ -411,7 +411,7 @@ export const SwimlaneContainer: FC = ({
>
<>
-
+
{showSwimlane && !isLoading && (
= ({ jobsWithTim
to: globalState.time.to,
});
}
- }, [globalState?.time?.from, globalState?.time?.to]);
+ }, [lastRefresh, globalState?.time?.from, globalState?.time?.to]);
const getJobsWithStoppedPartitions = useCallback(async (selectedJobIds: string[]) => {
try {
diff --git a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx
index ef669a7703c1f..6eb4386276753 100644
--- a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx
+++ b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx
@@ -142,10 +142,10 @@ export const TimeSeriesExplorerUrlStateManager: FC d.id);
@@ -254,7 +258,9 @@ export class AnomalyTimelineService {
latestMs,
swimlaneLimit,
perPage,
- fromPage
+ fromPage,
+ selectionInfluencers,
+ influencersFilterQuery
);
if (resp.influencers[viewBySwimlaneFieldName] === undefined) {
return [];
@@ -276,6 +282,8 @@ export class AnomalyTimelineService {
earliestMs,
latestMs,
this.getSwimlaneBucketInterval(selectedJobs, swimlaneContainerWidth).asMilliseconds(),
+ perPage,
+ fromPage,
swimlaneLimit
);
return Object.keys(resp.results);
diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/results.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/results.ts
index 25ef36782207f..a9f6dbb45f6e3 100644
--- a/x-pack/plugins/ml/public/application/services/ml_api_service/results.ts
+++ b/x-pack/plugins/ml/public/application/services/ml_api_service/results.ts
@@ -16,6 +16,11 @@ import { JobId } from '../../../../common/types/anomaly_detection_jobs';
import { JOB_ID, PARTITION_FIELD_VALUE } from '../../../../common/constants/anomalies';
import { PartitionFieldsDefinition } from '../results_service/result_service_rx';
import { PartitionFieldsConfig } from '../../../../common/types/storage';
+import {
+ ESSearchRequest,
+ ESSearchResponse,
+} from '../../../../../../../src/core/types/elasticsearch';
+import { MLAnomalyDoc } from '../../../../common/types/anomalies';
export const resultsApiProvider = (httpService: HttpService) => ({
getAnomaliesTableData(
@@ -112,18 +117,18 @@ export const resultsApiProvider = (httpService: HttpService) => ({
});
},
- anomalySearch(query: any, jobIds: string[]) {
+ anomalySearch(query: ESSearchRequest, jobIds: string[]) {
const body = JSON.stringify({ query, jobIds });
- return httpService.http({
+ return httpService.http>({
path: `${basePath()}/results/anomaly_search`,
method: 'POST',
body,
});
},
- anomalySearch$(query: any, jobIds: string[]) {
+ anomalySearch$(query: ESSearchRequest, jobIds: string[]) {
const body = JSON.stringify({ query, jobIds });
- return httpService.http$({
+ return httpService.http$>({
path: `${basePath()}/results/anomaly_search`,
method: 'POST',
body,
diff --git a/x-pack/plugins/ml/public/application/services/results_service/results_service.d.ts b/x-pack/plugins/ml/public/application/services/results_service/results_service.d.ts
index ea07d32bfff1d..1848b13cb5a1f 100644
--- a/x-pack/plugins/ml/public/application/services/results_service/results_service.d.ts
+++ b/x-pack/plugins/ml/public/application/services/results_service/results_service.d.ts
@@ -23,7 +23,8 @@ export function resultsServiceProvider(
intervalMs: number,
perPage?: number,
fromPage?: number,
- swimLaneSeverity?: number
+ swimLaneSeverity?: number,
+ influencersFilterQuery?: InfluencersFilterQuery
): Promise;
getTopInfluencers(
selectedJobIds: string[],
@@ -32,7 +33,7 @@ export function resultsServiceProvider(
maxFieldValues: number,
perPage?: number,
fromPage?: number,
- influencers?: any[],
+ influencers?: EntityField[],
influencersFilterQuery?: InfluencersFilterQuery
): Promise;
getTopInfluencerValues(): Promise;
diff --git a/x-pack/plugins/ml/server/models/job_service/model_snapshots.ts b/x-pack/plugins/ml/server/models/job_service/model_snapshots.ts
index 6cb5f67149fb6..56221f9a72c89 100644
--- a/x-pack/plugins/ml/server/models/job_service/model_snapshots.ts
+++ b/x-pack/plugins/ml/server/models/job_service/model_snapshots.ts
@@ -85,7 +85,6 @@ export function modelSnapshotProvider(client: IScopedClusterClient, mlClient: Ml
),
events: calendarEvents.map((s) => ({
calendar_id: calendarId,
- event_id: '',
description: s.description,
start_time: `${s.start}`,
end_time: `${s.end}`,
diff --git a/x-pack/plugins/observability/public/components/app/fleet_panel/index.tsx b/x-pack/plugins/observability/public/components/app/fleet_panel/index.tsx
index fce1cde38f587..b6db8b2308b78 100644
--- a/x-pack/plugins/observability/public/components/app/fleet_panel/index.tsx
+++ b/x-pack/plugins/observability/public/components/app/fleet_panel/index.tsx
@@ -16,9 +16,6 @@ export function FleetPanel() {
return (
{i18n.translate('xpack.observability.fleet.text', {
@@ -30,7 +27,7 @@ export function FleetPanel() {
footer={
{i18n.translate('xpack.observability.fleet.button', {
- defaultMessage: 'Try Fleet Beta',
+ defaultMessage: 'Try Fleet',
})}
}
diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.test.tsx
new file mode 100644
index 0000000000000..c32acc47abd1b
--- /dev/null
+++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.test.tsx
@@ -0,0 +1,131 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useEffect } from 'react';
+
+import { UrlStorageContextProvider, useSeriesStorage } from './use_series_storage';
+import { render } from '@testing-library/react';
+
+const mockSingleSeries = {
+ 'performance-distribution': {
+ reportType: 'data-distribution',
+ dataType: 'ux',
+ breakdown: 'user_agent.name',
+ time: { from: 'now-15m', to: 'now' },
+ },
+};
+
+const mockMultipleSeries = {
+ 'performance-distribution': {
+ reportType: 'data-distribution',
+ dataType: 'ux',
+ breakdown: 'user_agent.name',
+ time: { from: 'now-15m', to: 'now' },
+ },
+ 'kpi-over-time': {
+ reportType: 'kpi-over-time',
+ dataType: 'synthetics',
+ breakdown: 'user_agent.name',
+ time: { from: 'now-15m', to: 'now' },
+ },
+};
+
+describe('userSeries', function () {
+ function setupTestComponent(seriesData: any) {
+ const setData = jest.fn();
+ function TestComponent() {
+ const data = useSeriesStorage();
+
+ useEffect(() => {
+ setData(data);
+ }, [data]);
+
+ return Test;
+ }
+
+ render(
+
+
+
+ );
+
+ return setData;
+ }
+ it('should return expected result when there is one series', function () {
+ const setData = setupTestComponent(mockSingleSeries);
+
+ expect(setData).toHaveBeenCalledTimes(2);
+ expect(setData).toHaveBeenLastCalledWith(
+ expect.objectContaining({
+ allSeries: {
+ 'performance-distribution': {
+ breakdown: 'user_agent.name',
+ dataType: 'ux',
+ reportType: 'data-distribution',
+ time: { from: 'now-15m', to: 'now' },
+ },
+ },
+ allSeriesIds: ['performance-distribution'],
+ firstSeries: {
+ breakdown: 'user_agent.name',
+ dataType: 'ux',
+ reportType: 'data-distribution',
+ time: { from: 'now-15m', to: 'now' },
+ },
+ firstSeriesId: 'performance-distribution',
+ })
+ );
+ });
+
+ it('should return expected result when there are multiple series series', function () {
+ const setData = setupTestComponent(mockMultipleSeries);
+
+ expect(setData).toHaveBeenCalledTimes(2);
+ expect(setData).toHaveBeenLastCalledWith(
+ expect.objectContaining({
+ allSeries: {
+ 'performance-distribution': {
+ breakdown: 'user_agent.name',
+ dataType: 'ux',
+ reportType: 'data-distribution',
+ time: { from: 'now-15m', to: 'now' },
+ },
+ 'kpi-over-time': {
+ reportType: 'kpi-over-time',
+ dataType: 'synthetics',
+ breakdown: 'user_agent.name',
+ time: { from: 'now-15m', to: 'now' },
+ },
+ },
+ allSeriesIds: ['performance-distribution', 'kpi-over-time'],
+ firstSeries: {
+ breakdown: 'user_agent.name',
+ dataType: 'ux',
+ reportType: 'data-distribution',
+ time: { from: 'now-15m', to: 'now' },
+ },
+ firstSeriesId: 'performance-distribution',
+ })
+ );
+ });
+
+ it('should return expected result when there are no series', function () {
+ const setData = setupTestComponent({});
+
+ expect(setData).toHaveBeenCalledTimes(2);
+ expect(setData).toHaveBeenLastCalledWith(
+ expect.objectContaining({
+ allSeries: {},
+ allSeriesIds: [],
+ firstSeries: undefined,
+ firstSeriesId: undefined,
+ })
+ );
+ });
+});
diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.tsx
index 0add5a19a95cc..a47a124d14b4d 100644
--- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.tsx
+++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_series_storage.tsx
@@ -67,7 +67,7 @@ export function UrlStorageContextProvider({
setAllSeries(allSeriesN);
setFirstSeriesId(allSeriesIds?.[0]);
- setFirstSeries(allSeriesN?.[0]);
+ setFirstSeries(allSeriesN?.[allSeriesIds?.[0]]);
(storage as IKbnUrlStateStorage).set(allSeriesKey, allShortSeries);
}, [allShortSeries, storage]);
diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts
index 0561eab08fb45..6bafe465fd024 100644
--- a/x-pack/plugins/observability/public/index.ts
+++ b/x-pack/plugins/observability/public/index.ts
@@ -68,5 +68,9 @@ export { createExploratoryViewUrl } from './components/shared/exploratory_view/c
export { FilterValueLabel } from './components/shared/filter_value_label/filter_value_label';
export type { SeriesUrl } from './components/shared/exploratory_view/types';
-export type { ObservabilityRuleTypeRegistry } from './rules/create_observability_rule_type_registry';
+export type {
+ ObservabilityRuleTypeFormatter,
+ ObservabilityRuleTypeModel,
+ ObservabilityRuleTypeRegistry,
+} from './rules/create_observability_rule_type_registry';
export { createObservabilityRuleTypeRegistryMock } from './rules/observability_rule_type_registry_mock';
diff --git a/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts b/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts
index 35f2dc18c2f22..d6f8c08359888 100644
--- a/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts
+++ b/x-pack/plugins/observability/public/rules/create_observability_rule_type_registry.ts
@@ -5,19 +5,29 @@
* 2.0.
*/
-import { AlertTypeModel, AlertTypeRegistryContract } from '../../../triggers_actions_ui/public';
+import {
+ AlertTypeModel,
+ AlertTypeParams,
+ AlertTypeRegistryContract,
+} from '../../../triggers_actions_ui/public';
import { ParsedTechnicalFields } from '../../../rule_registry/common/parse_technical_fields';
import { AsDuration, AsPercent } from '../../common/utils/formatters';
-export type Formatter = (options: {
+export type ObservabilityRuleTypeFormatter = (options: {
fields: ParsedTechnicalFields & Record;
formatters: { asDuration: AsDuration; asPercent: AsPercent };
}) => { reason: string; link: string };
+export interface ObservabilityRuleTypeModel
+ extends AlertTypeModel {
+ format: ObservabilityRuleTypeFormatter;
+}
+
export function createObservabilityRuleTypeRegistry(alertTypeRegistry: AlertTypeRegistryContract) {
- const formatters: Array<{ typeId: string; fn: Formatter }> = [];
+ const formatters: Array<{ typeId: string; fn: ObservabilityRuleTypeFormatter }> = [];
+
return {
- register: (type: AlertTypeModel & { format: Formatter }) => {
+ register: (type: ObservabilityRuleTypeModel) => {
const { format, ...rest } = type;
formatters.push({ typeId: type.id, fn: format });
alertTypeRegistry.register(rest);
diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts
index 3e8f511eb1153..868e234fcb2a1 100644
--- a/x-pack/plugins/observability/server/plugin.ts
+++ b/x-pack/plugins/observability/server/plugin.ts
@@ -38,47 +38,49 @@ export class ObservabilityPlugin implements Plugin {
}
public setup(core: CoreSetup, plugins: PluginSetup) {
- plugins.features.registerKibanaFeature({
- id: casesFeatureId,
- name: i18n.translate('xpack.observability.featureRegistry.linkObservabilityTitle', {
- defaultMessage: 'Cases',
- }),
- order: 1100,
- category: DEFAULT_APP_CATEGORIES.observability,
- app: [casesFeatureId, 'kibana'],
- catalogue: [observabilityFeatureId],
- cases: [observabilityFeatureId],
- privileges: {
- all: {
- app: [casesFeatureId, 'kibana'],
- catalogue: [observabilityFeatureId],
- cases: {
- all: [observabilityFeatureId],
- },
- api: [],
- savedObject: {
- all: [],
- read: [],
- },
- ui: ['crud_cases', 'read_cases'], // uiCapabilities[casesFeatureId].crud_cases or read_cases
- },
- read: {
- app: [casesFeatureId, 'kibana'],
- catalogue: [observabilityFeatureId],
- cases: {
- read: [observabilityFeatureId],
+ const config = this.initContext.config.get();
+
+ if (config.unsafe.cases.enabled) {
+ plugins.features.registerKibanaFeature({
+ id: casesFeatureId,
+ name: i18n.translate('xpack.observability.featureRegistry.linkObservabilityTitle', {
+ defaultMessage: 'Cases',
+ }),
+ order: 1100,
+ category: DEFAULT_APP_CATEGORIES.observability,
+ app: [casesFeatureId, 'kibana'],
+ catalogue: [observabilityFeatureId],
+ cases: [observabilityFeatureId],
+ privileges: {
+ all: {
+ app: [casesFeatureId, 'kibana'],
+ catalogue: [observabilityFeatureId],
+ cases: {
+ all: [observabilityFeatureId],
+ },
+ api: [],
+ savedObject: {
+ all: [],
+ read: [],
+ },
+ ui: ['crud_cases', 'read_cases'], // uiCapabilities[casesFeatureId].crud_cases or read_cases
},
- api: [],
- savedObject: {
- all: [],
- read: [],
+ read: {
+ app: [casesFeatureId, 'kibana'],
+ catalogue: [observabilityFeatureId],
+ cases: {
+ read: [observabilityFeatureId],
+ },
+ api: [],
+ savedObject: {
+ all: [],
+ read: [],
+ },
+ ui: ['read_cases'], // uiCapabilities[uiCapabilities[casesFeatureId]].read_cases
},
- ui: ['read_cases'], // uiCapabilities[uiCapabilities[casesFeatureId]].read_cases
},
- },
- });
-
- const config = this.initContext.config.get();
+ });
+ }
let annotationsApiPromise: Promise | undefined;
diff --git a/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx b/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx
index 30df2267fbfa1..fc7cee2fc804c 100644
--- a/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx
+++ b/x-pack/plugins/osquery/public/saved_queries/saved_queries_dropdown.tsx
@@ -6,7 +6,7 @@
*/
import { find } from 'lodash/fp';
-import { EuiCodeBlock, EuiFormRow, EuiComboBox, EuiText } from '@elastic/eui';
+import { EuiCodeBlock, EuiFormRow, EuiComboBox, EuiTextColor } from '@elastic/eui';
import React, {
forwardRef,
useCallback,
@@ -19,6 +19,7 @@ import { SimpleSavedObject } from 'kibana/public';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { useHistory, useLocation } from 'react-router-dom';
+import styled from 'styled-components';
import { useSavedQueries } from './use_saved_queries';
@@ -26,6 +27,17 @@ export interface SavedQueriesDropdownRef {
clearSelection: () => void;
}
+const TextTruncate = styled.div`
+ overflow: hidden;
+ text-overflow: ellipsis;
+`;
+
+const StyledEuiCodeBlock = styled(EuiCodeBlock)`
+ .euiCodeBlock__line {
+ white-space: nowrap;
+ }
+`;
+
interface SavedQueriesDropdownProps {
disabled?: boolean;
onChange: (
@@ -88,12 +100,12 @@ const SavedQueriesDropdownComponent = forwardRef<
({ value }) => (
<>
{value.id}
-
- {value.description}
-
-
- {value.query}
-
+
+ {value.description}
+
+
+ {value.query.split('\n').join(' ')}
+
>
),
[]
@@ -145,7 +157,7 @@ const SavedQueriesDropdownComponent = forwardRef<
selectedOptions={selectedOptions}
onChange={handleSavedQueryChange}
renderOption={renderOption}
- rowHeight={90}
+ rowHeight={110}
/>
);
diff --git a/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts b/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts
index 1260413676a4e..6f4aa51710811 100644
--- a/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts
+++ b/x-pack/plugins/osquery/public/saved_queries/use_update_saved_query.ts
@@ -56,7 +56,7 @@ export const useUpdateSavedQuery = ({ savedQueryId }: UseUpdateSavedQueryProps)
i18n.translate('xpack.osquery.editSavedQuery.successToastMessageText', {
defaultMessage: 'Successfully updated "{savedQueryName}" query',
values: {
- savedQueryName: payload.attributes?.name ?? '',
+ savedQueryName: payload.attributes?.id ?? '',
},
})
);
diff --git a/x-pack/plugins/reporting/public/management/ilm_policy_link.tsx b/x-pack/plugins/reporting/public/management/ilm_policy_link.tsx
index 3945ec5be9fa7..a40f167de5bc3 100644
--- a/x-pack/plugins/reporting/public/management/ilm_policy_link.tsx
+++ b/x-pack/plugins/reporting/public/management/ilm_policy_link.tsx
@@ -21,7 +21,7 @@ interface Props {
const i18nTexts = {
buttonLabel: i18n.translate('xpack.reporting.listing.reports.ilmPolicyLinkText', {
- defaultMessage: 'Edit ILM policy',
+ defaultMessage: 'Edit reporting ILM policy',
}),
};
diff --git a/x-pack/plugins/reporting/public/management/migrate_ilm_policy_callout/ilm_policy_migration_needed_callout.tsx b/x-pack/plugins/reporting/public/management/migrate_ilm_policy_callout/ilm_policy_migration_needed_callout.tsx
index 5bb3ac524e130..e96cb842d55cf 100644
--- a/x-pack/plugins/reporting/public/management/migrate_ilm_policy_callout/ilm_policy_migration_needed_callout.tsx
+++ b/x-pack/plugins/reporting/public/management/migrate_ilm_policy_callout/ilm_policy_migration_needed_callout.tsx
@@ -19,12 +19,12 @@ import { useInternalApiClient } from '../../lib/reporting_api_client';
const i18nTexts = {
title: i18n.translate('xpack.reporting.listing.ilmPolicyCallout.migrationNeededTitle', {
- defaultMessage: 'Migrate reporting indices',
+ defaultMessage: 'Apply new lifecycle policy for reports',
}),
description: (
{ILM_POLICY_NAME},
}}
@@ -33,7 +33,10 @@ const i18nTexts = {
buttonLabel: i18n.translate(
'xpack.reporting.listing.ilmPolicyCallout.migrateIndicesButtonLabel',
{
- defaultMessage: 'Migrate indices',
+ defaultMessage: 'Apply {ilmPolicyName} policy',
+ values: {
+ ilmPolicyName: ILM_POLICY_NAME,
+ },
}
),
migrateErrorTitle: i18n.translate(
@@ -45,7 +48,7 @@ const i18nTexts = {
migrateSuccessTitle: i18n.translate(
'xpack.reporting.listing.ilmPolicyCallout.migrateIndicesSuccessTitle',
{
- defaultMessage: 'Successfully migrated reporting indices',
+ defaultMessage: 'Reporting policy active for all reporting indices',
}
),
};
diff --git a/x-pack/plugins/rule_registry/server/index.ts b/x-pack/plugins/rule_registry/server/index.ts
index b6fd6b9a605c0..19ea85b056bed 100644
--- a/x-pack/plugins/rule_registry/server/index.ts
+++ b/x-pack/plugins/rule_registry/server/index.ts
@@ -15,6 +15,11 @@ export { RuleDataClient } from './rule_data_client';
export { IRuleDataClient } from './rule_data_client/types';
export { getRuleExecutorData, RuleExecutorData } from './utils/get_rule_executor_data';
export { createLifecycleRuleTypeFactory } from './utils/create_lifecycle_rule_type_factory';
+export {
+ LifecycleRuleExecutor,
+ LifecycleAlertServices,
+ createLifecycleExecutor,
+} from './utils/create_lifecycle_executor';
export { createPersistenceRuleTypeFactory } from './utils/create_persistence_rule_type_factory';
export const plugin = (initContext: PluginInitializerContext) =>
diff --git a/x-pack/plugins/rule_registry/server/types.ts b/x-pack/plugins/rule_registry/server/types.ts
index f8bd1940b10a8..051789b1896bb 100644
--- a/x-pack/plugins/rule_registry/server/types.ts
+++ b/x-pack/plugins/rule_registry/server/types.ts
@@ -12,7 +12,7 @@ import {
AlertTypeParams,
AlertTypeState,
} from '../../alerting/common';
-import { AlertType } from '../../alerting/server';
+import { AlertExecutorOptions, AlertServices, AlertType } from '../../alerting/server';
import { AlertsClient } from './alert_data_client/alerts_client';
type SimpleAlertType<
@@ -41,6 +41,20 @@ export type AlertTypeWithExecutor<
executor: AlertTypeExecutor;
};
+export type AlertExecutorOptionsWithExtraServices<
+ Params extends AlertTypeParams = never,
+ State extends AlertTypeState = never,
+ InstanceState extends AlertInstanceState = never,
+ InstanceContext extends AlertInstanceContext = never,
+ ActionGroupIds extends string = never,
+ TExtraServices extends {} = never
+> = Omit<
+ AlertExecutorOptions,
+ 'services'
+> & {
+ services: AlertServices & TExtraServices;
+};
+
/**
* @public
*/
diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts
new file mode 100644
index 0000000000000..06c2cc8ff005d
--- /dev/null
+++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_executor.ts
@@ -0,0 +1,330 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { Logger } from '@kbn/logging';
+import { getOrElse } from 'fp-ts/lib/Either';
+import * as rt from 'io-ts';
+import { Mutable } from 'utility-types';
+import { v4 } from 'uuid';
+import {
+ AlertExecutorOptions,
+ AlertInstance,
+ AlertInstanceContext,
+ AlertInstanceState,
+ AlertTypeParams,
+ AlertTypeState,
+} from '../../../alerting/server';
+import { ParsedTechnicalFields, parseTechnicalFields } from '../../common/parse_technical_fields';
+import {
+ ALERT_DURATION,
+ ALERT_END,
+ ALERT_ID,
+ ALERT_START,
+ ALERT_STATUS,
+ ALERT_UUID,
+ EVENT_ACTION,
+ EVENT_KIND,
+ OWNER,
+ RULE_UUID,
+ TIMESTAMP,
+} from '../../common/technical_rule_data_field_names';
+import { RuleDataClient } from '../rule_data_client';
+import { AlertExecutorOptionsWithExtraServices } from '../types';
+import { getRuleData } from './get_rule_executor_data';
+
+type LifecycleAlertService<
+ InstanceState extends AlertInstanceState = never,
+ InstanceContext extends AlertInstanceContext = never,
+ ActionGroupIds extends string = never
+> = (alert: {
+ id: string;
+ fields: Record;
+}) => AlertInstance;
+
+export interface LifecycleAlertServices<
+ InstanceState extends AlertInstanceState = never,
+ InstanceContext extends AlertInstanceContext = never,
+ ActionGroupIds extends string = never
+> {
+ alertWithLifecycle: LifecycleAlertService;
+}
+
+export type LifecycleRuleExecutor<
+ Params extends AlertTypeParams = never,
+ State extends AlertTypeState = never,
+ InstanceState extends AlertInstanceState = never,
+ InstanceContext extends AlertInstanceContext = never,
+ ActionGroupIds extends string = never
+> = (
+ options: AlertExecutorOptionsWithExtraServices<
+ Params,
+ State,
+ InstanceState,
+ InstanceContext,
+ ActionGroupIds,
+ LifecycleAlertServices
+ >
+) => Promise;
+
+const trackedAlertStateRt = rt.type({
+ alertId: rt.string,
+ alertUuid: rt.string,
+ started: rt.string,
+});
+
+export type TrackedLifecycleAlertState = rt.TypeOf;
+
+const alertTypeStateRt = () =>
+ rt.record(rt.string, rt.unknown) as rt.Type;
+
+const wrappedStateRt = () =>
+ rt.type({
+ wrapped: alertTypeStateRt(),
+ trackedAlerts: rt.record(rt.string, trackedAlertStateRt),
+ });
+
+/**
+ * This is redefined instead of derived from above `wrappedStateRt` because
+ * there's no easy way to instantiate generic values such as the runtime type
+ * factory function.
+ */
+export type WrappedLifecycleRuleState = AlertTypeState & {
+ wrapped: State | void;
+ trackedAlerts: Record;
+};
+
+export const createLifecycleExecutor = (logger: Logger, ruleDataClient: RuleDataClient) => <
+ Params extends AlertTypeParams = never,
+ State extends AlertTypeState = never,
+ InstanceState extends AlertInstanceState = never,
+ InstanceContext extends AlertInstanceContext = never,
+ ActionGroupIds extends string = never
+>(
+ wrappedExecutor: LifecycleRuleExecutor<
+ Params,
+ State,
+ InstanceState,
+ InstanceContext,
+ ActionGroupIds
+ >
+) => async (
+ options: AlertExecutorOptions<
+ Params,
+ WrappedLifecycleRuleState,
+ InstanceState,
+ InstanceContext,
+ ActionGroupIds
+ >
+): Promise> => {
+ const {
+ rule,
+ services: { alertInstanceFactory },
+ state: previousState,
+ } = options;
+
+ const ruleExecutorData = getRuleData(options);
+
+ const state = getOrElse(
+ (): WrappedLifecycleRuleState => ({
+ wrapped: previousState as State,
+ trackedAlerts: {},
+ })
+ )(wrappedStateRt().decode(previousState));
+
+ const currentAlerts: Record = {};
+
+ const timestamp = options.startedAt.toISOString();
+
+ const lifecycleAlertServices: LifecycleAlertServices<
+ InstanceState,
+ InstanceContext,
+ ActionGroupIds
+ > = {
+ alertWithLifecycle: ({ id, fields }) => {
+ currentAlerts[id] = {
+ ...fields,
+ [ALERT_ID]: id,
+ };
+ return alertInstanceFactory(id);
+ },
+ };
+
+ const nextWrappedState = await wrappedExecutor({
+ ...options,
+ state: state.wrapped != null ? state.wrapped : ({} as State),
+ services: {
+ ...options.services,
+ ...lifecycleAlertServices,
+ },
+ });
+
+ const currentAlertIds = Object.keys(currentAlerts);
+ const trackedAlertIds = Object.keys(state.trackedAlerts);
+ const newAlertIds = currentAlertIds.filter((alertId) => !trackedAlertIds.includes(alertId));
+
+ const allAlertIds = [...new Set(currentAlertIds.concat(trackedAlertIds))];
+
+ const trackedAlertStatesOfRecovered = Object.values(state.trackedAlerts).filter(
+ (trackedAlertState) => !currentAlerts[trackedAlertState.alertId]
+ );
+
+ logger.debug(
+ `Tracking ${allAlertIds.length} alerts (${newAlertIds.length} new, ${trackedAlertStatesOfRecovered.length} recovered)`
+ );
+
+ const alertsDataMap: Record<
+ string,
+ {
+ [ALERT_ID]: string;
+ }
+ > = {
+ ...currentAlerts,
+ };
+
+ if (trackedAlertStatesOfRecovered.length) {
+ const { hits } = await ruleDataClient.getReader().search({
+ body: {
+ query: {
+ bool: {
+ filter: [
+ {
+ term: {
+ [RULE_UUID]: ruleExecutorData[RULE_UUID],
+ },
+ },
+ {
+ terms: {
+ [ALERT_UUID]: trackedAlertStatesOfRecovered.map(
+ (trackedAlertState) => trackedAlertState.alertUuid
+ ),
+ },
+ },
+ ],
+ },
+ },
+ size: trackedAlertStatesOfRecovered.length,
+ collapse: {
+ field: ALERT_UUID,
+ },
+ _source: false,
+ fields: [{ field: '*', include_unmapped: true }],
+ sort: {
+ [TIMESTAMP]: 'desc' as const,
+ },
+ },
+ allow_no_indices: true,
+ });
+
+ hits.hits.forEach((hit) => {
+ const fields = parseTechnicalFields(hit.fields);
+ const alertId = fields[ALERT_ID]!;
+ alertsDataMap[alertId] = {
+ ...fields,
+ [ALERT_ID]: alertId,
+ };
+ });
+ }
+
+ const eventsToIndex = allAlertIds.map((alertId) => {
+ const alertData = alertsDataMap[alertId];
+
+ if (!alertData) {
+ logger.warn(`Could not find alert data for ${alertId}`);
+ }
+
+ const event: Mutable = {
+ ...alertData,
+ ...ruleExecutorData,
+ [TIMESTAMP]: timestamp,
+ [EVENT_KIND]: 'event',
+ [OWNER]: rule.consumer,
+ [ALERT_ID]: alertId,
+ };
+
+ const isNew = !state.trackedAlerts[alertId];
+ const isRecovered = !currentAlerts[alertId];
+ const isActiveButNotNew = !isNew && !isRecovered;
+ const isActive = !isRecovered;
+
+ const { alertUuid, started } = state.trackedAlerts[alertId] ?? {
+ alertUuid: v4(),
+ started: timestamp,
+ };
+
+ event[ALERT_START] = started;
+ event[ALERT_UUID] = alertUuid;
+
+ if (isNew) {
+ event[EVENT_ACTION] = 'open';
+ }
+
+ if (isRecovered) {
+ event[ALERT_END] = timestamp;
+ event[EVENT_ACTION] = 'close';
+ event[ALERT_STATUS] = 'closed';
+ }
+
+ if (isActiveButNotNew) {
+ event[EVENT_ACTION] = 'active';
+ }
+
+ if (isActive) {
+ event[ALERT_STATUS] = 'open';
+ }
+
+ event[ALERT_DURATION] =
+ (options.startedAt.getTime() - new Date(event[ALERT_START]!).getTime()) * 1000;
+
+ return event;
+ });
+
+ if (eventsToIndex.length) {
+ const alertEvents: Map