From b99f498740659c24f2602400b97b3c571077de22 Mon Sep 17 00:00:00 2001
From: Tim Sullivan
Date: Thu, 16 Jun 2022 10:12:49 -0700
Subject: [PATCH 01/30] [Reporting] Improve Network Policy error, and unskip
network policy test (#134534)
* [Reporting] Unskip network policy test
* add DisallowedOutgoingUrl type of error for failing a job when we encounter a URL that is not allowed per network policy
* fix test
* fix ts
* fix jest tests
* Update x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts
Co-authored-by: Michael Dokolin
* correct consumer reference
Co-authored-by: Michael Dokolin
---
.../plugins/reporting/common/errors/index.ts | 12 ++++++-
.../common/errors/map_to_reporting_error.ts | 11 +++---
.../plugins/screenshotting/common/errors.ts | 2 ++
.../server/browsers/chromium/driver.ts | 36 ++++++++++---------
.../chromium/driver_factory/index.test.ts | 4 +--
.../browsers/chromium/driver_factory/index.ts | 25 ++++++-------
.../server/browsers/chromium/index.ts | 5 +++
.../screenshotting/server/browsers/mock.ts | 2 +-
.../server/screenshots/index.test.ts | 2 +-
.../server/screenshots/index.ts | 4 +--
.../reporting_and_security/network_policy.ts | 18 +++++-----
11 files changed, 70 insertions(+), 51 deletions(-)
diff --git a/x-pack/plugins/reporting/common/errors/index.ts b/x-pack/plugins/reporting/common/errors/index.ts
index 0876b84312053..9da4173aef91f 100644
--- a/x-pack/plugins/reporting/common/errors/index.ts
+++ b/x-pack/plugins/reporting/common/errors/index.ts
@@ -6,8 +6,8 @@
*/
/* eslint-disable max-classes-per-file */
-
import { i18n } from '@kbn/i18n';
+
export abstract class ReportingError extends Error {
/**
* A string that uniquely brands an error type. This is used to power telemetry
@@ -43,6 +43,16 @@ export class InvalidLayoutParametersError extends ReportingError {
}
}
+/**
+ * While loading requests in the Kibana app, a URL was encountered that the network policy did not allow.
+ */
+export class DisallowedOutgoingUrl extends ReportingError {
+ static code = 'disallowed_outgoing_url_error' as const;
+ public get code() {
+ return DisallowedOutgoingUrl.code;
+ }
+}
+
/**
* While performing some reporting action, like fetching data from ES, our
* access token expired.
diff --git a/x-pack/plugins/reporting/common/errors/map_to_reporting_error.ts b/x-pack/plugins/reporting/common/errors/map_to_reporting_error.ts
index 3a1c5d0987b05..f81c18d9421f7 100644
--- a/x-pack/plugins/reporting/common/errors/map_to_reporting_error.ts
+++ b/x-pack/plugins/reporting/common/errors/map_to_reporting_error.ts
@@ -7,14 +7,15 @@
import { errors } from '@kbn/screenshotting-plugin/common';
import {
- UnknownError,
- ReportingError,
BrowserCouldNotLaunchError,
- BrowserUnexpectedlyClosedError,
BrowserScreenshotError,
+ BrowserUnexpectedlyClosedError,
+ DisallowedOutgoingUrl,
+ InvalidLayoutParametersError,
PdfWorkerOutOfMemoryError,
+ ReportingError,
+ UnknownError,
VisualReportingSoftDisabledError,
- InvalidLayoutParametersError,
} from '.';
/**
@@ -33,6 +34,8 @@ export function mapToReportingError(error: unknown): ReportingError {
switch (true) {
case error instanceof errors.InvalidLayoutParametersError:
return new InvalidLayoutParametersError((error as Error).message);
+ case error instanceof errors.DisallowedOutgoingUrl:
+ return new DisallowedOutgoingUrl((error as Error).message);
case error instanceof errors.BrowserClosedUnexpectedly:
return new BrowserUnexpectedlyClosedError((error as Error).message);
case error instanceof errors.FailedToCaptureScreenshot:
diff --git a/x-pack/plugins/screenshotting/common/errors.ts b/x-pack/plugins/screenshotting/common/errors.ts
index 06823ef812922..193bb5d544d4d 100644
--- a/x-pack/plugins/screenshotting/common/errors.ts
+++ b/x-pack/plugins/screenshotting/common/errors.ts
@@ -14,6 +14,8 @@ export class FailedToSpawnBrowserError extends Error {}
export class BrowserClosedUnexpectedly extends Error {}
+export class DisallowedOutgoingUrl extends Error {}
+
export class FailedToCaptureScreenshot extends Error {}
export class InsufficientMemoryAvailableOnCloudError extends Error {}
diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts
index 40dce4e9b914e..44cd29e664fe3 100644
--- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts
+++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts
@@ -5,15 +5,17 @@
* 2.0.
*/
-import { truncate } from 'lodash';
-import open from 'opn';
-import puppeteer, { ElementHandle, EvaluateFn, Page, SerializableOrJSHandle } from 'puppeteer';
-import { parse as parseUrl } from 'url';
import { Headers, Logger } from '@kbn/core/server';
import {
KBN_SCREENSHOT_MODE_HEADER,
ScreenshotModePluginSetup,
} from '@kbn/screenshot-mode-plugin/server';
+import { truncate } from 'lodash';
+import open from 'opn';
+import puppeteer, { ElementHandle, EvaluateFn, Page, SerializableOrJSHandle } from 'puppeteer';
+import { Subject } from 'rxjs';
+import { parse as parseUrl } from 'url';
+import { getDisallowedOutgoingUrlError } from '.';
import { ConfigType } from '../../config';
import { allowRequest } from '../network_policy';
import { stripUnsafeHeaders } from './strip_unsafe_headers';
@@ -72,18 +74,14 @@ interface InterceptedRequest {
const WAIT_FOR_DELAY_MS: number = 100;
-function getDisallowedOutgoingUrlError(interceptedUrl: string) {
- return new Error(
- `Received disallowed outgoing URL: "${interceptedUrl}". Failing the request and closing the browser.`
- );
-}
-
/**
* @internal
*/
export class HeadlessChromiumDriver {
private listenersAttached = false;
private interceptedCount = 0;
+ private screenshottingErrorSubject = new Subject();
+ readonly screenshottingError$ = this.screenshottingErrorSubject.asObservable();
constructor(
private screenshotMode: ScreenshotModePluginSetup,
@@ -106,7 +104,7 @@ export class HeadlessChromiumDriver {
/*
* Call Page.goto and wait to see the Kibana DOM content
*/
- async open(
+ public async open(
url: string,
{ headers, context, waitForSelector: pageLoadSelector, timeout }: OpenOptions,
logger: Logger
@@ -183,7 +181,7 @@ export class HeadlessChromiumDriver {
}
}
- async printA4Pdf({ title, logo }: { title: string; logo?: string }): Promise {
+ public async printA4Pdf({ title, logo }: { title: string; logo?: string }): Promise {
await this.workaroundWebGLDrivenCanvases();
return this.page.pdf({
format: 'a4',
@@ -199,7 +197,7 @@ export class HeadlessChromiumDriver {
/*
* Receive a PNG buffer of the page screenshot from Chromium
*/
- async screenshot(elementPosition: ElementPosition): Promise {
+ public async screenshot(elementPosition: ElementPosition): Promise {
const { boundingClientRect, scroll } = elementPosition;
const screenshot = await this.page.screenshot({
clip: {
@@ -228,7 +226,7 @@ export class HeadlessChromiumDriver {
return this.page.evaluate(fn, ...args);
}
- async waitForSelector(
+ public async waitForSelector(
selector: string,
opts: WaitForSelectorOpts,
context: EvaluateMetaOpts,
@@ -262,7 +260,7 @@ export class HeadlessChromiumDriver {
* Setting the viewport is required to ensure that all capture elements are visible: anything not in the
* viewport can not be captured.
*/
- async setViewport(
+ public async setViewport(
{ width: _width, height: _height, zoom }: { zoom: number; width: number; height: number },
logger: Logger
): Promise {
@@ -308,7 +306,9 @@ export class HeadlessChromiumDriver {
requestId,
});
this.page.browser().close();
- logger.error(getDisallowedOutgoingUrlError(interceptedUrl));
+ const error = getDisallowedOutgoingUrlError(interceptedUrl);
+ this.screenshottingErrorSubject.next(error);
+ logger.error(error);
return;
}
@@ -358,7 +358,9 @@ export class HeadlessChromiumDriver {
if (!allowed || !this.allowRequest(interceptedUrl)) {
this.page.browser().close();
- logger.error(getDisallowedOutgoingUrlError(interceptedUrl));
+ const error = getDisallowedOutgoingUrlError(interceptedUrl);
+ this.screenshottingErrorSubject.next(error);
+ logger.error(error);
return;
}
});
diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts
index eff3be3d0c439..d7c6ecb042a16 100644
--- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts
+++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts
@@ -68,7 +68,7 @@ describe('HeadlessChromiumDriverFactory', () => {
});
describe('createPage', () => {
- it('returns browser driver, unexpected process exit observable, and close callback', async () => {
+ it('returns browser driver, error observable, and close callback', async () => {
await expect(
factory
.createPage({ openUrlTimeout: 0, defaultViewport: DEFAULT_VIEWPORT })
@@ -77,7 +77,7 @@ describe('HeadlessChromiumDriverFactory', () => {
).resolves.toEqual(
expect.objectContaining({
driver: expect.anything(),
- unexpectedExit$: expect.anything(),
+ error$: expect.anything(),
close: expect.anything(),
})
);
diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts
index bec10bf0caec7..bc001470a8e6e 100644
--- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts
+++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.ts
@@ -20,8 +20,8 @@ import {
catchError,
concatMap,
ignoreElements,
- map,
mergeMap,
+ map,
reduce,
takeUntil,
tap,
@@ -42,7 +42,7 @@ interface CreatePageOptions {
interface CreatePageResult {
driver: HeadlessChromiumDriver;
- unexpectedExit$: Rx.Observable;
+ error$: Rx.Observable;
/**
* Close the page and the browser.
*
@@ -258,14 +258,13 @@ export class HeadlessChromiumDriverFactory {
page
);
- // Rx.Observable: stream to interrupt page capture
- const unexpectedExit$ = this.getPageExit(browser, page);
+ const error$ = Rx.concat(driver.screenshottingError$, this.getPageExit(browser, page)).pipe(
+ mergeMap((err) => Rx.throwError(err))
+ );
+
+ const close = () => Rx.from(childProcess.kill());
- observer.next({
- driver,
- unexpectedExit$,
- close: () => Rx.from(childProcess.kill()),
- });
+ observer.next({ driver, error$, close });
// unsubscribe logic makes a best-effort attempt to delete the user data directory used by chromium
observer.add(() => {
@@ -376,15 +375,13 @@ export class HeadlessChromiumDriverFactory {
return processClose$; // ideally, this would also merge with observers for stdout and stderr
}
- getPageExit(browser: Browser, page: Page) {
+ getPageExit(browser: Browser, page: Page): Rx.Observable {
const pageError$ = Rx.fromEvent(page, 'error').pipe(
- mergeMap((err) => {
- return Rx.throwError(`Reporting encountered an error: ${err.toString()}`);
- })
+ map((err) => new Error(`Reporting encountered an error: ${err.toString()}`))
);
const browserDisconnect$ = Rx.fromEvent(browser, 'disconnected').pipe(
- mergeMap(() => Rx.throwError(getChromiumDisconnectedError()))
+ map(() => getChromiumDisconnectedError())
);
return Rx.merge(pageError$, browserDisconnect$);
diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/index.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/index.ts
index 70fb9caa45125..672ea2ec5f020 100644
--- a/x-pack/plugins/screenshotting/server/browsers/chromium/index.ts
+++ b/x-pack/plugins/screenshotting/server/browsers/chromium/index.ts
@@ -12,6 +12,11 @@ export const getChromiumDisconnectedError = () =>
'Browser was closed unexpectedly! Check the server logs for more info.'
);
+export const getDisallowedOutgoingUrlError = (interceptedUrl: string) =>
+ new errors.DisallowedOutgoingUrl(
+ `Received disallowed outgoing URL [${interceptedUrl}]! Check the server logs for more info.`
+ );
+
export { HeadlessChromiumDriver } from './driver';
export type { Context } from './driver';
export { DEFAULT_VIEWPORT, HeadlessChromiumDriverFactory } from './driver_factory';
diff --git a/x-pack/plugins/screenshotting/server/browsers/mock.ts b/x-pack/plugins/screenshotting/server/browsers/mock.ts
index 028bc6fc439d7..275a02e2638c9 100644
--- a/x-pack/plugins/screenshotting/server/browsers/mock.ts
+++ b/x-pack/plugins/screenshotting/server/browsers/mock.ts
@@ -92,7 +92,7 @@ export function createMockBrowserDriverFactory(
createPage: jest.fn(() =>
of({
driver: driver ?? createMockBrowserDriver(),
- unexpectedExit$: NEVER,
+ error$: NEVER,
close: () => of({}),
})
),
diff --git a/x-pack/plugins/screenshotting/server/screenshots/index.test.ts b/x-pack/plugins/screenshotting/server/screenshots/index.test.ts
index 1cc328509ab72..11ce25e0f86f1 100644
--- a/x-pack/plugins/screenshotting/server/screenshots/index.test.ts
+++ b/x-pack/plugins/screenshotting/server/screenshots/index.test.ts
@@ -169,7 +169,7 @@ describe('Screenshot Observable Pipeline', () => {
driverFactory.createPage.mockReturnValue(
of({
driver,
- unexpectedExit$: throwError('Instant timeout has fired!'),
+ error$: throwError('Instant timeout has fired!'),
close: () => of({}),
})
);
diff --git a/x-pack/plugins/screenshotting/server/screenshots/index.ts b/x-pack/plugins/screenshotting/server/screenshots/index.ts
index b0b591a16a5d0..a10ca72ab60c8 100644
--- a/x-pack/plugins/screenshotting/server/screenshots/index.ts
+++ b/x-pack/plugins/screenshotting/server/screenshots/index.ts
@@ -126,7 +126,7 @@ export class Screenshots {
)
.pipe(
this.semaphore.acquire(),
- mergeMap(({ driver, unexpectedExit$, close }) => {
+ mergeMap(({ driver, error$, close }) => {
const screen = new ScreenshotObservableHandler(
driver,
this.config,
@@ -145,7 +145,7 @@ export class Screenshots {
eventLogger.error(error, Transactions.SCREENSHOTTING);
return of({ ...DEFAULT_SETUP_RESULT, error }); // allow failover screenshot capture
}),
- takeUntil(unexpectedExit$),
+ takeUntil(error$),
screen.getScreenshots()
)
),
diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/network_policy.ts b/x-pack/test/reporting_api_integration/reporting_and_security/network_policy.ts
index 0bc164227fe14..40ee0986df9e5 100644
--- a/x-pack/test/reporting_api_integration/reporting_and_security/network_policy.ts
+++ b/x-pack/test/reporting_api_integration/reporting_and_security/network_policy.ts
@@ -15,11 +15,9 @@ export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
/*
- * The Reporting API Functional Test config implements a network policy that
- * is designed to disallow the following Canvas worksheet
+ * The tests server config implements a network policy that is designed to disallow the following Canvas worksheet
*/
- // FLAKY: https://github.com/elastic/kibana/issues/111381
- describe.skip('Network Policy', () => {
+ describe('Network Policy', () => {
before(async () => {
await reportingAPI.initLogs(); // includes a canvas worksheet with an offending image URL
});
@@ -28,18 +26,20 @@ export default function ({ getService }: FtrProviderContext) {
await reportingAPI.teardownLogs();
});
- it('should fail job when page voilates the network policy', async () => {
+ it('should fail job when page violates the network policy', async () => {
const downloadPath = await reportingAPI.postJob(
`/api/reporting/generate/printablePdf?jobParams=(layout:(dimensions:(height:720,width:1080),id:preserve_layout),objectType:'canvas%20workpad',relativeUrls:!(%2Fapp%2Fcanvas%23%2Fexport%2Fworkpad%2Fpdf%2Fworkpad-e7464259-0b75-4b8c-81c8-8422b15ff201%2Fpage%2F1),title:'My%20Canvas%20Workpad')`
);
// Retry the download URL until a "failed" response status is returned
+ let body: any;
await retry.tryForTime(120000, async () => {
- const { body } = await supertest.get(downloadPath).expect(500);
- expect(body.message).to.match(
- /Reporting generation failed: ReportingError\(code: browser_unexpectedly_closed_error\) "/
- );
+ body = (await supertest.get(downloadPath).expect(500)).body;
});
+
+ expect(body.message).to.match(
+ /Reporting generation failed: ReportingError\(code: disallowed_outgoing_url_error\)/
+ );
});
});
}
From a020f058aa616e9c9bdba6e521f87433651869b5 Mon Sep 17 00:00:00 2001
From: Chris Cowan
Date: Thu, 16 Jun 2022 11:15:41 -0600
Subject: [PATCH 02/30] [Infrastructure UI][Rules] Fix viewInAppUrl for custom
metrics for Inventory Threshold Rule (#134114)
* [Infrastructure UI][Rules] Fix viewInAppUrl for custom metrics for Inventory Threshold Rule
* Adding test for generating link, moving custom field inside if statement
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../alerting/metrics/alert_link.test.ts | 40 +++++++++++++++++++
.../common/alerting/metrics/alert_link.ts | 4 +-
2 files changed, 42 insertions(+), 2 deletions(-)
create mode 100644 x-pack/plugins/infra/common/alerting/metrics/alert_link.test.ts
diff --git a/x-pack/plugins/infra/common/alerting/metrics/alert_link.test.ts b/x-pack/plugins/infra/common/alerting/metrics/alert_link.test.ts
new file mode 100644
index 0000000000000..80bac365d3562
--- /dev/null
+++ b/x-pack/plugins/infra/common/alerting/metrics/alert_link.test.ts
@@ -0,0 +1,40 @@
+/*
+ * 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 { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common/parse_technical_fields';
+import { ALERT_RULE_PARAMETERS, TIMESTAMP } from '@kbn/rule-data-utils';
+import { getInventoryViewInAppUrl } from './alert_link';
+
+describe('Inventory Threshold Rule', () => {
+ describe('getInventoryViewInAppUrl', () => {
+ it('should work with custom metrics', () => {
+ const fields = {
+ [TIMESTAMP]: '2022-01-01T00:00:00.000Z',
+ [`${ALERT_RULE_PARAMETERS}.nodeType`]: 'host',
+ [`${ALERT_RULE_PARAMETERS}.criteria.metric`]: ['custom'],
+ [`${ALERT_RULE_PARAMETERS}.criteria.customMetric.id`]: ['alert-custom-metric'],
+ [`${ALERT_RULE_PARAMETERS}.criteria.customMetric.aggregation`]: ['avg'],
+ [`${ALERT_RULE_PARAMETERS}.criteria.customMetric.field`]: ['system.cpu.user.pct'],
+ } as unknown as ParsedTechnicalFields & Record;
+ const url = getInventoryViewInAppUrl(fields);
+ expect(url).toEqual(
+ '/app/metrics/link-to/inventory?customMetric=%28aggregation%3Aavg%2Cfield%3Asystem.cpu.user.pct%2Cid%3Aalert-custom-metric%2Ctype%3Acustom%29&metric=%28aggregation%3Aavg%2Cfield%3Asystem.cpu.user.pct%2Cid%3Aalert-custom-metric%2Ctype%3Acustom%29&nodeType=h×tamp=1640995200000'
+ );
+ });
+ it('should work with non-custom metrics', () => {
+ const fields = {
+ [TIMESTAMP]: '2022-01-01T00:00:00.000Z',
+ [`${ALERT_RULE_PARAMETERS}.nodeType`]: 'host',
+ [`${ALERT_RULE_PARAMETERS}.criteria.metric`]: ['cpu'],
+ } as unknown as ParsedTechnicalFields & Record;
+ const url = getInventoryViewInAppUrl(fields);
+ expect(url).toEqual(
+ '/app/metrics/link-to/inventory?customMetric=&metric=%28type%3Acpu%29&nodeType=h×tamp=1640995200000'
+ );
+ });
+ });
+});
diff --git a/x-pack/plugins/infra/common/alerting/metrics/alert_link.ts b/x-pack/plugins/infra/common/alerting/metrics/alert_link.ts
index 9fbb5fa9e4e98..11068df93b926 100644
--- a/x-pack/plugins/infra/common/alerting/metrics/alert_link.ts
+++ b/x-pack/plugins/infra/common/alerting/metrics/alert_link.ts
@@ -24,8 +24,8 @@ export const getInventoryViewInAppUrl = (
};
// We always pick the first criteria metric for the URL
const criteriaMetric = fields[`${ALERT_RULE_PARAMETERS}.criteria.metric`][0];
- const criteriaCustomMetricId = fields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.id`][0];
- if (criteriaCustomMetricId !== 'alert-custom-metric') {
+ if (criteriaMetric === 'custom') {
+ const criteriaCustomMetricId = fields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.id`][0];
const criteriaCustomMetricAggregation =
fields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.aggregation`][0];
const criteriaCustomMetricField =
From 0e195a6762f31f57b047194a53464440b579c5a6 Mon Sep 17 00:00:00 2001
From: Jason Rhodes
Date: Thu, 16 Jun 2022 13:31:46 -0400
Subject: [PATCH 03/30] Remove unified observability codeowner bottleneck
(#134584)
As the unified obs team is currently without active engineers, we don't want CODEOWNERS to require reviews from that team. I've changed exploratory view back to the Uptime team, the overview page to the obs design team, and 1-2 others now have no CODEOWNERS and will just require a review from someone other than the code author.
---
.github/CODEOWNERS | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index df984ce41e5e9..4c2348df2ffbe 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -90,12 +90,14 @@
# Observability Shared
/x-pack/plugins/observability/public/components/shared/date_picker/ @elastic/uptime
-# Unified Observability
-/x-pack/plugins/observability/public/components/shared/exploratory_view @elastic/unified-observability
-/x-pack/plugins/observability/public/context @elastic/unified-observability
-/x-pack/plugins/observability/public/pages/home @elastic/unified-observability
-/x-pack/plugins/observability/public/pages/landing @elastic/unified-observability
-/x-pack/plugins/observability/public/pages/overview @elastic/unified-observability
+# Unified Observability - on hold due to team capacity shortage
+# For now, if you're changing these pages, get a review from someone who understand the changes
+# /x-pack/plugins/observability/public/context @elastic/unified-observability
+
+# Home/Overview/Landing Pages
+/x-pack/plugins/observability/public/pages/home @elastic/observability-design
+/x-pack/plugins/observability/public/pages/landing @elastic/observability-design
+/x-pack/plugins/observability/public/pages/overview @elastic/observability-design
# Actionable Observability
/x-pack/plugins/observability/common/rules @elastic/actionable-observability
From 35874aa3a37782017845b2e260ed7bdd74a9b252 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 16 Jun 2022 19:32:56 +0200
Subject: [PATCH 04/30] Update dependency @elastic/charts to v46.10.1 (main)
(#134162)
---
package.json | 2 +-
.../charts/__snapshots__/donut_chart.test.tsx.snap | 9 +++++++++
yarn.lock | 8 ++++----
3 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/package.json b/package.json
index f1dd078bd6cbd..163bc55ab1e03 100644
--- a/package.json
+++ b/package.json
@@ -106,7 +106,7 @@
"@elastic/apm-rum": "^5.12.0",
"@elastic/apm-rum-react": "^1.4.2",
"@elastic/apm-synthtrace": "link:bazel-bin/packages/elastic-apm-synthtrace",
- "@elastic/charts": "46.9.0",
+ "@elastic/charts": "46.10.1",
"@elastic/datemath": "5.0.3",
"@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.2.0-canary.2",
"@elastic/ems-client": "8.3.3",
diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap
index 3bc2751d2c9d6..2930ba4d4570d 100644
--- a/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap
+++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/common/charts/__snapshots__/donut_chart.test.tsx.snap
@@ -400,6 +400,15 @@ exports[`DonutChart component passes correct props without errors for valid prop
"visible": true,
},
},
+ "metric": Object {
+ "background": "#FFFFFF",
+ "barBackground": "#EDF0F5",
+ "nonFiniteText": "N/A",
+ "text": Object {
+ "darkColor": "#343741",
+ "lightColor": "#E0E5EE",
+ },
+ },
"partition": Object {
"circlePadding": 2,
"emptySizeRatio": 0,
diff --git a/yarn.lock b/yarn.lock
index cd15e767aa6d8..a97bdb8ce72d5 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1429,10 +1429,10 @@
dependencies:
object-hash "^1.3.0"
-"@elastic/charts@46.9.0":
- version "46.9.0"
- resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-46.9.0.tgz#45a615eb84c81d8cc5219eaa4eef8f4761c9fc05"
- integrity sha512-RVDNgg9bZIZoq/8sfRg6n9gXHPzs4n8bAJogxnzv73f4sLcdytGqjNbdDpbDO2MPRKnA7nN92/L0Ewkp7SiU0Q==
+"@elastic/charts@46.10.1":
+ version "46.10.1"
+ resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-46.10.1.tgz#4613db939e05efa041199ba14ee4a7aa9cf2d2e7"
+ integrity sha512-zpQBmB/NnqOS14rwMbQYePErFshxnvmCW0cFSVkU2B4+2VhVBSueDZhbPbWAoOtmE61RprRxHxgT51JKCUwcAQ==
dependencies:
"@popperjs/core" "^2.4.0"
bezier-easing "^2.1.0"
From 12b56ab525981422e1af0a59763795bc1840db47 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Thu, 16 Jun 2022 13:44:56 -0400
Subject: [PATCH 05/30] skip failing test suite (#116056)
---
.../api_integration/apis/ml/data_frame_analytics/evaluate.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/evaluate.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/evaluate.ts
index 1cd71bc016262..896d797670166 100644
--- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/evaluate.ts
+++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/evaluate.ts
@@ -111,7 +111,8 @@ export default ({ getService }: FtrProviderContext) => {
}
}
- describe('POST data_frame/_evaluate', () => {
+ // Failing: See https://github.com/elastic/kibana/issues/116056
+ describe.skip('POST data_frame/_evaluate', () => {
before(async () => {
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/bm_classification');
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/egs_regression');
From 8da4cb29a754186e97be60b2a651ef33b483cc87 Mon Sep 17 00:00:00 2001
From: Ying Mao
Date: Thu, 16 Jun 2022 13:57:39 -0400
Subject: [PATCH 06/30] [Response Ops] Log stack traces in
alerting/actions/task executors on error (#133931)
* Showing stack track for alerting task runner
* Adding stack traces for action and task running
* wip
* Updating unit tests
* wip
* Dont return stack trace in action result
* Updating unit tests
* Updating functional tests
* Updating functional tests
* Separate log for error
* Separate log for error
* Moving error log
* Trying out putting stack trace in meta
* two logs and tags
* Adding tags to the error logging
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
x-pack/plugins/actions/common/types.ts | 4 ++
.../server/lib/action_executor.test.ts | 10 ++++-
.../actions/server/lib/action_executor.ts | 15 +++++--
x-pack/plugins/actions/server/types.ts | 2 +-
.../server/task_runner/task_runner.test.ts | 43 ++++++++++---------
.../server/task_runner/task_runner.ts | 10 ++++-
.../server/task_running/task_runner.test.ts | 9 +++-
.../server/task_running/task_runner.ts | 5 ++-
8 files changed, 66 insertions(+), 32 deletions(-)
diff --git a/x-pack/plugins/actions/common/types.ts b/x-pack/plugins/actions/common/types.ts
index 751b403780080..690d5034303d6 100644
--- a/x-pack/plugins/actions/common/types.ts
+++ b/x-pack/plugins/actions/common/types.ts
@@ -50,6 +50,10 @@ export interface ActionTypeExecutorResult {
retry?: null | boolean | Date;
}
+export type ActionTypeExecutorRawResult = ActionTypeExecutorResult & {
+ error?: Error;
+};
+
export function isActionTypeExecutorResult(
result: unknown
): result is ActionTypeExecutorResult {
diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts
index 12898cea5a482..5b50454ccec65 100644
--- a/x-pack/plugins/actions/server/lib/action_executor.test.ts
+++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts
@@ -516,13 +516,19 @@ test('logs a warning when alert executor has an error', async () => {
);
});
-test('logs a warning when alert executor throws an error', async () => {
+test('logs a warning and error when alert executor throws an error', async () => {
const executorMock = setupActionExecutorMock();
- executorMock.mockRejectedValue(new Error('this action execution is intended to fail'));
+ const err = new Error('this action execution is intended to fail');
+ err.stack = 'foo error\n stack 1\n stack 2\n stack 3';
+ executorMock.mockRejectedValue(err);
await actionExecutor.execute(executeParams);
expect(loggerMock.warn).toBeCalledWith(
'action execution failure: test:1: action-1: an error occurred while running the action: this action execution is intended to fail'
);
+ expect(loggerMock.error).toBeCalledWith(err, {
+ error: { stack_trace: 'foo error\n stack 1\n stack 2\n stack 3' },
+ tags: ['test', '1', 'action-run-failed'],
+ });
});
test('logs a warning when alert executor returns invalid status', async () => {
diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts
index b9ed252c6afc2..a27cfcccef9c8 100644
--- a/x-pack/plugins/actions/server/lib/action_executor.ts
+++ b/x-pack/plugins/actions/server/lib/action_executor.ts
@@ -21,6 +21,7 @@ import {
import {
ActionType,
ActionTypeExecutorResult,
+ ActionTypeExecutorRawResult,
ActionTypeRegistryContract,
GetServicesFunction,
PreConfiguredAction,
@@ -203,7 +204,7 @@ export class ActionExecutor {
eventLogger.logEvent(startEvent);
- let rawResult: ActionTypeExecutorResult;
+ let rawResult: ActionTypeExecutorRawResult;
try {
const { validatedParams, validatedConfig, validatedSecrets } = validateAction({
actionId,
@@ -231,6 +232,7 @@ export class ActionExecutor {
status: 'error',
message: 'an error occurred while running the action',
serviceMessage: err.message,
+ error: err,
retry: false,
};
}
@@ -256,6 +258,12 @@ export class ActionExecutor {
event.message = `action execution failure: ${actionLabel}`;
event.error = event.error || {};
event.error.message = actionErrorToMessage(result);
+ if (result.error) {
+ logger.error(result.error, {
+ tags: [actionTypeId, actionId, 'action-run-failed'],
+ error: { stack_trace: result.error.stack },
+ });
+ }
logger.warn(`action execution failure: ${actionLabel}: ${event.error.message}`);
} else {
span?.setOutcome('failure');
@@ -269,7 +277,8 @@ export class ActionExecutor {
}
eventLogger.logEvent(event);
- return result;
+ const { error, ...resultWithoutError } = result;
+ return resultWithoutError;
}
);
}
@@ -393,7 +402,7 @@ async function getActionInfoInternal(
};
}
-function actionErrorToMessage(result: ActionTypeExecutorResult): string {
+function actionErrorToMessage(result: ActionTypeExecutorRawResult): string {
let message = result.message || 'unknown error running action';
if (result.serviceMessage) {
diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts
index 491d7ab5be2e4..4f3a2ae6fa4f1 100644
--- a/x-pack/plugins/actions/server/types.ts
+++ b/x-pack/plugins/actions/server/types.ts
@@ -22,7 +22,7 @@ import { ActionTypeExecutorResult } from '../common';
import { TaskInfo } from './lib/action_executor';
import { ConnectorTokenClient } from './builtin_action_types/lib/connector_token_client';
-export type { ActionTypeExecutorResult } from '../common';
+export type { ActionTypeExecutorResult, ActionTypeExecutorRawResult } from '../common';
export type { GetFieldsByIssueTypeResponse as JiraGetFieldsResponse } from './builtin_action_types/jira/types';
export type { GetCommonFieldsResponse as ServiceNowGetFieldsResponse } from './builtin_action_types/servicenow/types';
export type { GetCommonFieldsResponse as ResilientGetFieldsResponse } from './builtin_action_types/resilient/types';
diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts
index f3d2c7039585b..5d2f6d7c1f659 100644
--- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts
+++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts
@@ -87,6 +87,7 @@ jest.mock('../lib/wrap_scoped_cluster_client', () => ({
jest.mock('../lib/alerting_event_logger/alerting_event_logger');
let fakeTimer: sinon.SinonFakeTimers;
+const logger: ReturnType = loggingSystemMock.createLogger();
const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract();
const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test');
@@ -139,7 +140,7 @@ describe('Task Runner', () => {
actionsPlugin: actionsMock.createStart(),
getRulesClientWithRequest: jest.fn().mockReturnValue(rulesClient),
encryptedSavedObjectsClient,
- logger: loggingSystemMock.create().get(),
+ logger,
executionContext: executionContextServiceMock.createInternalStartContract(),
spaceIdToNamespace: jest.fn().mockReturnValue(undefined),
basePathService: httpServiceMock.createBasePath(),
@@ -258,7 +259,6 @@ describe('Task Runner', () => {
expect(call.services.scopedClusterClient).toBeTruthy();
expect(call.services).toBeTruthy();
- const logger = taskRunnerFactoryInitializerParams.logger;
expect(logger.debug).toHaveBeenCalledTimes(4);
expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z');
expect(logger.debug).nthCalledWith(
@@ -331,7 +331,6 @@ describe('Task Runner', () => {
expect(enqueueFunction).toHaveBeenCalledTimes(1);
expect(enqueueFunction).toHaveBeenCalledWith(generateEnqueueFunctionInput());
- const logger = customTaskRunnerFactoryInitializerParams.logger;
expect(logger.debug).toHaveBeenCalledTimes(5);
expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z');
expect(logger.debug).nthCalledWith(
@@ -415,7 +414,6 @@ describe('Task Runner', () => {
await taskRunner.run();
expect(actionsClient.ephemeralEnqueuedExecution).toHaveBeenCalledTimes(0);
- const logger = taskRunnerFactoryInitializerParams.logger;
expect(logger.debug).toHaveBeenCalledTimes(6);
expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z');
expect(logger.debug).nthCalledWith(
@@ -541,7 +539,6 @@ describe('Task Runner', () => {
expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(expectedExecutions);
expect(actionsClient.ephemeralEnqueuedExecution).toHaveBeenCalledTimes(0);
- const logger = taskRunnerFactoryInitializerParams.logger;
const expectedMessage = `no scheduling of actions for rule test:1: '${RULE_NAME}': rule is snoozed.`;
if (expectedExecutions) {
expect(logger.debug).not.toHaveBeenCalledWith(expectedMessage);
@@ -591,7 +588,6 @@ describe('Task Runner', () => {
await taskRunner.run();
expect(enqueueFunction).toHaveBeenCalledTimes(1);
- const logger = customTaskRunnerFactoryInitializerParams.logger;
expect(logger.debug).toHaveBeenCalledTimes(6);
expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z');
expect(logger.debug).nthCalledWith(
@@ -671,7 +667,6 @@ describe('Task Runner', () => {
await taskRunner.run();
// expect(enqueueFunction).toHaveBeenCalledTimes(1);
- const logger = customTaskRunnerFactoryInitializerParams.logger;
// expect(logger.debug).toHaveBeenCalledTimes(5);
expect(logger.debug).nthCalledWith(
3,
@@ -716,7 +711,6 @@ describe('Task Runner', () => {
encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT);
await taskRunner.run();
expect(enqueueFunction).toHaveBeenCalledTimes(1);
- const logger = customTaskRunnerFactoryInitializerParams.logger;
expect(logger.debug).toHaveBeenCalledTimes(6);
expect(logger.debug).nthCalledWith(
3,
@@ -1099,7 +1093,6 @@ describe('Task Runner', () => {
generateAlertInstance({ id: 1, duration: MOCK_DURATION, start: DATE_1969 })
);
- const logger = customTaskRunnerFactoryInitializerParams.logger;
expect(logger.debug).toHaveBeenCalledTimes(6);
expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z');
expect(logger.debug).nthCalledWith(
@@ -1210,7 +1203,6 @@ describe('Task Runner', () => {
const runnerResult = await taskRunner.run();
expect(runnerResult.state.alertInstances).toEqual(generateAlertInstance());
- const logger = customTaskRunnerFactoryInitializerParams.logger;
expect(logger.debug).toHaveBeenCalledWith(
`rule test:1: '${RULE_NAME}' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]`
);
@@ -1455,9 +1447,13 @@ describe('Task Runner', () => {
encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT);
const runnerResult = await taskRunner.run();
expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 }));
- expect(taskRunnerFactoryInitializerParams.logger.error).toHaveBeenCalledWith(
- `Executing Rule foo:test:1 has resulted in Error: params invalid: [param1]: expected value of type [string] but got [undefined]`
+ const loggerCall = logger.error.mock.calls[0][0];
+ const loggerMeta = logger.error.mock.calls[0][1];
+ expect(loggerCall as string).toMatchInlineSnapshot(
+ `"Executing Rule foo:test:1 has resulted in Error: params invalid: [param1]: expected value of type [string] but got [undefined]"`
);
+ expect(loggerMeta?.tags).toEqual(['test', '1', 'rule-run-failed']);
+ expect(loggerMeta?.error?.stack_trace).toBeDefined();
expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled();
});
@@ -1583,6 +1579,12 @@ describe('Task Runner', () => {
});
expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled();
+
+ const loggerCall = logger.error.mock.calls[0][0];
+ const loggerMeta = logger.error.mock.calls[0][1];
+ expect(loggerCall as string).toMatchInlineSnapshot(`[Error: GENERIC ERROR MESSAGE]`);
+ expect(loggerMeta?.tags).toEqual(['test', '1', 'rule-run-failed']);
+ expect(loggerMeta?.error?.stack_trace).toBeDefined();
});
test('recovers gracefully when the Alert Task Runner throws an exception when fetching the encrypted attributes', async () => {
@@ -1789,12 +1791,13 @@ describe('Task Runner', () => {
encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT);
- const logger = taskRunnerFactoryInitializerParams.logger;
return taskRunner.run().catch((ex) => {
expect(ex.toString()).toEqual(`Error: Saved object [alert/1] not found`);
- expect(logger.debug).toHaveBeenCalledWith(
- `Executing Rule foo:test:1 has resulted in Error: Saved object [alert/1] not found`
+ const executeRuleDebugLogger = logger.debug.mock.calls[3][0];
+ expect(executeRuleDebugLogger as string).toMatchInlineSnapshot(
+ `"Executing Rule foo:test:1 has resulted in Error: Saved object [alert/1] not found"`
);
+ expect(logger.error).not.toHaveBeenCalled();
expect(logger.warn).toHaveBeenCalledTimes(1);
expect(logger.warn).nthCalledWith(
1,
@@ -1870,12 +1873,13 @@ describe('Task Runner', () => {
encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT);
- const logger = taskRunnerFactoryInitializerParams.logger;
return taskRunner.run().catch((ex) => {
expect(ex.toString()).toEqual(`Error: Saved object [alert/1] not found`);
- expect(logger.debug).toHaveBeenCalledWith(
- `Executing Rule test space:test:1 has resulted in Error: Saved object [alert/1] not found`
+ const ruleExecuteDebugLog = logger.debug.mock.calls[3][0];
+ expect(ruleExecuteDebugLog as string).toMatchInlineSnapshot(
+ `"Executing Rule test space:test:1 has resulted in Error: Saved object [alert/1] not found"`
);
+ expect(logger.error).not.toHaveBeenCalled();
expect(logger.warn).toHaveBeenCalledTimes(1);
expect(logger.warn).nthCalledWith(
1,
@@ -2325,7 +2329,6 @@ describe('Task Runner', () => {
expect(call.services.scopedClusterClient).toBeTruthy();
expect(call.services).toBeTruthy();
- const logger = taskRunnerFactoryInitializerParams.logger;
expect(logger.debug).toHaveBeenCalledTimes(4);
expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z');
expect(logger.debug).nthCalledWith(
@@ -2584,7 +2587,6 @@ describe('Task Runner', () => {
})
);
- const logger = taskRunnerFactoryInitializerParams.logger;
expect(logger.debug).toHaveBeenCalledTimes(6);
expect(logger.debug).nthCalledWith(
@@ -2750,7 +2752,6 @@ describe('Task Runner', () => {
})
);
- const logger = taskRunnerFactoryInitializerParams.logger;
expect(logger.debug).toHaveBeenCalledTimes(6);
expect(logger.debug).nthCalledWith(
diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts
index 9c8981ad5f4c2..b66452ca8c7ca 100644
--- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts
+++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts
@@ -397,7 +397,10 @@ export class TaskRunner<
`rule execution failure: ${ruleLabel}`,
err.message
);
-
+ this.logger.error(err, {
+ tags: [this.ruleType.id, ruleId, 'rule-run-failed'],
+ error: { stack_trace: err.stack },
+ });
throw new ErrorWithReason(RuleExecutionStatusErrorReasons.Execute, err);
}
@@ -724,7 +727,10 @@ export class TaskRunner<
if (isAlertSavedObjectNotFoundError(err, ruleId)) {
this.logger.debug(message);
} else {
- this.logger.error(message);
+ this.logger.error(message, {
+ tags: [this.ruleType.id, ruleId, 'rule-run-failed'],
+ error: { stack_trace: err.stack },
+ });
}
return originalState;
}
diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts
index 5cce445e7bb4c..f61bd762de458 100644
--- a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts
+++ b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts
@@ -719,8 +719,8 @@ describe('TaskManagerRunner', () => {
});
expect(mockApmTrans.end).toHaveBeenCalledWith('success');
});
- test('makes calls to APM as expected when task fails', async () => {
- const { runner } = await readyToRunStageSetup({
+ test('makes calls to APM and logs errors as expected when task fails', async () => {
+ const { runner, logger } = await readyToRunStageSetup({
instance: {
params: { a: 'b' },
state: { hey: 'there' },
@@ -741,6 +741,11 @@ describe('TaskManagerRunner', () => {
childOf: 'apmTraceparent',
});
expect(mockApmTrans.end).toHaveBeenCalledWith('failure');
+ const loggerCall = logger.error.mock.calls[0][0];
+ const loggerMeta = logger.error.mock.calls[0][1];
+ expect(loggerCall as string).toMatchInlineSnapshot(`"Task bar \\"foo\\" failed: Error: rar"`);
+ expect(loggerMeta?.tags).toEqual(['bar', 'foo', 'task-run-failed']);
+ expect(loggerMeta?.error?.stack_trace).toBeDefined();
});
test('provides execution context on run', async () => {
const { runner } = await readyToRunStageSetup({
diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.ts
index d305a49bef55e..0b735d6b0ede6 100644
--- a/x-pack/plugins/task_manager/server/task_running/task_runner.ts
+++ b/x-pack/plugins/task_manager/server/task_running/task_runner.ts
@@ -313,7 +313,10 @@ export class TaskManagerRunner implements TaskRunner {
if (apmTrans) apmTrans.end('success');
return processedResult;
} catch (err) {
- this.logger.error(`Task ${this} failed: ${err}`);
+ this.logger.error(`Task ${this} failed: ${err}`, {
+ tags: [this.taskType, this.instance.task.id, 'task-run-failed'],
+ error: { stack_trace: err.stack },
+ });
// in error scenario, we can not get the RunResult
// re-use modifiedContext's state, which is correct as of beforeRun
const processedResult = await withSpan({ name: 'process result', type: 'task manager' }, () =>
From 75f786bf734ada380ca829cbb0bc79672f89fa6a Mon Sep 17 00:00:00 2001
From: Nicolas Chaulet
Date: Thu, 16 Jun 2022 17:44:56 -0400
Subject: [PATCH 07/30] [Fleet] Add a pipeline processor to all the
ingest_pipeline installed by fleet (#134578)
---
.../{helper.test.ts => helpers.test.ts} | 68 ++++++++-
.../elasticsearch/ingest_pipeline/helpers.ts | 49 ++++++-
.../elasticsearch/ingest_pipeline/index.ts | 4 +-
.../elasticsearch/ingest_pipeline/install.ts | 45 +++---
.../elasticsearch/ingest_pipeline/types.ts | 19 +++
.../server/services/epm/elasticsearch/meta.ts | 2 +-
.../apis/epm/custom_ingest_pipeline.ts | 129 ++++++++++++++++++
.../fleet_api_integration/apis/epm/index.js | 1 +
8 files changed, 286 insertions(+), 31 deletions(-)
rename x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/{helper.test.ts => helpers.test.ts} (68%)
create mode 100644 x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/types.ts
create mode 100644 x-pack/test/fleet_api_integration/apis/epm/custom_ingest_pipeline.ts
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/helper.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/helpers.test.ts
similarity index 68%
rename from x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/helper.test.ts
rename to x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/helpers.test.ts
index a36c53ca9bab5..3699db0531db6 100644
--- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/helper.test.ts
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/helpers.test.ts
@@ -10,7 +10,12 @@ import path from 'path';
import type { RegistryDataStream } from '../../../../types';
-import { getPipelineNameForInstallation, rewriteIngestPipeline } from './helpers';
+import {
+ addCustomPipelineProcessor,
+ getCustomPipelineNameForDatastream,
+ getPipelineNameForInstallation,
+ rewriteIngestPipeline,
+} from './helpers';
test('a json-format pipeline with pipeline references is correctly rewritten', () => {
const inputStandard = readFileSync(
@@ -137,3 +142,64 @@ test('getPipelineNameForInstallation gets correct name', () => {
`${dataStream.type}-${dataStream.dataset}-${packageVersion}-${pipelineRefName}`
);
});
+
+describe('addCustomPipelineProcessor', () => {
+ it('add custom pipeline processor at the end of the pipeline for yaml pipeline', () => {
+ const pipelineInstall = addCustomPipelineProcessor({
+ contentForInstallation: `
+processors:
+ - set:
+ field: test
+ value: toto
+ `,
+ extension: 'yml',
+ nameForInstallation: 'logs-test-1.0.0',
+ customIngestPipelineNameForInstallation: 'logs-test@custom',
+ });
+
+ expect(pipelineInstall.contentForInstallation).toMatchInlineSnapshot(`
+ "---
+ processors:
+ - set:
+ field: test
+ value: toto
+ - pipeline:
+ name: logs-test@custom
+ ignore_missing_pipeline: true
+ "
+ `);
+ });
+
+ it('add custom pipeline processor at the end of the pipeline for json pipeline', () => {
+ const pipelineInstall = addCustomPipelineProcessor({
+ contentForInstallation: `{
+ "processors": [
+ {
+ "set": {
+ "field": "test",
+ "value": "toto"
+ }
+ }
+ ]
+ }`,
+ extension: 'json',
+ nameForInstallation: 'logs-test-1.0.0',
+ customIngestPipelineNameForInstallation: 'logs-test@custom',
+ });
+
+ expect(pipelineInstall.contentForInstallation).toMatchInlineSnapshot(
+ `"{\\"processors\\":[{\\"set\\":{\\"field\\":\\"test\\",\\"value\\":\\"toto\\"}},{\\"pipeline\\":{\\"name\\":\\"logs-test@custom\\",\\"ignore_missing_pipeline\\":true}}]}"`
+ );
+ });
+});
+
+describe('getCustomPipelineNameForDatastream', () => {
+ it('return the correct custom pipeline for datastream', () => {
+ const res = getCustomPipelineNameForDatastream({
+ type: 'logs',
+ dataset: 'test',
+ } as any);
+
+ expect(res).toBe('logs-test@custom');
+ });
+});
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/helpers.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/helpers.ts
index bbddb0e454174..b2022bfdb8aa3 100644
--- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/helpers.ts
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/helpers.ts
@@ -4,11 +4,14 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
+import { safeDump, safeLoad } from 'js-yaml';
import { ElasticsearchAssetType } from '../../../../types';
import type { RegistryDataStream } from '../../../../types';
import { getPathParts } from '../../archive';
+import type { PipelineInstall, RewriteSubstitution } from './types';
+
export const isTopLevelPipeline = (path: string) => {
const pathParts = getPathParts(path);
return (
@@ -45,11 +48,9 @@ export const getPipelineNameForDatastream = ({
return `${dataStream.type}-${dataStream.dataset}-${packageVersion}`;
};
-export interface RewriteSubstitution {
- source: string;
- target: string;
- templateFunction: string;
-}
+export const getCustomPipelineNameForDatastream = (dataStream: RegistryDataStream): string => {
+ return `${dataStream.type}-${dataStream.dataset}@custom`;
+};
export function rewriteIngestPipeline(
pipeline: string,
@@ -71,3 +72,41 @@ export function rewriteIngestPipeline(
});
return pipeline;
}
+
+function mutatePipelineContentWithNewProcessor(jsonPipelineContent: any, processor: any) {
+ if (!jsonPipelineContent.processors) {
+ jsonPipelineContent.processors = [];
+ }
+
+ jsonPipelineContent.processors.push(processor);
+}
+
+export function addCustomPipelineProcessor(pipeline: PipelineInstall): PipelineInstall {
+ if (!pipeline.customIngestPipelineNameForInstallation) {
+ return pipeline;
+ }
+
+ const customPipelineProcessor = {
+ pipeline: {
+ name: pipeline.customIngestPipelineNameForInstallation,
+ ignore_missing_pipeline: true,
+ },
+ };
+
+ if (pipeline.extension === 'yml') {
+ const parsedPipelineContent = safeLoad(pipeline.contentForInstallation);
+ mutatePipelineContentWithNewProcessor(parsedPipelineContent, customPipelineProcessor);
+ return {
+ ...pipeline,
+ contentForInstallation: `---\n${safeDump(parsedPipelineContent)}`,
+ };
+ }
+
+ const parsedPipelineContent = JSON.parse(pipeline.contentForInstallation);
+ mutatePipelineContentWithNewProcessor(parsedPipelineContent, customPipelineProcessor);
+
+ return {
+ ...pipeline,
+ contentForInstallation: JSON.stringify(parsedPipelineContent),
+ };
+}
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/index.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/index.ts
index 0c51cbcd9602a..a8f0065645b66 100644
--- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/index.ts
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/index.ts
@@ -5,6 +5,6 @@
* 2.0.
*/
-export { prepareToInstallPipelines, isTopLevelPipeline } from './install';
-export { getPipelineNameForDatastream } from './helpers';
+export { prepareToInstallPipelines } from './install';
+export { getPipelineNameForDatastream, isTopLevelPipeline } from './helpers';
export { deletePreviousPipelines, deletePipeline } from './remove';
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts
index 3401674dfda55..481d551cb07ac 100644
--- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/install.ts
@@ -19,28 +19,17 @@ import {
} from '../../../../constants';
import { appendMetadataToIngestPipeline } from '../meta';
-
import { retryTransientEsErrors } from '../retry';
import {
+ getCustomPipelineNameForDatastream,
getPipelineNameForDatastream,
getPipelineNameForInstallation,
rewriteIngestPipeline,
+ isTopLevelPipeline,
+ addCustomPipelineProcessor,
} from './helpers';
-import type { RewriteSubstitution } from './helpers';
-
-interface PipelineInstall {
- nameForInstallation: string;
- contentForInstallation: string;
- extension: string;
-}
-
-export const isTopLevelPipeline = (path: string) => {
- const pathParts = getPathParts(path);
- return (
- pathParts.type === ElasticsearchAssetType.ingestPipeline && pathParts.dataset === undefined
- );
-};
+import type { PipelineInstall, RewriteSubstitution } from './types';
export const prepareToInstallPipelines = (
installablePackage: InstallablePackage,
@@ -156,8 +145,8 @@ export async function installAllPipelines({
? paths.filter((path) => isDataStreamPipeline(path, dataStream.path))
: paths;
const pipelinesInfos: Array<{
- name: string;
nameForInstallation: string;
+ customIngestPipelineNameForInstallation?: string;
content: string;
extension: string;
}> = [];
@@ -176,8 +165,10 @@ export async function installAllPipelines({
});
const content = getAsset(path).toString('utf-8');
pipelinesInfos.push({
- name,
nameForInstallation,
+ customIngestPipelineNameForInstallation: dataStream
+ ? getCustomPipelineNameForDatastream(dataStream)
+ : undefined,
content,
extension,
});
@@ -203,6 +194,7 @@ export async function installAllPipelines({
pipelinesToInstall.push({
nameForInstallation,
+ customIngestPipelineNameForInstallation: getCustomPipelineNameForDatastream(dataStream),
contentForInstallation: 'processors: []',
extension: 'yml',
});
@@ -220,27 +212,36 @@ async function installPipeline({
logger,
pipeline,
installablePackage,
+ shouldAddCustomPipelineProcessor = true,
}: {
esClient: ElasticsearchClient;
logger: Logger;
pipeline: PipelineInstall;
installablePackage?: InstallablePackage;
+ shouldAddCustomPipelineProcessor?: boolean;
}): Promise {
- const pipelineWithMetadata = appendMetadataToIngestPipeline({
+ let pipelineToInstall = appendMetadataToIngestPipeline({
pipeline,
packageName: installablePackage?.name,
});
+ if (shouldAddCustomPipelineProcessor) {
+ pipelineToInstall = addCustomPipelineProcessor(pipelineToInstall);
+ }
+
const esClientParams = {
- id: pipelineWithMetadata.nameForInstallation,
- body: pipelineWithMetadata.contentForInstallation,
+ id: pipelineToInstall.nameForInstallation,
+ body:
+ pipelineToInstall.extension === 'yml'
+ ? pipelineToInstall.contentForInstallation
+ : JSON.parse(pipelineToInstall.contentForInstallation),
};
const esClientRequestOptions: TransportRequestOptions = {
ignore: [404],
};
- if (pipelineWithMetadata.extension === 'yml') {
+ if (pipelineToInstall.extension === 'yml') {
esClientRequestOptions.headers = {
// pipeline is YAML
'Content-Type': 'application/yaml',
@@ -255,7 +256,7 @@ async function installPipeline({
);
return {
- id: pipelineWithMetadata.nameForInstallation,
+ id: pipelineToInstall.nameForInstallation,
type: ElasticsearchAssetType.ingestPipeline,
};
}
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/types.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/types.ts
new file mode 100644
index 0000000000000..c9b7a68e3d892
--- /dev/null
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/ingest_pipeline/types.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.
+ */
+
+export interface PipelineInstall {
+ nameForInstallation: string;
+ contentForInstallation: string;
+ customIngestPipelineNameForInstallation?: string;
+ extension: string;
+}
+
+export interface RewriteSubstitution {
+ source: string;
+ target: string;
+ templateFunction: string;
+}
diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/meta.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/meta.ts
index d691cd8c700e5..0801de321f2b3 100644
--- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/meta.ts
+++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/meta.ts
@@ -58,6 +58,6 @@ export function appendMetadataToIngestPipeline({
return {
...pipeline,
- contentForInstallation: parsedPipelineContent,
+ contentForInstallation: JSON.stringify(parsedPipelineContent),
};
}
diff --git a/x-pack/test/fleet_api_integration/apis/epm/custom_ingest_pipeline.ts b/x-pack/test/fleet_api_integration/apis/epm/custom_ingest_pipeline.ts
new file mode 100644
index 0000000000000..0ef5f648ab36d
--- /dev/null
+++ b/x-pack/test/fleet_api_integration/apis/epm/custom_ingest_pipeline.ts
@@ -0,0 +1,129 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import expect from '@kbn/expect';
+import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
+import { setupFleetAndAgents } from '../agents/services';
+import { skipIfNoDockerRegistry } from '../../helpers';
+
+const TEST_INDEX = 'logs-log.log-test';
+
+const CUSTOM_PIPELINE = 'logs-log.log@custom';
+
+let pkgVersion: string;
+
+export default function (providerContext: FtrProviderContext) {
+ const { getService } = providerContext;
+ const supertest = getService('supertest');
+ const es = getService('es');
+ const esArchiver = getService('esArchiver');
+
+ describe('custom ingest pipeline for fleet managed datastreams', () => {
+ skipIfNoDockerRegistry(providerContext);
+ before(async () => {
+ await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server');
+ });
+ setupFleetAndAgents(providerContext);
+
+ // Use the custom log package to test the custom ingest pipeline
+ before(async () => {
+ const { body: getPackagesRes } = await supertest.get(
+ `/api/fleet/epm/packages?experimental=true`
+ );
+ const logPackage = getPackagesRes.items.find((p: any) => p.name === 'log');
+ if (!logPackage) {
+ throw new Error('No log package');
+ }
+
+ pkgVersion = logPackage.version;
+
+ await supertest
+ .post(`/api/fleet/epm/packages/log/${pkgVersion}`)
+ .set('kbn-xsrf', 'xxxx')
+ .send({ force: true })
+ .expect(200);
+ });
+ after(async () => {
+ await supertest
+ .delete(`/api/fleet/epm/packages/log/${pkgVersion}`)
+ .set('kbn-xsrf', 'xxxx')
+ .send({ force: true })
+ .expect(200);
+ });
+
+ after(async () => {
+ await esArchiver.unload('x-pack/test/functional/es_archives/fleet/empty_fleet_server');
+ });
+
+ after(async () => {
+ const res = await es.search({
+ index: TEST_INDEX,
+ });
+
+ for (const hit of res.hits.hits) {
+ await es.delete({
+ id: hit._id,
+ index: hit._index,
+ });
+ }
+ });
+
+ describe('Without custom pipeline', () => {
+ it('Should write doc correctly', async () => {
+ const res = await es.index({
+ index: 'logs-log.log-test',
+ body: {
+ '@timestamp': '2020-01-01T09:09:00',
+ message: 'hello',
+ },
+ });
+
+ await es.get({
+ id: res._id,
+ index: res._index,
+ });
+ });
+ });
+
+ describe('Without custom pipeline', () => {
+ before(() =>
+ es.ingest.putPipeline({
+ id: CUSTOM_PIPELINE,
+ processors: [
+ {
+ set: {
+ field: 'test',
+ value: 'itworks',
+ },
+ },
+ ],
+ })
+ );
+
+ after(() =>
+ es.ingest.deletePipeline({
+ id: CUSTOM_PIPELINE,
+ })
+ );
+ it('Should write doc correctly', async () => {
+ const res = await es.index({
+ index: 'logs-log.log-test',
+ body: {
+ '@timestamp': '2020-01-01T09:09:00',
+ message: 'hello',
+ },
+ });
+
+ const doc = await es.get<{ test: string }>({
+ id: res._id,
+ index: res._index,
+ });
+ expect(doc._source?.test).to.eql('itworks');
+ });
+ });
+ });
+}
diff --git a/x-pack/test/fleet_api_integration/apis/epm/index.js b/x-pack/test/fleet_api_integration/apis/epm/index.js
index ef103592dfb45..137d7d59d8bfa 100644
--- a/x-pack/test/fleet_api_integration/apis/epm/index.js
+++ b/x-pack/test/fleet_api_integration/apis/epm/index.js
@@ -30,5 +30,6 @@ export default function loadTests({ loadTestFile }) {
loadTestFile(require.resolve('./remove_legacy_templates'));
loadTestFile(require.resolve('./install_error_rollback'));
loadTestFile(require.resolve('./final_pipeline'));
+ loadTestFile(require.resolve('./custom_ingest_pipeline'));
});
}
From 03fe5d11b51ac9b4c1bdc10ec8ce18454a95ce6e Mon Sep 17 00:00:00 2001
From: Lisa Cawley
Date: Thu, 16 Jun 2022 19:07:21 -0700
Subject: [PATCH 08/30] [DOCS] Add server log connector details to connector
APIs (#134409)
---
.../actions-and-connectors/create.asciidoc | 9 +++-
.../actions-and-connectors/execute.asciidoc | 45 +++++++++++++++++--
2 files changed, 48 insertions(+), 6 deletions(-)
diff --git a/docs/api/actions-and-connectors/create.asciidoc b/docs/api/actions-and-connectors/create.asciidoc
index b8334573d2330..6a6a259135df3 100644
--- a/docs/api/actions-and-connectors/create.asciidoc
+++ b/docs/api/actions-and-connectors/create.asciidoc
@@ -31,7 +31,7 @@ You must have `all` privileges for the *Actions and Connectors* feature in the
=== {api-request-body-title}
`config`::
-(Required, object) The configuration for the connector. Configuration properties
+(Required^*^, object) The configuration for the connector. Configuration properties
vary depending on the connector type. For example:
+
--
@@ -68,12 +68,15 @@ For more information, refer to
{kibana-ref}/jira-action-type.html[{jira} connector and action].
====
+This object is not required for server log connectors.
+
For more configuration properties, refer to <>.
// end::connector-config[]
--
`connector_type_id`::
-(Required, string) The connector type ID for the connector.
+(Required, string) The connector type ID for the connector. For example,
+`.index`, `.jira`, or `.server-log`.
`name`::
(Required, string) The display name for the connector.
@@ -99,6 +102,8 @@ authentication.
`email`::
(Required, string) The account email for HTTP Basic authentication.
====
+
+This object is not required for index or server log connectors.
// end::connector-secrets[]
--
diff --git a/docs/api/actions-and-connectors/execute.asciidoc b/docs/api/actions-and-connectors/execute.asciidoc
index 19b03beaa56fe..ed66abf33f58b 100644
--- a/docs/api/actions-and-connectors/execute.asciidoc
+++ b/docs/api/actions-and-connectors/execute.asciidoc
@@ -34,17 +34,33 @@ If you use an index connector, you must also have `all`, `create`, `index`, or
=== {api-path-parms-title}
`id`::
- (Required, string) The ID of the connector.
+(Required, string) The ID of the connector.
`space_id`::
- (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
+(Optional, string) An identifier for the space. If `space_id` is not provided in
+the URL, the default space is used.
+[role="child_attributes"]
[[execute-connector-api-request-body]]
=== {api-request-body-title}
`params`::
- (Required, object) The parameters of the connector. Parameter properties vary depending on
- the connector type. For information about the parameter properties, refer to <>.
+(Required, object) The parameters of the connector. Parameter properties vary
+depending on the connector type. For information about the parameter properties,
+refer to <>.
++
+--
+.Server log connectors
+[%collapsible%open]
+====
+`level`::
+(Optional, string) The log level of the message: `trace`, `debug`, `info`,
+`warn`, `error`, or `fatal`. Defaults to `info`.
+
+`message`::
+(Required, string) The message to log.
+====
+--
[[execute-connector-api-codes]]
=== {api-response-codes-title}
@@ -105,3 +121,24 @@ The API returns the following:
"connector_id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad"
}
--------------------------------------------------
+
+Run a server log connector:
+
+[source,sh]
+--------------------------------------------------
+POST api/actions/connector/7fc7b9a0-ecc9-11ec-8736-e7d63118c907/_execute
+{
+ "params": {
+ "level": "warn",
+ "message": "Test warning message"
+ }
+}
+--------------------------------------------------
+// KIBANA
+
+The API returns the following:
+
+[source,sh]
+--------------------------------------------------
+{"status":"ok","connector_id":"7fc7b9a0-ecc9-11ec-8736-e7d63118c907"}
+--------------------------------------------------
\ No newline at end of file
From 1eeb0d86c03aeaa7de7a1f8909183dd2ca20f529 Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Fri, 17 Jun 2022 00:47:25 -0400
Subject: [PATCH 09/30] [api-docs] Daily api_docs build (#134624)
---
api_docs/actions.devdocs.json | 21 +++
api_docs/actions.mdx | 4 +-
api_docs/advanced_settings.mdx | 2 +-
api_docs/aiops.mdx | 2 +-
api_docs/alerting.mdx | 2 +-
api_docs/apm.devdocs.json | 24 +---
api_docs/apm.mdx | 2 +-
api_docs/banners.mdx | 2 +-
api_docs/bfetch.mdx | 2 +-
api_docs/canvas.mdx | 2 +-
api_docs/cases.mdx | 2 +-
api_docs/charts.mdx | 2 +-
api_docs/cloud.mdx | 2 +-
api_docs/cloud_security_posture.mdx | 2 +-
api_docs/console.mdx | 2 +-
api_docs/controls.mdx | 2 +-
api_docs/core.devdocs.json | 18 +--
api_docs/core.mdx | 2 +-
api_docs/core_application.mdx | 2 +-
api_docs/core_chrome.mdx | 2 +-
api_docs/core_http.mdx | 2 +-
api_docs/core_saved_objects.mdx | 2 +-
api_docs/custom_integrations.mdx | 2 +-
api_docs/dashboard.mdx | 2 +-
api_docs/dashboard_enhanced.mdx | 2 +-
api_docs/data.mdx | 2 +-
api_docs/data_query.mdx | 2 +-
api_docs/data_search.mdx | 2 +-
api_docs/data_view_editor.mdx | 2 +-
api_docs/data_view_field_editor.mdx | 2 +-
api_docs/data_view_management.mdx | 2 +-
api_docs/data_views.mdx | 2 +-
api_docs/data_visualizer.mdx | 2 +-
api_docs/deprecations_by_api.mdx | 2 +-
api_docs/deprecations_by_plugin.mdx | 2 +-
api_docs/deprecations_by_team.mdx | 2 +-
api_docs/dev_tools.mdx | 2 +-
api_docs/discover.mdx | 2 +-
api_docs/discover_enhanced.mdx | 2 +-
api_docs/elastic_apm_synthtrace.mdx | 2 +-
api_docs/embeddable.mdx | 2 +-
api_docs/embeddable_enhanced.mdx | 2 +-
api_docs/encrypted_saved_objects.mdx | 2 +-
api_docs/enterprise_search.mdx | 2 +-
api_docs/es_ui_shared.mdx | 2 +-
api_docs/event_annotation.mdx | 2 +-
api_docs/event_log.mdx | 2 +-
api_docs/expression_error.mdx | 2 +-
api_docs/expression_gauge.mdx | 2 +-
api_docs/expression_heatmap.mdx | 2 +-
api_docs/expression_image.mdx | 2 +-
api_docs/expression_metric.mdx | 2 +-
api_docs/expression_metric_vis.mdx | 2 +-
api_docs/expression_partition_vis.mdx | 2 +-
api_docs/expression_repeat_image.mdx | 2 +-
api_docs/expression_reveal_image.mdx | 2 +-
api_docs/expression_shape.mdx | 2 +-
api_docs/expression_tagcloud.mdx | 2 +-
api_docs/expression_x_y.mdx | 2 +-
api_docs/expressions.mdx | 2 +-
api_docs/features.mdx | 2 +-
api_docs/field_formats.mdx | 2 +-
api_docs/file_upload.mdx | 2 +-
api_docs/fleet.mdx | 2 +-
api_docs/global_search.mdx | 2 +-
api_docs/home.mdx | 2 +-
api_docs/index_lifecycle_management.mdx | 2 +-
api_docs/index_management.mdx | 2 +-
api_docs/infra.mdx | 2 +-
api_docs/inspector.mdx | 2 +-
api_docs/interactive_setup.mdx | 2 +-
api_docs/kbn_ace.mdx | 2 +-
api_docs/kbn_aiops_utils.mdx | 2 +-
api_docs/kbn_alerts.mdx | 2 +-
api_docs/kbn_analytics.mdx | 2 +-
api_docs/kbn_analytics_client.mdx | 2 +-
..._analytics_shippers_elastic_v3_browser.mdx | 2 +-
...n_analytics_shippers_elastic_v3_common.mdx | 2 +-
...n_analytics_shippers_elastic_v3_server.mdx | 2 +-
api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +-
api_docs/kbn_apm_config_loader.mdx | 2 +-
api_docs/kbn_apm_utils.mdx | 2 +-
api_docs/kbn_axe_config.mdx | 2 +-
api_docs/kbn_bazel_packages.mdx | 2 +-
api_docs/kbn_bazel_runner.mdx | 2 +-
api_docs/kbn_ci_stats_core.mdx | 2 +-
api_docs/kbn_ci_stats_reporter.mdx | 2 +-
api_docs/kbn_cli_dev_mode.mdx | 2 +-
api_docs/kbn_coloring.mdx | 2 +-
api_docs/kbn_config.mdx | 2 +-
api_docs/kbn_config_mocks.mdx | 2 +-
api_docs/kbn_config_schema.mdx | 2 +-
.../kbn_core_analytics_browser.devdocs.json | 82 +++++++++++
api_docs/kbn_core_analytics_browser.mdx | 27 ++++
...re_analytics_browser_internal.devdocs.json | 135 ++++++++++++++++++
.../kbn_core_analytics_browser_internal.mdx | 27 ++++
..._core_analytics_browser_mocks.devdocs.json | 90 ++++++++++++
api_docs/kbn_core_analytics_browser_mocks.mdx | 27 ++++
.../kbn_core_base_browser_mocks.devdocs.json | 71 +++++++++
api_docs/kbn_core_base_browser_mocks.mdx | 27 ++++
api_docs/kbn_core_base_common.mdx | 2 +-
api_docs/kbn_core_base_server_mocks.mdx | 2 +-
api_docs/kbn_core_doc_links_browser.mdx | 2 +-
api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +-
api_docs/kbn_core_doc_links_server.mdx | 2 +-
api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +-
.../kbn_core_injected_metadata_browser.mdx | 2 +-
...n_core_injected_metadata_browser_mocks.mdx | 2 +-
api_docs/kbn_core_theme_browser.mdx | 2 +-
api_docs/kbn_core_theme_browser_mocks.mdx | 2 +-
api_docs/kbn_crypto.mdx | 2 +-
api_docs/kbn_datemath.mdx | 2 +-
api_docs/kbn_dev_cli_errors.mdx | 2 +-
api_docs/kbn_dev_cli_runner.mdx | 2 +-
api_docs/kbn_dev_proc_runner.mdx | 2 +-
api_docs/kbn_dev_utils.mdx | 2 +-
api_docs/kbn_doc_links.devdocs.json | 2 +-
api_docs/kbn_doc_links.mdx | 2 +-
api_docs/kbn_docs_utils.mdx | 2 +-
api_docs/kbn_es_archiver.mdx | 2 +-
api_docs/kbn_es_query.mdx | 2 +-
api_docs/kbn_eslint_plugin_imports.mdx | 2 +-
api_docs/kbn_field_types.mdx | 2 +-
api_docs/kbn_find_used_node_modules.mdx | 2 +-
api_docs/kbn_generate.mdx | 2 +-
api_docs/kbn_handlebars.mdx | 2 +-
api_docs/kbn_i18n.mdx | 2 +-
api_docs/kbn_import_resolver.mdx | 2 +-
api_docs/kbn_interpreter.mdx | 2 +-
api_docs/kbn_io_ts_utils.mdx | 2 +-
api_docs/kbn_jest_serializers.mdx | 2 +-
api_docs/kbn_kibana_json_schema.mdx | 2 +-
api_docs/kbn_logging.devdocs.json | 2 +-
api_docs/kbn_logging.mdx | 2 +-
api_docs/kbn_logging_mocks.mdx | 2 +-
api_docs/kbn_mapbox_gl.mdx | 2 +-
api_docs/kbn_monaco.mdx | 2 +-
api_docs/kbn_optimizer.mdx | 2 +-
api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +-
..._performance_testing_dataset_extractor.mdx | 2 +-
api_docs/kbn_plugin_discovery.mdx | 2 +-
api_docs/kbn_plugin_generator.mdx | 2 +-
api_docs/kbn_plugin_helpers.mdx | 2 +-
api_docs/kbn_pm.mdx | 2 +-
api_docs/kbn_react_field.mdx | 2 +-
api_docs/kbn_rule_data_utils.mdx | 2 +-
.../kbn_scalability_simulation_generator.mdx | 2 +-
.../kbn_securitysolution_autocomplete.mdx | 2 +-
api_docs/kbn_securitysolution_es_utils.mdx | 2 +-
api_docs/kbn_securitysolution_hook_utils.mdx | 2 +-
..._securitysolution_io_ts_alerting_types.mdx | 2 +-
.../kbn_securitysolution_io_ts_list_types.mdx | 2 +-
api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +-
api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +-
api_docs/kbn_securitysolution_list_api.mdx | 2 +-
.../kbn_securitysolution_list_constants.mdx | 2 +-
api_docs/kbn_securitysolution_list_hooks.mdx | 2 +-
api_docs/kbn_securitysolution_list_utils.mdx | 2 +-
api_docs/kbn_securitysolution_rules.mdx | 2 +-
api_docs/kbn_securitysolution_t_grid.mdx | 2 +-
api_docs/kbn_securitysolution_utils.mdx | 2 +-
api_docs/kbn_server_http_tools.mdx | 2 +-
api_docs/kbn_server_route_repository.mdx | 2 +-
api_docs/kbn_shared_ux_components.mdx | 2 +-
.../kbn_shared_ux_page_analytics_no_data.mdx | 2 +-
.../kbn_shared_ux_page_kibana_no_data.mdx | 2 +-
.../kbn_shared_ux_prompt_no_data_views.mdx | 2 +-
api_docs/kbn_shared_ux_services.mdx | 2 +-
api_docs/kbn_shared_ux_storybook.mdx | 2 +-
api_docs/kbn_shared_ux_utility.mdx | 2 +-
api_docs/kbn_sort_package_json.mdx | 2 +-
api_docs/kbn_std.mdx | 2 +-
api_docs/kbn_stdio_dev_helpers.mdx | 2 +-
api_docs/kbn_storybook.mdx | 2 +-
api_docs/kbn_telemetry_tools.mdx | 2 +-
api_docs/kbn_test.mdx | 2 +-
api_docs/kbn_test_jest_helpers.mdx | 2 +-
api_docs/kbn_tooling_log.mdx | 2 +-
api_docs/kbn_type_summarizer.mdx | 2 +-
api_docs/kbn_typed_react_router_config.mdx | 2 +-
api_docs/kbn_ui_theme.mdx | 2 +-
api_docs/kbn_utility_types.mdx | 2 +-
api_docs/kbn_utility_types_jest.mdx | 2 +-
api_docs/kbn_utils.mdx | 2 +-
api_docs/kibana_overview.mdx | 2 +-
api_docs/kibana_react.devdocs.json | 8 +-
api_docs/kibana_react.mdx | 2 +-
api_docs/kibana_utils.mdx | 2 +-
api_docs/kubernetes_security.mdx | 2 +-
api_docs/lens.devdocs.json | 2 +-
api_docs/lens.mdx | 2 +-
api_docs/license_api_guard.mdx | 2 +-
api_docs/license_management.mdx | 2 +-
api_docs/licensing.mdx | 2 +-
api_docs/lists.mdx | 2 +-
api_docs/management.mdx | 2 +-
api_docs/maps.mdx | 2 +-
api_docs/maps_ems.mdx | 2 +-
api_docs/ml.mdx | 2 +-
api_docs/monitoring.mdx | 2 +-
api_docs/monitoring_collection.mdx | 2 +-
api_docs/navigation.mdx | 2 +-
api_docs/newsfeed.mdx | 2 +-
api_docs/observability.mdx | 2 +-
api_docs/osquery.mdx | 2 +-
api_docs/plugin_directory.mdx | 12 +-
api_docs/presentation_util.mdx | 2 +-
api_docs/remote_clusters.mdx | 2 +-
api_docs/reporting.mdx | 2 +-
api_docs/rollup.mdx | 2 +-
api_docs/rule_registry.mdx | 2 +-
api_docs/runtime_fields.mdx | 2 +-
api_docs/saved_objects.mdx | 2 +-
api_docs/saved_objects_management.mdx | 2 +-
api_docs/saved_objects_tagging.mdx | 2 +-
api_docs/saved_objects_tagging_oss.mdx | 2 +-
api_docs/screenshot_mode.mdx | 2 +-
api_docs/screenshotting.mdx | 2 +-
api_docs/security.mdx | 2 +-
api_docs/security_solution.mdx | 2 +-
api_docs/session_view.mdx | 2 +-
api_docs/share.mdx | 2 +-
api_docs/shared_u_x.mdx | 2 +-
api_docs/snapshot_restore.mdx | 2 +-
api_docs/spaces.mdx | 2 +-
api_docs/stack_alerts.mdx | 2 +-
api_docs/task_manager.mdx | 2 +-
api_docs/telemetry.mdx | 2 +-
api_docs/telemetry_collection_manager.mdx | 2 +-
api_docs/telemetry_collection_xpack.mdx | 2 +-
api_docs/telemetry_management_section.mdx | 2 +-
api_docs/timelines.mdx | 2 +-
api_docs/transform.mdx | 2 +-
api_docs/triggers_actions_ui.mdx | 2 +-
api_docs/ui_actions.mdx | 2 +-
api_docs/ui_actions_enhanced.mdx | 2 +-
api_docs/unified_search.mdx | 2 +-
api_docs/unified_search_autocomplete.mdx | 2 +-
api_docs/url_forwarding.mdx | 2 +-
api_docs/usage_collection.mdx | 2 +-
api_docs/ux.mdx | 2 +-
api_docs/vis_default_editor.mdx | 2 +-
api_docs/vis_type_gauge.mdx | 2 +-
api_docs/vis_type_heatmap.mdx | 2 +-
api_docs/vis_type_pie.mdx | 2 +-
api_docs/vis_type_table.mdx | 2 +-
api_docs/vis_type_timelion.mdx | 2 +-
api_docs/vis_type_timeseries.mdx | 2 +-
api_docs/vis_type_vega.mdx | 2 +-
api_docs/vis_type_vislib.mdx | 2 +-
api_docs/vis_type_xy.mdx | 2 +-
api_docs/visualizations.mdx | 2 +-
252 files changed, 766 insertions(+), 283 deletions(-)
create mode 100644 api_docs/kbn_core_analytics_browser.devdocs.json
create mode 100644 api_docs/kbn_core_analytics_browser.mdx
create mode 100644 api_docs/kbn_core_analytics_browser_internal.devdocs.json
create mode 100644 api_docs/kbn_core_analytics_browser_internal.mdx
create mode 100644 api_docs/kbn_core_analytics_browser_mocks.devdocs.json
create mode 100644 api_docs/kbn_core_analytics_browser_mocks.mdx
create mode 100644 api_docs/kbn_core_base_browser_mocks.devdocs.json
create mode 100644 api_docs/kbn_core_base_browser_mocks.mdx
diff --git a/api_docs/actions.devdocs.json b/api_docs/actions.devdocs.json
index 269b7af82a800..50d82f10e8ab6 100644
--- a/api_docs/actions.devdocs.json
+++ b/api_docs/actions.devdocs.json
@@ -3248,6 +3248,27 @@
"deprecated": false,
"initialIsOpen": false
},
+ {
+ "parentPluginId": "actions",
+ "id": "def-common.ActionTypeExecutorRawResult",
+ "type": "Type",
+ "tags": [],
+ "label": "ActionTypeExecutorRawResult",
+ "description": [],
+ "signature": [
+ {
+ "pluginId": "actions",
+ "scope": "common",
+ "docId": "kibActionsPluginApi",
+ "section": "def-common.ActionTypeExecutorResult",
+ "text": "ActionTypeExecutorResult"
+ },
+ " & { error?: Error | undefined; }"
+ ],
+ "path": "x-pack/plugins/actions/common/types.ts",
+ "deprecated": false,
+ "initialIsOpen": false
+ },
{
"parentPluginId": "actions",
"id": "def-common.ALERT_HISTORY_PREFIX",
diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx
index 4d28ab700d769..14e82bee3ef78 100644
--- a/api_docs/actions.mdx
+++ b/api_docs/actions.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/actions
title: "actions"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the actions plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
@@ -18,7 +18,7 @@ Contact [Response Ops](https://github.com/orgs/elastic/teams/response-ops) for q
| Public API count | Any count | Items lacking comments | Missing exports |
|-------------------|-----------|------------------------|-----------------|
-| 240 | 0 | 235 | 19 |
+| 241 | 0 | 236 | 19 |
## Client
diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx
index 96832d772c1ac..425741568eebb 100644
--- a/api_docs/advanced_settings.mdx
+++ b/api_docs/advanced_settings.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/advancedSettings
title: "advancedSettings"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the advancedSettings plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx
index 88bfb18a342f7..6db3aebe550b5 100644
--- a/api_docs/aiops.mdx
+++ b/api_docs/aiops.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/aiops
title: "aiops"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the aiops plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx
index 3afa8876a6202..92c92ea262f93 100644
--- a/api_docs/alerting.mdx
+++ b/api_docs/alerting.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/alerting
title: "alerting"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the alerting plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json
index 5a7405626d5c0..df9448f3adc92 100644
--- a/api_docs/apm.devdocs.json
+++ b/api_docs/apm.devdocs.json
@@ -790,7 +790,7 @@
"label": "APIEndpoint",
"description": [],
"signature": [
- "\"POST /internal/apm/data_view/static\" | \"GET /internal/apm/data_view/dynamic\" | \"GET /internal/apm/environments\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}\" | \"GET /internal/apm/services/{serviceName}/errors/distribution\" | \"POST /internal/apm/latency/overall_distribution\" | \"GET /internal/apm/services/{serviceName}/metrics/charts\" | \"GET /internal/apm/observability_overview\" | \"GET /internal/apm/observability_overview/has_data\" | \"GET /internal/apm/ux/client-metrics\" | \"GET /internal/apm/ux/page-load-distribution\" | \"GET /internal/apm/ux/page-load-distribution/breakdown\" | \"GET /internal/apm/ux/page-view-trends\" | \"GET /internal/apm/ux/visitor-breakdown\" | \"GET /internal/apm/ux/long-task-metrics\" | \"GET /api/apm/observability_overview/has_rum_data\" | \"GET /internal/apm/service-map\" | \"GET /internal/apm/service-map/service/{serviceName}\" | \"GET /internal/apm/service-map/backend\" | \"GET /internal/apm/services/{serviceName}/serviceNodes\" | \"GET /internal/apm/services\" | \"GET /internal/apm/services/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/metadata/details\" | \"GET /internal/apm/services/{serviceName}/metadata/icons\" | \"GET /internal/apm/services/{serviceName}/agent\" | \"GET /internal/apm/services/{serviceName}/transaction_types\" | \"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search\" | \"POST /api/apm/services/{serviceName}/annotation\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /internal/apm/services/{serviceName}/throughput\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/dependencies\" | \"GET /internal/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /internal/apm/services/{serviceName}/profiling/timeline\" | \"GET /internal/apm/services/{serviceName}/profiling/statistics\" | \"GET /internal/apm/services/{serviceName}/infrastructure_attributes_for_logs\" | \"GET /internal/apm/services/{serviceName}/anomaly_charts\" | \"GET /internal/apm/sorted_and_filtered_services\" | \"GET /internal/apm/service-groups\" | \"GET /internal/apm/service-group\" | \"POST /internal/apm/service-group\" | \"DELETE /internal/apm/service-group\" | \"GET /internal/apm/service-group/services\" | \"GET /internal/apm/suggestions\" | \"GET /internal/apm/traces/{traceId}\" | \"GET /internal/apm/traces\" | \"GET /internal/apm/traces/{traceId}/root_transaction\" | \"GET /internal/apm/transactions/{transactionId}\" | \"GET /internal/apm/traces/find\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /internal/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate_by_transaction_name\" | \"GET /internal/apm/alerts/chart_preview/transaction_error_rate\" | \"GET /internal/apm/alerts/chart_preview/transaction_duration\" | \"GET /internal/apm/alerts/chart_preview/transaction_error_count\" | \"GET /api/apm/settings/agent-configuration\" | \"GET /api/apm/settings/agent-configuration/view\" | \"DELETE /api/apm/settings/agent-configuration\" | \"PUT /api/apm/settings/agent-configuration\" | \"POST /api/apm/settings/agent-configuration/search\" | \"GET /api/apm/settings/agent-configuration/environments\" | \"GET /api/apm/settings/agent-configuration/agent_name\" | \"GET /internal/apm/settings/anomaly-detection/jobs\" | \"POST /internal/apm/settings/anomaly-detection/jobs\" | \"GET /internal/apm/settings/anomaly-detection/environments\" | \"POST /internal/apm/settings/anomaly-detection/update_to_v3\" | \"GET /internal/apm/settings/apm-index-settings\" | \"GET /internal/apm/settings/apm-indices\" | \"POST /internal/apm/settings/apm-indices/save\" | \"GET /internal/apm/settings/custom_links/transaction\" | \"GET /internal/apm/settings/custom_links\" | \"POST /internal/apm/settings/custom_links\" | \"PUT /internal/apm/settings/custom_links/{id}\" | \"DELETE /internal/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps\" | \"POST /api/apm/sourcemaps\" | \"DELETE /api/apm/sourcemaps/{id}\" | \"GET /internal/apm/fleet/has_apm_policies\" | \"GET /internal/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema\" | \"GET /internal/apm/fleet/apm_server_schema/unsupported\" | \"GET /internal/apm/fleet/migration_check\" | \"POST /internal/apm/fleet/cloud_apm_package_policy\" | \"GET /internal/apm/backends/top_backends\" | \"GET /internal/apm/backends/upstream_services\" | \"GET /internal/apm/backends/metadata\" | \"GET /internal/apm/backends/charts/latency\" | \"GET /internal/apm/backends/charts/throughput\" | \"GET /internal/apm/backends/charts/error_rate\" | \"GET /internal/apm/backends/operations\" | \"POST /internal/apm/correlations/p_values\" | \"GET /internal/apm/correlations/field_candidates\" | \"POST /internal/apm/correlations/field_stats\" | \"GET /internal/apm/correlations/field_value_stats\" | \"POST /internal/apm/correlations/field_value_pairs\" | \"POST /internal/apm/correlations/significant_correlations\" | \"GET /internal/apm/fallback_to_transactions\" | \"GET /internal/apm/has_data\" | \"GET /internal/apm/event_metadata/{processorEvent}/{id}\" | \"GET /internal/apm/agent_keys\" | \"GET /internal/apm/agent_keys/privileges\" | \"POST /internal/apm/api_key/invalidate\" | \"POST /api/apm/agent_keys\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/parents\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/children\" | \"GET /internal/apm/services/{serviceName}/infrastructure_attributes\" | \"GET /internal/apm/debug-telemetry\" | \"GET /internal/apm/time_range_metadata\""
+ "\"POST /internal/apm/data_view/static\" | \"GET /internal/apm/data_view/dynamic\" | \"GET /internal/apm/environments\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}\" | \"GET /internal/apm/services/{serviceName}/errors/distribution\" | \"POST /internal/apm/latency/overall_distribution\" | \"GET /internal/apm/services/{serviceName}/metrics/charts\" | \"GET /internal/apm/observability_overview\" | \"GET /internal/apm/observability_overview/has_data\" | \"GET /internal/apm/ux/client-metrics\" | \"GET /internal/apm/ux/page-load-distribution\" | \"GET /internal/apm/ux/page-load-distribution/breakdown\" | \"GET /internal/apm/ux/page-view-trends\" | \"GET /internal/apm/ux/visitor-breakdown\" | \"GET /internal/apm/ux/long-task-metrics\" | \"GET /internal/apm/service-map\" | \"GET /internal/apm/service-map/service/{serviceName}\" | \"GET /internal/apm/service-map/backend\" | \"GET /internal/apm/services/{serviceName}/serviceNodes\" | \"GET /internal/apm/services\" | \"GET /internal/apm/services/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/metadata/details\" | \"GET /internal/apm/services/{serviceName}/metadata/icons\" | \"GET /internal/apm/services/{serviceName}/agent\" | \"GET /internal/apm/services/{serviceName}/transaction_types\" | \"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search\" | \"POST /api/apm/services/{serviceName}/annotation\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /internal/apm/services/{serviceName}/throughput\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/dependencies\" | \"GET /internal/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /internal/apm/services/{serviceName}/profiling/timeline\" | \"GET /internal/apm/services/{serviceName}/profiling/statistics\" | \"GET /internal/apm/services/{serviceName}/infrastructure_attributes_for_logs\" | \"GET /internal/apm/services/{serviceName}/anomaly_charts\" | \"GET /internal/apm/sorted_and_filtered_services\" | \"GET /internal/apm/service-groups\" | \"GET /internal/apm/service-group\" | \"POST /internal/apm/service-group\" | \"DELETE /internal/apm/service-group\" | \"GET /internal/apm/service-group/services\" | \"GET /internal/apm/suggestions\" | \"GET /internal/apm/traces/{traceId}\" | \"GET /internal/apm/traces\" | \"GET /internal/apm/traces/{traceId}/root_transaction\" | \"GET /internal/apm/transactions/{transactionId}\" | \"GET /internal/apm/traces/find\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /internal/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate_by_transaction_name\" | \"GET /internal/apm/alerts/chart_preview/transaction_error_rate\" | \"GET /internal/apm/alerts/chart_preview/transaction_duration\" | \"GET /internal/apm/alerts/chart_preview/transaction_error_count\" | \"GET /api/apm/settings/agent-configuration\" | \"GET /api/apm/settings/agent-configuration/view\" | \"DELETE /api/apm/settings/agent-configuration\" | \"PUT /api/apm/settings/agent-configuration\" | \"POST /api/apm/settings/agent-configuration/search\" | \"GET /api/apm/settings/agent-configuration/environments\" | \"GET /api/apm/settings/agent-configuration/agent_name\" | \"GET /internal/apm/settings/anomaly-detection/jobs\" | \"POST /internal/apm/settings/anomaly-detection/jobs\" | \"GET /internal/apm/settings/anomaly-detection/environments\" | \"POST /internal/apm/settings/anomaly-detection/update_to_v3\" | \"GET /internal/apm/settings/apm-index-settings\" | \"GET /internal/apm/settings/apm-indices\" | \"POST /internal/apm/settings/apm-indices/save\" | \"GET /internal/apm/settings/custom_links/transaction\" | \"GET /internal/apm/settings/custom_links\" | \"POST /internal/apm/settings/custom_links\" | \"PUT /internal/apm/settings/custom_links/{id}\" | \"DELETE /internal/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps\" | \"POST /api/apm/sourcemaps\" | \"DELETE /api/apm/sourcemaps/{id}\" | \"GET /internal/apm/fleet/has_apm_policies\" | \"GET /internal/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema\" | \"GET /internal/apm/fleet/apm_server_schema/unsupported\" | \"GET /internal/apm/fleet/migration_check\" | \"POST /internal/apm/fleet/cloud_apm_package_policy\" | \"GET /internal/apm/backends/top_backends\" | \"GET /internal/apm/backends/upstream_services\" | \"GET /internal/apm/backends/metadata\" | \"GET /internal/apm/backends/charts/latency\" | \"GET /internal/apm/backends/charts/throughput\" | \"GET /internal/apm/backends/charts/error_rate\" | \"GET /internal/apm/backends/operations\" | \"POST /internal/apm/correlations/p_values\" | \"GET /internal/apm/correlations/field_candidates\" | \"POST /internal/apm/correlations/field_stats\" | \"GET /internal/apm/correlations/field_value_stats\" | \"POST /internal/apm/correlations/field_value_pairs\" | \"POST /internal/apm/correlations/significant_correlations\" | \"GET /internal/apm/fallback_to_transactions\" | \"GET /internal/apm/has_data\" | \"GET /internal/apm/event_metadata/{processorEvent}/{id}\" | \"GET /internal/apm/agent_keys\" | \"GET /internal/apm/agent_keys/privileges\" | \"POST /internal/apm/api_key/invalidate\" | \"POST /api/apm/agent_keys\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/parents\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/children\" | \"GET /internal/apm/services/{serviceName}/infrastructure_attributes\" | \"GET /internal/apm/debug-telemetry\" | \"GET /internal/apm/time_range_metadata\""
],
"path": "x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts",
"deprecated": false,
@@ -4686,28 +4686,6 @@
"ServiceAnomalyStats",
" | undefined; label: string | undefined; id?: string | undefined; parent?: string | undefined; position?: cytoscape.Position | undefined; } | { 'span.destination.service.resource': string; 'span.type': string; 'span.subtype': string; label: string | undefined; id?: string | undefined; parent?: string | undefined; position?: cytoscape.Position | undefined; } | { id: string; source: string | undefined; target: string | undefined; label: string | undefined; bidirectional?: boolean | undefined; isInverseEdge?: boolean | undefined; } | undefined)[]; }; } | { data: { id: string; source: string; target: string; }; })[]; }, ",
"APMRouteCreateOptions",
- ">; \"GET /api/apm/observability_overview/has_rum_data\": ",
- "ServerRoute",
- "<\"GET /api/apm/observability_overview/has_rum_data\", ",
- "PartialC",
- "<{ query: ",
- "PartialC",
- "<{ uiFilters: ",
- "StringC",
- "; start: ",
- "Type",
- "; end: ",
- "Type",
- "; }>; }>, ",
- {
- "pluginId": "apm",
- "scope": "server",
- "docId": "kibApmPluginApi",
- "section": "def-server.APMRouteHandlerResources",
- "text": "APMRouteHandlerResources"
- },
- ", { indices: string; hasData: boolean; serviceName: string | number | undefined; }, ",
- "APMRouteCreateOptions",
">; \"GET /internal/apm/ux/long-task-metrics\": ",
"ServerRoute",
"<\"GET /internal/apm/ux/long-task-metrics\", ",
diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx
index fd5a7531ff270..80513c9fe2b20 100644
--- a/api_docs/apm.mdx
+++ b/api_docs/apm.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/apm
title: "apm"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the apm plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx
index deeb88b763c5d..63ef2ef6fc1fc 100644
--- a/api_docs/banners.mdx
+++ b/api_docs/banners.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/banners
title: "banners"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the banners plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx
index 3ff01f989cc54..219c7e206a0e9 100644
--- a/api_docs/bfetch.mdx
+++ b/api_docs/bfetch.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/bfetch
title: "bfetch"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the bfetch plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx
index 930c69ee86ffe..0bce7fad086b6 100644
--- a/api_docs/canvas.mdx
+++ b/api_docs/canvas.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/canvas
title: "canvas"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the canvas plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx
index ee67d893f5d4d..b450cc77fa82a 100644
--- a/api_docs/cases.mdx
+++ b/api_docs/cases.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/cases
title: "cases"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the cases plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx
index 208b573144445..d30c10e9ab203 100644
--- a/api_docs/charts.mdx
+++ b/api_docs/charts.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/charts
title: "charts"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the charts plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx
index d3972a83409d5..a8f37e2a27277 100644
--- a/api_docs/cloud.mdx
+++ b/api_docs/cloud.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/cloud
title: "cloud"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the cloud plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx
index 6e039b8066b35..eb4dea98ab804 100644
--- a/api_docs/cloud_security_posture.mdx
+++ b/api_docs/cloud_security_posture.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture
title: "cloudSecurityPosture"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the cloudSecurityPosture plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/console.mdx b/api_docs/console.mdx
index 1fe970f82ca0c..012e1921786e2 100644
--- a/api_docs/console.mdx
+++ b/api_docs/console.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/console
title: "console"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the console plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx
index 3f0209131ac98..a981bacf2343c 100644
--- a/api_docs/controls.mdx
+++ b/api_docs/controls.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/controls
title: "controls"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the controls plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/core.devdocs.json b/api_docs/core.devdocs.json
index fe0959262596d..83f05f2617239 100644
--- a/api_docs/core.devdocs.json
+++ b/api_docs/core.devdocs.json
@@ -7525,7 +7525,7 @@
"tags": [],
"label": "AnalyticsServiceSetup",
"description": [
- "\nExposes the public APIs of the AnalyticsClient during the setup phase.\n{@link AnalyticsClient}"
+ "\r\nExposes the public APIs of the AnalyticsClient during the setup phase.\r\n{@link AnalyticsClient}"
],
"signature": [
"{ optIn: (optInConfig: ",
@@ -7546,7 +7546,7 @@
"ContextProviderOpts",
") => void; removeContextProvider: (contextProviderName: string) => void; }"
],
- "path": "src/core/public/analytics/analytics_service.ts",
+ "path": "node_modules/@types/kbn__core-analytics-browser/index.d.ts",
"deprecated": false,
"initialIsOpen": false
},
@@ -7557,7 +7557,7 @@
"tags": [],
"label": "AnalyticsServiceStart",
"description": [
- "\nExposes the public APIs of the AnalyticsClient during the start phase\n{@link AnalyticsClient}"
+ "\r\nExposes the public APIs of the AnalyticsClient during the start phase\r\n{@link AnalyticsClient}"
],
"signature": [
"{ optIn: (optInConfig: ",
@@ -7568,7 +7568,7 @@
"TelemetryCounter",
">; }"
],
- "path": "src/core/public/analytics/analytics_service.ts",
+ "path": "node_modules/@types/kbn__core-analytics-browser/index.d.ts",
"deprecated": false,
"initialIsOpen": false
},
@@ -7948,11 +7948,11 @@
"\nA sub-set of {@link UiSettingsParams} exposed to the client-side."
],
"signature": [
- "{ type?: ",
+ "{ metric?: { type: string; name: string; } | undefined; type?: ",
"UiSettingsType",
" | undefined; value?: unknown; description?: string | undefined; name?: string | undefined; options?: string[] | undefined; order?: number | undefined; category?: string[] | undefined; optionLabels?: Record | undefined; requiresPageReload?: boolean | undefined; readonly?: boolean | undefined; sensitive?: boolean | undefined; deprecation?: ",
"DeprecationSettings",
- " | undefined; metric?: { type: string; name: string; } | undefined; }"
+ " | undefined; }"
],
"path": "src/core/types/ui_settings.ts",
"deprecated": false,
@@ -24607,7 +24607,7 @@
"label": "EcsEventKind",
"description": [],
"signature": [
- "\"alert\" | \"state\" | \"metric\" | \"event\" | \"signal\" | \"pipeline_error\""
+ "\"metric\" | \"alert\" | \"state\" | \"event\" | \"signal\" | \"pipeline_error\""
],
"path": "node_modules/@types/kbn__logging/index.d.ts",
"deprecated": false,
@@ -26986,11 +26986,11 @@
"\nA sub-set of {@link UiSettingsParams} exposed to the client-side."
],
"signature": [
- "{ type?: ",
+ "{ metric?: { type: string; name: string; } | undefined; type?: ",
"UiSettingsType",
" | undefined; value?: unknown; description?: string | undefined; name?: string | undefined; options?: string[] | undefined; order?: number | undefined; category?: string[] | undefined; optionLabels?: Record | undefined; requiresPageReload?: boolean | undefined; readonly?: boolean | undefined; sensitive?: boolean | undefined; deprecation?: ",
"DeprecationSettings",
- " | undefined; metric?: { type: string; name: string; } | undefined; }"
+ " | undefined; }"
],
"path": "src/core/types/ui_settings.ts",
"deprecated": false,
diff --git a/api_docs/core.mdx b/api_docs/core.mdx
index 76c0c9eed54b1..9ab8c48a434e0 100644
--- a/api_docs/core.mdx
+++ b/api_docs/core.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core
title: "core"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the core plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/core_application.mdx b/api_docs/core_application.mdx
index d0ef203875d56..2887bb671225c 100644
--- a/api_docs/core_application.mdx
+++ b/api_docs/core_application.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-application
title: "core.application"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the core.application plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.application']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/core_chrome.mdx b/api_docs/core_chrome.mdx
index 9237f3bf4d578..61ad90364a074 100644
--- a/api_docs/core_chrome.mdx
+++ b/api_docs/core_chrome.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-chrome
title: "core.chrome"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the core.chrome plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.chrome']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/core_http.mdx b/api_docs/core_http.mdx
index 9c68cd0c4afe4..2ffc57c7c3b3a 100644
--- a/api_docs/core_http.mdx
+++ b/api_docs/core_http.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-http
title: "core.http"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the core.http plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.http']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/core_saved_objects.mdx b/api_docs/core_saved_objects.mdx
index 146d5a9ee9add..bc8fe337e21f7 100644
--- a/api_docs/core_saved_objects.mdx
+++ b/api_docs/core_saved_objects.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-savedObjects
title: "core.savedObjects"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the core.savedObjects plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.savedObjects']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx
index d087595293399..5aedc4f3e0951 100644
--- a/api_docs/custom_integrations.mdx
+++ b/api_docs/custom_integrations.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/customIntegrations
title: "customIntegrations"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the customIntegrations plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx
index eed342c9cd546..2f3723f1a5276 100644
--- a/api_docs/dashboard.mdx
+++ b/api_docs/dashboard.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dashboard
title: "dashboard"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the dashboard plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx
index d14c8bdd43f85..0125b564a2345 100644
--- a/api_docs/dashboard_enhanced.mdx
+++ b/api_docs/dashboard_enhanced.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced
title: "dashboardEnhanced"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the dashboardEnhanced plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/data.mdx b/api_docs/data.mdx
index bdb2a45c2fa52..ded5bcec67068 100644
--- a/api_docs/data.mdx
+++ b/api_docs/data.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/data
title: "data"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the data plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx
index d7765e9834e9f..f3cc5aeb5fbe6 100644
--- a/api_docs/data_query.mdx
+++ b/api_docs/data_query.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/data-query
title: "data.query"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the data.query plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx
index 4841f85cc8320..0d51bf5dde050 100644
--- a/api_docs/data_search.mdx
+++ b/api_docs/data_search.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/data-search
title: "data.search"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the data.search plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx
index 573a6c5cc2414..3346c3daa699e 100644
--- a/api_docs/data_view_editor.mdx
+++ b/api_docs/data_view_editor.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViewEditor
title: "dataViewEditor"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the dataViewEditor plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx
index 383c1b67c843c..e743338467fad 100644
--- a/api_docs/data_view_field_editor.mdx
+++ b/api_docs/data_view_field_editor.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor
title: "dataViewFieldEditor"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the dataViewFieldEditor plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx
index de0e359fd5f0a..12e90c08271b5 100644
--- a/api_docs/data_view_management.mdx
+++ b/api_docs/data_view_management.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViewManagement
title: "dataViewManagement"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the dataViewManagement plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx
index f47371d893291..ac66db25b92d5 100644
--- a/api_docs/data_views.mdx
+++ b/api_docs/data_views.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViews
title: "dataViews"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the dataViews plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx
index 6358b2fb6dad8..ab964b2f9445b 100644
--- a/api_docs/data_visualizer.mdx
+++ b/api_docs/data_visualizer.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataVisualizer
title: "dataVisualizer"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the dataVisualizer plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx
index 3586f39e6001a..fd794be9f40ee 100644
--- a/api_docs/deprecations_by_api.mdx
+++ b/api_docs/deprecations_by_api.mdx
@@ -3,7 +3,7 @@ id: kibDevDocsDeprecationsByApi
slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api
title: Deprecated API usage by API
summary: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by.
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system.
---
diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx
index 4f7b414967b0f..134b1bc796bcd 100644
--- a/api_docs/deprecations_by_plugin.mdx
+++ b/api_docs/deprecations_by_plugin.mdx
@@ -3,7 +3,7 @@ id: kibDevDocsDeprecationsByPlugin
slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin
title: Deprecated API usage by plugin
summary: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by.
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system.
---
diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx
index f27558d24406e..bc294651d31c0 100644
--- a/api_docs/deprecations_by_team.mdx
+++ b/api_docs/deprecations_by_team.mdx
@@ -3,7 +3,7 @@ id: kibDevDocsDeprecationsDueByTeam
slug: /kibana-dev-docs/api-meta/deprecations-due-by-team
title: Deprecated APIs due to be removed, by team
summary: Lists the teams that are referencing deprecated APIs with a remove by date.
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system.
---
diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx
index aa196cbfe5c3d..8c9a730a702b1 100644
--- a/api_docs/dev_tools.mdx
+++ b/api_docs/dev_tools.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/devTools
title: "devTools"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the devTools plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx
index cf505d782d95e..97b58fd6a9777 100644
--- a/api_docs/discover.mdx
+++ b/api_docs/discover.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/discover
title: "discover"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the discover plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx
index ef0fa4b796a60..1bc062f2b93f0 100644
--- a/api_docs/discover_enhanced.mdx
+++ b/api_docs/discover_enhanced.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced
title: "discoverEnhanced"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the discoverEnhanced plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/elastic_apm_synthtrace.mdx b/api_docs/elastic_apm_synthtrace.mdx
index 34e430a447bb3..7bcf609af04fc 100644
--- a/api_docs/elastic_apm_synthtrace.mdx
+++ b/api_docs/elastic_apm_synthtrace.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/elastic-apm-synthtrace
title: "@elastic/apm-synthtrace"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @elastic/apm-synthtrace plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@elastic/apm-synthtrace']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx
index 72fe88b5faafd..d1888f2409312 100644
--- a/api_docs/embeddable.mdx
+++ b/api_docs/embeddable.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/embeddable
title: "embeddable"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the embeddable plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx
index 9e9b3c9a07111..26e0ae1962c2b 100644
--- a/api_docs/embeddable_enhanced.mdx
+++ b/api_docs/embeddable_enhanced.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced
title: "embeddableEnhanced"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the embeddableEnhanced plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx
index effd93686b7ce..399eccc588fcf 100644
--- a/api_docs/encrypted_saved_objects.mdx
+++ b/api_docs/encrypted_saved_objects.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects
title: "encryptedSavedObjects"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the encryptedSavedObjects plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx
index 9d6941e0d31e2..6f6f7e669baa8 100644
--- a/api_docs/enterprise_search.mdx
+++ b/api_docs/enterprise_search.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch
title: "enterpriseSearch"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the enterpriseSearch plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx
index 52983caf8e0f8..1c5ea02607abb 100644
--- a/api_docs/es_ui_shared.mdx
+++ b/api_docs/es_ui_shared.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/esUiShared
title: "esUiShared"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the esUiShared plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx
index b65e56704097f..a34264a49d770 100644
--- a/api_docs/event_annotation.mdx
+++ b/api_docs/event_annotation.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/eventAnnotation
title: "eventAnnotation"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the eventAnnotation plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx
index 9796ae9597399..23bc8a890fbf2 100644
--- a/api_docs/event_log.mdx
+++ b/api_docs/event_log.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/eventLog
title: "eventLog"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the eventLog plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx
index a0c223ecfafc8..0cc6d4f9b19cd 100644
--- a/api_docs/expression_error.mdx
+++ b/api_docs/expression_error.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionError
title: "expressionError"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressionError plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx
index 5afad07465b02..2d46ef46f5d65 100644
--- a/api_docs/expression_gauge.mdx
+++ b/api_docs/expression_gauge.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionGauge
title: "expressionGauge"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressionGauge plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx
index 10bbe685b8a85..2cddb207e6342 100644
--- a/api_docs/expression_heatmap.mdx
+++ b/api_docs/expression_heatmap.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap
title: "expressionHeatmap"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressionHeatmap plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx
index 659458a80f989..749140407ddda 100644
--- a/api_docs/expression_image.mdx
+++ b/api_docs/expression_image.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionImage
title: "expressionImage"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressionImage plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx
index 5523e9a5d9b22..44dcc6139d9ca 100644
--- a/api_docs/expression_metric.mdx
+++ b/api_docs/expression_metric.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionMetric
title: "expressionMetric"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressionMetric plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx
index 81ad91e425bd4..1cc416f370df0 100644
--- a/api_docs/expression_metric_vis.mdx
+++ b/api_docs/expression_metric_vis.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis
title: "expressionMetricVis"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressionMetricVis plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx
index 53db2d5afb59a..79843a8b130de 100644
--- a/api_docs/expression_partition_vis.mdx
+++ b/api_docs/expression_partition_vis.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis
title: "expressionPartitionVis"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressionPartitionVis plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx
index d9c0f593df339..e341355358c2d 100644
--- a/api_docs/expression_repeat_image.mdx
+++ b/api_docs/expression_repeat_image.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage
title: "expressionRepeatImage"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressionRepeatImage plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx
index febd4e7102bc5..f5e84bb696834 100644
--- a/api_docs/expression_reveal_image.mdx
+++ b/api_docs/expression_reveal_image.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage
title: "expressionRevealImage"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressionRevealImage plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx
index 63698129558ea..e2aa75f04539e 100644
--- a/api_docs/expression_shape.mdx
+++ b/api_docs/expression_shape.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionShape
title: "expressionShape"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressionShape plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx
index fae0b0703fcf5..caae83e871921 100644
--- a/api_docs/expression_tagcloud.mdx
+++ b/api_docs/expression_tagcloud.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud
title: "expressionTagcloud"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressionTagcloud plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx
index ec4c89d362b8e..c9d2ca31aa1b9 100644
--- a/api_docs/expression_x_y.mdx
+++ b/api_docs/expression_x_y.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionXY
title: "expressionXY"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressionXY plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx
index d09308f991b92..f31b0e5d2bf2d 100644
--- a/api_docs/expressions.mdx
+++ b/api_docs/expressions.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressions
title: "expressions"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the expressions plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/features.mdx b/api_docs/features.mdx
index 08abd876b378b..7ffa6044ba520 100644
--- a/api_docs/features.mdx
+++ b/api_docs/features.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/features
title: "features"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the features plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx
index 2aafc2dae0353..0382794a17f74 100644
--- a/api_docs/field_formats.mdx
+++ b/api_docs/field_formats.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/fieldFormats
title: "fieldFormats"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the fieldFormats plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx
index 2c9d6aca1295a..3a953da9ab5f5 100644
--- a/api_docs/file_upload.mdx
+++ b/api_docs/file_upload.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/fileUpload
title: "fileUpload"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the fileUpload plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx
index e33d0ea894a81..ab876b56d2c4f 100644
--- a/api_docs/fleet.mdx
+++ b/api_docs/fleet.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/fleet
title: "fleet"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the fleet plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx
index 2fae29cc07c47..4e1db11ead733 100644
--- a/api_docs/global_search.mdx
+++ b/api_docs/global_search.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/globalSearch
title: "globalSearch"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the globalSearch plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/home.mdx b/api_docs/home.mdx
index 1ca07ffb82c6c..118c4e6a1fe8a 100644
--- a/api_docs/home.mdx
+++ b/api_docs/home.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/home
title: "home"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the home plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx
index 3c10da180b897..22b5f7c44703c 100644
--- a/api_docs/index_lifecycle_management.mdx
+++ b/api_docs/index_lifecycle_management.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement
title: "indexLifecycleManagement"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the indexLifecycleManagement plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx
index 322663333105e..34fe5b7cf827e 100644
--- a/api_docs/index_management.mdx
+++ b/api_docs/index_management.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/indexManagement
title: "indexManagement"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the indexManagement plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx
index 64b862b209f95..7e4607a07826f 100644
--- a/api_docs/infra.mdx
+++ b/api_docs/infra.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/infra
title: "infra"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the infra plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx
index 2c3ac7eb456c8..3fa5862104d7a 100644
--- a/api_docs/inspector.mdx
+++ b/api_docs/inspector.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/inspector
title: "inspector"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the inspector plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx
index cf1596c2f6455..f53f0af2ad8f9 100644
--- a/api_docs/interactive_setup.mdx
+++ b/api_docs/interactive_setup.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/interactiveSetup
title: "interactiveSetup"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the interactiveSetup plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx
index 4c52cf51431e4..47347d8e4422b 100644
--- a/api_docs/kbn_ace.mdx
+++ b/api_docs/kbn_ace.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ace
title: "@kbn/ace"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/ace plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx
index ea97f81828020..622e711358ca6 100644
--- a/api_docs/kbn_aiops_utils.mdx
+++ b/api_docs/kbn_aiops_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils
title: "@kbn/aiops-utils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/aiops-utils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx
index 1cbb2574f3890..778bf54528f15 100644
--- a/api_docs/kbn_alerts.mdx
+++ b/api_docs/kbn_alerts.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-alerts
title: "@kbn/alerts"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/alerts plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx
index ee0b434e4b97f..71e6eafb0595e 100644
--- a/api_docs/kbn_analytics.mdx
+++ b/api_docs/kbn_analytics.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics
title: "@kbn/analytics"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/analytics plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx
index 2c43946be4cfc..6c688f9c642f7 100644
--- a/api_docs/kbn_analytics_client.mdx
+++ b/api_docs/kbn_analytics_client.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client
title: "@kbn/analytics-client"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/analytics-client plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx
index c5197df8e8c5c..059a4dfb94a2a 100644
--- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx
+++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser
title: "@kbn/analytics-shippers-elastic-v3-browser"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx
index b50ed3226aee9..13f7ac9fa1727 100644
--- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx
+++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common
title: "@kbn/analytics-shippers-elastic-v3-common"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx
index a3c49c7b2eee7..363428a068d5b 100644
--- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx
+++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server
title: "@kbn/analytics-shippers-elastic-v3-server"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx
index 818eb28b38f72..b0f6c12b49f1f 100644
--- a/api_docs/kbn_analytics_shippers_fullstory.mdx
+++ b/api_docs/kbn_analytics_shippers_fullstory.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory
title: "@kbn/analytics-shippers-fullstory"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/analytics-shippers-fullstory plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx
index 294463866bb3c..2759bb8df0ca1 100644
--- a/api_docs/kbn_apm_config_loader.mdx
+++ b/api_docs/kbn_apm_config_loader.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader
title: "@kbn/apm-config-loader"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/apm-config-loader plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx
index 065993bafaf25..dabf2f2192b99 100644
--- a/api_docs/kbn_apm_utils.mdx
+++ b/api_docs/kbn_apm_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils
title: "@kbn/apm-utils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/apm-utils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx
index d234f4cdae936..b56378a4171e5 100644
--- a/api_docs/kbn_axe_config.mdx
+++ b/api_docs/kbn_axe_config.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config
title: "@kbn/axe-config"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/axe-config plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_bazel_packages.mdx b/api_docs/kbn_bazel_packages.mdx
index 450809b74c16d..cac719f110c16 100644
--- a/api_docs/kbn_bazel_packages.mdx
+++ b/api_docs/kbn_bazel_packages.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-bazel-packages
title: "@kbn/bazel-packages"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/bazel-packages plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bazel-packages']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_bazel_runner.mdx b/api_docs/kbn_bazel_runner.mdx
index 93a83ad186c78..927f0ac618e35 100644
--- a/api_docs/kbn_bazel_runner.mdx
+++ b/api_docs/kbn_bazel_runner.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-bazel-runner
title: "@kbn/bazel-runner"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/bazel-runner plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bazel-runner']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx
index 6cd91b7bf01d4..10bc53b1cc005 100644
--- a/api_docs/kbn_ci_stats_core.mdx
+++ b/api_docs/kbn_ci_stats_core.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core
title: "@kbn/ci-stats-core"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/ci-stats-core plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx
index 0534bda96362a..0365cc151c83b 100644
--- a/api_docs/kbn_ci_stats_reporter.mdx
+++ b/api_docs/kbn_ci_stats_reporter.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter
title: "@kbn/ci-stats-reporter"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/ci-stats-reporter plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx
index 0061a09a4d4bd..033e55d467de0 100644
--- a/api_docs/kbn_cli_dev_mode.mdx
+++ b/api_docs/kbn_cli_dev_mode.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode
title: "@kbn/cli-dev-mode"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/cli-dev-mode plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx
index c4820898d29b3..2abaf70ea13cf 100644
--- a/api_docs/kbn_coloring.mdx
+++ b/api_docs/kbn_coloring.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-coloring
title: "@kbn/coloring"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/coloring plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx
index 4800f5377e8f8..ca58427161495 100644
--- a/api_docs/kbn_config.mdx
+++ b/api_docs/kbn_config.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-config
title: "@kbn/config"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/config plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx
index e8de463192c51..02fa7d325d0df 100644
--- a/api_docs/kbn_config_mocks.mdx
+++ b/api_docs/kbn_config_mocks.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks
title: "@kbn/config-mocks"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/config-mocks plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx
index 529fd3574cc57..614c046f9cd0c 100644
--- a/api_docs/kbn_config_schema.mdx
+++ b/api_docs/kbn_config_schema.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema
title: "@kbn/config-schema"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/config-schema plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_core_analytics_browser.devdocs.json b/api_docs/kbn_core_analytics_browser.devdocs.json
new file mode 100644
index 0000000000000..c64c779b2034d
--- /dev/null
+++ b/api_docs/kbn_core_analytics_browser.devdocs.json
@@ -0,0 +1,82 @@
+{
+ "id": "@kbn/core-analytics-browser",
+ "client": {
+ "classes": [],
+ "functions": [],
+ "interfaces": [],
+ "enums": [],
+ "misc": [],
+ "objects": []
+ },
+ "server": {
+ "classes": [],
+ "functions": [],
+ "interfaces": [],
+ "enums": [],
+ "misc": [],
+ "objects": []
+ },
+ "common": {
+ "classes": [],
+ "functions": [],
+ "interfaces": [],
+ "enums": [],
+ "misc": [
+ {
+ "parentPluginId": "@kbn/core-analytics-browser",
+ "id": "def-common.AnalyticsServiceSetup",
+ "type": "Type",
+ "tags": [],
+ "label": "AnalyticsServiceSetup",
+ "description": [
+ "\nExposes the public APIs of the AnalyticsClient during the setup phase.\n{@link AnalyticsClient}"
+ ],
+ "signature": [
+ "{ optIn: (optInConfig: ",
+ "OptInConfig",
+ ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ",
+ "Observable",
+ "<",
+ "TelemetryCounter",
+ ">; registerEventType: (eventTypeOps: ",
+ "EventTypeOpts",
+ ") => void; registerShipper: (Shipper: ",
+ "ShipperClassConstructor",
+ ", shipperConfig: ShipperConfig, opts?: ",
+ "RegisterShipperOpts",
+ " | undefined) => void; registerContextProvider: (contextProviderOpts: ",
+ "ContextProviderOpts",
+ ") => void; removeContextProvider: (contextProviderName: string) => void; }"
+ ],
+ "path": "packages/core/analytics/core-analytics-browser/src/types.ts",
+ "deprecated": false,
+ "initialIsOpen": false
+ },
+ {
+ "parentPluginId": "@kbn/core-analytics-browser",
+ "id": "def-common.AnalyticsServiceStart",
+ "type": "Type",
+ "tags": [],
+ "label": "AnalyticsServiceStart",
+ "description": [
+ "\nExposes the public APIs of the AnalyticsClient during the start phase\n{@link AnalyticsClient}"
+ ],
+ "signature": [
+ "{ optIn: (optInConfig: ",
+ "OptInConfig",
+ ") => void; reportEvent: >(eventType: string, eventData: EventTypeData) => void; readonly telemetryCounter$: ",
+ "Observable",
+ "<",
+ "TelemetryCounter",
+ ">; }"
+ ],
+ "path": "packages/core/analytics/core-analytics-browser/src/types.ts",
+ "deprecated": false,
+ "initialIsOpen": false
+ }
+ ],
+ "objects": []
+ }
+}
\ No newline at end of file
diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx
new file mode 100644
index 0000000000000..3a1518cee32fd
--- /dev/null
+++ b/api_docs/kbn_core_analytics_browser.mdx
@@ -0,0 +1,27 @@
+---
+id: kibKbnCoreAnalyticsBrowserPluginApi
+slug: /kibana-dev-docs/api/kbn-core-analytics-browser
+title: "@kbn/core-analytics-browser"
+image: https://source.unsplash.com/400x175/?github
+summary: API docs for the @kbn/core-analytics-browser plugin
+date: 2022-06-17
+tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser']
+warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
+---
+import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json';
+
+
+
+Contact [Owner missing] for questions regarding this plugin.
+
+**Code health stats**
+
+| Public API count | Any count | Items lacking comments | Missing exports |
+|-------------------|-----------|------------------------|-----------------|
+| 2 | 0 | 0 | 0 |
+
+## Common
+
+### Consts, variables and types
+
+
diff --git a/api_docs/kbn_core_analytics_browser_internal.devdocs.json b/api_docs/kbn_core_analytics_browser_internal.devdocs.json
new file mode 100644
index 0000000000000..6f83d74200b91
--- /dev/null
+++ b/api_docs/kbn_core_analytics_browser_internal.devdocs.json
@@ -0,0 +1,135 @@
+{
+ "id": "@kbn/core-analytics-browser-internal",
+ "client": {
+ "classes": [],
+ "functions": [],
+ "interfaces": [],
+ "enums": [],
+ "misc": [],
+ "objects": []
+ },
+ "server": {
+ "classes": [],
+ "functions": [],
+ "interfaces": [],
+ "enums": [],
+ "misc": [],
+ "objects": []
+ },
+ "common": {
+ "classes": [
+ {
+ "parentPluginId": "@kbn/core-analytics-browser-internal",
+ "id": "def-common.AnalyticsService",
+ "type": "Class",
+ "tags": [],
+ "label": "AnalyticsService",
+ "description": [],
+ "path": "packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts",
+ "deprecated": false,
+ "children": [
+ {
+ "parentPluginId": "@kbn/core-analytics-browser-internal",
+ "id": "def-common.AnalyticsService.Unnamed",
+ "type": "Function",
+ "tags": [],
+ "label": "Constructor",
+ "description": [],
+ "signature": [
+ "any"
+ ],
+ "path": "packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts",
+ "deprecated": false,
+ "children": [
+ {
+ "parentPluginId": "@kbn/core-analytics-browser-internal",
+ "id": "def-common.AnalyticsService.Unnamed.$1",
+ "type": "Object",
+ "tags": [],
+ "label": "core",
+ "description": [],
+ "signature": [
+ "CoreContext"
+ ],
+ "path": "packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts",
+ "deprecated": false,
+ "isRequired": true
+ }
+ ],
+ "returnComment": []
+ },
+ {
+ "parentPluginId": "@kbn/core-analytics-browser-internal",
+ "id": "def-common.AnalyticsService.setup",
+ "type": "Function",
+ "tags": [],
+ "label": "setup",
+ "description": [],
+ "signature": [
+ "({ injectedMetadata }: ",
+ "AnalyticsServiceSetupDeps",
+ ") => ",
+ "AnalyticsServiceSetup"
+ ],
+ "path": "packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts",
+ "deprecated": false,
+ "children": [
+ {
+ "parentPluginId": "@kbn/core-analytics-browser-internal",
+ "id": "def-common.AnalyticsService.setup.$1",
+ "type": "Object",
+ "tags": [],
+ "label": "{ injectedMetadata }",
+ "description": [],
+ "signature": [
+ "AnalyticsServiceSetupDeps"
+ ],
+ "path": "packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts",
+ "deprecated": false,
+ "isRequired": true
+ }
+ ],
+ "returnComment": []
+ },
+ {
+ "parentPluginId": "@kbn/core-analytics-browser-internal",
+ "id": "def-common.AnalyticsService.start",
+ "type": "Function",
+ "tags": [],
+ "label": "start",
+ "description": [],
+ "signature": [
+ "() => ",
+ "AnalyticsServiceStart"
+ ],
+ "path": "packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts",
+ "deprecated": false,
+ "children": [],
+ "returnComment": []
+ },
+ {
+ "parentPluginId": "@kbn/core-analytics-browser-internal",
+ "id": "def-common.AnalyticsService.stop",
+ "type": "Function",
+ "tags": [],
+ "label": "stop",
+ "description": [],
+ "signature": [
+ "() => void"
+ ],
+ "path": "packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts",
+ "deprecated": false,
+ "children": [],
+ "returnComment": []
+ }
+ ],
+ "initialIsOpen": false
+ }
+ ],
+ "functions": [],
+ "interfaces": [],
+ "enums": [],
+ "misc": [],
+ "objects": []
+ }
+}
\ No newline at end of file
diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx
new file mode 100644
index 0000000000000..3c0dc4316ccc8
--- /dev/null
+++ b/api_docs/kbn_core_analytics_browser_internal.mdx
@@ -0,0 +1,27 @@
+---
+id: kibKbnCoreAnalyticsBrowserInternalPluginApi
+slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal
+title: "@kbn/core-analytics-browser-internal"
+image: https://source.unsplash.com/400x175/?github
+summary: API docs for the @kbn/core-analytics-browser-internal plugin
+date: 2022-06-17
+tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal']
+warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
+---
+import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json';
+
+
+
+Contact [Owner missing] for questions regarding this plugin.
+
+**Code health stats**
+
+| Public API count | Any count | Items lacking comments | Missing exports |
+|-------------------|-----------|------------------------|-----------------|
+| 7 | 0 | 7 | 1 |
+
+## Common
+
+### Classes
+
+
diff --git a/api_docs/kbn_core_analytics_browser_mocks.devdocs.json b/api_docs/kbn_core_analytics_browser_mocks.devdocs.json
new file mode 100644
index 0000000000000..6a05b53e9e44d
--- /dev/null
+++ b/api_docs/kbn_core_analytics_browser_mocks.devdocs.json
@@ -0,0 +1,90 @@
+{
+ "id": "@kbn/core-analytics-browser-mocks",
+ "client": {
+ "classes": [],
+ "functions": [],
+ "interfaces": [],
+ "enums": [],
+ "misc": [],
+ "objects": []
+ },
+ "server": {
+ "classes": [],
+ "functions": [],
+ "interfaces": [],
+ "enums": [],
+ "misc": [],
+ "objects": []
+ },
+ "common": {
+ "classes": [],
+ "functions": [],
+ "interfaces": [],
+ "enums": [],
+ "misc": [],
+ "objects": [
+ {
+ "parentPluginId": "@kbn/core-analytics-browser-mocks",
+ "id": "def-common.analyticsServiceMock",
+ "type": "Object",
+ "tags": [],
+ "label": "analyticsServiceMock",
+ "description": [],
+ "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts",
+ "deprecated": false,
+ "children": [
+ {
+ "parentPluginId": "@kbn/core-analytics-browser-mocks",
+ "id": "def-common.analyticsServiceMock.create",
+ "type": "Function",
+ "tags": [],
+ "label": "create",
+ "description": [],
+ "signature": [
+ "() => jest.Mocked"
+ ],
+ "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts",
+ "deprecated": false,
+ "returnComment": [],
+ "children": []
+ },
+ {
+ "parentPluginId": "@kbn/core-analytics-browser-mocks",
+ "id": "def-common.analyticsServiceMock.createAnalyticsServiceSetup",
+ "type": "Function",
+ "tags": [],
+ "label": "createAnalyticsServiceSetup",
+ "description": [],
+ "signature": [
+ "() => jest.Mocked<",
+ "AnalyticsServiceSetup",
+ ">"
+ ],
+ "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts",
+ "deprecated": false,
+ "returnComment": [],
+ "children": []
+ },
+ {
+ "parentPluginId": "@kbn/core-analytics-browser-mocks",
+ "id": "def-common.analyticsServiceMock.createAnalyticsServiceStart",
+ "type": "Function",
+ "tags": [],
+ "label": "createAnalyticsServiceStart",
+ "description": [],
+ "signature": [
+ "() => jest.Mocked<",
+ "AnalyticsServiceStart",
+ ">"
+ ],
+ "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts",
+ "deprecated": false,
+ "returnComment": [],
+ "children": []
+ }
+ ],
+ "initialIsOpen": false
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx
new file mode 100644
index 0000000000000..8762fe02af599
--- /dev/null
+++ b/api_docs/kbn_core_analytics_browser_mocks.mdx
@@ -0,0 +1,27 @@
+---
+id: kibKbnCoreAnalyticsBrowserMocksPluginApi
+slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks
+title: "@kbn/core-analytics-browser-mocks"
+image: https://source.unsplash.com/400x175/?github
+summary: API docs for the @kbn/core-analytics-browser-mocks plugin
+date: 2022-06-17
+tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks']
+warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
+---
+import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json';
+
+
+
+Contact [Owner missing] for questions regarding this plugin.
+
+**Code health stats**
+
+| Public API count | Any count | Items lacking comments | Missing exports |
+|-------------------|-----------|------------------------|-----------------|
+| 4 | 0 | 4 | 0 |
+
+## Common
+
+### Objects
+
+
diff --git a/api_docs/kbn_core_base_browser_mocks.devdocs.json b/api_docs/kbn_core_base_browser_mocks.devdocs.json
new file mode 100644
index 0000000000000..66f7ca69015a3
--- /dev/null
+++ b/api_docs/kbn_core_base_browser_mocks.devdocs.json
@@ -0,0 +1,71 @@
+{
+ "id": "@kbn/core-base-browser-mocks",
+ "client": {
+ "classes": [],
+ "functions": [],
+ "interfaces": [],
+ "enums": [],
+ "misc": [],
+ "objects": []
+ },
+ "server": {
+ "classes": [],
+ "functions": [],
+ "interfaces": [],
+ "enums": [],
+ "misc": [],
+ "objects": []
+ },
+ "common": {
+ "classes": [],
+ "functions": [],
+ "interfaces": [],
+ "enums": [],
+ "misc": [],
+ "objects": [
+ {
+ "parentPluginId": "@kbn/core-base-browser-mocks",
+ "id": "def-common.coreContextMock",
+ "type": "Object",
+ "tags": [],
+ "label": "coreContextMock",
+ "description": [],
+ "path": "packages/core/base/core-base-browser-mocks/src/core_context.mock.ts",
+ "deprecated": false,
+ "children": [
+ {
+ "parentPluginId": "@kbn/core-base-browser-mocks",
+ "id": "def-common.coreContextMock.create",
+ "type": "Function",
+ "tags": [],
+ "label": "create",
+ "description": [],
+ "signature": [
+ "({ production }?: { production?: boolean | undefined; }) => ",
+ "CoreContext"
+ ],
+ "path": "packages/core/base/core-base-browser-mocks/src/core_context.mock.ts",
+ "deprecated": false,
+ "returnComment": [],
+ "children": [
+ {
+ "parentPluginId": "@kbn/core-base-browser-mocks",
+ "id": "def-common.coreContextMock.create.$1",
+ "type": "Object",
+ "tags": [],
+ "label": "__0",
+ "description": [],
+ "signature": [
+ "{ production?: boolean | undefined; }"
+ ],
+ "path": "packages/core/base/core-base-browser-mocks/src/core_context.mock.ts",
+ "deprecated": false
+ }
+ ]
+ }
+ ],
+ "initialIsOpen": false
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx
new file mode 100644
index 0000000000000..70f76ae35ff88
--- /dev/null
+++ b/api_docs/kbn_core_base_browser_mocks.mdx
@@ -0,0 +1,27 @@
+---
+id: kibKbnCoreBaseBrowserMocksPluginApi
+slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks
+title: "@kbn/core-base-browser-mocks"
+image: https://source.unsplash.com/400x175/?github
+summary: API docs for the @kbn/core-base-browser-mocks plugin
+date: 2022-06-17
+tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks']
+warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
+---
+import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json';
+
+
+
+Contact [Owner missing] for questions regarding this plugin.
+
+**Code health stats**
+
+| Public API count | Any count | Items lacking comments | Missing exports |
+|-------------------|-----------|------------------------|-----------------|
+| 3 | 0 | 3 | 0 |
+
+## Common
+
+### Objects
+
+
diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx
index 72c2c0980276c..3e9ff4d92a0f8 100644
--- a/api_docs/kbn_core_base_common.mdx
+++ b/api_docs/kbn_core_base_common.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common
title: "@kbn/core-base-common"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/core-base-common plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx
index 787d170d2999f..5369479e0364a 100644
--- a/api_docs/kbn_core_base_server_mocks.mdx
+++ b/api_docs/kbn_core_base_server_mocks.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks
title: "@kbn/core-base-server-mocks"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/core-base-server-mocks plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx
index 1e330436a0ade..67263f141252d 100644
--- a/api_docs/kbn_core_doc_links_browser.mdx
+++ b/api_docs/kbn_core_doc_links_browser.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser
title: "@kbn/core-doc-links-browser"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/core-doc-links-browser plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx
index ed8e1d1140e2f..c67287e5f516e 100644
--- a/api_docs/kbn_core_doc_links_browser_mocks.mdx
+++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks
title: "@kbn/core-doc-links-browser-mocks"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/core-doc-links-browser-mocks plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx
index 36ebea2c7f0f3..30ad2c1234f08 100644
--- a/api_docs/kbn_core_doc_links_server.mdx
+++ b/api_docs/kbn_core_doc_links_server.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server
title: "@kbn/core-doc-links-server"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/core-doc-links-server plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx
index 281328a03d6e7..df8236f2c41a6 100644
--- a/api_docs/kbn_core_doc_links_server_mocks.mdx
+++ b/api_docs/kbn_core_doc_links_server_mocks.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks
title: "@kbn/core-doc-links-server-mocks"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/core-doc-links-server-mocks plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_core_injected_metadata_browser.mdx b/api_docs/kbn_core_injected_metadata_browser.mdx
index 06277e6747b8b..814d708aad31c 100644
--- a/api_docs/kbn_core_injected_metadata_browser.mdx
+++ b/api_docs/kbn_core_injected_metadata_browser.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser
title: "@kbn/core-injected-metadata-browser"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/core-injected-metadata-browser plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx
index 4d3a2fd970b4e..e12dd474f00ba 100644
--- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx
+++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks
title: "@kbn/core-injected-metadata-browser-mocks"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/core-injected-metadata-browser-mocks plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx
index ccedd8bbb046a..72f84be0166df 100644
--- a/api_docs/kbn_core_theme_browser.mdx
+++ b/api_docs/kbn_core_theme_browser.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser
title: "@kbn/core-theme-browser"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/core-theme-browser plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx
index c5876c0a67dc5..8ebe8ba0f5e74 100644
--- a/api_docs/kbn_core_theme_browser_mocks.mdx
+++ b/api_docs/kbn_core_theme_browser_mocks.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks
title: "@kbn/core-theme-browser-mocks"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/core-theme-browser-mocks plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx
index 6834c06269c11..6cd9730f95152 100644
--- a/api_docs/kbn_crypto.mdx
+++ b/api_docs/kbn_crypto.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-crypto
title: "@kbn/crypto"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/crypto plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx
index 6df1aa7371d1d..c5fab9bbed628 100644
--- a/api_docs/kbn_datemath.mdx
+++ b/api_docs/kbn_datemath.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-datemath
title: "@kbn/datemath"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/datemath plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx
index d4d1d0fc73720..7963efbbd363e 100644
--- a/api_docs/kbn_dev_cli_errors.mdx
+++ b/api_docs/kbn_dev_cli_errors.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors
title: "@kbn/dev-cli-errors"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/dev-cli-errors plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx
index a75484107ace4..eb042da0255f3 100644
--- a/api_docs/kbn_dev_cli_runner.mdx
+++ b/api_docs/kbn_dev_cli_runner.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner
title: "@kbn/dev-cli-runner"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/dev-cli-runner plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx
index 4251ec667fab0..682702495035a 100644
--- a/api_docs/kbn_dev_proc_runner.mdx
+++ b/api_docs/kbn_dev_proc_runner.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner
title: "@kbn/dev-proc-runner"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/dev-proc-runner plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx
index 0bbf9439d5873..9416965929c10 100644
--- a/api_docs/kbn_dev_utils.mdx
+++ b/api_docs/kbn_dev_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils
title: "@kbn/dev-utils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/dev-utils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json
index 848d48fff3212..6c5cd99cc6bce 100644
--- a/api_docs/kbn_doc_links.devdocs.json
+++ b/api_docs/kbn_doc_links.devdocs.json
@@ -510,7 +510,7 @@
"label": "securitySolution",
"description": [],
"signature": [
- "{ readonly trustedApps: string; readonly eventFilters: string; readonly blocklist: string; }"
+ "{ readonly trustedApps: string; readonly eventFilters: string; readonly blocklist: string; readonly policyResponseTroubleshooting: { full_disk_access: string; }; }"
],
"path": "packages/kbn-doc-links/src/types.ts",
"deprecated": false
diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx
index 3de32f29e3948..f67aaa784e29b 100644
--- a/api_docs/kbn_doc_links.mdx
+++ b/api_docs/kbn_doc_links.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links
title: "@kbn/doc-links"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/doc-links plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx
index 99b674a89b489..909aaa80fa0d2 100644
--- a/api_docs/kbn_docs_utils.mdx
+++ b/api_docs/kbn_docs_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils
title: "@kbn/docs-utils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/docs-utils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx
index 7dbb52b283616..979f943df99f4 100644
--- a/api_docs/kbn_es_archiver.mdx
+++ b/api_docs/kbn_es_archiver.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver
title: "@kbn/es-archiver"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/es-archiver plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx
index bc84717443339..2f0ff0182ea7c 100644
--- a/api_docs/kbn_es_query.mdx
+++ b/api_docs/kbn_es_query.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-es-query
title: "@kbn/es-query"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/es-query plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx
index 60a0f79abb762..4e63429f6cb57 100644
--- a/api_docs/kbn_eslint_plugin_imports.mdx
+++ b/api_docs/kbn_eslint_plugin_imports.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports
title: "@kbn/eslint-plugin-imports"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/eslint-plugin-imports plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx
index 1005c2bea691f..a5c39cd1867fa 100644
--- a/api_docs/kbn_field_types.mdx
+++ b/api_docs/kbn_field_types.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-field-types
title: "@kbn/field-types"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/field-types plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx
index 59d18084baeee..3326c532d0b6d 100644
--- a/api_docs/kbn_find_used_node_modules.mdx
+++ b/api_docs/kbn_find_used_node_modules.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules
title: "@kbn/find-used-node-modules"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/find-used-node-modules plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx
index d5bf4dd29bbfc..a582ce3ff84de 100644
--- a/api_docs/kbn_generate.mdx
+++ b/api_docs/kbn_generate.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-generate
title: "@kbn/generate"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/generate plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx
index 80e605d285752..870e2c0bf6b71 100644
--- a/api_docs/kbn_handlebars.mdx
+++ b/api_docs/kbn_handlebars.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars
title: "@kbn/handlebars"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/handlebars plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx
index 5e386b9a573a8..72895442ccdb3 100644
--- a/api_docs/kbn_i18n.mdx
+++ b/api_docs/kbn_i18n.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-i18n
title: "@kbn/i18n"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/i18n plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx
index 0dbdac2fb6e5f..c2dbc6c3cde98 100644
--- a/api_docs/kbn_import_resolver.mdx
+++ b/api_docs/kbn_import_resolver.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver
title: "@kbn/import-resolver"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/import-resolver plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx
index 6d9bda35f0ce8..6eb59b9a99fb3 100644
--- a/api_docs/kbn_interpreter.mdx
+++ b/api_docs/kbn_interpreter.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter
title: "@kbn/interpreter"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/interpreter plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx
index 3408b86d289ed..e13f2e083108c 100644
--- a/api_docs/kbn_io_ts_utils.mdx
+++ b/api_docs/kbn_io_ts_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils
title: "@kbn/io-ts-utils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/io-ts-utils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx
index 4eadab6a8a434..c422fa1fdb365 100644
--- a/api_docs/kbn_jest_serializers.mdx
+++ b/api_docs/kbn_jest_serializers.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers
title: "@kbn/jest-serializers"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/jest-serializers plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_kibana_json_schema.mdx b/api_docs/kbn_kibana_json_schema.mdx
index a028ea2818a63..87b0421bb2d95 100644
--- a/api_docs/kbn_kibana_json_schema.mdx
+++ b/api_docs/kbn_kibana_json_schema.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-json-schema
title: "@kbn/kibana-json-schema"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/kibana-json-schema plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-json-schema']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_logging.devdocs.json b/api_docs/kbn_logging.devdocs.json
index eb835715a1c77..29d30a1c83b2d 100644
--- a/api_docs/kbn_logging.devdocs.json
+++ b/api_docs/kbn_logging.devdocs.json
@@ -630,7 +630,7 @@
"label": "EcsEventKind",
"description": [],
"signature": [
- "\"alert\" | \"state\" | \"metric\" | \"event\" | \"signal\" | \"pipeline_error\""
+ "\"metric\" | \"alert\" | \"state\" | \"event\" | \"signal\" | \"pipeline_error\""
],
"path": "packages/kbn-logging/src/ecs/event.ts",
"deprecated": false,
diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx
index 4ab1195c94323..ba71b449bb39b 100644
--- a/api_docs/kbn_logging.mdx
+++ b/api_docs/kbn_logging.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-logging
title: "@kbn/logging"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/logging plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx
index f0a5d54da56e8..157a04b180d39 100644
--- a/api_docs/kbn_logging_mocks.mdx
+++ b/api_docs/kbn_logging_mocks.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks
title: "@kbn/logging-mocks"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/logging-mocks plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx
index 1e2273b6fba5d..8d7307cabb758 100644
--- a/api_docs/kbn_mapbox_gl.mdx
+++ b/api_docs/kbn_mapbox_gl.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl
title: "@kbn/mapbox-gl"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/mapbox-gl plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx
index 48cc0d76f4079..b1529587ed608 100644
--- a/api_docs/kbn_monaco.mdx
+++ b/api_docs/kbn_monaco.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-monaco
title: "@kbn/monaco"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/monaco plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx
index a6f3fcd10d02f..71e04274c6e5a 100644
--- a/api_docs/kbn_optimizer.mdx
+++ b/api_docs/kbn_optimizer.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer
title: "@kbn/optimizer"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/optimizer plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx
index e3be04849895b..709cd3289f655 100644
--- a/api_docs/kbn_optimizer_webpack_helpers.mdx
+++ b/api_docs/kbn_optimizer_webpack_helpers.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers
title: "@kbn/optimizer-webpack-helpers"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/optimizer-webpack-helpers plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx
index 32e132773b18d..1493a49ad177b 100644
--- a/api_docs/kbn_performance_testing_dataset_extractor.mdx
+++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor
title: "@kbn/performance-testing-dataset-extractor"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/performance-testing-dataset-extractor plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_plugin_discovery.mdx b/api_docs/kbn_plugin_discovery.mdx
index ba61f1d92322e..ba0b6561f4ecf 100644
--- a/api_docs/kbn_plugin_discovery.mdx
+++ b/api_docs/kbn_plugin_discovery.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-discovery
title: "@kbn/plugin-discovery"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/plugin-discovery plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-discovery']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx
index 830e24508a95e..d27a33e3cbf00 100644
--- a/api_docs/kbn_plugin_generator.mdx
+++ b/api_docs/kbn_plugin_generator.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator
title: "@kbn/plugin-generator"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/plugin-generator plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx
index 221dcc20a5baf..e482e27a9cca7 100644
--- a/api_docs/kbn_plugin_helpers.mdx
+++ b/api_docs/kbn_plugin_helpers.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers
title: "@kbn/plugin-helpers"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/plugin-helpers plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_pm.mdx b/api_docs/kbn_pm.mdx
index 68ecdaa011c7f..9a0e2f6ca782b 100644
--- a/api_docs/kbn_pm.mdx
+++ b/api_docs/kbn_pm.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-pm
title: "@kbn/pm"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/pm plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/pm']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx
index 66a1c9888ea1c..3c6f40ac4dc24 100644
--- a/api_docs/kbn_react_field.mdx
+++ b/api_docs/kbn_react_field.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-react-field
title: "@kbn/react-field"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/react-field plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx
index 9459554d95b74..49518a7bd805f 100644
--- a/api_docs/kbn_rule_data_utils.mdx
+++ b/api_docs/kbn_rule_data_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils
title: "@kbn/rule-data-utils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/rule-data-utils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_scalability_simulation_generator.mdx b/api_docs/kbn_scalability_simulation_generator.mdx
index 73b7c83f52ae1..e68ef70dfefc6 100644
--- a/api_docs/kbn_scalability_simulation_generator.mdx
+++ b/api_docs/kbn_scalability_simulation_generator.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-scalability-simulation-generator
title: "@kbn/scalability-simulation-generator"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/scalability-simulation-generator plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/scalability-simulation-generator']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx
index cf71b5468640a..b877e49a3c029 100644
--- a/api_docs/kbn_securitysolution_autocomplete.mdx
+++ b/api_docs/kbn_securitysolution_autocomplete.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete
title: "@kbn/securitysolution-autocomplete"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-autocomplete plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx
index 191c8c4b74769..392944bcb48ce 100644
--- a/api_docs/kbn_securitysolution_es_utils.mdx
+++ b/api_docs/kbn_securitysolution_es_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils
title: "@kbn/securitysolution-es-utils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-es-utils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx
index 416c5bb57d30c..3a6219c2658c6 100644
--- a/api_docs/kbn_securitysolution_hook_utils.mdx
+++ b/api_docs/kbn_securitysolution_hook_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils
title: "@kbn/securitysolution-hook-utils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-hook-utils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx
index 1635127fd5d2d..03ae9fd00c16f 100644
--- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx
+++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types
title: "@kbn/securitysolution-io-ts-alerting-types"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx
index 3ef212040e182..3ff41dbe18148 100644
--- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx
+++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types
title: "@kbn/securitysolution-io-ts-list-types"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-io-ts-list-types plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx
index 623afbeed9b83..393a487d4bc4b 100644
--- a/api_docs/kbn_securitysolution_io_ts_types.mdx
+++ b/api_docs/kbn_securitysolution_io_ts_types.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types
title: "@kbn/securitysolution-io-ts-types"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-io-ts-types plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx
index 06fd7e9469a3d..51aeddad968fd 100644
--- a/api_docs/kbn_securitysolution_io_ts_utils.mdx
+++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils
title: "@kbn/securitysolution-io-ts-utils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-io-ts-utils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx
index 48dac19441912..acbf663d3273a 100644
--- a/api_docs/kbn_securitysolution_list_api.mdx
+++ b/api_docs/kbn_securitysolution_list_api.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api
title: "@kbn/securitysolution-list-api"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-list-api plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx
index df75588d4d575..dc3cd07644998 100644
--- a/api_docs/kbn_securitysolution_list_constants.mdx
+++ b/api_docs/kbn_securitysolution_list_constants.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants
title: "@kbn/securitysolution-list-constants"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-list-constants plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx
index 6d655aac0aef3..ebe5deb1f5250 100644
--- a/api_docs/kbn_securitysolution_list_hooks.mdx
+++ b/api_docs/kbn_securitysolution_list_hooks.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks
title: "@kbn/securitysolution-list-hooks"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-list-hooks plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx
index 67e0ec1be06cb..0e97eeb1d4a38 100644
--- a/api_docs/kbn_securitysolution_list_utils.mdx
+++ b/api_docs/kbn_securitysolution_list_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils
title: "@kbn/securitysolution-list-utils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-list-utils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx
index f422a119097a6..f97548e09ee49 100644
--- a/api_docs/kbn_securitysolution_rules.mdx
+++ b/api_docs/kbn_securitysolution_rules.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules
title: "@kbn/securitysolution-rules"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-rules plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx
index 7058b40f4c5ac..e0da9e631a9aa 100644
--- a/api_docs/kbn_securitysolution_t_grid.mdx
+++ b/api_docs/kbn_securitysolution_t_grid.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid
title: "@kbn/securitysolution-t-grid"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-t-grid plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx
index 9fecb35c397d4..f2c49a4344bd6 100644
--- a/api_docs/kbn_securitysolution_utils.mdx
+++ b/api_docs/kbn_securitysolution_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils
title: "@kbn/securitysolution-utils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/securitysolution-utils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx
index 7695de91fa574..6919db327def9 100644
--- a/api_docs/kbn_server_http_tools.mdx
+++ b/api_docs/kbn_server_http_tools.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools
title: "@kbn/server-http-tools"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/server-http-tools plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx
index 17374e7faccaf..3935e723c61b6 100644
--- a/api_docs/kbn_server_route_repository.mdx
+++ b/api_docs/kbn_server_route_repository.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository
title: "@kbn/server-route-repository"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/server-route-repository plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_shared_ux_components.mdx b/api_docs/kbn_shared_ux_components.mdx
index 7557aa98eb1a1..2c258ed860458 100644
--- a/api_docs/kbn_shared_ux_components.mdx
+++ b/api_docs/kbn_shared_ux_components.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-components
title: "@kbn/shared-ux-components"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/shared-ux-components plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-components']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx
index 9e858743aca43..2e4fdb79ca33d 100644
--- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx
+++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data
title: "@kbn/shared-ux-page-analytics-no-data"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/shared-ux-page-analytics-no-data plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx
index 2c89e6a111f62..5ffd4b52d5b51 100644
--- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx
+++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data
title: "@kbn/shared-ux-page-kibana-no-data"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/shared-ux-page-kibana-no-data plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx
index 24797800e3ad6..842ac36e7e27a 100644
--- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx
+++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views
title: "@kbn/shared-ux-prompt-no-data-views"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/shared-ux-prompt-no-data-views plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_shared_ux_services.mdx b/api_docs/kbn_shared_ux_services.mdx
index 639f9ede5025d..14c40c014d15c 100644
--- a/api_docs/kbn_shared_ux_services.mdx
+++ b/api_docs/kbn_shared_ux_services.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-services
title: "@kbn/shared-ux-services"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/shared-ux-services plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-services']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_shared_ux_storybook.mdx b/api_docs/kbn_shared_ux_storybook.mdx
index c8cab050fb5f0..e1ff71868f097 100644
--- a/api_docs/kbn_shared_ux_storybook.mdx
+++ b/api_docs/kbn_shared_ux_storybook.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook
title: "@kbn/shared-ux-storybook"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/shared-ux-storybook plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx
index bb54681d731e4..8c1e375041264 100644
--- a/api_docs/kbn_shared_ux_utility.mdx
+++ b/api_docs/kbn_shared_ux_utility.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility
title: "@kbn/shared-ux-utility"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/shared-ux-utility plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_sort_package_json.mdx b/api_docs/kbn_sort_package_json.mdx
index 9f8a72cb41b84..eb1f54528e50f 100644
--- a/api_docs/kbn_sort_package_json.mdx
+++ b/api_docs/kbn_sort_package_json.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-sort-package-json
title: "@kbn/sort-package-json"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/sort-package-json plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx
index c37401c0aa6fb..d7d45419dd412 100644
--- a/api_docs/kbn_std.mdx
+++ b/api_docs/kbn_std.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-std
title: "@kbn/std"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/std plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx
index 0e72be0e49c15..9e767b31b5b6b 100644
--- a/api_docs/kbn_stdio_dev_helpers.mdx
+++ b/api_docs/kbn_stdio_dev_helpers.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers
title: "@kbn/stdio-dev-helpers"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/stdio-dev-helpers plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx
index b72301ee0e856..d7602a2006000 100644
--- a/api_docs/kbn_storybook.mdx
+++ b/api_docs/kbn_storybook.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-storybook
title: "@kbn/storybook"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/storybook plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx
index 100149cff3ef2..64f73d996436d 100644
--- a/api_docs/kbn_telemetry_tools.mdx
+++ b/api_docs/kbn_telemetry_tools.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools
title: "@kbn/telemetry-tools"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/telemetry-tools plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx
index ab97811f0ce3a..cf7531f5a03bf 100644
--- a/api_docs/kbn_test.mdx
+++ b/api_docs/kbn_test.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-test
title: "@kbn/test"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/test plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx
index 3d9854af58aaa..5cbedbbd4f208 100644
--- a/api_docs/kbn_test_jest_helpers.mdx
+++ b/api_docs/kbn_test_jest_helpers.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers
title: "@kbn/test-jest-helpers"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/test-jest-helpers plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx
index 9f4c5e6ccccba..c1af03730b727 100644
--- a/api_docs/kbn_tooling_log.mdx
+++ b/api_docs/kbn_tooling_log.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log
title: "@kbn/tooling-log"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/tooling-log plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx
index a49f048a50d29..606d5d1faff4c 100644
--- a/api_docs/kbn_type_summarizer.mdx
+++ b/api_docs/kbn_type_summarizer.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer
title: "@kbn/type-summarizer"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/type-summarizer plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx
index 98b3f6deb66f6..e84cb275d20a6 100644
--- a/api_docs/kbn_typed_react_router_config.mdx
+++ b/api_docs/kbn_typed_react_router_config.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config
title: "@kbn/typed-react-router-config"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/typed-react-router-config plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx
index 87738602829e4..442ef904b0b0a 100644
--- a/api_docs/kbn_ui_theme.mdx
+++ b/api_docs/kbn_ui_theme.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme
title: "@kbn/ui-theme"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/ui-theme plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx
index df4a858e5df42..1b9b85b9d594f 100644
--- a/api_docs/kbn_utility_types.mdx
+++ b/api_docs/kbn_utility_types.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types
title: "@kbn/utility-types"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/utility-types plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx
index cbd1b729a195d..b8bbdc07bad15 100644
--- a/api_docs/kbn_utility_types_jest.mdx
+++ b/api_docs/kbn_utility_types_jest.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest
title: "@kbn/utility-types-jest"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/utility-types-jest plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx
index d726ead3e62eb..5c5ff15678791 100644
--- a/api_docs/kbn_utils.mdx
+++ b/api_docs/kbn_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-utils
title: "@kbn/utils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the @kbn/utils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx
index ea49fd4401444..edd89efc527cb 100644
--- a/api_docs/kibana_overview.mdx
+++ b/api_docs/kibana_overview.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kibanaOverview
title: "kibanaOverview"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the kibanaOverview plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kibana_react.devdocs.json b/api_docs/kibana_react.devdocs.json
index cf9cfeb6d5569..10710db8a00bf 100644
--- a/api_docs/kibana_react.devdocs.json
+++ b/api_docs/kibana_react.devdocs.json
@@ -4383,13 +4383,7 @@
"description": [],
"signature": [
"{ analytics?: ",
- {
- "pluginId": "core",
- "scope": "public",
- "docId": "kibCorePluginApi",
- "section": "def-public.AnalyticsServiceStart",
- "text": "AnalyticsServiceStart"
- },
+ "AnalyticsServiceStart",
" | undefined; application?: ",
{
"pluginId": "core",
diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx
index b4d3fcaea8d48..127311d55587f 100644
--- a/api_docs/kibana_react.mdx
+++ b/api_docs/kibana_react.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kibanaReact
title: "kibanaReact"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the kibanaReact plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx
index 81408535d4cd1..54fbfec6cdf69 100644
--- a/api_docs/kibana_utils.mdx
+++ b/api_docs/kibana_utils.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kibanaUtils
title: "kibanaUtils"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the kibanaUtils plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx
index 058c1cbe5700b..3252797528030 100644
--- a/api_docs/kubernetes_security.mdx
+++ b/api_docs/kubernetes_security.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity
title: "kubernetesSecurity"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the kubernetesSecurity plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json
index 7045342fdff0d..b0c77e740df69 100644
--- a/api_docs/lens.devdocs.json
+++ b/api_docs/lens.devdocs.json
@@ -6857,7 +6857,7 @@
"section": "def-common.GaugeState",
"text": "GaugeState"
},
- ", \"goal\" | \"min\" | \"max\" | \"metric\"> & { metricAccessor?: string | undefined; minAccessor?: string | undefined; maxAccessor?: string | undefined; goalAccessor?: string | undefined; } & { layerId: string; layerType: ",
+ ", \"goal\" | \"metric\" | \"min\" | \"max\"> & { metricAccessor?: string | undefined; minAccessor?: string | undefined; maxAccessor?: string | undefined; goalAccessor?: string | undefined; } & { layerId: string; layerType: ",
{
"pluginId": "lens",
"scope": "common",
diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx
index fdbee38c92fc4..ba929e3728a69 100644
--- a/api_docs/lens.mdx
+++ b/api_docs/lens.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/lens
title: "lens"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the lens plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx
index dd8bf621ec14c..b715981ec5e13 100644
--- a/api_docs/license_api_guard.mdx
+++ b/api_docs/license_api_guard.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard
title: "licenseApiGuard"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the licenseApiGuard plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx
index c25f78dce1a58..6d0d51071820f 100644
--- a/api_docs/license_management.mdx
+++ b/api_docs/license_management.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/licenseManagement
title: "licenseManagement"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the licenseManagement plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx
index aa648f86afce0..b203a0c81433a 100644
--- a/api_docs/licensing.mdx
+++ b/api_docs/licensing.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/licensing
title: "licensing"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the licensing plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx
index 4c63d3e08a297..0beeaa4a83069 100644
--- a/api_docs/lists.mdx
+++ b/api_docs/lists.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/lists
title: "lists"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the lists plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/management.mdx b/api_docs/management.mdx
index 4c673466335ba..dee3f0cada74f 100644
--- a/api_docs/management.mdx
+++ b/api_docs/management.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/management
title: "management"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the management plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx
index 75449a04fff5b..acd77d3cf9a5a 100644
--- a/api_docs/maps.mdx
+++ b/api_docs/maps.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/maps
title: "maps"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the maps plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx
index 7ef0840638c70..fb0bc148d18c0 100644
--- a/api_docs/maps_ems.mdx
+++ b/api_docs/maps_ems.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/mapsEms
title: "mapsEms"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the mapsEms plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx
index bb0d485e98714..c713a39180419 100644
--- a/api_docs/ml.mdx
+++ b/api_docs/ml.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/ml
title: "ml"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the ml plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx
index 42d653f944912..4e1ce81202ba3 100644
--- a/api_docs/monitoring.mdx
+++ b/api_docs/monitoring.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/monitoring
title: "monitoring"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the monitoring plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx
index b752aff4d64a5..e5f02ba72d852 100644
--- a/api_docs/monitoring_collection.mdx
+++ b/api_docs/monitoring_collection.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/monitoringCollection
title: "monitoringCollection"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the monitoringCollection plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx
index d714e58941a58..27f94cc517ad2 100644
--- a/api_docs/navigation.mdx
+++ b/api_docs/navigation.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/navigation
title: "navigation"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the navigation plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx
index 9d5bc311a2517..68cd556717b01 100644
--- a/api_docs/newsfeed.mdx
+++ b/api_docs/newsfeed.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/newsfeed
title: "newsfeed"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the newsfeed plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx
index c05e264dfe97d..a0b77deedca0d 100644
--- a/api_docs/observability.mdx
+++ b/api_docs/observability.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/observability
title: "observability"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the observability plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx
index aeb1d77a08452..1b8fa17dc7afe 100644
--- a/api_docs/osquery.mdx
+++ b/api_docs/osquery.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/osquery
title: "osquery"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the osquery plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx
index bbf0d8f40b09b..ed3aec86b355a 100644
--- a/api_docs/plugin_directory.mdx
+++ b/api_docs/plugin_directory.mdx
@@ -3,7 +3,7 @@ id: kibDevDocsPluginDirectory
slug: /kibana-dev-docs/api-meta/plugin-api-directory
title: Directory
summary: Directory of public APIs available through plugins or packages.
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
@@ -12,19 +12,19 @@ warning: This document is auto-generated and is meant to be viewed inside our ex
| Count | Plugins or Packages with a
public API | Number of teams |
|--------------|----------|------------------------|
-| 283 | 226 | 35 |
+| 287 | 230 | 35 |
### Public API health stats
| API Count | Any Count | Missing comments | Missing exports |
|--------------|----------|-----------------|--------|
-| 26521 | 172 | 19084 | 1238 |
+| 26538 | 172 | 19099 | 1241 |
## Plugin Directory
| Plugin name | Maintaining team | Description | API Cnt | Any Cnt | Missing
comments | Missing
exports |
|--------------|----------------|-----------|--------------|----------|---------------|--------|
-| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 240 | 0 | 235 | 19 |
+| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 241 | 0 | 236 | 19 |
| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 23 | 0 | 19 | 1 |
| | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 11 | 0 | 0 | 0 |
| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 359 | 0 | 350 | 20 |
@@ -192,6 +192,10 @@ warning: This document is auto-generated and is meant to be viewed inside our ex
| | [Owner missing] | - | 73 | 0 | 44 | 1 |
| | [Owner missing] | - | 16 | 0 | 16 | 0 |
| | [Owner missing] | - | 129 | 3 | 127 | 17 |
+| | [Owner missing] | - | 2 | 0 | 0 | 0 |
+| | [Owner missing] | - | 7 | 0 | 7 | 1 |
+| | [Owner missing] | - | 4 | 0 | 4 | 0 |
+| | [Owner missing] | - | 3 | 0 | 3 | 0 |
| | [Owner missing] | - | 12 | 0 | 3 | 0 |
| | [Owner missing] | - | 3 | 0 | 3 | 0 |
| | [Owner missing] | - | 4 | 0 | 4 | 0 |
diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx
index 20930abddf141..7e3c34036a499 100644
--- a/api_docs/presentation_util.mdx
+++ b/api_docs/presentation_util.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/presentationUtil
title: "presentationUtil"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the presentationUtil plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx
index 7478e66b31c92..ae839caa65fb0 100644
--- a/api_docs/remote_clusters.mdx
+++ b/api_docs/remote_clusters.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/remoteClusters
title: "remoteClusters"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the remoteClusters plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx
index eff6da111ac5b..f1efebbc45e01 100644
--- a/api_docs/reporting.mdx
+++ b/api_docs/reporting.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/reporting
title: "reporting"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the reporting plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx
index b0f56a234ac5b..260262c77889b 100644
--- a/api_docs/rollup.mdx
+++ b/api_docs/rollup.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/rollup
title: "rollup"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the rollup plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx
index a0a0a693a26b9..364b4731b9f05 100644
--- a/api_docs/rule_registry.mdx
+++ b/api_docs/rule_registry.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/ruleRegistry
title: "ruleRegistry"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the ruleRegistry plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx
index 2087f6f1ec55f..8516236e1dcd9 100644
--- a/api_docs/runtime_fields.mdx
+++ b/api_docs/runtime_fields.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/runtimeFields
title: "runtimeFields"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the runtimeFields plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx
index b673115fe860b..2cb7bbadc983b 100644
--- a/api_docs/saved_objects.mdx
+++ b/api_docs/saved_objects.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjects
title: "savedObjects"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the savedObjects plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx
index bbd52111d216c..fa5b93807b40d 100644
--- a/api_docs/saved_objects_management.mdx
+++ b/api_docs/saved_objects_management.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement
title: "savedObjectsManagement"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the savedObjectsManagement plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx
index 548f76a8b6fe2..3236c24b72bad 100644
--- a/api_docs/saved_objects_tagging.mdx
+++ b/api_docs/saved_objects_tagging.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging
title: "savedObjectsTagging"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the savedObjectsTagging plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx
index 0c40d26d95d7f..904146a129e50 100644
--- a/api_docs/saved_objects_tagging_oss.mdx
+++ b/api_docs/saved_objects_tagging_oss.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss
title: "savedObjectsTaggingOss"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the savedObjectsTaggingOss plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx
index 8b7bc2df3a995..b966e8a251f92 100644
--- a/api_docs/screenshot_mode.mdx
+++ b/api_docs/screenshot_mode.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/screenshotMode
title: "screenshotMode"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the screenshotMode plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx
index f9390f7570258..06c71f49159b4 100644
--- a/api_docs/screenshotting.mdx
+++ b/api_docs/screenshotting.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/screenshotting
title: "screenshotting"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the screenshotting plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/security.mdx b/api_docs/security.mdx
index 16d9f6a9ff667..4518615f3bed7 100644
--- a/api_docs/security.mdx
+++ b/api_docs/security.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/security
title: "security"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the security plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx
index 930ff2ae95240..c3c7036439d7e 100644
--- a/api_docs/security_solution.mdx
+++ b/api_docs/security_solution.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/securitySolution
title: "securitySolution"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the securitySolution plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx
index 3ced7ddd2ac58..50aa98aa0108a 100644
--- a/api_docs/session_view.mdx
+++ b/api_docs/session_view.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/sessionView
title: "sessionView"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the sessionView plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/share.mdx b/api_docs/share.mdx
index 0a71613e949f2..e1a471d336d98 100644
--- a/api_docs/share.mdx
+++ b/api_docs/share.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/share
title: "share"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the share plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/shared_u_x.mdx b/api_docs/shared_u_x.mdx
index b72b705e16565..2ac1139d44805 100644
--- a/api_docs/shared_u_x.mdx
+++ b/api_docs/shared_u_x.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/sharedUX
title: "sharedUX"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the sharedUX plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sharedUX']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx
index c536b0d7c1519..16c511aa7406c 100644
--- a/api_docs/snapshot_restore.mdx
+++ b/api_docs/snapshot_restore.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/snapshotRestore
title: "snapshotRestore"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the snapshotRestore plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx
index 896ff51177780..7f3821890bbce 100644
--- a/api_docs/spaces.mdx
+++ b/api_docs/spaces.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/spaces
title: "spaces"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the spaces plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx
index 1c483f7415e98..42bc2368b94f9 100644
--- a/api_docs/stack_alerts.mdx
+++ b/api_docs/stack_alerts.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/stackAlerts
title: "stackAlerts"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the stackAlerts plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx
index b5aefb462d8f0..8c7110479b2a8 100644
--- a/api_docs/task_manager.mdx
+++ b/api_docs/task_manager.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/taskManager
title: "taskManager"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the taskManager plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx
index 62375b5cfdbd0..8ad7dc5b483e4 100644
--- a/api_docs/telemetry.mdx
+++ b/api_docs/telemetry.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/telemetry
title: "telemetry"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the telemetry plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx
index d90ce413ad60b..264d86f33babc 100644
--- a/api_docs/telemetry_collection_manager.mdx
+++ b/api_docs/telemetry_collection_manager.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager
title: "telemetryCollectionManager"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the telemetryCollectionManager plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx
index b2c67c19bff4a..395d9c9170b04 100644
--- a/api_docs/telemetry_collection_xpack.mdx
+++ b/api_docs/telemetry_collection_xpack.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack
title: "telemetryCollectionXpack"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the telemetryCollectionXpack plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx
index 5236f864bbec8..2bdac69098e60 100644
--- a/api_docs/telemetry_management_section.mdx
+++ b/api_docs/telemetry_management_section.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection
title: "telemetryManagementSection"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the telemetryManagementSection plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx
index 2383a376db94f..283d5fddf5ad1 100644
--- a/api_docs/timelines.mdx
+++ b/api_docs/timelines.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/timelines
title: "timelines"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the timelines plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx
index 30975b87dc4db..c73be6a3b325b 100644
--- a/api_docs/transform.mdx
+++ b/api_docs/transform.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/transform
title: "transform"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the transform plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx
index 8629252346fbc..8744787a89320 100644
--- a/api_docs/triggers_actions_ui.mdx
+++ b/api_docs/triggers_actions_ui.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi
title: "triggersActionsUi"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the triggersActionsUi plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx
index 8a2dd272ec7a1..7138b05a51332 100644
--- a/api_docs/ui_actions.mdx
+++ b/api_docs/ui_actions.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/uiActions
title: "uiActions"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the uiActions plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx
index a5a6b8b157fdb..dbb6805add960 100644
--- a/api_docs/ui_actions_enhanced.mdx
+++ b/api_docs/ui_actions_enhanced.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced
title: "uiActionsEnhanced"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the uiActionsEnhanced plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx
index fa0e583851fa5..45056d1bcf5f8 100644
--- a/api_docs/unified_search.mdx
+++ b/api_docs/unified_search.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/unifiedSearch
title: "unifiedSearch"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the unifiedSearch plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx
index 33e6e6a372462..ca9d8635db018 100644
--- a/api_docs/unified_search_autocomplete.mdx
+++ b/api_docs/unified_search_autocomplete.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete
title: "unifiedSearch.autocomplete"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the unifiedSearch.autocomplete plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx
index 68cc63dfa8720..35d39260ab2ff 100644
--- a/api_docs/url_forwarding.mdx
+++ b/api_docs/url_forwarding.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/urlForwarding
title: "urlForwarding"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the urlForwarding plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx
index 55b150d9c9e7c..7d28336057d36 100644
--- a/api_docs/usage_collection.mdx
+++ b/api_docs/usage_collection.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/usageCollection
title: "usageCollection"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the usageCollection plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx
index de6b40411703c..8f44817ef172c 100644
--- a/api_docs/ux.mdx
+++ b/api_docs/ux.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/ux
title: "ux"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the ux plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx
index 583915307439a..a8779d73312e7 100644
--- a/api_docs/vis_default_editor.mdx
+++ b/api_docs/vis_default_editor.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor
title: "visDefaultEditor"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the visDefaultEditor plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx
index 7b5951cce5dde..00e43f99f59a2 100644
--- a/api_docs/vis_type_gauge.mdx
+++ b/api_docs/vis_type_gauge.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeGauge
title: "visTypeGauge"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the visTypeGauge plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx
index bc518c8156412..465ccab4d9c81 100644
--- a/api_docs/vis_type_heatmap.mdx
+++ b/api_docs/vis_type_heatmap.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap
title: "visTypeHeatmap"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the visTypeHeatmap plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx
index a44f52447f08a..76805bc7416a4 100644
--- a/api_docs/vis_type_pie.mdx
+++ b/api_docs/vis_type_pie.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypePie
title: "visTypePie"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the visTypePie plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx
index 7a31d8844fa79..ae424b974b381 100644
--- a/api_docs/vis_type_table.mdx
+++ b/api_docs/vis_type_table.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeTable
title: "visTypeTable"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the visTypeTable plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx
index bea439614069e..d33d7b36de830 100644
--- a/api_docs/vis_type_timelion.mdx
+++ b/api_docs/vis_type_timelion.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion
title: "visTypeTimelion"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the visTypeTimelion plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx
index 30b0159f0778b..28ebd5bcc88df 100644
--- a/api_docs/vis_type_timeseries.mdx
+++ b/api_docs/vis_type_timeseries.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries
title: "visTypeTimeseries"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the visTypeTimeseries plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx
index 404db107ebe8e..8c8ae0a7dce7f 100644
--- a/api_docs/vis_type_vega.mdx
+++ b/api_docs/vis_type_vega.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeVega
title: "visTypeVega"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the visTypeVega plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx
index ca7c8a49ad004..ae5aef7e06252 100644
--- a/api_docs/vis_type_vislib.mdx
+++ b/api_docs/vis_type_vislib.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeVislib
title: "visTypeVislib"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the visTypeVislib plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx
index 7ea6323e773dc..9ad103431f8bb 100644
--- a/api_docs/vis_type_xy.mdx
+++ b/api_docs/vis_type_xy.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeXy
title: "visTypeXy"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the visTypeXy plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx
index 25c46f17f18c4..370b33d78d66d 100644
--- a/api_docs/visualizations.mdx
+++ b/api_docs/visualizations.mdx
@@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visualizations
title: "visualizations"
image: https://source.unsplash.com/400x175/?github
summary: API docs for the visualizations plugin
-date: 2022-06-16
+date: 2022-06-17
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
From c81da028a98698917c682300c8cfa7ec67dd95d9 Mon Sep 17 00:00:00 2001
From: Julia Rechkunova
Date: Fri, 17 Jun 2022 10:16:50 +0200
Subject: [PATCH 10/30] [Discover] Unify definition of field names and field
descriptions (#134463)
* [Discover] Address "Don't call Hooks" React error message
* [Discover] Unify definition of field names and field descriptions
* [Discover] Bring back source message
* [Discover] Update name function
* [Discover] Revert source logic
* [Discover] Update code style
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
src/plugins/discover/common/field_types.ts | 26 +++++++++
.../sidebar/discover_field_search.tsx | 11 +++-
.../sidebar/lib/get_field_type_description.ts | 49 +++++++++-------
.../public/utils/get_field_type_name.test.ts | 15 ++---
.../public/utils/get_field_type_name.ts | 57 +++++++++++--------
5 files changed, 104 insertions(+), 54 deletions(-)
create mode 100644 src/plugins/discover/common/field_types.ts
diff --git a/src/plugins/discover/common/field_types.ts b/src/plugins/discover/common/field_types.ts
new file mode 100644
index 0000000000000..bd24797ab5323
--- /dev/null
+++ b/src/plugins/discover/common/field_types.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+export enum KNOWN_FIELD_TYPES {
+ BOOLEAN = 'boolean',
+ CONFLICT = 'conflict',
+ DATE = 'date',
+ DATE_RANGE = 'date_range',
+ GEO_POINT = 'geo_point',
+ GEO_SHAPE = 'geo_shape',
+ HISTOGRAM = 'histogram',
+ IP = 'ip',
+ IP_RANGE = 'ip_range',
+ KEYWORD = 'keyword',
+ MURMUR3 = 'murmur3',
+ NUMBER = 'number',
+ NESTED = 'nested',
+ STRING = 'string',
+ TEXT = 'text',
+ VERSION = 'version',
+}
diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_field_search.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_field_search.tsx
index 3e01c62c449ba..d6f88c0626e7f 100644
--- a/src/plugins/discover/public/application/main/components/sidebar/discover_field_search.tsx
+++ b/src/plugins/discover/public/application/main/components/sidebar/discover_field_search.tsx
@@ -36,6 +36,7 @@ import {
import { FormattedMessage } from '@kbn/i18n-react';
import { FieldIcon } from '@kbn/react-field';
import { getFieldTypeDescription } from './lib/get_field_type_description';
+import { KNOWN_FIELD_TYPES } from '../../../../../common/field_types';
import { useDiscoverServices } from '../../../../utils/use_discover_services';
export interface State {
@@ -107,7 +108,9 @@ export function DiscoverFieldSearch({ onChange, value, types, presentFieldTypes
const { docLinks } = useDiscoverServices();
const items: FieldTypeTableItem[] = useMemo(() => {
+ const knownTypes = Object.values(KNOWN_FIELD_TYPES) as string[];
return presentFieldTypes
+ .filter((element) => knownTypes.includes(element))
.sort((one, another) => one.localeCompare(another))
.map((element, index) => ({
id: index,
@@ -122,7 +125,9 @@ export function DiscoverFieldSearch({ onChange, value, types, presentFieldTypes
const columnsSidebar: Array> = [
{
field: 'dataType',
- name: 'Data type',
+ name: i18n.translate('discover.fieldTypesPopover.dataTypeColumnTitle', {
+ defaultMessage: 'Data type',
+ }),
width: '110px',
render: (name: string) => (
@@ -135,7 +140,9 @@ export function DiscoverFieldSearch({ onChange, value, types, presentFieldTypes
},
{
field: 'description',
- name: 'Description',
+ name: i18n.translate('discover.fieldTypesPopover.descriptionColumnTitle', {
+ defaultMessage: 'Description',
+ }),
// eslint-disable-next-line react/no-danger
render: (description: string) => ,
},
diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_type_description.ts b/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_type_description.ts
index 3e19c05d01770..3b5b6aaa016ce 100644
--- a/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_type_description.ts
+++ b/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_type_description.ts
@@ -8,22 +8,28 @@
import type { DocLinksStart } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
+import { KNOWN_FIELD_TYPES } from '../../../../../../common/field_types';
+
+const UNKNOWN_FIELD_TYPE_DESC = i18n.translate('discover.fieldNameDescription.unknownField', {
+ defaultMessage: 'Unknown field',
+});
export function getFieldTypeDescription(type: string, docLinks: DocLinksStart) {
- switch (type) {
- case 'boolean':
+ const knownType: KNOWN_FIELD_TYPES = type as KNOWN_FIELD_TYPES;
+ switch (knownType) {
+ case KNOWN_FIELD_TYPES.BOOLEAN:
return i18n.translate('discover.fieldNameDescription.booleanField', {
defaultMessage: 'True and false values.',
});
- case 'conflict':
+ case KNOWN_FIELD_TYPES.CONFLICT:
return i18n.translate('discover.fieldNameDescription.conflictField', {
defaultMessage: 'Field has values of different types. Resolve in Management > Data Views.',
});
- case 'date':
+ case KNOWN_FIELD_TYPES.DATE:
return i18n.translate('discover.fieldNameDescription.dateField', {
defaultMessage: 'A date string or the number of seconds or milliseconds since 1/1/1970.',
});
- case 'date_range':
+ case KNOWN_FIELD_TYPES.DATE_RANGE:
return i18n.translate('discover.fieldNameDescription.dateRangeField', {
defaultMessage: 'Range of {dateFieldTypeLink} values. {viewSupportedDateFormatsLink}',
values: {
@@ -43,49 +49,52 @@ export function getFieldTypeDescription(type: string, docLinks: DocLinksStart) {
'',
},
});
- case 'geo_point':
+ case KNOWN_FIELD_TYPES.GEO_POINT:
return i18n.translate('discover.fieldNameDescription.geoPointField', {
defaultMessage: 'Latitude and longitude points.',
});
- case 'geo_shape':
+ case KNOWN_FIELD_TYPES.GEO_SHAPE:
return i18n.translate('discover.fieldNameDescription.geoShapeField', {
defaultMessage: 'Complex shapes, such as polygons.',
});
- case 'ip':
+ case KNOWN_FIELD_TYPES.HISTOGRAM:
+ return i18n.translate('discover.fieldNameDescription.histogramField', {
+ defaultMessage: 'Pre-aggregated numerical values in the form of a histogram.',
+ });
+ case KNOWN_FIELD_TYPES.IP:
return i18n.translate('discover.fieldNameDescription.ipAddressField', {
defaultMessage: 'IPv4 and IPv6 addresses.',
});
- case 'ip_range':
+ case KNOWN_FIELD_TYPES.IP_RANGE:
return i18n.translate('discover.fieldNameDescription.ipAddressRangeField', {
defaultMessage: 'Range of ip values supporting either IPv4 or IPv6 (or mixed) addresses.',
});
- case 'murmur3':
+ case KNOWN_FIELD_TYPES.MURMUR3:
return i18n.translate('discover.fieldNameDescription.murmur3Field', {
defaultMessage: 'Field that computes and stores hashes of values.',
});
- case 'number':
+ case KNOWN_FIELD_TYPES.NUMBER:
return i18n.translate('discover.fieldNameDescription.numberField', {
defaultMessage: 'Long, integer, short, byte, double, and float values.',
});
- case 'string':
+ case KNOWN_FIELD_TYPES.STRING:
return i18n.translate('discover.fieldNameDescription.stringField', {
defaultMessage: 'Full text such as the body of an email or a product description.',
});
- case 'text':
+ case KNOWN_FIELD_TYPES.TEXT:
return i18n.translate('discover.fieldNameDescription.textField', {
defaultMessage: 'Full text such as the body of an email or a product description.',
});
- case 'keyword':
+ case KNOWN_FIELD_TYPES.KEYWORD:
return i18n.translate('discover.fieldNameDescription.keywordField', {
defaultMessage:
'Structured content such as an ID, email address, hostname, status code, or tag.',
});
-
- case 'nested':
+ case KNOWN_FIELD_TYPES.NESTED:
return i18n.translate('discover.fieldNameDescription.nestedField', {
defaultMessage: 'JSON object that preserves the relationship between its subfields.',
});
- case 'version':
+ case KNOWN_FIELD_TYPES.VERSION:
return i18n.translate('discover.fieldNameDescription.versionField', {
defaultMessage: 'Software versions. Supports {SemanticVersioningLink} precedence rules.',
values: {
@@ -102,8 +111,8 @@ export function getFieldTypeDescription(type: string, docLinks: DocLinksStart) {
},
});
default:
- return i18n.translate('discover.fieldNameDescription.unknownField', {
- defaultMessage: 'Unknown field',
- });
+ // If you see a typescript error here, that's a sign that there are missing switch cases ^^
+ const _exhaustiveCheck: never = knownType;
+ return UNKNOWN_FIELD_TYPE_DESC || _exhaustiveCheck;
}
}
diff --git a/src/plugins/discover/public/utils/get_field_type_name.test.ts b/src/plugins/discover/public/utils/get_field_type_name.test.ts
index b612522d1c3ea..bada07e8ad9f7 100644
--- a/src/plugins/discover/public/utils/get_field_type_name.test.ts
+++ b/src/plugins/discover/public/utils/get_field_type_name.test.ts
@@ -6,11 +6,8 @@
* Side Public License, v 1.
*/
-import {
- getFieldTypeName,
- KNOWN_FIELD_TYPES,
- UNKNOWN_FIELD_TYPE_MESSAGE,
-} from './get_field_type_name';
+import { getFieldTypeName, UNKNOWN_FIELD_TYPE_MESSAGE } from './get_field_type_name';
+import { KNOWN_FIELD_TYPES } from '../../common/field_types';
describe('getFieldTypeName', () => {
describe('known field types should be recognized', () => {
@@ -28,7 +25,11 @@ describe('getFieldTypeName', () => {
expect(getFieldTypeName(undefined)).toBe(UNKNOWN_FIELD_TYPE_MESSAGE);
});
- it(`should return '${UNKNOWN_FIELD_TYPE_MESSAGE}' when passed an unknown field type`, () => {
- expect(getFieldTypeName('unknown_field_type')).toBe(UNKNOWN_FIELD_TYPE_MESSAGE);
+ it(`should return '${UNKNOWN_FIELD_TYPE_MESSAGE}' when passed 'unknown'`, () => {
+ expect(getFieldTypeName('unknown')).toBe(UNKNOWN_FIELD_TYPE_MESSAGE);
+ });
+
+ it('should return the original type string back when passed an unknown field type', () => {
+ expect(getFieldTypeName('unknown_field_type')).toBe('unknown_field_type');
});
});
diff --git a/src/plugins/discover/public/utils/get_field_type_name.ts b/src/plugins/discover/public/utils/get_field_type_name.ts
index 24d5e4602f3be..81a4346f63902 100644
--- a/src/plugins/discover/public/utils/get_field_type_name.ts
+++ b/src/plugins/discover/public/utils/get_field_type_name.ts
@@ -7,24 +7,8 @@
*/
import { i18n } from '@kbn/i18n';
-import { KBN_FIELD_TYPES, ES_FIELD_TYPES } from '@kbn/data-plugin/public';
-
-export const KNOWN_FIELD_TYPES = {
- BOOLEAN: KBN_FIELD_TYPES.BOOLEAN,
- CONFLICT: KBN_FIELD_TYPES.CONFLICT,
- DATE: KBN_FIELD_TYPES.DATE,
- GEO_POINT: KBN_FIELD_TYPES.GEO_POINT,
- GEO_SHAPE: KBN_FIELD_TYPES.GEO_SHAPE,
- IP: KBN_FIELD_TYPES.IP,
- KEYWORD: ES_FIELD_TYPES.KEYWORD,
- MURMUR3: KBN_FIELD_TYPES.MURMUR3,
- NUMBER: KBN_FIELD_TYPES.NUMBER,
- NESTED: KBN_FIELD_TYPES.NESTED,
- SOURCE: 'source',
- STRING: KBN_FIELD_TYPES.STRING,
- TEXT: ES_FIELD_TYPES.TEXT,
- VERSION: ES_FIELD_TYPES.VERSION,
-};
+import { KBN_FIELD_TYPES } from '@kbn/data-plugin/public';
+import { KNOWN_FIELD_TYPES } from '../../common/field_types';
export const UNKNOWN_FIELD_TYPE_MESSAGE = i18n.translate(
'discover.fieldNameIcons.unknownFieldAriaLabel',
@@ -34,7 +18,21 @@ export const UNKNOWN_FIELD_TYPE_MESSAGE = i18n.translate(
);
export function getFieldTypeName(type?: string) {
- switch (type) {
+ if (!type || type === KBN_FIELD_TYPES.UNKNOWN) {
+ return UNKNOWN_FIELD_TYPE_MESSAGE;
+ }
+
+ if (type === 'source') {
+ // TODO: check if we can remove this logic as outdated
+
+ // Note that this type is currently not provided, type for _source is undefined
+ return i18n.translate('discover.fieldNameIcons.sourceFieldAriaLabel', {
+ defaultMessage: 'Source field',
+ });
+ }
+
+ const knownType: KNOWN_FIELD_TYPES = type as KNOWN_FIELD_TYPES;
+ switch (knownType) {
case KNOWN_FIELD_TYPES.BOOLEAN:
return i18n.translate('discover.fieldNameIcons.booleanAriaLabel', {
defaultMessage: 'Boolean field',
@@ -47,6 +45,10 @@ export function getFieldTypeName(type?: string) {
return i18n.translate('discover.fieldNameIcons.dateFieldAriaLabel', {
defaultMessage: 'Date field',
});
+ case KNOWN_FIELD_TYPES.DATE_RANGE:
+ return i18n.translate('discover.fieldNameIcons.dateRangeFieldAriaLabel', {
+ defaultMessage: 'Date range field',
+ });
case KNOWN_FIELD_TYPES.GEO_POINT:
return i18n.translate('discover.fieldNameIcons.geoPointFieldAriaLabel', {
defaultMessage: 'Geo point field',
@@ -55,10 +57,18 @@ export function getFieldTypeName(type?: string) {
return i18n.translate('discover.fieldNameIcons.geoShapeFieldAriaLabel', {
defaultMessage: 'Geo shape field',
});
+ case KNOWN_FIELD_TYPES.HISTOGRAM:
+ return i18n.translate('discover.fieldNameIcons.histogramFieldAriaLabel', {
+ defaultMessage: 'Histogram field',
+ });
case KNOWN_FIELD_TYPES.IP:
return i18n.translate('discover.fieldNameIcons.ipAddressFieldAriaLabel', {
defaultMessage: 'IP address field',
});
+ case KNOWN_FIELD_TYPES.IP_RANGE:
+ return i18n.translate('discover.fieldNameIcons.ipRangeFieldAriaLabel', {
+ defaultMessage: 'IP range field',
+ });
case KNOWN_FIELD_TYPES.MURMUR3:
return i18n.translate('discover.fieldNameIcons.murmur3FieldAriaLabel', {
defaultMessage: 'Murmur3 field',
@@ -67,11 +77,6 @@ export function getFieldTypeName(type?: string) {
return i18n.translate('discover.fieldNameIcons.numberFieldAriaLabel', {
defaultMessage: 'Number field',
});
- case KNOWN_FIELD_TYPES.SOURCE:
- // Note that this type is currently not provided, type for _source is undefined
- return i18n.translate('discover.fieldNameIcons.sourceFieldAriaLabel', {
- defaultMessage: 'Source field',
- });
case KNOWN_FIELD_TYPES.STRING:
return i18n.translate('discover.fieldNameIcons.stringFieldAriaLabel', {
defaultMessage: 'String field',
@@ -93,6 +98,8 @@ export function getFieldTypeName(type?: string) {
defaultMessage: 'Version field',
});
default:
- return UNKNOWN_FIELD_TYPE_MESSAGE;
+ // If you see a typescript error here, that's a sign that there are missing switch cases ^^
+ const _exhaustiveCheck: never = knownType;
+ return knownType || _exhaustiveCheck;
}
}
From ce980d4507ade75509a7020c596ceb21a2d47db1 Mon Sep 17 00:00:00 2001
From: Maja Grubic
Date: Fri, 17 Jun 2022 10:17:26 +0200
Subject: [PATCH 11/30] [Kibana Overview] Add AnalyticsNoDataPage (#134172)
* Add test
* Revert changes to jest.config
* Fix i18n
* Remove core import
* Remove erroneus export
* Updating test
* Add mock import from data view editor
* Access DataViewEditor through context
* Change how we pass props around
* Update mocks
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
src/plugins/kibana_overview/kibana.json | 2 +-
.../kibana_overview/public/application.tsx | 7 +-
.../kibana_overview/public/components/app.tsx | 2 +-
.../__snapshots__/overview.test.tsx.snap | 317 ++++++++++++++++--
.../overview/overview.test.mocks.ts | 23 +-
.../components/overview/overview.test.tsx | 48 ++-
.../public/components/overview/overview.tsx | 187 +++++++----
src/plugins/kibana_overview/public/types.ts | 2 +
src/plugins/kibana_overview/tsconfig.json | 1 +
.../translations/translations/fr-FR.json | 4 -
.../translations/translations/ja-JP.json | 4 -
.../translations/translations/zh-CN.json | 4 -
12 files changed, 477 insertions(+), 124 deletions(-)
diff --git a/src/plugins/kibana_overview/kibana.json b/src/plugins/kibana_overview/kibana.json
index ee646689226b6..955b74d558319 100644
--- a/src/plugins/kibana_overview/kibana.json
+++ b/src/plugins/kibana_overview/kibana.json
@@ -7,7 +7,7 @@
"version": "kibana",
"server": false,
"ui": true,
- "requiredPlugins": ["navigation", "dataViews", "home", "share"],
+ "requiredPlugins": ["navigation", "dataViews", "home", "share", "dataViewEditor"],
"optionalPlugins": ["newsfeed", "usageCollection"],
"requiredBundles": ["kibanaReact", "newsfeed"]
}
diff --git a/src/plugins/kibana_overview/public/application.tsx b/src/plugins/kibana_overview/public/application.tsx
index adfd53c04ef48..8453c35ecba23 100644
--- a/src/plugins/kibana_overview/public/application.tsx
+++ b/src/plugins/kibana_overview/public/application.tsx
@@ -41,12 +41,7 @@ export const renderApp = (
diff --git a/src/plugins/kibana_overview/public/components/app.tsx b/src/plugins/kibana_overview/public/components/app.tsx
index 90e7051882cfd..9c82c9842e9df 100644
--- a/src/plugins/kibana_overview/public/components/app.tsx
+++ b/src/plugins/kibana_overview/public/components/app.tsx
@@ -49,7 +49,7 @@ export const KibanaOverviewApp = ({
-
+
diff --git a/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap b/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap
index 48e70a03d7fbb..c185e1b85ae1a 100644
--- a/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap
+++ b/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap
@@ -227,7 +227,7 @@ exports[`Overview render 1`] = `
]
}
>
-
+ >
+
+
`;
@@ -455,35 +457,292 @@ exports[`Overview when there is no user data view 1`] = `
]
}
>
- ,
- "rightSideItems": Array [],
+ "IndexPatternEditorComponent": [MockFunction],
+ "openEditor": [MockFunction],
+ "userPermissions": Object {
+ "editDataView": [MockFunction],
+ },
}
}
- template="empty"
- />
+ dataViews={
+ Object {
+ "hasData": Object {
+ "hasESData": [Function],
+ "hasUserDataView": [Function],
+ },
+ "hasUserDataView": [MockFunction],
+ }
+ }
+ >
+
+
`;
@@ -668,7 +927,7 @@ exports[`Overview without features 1`] = `
]
}
>
-
+ >
+
+
`;
@@ -861,7 +1122,7 @@ exports[`Overview without solutions 1`] = `
}
solutions={Array []}
>
-
+ >
+
+
`;
diff --git a/src/plugins/kibana_overview/public/components/overview/overview.test.mocks.ts b/src/plugins/kibana_overview/public/components/overview/overview.test.mocks.ts
index 4c58de5cdd245..ce02f2cb483e7 100644
--- a/src/plugins/kibana_overview/public/components/overview/overview.test.mocks.ts
+++ b/src/plugins/kibana_overview/public/components/overview/overview.test.mocks.ts
@@ -7,16 +7,35 @@
*/
import React from 'react';
+import { Observable } from 'rxjs';
+import { indexPatternEditorPluginMock } from '@kbn/data-view-editor-plugin/public/mocks';
-export const hasUserDataViewMock = jest.fn();
+export const hasUserDataView = jest.fn();
+export const hasESData = jest.fn();
jest.doMock('@kbn/kibana-react-plugin/public', () => ({
useKibana: jest.fn().mockReturnValue({
services: {
+ application: {
+ currentAppId$: new Observable(),
+ navigateToUrl: jest.fn(),
+ capabilities: {
+ navLinks: {
+ integrations: {
+ canAccessFleet: false,
+ },
+ },
+ },
+ },
http: { basePath: { prepend: jest.fn((path: string) => (path ? path : 'path')) } },
dataViews: {
- hasUserDataView: hasUserDataViewMock,
+ hasUserDataView: jest.fn(),
+ hasData: {
+ hasESData,
+ hasUserDataView,
+ },
},
+ dataViewEditor: indexPatternEditorPluginMock.createStartContract(),
share: { url: { locators: { get: () => ({ useUrl: () => '' }) } } },
uiSettings: { get: jest.fn() },
docLinks: {
diff --git a/src/plugins/kibana_overview/public/components/overview/overview.test.tsx b/src/plugins/kibana_overview/public/components/overview/overview.test.tsx
index 29d51087c3d67..b433e7a39da13 100644
--- a/src/plugins/kibana_overview/public/components/overview/overview.test.tsx
+++ b/src/plugins/kibana_overview/public/components/overview/overview.test.tsx
@@ -6,15 +6,40 @@
* Side Public License, v 1.
*/
-import { hasUserDataViewMock } from './overview.test.mocks';
+import React from 'react';
import { setTimeout as setTimeoutP } from 'timers/promises';
import moment from 'moment';
-import React from 'react';
import { act } from 'react-dom/test-utils';
import { ReactWrapper } from 'enzyme';
-import { Overview } from './overview';
+import { EuiLoadingSpinner } from '@elastic/eui';
import { mountWithIntl } from '@kbn/test-jest-helpers';
+import { KibanaPageTemplate } from '@kbn/shared-ux-components';
import type { FeatureCatalogueCategory } from '@kbn/home-plugin/public';
+import { AnalyticsNoDataPageKibanaProvider } from '@kbn/shared-ux-page-analytics-no-data';
+import { hasESData, hasUserDataView } from './overview.test.mocks';
+import { Overview } from './overview';
+
+jest.mock('@kbn/shared-ux-components', () => {
+ const MockedComponent: string = 'MockedKibanaPageTemplate';
+ const mockedModule = {
+ ...jest.requireActual('@kbn/shared-ux-components'),
+ KibanaPageTemplate: () => {
+ return ;
+ },
+ };
+ return mockedModule;
+});
+
+jest.mock('@kbn/shared-ux-page-analytics-no-data', () => {
+ const MockedComponent: string = 'MockedAnalyticsNoDataPage';
+ return {
+ ...jest.requireActual('@kbn/shared-ux-page-analytics-no-data'),
+ AnalyticsNoDataPageKibanaProvider: () => {
+ return ;
+ },
+ };
+});
+
const mockNewsFetchResult = {
error: null,
feedItems: [
@@ -135,8 +160,8 @@ const updateComponent = async (component: ReactWrapper) => {
describe('Overview', () => {
beforeEach(() => {
- hasUserDataViewMock.mockClear();
- hasUserDataViewMock.mockResolvedValue(true);
+ hasESData.mockResolvedValue(true);
+ hasUserDataView.mockResolvedValue(true);
});
afterAll(() => jest.clearAllMocks());
@@ -153,6 +178,7 @@ describe('Overview', () => {
await updateComponent(component);
expect(component).toMatchSnapshot();
+ expect(component.find(KibanaPageTemplate).length).toBe(1);
});
test('without solutions', async () => {
@@ -176,7 +202,8 @@ describe('Overview', () => {
});
test('when there is no user data view', async () => {
- hasUserDataViewMock.mockResolvedValue(false);
+ hasESData.mockResolvedValue(true);
+ hasUserDataView.mockResolvedValue(false);
const component = mountWithIntl(
{
await updateComponent(component);
expect(component).toMatchSnapshot();
+ expect(component.find(AnalyticsNoDataPageKibanaProvider).length).toBe(1);
+ expect(component.find(KibanaPageTemplate).length).toBe(0);
+ expect(component.find(EuiLoadingSpinner).length).toBe(0);
});
test('during loading', async () => {
- hasUserDataViewMock.mockImplementation(() => new Promise(() => {}));
+ hasESData.mockImplementation(() => new Promise(() => {}));
+ hasUserDataView.mockImplementation(() => new Promise(() => {}));
const component = mountWithIntl(
{
await updateComponent(component);
expect(component.render()).toMatchSnapshot();
+ expect(component.find(AnalyticsNoDataPageKibanaProvider).length).toBe(0);
+ expect(component.find(KibanaPageTemplate).length).toBe(0);
+ expect(component.find(EuiLoadingSpinner).length).toBe(1);
});
});
diff --git a/src/plugins/kibana_overview/public/components/overview/overview.tsx b/src/plugins/kibana_overview/public/components/overview/overview.tsx
index 5859ea2e8a58b..2258c662fe94e 100644
--- a/src/plugins/kibana_overview/public/components/overview/overview.tsx
+++ b/src/plugins/kibana_overview/public/components/overview/overview.tsx
@@ -20,16 +20,21 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { CoreStart } from '@kbn/core/public';
-import { i18n } from '@kbn/i18n';
import {
- RedirectAppLinks,
useKibana,
- KibanaPageTemplate,
KibanaPageTemplateSolutionNavAvatar,
- KibanaPageTemplateProps,
overviewPageActions,
OverviewPageFooter,
} from '@kbn/kibana-react-plugin/public';
+import { KibanaPageTemplate } from '@kbn/shared-ux-components';
+import {
+ AnalyticsNoDataPageKibanaProvider,
+ AnalyticsNoDataPage,
+} from '@kbn/shared-ux-page-analytics-no-data';
+import {
+ RedirectAppLinksContainer as RedirectAppLinks,
+ RedirectAppLinksKibanaProvider,
+} from '@kbn/shared-ux-link-redirect-app';
import { FetchResult } from '@kbn/newsfeed-plugin/public';
import {
FeatureCatalogueEntry,
@@ -54,10 +59,12 @@ interface Props {
export const Overview: FC = ({ newsFetchResult, solutions, features }) => {
const [isNewKibanaInstance, setNewKibanaInstance] = useState(false);
+ const [hasESData, setHasESData] = useState(false);
+ const [hasDataView, setHasDataView] = useState(false);
const [isLoading, setIsLoading] = useState(true);
- const {
- services: { http, docLinks, dataViews, share, uiSettings, application },
- } = useKibana();
+ const { services } = useKibana();
+ const { http, docLinks, dataViews, share, uiSettings, application, chrome, dataViewEditor } =
+ services;
const addBasePath = http.basePath.prepend;
const IS_DARK_THEME = uiSettings.get('theme:darkMode');
@@ -81,28 +88,6 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) =>
const addDataFeatures = getFeaturesByCategory('data');
const manageDataFeatures = getFeaturesByCategory('admin');
const devTools = findFeatureById('console');
- const noDataConfig: KibanaPageTemplateProps['noDataConfig'] = {
- solution: i18n.translate('kibanaOverview.noDataConfig.solutionName', {
- defaultMessage: `Analytics`,
- }),
- pageTitle: i18n.translate('kibanaOverview.noDataConfig.pageTitle', {
- defaultMessage: `Welcome to Analytics!`,
- }),
- logo: 'logoKibana',
- actions: {
- elasticAgent: {
- title: i18n.translate('kibanaOverview.noDataConfig.title', {
- defaultMessage: 'Add integrations',
- }),
- description: i18n.translate('kibanaOverview.noDataConfig.description', {
- defaultMessage:
- 'Use Elastic Agent or Beats to collect data and build out Analytics solutions.',
- }),
- 'data-test-subj': 'kbnOverviewAddIntegrations',
- },
- },
- docsLink: docLinks.links.kibana.guide,
- };
// Show card for console if none of the manage data plugins are available, most likely in OSS
if (manageDataFeatures.length < 1 && devTools) {
@@ -111,9 +96,21 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) =>
useEffect(() => {
const fetchIsNewKibanaInstance = async () => {
- const hasUserIndexPattern = await dataViews.hasUserDataView().catch(() => true);
+ const checkData = async () => {
+ const hasUserDataViewValue = await dataViews.hasData.hasUserDataView();
+ const hasESDataValue = await dataViews.hasData.hasESData();
+ setNewKibanaInstance((!hasUserDataViewValue && hasESDataValue) || !hasESDataValue);
+ setHasDataView(hasUserDataViewValue);
+ setHasESData(hasESDataValue);
+ };
+
+ await checkData().catch((e) => {
+ setNewKibanaInstance(false);
+ setHasDataView(true);
+ setHasESData(true);
+ setIsLoading(false);
+ });
- setNewKibanaInstance(!hasUserIndexPattern);
setIsLoading(false);
};
@@ -125,21 +122,33 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) =>
return app ? (
-
- {
- trackUiMetric(METRIC_TYPE.CLICK, `app_card_${appId}`);
- }}
- image={addBasePath(
- `/plugins/${PLUGIN_ID}/assets/kibana_${appId}_${IS_DARK_THEME ? 'dark' : 'light'}.svg`
- )}
- title={app.title}
- titleElement="h3"
- titleSize="s"
- />
-
+
+
+ {
+ trackUiMetric(METRIC_TYPE.CLICK, `app_card_${appId}`);
+ }}
+ image={addBasePath(
+ `/plugins/${PLUGIN_ID}/assets/kibana_${appId}_${
+ IS_DARK_THEME ? 'dark' : 'light'
+ }.svg`
+ )}
+ title={app.title}
+ titleElement="h3"
+ titleSize="s"
+ />
+
+
) : null;
};
@@ -148,6 +157,10 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) =>
const mainApps = ['dashboard', 'discover'];
const remainingApps = kibanaApps.map(({ id }) => id).filter((id) => !mainApps.includes(id));
+ const onDataViewCreated = () => {
+ setNewKibanaInstance(false);
+ };
+
if (isLoading) {
return (
@@ -158,6 +171,35 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) =>
);
}
+ if (isNewKibanaInstance) {
+ const analyticsServices = {
+ coreStart: {
+ application,
+ chrome,
+ docLinks,
+ http,
+ },
+ dataViews: {
+ ...dataViews,
+ hasData: {
+ ...dataViews.hasData,
+
+ // We've already called this, so we can optimize the analytics services to
+ // use the already-retrieved data to avoid a double-call.
+ hasESData: () => Promise.resolve(hasESData),
+ hasUserDataView: () => Promise.resolve(hasDataView),
+ },
+ },
+ dataViewEditor,
+ };
+
+ return (
+
+
+
+ );
+ }
+
return (
= ({ newsFetchResult, solutions, features }) =>
showManagementLink: !!manageDataFeatures,
}),
}}
- noDataConfig={isNewKibanaInstance ? noDataConfig : undefined}
template="empty"
>
<>
@@ -243,27 +284,37 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) =>
{solutions.map(({ id, title, description, icon, path }) => (
-
-
- }
- image={addBasePath(getSolutionGraphicURL(snakeCase(id)))}
- title={title}
- titleElement="h3"
- titleSize="xs"
- onClick={() => {
- trackUiMetric(METRIC_TYPE.CLICK, `solution_panel_${id}`);
- }}
- />
-
+
+
+
+ }
+ image={addBasePath(getSolutionGraphicURL(snakeCase(id)))}
+ title={title}
+ titleElement="h3"
+ titleSize="xs"
+ onClick={() => {
+ trackUiMetric(METRIC_TYPE.CLICK, `solution_panel_${id}`);
+ }}
+ />
+
+
))}
diff --git a/src/plugins/kibana_overview/public/types.ts b/src/plugins/kibana_overview/public/types.ts
index 7698b944bfc2f..9afe74434faa8 100644
--- a/src/plugins/kibana_overview/public/types.ts
+++ b/src/plugins/kibana_overview/public/types.ts
@@ -12,6 +12,7 @@ import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { NewsfeedPublicPluginStart } from '@kbn/newsfeed-plugin/public';
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
import { SharePluginStart } from '@kbn/share-plugin/public';
+import { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface KibanaOverviewPluginSetup {}
@@ -30,4 +31,5 @@ export interface AppPluginStartDependencies {
navigation: NavigationPublicPluginStart;
newsfeed?: NewsfeedPublicPluginStart;
share: SharePluginStart;
+ dataViewEditor: DataViewEditorStart;
}
diff --git a/src/plugins/kibana_overview/tsconfig.json b/src/plugins/kibana_overview/tsconfig.json
index fd9002d39ee8c..ffa89f25316a9 100644
--- a/src/plugins/kibana_overview/tsconfig.json
+++ b/src/plugins/kibana_overview/tsconfig.json
@@ -18,5 +18,6 @@
{ "path": "../../plugins/newsfeed/tsconfig.json" },
{ "path": "../../plugins/usage_collection/tsconfig.json" },
{ "path": "../../plugins/kibana_react/tsconfig.json" },
+ { "path": "../../plugins/data_view_editor/tsconfig.json" }
]
}
diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json
index 5dc1642ac8474..13edd89027a06 100644
--- a/x-pack/plugins/translations/translations/fr-FR.json
+++ b/x-pack/plugins/translations/translations/fr-FR.json
@@ -5057,10 +5057,6 @@
"kibanaOverview.manageData.sectionTitle": "Gérer vos données",
"kibanaOverview.more.title": "Toujours plus avec Elastic",
"kibanaOverview.news.title": "Nouveautés",
- "kibanaOverview.noDataConfig.description": "Utilisez Elastic Agent ou Beats pour collecter des données et créer des solutions Analytics.",
- "kibanaOverview.noDataConfig.pageTitle": "Bienvenue dans Analytics !",
- "kibanaOverview.noDataConfig.solutionName": "Analyse",
- "kibanaOverview.noDataConfig.title": "Ajouter des intégrations",
"lists.exceptions.doesNotExistOperatorLabel": "n'existe pas",
"lists.exceptions.existsOperatorLabel": "existe",
"lists.exceptions.isInListOperatorLabel": "est dans la liste",
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index b763e0b6206aa..49a2a99abcfc3 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -5153,10 +5153,6 @@
"kibanaOverview.manageData.sectionTitle": "データを管理",
"kibanaOverview.more.title": "Elasticではさまざまなことが可能です",
"kibanaOverview.news.title": "新機能",
- "kibanaOverview.noDataConfig.description": "ElasticエージェントまたはBeatsを使用して、データを収集し、分析ソリューションを構築します。",
- "kibanaOverview.noDataConfig.pageTitle": "Analyticsへようこそ!",
- "kibanaOverview.noDataConfig.solutionName": "分析",
- "kibanaOverview.noDataConfig.title": "統合の追加",
"lists.exceptions.doesNotExistOperatorLabel": "存在しない",
"lists.exceptions.existsOperatorLabel": "存在する",
"lists.exceptions.isInListOperatorLabel": "リストにある",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 38ea4b6526b23..ab53a184d44e6 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -5164,10 +5164,6 @@
"kibanaOverview.manageData.sectionTitle": "管理您的数据",
"kibanaOverview.more.title": "Elastic 让您事半功倍",
"kibanaOverview.news.title": "最新动态",
- "kibanaOverview.noDataConfig.description": "使用 Elastic 代理或 Beats 收集数据并构建分析解决方案。",
- "kibanaOverview.noDataConfig.pageTitle": "欢迎使用分析!",
- "kibanaOverview.noDataConfig.solutionName": "分析",
- "kibanaOverview.noDataConfig.title": "添加集成",
"lists.exceptions.doesNotExistOperatorLabel": "不存在",
"lists.exceptions.existsOperatorLabel": "存在",
"lists.exceptions.isInListOperatorLabel": "在列表中",
From c7262544b54422f8602c57e68a365554cd83f846 Mon Sep 17 00:00:00 2001
From: Christos Nasikas
Date: Fri, 17 Jun 2022 12:05:14 +0300
Subject: [PATCH 12/30] [ResponseOps] Sub actions framework: Create connector
forms (#131718)
* Create useSubAction hook
* Create SimpleConnectorForm
* Init create connector form
* Show action types
* Add banner to flyout
* Return back to action types
* Create useCreateConnector
* Use useCreateConnector
* POC: IBM resilient
* Refinements
* Show flyout header
* Support password text field
* Validate url
* Change connector types
* Jira
* Teams
* Pagerduty
* ITOM
* Server log
* Slack
* ES index draft
* ES index fix
* Password field
* SN
* Fix ES index
* Webhoo draft
* Webhook fixes
* Index fixes
* Email connector
* Fix disabled
* Xmatters connector
* Swimlane connector
* Swimlane fixes
* Improve form
* Presubmit validator
* SN connector
* Hidden field
* Rename
* fixes
* Edit flyout
* Test connector
* Improve structure
* Improve edit flyout structure
* Improvements
* Remove old files and fix tabs
* Modal
* Common label for encrypted fields
* First tests
* More tests
* Fix types
* Fix i18n
* Fixes
* Dynamic encrypted values callout
* Add tests
* Add flyout tests
* Fix cypress test
* Add README
* Fix tests
* Webhook serializer & deserializer
* Validate email
* Fix itom
* Fix validators
* Fix types
* Add more tests
* Add more tests
* Add more tests
* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'
* Pass connector's services with context
* Clean up translations
* Pass readOnly to all fields
* Fix bug with connectors provider
* PR feedback
* Fix i18n
* Fix types
* Fix subaction hook
* Add hook tests
* Fix bug with port
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
---
.../components/configure_cases/index.test.tsx | 7 +-
.../components/configure_cases/index.tsx | 12 +-
.../public/alerts/alert_form.test.tsx | 4 -
.../cypress/screens/configure_cases.ts | 2 +-
.../rules/rule_actions_field/index.tsx | 38 +-
.../journeys/alerts/default_email_settings.ts | 2 +-
.../settings/add_connector_flyout.tsx | 3 +-
.../translations/translations/fr-FR.json | 87 ---
.../translations/translations/ja-JP.json | 87 ---
.../translations/translations/zh-CN.json | 87 ---
x-pack/plugins/triggers_actions_ui/README.md | 495 ++++++-------
.../application/action_type_registry.mock.ts | 1 -
.../public/application/app.tsx | 45 +-
.../builtin_action_types/email/email.test.tsx | 296 --------
.../builtin_action_types/email/email.tsx | 88 +--
.../email/email_connector.test.tsx | 673 ++++++++++++++----
.../email/email_connector.tsx | 529 +++++++-------
.../email/exchange_form.test.tsx | 129 +++-
.../email/exchange_form.tsx | 216 ++----
.../email/translations.ts | 108 ++-
.../email/use_email_config.test.ts | 105 ++-
.../email/use_email_config.ts | 117 +--
.../es_index/es_index.test.tsx | 53 --
.../es_index/es_index.tsx | 22 +-
.../es_index/es_index_connector.test.tsx | 333 ++++++---
.../es_index/es_index_connector.tsx | 370 +++++-----
.../es_index/translations.ts | 34 +-
.../builtin_action_types/jira/jira.test.tsx | 63 --
.../builtin_action_types/jira/jira.tsx | 55 +-
.../jira/jira_connectors.test.tsx | 259 +++----
.../jira/jira_connectors.tsx | 194 +----
.../builtin_action_types/jira/translations.ts | 85 ---
.../pagerduty/pagerduty.test.tsx | 55 --
.../pagerduty/pagerduty.tsx | 23 +-
.../pagerduty/pagerduty_connectors.test.tsx | 258 ++++---
.../pagerduty/pagerduty_connectors.tsx | 159 ++---
.../pagerduty/translations.ts | 21 +
.../resilient/resilient.test.tsx | 63 --
.../resilient/resilient.tsx | 63 +-
.../resilient/resilient_connectors.test.tsx | 256 +++----
.../resilient/resilient_connectors.tsx | 198 +-----
.../resilient/translations.ts | 96 +--
.../server_log/server_log.test.tsx | 25 +-
.../server_log/server_log.tsx | 9 +-
.../application_required_callout.tsx | 2 +-
.../auth_types/credentials_auth.tsx | 122 +---
.../servicenow/auth_types/oauth.tsx | 309 +++-----
.../servicenow/credentials.test.tsx | 55 +-
.../servicenow/credentials.tsx | 88 +--
.../servicenow/credentials_api_url.tsx | 78 +-
.../servicenow/helpers.ts | 5 +-
.../servicenow/servicenow.test.tsx | 191 -----
.../servicenow/servicenow.tsx | 89 +--
.../servicenow/servicenow_connectors.test.tsx | 603 ++++++++--------
.../servicenow/servicenow_connectors.tsx | 221 +++---
.../servicenow_connectors_no_app.test.tsx | 162 +++++
.../servicenow_connectors_no_app.tsx | 30 +-
.../servicenow/sn_store_button.tsx | 6 +-
.../servicenow/translations.ts | 61 +-
.../servicenow/update_connector.test.tsx | 210 ++----
.../servicenow/update_connector.tsx | 272 ++++---
.../servicenow/use_get_app_info.tsx | 11 +-
.../builtin_action_types/slack/slack.test.tsx | 93 ---
.../builtin_action_types/slack/slack.tsx | 28 +-
.../slack/slack_connectors.test.tsx | 177 +++--
.../slack/slack_connectors.tsx | 102 ++-
.../slack/translations.ts | 19 +-
.../builtin_action_types/swimlane/helpers.ts | 32 +-
.../swimlane/steps/swimlane_connection.tsx | 190 ++---
.../swimlane/steps/swimlane_fields.tsx | 406 +++++------
.../swimlane/swimlane.test.tsx | 147 ----
.../swimlane/swimlane.tsx | 65 +-
.../swimlane/swimlane_connectors.test.tsx | 384 ++++++----
.../swimlane/swimlane_connectors.tsx | 134 ++--
.../swimlane/translations.ts | 64 --
.../swimlane/use_get_application.test.tsx | 52 +-
.../swimlane/use_get_application.tsx | 83 ++-
.../builtin_action_types/teams/teams.test.tsx | 93 ---
.../builtin_action_types/teams/teams.tsx | 28 +-
.../teams/teams_connectors.test.tsx | 176 ++---
.../teams/teams_connectors.tsx | 102 ++-
.../teams/translations.ts | 13 +-
.../builtin_action_types/test_utils.tsx | 127 ++++
.../webhook/translations.ts | 84 ++-
.../webhook/webhook.test.tsx | 135 ----
.../builtin_action_types/webhook/webhook.tsx | 55 +-
.../webhook/webhook_connectors.test.tsx | 321 ++++++---
.../webhook/webhook_connectors.tsx | 544 +++++---------
.../xmatters/translations.ts | 64 +-
.../xmatters/xmatters.test.tsx | 133 ----
.../xmatters/xmatters.tsx | 56 +-
.../xmatters/xmatters_connectors.test.tsx | 343 +++++----
.../xmatters/xmatters_connectors.tsx | 332 ++++-----
.../components/button_group_field.tsx | 80 +++
.../get_encrypted_field_notify_label.test.tsx | 70 --
.../get_encrypted_field_notify_label.tsx | 68 --
.../application/components/hidden_field.tsx | 25 +
.../application/components/password_field.tsx | 86 +++
.../components/simple_connector_form.test.tsx | 149 ++++
.../components/simple_connector_form.tsx | 159 +++++
.../application/context/connector_context.tsx | 24 +
.../context/use_connector_context.ts | 21 +
.../hooks/use_create_connector.test.tsx | 49 ++
.../hooks/use_create_connector.tsx | 86 +++
.../application/hooks/use_edit_connector.tsx | 85 +++
.../hooks/use_execute_connector.test.tsx | 45 ++
.../hooks/use_execute_connector.tsx | 68 ++
.../application/hooks/use_sub_action.test.tsx | 75 ++
.../application/hooks/use_sub_action.tsx | 144 ++++
.../rules_list_sandbox.tsx | 9 +-
.../lib/action_connector_api/create.test.ts | 17 +-
.../lib/action_connector_api/create.ts | 8 +-
.../action_connector_form.test.tsx | 61 --
.../action_connector_form.tsx | 254 -------
.../action_form.test.tsx | 23 +-
.../action_type_form.test.tsx | 14 +-
.../action_type_menu.test.tsx | 11 +-
.../connector_add_flyout.test.tsx | 210 ------
.../connector_add_flyout.tsx | 416 -----------
.../connector_add_modal.test.tsx | 5 +-
.../connector_add_modal.tsx | 248 +++----
.../connector_edit_flyout.scss | 3 -
.../connector_edit_flyout.test.tsx | 128 ----
.../connector_edit_flyout.tsx | 433 -----------
.../connector_error_mock.tsx | 45 ++
.../connector_form.test.tsx | 150 ++++
.../action_connector_form/connector_form.tsx | 149 ++++
.../connector_form_fields.test.tsx | 73 ++
.../connector_form_fields.tsx | 75 ++
.../connector_form_fields_global.test.tsx | 74 ++
.../connector_form_fields_global.tsx | 61 ++
.../action_connector_form/connector_mock.tsx | 54 ++
.../connector_reducer.test.ts | 103 ---
.../connector_reducer.ts | 135 ----
.../connectors_selection.test.tsx | 5 +-
.../create_connector_flyout/foooter.tsx | 106 +++
.../create_connector_flyout/header.tsx | 67 ++
.../create_connector_flyout/index.test.tsx | 544 ++++++++++++++
.../create_connector_flyout/index.tsx | 199 ++++++
.../upgrade_license_callout.tsx | 69 ++
.../edit_connector_flyout/foooter.tsx | 92 +++
.../edit_connector_flyout/header.tsx | 118 +++
.../edit_connector_flyout/index.test.tsx | 630 ++++++++++++++++
.../edit_connector_flyout/index.tsx | 294 ++++++++
.../encrypted_fields_callout.test.tsx | 123 ++++
.../encrypted_fields_callout.tsx | 134 ++++
.../sections/action_connector_form/index.ts | 13 +-
.../test_connector_form.test.tsx | 25 +-
.../test_connector_form.tsx | 26 +-
.../sections/action_connector_form/types.ts | 30 +
.../actions_connectors_list.test.tsx | 19 +-
.../components/actions_connectors_list.tsx | 32 +-
.../public/application/sections/index.tsx | 10 +-
.../sections/rule_form/rule_add.test.tsx | 4 -
.../sections/rule_form/rule_edit.test.tsx | 10 +-
.../sections/rule_form/rule_form.test.tsx | 11 -
.../public/application/type_registry.test.ts | 6 -
.../public/common/get_action_form.tsx | 22 +
.../public/common/get_add_alert_flyout.tsx | 13 +-
.../common/get_add_connector_flyout.tsx | 16 +-
.../public/common/get_edit_alert_flyout.tsx | 13 +-
.../common/get_edit_connector_flyout.tsx | 16 +-
.../public/common/get_rules_list.tsx | 10 +-
.../common/lib/kibana/kibana_react.mock.ts | 1 +
.../triggers_actions_ui/public/index.ts | 4 +-
.../triggers_actions_ui/public/mocks.ts | 21 +-
.../triggers_actions_ui/public/plugin.ts | 45 +-
.../triggers_actions_ui/public/types.ts | 62 +-
.../alert_create_flyout.ts | 4 +-
.../apps/triggers_actions_ui/connectors.ts | 22 +-
170 files changed, 9863 insertions(+), 9868 deletions(-)
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.test.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/test_utils.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/button_group_field.tsx
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.test.tsx
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/hidden_field.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/password_field.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.test.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/context/connector_context.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/context/use_connector_context.ts
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/hooks/use_create_connector.test.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/hooks/use_create_connector.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/hooks/use_edit_connector.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/hooks/use_execute_connector.test.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/hooks/use_execute_connector.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.test.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.tsx
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.scss
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_error_mock.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form.test.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form_fields.test.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form_fields.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form_fields_global.test.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_form_fields_global.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_mock.tsx
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.test.ts
delete mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.ts
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/foooter.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/header.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/index.test.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/index.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/upgrade_license_callout.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/foooter.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/header.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.test.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/edit_connector_flyout/index.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx
create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/types.ts
create mode 100644 x-pack/plugins/triggers_actions_ui/public/common/get_action_form.tsx
diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx
index 888ac6576ec23..890b8683ae6a5 100644
--- a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx
+++ b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx
@@ -553,7 +553,7 @@ describe('ConfigureCases', () => {
expect(wrapper.find('[data-test-subj="add-connector-flyout"]').exists()).toBe(true);
expect(getAddConnectorFlyoutMock).toHaveBeenCalledWith(
expect.objectContaining({
- actionTypes: [
+ supportedActionTypes: [
expect.objectContaining({
id: '.servicenow',
}),
@@ -575,9 +575,6 @@ describe('ConfigureCases', () => {
test('it show the edit flyout when pressing the update connector button', async () => {
const actionType = actionTypeRegistryMock.createMockActionTypeModel({
id: '.resilient',
- validateConnector: () => {
- return Promise.resolve({});
- },
validateParams: () => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
@@ -603,7 +600,7 @@ describe('ConfigureCases', () => {
wrapper.update();
expect(wrapper.find('[data-test-subj="edit-connector-flyout"]').exists()).toBe(true);
expect(getEditConnectorFlyoutMock).toHaveBeenCalledWith(
- expect.objectContaining({ initialConnector: connectors[1] })
+ expect.objectContaining({ connector: connectors[1] })
);
});
diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.tsx
index 16773306d6bef..2c04f9f0aa7da 100644
--- a/x-pack/plugins/cases/public/components/configure_cases/index.tsx
+++ b/x-pack/plugins/cases/public/components/configure_cases/index.tsx
@@ -90,7 +90,7 @@ export const ConfigureCases: React.FC = React.memo(() => {
[actionTypes]
);
- const onConnectorUpdate = useCallback(async () => {
+ const onConnectorUpdated = useCallback(async () => {
refetchConnectors();
refetchActionTypes();
refetchCaseConfigure();
@@ -168,10 +168,9 @@ export const ConfigureCases: React.FC = React.memo(() => {
() =>
addFlyoutVisible
? triggersActionsUi.getAddConnectorFlyout({
- consumer: 'case',
onClose: onCloseAddFlyout,
- actionTypes: supportedActionTypes,
- reloadConnectors: onConnectorUpdate,
+ supportedActionTypes,
+ onConnectorCreated: onConnectorUpdated,
})
: null,
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -182,10 +181,9 @@ export const ConfigureCases: React.FC = React.memo(() => {
() =>
editedConnectorItem && editFlyoutVisible
? triggersActionsUi.getEditConnectorFlyout({
- initialConnector: editedConnectorItem,
- consumer: 'case',
+ connector: editedConnectorItem,
onClose: onCloseEditFlyout,
- reloadConnectors: onConnectorUpdate,
+ onConnectorUpdated,
})
: null,
// eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx b/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx
index e8c6d57fcba19..14dbac775eaf4 100644
--- a/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx
+++ b/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx
@@ -19,7 +19,6 @@ import { ruleTypeRegistryMock } from '@kbn/triggers-actions-ui-plugin/public/app
import {
ValidationResult,
Rule,
- ConnectorValidationResult,
GenericValidationResult,
RuleTypeModel,
} from '@kbn/triggers-actions-ui-plugin/public/types';
@@ -91,9 +90,6 @@ describe('alert_form', () => {
id: 'alert-action-type',
iconClass: '',
selectMessage: '',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
validateParams: (): Promise> => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
diff --git a/x-pack/plugins/security_solution/cypress/screens/configure_cases.ts b/x-pack/plugins/security_solution/cypress/screens/configure_cases.ts
index c9ed5299c0336..3e83a569d04ec 100644
--- a/x-pack/plugins/security_solution/cypress/screens/configure_cases.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/configure_cases.ts
@@ -18,7 +18,7 @@ export const CONNECTORS_DROPDOWN = '[data-test-subj="dropdown-connectors"]';
export const PASSWORD = '[data-test-subj="connector-servicenow-password-form-input"]';
-export const SAVE_BTN = '[data-test-subj="saveNewActionButton"]';
+export const SAVE_BTN = '[data-test-subj="create-connector-flyout-save-btn"]';
export const SERVICE_NOW_CONNECTOR_CARD = '[data-test-subj=".servicenow-card"]';
diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx
index 01e978ca2e61c..5798081833887 100644
--- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx
@@ -13,7 +13,6 @@ import ReactMarkdown from 'react-markdown';
import styled from 'styled-components';
import {
- ActionForm,
ActionType,
loadActionTypes,
ActionVariables,
@@ -84,7 +83,7 @@ export const RuleActionsField: React.FC = ({
const { isSubmitted, isSubmitting, isValid } = form;
const {
http,
- triggersActionsUi: { actionTypeRegistry },
+ triggersActionsUi: { getActionForm },
} = useKibana().services;
const actions: RuleAction[] = useMemo(
@@ -135,6 +134,29 @@ export const RuleActionsField: React.FC = ({
[field.setValue, actions]
);
+ const actionForm = useMemo(
+ () =>
+ getActionForm({
+ actions,
+ messageVariables,
+ defaultActionGroupId: DEFAULT_ACTION_GROUP_ID,
+ setActionIdByIndex,
+ setActions: setAlertActionsProperty,
+ setActionParamsProperty,
+ actionTypes: supportedActionTypes,
+ defaultActionMessage: DEFAULT_ACTION_MESSAGE,
+ }),
+ [
+ actions,
+ getActionForm,
+ messageVariables,
+ setActionIdByIndex,
+ setActionParamsProperty,
+ setAlertActionsProperty,
+ supportedActionTypes,
+ ]
+ );
+
useEffect(() => {
(async function () {
const actionTypes = convertArrayToCamelCase(await loadActionTypes({ http })) as ActionType[];
@@ -168,17 +190,7 @@ export const RuleActionsField: React.FC = ({
>
) : null}
-
+ {actionForm}
);
};
diff --git a/x-pack/plugins/synthetics/e2e/journeys/alerts/default_email_settings.ts b/x-pack/plugins/synthetics/e2e/journeys/alerts/default_email_settings.ts
index a8be2f2129b32..6b0d9e541d4d3 100644
--- a/x-pack/plugins/synthetics/e2e/journeys/alerts/default_email_settings.ts
+++ b/x-pack/plugins/synthetics/e2e/journeys/alerts/default_email_settings.ts
@@ -62,7 +62,7 @@ journey('DefaultEmailSettings', async ({ page, params }) => {
await page.fill(byTestId('emailHostInput'), 'test');
await page.fill(byTestId('emailPortInput'), '1025');
await page.click('text=Require authentication for this server');
- await page.click(byTestId('saveNewActionButton'));
+ await page.click(byTestId('create-connector-flyout-save-btn'));
});
step('Select email connector', async () => {
diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/add_connector_flyout.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/add_connector_flyout.tsx
index 18f9e0b41d3db..f3cf70e9fe1ba 100644
--- a/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/add_connector_flyout.tsx
+++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/settings/add_connector_flyout.tsx
@@ -56,13 +56,12 @@ export const AddConnectorFlyout = ({ focusInput, isDisabled }: Props) => {
const ConnectorAddFlyout = useMemo(
() =>
getAddConnectorFlyout({
- consumer: 'uptime',
onClose: () => {
dispatch(getConnectorsAction.get());
setAddFlyoutVisibility(false);
focusInput();
},
- actionTypes: (actionTypes ?? []).filter((actionType) =>
+ supportedActionTypes: (actionTypes ?? []).filter((actionType) =>
ALLOWED_ACTION_TYPES.includes(actionType.id as ActionTypeId)
),
}),
diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json
index 13edd89027a06..8ece3d7c60386 100644
--- a/x-pack/plugins/translations/translations/fr-FR.json
+++ b/x-pack/plugins/translations/translations/fr-FR.json
@@ -28448,7 +28448,6 @@
"xpack.triggersActionsUI.actionVariables.ruleTagsLabel": "Balises de la règle.",
"xpack.triggersActionsUI.actionVariables.ruleTypeLabel": "Type de règle.",
"xpack.triggersActionsUI.appName": "Règles et connecteurs",
- "xpack.triggersActionsUI.cases.configureCases.mappingFieldSummary": "Résumé",
"xpack.triggersActionsUI.checkActionTypeEnabled.actionTypeDisabledByConfigMessage": "Ce connecteur est désactivé par la configuration de Kibana.",
"xpack.triggersActionsUI.checkActionTypeEnabled.actionTypeDisabledByLicenseMessage": "Ce connecteur requiert une licence {minimumLicenseRequired}.",
"xpack.triggersActionsUI.checkRuleTypeEnabled.ruleTypeDisabledByLicenseMessage": "Ce type de règle requiert une licence {minimumLicenseRequired}.",
@@ -28486,29 +28485,22 @@
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.gmailServerTypeLabel": "Gmail",
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.otherServerTypeLabel": "Autre",
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.outlookServerTypeLabel": "Outlook",
- "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.reenterClientSecretLabel": "L'identifiant client secret est chiffré. Veuillez entrer à nouveau une valeur pour ce champ.",
- "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.reenterValuesLabel": "Le nom d'utilisateur et le mot de passe sont chiffrés. Veuillez entrer à nouveau les valeurs de ces champs.",
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText": "Envoyez un e-mail à partir de votre serveur.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideSuffix": "L'index d'historique d'alertes doit contenir un suffixe valide.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideValue": "L'index d'historique d'alertes doit commencer par \"{alertHistoryPrefix}\".",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.formatFromText": "L'expéditeur n'est pas une adresse e-mail valide.",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthPasswordText": "Le mot de passe est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthUserNameText": "Le nom d'utilisateur est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredClientIdText": "L'ID client est requis.",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredClientSecretText": "L'identifiant client secret est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredDocumentJson": "Le document est requis et doit être un objet JSON valide.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredEntryText": "Aucune entrée À, Cc ou Cci. Au moins une entrée est requise.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredFromText": "L'expéditeur est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredHostText": "L'hôte est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredMessageText": "Le message est requis.",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPasswordText": "Le mot de passe est requis lorsque le nom d'utilisateur est utilisé.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPortText": "Le port est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServerLogMessageText": "Le message est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServiceText": "Le service est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSlackMessageText": "Le message est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSubjectText": "Le sujet est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredTenantIdText": "L'ID locataire est requis.",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredUserText": "Le nom d'utilisateur est requis lorsque le mot de passe est utilisé.",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookBodyText": "Le corps est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.actionTypeTitle": "Données d'index",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.chooseLabel": "Choisir…",
@@ -28517,7 +28509,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.definedateFieldTooltip": "Définissez ce champ temporel sur l'heure à laquelle le document a été indexé.",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.defineTimeFieldLabel": "Définissez l'heure pour chaque document",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.documentsFieldLabel": "Document à indexer",
- "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.requiredIndexText": "L'index est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.executionTimeFieldLabel": "Champ temporel",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.howToBroadenSearchQueryDescription": "Utilisez le caractère * pour élargir votre recherche.",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indexDocumentHelpLabel": "Exemple de document d'index.",
@@ -28533,25 +28524,14 @@
"xpack.triggersActionsUI.components.builtinActionTypes.jira.actionTypeTitle": "Jira",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.apiTokenTextFieldLabel": "Token d'API",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.apiUrlTextFieldLabel": "URL",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.authenticationLabel": "Authentification",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.commentsTextAreaFieldLabel": "Commentaires supplémentaires",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.descriptionTextAreaFieldLabel": "Description",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.emailTextFieldLabel": "Adresse e-mail",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.impactSelectFieldLabel": "Étiquettes",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.invalidApiUrlTextField": "L'URL n'est pas valide.",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.labelsSpacesErrorMessage": "Les étiquettes ne peuvent pas contenir d'espaces.",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.mappingFieldComments": "Commentaires",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.mappingFieldDescription": "Description",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.parentIssueSearchLabel": "Problème lié au parent",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.projectKey": "Clé de projet",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.reenterValuesLabel": "Les informations d'authentification sont chiffrées. Veuillez entrer à nouveau les valeurs de ces champs.",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredApiTokenTextField": "Le token d'API est requis",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredApiUrlTextField": "L'URL est requise.",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredDescriptionTextField": "La description est requise.",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredEmailTextField": "L'adresse e-mail est requise",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredProjectKeyTextField": "La clé de projet est requise",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredSummaryTextField": "Le résumé est requis.",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requireHttpsApiUrlTextField": "L'URL doit commencer par https://.",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxAriaLabel": "Taper pour rechercher",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxPlaceholder": "Taper pour rechercher",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesLoading": "Chargement...",
@@ -28563,7 +28543,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssuesMessage": "Impossible d'obtenir les problèmes",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueTypesMessage": "Impossible d'obtenir les types d'erreurs",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.urgencySelectFieldLabel": "Type d'erreur",
- "xpack.triggersActionsUI.components.builtinActionTypes.missingSecretsValuesLabel": "Les informations sensibles ne sont pas importées. Veuillez entrer {encryptedFieldsLength, plural, one {la valeur} other {les valeurs}} pour {encryptedFieldsLength, plural, one {le champ suivant} other {les champs suivants}}.",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.actionTypeTitle": "Envoyer à PagerDuty",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.apiUrlTextFieldLabel": "URL de l'API (facultative)",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.classFieldLabel": "Classe (facultative)",
@@ -28579,7 +28558,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectResolveOptionLabel": "Résoudre",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectTriggerOptionLabel": "Déclencher",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.groupTextFieldLabel": "Regrouper (facultatif)",
- "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.reenterValueLabel": "Cette clé est chiffrée. Veuillez entrer à nouveau une valeur pour ce champ.",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyNameHelpLabel": "Configurer un compte PagerDuty",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyTextFieldLabel": "Clé d'intégration",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText": "Envoyez un événement dans PagerDuty.",
@@ -28591,29 +28569,15 @@
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.sourceTextFieldLabel": "Source (facultative)",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.summaryFieldLabel": "Résumé",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.timestampTextFieldLabel": "Horodatage (facultatif)",
- "xpack.triggersActionsUI.components.builtinActionTypes.rememberValueLabel": "Mémorisez {encryptedFieldsLength, plural, one {cette valeur} other {ces valeurs}}. Vous devrez {encryptedFieldsLength, plural, one {l'entrer} other {les entrer}} à nouveau chaque fois que vous modifierez le connecteur.",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.actionTypeTitle": "Résilient",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKey": "Clé d'API",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeyId": "ID",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeySecret": "Secret",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiUrlTextFieldLabel": "URL",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.commentsTextAreaFieldLabel": "Commentaires supplémentaires",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.descriptionTextAreaFieldLabel": "Description",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.invalidApiUrlTextField": "L'URL n'est pas valide.",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldComments": "Commentaires",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldDescription": "Description",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldShortDescription": "Nom",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.nameFieldLabel": "Nom (requis)",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.orgId": "ID d'organisation",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.reenterValuesLabel": "L'ID et le secret sont chiffrés. Veuillez entrer à nouveau les valeurs de ces champs.",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.rememberValuesLabel": "Mémorisez ces valeurs. Vous devrez les entrer à nouveau chaque fois que vous modifierez le connecteur.",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiKeyIdTextField": "L'ID est requis",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiKeySecretTextField": "Le secret est requis",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiUrlTextField": "L'URL est requise.",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredDescriptionTextField": "La description est requise.",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredNameTextField": "Le nom est requis.",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredOrgIdTextField": "L'ID d'organisation est requis",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requireHttpsApiUrlTextField": "L'URL doit commencer par https://.",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.selectMessageText": "Créez un incident dans IBM Resilient.",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.severity": "Sévérité",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetIncidentTypesMessage": "Impossible d'obtenir les types d'incidents",
@@ -28624,7 +28588,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.logMessageFieldLabel": "Message",
"xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.selectMessageText": "Ajouter un message au log Kibana.",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiInfoError": "Statut reçu : {status} lors de la tentative d'obtention d'informations sur l'application",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiUrlHelpText": "Spécifiez l'URL complète.",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiUrlTextFieldLabel": "URL d'instance ServiceNow",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.appInstallationInfo": "{update} {create} ",
"xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout": "Application Elastic ServiceNow non installée",
@@ -28643,20 +28606,14 @@
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.descriptionTextAreaFieldLabel": "Description",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.eventClassTextAreaFieldLabel": "Instance source",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.impactSelectFieldLabel": "Impact",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.install": "installer",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.installationCalloutTitle": "Pour utiliser ce connecteur, installez d'abord l'application Elastic à partir de l'app store ServiceNow.",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.invalidApiUrlTextField": "L'URL n'est pas valide.",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.messageKeyTextAreaFieldLabel": "Clé de message",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.metricNameTextAreaFieldLabel": "Nom de l'indicateur",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.nodeTextAreaFieldLabel": "Nœud",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.passwordTextFieldLabel": "Mot de passe",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.prioritySelectFieldLabel": "Priorité",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.reenterValuesLabel": "Vous devrez vous authentifier chaque fois que vous modifierez le connecteur.",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredApiUrlTextField": "L'URL est requise.",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredPasswordTextField": "Le mot de passe est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredSeverityTextField": "La sévérité est requise.",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUsernameTextField": "Le nom d'utilisateur est requis.",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requireHttpsApiUrlTextField": "L'URL doit commencer par https://.",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.resourceTextAreaFieldLabel": "Ressource",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.setupDevInstance": "configurer une instance de développeur",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.severityRequiredSelectFieldLabel": "Sévérité (requise)",
@@ -28693,10 +28650,7 @@
"xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIRAction.correlationIDHelpLabel": "Identificateur pour les incidents de mise à jour",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.actionTypeTitle": "Envoyer vers Slack",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.invalidWebhookUrlText": "L'URL de webhook n'est pas valide.",
- "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.requiredWebhookUrlText": "L'URL de webhook est requise.",
- "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.requireHttpsWebhookUrlText": "L'URL de webhook doit commencer par https://.",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.messageTextAreaFieldLabel": "Message",
- "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.reenterValueLabel": "Cette URL est chiffrée. Veuillez entrer à nouveau une valeur pour ce champ.",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText": "Envoyez un message à un canal ou à un utilisateur Slack.",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlHelpLabel": "Créer une URL de webhook Slack",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel": "URL de webhook",
@@ -28704,7 +28658,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationMessage": "Impossible d'obtenir l'application avec l'ID {id}",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.actionTypeTitle": "Créer l'enregistrement Swimlane",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.alertIdFieldLabel": "ID de l'alerte",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.alertSourceFieldLabel": "Source de l'alerte",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenNameHelpLabel": "Fournir un token d'API Swimlane",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenTextFieldLabel": "Token d'API",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiUrlTextFieldLabel": "URL d'API",
@@ -28718,71 +28671,47 @@
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningDesc": "Ce connecteur ne peut pas être sélectionné, car il ne possède pas les mappings de champs d'alerte requis. Vous pouvez modifier ce connecteur pour ajouter les mappings de champs requis ou sélectionner un connecteur de type Alertes.",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningTitle": "Ce connecteur ne possède pas de mappings de champs",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAlertID": "L'ID d'alerte est requis.",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAlertSource": "La source de l'alerte est requise.",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredApiTokenText": "Un token d'API est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAppIdText": "Un ID d'application est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseID": "L'ID de cas est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseName": "Le nom de cas est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredComments": "Les commentaires sont requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredDescription": "La description est requise.",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredFieldMappingsText": "Les mappings de champs sont requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredRuleName": "Le nom de règle est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredSeverity": "La sévérité est requise.",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.invalidApiUrlTextField": "L'URL n'est pas valide.",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingDescriptionTextFieldLabel": "Utilisé pour spécifier les noms de champs dans l'application Swimlane",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingFieldRequired": "Le mapping de champs est requis.",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingTitleTextFieldLabel": "Configurer les mappings de champs",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStep": "Suivant",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStepHelpText": "Si les mappings de champs ne sont pas configurés, le type de connecteur Swimlane sera défini sur Tous.",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.prevStep": "Précédent",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.reenterValueLabel": "Cette clé est chiffrée. Veuillez entrer à nouveau une valeur pour ce champ.",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.rememberValueLabel": "Mémorisez cette valeur. Vous devrez l'entrer à nouveau chaque fois que vous modifierez le connecteur.",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.requiredApiUrlTextField": "L'URL est requise.",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.retrieveConfigurationLabel": "Configurer les champs",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.ruleNameFieldLabel": "Nom de règle",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.selectMessageText": "Créer un enregistrement dans Swimlane",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.severityFieldLabel": "Sévérité",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.actionTypeTitle": "Envoyer un message à un canal Microsoft Teams.",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.invalidWebhookUrlText": "L'URL de webhook n'est pas valide.",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requiredMessageText": "Le message est requis.",
- "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requiredWebhookUrlText": "L'URL de webhook est requise.",
- "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requireHttpsWebhookUrlText": "L'URL de webhook doit commencer par https://.",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.messageTextAreaFieldLabel": "Message",
- "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.reenterValueLabel": "Cette URL est chiffrée. Veuillez entrer à nouveau une valeur pour ce champ.",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.selectMessageText": "Envoyer un message à un canal Microsoft Teams.",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlHelpLabel": "Créer une URL de webhook Microsoft Teams",
- "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlTextFieldLabel": "URL de webhook",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.actionTypeTitle": "Données de webhook",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.addHeader": "Ajouter un en-tête",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.addHeaderButton": "Ajouter",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.authenticationLabel": "Authentification",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyCodeEditorAriaLabel": "Éditeur de code",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyFieldLabel": "Corps",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.deleteHeaderButton": "Supprimer",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.invalidUrlTextField": "L'URL n'est pas valide.",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.requiredUrlText": "L'URL est requise.",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.hasAuthSwitchLabel": "Demander une authentification pour ce webhook",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.httpHeadersTitle": "En-têtes utilisés",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.keyTextFieldLabel": "Clé",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.methodTextFieldLabel": "Méthode",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.passwordTextFieldLabel": "Mot de passe",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.reenterValuesLabel": "Le nom d'utilisateur et le mot de passe sont chiffrés. Veuillez entrer à nouveau les valeurs de ces champs.",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText": "Envoyer une requête à un service Web.",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.urlTextFieldLabel": "URL",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.userTextFieldLabel": "Nom d'utilisateur",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.valueTextFieldLabel": "Valeur",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.viewHeadersSwitch": "Ajouter un en-tête HTTP",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.actionTypeTitle": "Données xMatters",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.authenticationLabel": "Authentification",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.basicAuthLabel": "Authentification de base",
- "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.connectorSettingsFieldLabel": "URL d'initiation",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.connectorSettingsLabel": "Sélectionnez la méthode d'authentification utilisée pour la configuration du déclencheur xMatters.",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.invalidUrlTextField": "L'URL n'est pas valide.",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.requiredUrlText": "L'URL est requise.",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.initiationUrlHelpText": "Spécifiez l'URL xMatters complète.",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.passwordTextFieldLabel": "Mot de passe",
- "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.reenterBasicAuthValuesLabel": "Le nom d'utilisateur et le mot de passe sont chiffrés. Veuillez entrer à nouveau les valeurs de ces champs.",
- "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.reenterUrlAuthValuesLabel": "L'URL est chiffrée. Veuillez entrer à nouveau des valeurs pour ce champ.",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.selectMessageText": "Déclenchez un workflow xMatters.",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severity": "Sévérité",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectCriticalOptionLabel": "Critique",
@@ -28855,10 +28784,6 @@
"xpack.triggersActionsUI.sections.actionConnectorAdd.upgradeYourPlanBannerLinkTitle": "Plans d'abonnement",
"xpack.triggersActionsUI.sections.actionConnectorAdd.upgradeYourPlanBannerMessage": "Mettez à niveau votre licence ou démarrez un essai gratuit de 30 jours pour obtenir un accès immédiat à tous les connecteurs tiers.",
"xpack.triggersActionsUI.sections.actionConnectorAdd.upgradeYourPlanBannerTitle": "Mettre à niveau votre licence pour accéder à tous les connecteurs",
- "xpack.triggersActionsUI.sections.actionConnectorForm.actionNameLabel": "Nom du connecteur",
- "xpack.triggersActionsUI.sections.actionConnectorForm.actions.actionConfigurationWarningHelpLinkText": "En savoir plus.",
- "xpack.triggersActionsUI.sections.actionConnectorForm.actions.connectorTypeConfigurationWarningDescriptionText": "Pour créer ce connecteur, vous devez configurer au moins un compte {connectorType}. {docLink}",
- "xpack.triggersActionsUI.sections.actionConnectorForm.actions.connectorTypeConfigurationWarningTitleText": "Type de connecteur non enregistré",
"xpack.triggersActionsUI.sections.actionConnectorForm.connectorSettingsLabel": "Paramètres du connecteur",
"xpack.triggersActionsUI.sections.actionConnectorForm.error.requiredNameText": "Le nom est requis.",
"xpack.triggersActionsUI.sections.actionConnectorForm.loadingConnectorSettingsDescription": "Chargement des paramètres du connecteur…",
@@ -28906,25 +28831,14 @@
"xpack.triggersActionsUI.sections.actionTypeForm.addNewActionConnectorActionGroup.display": "{actionGroupName} (non pris en charge actuellement)",
"xpack.triggersActionsUI.sections.actionTypeForm.addNewConnectorEmptyButton": "Ajouter un connecteur",
"xpack.triggersActionsUI.sections.actionTypeForm.existingAlertActionTypeEditTitle": "{actionConnectorName}",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthPasswordText": "Le mot de passe est requis.",
"xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthUserNameText": "Le nom d'utilisateur est requis.",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredHeaderKeyText": "La clé est requise.",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredHeaderValueText": "La valeur est requise.",
"xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText": "La méthode est requise.",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredPasswordText": "Le mot de passe est requis lorsque le nom d'utilisateur est utilisé.",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredUserText": "Le nom d'utilisateur est requis lorsque le mot de passe est utilisé.",
- "xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredAuthPasswordText": "Le mot de passe est requis.",
- "xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredAuthUserNameText": "Le nom d'utilisateur est requis.",
- "xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredPasswordText": "Le mot de passe est requis lorsque le nom d'utilisateur est utilisé.",
- "xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredUserText": "Le nom d'utilisateur est requis lorsque le mot de passe est utilisé.",
"xpack.triggersActionsUI.sections.addConnectorForm.flyoutTitle": "Connecteur {actionTypeName}",
"xpack.triggersActionsUI.sections.addConnectorForm.selectConnectorFlyoutTitle": "Sélectionner un connecteur",
- "xpack.triggersActionsUI.sections.addConnectorForm.updateErrorNotificationText": "Impossible de créer un connecteur.",
"xpack.triggersActionsUI.sections.addConnectorForm.updateSuccessNotificationText": "Création de \"{connectorName}\" effectuée",
"xpack.triggersActionsUI.sections.addModalConnectorForm.cancelButtonLabel": "Annuler",
"xpack.triggersActionsUI.sections.addModalConnectorForm.flyoutTitle": "Connecteur {actionTypeName}",
"xpack.triggersActionsUI.sections.addModalConnectorForm.saveButtonLabel": "Enregistrer",
- "xpack.triggersActionsUI.sections.addModalConnectorForm.updateSuccessNotificationText": "Création de \"{connectorName}\" effectuée",
"xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.addBccButton": "Cci",
"xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.addCcButton": "Cc",
"xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.authenticationLabel": "Authentification",
@@ -28962,7 +28876,6 @@
"xpack.triggersActionsUI.sections.connectorAddInline.unableToLoadConnectorTitle'": "Impossible de charger le connecteur",
"xpack.triggersActionsUI.sections.connectorAddInline.unauthorizedToCreateForEmptyConnectors": "Seuls les utilisateurs autorisés peuvent configurer un connecteur. Contactez votre administrateur.",
"xpack.triggersActionsUI.sections.deprecatedTitleMessage": "(déclassé)",
- "xpack.triggersActionsUI.sections.editConnectorForm.actionTypeDescription": "{actionDescription}",
"xpack.triggersActionsUI.sections.editConnectorForm.cancelButtonLabel": "Annuler",
"xpack.triggersActionsUI.sections.editConnectorForm.descriptionText": "Ce connecteur est en lecture seule.",
"xpack.triggersActionsUI.sections.editConnectorForm.flyoutPreconfiguredTitle": "Modifier un connecteur",
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 49a2a99abcfc3..4b1f81af84000 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -28609,7 +28609,6 @@
"xpack.triggersActionsUI.actionVariables.ruleTagsLabel": "ルールのタグ。",
"xpack.triggersActionsUI.actionVariables.ruleTypeLabel": "ルールのタイプ。",
"xpack.triggersActionsUI.appName": "ルールとコネクター",
- "xpack.triggersActionsUI.cases.configureCases.mappingFieldSummary": "まとめ",
"xpack.triggersActionsUI.checkActionTypeEnabled.actionTypeDisabledByConfigMessage": "このコネクターは Kibana の構成で無効になっています。",
"xpack.triggersActionsUI.checkActionTypeEnabled.actionTypeDisabledByLicenseMessage": "このコネクターには {minimumLicenseRequired} ライセンスが必要です。",
"xpack.triggersActionsUI.checkRuleTypeEnabled.ruleTypeDisabledByLicenseMessage": "このルールタイプには{minimumLicenseRequired}ライセンスが必要です。",
@@ -28647,29 +28646,22 @@
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.gmailServerTypeLabel": "Gmail",
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.otherServerTypeLabel": "その他",
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.outlookServerTypeLabel": "Outlook",
- "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.reenterClientSecretLabel": "クライアントシークレットは暗号化されています。このフィールドの値を再入力してください。",
- "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.reenterValuesLabel": "ユーザー名とパスワードは暗号化されます。これらのフィールドの値を再入力してください。",
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText": "サーバーからメールを送信します。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideSuffix": "アラート履歴インデックスには有効なサフィックスを含める必要があります。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideValue": "アラート履歴インデックスの先頭は\"{alertHistoryPrefix}\"でなければなりません。",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.formatFromText": "送信元は有効なメールアドレスではありません。",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthPasswordText": "パスワードが必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthUserNameText": "ユーザー名が必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredClientIdText": "クライアントIDは必須です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredClientSecretText": "クライアントシークレットは必須です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredDocumentJson": "ドキュメントが必要です。有効なJSONオブジェクトにしてください。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredEntryText": "To、Cc、または Bcc のエントリーがありません。 1 つ以上のエントリーが必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredFromText": "送信元が必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredHostText": "ホストが必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredMessageText": "メッセージが必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPasswordText": "ユーザー名の使用時にはパスワードが必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPortText": "ポートが必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServerLogMessageText": "メッセージが必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServiceText": "サービスは必須です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSlackMessageText": "メッセージが必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSubjectText": "件名が必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredTenantIdText": "テナントIDは必須です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredUserText": "パスワードの使用時にはユーザー名が必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookBodyText": "本文が必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.actionTypeTitle": "データをインデックスする",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.chooseLabel": "選択…",
@@ -28678,7 +28670,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.definedateFieldTooltip": "この時間フィールドをドキュメントにインデックスが作成された時刻に設定します。",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.defineTimeFieldLabel": "各ドキュメントの時刻フィールドを定義",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.documentsFieldLabel": "インデックスするドキュメント",
- "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.requiredIndexText": "インデックスが必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.executionTimeFieldLabel": "時間フィールド",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.howToBroadenSearchQueryDescription": "* で検索クエリの範囲を広げます。",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indexDocumentHelpLabel": "インデックスドキュメントの例。",
@@ -28694,25 +28685,14 @@
"xpack.triggersActionsUI.components.builtinActionTypes.jira.actionTypeTitle": "Jira",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.apiTokenTextFieldLabel": "APIトークン",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.apiUrlTextFieldLabel": "URL",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.authenticationLabel": "認証",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.commentsTextAreaFieldLabel": "追加のコメント",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.descriptionTextAreaFieldLabel": "説明",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.emailTextFieldLabel": "メールアドレス",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.impactSelectFieldLabel": "ラベル",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.invalidApiUrlTextField": "URL が無効です。",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.labelsSpacesErrorMessage": "ラベルにはスペースを使用できません。",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.mappingFieldComments": "コメント",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.mappingFieldDescription": "説明",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.parentIssueSearchLabel": "親問題",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.projectKey": "プロジェクトキー",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.reenterValuesLabel": "認証資格情報は暗号化されます。これらのフィールドの値を再入力してください。",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredApiTokenTextField": "APIトークンが必要です",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredApiUrlTextField": "URL が必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredDescriptionTextField": "説明が必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredEmailTextField": "メールアドレスが必要です",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredProjectKeyTextField": "プロジェクトキーが必要です",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredSummaryTextField": "概要が必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requireHttpsApiUrlTextField": "URL は https:// から始める必要があります。",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxAriaLabel": "入力して検索",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxPlaceholder": "入力して検索",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesLoading": "読み込み中...",
@@ -28724,7 +28704,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssuesMessage": "問題を取得できません",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueTypesMessage": "問題タイプを取得できません",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.urgencySelectFieldLabel": "問題タイプ",
- "xpack.triggersActionsUI.components.builtinActionTypes.missingSecretsValuesLabel": "機密情報はインポートされません。次のフィールド{encryptedFieldsLength, plural, other {}}の値{encryptedFieldsLength, plural, other {}}入力してください。",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.actionTypeTitle": "PagerDuty に送信",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.apiUrlTextFieldLabel": "API URL(任意)",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.classFieldLabel": "クラス(任意)",
@@ -28740,7 +28719,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectResolveOptionLabel": "解決",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectTriggerOptionLabel": "トリガー",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.groupTextFieldLabel": "グループ(任意)",
- "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.reenterValueLabel": "このキーは暗号化されています。このフィールドの値を再入力してください。",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyNameHelpLabel": "PagerDuty アカウントを構成します",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyTextFieldLabel": "統合キー",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText": "PagerDuty でイベントを送信します。",
@@ -28752,29 +28730,15 @@
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.sourceTextFieldLabel": "ソース(任意)",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.summaryFieldLabel": "まとめ",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.timestampTextFieldLabel": "タイムスタンプ(任意)",
- "xpack.triggersActionsUI.components.builtinActionTypes.rememberValueLabel": "{encryptedFieldsLength, plural, one {この} other {これらの}}値を記憶しておいてください。コネクターを編集するたびに、{encryptedFieldsLength, plural, one {それを} other {それらを}}再入力する必要があります。",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.actionTypeTitle": "Resilient",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKey": "API キー",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeyId": "ID",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeySecret": "シークレット",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiUrlTextFieldLabel": "URL",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.commentsTextAreaFieldLabel": "追加のコメント",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.descriptionTextAreaFieldLabel": "説明",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.invalidApiUrlTextField": "URL が無効です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldComments": "コメント",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldDescription": "説明",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldShortDescription": "名前",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.nameFieldLabel": "名前(必須)",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.orgId": "組織 ID",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.reenterValuesLabel": "ID とシークレットは暗号化されます。これらのフィールドの値を再入力してください。",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.rememberValuesLabel": "これらの値を覚えておいてください。コネクターを編集するたびに再入力する必要があります。",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiKeyIdTextField": "IDが必要です",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiKeySecretTextField": "シークレットが必要です",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiUrlTextField": "URL が必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredDescriptionTextField": "説明が必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredNameTextField": "名前が必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredOrgIdTextField": "組織 ID が必要です",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requireHttpsApiUrlTextField": "URL は https:// から始める必要があります。",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.selectMessageText": "IBM Resilient でインシデントを作成します。",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.severity": "深刻度",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetIncidentTypesMessage": "インシデントタイプを取得できません",
@@ -28785,7 +28749,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.logMessageFieldLabel": "メッセージ",
"xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.selectMessageText": "Kibana ログにメッセージを追加します。",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiInfoError": "アプリケーション情報の取得を試みるときの受信ステータス:{status}",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiUrlHelpText": "完全なURLを含めます。",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiUrlTextFieldLabel": "ServiceNowインスタンスURL",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.appInstallationInfo": "{update} {create} ",
"xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout": "Elastic ServiceNowアプリがインストールされていません",
@@ -28804,20 +28767,14 @@
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.descriptionTextAreaFieldLabel": "説明",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.eventClassTextAreaFieldLabel": "ソースインスタンス",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.impactSelectFieldLabel": "インパクト",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.install": "インストール",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.installationCalloutTitle": "このコネクターを使用するには、まずServiceNowアプリストアからElasticアプリをインストールします。",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.invalidApiUrlTextField": "URL が無効です。",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.messageKeyTextAreaFieldLabel": "メッセージキー",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.metricNameTextAreaFieldLabel": "メトリック名",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.nodeTextAreaFieldLabel": "ノード",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.passwordTextFieldLabel": "パスワード",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.prioritySelectFieldLabel": "優先度",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.reenterValuesLabel": "コネクターを編集するたびに認証する必要があります。",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredApiUrlTextField": "URL が必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredPasswordTextField": "パスワードが必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredSeverityTextField": "重要度は必須です。",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUsernameTextField": "ユーザー名が必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requireHttpsApiUrlTextField": "URL は https:// から始める必要があります。",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.resourceTextAreaFieldLabel": "リソース",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.setupDevInstance": "開発者インスタンスを設定",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.severityRequiredSelectFieldLabel": "重要度(必須)",
@@ -28854,10 +28811,7 @@
"xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIRAction.correlationIDHelpLabel": "インシデントを更新するID",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.actionTypeTitle": "Slack に送信",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.invalidWebhookUrlText": "Web フック URL が無効です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.requiredWebhookUrlText": "Web フック URL が必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.requireHttpsWebhookUrlText": "Web フック URL は https:// から始める必要があります。",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.messageTextAreaFieldLabel": "メッセージ",
- "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.reenterValueLabel": "この URL は暗号化されています。このフィールドの値を再入力してください。",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText": "Slack チャネルにメッセージを送信します。",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlHelpLabel": "Slack Web フック URL を作成",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel": "Web フック URL",
@@ -28865,7 +28819,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationMessage": "id {id}のアプリケーションフィールドを取得できません",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.actionTypeTitle": "Swimlaneレコードを作成",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.alertIdFieldLabel": "アラートID",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.alertSourceFieldLabel": "アラートソース",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenNameHelpLabel": "Swimlane APIトークンを指定",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenTextFieldLabel": "APIトークン",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiUrlTextFieldLabel": "API Url",
@@ -28879,71 +28832,47 @@
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningDesc": "このコネクターを選択できません。必要なアラートフィールドマッピングがありません。このコネクターを編集して、必要なフィールドマッピングを追加するか、タイプがアラートのコネクターを選択できます。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningTitle": "このコネクターにはフィールドマッピングがありません。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAlertID": "アラートIDは必須です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAlertSource": "アラートソースは必須です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredApiTokenText": "API トークンは必須です。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAppIdText": "アプリIDは必須です。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseID": "ケースIDは必須です。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseName": "ケース名は必須です。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredComments": "コメントは必須です。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredDescription": "説明が必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredFieldMappingsText": "フィールドマッピングは必須です。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredRuleName": "ルール名は必須です。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredSeverity": "重要度は必須です。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.invalidApiUrlTextField": "URL が無効です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingDescriptionTextFieldLabel": "Swimlaneアプリケーションでフィールド名を指定するために使用されます",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingFieldRequired": "フィールドマッピングは必須です。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingTitleTextFieldLabel": "フィールドマッピングを構成",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStep": "次へ",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStepHelpText": "フィールドマッピングが構成されていない場合、Swimlaneコネクタータイプはすべてに設定されます。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.prevStep": "戻る",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.reenterValueLabel": "このキーは暗号化されています。このフィールドの値を再入力してください。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.rememberValueLabel": "この値を覚えておいてください。コネクターを編集するたびに再入力する必要があります。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.requiredApiUrlTextField": "URL が必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.retrieveConfigurationLabel": "フィールドを構成",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.ruleNameFieldLabel": "ルール名",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.selectMessageText": "Swimlaneでレコードを作成",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.severityFieldLabel": "深刻度",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.actionTypeTitle": "メッセージを Microsoft Teams チャネルに送信します。",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.invalidWebhookUrlText": "Web フック URL が無効です。",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requiredMessageText": "メッセージが必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requiredWebhookUrlText": "Web フック URL が必要です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requireHttpsWebhookUrlText": "Web フック URL は https:// から始める必要があります。",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.messageTextAreaFieldLabel": "メッセージ",
- "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.reenterValueLabel": "この URL は暗号化されています。このフィールドの値を再入力してください。",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.selectMessageText": "メッセージを Microsoft Teams チャネルに送信します。",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlHelpLabel": "Microsoft Teams Web フック URL を作成",
- "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlTextFieldLabel": "Web フック URL",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.actionTypeTitle": "Web フックデータ",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.addHeader": "ヘッダーを追加",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.addHeaderButton": "追加",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.authenticationLabel": "認証",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyCodeEditorAriaLabel": "コードエディター",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyFieldLabel": "本文",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.deleteHeaderButton": "削除",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.invalidUrlTextField": "URL が無効です。",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.requiredUrlText": "URL が必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.hasAuthSwitchLabel": "この Web フックの認証が必要です",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.httpHeadersTitle": "使用中のヘッダー",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.keyTextFieldLabel": "キー",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.methodTextFieldLabel": "メソド",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.passwordTextFieldLabel": "パスワード",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.reenterValuesLabel": "ユーザー名とパスワードは暗号化されます。これらのフィールドの値を再入力してください。",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText": "Web サービスにリクエストを送信してください。",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.urlTextFieldLabel": "URL",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.userTextFieldLabel": "ユーザー名",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.valueTextFieldLabel": "値",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.viewHeadersSwitch": "HTTP ヘッダーを追加",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.actionTypeTitle": "xMattersデータ",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.authenticationLabel": "認証",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.basicAuthLabel": "基本認証",
- "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.connectorSettingsFieldLabel": "開始URL",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.connectorSettingsLabel": "xMattersトリガーを設定するときに使用される認証方法を選択します。",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.invalidUrlTextField": "URL が無効です。",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.requiredUrlText": "URL が必要です。",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.initiationUrlHelpText": "完全なxMatters URLを含めます。",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.passwordTextFieldLabel": "パスワード",
- "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.reenterBasicAuthValuesLabel": "ユーザー名とパスワードは暗号化されます。これらのフィールドの値を再入力してください。",
- "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.reenterUrlAuthValuesLabel": "URLは暗号化されています。このフィールドの値を再入力してください。",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.selectMessageText": "xMattersワークフローをトリガーします。",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severity": "深刻度",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectCriticalOptionLabel": "重大",
@@ -29017,10 +28946,6 @@
"xpack.triggersActionsUI.sections.actionConnectorAdd.upgradeYourPlanBannerLinkTitle": "サブスクリプションオプション",
"xpack.triggersActionsUI.sections.actionConnectorAdd.upgradeYourPlanBannerMessage": "すべてのサードパーティコネクターにすぐにアクセスするには、ライセンスをアップグレードするか、30日間無料の試用版を開始してください。",
"xpack.triggersActionsUI.sections.actionConnectorAdd.upgradeYourPlanBannerTitle": "ライセンスをアップグレードしてすべてのコネクターにアクセス",
- "xpack.triggersActionsUI.sections.actionConnectorForm.actionNameLabel": "コネクター名",
- "xpack.triggersActionsUI.sections.actionConnectorForm.actions.actionConfigurationWarningHelpLinkText": "詳細情報",
- "xpack.triggersActionsUI.sections.actionConnectorForm.actions.connectorTypeConfigurationWarningDescriptionText": "コネクターを作成するには、1 つ以上の{connectorType}アカウントを構成する必要があります。{docLink}",
- "xpack.triggersActionsUI.sections.actionConnectorForm.actions.connectorTypeConfigurationWarningTitleText": "コネクタータイプが登録されていません",
"xpack.triggersActionsUI.sections.actionConnectorForm.connectorSettingsLabel": "コネクター設定",
"xpack.triggersActionsUI.sections.actionConnectorForm.error.requiredNameText": "名前が必要です。",
"xpack.triggersActionsUI.sections.actionConnectorForm.loadingConnectorSettingsDescription": "コネクター設定を読み込んでいます...",
@@ -29068,25 +28993,14 @@
"xpack.triggersActionsUI.sections.actionTypeForm.addNewActionConnectorActionGroup.display": "{actionGroupName}(現在サポートされていません)",
"xpack.triggersActionsUI.sections.actionTypeForm.addNewConnectorEmptyButton": "コネクターの追加",
"xpack.triggersActionsUI.sections.actionTypeForm.existingAlertActionTypeEditTitle": "{actionConnectorName}",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthPasswordText": "パスワードが必要です。",
"xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthUserNameText": "ユーザー名が必要です。",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredHeaderKeyText": "キーが必要です。",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredHeaderValueText": "値が必要です。",
"xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText": "メソッドが必要です。",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredPasswordText": "ユーザー名の使用時にはパスワードが必要です。",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredUserText": "パスワードの使用時にはユーザー名が必要です。",
- "xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredAuthPasswordText": "パスワードが必要です。",
- "xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredAuthUserNameText": "ユーザー名が必要です。",
- "xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredPasswordText": "ユーザー名の使用時にはパスワードが必要です。",
- "xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredUserText": "パスワードの使用時にはユーザー名が必要です。",
"xpack.triggersActionsUI.sections.addConnectorForm.flyoutTitle": "{actionTypeName}コネクター",
"xpack.triggersActionsUI.sections.addConnectorForm.selectConnectorFlyoutTitle": "コネクターを選択",
- "xpack.triggersActionsUI.sections.addConnectorForm.updateErrorNotificationText": "コネクターを作成できません。",
"xpack.triggersActionsUI.sections.addConnectorForm.updateSuccessNotificationText": "「{connectorName}」を作成しました",
"xpack.triggersActionsUI.sections.addModalConnectorForm.cancelButtonLabel": "キャンセル",
"xpack.triggersActionsUI.sections.addModalConnectorForm.flyoutTitle": "{actionTypeName}コネクター",
"xpack.triggersActionsUI.sections.addModalConnectorForm.saveButtonLabel": "保存",
- "xpack.triggersActionsUI.sections.addModalConnectorForm.updateSuccessNotificationText": "「{connectorName}」を作成しました",
"xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.addBccButton": "Bcc",
"xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.addCcButton": "Cc",
"xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.authenticationLabel": "認証",
@@ -29124,7 +29038,6 @@
"xpack.triggersActionsUI.sections.connectorAddInline.unableToLoadConnectorTitle'": "コネクターを読み込めません",
"xpack.triggersActionsUI.sections.connectorAddInline.unauthorizedToCreateForEmptyConnectors": "許可されたユーザーのみがコネクターを構成できます。管理者にお問い合わせください。",
"xpack.triggersActionsUI.sections.deprecatedTitleMessage": "(非推奨)",
- "xpack.triggersActionsUI.sections.editConnectorForm.actionTypeDescription": "{actionDescription}",
"xpack.triggersActionsUI.sections.editConnectorForm.cancelButtonLabel": "キャンセル",
"xpack.triggersActionsUI.sections.editConnectorForm.descriptionText": "このコネクターは読み取り専用です。",
"xpack.triggersActionsUI.sections.editConnectorForm.flyoutPreconfiguredTitle": "コネクターを編集",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index ab53a184d44e6..fa9b9677ccbe1 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -28643,7 +28643,6 @@
"xpack.triggersActionsUI.actionVariables.ruleTagsLabel": "规则的标签。",
"xpack.triggersActionsUI.actionVariables.ruleTypeLabel": "规则的类型。",
"xpack.triggersActionsUI.appName": "规则和连接器",
- "xpack.triggersActionsUI.cases.configureCases.mappingFieldSummary": "摘要",
"xpack.triggersActionsUI.checkActionTypeEnabled.actionTypeDisabledByConfigMessage": "连接器已由 Kibana 配置禁用。",
"xpack.triggersActionsUI.checkActionTypeEnabled.actionTypeDisabledByLicenseMessage": "此连接器需要{minimumLicenseRequired}许可证。",
"xpack.triggersActionsUI.checkRuleTypeEnabled.ruleTypeDisabledByLicenseMessage": "此规则类型需要{minimumLicenseRequired}许可证。",
@@ -28681,29 +28680,22 @@
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.gmailServerTypeLabel": "Gmail",
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.otherServerTypeLabel": "其他",
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.outlookServerTypeLabel": "Outlook",
- "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.reenterClientSecretLabel": "客户端密钥已加密。请为此字段重新输入值。",
- "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.reenterValuesLabel": "用户名和密码已加密。请为这些字段重新输入值。",
"xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText": "从您的服务器发送电子邮件。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideSuffix": "告警历史记录索引必须包含有效的后缀。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.badIndexOverrideValue": "告警历史记录索引必须以“{alertHistoryPrefix}”开头。",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.formatFromText": "发送者电子邮件地址无效。",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthPasswordText": "“密码”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthUserNameText": "“用户名”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredClientIdText": "“客户端 ID”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredClientSecretText": "“客户端密钥”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredDocumentJson": "“文档”必填,并且应为有效的 JSON 对象。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredEntryText": "未输入收件人、抄送、密送。 至少需要输入一个。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredFromText": "“发送者”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredHostText": "“主机”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredMessageText": "“消息”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPasswordText": "使用用户名时,“密码”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPortText": "“端口”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServerLogMessageText": "“消息”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredServiceText": "“服务”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSlackMessageText": "“消息”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSubjectText": "“主题”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredTenantIdText": "“租户 ID”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.error.requiredUserText": "使用密码时,“用户名”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.error.requiredWebhookBodyText": "“正文”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.actionTypeTitle": "索引数据",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.chooseLabel": "选择……",
@@ -28712,7 +28704,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.definedateFieldTooltip": "将此时间字段设置为索引文档的时间。",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.defineTimeFieldLabel": "为每个文档定义时间字段",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.documentsFieldLabel": "要索引的文档",
- "xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.requiredIndexText": "“索引”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.executionTimeFieldLabel": "时间字段",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.howToBroadenSearchQueryDescription": "使用 * 可扩大您的查询范围。",
"xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indexDocumentHelpLabel": "索引文档示例。",
@@ -28728,25 +28719,14 @@
"xpack.triggersActionsUI.components.builtinActionTypes.jira.actionTypeTitle": "Jira",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.apiTokenTextFieldLabel": "API 令牌",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.apiUrlTextFieldLabel": "URL",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.authenticationLabel": "身份验证",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.commentsTextAreaFieldLabel": "其他注释",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.descriptionTextAreaFieldLabel": "描述",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.emailTextFieldLabel": "电子邮件地址",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.impactSelectFieldLabel": "标签",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.invalidApiUrlTextField": "URL 无效。",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.labelsSpacesErrorMessage": "标签不能包含空格。",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.mappingFieldComments": "注释",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.mappingFieldDescription": "描述",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.parentIssueSearchLabel": "父问题",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.projectKey": "项目键",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.reenterValuesLabel": "验证凭据已加密。请为这些字段重新输入值。",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredApiTokenTextField": "“API 令牌”必填",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredApiUrlTextField": "“URL”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredDescriptionTextField": "“描述”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredEmailTextField": "电子邮件地址必填",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredProjectKeyTextField": "“项目键”必填",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredSummaryTextField": "“摘要”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.jira.requireHttpsApiUrlTextField": "URL 必须以 https:// 开头。",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxAriaLabel": "键入内容进行搜索",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxPlaceholder": "键入内容进行搜索",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesLoading": "正在加载……",
@@ -28758,7 +28738,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssuesMessage": "无法获取问题",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueTypesMessage": "无法获取问题类型",
"xpack.triggersActionsUI.components.builtinActionTypes.jira.urgencySelectFieldLabel": "问题类型",
- "xpack.triggersActionsUI.components.builtinActionTypes.missingSecretsValuesLabel": "未导入敏感信息。请为以下字段{encryptedFieldsLength, plural, other {}}输入值{encryptedFieldsLength, plural, other {}}。",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.actionTypeTitle": "发送到 PagerDuty",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.apiUrlTextFieldLabel": "API URL(可选)",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.classFieldLabel": "类(可选)",
@@ -28774,7 +28753,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectResolveOptionLabel": "解决",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.eventSelectTriggerOptionLabel": "触发",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.groupTextFieldLabel": "组(可选)",
- "xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.reenterValueLabel": "此密钥已加密。请为此字段重新输入值。",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyNameHelpLabel": "配置 PagerDuty 帐户",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyTextFieldLabel": "集成密钥",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText": "在 PagerDuty 中发送事件。",
@@ -28786,29 +28764,15 @@
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.sourceTextFieldLabel": "源(可选)",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.summaryFieldLabel": "摘要",
"xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.timestampTextFieldLabel": "时间戳(可选)",
- "xpack.triggersActionsUI.components.builtinActionTypes.rememberValueLabel": "记住{encryptedFieldsLength, plural, one {此} other {这些}}值。每次编辑连接器时都必须重新输入{encryptedFieldsLength, plural, other {值}}。",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.actionTypeTitle": "Resilient",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKey": "API 密钥",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeyId": "ID",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeySecret": "机密",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiUrlTextFieldLabel": "URL",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.commentsTextAreaFieldLabel": "其他注释",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.descriptionTextAreaFieldLabel": "描述",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.invalidApiUrlTextField": "URL 无效。",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldComments": "注释",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldDescription": "描述",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldShortDescription": "名称",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.nameFieldLabel": "名称(必填)",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.orgId": "组织 ID",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.reenterValuesLabel": "ID 和密钥已加密。请为这些字段重新输入值。",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.rememberValuesLabel": "请记住这些值。每次编辑连接器时都必须重新输入。",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiKeyIdTextField": "“ID”必填",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiKeySecretTextField": "“密钥”必填",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiUrlTextField": "“URL”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredDescriptionTextField": "“描述”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredNameTextField": "“名称”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredOrgIdTextField": "“组织 ID”必填",
- "xpack.triggersActionsUI.components.builtinActionTypes.resilient.requireHttpsApiUrlTextField": "URL 必须以 https:// 开头。",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.selectMessageText": "在 IBM Resilient 中创建事件。",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.severity": "严重性",
"xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetIncidentTypesMessage": "无法获取事件类型",
@@ -28819,7 +28783,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.logMessageFieldLabel": "消息",
"xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.selectMessageText": "将消息添加到 Kibana 日志。",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiInfoError": "尝试获取应用程序信息时收到的状态:{status}",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiUrlHelpText": "包括完整 URL。",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiUrlTextFieldLabel": "ServiceNow 实例 URL",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.appInstallationInfo": "{update} {create} ",
"xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.applicationRequiredCallout": "未安装 Elastic ServiceNow 应用",
@@ -28838,20 +28801,14 @@
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.descriptionTextAreaFieldLabel": "描述",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.eventClassTextAreaFieldLabel": "源实例",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.impactSelectFieldLabel": "影响",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.install": "安装",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.installationCalloutTitle": "要使用此连接器,请先从 ServiceNow 应用商店安装 Elastic 应用。",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.invalidApiUrlTextField": "URL 无效。",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.messageKeyTextAreaFieldLabel": "消息密钥",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.metricNameTextAreaFieldLabel": "指标名称",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.nodeTextAreaFieldLabel": "节点",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.passwordTextFieldLabel": "密码",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.prioritySelectFieldLabel": "优先级",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.reenterValuesLabel": "每次编辑连接器时都必须进行身份验证。",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredApiUrlTextField": "“URL”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredPasswordTextField": "“密码”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredSeverityTextField": "“严重性”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUsernameTextField": "“用户名”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requireHttpsApiUrlTextField": "URL 必须以 https:// 开头。",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.resourceTextAreaFieldLabel": "资源",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.setupDevInstance": "设置开发者实例",
"xpack.triggersActionsUI.components.builtinActionTypes.servicenow.severityRequiredSelectFieldLabel": "严重性(必需)",
@@ -28888,10 +28845,7 @@
"xpack.triggersActionsUI.components.builtinActionTypes.serviceNowSIRAction.correlationIDHelpLabel": "用于更新事件的标识符",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.actionTypeTitle": "发送到 Slack",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.invalidWebhookUrlText": "Webhook URL 无效。",
- "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.requiredWebhookUrlText": "“Webhook URL”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.requireHttpsWebhookUrlText": "Webhook URL 必须以 https:// 开头。",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.messageTextAreaFieldLabel": "消息",
- "xpack.triggersActionsUI.components.builtinActionTypes.slackAction.reenterValueLabel": "此 URL 已加密。请为此字段重新输入值。",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText": "向 Slack 频道或用户发送消息。",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlHelpLabel": "创建 Slack webhook URL",
"xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel": "Webhook URL",
@@ -28899,7 +28853,6 @@
"xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationMessage": "无法获取 ID 为 {id} 的应用程序",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.actionTypeTitle": "创建泳道记录",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.alertIdFieldLabel": "告警 ID",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.alertSourceFieldLabel": "告警源",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenNameHelpLabel": "提供泳道 API 令牌",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiTokenTextFieldLabel": "API 令牌",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.apiUrlTextFieldLabel": "API URL",
@@ -28913,71 +28866,47 @@
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningDesc": "无法选择此连接器,因为其缺失所需的告警字段映射。您可以编辑此连接器以添加所需的字段映射或选择告警类型的连接器。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningTitle": "此连接器缺失字段映射",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAlertID": "“告警 ID”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAlertSource": "“告警源”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredApiTokenText": "“API 令牌”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAppIdText": "“应用 ID”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseID": "“案例 ID”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredCaseName": "“案例名称”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredComments": "“注释”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredDescription": "“描述”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredFieldMappingsText": "“字段映射”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredRuleName": "“规则名称”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredSeverity": "“严重性”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.invalidApiUrlTextField": "URL 无效。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingDescriptionTextFieldLabel": "用于指定泳道应用程序中的字段名称",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingFieldRequired": "“字段映射”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingTitleTextFieldLabel": "配置字段映射",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStep": "下一步",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.nextStepHelpText": "如果未配置字段映射,泳道连接器类型将设置为 all。",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.prevStep": "返回",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.reenterValueLabel": "此密钥已加密。请为此字段重新输入值。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.rememberValueLabel": "请记住此值。每次编辑连接器时都必须重新输入。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.requiredApiUrlTextField": "“URL”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.retrieveConfigurationLabel": "配置字段",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.ruleNameFieldLabel": "规则名称",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.selectMessageText": "在泳道中创建记录",
"xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.severityFieldLabel": "严重性",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.actionTypeTitle": "向 Microsoft Teams 频道发送消息。",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.invalidWebhookUrlText": "Webhook URL 无效。",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requiredMessageText": "“消息”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requiredWebhookUrlText": "“Webhook URL”必填。",
- "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requireHttpsWebhookUrlText": "Webhook URL 必须以 https:// 开头。",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.messageTextAreaFieldLabel": "消息",
- "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.reenterValueLabel": "此 URL 已加密。请为此字段重新输入值。",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.selectMessageText": "向 Microsoft Teams 频道发送消息。",
"xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlHelpLabel": "创建 Microsoft Teams Webhook URL",
- "xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlTextFieldLabel": "Webhook URL",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.actionTypeTitle": "Webhook 数据",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.addHeader": "添加标头",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.addHeaderButton": "添加",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.authenticationLabel": "身份验证",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyCodeEditorAriaLabel": "代码编辑器",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.bodyFieldLabel": "正文",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.deleteHeaderButton": "删除",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.invalidUrlTextField": "URL 无效。",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.requiredUrlText": "“URL”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.hasAuthSwitchLabel": "此 Webhook 需要身份验证",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.httpHeadersTitle": "在用的标头",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.keyTextFieldLabel": "钥匙",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.methodTextFieldLabel": "方法",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.passwordTextFieldLabel": "密码",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.reenterValuesLabel": "用户名和密码已加密。请为这些字段重新输入值。",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText": "将请求发送到 Web 服务。",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.urlTextFieldLabel": "URL",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.userTextFieldLabel": "用户名",
- "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.valueTextFieldLabel": "值",
"xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.viewHeadersSwitch": "添加 HTTP 标头",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.actionTypeTitle": "xMatters 数据",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.authenticationLabel": "身份验证",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.basicAuthLabel": "基本身份验证",
- "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.connectorSettingsFieldLabel": "发起 URL",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.connectorSettingsLabel": "选择在设置 xMatters 触发器时使用的身份验证方法。",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.invalidUrlTextField": "URL 无效。",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.requiredUrlText": "“URL”必填。",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.initiationUrlHelpText": "包括完整 xMatters url。",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.passwordTextFieldLabel": "密码",
- "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.reenterBasicAuthValuesLabel": "用户和密码已加密。请为这些字段重新输入值。",
- "xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.reenterUrlAuthValuesLabel": "URL 已加密。请为此字段重新输入值。",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.selectMessageText": "触发 xMatters 工作流。",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severity": "严重性",
"xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.severitySelectCriticalOptionLabel": "紧急",
@@ -29050,10 +28979,6 @@
"xpack.triggersActionsUI.sections.actionConnectorAdd.upgradeYourPlanBannerLinkTitle": "订阅计划",
"xpack.triggersActionsUI.sections.actionConnectorAdd.upgradeYourPlanBannerMessage": "升级您的许可证或开始为期 30 天的免费试用,以便可以立即使用所有第三方连接器。",
"xpack.triggersActionsUI.sections.actionConnectorAdd.upgradeYourPlanBannerTitle": "升级您的许可证以访问所有连接器",
- "xpack.triggersActionsUI.sections.actionConnectorForm.actionNameLabel": "连接器名称",
- "xpack.triggersActionsUI.sections.actionConnectorForm.actions.actionConfigurationWarningHelpLinkText": "了解详情。",
- "xpack.triggersActionsUI.sections.actionConnectorForm.actions.connectorTypeConfigurationWarningDescriptionText": "要创建此连接器,必须至少配置一个 {connectorType} 帐户。{docLink}",
- "xpack.triggersActionsUI.sections.actionConnectorForm.actions.connectorTypeConfigurationWarningTitleText": "未注册连接器类型",
"xpack.triggersActionsUI.sections.actionConnectorForm.connectorSettingsLabel": "连接器设置",
"xpack.triggersActionsUI.sections.actionConnectorForm.error.requiredNameText": "“名称”必填。",
"xpack.triggersActionsUI.sections.actionConnectorForm.loadingConnectorSettingsDescription": "正在加载连接器设置……",
@@ -29101,25 +29026,14 @@
"xpack.triggersActionsUI.sections.actionTypeForm.addNewActionConnectorActionGroup.display": "{actionGroupName}(当前不支持)",
"xpack.triggersActionsUI.sections.actionTypeForm.addNewConnectorEmptyButton": "添加连接器",
"xpack.triggersActionsUI.sections.actionTypeForm.existingAlertActionTypeEditTitle": "{actionConnectorName}",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthPasswordText": "“密码”必填。",
"xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthUserNameText": "“用户名”必填。",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredHeaderKeyText": "“键”必填。",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredHeaderValueText": "“值”必填。",
"xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText": "“方法”必填",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredPasswordText": "使用用户名时,“密码”必填。",
- "xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredUserText": "使用密码时,“用户名”必填。",
- "xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredAuthPasswordText": "“密码”必填。",
- "xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredAuthUserNameText": "“用户名”必填。",
- "xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredPasswordText": "使用用户名时,“密码”必填。",
- "xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredUserText": "使用密码时,“用户名”必填。",
"xpack.triggersActionsUI.sections.addConnectorForm.flyoutTitle": "{actionTypeName} 连接器",
"xpack.triggersActionsUI.sections.addConnectorForm.selectConnectorFlyoutTitle": "选择连接器",
- "xpack.triggersActionsUI.sections.addConnectorForm.updateErrorNotificationText": "无法创建连接器。",
"xpack.triggersActionsUI.sections.addConnectorForm.updateSuccessNotificationText": "已创建“{connectorName}”",
"xpack.triggersActionsUI.sections.addModalConnectorForm.cancelButtonLabel": "取消",
"xpack.triggersActionsUI.sections.addModalConnectorForm.flyoutTitle": "{actionTypeName} 连接器",
"xpack.triggersActionsUI.sections.addModalConnectorForm.saveButtonLabel": "保存",
- "xpack.triggersActionsUI.sections.addModalConnectorForm.updateSuccessNotificationText": "已创建“{connectorName}”",
"xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.addBccButton": "密送",
"xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.addCcButton": "抄送",
"xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.authenticationLabel": "身份验证",
@@ -29157,7 +29071,6 @@
"xpack.triggersActionsUI.sections.connectorAddInline.unableToLoadConnectorTitle'": "无法加载连接器",
"xpack.triggersActionsUI.sections.connectorAddInline.unauthorizedToCreateForEmptyConnectors": "只有获得授权的用户才能配置连接器。请联系您的管理员。",
"xpack.triggersActionsUI.sections.deprecatedTitleMessage": "(已过时)",
- "xpack.triggersActionsUI.sections.editConnectorForm.actionTypeDescription": "{actionDescription}",
"xpack.triggersActionsUI.sections.editConnectorForm.cancelButtonLabel": "取消",
"xpack.triggersActionsUI.sections.editConnectorForm.descriptionText": "此连接器为只读。",
"xpack.triggersActionsUI.sections.editConnectorForm.flyoutPreconfiguredTitle": "编辑连接器",
diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md
index 7fde1f1512302..b106118ef8d34 100644
--- a/x-pack/plugins/triggers_actions_ui/README.md
+++ b/x-pack/plugins/triggers_actions_ui/README.md
@@ -13,42 +13,43 @@ As a developer you can reuse and extend built-in alerts and actions UI functiona
Table of Contents
- [Kibana Alerts and Actions UI](#kibana-alerts-and-actions-ui)
- - [Build and register Alert Types](#build-and-register-alert-types)
- - [Built-in Alert Types](#built-in-alert-types)
- - [Index Threshold Alert](#index-threshold-alert)
- - [Alert type model definition](#alert-type-model-definition)
- - [Register alert type model](#register-alert-type-model)
- - [Create and register new alert type UI example](#create-and-register-new-alert-type-ui-example)
- - [Common expression components](#common-expression-components)
- - [WHEN expression component](#when-expression-component)
- - [OF expression component](#of-expression-component)
- - [GROUPED BY expression component](#grouped-by-expression-component)
- - [FOR THE LAST expression component](#for-the-last-expression-component)
- - [THRESHOLD expression component](#threshold-expression-component)
- - [Alert Conditions Components](#alert-conditions-components)
- - [Embed the Create Alert flyout within any Kibana plugin](#embed-the-create-alert-flyout-within-any-kibana-plugin)
+ - [Built-in Alert Types](#built-in-alert-types)
+ - [Index Threshold Alert](#index-threshold-alert)
+ - [Alert type model definition](#alert-type-model-definition)
+ - [Register alert type model](#register-alert-type-model)
+ - [Create and register new alert type UI example](#create-and-register-new-alert-type-ui-example)
+ - [Common expression components](#common-expression-components)
+ - [WHEN expression component](#when-expression-component)
+ - [OF expression component](#of-expression-component)
+ - [GROUPED BY expression component](#grouped-by-expression-component)
+ - [FOR THE LAST expression component](#for-the-last-expression-component)
+ - [THRESHOLD expression component](#threshold-expression-component)
+ - [Alert Conditions Components](#alert-conditions-components)
+ - [The AlertConditions component](#the-alertconditions-component)
+ - [The AlertConditionsGroup component](#the-alertconditionsgroup-component)
+ - [Embed the Create Alert flyout within any Kibana plugin](#embed-the-create-alert-flyout-within-any-kibana-plugin)
- [Build and register Action Types](#build-and-register-action-types)
- - [Built-in Action Types](#built-in-action-types)
- - [Server log](#server-log)
- - [Email](#email)
- - [Slack](#slack)
- - [Index](#index)
- - [Webhook](#webhook)
- - [PagerDuty](#pagerduty)
- - [Action type model definition](#action-type-model-definition)
- - [Register action type model](#register-action-type-model)
- - [Create and register new action type UI example](#reate-and-register-new-action-type-ui-example)
- - [Embed the Alert Actions form within any Kibana plugin](#embed-the-alert-actions-form-within-any-kibana-plugin)
- - [Embed the Create Connector flyout within any Kibana plugin](#embed-the-create-connector-flyout-within-any-kibana-plugin)
- - [Embed the Edit Connector flyout within any Kibana plugin](#embed-the-edit-connector-flyout-within-any-kibana-plugin)
+ - [Server log](#server-log)
+ - [Email](#email)
+ - [Slack](#slack)
+ - [Index](#index)
+ - [Webhook](#webhook)
+ - [PagerDuty](#pagerduty)
+ - [Action type model definition](#action-type-model-definition)
+ - [CustomConnectorSelectionItem Properties](#customconnectorselectionitem-properties)
+ - [Register action type model](#register-action-type-model)
+ - [Create and register new action type UI](#create-and-register-new-action-type-ui)
+ - [Embed the Alert Actions form within any Kibana plugin](#embed-the-alert-actions-form-within-any-kibana-plugin)
+ - [Embed the Create Connector flyout within any Kibana plugin](#embed-the-create-connector-flyout-within-any-kibana-plugin)
+ - [Embed the Edit Connector flyout within any Kibana plugin](#embed-the-edit-connector-flyout-within-any-kibana-plugin)
## Built-in Alert Types
Kibana ships with several built-in alert types:
-|Type|Id|Description|
-|---|---|---|
-|[Index Threshold](#index-threshold-alert)|`threshold`|Index Threshold Alert|
+| Type | Id | Description |
+| ----------------------------------------- | ----------- | --------------------- |
+| [Index Threshold](#index-threshold-alert) | `threshold` | Index Threshold Alert |
Every alert type must be registered server side, and can optionally be registered client side.
Only alert types registered on both client and server will be displayed in the Create Alert flyout, as a part of the UI.
@@ -89,12 +90,12 @@ interface IndexThresholdProps {
}
```
-|Property|Description|
-|---|---|
-|ruleParams|Set of Alert params relevant for the index threshold alert type.|
-|setRuleParams|Alert reducer method, which is used to create a new copy of alert object with the changed params property any subproperty value.|
-|setRuleProperty|Alert reducer method, which is used to create a new copy of alert object with the changed any direct alert property value.|
-|errors|Alert level errors tracking object.|
+| Property | Description |
+| --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
+| ruleParams | Set of Alert params relevant for the index threshold alert type. |
+| setRuleParams | Alert reducer method, which is used to create a new copy of alert object with the changed params property any subproperty value. |
+| setRuleProperty | Alert reducer method, which is used to create a new copy of alert object with the changed any direct alert property value. |
+| errors | Alert level errors tracking object. |
Alert reducer is defined on the AlertAdd functional component level and passed down to the subcomponents to provide a new state of Alert object:
@@ -245,16 +246,16 @@ Each alert type should be defined as `RuleTypeModel` object with the these prope
>;
defaultActionMessage?: string;
```
-|Property|Description|
-|---|---|
-|id|Alert type id. Should be the same as on the server side.|
-|name|Name of the alert type that will be displayed on the select card in the UI.|
-|iconClass|Icon of the alert type that will be displayed on the select card in the UI.|
-|validate|Validation function for the alert params.|
-|ruleParamsExpression| A lazy loaded React component for building UI of the current alert type params.|
-|defaultActionMessage|Optional property for providing default messages for all added actions, excluding the Recovery action group, with `message` property. |
-|defaultRecoveryMessage|Optional property for providing a default message for all added actions with `message` property for the Recovery action group.|
-|requiresAppContext|Define if alert type is enabled for create and edit in the alerting management UI.|
+| Property | Description |
+| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
+| id | Alert type id. Should be the same as on the server side. |
+| name | Name of the alert type that will be displayed on the select card in the UI. |
+| iconClass | Icon of the alert type that will be displayed on the select card in the UI. |
+| validate | Validation function for the alert params. |
+| ruleParamsExpression | A lazy loaded React component for building UI of the current alert type params. |
+| defaultActionMessage | Optional property for providing default messages for all added actions, excluding the Recovery action group, with `message` property. |
+| defaultRecoveryMessage | Optional property for providing a default message for all added actions with `message` property for the Recovery action group. |
+| requiresAppContext | Define if alert type is enabled for create and edit in the alerting management UI. |
IMPORTANT: The current UI supports a single action group only.
Action groups are mapped from the server API result for [GET /api/alerts/list_alert_types: List alert types](https://github.com/elastic/kibana/tree/main/x-pack/plugins/alerting#get-apialerttypes-list-alert-types).
@@ -445,12 +446,12 @@ interface WhenExpressionProps {
}
```
-|Property|Description|
-|---|---|
-|aggType|Selected aggregation type that will be set as the alert type property.|
-|customAggTypesOptions|(Optional) List of aggregation types that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/aggregation_types.ts`.|
-|onChangeSelectedAggType|event handler that will be executed when selected aggregation type is changed.|
-|popupPosition|(Optional) expression popup position. Default is `downLeft`. Recommend changing it for a small parent window space.|
+| Property | Description |
+| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| aggType | Selected aggregation type that will be set as the alert type property. |
+| customAggTypesOptions | (Optional) List of aggregation types that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/aggregation_types.ts`. |
+| onChangeSelectedAggType | event handler that will be executed when selected aggregation type is changed. |
+| popupPosition | (Optional) expression popup position. Default is `downLeft`. Recommend changing it for a small parent window space. |
### OF expression component
@@ -486,15 +487,15 @@ interface OfExpressionProps {
}
```
-|Property|Description|
-|---|---|
-|aggType|Selected aggregation type that will be set as the alert type property.|
-|aggField|Selected aggregation field that will be set as the alert type property.|
-|errors|List of errors with proper messages for the alert params that should be validated. In current component is validated `aggField`.|
-|onChangeSelectedAggField|Event handler that will be excuted if selected aggregation field is changed.|
-|fields|Fields list that will be available in the OF `Select a field` dropdown.|
-|customAggTypesOptions|(Optional) List of aggregation types that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/aggregation_types.ts`.|
-|popupPosition|(Optional) expression popup position. Default is `downRight`. Recommend changing it for a small parent window space.|
+| Property | Description |
+| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| aggType | Selected aggregation type that will be set as the alert type property. |
+| aggField | Selected aggregation field that will be set as the alert type property. |
+| errors | List of errors with proper messages for the alert params that should be validated. In current component is validated `aggField`. |
+| onChangeSelectedAggField | Event handler that will be excuted if selected aggregation field is changed. |
+| fields | Fields list that will be available in the OF `Select a field` dropdown. |
+| customAggTypesOptions | (Optional) List of aggregation types that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/aggregation_types.ts`. |
+| popupPosition | (Optional) expression popup position. Default is `downRight`. Recommend changing it for a small parent window space. |
### GROUPED BY expression component
@@ -536,18 +537,18 @@ interface GroupByExpressionProps {
}
```
-|Property|Description|
-|---|---|
-|groupBy|Selected group by type that will be set as the alert type property.|
-|termSize|Selected term size that will be set as the alert type property.|
-|termField|Selected term field that will be set as the alert type property.|
-|errors|List of errors with proper messages for the alert params that should be validated. In current component is validated `termSize` and `termField`.|
-|onChangeSelectedTermSize|Event handler that will be excuted if selected term size is changed.|
-|onChangeSelectedTermField|Event handler that will be excuted if selected term field is changed.|
-|onChangeSelectedGroupBy|Event handler that will be excuted if selected group by is changed.|
-|fields|Fields list with options for the `termField` dropdown.|
-|customGroupByTypes|(Optional) List of group by types that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/group_by_types.ts`.|
-|popupPosition|(Optional) expression popup position. Default is `downLeft`. Recommend changing it for a small parent window space.|
+| Property | Description |
+| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| groupBy | Selected group by type that will be set as the alert type property. |
+| termSize | Selected term size that will be set as the alert type property. |
+| termField | Selected term field that will be set as the alert type property. |
+| errors | List of errors with proper messages for the alert params that should be validated. In current component is validated `termSize` and `termField`. |
+| onChangeSelectedTermSize | Event handler that will be excuted if selected term size is changed. |
+| onChangeSelectedTermField | Event handler that will be excuted if selected term field is changed. |
+| onChangeSelectedGroupBy | Event handler that will be excuted if selected group by is changed. |
+| fields | Fields list with options for the `termField` dropdown. |
+| customGroupByTypes | (Optional) List of group by types that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/group_by_types.ts`. |
+| popupPosition | (Optional) expression popup position. Default is `downLeft`. Recommend changing it for a small parent window space. |
### FOR THE LAST expression component
@@ -580,14 +581,14 @@ interface ForLastExpressionProps {
}
```
-|Property|Description|
-|---|---|
-|timeWindowSize|Selected time window size that will be set as the alert type property.|
-|timeWindowUnit|Selected time window unit that will be set as the alert type property.|
-|errors|List of errors with proper messages for the alert params that should be validated. In current component is validated `termWindowSize`.|
-|onChangeWindowSize|Event handler that will be excuted if selected window size is changed.|
-|onChangeWindowUnit|Event handler that will be excuted if selected window unit is changed.|
-|popupPosition|(Optional) expression popup position. Default is `downLeft`. Recommend changing it for a small parent window space.|
+| Property | Description |
+| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
+| timeWindowSize | Selected time window size that will be set as the alert type property. |
+| timeWindowUnit | Selected time window unit that will be set as the alert type property. |
+| errors | List of errors with proper messages for the alert params that should be validated. In current component is validated `termWindowSize`. |
+| onChangeWindowSize | Event handler that will be excuted if selected window size is changed. |
+| onChangeWindowUnit | Event handler that will be excuted if selected window unit is changed. |
+| popupPosition | (Optional) expression popup position. Default is `downLeft`. Recommend changing it for a small parent window space. |
### THRESHOLD expression component
@@ -623,15 +624,15 @@ interface ThresholdExpressionProps {
}
```
-|Property|Description|
-|---|---|
-|thresholdComparator|Selected time window size that will be set as the alert type property.|
-|threshold|Selected time window size that will be set as the alert type property.|
-|errors|List of errors with proper messages for the alert params that should be validated. In current component is validated `threshold0` and `threshold1`.|
-|onChangeSelectedThresholdComparator|Event handler that will be excuted if selected threshold comparator is changed.|
-|onChangeSelectedThreshold|Event handler that will be excuted if selected threshold is changed.|
-|customComparators|(Optional) List of comparators that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/comparators.ts`.|
-|popupPosition|(Optional) expression popup position. Default is `downLeft`. Recommend changing it for a small parent window space.|
+| Property | Description |
+| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| thresholdComparator | Selected time window size that will be set as the alert type property. |
+| threshold | Selected time window size that will be set as the alert type property. |
+| errors | List of errors with proper messages for the alert params that should be validated. In current component is validated `threshold0` and `threshold1`. |
+| onChangeSelectedThresholdComparator | Event handler that will be excuted if selected threshold comparator is changed. |
+| onChangeSelectedThreshold | Event handler that will be excuted if selected threshold is changed. |
+| customComparators | (Optional) List of comparators that replaces the default options defined in constants `x-pack/plugins/triggers_actions_ui/public/common/constants/comparators.ts`. |
+| popupPosition | (Optional) expression popup position. Default is `downLeft`. Recommend changing it for a small parent window space. |
## Alert Conditions Components
To aid in creating a uniform UX across Alert Types, we provide two components for specifying the conditions for detection of a certain alert under within any specific Action Groups:
@@ -767,19 +768,19 @@ const DEFAULT_THRESHOLDS: ThresholdAlertTypeParams['threshold] = {
This component will render the `Conditions` header & headline, along with the selectors for adding every Action Group you specity.
Additionally it will clone its `children` for _each_ action group which has a `condition` specified for it, passing in the appropriate `actionGroup` prop for each one.
-|Property|Description|
-|---|---|
-|headline|The headline title displayed above the fields |
-|actionGroups|A list of `ActionGroupWithCondition` which includes all the action group you wish to offer the user and what conditions they are already configured to follow|
-|onInitializeConditionsFor|A callback which is called when the user ask for a certain actionGroup to be initialized with an initial default condition. If you have no specific default, that's fine, as the component will render the action group's field even if the condition is empty (using a `null` or an `undefined`) and determines whether to render these fields by _the very presence_ of a `condition` field|
+| Property | Description |
+| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| headline | The headline title displayed above the fields |
+| actionGroups | A list of `ActionGroupWithCondition` which includes all the action group you wish to offer the user and what conditions they are already configured to follow |
+| onInitializeConditionsFor | A callback which is called when the user ask for a certain actionGroup to be initialized with an initial default condition. If you have no specific default, that's fine, as the component will render the action group's field even if the condition is empty (using a `null` or an `undefined`) and determines whether to render these fields by _the very presence_ of a `condition` field |
### The AlertConditionsGroup component
This component renders a standard EuiTitle foe each action group, wrapping the Alert Type specific component, in addition to a "reset" button which allows the user to reset the condition for that action group. The definition of what a _reset_ actually means is Alert Type specific, and up to the implementor to decide. In some case it might mean removing the condition, in others it might mean to reset it to some default value on the server side. In either case, it should _delete_ the `condition` field from the appropriate `actionGroup` as per the above example.
-|Property|Description|
-|---|---|
-|onResetConditionsFor|A callback which is called when the user clicks the _reset_ button besides the action group's title. The implementor should use this to remove the `condition` from the specified actionGroup|
+| Property | Description |
+| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| onResetConditionsFor | A callback which is called when the user clicks the _reset_ button besides the action group's title. The implementor should use this to remove the `condition` from the specified actionGroup |
## Embed the Create Alert flyout within any Kibana plugin
@@ -839,29 +840,29 @@ interface AlertAddProps {
}
```
-|Property|Description|
-|---|---|
-|consumer|Name of the plugin that creates an alert.|
-|addFlyoutVisible|Visibility state of the Create Alert flyout.|
-|setAddFlyoutVisibility|Function for changing visibility state of the Create Alert flyout.|
-|alertTypeId|Optional property to preselect alert type.|
-|canChangeTrigger|Optional property, that hides change alert type possibility.|
-|onSave|Optional function, which will be executed if alert was saved sucsessfuly.|
-|initialValues|Default values for Alert properties.|
-|metadata|Optional generic property, which allows to define component specific metadata. This metadata can be used for passing down preloaded data for Alert type expression component.|
+| Property | Description |
+| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| consumer | Name of the plugin that creates an alert. |
+| addFlyoutVisible | Visibility state of the Create Alert flyout. |
+| setAddFlyoutVisibility | Function for changing visibility state of the Create Alert flyout. |
+| alertTypeId | Optional property to preselect alert type. |
+| canChangeTrigger | Optional property, that hides change alert type possibility. |
+| onSave | Optional function, which will be executed if alert was saved sucsessfuly. |
+| initialValues | Default values for Alert properties. |
+| metadata | Optional generic property, which allows to define component specific metadata. This metadata can be used for passing down preloaded data for Alert type expression component. |
## Build and register Action Types
Kibana ships with a set of built-in action types UI:
-|Type|Id|Description|
-|---|---|---|
-|[Server log](#server-log)|`.log`|Logs messages to the Kibana log using `server.log()`|
-|[Email](#email)|`.email`|Sends an email using SMTP|
-|[Slack](#slack)|`.slack`|Posts a message to a Slack channel|
-|[Index](#index)|`.index`|Indexes document(s) into Elasticsearch|
-|[Webhook](#webhook)|`.webhook`|Sends a payload to a web service using HTTP POST or PUT|
-|[PagerDuty](#pagerduty)|`.pagerduty`|Triggers, resolves, or acknowledges an incident to a PagerDuty service|
+| Type | Id | Description |
+| ------------------------- | ------------ | ---------------------------------------------------------------------- |
+| [Server log](#server-log) | `.log` | Logs messages to the Kibana log using `server.log()` |
+| [Email](#email) | `.email` | Sends an email using SMTP |
+| [Slack](#slack) | `.slack` | Posts a message to a Slack channel |
+| [Index](#index) | `.index` | Indexes document(s) into Elasticsearch |
+| [Webhook](#webhook) | `.webhook` | Sends a payload to a web service using HTTP POST or PUT |
+| [PagerDuty](#pagerduty) | `.pagerduty` | Triggers, resolves, or acknowledges an incident to a PagerDuty service |
Every action type should be registered server side, and can be optionally registered client side.
Only action types registered on both client and server will be displayed in the Alerts and Actions UI.
@@ -889,9 +890,6 @@ export function getActionType(): ActionTypeModel {
defaultMessage: 'Send to Server log',
}
),
- validateConnector: (): Promise => {
- return { errors: {} };
- },
validateParams: (actionParams: ServerLogActionParams): Promise => {
// validation of action params implementation
},
@@ -930,9 +928,6 @@ export function getActionType(): ActionTypeModel {
defaultMessage: 'Send to email',
}
),
- validateConnector: (action: EmailActionConnector): Promise => {
- // validation of connector properties implementation
- },
validateParams: (actionParams: EmailActionParams): Promise => {
// validation of action params implementation
},
@@ -968,9 +963,6 @@ export function getActionType(): ActionTypeModel {
defaultMessage: 'Send to Slack',
}
),
- validateConnector: (action: SlackActionConnector): Promise => {
- // validation of connector properties implementation
- },
validateParams: (actionParams: SlackActionParams): Promise => {
// validation of action params implementation
},
@@ -1001,9 +993,6 @@ export function getActionType(): ActionTypeModel {
defaultMessage: 'Index data into Elasticsearch.',
}
),
- validateConnector: (): Promise => {
- return { errors: {} };
- },
actionConnectorFields: IndexActionConnectorFields,
actionParamsFields: IndexParamsFields,
validateParams: (): Promise => {
@@ -1047,9 +1036,6 @@ export function getActionType(): ActionTypeModel {
defaultMessage: 'Send a request to a web service.',
}
),
- validateConnector: (action: WebhookActionConnector): Promise => {
- // validation of connector properties implementation
- },
validateParams: (actionParams: WebhookActionParams): Promise => {
// validation of action params implementation
},
@@ -1087,9 +1073,6 @@ export function getActionType(): ActionTypeModel {
defaultMessage: 'Send to PagerDuty',
}
),
- validateConnector: (action: PagerDutyActionConnector): Promise => {
- // validation of connector properties implementation
- },
validateParams: (actionParams: PagerDutyActionParams): Promise => {
// validation of action params implementation
},
@@ -1114,22 +1097,20 @@ Each action type should be defined as an `ActionTypeModel` object with the follo
iconClass: IconType;
selectMessage: string;
actionTypeTitle?: string;
- validateConnector: (connector: any) => Promise;
validateParams: (actionParams: any) => Promise;
actionConnectorFields: React.FunctionComponent | null;
actionParamsFields: React.LazyExoticComponent>>;
customConnectorSelectItem?: CustomConnectorSelectionItem;
```
-|Property|Description|
-|---|---|
-|id|Action type id. Should be the same as on server side.|
-|iconClass|Setting for icon to be displayed to the user. EUI supports any known EUI icon, SVG URL, or a lazy loaded React component, ReactElement.|
-|selectMessage|Short description of action type responsibility, that will be displayed on the select card in UI.|
-|validateConnector|Validation function for action connector.|
-|validateParams|Validation function for action params.|
-|actionConnectorFields|A lazy loaded React component for building UI of current action type connector.|
-|actionParamsFields|A lazy loaded React component for building UI of current action type params. Displayed as a part of Create Alert flyout.|
-|customConnectorSelectItem|Optional, an object for customizing the selection row of the action connector form.|
+| Property | Description |
+| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
+| id | Action type id. Should be the same as on server side. |
+| iconClass | Setting for icon to be displayed to the user. EUI supports any known EUI icon, SVG URL, or a lazy loaded React component, ReactElement. |
+| selectMessage | Short description of action type responsibility, that will be displayed on the select card in UI. |
+| validateParams | Validation function for action params. |
+| actionConnectorFields | A lazy loaded React component for building UI of current action type connector. |
+| actionParamsFields | A lazy loaded React component for building UI of current action type params. Displayed as a part of Create Alert flyout. |
+| customConnectorSelectItem | Optional, an object for customizing the selection row of the action connector form. |
### CustomConnectorSelectionItem Properties
@@ -1139,10 +1120,10 @@ Each action type should be defined as an `ActionTypeModel` object with the follo
LazyExoticComponent | undefined;
```
-|Property|Description|
-|---|---|
-|getText|Function for returning the text to display for the row.|
-|getComponent|Function for returning a lazy loaded React component for customizing the selection row of the action connector form. Or undefined if if no customization is needed.|
+| Property | Description |
+| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| getText | Function for returning the text to display for the row. |
+| getComponent | Function for returning a lazy loaded React component for customizing the selection row of the action connector form. Or undefined if if no customization is needed. |
## Register action type model
@@ -1169,6 +1150,8 @@ Before starting the UI implementation, the [server side registration](https://gi
Action type UI is expected to be defined as `ActionTypeModel` object.
+The framework uses the [Form lib](https://github.com/elastic/kibana/blob/main/src/plugins/es_ui_shared/static/forms/docs/welcome.mdx). Please refer to the documentation of the library to learn more.
+
Below is a list of steps that should be done to build and register a new action type with the name `Example Action Type`:
1. At any suitable place in Kibana, create a file, which will expose an object implementing interface [ActionTypeModel]:
@@ -1202,24 +1185,6 @@ export function getActionType(): ActionTypeModel {
defaultMessage: 'Example Action',
}
),
- validateConnector: (action: ExampleActionConnector): Promise => {
- const validationResult = { errors: {} };
- const errors = {
- someConnectorField: new Array(),
- };
- validationResult.errors = errors;
- if (!action.config.someConnectorField) {
- errors.someConnectorField.push(
- i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSomeConnectorFieldeText',
- {
- defaultMessage: 'SomeConnectorField is required.',
- }
- )
- );
- }
- return validationResult;
- },
validateParams: (actionParams: ExampleActionParams): Promise => {
const validationResult = { errors: {} };
const errors = {
@@ -1248,42 +1213,39 @@ export function getActionType(): ActionTypeModel {
```
import React from 'react';
import { i18n } from '@kbn/i18n';
-import { EuiFieldText } from '@elastic/eui';
-import { EuiTextArea } from '@elastic/eui';
-import {
- ActionTypeModel,
- ValidationResult,
- ActionConnectorFieldsProps,
- ActionParamsProps,
-} from '../../../types';
+import { FieldConfig, UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { TextField } from '@kbn/es-ui-shared-plugin/static/forms/components';
+import { ActionConnectorFieldsProps } from '../../../types';
-interface ExampleActionConnector {
- config: {
- someConnectorField: string;
- };
-}
+const { emptyField } = fieldValidators;
-const ExampleConnectorFields: React.FunctionComponent> = ({ action, editActionConfig, errors }) => {
- const { someConnectorField } = action.config;
- return (
- <>
- 0 && someConnectorField !== undefined}
- name="someConnectorField"
- value={someConnectorField || ''}
- onChange={e => {
- editActionConfig('someConnectorField', e.target.value);
- }}
- onBlur={() => {
- if (!someConnectorField) {
- editActionConfig('someConnectorField', '');
+const fieldConfig: FieldConfig = {
+ label: 'My field',
+ validations: [
+ {
+ validator: emptyField(
+ i18n.translate(
+ 'xpack.triggersActionsUI.sections.actionConnectorForm.error.requiredField',
+ {
+ defaultMessage: 'Field is required.',
}
+ )
+ ),
+ },
+ ],
+};
+
+const ExampleConnectorFields: React.FunctionComponent = ({ isEdit, readOnly, registerPreSubmitValidator }) => {
+ return (
+
- >
);
};
@@ -1466,35 +1428,35 @@ interface ActionAccordionFormProps {
```
-|Property|Description|
-|---|---|
-|actions|List of actions comes from alert.actions property.|
-|defaultActionGroupId|Default action group id to which each new action will belong by default.|
-|actionGroups|Optional. List of action groups to which new action can be assigned. The RunWhen field is only displayed when these action groups are specified|
-|setActionIdByIndex|Function for changing action 'id' by the proper index in alert.actions array.|
-|setActionGroupIdByIndex|Function for changing action 'group' by the proper index in alert.actions array.|
-|setRuleProperty|Function for changing alert property 'actions'. Used when deleting action from the array to reset it.|
-|setActionParamsProperty|Function for changing action key/value property by index in alert.actions array.|
-|http|HttpSetup needed for executing API calls.|
-|actionTypeRegistry|Registry for action types.|
-|toastNotifications|Toast messages Plugin Setup Contract.|
-|docLinks|Documentation links Plugin Start Contract.|
-|actionTypes|Optional property, which allows to define a list of available actions specific for a current plugin.|
-|messageVariables|Optional property, which allows to define a list of variables for action 'message' property. Set `useWithTripleBracesInTemplates` to true if you don't want the variable escaped when rendering.|
-|defaultActionMessage|Optional property, which allows to define a message value for action with 'message' property.|
-|capabilities|Kibana core's Capabilities ApplicationStart['capabilities'].|
-
-|Property|Description|
-|---|---|
-|onSave|Optional function, which will be executed if alert was saved sucsessfuly.|
-|http|HttpSetup needed for executing API calls.|
-|ruleTypeRegistry|Registry for alert types.|
-|actionTypeRegistry|Registry for action types.|
-|uiSettings|Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring.|
-|docLinks|Documentation Links, needed to link to the documentation from informational callouts.|
-|toastNotifications|Toast messages.|
-|charts|Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring.|
-|dataFieldsFormats|Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring.|
+| Property | Description |
+| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| actions | List of actions comes from alert.actions property. |
+| defaultActionGroupId | Default action group id to which each new action will belong by default. |
+| actionGroups | Optional. List of action groups to which new action can be assigned. The RunWhen field is only displayed when these action groups are specified |
+| setActionIdByIndex | Function for changing action 'id' by the proper index in alert.actions array. |
+| setActionGroupIdByIndex | Function for changing action 'group' by the proper index in alert.actions array. |
+| setRuleProperty | Function for changing alert property 'actions'. Used when deleting action from the array to reset it. |
+| setActionParamsProperty | Function for changing action key/value property by index in alert.actions array. |
+| http | HttpSetup needed for executing API calls. |
+| actionTypeRegistry | Registry for action types. |
+| toastNotifications | Toast messages Plugin Setup Contract. |
+| docLinks | Documentation links Plugin Start Contract. |
+| actionTypes | Optional property, which allows to define a list of available actions specific for a current plugin. |
+| messageVariables | Optional property, which allows to define a list of variables for action 'message' property. Set `useWithTripleBracesInTemplates` to true if you don't want the variable escaped when rendering. |
+| defaultActionMessage | Optional property, which allows to define a message value for action with 'message' property. |
+| capabilities | Kibana core's Capabilities ApplicationStart['capabilities']. |
+
+| Property | Description |
+| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
+| onSave | Optional function, which will be executed if alert was saved sucsessfuly. |
+| http | HttpSetup needed for executing API calls. |
+| ruleTypeRegistry | Registry for alert types. |
+| actionTypeRegistry | Registry for action types. |
+| uiSettings | Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring. |
+| docLinks | Documentation Links, needed to link to the documentation from informational callouts. |
+| toastNotifications | Toast messages. |
+| charts | Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring. |
+| dataFieldsFormats | Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring. |
## Embed the Create Connector flyout within any Kibana plugin
@@ -1517,10 +1479,11 @@ Then this dependency will be used to embed Create Connector flyout or register n
2. Add Create Connector flyout to React component:
```
// import section
-import { ActionsConnectorsContextProvider, ConnectorAddFlyout } from '../../../../../../../triggers_actions_ui/public';
+import { ActionsConnectorsContextProvider, CreateConnectorFlyout } from '../../../../../../../triggers_actions_ui/public';
// in the component state definition section
const [addFlyoutVisible, setAddFlyoutVisibility] = useState(false);
+const onClose = useCallback(() => setAddFlyoutVisibility(false), []);
// load required dependancied
const { http, triggersActionsUi, notifications, application, docLinks } = useKibana().services;
@@ -1549,35 +1512,38 @@ const connector = {
// in render section of component
-
```
-ConnectorAddFlyout Props definition:
+CreateConnectorFlyout Props definition:
```
export interface ConnectorAddFlyoutProps {
- addFlyoutVisible: boolean;
- setAddFlyoutVisibility: React.Dispatch>;
- actionTypes?: ActionType[];
+ actionTypeRegistry: ActionTypeRegistryContract;
+ onClose: () => void;
+ supportedActionTypes?: ActionType[];
+ onConnectorCreated?: (connector: ActionConnector) => void;
+ onTestConnector?: (connector: ActionConnector) => void;
}
```
-|Property|Description|
-|---|---|
-|addFlyoutVisible|Visibility state of the Create Connector flyout.|
-|setAddFlyoutVisibility|Function for changing visibility state of the Create Connector flyout.|
-|actionTypes|Optional property, that allows to define only specific action types list which is available for a current plugin.|
+| Property | Description |
+| -------------------- | ----------------------------------------------------------------------------------------------------------------- |
+| actionTypeRegistry | The action type registry. |
+| onClose | Called when closing the flyout |
+| supportedActionTypes | Optional property, that allows to define only specific action types list which is available for a current plugin. |
+| onConnectorCreated | Optional property. Function to be called after the creation of the connector. |
+| onTestConnector | Optional property. Function to be called when the user press the Save & Test button. |
## Embed the Edit Connector flyout within any Kibana plugin
@@ -1600,7 +1566,7 @@ Then this dependency will be used to embed Edit Connector flyout.
2. Add Create Connector flyout to React component:
```
// import section
-import { ActionsConnectorsContextProvider, ConnectorEditFlyout } from '../../../../../../../triggers_actions_ui/public';
+import { ActionsConnectorsContextProvider, EditConnectorFlyout } from '../../../../../../../triggers_actions_ui/public';
// in the component state definition section
const [editFlyoutVisible, setEditFlyoutVisibility] = useState(false);
@@ -1622,31 +1588,32 @@ const { http, triggersActionsUi, notifications, application } = useKibana().serv
// in render section of component
-
```
-ConnectorEditFlyout Props definition:
+EditConnectorFlyout Props definition:
```
export interface ConnectorEditProps {
- initialConnector: ActionConnector;
+ actionTypeRegistry: ActionTypeRegistryContract;
+ connector: ActionConnector;
onClose: () => void;
- tab?: EditConectorTabs;
- reloadConnectors?: () => Promise;
- consumer?: string;
+ tab?: EditConnectorTabs;
+ onConnectorUpdated?: (connector: ActionConnector) => void;
}
```
-|Property|Description|
-|---|---|
-|initialConnector|Property, that allows to define the initial state of edited connector.|
-|editFlyoutVisible|Visibility state of the Edit Connector flyout.|
-|setEditFlyoutVisibility|Function for changing visibility state of the Edit Connector flyout.|
+| Property | Description |
+| ------------------ | --------------------------------------------------------------------------- |
+| actionTypeRegistry | The action type registry. |
+| connector | Property, that allows to define the initial state of edited connector. |
+| onClose | Called when closing the flyout |
+| onConnectorUpdated | Optional property. Function to be called after the update of the connector. |
ActionsConnectorsContextValue options:
```
@@ -1662,10 +1629,10 @@ export interface ActionsConnectorsContextValue {
}
```
-|Property|Description|
-|---|---|
-|http|HttpSetup needed for executing API calls.|
-|actionTypeRegistry|Registry for action types.|
-|capabilities|Property, which is defining action current user usage capabilities like canSave or canDelete.|
-|toastNotifications|Toast messages.|
-|reloadConnectors|Optional function, which will be executed if connector was saved sucsessfuly, like reload list of connecotrs.|
+| Property | Description |
+| ------------------ | ------------------------------------------------------------------------------------------------------------- |
+| http | HttpSetup needed for executing API calls. |
+| actionTypeRegistry | Registry for action types. |
+| capabilities | Property, which is defining action current user usage capabilities like canSave or canDelete. |
+| toastNotifications | Toast messages. |
+| reloadConnectors | Optional function, which will be executed if connector was saved sucsessfuly, like reload list of connecotrs. |
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/action_type_registry.mock.ts b/x-pack/plugins/triggers_actions_ui/public/application/action_type_registry.mock.ts
index 3c5c1b551028e..8260f208f353d 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/action_type_registry.mock.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/action_type_registry.mock.ts
@@ -31,7 +31,6 @@ const createMockActionTypeModel = (actionType: Partial = {}): A
id,
iconClass: `iconClass-${id}`,
selectMessage: `selectMessage-${id}`,
- validateConnector: jest.fn(),
validateParams: jest.fn(),
actionConnectorFields: null,
actionParamsFields: mockedActionParamsFields,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
index c4c273bd003c5..d47529c47c19d 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx
@@ -23,6 +23,7 @@ import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';
import { Storage } from '@kbn/kibana-utils-plugin/public';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
+import { ActionsPublicPluginSetup } from '@kbn/actions-plugin/public';
import { suspendedComponentWithProps } from './lib/suspended_component_with_props';
import {
ActionTypeRegistryContract,
@@ -32,7 +33,8 @@ import {
import { Section, routeToRuleDetails, legacyRouteToRuleDetails } from './constants';
import { setDataViewsService } from '../common/lib/data_apis';
-import { KibanaContextProvider } from '../common/lib/kibana';
+import { KibanaContextProvider, useKibana } from '../common/lib/kibana';
+import { ConnectorProvider } from './context/connector_context';
const TriggersActionsUIHome = lazy(() => import('./home'));
const RuleDetailsRoute = lazy(
@@ -40,6 +42,7 @@ const RuleDetailsRoute = lazy(
);
export interface TriggersAndActionsUiServices extends CoreStart {
+ actions: ActionsPublicPluginSetup;
data: DataPublicPluginStart;
dataViews: DataViewsPublicPluginStart;
charts: ChartsPluginStart;
@@ -89,23 +92,29 @@ export const App = ({ deps }: { deps: TriggersAndActionsUiServices }) => {
};
export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) => {
+ const {
+ actions: { validateEmailAddresses },
+ } = useKibana().services;
+
return (
-
-
-
- }
- />
-
-
-
+
+
+
+
+ }
+ />
+
+
+
+
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx
index 31ff85a20ed3b..9e61d84f5fb31 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx
@@ -8,7 +8,6 @@
import { TypeRegistry } from '../../../type_registry';
import { registerBuiltInActionTypes } from '..';
import { ActionTypeModel } from '../../../../types';
-import { EmailActionConnector } from '../types';
import { getEmailServices } from './email';
import {
ValidatedEmail,
@@ -73,301 +72,6 @@ describe('getEmailServices', () => {
});
});
-describe('connector validation', () => {
- test('connector validation succeeds when connector config is valid', async () => {
- const actionConnector = {
- secrets: {
- user: 'user',
- password: 'pass',
- clientSecret: null,
- },
- id: 'test',
- actionTypeId: '.email',
- name: 'email',
- isPreconfigured: false,
- isDeprecated: false,
- config: {
- from: 'test@test.com',
- port: 2323,
- host: 'localhost',
- test: 'test',
- hasAuth: true,
- service: 'other',
- },
- } as EmailActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- from: [],
- port: [],
- host: [],
- service: [],
- clientId: [],
- tenantId: [],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: [],
- clientSecret: [],
- },
- },
- });
- });
-
- test('connector validation succeeds when connector config is valid with empty user/password', async () => {
- const actionConnector = {
- secrets: {
- user: null,
- password: null,
- clientSecret: null,
- },
- id: 'test',
- actionTypeId: '.email',
- isPreconfigured: false,
- isDeprecated: false,
- name: 'email',
- config: {
- from: 'test@test.com',
- port: 2323,
- host: 'localhost',
- test: 'test',
- hasAuth: false,
- service: 'other',
- },
- } as EmailActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- from: [],
- port: [],
- host: [],
- service: [],
- clientId: [],
- tenantId: [],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: [],
- clientSecret: [],
- },
- },
- });
- });
- test('connector validation fails when connector config is not valid', async () => {
- const actionConnector = {
- secrets: {
- user: 'user',
- password: 'pass',
- },
- id: 'test',
- actionTypeId: '.email',
- name: 'email',
- config: {
- from: 'test@notallowed.com',
- hasAuth: true,
- service: 'other',
- },
- } as EmailActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- from: ['Email address test@notallowed.com is not allowed.'],
- port: ['Port is required.'],
- host: ['Host is required.'],
- service: [],
- clientId: [],
- tenantId: [],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: [],
- clientSecret: [],
- },
- },
- });
-
- // also check that mustache is not valid
- actionConnector.config.from = '{{mustached}}';
- const validation = await actionTypeModel.validateConnector(actionConnector);
- expect(validation?.config?.errors?.from).toEqual(['Email address {{mustached}} is not valid.']);
- });
-
- test('connector validation fails when user specified but not password', async () => {
- const actionConnector = {
- secrets: {
- user: 'user',
- password: null,
- clientSecret: null,
- },
- id: 'test',
- actionTypeId: '.email',
- isPreconfigured: false,
- isDeprecated: false,
- name: 'email',
- config: {
- from: 'test@test.com',
- port: 2323,
- host: 'localhost',
- test: 'test',
- hasAuth: true,
- service: 'other',
- },
- } as EmailActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- from: [],
- port: [],
- host: [],
- service: [],
- clientId: [],
- tenantId: [],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: ['Password is required when username is used.'],
- clientSecret: [],
- },
- },
- });
- });
- test('connector validation fails when password specified but not user', async () => {
- const actionConnector = {
- secrets: {
- user: null,
- password: 'password',
- clientSecret: null,
- },
- id: 'test',
- actionTypeId: '.email',
- isPreconfigured: false,
- isDeprecated: false,
- name: 'email',
- config: {
- from: 'test@test.com',
- port: 2323,
- host: 'localhost',
- test: 'test',
- hasAuth: true,
- service: 'other',
- },
- } as EmailActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- from: [],
- port: [],
- host: [],
- service: [],
- clientId: [],
- tenantId: [],
- },
- },
- secrets: {
- errors: {
- user: ['Username is required when password is used.'],
- password: [],
- clientSecret: [],
- },
- },
- });
- });
- test('connector validation fails when server type is not selected', async () => {
- const actionConnector = {
- secrets: {
- user: 'user',
- password: 'password',
- },
- id: 'test',
- actionTypeId: '.email',
- isPreconfigured: false,
- isDeprecated: false,
- name: 'email',
- config: {
- from: 'test@test.com',
- port: 2323,
- host: 'localhost',
- test: 'test',
- hasAuth: true,
- },
- };
-
- expect(
- await actionTypeModel.validateConnector(actionConnector as unknown as EmailActionConnector)
- ).toEqual({
- config: {
- errors: {
- from: [],
- port: [],
- host: [],
- service: ['Service is required.'],
- clientId: [],
- tenantId: [],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: [],
- clientSecret: [],
- },
- },
- });
- });
- test('connector validation fails when for exchange service selected, but clientId, tenantId and clientSecrets were not defined', async () => {
- const actionConnector = {
- secrets: {
- user: 'user',
- password: 'pass',
- clientSecret: null,
- },
- id: 'test',
- actionTypeId: '.email',
- name: 'email',
- isPreconfigured: false,
- isDeprecated: false,
- config: {
- from: 'test@test.com',
- hasAuth: true,
- service: 'exchange_server',
- },
- } as EmailActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- from: [],
- port: [],
- host: [],
- service: [],
- clientId: ['Client ID is required.'],
- tenantId: ['Tenant ID is required.'],
- },
- },
- secrets: {
- errors: {
- clientSecret: ['Client Secret is required.'],
- password: [],
- user: [],
- },
- },
- });
- });
-});
-
describe('action params validation', () => {
test('action params validation succeeds when action params is valid', async () => {
const actionParams = {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx
index 0add2396a74d0..b44d13fb02ec1 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx
@@ -9,13 +9,9 @@ import { uniq } from 'lodash';
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiSelectOption } from '@elastic/eui';
-import { AdditionalEmailServices, InvalidEmailReason } from '@kbn/actions-plugin/common';
-import {
- ActionTypeModel,
- ConnectorValidationResult,
- GenericValidationResult,
-} from '../../../../types';
-import { EmailActionParams, EmailConfig, EmailSecrets, EmailActionConnector } from '../types';
+import { InvalidEmailReason } from '@kbn/actions-plugin/common';
+import { ActionTypeModel, GenericValidationResult } from '../../../../types';
+import { EmailActionParams, EmailConfig, EmailSecrets } from '../types';
import { RegistrationServices } from '..';
const emailServices: EuiSelectOption[] = [
@@ -99,84 +95,6 @@ export function getActionType(
defaultMessage: 'Send to email',
}
),
- validateConnector: async (
- action: EmailActionConnector
- ): Promise<
- ConnectorValidationResult, EmailSecrets>
- > => {
- const translations = await import('./translations');
- const configErrors = {
- from: new Array(),
- port: new Array(),
- host: new Array(),
- service: new Array(),
- clientId: new Array(),
- tenantId: new Array(),
- };
- const secretsErrors = {
- user: new Array(),
- password: new Array(),
- clientSecret: new Array(),
- };
-
- const validationResult = {
- config: { errors: configErrors },
- secrets: { errors: secretsErrors },
- };
- if (!action.config.from) {
- configErrors.from.push(translations.SENDER_REQUIRED);
- } else {
- const validatedEmail = services.validateEmailAddresses([action.config.from])[0];
- if (!validatedEmail.valid) {
- const message =
- validatedEmail.reason === InvalidEmailReason.notAllowed
- ? translations.getNotAllowedEmailAddress(action.config.from)
- : translations.getInvalidEmailAddress(action.config.from);
- configErrors.from.push(message);
- }
- }
- if (action.config.service !== AdditionalEmailServices.EXCHANGE) {
- if (!action.config.port) {
- configErrors.port.push(translations.PORT_REQUIRED);
- }
- if (!action.config.host) {
- configErrors.host.push(translations.HOST_REQUIRED);
- }
- if (action.config.hasAuth && !action.secrets.user && !action.secrets.password) {
- secretsErrors.user.push(translations.USERNAME_REQUIRED);
- }
- if (action.config.hasAuth && !action.secrets.user && !action.secrets.password) {
- secretsErrors.password.push(translations.PASSWORD_REQUIRED);
- }
- } else {
- if (!action.config.clientId) {
- configErrors.clientId.push(translations.CLIENT_ID_REQUIRED);
- }
- if (!action.config.tenantId) {
- configErrors.tenantId.push(translations.TENANT_ID_REQUIRED);
- }
- if (!action.secrets.clientSecret) {
- secretsErrors.clientSecret.push(translations.CLIENT_SECRET_REQUIRED);
- }
- }
- if (!action.config.service) {
- configErrors.service.push(translations.SERVICE_REQUIRED);
- }
- if (action.secrets.user && !action.secrets.password) {
- secretsErrors.password.push(translations.PASSWORD_REQUIRED_FOR_USER_USED);
- }
- if (!action.secrets.user && action.secrets.password) {
- secretsErrors.user.push(
- i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredUserText',
- {
- defaultMessage: 'Username is required when password is used.',
- }
- )
- );
- }
- return validationResult;
- },
validateParams: async (
actionParams: EmailActionParams
): Promise> => {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx
index 742e8981a1a1a..b06340c7a180d 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx
@@ -5,16 +5,25 @@
* 2.0.
*/
-import React from 'react';
+import React, { Suspense } from 'react';
import { mountWithIntl } from '@kbn/test-jest-helpers';
-import { EmailActionConnector } from '../types';
+import { act } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { useKibana } from '../../../../common/lib/kibana';
import EmailActionConnectorFields from './email_connector';
import * as hooks from './use_email_config';
+import {
+ AppMockRenderer,
+ ConnectorFormTestProvider,
+ createAppMockRenderer,
+ waitForComponentToUpdate,
+} from '../test_utils';
jest.mock('../../../../common/lib/kibana');
+const useKibanaMock = useKibana as jest.Mocked;
-describe('EmailActionConnectorFields renders', () => {
- test('all connector fields is rendered', () => {
+describe('EmailActionConnectorFields', () => {
+ test('all connector fields are rendered', async () => {
const actionConnector = {
secrets: {
user: 'user',
@@ -27,18 +36,21 @@ describe('EmailActionConnectorFields renders', () => {
from: 'test@test.com',
hasAuth: true,
},
- } as EmailActionConnector;
+ isDeprecated: false,
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
+
+ await waitForComponentToUpdate();
+
expect(wrapper.find('[data-test-subj="emailFromInput"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="emailFromInput"]').first().prop('value')).toBe(
'test@test.com'
@@ -50,7 +62,7 @@ describe('EmailActionConnectorFields renders', () => {
expect(wrapper.find('[data-test-subj="emailPasswordInput"]').length > 0).toBeTruthy();
});
- test('secret connector fields is not rendered when hasAuth false', () => {
+ test('secret connector fields are not rendered when hasAuth false', async () => {
const actionConnector = {
secrets: {},
id: 'test',
@@ -60,18 +72,21 @@ describe('EmailActionConnectorFields renders', () => {
from: 'test@test.com',
hasAuth: false,
},
- } as EmailActionConnector;
+ isDeprecated: false,
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
+
+ await waitForComponentToUpdate();
+
expect(wrapper.find('[data-test-subj="emailFromInput"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="emailFromInput"]').first().prop('value')).toBe(
'test@test.com'
@@ -82,7 +97,7 @@ describe('EmailActionConnectorFields renders', () => {
expect(wrapper.find('[data-test-subj="emailPasswordInput"]').length > 0).toBeFalsy();
});
- test('service field defaults to empty when not defined', () => {
+ test('service field defaults to empty when not defined', async () => {
const actionConnector = {
secrets: {
user: 'user',
@@ -95,18 +110,21 @@ describe('EmailActionConnectorFields renders', () => {
from: 'test@test.com',
hasAuth: true,
},
- } as EmailActionConnector;
+ isDeprecated: false,
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
+
+ await waitForComponentToUpdate();
+
expect(wrapper.find('[data-test-subj="emailFromInput"]').first().prop('value')).toBe(
'test@test.com'
);
@@ -116,7 +134,7 @@ describe('EmailActionConnectorFields renders', () => {
);
});
- test('service field is correctly selected when defined', () => {
+ test('service field are correctly selected when defined', async () => {
const actionConnector = {
secrets: {
user: 'user',
@@ -130,28 +148,35 @@ describe('EmailActionConnectorFields renders', () => {
hasAuth: true,
service: 'gmail',
},
- } as EmailActionConnector;
+ isDeprecated: false,
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
+
+ await waitForComponentToUpdate();
+
expect(wrapper.find('[data-test-subj="emailServiceSelectInput"]').length > 0).toBeTruthy();
expect(wrapper.find('select[data-test-subj="emailServiceSelectInput"]').prop('value')).toEqual(
'gmail'
);
});
- test('host, port and secure fields should be disabled when service field is set to well known service', () => {
+ test('host, port and secure fields should be disabled when service field is set to well known service', async () => {
+ const getEmailServiceConfig = jest
+ .fn()
+ .mockResolvedValue({ host: 'https://example.com', port: 80, secure: false });
jest
.spyOn(hooks, 'useEmailConfig')
- .mockImplementation(() => ({ emailServiceConfigurable: false, setEmailService: jest.fn() }));
+ .mockImplementation(() => ({ isLoading: false, getEmailServiceConfig }));
+
const actionConnector = {
secrets: {
user: 'user',
@@ -165,18 +190,22 @@ describe('EmailActionConnectorFields renders', () => {
hasAuth: true,
service: 'gmail',
},
- } as EmailActionConnector;
+ isDeprecated: false,
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
+
+ await waitForComponentToUpdate();
+
+ wrapper.update();
expect(wrapper.find('[data-test-subj="emailHostInput"]').first().prop('disabled')).toBe(true);
expect(wrapper.find('[data-test-subj="emailPortInput"]').first().prop('disabled')).toBe(true);
expect(wrapper.find('[data-test-subj="emailSecureSwitch"]').first().prop('disabled')).toBe(
@@ -184,10 +213,14 @@ describe('EmailActionConnectorFields renders', () => {
);
});
- test('host, port and secure fields should not be disabled when service field is set to other', () => {
+ test('host, port and secure fields should not be disabled when service field is set to other', async () => {
+ const getEmailServiceConfig = jest
+ .fn()
+ .mockResolvedValue({ host: 'https://example.com', port: 80, secure: false });
jest
.spyOn(hooks, 'useEmailConfig')
- .mockImplementation(() => ({ emailServiceConfigurable: true, setEmailService: jest.fn() }));
+ .mockImplementation(() => ({ isLoading: false, getEmailServiceConfig }));
+
const actionConnector = {
secrets: {
user: 'user',
@@ -201,18 +234,21 @@ describe('EmailActionConnectorFields renders', () => {
hasAuth: true,
service: 'other',
},
- } as EmailActionConnector;
+ isDeprecated: false,
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
+
+ await waitForComponentToUpdate();
+
expect(wrapper.find('[data-test-subj="emailHostInput"]').first().prop('disabled')).toBe(false);
expect(wrapper.find('[data-test-subj="emailPortInput"]').first().prop('disabled')).toBe(false);
expect(wrapper.find('[data-test-subj="emailSecureSwitch"]').first().prop('disabled')).toBe(
@@ -220,75 +256,436 @@ describe('EmailActionConnectorFields renders', () => {
);
});
- test('should display a message to remember username and password when creating a connector with authentication', () => {
- const actionConnector = {
- actionTypeId: '.email',
- config: {
- hasAuth: true,
- },
- secrets: {},
- } as EmailActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0);
- });
+ describe('Validation', () => {
+ let appMockRenderer: AppMockRenderer;
+ const onSubmit = jest.fn();
+ const validateEmailAddresses = jest.fn();
- test('should display a message for missing secrets after import', () => {
- const actionConnector = {
- actionTypeId: '.email',
- config: {
- hasAuth: true,
- },
- isMissingSecrets: true,
- secrets: {},
- } as EmailActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0);
- });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ appMockRenderer = createAppMockRenderer();
+ validateEmailAddresses.mockReturnValue([{ valid: true }]);
+ });
- test('should display a message when editing an authenticated email connector explaining why username and password must be re-entered', () => {
- const actionConnector = {
- secrets: {},
- id: 'test',
- actionTypeId: '.email',
- name: 'email',
- config: {
- from: 'test@test.com',
- hasAuth: true,
- },
- } as EmailActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+ it('submits the connector', async () => {
+ const actionConnector = {
+ secrets: {
+ user: 'user',
+ password: 'pass',
+ clientSecret: null,
+ },
+ id: 'test',
+ actionTypeId: '.email',
+ name: 'email',
+ config: {
+ from: 'test@test.com',
+ port: 2323,
+ host: 'localhost',
+ test: 'test',
+ hasAuth: true,
+ service: 'other',
+ },
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ await waitForComponentToUpdate();
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ actionTypeId: '.email',
+ config: {
+ from: 'test@test.com',
+ hasAuth: true,
+ host: 'localhost',
+ port: 2323,
+ secure: false,
+ service: 'other',
+ },
+ id: 'test',
+ isDeprecated: false,
+ name: 'email',
+ secrets: {
+ user: 'user',
+ password: 'pass',
+ },
+ },
+ isValid: true,
+ });
+ });
+
+ it('submits the connector with auth false', async () => {
+ const actionConnector = {
+ secrets: {
+ user: null,
+ password: null,
+ clientSecret: null,
+ },
+ id: 'test',
+ actionTypeId: '.email',
+ name: 'email',
+ config: {
+ from: 'test@test.com',
+ port: 2323,
+ host: 'localhost',
+ test: 'test',
+ hasAuth: false,
+ service: 'other',
+ },
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ await waitForComponentToUpdate();
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ actionTypeId: '.email',
+ config: {
+ from: 'test@test.com',
+ port: 2323,
+ host: 'localhost',
+ hasAuth: false,
+ service: 'other',
+ secure: false,
+ },
+ id: 'test',
+ isDeprecated: false,
+ name: 'email',
+ },
+ isValid: true,
+ });
+ });
+
+ it('connector validation fails when connector config is not valid', async () => {
+ useKibanaMock().services.actions.validateEmailAddresses = jest
+ .fn()
+ .mockReturnValue([{ valid: false }]);
+ const actionConnector = {
+ secrets: {
+ user: 'user',
+ password: 'pass',
+ },
+ id: 'test',
+ actionTypeId: '.email',
+ name: 'email',
+ config: {
+ from: 'test@notallowed.com',
+ hasAuth: true,
+ service: 'other',
+ },
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ await waitForComponentToUpdate();
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {},
+ isValid: false,
+ });
+ });
+
+ it('connector validation fails when user specified but not password', async () => {
+ const actionConnector = {
+ secrets: {
+ user: 'user',
+ password: '',
+ clientSecret: null,
+ },
+ id: 'test',
+ actionTypeId: '.email',
+ isPreconfigured: false,
+ isDeprecated: false,
+ name: 'email',
+ config: {
+ from: 'test@test.com',
+ port: 2323,
+ host: 'localhost',
+ test: 'test',
+ hasAuth: true,
+ service: 'other',
+ },
+ };
+
+ const { getByTestId } = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ await waitForComponentToUpdate();
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {},
+ isValid: false,
+ });
+ });
+
+ it('connector validation fails when server type is not selected', async () => {
+ const actionConnector = {
+ secrets: {
+ user: 'user',
+ password: 'password',
+ },
+ id: 'test',
+ actionTypeId: '.email',
+ isPreconfigured: false,
+ isDeprecated: false,
+ name: 'email',
+ config: {
+ from: 'test@test.com',
+ port: 2323,
+ host: 'localhost',
+ test: 'test',
+ hasAuth: true,
+ },
+ };
+
+ const { getByTestId } = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ await waitForComponentToUpdate();
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {},
+ isValid: false,
+ });
+ });
+
+ it('connector validation fails when exchange service is selected, but clientId, tenantId and clientSecrets were not defined', async () => {
+ const actionConnector = {
+ secrets: {
+ user: 'user',
+ password: 'pass',
+ clientSecret: null,
+ },
+ id: 'test',
+ actionTypeId: '.email',
+ name: 'email',
+ isPreconfigured: false,
+ isDeprecated: false,
+ config: {
+ from: 'test@test.com',
+ hasAuth: true,
+ service: 'exchange_server',
+ },
+ };
+
+ const { getByTestId } = appMockRenderer.render(
+
+
+ {}}
+ />
+
+
+ );
+
+ await waitForComponentToUpdate();
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {},
+ isValid: false,
+ });
+ });
+
+ it.each([[123.5], ['123.5']])(
+ 'connector validation fails when port is not an integer: %p',
+ async (port) => {
+ const actionConnector = {
+ secrets: {
+ user: 'user',
+ password: 'pass',
+ },
+ id: 'test',
+ actionTypeId: '.email',
+ name: 'email',
+ config: {
+ from: 'test@notallowed.com',
+ hasAuth: true,
+ service: 'other',
+ host: 'my-host',
+ port,
+ },
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ await waitForComponentToUpdate();
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {},
+ isValid: false,
+ });
+ }
);
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0);
+
+ it.each([[123], ['123']])('connector validation pass when port is valid: %p', async (port) => {
+ const actionConnector = {
+ secrets: {
+ user: 'user',
+ password: 'pass',
+ },
+ id: 'test',
+ actionTypeId: '.email',
+ name: 'email',
+ config: {
+ from: 'test@notallowed.com',
+ hasAuth: true,
+ service: 'other',
+ host: 'my-host',
+ port,
+ },
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ await waitForComponentToUpdate();
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ actionTypeId: '.email',
+ config: {
+ from: 'test@notallowed.com',
+ hasAuth: true,
+ host: 'my-host',
+ port,
+ secure: false,
+ service: 'other',
+ },
+ id: 'test',
+ isDeprecated: false,
+ name: 'email',
+ secrets: {
+ password: 'pass',
+ user: 'user',
+ },
+ },
+ isValid: true,
+ });
+ });
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx
index e870e0ef38439..c2a27ab8bae23 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx
@@ -5,314 +5,244 @@
* 2.0.
*/
-import React, { lazy, useEffect } from 'react';
-import {
- EuiFieldText,
- EuiFlexItem,
- EuiFlexGroup,
- EuiFieldNumber,
- EuiFieldPassword,
- EuiSelect,
- EuiSwitch,
- EuiFormRow,
- EuiTitle,
- EuiSpacer,
-} from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
+import React, { lazy, useEffect, useMemo } from 'react';
+import { isEmpty } from 'lodash';
+import { EuiFlexItem, EuiFlexGroup, EuiTitle, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiLink } from '@elastic/eui';
-import { AdditionalEmailServices } from '@kbn/actions-plugin/common';
+import { AdditionalEmailServices, InvalidEmailReason } from '@kbn/actions-plugin/common';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { ActionsPublicPluginSetup } from '@kbn/actions-plugin/public';
+import {
+ UseField,
+ useFormContext,
+ useFormData,
+ FieldConfig,
+} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import {
+ NumericField,
+ SelectField,
+ TextField,
+ ToggleField,
+} from '@kbn/es-ui-shared-plugin/static/forms/components';
import { ActionConnectorFieldsProps } from '../../../../types';
-import { EmailActionConnector } from '../types';
import { useKibana } from '../../../../common/lib/kibana';
-import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label';
import { getEmailServices } from './email';
import { useEmailConfig } from './use_email_config';
+import { PasswordField } from '../../password_field';
+import * as i18n from './translations';
+import { useConnectorContext } from '../../../context/use_connector_context';
+
+const { emptyField } = fieldValidators;
const ExchangeFormFields = lazy(() => import('./exchange_form'));
-export const EmailActionConnectorFields: React.FunctionComponent<
- ActionConnectorFieldsProps
-> = ({ action, editActionConfig, editActionSecrets, errors, readOnly }) => {
- const { docLinks, http, isCloud } = useKibana().services;
- const { from, host, port, secure, hasAuth, service } = action.config;
- const { user, password } = action.secrets;
- const { emailServiceConfigurable, setEmailService } = useEmailConfig(
+const shouldDisableEmailConfiguration = (service: string | null | undefined) =>
+ isEmpty(service) ||
+ (service !== AdditionalEmailServices.EXCHANGE && service !== AdditionalEmailServices.OTHER);
+
+const getEmailConfig = (
+ href: string,
+ validateFunc: ActionsPublicPluginSetup['validateEmailAddresses']
+): FieldConfig => ({
+ label: i18n.FROM_LABEL,
+ helpText: (
+
+
+
+ ),
+ validations: [
+ { validator: emptyField(i18n.SENDER_REQUIRED) },
+ {
+ validator: ({ value }) => {
+ const validatedEmail = validateFunc([value])[0];
+ if (!validatedEmail.valid) {
+ const message =
+ validatedEmail.reason === InvalidEmailReason.notAllowed
+ ? i18n.getNotAllowedEmailAddress(value)
+ : i18n.getInvalidEmailAddress(value);
+
+ return {
+ message,
+ };
+ }
+ },
+ },
+ ],
+});
+
+const portConfig: FieldConfig = {
+ label: i18n.PORT_LABEL,
+ validations: [
+ {
+ validator: emptyField(i18n.PORT_REQUIRED),
+ },
+ {
+ validator: ({ value }) => {
+ const port = Number.parseFloat(value);
+
+ if (!Number.isInteger(port)) {
+ return { message: i18n.PORT_INVALID };
+ }
+ },
+ },
+ ],
+};
+
+export const EmailActionConnectorFields: React.FunctionComponent = ({
+ readOnly,
+}) => {
+ const {
+ docLinks,
http,
- service,
- editActionConfig
+ isCloud,
+ notifications: { toasts },
+ } = useKibana().services;
+ const {
+ services: { validateEmailAddresses },
+ } = useConnectorContext();
+
+ const form = useFormContext();
+ const { updateFieldValues } = form;
+ const [{ config }] = useFormData({
+ watch: ['config.service', 'config.hasAuth'],
+ });
+
+ const emailFieldConfig = useMemo(
+ () => getEmailConfig(docLinks.links.alerting.emailActionConfig, validateEmailAddresses),
+ [docLinks.links.alerting.emailActionConfig, validateEmailAddresses]
);
+ const { service = null, hasAuth = false } = config ?? {};
+ const disableServiceConfig = shouldDisableEmailConfiguration(service);
+ const { isLoading, getEmailServiceConfig } = useEmailConfig({ http, toasts });
+
useEffect(() => {
- if (!action.id) {
- editActionConfig('hasAuth', true);
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
+ async function fetchConfig() {
+ if (
+ service === null ||
+ service === AdditionalEmailServices.OTHER ||
+ service === AdditionalEmailServices.EXCHANGE
+ ) {
+ return;
+ }
- const isFromInvalid: boolean =
- from !== undefined && errors.from !== undefined && errors.from.length > 0;
- const isHostInvalid: boolean =
- host !== undefined && errors.host !== undefined && errors.host.length > 0;
- const isServiceInvalid: boolean =
- service !== undefined && errors.service !== undefined && errors.service.length > 0;
- const isPortInvalid: boolean =
- port !== undefined && errors.port !== undefined && errors.port.length > 0;
+ const emailConfig = await getEmailServiceConfig(service);
+ updateFieldValues({
+ config: {
+ host: emailConfig?.host,
+ port: emailConfig?.port,
+ secure: emailConfig?.secure,
+ },
+ });
+ }
- const isPasswordInvalid: boolean =
- password !== undefined && errors.password !== undefined && errors.password.length > 0;
- const isUserInvalid: boolean =
- user !== undefined && errors.user !== undefined && errors.user.length > 0;
-
- const authForm = (
- <>
- {getEncryptedFieldNotifyLabel(
- !action.id,
- 2,
- action.isMissingSecrets ?? false,
- i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.reenterValuesLabel',
- {
- defaultMessage:
- 'Username and password are encrypted. Please reenter values for these fields.',
- }
- )
- )}
-
-
-
- {
- editActionSecrets('user', nullableString(e.target.value));
- }}
- onBlur={() => {
- if (!user) {
- editActionSecrets('user', '');
- }
- }}
- />
-
-
-
-
- {
- editActionSecrets('password', nullableString(e.target.value));
- }}
- onBlur={() => {
- if (!password) {
- editActionSecrets('password', '');
- }
- }}
- />
-
-
-
- >
- );
+ fetchConfig();
+ }, [updateFieldValues, getEmailServiceConfig, service]);
return (
<>
-
-
-
- }
- >
- {
- editActionConfig('from', e.target.value);
- }}
- onBlur={() => {
- if (!from) {
- editActionConfig('from', '');
- }
- }}
- />
-
+
-
- {
- setEmailService(e.target.value);
- }}
- />
-
+
{service === AdditionalEmailServices.EXCHANGE ? (
-
+
) : (
<>
-
- {
- editActionConfig('host', e.target.value);
- }}
- onBlur={() => {
- if (!host) {
- editActionConfig('host', '');
- }
- }}
- />
-
+
-
- {
- editActionConfig('port', parseInt(e.target.value, 10));
- }}
- onBlur={() => {
- if (!port) {
- editActionConfig('port', 0);
- }
- }}
- />
-
+
-
-
- {
- editActionConfig('secure', e.target.checked);
- }}
- />
-
-
+
@@ -329,26 +259,51 @@ export const EmailActionConnectorFields: React.FunctionComponent<
- {
- editActionConfig('hasAuth', e.target.checked);
- if (!e.target.checked) {
- editActionSecrets('user', null);
- editActionSecrets('password', null);
- }
+
- {hasAuth ? authForm : null}
+ {hasAuth ? (
+ <>
+
+
+
+
+
+
+
+
+ >
+ ) : null}
>
)}
>
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/exchange_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/exchange_form.test.tsx
index d87efe5134794..a07ecde41c76d 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/exchange_form.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/exchange_form.test.tsx
@@ -7,36 +7,35 @@
import React from 'react';
import { mountWithIntl } from '@kbn/test-jest-helpers';
-import { EmailActionConnector } from '../types';
import ExchangeFormFields from './exchange_form';
+import { ConnectorFormTestProvider } from '../test_utils';
+import { act, render } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
jest.mock('../../../../common/lib/kibana');
describe('ExchangeFormFields renders', () => {
+ const actionConnector = {
+ secrets: {
+ clientSecret: 'secret',
+ },
+ id: 'test',
+ actionTypeId: '.email',
+ name: 'email',
+ isDeprecated: false,
+ config: {
+ from: 'test@test.com',
+ service: 'exchange_server',
+ tenantId: 'tenant-id',
+ clientId: 'clientId-id',
+ },
+ };
+
test('should display exchange form fields', () => {
- const actionConnector = {
- secrets: {
- clientSecret: 'user',
- },
- id: 'test',
- actionTypeId: '.email',
- name: 'exchange email',
- config: {
- from: 'test@test.com',
- hasAuth: true,
- service: 'exchange_server',
- clientId: '123',
- tenantId: '1234',
- },
- } as EmailActionConnector;
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- />
+
+
+
);
expect(wrapper.find('[data-test-subj="emailClientSecret"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="emailClientId"]').length > 0).toBeTruthy();
@@ -44,25 +43,18 @@ describe('ExchangeFormFields renders', () => {
});
test('exchange field defaults to empty when not defined', () => {
- const actionConnector = {
+ const connector = {
+ ...actionConnector,
secrets: {},
- id: 'test',
- actionTypeId: '.email',
- name: 'email',
config: {
from: 'test@test.com',
- hasAuth: true,
- service: 'exchange_server',
},
- } as EmailActionConnector;
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- />
+
+
+
);
expect(wrapper.find('[data-test-subj="emailClientSecret"]').length > 0).toBeTruthy();
expect(wrapper.find('input[data-test-subj="emailClientSecret"]').prop('value')).toEqual('');
@@ -73,4 +65,67 @@ describe('ExchangeFormFields renders', () => {
expect(wrapper.find('[data-test-subj="emailTenantId"]').length > 0).toBeTruthy();
expect(wrapper.find('input[data-test-subj="emailTenantId"]').prop('value')).toEqual('');
});
+
+ describe('Validation', () => {
+ const onSubmit = jest.fn();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ const tests: Array<[string, string]> = [
+ ['emailTenantId', ''],
+ ['emailClientId', ''],
+ ['emailClientSecret', ''],
+ ];
+
+ it('connector validation succeeds when connector config is valid', async () => {
+ const { getByTestId } = render(
+
+
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ secrets: {
+ clientSecret: 'secret',
+ },
+ id: 'test',
+ actionTypeId: '.email',
+ name: 'email',
+ isDeprecated: false,
+ config: {
+ tenantId: 'tenant-id',
+ clientId: 'clientId-id',
+ },
+ },
+ isValid: true,
+ });
+ });
+
+ it.each(tests)('validates correctly %p', async (field, value) => {
+ const res = render(
+
+
+
+ );
+
+ await act(async () => {
+ await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
+ });
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/exchange_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/exchange_form.tsx
index 1616e964147ab..41f82b3a32bfc 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/exchange_form.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/exchange_form.tsx
@@ -6,158 +6,88 @@
*/
import React from 'react';
-import {
- EuiFieldText,
- EuiFlexItem,
- EuiFlexGroup,
- EuiFormRow,
- EuiFieldPassword,
- EuiLink,
-} from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
+import { EuiFlexItem, EuiFlexGroup, EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
-import { IErrorObject } from '../../../../types';
-import { EmailActionConnector } from '../types';
-import { nullableString } from './email_connector';
-import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { TextField } from '@kbn/es-ui-shared-plugin/static/forms/components';
import { useKibana } from '../../../../common/lib/kibana';
+import * as i18n from './translations';
+import { PasswordField } from '../../password_field';
+
+const { emptyField } = fieldValidators;
interface ExchangeFormFieldsProps {
- action: EmailActionConnector;
- editActionConfig: (property: string, value: unknown) => void;
- editActionSecrets: (property: string, value: unknown) => void;
- errors: IErrorObject;
readOnly: boolean;
}
-const ExchangeFormFields: React.FunctionComponent = ({
- action,
- editActionConfig,
- editActionSecrets,
- errors,
- readOnly,
-}) => {
+const ExchangeFormFields: React.FC = ({ readOnly }) => {
const { docLinks } = useKibana().services;
- const { tenantId, clientId } = action.config;
- const { clientSecret } = action.secrets;
-
- const isClientIdInvalid: boolean =
- clientId !== undefined && errors.clientId !== undefined && errors.clientId.length > 0;
- const isTenantIdInvalid: boolean =
- tenantId !== undefined && errors.tenantId !== undefined && errors.tenantId.length > 0;
- const isClientSecretInvalid: boolean =
- clientSecret !== undefined &&
- errors.clientSecret !== undefined &&
- errors.clientSecret.length > 0;
return (
<>
-
-
-
- }
- >
- {
- editActionConfig('tenantId', nullableString(e.target.value));
- }}
- onBlur={() => {
- if (!tenantId) {
- editActionConfig('tenantId', '');
- }
- }}
- />
-
+
+
+
+ ),
+ validations: [
+ {
+ validator: emptyField(i18n.TENANT_ID_REQUIRED),
+ },
+ ],
+ }}
+ componentProps={{
+ euiFieldProps: { 'data-test-subj': 'emailTenantId' },
+ readOnly,
+ placeholder: '00000000-0000-0000-0000-000000000000',
+ }}
+ />
-
-
-
- }
- >
- {
- editActionConfig('clientId', nullableString(e.target.value));
- }}
- onBlur={() => {
- if (!clientId) {
- editActionConfig('clientId', '');
- }
- }}
- />
-
+
+
+
+ ),
+ validations: [
+ {
+ validator: emptyField(i18n.CLIENT_ID_REQUIRED),
+ },
+ ],
+ }}
+ componentProps={{
+ euiFieldProps: { 'data-test-subj': 'emailClientId' },
+ readOnly,
+ placeholder: '00000000-0000-0000-0000-000000000000',
+ }}
+ />
- {getEncryptedFieldNotifyLabel(
- !action.id,
- 1,
- action.isMissingSecrets ?? false,
- i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.reenterClientSecretLabel',
- {
- defaultMessage: 'Client Secret is encrypted. Please reenter value for this field.',
- }
- )
- )}
- = ({
/>
}
- >
- {
- editActionSecrets('clientSecret', nullableString(e.target.value));
- }}
- onBlur={() => {
- if (!clientSecret) {
- editActionSecrets('clientSecret', '');
- }
- }}
- />
-
+ data-test-subj="emailClientSecret"
+ />
>
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/translations.ts
index e1bee12d98993..65fc2bdb542e8 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/translations.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/translations.ts
@@ -7,17 +7,87 @@
import { i18n } from '@kbn/i18n';
-export const SENDER_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredFromText',
+export const USERNAME_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.userTextFieldLabel',
{
- defaultMessage: 'Sender is required.',
+ defaultMessage: 'Username',
+ }
+);
+
+export const PASSWORD_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.passwordFieldLabel',
+ {
+ defaultMessage: 'Password',
+ }
+);
+
+export const FROM_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.fromTextFieldLabel',
+ {
+ defaultMessage: 'Sender',
+ }
+);
+
+export const SERVICE_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.serviceTextFieldLabel',
+ {
+ defaultMessage: 'Service',
+ }
+);
+
+export const TENANT_ID_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.tenantIdFieldLabel',
+ {
+ defaultMessage: 'Tenant ID',
+ }
+);
+
+export const CLIENT_ID_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.clientIdFieldLabel',
+ {
+ defaultMessage: 'Client ID',
+ }
+);
+
+export const CLIENT_SECRET_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.clientSecretTextFieldLabel',
+ {
+ defaultMessage: 'Client Secret',
+ }
+);
+
+export const HOST_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hostTextFieldLabel',
+ {
+ defaultMessage: 'Host',
+ }
+);
+
+export const PORT_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.portTextFieldLabel',
+ {
+ defaultMessage: 'Port',
}
);
-export const SENDER_NOT_VALID = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.error.formatFromText',
+export const SECURE_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.secureSwitchLabel',
+ {
+ defaultMessage: 'Secure',
+ }
+);
+
+export const HAS_AUTH_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hasAuthSwitchLabel',
+ {
+ defaultMessage: 'Require authentication for this server',
+ }
+);
+
+export const SENDER_REQUIRED = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredFromText',
{
- defaultMessage: 'Sender is not a valid email address.',
+ defaultMessage: 'Sender is required.',
}
);
@@ -35,17 +105,17 @@ export const TENANT_ID_REQUIRED = i18n.translate(
}
);
-export const CLIENT_SECRET_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredClientSecretText',
+export const PORT_REQUIRED = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPortText',
{
- defaultMessage: 'Client Secret is required.',
+ defaultMessage: 'Port is required.',
}
);
-export const PORT_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPortText',
+export const PORT_INVALID = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.error.invalidPortText',
{
- defaultMessage: 'Port is required.',
+ defaultMessage: 'Port is invalid.',
}
);
@@ -70,20 +140,6 @@ export const USERNAME_REQUIRED = i18n.translate(
}
);
-export const PASSWORD_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthPasswordText',
- {
- defaultMessage: 'Password is required.',
- }
-);
-
-export const PASSWORD_REQUIRED_FOR_USER_USED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredPasswordText',
- {
- defaultMessage: 'Password is required when username is used.',
- }
-);
-
export const TO_CC_REQUIRED = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredEntryText',
{
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.test.ts
index 6a640cc734071..f03c869ea5ca4 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.test.ts
@@ -5,112 +5,93 @@
* 2.0.
*/
+import { httpServiceMock, notificationServiceMock } from '@kbn/core/public/mocks';
import { renderHook, act } from '@testing-library/react-hooks';
-import { HttpSetup } from '@kbn/core/public';
import { useEmailConfig } from './use_email_config';
-const http = {
- get: jest.fn(),
-};
-
-const editActionConfig = jest.fn();
+const http = httpServiceMock.createStartContract();
+const toasts = notificationServiceMock.createStartContract().toasts;
const renderUseEmailConfigHook = (currentService?: string) =>
- renderHook(() => useEmailConfig(http as unknown as HttpSetup, currentService, editActionConfig));
+ renderHook(() => useEmailConfig({ http, toasts }));
describe('useEmailConfig', () => {
beforeEach(() => jest.clearAllMocks());
- it('should call get email config API when service changes and handle result', async () => {
+ it('should return the correct result when requesting the config of a service', async () => {
http.get.mockResolvedValueOnce({
host: 'smtp.gmail.com',
port: 465,
secure: true,
});
- const { result, waitForNextUpdate } = renderUseEmailConfigHook();
+
+ const { result } = renderUseEmailConfigHook();
await act(async () => {
- result.current.setEmailService('gmail');
- await waitForNextUpdate();
+ const res = await result.current.getEmailServiceConfig('gmail');
+ expect(http.get).toHaveBeenCalledWith('/internal/actions/connector/_email_config/gmail');
+ expect(res).toEqual({
+ host: 'smtp.gmail.com',
+ port: 465,
+ secure: true,
+ });
});
-
- expect(http.get).toHaveBeenCalledWith('/internal/actions/connector/_email_config/gmail');
- expect(editActionConfig).toHaveBeenCalledWith('service', 'gmail');
-
- expect(editActionConfig).toHaveBeenCalledWith('host', 'smtp.gmail.com');
- expect(editActionConfig).toHaveBeenCalledWith('port', 465);
- expect(editActionConfig).toHaveBeenCalledWith('secure', true);
-
- expect(result.current.emailServiceConfigurable).toEqual(false);
});
- it('should call get email config API when service changes and handle partial result', async () => {
+ it('should return the correct result when requesting the config of a service on partial result', async () => {
http.get.mockResolvedValueOnce({
host: 'smtp.gmail.com',
port: 465,
});
- const { result, waitForNextUpdate } = renderUseEmailConfigHook();
+
+ const { result } = renderUseEmailConfigHook();
await act(async () => {
- result.current.setEmailService('gmail');
- await waitForNextUpdate();
+ const res = await result.current.getEmailServiceConfig('gmail');
+ expect(http.get).toHaveBeenCalledWith('/internal/actions/connector/_email_config/gmail');
+ expect(res).toEqual({
+ host: 'smtp.gmail.com',
+ port: 465,
+ secure: false,
+ });
});
-
- expect(http.get).toHaveBeenCalledWith('/internal/actions/connector/_email_config/gmail');
- expect(editActionConfig).toHaveBeenCalledWith('service', 'gmail');
-
- expect(editActionConfig).toHaveBeenCalledWith('host', 'smtp.gmail.com');
- expect(editActionConfig).toHaveBeenCalledWith('port', 465);
- expect(editActionConfig).toHaveBeenCalledWith('secure', false);
-
- expect(result.current.emailServiceConfigurable).toEqual(false);
});
- it('should call get email config API when service changes and handle empty result', async () => {
+ it('should return the correct result when requesting the config of a service on empty result', async () => {
http.get.mockResolvedValueOnce({});
- const { result, waitForNextUpdate } = renderUseEmailConfigHook();
+ const { result } = renderUseEmailConfigHook();
+
await act(async () => {
- result.current.setEmailService('foo');
- await waitForNextUpdate();
+ const res = await result.current.getEmailServiceConfig('foo');
+ expect(http.get).toHaveBeenCalledWith('/internal/actions/connector/_email_config/foo');
+ expect(res).toEqual({
+ host: '',
+ port: 0,
+ secure: false,
+ });
});
-
- expect(http.get).toHaveBeenCalledWith('/internal/actions/connector/_email_config/foo');
- expect(editActionConfig).toHaveBeenCalledWith('service', 'foo');
-
- expect(editActionConfig).toHaveBeenCalledWith('host', '');
- expect(editActionConfig).toHaveBeenCalledWith('port', 0);
- expect(editActionConfig).toHaveBeenCalledWith('secure', false);
-
- expect(result.current.emailServiceConfigurable).toEqual(true);
});
- it('should call get email config API when service changes and handle errors', async () => {
+ it('should show a danger toaster on error', async () => {
http.get.mockImplementationOnce(() => {
throw new Error('no!');
});
+
const { result, waitForNextUpdate } = renderUseEmailConfigHook();
+
await act(async () => {
- result.current.setEmailService('foo');
+ result.current.getEmailServiceConfig('foo');
await waitForNextUpdate();
+ expect(toasts.addDanger).toHaveBeenCalled();
});
-
- expect(http.get).toHaveBeenCalledWith('/internal/actions/connector/_email_config/foo');
- expect(editActionConfig).toHaveBeenCalledWith('service', 'foo');
-
- expect(editActionConfig).toHaveBeenCalledWith('host', '');
- expect(editActionConfig).toHaveBeenCalledWith('port', 0);
- expect(editActionConfig).toHaveBeenCalledWith('secure', false);
-
- expect(result.current.emailServiceConfigurable).toEqual(true);
});
- it('should call get email config API when initial service value is passed and determine if config is editable without overwriting config', async () => {
+ it('should not make an API call if the service is empty', async () => {
http.get.mockResolvedValueOnce({
host: 'smtp.gmail.com',
port: 465,
secure: true,
});
- const { result } = renderUseEmailConfigHook('gmail');
- expect(http.get).toHaveBeenCalledWith('/internal/actions/connector/_email_config/gmail');
- expect(editActionConfig).not.toHaveBeenCalled();
- expect(result.current.emailServiceConfigurable).toEqual(false);
+
+ renderUseEmailConfigHook('');
+ expect(http.get).not.toHaveBeenCalled();
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.ts
index 53ec70b8b62b3..c706630f1f6bb 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/use_email_config.ts
@@ -5,66 +5,95 @@
* 2.0.
*/
-import { useCallback, useEffect, useState } from 'react';
-import { HttpSetup } from '@kbn/core/public';
import { isEmpty } from 'lodash';
+import { useCallback, useEffect, useRef, useState } from 'react';
+import { HttpSetup, IToasts } from '@kbn/core/public';
import { AdditionalEmailServices } from '@kbn/actions-plugin/common';
+import { i18n } from '@kbn/i18n';
import { EmailConfig } from '../types';
import { getServiceConfig } from './api';
-export function useEmailConfig(
- http: HttpSetup,
- currentService: string | undefined,
- editActionConfig: (property: string, value: unknown) => void
-) {
- const [emailServiceConfigurable, setEmailServiceConfigurable] = useState(false);
- const [emailService, setEmailService] = useState(undefined);
+interface Props {
+ http: HttpSetup;
+ toasts: IToasts;
+}
+
+interface UseEmailConfigReturnValue {
+ isLoading: boolean;
+ getEmailServiceConfig: (
+ service: string
+ ) => Promise> | undefined>;
+}
+
+const getConfig = (
+ service: string,
+ config: Partial>
+): Pick | undefined => {
+ if (service) {
+ if (service === AdditionalEmailServices.EXCHANGE) {
+ return;
+ }
+
+ return {
+ host: config?.host ? config.host : '',
+ port: config?.port ? config.port : 0,
+ secure: null != config?.secure ? config.secure : false,
+ };
+ }
+};
+
+export function useEmailConfig({ http, toasts }: Props): UseEmailConfigReturnValue {
+ const [isLoading, setIsLoading] = useState(false);
+ const abortCtrlRef = useRef(new AbortController());
+ const isMounted = useRef(false);
const getEmailServiceConfig = useCallback(
- async (service: string) => {
- let serviceConfig: Partial>;
- try {
- serviceConfig = await getServiceConfig({ http, service });
- setEmailServiceConfigurable(isEmpty(serviceConfig));
- } catch (err) {
- serviceConfig = {};
- setEmailServiceConfigurable(true);
+ async (service: string | null) => {
+ if (service == null || isEmpty(service)) {
+ return;
}
- return serviceConfig;
- },
- // eslint-disable-next-line react-hooks/exhaustive-deps
- [editActionConfig]
- );
+ setIsLoading(true);
+ isMounted.current = true;
+ abortCtrlRef.current.abort();
+ abortCtrlRef.current = new AbortController();
- useEffect(() => {
- (async () => {
- if (emailService) {
- editActionConfig('service', emailService);
- if (emailService === AdditionalEmailServices.EXCHANGE) {
- return;
+ try {
+ const serviceConfig = await getServiceConfig({ http, service });
+ if (isMounted.current) {
+ setIsLoading(false);
}
- const serviceConfig = await getEmailServiceConfig(emailService);
- editActionConfig('host', serviceConfig?.host ? serviceConfig.host : '');
- editActionConfig('port', serviceConfig?.port ? serviceConfig.port : 0);
- editActionConfig('secure', null != serviceConfig?.secure ? serviceConfig.secure : false);
+ return getConfig(service, serviceConfig);
+ } catch (error) {
+ if (isMounted.current) {
+ setIsLoading(false);
+
+ if (error.name !== 'AbortError') {
+ toasts.addDanger(
+ error.body?.message ??
+ i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.updateErrorNotificationText',
+ { defaultMessage: 'Cannot get service configuration' }
+ )
+ );
+ }
+ }
}
- })();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [emailService]);
+ },
+ [http, toasts]
+ );
useEffect(() => {
- (async () => {
- if (currentService) {
- await getEmailServiceConfig(currentService);
- }
- })();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [currentService]);
+ isMounted.current = true;
+ return () => {
+ isMounted.current = false;
+ abortCtrlRef.current.abort();
+ };
+ }, []);
return {
- emailServiceConfigurable,
- setEmailService,
+ isLoading,
+ getEmailServiceConfig,
};
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.test.tsx
index 42dc8a16a8b19..ccf90cb91d837 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.test.tsx
@@ -8,7 +8,6 @@
import { TypeRegistry } from '../../../type_registry';
import { registerBuiltInActionTypes } from '..';
import { ActionTypeModel } from '../../../../types';
-import { EsIndexActionConnector } from '../types';
import { registrationServicesMock } from '../../../../mocks';
const ACTION_TYPE_ID = '.index';
@@ -30,58 +29,6 @@ describe('actionTypeRegistry.get() works', () => {
});
});
-describe('index connector validation', () => {
- test('connector validation succeeds when connector config is valid', async () => {
- const actionConnector = {
- secrets: {},
- id: 'test',
- actionTypeId: '.index',
- name: 'es_index',
- config: {
- index: 'test_es_index',
- refresh: false,
- executionTimeField: '1',
- },
- } as EsIndexActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- index: [],
- },
- },
- secrets: {
- errors: {},
- },
- });
- });
-});
-
-describe('index connector validation with minimal config', () => {
- test('connector validation succeeds when connector config is valid', async () => {
- const actionConnector = {
- secrets: {},
- id: 'test',
- actionTypeId: '.index',
- name: 'es_index',
- config: {
- index: 'test_es_index',
- },
- } as EsIndexActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- index: [],
- },
- },
- secrets: {
- errors: {},
- },
- });
- });
-});
-
describe('action params validation', () => {
test('action params validation succeeds when action params are valid', async () => {
expect(
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx
index 80d38bda22ab3..75666c1282da3 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx
@@ -7,13 +7,8 @@
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
-import {
- ActionTypeModel,
- GenericValidationResult,
- ConnectorValidationResult,
- ALERT_HISTORY_PREFIX,
-} from '../../../../types';
-import { EsIndexActionConnector, EsIndexConfig, IndexActionParams } from '../types';
+import { ActionTypeModel, GenericValidationResult, ALERT_HISTORY_PREFIX } from '../../../../types';
+import { EsIndexConfig, IndexActionParams } from '../types';
export function getActionType(): ActionTypeModel {
return {
@@ -31,19 +26,6 @@ export function getActionType(): ActionTypeModel, unknown>> => {
- const translations = await import('./translations');
- const configErrors = {
- index: new Array(),
- };
- const validationResult = { config: { errors: configErrors }, secrets: { errors: {} } };
- if (!action.config.index) {
- configErrors.index.push(translations.INDEX_REQUIRED);
- }
- return validationResult;
- },
actionConnectorFields: lazy(() => import('./es_index_connector')),
actionParamsFields: lazy(() => import('./es_index_params')),
validateParams: async (
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.test.tsx
index bddc408026698..a44daf93e7ff9 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.test.tsx
@@ -6,12 +6,14 @@
*/
import React from 'react';
-import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers';
import { act } from 'react-dom/test-utils';
-import { EsIndexActionConnector } from '../types';
+import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers';
+import { screen, fireEvent, waitFor, render } from '@testing-library/react';
import IndexActionConnectorFields from './es_index_connector';
import { EuiComboBox, EuiSwitch, EuiSwitchEvent, EuiSelect } from '@elastic/eui';
-import { screen, render, fireEvent } from '@testing-library/react';
+import { AppMockRenderer, ConnectorFormTestProvider, createAppMockRenderer } from '../test_utils';
+import userEvent from '@testing-library/user-event';
+
jest.mock('../../../../common/lib/kibana');
jest.mock('lodash', () => {
@@ -23,7 +25,10 @@ jest.mock('lodash', () => {
});
jest.mock('../../../../common/index_controls', () => ({
- firstFieldOption: jest.fn(),
+ firstFieldOption: {
+ text: 'Select a field',
+ value: '',
+ },
getFields: jest.fn(),
getIndexOptions: jest.fn(),
}));
@@ -42,12 +47,22 @@ getIndexOptions.mockResolvedValueOnce([
const { getFields } = jest.requireMock('../../../../common/index_controls');
-async function setup(props: any) {
- const wrapper = mountWithIntl();
+async function setup(actionConnector: any) {
+ const wrapper = mountWithIntl(
+
+ {}}
+ />
+
+ );
+
await act(async () => {
await nextTick();
wrapper.update();
});
+
return wrapper;
}
@@ -64,22 +79,16 @@ function setupGetFieldsResponse(getFieldsWithDateMapping: boolean) {
]);
}
-describe('IndexActionConnectorFields renders', () => {
+describe('IndexActionConnectorFields', () => {
test('renders correctly when creating connector', async () => {
- const props = {
- action: {
- actionTypeId: '.index',
- config: {},
- secrets: {},
- } as EsIndexActionConnector,
- editActionConfig: () => {},
- editActionSecrets: () => {},
- errors: { index: [] },
- readOnly: false,
- setCallbacks: () => {},
- isEdit: false,
+ const connector = {
+ actionTypeId: '.index',
+ config: {},
+ secrets: {},
};
- const wrapper = mountWithIntl();
+
+ setupGetFieldsResponse(false);
+ const wrapper = await setup(connector);
expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').exists()).toBeTruthy();
expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').exists()).toBeTruthy();
@@ -95,34 +104,41 @@ describe('IndexActionConnectorFields renders', () => {
// time field switch should show up if index has date type field mapping
setupGetFieldsResponse(true);
await act(async () => {
- indexComboBox.prop('onChange')!([{ label: 'selection' }]);
+ indexComboBox.prop('onChange')!([{ label: 'selection', value: 'selection' }]);
await nextTick();
wrapper.update();
});
+
+ wrapper.update();
expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeTruthy();
expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeFalsy();
+ // time field switch should show up if index has date type field mapping
// time field switch should go away if index does not has date type field mapping
setupGetFieldsResponse(false);
await act(async () => {
- indexComboBox.prop('onChange')!([{ label: 'selection' }]);
+ indexComboBox.prop('onChange')!([{ label: 'selection', value: 'selection' }]);
await nextTick();
wrapper.update();
});
+
expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeFalsy();
expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeFalsy();
// time field dropdown should show up if index has date type field mapping and time switch is clicked
setupGetFieldsResponse(true);
await act(async () => {
- indexComboBox.prop('onChange')!([{ label: 'selection' }]);
+ indexComboBox.prop('onChange')!([{ label: 'selection', value: 'selection' }]);
await nextTick();
wrapper.update();
});
+
expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeTruthy();
+
const timeFieldSwitch = wrapper
.find(EuiSwitch)
.filter('[data-test-subj="hasTimeFieldCheckbox"]');
+
await act(async () => {
timeFieldSwitch.prop('onChange')!({
target: { checked: true },
@@ -130,26 +146,25 @@ describe('IndexActionConnectorFields renders', () => {
await nextTick();
wrapper.update();
});
- expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeTruthy();
+
+ await waitFor(() => {
+ wrapper.update();
+ expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeTruthy();
+ });
});
test('renders correctly when editing connector - no date type field mapping', async () => {
const indexName = 'index-no-date-fields';
const props = {
- action: {
- name: 'Index Connector for Index With No Date Type',
- actionTypeId: '.index',
- config: {
- index: indexName,
- refresh: false,
- },
- secrets: {},
- } as EsIndexActionConnector,
- editActionConfig: () => {},
- editActionSecrets: () => {},
- errors: { index: [] },
- readOnly: false,
+ name: 'Index Connector for Index With No Date Type',
+ actionTypeId: '.index',
+ config: {
+ index: indexName,
+ refresh: false,
+ },
+ secrets: {},
};
+
setupGetFieldsResponse(false);
const wrapper = await setup(props);
@@ -172,20 +187,15 @@ describe('IndexActionConnectorFields renders', () => {
test('renders correctly when editing connector - refresh set to true', async () => {
const indexName = 'index-no-date-fields';
const props = {
- action: {
- name: 'Index Connector for Index With No Date Type',
- actionTypeId: '.index',
- config: {
- index: indexName,
- refresh: true,
- },
- secrets: {},
- } as EsIndexActionConnector,
- editActionConfig: () => {},
- editActionSecrets: () => {},
- errors: { index: [] },
- readOnly: false,
+ name: 'Index Connector for Index With No Date Type',
+ actionTypeId: '.index',
+ config: {
+ index: indexName,
+ refresh: true,
+ },
+ secrets: {},
};
+
setupGetFieldsResponse(false);
const wrapper = await setup(props);
@@ -206,27 +216,24 @@ describe('IndexActionConnectorFields renders', () => {
test('renders correctly when editing connector - with date type field mapping but no time field selected', async () => {
const indexName = 'index-no-date-fields';
const props = {
- action: {
- name: 'Index Connector for Index With No Date Type',
- actionTypeId: '.index',
- config: {
- index: indexName,
- refresh: false,
- },
- secrets: {},
- } as EsIndexActionConnector,
- editActionConfig: () => {},
- editActionSecrets: () => {},
- errors: { index: [] },
- readOnly: false,
+ name: 'Index Connector for Index With No Date Type',
+ actionTypeId: '.index',
+ config: {
+ index: indexName,
+ refresh: false,
+ },
+ secrets: {},
};
+
setupGetFieldsResponse(true);
const wrapper = await setup(props);
- expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeFalsy();
+ await waitFor(() => {
+ expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeFalsy();
+ });
const indexComboBox = wrapper
.find(EuiComboBox)
@@ -245,28 +252,25 @@ describe('IndexActionConnectorFields renders', () => {
test('renders correctly when editing connector - with date type field mapping and selected time field', async () => {
const indexName = 'index-no-date-fields';
const props = {
- action: {
- name: 'Index Connector for Index With No Date Type',
- actionTypeId: '.index',
- config: {
- index: indexName,
- refresh: false,
- executionTimeField: 'test1',
- },
- secrets: {},
- } as EsIndexActionConnector,
- editActionConfig: () => {},
- editActionSecrets: () => {},
- errors: { index: [] },
- readOnly: false,
+ name: 'Index Connector for Index With No Date Type',
+ actionTypeId: '.index',
+ config: {
+ index: indexName,
+ refresh: false,
+ executionTimeField: 'test1',
+ },
};
+
setupGetFieldsResponse(true);
const wrapper = await setup(props);
- expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeTruthy();
+ await waitFor(() => {
+ wrapper.update();
+ expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeTruthy();
+ });
const indexComboBox = wrapper
.find(EuiComboBox)
@@ -289,20 +293,23 @@ describe('IndexActionConnectorFields renders', () => {
test('fetches index names on index combobox input change', async () => {
const mockIndexName = 'test-index';
- const props = {
- action: {
- actionTypeId: '.index',
- config: {},
- secrets: {},
- } as EsIndexActionConnector,
- editActionConfig: () => {},
- editActionSecrets: () => {},
- errors: { index: [] },
- readOnly: false,
- setCallbacks: () => {},
- isEdit: false,
+ const connector = {
+ actionTypeId: '.index',
+ name: 'index',
+ isDeprecated: false,
+ config: {},
+ secrets: {},
};
- render();
+
+ render(
+
+ {}}
+ />
+
+ );
const indexComboBox = await screen.findByTestId('connectorIndexesComboBox');
@@ -322,4 +329,134 @@ describe('IndexActionConnectorFields renders', () => {
expect(screen.getByText('indexPattern1')).toBeInTheDocument();
expect(screen.getByText('indexPattern2')).toBeInTheDocument();
});
+
+ describe('Validation', () => {
+ let appMockRenderer: AppMockRenderer;
+ const onSubmit = jest.fn();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ appMockRenderer = createAppMockRenderer();
+ });
+
+ test('connector validation succeeds when connector config is valid', async () => {
+ const actionConnector = {
+ secrets: {},
+ id: 'test',
+ actionTypeId: '.index',
+ name: 'es_index',
+ config: {
+ index: 'test_es_index',
+ refresh: false,
+ executionTimeField: '1',
+ },
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ actionTypeId: '.index',
+ config: {
+ index: 'test_es_index',
+ refresh: false,
+ executionTimeField: '1',
+ },
+ id: 'test',
+ isDeprecated: false,
+ __internal__: {
+ hasTimeFieldCheckbox: true,
+ },
+ name: 'es_index',
+ },
+ isValid: true,
+ });
+ });
+
+ test('connector validation succeeds when connector config is valid with minimal config', async () => {
+ const actionConnector = {
+ secrets: {},
+ id: 'test',
+ actionTypeId: '.index',
+ name: 'es_index',
+ config: {
+ index: 'test_es_index',
+ },
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ actionTypeId: '.index',
+ config: {
+ index: 'test_es_index',
+ refresh: false,
+ },
+ id: 'test',
+ isDeprecated: false,
+ name: 'es_index',
+ },
+ isValid: true,
+ });
+ });
+
+ test('connector validation fails when index is empty', async () => {
+ const actionConnector = {
+ secrets: {},
+ id: 'test',
+ actionTypeId: '.index',
+ name: 'es_index',
+ config: {
+ index: '',
+ },
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {},
+ isValid: false,
+ });
+ });
+ });
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx
index 1e7259dd802d4..12fb79bd2845a 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx
@@ -6,54 +6,95 @@
*/
import React, { useState, useEffect } from 'react';
+import { debounce } from 'lodash';
import {
EuiFormRow,
- EuiSwitch,
EuiSpacer,
EuiComboBox,
EuiComboBoxOptionOption,
- EuiSelect,
EuiTitle,
EuiIconTip,
EuiLink,
} from '@elastic/eui';
+import {
+ FieldConfig,
+ getFieldValidityAndErrorMessage,
+ UseField,
+ useFormContext,
+ useFormData,
+ VALIDATION_TYPES,
+} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { ToggleField, SelectField } from '@kbn/es-ui-shared-plugin/static/forms/components';
+import { DocLinksStart } from '@kbn/core/public';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
-import { debounce } from 'lodash';
import { ActionConnectorFieldsProps } from '../../../../types';
-import { EsIndexActionConnector } from '../types';
import { getTimeFieldOptions } from '../../../../common/lib/get_time_options';
import { firstFieldOption, getFields, getIndexOptions } from '../../../../common/index_controls';
import { useKibana } from '../../../../common/lib/kibana';
+import * as translations from './translations';
interface TimeFieldOptions {
value: string;
text: string;
}
-const IndexActionConnectorFields: React.FunctionComponent<
- ActionConnectorFieldsProps
-> = ({ action, editActionConfig, errors, readOnly }) => {
+const { indexPatternField, emptyField } = fieldValidators;
+
+const getIndexConfig = (docLinks: DocLinksStart): FieldConfig => ({
+ label: translations.INDEX_LABEL,
+ helpText: (
+ <>
+
+
+
+
+
+ >
+ ),
+ validations: [
+ {
+ validator: emptyField(translations.INDEX_IS_NOT_VALID),
+ },
+ {
+ validator: indexPatternField(i18n),
+ type: VALIDATION_TYPES.ARRAY_ITEM,
+ },
+ ],
+});
+
+const IndexActionConnectorFields: React.FunctionComponent = ({
+ readOnly,
+}) => {
const { http, docLinks } = useKibana().services;
- const { index, refresh, executionTimeField } = action.config;
- const [showTimeFieldCheckbox, setShowTimeFieldCheckboxState] = useState(
- executionTimeField != null
- );
- const [hasTimeFieldCheckbox, setHasTimeFieldCheckboxState] = useState(
- executionTimeField != null
- );
+ const { getFieldDefaultValue } = useFormContext();
+ const [{ config, __internal__ }] = useFormData({
+ watch: ['config.executionTimeField', 'config.index', '__internal__.hasTimeFieldCheckbox'],
+ });
+
+ const { index = null } = config ?? {};
const [indexOptions, setIndexOptions] = useState([]);
const [timeFieldOptions, setTimeFieldOptions] = useState([]);
const [areIndiciesLoading, setAreIndicesLoading] = useState(false);
+ const hasTimeFieldCheckboxDefaultValue = !!getFieldDefaultValue(
+ 'config.executionTimeField'
+ );
+ const showTimeFieldCheckbox = index != null && timeFieldOptions.length > 0;
+ const showTimeFieldSelect = __internal__ != null ? __internal__.hasTimeFieldCheckbox : false;
+
const setTimeFields = (fields: TimeFieldOptions[]) => {
if (fields.length > 0) {
- setShowTimeFieldCheckboxState(true);
setTimeFieldOptions([firstFieldOption, ...fields]);
} else {
- setHasTimeFieldCheckboxState(false);
- setShowTimeFieldCheckboxState(false);
setTimeFieldOptions([]);
}
};
@@ -68,14 +109,13 @@ const IndexActionConnectorFields: React.FunctionComponent<
const indexPatternsFunction = async () => {
if (index) {
const currentEsFields = await getFields(http!, [index]);
- setTimeFields(getTimeFieldOptions(currentEsFields as any));
+ if (Array.isArray(currentEsFields)) {
+ setTimeFields(getTimeFieldOptions(currentEsFields as any));
+ }
}
};
indexPatternsFunction();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
- const isIndexInvalid: boolean =
- errors.index !== undefined && errors.index.length > 0 && index !== undefined;
+ }, [http, index]);
return (
<>
@@ -88,57 +128,13 @@ const IndexActionConnectorFields: React.FunctionComponent<
-
- }
- isInvalid={isIndexInvalid}
- error={errors.index}
- helpText={
- <>
-
-
-
-
-
- >
- }
- >
- {
- editActionConfig('index', selected.length > 0 ? selected[0].value : '');
- const indices = selected.map((s) => s.value as string);
+
+ {(field) => {
+ const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field);
+
+ const onComboChange = async (options: EuiComboBoxOptionOption[]) => {
+ field.setValue(options.length > 0 ? options[0].value : '');
+ const indices = options.map((s) => s.value as string);
// reset time field and expression fields if indices are deleted
if (indices.length === 0) {
@@ -147,117 +143,149 @@ const IndexActionConnectorFields: React.FunctionComponent<
}
const currentEsFields = await getFields(http!, indices);
setTimeFields(getTimeFieldOptions(currentEsFields as any));
- }}
- onSearchChange={loadIndexOptions}
- onBlur={() => {
- if (!index) {
- editActionConfig('index', '');
+ };
+
+ const onSearchComboChange = (value: string) => {
+ if (value !== undefined) {
+ field.clearErrors(VALIDATION_TYPES.ARRAY_ITEM);
}
- }}
- />
-
+
+ loadIndexOptions(value);
+ };
+
+ return (
+
+ }
+ isInvalid={isInvalid}
+ error={errorMessage}
+ helpText={
+ <>
+
+
+
+
+
+ >
+ }
+ >
+
+
+ );
+ }}
+
- {
- editActionConfig('refresh', e.target.checked);
+
+ {' '}
+
+ >
+ ),
+ disabled: readOnly,
+ 'data-test-subj': 'indexRefreshCheckbox',
+ },
}}
- label={
- <>
- {' '}
-
- >
- }
/>
- {showTimeFieldCheckbox && (
- {
- setHasTimeFieldCheckboxState(!hasTimeFieldCheckbox);
- // if changing from checked to not checked (hasTimeField === true),
- // set time field to null
- if (hasTimeFieldCheckbox) {
- editActionConfig('executionTimeField', null);
- }
+ {showTimeFieldCheckbox ? (
+
+
+
+ >
+ ),
+ disabled: readOnly,
+ 'data-test-subj': 'hasTimeFieldCheckbox',
+ },
}}
- label={
- <>
-
-
- >
- }
/>
- )}
- {hasTimeFieldCheckbox && (
+ ) : null}
+ {showTimeFieldSelect ? (
<>
-
- }
- >
- {
- editActionConfig('executionTimeField', nullableString(e.target.value));
- }}
- onBlur={() => {
- if (executionTimeField === undefined) {
- editActionConfig('executionTimeField', null);
- }
- }}
- />
-
+
>
- )}
+ ) : null}
>
);
};
-// if the string == null or is empty, return null, else return string
-function nullableString(str: string | null | undefined) {
- if (str == null || str.trim() === '') return null;
- return str;
-}
-
// eslint-disable-next-line import/no-default-export
export { IndexActionConnectorFields as default };
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/translations.ts
index b7dd6ac749909..d86824fd1813f 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/translations.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/translations.ts
@@ -7,10 +7,10 @@
import { i18n } from '@kbn/i18n';
-export const INDEX_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.requiredIndexText',
+export const INDEX_IS_NOT_VALID = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.notValidIndexText',
{
- defaultMessage: 'Index is required.',
+ defaultMessage: 'Index is not valid.',
}
);
@@ -27,3 +27,31 @@ export const HISTORY_NOT_VALID = i18n.translate(
defaultMessage: 'Alert history index must contain valid suffix.',
}
);
+
+export const EXECUTION_TIME_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.executionTimeFieldLabel',
+ {
+ defaultMessage: 'Time field',
+ }
+);
+
+export const SHOW_TIME_FIELD_TOGGLE_TOOLTIP = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.definedateFieldTooltip',
+ {
+ defaultMessage: `Set this time field to the time the document was indexed.`,
+ }
+);
+
+export const REFRESH_FIELD_TOGGLE_TOOLTIP = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.refreshTooltip',
+ {
+ defaultMessage: 'Refresh the affected shards to make this operation visible to search.',
+ }
+);
+
+export const INDEX_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesToQueryLabel',
+ {
+ defaultMessage: 'Index',
+ }
+);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.test.tsx
index 3bb023b135c40..07aa45cfe1925 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.test.tsx
@@ -8,7 +8,6 @@
import { TypeRegistry } from '../../../type_registry';
import { registerBuiltInActionTypes } from '..';
import { ActionTypeModel } from '../../../../types';
-import { JiraActionConnector } from './types';
import { registrationServicesMock } from '../../../../mocks';
const ACTION_TYPE_ID = '.jira';
@@ -29,68 +28,6 @@ describe('actionTypeRegistry.get() works', () => {
});
});
-describe('jira connector validation', () => {
- test('connector validation succeeds when connector config is valid', async () => {
- const actionConnector = {
- secrets: {
- email: 'email',
- apiToken: 'apiToken',
- },
- id: 'test',
- actionTypeId: '.jira',
- name: 'jira',
- isPreconfigured: false,
- isDeprecated: false,
- config: {
- apiUrl: 'https://siem-kibana.atlassian.net',
- projectKey: 'CK',
- },
- } as JiraActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- apiUrl: [],
- projectKey: [],
- },
- },
- secrets: {
- errors: {
- apiToken: [],
- email: [],
- },
- },
- });
- });
-
- test('connector validation fails when connector config is not valid', async () => {
- const actionConnector = {
- secrets: {
- email: 'user',
- },
- id: '.jira',
- actionTypeId: '.jira',
- name: 'jira',
- config: {},
- } as unknown as JiraActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- apiUrl: ['URL is required.'],
- projectKey: ['Project key is required'],
- },
- },
- secrets: {
- errors: {
- apiToken: ['API token is required'],
- email: [],
- },
- },
- });
- });
-});
-
describe('jira action params validation', () => {
test('action params validation succeeds when action params is valid', async () => {
const actionParams = {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx
index 8e3424a16c295..627429a39b5b3 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx
@@ -7,58 +7,8 @@
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
-import {
- GenericValidationResult,
- ActionTypeModel,
- ConnectorValidationResult,
-} from '../../../../types';
-import { JiraActionConnector, JiraConfig, JiraSecrets, JiraActionParams } from './types';
-import { isValidUrl } from '../../../lib/value_validators';
-
-const validateConnector = async (
- action: JiraActionConnector
-): Promise> => {
- const translations = await import('./translations');
- const configErrors = {
- apiUrl: new Array(),
- projectKey: new Array(),
- };
- const secretsErrors = {
- email: new Array(),
- apiToken: new Array(),
- };
-
- const validationResult = {
- config: { errors: configErrors },
- secrets: { errors: secretsErrors },
- };
-
- if (!action.config.apiUrl) {
- configErrors.apiUrl = [...configErrors.apiUrl, translations.API_URL_REQUIRED];
- }
-
- if (action.config.apiUrl) {
- if (!isValidUrl(action.config.apiUrl)) {
- configErrors.apiUrl = [...configErrors.apiUrl, translations.API_URL_INVALID];
- } else if (!isValidUrl(action.config.apiUrl, 'https:')) {
- configErrors.apiUrl = [...configErrors.apiUrl, translations.API_URL_REQUIRE_HTTPS];
- }
- }
-
- if (!action.config.projectKey) {
- configErrors.projectKey = [...configErrors.projectKey, translations.JIRA_PROJECT_KEY_REQUIRED];
- }
-
- if (!action.secrets.email) {
- secretsErrors.email = [...secretsErrors.email, translations.JIRA_EMAIL_REQUIRED];
- }
-
- if (!action.secrets.apiToken) {
- secretsErrors.apiToken = [...secretsErrors.apiToken, translations.JIRA_API_TOKEN_REQUIRED];
- }
-
- return validationResult;
-};
+import { GenericValidationResult, ActionTypeModel } from '../../../../types';
+import { JiraConfig, JiraSecrets, JiraActionParams } from './types';
export const JIRA_DESC = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.jira.selectMessageText',
@@ -80,7 +30,6 @@ export function getActionType(): ActionTypeModel import('./logo')),
selectMessage: JIRA_DESC,
actionTypeTitle: JIRA_TITLE,
- validateConnector,
actionConnectorFields: lazy(() => import('./jira_connectors')),
validateParams: async (
actionParams: JiraActionParams
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.test.tsx
index 22d154373ea66..3afb0d16f8cbd 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.test.tsx
@@ -8,168 +8,141 @@
import React from 'react';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import JiraConnectorFields from './jira_connectors';
-import { JiraActionConnector } from './types';
+import { ConnectorFormTestProvider } from '../test_utils';
+import { act, render } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
jest.mock('../../../../common/lib/kibana');
describe('JiraActionConnectorFields renders', () => {
- test('alerting Jira connector fields are rendered', () => {
+ test('Jira connector fields are rendered', () => {
const actionConnector = {
- secrets: {
- email: 'email',
- apiToken: 'token',
- },
- id: 'test',
actionTypeId: '.jira',
- isPreconfigured: false,
- isDeprecated: false,
name: 'jira',
config: {
- apiUrl: 'https://test/',
+ apiUrl: 'https://test.com',
projectKey: 'CK',
},
- } as JiraActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
-
- expect(wrapper.find('[data-test-subj="apiUrlFromInput"]').length > 0).toBeTruthy();
- expect(
- wrapper.find('[data-test-subj="connector-jira-project-key-form-input"]').length > 0
- ).toBeTruthy();
-
- expect(
- wrapper.find('[data-test-subj="connector-jira-email-form-input"]').length > 0
- ).toBeTruthy();
-
- expect(
- wrapper.find('[data-test-subj="connector-jira-apiToken-form-input"]').length > 0
- ).toBeTruthy();
- });
-
- test('case specific Jira connector fields is rendered', () => {
- const actionConnector = {
secrets: {
email: 'email',
apiToken: 'token',
},
- id: 'test',
- actionTypeId: '.jira',
- isPreconfigured: false,
isDeprecated: false,
- name: 'jira',
- config: {
- apiUrl: 'https://test/',
- projectKey: 'CK',
- },
- } as JiraActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- consumer={'case'}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="apiUrlFromInput"]').length > 0).toBeTruthy();
- expect(
- wrapper.find('[data-test-subj="connector-jira-project-key-form-input"]').length > 0
- ).toBeTruthy();
-
- expect(
- wrapper.find('[data-test-subj="connector-jira-email-form-input"]').length > 0
- ).toBeTruthy();
-
- expect(
- wrapper.find('[data-test-subj="connector-jira-apiToken-form-input"]').length > 0
- ).toBeTruthy();
- });
+ };
- test('should display a message on create to remember credentials', () => {
- const actionConnector = {
- actionTypeId: '.jira',
- isPreconfigured: false,
- isDeprecated: false,
- secrets: {},
- config: {},
- } as JiraActionConnector;
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0);
- });
- test('should display a message when secrets is missing', () => {
- const actionConnector = {
- actionTypeId: '.jira',
- isPreconfigured: false,
- isDeprecated: false,
- isMissingSecrets: true,
- secrets: {},
- config: {},
- } as JiraActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0);
+ expect(wrapper.find('[data-test-subj="config.apiUrl-input"]').length > 0).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="config.projectKey-input"]').length > 0).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="secrets.email-input"]').length > 0).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="secrets.apiToken-input"]').length > 0).toBeTruthy();
});
- test('should display a message on edit to re-enter credentials', () => {
- const actionConnector = {
- secrets: {
- email: 'email',
- apiToken: 'token',
- },
- id: 'test',
- actionTypeId: '.jira',
- isPreconfigured: false,
- isDeprecated: false,
- name: 'jira',
- config: {
- apiUrl: 'https://test/',
- projectKey: 'CK',
- },
- } as JiraActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0);
+ describe('Validation', () => {
+ const onSubmit = jest.fn();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ const tests: Array<[string, string]> = [
+ ['config.apiUrl-input', 'not-valid'],
+ ['config.projectKey-input', ''],
+ ['secrets.email-input', ''],
+ ['secrets.apiToken-input', ''],
+ ];
+
+ it('connector validation succeeds when connector config is valid', async () => {
+ const actionConnector = {
+ actionTypeId: '.jira',
+ name: 'jira',
+ config: {
+ apiUrl: 'https://test.com',
+ projectKey: 'CK',
+ },
+ secrets: {
+ email: 'email',
+ apiToken: 'token',
+ },
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ actionTypeId: '.jira',
+ name: 'jira',
+ config: {
+ apiUrl: 'https://test.com',
+ projectKey: 'CK',
+ },
+ secrets: {
+ email: 'email',
+ apiToken: 'token',
+ },
+ isDeprecated: false,
+ },
+ isValid: true,
+ });
+ });
+
+ it.each(tests)('validates correctly %p', async (field, value) => {
+ const actionConnector = {
+ actionTypeId: '.jira',
+ name: 'jira',
+ config: {
+ apiUrl: 'https://test.com',
+ projectKey: 'CK',
+ },
+ secrets: {
+ email: 'email',
+ apiToken: 'token',
+ },
+ isDeprecated: false,
+ };
+
+ const res = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.tsx
index 7aec0a405d0d5..5d056281cd38a 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_connectors.tsx
@@ -5,182 +5,34 @@
* 2.0.
*/
-import React, { useCallback } from 'react';
-
-import {
- EuiFieldText,
- EuiFlexGroup,
- EuiFlexItem,
- EuiFormRow,
- EuiFieldPassword,
- EuiSpacer,
- EuiTitle,
-} from '@elastic/eui';
+import React from 'react';
import { ActionConnectorFieldsProps } from '../../../../types';
import * as i18n from './translations';
-import { JiraActionConnector } from './types';
-import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label';
-
-const JiraConnectorFields: React.FC> = ({
- action,
- editActionSecrets,
- editActionConfig,
- errors,
- readOnly,
-}) => {
- const { apiUrl, projectKey } = action.config;
-
- const isApiUrlInvalid: boolean =
- apiUrl !== undefined && errors.apiUrl !== undefined && errors.apiUrl.length > 0;
-
- const { email, apiToken } = action.secrets;
-
- const isProjectKeyInvalid: boolean =
- projectKey !== undefined && errors.projectKey !== undefined && errors.projectKey.length > 0;
- const isEmailInvalid: boolean =
- email !== undefined && errors.email !== undefined && errors.email.length > 0;
- const isApiTokenInvalid: boolean =
- apiToken !== undefined && errors.apiToken !== undefined && errors.apiToken.length > 0;
-
- const handleOnChangeActionConfig = useCallback(
- (key: string, value: string) => editActionConfig(key, value),
- [editActionConfig]
- );
-
- const handleOnChangeSecretConfig = useCallback(
- (key: string, value: string) => editActionSecrets(key, value),
- [editActionSecrets]
- );
-
- const handleResetField = useCallback(
- (checkValue, fieldName: string, actionField: 'config' | 'secrets') => {
- if (!checkValue) {
- if (actionField === 'config') {
- handleOnChangeActionConfig(fieldName, '');
- } else {
- handleOnChangeSecretConfig(fieldName, '');
- }
- }
- },
- [handleOnChangeActionConfig, handleOnChangeSecretConfig]
- );
-
+import {
+ ConfigFieldSchema,
+ SimpleConnectorForm,
+ SecretsFieldSchema,
+} from '../../simple_connector_form';
+
+const configFormSchema: ConfigFieldSchema[] = [
+ { id: 'apiUrl', label: i18n.API_URL_LABEL, isUrlField: true },
+ { id: 'projectKey', label: i18n.JIRA_PROJECT_KEY_LABEL },
+];
+const secretsFormSchema: SecretsFieldSchema[] = [
+ { id: 'email', label: i18n.JIRA_EMAIL_LABEL },
+ { id: 'apiToken', label: i18n.JIRA_API_TOKEN_LABEL, isPasswordField: true },
+];
+
+const JiraConnectorFields: React.FC = ({ readOnly, isEdit }) => {
return (
- <>
-
-
-
- handleOnChangeActionConfig('apiUrl', evt.target.value)}
- onBlur={() => handleResetField(apiUrl, 'apiUrl', 'config')}
- />
-
-
-
-
-
-
-
- handleOnChangeActionConfig('projectKey', evt.target.value)}
- onBlur={() => handleResetField(projectKey, 'projectKey', 'config')}
- />
-
-
-
-
-
-
-
- {i18n.JIRA_AUTHENTICATION_LABEL}
-
-
-
-
-
-
-
- {getEncryptedFieldNotifyLabel(
- !action.id,
- 2,
- action.isMissingSecrets ?? false,
- i18n.JIRA_REENTER_VALUES_LABEL
- )}
-
-
-
-
-
-
-
- handleOnChangeSecretConfig('email', evt.target.value)}
- onBlur={() => handleResetField(email, 'email', 'secrets')}
- />
-
-
-
-
-
-
-
- handleOnChangeSecretConfig('apiToken', evt.target.value)}
- onBlur={() => handleResetField(apiToken, 'apiToken', 'secrets')}
- />
-
-
-
- >
+
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts
index 1b9f3e2caa6ef..0a0fd0462456f 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts
@@ -14,27 +14,6 @@ export const API_URL_LABEL = i18n.translate(
}
);
-export const API_URL_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredApiUrlTextField',
- {
- defaultMessage: 'URL is required.',
- }
-);
-
-export const API_URL_INVALID = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.jira.invalidApiUrlTextField',
- {
- defaultMessage: 'URL is invalid.',
- }
-);
-
-export const API_URL_REQUIRE_HTTPS = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.jira.requireHttpsApiUrlTextField',
- {
- defaultMessage: 'URL must start with https://.',
- }
-);
-
export const JIRA_PROJECT_KEY_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.jira.projectKey',
{
@@ -42,28 +21,6 @@ export const JIRA_PROJECT_KEY_LABEL = i18n.translate(
}
);
-export const JIRA_PROJECT_KEY_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredProjectKeyTextField',
- {
- defaultMessage: 'Project key is required',
- }
-);
-
-export const JIRA_AUTHENTICATION_LABEL = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.jira.authenticationLabel',
- {
- defaultMessage: 'Authentication',
- }
-);
-
-export const JIRA_REENTER_VALUES_LABEL = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.jira.reenterValuesLabel',
- {
- defaultMessage:
- 'Authentication credentials are encrypted. Please reenter values for these fields.',
- }
-);
-
export const JIRA_EMAIL_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.jira.emailTextFieldLabel',
{
@@ -71,13 +28,6 @@ export const JIRA_EMAIL_LABEL = i18n.translate(
}
);
-export const JIRA_EMAIL_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredEmailTextField',
- {
- defaultMessage: 'Email address is required',
- }
-);
-
export const JIRA_API_TOKEN_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.jira.apiTokenTextFieldLabel',
{
@@ -85,27 +35,6 @@ export const JIRA_API_TOKEN_LABEL = i18n.translate(
}
);
-export const JIRA_API_TOKEN_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredApiTokenTextField',
- {
- defaultMessage: 'API token is required',
- }
-);
-
-export const MAPPING_FIELD_SUMMARY = i18n.translate(
- 'xpack.triggersActionsUI.cases.configureCases.mappingFieldSummary',
- {
- defaultMessage: 'Summary',
- }
-);
-
-export const DESCRIPTION_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredDescriptionTextField',
- {
- defaultMessage: 'Description is required.',
- }
-);
-
export const SUMMARY_REQUIRED = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.jira.requiredSummaryTextField',
{
@@ -113,20 +42,6 @@ export const SUMMARY_REQUIRED = i18n.translate(
}
);
-export const MAPPING_FIELD_DESC = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.jira.mappingFieldDescription',
- {
- defaultMessage: 'Description',
- }
-);
-
-export const MAPPING_FIELD_COMMENTS = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.jira.mappingFieldComments',
- {
- defaultMessage: 'Comments',
- }
-);
-
export const ISSUE_TYPES_API_ERROR = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.jira.unableToGetIssueTypesMessage',
{
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx
index a8274729506af..59e2eaa55c5bc 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.test.tsx
@@ -8,7 +8,6 @@
import { TypeRegistry } from '../../../type_registry';
import { registerBuiltInActionTypes } from '..';
import { ActionTypeModel } from '../../../../types';
-import { PagerDutyActionConnector } from '../types';
import { registrationServicesMock } from '../../../../mocks';
const ACTION_TYPE_ID = '.pagerduty';
@@ -30,60 +29,6 @@ describe('actionTypeRegistry.get() works', () => {
});
});
-describe('pagerduty connector validation', () => {
- test('connector validation succeeds when connector config is valid', async () => {
- const actionConnector = {
- secrets: {
- routingKey: 'test',
- },
- id: 'test',
- actionTypeId: '.pagerduty',
- name: 'pagerduty',
- config: {
- apiUrl: 'http:\\test',
- },
- } as PagerDutyActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- secrets: {
- errors: {
- routingKey: [],
- },
- },
- });
-
- delete actionConnector.config.apiUrl;
- actionConnector.secrets.routingKey = 'test1';
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- secrets: {
- errors: {
- routingKey: [],
- },
- },
- });
- });
-
- test('connector validation fails when connector config is not valid', async () => {
- const actionConnector = {
- secrets: {},
- id: 'test',
- actionTypeId: '.pagerduty',
- name: 'pagerduty',
- config: {
- apiUrl: 'http:\\test',
- },
- } as PagerDutyActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- secrets: {
- errors: {
- routingKey: ['An integration key / routing key is required.'],
- },
- },
- });
- });
-});
-
describe('pagerduty action params validation', () => {
test('action params validation succeeds when action params is valid', async () => {
const actionParams = {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx
index 42813186fe9f1..53b41ea49a274 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx
@@ -8,13 +8,8 @@
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
import moment from 'moment';
+import { ActionTypeModel, GenericValidationResult } from '../../../../types';
import {
- ActionTypeModel,
- GenericValidationResult,
- ConnectorValidationResult,
-} from '../../../../types';
-import {
- PagerDutyActionConnector,
PagerDutyConfig,
PagerDutySecrets,
PagerDutyActionParams,
@@ -42,22 +37,6 @@ export function getActionType(): ActionTypeModel<
defaultMessage: 'Send to PagerDuty',
}
),
- validateConnector: async (
- action: PagerDutyActionConnector
- ): Promise> => {
- const translations = await import('./translations');
- const secretsErrors = {
- routingKey: new Array(),
- };
- const validationResult = {
- secrets: { errors: secretsErrors },
- };
-
- if (!action.secrets.routingKey) {
- secretsErrors.routingKey.push(translations.INTEGRATION_KEY_REQUIRED);
- }
- return validationResult;
- },
validateParams: async (
actionParams: PagerDutyActionParams
): Promise<
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx
index ee0b4f214cb5c..1ccd7fba702f6 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.test.tsx
@@ -8,8 +8,11 @@
import React from 'react';
import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers';
import { act } from 'react-dom/test-utils';
-import { PagerDutyActionConnector } from '../types';
import PagerDutyActionConnectorFields from './pagerduty_connectors';
+import { ConnectorFormTestProvider } from '../test_utils';
+import { render } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
jest.mock('../../../../common/lib/kibana');
describe('PagerDutyActionConnectorFields renders', () => {
@@ -22,20 +25,19 @@ describe('PagerDutyActionConnectorFields renders', () => {
actionTypeId: '.pagerduty',
name: 'pagerduty',
config: {
- apiUrl: 'http:\\test',
+ apiUrl: 'http://test.com',
},
- } as PagerDutyActionConnector;
+ isDeprecated: false,
+ };
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
await act(async () => {
@@ -45,83 +47,171 @@ describe('PagerDutyActionConnectorFields renders', () => {
expect(wrapper.find('[data-test-subj="pagerdutyApiUrlInput"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="pagerdutyApiUrlInput"]').first().prop('value')).toBe(
- 'http:\\test'
+ 'http://test.com'
);
expect(wrapper.find('[data-test-subj="pagerdutyRoutingKeyInput"]').length > 0).toBeTruthy();
});
- test('should display a message on create to remember credentials', () => {
- const actionConnector = {
- actionTypeId: '.pagerduty',
- secrets: {},
- config: {},
- } as PagerDutyActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0);
- });
+ describe('Validation', () => {
+ const onSubmit = jest.fn();
- test('should display a message on edit to re-enter credentials', () => {
- const actionConnector = {
- secrets: {
- routingKey: 'test',
- },
- id: 'test',
- actionTypeId: '.pagerduty',
- name: 'pagerduty',
- config: {
- apiUrl: 'http:\\test',
- },
- } as PagerDutyActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0);
- });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
- test('should display a message for missing secrets after import', () => {
- const actionConnector = {
- secrets: {
- routingKey: 'test',
- },
- id: 'test',
- actionTypeId: '.pagerduty',
- isMissingSecrets: true,
- name: 'pagerduty',
- config: {
- apiUrl: 'http:\\test',
- },
- } as PagerDutyActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0);
+ it('connector validation succeeds when connector config is valid', async () => {
+ const actionConnector = {
+ secrets: {
+ routingKey: 'test',
+ },
+ id: 'test',
+ actionTypeId: '.pagerduty',
+ name: 'pagerduty',
+ config: {
+ apiUrl: 'http://test.com',
+ },
+ isDeprecated: false,
+ };
+
+ const res = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ secrets: {
+ routingKey: 'test',
+ },
+ id: 'test',
+ actionTypeId: '.pagerduty',
+ name: 'pagerduty',
+ config: {
+ apiUrl: 'http://test.com',
+ },
+ isDeprecated: false,
+ },
+ isValid: true,
+ });
+ });
+
+ it('validates correctly if the apiUrl is empty', async () => {
+ const actionConnector = {
+ secrets: {
+ routingKey: 'test',
+ },
+ id: 'test',
+ actionTypeId: '.pagerduty',
+ name: 'pagerduty',
+ config: {
+ apiUrl: '',
+ },
+ isDeprecated: false,
+ };
+
+ const res = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ secrets: {
+ routingKey: 'test',
+ },
+ id: 'test',
+ actionTypeId: '.pagerduty',
+ name: 'pagerduty',
+ isDeprecated: false,
+ },
+ isValid: true,
+ });
+ });
+
+ it('validates correctly if the apiUrl is not empty and not a valid url', async () => {
+ const actionConnector = {
+ secrets: {
+ routingKey: 'test',
+ },
+ id: 'test',
+ actionTypeId: '.pagerduty',
+ name: 'pagerduty',
+ config: {
+ apiUrl: 'not-valid',
+ },
+ isDeprecated: false,
+ };
+
+ const res = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {},
+ isValid: false,
+ });
+ });
+
+ it('validates correctly the routingKey', async () => {
+ const actionConnector = {
+ secrets: {
+ routingKey: '',
+ },
+ id: 'test',
+ actionTypeId: '.pagerduty',
+ name: 'pagerduty',
+ config: {
+ apiUrl: 'not-valid',
+ },
+ isDeprecated: false,
+ };
+
+ const res = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {},
+ isValid: false,
+ });
+ });
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx
index 49aaec5bb6f24..21f7443b6b545 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_connectors.tsx
@@ -6,99 +6,88 @@
*/
import React from 'react';
-import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
+import { EuiLink } from '@elastic/eui';
+import { isEmpty } from 'lodash';
import { FormattedMessage } from '@kbn/i18n-react';
+import { FieldConfig, UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components';
+import { DocLinksStart } from '@kbn/core/public';
import { ActionConnectorFieldsProps } from '../../../../types';
-import { PagerDutyActionConnector } from '../types';
import { useKibana } from '../../../../common/lib/kibana';
-import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label';
+import * as i18n from './translations';
-const PagerDutyActionConnectorFields: React.FunctionComponent<
- ActionConnectorFieldsProps
-> = ({ errors, action, editActionConfig, editActionSecrets, readOnly }) => {
+const { emptyField, urlField } = fieldValidators;
+
+const getApiURLConfig = (): FieldConfig => ({
+ label: i18n.API_URL_LABEL,
+ validations: [
+ {
+ validator: (args) => {
+ const { value } = args;
+ /**
+ * The field is optional so if it is empty
+ * we do not validate
+ */
+ if (isEmpty(value)) {
+ return;
+ }
+
+ return urlField(i18n.API_URL_INVALID)(args);
+ },
+ },
+ ],
+});
+
+const getRoutingKeyConfig = (docLinks: DocLinksStart): FieldConfig => ({
+ label: i18n.INTEGRATION_KEY_LABEL,
+ helpText: (
+
+
+
+ ),
+ validations: [
+ {
+ validator: emptyField(i18n.INTEGRATION_KEY_REQUIRED),
+ },
+ ],
+});
+
+const PagerDutyActionConnectorFields: React.FunctionComponent = ({
+ readOnly,
+ isEdit,
+}) => {
const { docLinks } = useKibana().services;
- const { apiUrl } = action.config;
- const { routingKey } = action.secrets;
- const isRoutingKeyInvalid: boolean =
- routingKey !== undefined && errors.routingKey !== undefined && errors.routingKey.length > 0;
return (
<>
-
- ) => {
- editActionConfig('apiUrl', e.target.value);
- }}
- onBlur={() => {
- if (!apiUrl) {
- editActionConfig('apiUrl', '');
- }
- }}
- />
-
-
-
-
- }
- error={errors.routingKey}
- isInvalid={isRoutingKeyInvalid}
- label={i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyTextFieldLabel',
- {
- defaultMessage: 'Integration key',
- }
- )}
- >
- <>
- {getEncryptedFieldNotifyLabel(
- !action.id,
- 1,
- action.isMissingSecrets ?? false,
- i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.reenterValueLabel',
- { defaultMessage: 'This key is encrypted. Please reenter a value for this field.' }
- )
- )}
- ) => {
- editActionSecrets('routingKey', e.target.value);
- }}
- onBlur={() => {
- if (!routingKey) {
- editActionSecrets('routingKey', '');
- }
- }}
- />
- >
-
+
+
>
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/translations.ts
index a907b19a1d733..22649354f20ff 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/translations.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/translations.ts
@@ -27,3 +27,24 @@ export const INTEGRATION_KEY_REQUIRED = i18n.translate(
defaultMessage: 'An integration key / routing key is required.',
}
);
+
+export const API_URL_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.apiUrlTextFieldLabel',
+ {
+ defaultMessage: 'API URL (optional)',
+ }
+);
+
+export const API_URL_INVALID = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.apiUrlInvalid',
+ {
+ defaultMessage: 'Invalid API URL',
+ }
+);
+
+export const INTEGRATION_KEY_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.routingKeyTextFieldLabel',
+ {
+ defaultMessage: 'Integration key',
+ }
+);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.test.tsx
index 209606913dce6..c46bcd6a02c71 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.test.tsx
@@ -8,7 +8,6 @@
import { TypeRegistry } from '../../../type_registry';
import { registerBuiltInActionTypes } from '..';
import { ActionTypeModel } from '../../../../types';
-import { ResilientActionConnector } from './types';
import { registrationServicesMock } from '../../../../mocks';
const ACTION_TYPE_ID = '.resilient';
@@ -29,68 +28,6 @@ describe('actionTypeRegistry.get() works', () => {
});
});
-describe('resilient connector validation', () => {
- test('connector validation succeeds when connector config is valid', async () => {
- const actionConnector = {
- secrets: {
- apiKeyId: 'email',
- apiKeySecret: 'token',
- },
- id: 'test',
- actionTypeId: '.resilient',
- isPreconfigured: false,
- isDeprecated: false,
- name: 'resilient',
- config: {
- apiUrl: 'https://test/',
- orgId: '201',
- },
- } as ResilientActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- apiUrl: [],
- orgId: [],
- },
- },
- secrets: {
- errors: {
- apiKeySecret: [],
- apiKeyId: [],
- },
- },
- });
- });
-
- test('connector validation fails when connector config is not valid', async () => {
- const actionConnector = {
- secrets: {
- apiKeyId: 'user',
- },
- id: '.jira',
- actionTypeId: '.jira',
- name: 'jira',
- config: {},
- } as unknown as ResilientActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- apiUrl: ['URL is required.'],
- orgId: ['Organization ID is required'],
- },
- },
- secrets: {
- errors: {
- apiKeySecret: ['Secret is required'],
- apiKeyId: [],
- },
- },
- });
- });
-});
-
describe('resilient action params validation', () => {
test('action params validation succeeds when action params is valid', async () => {
const actionParams = {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx
index f20204af17697..2297107e914cc 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx
@@ -7,66 +7,8 @@
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
-import {
- GenericValidationResult,
- ActionTypeModel,
- ConnectorValidationResult,
-} from '../../../../types';
-import {
- ResilientActionConnector,
- ResilientConfig,
- ResilientSecrets,
- ResilientActionParams,
-} from './types';
-import { isValidUrl } from '../../../lib/value_validators';
-
-const validateConnector = async (
- action: ResilientActionConnector
-): Promise> => {
- const translations = await import('./translations');
- const configErrors = {
- apiUrl: new Array(),
- orgId: new Array(),
- };
- const secretsErrors = {
- apiKeyId: new Array(),
- apiKeySecret: new Array(),
- };
-
- const validationResult = {
- config: { errors: configErrors },
- secrets: { errors: secretsErrors },
- };
-
- if (!action.config.apiUrl) {
- configErrors.apiUrl = [...configErrors.apiUrl, translations.API_URL_REQUIRED];
- }
-
- if (action.config.apiUrl) {
- if (!isValidUrl(action.config.apiUrl)) {
- configErrors.apiUrl = [...configErrors.apiUrl, translations.API_URL_INVALID];
- } else if (!isValidUrl(action.config.apiUrl, 'https:')) {
- configErrors.apiUrl = [...configErrors.apiUrl, translations.API_URL_REQUIRE_HTTPS];
- }
- }
-
- if (!action.config.orgId) {
- configErrors.orgId = [...configErrors.orgId, translations.ORG_ID_REQUIRED];
- }
-
- if (!action.secrets.apiKeyId) {
- secretsErrors.apiKeyId = [...secretsErrors.apiKeyId, translations.API_KEY_ID_REQUIRED];
- }
-
- if (!action.secrets.apiKeySecret) {
- secretsErrors.apiKeySecret = [
- ...secretsErrors.apiKeySecret,
- translations.API_KEY_SECRET_REQUIRED,
- ];
- }
-
- return validationResult;
-};
+import { GenericValidationResult, ActionTypeModel } from '../../../../types';
+import { ResilientConfig, ResilientSecrets, ResilientActionParams } from './types';
export const DESC = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.resilient.selectMessageText',
@@ -92,7 +34,6 @@ export function getActionType(): ActionTypeModel<
iconClass: lazy(() => import('./logo')),
selectMessage: DESC,
actionTypeTitle: TITLE,
- validateConnector,
actionConnectorFields: lazy(() => import('./resilient_connectors')),
validateParams: async (
actionParams: ResilientActionParams
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx
index 7db5cc113fcb9..0fbe63b28b166 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx
@@ -8,169 +8,141 @@
import React from 'react';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import ResilientConnectorFields from './resilient_connectors';
-import { ResilientActionConnector } from './types';
+import { ConnectorFormTestProvider } from '../test_utils';
+import { act, render } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
jest.mock('../../../../common/lib/kibana');
describe('ResilientActionConnectorFields renders', () => {
test('alerting Resilient connector fields are rendered', () => {
const actionConnector = {
- secrets: {
- apiKeyId: 'key',
- apiKeySecret: 'secret',
- },
- id: 'test',
actionTypeId: '.resilient',
- isPreconfigured: false,
- isDeprecated: false,
name: 'resilient',
config: {
- apiUrl: 'https://test/',
+ apiUrl: 'https://test.com',
orgId: '201',
},
- } as ResilientActionConnector;
+ secrets: {
+ apiKeyId: 'key',
+ apiKeySecret: 'secret',
+ },
+ isDeprecated: false,
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
- expect(wrapper.find('[data-test-subj="apiUrlFromInput"]').length > 0).toBeTruthy();
- expect(
- wrapper.find('[data-test-subj="connector-resilient-orgId-form-input"]').length > 0
- ).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="config.apiUrl-input"]').length > 0).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="config.orgId-input"]').length > 0).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="secrets.apiKeyId-input"]').length > 0).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="secrets.apiKeySecret-input"]').length > 0).toBeTruthy();
+ });
- expect(
- wrapper.find('[data-test-subj="connector-resilient-apiKeySecret-form-input"]').length > 0
- ).toBeTruthy();
+ describe('Validation', () => {
+ const onSubmit = jest.fn();
- expect(
- wrapper.find('[data-test-subj="connector-resilient-apiKeySecret-form-input"]').length > 0
- ).toBeTruthy();
- });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
- test('case specific Resilient connector fields is rendered', () => {
- const actionConnector = {
- secrets: {
- apiKeyId: 'email',
- apiKeySecret: 'token',
- },
- id: 'test',
- actionTypeId: '.resilient',
- isPreconfigured: false,
- isDeprecated: false,
- name: 'resilient',
- config: {
- apiUrl: 'https://test/',
- orgId: '201',
- },
- } as ResilientActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- consumer={'case'}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
+ const tests: Array<[string, string]> = [
+ ['config.apiUrl-input', 'not-valid'],
+ ['config.orgId-input', ''],
+ ['secrets.apiKeyId-input', ''],
+ ['secrets.apiKeySecret-input', ''],
+ ];
- expect(wrapper.find('[data-test-subj="apiUrlFromInput"]').length > 0).toBeTruthy();
- expect(
- wrapper.find('[data-test-subj="connector-resilient-orgId-form-input"]').length > 0
- ).toBeTruthy();
+ it('connector validation succeeds when connector config is valid', async () => {
+ const actionConnector = {
+ actionTypeId: '.resilient',
+ name: 'resilient',
+ config: {
+ apiUrl: 'https://test.com',
+ orgId: '201',
+ },
+ secrets: {
+ apiKeyId: 'key',
+ apiKeySecret: 'secret',
+ },
+ isDeprecated: false,
+ };
- expect(
- wrapper.find('[data-test-subj="connector-resilient-apiKeySecret-form-input"]').length > 0
- ).toBeTruthy();
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
- expect(
- wrapper.find('[data-test-subj="connector-resilient-apiKeySecret-form-input"]').length > 0
- ).toBeTruthy();
- });
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
- test('should display a message on create to remember credentials', () => {
- const actionConnector = {
- actionTypeId: '.resilient',
- isPreconfigured: false,
- isDeprecated: false,
- config: {},
- secrets: {},
- } as ResilientActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0);
- });
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ actionTypeId: '.resilient',
+ name: 'resilient',
+ config: {
+ apiUrl: 'https://test.com',
+ orgId: '201',
+ },
+ secrets: {
+ apiKeyId: 'key',
+ apiKeySecret: 'secret',
+ },
+ isDeprecated: false,
+ },
+ isValid: true,
+ });
+ });
- test('should display a message for missing secrets after import', () => {
- const actionConnector = {
- actionTypeId: '.resilient',
- isPreconfigured: false,
- isDeprecated: false,
- config: {},
- secrets: {},
- isMissingSecrets: true,
- } as ResilientActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0);
- });
+ it.each(tests)('validates correctly %p', async (field, value) => {
+ const actionConnector = {
+ actionTypeId: '.resilient',
+ name: 'resilient',
+ config: {
+ apiUrl: 'https://test.com',
+ orgId: '201',
+ },
+ secrets: {
+ apiKeyId: 'key',
+ apiKeySecret: 'secret',
+ },
+ isDeprecated: false,
+ };
- test('should display a message on edit to re-enter credentials', () => {
- const actionConnector = {
- secrets: {
- apiKeyId: 'key',
- apiKeySecret: 'secret',
- },
- id: 'test',
- actionTypeId: '.resilient',
- isPreconfigured: false,
- isDeprecated: false,
- name: 'resilient',
- config: {
- apiUrl: 'https://test/',
- orgId: '201',
- },
- } as ResilientActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0);
+ const res = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx
index 1270f19820f4c..6cc4d31f1b405 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx
@@ -5,185 +5,33 @@
* 2.0.
*/
-import React, { useCallback } from 'react';
-
-import {
- EuiFieldText,
- EuiFlexGroup,
- EuiFlexItem,
- EuiFormRow,
- EuiFieldPassword,
- EuiSpacer,
- EuiTitle,
-} from '@elastic/eui';
+import React from 'react';
import { ActionConnectorFieldsProps } from '../../../../types';
import * as i18n from './translations';
-import { ResilientActionConnector } from './types';
-import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label';
-
-const ResilientConnectorFields: React.FC> = ({
- action,
- editActionSecrets,
- editActionConfig,
- errors,
- readOnly,
-}) => {
- const { apiUrl, orgId } = action.config;
- const isApiUrlInvalid: boolean =
- apiUrl !== undefined && errors.apiUrl !== undefined && errors.apiUrl.length > 0;
-
- const { apiKeyId, apiKeySecret } = action.secrets;
-
- const isOrgIdInvalid: boolean =
- orgId !== undefined && errors.orgId !== undefined && errors.orgId.length > 0;
- const isApiKeyInvalid: boolean =
- apiKeyId !== undefined && errors.apiKeyId !== undefined && errors.apiKeyId.length > 0;
- const isApiKeySecretInvalid: boolean =
- apiKeySecret !== undefined &&
- errors.apiKeySecret !== undefined &&
- errors.apiKeySecret.length > 0;
-
- const handleOnChangeActionConfig = useCallback(
- (key: string, value: string) => editActionConfig(key, value),
- [editActionConfig]
- );
-
- const handleOnChangeSecretConfig = useCallback(
- (key: string, value: string) => editActionSecrets(key, value),
- [editActionSecrets]
- );
-
+import {
+ ConfigFieldSchema,
+ SecretsFieldSchema,
+ SimpleConnectorForm,
+} from '../../simple_connector_form';
+
+const configFormSchema: ConfigFieldSchema[] = [
+ { id: 'apiUrl', label: i18n.API_URL_LABEL, isUrlField: true },
+ { id: 'orgId', label: i18n.ORG_ID_LABEL },
+];
+const secretsFormSchema: SecretsFieldSchema[] = [
+ { id: 'apiKeyId', label: i18n.API_KEY_ID_LABEL },
+ { id: 'apiKeySecret', label: i18n.API_KEY_SECRET_LABEL, isPasswordField: true },
+];
+
+const ResilientConnectorFields: React.FC = ({ readOnly, isEdit }) => {
return (
- <>
-
-
-
- handleOnChangeActionConfig('apiUrl', evt.target.value)}
- onBlur={() => {
- if (!apiUrl) {
- editActionConfig('apiUrl', '');
- }
- }}
- />
-
-
-
-
-
-
-
- handleOnChangeActionConfig('orgId', evt.target.value)}
- onBlur={() => {
- if (!orgId) {
- editActionConfig('orgId', '');
- }
- }}
- />
-
-
-
-
-
-
-
- {i18n.API_KEY_LABEL}
-
-
-
-
-
-
-
- {getEncryptedFieldNotifyLabel(
- !action.id,
- 2,
- action.isMissingSecrets ?? false,
- i18n.REENTER_VALUES_LABEL
- )}
-
-
-
-
-
-
-
- handleOnChangeSecretConfig('apiKeyId', evt.target.value)}
- onBlur={() => {
- if (!apiKeyId) {
- editActionSecrets('apiKeyId', '');
- }
- }}
- />
-
-
-
-
-
-
-
- handleOnChangeSecretConfig('apiKeySecret', evt.target.value)}
- onBlur={() => {
- if (!apiKeySecret) {
- editActionSecrets('apiKeySecret', '');
- }
- }}
- />
-
-
-
- >
+
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/translations.ts
index 6821c73b259f8..df4c15b0f322a 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/translations.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/translations.ts
@@ -14,27 +14,6 @@ export const API_URL_LABEL = i18n.translate(
}
);
-export const API_URL_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiUrlTextField',
- {
- defaultMessage: 'URL is required.',
- }
-);
-
-export const API_URL_INVALID = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.invalidApiUrlTextField',
- {
- defaultMessage: 'URL is invalid.',
- }
-);
-
-export const API_URL_REQUIRE_HTTPS = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.requireHttpsApiUrlTextField',
- {
- defaultMessage: 'URL must start with https://.',
- }
-);
-
export const ORG_ID_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.resilient.orgId',
{
@@ -42,88 +21,17 @@ export const ORG_ID_LABEL = i18n.translate(
}
);
-export const ORG_ID_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredOrgIdTextField',
- {
- defaultMessage: 'Organization ID is required',
- }
-);
-
-export const API_KEY_LABEL = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKey',
- {
- defaultMessage: 'API key',
- }
-);
-
-export const REMEMBER_VALUES_LABEL = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.rememberValuesLabel',
- {
- defaultMessage:
- 'Remember these values. You must reenter them each time you edit the connector.',
- }
-);
-
-export const REENTER_VALUES_LABEL = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.reenterValuesLabel',
- {
- defaultMessage: 'ID and secret are encrypted. Please reenter values for these fields.',
- }
-);
-
export const API_KEY_ID_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeyId',
{
- defaultMessage: 'ID',
- }
-);
-
-export const API_KEY_ID_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiKeyIdTextField',
- {
- defaultMessage: 'ID is required',
+ defaultMessage: 'API Key ID',
}
);
export const API_KEY_SECRET_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeySecret',
{
- defaultMessage: 'Secret',
- }
-);
-
-export const API_KEY_SECRET_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiKeySecretTextField',
- {
- defaultMessage: 'Secret is required',
- }
-);
-
-export const MAPPING_FIELD_NAME = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldShortDescription',
- {
- defaultMessage: 'Name',
- }
-);
-
-export const MAPPING_FIELD_DESC = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldDescription',
- {
- defaultMessage: 'Description',
- }
-);
-
-export const MAPPING_FIELD_COMMENTS = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldComments',
- {
- defaultMessage: 'Comments',
- }
-);
-
-export const DESCRIPTION_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredDescriptionTextField',
- {
- defaultMessage: 'Description is required.',
+ defaultMessage: 'API Key Secret',
}
);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx
index 012a233973012..098c65f294560 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx
@@ -7,7 +7,7 @@
import { TypeRegistry } from '../../../type_registry';
import { registerBuiltInActionTypes } from '..';
-import { ActionTypeModel, UserConfiguredActionConnector } from '../../../../types';
+import { ActionTypeModel } from '../../../../types';
import { registrationServicesMock } from '../../../../mocks';
const ACTION_TYPE_ID = '.server-log';
@@ -29,29 +29,6 @@ describe('actionTypeRegistry.get() works', () => {
});
});
-describe('server-log connector validation', () => {
- test('connector validation succeeds when connector config is valid', async () => {
- const actionConnector: UserConfiguredActionConnector<{}, {}> = {
- secrets: {},
- id: 'test',
- actionTypeId: '.server-log',
- name: 'server-log',
- config: {},
- isPreconfigured: false,
- isDeprecated: false,
- };
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {},
- },
- secrets: {
- errors: {},
- },
- });
- });
-});
-
describe('action params validation', () => {
test('action params validation succeeds when action params is valid', async () => {
const actionParams = {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx
index 066c5c0a2f385..4fe024f8861f0 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx
@@ -7,11 +7,7 @@
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
-import {
- ActionTypeModel,
- GenericValidationResult,
- ConnectorValidationResult,
-} from '../../../../types';
+import { ActionTypeModel, GenericValidationResult } from '../../../../types';
import { ServerLogActionParams } from '../types';
export function getActionType(): ActionTypeModel {
@@ -30,9 +26,6 @@ export function getActionType(): ActionTypeModel> => {
- return Promise.resolve({ config: { errors: {} }, secrets: { errors: {} } });
- },
validateParams: (
actionParams: ServerLogActionParams
): Promise>> => {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/application_required_callout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/application_required_callout.tsx
index 462d68d50508d..9aefd7a2259b2 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/application_required_callout.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/application_required_callout.tsx
@@ -25,7 +25,7 @@ const ERROR_MESSAGE = i18n.translate(
);
interface Props {
- appId: string;
+ appId?: string;
message?: string | null;
}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/credentials_auth.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/credentials_auth.tsx
index a9a7b7b0ebdb7..b996d8feac8e2 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/credentials_auth.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/credentials_auth.tsx
@@ -5,104 +5,52 @@
* 2.0.
*/
-import React, { memo, useCallback } from 'react';
-import { EuiFormRow, EuiFieldText, EuiFieldPassword } from '@elastic/eui';
-import type { ActionConnectorFieldsProps } from '../../../../../types';
+import React, { memo } from 'react';
+import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { TextField } from '@kbn/es-ui-shared-plugin/static/forms/components';
+import { PasswordField } from '../../../password_field';
import * as i18n from '../translations';
-import type { ServiceNowActionConnector } from '../types';
-import { isFieldInvalid } from '../helpers';
-import { getEncryptedFieldNotifyLabel } from '../../../get_encrypted_field_notify_label';
interface Props {
- action: ActionConnectorFieldsProps['action'];
- errors: ActionConnectorFieldsProps['errors'];
readOnly: boolean;
isLoading: boolean;
- editActionSecrets: ActionConnectorFieldsProps['editActionSecrets'];
+ pathPrefix?: string;
}
-const NUMBER_OF_FIELDS = 2;
-
-const CredentialsAuthComponent: React.FC = ({
- action,
- errors,
- isLoading,
- readOnly,
- editActionSecrets,
-}) => {
- const { username, password } = action.secrets;
-
- const isUsernameInvalid = isFieldInvalid(username, errors.username);
- const isPasswordInvalid = isFieldInvalid(password, errors.password);
-
- const onChangeUsernameEvent = useCallback(
- (event?: React.ChangeEvent) =>
- editActionSecrets('username', event?.target.value ?? ''),
- [editActionSecrets]
- );
-
- const onChangePasswordEvent = useCallback(
- (event?: React.ChangeEvent) =>
- editActionSecrets('password', event?.target.value ?? ''),
- [editActionSecrets]
- );
+const { emptyField } = fieldValidators;
+const CredentialsAuthComponent: React.FC = ({ isLoading, readOnly, pathPrefix = '' }) => {
return (
<>
-
- {getEncryptedFieldNotifyLabel(
- !action.id,
- NUMBER_OF_FIELDS,
- action.isMissingSecrets ?? false,
- i18n.REENTER_VALUES_LABEL
- )}
-
-
- {
- if (!username) {
- onChangeUsernameEvent();
- }
- }}
- disabled={isLoading}
- />
-
-
+
- {
- if (!password) {
- onChangePasswordEvent();
- }
- }}
- disabled={isLoading}
- />
-
+ readOnly={readOnly}
+ data-test-subj="connector-servicenow-password-form-input"
+ isLoading={isLoading}
+ disabled={readOnly || isLoading}
+ />
>
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/oauth.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/oauth.tsx
index f08759b675454..4c51641ea0bf1 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/oauth.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/auth_types/oauth.tsx
@@ -5,224 +5,121 @@
* 2.0.
*/
-import React, { memo, useCallback } from 'react';
-import { EuiFormRow, EuiFieldText, EuiFieldPassword, EuiTextArea } from '@elastic/eui';
-import { getEncryptedFieldNotifyLabel } from '../../../get_encrypted_field_notify_label';
-import type { ActionConnectorFieldsProps } from '../../../../../types';
-import type { ServiceNowActionConnector } from '../types';
+import React, { memo } from 'react';
+import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { TextAreaField, TextField } from '@kbn/es-ui-shared-plugin/static/forms/components';
import * as i18n from '../translations';
-import { isFieldInvalid } from '../helpers';
-import { PRIVATE_KEY_PASSWORD_HELPER_TEXT } from '../translations';
+import { PasswordField } from '../../../password_field';
interface Props {
- action: ActionConnectorFieldsProps['action'];
- errors: ActionConnectorFieldsProps['errors'];
readOnly: boolean;
isLoading: boolean;
- editActionSecrets: ActionConnectorFieldsProps['editActionSecrets'];
- editActionConfig: ActionConnectorFieldsProps['editActionConfig'];
+ pathPrefix?: string;
}
-const NUMBER_OF_FIELDS = 3;
-
-const OAuthComponent: React.FC = ({
- action,
- errors,
- isLoading,
- readOnly,
- editActionSecrets,
- editActionConfig,
-}) => {
- const { clientId, userIdentifierValue, jwtKeyId } = action.config;
- const { clientSecret, privateKey, privateKeyPassword } = action.secrets;
-
- const isClientIdInvalid = isFieldInvalid(clientId, errors.clientId);
- const isUserIdentifierInvalid = isFieldInvalid(userIdentifierValue, errors.userIdentifierValue);
- const isKeyIdInvalid = isFieldInvalid(jwtKeyId, errors.jwtKeyId);
- const isClientSecretInvalid = isFieldInvalid(clientSecret, errors.clientSecret);
- const isPrivateKeyInvalid = isFieldInvalid(privateKey, errors.privateKey);
-
- const onChangeClientIdEvent = useCallback(
- (event?: React.ChangeEvent) =>
- editActionConfig('clientId', event?.target.value ?? ''),
- [editActionConfig]
- );
- const onChangeUserIdentifierInvalidEvent = useCallback(
- (event?: React.ChangeEvent) =>
- editActionConfig('userIdentifierValue', event?.target.value ?? ''),
- [editActionConfig]
- );
- const onChangeJWTKeyIdEvent = useCallback(
- (event?: React.ChangeEvent) =>
- editActionConfig('jwtKeyId', event?.target.value ?? ''),
- [editActionConfig]
- );
-
- const onChangeClientSecretEvent = useCallback(
- (event?: React.ChangeEvent) =>
- editActionSecrets('clientSecret', event?.target.value ?? ''),
- [editActionSecrets]
- );
-
- const onChangePrivateKeyEvent = useCallback(
- (event?: React.ChangeEvent) =>
- editActionSecrets('privateKey', event?.target.value ?? ''),
- [editActionSecrets]
- );
-
- const onChangePrivateKeyPasswordEvent = useCallback(
- (event?: React.ChangeEvent) =>
- editActionSecrets('privateKeyPassword', event?.target.value ?? ''),
- [editActionSecrets]
- );
+const { emptyField } = fieldValidators;
+const OAuthComponent: React.FC = ({ isLoading, readOnly, pathPrefix = '' }) => {
return (
<>
-
- {getEncryptedFieldNotifyLabel(
- !action.id,
- NUMBER_OF_FIELDS,
- action.isMissingSecrets ?? false,
- i18n.REENTER_VALUES_LABEL
- )}
-
-
- {
- if (!clientId) {
- onChangeClientIdEvent();
- }
- }}
- disabled={isLoading}
- />
-
-
- {
- if (!userIdentifierValue) {
- onChangeUserIdentifierInvalidEvent();
- }
- }}
- disabled={isLoading}
- />
-
-
- {
- if (!jwtKeyId) {
- onChangeJWTKeyIdEvent();
- }
- }}
- disabled={isLoading}
- />
-
-
+
+
+
- {
- if (!clientSecret) {
- onChangeClientSecretEvent();
- }
- }}
- disabled={isLoading}
- />
-
-
- {
- if (!privateKey) {
- onChangePrivateKeyEvent();
- }
- }}
- disabled={isLoading}
- />
-
-
+
+
- {
- if (!privateKeyPassword) {
- onChangePrivateKeyPasswordEvent();
- }
- }}
- disabled={isLoading}
- />
-
+ helpText={i18n.PRIVATE_KEY_PASSWORD_HELPER_TEXT}
+ validate={false}
+ data-test-subj="connector-servicenow-private-key-password-form-input"
+ readOnly={readOnly}
+ isLoading={isLoading}
+ disabled={readOnly || isLoading}
+ />
>
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.test.tsx
index 81e7a720dff3b..d0b0b50b0ffc6 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.test.tsx
@@ -8,31 +8,29 @@
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import { Credentials } from './credentials';
-import { ServiceNowActionConnector } from './types';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
-jest.mock('../../../../common/lib/kibana');
-
-const editActionConfigMock = jest.fn();
-const editActionSecretsMock = jest.fn();
+import { ConnectorFormTestProvider } from '../test_utils';
-const basicAuthConnector: ServiceNowActionConnector = {
- secrets: { username: 'test', password: 'test' },
- config: { isOAuth: false, apiUrl: 'https://example.com', usesTableApi: false },
-} as ServiceNowActionConnector;
+jest.mock('../../../../common/lib/kibana');
describe('Credentials', () => {
+ const connector = {
+ id: 'test',
+ actionTypeId: '.servicenow',
+ isPreconfigured: false,
+ isDeprecated: true,
+ name: 'SN',
+ config: {},
+ secrets: {},
+ };
+
it('renders basic auth form', async () => {
render(
-
- {}}
- editActionConfig={() => {}}
- />
-
+
+
+
+
+
);
expect(screen.getByRole('switch')).toBeInTheDocument();
expect((await screen.findByRole('switch')).getAttribute('aria-checked')).toEqual('false');
@@ -50,24 +48,15 @@ describe('Credentials', () => {
it('switches to oauth form', async () => {
render(
-
-
-
+
+
+
+
+
);
fireEvent.click(screen.getByRole('switch'));
- expect(editActionConfigMock).toHaveBeenCalledWith('isOAuth', true);
- expect(editActionSecretsMock).toHaveBeenCalledWith('username', null);
- expect(editActionSecretsMock).toHaveBeenCalledWith('password', null);
-
expect((await screen.findByRole('switch')).getAttribute('aria-checked')).toEqual('true');
expect(screen.getByLabelText('ServiceNow instance URL')).toBeInTheDocument();
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.tsx
index 4073deca3981a..8953757ac2f9e 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials.tsx
@@ -5,56 +5,21 @@
* 2.0.
*/
-import React, { memo, useState } from 'react';
-import {
- EuiFlexGroup,
- EuiFlexItem,
- EuiSpacer,
- EuiTitle,
- EuiSwitch,
- EuiSwitchEvent,
-} from '@elastic/eui';
-import { ActionConnectorFieldsProps } from '../../../../types';
+import React, { memo } from 'react';
+import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
+import { ToggleField } from '@kbn/es-ui-shared-plugin/static/forms/components';
+import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import * as i18n from './translations';
-import { ServiceNowActionConnector } from './types';
import { CredentialsApiUrl } from './credentials_api_url';
import { CredentialsAuth, OAuth } from './auth_types';
interface Props {
- action: ActionConnectorFieldsProps['action'];
- errors: ActionConnectorFieldsProps['errors'];
+ isOAuth: boolean;
readOnly: boolean;
isLoading: boolean;
- editActionSecrets: ActionConnectorFieldsProps['editActionSecrets'];
- editActionConfig: ActionConnectorFieldsProps['editActionConfig'];
}
-const CredentialsComponent: React.FC = ({
- action,
- errors,
- readOnly,
- isLoading,
- editActionSecrets,
- editActionConfig,
-}) => {
- const [isOAuth, setIsOAuth] = useState(action.config.isOAuth);
-
- const switchIsOAuth = (e: EuiSwitchEvent) => {
- setIsOAuth(e.target.checked);
- editActionConfig('isOAuth', e.target.checked);
- if (!e.target.checked) {
- editActionConfig('clientId', null);
- editActionConfig('userIdentifierValue', null);
- editActionConfig('jwtKeyId', null);
- editActionSecrets('clientSecret', null);
- editActionSecrets('privateKey', null);
- editActionSecrets('privateKeyPassword', null);
- } else {
- editActionSecrets('username', null);
- editActionSecrets('password', null);
- }
- };
-
+const CredentialsComponent: React.FC = ({ readOnly, isLoading, isOAuth }) => {
return (
<>
@@ -62,13 +27,7 @@ const CredentialsComponent: React.FC = ({
{i18n.SN_INSTANCE_LABEL}
-
+
@@ -80,31 +39,24 @@ const CredentialsComponent: React.FC = ({
-
{isOAuth ? (
-
+
) : (
-
+
)}
>
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials_api_url.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials_api_url.tsx
index f6247bd12f61c..392b173080346 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials_api_url.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/credentials_api_url.tsx
@@ -5,40 +5,26 @@
* 2.0.
*/
-import React, { memo, useCallback } from 'react';
+import React, { memo } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
-import { EuiFormRow, EuiLink, EuiFieldText, EuiSpacer } from '@elastic/eui';
+import { EuiFormRow, EuiLink, EuiSpacer } from '@elastic/eui';
+import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { TextField } from '@kbn/es-ui-shared-plugin/static/forms/components';
+
import { useKibana } from '../../../../common/lib/kibana';
-import type { ActionConnectorFieldsProps } from '../../../../types';
import * as i18n from './translations';
-import type { ServiceNowActionConnector } from './types';
-import { isFieldInvalid } from './helpers';
interface Props {
- action: ActionConnectorFieldsProps['action'];
- errors: ActionConnectorFieldsProps['errors'];
readOnly: boolean;
isLoading: boolean;
- editActionConfig: ActionConnectorFieldsProps['editActionConfig'];
+ pathPrefix?: string;
}
-const CredentialsApiUrlComponent: React.FC = ({
- action,
- errors,
- isLoading,
- readOnly,
- editActionConfig,
-}) => {
- const { docLinks } = useKibana().services;
- const { apiUrl } = action.config;
-
- const isApiUrlInvalid = isFieldInvalid(apiUrl, errors.apiUrl);
+const { urlField } = fieldValidators;
- const onChangeApiUrlEvent = useCallback(
- (event?: React.ChangeEvent) =>
- editActionConfig('apiUrl', event?.target.value ?? ''),
- [editActionConfig]
- );
+const CredentialsApiUrlComponent: React.FC = ({ isLoading, readOnly, pathPrefix = '' }) => {
+ const { docLinks } = useKibana().services;
return (
<>
@@ -58,30 +44,26 @@ const CredentialsApiUrlComponent: React.FC = ({
-
- {
- if (!apiUrl) {
- onChangeApiUrlEvent();
- }
- }}
- disabled={isLoading}
- />
-
+
>
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts
index 0f99c67128326..3798a312083e1 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts
@@ -16,8 +16,9 @@ export const DEFAULT_CORRELATION_ID = '{{rule.id}}:{{alert.id}}';
export const choicesToEuiOptions = (choices: Choice[]): EuiSelectOption[] =>
choices.map((choice) => ({ value: choice.value, text: choice.label }));
-export const isRESTApiError = (res: AppInfo | RESTApiError): res is RESTApiError =>
- (res as RESTApiError).error != null || (res as RESTApiError).status === 'failure';
+export const isRESTApiError = (res: AppInfo | RESTApiError | undefined): res is RESTApiError =>
+ res != null &&
+ ((res as RESTApiError).error != null || (res as RESTApiError).status === 'failure');
export const isFieldInvalid = (
field: string | undefined | null,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.test.tsx
index aa37e31100a1e..9e786a189ec42 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.test.tsx
@@ -8,7 +8,6 @@
import { TypeRegistry } from '../../../type_registry';
import { registerBuiltInActionTypes } from '..';
import { ActionTypeModel } from '../../../../types';
-import { ServiceNowActionConnector } from './types';
import { registrationServicesMock } from '../../../../mocks';
const SERVICENOW_ITSM_ACTION_TYPE_ID = '.servicenow';
@@ -34,196 +33,6 @@ describe('actionTypeRegistry.get() works', () => {
});
});
-describe('servicenow connector validation', () => {
- [
- SERVICENOW_ITSM_ACTION_TYPE_ID,
- SERVICENOW_SIR_ACTION_TYPE_ID,
- SERVICENOW_ITOM_ACTION_TYPE_ID,
- ].forEach((id) => {
- test(`${id}: connector validation succeeds when connector config is valid`, async () => {
- const actionTypeModel = actionTypeRegistry.get(id);
- const actionConnector = {
- secrets: {
- username: 'user',
- password: 'pass',
- },
- id: 'test',
- actionTypeId: id,
- name: 'ServiceNow',
- isPreconfigured: false,
- isDeprecated: false,
- config: {
- isOAuth: false,
- apiUrl: 'https://dev94428.service-now.com/',
- usesTableApi: false,
- },
- } as ServiceNowActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- apiUrl: [],
- clientId: [],
- jwtKeyId: [],
- userIdentifierValue: [],
- usesTableApi: [],
- },
- },
- secrets: {
- errors: {
- username: [],
- password: [],
- clientSecret: [],
- privateKey: [],
- },
- },
- });
- });
-
- test(`${id}: connector validation fails when connector config is not valid`, async () => {
- const actionTypeModel = actionTypeRegistry.get(id);
- const actionConnector = {
- secrets: {
- username: 'user',
- },
- id,
- actionTypeId: id,
- name: 'servicenow',
- config: {},
- } as unknown as ServiceNowActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- apiUrl: ['URL is required.'],
- usesTableApi: [],
- clientId: [],
- jwtKeyId: [],
- userIdentifierValue: [],
- },
- },
- secrets: {
- errors: {
- username: [],
- password: ['Password is required.'],
- clientSecret: [],
- privateKey: [],
- },
- },
- });
- });
- });
-});
-
-describe('servicenow connector validation for OAuth', () => {
- [
- SERVICENOW_ITSM_ACTION_TYPE_ID,
- SERVICENOW_SIR_ACTION_TYPE_ID,
- SERVICENOW_ITOM_ACTION_TYPE_ID,
- ].forEach((id) => {
- const mockConnector = ({
- actionTypeId = '',
- clientSecret = 'clientSecret',
- privateKey = 'privateKey',
- privateKeyPassword = 'privateKeyPassword',
- isOAuth = true,
- apiUrl = 'https://dev94428.service-now.com/',
- usesTableApi = false,
- clientId = 'clientId',
- jwtKeyId = 'jwtKeyId',
- userIdentifierValue = 'userIdentifierValue',
- }: {
- actionTypeId?: string | null;
- clientSecret?: string | null;
- privateKey?: string | null;
- privateKeyPassword?: string | null;
- isOAuth?: boolean;
- apiUrl?: string | null;
- usesTableApi?: boolean | null;
- clientId?: string | null;
- jwtKeyId?: string | null;
- userIdentifierValue?: string | null;
- }) =>
- ({
- secrets: {
- clientSecret,
- privateKey,
- privateKeyPassword,
- },
- id,
- actionTypeId,
- name: 'servicenow',
- config: {
- isOAuth,
- apiUrl,
- usesTableApi,
- clientId,
- jwtKeyId,
- userIdentifierValue,
- },
- } as unknown as ServiceNowActionConnector);
-
- test(`${id}: valid OAuth Connector`, async () => {
- const actionTypeModel = actionTypeRegistry.get(id);
- const actionConnector = mockConnector({ actionTypeId: id });
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- apiUrl: [],
- usesTableApi: [],
- clientId: [],
- jwtKeyId: [],
- userIdentifierValue: [],
- },
- },
- secrets: {
- errors: {
- username: [],
- password: [],
- clientSecret: [],
- privateKey: [],
- },
- },
- });
- });
-
- test(`${id}: has invalid fields`, async () => {
- const actionTypeModel = actionTypeRegistry.get(id);
- const actionConnector = mockConnector({
- actionTypeId: id,
- apiUrl: null,
- clientId: null,
- jwtKeyId: null,
- userIdentifierValue: null,
- clientSecret: null,
- privateKey: null,
- privateKeyPassword: null,
- });
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- apiUrl: ['URL is required.'],
- usesTableApi: [],
- clientId: ['Client ID is required.'],
- jwtKeyId: ['JWT Verifier Key ID is required.'],
- userIdentifierValue: ['User Identifier is required.'],
- },
- },
- secrets: {
- errors: {
- username: [],
- password: [],
- clientSecret: ['Client Secret is required.'],
- privateKey: ['Private Key is required.'],
- },
- },
- });
- });
- });
-});
-
describe('servicenow action params validation', () => {
[SERVICENOW_ITSM_ACTION_TYPE_ID, SERVICENOW_SIR_ACTION_TYPE_ID].forEach((id) => {
test(`${id}: action params validation succeeds when action params is valid`, async () => {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx
index a5f0a65d90abe..85b2aea862848 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx
@@ -7,20 +7,14 @@
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
+import { ActionTypeModel, GenericValidationResult } from '../../../../types';
import {
- ActionTypeModel,
- ConnectorValidationResult,
- GenericValidationResult,
-} from '../../../../types';
-import {
- ServiceNowActionConnector,
ServiceNowConfig,
ServiceNowITOMActionParams,
ServiceNowITSMActionParams,
ServiceNowSecrets,
ServiceNowSIRActionParams,
} from './types';
-import { isValidUrl } from '../../../lib/value_validators';
import { getConnectorDescriptiveTitle, getSelectedConnectorIcon } from './helpers';
export const SERVICENOW_ITOM_TITLE = i18n.translate(
@@ -65,84 +59,6 @@ export const SERVICENOW_SIR_TITLE = i18n.translate(
}
);
-const validateConnector = async (
- action: ServiceNowActionConnector
-): Promise<
- ConnectorValidationResult<
- Omit,
- Omit
- >
-> => {
- const translations = await import('./translations');
-
- const configErrors = {
- apiUrl: new Array(),
- usesTableApi: new Array(),
- clientId: new Array(),
- userIdentifierValue: new Array(),
- jwtKeyId: new Array(),
- };
-
- const secretsErrors = {
- username: new Array(),
- password: new Array(),
- clientSecret: new Array(),
- privateKey: new Array(),
- };
-
- if (!action.config.apiUrl) {
- configErrors.apiUrl = [...configErrors.apiUrl, translations.API_URL_REQUIRED];
- }
-
- if (action.config.apiUrl) {
- if (!isValidUrl(action.config.apiUrl)) {
- configErrors.apiUrl = [...configErrors.apiUrl, translations.API_URL_INVALID];
- } else if (!isValidUrl(action.config.apiUrl, 'https:')) {
- configErrors.apiUrl = [...configErrors.apiUrl, translations.API_URL_REQUIRE_HTTPS];
- }
- }
-
- if (action.config.isOAuth) {
- if (!action.config.clientId) {
- configErrors.clientId = [...configErrors.clientId, translations.CLIENTID_REQUIRED];
- }
-
- if (!action.config.userIdentifierValue) {
- configErrors.userIdentifierValue = [
- ...configErrors.userIdentifierValue,
- translations.USER_EMAIL_REQUIRED,
- ];
- }
-
- if (!action.config.jwtKeyId) {
- configErrors.jwtKeyId = [...configErrors.jwtKeyId, translations.KEYID_REQUIRED];
- }
-
- if (!action.secrets.clientSecret) {
- secretsErrors.clientSecret = [
- ...secretsErrors.clientSecret,
- translations.CLIENTSECRET_REQUIRED,
- ];
- }
-
- if (!action.secrets.privateKey) {
- secretsErrors.privateKey = [...secretsErrors.privateKey, translations.PRIVATE_KEY_REQUIRED];
- }
- } else {
- if (!action.secrets.username) {
- secretsErrors.username = [...secretsErrors.username, translations.USERNAME_REQUIRED];
- }
- if (!action.secrets.password) {
- secretsErrors.password = [...secretsErrors.password, translations.PASSWORD_REQUIRED];
- }
- }
-
- return {
- config: { errors: configErrors },
- secrets: { errors: secretsErrors },
- };
-};
-
export function getServiceNowITSMActionType(): ActionTypeModel<
ServiceNowConfig,
ServiceNowSecrets,
@@ -153,7 +69,6 @@ export function getServiceNowITSMActionType(): ActionTypeModel<
iconClass: lazy(() => import('./logo')),
selectMessage: SERVICENOW_ITSM_DESC,
actionTypeTitle: SERVICENOW_ITSM_TITLE,
- validateConnector,
actionConnectorFields: lazy(() => import('./servicenow_connectors')),
validateParams: async (
actionParams: ServiceNowITSMActionParams
@@ -192,7 +107,6 @@ export function getServiceNowSIRActionType(): ActionTypeModel<
iconClass: lazy(() => import('./logo')),
selectMessage: SERVICENOW_SIR_DESC,
actionTypeTitle: SERVICENOW_SIR_TITLE,
- validateConnector,
actionConnectorFields: lazy(() => import('./servicenow_connectors')),
validateParams: async (
actionParams: ServiceNowSIRActionParams
@@ -231,7 +145,6 @@ export function getServiceNowITOMActionType(): ActionTypeModel<
iconClass: lazy(() => import('./logo')),
selectMessage: SERVICENOW_ITOM_DESC,
actionTypeTitle: SERVICENOW_ITOM_TITLE,
- validateConnector,
actionConnectorFields: lazy(() => import('./servicenow_connectors_no_app')),
validateParams: async (
actionParams: ServiceNowITOMActionParams
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx
index 75afe08bc87ca..f3ec5594144ec 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx
@@ -6,15 +6,18 @@
*/
import React from 'react';
-import { act } from '@testing-library/react';
+import { act, within } from '@testing-library/react';
import { mountWithIntl } from '@kbn/test-jest-helpers';
+import { render, act as reactAct } from '@testing-library/react';
+import { ConnectorValidationFunc } from '../../../../types';
import { useKibana } from '../../../../common/lib/kibana';
-import { ActionConnectorFieldsSetCallbacks } from '../../../../types';
import { updateActionConnector } from '../../../lib/action_connector_api';
import ServiceNowConnectorFields from './servicenow_connectors';
-import { ServiceNowActionConnector } from './types';
import { getAppInfo } from './api';
+import { ConnectorFormTestProvider } from '../test_utils';
+import { mount } from 'enzyme';
+import userEvent from '@testing-library/user-event';
jest.mock('../../../../common/lib/kibana');
jest.mock('../../../lib/action_connector_api');
@@ -26,41 +29,57 @@ const updateActionConnectorMock = updateActionConnector as jest.Mock;
describe('ServiceNowActionConnectorFields renders', () => {
const usesTableApiConnector = {
- secrets: {
- username: 'user',
- password: 'pass',
- },
id: 'test',
actionTypeId: '.servicenow',
- isPreconfigured: false,
isDeprecated: true,
name: 'SN',
config: {
- apiUrl: 'https://test/',
+ apiUrl: 'https://test.com',
usesTableApi: true,
},
- } as ServiceNowActionConnector;
+ secrets: {
+ username: 'user',
+ password: 'pass',
+ },
+ };
const usesImportSetApiConnector = {
...usesTableApiConnector,
isDeprecated: false,
config: {
...usesTableApiConnector.config,
+ isOAuth: false,
+ usesTableApi: false,
+ },
+ };
+
+ const usesImportSetApiConnectorOauth = {
+ ...usesTableApiConnector,
+ isDeprecated: false,
+ config: {
+ ...usesTableApiConnector.config,
+ isOAuth: true,
usesTableApi: false,
+ clientId: 'test-id',
+ userIdentifierValue: 'email',
+ jwtKeyId: 'test-id',
+ },
+ secrets: {
+ clientSecret: 'secret',
+ privateKey: 'secret-key',
+ privateKeyPassword: 'secret-pass',
},
- } as ServiceNowActionConnector;
+ };
test('alerting servicenow connector fields are rendered', () => {
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
expect(
wrapper.find('[data-test-subj="connector-servicenow-username-form-input"]').length > 0
@@ -74,16 +93,13 @@ describe('ServiceNowActionConnectorFields renders', () => {
test('case specific servicenow connector fields is rendered', () => {
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- consumer={'case'}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
expect(wrapper.find('[data-test-subj="credentialsApiUrlFromInput"]').length > 0).toBeTruthy();
@@ -92,70 +108,6 @@ describe('ServiceNowActionConnectorFields renders', () => {
).toBeTruthy();
});
- test('should display a message on create to remember credentials', () => {
- const actionConnector = {
- actionTypeId: '.servicenow',
- isPreconfigured: false,
- isDeprecated: false,
- config: {},
- secrets: {},
- } as ServiceNowActionConnector;
-
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0);
- });
-
- test('should display a message for missing secrets after import', () => {
- const actionConnector = {
- actionTypeId: '.servicenow',
- isPreconfigured: false,
- isDeprecated: false,
- isMissingSecrets: true,
- config: {},
- secrets: {},
- } as ServiceNowActionConnector;
-
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0);
- });
-
- test('should display a message on edit to re-enter credentials', () => {
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0);
- });
-
describe('Elastic certified ServiceNow application', () => {
const { services } = useKibanaMock();
const applicationInfoData = {
@@ -164,14 +116,11 @@ describe('ServiceNowActionConnectorFields renders', () => {
version: '1.0.0',
};
- let beforeActionConnectorSaveFn: () => Promise;
- const setCallbacks = (({
- beforeActionConnectorSave,
- }: {
- beforeActionConnectorSave: () => Promise;
- }) => {
- beforeActionConnectorSaveFn = beforeActionConnectorSave;
- }) as ActionConnectorFieldsSetCallbacks;
+ let preSubmitValidator: ConnectorValidationFunc;
+
+ const registerPreSubmitValidator = (validator: ConnectorValidationFunc) => {
+ preSubmitValidator = validator;
+ };
beforeEach(() => {
jest.clearAllMocks();
@@ -179,16 +128,15 @@ describe('ServiceNowActionConnectorFields renders', () => {
test('should render the correct callouts when the connectors needs the application', () => {
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+
+
);
+
expect(wrapper.find('[data-test-subj="snInstallationCallout"]').exists()).toBeTruthy();
expect(wrapper.find('[data-test-subj="snDeprecatedCallout"]').exists()).toBeFalsy();
expect(wrapper.find('[data-test-subj="snApplicationCallout"]').exists()).toBeFalsy();
@@ -196,16 +144,15 @@ describe('ServiceNowActionConnectorFields renders', () => {
test('should render the correct callouts if the connector uses the table API', () => {
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+
+
);
+
expect(wrapper.find('[data-test-subj="snInstallationCallout"]').exists()).toBeFalsy();
expect(wrapper.find('[data-test-subj="snDeprecatedCallout"]').exists()).toBeTruthy();
expect(wrapper.find('[data-test-subj="snApplicationCallout"]').exists()).toBeFalsy();
@@ -215,19 +162,17 @@ describe('ServiceNowActionConnectorFields renders', () => {
getAppInfoMock.mockResolvedValue(applicationInfoData);
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={setCallbacks}
- isEdit={false}
- />
+
+
+
);
await act(async () => {
- await beforeActionConnectorSaveFn();
+ await preSubmitValidator();
});
expect(getAppInfoMock).toHaveBeenCalledTimes(1);
@@ -236,19 +181,17 @@ describe('ServiceNowActionConnectorFields renders', () => {
test('should NOT get application information when the connector uses the old API', async () => {
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={setCallbacks}
- isEdit={false}
- />
+
+
+
);
await act(async () => {
- await beforeActionConnectorSaveFn();
+ await preSubmitValidator();
});
expect(getAppInfoMock).toHaveBeenCalledTimes(0);
@@ -256,110 +199,113 @@ describe('ServiceNowActionConnectorFields renders', () => {
});
test('should render error when save failed', async () => {
- expect.assertions(4);
-
const errorMessage = 'request failed';
getAppInfoMock.mockRejectedValueOnce(new Error(errorMessage));
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={setCallbacks}
- isEdit={false}
- />
+ mountWithIntl(
+
+
+
);
- await expect(
- // The async is needed so the act will finished before asserting for the callout
- async () => await act(async () => await beforeActionConnectorSaveFn())
- ).rejects.toThrow(errorMessage);
- expect(getAppInfoMock).toHaveBeenCalledTimes(1);
-
- wrapper.update();
- expect(wrapper.find('[data-test-subj="snApplicationCallout"]').exists()).toBeTruthy();
- expect(
- wrapper
- .find('[data-test-subj="snApplicationCallout"]')
- .first()
- .text()
- .includes(errorMessage)
- ).toBeTruthy();
+ await act(async () => {
+ const res = await preSubmitValidator();
+ const messageWrapper = mount(<>{res?.message}>);
+
+ expect(getAppInfoMock).toHaveBeenCalledTimes(1);
+ expect(
+ messageWrapper.find('[data-test-subj="snApplicationCallout"]').exists()
+ ).toBeTruthy();
+
+ expect(
+ messageWrapper
+ .find('[data-test-subj="snApplicationCallout"]')
+ .first()
+ .text()
+ .includes(errorMessage)
+ ).toBeTruthy();
+ });
});
test('should render error when the response is a REST api error', async () => {
- expect.assertions(4);
-
const errorMessage = 'request failed';
getAppInfoMock.mockResolvedValue({ error: { message: errorMessage }, status: 'failure' });
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={setCallbacks}
- isEdit={false}
- />
+ mountWithIntl(
+
+
+
);
- await expect(
- // The async is needed so the act will finished before asserting for the callout
- async () => await act(async () => await beforeActionConnectorSaveFn())
- ).rejects.toThrow(errorMessage);
- expect(getAppInfoMock).toHaveBeenCalledTimes(1);
-
- wrapper.update();
- expect(wrapper.find('[data-test-subj="snApplicationCallout"]').exists()).toBeTruthy();
- expect(
- wrapper
- .find('[data-test-subj="snApplicationCallout"]')
- .first()
- .text()
- .includes(errorMessage)
- ).toBeTruthy();
+ await act(async () => {
+ const res = await preSubmitValidator();
+ const messageWrapper = mount(<>{res?.message}>);
+
+ expect(getAppInfoMock).toHaveBeenCalledTimes(1);
+ expect(
+ messageWrapper.find('[data-test-subj="snApplicationCallout"]').exists()
+ ).toBeTruthy();
+
+ expect(
+ messageWrapper
+ .find('[data-test-subj="snApplicationCallout"]')
+ .first()
+ .text()
+ .includes(errorMessage)
+ ).toBeTruthy();
+ });
});
- test('should migrate the deprecated connector when the application throws', async () => {
+ test('should migrate the deprecated connector correctly', async () => {
getAppInfoMock.mockResolvedValue(applicationInfoData);
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={setCallbacks}
- isEdit={false}
- />
+ updateActionConnectorMock.mockResolvedValue({ isDeprecated: false });
+
+ const { getByTestId, queryByTestId } = render(
+
+
+
);
- expect(wrapper.find('[data-test-subj="update-connector-btn"]').exists()).toBeTruthy();
- wrapper
- .find('[data-test-subj="update-connector-btn"]')
- .first()
- .find('button')
- .simulate('click');
- expect(wrapper.find('[data-test-subj="updateConnectorForm"]').exists()).toBeTruthy();
+ await reactAct(async () => {
+ userEvent.click(getByTestId('update-connector-btn'));
+ });
- await act(async () => {
- // Update the connector
- wrapper.find('[data-test-subj="snUpdateInstallationSubmit"]').first().simulate('click');
+ await reactAct(async () => {
+ const updateConnectorForm = getByTestId('updateConnectorForm');
+ const urlInput = within(updateConnectorForm).getByTestId('credentialsApiUrlFromInput');
+ const usernameInput = within(updateConnectorForm).getByTestId(
+ 'connector-servicenow-username-form-input'
+ );
+ const passwordInput = within(updateConnectorForm).getByTestId(
+ 'connector-servicenow-password-form-input'
+ );
+
+ await userEvent.type(urlInput, 'https://example.com', { delay: 100 });
+ await userEvent.type(usernameInput, 'user', { delay: 100 });
+ await userEvent.type(passwordInput, 'pass', { delay: 100 });
+ userEvent.click(within(updateConnectorForm).getByTestId('snUpdateInstallationSubmit'));
});
expect(getAppInfoMock).toHaveBeenCalledTimes(1);
expect(updateActionConnectorMock).toHaveBeenCalledWith(
expect.objectContaining({
- id: usesTableApiConnector.id,
connector: {
- name: usesTableApiConnector.name,
- config: { ...usesTableApiConnector.config, usesTableApi: false },
- secrets: usesTableApiConnector.secrets,
+ config: { apiUrl: 'https://example.com', usesTableApi: false },
+ id: 'test',
+ name: 'SN',
+ secrets: { password: 'pass', username: 'user' },
},
})
);
@@ -369,100 +315,181 @@ describe('ServiceNowActionConnectorFields renders', () => {
title: 'SN connector updated',
});
- // The flyout is closed
- wrapper.update();
- expect(wrapper.find('[data-test-subj="updateConnectorForm"]').exists()).toBeFalsy();
+ expect(queryByTestId('updateConnectorForm')).toBe(null);
+ expect(queryByTestId('snDeprecatedCallout')).toBe(null);
+ expect(getByTestId('snInstallationCallout')).toBeInTheDocument();
});
test('should NOT migrate the deprecated connector when there is an error', async () => {
const errorMessage = 'request failed';
getAppInfoMock.mockRejectedValueOnce(new Error(errorMessage));
-
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={setCallbacks}
- isEdit={false}
- />
+ updateActionConnectorMock.mockResolvedValue({ isDeprecated: false });
+
+ const { getByTestId } = render(
+
+
+
);
- expect(wrapper.find('[data-test-subj="update-connector-btn"]').exists()).toBeTruthy();
- wrapper
- .find('[data-test-subj="update-connector-btn"]')
- .first()
- .find('button')
- .simulate('click');
- expect(wrapper.find('[data-test-subj="updateConnectorForm"]').exists()).toBeTruthy();
+ await reactAct(async () => {
+ userEvent.click(getByTestId('update-connector-btn'));
+ });
- // The async is needed so the act will finished before asserting for the callout
- await act(async () => {
- wrapper.find('[data-test-subj="snUpdateInstallationSubmit"]').first().simulate('click');
+ await reactAct(async () => {
+ const updateConnectorForm = getByTestId('updateConnectorForm');
+ const urlInput = within(updateConnectorForm).getByTestId('credentialsApiUrlFromInput');
+ const usernameInput = within(updateConnectorForm).getByTestId(
+ 'connector-servicenow-username-form-input'
+ );
+ const passwordInput = within(updateConnectorForm).getByTestId(
+ 'connector-servicenow-password-form-input'
+ );
+
+ await userEvent.type(urlInput, 'https://example.com', { delay: 100 });
+ await userEvent.type(usernameInput, 'user', { delay: 100 });
+ await userEvent.type(passwordInput, 'pass', { delay: 100 });
+ userEvent.click(within(updateConnectorForm).getByTestId('snUpdateInstallationSubmit'));
});
expect(getAppInfoMock).toHaveBeenCalledTimes(1);
expect(updateActionConnectorMock).not.toHaveBeenCalled();
-
expect(services.notifications.toasts.addSuccess).not.toHaveBeenCalled();
+ expect(getByTestId('updateConnectorForm')).toBeInTheDocument();
+ expect(
+ within(getByTestId('updateConnectorForm')).getByTestId('snApplicationCallout')
+ ).toBeInTheDocument();
+ });
+ });
- // The flyout is still open
- wrapper.update();
- expect(wrapper.find('[data-test-subj="updateConnectorForm"]').exists()).toBeTruthy();
+ describe('Validation', () => {
+ const onSubmit = jest.fn();
- // The error message should be shown to the user
- expect(
- wrapper
- .find('[data-test-subj="updateConnectorForm"] [data-test-subj="snApplicationCallout"]')
- .exists()
- ).toBeTruthy();
- expect(
- wrapper
- .find('[data-test-subj="updateConnectorForm"] [data-test-subj="snApplicationCallout"]')
- .first()
- .text()
- .includes(errorMessage)
- ).toBeTruthy();
+ beforeEach(() => {
+ jest.clearAllMocks();
});
- test('should set the usesTableApi to false when creating a connector', async () => {
- const newConnector = { ...usesTableApiConnector, config: {}, secrets: {} };
- const editActionConfig = jest.fn();
+ const basicAuthTests: Array<[string, string]> = [
+ ['credentialsApiUrlFromInput', 'not-valid'],
+ ['connector-servicenow-username-form-input', ''],
+ ['connector-servicenow-password-form-input', ''],
+ ];
+
+ const oauthTests: Array<[string, string]> = [
+ ['credentialsApiUrlFromInput', 'not-valid'],
+ ['connector-servicenow-client-id-form-input', ''],
+ ['connector-servicenow-user-identifier-form-input', ''],
+ ['connector-servicenow-jwt-key-id-form-input', ''],
+ ['connector-servicenow-client-secret-form-input', ''],
+ ['connector-servicenow-private-key-form-input', ''],
+ ];
+
+ it.each([[usesImportSetApiConnector], [usesImportSetApiConnectorOauth]])(
+ 'connector validation succeeds when connector config is valid',
+ async (connector) => {
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: { ...connector }, isValid: true });
+ }
+ );
- mountWithIntl(
- {}}
- readOnly={false}
- setCallbacks={setCallbacks}
- isEdit={false}
- />
+ it('submits if the private key password is empty', async () => {
+ const connector = {
+ ...usesImportSetApiConnectorOauth,
+ secrets: {
+ ...usesImportSetApiConnectorOauth.secrets,
+ clientSecret: 'secret',
+ privateKey: 'secret-key',
+ privateKeyPassword: '',
+ },
+ };
+
+ const { getByTestId } = render(
+
+ {}}
+ />
+
);
- expect(editActionConfig).toHaveBeenCalledWith('usesTableApi', false);
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ const {
+ secrets: { clientSecret, privateKey },
+ ...rest
+ } = connector;
+
+ expect(onSubmit).toHaveBeenCalledWith({
+ data: { ...rest, secrets: { clientSecret, privateKey } },
+ isValid: true,
+ });
});
- test('it should set the legacy attribute if it is not undefined', async () => {
- const editActionConfig = jest.fn();
+ it.each(basicAuthTests)('validates correctly %p', async (field, value) => {
+ const res = render(
+
+ {}}
+ />
+
+ );
- mountWithIntl(
- {}}
- readOnly={false}
- setCallbacks={setCallbacks}
- isEdit={false}
- />
+ await act(async () => {
+ await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
+
+ it.each(oauthTests)('validates correctly %p', async (field, value) => {
+ const res = render(
+
+ {}}
+ />
+
);
- expect(editActionConfig).not.toHaveBeenCalled();
+ await act(async () => {
+ await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
});
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx
index cc2756d178f11..54cb052c50212 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx
@@ -5,157 +5,176 @@
* 2.0.
*/
-import React, { useCallback, useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useState, useMemo } from 'react';
import { EuiSpacer } from '@elastic/eui';
import { snExternalServiceConfig } from '@kbn/actions-plugin/common';
-import { ActionConnectorFieldsProps } from '../../../../types';
+import { useFormContext, useFormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
-import * as i18n from './translations';
-import { ServiceNowActionConnector } from './types';
+import { ActionConnectorFieldsProps } from '../../../../types';
import { useKibana } from '../../../../common/lib/kibana';
import { DeprecatedCallout } from './deprecated_callout';
import { useGetAppInfo } from './use_get_app_info';
import { ApplicationRequiredCallout } from './application_required_callout';
import { isRESTApiError } from './helpers';
import { InstallationCallout } from './installation_callout';
-import { UpdateConnector } from './update_connector';
+import { UpdateConnector, UpdateConnectorFormSchema } from './update_connector';
import { updateActionConnector } from '../../../lib/action_connector_api';
import { Credentials } from './credentials';
+import * as i18n from './translations';
+import { ServiceNowActionConnector, ServiceNowConfig, ServiceNowSecrets } from './types';
+import { HiddenField } from '../../hidden_field';
+import { ConnectorFormSchema } from '../../../sections/action_connector_form/types';
// eslint-disable-next-line import/no-default-export
export { ServiceNowConnectorFields as default };
-const ServiceNowConnectorFields: React.FC<
- ActionConnectorFieldsProps
-> = ({ action, editActionSecrets, editActionConfig, errors, readOnly, setCallbacks }) => {
+const ServiceNowConnectorFields: React.FC = ({
+ readOnly,
+ registerPreSubmitValidator,
+ isEdit,
+}) => {
const {
http,
notifications: { toasts },
} = useKibana().services;
- const { config, secrets } = action;
- const requiresNewApplication = !action.isDeprecated;
+ const { updateFieldValues } = useFormContext();
+ const [{ id, isDeprecated, actionTypeId, name, config, secrets }] = useFormData<
+ ConnectorFormSchema
+ >({
+ watch: [
+ 'id',
+ 'isDeprecated',
+ 'actionTypeId',
+ 'name',
+ 'config.apiUrl',
+ 'config.isOAuth',
+ 'secrets.username',
+ 'secrets.password',
+ ],
+ });
- const [showUpdateConnector, setShowUpdateConnector] = useState(false);
+ const requiresNewApplication = isDeprecated != null ? !isDeprecated : true;
+ const { isOAuth = false } = config ?? {};
+ const action = useMemo(
+ () => ({
+ name,
+ actionTypeId,
+ config,
+ secrets,
+ }),
+ [name, actionTypeId, config, secrets]
+ ) as ServiceNowActionConnector;
+
+ const [showUpdateConnector, setShowUpdateConnector] = useState(false);
+ const [updateErrorMessage, setUpdateErrorMessage] = useState(null);
const { fetchAppInfo, isLoading } = useGetAppInfo({
- actionTypeId: action.actionTypeId,
+ actionTypeId,
http,
});
- const [showApplicationRequiredCallout, setShowApplicationRequiredCallout] =
- useState(false);
- const [applicationInfoErrorMsg, setApplicationInfoErrorMsg] = useState(null);
-
- const getApplicationInfo = useCallback(async () => {
- setShowApplicationRequiredCallout(false);
- setApplicationInfoErrorMsg(null);
-
- try {
- const res = await fetchAppInfo(action);
- if (isRESTApiError(res)) {
- throw new Error(res.error?.message ?? i18n.UNKNOWN);
+ const getApplicationInfo = useCallback(
+ async (connector: ServiceNowActionConnector) => {
+ try {
+ const res = await fetchAppInfo(connector);
+ if (isRESTApiError(res)) {
+ throw new Error(res.error?.message ?? i18n.UNKNOWN);
+ }
+
+ return res;
+ } catch (e) {
+ throw e;
}
+ },
+ [fetchAppInfo]
+ );
- return res;
- } catch (e) {
- setShowApplicationRequiredCallout(true);
- setApplicationInfoErrorMsg(e.message);
- // We need to throw here so the connector will be not be saved.
- throw e;
- }
- }, [action, fetchAppInfo]);
-
- const beforeActionConnectorSave = useCallback(async () => {
+ const preSubmitValidator = useCallback(async () => {
if (requiresNewApplication) {
- await getApplicationInfo();
+ try {
+ await getApplicationInfo(action);
+ } catch (error) {
+ return {
+ message: (
+
+ ),
+ };
+ }
}
- }, [getApplicationInfo, requiresNewApplication]);
+ }, [action, actionTypeId, getApplicationInfo, requiresNewApplication]);
useEffect(
- () => setCallbacks({ beforeActionConnectorSave }),
- [beforeActionConnectorSave, setCallbacks]
+ () => registerPreSubmitValidator(preSubmitValidator),
+ [preSubmitValidator, registerPreSubmitValidator]
);
const onMigrateClick = useCallback(() => setShowUpdateConnector(true), []);
const onModalCancel = useCallback(() => setShowUpdateConnector(false), []);
- const onUpdateConnectorConfirm = useCallback(async () => {
- try {
- await getApplicationInfo();
-
- await updateActionConnector({
- http,
- connector: {
- name: action.name,
- config: { ...config, usesTableApi: false },
- secrets: { ...secrets },
- },
- id: action.id,
- });
-
- editActionConfig('usesTableApi', false);
- setShowUpdateConnector(false);
-
- toasts.addSuccess({
- title: i18n.UPDATE_SUCCESS_TOAST_TITLE(action.name),
- text: i18n.UPDATE_SUCCESS_TOAST_TEXT,
- });
- } catch (err) {
- /**
- * getApplicationInfo may throw an error if the request
- * fails or if there is a REST api error.
- *
- * We silent the errors as a callout will show and inform the user
- */
- }
- }, [getApplicationInfo, http, action.name, action.id, secrets, config, editActionConfig, toasts]);
-
- /**
- * Defaults the usesTableApi attribute to false
- * if it is not defined. The usesTableApi attribute
- * will be undefined only at the creation of
- * the connector.
- */
- useEffect(() => {
- if (config.usesTableApi == null) {
- editActionConfig('usesTableApi', false);
- }
- });
+ const onUpdateConnectorConfirm = useCallback(
+ async (updatedConnector: UpdateConnectorFormSchema['updatedConnector']) => {
+ const connectorToUpdate = {
+ name: name ?? '',
+ config: { ...updatedConnector.config, usesTableApi: false },
+ secrets: { ...updatedConnector.secrets },
+ id: id ?? '',
+ };
+
+ try {
+ await getApplicationInfo({
+ ...connectorToUpdate,
+ isDeprecated,
+ isPreconfigured: false,
+ actionTypeId,
+ });
+
+ const res = await updateActionConnector({
+ http,
+ connector: connectorToUpdate,
+ id: id ?? '',
+ });
+
+ toasts.addSuccess({
+ title: i18n.UPDATE_SUCCESS_TOAST_TITLE(name ?? ''),
+ text: i18n.UPDATE_SUCCESS_TOAST_TEXT,
+ });
+
+ setShowUpdateConnector(false);
+
+ updateFieldValues({
+ isDeprecated: res.isDeprecated,
+ config: updatedConnector.config,
+ });
+ } catch (err) {
+ setUpdateErrorMessage(err.message);
+ }
+ },
+ [name, id, getApplicationInfo, isDeprecated, actionTypeId, http, updateFieldValues, toasts]
+ );
return (
<>
- {showUpdateConnector && (
+ {actionTypeId && showUpdateConnector && (
)}
{requiresNewApplication && (
-
+
)}
{!requiresNewApplication && }
-
- {showApplicationRequiredCallout && requiresNewApplication && (
-
- )}
+
+
>
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.test.tsx
new file mode 100644
index 0000000000000..a0dda6edf76e0
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.test.tsx
@@ -0,0 +1,162 @@
+/*
+ * 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 '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import React from 'react';
+import { AppMockRenderer, ConnectorFormTestProvider, createAppMockRenderer } from '../test_utils';
+import ServiceNowConnectorFieldsNoApp from './servicenow_connectors_no_app';
+
+describe('ServiceNowActionConnectorFields renders', () => {
+ const basicAuthConnector = {
+ id: 'test',
+ actionTypeId: '.servicenow',
+ isDeprecated: true,
+ name: 'SN',
+ config: {
+ apiUrl: 'https://test.com',
+ isOAuth: false,
+ usesTableApi: false,
+ },
+ secrets: {
+ username: 'user',
+ password: 'pass',
+ },
+ };
+
+ const oauthConnector = {
+ id: 'test',
+ actionTypeId: '.servicenow',
+ isDeprecated: true,
+ name: 'SN',
+ config: {
+ apiUrl: 'https://test.com',
+ isOAuth: true,
+ usesTableApi: false,
+ clientId: 'test-id',
+ userIdentifierValue: 'email',
+ jwtKeyId: 'test-id',
+ },
+ secrets: {
+ clientSecret: 'secret',
+ privateKey: 'secret-key',
+ privateKeyPassword: 'secret-pass',
+ },
+ };
+
+ let appMockRenderer: AppMockRenderer;
+ beforeEach(() => {
+ jest.clearAllMocks();
+ appMockRenderer = createAppMockRenderer();
+ });
+
+ it('renders a basic auth connector', () => {
+ const { getByTestId } = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ expect(getByTestId('credentialsApiUrlFromInput')).toBeInTheDocument();
+ expect(getByTestId('connector-servicenow-username-form-input')).toBeInTheDocument();
+ expect(getByTestId('connector-servicenow-password-form-input')).toBeInTheDocument();
+ });
+
+ it('renders an oauth connector', () => {
+ const { getByTestId } = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ expect(getByTestId('credentialsApiUrlFromInput')).toBeInTheDocument();
+ expect(getByTestId('connector-servicenow-client-id-form-input')).toBeInTheDocument();
+ expect(getByTestId('connector-servicenow-user-identifier-form-input')).toBeInTheDocument();
+ expect(getByTestId('connector-servicenow-jwt-key-id-form-input')).toBeInTheDocument();
+ expect(getByTestId('connector-servicenow-client-secret-form-input')).toBeInTheDocument();
+ expect(getByTestId('connector-servicenow-private-key-form-input')).toBeInTheDocument();
+ });
+
+ describe('Validation', () => {
+ const onSubmit = jest.fn();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ const basicAuthTests: Array<[string, string]> = [
+ ['credentialsApiUrlFromInput', 'not-valid'],
+ ['connector-servicenow-username-form-input', ''],
+ ['connector-servicenow-password-form-input', ''],
+ ];
+
+ const oauthTests: Array<[string, string]> = [
+ ['credentialsApiUrlFromInput', 'not-valid'],
+ ['connector-servicenow-client-id-form-input', ''],
+ ['connector-servicenow-user-identifier-form-input', ''],
+ ['connector-servicenow-jwt-key-id-form-input', ''],
+ ['connector-servicenow-client-secret-form-input', ''],
+ ['connector-servicenow-private-key-form-input', ''],
+ ];
+
+ it.each(basicAuthTests)('validates correctly %p', async (field, value) => {
+ const res = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
+
+ it.each(oauthTests)('validates correctly %p', async (field, value) => {
+ const res = appMockRenderer.render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
+ });
+});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.tsx
index f49c2d34d3a8d..e4332f163151b 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.tsx
@@ -6,27 +6,23 @@
*/
import React from 'react';
+import { useFormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import { ActionConnectorFieldsProps } from '../../../../types';
-
-import { ServiceNowActionConnector } from './types';
import { Credentials } from './credentials';
+import { ConnectorFormSchema } from '../../../sections/action_connector_form/types';
+import { ServiceNowConfig, ServiceNowSecrets } from './types';
+
+const ServiceNowConnectorFieldsNoApp: React.FC = ({
+ isEdit,
+ readOnly,
+}) => {
+ const [{ config }] = useFormData>({
+ watch: ['config.isOAuth'],
+ });
+ const { isOAuth = false } = config ?? {};
-const ServiceNowConnectorFieldsNoApp: React.FC<
- ActionConnectorFieldsProps
-> = ({ action, editActionSecrets, editActionConfig, errors, readOnly }) => {
- return (
- <>
-
- >
- );
+ return ;
};
// eslint-disable-next-line import/no-default-export
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/sn_store_button.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/sn_store_button.tsx
index 3cbec513d179c..1fb58b24c68e9 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/sn_store_button.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/sn_store_button.tsx
@@ -14,11 +14,11 @@ const getStoreURL = (appId: string): string =>
`https://store.servicenow.com/sn_appstore_store.do#!/store/application/${appId}`;
interface Props {
- appId: string;
+ appId?: string;
color: EuiButtonProps['color'];
}
-const SNStoreButtonComponent: React.FC = ({ color, appId }) => {
+const SNStoreButtonComponent: React.FC = ({ color, appId = '' }) => {
return (
= ({ color, appId }) => {
export const SNStoreButton = memo(SNStoreButtonComponent);
-const SNStoreLinkComponent: React.FC> = ({ appId }) => (
+const SNStoreLinkComponent: React.FC> = ({ appId = '' }) => (
{i18n.VISIT_SN_STORE}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/translations.ts
index 77416287cfdcc..504e95636749b 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/translations.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/translations.ts
@@ -14,20 +14,6 @@ export const API_URL_LABEL = i18n.translate(
}
);
-export const API_URL_HELPTEXT = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.apiUrlHelpText',
- {
- defaultMessage: 'Include the full URL.',
- }
-);
-
-export const API_URL_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredApiUrlTextField',
- {
- defaultMessage: 'URL is required.',
- }
-);
-
export const API_URL_INVALID = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.invalidApiUrlTextField',
{
@@ -35,13 +21,6 @@ export const API_URL_INVALID = i18n.translate(
}
);
-export const API_URL_REQUIRE_HTTPS = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requireHttpsApiUrlTextField',
- {
- defaultMessage: 'URL must start with https://.',
- }
-);
-
export const AUTHENTICATION_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.authenticationLabel',
{
@@ -49,13 +28,6 @@ export const AUTHENTICATION_LABEL = i18n.translate(
}
);
-export const REENTER_VALUES_LABEL = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.reenterValuesLabel',
- {
- defaultMessage: 'You must authenticate each time you edit the connector.',
- }
-);
-
export const USERNAME_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.usernameTextFieldLabel',
{
@@ -77,13 +49,6 @@ export const PASSWORD_LABEL = i18n.translate(
}
);
-export const PASSWORD_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredPasswordTextField',
- {
- defaultMessage: 'Password is required.',
- }
-);
-
export const TITLE_REQUIRED = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.common.requiredShortDescTextField',
{
@@ -181,13 +146,6 @@ export const API_INFO_ERROR = (status: number) =>
defaultMessage: 'Received status: {status} when attempting to get application information',
});
-export const INSTALL = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.install',
- {
- defaultMessage: 'install',
- }
-);
-
export const INSTALLATION_CALLOUT_TITLE = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.installationCalloutTitle',
{
@@ -350,7 +308,7 @@ export const KEY_ID_LABEL = i18n.translate(
}
);
-export const USER_EMAIL_LABEL = i18n.translate(
+export const USER_IDENTIFIER_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.userEmailTextFieldLabel',
{
defaultMessage: 'User Identifier',
@@ -392,27 +350,20 @@ export const PRIVATE_KEY_REQUIRED = i18n.translate(
}
);
-export const CLIENTSECRET_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredClientSecretTextField',
+export const KEYID_REQUIRED = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredKeyIdTextField',
{
- defaultMessage: 'Client Secret is required.',
+ defaultMessage: 'JWT Verifier Key ID is required.',
}
);
-export const USER_EMAIL_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUserEmailTextField',
+export const USER_IDENTIFIER_REQUIRED = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredUserIdentifierTextField',
{
defaultMessage: 'User Identifier is required.',
}
);
-export const KEYID_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.requiredKeyIdTextField',
- {
- defaultMessage: 'JWT Verifier Key ID is required.',
- }
-);
-
export const IS_OAUTH = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.servicenow.useOAuth',
{
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.test.tsx
index 53939ba5644b5..219ba68114852 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.test.tsx
@@ -6,62 +6,21 @@
*/
import React from 'react';
+import { I18nProvider } from '@kbn/i18n-react';
+import userEvent from '@testing-library/user-event';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import { Props, UpdateConnector } from './update_connector';
-import { ServiceNowActionConnector } from './types';
-import { ActionConnectorFieldsProps } from '../../../../types';
+import { act } from 'react-dom/test-utils';
+import { render, act as reactAct } from '@testing-library/react';
jest.mock('../../../../common/lib/kibana');
-const actionConnectorBasicAuth: ServiceNowActionConnector = {
- secrets: {
- username: 'user',
- password: 'pass',
- },
- id: 'test',
- actionTypeId: '.servicenow',
- isPreconfigured: false,
- isDeprecated: false,
- name: 'servicenow',
- config: {
- apiUrl: 'https://test/',
- usesTableApi: true,
- isOAuth: false,
- },
-};
-
-const actionConnectorOAuth: ServiceNowActionConnector = {
- secrets: {
- clientSecret: 'clientSecret',
- privateKey: 'privateKey',
- privateKeyPassword: 'privateKeyPassword',
- },
- id: 'test',
- actionTypeId: '.servicenow',
- isPreconfigured: false,
- isDeprecated: false,
- name: 'servicenow',
- config: {
- apiUrl: 'https://test/',
- usesTableApi: true,
- isOAuth: true,
- clientId: 'cid',
- userIdentifierValue: 'test@testuserIdentifierValue.com',
- jwtKeyId: 'jwtKeyId',
- },
-};
-
-const mountUpdateConnector = (
- props: Partial = {},
- action: ActionConnectorFieldsProps['action'] = actionConnectorBasicAuth
-) => {
+const mountUpdateConnector = (props: Partial = {}, isOAuth: boolean = false) => {
return mountWithIntl(
{}}
- editActionSecrets={() => {}}
+ actionTypeId=".servicenow"
+ isOAuth={isOAuth}
+ updateErrorMessage={null}
readOnly={false}
isLoading={false}
onConfirm={() => {}}
@@ -87,7 +46,7 @@ describe('UpdateConnector renders', () => {
});
it('should render update connector fields for OAuth', () => {
- const wrapper = mountUpdateConnector({}, actionConnectorOAuth);
+ const wrapper = mountUpdateConnector({}, true);
expect(
wrapper.find('[data-test-subj="connector-servicenow-client-id-form-input"]').exists()
).toBeTruthy();
@@ -114,8 +73,9 @@ describe('UpdateConnector renders', () => {
).toBeTruthy();
});
- it('should disable inputs on loading', () => {
+ it('should disable inputs on loading', async () => {
const wrapper = mountUpdateConnector({ isLoading: true });
+
expect(
wrapper.find('[data-test-subj="credentialsApiUrlFromInput"]').first().prop('disabled')
).toBeTruthy();
@@ -134,7 +94,7 @@ describe('UpdateConnector renders', () => {
});
it('should disable inputs on loading for OAuth', () => {
- const wrapper = mountUpdateConnector({ isLoading: true }, actionConnectorOAuth);
+ const wrapper = mountUpdateConnector({ isLoading: true }, true);
expect(
wrapper
@@ -199,7 +159,7 @@ describe('UpdateConnector renders', () => {
});
it('should set inputs as read-only for OAuth', () => {
- const wrapper = mountUpdateConnector({ readOnly: true }, actionConnectorOAuth);
+ const wrapper = mountUpdateConnector({ readOnly: true }, true);
expect(
wrapper
@@ -243,116 +203,58 @@ describe('UpdateConnector renders', () => {
).toBeTruthy();
});
- it('should disable submit button if errors or fields missing', () => {
- const wrapper = mountUpdateConnector(
- {
- errors: { apiUrl: ['some error'], username: [], password: [] },
- },
- actionConnectorBasicAuth
- );
-
- expect(
- wrapper.find('[data-test-subj="snUpdateInstallationSubmit"]').first().prop('disabled')
- ).toBeTruthy();
-
- wrapper.setProps({ ...wrapper.props(), errors: { apiUrl: [], username: [], password: [] } });
- expect(
- wrapper.find('[data-test-subj="snUpdateInstallationSubmit"]').first().prop('disabled')
- ).toBeFalsy();
+ it('should disable submit button on form errors', async () => {
+ const wrapper = mountUpdateConnector();
- wrapper.setProps({
- ...wrapper.props(),
- action: {
- ...actionConnectorBasicAuth,
- secrets: { ...actionConnectorBasicAuth.secrets, username: undefined },
- },
+ await act(async () => {
+ wrapper.find('[data-test-subj="snUpdateInstallationSubmit"]').first().simulate('click');
+ wrapper.update();
});
+
expect(
wrapper.find('[data-test-subj="snUpdateInstallationSubmit"]').first().prop('disabled')
).toBeTruthy();
});
- it('should call editActionConfig when editing api url', () => {
- const editActionConfig = jest.fn();
- const wrapper = mountUpdateConnector({ editActionConfig });
-
- expect(editActionConfig).not.toHaveBeenCalled();
- wrapper
- .find('input[data-test-subj="credentialsApiUrlFromInput"]')
- .simulate('change', { target: { value: 'newUrl' } });
- expect(editActionConfig).toHaveBeenCalledWith('apiUrl', 'newUrl');
- });
-
- it('should call editActionSecrets when editing username or password', () => {
- const editActionSecrets = jest.fn();
- const wrapper = mountUpdateConnector({ editActionSecrets });
-
- expect(editActionSecrets).not.toHaveBeenCalled();
- wrapper
- .find('input[data-test-subj="connector-servicenow-username-form-input"]')
- .simulate('change', { target: { value: 'new username' } });
- expect(editActionSecrets).toHaveBeenCalledWith('username', 'new username');
-
- wrapper
- .find('input[data-test-subj="connector-servicenow-password-form-input"]')
- .simulate('change', { target: { value: 'new pass' } });
-
- expect(editActionSecrets).toHaveBeenCalledTimes(2);
- expect(editActionSecrets).toHaveBeenLastCalledWith('password', 'new pass');
- });
+ it('should confirm the update when submit button clicked', async () => {
+ const onConfirm = jest.fn();
- it('should call editActionSecrets and/or editActionConfig when editing oAuth fields', () => {
- const editActionSecrets = jest.fn();
- const editActionConfig = jest.fn();
- const wrapper = mountUpdateConnector(
- { editActionSecrets, editActionConfig },
- actionConnectorOAuth
+ const { getByTestId } = render(
+
+ {}}
+ />
+
);
- expect(editActionSecrets).not.toHaveBeenCalled();
-
- wrapper
- .find('input[data-test-subj="connector-servicenow-client-id-form-input"]')
- .simulate('change', { target: { value: 'new-value' } });
- expect(editActionConfig).toHaveBeenCalledWith('clientId', 'new-value');
-
- wrapper
- .find('input[data-test-subj="connector-servicenow-user-identifier-form-input"]')
- .simulate('change', { target: { value: 'new-value' } });
- expect(editActionConfig).toHaveBeenCalledWith('userIdentifierValue', 'new-value');
-
- wrapper
- .find('input[data-test-subj="connector-servicenow-jwt-key-id-form-input"]')
- .simulate('change', { target: { value: 'new-value' } });
- expect(editActionConfig).toHaveBeenCalledWith('jwtKeyId', 'new-value');
-
- wrapper
- .find('input[data-test-subj="connector-servicenow-client-secret-form-input"]')
- .simulate('change', { target: { value: 'new-value' } });
- expect(editActionSecrets).toHaveBeenCalledWith('clientSecret', 'new-value');
-
- wrapper
- .find('textarea[data-test-subj="connector-servicenow-private-key-form-input"]')
- .simulate('change', { target: { value: 'new-value' } });
- expect(editActionSecrets).toHaveBeenCalledWith('privateKey', 'new-value');
-
- wrapper
- .find('input[data-test-subj="connector-servicenow-private-key-password-form-input"]')
- .simulate('change', { target: { value: 'new-value' } });
- expect(editActionSecrets).toHaveBeenCalledWith('privateKeyPassword', 'new-value');
-
- expect(editActionConfig).toHaveBeenCalledTimes(3);
- expect(editActionSecrets).toHaveBeenCalledTimes(3);
- expect(editActionSecrets).toHaveBeenLastCalledWith('privateKeyPassword', 'new-value');
- });
+ expect(onConfirm).not.toHaveBeenCalled();
- it('should confirm the update when submit button clicked', () => {
- const onConfirm = jest.fn();
- const wrapper = mountUpdateConnector({ onConfirm });
+ await reactAct(async () => {
+ const urlInput = getByTestId('credentialsApiUrlFromInput');
+ const usernameInput = getByTestId('connector-servicenow-username-form-input');
+ const passwordInput = getByTestId('connector-servicenow-password-form-input');
- expect(onConfirm).not.toHaveBeenCalled();
- wrapper.find('[data-test-subj="snUpdateInstallationSubmit"]').first().simulate('click');
- expect(onConfirm).toHaveBeenCalled();
+ await userEvent.type(urlInput, 'https://example.com', { delay: 100 });
+ await userEvent.type(usernameInput, 'user', { delay: 100 });
+ await userEvent.type(passwordInput, 'pass', { delay: 100 });
+ userEvent.click(getByTestId('snUpdateInstallationSubmit'));
+ });
+
+ expect(onConfirm).toHaveBeenCalledWith({
+ config: {
+ apiUrl: 'https://example.com',
+ },
+ secrets: {
+ password: 'pass',
+ username: 'user',
+ },
+ });
});
it('should cancel the update when cancel button clicked', () => {
@@ -365,14 +267,14 @@ describe('UpdateConnector renders', () => {
});
it('should show error message if present', () => {
- const applicationInfoErrorMsg = 'some application error';
+ const updateErrorMessage = 'some application error';
const wrapper = mountUpdateConnector({
- applicationInfoErrorMsg,
+ updateErrorMessage,
});
expect(wrapper.find('[data-test-subj="snApplicationCallout"]').exists()).toBeTruthy();
expect(wrapper.find('[data-test-subj="snApplicationCallout"]').first().text()).toContain(
- applicationInfoErrorMsg
+ updateErrorMessage
);
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.tsx
index d28ca1bdf2001..16363cd0b8e43 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/update_connector.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import React, { memo } from 'react';
+import React, { memo, useCallback } from 'react';
import {
EuiButton,
EuiButtonEmpty,
@@ -22,13 +22,12 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { snExternalServiceConfig } from '@kbn/actions-plugin/common';
-import { ActionConnectorFieldsProps } from '../../../../types';
-import { ServiceNowActionConnector } from './types';
+import { useForm, Form } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import { CredentialsApiUrl } from './credentials_api_url';
-import { isFieldInvalid } from './helpers';
-import { ApplicationRequiredCallout } from './application_required_callout';
-import { SNStoreLink } from './sn_store_button';
import { CredentialsAuth, OAuth } from './auth_types';
+import { SNStoreLink } from './sn_store_button';
+import { ApplicationRequiredCallout } from './application_required_callout';
+import { ServiceNowConfig, ServiceNowSecrets } from './types';
const title = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.serviceNow.updateFormTitle',
@@ -78,167 +77,142 @@ const warningMessage = i18n.translate(
defaultMessage: 'This updates all instances of this connector and cannot be reversed.',
}
);
+export interface UpdateConnectorFormSchema {
+ updatedConnector: {
+ config: ServiceNowConfig;
+ secrets: ServiceNowSecrets;
+ };
+}
export interface Props {
- action: ActionConnectorFieldsProps['action'];
- applicationInfoErrorMsg: string | null;
- errors: ActionConnectorFieldsProps['errors'];
+ actionTypeId: string;
+ isOAuth: boolean;
isLoading: boolean;
readOnly: boolean;
- editActionSecrets: ActionConnectorFieldsProps['editActionSecrets'];
- editActionConfig: ActionConnectorFieldsProps['editActionConfig'];
+ updateErrorMessage?: string | null;
onCancel: () => void;
- onConfirm: () => void;
+ onConfirm: (connector: UpdateConnectorFormSchema['updatedConnector']) => void;
}
+const PATH_PREFIX = 'updatedConnector.';
+
const UpdateConnectorComponent: React.FC = ({
- action,
- applicationInfoErrorMsg,
- errors,
+ actionTypeId,
+ isOAuth,
isLoading,
readOnly,
- editActionSecrets,
- editActionConfig,
onCancel,
onConfirm,
+ updateErrorMessage,
}) => {
- const { apiUrl, isOAuth, jwtKeyId, userIdentifierValue, clientId } = action.config;
- const { username, password, privateKeyPassword, privateKey, clientSecret } = action.secrets;
+ const { form } = useForm();
+ const { submit, isValid } = form;
- let hasErrorsOrEmptyFields;
+ const onSubmit = useCallback(async () => {
+ const { data, isValid: isSubmitValid } = await submit();
+ if (!isSubmitValid) {
+ return;
+ }
- hasErrorsOrEmptyFields = apiUrl === undefined || isFieldInvalid(apiUrl, errors.apiUrl);
-
- if (isOAuth) {
- hasErrorsOrEmptyFields =
- hasErrorsOrEmptyFields ||
- jwtKeyId === undefined ||
- userIdentifierValue === undefined ||
- clientId === undefined ||
- privateKeyPassword === undefined ||
- privateKey === undefined ||
- clientSecret === undefined ||
- isFieldInvalid(jwtKeyId, errors.apiUrl) ||
- isFieldInvalid(userIdentifierValue, errors.userIdentifierValue) ||
- isFieldInvalid(clientId, errors.clientId) ||
- isFieldInvalid(privateKeyPassword, errors.privateKeyPassword) ||
- isFieldInvalid(privateKey, errors.privateKey) ||
- isFieldInvalid(clientSecret, errors.clientSecret);
- } else {
- hasErrorsOrEmptyFields =
- hasErrorsOrEmptyFields ||
- username === undefined ||
- password === undefined ||
- isFieldInvalid(username, errors.username) ||
- isFieldInvalid(password, errors.password);
- }
+ const { updatedConnector } = data;
+ onConfirm(updatedConnector);
+ }, [onConfirm, submit]);
return (
-
-
-
- {title}
-
-
-
- }
- >
-
-
- ),
- }}
- />
- ),
- },
- {
- title: step2InstanceUrlTitle,
- children: (
-
- ),
- },
- {
- title: step3CredentialsTitle,
- children: isOAuth ? (
-
- ) : (
-
- ),
- },
- ]}
- />
-
-
-
- {applicationInfoErrorMsg && (
-
- )}
-
-
-
-
-
-
-
- {cancelButtonText}
-
-
-
-
+
+
+
+ {title}
+
+
+
- {confirmButtonText}
-
-
-
-
-
+ iconType="alert"
+ data-test-subj="snUpdateInstallationCallout"
+ title={warningMessage}
+ />
+ }
+ >
+
+
+ ),
+ }}
+ />
+ ),
+ },
+ {
+ title: step2InstanceUrlTitle,
+ children: (
+
+ ),
+ },
+ {
+ title: step3CredentialsTitle,
+ children: isOAuth ? (
+
+ ) : (
+
+ ),
+ },
+ ]}
+ />
+
+
+
+ {updateErrorMessage != null ? (
+
+ ) : null}
+
+
+
+
+
+
+
+ {cancelButtonText}
+
+
+
+
+ {confirmButtonText}
+
+
+
+
+
+
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_app_info.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_app_info.tsx
index 21a913b63641f..a086071a63487 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_app_info.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_app_info.tsx
@@ -5,18 +5,21 @@
* 2.0.
*/
+import { isEmpty } from 'lodash';
import { useState, useEffect, useRef, useCallback } from 'react';
import { HttpStart } from '@kbn/core/public';
import { getAppInfo } from './api';
import { AppInfo, RESTApiError, ServiceNowActionConnector } from './types';
export interface UseGetAppInfoProps {
- actionTypeId: string;
+ actionTypeId?: string;
http: HttpStart;
}
export interface UseGetAppInfo {
- fetchAppInfo: (connector: ServiceNowActionConnector) => Promise;
+ fetchAppInfo: (
+ connector: ServiceNowActionConnector
+ ) => Promise;
isLoading: boolean;
}
@@ -28,6 +31,10 @@ export const useGetAppInfo = ({ actionTypeId, http }: UseGetAppInfoProps): UseGe
const fetchAppInfo = useCallback(
async (connector) => {
try {
+ if (!actionTypeId || isEmpty(actionTypeId)) {
+ return;
+ }
+
didCancel.current = false;
abortCtrl.current.abort();
abortCtrl.current = new AbortController();
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx
index 76a23ab94d972..45689fbd50264 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.test.tsx
@@ -8,7 +8,6 @@
import { TypeRegistry } from '../../../type_registry';
import { registerBuiltInActionTypes } from '..';
import { ActionTypeModel } from '../../../../types';
-import { SlackActionConnector } from '../types';
import { registrationServicesMock } from '../../../../mocks';
const ACTION_TYPE_ID = '.slack';
@@ -30,98 +29,6 @@ describe('actionTypeRegistry.get() works', () => {
});
});
-describe('slack connector validation', () => {
- test('connector validation succeeds when connector config is valid', async () => {
- const actionConnector = {
- secrets: {
- webhookUrl: 'https:\\test',
- },
- id: 'test',
- actionTypeId: '.slack',
- name: 'slack',
- config: {},
- } as SlackActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {},
- },
- secrets: {
- errors: {
- webhookUrl: [],
- },
- },
- });
- });
-
- test('connector validation fails when connector config is not valid - no webhook url', async () => {
- const actionConnector = {
- secrets: {},
- id: 'test',
- actionTypeId: '.slack',
- name: 'slack',
- config: {},
- } as SlackActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {},
- },
- secrets: {
- errors: {
- webhookUrl: ['Webhook URL is required.'],
- },
- },
- });
- });
-
- test('connector validation fails when connector config is not valid - invalid webhook protocol', async () => {
- const actionConnector = {
- secrets: {
- webhookUrl: 'http:\\test',
- },
- id: 'test',
- actionTypeId: '.slack',
- name: 'slack',
- config: {},
- } as SlackActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {},
- },
- secrets: {
- errors: {
- webhookUrl: ['Webhook URL must start with https://.'],
- },
- },
- });
- });
-
- test('connector validation fails when connector config is not valid - invalid webhook url', async () => {
- const actionConnector = {
- secrets: {
- webhookUrl: 'h',
- },
- id: 'test',
- actionTypeId: '.slack',
- name: 'slack',
- config: {},
- } as SlackActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {},
- },
- secrets: {
- errors: {
- webhookUrl: ['Webhook URL is invalid.'],
- },
- },
- });
- });
-});
-
describe('slack action params validation', () => {
test('if action params validation succeeds when action params is valid', async () => {
const actionParams = {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx
index d3df034a90bf2..2252677084ba6 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx
@@ -7,13 +7,8 @@
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
-import {
- ActionTypeModel,
- GenericValidationResult,
- ConnectorValidationResult,
-} from '../../../../types';
-import { SlackActionParams, SlackSecrets, SlackActionConnector } from '../types';
-import { isValidUrl } from '../../../lib/value_validators';
+import { ActionTypeModel, GenericValidationResult } from '../../../../types';
+import { SlackActionParams, SlackSecrets } from '../types';
export function getActionType(): ActionTypeModel {
return {
@@ -31,25 +26,6 @@ export function getActionType(): ActionTypeModel> => {
- const translations = await import('./translations');
- const secretsErrors = {
- webhookUrl: new Array(),
- };
- const validationResult = { config: { errors: {} }, secrets: { errors: secretsErrors } };
- if (!action.secrets.webhookUrl) {
- secretsErrors.webhookUrl.push(translations.WEBHOOK_URL_REQUIRED);
- } else if (action.secrets.webhookUrl) {
- if (!isValidUrl(action.secrets.webhookUrl)) {
- secretsErrors.webhookUrl.push(translations.WEBHOOK_URL_INVALID);
- } else if (!isValidUrl(action.secrets.webhookUrl, 'https:')) {
- secretsErrors.webhookUrl.push(translations.WEBHOOK_URL_HTTP_INVALID);
- }
- }
- return validationResult;
- },
validateParams: async (
actionParams: SlackActionParams
): Promise> => {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx
index c0f6a17cae4fe..640bef0c22685 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.test.tsx
@@ -7,108 +7,127 @@
import React from 'react';
import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers';
-import { act } from '@testing-library/react';
-import { SlackActionConnector } from '../types';
+import { act, render } from '@testing-library/react';
import SlackActionFields from './slack_connectors';
+import { ConnectorFormTestProvider } from '../test_utils';
+import userEvent from '@testing-library/user-event';
+
jest.mock('../../../../common/lib/kibana');
describe('SlackActionFields renders', () => {
test('all connector fields is rendered', async () => {
const actionConnector = {
secrets: {
- webhookUrl: 'http:\\test',
+ webhookUrl: 'http://test.com',
},
id: 'test',
- actionTypeId: '.email',
- name: 'email',
+ actionTypeId: '.slack',
+ name: 'slack',
config: {},
- } as SlackActionConnector;
+ isDeprecated: false,
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}} />
+
);
await act(async () => {
await nextTick();
wrapper.update();
});
+
expect(wrapper.find('[data-test-subj="slackWebhookUrlInput"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="slackWebhookUrlInput"]').first().prop('value')).toBe(
- 'http:\\test'
+ 'http://test.com'
);
});
- test('should display a message on create to remember credentials', () => {
- const actionConnector = {
- actionTypeId: '.email',
- config: {},
- secrets: {},
- } as SlackActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0);
- });
+ describe('Validation', () => {
+ const onSubmit = jest.fn();
- test('should display a message for missing secrets after import', () => {
- const actionConnector = {
- actionTypeId: '.email',
- isMissingSecrets: true,
- config: {},
- secrets: {},
- } as SlackActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0);
- });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
- test('should display a message on edit to re-enter credentials', () => {
- const actionConnector = {
- secrets: {
- webhookUrl: 'http:\\test',
- },
- id: 'test',
- actionTypeId: '.email',
- name: 'email',
- config: {},
- } as SlackActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0);
+ it('connector validation succeeds when connector config is valid', async () => {
+ const actionConnector = {
+ secrets: {
+ webhookUrl: 'http://test.com',
+ },
+ id: 'test',
+ actionTypeId: '.slack',
+ name: 'slack',
+ config: {},
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ secrets: {
+ webhookUrl: 'http://test.com',
+ },
+ id: 'test',
+ actionTypeId: '.slack',
+ name: 'slack',
+ isDeprecated: false,
+ },
+ isValid: true,
+ });
+ });
+
+ it('validates teh web hook url field correctly', async () => {
+ const actionConnector = {
+ secrets: {
+ webhookUrl: 'http://test.com',
+ },
+ id: 'test',
+ actionTypeId: '.slack',
+ name: 'slack',
+ config: {},
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ await userEvent.type(
+ getByTestId('slackWebhookUrlInput'),
+ `{selectall}{backspace}no-valid`,
+ {
+ delay: 10,
+ }
+ );
+ });
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx
index 39e05c2c09c7e..0725096c5a719 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_connectors.tsx
@@ -6,73 +6,55 @@
*/
import React from 'react';
-import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
+import { EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
+import { FieldConfig, UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components';
+import { DocLinksStart } from '@kbn/core/public';
+
import { ActionConnectorFieldsProps } from '../../../../types';
-import { SlackActionConnector } from '../types';
import { useKibana } from '../../../../common/lib/kibana';
-import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label';
+import * as i18n from './translations';
+
+const { urlField } = fieldValidators;
+
+const getWebhookUrlConfig = (docLinks: DocLinksStart): FieldConfig => ({
+ label: i18n.WEBHOOK_URL_LABEL,
+ helpText: (
+
+
+
+ ),
+ validations: [
+ {
+ validator: urlField(i18n.WEBHOOK_URL_INVALID),
+ },
+ ],
+});
-const SlackActionFields: React.FunctionComponent<
- ActionConnectorFieldsProps
-> = ({ action, editActionSecrets, errors, readOnly }) => {
+const SlackActionFields: React.FunctionComponent = ({
+ isEdit,
+ readOnly,
+}) => {
const { docLinks } = useKibana().services;
- const { webhookUrl } = action.secrets;
- const isWebhookUrlInvalid: boolean =
- errors.webhookUrl !== undefined && errors.webhookUrl.length > 0 && webhookUrl !== undefined;
return (
- <>
-
-
-
- }
- error={errors.webhookUrl}
- isInvalid={isWebhookUrlInvalid}
- label={i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel',
- {
- defaultMessage: 'Webhook URL',
- }
- )}
- >
- <>
- {getEncryptedFieldNotifyLabel(
- !action.id,
- 1,
- action.isMissingSecrets ?? false,
- i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.reenterValueLabel',
- { defaultMessage: 'This URL is encrypted. Please reenter a value for this field.' }
- )
- )}
- {
- editActionSecrets('webhookUrl', e.target.value);
- }}
- onBlur={() => {
- if (!webhookUrl) {
- editActionSecrets('webhookUrl', '');
- }
- }}
- />
- >
-
- >
+
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/translations.ts
index bd1fd8ea194f6..e7d37082b53ff 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/translations.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/translations.ts
@@ -7,13 +7,6 @@
import { i18n } from '@kbn/i18n';
-export const WEBHOOK_URL_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.requiredWebhookUrlText',
- {
- defaultMessage: 'Webhook URL is required.',
- }
-);
-
export const WEBHOOK_URL_INVALID = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.invalidWebhookUrlText',
{
@@ -21,16 +14,16 @@ export const WEBHOOK_URL_INVALID = i18n.translate(
}
);
-export const WEBHOOK_URL_HTTP_INVALID = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.error.requireHttpsWebhookUrlText',
+export const MESSAGE_REQUIRED = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSlackMessageText',
{
- defaultMessage: 'Webhook URL must start with https://.',
+ defaultMessage: 'Message is required.',
}
);
-export const MESSAGE_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSlackMessageText',
+export const WEBHOOK_URL_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.webhookUrlTextFieldLabel',
{
- defaultMessage: 'Message is required.',
+ defaultMessage: 'Webhook URL',
}
);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/helpers.ts
index 413b952675b8c..4384cebf42e5a 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/helpers.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/helpers.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
+import { isEmpty } from 'lodash';
import { SwimlaneConnectorType, SwimlaneMappingConfig, MappingConfigurationKeys } from './types';
import * as i18n from './translations';
@@ -18,7 +19,7 @@ const casesFields = [...casesRequiredFields];
const alertsRequiredFields: MappingConfigurationKeys[] = ['ruleNameConfig', 'alertIdConfig'];
const alertsFields = ['severityConfig', 'commentsConfig', ...alertsRequiredFields];
-const translationMapping: Record = {
+export const translationMapping: Record = {
caseIdConfig: i18n.SW_REQUIRED_CASE_ID,
alertIdConfig: i18n.SW_REQUIRED_ALERT_ID,
caseNameConfig: i18n.SW_REQUIRED_CASE_NAME,
@@ -29,16 +30,33 @@ const translationMapping: Record = {
};
export const isValidFieldForConnector = (
- connector: SwimlaneConnectorType,
- field: MappingConfigurationKeys
+ connectorType: SwimlaneConnectorType,
+ fieldId: MappingConfigurationKeys
): boolean => {
- if (connector === SwimlaneConnectorType.All) {
+ if (connectorType === SwimlaneConnectorType.All) {
+ return true;
+ }
+
+ return connectorType === SwimlaneConnectorType.Alerts
+ ? alertsFields.includes(fieldId)
+ : casesFields.includes(fieldId);
+};
+
+export const isRequiredField = (
+ connectorType: SwimlaneConnectorType,
+ fieldId: MappingConfigurationKeys | undefined
+) => {
+ if (connectorType === SwimlaneConnectorType.All) {
+ return false;
+ }
+
+ if (fieldId == null || isEmpty(fieldId)) {
return true;
}
- return connector === SwimlaneConnectorType.Alerts
- ? alertsFields.includes(field)
- : casesFields.includes(field);
+ return connectorType === SwimlaneConnectorType.Alerts
+ ? alertsFields.includes(fieldId)
+ : casesFields.includes(fieldId);
};
export const validateMappingForConnector = (
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_connection.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_connection.tsx
index 823883b3a5b60..1dbb2f21a4fb8 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_connection.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_connection.tsx
@@ -4,115 +4,69 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import {
- EuiCallOut,
- EuiFieldText,
- EuiFormRow,
- EuiLink,
- EuiSpacer,
- EuiText,
- EuiFieldPassword,
-} from '@elastic/eui';
-import React, { useCallback } from 'react';
+
+import React from 'react';
+import { EuiLink } from '@elastic/eui';
+import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { TextField } from '@kbn/es-ui-shared-plugin/static/forms/components';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
import { FormattedMessage } from '@kbn/i18n-react';
-import * as i18n from '../translations';
+import { PasswordField } from '../../../password_field';
import { useKibana } from '../../../../../common/lib/kibana';
-import { SwimlaneActionConnector } from '../types';
-import { IErrorObject } from '../../../../../types';
+import * as i18n from '../translations';
interface Props {
- action: SwimlaneActionConnector;
- editActionConfig: (property: string, value: any) => void;
- editActionSecrets: (property: string, value: any) => void;
- errors: IErrorObject;
readOnly: boolean;
}
-const SwimlaneConnectionComponent: React.FunctionComponent = ({
- action,
- editActionConfig,
- editActionSecrets,
- errors,
- readOnly,
-}) => {
- const { apiUrl, appId } = action.config;
- const { apiToken } = action.secrets;
- const { docLinks } = useKibana().services;
-
- const onChangeConfig = useCallback(
- (e: React.ChangeEvent, key: 'apiUrl' | 'appId') => {
- editActionConfig(key, e.target.value);
- },
- [editActionConfig]
- );
-
- const onBlurConfig = useCallback(
- (key: 'apiUrl' | 'appId') => {
- if (!action.config[key]) {
- editActionConfig(key, '');
- }
- },
- [action.config, editActionConfig]
- );
-
- const onChangeSecrets = useCallback(
- (e: React.ChangeEvent) => {
- editActionSecrets('apiToken', e.target.value);
- },
- [editActionSecrets]
- );
-
- const onBlurSecrets = useCallback(() => {
- if (!apiToken) {
- editActionSecrets('apiToken', '');
- }
- }, [apiToken, editActionSecrets]);
-
- const isApiUrlInvalid = errors.apiUrl?.length > 0 && apiToken !== undefined;
- const isAppIdInvalid = errors.appId?.length > 0 && apiToken !== undefined;
- const isApiTokenInvalid = errors.apiToken?.length > 0 && apiToken !== undefined;
+const { emptyField, urlField } = fieldValidators;
+const SwimlaneConnectionComponent: React.FunctionComponent = ({ readOnly }) => {
+ const { docLinks } = useKibana().services;
return (
<>
-
- onChangeConfig(e, 'apiUrl')}
- onBlur={() => onBlurConfig('apiUrl')}
- />
-
-
- onChangeConfig(e, 'appId')}
- onBlur={() => onBlurConfig('appId')}
- />
-
-
+
+ = ({
/>
}
- error={errors.apiToken}
- isInvalid={isApiTokenInvalid}
- label={i18n.SW_API_TOKEN_TEXT_FIELD_LABEL}
- >
- <>
- {!action.id ? (
- <>
-
-
- {i18n.SW_REMEMBER_VALUE_LABEL}
-
-
- >
- ) : (
- <>
-
-
-
- >
- )}
-
- >
-
+ data-test-subj="swimlaneApiTokenInput"
+ />
>
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_fields.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_fields.tsx
index 707687f5b6716..10afa07c65a16 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_fields.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/steps/swimlane_fields.tsx
@@ -5,17 +5,25 @@
* 2.0.
*/
-import React, { useMemo, useCallback, useEffect, useRef } from 'react';
-import { EuiFormRow, EuiComboBox, EuiComboBoxOptionOption, EuiButtonGroup } from '@elastic/eui';
+import React, { useMemo } from 'react';
+import { EuiComboBox, EuiComboBoxOptionOption, EuiComboBoxProps, EuiFormRow } from '@elastic/eui';
+import {
+ FieldConfig,
+ getFieldValidityAndErrorMessage,
+ UseField,
+ useFormData,
+ VALIDATION_TYPES,
+} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { ComboBoxField } from '@kbn/es-ui-shared-plugin/static/forms/components';
+
import * as i18n from '../translations';
import {
- SwimlaneActionConnector,
+ MappingConfigurationKeys,
SwimlaneConnectorType,
SwimlaneFieldMappingConfig,
- SwimlaneMappingConfig,
} from '../types';
-import { IErrorObject } from '../../../../../types';
-import { isValidFieldForConnector } from '../helpers';
+import { isRequiredField, isValidFieldForConnector } from '../helpers';
+import { ButtonGroupField } from '../../../button_group_field';
const SINGLE_SELECTION = { asPlainText: true };
const EMPTY_COMBO_BOX_ARRAY: Array> | undefined = [];
@@ -29,11 +37,8 @@ const createSelectedOption = (field: SwimlaneFieldMappingConfig | null | undefin
field != null ? [formatOption(field)] : EMPTY_COMBO_BOX_ARRAY;
interface Props {
- action: SwimlaneActionConnector;
- editActionConfig: (property: string, value: any) => void;
- updateCurrentStep: (step: number) => void;
+ readOnly: boolean;
fields: SwimlaneFieldMappingConfig[];
- errors: IErrorObject;
}
const connectorTypeButtons = [
@@ -42,16 +47,107 @@ const connectorTypeButtons = [
{ id: SwimlaneConnectorType.Cases, label: 'Cases' },
];
-const SwimlaneFieldsComponent: React.FC = ({
- action,
- editActionConfig,
- updateCurrentStep,
- fields,
- errors,
-}) => {
- const { mappings, connectorType = SwimlaneConnectorType.All } = action.config;
- const prevConnectorType = useRef(connectorType);
- const hasChangedConnectorType = connectorType !== prevConnectorType.current;
+const mappingConfig: FieldConfig = {
+ defaultValue: null,
+ validations: [
+ {
+ validator: ({ value, customData }) => {
+ const data = customData.value as {
+ connectorType: SwimlaneConnectorType;
+ validationLabel: string;
+ };
+ if (isRequiredField(data.connectorType, value?.id as MappingConfigurationKeys)) {
+ return {
+ message: data.validationLabel,
+ };
+ }
+ },
+ },
+ ],
+};
+
+const MappingField: React.FC<{
+ path: string;
+ label: string;
+ validationLabel: string;
+ options: EuiComboBoxProps['options'];
+ fieldIdMap: Map;
+ connectorType: SwimlaneConnectorType;
+ readOnly: boolean;
+ dataTestSubj?: string;
+}> = React.memo(
+ ({
+ path,
+ options,
+ label,
+ validationLabel,
+ dataTestSubj,
+ fieldIdMap,
+ connectorType,
+ readOnly,
+ }) => {
+ return (
+
+ path={path}
+ component={ComboBoxField}
+ config={mappingConfig}
+ validationData={{ connectorType, validationLabel }}
+ >
+ {(field) => {
+ const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field);
+
+ const onComboChange = (opt: Array>) => {
+ const option = opt[0];
+
+ const item = fieldIdMap.get(option?.value ?? '');
+ if (!item) {
+ field.setValue(null);
+ return;
+ }
+
+ field.setValue({
+ id: item.id,
+ name: item.name,
+ key: item.key,
+ fieldType: item.fieldType,
+ });
+ };
+
+ const onSearchComboChange = (value: string) => {
+ if (value !== undefined) {
+ field.clearErrors(VALIDATION_TYPES.ARRAY_ITEM);
+ }
+ };
+
+ const selectedOptions = createSelectedOption(fieldIdMap.get(field.value?.id ?? ''));
+
+ return (
+
+
+
+ );
+ }}
+
+ );
+ }
+);
+
+const SwimlaneFieldsComponent: React.FC = ({ fields, readOnly }) => {
+ const [{ config }] = useFormData({
+ watch: ['config.connectorType'],
+ });
+
+ const connectorType = config?.connectorType ?? SwimlaneConnectorType.All;
const [fieldTypeMap, fieldIdMap] = useMemo(
() =>
@@ -78,214 +174,98 @@ const SwimlaneFieldsComponent: React.FC = ({
const textOptions = useMemo(() => fieldTypeMap.get('text') ?? [], [fieldTypeMap]);
const commentsOptions = useMemo(() => fieldTypeMap.get('comments') ?? [], [fieldTypeMap]);
- const state = useMemo(
- () => ({
- alertIdConfig: createSelectedOption(mappings?.alertIdConfig),
- severityConfig: createSelectedOption(mappings?.severityConfig),
- ruleNameConfig: createSelectedOption(mappings?.ruleNameConfig),
- caseIdConfig: createSelectedOption(mappings?.caseIdConfig),
- caseNameConfig: createSelectedOption(mappings?.caseNameConfig),
- commentsConfig: createSelectedOption(mappings?.commentsConfig),
- descriptionConfig: createSelectedOption(mappings?.descriptionConfig),
- }),
- [mappings]
- );
-
- const mappingErrors: Record = useMemo(
- () => (Array.isArray(errors?.mappings) ? errors?.mappings[0] : {}),
- [errors]
- );
-
- const editMappings = useCallback(
- (key: keyof SwimlaneMappingConfig, e: Array>) => {
- if (e.length === 0) {
- const newProps = {
- ...mappings,
- [key]: null,
- };
- editActionConfig('mappings', newProps);
- return;
- }
-
- const option = e[0];
- const item = fieldIdMap.get(option.value ?? '');
- if (!item) {
- return;
- }
-
- const newProps = {
- ...mappings,
- [key]: { id: item.id, name: item.name, key: item.key, fieldType: item.fieldType },
- };
- editActionConfig('mappings', newProps);
- },
- [editActionConfig, fieldIdMap, mappings]
- );
-
- useEffect(() => {
- if (connectorType !== prevConnectorType.current) {
- prevConnectorType.current = connectorType;
- }
- }, [connectorType]);
-
return (
<>
-
- editActionConfig('connectorType', type)}
- buttonSize="compressed"
- />
-
+
{isValidFieldForConnector(connectorType as SwimlaneConnectorType.All, 'alertIdConfig') && (
- <>
-
- editMappings('alertIdConfig', e)}
- isInvalid={mappingErrors?.alertIdConfig != null && !hasChangedConnectorType}
- />
-
- >
+
)}
{isValidFieldForConnector(connectorType as SwimlaneConnectorType, 'ruleNameConfig') && (
- <>
-
- editMappings('ruleNameConfig', e)}
- isInvalid={mappingErrors?.ruleNameConfig != null && !hasChangedConnectorType}
- />
-
- >
+
)}
{isValidFieldForConnector(connectorType as SwimlaneConnectorType, 'severityConfig') && (
- <>
-
- editMappings('severityConfig', e)}
- isInvalid={mappingErrors?.severityConfig != null && !hasChangedConnectorType}
- />
-
- >
+
)}
{isValidFieldForConnector(connectorType as SwimlaneConnectorType, 'caseIdConfig') && (
- <>
-
- editMappings('caseIdConfig', e)}
- isInvalid={mappingErrors?.caseIdConfig != null && !hasChangedConnectorType}
- />
-
- >
+
)}
{isValidFieldForConnector(connectorType as SwimlaneConnectorType, 'caseNameConfig') && (
- <>
-
- editMappings('caseNameConfig', e)}
- isInvalid={mappingErrors?.caseNameConfig != null && !hasChangedConnectorType}
- />
-
- >
+
)}
{isValidFieldForConnector(connectorType as SwimlaneConnectorType, 'commentsConfig') && (
- <>
-
- >
+
)}
{isValidFieldForConnector(connectorType as SwimlaneConnectorType, 'descriptionConfig') && (
- <>
-
- editMappings('descriptionConfig', e)}
- isInvalid={mappingErrors?.descriptionConfig != null && !hasChangedConnectorType}
- />
-
- >
+
)}
>
);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.test.tsx
index 45d68c8ab39e8..479496b57ba83 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.test.tsx
@@ -8,7 +8,6 @@
import { TypeRegistry } from '../../../type_registry';
import { registerBuiltInActionTypes } from '..';
import { ActionTypeModel } from '../../../../types';
-import { SwimlaneActionConnector } from './types';
import { registrationServicesMock } from '../../../../mocks';
const ACTION_TYPE_ID = '.swimlane';
@@ -29,152 +28,6 @@ describe('actionTypeRegistry.get() works', () => {
});
});
-describe('swimlane connector validation', () => {
- test('connector validation succeeds when connector is valid', async () => {
- const actionConnector = {
- secrets: {
- apiToken: 'test',
- },
- id: 'test',
- actionTypeId: '.swimlane',
- name: 'swimlane',
- config: {
- apiUrl: 'http:\\test',
- appId: '1234567asbd32',
- connectorType: 'all',
- mappings: {
- alertIdConfig: { id: '1234' },
- severityConfig: { id: '1234' },
- ruleNameConfig: { id: '1234' },
- caseIdConfig: { id: '1234' },
- caseNameConfig: { id: '1234' },
- descriptionConfig: { id: '1234' },
- commentsConfig: { id: '1234' },
- },
- },
- } as SwimlaneActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: { errors: { apiUrl: [], appId: [], mappings: [], connectorType: [] } },
- secrets: { errors: { apiToken: [] } },
- });
- });
-
- test('it validates correctly when connectorType=all', async () => {
- const actionConnector = {
- secrets: {
- apiToken: 'test',
- },
- id: 'test',
- actionTypeId: '.swimlane',
- name: 'swimlane',
- config: {
- apiUrl: 'http:\\test',
- appId: '1234567asbd32',
- connectorType: 'all',
- mappings: {},
- },
- } as SwimlaneActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: { errors: { apiUrl: [], appId: [], mappings: [], connectorType: [] } },
- secrets: { errors: { apiToken: [] } },
- });
- });
-
- test('it validates correctly when connectorType=cases', async () => {
- const actionConnector = {
- secrets: {
- apiToken: 'test',
- },
- id: 'test',
- actionTypeId: '.swimlane',
- name: 'swimlane',
- config: {
- apiUrl: 'http:\\test',
- appId: '1234567asbd32',
- connectorType: 'cases',
- mappings: {},
- },
- } as SwimlaneActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- apiUrl: [],
- appId: [],
- mappings: [
- {
- caseIdConfig: 'Case ID is required.',
- caseNameConfig: 'Case name is required.',
- commentsConfig: 'Comments are required.',
- descriptionConfig: 'Description is required.',
- },
- ],
- connectorType: [],
- },
- },
- secrets: { errors: { apiToken: [] } },
- });
- });
-
- test('it validates correctly when connectorType=alerts', async () => {
- const actionConnector = {
- secrets: {
- apiToken: 'test',
- },
- id: 'test',
- actionTypeId: '.swimlane',
- name: 'swimlane',
- config: {
- apiUrl: 'http:\\test',
- appId: '1234567asbd32',
- connectorType: 'alerts',
- mappings: {},
- },
- } as SwimlaneActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- apiUrl: [],
- appId: [],
- mappings: [
- {
- alertIdConfig: 'Alert ID is required.',
- ruleNameConfig: 'Rule name is required.',
- },
- ],
- connectorType: [],
- },
- },
- secrets: { errors: { apiToken: [] } },
- });
- });
-
- test('it validates correctly required config/secrets fields', async () => {
- const actionConnector = {
- secrets: {},
- id: 'test',
- actionTypeId: '.swimlane',
- name: 'swimlane',
- config: {},
- } as SwimlaneActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- apiUrl: ['URL is required.'],
- appId: ['An App ID is required.'],
- mappings: [],
- connectorType: [],
- },
- },
- secrets: { errors: { apiToken: ['An API token is required.'] } },
- });
- });
-});
-
describe('swimlane action params validation', () => {
test('action params validation succeeds when action params is valid', async () => {
const actionParams = {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.tsx
index 5797017de7baf..5a072a54efb67 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane.tsx
@@ -5,22 +5,10 @@
* 2.0.
*/
-import { isEmpty } from 'lodash';
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
-import {
- ActionTypeModel,
- ConnectorValidationResult,
- GenericValidationResult,
-} from '../../../../types';
-import {
- SwimlaneActionConnector,
- SwimlaneConfig,
- SwimlaneSecrets,
- SwimlaneActionParams,
-} from './types';
-import { isValidUrl } from '../../../lib/value_validators';
-import { validateMappingForConnector } from './helpers';
+import { ActionTypeModel, GenericValidationResult } from '../../../../types';
+import { SwimlaneConfig, SwimlaneSecrets, SwimlaneActionParams } from './types';
export const SW_SELECT_MESSAGE_TEXT = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.selectMessageText',
@@ -46,55 +34,6 @@ export function getActionType(): ActionTypeModel<
iconClass: lazy(() => import('./logo')),
selectMessage: SW_SELECT_MESSAGE_TEXT,
actionTypeTitle: SW_ACTION_TYPE_TITLE,
- validateConnector: async (
- action: SwimlaneActionConnector
- ): Promise> => {
- const translations = await import('./translations');
- const configErrors = {
- apiUrl: new Array(),
- appId: new Array(),
- connectorType: new Array(),
- mappings: new Array>(),
- };
- const secretsErrors = {
- apiToken: new Array(),
- };
-
- const validationResult = {
- config: { errors: configErrors },
- secrets: { errors: secretsErrors },
- };
-
- if (!action.config.apiUrl) {
- configErrors.apiUrl = [...configErrors.apiUrl, translations.SW_API_URL_REQUIRED];
- } else if (action.config.apiUrl) {
- if (!isValidUrl(action.config.apiUrl)) {
- configErrors.apiUrl = [...configErrors.apiUrl, translations.SW_API_URL_INVALID];
- }
- }
-
- if (!action.secrets.apiToken) {
- secretsErrors.apiToken = [
- ...secretsErrors.apiToken,
- translations.SW_REQUIRED_API_TOKEN_TEXT,
- ];
- }
-
- if (!action.config.appId) {
- configErrors.appId = [...configErrors.appId, translations.SW_REQUIRED_APP_ID_TEXT];
- }
-
- const mappingErrors = validateMappingForConnector(
- action.config.connectorType,
- action.config.mappings
- );
-
- if (!isEmpty(mappingErrors)) {
- configErrors.mappings = [...configErrors.mappings, mappingErrors];
- }
-
- return validationResult;
- },
validateParams: async (
actionParams: SwimlaneActionParams
): Promise> => {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.test.tsx
index 47e5c09c13864..bbe86a4fe7fe1 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.test.tsx
@@ -8,10 +8,13 @@
import React from 'react';
import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers';
import { act } from 'react-dom/test-utils';
-import { SwimlaneActionConnector } from './types';
import SwimlaneActionConnectorFields from './swimlane_connectors';
import { useGetApplication } from './use_get_application';
import { applicationFields, mappings } from './mocks';
+import { ConnectorFormTestProvider } from '../test_utils';
+import { waitFor } from '@testing-library/dom';
+import userEvent from '@testing-library/user-event';
+import { render } from '@testing-library/react';
jest.mock('../../../../common/lib/kibana');
jest.mock('./use_get_application');
@@ -29,10 +32,6 @@ describe('SwimlaneActionConnectorFields renders', () => {
test('all connector fields are rendered', async () => {
const actionConnector = {
- secrets: {
- apiToken: 'test',
- },
- id: 'test',
actionTypeId: '.swimlane',
name: 'swimlane',
config: {
@@ -41,18 +40,20 @@ describe('SwimlaneActionConnectorFields renders', () => {
connectorType: 'all',
mappings,
},
- } as SwimlaneActionConnector;
+ secrets: {
+ apiToken: 'test',
+ },
+ isDeprecated: false,
+ };
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
await act(async () => {
@@ -65,69 +66,12 @@ describe('SwimlaneActionConnectorFields renders', () => {
expect(wrapper.find('[data-test-subj="swimlaneApiTokenInput"]').exists()).toBeTruthy();
});
- test('should display a message on create to remember credentials', () => {
- const actionConnector = {
- actionTypeId: '.swimlane',
- secrets: {},
- config: {},
- } as SwimlaneActionConnector;
-
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0);
- });
-
- test('should display a message on edit to re-enter credentials', () => {
- const actionConnector = {
- secrets: {
- apiToken: 'test',
- },
- id: 'test',
- actionTypeId: '.swimlane',
- name: 'swimlane',
- config: {
- apiUrl: 'http:\\test',
- appId: '1234567asbd32',
- connectorType: 'all',
- mappings,
- },
- } as SwimlaneActionConnector;
-
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0);
- });
-
test('renders the mappings correctly - connector type all', async () => {
getApplication.mockResolvedValue({
fields: applicationFields,
});
const actionConnector = {
- secrets: {
- apiToken: 'test',
- },
- id: 'test',
actionTypeId: '.swimlane',
name: 'swimlane',
config: {
@@ -136,18 +80,20 @@ describe('SwimlaneActionConnectorFields renders', () => {
connectorType: 'all',
mappings,
},
- } as SwimlaneActionConnector;
+ secrets: {
+ apiToken: 'test',
+ },
+ isDeprecated: false,
+ };
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
await act(async () => {
@@ -156,13 +102,15 @@ describe('SwimlaneActionConnectorFields renders', () => {
wrapper.update();
});
- expect(wrapper.find('[data-test-subj="swimlaneAlertIdInput"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="swimlaneAlertNameInput"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="swimlaneSeverityInput"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="swimlaneCaseIdConfig"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="swimlaneCaseNameConfig"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="swimlaneCommentsConfig"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="swimlaneDescriptionConfig"]').exists()).toBeTruthy();
+ await waitFor(() => {
+ expect(wrapper.find('[data-test-subj="swimlaneAlertIdInput"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="swimlaneAlertNameInput"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="swimlaneSeverityInput"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="swimlaneCaseIdConfig"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="swimlaneCaseNameConfig"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="swimlaneCommentsConfig"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="swimlaneDescriptionConfig"]').exists()).toBeTruthy();
+ });
});
test('renders the mappings correctly - connector type cases', async () => {
@@ -171,10 +119,6 @@ describe('SwimlaneActionConnectorFields renders', () => {
});
const actionConnector = {
- secrets: {
- apiToken: 'test',
- },
- id: 'test',
actionTypeId: '.swimlane',
name: 'swimlane',
config: {
@@ -183,18 +127,20 @@ describe('SwimlaneActionConnectorFields renders', () => {
connectorType: 'cases',
mappings,
},
- } as SwimlaneActionConnector;
+ secrets: {
+ apiToken: 'test',
+ },
+ isDeprecated: false,
+ };
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
await act(async () => {
@@ -203,6 +149,7 @@ describe('SwimlaneActionConnectorFields renders', () => {
wrapper.update();
});
+ await waitFor(() => {});
expect(wrapper.find('[data-test-subj="swimlaneAlertIdInput"]').exists()).toBeFalsy();
expect(wrapper.find('[data-test-subj="swimlaneAlertNameInput"]').exists()).toBeFalsy();
expect(wrapper.find('[data-test-subj="swimlaneSeverityInput"]').exists()).toBeFalsy();
@@ -218,10 +165,6 @@ describe('SwimlaneActionConnectorFields renders', () => {
});
const actionConnector = {
- secrets: {
- apiToken: 'test',
- },
- id: 'test',
actionTypeId: '.swimlane',
name: 'swimlane',
config: {
@@ -230,18 +173,20 @@ describe('SwimlaneActionConnectorFields renders', () => {
connectorType: 'alerts',
mappings,
},
- } as SwimlaneActionConnector;
+ secrets: {
+ apiToken: 'test',
+ },
+ isDeprecated: false,
+ };
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
await act(async () => {
@@ -250,13 +195,15 @@ describe('SwimlaneActionConnectorFields renders', () => {
wrapper.update();
});
- expect(wrapper.find('[data-test-subj="swimlaneAlertIdInput"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="swimlaneAlertNameInput"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="swimlaneSeverityInput"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="swimlaneCaseIdConfig"]').exists()).toBeFalsy();
- expect(wrapper.find('[data-test-subj="swimlaneCaseNameConfig"]').exists()).toBeFalsy();
- expect(wrapper.find('[data-test-subj="swimlaneCommentsConfig"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="swimlaneDescriptionConfig"]').exists()).toBeFalsy();
+ await waitFor(() => {
+ expect(wrapper.find('[data-test-subj="swimlaneAlertIdInput"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="swimlaneAlertNameInput"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="swimlaneSeverityInput"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="swimlaneCaseIdConfig"]').exists()).toBeFalsy();
+ expect(wrapper.find('[data-test-subj="swimlaneCaseNameConfig"]').exists()).toBeFalsy();
+ expect(wrapper.find('[data-test-subj="swimlaneCommentsConfig"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="swimlaneDescriptionConfig"]').exists()).toBeFalsy();
+ });
});
test('renders the correct options per field', async () => {
@@ -265,19 +212,19 @@ describe('SwimlaneActionConnectorFields renders', () => {
});
const actionConnector = {
- secrets: {
- apiToken: 'test',
- },
- id: 'test',
actionTypeId: '.swimlane',
name: 'swimlane',
config: {
- apiUrl: 'http:\\test',
+ apiUrl: 'http://test.com',
appId: '1234567asbd32',
connectorType: 'all',
mappings,
},
- } as SwimlaneActionConnector;
+ secrets: {
+ apiToken: 'test',
+ },
+ isDeprecated: false,
+ };
const textOptions = [
{ label: 'Alert Id (alert-id)', value: 'a6ide' },
@@ -291,17 +238,23 @@ describe('SwimlaneActionConnectorFields renders', () => {
const commentOptions = [{ label: 'Comments (notes)', value: 'a6fdf' }];
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
+ await waitFor(async () => {
+ await nextTick();
+ wrapper.update();
+ expect(wrapper.find('[data-test-subj="swimlaneApiUrlInput"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="swimlaneAppIdInput"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="swimlaneApiTokenInput"]').exists()).toBeTruthy();
+ });
+
await act(async () => {
wrapper.find('[data-test-subj="swimlaneConfigureMapping"]').first().simulate('click');
await nextTick();
@@ -330,4 +283,157 @@ describe('SwimlaneActionConnectorFields renders', () => {
wrapper.find('[data-test-subj="swimlaneDescriptionConfig"]').first().prop('options')
).toEqual(textOptions);
});
+
+ describe('Validation', () => {
+ const onSubmit = jest.fn();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ getApplication.mockResolvedValue({
+ fields: applicationFields,
+ });
+ });
+
+ const getConnector = (connectorType: string = 'all') => ({
+ actionTypeId: '.swimlane',
+ name: 'swimlane',
+ config: {
+ apiUrl: 'http://test.com',
+ appId: '1234567asbd32',
+ connectorType: 'all',
+ mappings,
+ },
+ secrets: {
+ apiToken: 'test',
+ },
+ isDeprecated: false,
+ });
+
+ const getConnectorWithEmptyMappings = (connectorType: string = 'all') => {
+ const actionConnector = getConnector(connectorType);
+ return {
+ ...actionConnector,
+ config: {
+ ...actionConnector.config,
+ connectorType,
+ mappings: {},
+ },
+ };
+ };
+
+ const tests: Array<[string, string]> = [
+ ['swimlaneApiUrlInput', 'not-valid'],
+ ['swimlaneAppIdInput', ''],
+ ['swimlaneApiTokenInput', ''],
+ ];
+
+ it.each([['cases'], ['alerts']])(
+ 'connector validation succeeds when connector config is valid for connectorType=%p',
+ async (connectorType) => {
+ const connector = getConnector(connectorType);
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: { ...connector }, isValid: true });
+ }
+ );
+
+ it.each(tests)('validates correctly %p', async (field, value) => {
+ const connector = getConnector();
+ const res = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
+
+ it('connector validation succeeds when when connectorType=all with empty mappings', async () => {
+ const connector = getConnectorWithEmptyMappings();
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({
+ data: {
+ ...connector,
+ config: {
+ ...connector.config,
+ mappings: {
+ alertIdConfig: null,
+ caseIdConfig: null,
+ caseNameConfig: null,
+ commentsConfig: null,
+ descriptionConfig: null,
+ ruleNameConfig: null,
+ severityConfig: null,
+ },
+ },
+ },
+ isValid: true,
+ });
+ });
+
+ it.each([['cases'], ['alerts']])(
+ 'validates correctly when when connectorType=%p',
+ async (connectorType) => {
+ const connector = getConnectorWithEmptyMappings(connectorType);
+
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({
+ data: {},
+ isValid: false,
+ });
+ }
+ );
+ });
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.tsx
index c8db48421264c..af59b9517b91d 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/swimlane_connectors.tsx
@@ -5,46 +5,42 @@
* 2.0.
*/
-import React, { Fragment, useCallback, useMemo, useState, useEffect } from 'react';
+import React, { Fragment, useCallback, useMemo, useState } from 'react';
import {
EuiForm,
EuiSpacer,
EuiStepsHorizontal,
- EuiStepStatus,
EuiButton,
EuiFormRow,
+ EuiStepStatus,
} from '@elastic/eui';
+import { useFormContext, useFormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import { useKibana } from '../../../../common/lib/kibana';
import { ActionConnectorFieldsProps } from '../../../../types';
-import {
- SwimlaneActionConnector,
- SwimlaneConnectorType,
- SwimlaneFieldMappingConfig,
-} from './types';
+import { SwimlaneFieldMappingConfig } from './types';
import { SwimlaneConnection, SwimlaneFields } from './steps';
import { useGetApplication } from './use_get_application';
import * as i18n from './translations';
-const SwimlaneActionConnectorFields: React.FunctionComponent<
- ActionConnectorFieldsProps
-> = ({ errors, action, editActionConfig, editActionSecrets, readOnly }) => {
+const SwimlaneActionConnectorFields: React.FunctionComponent = ({
+ readOnly,
+}) => {
const {
notifications: { toasts },
} = useKibana().services;
- const { apiUrl, appId, mappings, connectorType } = action.config;
- const { apiToken } = action.secrets;
-
+ const [hasConfigurationErrors, setHasConfigurationError] = useState(false);
+ const { isValid, validateFields } = useFormContext();
+ const [{ config, secrets }] = useFormData({
+ watch: ['config.apiUrl', 'config.appId', 'secrets.apiToken'],
+ });
const { getApplication, isLoading: isLoadingApplication } = useGetApplication({
toastNotifications: toasts,
- apiToken,
- appId,
- apiUrl,
});
- const hasConfigurationErrors =
- errors.apiUrl?.length > 0 || errors.appId?.length > 0 || errors.apiToken?.length > 0;
-
+ const apiUrl = config?.apiUrl ?? '';
+ const appId = config?.appId ?? '';
+ const apiToken = secrets?.apiToken ?? '';
const [currentStep, setCurrentStep] = useState(1);
const [fields, setFields] = useState([]);
@@ -53,25 +49,37 @@ const SwimlaneActionConnectorFields: React.FunctionComponent<
}, []);
const onNextStep = useCallback(async () => {
+ setHasConfigurationError(false);
+
+ const { areFieldsValid } = await validateFields([
+ 'config.apiUrl',
+ 'config.appId',
+ 'secrets.apiToken',
+ ]);
+
+ if (!areFieldsValid) {
+ setHasConfigurationError(true);
+ return;
+ }
+
// fetch swimlane application configuration
- const application = await getApplication();
+ const application = await getApplication({
+ apiUrl,
+ appId,
+ apiToken,
+ });
if (application?.fields) {
const allFields = application.fields;
setFields(allFields);
setCurrentStep(2);
}
- }, [getApplication]);
+ }, [apiToken, apiUrl, appId, getApplication, validateFields]);
const resetConnection = useCallback(() => {
setCurrentStep(1);
}, []);
- const hasMappingErrors = useMemo(
- () => Object.values(errors?.mappings ?? {}).some((mappingError) => mappingError.length !== 0),
- [errors?.mappings]
- );
-
const steps = useMemo(
() => [
{
@@ -88,7 +96,7 @@ const SwimlaneActionConnectorFields: React.FunctionComponent<
title: i18n.SW_MAPPING_TITLE_TEXT_FIELD_LABEL,
disabled: hasConfigurationErrors || isLoadingApplication,
onClick: onNextStep,
- status: (hasMappingErrors
+ status: (!isValid
? 'danger'
: currentStep === 2
? 'selected'
@@ -98,68 +106,40 @@ const SwimlaneActionConnectorFields: React.FunctionComponent<
[
currentStep,
hasConfigurationErrors,
- hasMappingErrors,
isLoadingApplication,
+ isValid,
onNextStep,
updateCurrentStep,
]
);
- /**
- * Connector type needs to be updated on mount to All.
- * Otherwise it is undefined and this will cause an error
- * if the user saves the connector without going to the
- * second step. Same for mapping.
- */
- useEffect(() => {
- editActionConfig('connectorType', connectorType ?? SwimlaneConnectorType.All);
- editActionConfig('mappings', mappings ?? {});
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
return (
- {currentStep === 1 && (
- <>
-
-
-
-
- {i18n.SW_NEXT}
-
-
- >
- )}
- {currentStep === 2 && (
- <>
-
-
- {i18n.SW_BACK}
+
+
+
+
+
+ {i18n.SW_NEXT}
- >
- )}
+
+
+
+
+
+ {i18n.SW_BACK}
+
+
);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/translations.ts
index a1dbf88f92994..631e69e8e7a66 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/translations.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/translations.ts
@@ -21,20 +21,6 @@ export const SW_REQUIRED_APP_ID_TEXT = i18n.translate(
}
);
-export const SW_REQUIRED_FIELD_MAPPINGS_TEXT = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredFieldMappingsText',
- {
- defaultMessage: 'Field mappings are required.',
- }
-);
-
-export const SW_REQUIRED_API_TOKEN_TEXT = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredApiTokenText',
- {
- defaultMessage: 'An API token is required.',
- }
-);
-
export const SW_GET_APPLICATION_API_ERROR = (id: string | null) =>
i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.swimlane.unableToGetApplicationMessage',
@@ -58,13 +44,6 @@ export const SW_API_URL_TEXT_FIELD_LABEL = i18n.translate(
}
);
-export const SW_API_URL_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.requiredApiUrlTextField',
- {
- defaultMessage: 'URL is required.',
- }
-);
-
export const SW_API_URL_INVALID = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.invalidApiUrlTextField',
{
@@ -93,13 +72,6 @@ export const SW_MAPPING_TITLE_TEXT_FIELD_LABEL = i18n.translate(
}
);
-export const SW_ALERT_SOURCE_FIELD_LABEL = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.alertSourceFieldLabel',
- {
- defaultMessage: 'Alert source',
- }
-);
-
export const SW_SEVERITY_FIELD_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.severityFieldLabel',
{
@@ -107,13 +79,6 @@ export const SW_SEVERITY_FIELD_LABEL = i18n.translate(
}
);
-export const SW_MAPPING_DESCRIPTION_TEXT_FIELD_LABEL = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingDescriptionTextFieldLabel',
- {
- defaultMessage: 'Used to specify the field names in the Swimlane Application',
- }
-);
-
export const SW_RULE_NAME_FIELD_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.ruleNameFieldLabel',
{
@@ -156,26 +121,11 @@ export const SW_DESCRIPTION_FIELD_LABEL = i18n.translate(
}
);
-export const SW_REMEMBER_VALUE_LABEL = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.rememberValueLabel',
- { defaultMessage: 'Remember this value. You must reenter it each time you edit the connector.' }
-);
-
-export const SW_REENTER_VALUE_LABEL = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.reenterValueLabel',
- { defaultMessage: 'This key is encrypted. Please reenter a value for this field.' }
-);
-
export const SW_CONFIGURE_CONNECTION_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.configureConnectionLabel',
{ defaultMessage: 'Configure API Connection' }
);
-export const SW_RETRIEVE_CONFIGURATION_LABEL = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.retrieveConfigurationLabel',
- { defaultMessage: 'Configure Fields' }
-);
-
export const SW_CONNECTOR_TYPE_LABEL = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.connectorType',
{
@@ -183,13 +133,6 @@ export const SW_CONNECTOR_TYPE_LABEL = i18n.translate(
}
);
-export const SW_FIELD_MAPPING_IS_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.mappingFieldRequired',
- {
- defaultMessage: 'Field mapping is required.',
- }
-);
-
export const EMPTY_MAPPING_WARNING_TITLE = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.emptyMappingWarningTitle',
{
@@ -205,13 +148,6 @@ export const EMPTY_MAPPING_WARNING_DESC = i18n.translate(
}
);
-export const SW_REQUIRED_ALERT_SOURCE = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredAlertSource',
- {
- defaultMessage: 'Alert source is required.',
- }
-);
-
export const SW_REQUIRED_SEVERITY = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.swimlaneAction.error.requiredSeverity',
{
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.test.tsx
index fc0ae22efe377..f852d40ebef2f 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.test.tsx
@@ -9,7 +9,6 @@ import { renderHook, act } from '@testing-library/react-hooks';
import { useKibana } from '../../../../common/lib/kibana';
import { getApplication } from './api';
-import { SwimlaneActionConnector } from './types';
import { useGetApplication, UseGetApplication } from './use_get_application';
jest.mock('./api');
@@ -30,7 +29,7 @@ const action = {
appId: 'bcq16kdTbz5jlwM6h',
mappings: {},
},
-} as SwimlaneActionConnector;
+};
describe('useGetApplication', () => {
const { services } = useKibanaMock();
@@ -48,9 +47,6 @@ describe('useGetApplication', () => {
await act(async () => {
const { result, waitForNextUpdate } = renderHook(() =>
useGetApplication({
- appId: action.config.appId,
- apiToken: action.secrets.apiToken,
- apiUrl: action.config.apiUrl,
toastNotifications: services.notifications.toasts,
})
);
@@ -67,16 +63,18 @@ describe('useGetApplication', () => {
await act(async () => {
const { result, waitForNextUpdate } = renderHook(() =>
useGetApplication({
- appId: action.config.appId,
- apiToken: action.secrets.apiToken,
- apiUrl: action.config.apiUrl,
toastNotifications: services.notifications.toasts,
})
);
await waitForNextUpdate();
- result.current.getApplication();
+ result.current.getApplication({
+ appId: action.config.appId,
+ apiToken: action.secrets.apiToken,
+ apiUrl: action.config.apiUrl,
+ });
+
await waitForNextUpdate();
expect(getApplicationMock).toBeCalledWith({
signal: abortCtrl.signal,
@@ -91,15 +89,16 @@ describe('useGetApplication', () => {
await act(async () => {
const { result, waitForNextUpdate } = renderHook(() =>
useGetApplication({
- appId: action.config.appId,
- apiToken: action.secrets.apiToken,
- apiUrl: action.config.apiUrl,
toastNotifications: services.notifications.toasts,
})
);
await waitForNextUpdate();
- result.current.getApplication();
+ result.current.getApplication({
+ appId: action.config.appId,
+ apiToken: action.secrets.apiToken,
+ apiUrl: action.config.apiUrl,
+ });
await waitForNextUpdate();
expect(result.current).toEqual({
@@ -113,15 +112,16 @@ describe('useGetApplication', () => {
await act(async () => {
const { result, waitForNextUpdate } = renderHook(() =>
useGetApplication({
- appId: action.config.appId,
- apiToken: action.secrets.apiToken,
- apiUrl: action.config.apiUrl,
toastNotifications: services.notifications.toasts,
})
);
await waitForNextUpdate();
- result.current.getApplication();
+ result.current.getApplication({
+ appId: action.config.appId,
+ apiToken: action.secrets.apiToken,
+ apiUrl: action.config.apiUrl,
+ });
expect(result.current.isLoading).toBe(true);
});
@@ -135,14 +135,15 @@ describe('useGetApplication', () => {
await act(async () => {
const { result, waitForNextUpdate } = renderHook(() =>
useGetApplication({
- appId: action.config.appId,
- apiToken: action.secrets.apiToken,
- apiUrl: action.config.apiUrl,
toastNotifications: services.notifications.toasts,
})
);
await waitForNextUpdate();
- result.current.getApplication();
+ result.current.getApplication({
+ appId: action.config.appId,
+ apiToken: action.secrets.apiToken,
+ apiUrl: action.config.apiUrl,
+ });
expect(result.current).toEqual({
isLoading: false,
@@ -162,14 +163,15 @@ describe('useGetApplication', () => {
await act(async () => {
const { result, waitForNextUpdate } = renderHook(() =>
useGetApplication({
- appId: action.config.appId,
- apiToken: action.secrets.apiToken,
- apiUrl: action.config.apiUrl,
toastNotifications: services.notifications.toasts,
})
);
await waitForNextUpdate();
- result.current.getApplication();
+ result.current.getApplication({
+ appId: action.config.appId,
+ apiToken: action.secrets.apiToken,
+ apiUrl: action.config.apiUrl,
+ });
await waitForNextUpdate();
expect(services.notifications.toasts.addDanger).toHaveBeenCalledWith({
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.tsx
index fc35c6c26c945..15b449f10f5bf 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/swimlane/use_get_application.tsx
@@ -6,6 +6,7 @@
*/
import { useState, useCallback, useRef } from 'react';
+import { isEmpty } from 'lodash';
import { ToastsApi } from '@kbn/core/public';
import { getApplication as getApplicationApi } from './api';
import * as i18n from './translations';
@@ -22,58 +23,64 @@ interface Props {
}
export interface UseGetApplication {
- getApplication: () => Promise<{ fields?: SwimlaneFieldMappingConfig[] } | undefined>;
+ getApplication: (
+ args: Omit
+ ) => Promise<{ fields?: SwimlaneFieldMappingConfig[] } | undefined>;
isLoading: boolean;
}
export const useGetApplication = ({
toastNotifications,
- appId,
- apiToken,
- apiUrl,
-}: Props): UseGetApplication => {
+}: Pick): UseGetApplication => {
const [isLoading, setIsLoading] = useState(false);
const isCancelledRef = useRef(false);
const abortCtrlRef = useRef(new AbortController());
- const getApplication = useCallback(async () => {
- try {
- isCancelledRef.current = false;
- abortCtrlRef.current.abort();
- abortCtrlRef.current = new AbortController();
- setIsLoading(true);
+ const getApplication = useCallback(
+ async ({ appId, apiToken, apiUrl }: Omit) => {
+ try {
+ if (isEmpty(appId) || isEmpty(apiToken) || isEmpty(apiUrl)) {
+ return;
+ }
- const data = await getApplicationApi({
- signal: abortCtrlRef.current.signal,
- appId,
- apiToken,
- url: apiUrl,
- });
+ isCancelledRef.current = false;
+ abortCtrlRef.current.abort();
+ abortCtrlRef.current = new AbortController();
+ setIsLoading(true);
- if (!isCancelledRef.current) {
- setIsLoading(false);
- if (!data.fields) {
- // If the response was malformed and fields doesn't exist, show an error toast
- toastNotifications.addDanger({
- title: i18n.SW_GET_APPLICATION_API_ERROR(appId),
- text: i18n.SW_GET_APPLICATION_API_NO_FIELDS_ERROR,
- });
- return;
+ const data = await getApplicationApi({
+ signal: abortCtrlRef.current.signal,
+ appId,
+ apiToken,
+ url: apiUrl,
+ });
+
+ if (!isCancelledRef.current) {
+ setIsLoading(false);
+ if (!data.fields) {
+ // If the response was malformed and fields doesn't exist, show an error toast
+ toastNotifications.addDanger({
+ title: i18n.SW_GET_APPLICATION_API_ERROR(appId),
+ text: i18n.SW_GET_APPLICATION_API_NO_FIELDS_ERROR,
+ });
+ return;
+ }
+ return data;
}
- return data;
- }
- } catch (error) {
- if (!isCancelledRef.current) {
- if (error.name !== 'AbortError') {
- toastNotifications.addDanger({
- title: i18n.SW_GET_APPLICATION_API_ERROR(appId),
- text: error.message,
- });
+ } catch (error) {
+ if (!isCancelledRef.current) {
+ if (error.name !== 'AbortError') {
+ toastNotifications.addDanger({
+ title: i18n.SW_GET_APPLICATION_API_ERROR(appId),
+ text: error.message,
+ });
+ }
+ setIsLoading(false);
}
- setIsLoading(false);
}
- }
- }, [apiToken, apiUrl, appId, toastNotifications]);
+ },
+ [toastNotifications]
+ );
return {
isLoading,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.test.tsx
index 8590433f39cc0..e08853abf9c12 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.test.tsx
@@ -8,7 +8,6 @@
import { TypeRegistry } from '../../../type_registry';
import { registerBuiltInActionTypes } from '..';
import { ActionTypeModel } from '../../../../types';
-import { TeamsActionConnector } from '../types';
import { registrationServicesMock } from '../../../../mocks';
const ACTION_TYPE_ID = '.teams';
@@ -29,98 +28,6 @@ describe('actionTypeRegistry.get() works', () => {
});
});
-describe('teams connector validation', () => {
- test('connector validation succeeds when connector config is valid', async () => {
- const actionConnector = {
- secrets: {
- webhookUrl: 'https:\\test',
- },
- id: 'test',
- actionTypeId: '.teams',
- name: 'team',
- config: {},
- } as TeamsActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {},
- },
- secrets: {
- errors: {
- webhookUrl: [],
- },
- },
- });
- });
-
- test('connector validation fails when connector config is not valid - empty webhook url', async () => {
- const actionConnector = {
- secrets: {},
- id: 'test',
- actionTypeId: '.teams',
- name: 'team',
- config: {},
- } as TeamsActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {},
- },
- secrets: {
- errors: {
- webhookUrl: ['Webhook URL is required.'],
- },
- },
- });
- });
-
- test('connector validation fails when connector config is not valid - invalid webhook url', async () => {
- const actionConnector = {
- secrets: {
- webhookUrl: 'h',
- },
- id: 'test',
- actionTypeId: '.teams',
- name: 'team',
- config: {},
- } as TeamsActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {},
- },
- secrets: {
- errors: {
- webhookUrl: ['Webhook URL is invalid.'],
- },
- },
- });
- });
-
- test('connector validation fails when connector config is not valid - invalid webhook url protocol', async () => {
- const actionConnector = {
- secrets: {
- webhookUrl: 'http://insecure',
- },
- id: 'test',
- actionTypeId: '.teams',
- name: 'team',
- config: {},
- } as TeamsActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {},
- },
- secrets: {
- errors: {
- webhookUrl: ['Webhook URL must start with https://.'],
- },
- },
- });
- });
-});
-
describe('teams action params validation', () => {
test('if action params validation succeeds when action params is valid', async () => {
const actionParams = {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.tsx
index c48b4f950855d..e9c286cdc1b56 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams.tsx
@@ -7,13 +7,8 @@
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
-import {
- ActionTypeModel,
- GenericValidationResult,
- ConnectorValidationResult,
-} from '../../../../types';
-import { TeamsActionParams, TeamsSecrets, TeamsActionConnector } from '../types';
-import { isValidUrl } from '../../../lib/value_validators';
+import { ActionTypeModel, GenericValidationResult } from '../../../../types';
+import { TeamsActionParams, TeamsSecrets } from '../types';
export function getActionType(): ActionTypeModel {
return {
@@ -31,25 +26,6 @@ export function getActionType(): ActionTypeModel> => {
- const translations = await import('./translations');
- const secretsErrors = {
- webhookUrl: new Array(),
- };
- const validationResult = { config: { errors: {} }, secrets: { errors: secretsErrors } };
- if (!action.secrets.webhookUrl) {
- secretsErrors.webhookUrl.push(translations.WEBHOOK_URL_REQUIRED);
- } else if (action.secrets.webhookUrl) {
- if (!isValidUrl(action.secrets.webhookUrl)) {
- secretsErrors.webhookUrl.push(translations.WEBHOOK_URL_INVALID);
- } else if (!isValidUrl(action.secrets.webhookUrl, 'https:')) {
- secretsErrors.webhookUrl.push(translations.WEBHOOK_URL_HTTP_INVALID);
- }
- }
- return validationResult;
- },
validateParams: async (
actionParams: TeamsActionParams
): Promise> => {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.test.tsx
index 2d2b6d5052bb8..a0a082d36a864 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.test.tsx
@@ -7,112 +7,126 @@
import React from 'react';
import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers';
-import { act } from '@testing-library/react';
-import { TeamsActionConnector } from '../types';
+import { act, render } from '@testing-library/react';
import TeamsActionFields from './teams_connectors';
+import { ConnectorFormTestProvider } from '../test_utils';
+import userEvent from '@testing-library/user-event';
jest.mock('../../../../common/lib/kibana');
describe('TeamsActionFields renders', () => {
test('all connector fields are rendered', async () => {
const actionConnector = {
secrets: {
- webhookUrl: 'https:\\test',
+ webhookUrl: 'https://test.com',
},
id: 'test',
actionTypeId: '.teams',
name: 'teams',
config: {},
- } as TeamsActionConnector;
+ isDeprecated: false,
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}} />
+
);
await act(async () => {
await nextTick();
wrapper.update();
});
+
expect(wrapper.find('[data-test-subj="teamsWebhookUrlInput"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="teamsWebhookUrlInput"]').first().prop('value')).toBe(
- 'https:\\test'
+ 'https://test.com'
);
});
- test('should display a message on create to remember credentials', () => {
- const actionConnector = {
- actionTypeId: '.teams',
- config: {},
- secrets: {},
- } as TeamsActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0);
- });
+ describe('Validation', () => {
+ const onSubmit = jest.fn();
- test('should display a message on edit to re-enter credentials', () => {
- const actionConnector = {
- secrets: {
- webhookUrl: 'http:\\test',
- },
- id: 'test',
- actionTypeId: '.teams',
- name: 'teams',
- config: {},
- } as TeamsActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0);
- });
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
- test('should display a message for missing secrets after import', () => {
- const actionConnector = {
- secrets: {
- webhookUrl: 'http:\\test',
- },
- id: 'test',
- actionTypeId: '.teams',
- isMissingSecrets: true,
- name: 'teams',
- config: {},
- } as TeamsActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0);
+ it('connector validation succeeds when connector config is valid', async () => {
+ const actionConnector = {
+ secrets: {
+ webhookUrl: 'https://test.com',
+ },
+ id: 'test',
+ actionTypeId: '.teams',
+ name: 'teams',
+ config: {},
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ secrets: {
+ webhookUrl: 'https://test.com',
+ },
+ id: 'test',
+ actionTypeId: '.teams',
+ name: 'teams',
+ isDeprecated: false,
+ },
+ isValid: true,
+ });
+ });
+
+ it('validates teh web hook url field correctly', async () => {
+ const actionConnector = {
+ secrets: {
+ webhookUrl: 'https://test.com',
+ },
+ id: 'test',
+ actionTypeId: '.teams',
+ name: 'teams',
+ config: {},
+ isDeprecated: false,
+ };
+
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ await userEvent.type(
+ getByTestId('teamsWebhookUrlInput'),
+ `{selectall}{backspace}no-valid`,
+ {
+ delay: 10,
+ }
+ );
+ });
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.tsx
index b6c41cb649dbd..34e2e02a0611c 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/teams_connectors.tsx
@@ -6,74 +6,54 @@
*/
import React from 'react';
-import { EuiFieldText, EuiFormRow, EuiLink } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
+import { EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
+import { FieldConfig, UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components';
+import { DocLinksStart } from '@kbn/core/public';
import { ActionConnectorFieldsProps } from '../../../../types';
-import { TeamsActionConnector } from '../types';
import { useKibana } from '../../../../common/lib/kibana';
-import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label';
+import * as i18n from './translations';
-const TeamsActionFields: React.FunctionComponent<
- ActionConnectorFieldsProps
-> = ({ action, editActionSecrets, errors, readOnly }) => {
- const { webhookUrl } = action.secrets;
- const { docLinks } = useKibana().services;
+const { urlField } = fieldValidators;
+
+const getWebhookUrlConfig = (docLinks: DocLinksStart): FieldConfig => ({
+ label: i18n.WEBHOOK_URL_LABEL,
+ helpText: (
+
+
+
+ ),
+ validations: [
+ {
+ validator: urlField(i18n.WEBHOOK_URL_INVALID),
+ },
+ ],
+});
- const isWebhookUrlInvalid: boolean =
- errors.webhookUrl !== undefined && errors.webhookUrl.length > 0 && webhookUrl !== undefined;
+const TeamsActionFields: React.FunctionComponent = ({
+ readOnly,
+ isEdit,
+}) => {
+ const { docLinks } = useKibana().services;
return (
- <>
-
-
-
- }
- error={errors.webhookUrl}
- isInvalid={isWebhookUrlInvalid}
- label={i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.webhookUrlTextFieldLabel',
- {
- defaultMessage: 'Webhook URL',
- }
- )}
- >
- <>
- {getEncryptedFieldNotifyLabel(
- !action.id,
- 1,
- action.isMissingSecrets ?? false,
- i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.reenterValueLabel',
- { defaultMessage: 'This URL is encrypted. Please reenter a value for this field.' }
- )
- )}
- {
- editActionSecrets('webhookUrl', e.target.value);
- }}
- onBlur={() => {
- if (!webhookUrl) {
- editActionSecrets('webhookUrl', '');
- }
- }}
- />
- >
-
- >
+
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/translations.ts
index 790a3b3bac32f..2bf4cae881f7b 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/translations.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/teams/translations.ts
@@ -7,10 +7,10 @@
import { i18n } from '@kbn/i18n';
-export const WEBHOOK_URL_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requiredWebhookUrlText',
+export const WEBHOOK_URL_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.webhookUrlTextLabel',
{
- defaultMessage: 'Webhook URL is required.',
+ defaultMessage: 'Webhook URL',
}
);
@@ -21,13 +21,6 @@ export const WEBHOOK_URL_INVALID = i18n.translate(
}
);
-export const WEBHOOK_URL_HTTP_INVALID = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requireHttpsWebhookUrlText',
- {
- defaultMessage: 'Webhook URL must start with https://.',
- }
-);
-
export const MESSAGE_REQUIRED = i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.teamsAction.error.requiredMessageText',
{
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/test_utils.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/test_utils.tsx
new file mode 100644
index 0000000000000..c1c5eaefaa42c
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/test_utils.tsx
@@ -0,0 +1,127 @@
+/*
+ * 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, { useCallback } from 'react';
+import { ReactWrapper } from 'enzyme';
+import { of } from 'rxjs';
+import { I18nProvider } from '@kbn/i18n-react';
+import { EuiButton } from '@elastic/eui';
+import { Form, useForm, FormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { act } from 'react-dom/test-utils';
+import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
+import { render as reactRender, RenderOptions, RenderResult } from '@testing-library/react';
+import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
+
+import { ConnectorServices } from '../../../types';
+import { TriggersAndActionsUiServices } from '../../..';
+import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock';
+import { ConnectorFormSchema } from '../../sections/action_connector_form/types';
+import { ConnectorFormFieldsGlobal } from '../../sections/action_connector_form/connector_form_fields_global';
+import { ConnectorProvider } from '../../context/connector_context';
+
+interface FormTestProviderProps {
+ children: React.ReactNode;
+ defaultValue?: Record;
+ onSubmit?: ({ data, isValid }: { data: FormData; isValid: boolean }) => Promise;
+ connectorServices?: ConnectorServices;
+}
+
+type ConnectorFormTestProviderProps = Omit & {
+ connector: ConnectorFormSchema;
+};
+
+const ConnectorFormTestProviderComponent: React.FC = ({
+ children,
+ connector,
+ onSubmit,
+ connectorServices,
+}) => {
+ return (
+
+
+ {children}
+
+ );
+};
+
+ConnectorFormTestProviderComponent.displayName = 'ConnectorFormTestProvider';
+export const ConnectorFormTestProvider = React.memo(ConnectorFormTestProviderComponent);
+
+const FormTestProviderComponent: React.FC = ({
+ children,
+ defaultValue,
+ onSubmit,
+ connectorServices = { validateEmailAddresses: jest.fn() },
+}) => {
+ const { form } = useForm({ defaultValue });
+ const { submit } = form;
+
+ const onClick = useCallback(async () => {
+ const res = await submit();
+ if (onSubmit) {
+ onSubmit(res);
+ }
+ }, [onSubmit, submit]);
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+FormTestProviderComponent.displayName = 'FormTestProvider';
+export const FormTestProvider = React.memo(FormTestProviderComponent);
+
+export async function waitForComponentToPaint(wrapper: ReactWrapper
, amount = 0) {
+ await act(async () => {
+ await new Promise((resolve) => setTimeout(resolve, amount));
+ wrapper.update();
+ });
+}
+
+export const waitForComponentToUpdate = async () =>
+ await act(async () => {
+ return Promise.resolve();
+ });
+
+type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult;
+export interface AppMockRenderer {
+ render: UiRender;
+ coreStart: TriggersAndActionsUiServices;
+}
+
+export const createAppMockRenderer = (): AppMockRenderer => {
+ const services = createStartServicesMock();
+ const theme$ = of({ darkMode: false });
+
+ const AppWrapper: React.FC<{ children: React.ReactElement }> = ({ children }) => (
+
+
+ {children}
+
+
+ );
+ AppWrapper.displayName = 'AppWrapper';
+ const render: UiRender = (ui, options) => {
+ return reactRender(ui, {
+ wrapper: AppWrapper,
+ ...options,
+ });
+ };
+ return {
+ coreStart: services,
+ render,
+ };
+};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/translations.ts
index 3550121e81694..27a7d08b8c767 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/translations.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/translations.ts
@@ -7,52 +7,94 @@
import { i18n } from '@kbn/i18n';
-export const URL_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.requiredUrlText',
+export const METHOD_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.methodTextFieldLabel',
{
- defaultMessage: 'URL is required.',
+ defaultMessage: 'Method',
}
);
-export const URL_INVALID = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.invalidUrlTextField',
+export const HAS_AUTH_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.hasAuthSwitchLabel',
{
- defaultMessage: 'URL is invalid.',
+ defaultMessage: 'Require authentication for this webhook',
}
);
-export const METHOD_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText',
+export const URL_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.urlTextFieldLabel',
{
- defaultMessage: 'Method is required.',
+ defaultMessage: 'URL',
}
);
-export const USERNAME_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthUserNameText',
+export const USERNAME_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.userTextFieldLabel',
{
- defaultMessage: 'Username is required.',
+ defaultMessage: 'Username',
+ }
+);
+
+export const PASSWORD_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.passwordTextFieldLabel',
+ {
+ defaultMessage: 'Password',
+ }
+);
+
+export const ADD_HEADERS_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.viewHeadersSwitch',
+ {
+ defaultMessage: 'Add HTTP header',
+ }
+);
+
+export const HEADER_KEY_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.headerKeyTextFieldLabel',
+ {
+ defaultMessage: 'Key',
}
);
-export const PASSWORD_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthPasswordText',
+export const REMOVE_ITEM_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.removeHeaderIconLabel',
{
- defaultMessage: 'Password is required.',
+ defaultMessage: 'Key',
}
);
-export const PASSWORD_REQUIRED_FOR_USER = i18n.translate(
- 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredPasswordText',
+export const ADD_HEADER_BTN = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.addHeaderButtonLabel',
{
- defaultMessage: 'Password is required when username is used.',
+ defaultMessage: 'Add header',
}
);
-export const USERNAME_REQUIRED_FOR_PASSWORD = i18n.translate(
- 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredUserText',
+export const HEADER_VALUE_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.headerValueTextFieldLabel',
{
- defaultMessage: 'Username is required when password is used.',
+ defaultMessage: 'Value',
+ }
+);
+
+export const URL_INVALID = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.error.invalidUrlTextField',
+ {
+ defaultMessage: 'URL is invalid.',
+ }
+);
+
+export const METHOD_REQUIRED = i18n.translate(
+ 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredMethodText',
+ {
+ defaultMessage: 'Method is required.',
+ }
+);
+
+export const USERNAME_REQUIRED = i18n.translate(
+ 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredAuthUserNameText',
+ {
+ defaultMessage: 'Username is required.',
}
);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx
index 771786157ed4c..dfc3aae39586d 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.test.tsx
@@ -8,7 +8,6 @@
import { TypeRegistry } from '../../../type_registry';
import { registerBuiltInActionTypes } from '..';
import { ActionTypeModel } from '../../../../types';
-import { WebhookActionConnector } from '../types';
import { registrationServicesMock } from '../../../../mocks';
const ACTION_TYPE_ID = '.webhook';
@@ -30,140 +29,6 @@ describe('actionTypeRegistry.get() works', () => {
});
});
-describe('webhook connector validation', () => {
- test('connector validation succeeds when hasAuth is true and connector config is valid', async () => {
- const actionConnector = {
- secrets: {
- user: 'user',
- password: 'pass',
- },
- id: 'test',
- actionTypeId: '.webhook',
- name: 'webhook',
- isPreconfigured: false,
- isDeprecated: false,
- config: {
- method: 'PUT',
- url: 'http://test.com',
- headers: { 'content-type': 'text' },
- hasAuth: true,
- },
- } as WebhookActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- url: [],
- method: [],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: [],
- },
- },
- });
- });
-
- test('connector validation succeeds when hasAuth is false and connector config is valid', async () => {
- const actionConnector = {
- secrets: {
- user: '',
- password: '',
- },
- id: 'test',
- actionTypeId: '.webhook',
- name: 'webhook',
- isPreconfigured: false,
- isDeprecated: false,
- config: {
- method: 'PUT',
- url: 'http://test.com',
- headers: { 'content-type': 'text' },
- hasAuth: false,
- },
- } as WebhookActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- url: [],
- method: [],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: [],
- },
- },
- });
- });
-
- test('connector validation fails when connector config is not valid', async () => {
- const actionConnector = {
- secrets: {
- user: 'user',
- },
- id: 'test',
- actionTypeId: '.webhook',
- name: 'webhook',
- config: {
- method: 'PUT',
- hasAuth: true,
- },
- } as WebhookActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- url: ['URL is required.'],
- method: [],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: ['Password is required when username is used.'],
- },
- },
- });
- });
-
- test('connector validation fails when url in config is not valid', async () => {
- const actionConnector = {
- secrets: {
- user: 'user',
- password: 'pass',
- },
- id: 'test',
- actionTypeId: '.webhook',
- name: 'webhook',
- config: {
- method: 'PUT',
- url: 'invalid.url',
- hasAuth: true,
- },
- } as WebhookActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- url: ['URL is invalid.'],
- method: [],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: [],
- },
- },
- });
- });
-});
-
describe('webhook action params validation', () => {
test('action params validation succeeds when action params is valid', async () => {
const actionParams = {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx
index a668f531a6d4c..5ee08cc027003 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx
@@ -7,18 +7,8 @@
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
-import {
- ActionTypeModel,
- GenericValidationResult,
- ConnectorValidationResult,
-} from '../../../../types';
-import {
- WebhookActionParams,
- WebhookConfig,
- WebhookSecrets,
- WebhookActionConnector,
-} from '../types';
-import { isValidUrl } from '../../../lib/value_validators';
+import { ActionTypeModel, GenericValidationResult } from '../../../../types';
+import { WebhookActionParams, WebhookConfig, WebhookSecrets } from '../types';
export function getActionType(): ActionTypeModel<
WebhookConfig,
@@ -40,47 +30,6 @@ export function getActionType(): ActionTypeModel<
defaultMessage: 'Webhook data',
}
),
- validateConnector: async (
- action: WebhookActionConnector
- ): Promise<
- ConnectorValidationResult, WebhookSecrets>
- > => {
- const translations = await import('./translations');
- const configErrors = {
- url: new Array(),
- method: new Array(),
- };
- const secretsErrors = {
- user: new Array(),
- password: new Array(),
- };
- const validationResult = {
- config: { errors: configErrors },
- secrets: { errors: secretsErrors },
- };
- if (!action.config.url) {
- configErrors.url.push(translations.URL_REQUIRED);
- }
- if (action.config.url && !isValidUrl(action.config.url)) {
- configErrors.url = [...configErrors.url, translations.URL_INVALID];
- }
- if (!action.config.method) {
- configErrors.method.push(translations.METHOD_REQUIRED);
- }
- if (action.config.hasAuth && !action.secrets.user && !action.secrets.password) {
- secretsErrors.user.push(translations.USERNAME_REQUIRED);
- }
- if (action.config.hasAuth && !action.secrets.user && !action.secrets.password) {
- secretsErrors.password.push(translations.PASSWORD_REQUIRED);
- }
- if (action.secrets.user && !action.secrets.password) {
- secretsErrors.password.push(translations.PASSWORD_REQUIRED_FOR_USER);
- }
- if (!action.secrets.user && action.secrets.password) {
- secretsErrors.user.push(translations.USERNAME_REQUIRED_FOR_PASSWORD);
- }
- return validationResult;
- },
validateParams: async (
actionParams: WebhookActionParams
): Promise> => {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx
index 533d6d9a9b605..d3b933e9e2dc4 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx
@@ -7,41 +7,42 @@
import React from 'react';
import { mountWithIntl } from '@kbn/test-jest-helpers';
-import { WebhookActionConnector } from '../types';
import WebhookActionConnectorFields from './webhook_connectors';
+import { ConnectorFormTestProvider, waitForComponentToUpdate } from '../test_utils';
+import { act, render } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
describe('WebhookActionConnectorFields renders', () => {
- test('all connector fields is rendered', () => {
+ test('all connector fields is rendered', async () => {
const actionConnector = {
- secrets: {
- user: 'user',
- password: 'pass',
- },
- id: 'test',
actionTypeId: '.webhook',
- isPreconfigured: false,
- isDeprecated: false,
name: 'webhook',
config: {
method: 'PUT',
- url: 'http:\\test',
- headers: { 'content-type': 'text' },
+ url: 'https://test.com',
+ headers: [{ key: 'content-type', value: 'text' }],
hasAuth: true,
},
- } as WebhookActionConnector;
+ secrets: {
+ user: 'user',
+ password: 'pass',
+ },
+ isDeprecated: false,
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
+
+ await waitForComponentToUpdate();
+
expect(wrapper.find('[data-test-subj="webhookViewHeadersSwitch"]').length > 0).toBeTruthy();
- expect(wrapper.find('[data-test-subj="webhookHeaderText"]').length > 0).toBeTruthy();
wrapper.find('[data-test-subj="webhookViewHeadersSwitch"]').first().simulate('click');
expect(wrapper.find('[data-test-subj="webhookMethodSelect"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="webhookUrlText"]').length > 0).toBeTruthy();
@@ -49,94 +50,216 @@ describe('WebhookActionConnectorFields renders', () => {
expect(wrapper.find('[data-test-subj="webhookPasswordInput"]').length > 0).toBeTruthy();
});
- test('should display a message on create to remember credentials', () => {
- const actionConnector = {
- secrets: {},
- actionTypeId: '.webhook',
- isPreconfigured: false,
- isDeprecated: false,
- config: {
- hasAuth: true,
- },
- } as WebhookActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0);
- });
-
- test('should display a message on edit to re-enter credentials', () => {
+ describe('Validation', () => {
+ const onSubmit = jest.fn();
const actionConnector = {
- secrets: {
- user: 'user',
- password: 'pass',
- },
- id: 'test',
actionTypeId: '.webhook',
- isPreconfigured: false,
- isDeprecated: false,
name: 'webhook',
config: {
method: 'PUT',
- url: 'http:\\test',
- headers: { 'content-type': 'text' },
+ url: 'https://test.com',
+ headers: [{ key: 'content-type', value: 'text' }],
hasAuth: true,
},
- } as WebhookActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0);
- });
-
- test('should display a message for missing secrets after import', () => {
- const actionConnector = {
secrets: {
user: 'user',
password: 'pass',
},
- id: 'test',
- actionTypeId: '.webhook',
- isPreconfigured: false,
isDeprecated: false,
- isMissingSecrets: true,
- name: 'webhook',
- config: {
- method: 'PUT',
- url: 'http:\\test',
- headers: { 'content-type': 'text' },
- hasAuth: true,
- },
- } as WebhookActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0);
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ const tests: Array<[string, string]> = [
+ ['webhookUrlText', 'not-valid'],
+ ['webhookUserInput', ''],
+ ['webhookPasswordInput', ''],
+ ];
+
+ it('connector validation succeeds when connector config is valid', async () => {
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ actionTypeId: '.webhook',
+ name: 'webhook',
+ config: {
+ method: 'PUT',
+ url: 'https://test.com',
+ headers: [{ key: 'content-type', value: 'text' }],
+ hasAuth: true,
+ },
+ secrets: {
+ user: 'user',
+ password: 'pass',
+ },
+ __internal__: {
+ hasHeaders: true,
+ },
+ isDeprecated: false,
+ },
+ isValid: true,
+ });
+ });
+
+ it('connector validation succeeds when auth=false', async () => {
+ const connector = {
+ ...actionConnector,
+ config: {
+ ...actionConnector.config,
+ hasAuth: false,
+ },
+ };
+
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ actionTypeId: '.webhook',
+ name: 'webhook',
+ config: {
+ method: 'PUT',
+ url: 'https://test.com',
+ headers: [{ key: 'content-type', value: 'text' }],
+ hasAuth: false,
+ },
+ __internal__: {
+ hasHeaders: true,
+ },
+ isDeprecated: false,
+ },
+ isValid: true,
+ });
+ });
+
+ it('connector validation succeeds without headers', async () => {
+ const connector = {
+ ...actionConnector,
+ config: {
+ method: 'PUT',
+ url: 'https://test.com',
+ hasAuth: true,
+ },
+ };
+
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ actionTypeId: '.webhook',
+ name: 'webhook',
+ config: {
+ method: 'PUT',
+ url: 'https://test.com',
+ hasAuth: true,
+ },
+ secrets: {
+ user: 'user',
+ password: 'pass',
+ },
+ __internal__: {
+ hasHeaders: false,
+ },
+ isDeprecated: false,
+ },
+ isValid: true,
+ });
+ });
+
+ it('validates correctly if the method is empty', async () => {
+ const connector = {
+ ...actionConnector,
+ config: {
+ ...actionConnector.config,
+ method: '',
+ },
+ };
+
+ const res = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
+
+ it.each(tests)('validates correctly %p', async (field, value) => {
+ const connector = {
+ ...actionConnector,
+ config: {
+ ...actionConnector.config,
+ headers: [],
+ },
+ };
+
+ const res = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx
index fbd3bdbef2e71..7981f8fa4fa78 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx
@@ -5,285 +5,92 @@
* 2.0.
*/
-import React, { useEffect, useState } from 'react';
+import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import {
- EuiFieldPassword,
- EuiFieldText,
- EuiFormRow,
- EuiSelect,
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
EuiButtonIcon,
- EuiDescriptionList,
- EuiDescriptionListDescription,
- EuiDescriptionListTitle,
EuiTitle,
- EuiSwitch,
EuiButtonEmpty,
} from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
+import {
+ UseArray,
+ UseField,
+ useFormContext,
+ useFormData,
+} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import {
+ Field,
+ SelectField,
+ TextField,
+ ToggleField,
+} from '@kbn/es-ui-shared-plugin/static/forms/components';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
import { ActionConnectorFieldsProps } from '../../../../types';
-import { WebhookActionConnector } from '../types';
-import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label';
+import * as i18n from './translations';
+import { PasswordField } from '../../password_field';
const HTTP_VERBS = ['post', 'put'];
-
-const WebhookActionConnectorFields: React.FunctionComponent<
- ActionConnectorFieldsProps
-> = ({ action, editActionConfig, editActionSecrets, errors, readOnly }) => {
- const { user, password } = action.secrets;
- const { method, url, headers, hasAuth } = action.config;
-
- const [httpHeaderKey, setHttpHeaderKey] = useState('');
- const [httpHeaderValue, setHttpHeaderValue] = useState('');
- const [hasHeaders, setHasHeaders] = useState(false);
-
- useEffect(() => {
- if (!action.id) {
- editActionConfig('hasAuth', true);
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
- if (!method) {
- editActionConfig('method', 'post'); // set method to POST by default
- }
-
- const headerErrors = {
- keyHeader: new Array(),
- valueHeader: new Array(),
- };
- if (!httpHeaderKey && httpHeaderValue) {
- headerErrors.keyHeader.push(
- i18n.translate(
- 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredHeaderKeyText',
- {
- defaultMessage: 'Key is required.',
- }
- )
- );
- }
- if (httpHeaderKey && !httpHeaderValue) {
- headerErrors.valueHeader.push(
- i18n.translate(
- 'xpack.triggersActionsUI.sections.addAction.webhookAction.error.requiredHeaderValueText',
- {
- defaultMessage: 'Value is required.',
- }
- )
- );
- }
- const hasHeaderErrors: boolean =
- (headerErrors.keyHeader !== undefined &&
- headerErrors.valueHeader !== undefined &&
- headerErrors.keyHeader.length > 0) ||
- headerErrors.valueHeader.length > 0;
-
- function addHeader() {
- if (headers && !!Object.keys(headers).find((key) => key === httpHeaderKey)) {
- return;
- }
- const updatedHeaders = headers
- ? { ...headers, [httpHeaderKey]: httpHeaderValue }
- : { [httpHeaderKey]: httpHeaderValue };
- editActionConfig('headers', updatedHeaders);
- setHttpHeaderKey('');
- setHttpHeaderValue('');
- }
-
- function viewHeaders() {
- setHasHeaders(!hasHeaders);
- if (!hasHeaders && !headers) {
- editActionConfig('headers', {});
- }
- }
-
- function removeHeader(keyToRemove: string) {
- const updatedHeaders = Object.keys(headers)
- .filter((key) => key !== keyToRemove)
- .reduce((headerToRemove: Record, key: string) => {
- headerToRemove[key] = headers[key];
- return headerToRemove;
- }, {});
- editActionConfig('headers', updatedHeaders);
- }
-
- let headerControl;
- if (hasHeaders) {
- headerControl = (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- addHeader()}
- >
-
-
-
-
-
- >
- );
- }
-
- const headersList = Object.keys(headers || {}).map((key: string) => {
- return (
-
-
- removeHeader(key)}
- />
-
-
-
- {key}
- {headers[key]}
-
-
-
- );
+const { emptyField, urlField } = fieldValidators;
+
+const WebhookActionConnectorFields: React.FunctionComponent = ({
+ readOnly,
+}) => {
+ const { getFieldDefaultValue } = useFormContext();
+ const [{ config, __internal__ }] = useFormData({
+ watch: ['config.hasAuth', '__internal__.hasHeaders'],
});
- const isUrlInvalid: boolean =
- errors.url !== undefined && errors.url.length > 0 && url !== undefined;
- const isPasswordInvalid: boolean =
- password !== undefined && errors.password !== undefined && errors.password.length > 0;
- const isUserInvalid: boolean =
- user !== undefined && errors.user !== undefined && errors.user.length > 0;
+ const hasHeadersDefaultValue = !!getFieldDefaultValue('config.headers');
+
+ const hasAuth = config == null ? true : config.hasAuth;
+ const hasHeaders = __internal__ != null ? __internal__.hasHeaders : false;
return (
<>
-
- ({ text: verb.toUpperCase(), value: verb }))}
- onChange={(e) => {
- editActionConfig('method', e.target.value);
- }}
- />
-
+ ({ text: verb.toUpperCase(), value: verb })),
+ fullWidth: true,
+ readOnly,
+ },
+ }}
+ />
-
- {
- editActionConfig('url', e.target.value);
- }}
- onBlur={() => {
- if (!url) {
- editActionConfig('url', '');
- }
- }}
- />
-
+
@@ -298,140 +105,115 @@ const WebhookActionConnectorFields: React.FunctionComponent<
- {
- editActionConfig('hasAuth', e.target.checked);
- if (!e.target.checked) {
- editActionSecrets('user', null);
- editActionSecrets('password', null);
- }
+
{hasAuth ? (
- <>
- {getEncryptedFieldNotifyLabel(
- !action.id,
- 2,
- action.isMissingSecrets ?? false,
- i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.reenterValuesLabel',
- {
- defaultMessage:
- 'Username and password are encrypted. Please reenter values for these fields.',
- }
- )
- )}
-
-
-
+
+
- {
- editActionSecrets('user', e.target.value);
- }}
- onBlur={() => {
- if (!user) {
- editActionSecrets('user', '');
- }
- }}
- />
-
-
-
-
- {
- editActionSecrets('password', e.target.value);
- }}
- onBlur={() => {
- if (!password) {
- editActionSecrets('password', '');
- }
- }}
- />
-
-
-
- >
+ validator: emptyField(i18n.USERNAME_REQUIRED),
+ },
+ ],
+ }}
+ component={Field}
+ componentProps={{
+ euiFieldProps: { readOnly, 'data-test-subj': 'webhookUserInput', fullWidth: true },
+ }}
+ />
+
+
+
+
+
) : null}
- viewHeaders()}
+
-
-
- {Object.keys(headers || {}).length > 0 ? (
- <>
-
-
-
-
-
-
-
- {headersList}
- >
- ) : null}
-
- {hasHeaders && headerControl}
-
-
+ {hasHeaders ? (
+
+ {({ items, addItem, removeItem }) => {
+ return (
+ <>
+ {items.map((item) => (
+
+
+
+
+
+
+
+
+ removeItem(item.id)}
+ iconType="minusInCircle"
+ aria-label={i18n.REMOVE_ITEM_LABEL}
+ style={{ marginTop: '28px' }}
+ />
+
+
+ ))}
+
+
+ {i18n.ADD_HEADER_BTN}
+
+
+ >
+ );
+ }}
+
+ ) : null}
>
);
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/translations.ts
index 51626b6a31ea6..d883b4d98c81b 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/translations.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/translations.ts
@@ -7,44 +7,72 @@
import { i18n } from '@kbn/i18n';
-export const URL_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.requiredUrlText',
+export const BASIC_AUTH_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.connectorSettingsLabel',
{
- defaultMessage: 'URL is required.',
+ defaultMessage: 'Select the authentication method used when setting up the xMatters trigger.',
}
);
-export const URL_INVALID = i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.invalidUrlTextField',
+export const BASIC_AUTH_BUTTON_GROUP_LEGEND = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.basicAuthButtonGroupLegend',
{
- defaultMessage: 'URL is invalid.',
+ defaultMessage: 'Basic Authentication',
+ }
+);
+
+export const URL_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.urlLabel',
+ {
+ defaultMessage: 'Initiation URL',
+ }
+);
+
+export const USERNAME_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.userTextFieldLabel',
+ {
+ defaultMessage: 'Username',
}
);
-export const USERNAME_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredAuthUserNameText',
+export const PASSWORD_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.passwordTextFieldLabel',
{
- defaultMessage: 'Username is required.',
+ defaultMessage: 'Password',
}
);
-export const PASSWORD_REQUIRED = i18n.translate(
- 'xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredAuthPasswordText',
+export const BASIC_AUTH_BUTTON_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.basicAuthLabel',
{
- defaultMessage: 'Password is required.',
+ defaultMessage: 'Basic Authentication',
}
);
-export const PASSWORD_REQUIRED_FOR_USER = i18n.translate(
- 'xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredPasswordText',
+export const URL_AUTH_BUTTON_LABEL = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.urlAuthLabel',
{
- defaultMessage: 'Password is required when username is used.',
+ defaultMessage: 'URL Authentication',
+ }
+);
+
+export const URL_REQUIRED = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.requiredUrlText',
+ {
+ defaultMessage: 'URL is required.',
+ }
+);
+
+export const URL_INVALID = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.invalidUrlTextField',
+ {
+ defaultMessage: 'URL is invalid.',
}
);
-export const USERNAME_REQUIRED_FOR_PASSWORD = i18n.translate(
- 'xpack.triggersActionsUI.sections.addAction.xmattersAction.error.requiredUserText',
+export const USERNAME_INVALID = i18n.translate(
+ 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.error.invalidUsernameTextField',
{
- defaultMessage: 'Username is required when password is used.',
+ defaultMessage: 'Username is invalid.',
}
);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.test.tsx
index 7e9dbc4cf885a..980fa90caf4bb 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.test.tsx
@@ -8,7 +8,6 @@
import { TypeRegistry } from '../../../type_registry';
import { registerBuiltInActionTypes } from '..';
import { ActionTypeModel } from '../../../../types';
-import { XmattersActionConnector } from '../types';
import { registrationServicesMock } from '../../../../mocks';
const ACTION_TYPE_ID = '.xmatters';
@@ -30,138 +29,6 @@ describe('actionTypeRegistry.get() works', () => {
});
});
-describe('xmatters connector validation', () => {
- test('connector validation succeeds when usesBasic is true and connector config is valid', async () => {
- const actionConnector = {
- secrets: {
- user: 'user',
- password: 'pass',
- },
- id: 'test',
- actionTypeId: '.xmatters',
- name: 'xmatters',
- isPreconfigured: false,
- isDeprecated: false,
- config: {
- configUrl: 'http://test.com',
- usesBasic: true,
- },
- } as XmattersActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- configUrl: [],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: [],
- secretsUrl: [],
- },
- },
- });
- });
-
- test('connector validation succeeds when usesBasic is false and connector config is valid', async () => {
- const actionConnector = {
- secrets: {
- user: '',
- password: '',
- secretsUrl: 'https://test.com?apiKey=someKey',
- },
- id: 'test',
- actionTypeId: '.xmatters',
- name: 'xmatters',
- isPreconfigured: false,
- isDeprecated: false,
- config: {
- usesBasic: false,
- },
- } as XmattersActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- configUrl: [],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: [],
- secretsUrl: [],
- },
- },
- });
- });
-
- test('connector validation fails when connector config is not valid', async () => {
- const actionConnector = {
- secrets: {
- user: 'user',
- },
- id: 'test',
- actionTypeId: '.xmatters',
- name: 'xmatters',
- config: {
- usesBasic: true,
- },
- isPreconfigured: false,
- isDeprecated: false,
- } as XmattersActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- configUrl: ['URL is required.'],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: ['Password is required when username is used.'],
- secretsUrl: [],
- },
- },
- });
- });
-
- test('connector validation fails when url in config is not valid', async () => {
- const actionConnector = {
- secrets: {
- user: 'user',
- password: 'pass',
- },
- id: 'test',
- actionTypeId: '.xmatters',
- name: 'xmatters',
- config: {
- configUrl: 'invalid.url',
- usesBasic: true,
- },
- isPreconfigured: false,
- isDeprecated: false,
- } as XmattersActionConnector;
-
- expect(await actionTypeModel.validateConnector(actionConnector)).toEqual({
- config: {
- errors: {
- configUrl: ['URL is invalid.'],
- },
- },
- secrets: {
- errors: {
- user: [],
- password: [],
- secretsUrl: [],
- },
- },
- });
- });
-});
-
describe('xmatters action params validation', () => {
test('action params validation succeeds when action params is valid', async () => {
const actionParams = {
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.tsx
index d638056ba31d9..2ba8d78d90aec 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters.tsx
@@ -7,18 +7,8 @@
import { lazy } from 'react';
import { i18n } from '@kbn/i18n';
-import {
- ActionTypeModel,
- GenericValidationResult,
- ConnectorValidationResult,
-} from '../../../../types';
-import {
- XmattersActionParams,
- XmattersConfig,
- XmattersSecrets,
- XmattersActionConnector,
-} from '../types';
-import { isValidUrl } from '../../../lib/value_validators';
+import { ActionTypeModel, GenericValidationResult } from '../../../../types';
+import { XmattersActionParams, XmattersConfig, XmattersSecrets } from '../types';
export function getActionType(): ActionTypeModel<
XmattersConfig,
@@ -40,48 +30,6 @@ export function getActionType(): ActionTypeModel<
defaultMessage: 'xMatters data',
}
),
- validateConnector: async (
- action: XmattersActionConnector
- ): Promise, XmattersSecrets>> => {
- const translations = await import('./translations');
- const configErrors = {
- configUrl: new Array(),
- };
- const secretsErrors = {
- user: new Array(),
- password: new Array(),
- secretsUrl: new Array(),
- };
- const validationResult = {
- config: { errors: configErrors },
- secrets: { errors: secretsErrors },
- };
- // basic auth validation
- if (!action.config.configUrl && action.config.usesBasic) {
- configErrors.configUrl.push(translations.URL_REQUIRED);
- }
- if (action.config.usesBasic && !action.secrets.user && !action.secrets.password) {
- secretsErrors.user.push(translations.USERNAME_REQUIRED);
- secretsErrors.password.push(translations.PASSWORD_REQUIRED);
- }
- if (action.config.configUrl && !isValidUrl(action.config.configUrl)) {
- configErrors.configUrl = [...configErrors.configUrl, translations.URL_INVALID];
- }
- if (action.config.usesBasic && action.secrets.user && !action.secrets.password) {
- secretsErrors.password.push(translations.PASSWORD_REQUIRED_FOR_USER);
- }
- if (action.config.usesBasic && !action.secrets.user && action.secrets.password) {
- secretsErrors.user.push(translations.USERNAME_REQUIRED_FOR_PASSWORD);
- }
- // API Key auth validation
- if (!action.config.usesBasic && !action.secrets.secretsUrl) {
- secretsErrors.secretsUrl.push(translations.URL_REQUIRED);
- }
- if (action.secrets.secretsUrl && !isValidUrl(action.secrets.secretsUrl)) {
- secretsErrors.secretsUrl.push(translations.URL_INVALID);
- }
- return validationResult;
- },
validateParams: async (
actionParams: XmattersActionParams
): Promise<
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.test.tsx
index 7da6cfe0a4a37..cef4c2c2bef81 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.test.tsx
@@ -7,70 +7,72 @@
import React from 'react';
import { mountWithIntl } from '@kbn/test-jest-helpers';
-import { XmattersActionConnector } from '../types';
import XmattersActionConnectorFields from './xmatters_connectors';
+import { ConnectorFormTestProvider, waitForComponentToUpdate } from '../test_utils';
+import userEvent from '@testing-library/user-event';
+import { act } from 'react-dom/test-utils';
+import { render } from '@testing-library/react';
describe('XmattersActionConnectorFields renders', () => {
- test('all connector fields is rendered', () => {
+ test('all connector fields is rendered', async () => {
const actionConnector = {
- secrets: {
- user: 'user',
- password: 'pass',
- },
- id: 'test',
actionTypeId: '.xmatters',
- isPreconfigured: false,
- isDeprecated: false,
name: 'xmatters',
config: {
- configUrl: 'http:\\test',
+ configUrl: 'https://test.com',
usesBasic: true,
},
- } as XmattersActionConnector;
+ secrets: {
+ user: 'user',
+ password: 'pass',
+ },
+ isDeprecated: false,
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
- expect(wrapper.find('[data-test-subj="xmattersUrlText"]').length > 0).toBeTruthy();
+
+ await waitForComponentToUpdate();
+
+ expect(wrapper.find('[data-test-subj="config.configUrl"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="xmattersUserInput"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="xmattersPasswordInput"]').length > 0).toBeTruthy();
});
test('should show only basic auth info when basic selected', () => {
const actionConnector = {
- secrets: {
- user: 'user',
- password: 'pass',
- },
id: 'test',
actionTypeId: '.xmatters',
- isPreconfigured: false,
- isDeprecated: false,
name: 'xmatters',
config: {
- configUrl: 'http:\\test',
+ configUrl: 'https://test.com',
usesBasic: true,
},
- } as XmattersActionConnector;
+ secrets: {
+ user: 'user',
+ password: 'pass',
+ },
+ isDeprecated: false,
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
- expect(wrapper.find('[data-test-subj="xmattersUrlText"]').length > 0).toBeTruthy();
+
+ expect(wrapper.find('[data-test-subj="config.configUrl"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="xmattersUserInput"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="xmattersPasswordInput"]').length > 0).toBeTruthy();
});
@@ -78,7 +80,7 @@ describe('XmattersActionConnectorFields renders', () => {
test('should show only url auth info when url selected', () => {
const actionConnector = {
secrets: {
- secretsUrl: 'http:\\test',
+ secretsUrl: 'https://test.com',
},
id: 'test',
actionTypeId: '.xmatters',
@@ -88,136 +90,175 @@ describe('XmattersActionConnectorFields renders', () => {
config: {
usesBasic: false,
},
- } as XmattersActionConnector;
+ };
+
const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
+
+ {}}
+ />
+
);
- expect(wrapper.find('[data-test-subj="xmattersUrlText"]').length > 0).toBeTruthy();
+
+ expect(wrapper.find('[data-test-subj="secrets.secretsUrl"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="xmattersUserInput"]').length === 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="xmattersPasswordInput"]').length === 0).toBeTruthy();
});
- test('should display a message on create to remember credentials', () => {
- const actionConnector = {
- secrets: {},
- actionTypeId: '.xmatters',
- isPreconfigured: false,
- isDeprecated: false,
- config: {
- usesBasic: true,
- },
- } as XmattersActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toEqual(0);
- });
-
- test('should display a message on edit to re-enter credentials, Basic Auth', () => {
- const actionConnector = {
- secrets: {
- user: 'user',
- password: 'pass',
- },
- id: 'test',
+ describe('Validation', () => {
+ const onSubmit = jest.fn();
+ const basicAuthConnector = {
actionTypeId: '.xmatters',
- isPreconfigured: false,
- isDeprecated: false,
name: 'xmatters',
config: {
- configUrl: 'http:\\test',
+ configUrl: 'https://test.com',
usesBasic: true,
},
- } as XmattersActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0);
- });
-
- test('should display a message for missing secrets after import', () => {
- const actionConnector = {
secrets: {
user: 'user',
password: 'pass',
},
- id: 'test',
- actionTypeId: '.xmatters',
- isPreconfigured: false,
isDeprecated: false,
- isMissingSecrets: true,
- name: 'xmatters',
- config: {
- configUrl: 'http:\\test',
- usesBasic: true,
- },
- } as XmattersActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="missingSecretsMessage"]').length).toBeGreaterThan(0);
- });
+ };
- test('should display a message on edit to re-enter credentials, URL Auth', () => {
- const actionConnector = {
- secrets: {
- secretsUrl: 'http:\\test?apiKey=someKey',
- },
- id: 'test',
- actionTypeId: '.xmatters',
- isPreconfigured: false,
- isDeprecated: false,
- name: 'xmatters',
+ const urlAuthConnector = {
+ ...basicAuthConnector,
config: {
usesBasic: false,
},
- } as XmattersActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- expect(wrapper.find('[data-test-subj="reenterValuesMessage"]').length).toBeGreaterThan(0);
- expect(wrapper.find('[data-test-subj="rememberValuesMessage"]').length).toEqual(0);
+ secrets: {
+ secretsUrl: 'https://test.com',
+ },
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ const basicAuthTests: Array<[string, string]> = [
+ ['config.configUrl', 'not-valid'],
+ ['xmattersUserInput', ''],
+ ['xmattersPasswordInput', ''],
+ ];
+
+ const urlAuthTests: Array<[string, string]> = [['secrets.secretsUrl', 'not-valid']];
+
+ it('connector validation succeeds when connector config is valid and uses basic auth', async () => {
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ actionTypeId: '.xmatters',
+ name: 'xmatters',
+ config: {
+ configUrl: 'https://test.com',
+ usesBasic: true,
+ },
+ secrets: {
+ user: 'user',
+ password: 'pass',
+ },
+ __internal__: {
+ auth: 'Basic Authentication',
+ },
+ isDeprecated: false,
+ },
+ isValid: true,
+ });
+ });
+
+ it('connector validation succeeds when connector config is valid and uses url auth', async () => {
+ const { getByTestId } = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ userEvent.click(getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toBeCalledWith({
+ data: {
+ actionTypeId: '.xmatters',
+ name: 'xmatters',
+ config: {
+ usesBasic: false,
+ },
+ secrets: {
+ secretsUrl: 'https://test.com',
+ },
+ __internal__: { auth: 'URL Authentication' },
+ isDeprecated: false,
+ },
+ isValid: true,
+ });
+ });
+
+ it.each(basicAuthTests)('validates correctly %p', async (field, value) => {
+ const res = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
+
+ it.each(urlAuthTests)('validates correctly %p', async (field, value) => {
+ const res = render(
+
+ {}}
+ />
+
+ );
+
+ await act(async () => {
+ await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.tsx
index 20ade11303350..247a700876d14 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/xmatters/xmatters_connectors.tsx
@@ -5,78 +5,94 @@
* 2.0.
*/
-import React, { useEffect, useState } from 'react';
+import React, { useEffect } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
+import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
import {
- EuiFieldPassword,
- EuiFieldText,
- EuiFormRow,
- EuiFlexGroup,
- EuiFlexItem,
- EuiSpacer,
- EuiTitle,
- EuiButtonGroup,
-} from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
+ UseField,
+ useFormContext,
+ useFormData,
+} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { TextField } from '@kbn/es-ui-shared-plugin/static/forms/components';
import { ActionConnectorFieldsProps } from '../../../../types';
-import { XmattersActionConnector, XmattersAuthenticationType } from '../types';
-import { getEncryptedFieldNotifyLabel } from '../../get_encrypted_field_notify_label';
+import { XmattersAuthenticationType } from '../types';
+import { ButtonGroupField } from '../../button_group_field';
+import * as i18n from './translations';
+import { PasswordField } from '../../password_field';
+import { HiddenField } from '../../hidden_field';
-const XmattersActionConnectorFields: React.FunctionComponent<
- ActionConnectorFieldsProps
-> = ({ action, editActionConfig, editActionSecrets, errors, readOnly }) => {
- const { user, password, secretsUrl } = action.secrets;
- const { configUrl, usesBasic } = action.config;
+const { emptyField, urlField } = fieldValidators;
- useEffect(() => {
- if (!action.id) {
- editActionConfig('usesBasic', true);
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
+const isBasicAuth = (auth: { auth: string } | null | undefined) => {
+ if (auth == null) {
+ return true;
+ }
- const isUrlInvalid: boolean = usesBasic
- ? errors.configUrl !== undefined && errors.configUrl.length > 0 && configUrl !== undefined
- : errors.secretsUrl !== undefined && errors.secretsUrl.length > 0 && secretsUrl !== undefined;
- const isPasswordInvalid: boolean =
- password !== undefined && errors.password !== undefined && errors.password.length > 0;
- const isUserInvalid: boolean =
- user !== undefined && errors.user !== undefined && errors.user.length > 0;
+ return auth.auth === XmattersAuthenticationType.Basic ? true : false;
+};
- const authenticationButtons = [
- {
- id: XmattersAuthenticationType.Basic,
- label: i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.basicAuthLabel',
- {
- defaultMessage: 'Basic Authentication',
- }
- ),
- },
- {
- id: XmattersAuthenticationType.URL,
- label: i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.urlAuthLabel',
- {
- defaultMessage: 'URL Authentication',
- }
- ),
- },
- ];
+const authenticationButtons = [
+ {
+ id: XmattersAuthenticationType.Basic,
+ label: i18n.BASIC_AUTH_BUTTON_LABEL,
+ },
+ {
+ id: XmattersAuthenticationType.URL,
+ label: i18n.URL_AUTH_BUTTON_LABEL,
+ },
+];
- let initialState;
- if (typeof usesBasic === 'undefined') {
- initialState = XmattersAuthenticationType.Basic;
- } else {
- initialState = usesBasic ? XmattersAuthenticationType.Basic : XmattersAuthenticationType.URL;
- if (usesBasic) {
- editActionSecrets('secretsUrl', '');
- } else {
- editActionConfig('configUrl', '');
- }
- }
- const [selectedAuth, setSelectedAuth] = useState(initialState);
+const XmattersUrlField: React.FC<{ path: string; readOnly: boolean }> = ({ path, readOnly }) => {
+ return (
+
+ ),
+ validations: [
+ {
+ validator: urlField(i18n.URL_INVALID),
+ },
+ ],
+ }}
+ componentProps={{
+ euiFieldProps: { 'data-test-subj': path, readOnly },
+ }}
+ />
+ );
+};
+
+const XmattersActionConnectorFields: React.FunctionComponent = ({
+ readOnly,
+}) => {
+ const { setFieldValue, getFieldDefaultValue } = useFormContext();
+ const [{ config, __internal__ }] = useFormData({
+ watch: ['config.usesBasic', '__internal__.auth'],
+ });
+
+ const usesBasicDefaultValue =
+ getFieldDefaultValue('config.usesBasic') ?? true;
+
+ const selectedAuthDefaultValue = usesBasicDefaultValue
+ ? XmattersAuthenticationType.Basic
+ : XmattersAuthenticationType.URL;
+
+ const selectedAuth =
+ config != null && !config.usesBasic
+ ? XmattersAuthenticationType.URL
+ : XmattersAuthenticationType.Basic;
+
+ useEffect(() => {
+ setFieldValue('config.usesBasic', isBasicAuth(__internal__));
+ }, [__internal__, setFieldValue]);
return (
<>
@@ -89,92 +105,29 @@ const XmattersActionConnectorFields: React.FunctionComponent<
-
-
-
-
-
-
- {
- if (id === XmattersAuthenticationType.Basic) {
- setSelectedAuth(XmattersAuthenticationType.Basic);
- editActionConfig('usesBasic', true);
- editActionSecrets('secretsUrl', '');
- } else {
- setSelectedAuth(XmattersAuthenticationType.URL);
- editActionConfig('usesBasic', false);
- editActionConfig('configUrl', '');
- editActionSecrets('user', '');
- editActionSecrets('password', '');
- }
- }}
/>
+
{selectedAuth === XmattersAuthenticationType.URL ? (
- <>
- {getEncryptedFieldNotifyLabel(
- !action.id,
- 1,
- action.isMissingSecrets ?? false,
- i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.reenterUrlAuthValuesLabel',
- {
- defaultMessage: 'URL is encrypted. Please reenter values for this field.',
- }
- )
- )}
- >
+
+
+
+
+
) : null}
-
-
-
- }
- >
- {
- if (selectedAuth === XmattersAuthenticationType.Basic) {
- editActionConfig('configUrl', e.target.value);
- } else {
- editActionSecrets('secretsUrl', e.target.value);
- }
- }}
- />
-
-
-
{selectedAuth === XmattersAuthenticationType.Basic ? (
<>
+
+
+
+
+
@@ -186,83 +139,38 @@ const XmattersActionConnectorFields: React.FunctionComponent<
- {getEncryptedFieldNotifyLabel(
- !action.id,
- 2,
- action.isMissingSecrets ?? false,
- i18n.translate(
- 'xpack.triggersActionsUI.components.builtinActionTypes.xmattersAction.reenterBasicAuthValuesLabel',
- {
- defaultMessage:
- 'User and password are encrypted. Please reenter values for these fields.',
- }
- )
- )}
-
- {
- editActionSecrets('user', e.target.value);
- }}
- onBlur={() => {
- if (!user) {
- editActionSecrets('user', '');
- }
- }}
- />
-
+
-
- {
- editActionSecrets('password', e.target.value);
- }}
- onBlur={() => {
- if (!password) {
- editActionSecrets('password', '');
- }
- }}
- />
-
+
>
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/button_group_field.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/button_group_field.tsx
new file mode 100644
index 0000000000000..475329049d782
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/button_group_field.tsx
@@ -0,0 +1,80 @@
+/*
+ * 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, { memo, ReactNode } from 'react';
+import { EuiButtonGroup, EuiFormRow } from '@elastic/eui';
+import {
+ getFieldValidityAndErrorMessage,
+ UseField,
+} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { i18n } from '@kbn/i18n';
+
+const { emptyField } = fieldValidators;
+
+const getFieldConfig = ({ label, defaultValue }: { label: string; defaultValue?: string }) => ({
+ label,
+ defaultValue,
+ validations: [
+ {
+ validator: emptyField(
+ i18n.translate('xpack.triggersActionsUI.components.buttonGroupField.error.requiredField', {
+ values: { label },
+ defaultMessage: '{label} is required.',
+ })
+ ),
+ },
+ ],
+});
+
+interface Props {
+ path: string;
+ label: string;
+ defaultValue?: string;
+ helpText?: string | ReactNode;
+ [key: string]: any;
+}
+
+const ButtonGroupFieldComponent: React.FC = ({
+ path,
+ label,
+ helpText,
+ defaultValue,
+ ...rest
+}) => {
+ return (
+ path={path} config={getFieldConfig({ label, defaultValue })}>
+ {(field) => {
+ const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field);
+
+ return (
+
+
+
+ );
+ }}
+
+ );
+};
+
+export const ButtonGroupField = memo(ButtonGroupFieldComponent);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.test.tsx
deleted file mode 100644
index d627f75080f5b..0000000000000
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.test.tsx
+++ /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.
- */
-
-import { getEncryptedFieldNotifyLabel } from './get_encrypted_field_notify_label';
-
-describe('getEncryptedFieldNotifyLabel', () => {
- test('renders proper notify label when isCreate equals true', () => {
- const jsxObject = getEncryptedFieldNotifyLabel(true, 2, false, 'test');
-
- expect(
- jsxObject.props.children.filter(
- (child: any) => child.props['data-test-subj'] === 'rememberValuesMessage'
- ).length
- ).toBeGreaterThan(0);
- expect(
- jsxObject.props.children.filter(
- (child: any) => child.props['data-test-subj'] === 'missingSecretsMessage'
- ).length
- ).toBe(0);
- expect(
- jsxObject.props.children.filter(
- (child: any) => child.props['data-test-subj'] === 'reenterValuesMessage'
- ).length
- ).toBe(0);
- });
-
- test('renders proper notify label when secrets is missing', () => {
- const jsxObject = getEncryptedFieldNotifyLabel(false, 2, true, 'test');
-
- expect(
- jsxObject.props.children.filter(
- (child: any) => child.props['data-test-subj'] === 'rememberValuesMessage'
- ).length
- ).toBe(0);
- expect(
- jsxObject.props.children.filter(
- (child: any) => child.props['data-test-subj'] === 'missingSecretsMessage'
- ).length
- ).toBeGreaterThan(0);
- expect(
- jsxObject.props.children.filter(
- (child: any) => child.props['data-test-subj'] === 'reenterValuesMessage'
- ).length
- ).toBe(0);
- });
-
- test('renders proper notify label when isCreate false (edit mode) and isMissingSecrets false', () => {
- const jsxObject = getEncryptedFieldNotifyLabel(false, 2, false, 'test');
-
- expect(
- jsxObject.props.children.filter(
- (child: any) => child.props['data-test-subj'] === 'rememberValuesMessage'
- ).length
- ).toBe(0);
- expect(
- jsxObject.props.children.filter(
- (child: any) => child.props['data-test-subj'] === 'missingSecretsMessage'
- ).length
- ).toBe(0);
- expect(
- jsxObject.props.children.filter(
- (child: any) => child.props['data-test-subj'] === 'reenterValuesMessage'
- ).length
- ).toBeGreaterThan(0);
- });
-});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.tsx
deleted file mode 100644
index af23c471f2b41..0000000000000
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/get_encrypted_field_notify_label.tsx
+++ /dev/null
@@ -1,68 +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 { EuiSpacer, EuiCallOut, EuiText } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
-import React from 'react';
-import { FormattedMessage } from '@kbn/i18n-react';
-
-export const getEncryptedFieldNotifyLabel = (
- isCreate: boolean,
- encryptedFieldsLength: number,
- isMissingSecrets: boolean,
- reEnterDefaultMessage: string
-) => {
- if (isMissingSecrets) {
- return (
- <>
-
-
-
- >
- );
- }
- if (isCreate) {
- return (
- <>
-
-
-
-
-
- >
- );
- }
- return (
- <>
-
-
-
- >
- );
-};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/hidden_field.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/hidden_field.tsx
new file mode 100644
index 0000000000000..89385be8e5e1c
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/hidden_field.tsx
@@ -0,0 +1,25 @@
+/*
+ * 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, { memo } from 'react';
+import { UseField, UseFieldProps } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+
+const HiddenFieldComponent = (props: UseFieldProps) => {
+ return (
+ {...props}>
+ {(field) => {
+ /**
+ * This is a hidden field. We return null so we do not render
+ * any field on the form
+ */
+ return null;
+ }}
+
+ );
+};
+
+export const HiddenField = memo(HiddenFieldComponent);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/password_field.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/password_field.tsx
new file mode 100644
index 0000000000000..841e1fe7b40f5
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/password_field.tsx
@@ -0,0 +1,86 @@
+/*
+ * 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, { memo, ReactNode } from 'react';
+import { EuiFieldPassword, EuiFormRow } from '@elastic/eui';
+import {
+ getFieldValidityAndErrorMessage,
+ UseField,
+} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { i18n } from '@kbn/i18n';
+
+const { emptyField } = fieldValidators;
+
+const getFieldConfig = ({ label, validate }: { label: string; validate: boolean }) => ({
+ label,
+ validations: [
+ ...(validate
+ ? [
+ {
+ validator: emptyField(
+ i18n.translate(
+ 'xpack.triggersActionsUI.components.passwordField.error.requiredNameText',
+ {
+ values: { label },
+ defaultMessage: '{label} is required.',
+ }
+ )
+ ),
+ },
+ ]
+ : []),
+ ],
+});
+
+interface PasswordFieldProps {
+ path: string;
+ label: string;
+ helpText?: string | ReactNode;
+ validate?: boolean;
+ isLoading?: boolean;
+ [key: string]: any;
+}
+
+const PasswordFieldComponent: React.FC = ({
+ path,
+ label,
+ helpText,
+ validate = true,
+ isLoading,
+ ...rest
+}) => {
+ return (
+ path={path} config={getFieldConfig({ label, validate })}>
+ {(field) => {
+ const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field);
+
+ return (
+
+
+
+ );
+ }}
+
+ );
+};
+
+export const PasswordField = memo(PasswordFieldComponent);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.test.tsx
new file mode 100644
index 0000000000000..62745a6eb0995
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.test.tsx
@@ -0,0 +1,149 @@
+/*
+ * 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 { act, render, RenderResult } from '@testing-library/react';
+import { FormTestProvider } from './builtin_action_types/test_utils';
+import {
+ ConfigFieldSchema,
+ SecretsFieldSchema,
+ SimpleConnectorForm,
+} from './simple_connector_form';
+import userEvent from '@testing-library/user-event';
+
+const fillForm = async ({ getByTestId }: RenderResult) => {
+ await act(async () => {
+ await userEvent.type(getByTestId('config.url-input'), 'https://example.com', {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ await userEvent.type(getByTestId('config.test-config-input'), 'My text field', {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ await userEvent.type(getByTestId('secrets.username-input'), 'elastic', {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ await userEvent.type(getByTestId('secrets.password-input'), 'changeme', {
+ delay: 10,
+ });
+ });
+};
+
+describe('SimpleConnectorForm', () => {
+ const configFormSchema: ConfigFieldSchema[] = [
+ { id: 'url', label: 'Url', isUrlField: true },
+ { id: 'test-config', label: 'Test config', helpText: 'Test help text' },
+ ];
+ const secretsFormSchema: SecretsFieldSchema[] = [
+ { id: 'username', label: 'Username' },
+ { id: 'password', label: 'Password', isPasswordField: true },
+ ];
+
+ const onSubmit = jest.fn();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders correctly', async () => {
+ const { getByText } = render(
+
+
+
+ );
+
+ expect(getByText('Url')).toBeInTheDocument();
+ expect(getByText('Test config')).toBeInTheDocument();
+ expect(getByText('Test help text')).toBeInTheDocument();
+
+ expect(getByText('Authentication')).toBeInTheDocument();
+ expect(getByText('Username')).toBeInTheDocument();
+ expect(getByText('Password')).toBeInTheDocument();
+ });
+
+ it('submits correctly', async () => {
+ const res = render(
+
+
+
+ );
+
+ await fillForm(res);
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({
+ data: {
+ config: {
+ 'test-config': 'My text field',
+ url: 'https://example.com',
+ },
+ secrets: {
+ password: 'changeme',
+ username: 'elastic',
+ },
+ },
+ isValid: true,
+ });
+ });
+
+ describe('Validation', () => {
+ const tests: Array<[string, string]> = [
+ ['config.url-input', 'not-valid'],
+ ['config.test-config-input', ''],
+ ['secrets.username-input', ''],
+ ['secrets.password-input', ''],
+ ];
+
+ it.each(tests)('validates correctly %p', async (field, value) => {
+ const res = render(
+
+
+
+ );
+
+ await fillForm(res);
+
+ await act(async () => {
+ await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, {
+ delay: 10,
+ });
+ });
+
+ await act(async () => {
+ userEvent.click(res.getByTestId('form-test-provide-submit'));
+ });
+
+ expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false });
+ });
+ });
+});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.tsx
new file mode 100644
index 0000000000000..6bbf88ebc104d
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/simple_connector_form.tsx
@@ -0,0 +1,159 @@
+/*
+ * 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, { memo } from 'react';
+import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
+import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components';
+import { getUseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
+import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
+import { i18n } from '@kbn/i18n';
+import { PasswordField } from './password_field';
+
+export interface CommonFieldSchema {
+ id: string;
+ label: string;
+ helpText?: string;
+}
+
+export interface ConfigFieldSchema extends CommonFieldSchema {
+ isUrlField?: boolean;
+}
+
+export interface SecretsFieldSchema extends CommonFieldSchema {
+ isPasswordField?: boolean;
+}
+
+interface SimpleConnectorFormProps {
+ isEdit: boolean;
+ readOnly: boolean;
+ configFormSchema: ConfigFieldSchema[];
+ secretsFormSchema: SecretsFieldSchema[];
+}
+
+type FormRowProps = ConfigFieldSchema & SecretsFieldSchema & { readOnly: boolean };
+
+const UseField = getUseField({ component: Field });
+const { emptyField, urlField } = fieldValidators;
+
+const getFieldConfig = ({
+ label,
+ isUrlField = false,
+}: {
+ label: string;
+ isUrlField?: boolean;
+}) => ({
+ label,
+ validations: [
+ {
+ validator: emptyField(
+ i18n.translate(
+ 'xpack.triggersActionsUI.sections.actionConnectorForm.error.requireFieldText',
+ {
+ values: { label },
+ defaultMessage: `{label} is required.`,
+ }
+ )
+ ),
+ },
+ ...(isUrlField
+ ? [
+ {
+ validator: urlField(
+ i18n.translate(
+ 'xpack.triggersActionsUI.sections.actionConnectorForm.error.invalidURL',
+ {
+ defaultMessage: 'Invalid URL',
+ }
+ )
+ ),
+ },
+ ]
+ : []),
+ ],
+});
+
+const FormRow: React.FC = ({
+ id,
+ label,
+ readOnly,
+ isPasswordField,
+ isUrlField,
+ helpText,
+}) => {
+ const dataTestSub = `${id}-input`;
+ return (
+ <>
+
+
+ {!isPasswordField ? (
+
+ ) : (
+
+ )}
+
+
+ >
+ );
+};
+
+const SimpleConnectorFormComponent: React.FC = ({
+ isEdit,
+ readOnly,
+ configFormSchema,
+ secretsFormSchema,
+}) => {
+ return (
+ <>
+ {configFormSchema.map(({ id, ...restConfigSchema }, index) => (
+
+
+ {index !== configFormSchema.length ? : null}
+
+ ))}
+
+
+
+
+ {i18n.translate(
+ 'xpack.triggersActionsUI.components.simpleConnectorForm.secrets.authenticationLabel',
+ {
+ defaultMessage: 'Authentication',
+ }
+ )}
+
+
+
+
+
+ {secretsFormSchema.map(({ id, ...restSecretsSchema }, index) => (
+
+
+ {index !== secretsFormSchema.length ? : null}
+
+ ))}
+ >
+ );
+};
+
+export const SimpleConnectorForm = memo(SimpleConnectorFormComponent);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/context/connector_context.tsx b/x-pack/plugins/triggers_actions_ui/public/application/context/connector_context.tsx
new file mode 100644
index 0000000000000..e97cf789936ae
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/context/connector_context.tsx
@@ -0,0 +1,24 @@
+/*
+ * 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 { ConnectorServices } from '../../types';
+
+export interface ConnectorContextValue {
+ services: ConnectorServices;
+}
+
+export const ConnectorContext = React.createContext(undefined);
+
+export const ConnectorProvider: React.FC<{ value: ConnectorContextValue }> = ({
+ children,
+ value,
+}) => {
+ return {children};
+};
+
+ConnectorProvider.displayName = 'ConnectorProvider';
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/context/use_connector_context.ts b/x-pack/plugins/triggers_actions_ui/public/application/context/use_connector_context.ts
new file mode 100644
index 0000000000000..84cfb3279d09c
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/context/use_connector_context.ts
@@ -0,0 +1,21 @@
+/*
+ * 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 { useContext } from 'react';
+import { ConnectorContext } from './connector_context';
+
+export const useConnectorContext = () => {
+ const connectorContext = useContext(ConnectorContext);
+
+ if (!connectorContext) {
+ throw new Error(
+ 'useConnectorContext must be used within a ConnectorProvider and have a defined value.'
+ );
+ }
+
+ return connectorContext;
+};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_create_connector.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_create_connector.test.tsx
new file mode 100644
index 0000000000000..5fe09dd8ae906
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_create_connector.test.tsx
@@ -0,0 +1,49 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { act, renderHook } from '@testing-library/react-hooks';
+import { useKibana } from '../../common/lib/kibana';
+import { useCreateConnector } from './use_create_connector';
+
+jest.mock('../../common/lib/kibana');
+
+const useKibanaMock = useKibana as jest.Mocked;
+
+describe('useCreateConnector', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ useKibanaMock().services.http.post = jest.fn().mockResolvedValue({ id: 'test-id' });
+ });
+
+ it('init', async () => {
+ const { result } = renderHook(() => useCreateConnector());
+
+ expect(result.current).toEqual({
+ isLoading: false,
+ createConnector: expect.anything(),
+ });
+ });
+
+ it('executes correctly', async () => {
+ const { result, waitForNextUpdate } = renderHook(() => useCreateConnector());
+
+ act(() => {
+ result.current.createConnector({
+ actionTypeId: '.test',
+ name: 'test',
+ config: {},
+ secrets: {},
+ });
+ });
+
+ await waitForNextUpdate();
+
+ expect(useKibanaMock().services.http.post).toHaveBeenCalledWith('/api/actions/connector', {
+ body: '{"name":"test","config":{},"secrets":{},"connector_type_id":".test"}',
+ });
+ });
+});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_create_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_create_connector.tsx
new file mode 100644
index 0000000000000..3771e53497e9d
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_create_connector.tsx
@@ -0,0 +1,86 @@
+/*
+ * 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 { useRef, useState, useEffect } from 'react';
+import { i18n } from '@kbn/i18n';
+import { ActionConnector, ActionConnectorWithoutId } from '../../types';
+import { createActionConnector } from '../lib/action_connector_api';
+import { useKibana } from '../../common/lib/kibana';
+
+type CreateConnectorSchema = Pick<
+ ActionConnectorWithoutId,
+ 'actionTypeId' | 'name' | 'config' | 'secrets'
+>;
+
+interface UseCreateConnectorReturnValue {
+ isLoading: boolean;
+ createConnector: (connector: CreateConnectorSchema) => Promise;
+}
+
+export const useCreateConnector = (): UseCreateConnectorReturnValue => {
+ const {
+ http,
+ notifications: { toasts },
+ } = useKibana().services;
+
+ const [isLoading, setIsLoading] = useState(false);
+ const abortCtrlRef = useRef(new AbortController());
+ const isMounted = useRef(false);
+
+ async function createConnector(connector: CreateConnectorSchema) {
+ setIsLoading(true);
+ isMounted.current = true;
+ abortCtrlRef.current.abort();
+ abortCtrlRef.current = new AbortController();
+
+ try {
+ const res = await createActionConnector({ http, connector });
+
+ if (isMounted.current) {
+ setIsLoading(false);
+
+ toasts.addSuccess(
+ i18n.translate(
+ 'xpack.triggersActionsUI.sections.addConnectorForm.updateSuccessNotificationText',
+ {
+ defaultMessage: "Created '{connectorName}'",
+ values: {
+ connectorName: res.name,
+ },
+ }
+ )
+ );
+ }
+
+ return res;
+ } catch (error) {
+ if (isMounted.current) {
+ setIsLoading(false);
+
+ if (error.name !== 'AbortError') {
+ toasts.addDanger(
+ error.body?.message ??
+ i18n.translate(
+ 'xpack.triggersActionsUI.sections.useCreateConnector.updateErrorNotificationText',
+ { defaultMessage: 'Cannot create a connector.' }
+ )
+ );
+ }
+ }
+ }
+ }
+
+ useEffect(() => {
+ isMounted.current = true;
+ return () => {
+ isMounted.current = false;
+ abortCtrlRef.current.abort();
+ };
+ }, []);
+
+ return { isLoading, createConnector };
+};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_edit_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_edit_connector.tsx
new file mode 100644
index 0000000000000..531da7ee51f1d
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_edit_connector.tsx
@@ -0,0 +1,85 @@
+/*
+ * 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 { useRef, useState, useEffect } from 'react';
+import { i18n } from '@kbn/i18n';
+import { ActionConnector, ActionConnectorWithoutId } from '../../types';
+import { updateActionConnector } from '../lib/action_connector_api';
+import { useKibana } from '../../common/lib/kibana';
+
+type UpdateConnectorSchema = Pick & {
+ id: string;
+};
+
+interface UseUpdateConnectorReturnValue {
+ isLoading: boolean;
+ updateConnector: (connector: UpdateConnectorSchema) => Promise;
+}
+
+export const useUpdateConnector = (): UseUpdateConnectorReturnValue => {
+ const {
+ http,
+ notifications: { toasts },
+ } = useKibana().services;
+
+ const [isLoading, setIsLoading] = useState(false);
+ const abortCtrlRef = useRef(new AbortController());
+ const isMounted = useRef(false);
+
+ async function updateConnector(connector: UpdateConnectorSchema) {
+ setIsLoading(true);
+ isMounted.current = true;
+ abortCtrlRef.current.abort();
+ abortCtrlRef.current = new AbortController();
+
+ try {
+ const res = await updateActionConnector({ http, connector, id: connector.id });
+
+ if (isMounted.current) {
+ setIsLoading(false);
+
+ toasts.addSuccess(
+ i18n.translate(
+ 'xpack.triggersActionsUI.sections.editConnectorForm.updateSuccessNotificationText',
+ {
+ defaultMessage: "Updated '{connectorName}'",
+ values: {
+ connectorName: res.name,
+ },
+ }
+ )
+ );
+ }
+
+ return res;
+ } catch (error) {
+ if (isMounted.current) {
+ setIsLoading(false);
+
+ if (error.name !== 'AbortError') {
+ toasts.addDanger(
+ error.body?.message ??
+ i18n.translate(
+ 'xpack.triggersActionsUI.sections.editConnectorForm.updateErrorNotificationText',
+ { defaultMessage: 'Cannot update a connector.' }
+ )
+ );
+ }
+ }
+ }
+ }
+
+ useEffect(() => {
+ isMounted.current = true;
+ return () => {
+ isMounted.current = false;
+ abortCtrlRef.current.abort();
+ };
+ }, []);
+
+ return { isLoading, updateConnector };
+};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_execute_connector.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_execute_connector.test.tsx
new file mode 100644
index 0000000000000..4b93900ea6b4e
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_execute_connector.test.tsx
@@ -0,0 +1,45 @@
+/*
+ * 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, renderHook } from '@testing-library/react-hooks';
+import { useKibana } from '../../common/lib/kibana';
+import { useExecuteConnector } from './use_execute_connector';
+
+jest.mock('../../common/lib/kibana');
+
+const useKibanaMock = useKibana as jest.Mocked;
+
+describe('useExecuteConnector', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ useKibanaMock().services.http.post = jest.fn().mockResolvedValue({ status: 'ok', data: {} });
+ });
+
+ it('init', async () => {
+ const { result } = renderHook(() => useExecuteConnector());
+
+ expect(result.current).toEqual({
+ isLoading: false,
+ executeConnector: expect.anything(),
+ });
+ });
+
+ it('executes correctly', async () => {
+ const { result, waitForNextUpdate } = renderHook(() => useExecuteConnector());
+
+ act(() => {
+ result.current.executeConnector({ connectorId: 'test-id', params: {} });
+ });
+
+ await waitForNextUpdate();
+
+ expect(useKibanaMock().services.http.post).toHaveBeenCalledWith(
+ '/api/actions/connector/test-id/_execute',
+ { body: '{"params":{}}' }
+ );
+ });
+});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_execute_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_execute_connector.tsx
new file mode 100644
index 0000000000000..9b68eb3722210
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_execute_connector.tsx
@@ -0,0 +1,68 @@
+/*
+ * 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 { ActionTypeExecutorResult } from '@kbn/actions-plugin/common';
+import { useRef, useState, useEffect } from 'react';
+import { executeAction } from '../lib/action_connector_api';
+import { useKibana } from '../../common/lib/kibana';
+
+interface UseExecuteConnectorReturnValue {
+ isLoading: boolean;
+ executeConnector: (args: {
+ connectorId: string;
+ params: Record;
+ }) => Promise | undefined>;
+}
+
+export const useExecuteConnector = (): UseExecuteConnectorReturnValue => {
+ const { http } = useKibana().services;
+
+ const [isLoading, setIsLoading] = useState(false);
+ const abortCtrlRef = useRef(new AbortController());
+ const isMounted = useRef(false);
+
+ async function executeConnector({
+ connectorId,
+ params,
+ }: {
+ connectorId: string;
+ params: Record;
+ }) {
+ setIsLoading(true);
+ isMounted.current = true;
+ abortCtrlRef.current.abort();
+ abortCtrlRef.current = new AbortController();
+
+ try {
+ const res = await executeAction({ http, id: connectorId, params });
+
+ if (isMounted.current) {
+ setIsLoading(false);
+ }
+
+ return res;
+ } catch (error) {
+ if (isMounted.current) {
+ setIsLoading(false);
+
+ if (error.name !== 'AbortError') {
+ throw error;
+ }
+ }
+ }
+ }
+
+ useEffect(() => {
+ isMounted.current = true;
+ return () => {
+ isMounted.current = false;
+ abortCtrlRef.current.abort();
+ };
+ }, []);
+
+ return { isLoading, executeConnector };
+};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.test.tsx
new file mode 100644
index 0000000000000..01ad50456b04b
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.test.tsx
@@ -0,0 +1,75 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { renderHook } from '@testing-library/react-hooks';
+import { useKibana } from '../../common/lib/kibana';
+import { useSubAction } from './use_sub_action';
+
+jest.mock('../../common/lib/kibana');
+
+const useKibanaMock = useKibana as jest.Mocked;
+
+describe('useSubAction', () => {
+ const params = {
+ connectorId: 'test-id',
+ subAction: 'test',
+ subActionParams: {},
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ useKibanaMock().services.http.post = jest.fn().mockResolvedValue({ status: 'ok', data: {} });
+ });
+
+ it('init', async () => {
+ const { result, waitForNextUpdate } = renderHook(() => useSubAction(params));
+ await waitForNextUpdate();
+
+ expect(result.current).toEqual({
+ isError: false,
+ isLoading: false,
+ response: {},
+ error: null,
+ });
+ });
+
+ it('executes the sub action correctly', async () => {
+ const { waitForNextUpdate } = renderHook(() => useSubAction(params));
+ await waitForNextUpdate();
+
+ expect(useKibanaMock().services.http.post).toHaveBeenCalledWith(
+ '/api/actions/connector/test-id/_execute',
+ { body: '{"params":{"subAction":"test","subActionParams":{}}}' }
+ );
+ });
+
+ it('returns an error correctly', async () => {
+ useKibanaMock().services.http.post = jest.fn().mockRejectedValue(new Error('error executing'));
+
+ const { result, waitForNextUpdate } = renderHook(() => useSubAction(params));
+ await waitForNextUpdate();
+
+ expect(result.current).toEqual({
+ isError: true,
+ isLoading: false,
+ response: undefined,
+ error: expect.anything(),
+ });
+ });
+
+ it('does not execute if params are null', async () => {
+ const { result } = renderHook(() => useSubAction(null));
+
+ expect(useKibanaMock().services.http.post).not.toHaveBeenCalled();
+ expect(result.current).toEqual({
+ isError: false,
+ isLoading: false,
+ response: undefined,
+ error: null,
+ });
+ });
+});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.tsx
new file mode 100644
index 0000000000000..a219caa9b4d23
--- /dev/null
+++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_sub_action.tsx
@@ -0,0 +1,144 @@
+/*
+ * 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 { useCallback, useEffect, useReducer, useRef } from 'react';
+import { ActionTypeExecutorResult } from '@kbn/actions-plugin/common';
+import { useKibana } from '../../common/lib/kibana';
+import { executeAction } from '../lib/action_connector_api';
+
+interface UseSubActionParams {
+ connectorId: string;
+ subAction: string;
+ subActionParams: Record;
+}
+
+interface SubActionsState {
+ isLoading: boolean;
+ isError: boolean;
+ response: unknown | undefined;
+ error: Error | null;
+}
+
+enum SubActionsActionsList {
+ INIT,
+ LOADING,
+ SUCCESS,
+ ERROR,
+}
+
+type Action =
+ | { type: SubActionsActionsList.INIT }
+ | { type: SubActionsActionsList.LOADING }
+ | { type: SubActionsActionsList.SUCCESS; payload: T | undefined }
+ | { type: SubActionsActionsList.ERROR; payload: Error | null };
+
+const dataFetchReducer = (state: SubActionsState, action: Action): SubActionsState => {
+ switch (action.type) {
+ case SubActionsActionsList.INIT:
+ return {
+ ...state,
+ isLoading: false,
+ isError: false,
+ };
+
+ case SubActionsActionsList.LOADING:
+ return {
+ ...state,
+ isLoading: true,
+ isError: false,
+ };
+
+ case SubActionsActionsList.SUCCESS:
+ return {
+ ...state,
+ response: action.payload,
+ isLoading: false,
+ isError: false,
+ };
+
+ case SubActionsActionsList.ERROR:
+ return {
+ ...state,
+ error: action.payload,
+ isLoading: false,
+ isError: true,
+ };
+
+ default:
+ return state;
+ }
+};
+
+export const useSubAction = (params: UseSubActionParams | null) => {
+ const { http } = useKibana().services;
+ const [state, dispatch] = useReducer(dataFetchReducer, {
+ isError: false,
+ isLoading: false,
+ response: undefined,
+ error: null,
+ });
+
+ const abortCtrl = useRef(new AbortController());
+ const isMounted = useRef(false);
+
+ const executeSubAction = useCallback(async () => {
+ if (params == null) {
+ return;
+ }
+
+ const { connectorId, subAction, subActionParams } = params;
+ dispatch({ type: SubActionsActionsList.INIT });
+
+ try {
+ abortCtrl.current.abort();
+ abortCtrl.current = new AbortController();
+ dispatch({ type: SubActionsActionsList.LOADING });
+
+ const res = (await executeAction({
+ id: connectorId,
+ http,
+ params: {
+ subAction,
+ subActionParams,
+ },
+ })) as ActionTypeExecutorResult;
+
+ if (isMounted.current) {
+ if (res.status && res.status === 'error') {
+ dispatch({
+ type: SubActionsActionsList.ERROR,
+ payload: new Error(`${res.message}: ${res.serviceMessage}`),
+ });
+ }
+
+ dispatch({ type: SubActionsActionsList.SUCCESS, payload: res.data });
+ }
+
+ return res.data;
+ } catch (e) {
+ if (isMounted.current) {
+ dispatch({
+ type: SubActionsActionsList.ERROR,
+ payload: e,
+ });
+ }
+ }
+ }, [http, params]);
+
+ useEffect(() => {
+ isMounted.current = true;
+ executeSubAction();
+ return () => {
+ isMounted.current = false;
+ abortCtrl.current.abort();
+ };
+ }, [executeSubAction]);
+
+ return {
+ ...state,
+ };
+};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rules_list_sandbox.tsx b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rules_list_sandbox.tsx
index 7702b914cfd36..0083811802955 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rules_list_sandbox.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/internal/shareable_components_sandbox/rules_list_sandbox.tsx
@@ -6,11 +6,18 @@
*/
import React from 'react';
import { getRulesListLazy } from '../../../common/get_rules_list';
+import { useConnectorContext } from '../../context/use_connector_context';
const style = {
flex: 1,
};
export const RulesListSandbox = () => {
- return {getRulesListLazy()}
;
+ const {
+ services: { validateEmailAddresses },
+ } = useConnectorContext();
+
+ return (
+ {getRulesListLazy({ connectorServices: { validateEmailAddresses } })}
+ );
};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.test.ts
index a2ed111daa2be..fa8af621b9521 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.test.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.test.ts
@@ -26,15 +26,22 @@ describe('createActionConnector', () => {
};
http.post.mockResolvedValueOnce(apiResponse);
- const connector: ActionConnectorWithoutId<{}, {}> = {
+ const connector: Pick<
+ ActionConnectorWithoutId,
+ 'actionTypeId' | 'name' | 'config' | 'secrets'
+ > = {
actionTypeId: 'test',
- isPreconfigured: false,
- isDeprecated: false,
name: 'My test',
config: {},
secrets: {},
};
- const resolvedValue = { ...connector, id: '123' };
+ const resolvedValue = {
+ ...connector,
+ id: '123',
+ isDeprecated: false,
+ isPreconfigured: false,
+ isMissingSecrets: undefined,
+ };
const result = await createActionConnector({ http, connector });
expect(result).toEqual(resolvedValue);
@@ -42,7 +49,7 @@ describe('createActionConnector', () => {
Array [
"/api/actions/connector",
Object {
- "body": "{\\"name\\":\\"My test\\",\\"config\\":{},\\"secrets\\":{},\\"connector_type_id\\":\\"test\\",\\"is_preconfigured\\":false,\\"is_deprecated\\":false}",
+ "body": "{\\"name\\":\\"My test\\",\\"config\\":{},\\"secrets\\":{},\\"connector_type_id\\":\\"test\\"}",
},
]
`);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts
index 9227c4747c84a..287c27a1c0e3f 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts
+++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/create.ts
@@ -14,12 +14,10 @@ import type {
} from '../../../types';
const rewriteBodyRequest: RewriteResponseCase<
- Omit
-> = ({ actionTypeId, isPreconfigured, isDeprecated, ...res }) => ({
+ Pick
+> = ({ actionTypeId, ...res }) => ({
...res,
connector_type_id: actionTypeId,
- is_preconfigured: isPreconfigured,
- is_deprecated: isDeprecated,
});
const rewriteBodyRes: RewriteRequestCase<
@@ -43,7 +41,7 @@ export async function createActionConnector({
connector,
}: {
http: HttpSetup;
- connector: Omit;
+ connector: Pick;
}): Promise {
const res = await http.post[0]>(
`${BASE_ACTION_API_PATH}/connector`,
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx
deleted file mode 100644
index b86a3952eb0a6..0000000000000
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx
+++ /dev/null
@@ -1,61 +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 * as React from 'react';
-import { mountWithIntl } from '@kbn/test-jest-helpers';
-import { actionTypeRegistryMock } from '../../action_type_registry.mock';
-import {
- UserConfiguredActionConnector,
- GenericValidationResult,
- ConnectorValidationResult,
-} from '../../../types';
-import { ActionConnectorForm } from './action_connector_form';
-const actionTypeRegistry = actionTypeRegistryMock.create();
-jest.mock('../../../common/lib/kibana');
-
-describe('action_connector_form', () => {
- it('renders action_connector_form', () => {
- const actionType = actionTypeRegistryMock.createMockActionTypeModel({
- id: 'my-action-type',
- iconClass: 'test',
- selectMessage: 'test',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
- validateParams: (): Promise> => {
- const validationResult = { errors: {} };
- return Promise.resolve(validationResult);
- },
- });
- actionTypeRegistry.get.mockReturnValue(actionType);
- actionTypeRegistry.has.mockReturnValue(true);
-
- const initialConnector: UserConfiguredActionConnector<{}, {}> = {
- id: '123',
- name: '',
- actionTypeId: actionType.id,
- config: {},
- secrets: {},
- isPreconfigured: false,
- isDeprecated: false,
- };
- const wrapper = mountWithIntl(
- {}}
- errors={{ name: [] }}
- actionTypeRegistry={actionTypeRegistry}
- setCallbacks={() => {}}
- isEdit={false}
- />
- );
- const connectorNameField = wrapper?.find('[data-test-subj="nameInput"]');
- expect(connectorNameField?.exists()).toBeTruthy();
- expect(connectorNameField?.first().prop('value')).toBe('');
- });
-});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx
deleted file mode 100644
index b2000f172ef32..0000000000000
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx
+++ /dev/null
@@ -1,254 +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 React, { Suspense } from 'react';
-import {
- EuiForm,
- EuiCallOut,
- EuiLink,
- EuiText,
- EuiSpacer,
- EuiFieldText,
- EuiFormRow,
- EuiErrorBoundary,
- EuiTitle,
-} from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n-react';
-import {
- IErrorObject,
- ActionTypeRegistryContract,
- UserConfiguredActionConnector,
- ActionTypeModel,
- ActionConnectorFieldsSetCallbacks,
-} from '../../../types';
-import { hasSaveActionsCapability } from '../../lib/capabilities';
-import { useKibana } from '../../../common/lib/kibana';
-import { SectionLoading } from '../../components/section_loading';
-import { ConnectorReducerAction } from './connector_reducer';
-
-export function validateBaseProperties(
- actionObject: UserConfiguredActionConnector
-) {
- const validationResult = { errors: {} };
- const verrors = {
- name: new Array(),
- };
- validationResult.errors = verrors;
- if (!actionObject.name) {
- verrors.name.push(
- i18n.translate(
- 'xpack.triggersActionsUI.sections.actionConnectorForm.error.requiredNameText',
- {
- defaultMessage: 'Name is required.',
- }
- )
- );
- }
- return validationResult;
-}
-
-export async function getConnectorErrors(
- connector: UserConfiguredActionConnector,
- actionTypeModel: ActionTypeModel
-) {
- const connectorValidationResult = await actionTypeModel?.validateConnector(connector);
- const configErrors = (
- connectorValidationResult.config ? connectorValidationResult.config.errors : {}
- ) as IErrorObject;
- const secretsErrors = (
- connectorValidationResult.secrets ? connectorValidationResult.secrets.errors : {}
- ) as IErrorObject;
- const connectorBaseErrors = validateBaseProperties(connector).errors;
- const connectorErrors = {
- ...configErrors,
- ...secretsErrors,
- ...connectorBaseErrors,
- } as IErrorObject;
- return {
- configErrors,
- secretsErrors,
- connectorBaseErrors,
- connectorErrors,
- };
-}
-
-interface ActionConnectorProps<
- ConnectorConfig = Record,
- ConnectorSecrets = Record
-> {
- connector: UserConfiguredActionConnector;
- dispatch: React.Dispatch>;
- errors: IErrorObject;
- actionTypeRegistry: ActionTypeRegistryContract;
- consumer?: string;
- actionTypeName?: string;
- serverError?: {
- body: { message: string; error: string };
- };
- setCallbacks: ActionConnectorFieldsSetCallbacks;
- isEdit: boolean;
-}
-
-export const ActionConnectorForm = ({
- connector,
- dispatch,
- actionTypeName,
- serverError,
- errors,
- actionTypeRegistry,
- consumer,
- setCallbacks,
- isEdit,
-}: ActionConnectorProps) => {
- const {
- docLinks,
- application: { capabilities },
- } = useKibana().services;
- const canSave = hasSaveActionsCapability(capabilities);
-
- const setActionProperty = <
- Key extends keyof UserConfiguredActionConnector<
- Record,
- Record
- >
- >(
- key: Key,
- value:
- | UserConfiguredActionConnector, Record>[Key]
- | null
- ) => {
- dispatch({ command: { type: 'setProperty' }, payload: { key, value } });
- };
-
- const setActionConfigProperty = >(
- key: Key,
- value: Record[Key]
- ) => {
- dispatch({ command: { type: 'setConfigProperty' }, payload: { key, value } });
- };
-
- const setActionSecretsProperty = >(
- key: Key,
- value: Record[Key]
- ) => {
- dispatch({ command: { type: 'setSecretsProperty' }, payload: { key, value } });
- };
-
- const actionTypeRegistered = actionTypeRegistry.get(connector.actionTypeId);
- if (!actionTypeRegistered)
- return (
- <>
-
-
-
-
-
-
- ),
- }}
- />
-
-
-
-
- >
- );
-
- const FieldsComponent = actionTypeRegistered.actionConnectorFields;
- const isNameInvalid: boolean =
- connector.name !== undefined && errors.name !== undefined && errors.name.length > 0;
- return (
-
-
- }
- isInvalid={isNameInvalid}
- error={errors.name}
- >
- {
- setActionProperty('name', e.target.value);
- }}
- onBlur={() => {
- if (!connector.name) {
- setActionProperty('name', '');
- }
- }}
- />
-
-
- {FieldsComponent !== null ? (
- <>
-
-
-
-
-
-
-
-
-
-
- }
- >
-
-
-
- >
- ) : null}
-
- );
-};
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx
index 93b912113a1a9..002475c21882e 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx
@@ -11,13 +11,7 @@ import { EuiAccordion } from '@elastic/eui';
import { coreMock } from '@kbn/core/public/mocks';
import { act } from 'react-dom/test-utils';
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
-import {
- ValidationResult,
- Rule,
- RuleAction,
- ConnectorValidationResult,
- GenericValidationResult,
-} from '../../../types';
+import { ValidationResult, Rule, RuleAction, GenericValidationResult } from '../../../types';
import ActionForm from './action_form';
import { useKibana } from '../../../common/lib/kibana';
import {
@@ -53,9 +47,6 @@ describe('action_form', () => {
id: 'my-action-type',
iconClass: 'test',
selectMessage: 'test',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
validateParams: (): Promise> => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
@@ -68,9 +59,6 @@ describe('action_form', () => {
id: 'disabled-by-config',
iconClass: 'test',
selectMessage: 'test',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
validateParams: (): Promise> => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
@@ -83,9 +71,6 @@ describe('action_form', () => {
id: '.jira',
iconClass: 'test',
selectMessage: 'test',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
validateParams: (): Promise> => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
@@ -98,9 +83,6 @@ describe('action_form', () => {
id: 'disabled-by-license',
iconClass: 'test',
selectMessage: 'test',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
validateParams: (): Promise> => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
@@ -113,9 +95,6 @@ describe('action_form', () => {
id: 'preconfigured',
iconClass: 'test',
selectMessage: 'test',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
validateParams: (): Promise> => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx
index 7b89d720eabe3..a06cba11a4134 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx
@@ -8,13 +8,7 @@ import * as React from 'react';
import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers';
import { ActionTypeForm } from './action_type_form';
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
-import {
- ActionConnector,
- ActionType,
- RuleAction,
- ConnectorValidationResult,
- GenericValidationResult,
-} from '../../../types';
+import { ActionConnector, ActionType, RuleAction, GenericValidationResult } from '../../../types';
import { act } from 'react-dom/test-utils';
import { EuiFieldText } from '@elastic/eui';
import { DefaultActionParams } from '../../lib/get_defaults_for_action_params';
@@ -43,9 +37,6 @@ describe('action_type_form', () => {
id: '.pagerduty',
iconClass: 'test',
selectMessage: 'test',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
validateParams: (): Promise> => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
@@ -92,9 +83,6 @@ describe('action_type_form', () => {
id: '.pagerduty',
iconClass: 'test',
selectMessage: 'test',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
validateParams: (): Promise> => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.test.tsx
index 8063bc97334f5..ef803e50bb60d 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.test.tsx
@@ -10,7 +10,7 @@ import { mountWithIntl } from '@kbn/test-jest-helpers';
import { coreMock } from '@kbn/core/public/mocks';
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
import { ActionTypeMenu } from './action_type_menu';
-import { ConnectorValidationResult, GenericValidationResult } from '../../../types';
+import { GenericValidationResult } from '../../../types';
import { useKibana } from '../../../common/lib/kibana';
jest.mock('../../../common/lib/kibana');
const actionTypeRegistry = actionTypeRegistryMock.create();
@@ -40,9 +40,6 @@ describe('connector_add_flyout', () => {
id: 'my-action-type',
iconClass: 'test',
selectMessage: 'test',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
validateParams: (): Promise> => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
@@ -77,9 +74,6 @@ describe('connector_add_flyout', () => {
id: 'my-action-type',
iconClass: 'test',
selectMessage: 'test',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
validateParams: (): Promise> => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
@@ -114,9 +108,6 @@ describe('connector_add_flyout', () => {
id: 'my-action-type',
iconClass: 'test',
selectMessage: 'test',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
validateParams: (): Promise> => {
const validationResult = { errors: {} };
return Promise.resolve(validationResult);
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx
deleted file mode 100644
index 02819a714c833..0000000000000
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx
+++ /dev/null
@@ -1,210 +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 * as React from 'react';
-import { mountWithIntl } from '@kbn/test-jest-helpers';
-import { coreMock } from '@kbn/core/public/mocks';
-import ConnectorAddFlyout from './connector_add_flyout';
-import { actionTypeRegistryMock } from '../../action_type_registry.mock';
-import { ConnectorValidationResult, GenericValidationResult } from '../../../types';
-import { useKibana } from '../../../common/lib/kibana';
-jest.mock('../../../common/lib/kibana');
-
-const actionTypeRegistry = actionTypeRegistryMock.create();
-const useKibanaMock = useKibana as jest.Mocked;
-
-describe('connector_add_flyout', () => {
- beforeAll(async () => {
- const mocks = coreMock.createSetup();
- const [
- {
- application: { capabilities },
- },
- ] = await mocks.getStartServices();
- useKibanaMock().services.application.capabilities = {
- ...capabilities,
- actions: {
- show: true,
- save: true,
- delete: true,
- },
- };
- });
-
- it('renders action type menu on flyout open', () => {
- const actionType = createActionType();
- actionTypeRegistry.get.mockReturnValueOnce(actionType);
- actionTypeRegistry.has.mockReturnValue(true);
-
- const wrapper = mountWithIntl(
- {}}
- actionTypes={[
- {
- id: actionType.id,
- enabled: true,
- name: 'Test',
- enabledInConfig: true,
- enabledInLicense: true,
- minimumLicenseRequired: 'basic',
- },
- ]}
- reloadConnectors={() => {
- return new Promise(() => {});
- }}
- actionTypeRegistry={actionTypeRegistry}
- />
- );
- expect(wrapper.find('ActionTypeMenu')).toHaveLength(1);
- expect(wrapper.find(`[data-test-subj="${actionType.id}-card"]`).exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="cancelButton"]').exists()).toBeTruthy();
- expect(wrapper.find('[data-test-subj="backButton"]').exists()).toBeFalsy();
- });
-
- it('renders banner with subscription links when gold features are disabled due to licensing ', () => {
- const actionType = createActionType();
- const disabledActionType = createActionType();
-
- actionTypeRegistry.get.mockReturnValueOnce(actionType);
- actionTypeRegistry.has.mockReturnValue(true);
-
- const wrapper = mountWithIntl(
- {}}
- actionTypes={[
- {
- id: actionType.id,
- enabled: true,
- name: 'Test',
- enabledInConfig: true,
- enabledInLicense: true,
- minimumLicenseRequired: 'basic',
- },
- {
- id: disabledActionType.id,
- enabled: true,
- name: 'Test',
- enabledInConfig: true,
- enabledInLicense: false,
- minimumLicenseRequired: 'gold',
- },
- ]}
- reloadConnectors={() => {
- return new Promise(() => {});
- }}
- actionTypeRegistry={actionTypeRegistry}
- />
- );
- const callout = wrapper.find('UpgradeYourLicenseCallOut');
- expect(callout).toHaveLength(1);
-
- const manageLink = callout.find('EuiButton');
- expect(manageLink).toHaveLength(1);
- expect(manageLink.getElements()[0].props.href).toMatchInlineSnapshot(
- `"/app/management/stack/license_management"`
- );
-
- const subscriptionLink = callout.find('EuiButtonEmpty');
- expect(subscriptionLink).toHaveLength(1);
- expect(subscriptionLink.getElements()[0].props.href).toMatchInlineSnapshot(
- `"https://www.elastic.co/subscriptions"`
- );
- });
-
- it('does not render banner with subscription links when only platinum features are disabled due to licensing ', () => {
- const actionType = createActionType();
- const disabledActionType = createActionType();
-
- actionTypeRegistry.get.mockReturnValueOnce(actionType);
- actionTypeRegistry.has.mockReturnValue(true);
-
- const wrapper = mountWithIntl(
- {}}
- actionTypes={[
- {
- id: actionType.id,
- enabled: true,
- name: 'Test',
- enabledInConfig: true,
- enabledInLicense: true,
- minimumLicenseRequired: 'basic',
- },
- {
- id: disabledActionType.id,
- enabled: true,
- name: 'Test',
- enabledInConfig: true,
- enabledInLicense: false,
- minimumLicenseRequired: 'platinum',
- },
- ]}
- reloadConnectors={() => {
- return new Promise(() => {});
- }}
- actionTypeRegistry={actionTypeRegistry}
- />
- );
- const callout = wrapper.find('UpgradeYourLicenseCallOut');
- expect(callout).toHaveLength(0);
- });
-
- it('does not render banner with subscription links when only enterprise features are disabled due to licensing ', () => {
- const actionType = createActionType();
- const disabledActionType = createActionType();
-
- actionTypeRegistry.get.mockReturnValueOnce(actionType);
- actionTypeRegistry.has.mockReturnValue(true);
-
- const wrapper = mountWithIntl(
- {}}
- actionTypes={[
- {
- id: actionType.id,
- enabled: true,
- name: 'Test',
- enabledInConfig: true,
- enabledInLicense: true,
- minimumLicenseRequired: 'basic',
- },
- {
- id: disabledActionType.id,
- enabled: true,
- name: 'Test',
- enabledInConfig: true,
- enabledInLicense: false,
- minimumLicenseRequired: 'enterprise',
- },
- ]}
- reloadConnectors={() => {
- return new Promise(() => {});
- }}
- actionTypeRegistry={actionTypeRegistry}
- />
- );
- const callout = wrapper.find('UpgradeYourLicenseCallOut');
- expect(callout).toHaveLength(0);
- });
-});
-
-let count = 0;
-function createActionType() {
- return actionTypeRegistryMock.createMockActionTypeModel({
- id: `my-action-type-${++count}`,
- iconClass: 'test',
- selectMessage: 'test',
- validateConnector: (): Promise> => {
- return Promise.resolve({});
- },
- validateParams: (): Promise> => {
- const validationResult = { errors: {} };
- return Promise.resolve(validationResult);
- },
- actionConnectorFields: null,
- });
-}
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx
deleted file mode 100644
index 06874f22b6428..0000000000000
--- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx
+++ /dev/null
@@ -1,416 +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 React, { useCallback, useState, useReducer, useEffect } from 'react';
-import { FormattedMessage } from '@kbn/i18n-react';
-import {
- EuiTitle,
- EuiFlyoutHeader,
- EuiFlyout,
- EuiFlexGroup,
- EuiFlexItem,
- EuiIcon,
- EuiText,
- EuiFlyoutFooter,
- EuiButtonEmpty,
- EuiButton,
- EuiFlyoutBody,
- EuiCallOut,
- EuiSpacer,
-} from '@elastic/eui';
-import { HttpSetup } from '@kbn/core/public';
-import { i18n } from '@kbn/i18n';
-import { ActionTypeMenu } from './action_type_menu';
-import { ActionConnectorForm, getConnectorErrors } from './action_connector_form';
-import {
- ActionType,
- ActionConnector,
- UserConfiguredActionConnector,
- IErrorObject,
- ConnectorAddFlyoutProps,
- ActionTypeModel,
- ActionConnectorFieldsCallbacks,
-} from '../../../types';
-import { hasSaveActionsCapability } from '../../lib/capabilities';
-import { createActionConnector } from '../../lib/action_connector_api';
-import { VIEW_LICENSE_OPTIONS_LINK } from '../../../common/constants';
-import { useKibana } from '../../../common/lib/kibana';
-import { createConnectorReducer, InitialConnector, ConnectorReducer } from './connector_reducer';
-import { getConnectorWithInvalidatedFields } from '../../lib/value_validators';
-import { CenterJustifiedSpinner } from '../../components/center_justified_spinner';
-
-const ConnectorAddFlyout: React.FunctionComponent = ({
- onClose,
- actionTypes,
- onTestConnector,
- reloadConnectors,
- consumer,
- actionTypeRegistry,
-}) => {
- const [hasErrors, setHasErrors] = useState(true);
- let actionTypeModel: ActionTypeModel | undefined;
-
- const {
- http,
- notifications: { toasts },
- application: { capabilities },
- } = useKibana().services;
- const [actionType, setActionType] = useState(undefined);
- const [hasActionsUpgradeableByTrial, setHasActionsUpgradeableByTrial] = useState(false);
- const [errors, setErrors] = useState<{
- configErrors: IErrorObject;
- connectorBaseErrors: IErrorObject;
- connectorErrors: IErrorObject;
- secretsErrors: IErrorObject;
- }>({
- configErrors: {},
- connectorBaseErrors: {},
- connectorErrors: {},
- secretsErrors: {},
- });
- // hooks
- const initialConnector: InitialConnector, Record> = {
- actionTypeId: actionType?.id ?? '',
- config: {},
- secrets: {},
- };
-
- const reducer: ConnectorReducer<
- Record,
- Record
- > = createConnectorReducer, Record>();
- const [{ connector }, dispatch] = useReducer(reducer, {
- connector: initialConnector as UserConfiguredActionConnector<
- Record