forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SecuritySolution] Add "Install" and "Reinstall" button on Entity Sto…
…re status page (elastic#208149) ## Summary Add "Install" and "Reinstall" buttons on Entity Store status page * It also adds an extra loading state for the 'enable' switch in the header  ### How to test it? **1)** * Start a Kibana repository with entity data * Install the entity store * Delete one engine using Dev Tools `DELETE kbn:/api/entity_store/engines/user` * Go to the manage entity store status tab * Verify that it displays an install button for the uninstalled engine * Install the engine **2)** * Start a Kibana repository with entity data * Install the entity store * Go to the manage entity store status tab * Delete one component of an installed engine (transform) * Go to the manage entity store status tab * Verify it displays a reinstall button for the engine you uninstalled the component * Reinstall the engine * Everything should look ok * Extra step: Verify if there was any data lost after reinstalling the engine ### Checklist Reviewers should verify this PR satisfies this list as well. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [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 - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
- Loading branch information
Showing
9 changed files
with
328 additions
and
57 deletions.
There are no files selected for viewing
33 changes: 33 additions & 0 deletions
33
...omponents/entity_store/components/engines_status/components/engine_status_header.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import React from 'react'; | ||
import { render } from '@testing-library/react'; | ||
import { EngineStatusHeader } from './engine_status_header'; | ||
import { capitalize } from 'lodash/fp'; | ||
import { EntityType } from '../../../../../../../common/entity_analytics/types'; | ||
import { TestProviders } from '../../../../../../common/mock'; | ||
|
||
describe('EngineStatusHeader', () => { | ||
it('renders the title with the capitalized entity type', () => { | ||
const { getByText } = render(<EngineStatusHeader entityType={EntityType.host} />, { | ||
wrapper: TestProviders, | ||
}); | ||
expect(getByText(`${capitalize(EntityType.host)} Store`)).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders the action button if provided', () => { | ||
const actionButton = <button type="button">{'Click me'}</button>; | ||
const { getByText } = render( | ||
<EngineStatusHeader entityType={EntityType.host} actionButton={actionButton} />, | ||
{ | ||
wrapper: TestProviders, | ||
} | ||
); | ||
expect(getByText('Click me')).toBeInTheDocument(); | ||
}); | ||
}); |
39 changes: 39 additions & 0 deletions
39
...ics/components/entity_store/components/engines_status/components/engine_status_header.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 React from 'react'; | ||
import { EuiFlexItem, EuiTitle, EuiFlexGroup } from '@elastic/eui'; | ||
import { capitalize } from 'lodash/fp'; | ||
import { FormattedMessage } from '@kbn/i18n-react'; | ||
import type { EntityType } from '../../../../../../../common/entity_analytics/types'; | ||
|
||
export const EngineStatusHeader = ({ | ||
entityType, | ||
actionButton, | ||
}: { | ||
entityType: EntityType; | ||
actionButton?: React.ReactNode; | ||
}) => ( | ||
<EuiTitle size="s"> | ||
<h4> | ||
<EuiFlexGroup direction="row" gutterSize="m" alignItems="baseline" responsive={false}> | ||
<EuiFlexItem grow={false}> | ||
<FormattedMessage | ||
id="xpack.securitySolution.entityAnalytics.entityStore.enginesStatus.title" | ||
defaultMessage="{type} Store" | ||
values={{ | ||
type: capitalize(entityType), | ||
}} | ||
/> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={false} direction="row"> | ||
{actionButton} | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</h4> | ||
</EuiTitle> | ||
); |
118 changes: 118 additions & 0 deletions
118
...ts/entity_store/components/engines_status/components/engine_status_header_action.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import React from 'react'; | ||
import { render, screen, fireEvent } from '@testing-library/react'; | ||
import { EngineStatusHeaderAction } from './engine_status_header_action'; | ||
import { useEnableEntityStoreMutation } from '../../../hooks/use_entity_store'; | ||
import { isEngineLoading } from '../helpers'; | ||
import type { GetEntityStoreStatusResponse } from '../../../../../../../common/api/entity_analytics/entity_store/status.gen'; | ||
import { EntityType } from '../../../../../../../common/entity_analytics/types'; | ||
import { TestProviders } from '../../../../../../common/mock'; | ||
import type { EngineComponentStatus } from '../../../../../../../common/api/entity_analytics'; | ||
|
||
jest.mock('../../../hooks/use_entity_store'); | ||
jest.mock('../helpers'); | ||
|
||
const mockUseEnableEntityStoreMutation = useEnableEntityStoreMutation as jest.Mock; | ||
const mockIsEngineLoading = isEngineLoading as jest.Mock; | ||
|
||
const defaultComponent: EngineComponentStatus = { | ||
id: 'component1', | ||
resource: 'entity_engine', | ||
installed: true, | ||
}; | ||
|
||
const defaultEngineResponse: GetEntityStoreStatusResponse['engines'][0] = { | ||
type: EntityType.user, | ||
indexPattern: '', | ||
status: 'started', | ||
fieldHistoryLength: 0, | ||
components: [defaultComponent], | ||
lookbackPeriod: '', | ||
}; | ||
|
||
describe('EngineStatusHeaderAction', () => { | ||
beforeEach(() => { | ||
mockUseEnableEntityStoreMutation.mockReturnValue({ | ||
mutate: jest.fn(), | ||
isLoading: false, | ||
}); | ||
mockIsEngineLoading.mockReturnValue(false); | ||
}); | ||
|
||
it('renders loading spinner when loading', () => { | ||
mockUseEnableEntityStoreMutation.mockReturnValue({ | ||
mutate: jest.fn(), | ||
isLoading: true, | ||
}); | ||
|
||
render(<EngineStatusHeaderAction engine={undefined} type={EntityType.user} />, { | ||
wrapper: TestProviders, | ||
}); | ||
expect(screen.getByRole('progressbar')).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders install button when engine is undefined', () => { | ||
render(<EngineStatusHeaderAction engine={undefined} type={EntityType.user} />, { | ||
wrapper: TestProviders, | ||
}); | ||
expect(screen.getByText('Install')).toBeInTheDocument(); | ||
}); | ||
|
||
it('calls installEntityStore when install button is clicked', () => { | ||
const mutate = jest.fn(); | ||
mockUseEnableEntityStoreMutation.mockReturnValue({ | ||
mutate, | ||
isLoading: false, | ||
}); | ||
|
||
render(<EngineStatusHeaderAction engine={undefined} type={EntityType.user} />, { | ||
wrapper: TestProviders, | ||
}); | ||
fireEvent.click(screen.getByText('Install')); | ||
expect(mutate).toHaveBeenCalledWith({ entityTypes: [EntityType.user] }); | ||
}); | ||
|
||
it('calls installEntityStore when reinstall button is clicked', () => { | ||
const engine: GetEntityStoreStatusResponse['engines'][0] = { | ||
...defaultEngineResponse, | ||
components: [{ ...defaultComponent, installed: false }], | ||
}; | ||
const mutate = jest.fn(); | ||
mockUseEnableEntityStoreMutation.mockReturnValue({ | ||
mutate, | ||
isLoading: false, | ||
}); | ||
|
||
render(<EngineStatusHeaderAction engine={engine} type={EntityType.user} />, { | ||
wrapper: TestProviders, | ||
}); | ||
fireEvent.click(screen.getByText('Reinstall')); | ||
expect(mutate).toHaveBeenCalledWith({ entityTypes: [EntityType.user] }); | ||
}); | ||
|
||
it('renders reinstall button and tooltip when a component is not installed', () => { | ||
const engine: GetEntityStoreStatusResponse['engines'][0] = { | ||
...defaultEngineResponse, | ||
components: [{ ...defaultComponent, installed: false }], | ||
}; | ||
|
||
render(<EngineStatusHeaderAction engine={engine} type={EntityType.user} />, { | ||
wrapper: TestProviders, | ||
}); | ||
expect(screen.getByText('Reinstall')).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders not action when engine is defined and no error', () => { | ||
render(<EngineStatusHeaderAction engine={defaultEngineResponse} type={EntityType.user} />, { | ||
wrapper: TestProviders, | ||
}); | ||
expect(screen.queryByText('Install')).not.toBeInTheDocument(); | ||
expect(screen.queryByText('Reinstall')).not.toBeInTheDocument(); | ||
}); | ||
}); |
70 changes: 70 additions & 0 deletions
70
...ponents/entity_store/components/engines_status/components/engine_status_header_action.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import React from 'react'; | ||
import { EuiLoadingSpinner, EuiButtonEmpty, EuiIconTip } from '@elastic/eui'; | ||
import { FormattedMessage } from '@kbn/i18n-react'; | ||
import { useEnableEntityStoreMutation } from '../../../hooks/use_entity_store'; | ||
import type { GetEntityStoreStatusResponse } from '../../../../../../../common/api/entity_analytics/entity_store/status.gen'; | ||
import type { EntityType } from '../../../../../../../common/entity_analytics/types'; | ||
import { isEngineLoading } from '../helpers'; | ||
|
||
export function EngineStatusHeaderAction({ | ||
engine, | ||
type, | ||
}: { | ||
engine: GetEntityStoreStatusResponse['engines'][0] | undefined; | ||
type: EntityType; | ||
}) { | ||
const enableEntityStore = useEnableEntityStoreMutation(); | ||
const installEntityStore = () => { | ||
enableEntityStore.mutate({ entityTypes: [type] }); | ||
}; | ||
const hasUninstalledComponent = engine?.components?.some(({ installed }) => !installed); | ||
|
||
if (enableEntityStore.isLoading || isEngineLoading(engine?.status)) { | ||
return <EuiLoadingSpinner size="s" />; | ||
} | ||
|
||
if (!engine) { | ||
return ( | ||
<EuiButtonEmpty onClick={installEntityStore}> | ||
<FormattedMessage | ||
id="xpack.securitySolution.entityAnalytics.entityStore.enginesStatus.installButton" | ||
defaultMessage="Install" | ||
/> | ||
</EuiButtonEmpty> | ||
); | ||
} | ||
|
||
if (hasUninstalledComponent) { | ||
return ( | ||
<div> | ||
<EuiButtonEmpty onClick={installEntityStore} iconType="refresh" color="warning"> | ||
<FormattedMessage | ||
id="xpack.securitySolution.entityAnalytics.entityStore.enginesStatus.reinstallButton" | ||
defaultMessage="Reinstall" | ||
/> | ||
</EuiButtonEmpty> | ||
|
||
<EuiIconTip | ||
content={ | ||
<FormattedMessage | ||
id="xpack.securitySolution.entityAnalytics.entityStore.enginesStatus.reinstallToolTip" | ||
defaultMessage="The components associated with this entity type are experiencing issues. Reinstall them to restore functionality" | ||
/> | ||
} | ||
color="warning" | ||
position="right" | ||
type="iInCircle" | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
return null; | ||
} |
11 changes: 11 additions & 0 deletions
11
...tion/public/entity_analytics/components/entity_store/components/engines_status/helpers.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import type { EngineStatus } from '../../../../../../common/api/entity_analytics/entity_store/common.gen'; | ||
|
||
export const isEngineLoading = (status: EngineStatus | undefined) => | ||
status === 'updating' || status === 'installing'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.