@@ -107,7 +102,11 @@ export const SecuritySideNavigation: SideNavComponent = React.memo(function Secu
)}
-
+
>
);
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation_footer.test.tsx b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation_footer.test.tsx
index 02e4979c1fba2..fdfd3216d606d 100644
--- a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation_footer.test.tsx
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation_footer.test.tsx
@@ -12,6 +12,7 @@ import { SideNavigationFooter } from './side_navigation_footer';
import { ExternalPageName } from '../links/constants';
import { I18nProvider } from '@kbn/i18n-react';
import type { ProjectSideNavItem } from './types';
+import { FOOTER_CATEGORIES } from '../categories';
jest.mock('../../common/services');
@@ -54,9 +55,12 @@ describe('SideNavigationFooter', () => {
});
it('should render all the items', () => {
- const component = render(
, {
- wrapper: I18nProvider,
- });
+ const component = render(
+
,
+ {
+ wrapper: I18nProvider,
+ }
+ );
items.forEach((item) => {
expect(component.queryByTestId(`solutionSideNavItemLink-${item.id}`)).toBeInTheDocument();
@@ -64,9 +68,16 @@ describe('SideNavigationFooter', () => {
});
it('should highlight the active node', () => {
- const component = render(
, {
- wrapper: I18nProvider,
- });
+ const component = render(
+
,
+ {
+ wrapper: I18nProvider,
+ }
+ );
items.forEach((item) => {
const isSelected = component
@@ -82,9 +93,16 @@ describe('SideNavigationFooter', () => {
});
it('should highlight the active node inside the collapsible', () => {
- const component = render(
, {
- wrapper: I18nProvider,
- });
+ const component = render(
+
,
+ {
+ wrapper: I18nProvider,
+ }
+ );
items.forEach((item) => {
const isSelected = component
@@ -100,9 +118,12 @@ describe('SideNavigationFooter', () => {
});
it('should render closed collapsible if it has no active node', () => {
- const component = render(
, {
- wrapper: I18nProvider,
- });
+ const component = render(
+
,
+ {
+ wrapper: I18nProvider,
+ }
+ );
const isOpen = component
.queryByTestId('navFooterCollapsible-project-settings')
@@ -112,9 +133,16 @@ describe('SideNavigationFooter', () => {
});
it('should open collapsible if it has an active node', () => {
- const component = render(
, {
- wrapper: I18nProvider,
- });
+ const component = render(
+
,
+ {
+ wrapper: I18nProvider,
+ }
+ );
const isOpen = component
.queryByTestId('navFooterCollapsible-project-settings')
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation_footer.tsx b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation_footer.tsx
index 2c8cf2369c50b..0ed8c1e80f256 100644
--- a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation_footer.tsx
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/side_navigation_footer.tsx
@@ -8,50 +8,32 @@
import React, { useEffect, useMemo, useState } from 'react';
import type { EuiCollapsibleNavSubItemProps, IconType } from '@elastic/eui';
import { EuiCollapsibleNavItem } from '@elastic/eui';
-import { SecurityPageName } from '@kbn/security-solution-navigation';
-import { ExternalPageName } from '../links/constants';
+import {
+ isAccordionLinkCategory,
+ isSeparatorLinkCategory,
+ type LinkCategory,
+} from '@kbn/security-solution-navigation';
import { getNavLinkIdFromProjectPageName } from '../links/util';
import type { ProjectSideNavItem } from './types';
-interface FooterCategory {
- type: 'standalone' | 'collapsible';
- title?: string;
- icon?: IconType;
- linkIds: string[];
-}
-
-const categories: FooterCategory[] = [
- { type: 'standalone', linkIds: [SecurityPageName.landing, ExternalPageName.devTools] },
- {
- type: 'collapsible',
- title: 'Project Settings',
- icon: 'gear',
- linkIds: [
- ExternalPageName.management,
- ExternalPageName.integrationsSecurity,
- ExternalPageName.cloudUsersAndRoles,
- ExternalPageName.cloudPerformance,
- ExternalPageName.cloudBilling,
- ],
- },
-];
-
export const SideNavigationFooter: React.FC<{
activeNodeId: string;
items: ProjectSideNavItem[];
-}> = ({ activeNodeId, items }) => {
+ categories: LinkCategory[];
+}> = ({ activeNodeId, items, categories }) => {
return (
<>
{categories.map((category, index) => {
- const categoryItems = category.linkIds.reduce
((acc, linkId) => {
- const item = items.find(({ id }) => id === linkId);
- if (item) {
- acc.push(item);
- }
- return acc;
- }, []);
+ const categoryItems =
+ category.linkIds?.reduce((acc, linkId) => {
+ const item = items.find(({ id }) => id === linkId);
+ if (item) {
+ acc.push(item);
+ }
+ return acc;
+ }, []) ?? [];
- if (category.type === 'standalone') {
+ if (isSeparatorLinkCategory(category)) {
return (
);
}
- if (category.type === 'collapsible') {
+ if (isAccordionLinkCategory(category)) {
return (
);
}
diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/use_side_nav_items.ts b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/use_side_nav_items.ts
index 9b61439712221..32945e89765e1 100644
--- a/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/use_side_nav_items.ts
+++ b/x-pack/plugins/security_solution_serverless/public/navigation/side_navigation/use_side_nav_items.ts
@@ -6,27 +6,18 @@
*/
import { useCallback, useMemo } from 'react';
-import { SecurityPageName, type NavigationLink } from '@kbn/security-solution-navigation';
+import { type NavigationLink } from '@kbn/security-solution-navigation';
import { useGetLinkProps } from '@kbn/security-solution-navigation/links';
import { SolutionSideNavItemPosition } from '@kbn/security-solution-side-nav';
import { useNavLinks } from '../../common/hooks/use_nav_links';
-import { ExternalPageName } from '../links/constants';
import type { ProjectSideNavItem } from './types';
-import type { ProjectPageName } from '../links/types';
+import type { ProjectNavigationLink, ProjectPageName } from '../links/types';
+import { isBottomNavItemId } from '../links/util';
type GetLinkProps = (link: NavigationLink) => {
href: string & Partial;
};
-const isBottomNavItem = (id: string) =>
- id === SecurityPageName.landing ||
- id === ExternalPageName.devTools ||
- id === ExternalPageName.management ||
- id === ExternalPageName.integrationsSecurity ||
- id === ExternalPageName.cloudUsersAndRoles ||
- id === ExternalPageName.cloudPerformance ||
- id === ExternalPageName.cloudBilling;
-
/**
* Formats generic navigation links into the shape expected by the `SolutionSideNav`
*/
@@ -52,7 +43,7 @@ const formatLink = (
id: navLink.id,
label: navLink.title,
iconType: navLink.sideNavIcon,
- position: isBottomNavItem(navLink.id)
+ position: isBottomNavItemId(navLink.id)
? SolutionSideNavItemPosition.bottom
: SolutionSideNavItemPosition.top,
...getLinkProps(navLink),
@@ -66,6 +57,15 @@ const formatLink = (
*/
export const useSideNavItems = (): ProjectSideNavItem[] => {
const navLinks = useNavLinks();
+ return useFormattedSideNavItems(navLinks);
+};
+
+/**
+ * Returns all the formatted SideNavItems, including external links
+ */
+export const useFormattedSideNavItems = (
+ navLinks: ProjectNavigationLink[]
+): ProjectSideNavItem[] => {
const getKibanaLinkProps = useGetLinkProps();
const getLinkProps = useCallback(
From 2146a7ef1634a06cf6b4b70edc55dd22fd5b6a4c Mon Sep 17 00:00:00 2001
From: Christos Nasikas
Date: Mon, 23 Oct 2023 12:21:25 +0300
Subject: [PATCH 04/11] [Cases] Unskip MKI tests (#168924)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
---
.../kbn_client/kbn_client_saved_objects.ts | 5 +++
.../test/functional/services/cases/common.ts | 1 -
.../apps/cases/group1/view_case.ts | 21 ++++++-----
.../apps/cases/group2/attachment_framework.ts | 5 +++
.../api_integration/services/svl_cases/api.ts | 4 +--
.../observability/cases/get_case.ts | 2 +-
.../observability/cases/post_case.ts | 2 +-
.../test_suites/security/cases/get_case.ts | 2 +-
.../test_suites/security/cases/post_case.ts | 2 +-
.../cases/attachment_framework.ts | 17 +++++----
.../observability/cases/configure.ts | 5 ++-
.../observability/cases/create_case_form.ts | 5 ++-
.../test_suites/observability/cases/index.ts | 18 ++++++++++
.../observability/cases/list_view.ts | 12 +++----
.../observability/cases/view_case.ts | 31 ++++++++--------
.../test_suites/observability/index.ts | 6 +---
.../ftr/cases/attachment_framework.ts | 35 +++++++++----------
.../security/ftr/cases/configure.ts | 5 ++-
.../security/ftr/cases/create_case_form.ts | 4 +--
.../test_suites/security/ftr/cases/index.ts | 18 ++++++++++
.../security/ftr/cases/list_view.ts | 14 ++++----
.../security/ftr/cases/view_case.ts | 31 ++++++++--------
.../functional/test_suites/security/index.ts | 6 +---
.../shared/lib/cases/helpers.ts | 8 ++---
24 files changed, 149 insertions(+), 110 deletions(-)
create mode 100644 x-pack/test_serverless/functional/test_suites/observability/cases/index.ts
create mode 100644 x-pack/test_serverless/functional/test_suites/security/ftr/cases/index.ts
diff --git a/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts b/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts
index 63e60a0eb19e4..caaf072c934ca 100644
--- a/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts
+++ b/packages/kbn-test/src/kbn_client/kbn_client_saved_objects.ts
@@ -98,7 +98,12 @@ const STANDARD_LIST_TYPES = [
'lens',
'links',
'map',
+ // cases saved objects
'cases',
+ 'cases-comments',
+ 'cases-user-actions',
+ 'cases-configure',
+ 'cases-connector-mappings',
// synthetics based objects
'synthetics-monitor',
'uptime-dynamic-settings',
diff --git a/x-pack/test/functional/services/cases/common.ts b/x-pack/test/functional/services/cases/common.ts
index 4fe8523a6bb87..4809fe4278814 100644
--- a/x-pack/test/functional/services/cases/common.ts
+++ b/x-pack/test/functional/services/cases/common.ts
@@ -96,7 +96,6 @@ export function CasesCommonServiceProvider({ getService, getPageObject }: FtrPro
async expectToasterToContain(content: string) {
const toast = await toasts.getToastElement(1);
expect(await toast.getVisibleText()).to.contain(content);
- await toasts.dismissAllToasts();
},
async assertCaseModalVisible(expectVisible = true) {
diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/group1/view_case.ts b/x-pack/test/functional_with_es_ssl/apps/cases/group1/view_case.ts
index 9391c9f1e77db..77ceb4b1a2117 100644
--- a/x-pack/test/functional_with_es_ssl/apps/cases/group1/view_case.ts
+++ b/x-pack/test/functional_with_es_ssl/apps/cases/group1/view_case.ts
@@ -1154,8 +1154,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
});
- // FLAKY: https://github.com/elastic/kibana/issues/168534
- describe.skip('customFields', () => {
+ describe('customFields', () => {
const customFields = [
{
key: 'valid_key_1',
@@ -1198,13 +1197,13 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
it('updates a custom field correctly', async () => {
- const summary = await testSubjects.find(`case-text-custom-field-${customFields[0].key}`);
- expect(await summary.getVisibleText()).equal('this is a text field value');
+ const textField = await testSubjects.find(`case-text-custom-field-${customFields[0].key}`);
+ expect(await textField.getVisibleText()).equal('this is a text field value');
- const sync = await testSubjects.find(
+ const toggle = await testSubjects.find(
`case-toggle-custom-field-form-field-${customFields[1].key}`
);
- expect(await sync.getAttribute('aria-checked')).equal('true');
+ expect(await toggle.getAttribute('aria-checked')).equal('true');
await testSubjects.click(`case-text-custom-field-edit-button-${customFields[0].key}`);
@@ -1222,19 +1221,23 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
await testSubjects.click(`case-text-custom-field-submit-button-${customFields[0].key}`);
+ await header.waitUntilLoadingHasFinished();
+
await retry.waitFor('update toast exist', async () => {
return await testSubjects.exists('toastCloseButton');
});
await testSubjects.click('toastCloseButton');
- await sync.click();
+ await header.waitUntilLoadingHasFinished();
+
+ await toggle.click();
await header.waitUntilLoadingHasFinished();
- expect(await summary.getVisibleText()).equal('this is a text field value edited!!');
+ expect(await textField.getVisibleText()).equal('this is a text field value edited!!');
- expect(await sync.getAttribute('aria-checked')).equal('false');
+ expect(await toggle.getAttribute('aria-checked')).equal('false');
// validate user action
const userActions = await find.allByCssSelector(
diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/group2/attachment_framework.ts b/x-pack/test/functional_with_es_ssl/apps/cases/group2/attachment_framework.ts
index ca9c4ad3af49a..5ac08acfbc6ed 100644
--- a/x-pack/test/functional_with_es_ssl/apps/cases/group2/attachment_framework.ts
+++ b/x-pack/test/functional_with_es_ssl/apps/cases/group2/attachment_framework.ts
@@ -61,6 +61,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const dashboard = getPageObject('dashboard');
const lens = getPageObject('lens');
const listingTable = getService('listingTable');
+ const toasts = getService('toasts');
const createAttachmentAndNavigate = async (attachment: AttachmentRequest) => {
const caseData = await cases.api.createCase({
@@ -249,6 +250,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
*/
await cases.create.createCase({ owner });
await cases.common.expectToasterToContain('has been updated');
+ await toasts.dismissAllToastsWithChecks();
}
const casesCreatedFromFlyout = await findCases({ supertest });
@@ -325,6 +327,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
await testSubjects.click(`cases-table-row-select-${currentCaseId}`);
await cases.common.expectToasterToContain('has been updated');
+ await toasts.dismissAllToastsWithChecks();
await ensureFirstCommentOwner(currentCaseId, owner);
}
});
@@ -387,6 +390,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
await cases.common.expectToasterToContain(`${caseTitle} has been updated`);
await testSubjects.click('toaster-content-case-view-link');
+ await toasts.dismissAllToastsWithChecks();
const title = await find.byCssSelector('[data-test-subj="editable-title-header-value"]');
expect(await title.getVisibleText()).toEqual(caseTitle);
@@ -414,6 +418,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
await cases.common.expectToasterToContain(`${theCaseTitle} has been updated`);
await testSubjects.click('toaster-content-case-view-link');
+ await toasts.dismissAllToastsWithChecks();
const title = await find.byCssSelector('[data-test-subj="editable-title-header-value"]');
expect(await title.getVisibleText()).toEqual(theCaseTitle);
diff --git a/x-pack/test_serverless/api_integration/services/svl_cases/api.ts b/x-pack/test_serverless/api_integration/services/svl_cases/api.ts
index a504b71240dd5..8f23eba1ea981 100644
--- a/x-pack/test_serverless/api_integration/services/svl_cases/api.ts
+++ b/x-pack/test_serverless/api_integration/services/svl_cases/api.ts
@@ -79,7 +79,7 @@ export function SvlCasesApiServiceProvider({ getService }: FtrProviderContext) {
async deleteAllCaseItems() {
await Promise.all([
- this.deleteCasesByESQuery(),
+ this.deleteCases(),
this.deleteCasesUserActions(),
this.deleteComments(),
this.deleteConfiguration(),
@@ -91,7 +91,7 @@ export function SvlCasesApiServiceProvider({ getService }: FtrProviderContext) {
await kbnServer.savedObjects.clean({ types: ['cases-user-actions'] });
},
- async deleteCasesByESQuery(): Promise {
+ async deleteCases(): Promise {
await kbnServer.savedObjects.clean({ types: ['cases'] });
},
diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/cases/get_case.ts b/x-pack/test_serverless/api_integration/test_suites/observability/cases/get_case.ts
index d1f601f709af2..30fbe518085cb 100644
--- a/x-pack/test_serverless/api_integration/test_suites/observability/cases/get_case.ts
+++ b/x-pack/test_serverless/api_integration/test_suites/observability/cases/get_case.ts
@@ -14,7 +14,7 @@ export default ({ getService }: FtrProviderContext): void => {
describe('get_case', () => {
afterEach(async () => {
- await svlCases.api.deleteCasesByESQuery();
+ await svlCases.api.deleteCases();
});
it('should return a case', async () => {
diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/cases/post_case.ts b/x-pack/test_serverless/api_integration/test_suites/observability/cases/post_case.ts
index 01cf60d424f90..79f180a5092ff 100644
--- a/x-pack/test_serverless/api_integration/test_suites/observability/cases/post_case.ts
+++ b/x-pack/test_serverless/api_integration/test_suites/observability/cases/post_case.ts
@@ -15,7 +15,7 @@ export default ({ getService }: FtrProviderContext): void => {
describe('post_case', () => {
afterEach(async () => {
- await svlCases.api.deleteCasesByESQuery();
+ await svlCases.api.deleteCases();
});
it('should create a case', async () => {
diff --git a/x-pack/test_serverless/api_integration/test_suites/security/cases/get_case.ts b/x-pack/test_serverless/api_integration/test_suites/security/cases/get_case.ts
index 719841ff28ab5..fe3c02a7342a9 100644
--- a/x-pack/test_serverless/api_integration/test_suites/security/cases/get_case.ts
+++ b/x-pack/test_serverless/api_integration/test_suites/security/cases/get_case.ts
@@ -13,7 +13,7 @@ export default ({ getService }: FtrProviderContext): void => {
describe('get_case', () => {
afterEach(async () => {
- await svlCases.api.deleteCasesByESQuery();
+ await svlCases.api.deleteCases();
});
it('should return a case', async () => {
diff --git a/x-pack/test_serverless/api_integration/test_suites/security/cases/post_case.ts b/x-pack/test_serverless/api_integration/test_suites/security/cases/post_case.ts
index 77917915d4bcc..db5967654de85 100644
--- a/x-pack/test_serverless/api_integration/test_suites/security/cases/post_case.ts
+++ b/x-pack/test_serverless/api_integration/test_suites/security/cases/post_case.ts
@@ -15,7 +15,7 @@ export default ({ getService }: FtrProviderContext): void => {
describe('post_case', () => {
afterEach(async () => {
- await svlCases.api.deleteCasesByESQuery();
+ await svlCases.api.deleteCases();
});
it('should create a case', async () => {
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/attachment_framework.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/attachment_framework.ts
index cd0647a1a35d7..e1a13b456c0f3 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/attachment_framework.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/attachment_framework.ts
@@ -18,39 +18,37 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const cases = getService('cases');
+ const svlCases = getService('svlCases');
const find = getService('find');
+ const toasts = getService('toasts');
- // failing test https://github.com/elastic/kibana/issues/166592
- describe.skip('Cases persistable attachments', function () {
- // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
- this.tags(['failsOnMKI']);
+ describe('Cases persistable attachments', function () {
describe('lens visualization', () => {
before(async () => {
await svlCommonPage.login();
+ await kibanaServer.savedObjects.cleanStandardList();
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional');
await kibanaServer.importExport.load(
'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json'
);
await svlObltNavigation.navigateToLandingPage();
-
await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'dashboards' });
await dashboard.clickNewDashboard();
-
await lens.createAndAddLensFromDashboard({});
-
await dashboard.waitForRenderComplete();
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional');
await kibanaServer.importExport.unload(
'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json'
);
+ await kibanaServer.savedObjects.cleanStandardList();
await svlCommonPage.forceLogout();
});
@@ -73,8 +71,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
await testSubjects.click('create-case-submit');
await cases.common.expectToasterToContain(`${caseTitle} has been updated`);
-
await testSubjects.click('toaster-content-case-view-link');
+ await toasts.dismissAllToastsWithChecks();
if (await testSubjects.exists('appLeaveConfirmModal')) {
await testSubjects.exists('confirmModalConfirmButton');
@@ -108,6 +106,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
await cases.common.expectToasterToContain(`${theCaseTitle} has been updated`);
await testSubjects.click('toaster-content-case-view-link');
+ await toasts.dismissAllToastsWithChecks();
if (await testSubjects.exists('appLeaveConfirmModal')) {
await testSubjects.exists('confirmModalConfirmButton');
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/configure.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/configure.ts
index f63ce94ead3fe..79339d0dc345b 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/configure.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/configure.ts
@@ -16,13 +16,12 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const svlObltNavigation = getService('svlObltNavigation');
const testSubjects = getService('testSubjects');
const cases = getService('cases');
+ const svlCases = getService('svlCases');
const toasts = getService('toasts');
const retry = getService('retry');
const find = getService('find');
describe('Configure Case', function () {
- // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
- this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
await svlObltNavigation.navigateToLandingPage();
@@ -42,7 +41,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await svlCommonPage.forceLogout();
});
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/create_case_form.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/create_case_form.ts
index 58fb83ce2c877..9bc03b8e232b8 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/create_case_form.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/create_case_form.ts
@@ -16,10 +16,9 @@ const owner = OBSERVABILITY_OWNER;
export default ({ getService, getPageObject }: FtrProviderContext) => {
describe('Create Case', function () {
- // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
- this.tags(['failsOnMKI']);
const find = getService('find');
const cases = getService('cases');
+ const svlCases = getService('svlCases');
const testSubjects = getService('testSubjects');
const svlCommonPage = getPageObject('svlCommonPage');
const config = getService('config');
@@ -35,7 +34,7 @@ export default ({ getService, getPageObject }: FtrProviderContext) => {
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await svlCommonPage.forceLogout();
});
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/index.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/index.ts
new file mode 100644
index 0000000000000..801166c562b45
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/index.ts
@@ -0,0 +1,18 @@
+/*
+ * 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 { FtrProviderContext } from '../../../ftr_provider_context';
+
+export default function ({ loadTestFile }: FtrProviderContext) {
+ describe('Serverless Observability Cases', function () {
+ loadTestFile(require.resolve('./attachment_framework'));
+ loadTestFile(require.resolve('./view_case'));
+ loadTestFile(require.resolve('./configure'));
+ loadTestFile(require.resolve('./create_case_form'));
+ loadTestFile(require.resolve('./list_view'));
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/list_view.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/list_view.ts
index 30fd021e5c6f5..b001adb306a44 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/list_view.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/list_view.ts
@@ -14,13 +14,12 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const header = getPageObject('header');
const testSubjects = getService('testSubjects');
const cases = getService('cases');
+ const svlCases = getService('svlCases');
const svlCommonNavigation = getPageObject('svlCommonNavigation');
const svlCommonPage = getPageObject('svlCommonPage');
const svlObltNavigation = getService('svlObltNavigation');
describe('Cases list', function () {
- // multiple errors in after hook due to delete permission
- this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
await svlObltNavigation.navigateToLandingPage();
@@ -28,7 +27,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await cases.casesTable.waitForCasesToBeDeleted();
await svlCommonPage.forceLogout();
});
@@ -107,7 +106,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
afterEach(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await cases.casesTable.waitForCasesToBeDeleted();
});
@@ -170,7 +169,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await cases.casesTable.waitForCasesToBeDeleted();
});
@@ -274,6 +273,7 @@ const createNCasesBeforeDeleteAllAfter = (
getService: FtrProviderContext['getService']
) => {
const cases = getService('cases');
+ const svlCases = getService('svlCases');
const header = getPageObject('header');
before(async () => {
@@ -283,7 +283,7 @@ const createNCasesBeforeDeleteAllAfter = (
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await cases.casesTable.waitForCasesToBeDeleted();
});
};
diff --git a/x-pack/test_serverless/functional/test_suites/observability/cases/view_case.ts b/x-pack/test_serverless/functional/test_suites/observability/cases/view_case.ts
index a0fbd090ea97a..c60b7a8ed103c 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/cases/view_case.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/cases/view_case.ts
@@ -26,6 +26,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const header = getPageObject('header');
const testSubjects = getService('testSubjects');
const cases = getService('cases');
+ const svlCases = getService('svlCases');
const find = getService('find');
const retry = getService('retry');
@@ -34,14 +35,12 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const svlCommonPage = getPageObject('svlCommonPage');
describe('Case View', function () {
- // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
- this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await svlCommonPage.forceLogout();
});
@@ -280,7 +279,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
after(async () => {
await cases.testResources.removeKibanaSampleData('logs');
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
});
it('adds lens visualization in description', async () => {
@@ -325,7 +324,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
});
it('initially renders user actions list correctly', async () => {
@@ -437,7 +436,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
});
it('should set the cases title', async () => {
@@ -498,17 +497,17 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
afterEach(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
});
it('updates a custom field correctly', async () => {
- const summary = await testSubjects.find(`case-text-custom-field-${customFields[0].key}`);
- expect(await summary.getVisibleText()).equal('this is a text field value');
+ const textField = await testSubjects.find(`case-text-custom-field-${customFields[0].key}`);
+ expect(await textField.getVisibleText()).equal('this is a text field value');
- const sync = await testSubjects.find(
+ const toggle = await testSubjects.find(
`case-toggle-custom-field-form-field-${customFields[1].key}`
);
- expect(await sync.getAttribute('aria-checked')).equal('true');
+ expect(await toggle.getAttribute('aria-checked')).equal('true');
await testSubjects.click(`case-text-custom-field-edit-button-${customFields[0].key}`);
@@ -526,19 +525,23 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
await testSubjects.click(`case-text-custom-field-submit-button-${customFields[0].key}`);
+ await header.waitUntilLoadingHasFinished();
+
await retry.waitFor('update toast exist', async () => {
return await testSubjects.exists('toastCloseButton');
});
await testSubjects.click('toastCloseButton');
- await sync.click();
+ await header.waitUntilLoadingHasFinished();
+
+ await toggle.click();
await header.waitUntilLoadingHasFinished();
- expect(await summary.getVisibleText()).equal('this is a text field value edited!!');
+ expect(await textField.getVisibleText()).equal('this is a text field value edited!!');
- expect(await sync.getAttribute('aria-checked')).equal('false');
+ expect(await toggle.getAttribute('aria-checked')).equal('false');
// validate user action
const userActions = await find.allByCssSelector(
diff --git a/x-pack/test_serverless/functional/test_suites/observability/index.ts b/x-pack/test_serverless/functional/test_suites/observability/index.ts
index 8611ba5c3abbc..3de929288c253 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/index.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/index.ts
@@ -12,12 +12,8 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./landing_page'));
loadTestFile(require.resolve('./navigation'));
loadTestFile(require.resolve('./observability_log_explorer'));
- loadTestFile(require.resolve('./cases/attachment_framework'));
loadTestFile(require.resolve('./rules/rules_list'));
- loadTestFile(require.resolve('./cases/view_case'));
- loadTestFile(require.resolve('./cases/configure'));
- loadTestFile(require.resolve('./cases/create_case_form'));
- loadTestFile(require.resolve('./cases/list_view'));
+ loadTestFile(require.resolve('./cases'));
loadTestFile(require.resolve('./advanced_settings'));
loadTestFile(require.resolve('./infra'));
loadTestFile(require.resolve('./ml'));
diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/attachment_framework.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/attachment_framework.ts
index 6fb8c2ae94e44..24da4464e9fb3 100644
--- a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/attachment_framework.ts
+++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/attachment_framework.ts
@@ -12,40 +12,38 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const dashboard = getPageObject('dashboard');
const lens = getPageObject('lens');
const svlSecNavigation = getService('svlSecNavigation');
+ const svlCommonPage = getPageObject('svlCommonPage');
const testSubjects = getService('testSubjects');
- const esArchiver = getService('esArchiver');
- const kibanaServer = getService('kibanaServer');
const cases = getService('cases');
+ const svlCases = getService('svlCases');
const find = getService('find');
+ const retry = getService('retry');
+ const header = getPageObject('header');
+ const toasts = getService('toasts');
- // Failing
- // Issue: https://github.com/elastic/kibana/issues/165135
- describe.skip('Cases persistable attachments', () => {
+ describe('Cases persistable attachments', () => {
describe('lens visualization', () => {
before(async () => {
- await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional');
- await kibanaServer.importExport.load(
- 'x-pack/test/functional/fixtures/kbn_archiver/dashboard/feature_controls/security/security.json'
- );
-
+ await svlCommonPage.login();
await svlSecNavigation.navigateToLandingPage();
await testSubjects.click('solutionSideNavItemLink-dashboards');
+ await header.waitUntilLoadingHasFinished();
+
+ await retry.waitFor('createDashboardButton', async () => {
+ return await testSubjects.exists('createDashboardButton');
+ });
await testSubjects.click('createDashboardButton');
+ await header.waitUntilLoadingHasFinished();
await lens.createAndAddLensFromDashboard({});
-
await dashboard.waitForRenderComplete();
});
after(async () => {
- await cases.api.deleteAllCases();
-
- await esArchiver.unload('x-pack/test/functional/es_archives/logstash_functional');
- await kibanaServer.importExport.unload(
- 'x-pack/test/functional/fixtures/kbn_archiver/lens/lens_basic.json'
- );
+ await svlCases.api.deleteAllCaseItems();
+ await svlCommonPage.forceLogout();
});
it('adds lens visualization to a new case', async () => {
@@ -68,8 +66,8 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
await testSubjects.click('create-case-submit');
await cases.common.expectToasterToContain(`${caseTitle} has been updated`);
-
await testSubjects.click('toaster-content-case-view-link');
+ await toasts.dismissAllToastsWithChecks();
if (await testSubjects.exists('appLeaveConfirmModal')) {
await testSubjects.exists('confirmModalConfirmButton');
@@ -107,6 +105,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
await cases.common.expectToasterToContain(`${theCaseTitle} has been updated`);
await testSubjects.click('toaster-content-case-view-link');
+ await toasts.dismissAllToastsWithChecks();
if (await testSubjects.exists('appLeaveConfirmModal')) {
await testSubjects.exists('confirmModalConfirmButton');
diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts
index 19cd7a3ccdce0..320f83790d301 100644
--- a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts
+++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/configure.ts
@@ -15,13 +15,12 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const svlSecNavigation = getService('svlSecNavigation');
const testSubjects = getService('testSubjects');
const cases = getService('cases');
+ const svlCases = getService('svlCases');
const toasts = getService('toasts');
const retry = getService('retry');
const find = getService('find');
describe('Configure Case', function () {
- // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
- this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
await svlSecNavigation.navigateToLandingPage();
@@ -41,7 +40,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await svlCommonPage.forceLogout();
});
diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/create_case_form.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/create_case_form.ts
index 9acc6378b620e..b5ef129b467dc 100644
--- a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/create_case_form.ts
+++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/create_case_form.ts
@@ -16,9 +16,9 @@ const owner = SECURITY_SOLUTION_OWNER;
export default ({ getService, getPageObject }: FtrProviderContext) => {
describe('Create Case', function () {
- this.tags(['failsOnMKI']);
const find = getService('find');
const cases = getService('cases');
+ const svlCases = getService('svlCases');
const testSubjects = getService('testSubjects');
const config = getService('config');
const svlCommonPage = getPageObject('svlCommonPage');
@@ -34,7 +34,7 @@ export default ({ getService, getPageObject }: FtrProviderContext) => {
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await svlCommonPage.forceLogout();
});
diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/index.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/index.ts
new file mode 100644
index 0000000000000..998c74e23f121
--- /dev/null
+++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/index.ts
@@ -0,0 +1,18 @@
+/*
+ * 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 { FtrProviderContext } from '../../../../ftr_provider_context';
+
+export default function ({ loadTestFile }: FtrProviderContext) {
+ describe('Serverless Security Cases', function () {
+ loadTestFile(require.resolve('./attachment_framework'));
+ loadTestFile(require.resolve('./view_case'));
+ loadTestFile(require.resolve('./create_case_form'));
+ loadTestFile(require.resolve('./configure'));
+ loadTestFile(require.resolve('./list_view'));
+ });
+}
diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/list_view.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/list_view.ts
index 045d56b9c5dd8..8a753e5d4829a 100644
--- a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/list_view.ts
+++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/list_view.ts
@@ -14,12 +14,11 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const header = getPageObject('header');
const testSubjects = getService('testSubjects');
const cases = getService('cases');
+ const svlCases = getService('svlCases');
const svlSecNavigation = getService('svlSecNavigation');
const svlCommonPage = getPageObject('svlCommonPage');
describe('Cases List', function () {
- // multiple errors in after hook due to delete permission
- this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
@@ -29,7 +28,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await cases.casesTable.waitForCasesToBeDeleted();
await svlCommonPage.forceLogout();
});
@@ -49,8 +48,6 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
describe('bulk actions', () => {
- // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
- // action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
describe('delete', () => {
createNCasesBeforeDeleteAllAfter(8, getPageObject, getService);
@@ -110,7 +107,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
afterEach(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await cases.casesTable.waitForCasesToBeDeleted();
});
@@ -174,7 +171,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await cases.casesTable.waitForCasesToBeDeleted();
});
@@ -279,6 +276,7 @@ const createNCasesBeforeDeleteAllAfter = (
getService: FtrProviderContext['getService']
) => {
const cases = getService('cases');
+ const svlCases = getService('svlCases');
const header = getPageObject('header');
before(async () => {
@@ -288,7 +286,7 @@ const createNCasesBeforeDeleteAllAfter = (
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await cases.casesTable.waitForCasesToBeDeleted();
});
};
diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/view_case.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/view_case.ts
index 85ed51c191ba5..c3d8285857634 100644
--- a/x-pack/test_serverless/functional/test_suites/security/ftr/cases/view_case.ts
+++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cases/view_case.ts
@@ -26,6 +26,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const header = getPageObject('header');
const testSubjects = getService('testSubjects');
const cases = getService('cases');
+ const svlCases = getService('svlCases');
const find = getService('find');
const retry = getService('retry');
@@ -34,14 +35,12 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
const svlCommonPage = getPageObject('svlCommonPage');
describe('Case View', function () {
- // security_exception: action [indices:data/write/delete/byquery] is unauthorized for user [elastic] with effective roles [superuser] on restricted indices [.kibana_alerting_cases], this action is granted by the index privileges [delete,write,all]
- this.tags(['failsOnMKI']);
before(async () => {
await svlCommonPage.login();
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
await svlCommonPage.forceLogout();
});
@@ -279,7 +278,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
after(async () => {
await cases.testResources.removeKibanaSampleData('logs');
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
});
it('adds lens visualization in description', async () => {
@@ -324,7 +323,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
});
it('initially renders user actions list correctly', async () => {
@@ -436,7 +435,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
});
it('should set the cases title', async () => {
@@ -498,17 +497,17 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
});
afterEach(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
});
it('updates a custom field correctly', async () => {
- const summary = await testSubjects.find(`case-text-custom-field-${customFields[0].key}`);
- expect(await summary.getVisibleText()).equal('this is a text field value');
+ const textField = await testSubjects.find(`case-text-custom-field-${customFields[0].key}`);
+ expect(await textField.getVisibleText()).equal('this is a text field value');
- const sync = await testSubjects.find(
+ const toggle = await testSubjects.find(
`case-toggle-custom-field-form-field-${customFields[1].key}`
);
- expect(await sync.getAttribute('aria-checked')).equal('true');
+ expect(await toggle.getAttribute('aria-checked')).equal('true');
await testSubjects.click(`case-text-custom-field-edit-button-${customFields[0].key}`);
@@ -526,19 +525,23 @@ export default ({ getPageObject, getService }: FtrProviderContext) => {
await testSubjects.click(`case-text-custom-field-submit-button-${customFields[0].key}`);
+ await header.waitUntilLoadingHasFinished();
+
await retry.waitFor('update toast exist', async () => {
return await testSubjects.exists('toastCloseButton');
});
await testSubjects.click('toastCloseButton');
- await sync.click();
+ await header.waitUntilLoadingHasFinished();
+
+ await toggle.click();
await header.waitUntilLoadingHasFinished();
- expect(await summary.getVisibleText()).equal('this is a text field value edited!!');
+ expect(await textField.getVisibleText()).equal('this is a text field value edited!!');
- expect(await sync.getAttribute('aria-checked')).equal('false');
+ expect(await toggle.getAttribute('aria-checked')).equal('false');
// validate user action
const userActions = await find.allByCssSelector(
diff --git a/x-pack/test_serverless/functional/test_suites/security/index.ts b/x-pack/test_serverless/functional/test_suites/security/index.ts
index cadf61fdf5eda..fabe48960b111 100644
--- a/x-pack/test_serverless/functional/test_suites/security/index.ts
+++ b/x-pack/test_serverless/functional/test_suites/security/index.ts
@@ -11,11 +11,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
describe('serverless security UI', function () {
loadTestFile(require.resolve('./ftr/landing_page'));
loadTestFile(require.resolve('./ftr/navigation'));
- loadTestFile(require.resolve('./ftr/cases/attachment_framework'));
- loadTestFile(require.resolve('./ftr/cases/view_case'));
- loadTestFile(require.resolve('./ftr/cases/create_case_form'));
- loadTestFile(require.resolve('./ftr/cases/configure'));
- loadTestFile(require.resolve('./ftr/cases/list_view'));
+ loadTestFile(require.resolve('./ftr/cases'));
loadTestFile(require.resolve('./advanced_settings'));
loadTestFile(require.resolve('./ml'));
});
diff --git a/x-pack/test_serverless/shared/lib/cases/helpers.ts b/x-pack/test_serverless/shared/lib/cases/helpers.ts
index 98dcfb6d31ebc..5cc4aa637ec43 100644
--- a/x-pack/test_serverless/shared/lib/cases/helpers.ts
+++ b/x-pack/test_serverless/shared/lib/cases/helpers.ts
@@ -13,14 +13,14 @@ export const createOneCaseBeforeDeleteAllAfter = (
getService: FtrProviderContext['getService'],
owner: string
) => {
- const cases = getService('cases');
+ const svlCases = getService('svlCases');
before(async () => {
await createAndNavigateToCase(getPageObject, getService, owner);
});
after(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
});
};
@@ -29,14 +29,14 @@ export const createOneCaseBeforeEachDeleteAllAfterEach = (
getService: FtrProviderContext['getService'],
owner: string
) => {
- const cases = getService('cases');
+ const svlCases = getService('svlCases');
beforeEach(async () => {
await createAndNavigateToCase(getPageObject, getService, owner);
});
afterEach(async () => {
- await cases.api.deleteAllCases();
+ await svlCases.api.deleteAllCaseItems();
});
};
From 71bbbacd594885553ae10fc964ad1be9e0f63cf2 Mon Sep 17 00:00:00 2001
From: Sid
Date: Mon, 23 Oct 2023 11:51:43 +0200
Subject: [PATCH 05/11] Add screen reader attributes for API key flyout code
editors (#169265)
## Summary
Closes #169127
## Fixes
- Added aria-labels for Access control, Role Descriptors and Metadata
code editors
---
.../api_keys/api_keys_grid/api_key_flyout.tsx | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_key_flyout.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_key_flyout.tsx
index b3792941fc6d0..b6287b423589c 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_key_flyout.tsx
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_key_flyout.tsx
@@ -437,6 +437,12 @@ export const ApiKeyFlyout: FunctionComponent = ({
formik.setFieldValue('access', value)}
@@ -500,6 +506,12 @@ export const ApiKeyFlyout: FunctionComponent = ({
@@ -631,6 +643,13 @@ export const ApiKeyFlyout: FunctionComponent = ({
formik.setFieldValue('metadata', value)}
From 6adb8bde2b969671abddb66a019b0253bc511cfd Mon Sep 17 00:00:00 2001
From: Lisa Cawley
Date: Mon, 23 Oct 2023 04:27:16 -0700
Subject: [PATCH 06/11] [DOCS] Automate Observability and Security case setting
screenshots (#168774)
---
.../observability_cases/custom_fields.ts | 40 +++++++++++++++++++
.../observability_cases/index.ts | 1 +
.../observability_cases/list_view.ts | 6 +++
.../security_cases/custom_fields.ts | 39 ++++++++++++++++++
.../response_ops_docs/security_cases/index.ts | 1 +
5 files changed, 87 insertions(+)
create mode 100644 x-pack/test/screenshot_creation/apps/response_ops_docs/observability_cases/custom_fields.ts
create mode 100644 x-pack/test/screenshot_creation/apps/response_ops_docs/security_cases/custom_fields.ts
diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/observability_cases/custom_fields.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/observability_cases/custom_fields.ts
new file mode 100644
index 0000000000000..1cd8ae736afbc
--- /dev/null
+++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/observability_cases/custom_fields.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 { FtrProviderContext } from '../../../ftr_provider_context';
+
+export default function ({ getPageObjects, getService }: FtrProviderContext) {
+ const cases = getService('cases');
+ const commonScreenshots = getService('commonScreenshots');
+ const pageObjects = getPageObjects(['common', 'header']);
+ const screenshotDirectories = ['response_ops_docs', 'observability_cases'];
+ const testSubjects = getService('testSubjects');
+
+ describe('Observability case settings and custom fields', function () {
+ it('case settings screenshots', async () => {
+ await cases.navigation.navigateToApp('observability/cases', 'cases-all-title');
+ await pageObjects.header.waitUntilLoadingHasFinished();
+ await testSubjects.click('configure-case-button');
+ await commonScreenshots.takeScreenshot('cases-settings', screenshotDirectories);
+ await testSubjects.click('add-custom-field');
+ await commonScreenshots.takeScreenshot(
+ 'cases-add-custom-field',
+ screenshotDirectories,
+ 1400,
+ 600
+ );
+ await testSubjects.setValue('custom-field-label-input', 'my-field');
+ await testSubjects.click('custom-field-flyout-save');
+ await commonScreenshots.takeScreenshot(
+ 'cases-custom-field-settings',
+ screenshotDirectories,
+ 1400,
+ 1024
+ );
+ });
+ });
+}
diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/observability_cases/index.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/observability_cases/index.ts
index 0e0e81dca10c9..2b513391209a5 100644
--- a/x-pack/test/screenshot_creation/apps/response_ops_docs/observability_cases/index.ts
+++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/observability_cases/index.ts
@@ -10,5 +10,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('observability cases', function () {
loadTestFile(require.resolve('./list_view'));
+ loadTestFile(require.resolve('./custom_fields'));
});
}
diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/observability_cases/list_view.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/observability_cases/list_view.ts
index 309c17f6ee498..e156ef4fbfa30 100644
--- a/x-pack/test/screenshot_creation/apps/response_ops_docs/observability_cases/list_view.ts
+++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/observability_cases/list_view.ts
@@ -96,5 +96,11 @@ export default function ({ getPageObject, getService }: FtrProviderContext) {
1024
);
});
+
+ it('case settings screenshot', async () => {
+ await cases.navigation.navigateToApp('observability/cases', 'cases-all-title');
+ await testSubjects.click('configure-case-button');
+ await commonScreenshots.takeScreenshot('add-case-connector', screenshotDirectories);
+ });
});
}
diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/security_cases/custom_fields.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/security_cases/custom_fields.ts
new file mode 100644
index 0000000000000..412f102353cc0
--- /dev/null
+++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/security_cases/custom_fields.ts
@@ -0,0 +1,39 @@
+/*
+ * 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 { FtrProviderContext } from '../../../ftr_provider_context';
+
+export default function ({ getService, getPageObjects }: FtrProviderContext) {
+ const commonScreenshots = getService('commonScreenshots');
+ const pageObjects = getPageObjects(['common', 'header']);
+ const screenshotDirectories = ['response_ops_docs', 'security_cases'];
+ const testSubjects = getService('testSubjects');
+
+ describe('Security case settings and custom fields', function () {
+ it('case settings screenshots', async () => {
+ await pageObjects.common.navigateToApp('security', { path: 'cases' });
+ await pageObjects.header.waitUntilLoadingHasFinished();
+ await testSubjects.click('configure-case-button');
+ await commonScreenshots.takeScreenshot('cases-settings', screenshotDirectories);
+ await testSubjects.click('add-custom-field');
+ await commonScreenshots.takeScreenshot(
+ 'cases-add-custom-field',
+ screenshotDirectories,
+ 1400,
+ 600
+ );
+ await testSubjects.setValue('custom-field-label-input', 'my-field');
+ await testSubjects.click('custom-field-flyout-save');
+ await commonScreenshots.takeScreenshot(
+ 'cases-custom-field-settings',
+ screenshotDirectories,
+ 1400,
+ 1024
+ );
+ });
+ });
+}
diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/security_cases/index.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/security_cases/index.ts
index dc038fc7cf46f..599b8f49c6b09 100644
--- a/x-pack/test/screenshot_creation/apps/response_ops_docs/security_cases/index.ts
+++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/security_cases/index.ts
@@ -10,5 +10,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('security cases', function () {
loadTestFile(require.resolve('./list_view'));
+ loadTestFile(require.resolve('./custom_fields'));
});
}
From 672d8f2caa798f213aa5d20a49f6d7232f74a7ee Mon Sep 17 00:00:00 2001
From: Abdul Wahab Zahid
Date: Mon, 23 Oct 2023 14:06:21 +0200
Subject: [PATCH 07/11] [Synthetics] Fix monitor error duration on Monitor
Errors page (#168755)
Fixes #168724
## Summary
The PR fixes the issue by sorting the data as needed.
---
.../components/monitor_details/hooks/use_monitor_errors.tsx | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_errors.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_errors.tsx
index 969e98a21c36c..5a7673f0513d5 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_errors.tsx
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_monitor_errors.tsx
@@ -120,9 +120,13 @@ export function useMonitorErrors(monitorIdArg?: string) {
(hits[0]?._source as Ping).monitor?.status === 'down' &&
!!errorStates?.length;
+ const upStatesSortedAsc = upStates.sort(
+ (a, b) => Number(new Date(a.state.started_at)) - Number(new Date(b.state.started_at))
+ );
+
return {
errorStates,
- upStates,
+ upStates: upStatesSortedAsc,
loading,
data,
hasActiveError,
From 4bf4c05b482edde65f1d4532447ed3c6b5068f1f Mon Sep 17 00:00:00 2001
From: Yngrid Coello
Date: Mon, 23 Oct 2023 14:22:12 +0200
Subject: [PATCH 08/11] [APM] conditionally waiting for index status (#166438)
Closes https://github.com/elastic/kibana/issues/166831.
Checking for cluster health is not available on serverless. (More
context in this [internal
conversation](https://elastic.slack.com/archives/C877NKGBY/p1694595738607269?thread_ts=1694006744.669319&cid=C877NKGBY))
This Pr aims to conditionally waitForIndex status only if we are in a
non-serverless deployment
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
---
.../create_anomaly_detection_jobs.ts | 24 ++++++++++++++-----
.../server/lib/helpers/get_es_capabilities.ts | 14 +++++++++++
.../settings/anomaly_detection/route.ts | 14 ++++++++++-
.../anomaly_detection/update_to_v3.ts | 4 ++++
4 files changed, 49 insertions(+), 7 deletions(-)
create mode 100644 x-pack/plugins/apm/server/lib/helpers/get_es_capabilities.ts
diff --git a/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts b/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts
index e90776a4ee666..347a6de3b7a3f 100644
--- a/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts
+++ b/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts
@@ -13,6 +13,7 @@ import { v4 as uuidv4 } from 'uuid';
import { ProcessorEvent } from '@kbn/observability-plugin/common';
import { waitForIndexStatus } from '@kbn/core-saved-objects-migration-server-internal';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
+import { ElasticsearchCapabilities } from '@kbn/core-elasticsearch-server';
import { ML_ERRORS } from '../../../common/anomaly_detection';
import { METRICSET_NAME, PROCESSOR_EVENT } from '../../../common/es_fields/apm';
import { Environment } from '../../../common/environment_rt';
@@ -30,12 +31,14 @@ export async function createAnomalyDetectionJobs({
indices,
environments,
logger,
+ esCapabilities,
}: {
mlClient?: MlClient;
esClient: ElasticsearchClient;
indices: APMIndices;
environments: Environment[];
logger: Logger;
+ esCapabilities: ElasticsearchCapabilities;
}) {
if (!mlClient) {
throw Boom.notImplemented(ML_ERRORS.ML_NOT_AVAILABLE);
@@ -68,6 +71,7 @@ export async function createAnomalyDetectionJobs({
esClient,
environment,
apmMetricIndex,
+ esCapabilities,
})
);
} catch (e) {
@@ -97,12 +101,16 @@ async function createAnomalyDetectionJob({
esClient,
environment,
apmMetricIndex,
+ esCapabilities,
}: {
mlClient: Required;
esClient: ElasticsearchClient;
environment: string;
apmMetricIndex: string;
+ esCapabilities: ElasticsearchCapabilities;
}) {
+ const { serverless } = esCapabilities;
+
return withApmSpan('create_anomaly_detection_job', async () => {
const randomToken = uuidv4().substr(-4);
@@ -136,12 +144,16 @@ async function createAnomalyDetectionJob({
],
});
- await waitForIndexStatus({
- client: esClient,
- index: '.ml-*',
- timeout: DEFAULT_TIMEOUT,
- status: 'yellow',
- })();
+ // Waiting for the index is not enabled in serverless, this could potentially cause
+ // problems when creating jobs in parallels
+ if (!serverless) {
+ await waitForIndexStatus({
+ client: esClient,
+ index: '.ml-*',
+ timeout: DEFAULT_TIMEOUT,
+ status: 'yellow',
+ })();
+ }
return anomalyDetectionJob;
});
diff --git a/x-pack/plugins/apm/server/lib/helpers/get_es_capabilities.ts b/x-pack/plugins/apm/server/lib/helpers/get_es_capabilities.ts
new file mode 100644
index 0000000000000..ab262270075f0
--- /dev/null
+++ b/x-pack/plugins/apm/server/lib/helpers/get_es_capabilities.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { APMRouteHandlerResources } from '../../routes/apm_routes/register_apm_server_routes';
+
+export async function getESCapabilities({ core }: APMRouteHandlerResources) {
+ const es = (await core.start()).elasticsearch;
+
+ return es.getCapabilities();
+}
diff --git a/x-pack/plugins/apm/server/routes/settings/anomaly_detection/route.ts b/x-pack/plugins/apm/server/routes/settings/anomaly_detection/route.ts
index 8ad3d6bb92b6c..33b8897bd5aea 100644
--- a/x-pack/plugins/apm/server/routes/settings/anomaly_detection/route.ts
+++ b/x-pack/plugins/apm/server/routes/settings/anomaly_detection/route.ts
@@ -9,6 +9,7 @@ import * as t from 'io-ts';
import Boom from '@hapi/boom';
import { maxSuggestions } from '@kbn/observability-plugin/common';
import { ElasticsearchClient } from '@kbn/core/server';
+import { getESCapabilities } from '../../../lib/helpers/get_es_capabilities';
import { isActivePlatinumLicense } from '../../../../common/license_check';
import { ML_ERRORS } from '../../../../common/anomaly_detection';
import { createApmServerRoute } from '../../apm_routes/create_apm_server_route';
@@ -72,6 +73,8 @@ const createAnomalyDetectionJobsRoute = createApmServerRoute({
const licensingContext = await context.licensing;
const esClient = (await context.core).elasticsearch.client;
+ const esCapabilities = await getESCapabilities(resources);
+
const [mlClient, indices] = await Promise.all([
getMlClient(resources),
getApmIndices(),
@@ -87,6 +90,7 @@ const createAnomalyDetectionJobsRoute = createApmServerRoute({
indices,
environments,
logger,
+ esCapabilities,
});
notifyFeatureUsage({
@@ -149,10 +153,18 @@ const anomalyDetectionUpdateToV3Route = createApmServerRoute({
),
]);
+ const esCapabilities = await getESCapabilities(resources);
+
const { logger } = resources;
return {
- update: await updateToV3({ mlClient, logger, indices, esClient }),
+ update: await updateToV3({
+ mlClient,
+ logger,
+ indices,
+ esClient,
+ esCapabilities,
+ }),
};
},
});
diff --git a/x-pack/plugins/apm/server/routes/settings/anomaly_detection/update_to_v3.ts b/x-pack/plugins/apm/server/routes/settings/anomaly_detection/update_to_v3.ts
index 577273cbfb03c..e1b621766293f 100644
--- a/x-pack/plugins/apm/server/routes/settings/anomaly_detection/update_to_v3.ts
+++ b/x-pack/plugins/apm/server/routes/settings/anomaly_detection/update_to_v3.ts
@@ -10,6 +10,7 @@ import pLimit from 'p-limit';
import { ElasticsearchClient } from '@kbn/core/server';
import { JOB_STATE } from '@kbn/ml-plugin/common';
import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
+import { ElasticsearchCapabilities } from '@kbn/core-elasticsearch-server';
import { createAnomalyDetectionJobs } from '../../../lib/anomaly_detection/create_anomaly_detection_jobs';
import { getAnomalyDetectionJobs } from '../../../lib/anomaly_detection/get_anomaly_detection_jobs';
import { MlClient } from '../../../lib/helpers/get_ml_client';
@@ -20,11 +21,13 @@ export async function updateToV3({
indices,
mlClient,
esClient,
+ esCapabilities,
}: {
logger: Logger;
mlClient?: MlClient;
indices: APMIndices;
esClient: ElasticsearchClient;
+ esCapabilities: ElasticsearchCapabilities;
}) {
const allJobs = await getAnomalyDetectionJobs(mlClient);
@@ -63,6 +66,7 @@ export async function updateToV3({
indices,
environments,
logger,
+ esCapabilities,
});
return true;
From 8805e74f5d957ac5751227c5463032cc82794852 Mon Sep 17 00:00:00 2001
From: Kevin Logan <56395104+kevinlog@users.noreply.github.com>
Date: Mon, 23 Oct 2023 08:34:34 -0400
Subject: [PATCH 09/11] [EDR Workflows] Catch docker container deletion error
(#168982)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
This PR will have our `after:run` task catch potential errors when
cleaning up the docker container used.
There were instances where our tests were failing to complete properly
because we were failing at this step:
https://buildkite.com/elastic/kibana-pull-request/builds/168052#018b37fc-b2b1-4986-8c11-0701f447d972/6-2209
```
An error was thrown in your plugins file while executing the handler for the after:run event.
--
 | Â
 | The error we received was:
 | Â
 | Error: Command failed with exit code 1: docker kill 4080ac06a71871298f2a3163c915f0170e8f3dd9f6814e022d727e9958401361
 | Error response from daemon: Cannot kill container: 4080ac06a71871298f2a3163c915f0170e8f3dd9f6814e022d727e9958401361: No such container: 4080ac06a71871298f2a3163c915f0170e8f3dd9f6814e022d727e9958401361
 | at makeError (/opt/local-ssd/buildkite/builds/kb-n2-4-virt-debbcb53f059032d/elastic/kibana-pull-request/kibana/node_modules/execa/lib/error.js:60:11)
 | at Function.module.exports.sync (/opt/local-ssd/buildkite/builds/kb-n2-4-virt-debbcb53f059032d/elastic/kibana-pull-request/kibana/node_modules/execa/index.js:194:17)
 | at Object.handler (/opt/local-ssd/buildkite/builds/kb-n2-4-virt-debbcb53f059032d/elastic/kibana-pull-request/kibana/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts:321:13)
 | at invoke (/var/lib/buildkite-agent/.cache/Cypress/13.3.0/Cypress/resources/app/node_modules/@packages/server/lib/plugins/child/run_plugins.js:183:18)
 | at /var/lib/buildkite-agent/.cache/Cypress/13.3.0/Cypress/resources/app/node_modules/@packages/server/lib/plugins/util.js:59:14
 | at tryCatcher (/var/lib/buildkite-agent/.cache/Cypress/13.3.0/Cypress/resources/app/node_modules/bluebird/js/release/util.js:16:23)
 | at Function.Promise.attempt.Promise.try (/var/lib/buildkite-agent/.cache/Cypress/13.3.0/Cypress/resources/app/node_modules/bluebird/js/release/method.js:39:29)
 | at Object.wrapChildPromise (/var/lib/buildkite-agent/.cache/Cypress/13.3.0/Cypress/resources/app/node_modules/@packages/server/lib/plugins/util.js:58:23)
 | at RunPlugins.execute (/var/lib/buildkite-agent/.cache/Cypress/13.3.0/Cypress/resources/app/node_modules/@packages/server/lib/plugins/child/run_plugins.js:164:21)
 | at EventEmitter. (/var/lib/buildkite-agent/.cache/Cypress/13.3.0/Cypress/resources/app/node_modules/@packages/server/lib/plugins/child/run_plugins.js:56:12)
 | at EventEmitter.emit (node:events:514:28)
 | at EventEmitter.emit (node:domain:489:12)
 | at process. (/var/lib/buildkite-agent/.cache/Cypress/13.3.0/Cypress/resources/app/node_modules/@packages/server/lib/plugins/util.js:33:22)
 | at process.emit (node:events:514:28)
 | at process.emit (node:domain:489:12)
 | at process.emit.sharedData.processEmitHook.installedValue [as emit] (/var/lib/buildkite-agent/.cache/Cypress/13.3.0/Cypress/resources/app/node_modules/@cspotcode/source-map-support/source-map-support.js:745:40)
 | at emit (node:internal/child_process:937:14)
 | at processTicksAndRejections (node:internal/process/task_queues:83:21)
```
In addition, I have a flaky test runner to ensure that we don't fail due
to this error again:
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3527
2nd flaky run:
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3535
3rd flaky run ✅ :
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3592#_
- Note this run has a couple failures but they are on unrelated flaky
tests that are being addressed in other PRs.
### Checklist
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Patryk Kopyciński
---
.../public/management/cypress/support/data_loaders.ts | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts b/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts
index 93614b6dbb86d..82f9af7e114d2 100644
--- a/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts
+++ b/x-pack/plugins/security_solution/public/management/cypress/support/data_loaders.ts
@@ -316,9 +316,14 @@ export const dataLoadersForRealEndpoints = (
fleetServerContainerId = data?.fleetServerContainerId;
});
- on('after:run', () => {
+ on('after:run', async () => {
+ const { log } = await stackServicesPromise;
if (fleetServerContainerId) {
- execa.sync('docker', ['kill', fleetServerContainerId]);
+ try {
+ execa.sync('docker', ['kill', fleetServerContainerId]);
+ } catch (error) {
+ log.error(error);
+ }
}
});
From 882e0bf81a5b415671c82f0b980de672fb05f630 Mon Sep 17 00:00:00 2001
From: Shahzad
Date: Mon, 23 Oct 2023 15:14:37 +0200
Subject: [PATCH 10/11] [Uptime] Settings public API (#163400)
---
docs/api/uptime-api.asciidoc | 11 ++
docs/api/uptime/get-settings.asciidoc | 39 ++++++
docs/api/uptime/update-settings.asciidoc | 117 ++++++++++++++++++
docs/user/api.asciidoc | 1 +
.../common/constants/synthetics/rest_api.ts | 2 +-
.../journeys/overview_scrolling.journey.ts | 1 -
.../apps/synthetics/state/settings/api.ts | 17 ++-
.../public/utils/api_service/api_service.ts | 26 ++--
.../uptime/common/constants/rest_api.ts | 2 +-
x-pack/plugins/uptime/common/constants/ui.ts | 2 +
.../state/api/dynamic_settings.ts | 14 ++-
.../lib/saved_objects/saved_objects.ts | 26 ++--
.../routes/dynamic_settings.test.ts | 83 -------------
.../legacy_uptime/routes/dynamic_settings.ts | 104 ++++++----------
.../server/legacy_uptime/routes/index.ts | 7 +-
.../server/legacy_uptime/uptime_server.ts | 79 +++++++++++-
.../observability/synthetics_rule.ts | 4 +-
.../apis/uptime/rest/dynamic_settings.ts | 15 ++-
18 files changed, 365 insertions(+), 185 deletions(-)
create mode 100644 docs/api/uptime-api.asciidoc
create mode 100644 docs/api/uptime/get-settings.asciidoc
create mode 100644 docs/api/uptime/update-settings.asciidoc
delete mode 100644 x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.test.ts
diff --git a/docs/api/uptime-api.asciidoc b/docs/api/uptime-api.asciidoc
new file mode 100644
index 0000000000000..afc5013cb9af7
--- /dev/null
+++ b/docs/api/uptime-api.asciidoc
@@ -0,0 +1,11 @@
+[[uptime-apis]]
+== Uptime APIs
+
+The following APIs are available for Uptime.
+
+* <> to get a settings
+
+* <> to update the attributes for existing settings
+
+include::uptime/get-settings.asciidoc[leveloffset=+1]
+include::uptime/update-settings.asciidoc[leveloffset=+1]
diff --git a/docs/api/uptime/get-settings.asciidoc b/docs/api/uptime/get-settings.asciidoc
new file mode 100644
index 0000000000000..1b193c9f338b5
--- /dev/null
+++ b/docs/api/uptime/get-settings.asciidoc
@@ -0,0 +1,39 @@
+[[get-settings-api]]
+== Get settings API
+++++
+Get settings
+++++
+
+Retrieve uptime settings existing settings.
+
+[[get-settings-api-request]]
+=== {api-request-title}
+
+`GET :/api/uptime/settings`
+
+`GET :/s//api/uptime/settings`
+
+=== {api-prereq-title}
+
+You must have `read` privileges for the *uptime* feature in *{observability}* section of the
+<>.
+
+The API returns the following:
+
+[source,sh]
+--------------------------------------------------
+{
+ "heartbeatIndices": "heartbeat-8*",
+ "certExpirationThreshold": 30,
+ "certAgeThreshold": 730,
+ "defaultConnectors": [
+ "08990f40-09c5-11ee-97ae-912b222b13d4",
+ "db25f830-2318-11ee-9391-6b0c030836d6"
+ ],
+ "defaultEmail": {
+ "to": [],
+ "cc": [],
+ "bcc": []
+ }
+}
+--------------------------------------------------
diff --git a/docs/api/uptime/update-settings.asciidoc b/docs/api/uptime/update-settings.asciidoc
new file mode 100644
index 0000000000000..c3dc34a8c150c
--- /dev/null
+++ b/docs/api/uptime/update-settings.asciidoc
@@ -0,0 +1,117 @@
+[[update-settings-api]]
+== Update settings API
+++++
+Update settings
+++++
+
+Updates uptime settings attributes like heartbeatIndices, certExpirationThreshold, certAgeThreshold, defaultConnectors or defaultEmail
+
+=== {api-request-title}
+
+`PUT :/api/uptime/settings`
+
+`PUT :/s//api/uptime/settings`
+
+=== {api-prereq-title}
+
+You must have `all` privileges for the *uptime* feature in *{observability}* section of the
+<>.
+
+[[settings-api-update-path-params]]
+==== Path parameters
+
+`space_id`::
+(Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used.
+
+[[api-update-request-body]]
+==== Request body
+
+A partial update is supported, provided settings keys will be merged with existing settings.
+
+`heartbeatIndices`::
+(Optional, string) index pattern string to be used within uptime app/alerts to query heartbeat data. Defaults to `heartbeat-*`.
+
+
+`certExpirationThreshold`::
+(Optional, number) Number of days before a certificate expires to trigger an alert. Defaults to `30`.
+
+`certAgeThreshold`::
+(Optional, number) Number of days after a certificate is created to trigger an alert. Defaults to `730`.
+
+`defaultConnectors`::
+(Optional, array) List of connector IDs to be used as default connectors for new alerts. Defaults to `[]`.
+
+`defaultEmail`::
+(Optional, object) Default email configuration for new alerts. Defaults to `{"to": [], "cc": [], "bcc": []}`.
+
+[[settings-api-update-example]]
+==== Example
+
+[source,sh]
+--------------------------------------------------
+PUT api/uptime/settings
+{
+ "heartbeatIndices": "heartbeat-8*",
+ "certExpirationThreshold": 30,
+ "certAgeThreshold": 730,
+ "defaultConnectors": [
+ "08990f40-09c5-11ee-97ae-912b222b13d4",
+ "db25f830-2318-11ee-9391-6b0c030836d6"
+ ],
+ "defaultEmail": {
+ "to": [],
+ "cc": [],
+ "bcc": []
+ }
+}
+--------------------------------------------------
+
+The API returns the following:
+
+[source,json]
+--------------------------------------------------
+{
+ "heartbeatIndices": "heartbeat-8*",
+ "certExpirationThreshold": 30,
+ "certAgeThreshold": 730,
+ "defaultConnectors": [
+ "08990f40-09c5-11ee-97ae-912b222b13d4",
+ "db25f830-2318-11ee-9391-6b0c030836d6"
+ ],
+ "defaultEmail": {
+ "to": [],
+ "cc": [],
+ "bcc": []
+ }
+}
+--------------------------------------------------
+[[settings-api-partial-update-example]]
+==== Partial update example
+
+[source,sh]
+--------------------------------------------------
+PUT api/uptime/settings
+{
+ "heartbeatIndices": "heartbeat-8*",
+}
+--------------------------------------------------
+
+The API returns the following:
+
+[source,json]
+--------------------------------------------------
+{
+ "heartbeatIndices": "heartbeat-8*",
+ "certExpirationThreshold": 30,
+ "certAgeThreshold": 730,
+ "defaultConnectors": [
+ "08990f40-09c5-11ee-97ae-912b222b13d4",
+ "db25f830-2318-11ee-9391-6b0c030836d6"
+ ],
+ "defaultEmail": {
+ "to": [],
+ "cc": [],
+ "bcc": []
+ }
+}
+--------------------------------------------------
\ No newline at end of file
diff --git a/docs/user/api.asciidoc b/docs/user/api.asciidoc
index 32e4115fe59dc..4358c448f3634 100644
--- a/docs/user/api.asciidoc
+++ b/docs/user/api.asciidoc
@@ -109,3 +109,4 @@ include::{kib-repo-dir}/api/osquery-manager.asciidoc[]
include::{kib-repo-dir}/api/short-urls.asciidoc[]
include::{kib-repo-dir}/api/task-manager/health.asciidoc[]
include::{kib-repo-dir}/api/upgrade-assistant.asciidoc[]
+include::{kib-repo-dir}/api/uptime-api.asciidoc[]
diff --git a/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts b/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts
index 02df09faae126..40ae2656e2b26 100644
--- a/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts
+++ b/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts
@@ -48,5 +48,5 @@ export enum SYNTHETICS_API_URLS {
SYNTHETICS_MONITORS_PROJECT_UPDATE = '/api/synthetics/project/{projectName}/monitors/_bulk_update',
SYNTHETICS_MONITORS_PROJECT_DELETE = '/api/synthetics/project/{projectName}/monitors/_bulk_delete',
- DYNAMIC_SETTINGS = `/internal/uptime/dynamic_settings`,
+ DYNAMIC_SETTINGS = `/api/uptime/settings`,
}
diff --git a/x-pack/plugins/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts b/x-pack/plugins/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts
index 59c47cf9ce20b..7bec9a7a4b389 100644
--- a/x-pack/plugins/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts
+++ b/x-pack/plugins/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts
@@ -24,7 +24,6 @@ journey('OverviewScrolling', async ({ page, params }) => {
const listOfRequests: string[] = [];
const expected = [
'http://localhost:5620/internal/synthetics/service/enablement',
- 'http://localhost:5620/internal/uptime/dynamic_settings',
'http://localhost:5620/internal/synthetics/monitor/filters',
'http://localhost:5620/internal/uptime/service/locations',
'http://localhost:5620/internal/synthetics/overview?sortField=status&sortOrder=asc&',
diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts
index 46e8111c8ca9f..62bc2e4226087 100644
--- a/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts
+++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/settings/api.ts
@@ -21,20 +21,29 @@ import {
import { SYNTHETICS_API_URLS } from '../../../../../common/constants';
import { LocationMonitor } from '.';
-const apiPath = SYNTHETICS_API_URLS.DYNAMIC_SETTINGS;
-
interface SaveApiRequest {
settings: DynamicSettings;
}
export const getDynamicSettings = async (): Promise => {
- return await apiService.get(apiPath, undefined, DynamicSettingsCodec);
+ return await apiService.get(
+ SYNTHETICS_API_URLS.DYNAMIC_SETTINGS,
+ { version: '2023-10-31' },
+ DynamicSettingsCodec
+ );
};
export const setDynamicSettings = async ({
settings,
}: SaveApiRequest): Promise => {
- return await apiService.post(apiPath, settings, DynamicSettingsSaveCodec);
+ return await apiService.put(
+ SYNTHETICS_API_URLS.DYNAMIC_SETTINGS,
+ settings,
+ DynamicSettingsSaveCodec,
+ {
+ version: '2023-10-31',
+ }
+ );
};
export const fetchLocationMonitors = async (): Promise => {
diff --git a/x-pack/plugins/synthetics/public/utils/api_service/api_service.ts b/x-pack/plugins/synthetics/public/utils/api_service/api_service.ts
index 9dead89809ee9..f1eb2607dd25b 100644
--- a/x-pack/plugins/synthetics/public/utils/api_service/api_service.ts
+++ b/x-pack/plugins/synthetics/public/utils/api_service/api_service.ts
@@ -9,7 +9,7 @@ import { isRight } from 'fp-ts/lib/Either';
import { formatErrors } from '@kbn/securitysolution-io-ts-utils';
import { HttpFetchQuery, HttpSetup } from '@kbn/core/public';
import { FETCH_STATUS, AddInspectorRequest } from '@kbn/observability-shared-plugin/public';
-
+type Params = HttpFetchQuery & { version?: string };
class ApiService {
private static instance: ApiService;
private _http!: HttpSetup;
@@ -59,16 +59,13 @@ class ApiService {
return response;
}
- public async get(
- apiUrl: string,
- params?: HttpFetchQuery,
- decodeType?: any,
- asResponse = false
- ) {
+ public async get(apiUrl: string, params: Params = {}, decodeType?: any, asResponse = false) {
+ const { version, ...queryParams } = params;
const response = await this._http!.fetch({
path: apiUrl,
- query: params,
+ query: queryParams,
asResponse,
+ version,
});
this.addInspectorRequest?.({ data: response, status: FETCH_STATUS.SUCCESS, loading: false });
@@ -76,11 +73,14 @@ class ApiService {
return this.parseResponse(response, apiUrl, decodeType);
}
- public async post(apiUrl: string, data?: any, decodeType?: any, params?: HttpFetchQuery) {
+ public async post(apiUrl: string, data?: any, decodeType?: any, params: Params = {}) {
+ const { version, ...queryParams } = params;
+
const response = await this._http!.post(apiUrl, {
method: 'POST',
body: JSON.stringify(data),
- query: params,
+ query: queryParams,
+ version,
});
this.addInspectorRequest?.({ data: response, status: FETCH_STATUS.SUCCESS, loading: false });
@@ -88,10 +88,14 @@ class ApiService {
return this.parseResponse(response, apiUrl, decodeType);
}
- public async put(apiUrl: string, data?: any, decodeType?: any) {
+ public async put(apiUrl: string, data?: any, decodeType?: any, params: Params = {}) {
+ const { version, ...queryParams } = params;
+
const response = await this._http!.put(apiUrl, {
method: 'PUT',
body: JSON.stringify(data),
+ query: queryParams,
+ version,
});
return this.parseResponse(response, apiUrl, decodeType);
diff --git a/x-pack/plugins/uptime/common/constants/rest_api.ts b/x-pack/plugins/uptime/common/constants/rest_api.ts
index bdc9fcd04dd12..eaefdb71f7ba5 100644
--- a/x-pack/plugins/uptime/common/constants/rest_api.ts
+++ b/x-pack/plugins/uptime/common/constants/rest_api.ts
@@ -6,7 +6,7 @@
*/
export enum API_URLS {
- DYNAMIC_SETTINGS = `/internal/uptime/dynamic_settings`,
+ DYNAMIC_SETTINGS = `/api/uptime/settings`,
INDEX_STATUS = '/internal/uptime/index_status',
MONITOR_LIST = `/internal/uptime/monitor/list`,
MONITOR_LOCATIONS = `/internal/uptime/monitor/locations`,
diff --git a/x-pack/plugins/uptime/common/constants/ui.ts b/x-pack/plugins/uptime/common/constants/ui.ts
index d014b8b8ea6ff..19d980dfa1534 100644
--- a/x-pack/plugins/uptime/common/constants/ui.ts
+++ b/x-pack/plugins/uptime/common/constants/ui.ts
@@ -109,3 +109,5 @@ export const SYNTHETICS_INDEX_PATTERN = 'synthetics-*';
export const LICENSE_NOT_ACTIVE_ERROR = 'License not active';
export const LICENSE_MISSING_ERROR = 'Missing license information';
export const LICENSE_NOT_SUPPORTED_ERROR = 'License not supported';
+
+export const INITIAL_REST_VERSION = '2023-10-31';
diff --git a/x-pack/plugins/uptime/public/legacy_uptime/state/api/dynamic_settings.ts b/x-pack/plugins/uptime/public/legacy_uptime/state/api/dynamic_settings.ts
index 661fdcf46ad89..e1cb67987af5d 100644
--- a/x-pack/plugins/uptime/public/legacy_uptime/state/api/dynamic_settings.ts
+++ b/x-pack/plugins/uptime/public/legacy_uptime/state/api/dynamic_settings.ts
@@ -12,20 +12,24 @@ import {
DynamicSettingsSaveCodec,
} from '../../../../common/runtime_types';
import { apiService } from './utils';
-import { API_URLS } from '../../../../common/constants';
-
-const apiPath = API_URLS.DYNAMIC_SETTINGS;
+import { API_URLS, INITIAL_REST_VERSION } from '../../../../common/constants';
interface SaveApiRequest {
settings: DynamicSettings;
}
export const getDynamicSettings = async (): Promise => {
- return await apiService.get(apiPath, undefined, DynamicSettingsCodec);
+ return await apiService.get(
+ API_URLS.DYNAMIC_SETTINGS,
+ { version: INITIAL_REST_VERSION },
+ DynamicSettingsCodec
+ );
};
export const setDynamicSettings = async ({
settings,
}: SaveApiRequest): Promise => {
- return await apiService.post(apiPath, settings, DynamicSettingsSaveCodec);
+ return await apiService.put(API_URLS.DYNAMIC_SETTINGS, settings, DynamicSettingsSaveCodec, {
+ version: INITIAL_REST_VERSION,
+ });
};
diff --git a/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/saved_objects.ts b/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/saved_objects.ts
index 3108f29a97d95..99d2c717e4f94 100644
--- a/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/saved_objects.ts
+++ b/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/saved_objects.ts
@@ -5,7 +5,11 @@
* 2.0.
*/
-import { SavedObjectsErrorHelpers, SavedObjectsServiceSetup } from '@kbn/core/server';
+import {
+ SavedObjectsClientContract,
+ SavedObjectsErrorHelpers,
+ SavedObjectsServiceSetup,
+} from '@kbn/core/server';
import { DYNAMIC_SETTINGS_DEFAULT_ATTRIBUTES } from '../../../constants/settings';
import { DynamicSettingsAttributes } from '../../../runtime_types/settings';
@@ -20,7 +24,10 @@ export const registerUptimeSavedObjects = (savedObjectsService: SavedObjectsServ
export interface UMSavedObjectsAdapter {
config: UptimeConfig | null;
getUptimeDynamicSettings: UMSavedObjectsQueryFn;
- setUptimeDynamicSettings: UMSavedObjectsQueryFn;
+ setUptimeDynamicSettings: (
+ client: SavedObjectsClientContract,
+ attr: DynamicSettingsAttributes
+ ) => Promise;
}
export const savedObjectsAdapter: UMSavedObjectsAdapter = {
@@ -43,10 +50,15 @@ export const savedObjectsAdapter: UMSavedObjectsAdapter = {
throw getErr;
}
},
- setUptimeDynamicSettings: async (client, settings: DynamicSettingsAttributes | undefined) => {
- await client.create(umDynamicSettings.name, settings, {
- id: settingsObjectId,
- overwrite: true,
- });
+ setUptimeDynamicSettings: async (client, settings: DynamicSettingsAttributes) => {
+ const newObj = await client.create(
+ umDynamicSettings.name,
+ settings,
+ {
+ id: settingsObjectId,
+ overwrite: true,
+ }
+ );
+ return newObj.attributes;
},
};
diff --git a/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.test.ts b/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.test.ts
deleted file mode 100644
index 117e39c0b6437..0000000000000
--- a/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.test.ts
+++ /dev/null
@@ -1,83 +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 { validateCertsValues } from './dynamic_settings';
-
-describe('dynamic settings', () => {
- describe('validateCertValues', () => {
- it(`doesn't allow age threshold values less than 0`, () => {
- expect(
- validateCertsValues({
- certAgeThreshold: -1,
- certExpirationThreshold: 2,
- heartbeatIndices: 'foo',
- defaultConnectors: [],
- })
- ).toMatchInlineSnapshot(`
- Object {
- "certAgeThreshold": "Value must be greater than 0.",
- }
- `);
- });
-
- it(`doesn't allow non-integer age threshold values`, () => {
- expect(
- validateCertsValues({
- certAgeThreshold: 10.2,
- certExpirationThreshold: 2,
- heartbeatIndices: 'foo',
- defaultConnectors: [],
- })
- ).toMatchInlineSnapshot(`
- Object {
- "certAgeThreshold": "Value must be an integer.",
- }
- `);
- });
-
- it(`doesn't allow expiration threshold values less than 0`, () => {
- expect(
- validateCertsValues({
- certAgeThreshold: 2,
- certExpirationThreshold: -1,
- heartbeatIndices: 'foo',
- defaultConnectors: [],
- })
- ).toMatchInlineSnapshot(`
- Object {
- "certExpirationThreshold": "Value must be greater than 0.",
- }
- `);
- });
-
- it(`doesn't allow non-integer expiration threshold values`, () => {
- expect(
- validateCertsValues({
- certAgeThreshold: 2,
- certExpirationThreshold: 1.23,
- heartbeatIndices: 'foo',
- defaultConnectors: [],
- })
- ).toMatchInlineSnapshot(`
- Object {
- "certExpirationThreshold": "Value must be an integer.",
- }
- `);
- });
-
- it('allows valid values', () => {
- expect(
- validateCertsValues({
- certAgeThreshold: 2,
- certExpirationThreshold: 13,
- heartbeatIndices: 'foo',
- defaultConnectors: [],
- })
- ).toBeUndefined();
- });
- });
-});
diff --git a/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.ts b/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.ts
index 36c2de9a37cba..e5fdcf3aa7f61 100644
--- a/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.ts
+++ b/x-pack/plugins/uptime/server/legacy_uptime/routes/dynamic_settings.ts
@@ -6,17 +6,12 @@
*/
import { schema } from '@kbn/config-schema';
-import { isRight } from 'fp-ts/lib/Either';
-import { PathReporter } from 'io-ts/lib/PathReporter';
import { UMServerLibs } from '../lib/lib';
-import { DynamicSettings, DynamicSettingsCodec } from '../../../common/runtime_types';
+import { DynamicSettings } from '../../../common/runtime_types';
import { DynamicSettingsAttributes } from '../../runtime_types/settings';
import { UMRestApiRouteFactory } from '.';
import { savedObjectsAdapter } from '../lib/saved_objects/saved_objects';
-import {
- VALUE_MUST_BE_GREATER_THAN_ZERO,
- VALUE_MUST_BE_AN_INTEGER,
-} from '../../../common/translations';
+import { VALUE_MUST_BE_AN_INTEGER } from '../../../common/translations';
import { API_URLS } from '../../../common/constants';
export const createGetDynamicSettingsRoute: UMRestApiRouteFactory = (
@@ -28,75 +23,56 @@ export const createGetDynamicSettingsRoute: UMRestApiRouteFactory {
const dynamicSettingsAttributes: DynamicSettingsAttributes =
await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient);
- return {
- heartbeatIndices: dynamicSettingsAttributes.heartbeatIndices,
- certExpirationThreshold: dynamicSettingsAttributes.certExpirationThreshold,
- certAgeThreshold: dynamicSettingsAttributes.certAgeThreshold,
- defaultConnectors: dynamicSettingsAttributes.defaultConnectors,
- defaultEmail: dynamicSettingsAttributes.defaultEmail,
- };
+ return fromAttribute(dynamicSettingsAttributes);
},
});
-export const validateCertsValues = (
- settings: DynamicSettings
-): Record | undefined => {
- const errors: any = {};
- if (settings.certAgeThreshold <= 0) {
- errors.certAgeThreshold = VALUE_MUST_BE_GREATER_THAN_ZERO;
- } else if (settings.certAgeThreshold % 1) {
- errors.certAgeThreshold = VALUE_MUST_BE_AN_INTEGER;
- }
- if (settings.certExpirationThreshold <= 0) {
- errors.certExpirationThreshold = VALUE_MUST_BE_GREATER_THAN_ZERO;
- } else if (settings.certExpirationThreshold % 1) {
- errors.certExpirationThreshold = VALUE_MUST_BE_AN_INTEGER;
- }
- if (errors.certAgeThreshold || errors.certExpirationThreshold) {
- return errors;
+export const validateInteger = (value: number): string | undefined => {
+ if (value % 1) {
+ return VALUE_MUST_BE_AN_INTEGER;
}
};
+export const DynamicSettingsSchema = schema.object({
+ heartbeatIndices: schema.maybe(schema.string({ minLength: 1 })),
+ certAgeThreshold: schema.maybe(schema.number({ min: 1, validate: validateInteger })),
+ certExpirationThreshold: schema.maybe(schema.number({ min: 1, validate: validateInteger })),
+ defaultConnectors: schema.maybe(schema.arrayOf(schema.string())),
+ defaultEmail: schema.maybe(
+ schema.object({
+ to: schema.arrayOf(schema.string()),
+ cc: schema.maybe(schema.arrayOf(schema.string())),
+ bcc: schema.maybe(schema.arrayOf(schema.string())),
+ })
+ ),
+});
+
export const createPostDynamicSettingsRoute: UMRestApiRouteFactory = (_libs: UMServerLibs) => ({
- method: 'POST',
+ method: 'PUT',
path: API_URLS.DYNAMIC_SETTINGS,
validate: {
- body: schema.object({
- heartbeatIndices: schema.string(),
- certAgeThreshold: schema.number(),
- certExpirationThreshold: schema.number(),
- defaultConnectors: schema.arrayOf(schema.string()),
- defaultEmail: schema.maybe(
- schema.object({
- to: schema.arrayOf(schema.string()),
- cc: schema.maybe(schema.arrayOf(schema.string())),
- bcc: schema.maybe(schema.arrayOf(schema.string())),
- })
- ),
- }),
+ body: DynamicSettingsSchema,
},
writeAccess: true,
- handler: async ({ savedObjectsClient, request, response }): Promise => {
- const decoded = DynamicSettingsCodec.decode(request.body);
- const certThresholdErrors = validateCertsValues(request.body as DynamicSettings);
+ handler: async ({ savedObjectsClient, request }): Promise => {
+ const newSettings = request.body;
+ const prevSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient);
- if (isRight(decoded) && !certThresholdErrors) {
- const newSettings: DynamicSettings = decoded.right;
- await savedObjectsAdapter.setUptimeDynamicSettings(
- savedObjectsClient,
- newSettings as DynamicSettingsAttributes
- );
+ const attr = await savedObjectsAdapter.setUptimeDynamicSettings(savedObjectsClient, {
+ ...prevSettings,
+ ...newSettings,
+ } as DynamicSettingsAttributes);
- return response.ok({
- body: {
- success: true,
- },
- });
- } else {
- const error = PathReporter.report(decoded).join(', ');
- return response.badRequest({
- body: JSON.stringify(certThresholdErrors) || error,
- });
- }
+ return fromAttribute(attr);
},
});
+
+const fromAttribute = (attr: DynamicSettingsAttributes) => {
+ return {
+ heartbeatIndices: attr.heartbeatIndices,
+ certExpirationThreshold: attr.certExpirationThreshold,
+ certAgeThreshold: attr.certAgeThreshold,
+ defaultConnectors: attr.defaultConnectors,
+ defaultEmail: attr.defaultEmail,
+ };
+};
diff --git a/x-pack/plugins/uptime/server/legacy_uptime/routes/index.ts b/x-pack/plugins/uptime/server/legacy_uptime/routes/index.ts
index bdffc2a44fd0d..7253c3a9096c4 100644
--- a/x-pack/plugins/uptime/server/legacy_uptime/routes/index.ts
+++ b/x-pack/plugins/uptime/server/legacy_uptime/routes/index.ts
@@ -34,8 +34,6 @@ export { uptimeRouteWrapper } from './uptime_route_wrapper';
export const legacyUptimeRestApiRoutes: UMRestApiRouteFactory[] = [
createGetPingsRoute,
createGetIndexStatusRoute,
- createGetDynamicSettingsRoute,
- createPostDynamicSettingsRoute,
createGetMonitorDetailsRoute,
createGetMonitorLocationsRoute,
createMonitorListRoute,
@@ -50,3 +48,8 @@ export const legacyUptimeRestApiRoutes: UMRestApiRouteFactory[] = [
createLastSuccessfulCheckRoute,
createJourneyScreenshotBlocksRoute,
];
+
+export const legacyUptimePublicRestApiRoutes: UMRestApiRouteFactory[] = [
+ createGetDynamicSettingsRoute,
+ createPostDynamicSettingsRoute,
+];
diff --git a/x-pack/plugins/uptime/server/legacy_uptime/uptime_server.ts b/x-pack/plugins/uptime/server/legacy_uptime/uptime_server.ts
index cd61d40948ab1..dd01c36662e06 100644
--- a/x-pack/plugins/uptime/server/legacy_uptime/uptime_server.ts
+++ b/x-pack/plugins/uptime/server/legacy_uptime/uptime_server.ts
@@ -7,9 +7,16 @@
import { Logger } from '@kbn/core/server';
import { createLifecycleRuleTypeFactory, IRuleDataClient } from '@kbn/rule-registry-plugin/server';
+import { INITIAL_REST_VERSION } from '../../common/constants';
+import { DynamicSettingsSchema } from './routes/dynamic_settings';
import { UptimeRouter } from '../types';
import { uptimeRequests } from './lib/requests';
-import { createRouteWithAuth, legacyUptimeRestApiRoutes, uptimeRouteWrapper } from './routes';
+import {
+ createRouteWithAuth,
+ legacyUptimePublicRestApiRoutes,
+ legacyUptimeRestApiRoutes,
+ uptimeRouteWrapper,
+} from './routes';
import { UptimeServerSetup, UptimeCorePluginsSetup } from './lib/adapters';
import { statusCheckAlertFactory } from './lib/alerts/status_check';
@@ -62,6 +69,76 @@ export const initUptimeServer = (
}
});
+ legacyUptimePublicRestApiRoutes.forEach((route) => {
+ const { method, options, handler, validate, path } = uptimeRouteWrapper(
+ createRouteWithAuth(libs, route),
+ server
+ );
+
+ const routeDefinition = {
+ path,
+ validate,
+ options,
+ };
+
+ switch (method) {
+ case 'GET':
+ router.versioned
+ .get({
+ access: 'public',
+ path: routeDefinition.path,
+ options: {
+ tags: options?.tags,
+ },
+ })
+ .addVersion(
+ {
+ version: INITIAL_REST_VERSION,
+ validate: {
+ request: {
+ body: validate ? validate?.body : undefined,
+ },
+ response: {
+ 200: {
+ body: DynamicSettingsSchema,
+ },
+ },
+ },
+ },
+ handler
+ );
+ break;
+ case 'PUT':
+ router.versioned
+ .put({
+ access: 'public',
+ path: routeDefinition.path,
+ options: {
+ tags: options?.tags,
+ },
+ })
+ .addVersion(
+ {
+ version: INITIAL_REST_VERSION,
+ validate: {
+ request: {
+ body: validate ? validate?.body : undefined,
+ },
+ response: {
+ 200: {
+ body: DynamicSettingsSchema,
+ },
+ },
+ },
+ },
+ handler
+ );
+ break;
+ default:
+ throw new Error(`Handler for method ${method} is not defined`);
+ }
+ });
+
const {
alerting: { registerType },
} = plugins;
diff --git a/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts b/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts
index 21a3749fc3365..328384e8a96d1 100644
--- a/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts
+++ b/x-pack/test/alerting_api_integration/observability/synthetics_rule.ts
@@ -36,7 +36,7 @@ export default function ({ getService }: FtrProviderContext) {
it('creates rule when settings are configured', async () => {
await supertest
- .post(SYNTHETICS_API_URLS.DYNAMIC_SETTINGS)
+ .put(SYNTHETICS_API_URLS.DYNAMIC_SETTINGS)
.set('kbn-xsrf', 'true')
.send({
heartbeatIndices: 'heartbeat-*',
@@ -76,7 +76,7 @@ export default function ({ getService }: FtrProviderContext) {
it('updates rules when settings are updated', async () => {
await supertest
- .post(SYNTHETICS_API_URLS.DYNAMIC_SETTINGS)
+ .put(SYNTHETICS_API_URLS.DYNAMIC_SETTINGS)
.set('kbn-xsrf', 'true')
.send({
heartbeatIndices: 'heartbeat-*',
diff --git a/x-pack/test/api_integration/apis/uptime/rest/dynamic_settings.ts b/x-pack/test/api_integration/apis/uptime/rest/dynamic_settings.ts
index 8ecdbc9b615da..987bb8c1cd64d 100644
--- a/x-pack/test/api_integration/apis/uptime/rest/dynamic_settings.ts
+++ b/x-pack/test/api_integration/apis/uptime/rest/dynamic_settings.ts
@@ -29,15 +29,24 @@ export default function ({ getService }: FtrProviderContext) {
defaultConnectors: [],
};
const postResponse = await supertest
- .post(API_URLS.DYNAMIC_SETTINGS)
+ .put(API_URLS.DYNAMIC_SETTINGS)
.set('kbn-xsrf', 'true')
.send(newSettings);
- expect(postResponse.body).to.eql({ success: true });
+ expect(postResponse.body).to.eql({
+ heartbeatIndices: 'myIndex1*',
+ certExpirationThreshold: 5,
+ certAgeThreshold: 15,
+ defaultConnectors: [],
+ defaultEmail: { to: [], cc: [], bcc: [] },
+ });
expect(postResponse.status).to.eql(200);
const getResponse = await supertest.get(API_URLS.DYNAMIC_SETTINGS);
- expect(getResponse.body).to.eql(newSettings);
+ expect(getResponse.body).to.eql({
+ ...newSettings,
+ defaultEmail: { to: [], cc: [], bcc: [] },
+ });
expect(isRight(DynamicSettingsCodec.decode(getResponse.body))).to.be.ok();
});
});
From 0a44a58800b3dfd550aff64e6e8414edee36ea02 Mon Sep 17 00:00:00 2001
From: Gloria Hornero
Date: Mon, 23 Oct 2023 15:17:32 +0200
Subject: [PATCH 11/11] [Security Solution] Unskipping
`x-pack/test/security_solution_cypress/cypress/e2e/explore/users/` working
tests on serverless (#169475)
---
.../cypress/e2e/explore/users/user_details.cy.ts | 2 +-
.../cypress/e2e/explore/users/users_tabs.cy.ts | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/users/user_details.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/users/user_details.cy.ts
index 2e7f81f3cd1fe..3076a7f1fcccd 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/users/user_details.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/users/user_details.cy.ts
@@ -21,7 +21,7 @@ import {
} from '../../../tasks/alerts';
import { USER_COLUMN } from '../../../screens/alerts';
-describe('user details flyout', () => {
+describe('user details flyout', { tags: ['@ess', '@serverless'] }, () => {
beforeEach(() => {
cleanKibana();
login();
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/explore/users/users_tabs.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/explore/users/users_tabs.cy.ts
index fad136becf1a2..e066c5c9ec533 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/explore/users/users_tabs.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/explore/users/users_tabs.cy.ts
@@ -16,11 +16,11 @@ import { RISK_SCORE_TAB, RISK_SCORE_TAB_CONTENT } from '../../../screens/users/u
import { cleanKibana } from '../../../tasks/common';
import { login } from '../../../tasks/login';
-import { visit, visitUserDetailsPage } from '../../../tasks/navigation';
+import { visitUserDetailsPage, visitWithTimeRange } from '../../../tasks/navigation';
import { USERS_URL } from '../../../urls/navigation';
-describe('Users stats and tables', () => {
+describe('Users stats and tables', { tags: ['@ess', '@serverless'] }, () => {
before(() => {
cleanKibana();
cy.task('esArchiverLoad', { archiveName: 'users' });
@@ -30,7 +30,7 @@ describe('Users stats and tables', () => {
beforeEach(() => {
login();
- visit(USERS_URL);
+ visitWithTimeRange(USERS_URL);
});
after(() => {