diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts index 9f2b7bb319f7d..988bcb43ebf86 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { AssetCriticalityRecord } from '../../../common/api/entity_analytics/asset_criticality'; import type { RiskScoreEntity } from '../../../common/search_strategy'; import { RISK_ENGINE_STATUS_URL, @@ -14,6 +15,7 @@ import { RISK_ENGINE_INIT_URL, RISK_ENGINE_PRIVILEGES_URL, ASSET_CRITICALITY_PRIVILEGES_URL, + ASSET_CRITICALITY_URL, RISK_SCORE_INDEX_STATUS_API_URL, } from '../../../common/constants'; @@ -26,6 +28,8 @@ import type { } from '../../../server/lib/entity_analytics/types'; import type { RiskScorePreviewRequestSchema } from '../../../common/entity_analytics/risk_engine/risk_score_preview/request_schema'; import type { EntityAnalyticsPrivileges } from '../../../common/api/entity_analytics/common'; +import type { SnakeToCamelCase } from '../common/utils'; + import { useKibana } from '../../common/lib/kibana/kibana_react'; export const useEntityAnalyticsRoutes = () => { @@ -103,6 +107,35 @@ export const useEntityAnalyticsRoutes = () => { method: 'GET', }); + /** + * Create asset criticality + */ + const createAssetCriticality = async ( + params: Pick + ): Promise => + http.fetch(ASSET_CRITICALITY_URL, { + version: '1', + method: 'POST', + body: JSON.stringify({ + id_value: params.idValue, + id_field: params.idField, + criticality_level: params.criticalityLevel, + }), + }); + + /** + * Get asset criticality + */ + const fetchAssetCriticality = async ( + params: Pick + ): Promise => { + return http.fetch(ASSET_CRITICALITY_URL, { + version: '1', + method: 'GET', + query: { id_value: params.idValue, id_field: params.idField }, + }); + }; + const getRiskScoreIndexStatus = ({ query, signal, @@ -132,6 +165,10 @@ export const useEntityAnalyticsRoutes = () => { disableRiskEngine, fetchRiskEnginePrivileges, fetchAssetCriticalityPrivileges, + createAssetCriticality, + fetchAssetCriticality, getRiskScoreIndexStatus, }; }; + +export type AssetCriticality = SnakeToCamelCase; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/common/utils.ts b/x-pack/plugins/security_solution/public/entity_analytics/common/utils.ts index ee343526ea2b0..f6009c85dbd43 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/common/utils.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/common/utils.ts @@ -33,6 +33,23 @@ export const RISK_SCORE_RANGES = { [RiskSeverity.critical]: { start: 90, stop: 100 }, }; +type SnakeToCamelCaseString = S extends `${infer T}_${infer U}` + ? `${T}${Capitalize>}` + : S; + +type SnakeToCamelCaseArray = T extends Array + ? Array> + : T; + +// TODO #173073 @tiansivive Add to utilities in `packages/kbn-utility-types` +export type SnakeToCamelCase = T extends Record + ? { + [K in keyof T as SnakeToCamelCaseString]: SnakeToCamelCase; + } + : T extends unknown[] + ? SnakeToCamelCaseArray + : T; + export enum UserRiskScoreQueryId { USERS_BY_RISK = 'UsersByRisk', USER_DETAILS_RISK_SCORE = 'UserDetailsRiskScore', diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/asset_criticality_selector.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/asset_criticality_selector.tsx new file mode 100644 index 0000000000000..f2a4c4c3a3288 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/asset_criticality_selector.tsx @@ -0,0 +1,207 @@ +/* + * 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 type { EuiSuperSelectOption } from '@elastic/eui'; + +import { + EuiAccordion, + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiHealth, + EuiLoadingSpinner, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiSuperSelect, + EuiText, + EuiHorizontalRule, +} from '@elastic/eui'; + +import React, { useState } from 'react'; + +import { FormattedMessage } from '@kbn/i18n-react'; + +import { + CRITICALITY_LEVEL_DESCRIPTION, + CRITICALITY_LEVEL_TITLE, + PICK_ASSET_CRITICALITY, +} from './translations'; +import type { Entity, ModalState, State } from './use_asset_criticality'; +import { useAssetCriticalityData, useCriticalityModal } from './use_asset_criticality'; +import type { CriticalityLevel } from './common'; +import { CRITICALITY_LEVEL_COLOR } from './common'; + +interface Props { + entity: Entity; +} +export const AssetCriticalitySelector: React.FC = ({ entity }) => { + const modal = useCriticalityModal(); + const criticality = useAssetCriticalityData(entity, modal); + + if (criticality.privileges.isLoading || !criticality.privileges.data?.has_all_required) { + return null; + } + + return ( + <> + + + } + data-test-subj="asset-criticality-selector" + > + {criticality.query.isLoading || criticality.mutation.isLoading ? ( + + ) : ( + + + + {criticality.status === 'update' && criticality.query.data?.criticality_level ? ( + + {CRITICALITY_LEVEL_TITLE[criticality.query.data.criticality_level]} + + ) : ( + + + + )} + + + + modal.toggle(true)} + > + {criticality.status === 'update' ? ( + + ) : ( + + )} + + + + )} + + + {modal.visible ? ( + + ) : null} + + ); +}; + +interface ModalProps { + criticality: State; + modal: ModalState; + entity: Entity; +} +const AssetCriticalityModal: React.FC = ({ criticality, modal, entity }) => { + const [value, setNewValue] = useState( + criticality.query.data?.criticality_level ?? 'normal' + ); + + return ( + modal.toggle(false)}> + + + {PICK_ASSET_CRITICALITY} + + + + + + + modal.toggle(false)}> + + + + + criticality.mutation.mutate({ + criticalityLevel: value, + idField: `${entity.type}.name`, + idValue: entity.name, + }) + } + fill + data-test-subj="asset-criticality-modal-save-btn" + > + + + + + ); +}; + +const option = (level: CriticalityLevel): EuiSuperSelectOption => ({ + value: level, + dropdownDisplay: ( + + {CRITICALITY_LEVEL_TITLE[level]} + +

{CRITICALITY_LEVEL_DESCRIPTION[level]}

+
+
+ ), + inputDisplay: ( + + {CRITICALITY_LEVEL_TITLE[level]} + + ), +}); +const options: Array> = [ + option('normal'), + option('not_important'), + option('important'), + option('very_important'), +]; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/common.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/common.ts new file mode 100644 index 0000000000000..0c3e16c0e77a2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/common.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { euiLightVars } from '@kbn/ui-theme'; +import type { AssetCriticalityRecord } from '../../../../common/api/entity_analytics/asset_criticality'; + +export type CriticalityLevel = AssetCriticalityRecord['criticality_level']; + +export const CRITICALITY_LEVEL_COLOR: Record = { + very_important: '#E7664C', + important: '#D6BF57', + normal: '#54B399', + not_important: euiLightVars.euiColorMediumShade, +}; + +// SUGGESTION: @tiansivive Move this to some more general place within Entity Analytics +export const buildCriticalityQueryKeys = (id: string) => { + const ASSET_CRITICALITY = 'ASSET_CRITICALITY'; + const PRIVILEGES = 'PRIVILEGES'; + return { + doc: [ASSET_CRITICALITY, id], + privileges: [ASSET_CRITICALITY, PRIVILEGES, id], + }; +}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/translations.ts new file mode 100644 index 0000000000000..bb4d4f51ace09 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/translations.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { CriticalityLevel } from './common'; + +export const PICK_ASSET_CRITICALITY = i18n.translate( + 'xpack.securitySolution.entityAnalytics.assetCriticality.pickerText', + { + defaultMessage: 'Pick asset criticality level', + } +); + +export const CRITICALITY_LEVEL_TITLE: Record = { + normal: i18n.translate( + 'xpack.securitySolution.entityAnalytics.assetCriticality.levelTitle.normal', + { + defaultMessage: 'Normal', + } + ), + not_important: i18n.translate( + 'xpack.securitySolution.entityAnalytics.assetCriticality.levelTitle.notImportant', + { + defaultMessage: 'Not important', + } + ), + important: i18n.translate( + 'xpack.securitySolution.entityAnalytics.assetCriticality.levelTitle.important', + { + defaultMessage: 'Important', + } + ), + very_important: i18n.translate( + 'xpack.securitySolution.entityAnalytics.assetCriticality.levelTitle.veryImportant', + { + defaultMessage: 'Very important', + } + ), +}; +export const CRITICALITY_LEVEL_DESCRIPTION: Record = { + normal: i18n.translate( + 'xpack.securitySolution.entityAnalytics.assetCriticality.levelDescription.normal', + { + defaultMessage: 'Entity risk score rises at normal speed', + } + ), + not_important: i18n.translate( + 'xpack.securitySolution.entityAnalytics.assetCriticality.levelDescription.notImportant', + { + defaultMessage: 'Entity risk score rises slower', + } + ), + important: i18n.translate( + 'xpack.securitySolution.entityAnalytics.assetCriticality.levelDescription.important', + { + defaultMessage: 'Entity risk score rises faster', + } + ), + very_important: i18n.translate( + 'xpack.securitySolution.entityAnalytics.assetCriticality.levelDescription.veryImportant', + { + defaultMessage: 'Entity risk score rises much faster', + } + ), +}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/use_asset_criticality.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/use_asset_criticality.ts new file mode 100644 index 0000000000000..669bc878b18a5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/use_asset_criticality.ts @@ -0,0 +1,76 @@ +/* + * 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 { useGeneratedHtmlId } from '@elastic/eui'; +import type { UseMutationResult, UseQueryResult } from '@tanstack/react-query'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; + +import { useToggle } from 'react-use'; +import type { AssetCriticalityRecord } from '../../../../common/api/entity_analytics/asset_criticality'; +import type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics/common'; +import type { AssetCriticality } from '../../api/api'; +import { useEntityAnalyticsRoutes } from '../../api/api'; +import { buildCriticalityQueryKeys } from './common'; + +export const useAssetCriticalityData = (entity: Entity, modal: ModalState): State => { + const QC = useQueryClient(); + const QUERY_KEYS = buildCriticalityQueryKeys(entity.name); + + const { fetchAssetCriticality, createAssetCriticality, fetchAssetCriticalityPrivileges } = + useEntityAnalyticsRoutes(); + + const privileges = useQuery({ + queryKey: QUERY_KEYS.privileges, + queryFn: fetchAssetCriticalityPrivileges, + }); + const query = useQuery({ + queryKey: QUERY_KEYS.doc, + queryFn: () => fetchAssetCriticality({ idField: `${entity.type}.name`, idValue: entity.name }), + retry: (failureCount, error) => error.body.statusCode === 404 && failureCount > 0, + enabled: privileges.data?.has_all_required, + }); + + const mutation = useMutation({ + mutationFn: createAssetCriticality, + onSuccess: (data) => { + QC.setQueryData(QUERY_KEYS.doc, data); + modal.toggle(false); + }, + }); + + return { + status: query.isError && query.error.body.statusCode === 404 ? 'create' : 'update', + query, + mutation, + privileges, + }; +}; + +export const useCriticalityModal = () => { + const [visible, toggle] = useToggle(false); + const basicSelectId = useGeneratedHtmlId({ prefix: 'basicSelect' }); + return { visible, toggle, basicSelectId }; +}; + +export interface State { + status: 'create' | 'update'; + query: UseQueryResult; + privileges: UseQueryResult; + mutation: UseMutationResult; +} +type Params = Pick; + +export interface ModalState { + basicSelectId: string; + visible: boolean; + toggle: (next: boolean) => void; +} + +export interface Entity { + name: string; + type: 'host' | 'user'; +} diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/expandable_host.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/expandable_host.tsx index d307b9e8273ea..c5f805a53ac87 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/expandable_host.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/expandable_host.tsx @@ -35,7 +35,7 @@ const StyledTitle = styled.h4` export const ExpandableHostDetailsTitle = ({ hostName }: ExpandableHostProps) => ( - + {i18n.translate('xpack.securitySolution.timeline.sidePanel.hostDetails.title', { defaultMessage: 'Host details', })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/index.tsx index 344255c70d1bd..981e81e71491d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/host_details/index.tsx @@ -16,6 +16,7 @@ import { import { i18n } from '@kbn/i18n'; import React from 'react'; import styled from 'styled-components'; +import { AssetCriticalitySelector } from '../../../../entity_analytics/components/asset_criticality/asset_criticality_selector'; import { ExpandableHostDetails, ExpandableHostDetailsPageLink, @@ -80,7 +81,7 @@ export const HostDetailsPanel: React.FC = React.memo( - + diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/user_details/user_details_flyout.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/user_details/user_details_flyout.tsx index 9bc87bdc49751..e2963c2e3ca3c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/user_details/user_details_flyout.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/user_details/user_details_flyout.tsx @@ -8,6 +8,7 @@ import { EuiFlyoutHeader, EuiFlyoutBody, EuiSpacer } from '@elastic/eui'; import React from 'react'; import styled from 'styled-components'; +import { AssetCriticalitySelector } from '../../../../entity_analytics/components/asset_criticality/asset_criticality_selector'; import { ExpandableUserDetailsTitle, ExpandableUserDetailsPageLink, @@ -43,7 +44,7 @@ export const UserDetailsFlyout = ({ - + diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/asset_criticality/host_flyout.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/asset_criticality/host_flyout.cy.ts new file mode 100644 index 0000000000000..2ec2e40cb72ac --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/asset_criticality/host_flyout.cy.ts @@ -0,0 +1,94 @@ +/* + * 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 { getNewRule } from '../../../objects/rule'; +import { + HOST_DETAILS_FLYOUT_SECTION_HEADER, + HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_SELECTOR, + HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_BUTTON, + HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_MODAL_TITLE, + HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_LEVEL, +} from '../../../screens/asset_criticality/flyouts'; +import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; +import { + expandFirstAlertHostFlyout, + selectAssetCriticalityLevel, + toggleAssetCriticalityAccordion, + toggleAssetCriticalityModal, +} from '../../../tasks/asset_criticality/common'; +import { createRule } from '../../../tasks/api_calls/rules'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; +import { login } from '../../../tasks/login'; +import { visit } from '../../../tasks/navigation'; +import { ALERTS_URL } from '../../../urls/navigation'; + +describe( + 'Host details flyout', + { + tags: ['@ess', '@serverless'], + env: { + ftrConfig: { + kbnServerArgs: [ + `--xpack.securitySolution.enableExperimental=${JSON.stringify([ + 'entityAnalyticsAssetCriticalityEnabled', + ])}`, + ], + }, + }, + }, + () => { + const rule = { ...getNewRule(), investigation_fields: { field_names: ['host.os.name'] } }; + + beforeEach(() => { + deleteAlertsAndRules(); + login(); + createRule(rule); + visit(ALERTS_URL); + waitForAlertsToPopulate(); + expandFirstAlertHostFlyout(); + }); + + describe('Host flyout', () => { + it('should display header section', () => { + cy.log('header and content'); + + cy.get(HOST_DETAILS_FLYOUT_SECTION_HEADER).should('contain.text', 'Host details'); + }); + + it('should display asset criticality accordion', () => { + cy.log('asset criticality'); + + cy.get(HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_SELECTOR).should( + 'contain.text', + 'Asset Criticality' + ); + + toggleAssetCriticalityAccordion(); + cy.get(HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_BUTTON).should('have.text', 'Create'); + }); + + it('should display asset criticality modal', () => { + cy.log('asset criticality modal'); + + toggleAssetCriticalityModal(); + cy.get(HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_MODAL_TITLE).should( + 'have.text', + 'Pick asset criticality level' + ); + }); + + it('should update asset criticality state', () => { + cy.log('asset criticality update'); + + selectAssetCriticalityLevel('Important'); + cy.get(HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_LEVEL) + .contains('Important') + .should('be.visible'); + }); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/asset_criticality/flyouts.ts b/x-pack/test/security_solution_cypress/cypress/screens/asset_criticality/flyouts.ts new file mode 100644 index 0000000000000..7fbfb5f3a03e0 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/screens/asset_criticality/flyouts.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getDataTestSubjectSelector } from '../../helpers/common'; + +export const HOST_DETAILS_FLYOUT_SECTION_HEADER = getDataTestSubjectSelector('host-details-header'); +export const HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_SELECTOR = getDataTestSubjectSelector( + 'asset-criticality-selector' +); +export const HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_LEVEL = + getDataTestSubjectSelector('asset-criticality-level'); +export const HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_BUTTON = getDataTestSubjectSelector( + 'asset-criticality-change-btn' +); +export const HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_MODAL_TITLE = getDataTestSubjectSelector( + 'asset-criticality-modal-title' +); +export const HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_MODAL_SELECT = getDataTestSubjectSelector( + 'asset-criticality-modal-select' +); +export const HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_MODAL_SELECT_OPTION = getDataTestSubjectSelector( + 'asset-criticality-modal-select-option' +); +export const HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_MODAL_SAVE_BTN = getDataTestSubjectSelector( + 'asset-criticality-modal-save-btn' +); + +export const OPEN_HOST_FLYOUT_BUTTON = getDataTestSubjectSelector('host-details-button'); +export const OPEN_USER_FLYOUT_BUTTON = getDataTestSubjectSelector('user-details-button'); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/asset_criticality/common.ts b/x-pack/test/security_solution_cypress/cypress/tasks/asset_criticality/common.ts new file mode 100644 index 0000000000000..79979c8a33016 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/tasks/asset_criticality/common.ts @@ -0,0 +1,56 @@ +/* + * 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 { + OPEN_HOST_FLYOUT_BUTTON, + OPEN_USER_FLYOUT_BUTTON, + HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_SELECTOR, + HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_BUTTON, + HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_MODAL_SELECT, + HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_MODAL_SELECT_OPTION, + HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_MODAL_SAVE_BTN, +} from '../../screens/asset_criticality/flyouts'; + +/** + * Find the first alert row in the alerts table then click on the host name to open the flyout + */ +export const expandFirstAlertHostFlyout = () => { + cy.get(OPEN_HOST_FLYOUT_BUTTON).first().click(); +}; + +/** + * Find the first alert row in the alerts table then click on the host name to open the flyout + */ +export const expandFirstAlertUserFlyout = () => { + cy.get(OPEN_USER_FLYOUT_BUTTON).first().click(); +}; + +/** + * Open the asset criticality accordion + */ +export const toggleAssetCriticalityAccordion = () => { + cy.get(HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_SELECTOR).click(); +}; + +/** + * Open the asset criticality modal + */ +export const toggleAssetCriticalityModal = () => { + toggleAssetCriticalityAccordion(); + cy.get(HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_BUTTON).click(); +}; + +/** + * Open the asset criticality modal + */ +export const selectAssetCriticalityLevel = (option: string) => { + toggleAssetCriticalityAccordion(); + toggleAssetCriticalityModal(); + cy.get(HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_MODAL_SELECT).click(); + cy.get(HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_MODAL_SELECT_OPTION).contains(option).click(); + cy.get(HOST_DETAILS_FLYOUT_ASSET_CRITICALITY_MODAL_SAVE_BTN).click(); +};