mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[Security Solution][Endpoint] Show list of trusted application on the Policy Details (#112182)
* New Artifact Collapsible card and Grid generic components * Fleet setup test data loader - ignore 409 concurrent installs in data loader for fleet setup * Adds `ContextMenuWithRouterSupport` prop for `maxWidth` and `truncateText` prop for `ContextMenuItemNaByRouter` * trustedApps generator loader - use existing policies (if any) when loading TAs * `CardCompressedHeaderLayout` support for `flushTop` prop
This commit is contained in:
parent
6bfa2a4c2c
commit
36ce6bda67
57 changed files with 1694 additions and 326 deletions
|
@ -110,15 +110,20 @@ export const installOrUpgradeEndpointFleetPackage = async (
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFleetBulkInstallError(bulkResp[0])) {
|
const firstError = bulkResp[0];
|
||||||
if (bulkResp[0].error instanceof Error) {
|
|
||||||
|
if (isFleetBulkInstallError(firstError)) {
|
||||||
|
if (firstError.error instanceof Error) {
|
||||||
throw new EndpointDataLoadingError(
|
throw new EndpointDataLoadingError(
|
||||||
`Installing the Endpoint package failed: ${bulkResp[0].error.message}, exiting`,
|
`Installing the Endpoint package failed: ${firstError.error.message}, exiting`,
|
||||||
bulkResp
|
bulkResp
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new EndpointDataLoadingError(bulkResp[0].error, bulkResp);
|
// Ignore `409` (conflicts due to Concurrent install or upgrades of package) errors
|
||||||
|
if (firstError.statusCode !== 409) {
|
||||||
|
throw new EndpointDataLoadingError(firstError.error, bulkResp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bulkResp[0] as BulkInstallPackageInfo;
|
return bulkResp[0] as BulkInstallPackageInfo;
|
||||||
|
|
|
@ -32,7 +32,7 @@ export type GetTrustedAppsListRequest = TypeOf<typeof GetTrustedAppsRequestSchem
|
||||||
/** API request params for retrieving summary of Trusted Apps */
|
/** API request params for retrieving summary of Trusted Apps */
|
||||||
export type GetTrustedAppsSummaryRequest = TypeOf<typeof GetTrustedAppsSummaryRequestSchema.query>;
|
export type GetTrustedAppsSummaryRequest = TypeOf<typeof GetTrustedAppsSummaryRequestSchema.query>;
|
||||||
|
|
||||||
export interface GetTrustedListAppsResponse {
|
export interface GetTrustedAppsListResponse {
|
||||||
per_page: number;
|
per_page: number;
|
||||||
page: number;
|
page: number;
|
||||||
total: number;
|
total: number;
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { i18n } from '@kbn/i18n';
|
||||||
import {
|
import {
|
||||||
ContextMenuItemNavByRouter,
|
ContextMenuItemNavByRouter,
|
||||||
ContextMenuItemNavByRouterProps,
|
ContextMenuItemNavByRouterProps,
|
||||||
} from '../context_menu_with_router_support/context_menu_item_nav_by_rotuer';
|
} from '../context_menu_with_router_support/context_menu_item_nav_by_router';
|
||||||
import { useTestIdGenerator } from '../hooks/use_test_id_generator';
|
import { useTestIdGenerator } from '../hooks/use_test_id_generator';
|
||||||
|
|
||||||
export interface ActionsContextMenuProps {
|
export interface ActionsContextMenuProps {
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* 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 { TrustedAppGenerator } from '../../../../common/endpoint/data_generators/trusted_app_generator';
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { getExceptionListItemSchemaMock } from '../../../../../lists/common/schemas/response/exception_list_item_schema.mock';
|
||||||
|
import { AppContextTestRender, createAppRootMockRenderer } from '../../../common/mock/endpoint';
|
||||||
|
import React from 'react';
|
||||||
|
import { ArtifactCardGrid, ArtifactCardGridProps } from './artifact_card_grid';
|
||||||
|
|
||||||
|
// FIXME:PT refactor helpers below after merge of PR https://github.com/elastic/kibana/pull/113363
|
||||||
|
|
||||||
|
const getCommonItemDataOverrides = () => {
|
||||||
|
return {
|
||||||
|
name: 'some internal app',
|
||||||
|
description: 'this app is trusted by the company',
|
||||||
|
created_at: new Date('2021-07-01').toISOString(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTrustedAppProvider = () =>
|
||||||
|
new TrustedAppGenerator('seed').generate(getCommonItemDataOverrides());
|
||||||
|
|
||||||
|
const getExceptionProvider = () => {
|
||||||
|
// cloneDeep needed because exception mock generator uses state across instances
|
||||||
|
return cloneDeep(
|
||||||
|
getExceptionListItemSchemaMock({
|
||||||
|
...getCommonItemDataOverrides(),
|
||||||
|
os_types: ['windows'],
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
created_by: 'Justa',
|
||||||
|
updated_by: 'Mara',
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
field: 'process.hash.*',
|
||||||
|
operator: 'included',
|
||||||
|
type: 'match',
|
||||||
|
value: '1234234659af249ddf3e40864e9fb241',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'process.executable.caseless',
|
||||||
|
operator: 'included',
|
||||||
|
type: 'match',
|
||||||
|
value: '/one/two/three',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tags: ['policy:all'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe.each([
|
||||||
|
['trusted apps', getTrustedAppProvider],
|
||||||
|
['exceptions/event filters', getExceptionProvider],
|
||||||
|
])('when using the ArtifactCardGrid component %s', (_, generateItem) => {
|
||||||
|
let appTestContext: AppContextTestRender;
|
||||||
|
let renderResult: ReturnType<AppContextTestRender['render']>;
|
||||||
|
let render: (
|
||||||
|
props?: Partial<ArtifactCardGridProps>
|
||||||
|
) => ReturnType<AppContextTestRender['render']>;
|
||||||
|
let items: ArtifactCardGridProps['items'];
|
||||||
|
let pageChangeHandler: jest.Mock<ArtifactCardGridProps['onPageChange']>;
|
||||||
|
let expandCollapseHandler: jest.Mock<ArtifactCardGridProps['onExpandCollapse']>;
|
||||||
|
let cardComponentPropsProvider: Required<ArtifactCardGridProps>['cardComponentProps'];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
items = Array.from({ length: 5 }, () => generateItem());
|
||||||
|
pageChangeHandler = jest.fn();
|
||||||
|
expandCollapseHandler = jest.fn();
|
||||||
|
cardComponentPropsProvider = jest.fn().mockReturnValue({});
|
||||||
|
|
||||||
|
appTestContext = createAppRootMockRenderer();
|
||||||
|
render = (props = {}) => {
|
||||||
|
renderResult = appTestContext.render(
|
||||||
|
<ArtifactCardGrid
|
||||||
|
{...{
|
||||||
|
items,
|
||||||
|
onPageChange: pageChangeHandler!,
|
||||||
|
onExpandCollapse: expandCollapseHandler!,
|
||||||
|
cardComponentProps: cardComponentPropsProvider,
|
||||||
|
'data-test-subj': 'testGrid',
|
||||||
|
...props,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
return renderResult;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the cards', () => {
|
||||||
|
render();
|
||||||
|
|
||||||
|
expect(renderResult.getAllByTestId('testGrid-card')).toHaveLength(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['header', 'testGrid-header'],
|
||||||
|
['expand/collapse placeholder', 'testGrid-header-expandCollapsePlaceHolder'],
|
||||||
|
['name column', 'testGrid-header-layout-title'],
|
||||||
|
['description column', 'testGrid-header-layout-description'],
|
||||||
|
['description column', 'testGrid-header-layout-cardActionsPlaceholder'],
|
||||||
|
])('should display the Grid Header - %s', (__, selector) => {
|
||||||
|
render();
|
||||||
|
|
||||||
|
expect(renderResult.getByTestId(selector)).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.todo('should call onPageChange callback when paginating');
|
||||||
|
|
||||||
|
it.todo('should use the props provided by cardComponentProps callback');
|
||||||
|
|
||||||
|
describe('and when cards are expanded/collapsed', () => {
|
||||||
|
it.todo('should call onExpandCollapse callback');
|
||||||
|
|
||||||
|
it.todo('should provide list of cards that are expanded and collapsed');
|
||||||
|
|
||||||
|
it.todo('should show card expanded if card props defined it as such');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* 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, { ComponentType, memo, useCallback, useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
AnyArtifact,
|
||||||
|
ArtifactEntryCollapsibleCard,
|
||||||
|
ArtifactEntryCollapsibleCardProps,
|
||||||
|
} from '../artifact_entry_card';
|
||||||
|
import { PaginatedContent as _PaginatedContent, PaginatedContentProps } from '../paginated_content';
|
||||||
|
import { GridHeader } from './components/grid_header';
|
||||||
|
import { MaybeImmutable } from '../../../../common/endpoint/types';
|
||||||
|
import { useTestIdGenerator } from '../hooks/use_test_id_generator';
|
||||||
|
|
||||||
|
const PaginatedContent: ArtifactsPaginatedComponent = _PaginatedContent;
|
||||||
|
|
||||||
|
type ArtifactsPaginatedContentProps = PaginatedContentProps<
|
||||||
|
AnyArtifact,
|
||||||
|
typeof ArtifactEntryCollapsibleCard
|
||||||
|
>;
|
||||||
|
|
||||||
|
type ArtifactsPaginatedComponent = ComponentType<ArtifactsPaginatedContentProps>;
|
||||||
|
|
||||||
|
interface CardExpandCollapseState {
|
||||||
|
expanded: MaybeImmutable<AnyArtifact[]>;
|
||||||
|
collapsed: MaybeImmutable<AnyArtifact[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ArtifactCardGridCardComponentProps = Omit<
|
||||||
|
ArtifactEntryCollapsibleCardProps,
|
||||||
|
'onExpandCollapse' | 'item'
|
||||||
|
>;
|
||||||
|
export type ArtifactCardGridProps = Omit<
|
||||||
|
ArtifactsPaginatedContentProps,
|
||||||
|
'ItemComponent' | 'itemComponentProps' | 'items' | 'onChange'
|
||||||
|
> & {
|
||||||
|
items: MaybeImmutable<AnyArtifact[]>;
|
||||||
|
|
||||||
|
/** Callback to handle pagination changes */
|
||||||
|
onPageChange: ArtifactsPaginatedContentProps['onChange'];
|
||||||
|
|
||||||
|
/** callback for handling changes to the card's expand/collapse state */
|
||||||
|
onExpandCollapse: (state: CardExpandCollapseState) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to provide additional props for the `ArtifactEntryCollapsibleCard`
|
||||||
|
*/
|
||||||
|
cardComponentProps?: (item: MaybeImmutable<AnyArtifact>) => ArtifactCardGridCardComponentProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ArtifactCardGrid = memo<ArtifactCardGridProps>(
|
||||||
|
({
|
||||||
|
items: _items,
|
||||||
|
cardComponentProps,
|
||||||
|
onPageChange,
|
||||||
|
onExpandCollapse,
|
||||||
|
'data-test-subj': dataTestSubj,
|
||||||
|
...paginatedContentProps
|
||||||
|
}) => {
|
||||||
|
const getTestId = useTestIdGenerator(dataTestSubj);
|
||||||
|
|
||||||
|
const items = _items as AnyArtifact[];
|
||||||
|
|
||||||
|
// The list of card props that the caller can define
|
||||||
|
type PartialCardProps = Map<AnyArtifact, ArtifactCardGridCardComponentProps>;
|
||||||
|
const callerDefinedCardProps = useMemo<PartialCardProps>(() => {
|
||||||
|
const cardProps: PartialCardProps = new Map();
|
||||||
|
|
||||||
|
for (const artifact of items) {
|
||||||
|
cardProps.set(artifact, cardComponentProps ? cardComponentProps(artifact) : {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return cardProps;
|
||||||
|
}, [cardComponentProps, items]);
|
||||||
|
|
||||||
|
// Handling of what is expanded or collapsed is done by looking at the at what the caller card's
|
||||||
|
// `expanded` prop value was and then invert it for the card that the user clicked expand/collapse
|
||||||
|
const handleCardExpandCollapse = useCallback(
|
||||||
|
(item: AnyArtifact) => {
|
||||||
|
const expanded = [];
|
||||||
|
const collapsed = [];
|
||||||
|
|
||||||
|
for (const [artifact, currentCardProps] of callerDefinedCardProps) {
|
||||||
|
const currentExpandedState = Boolean(currentCardProps.expanded);
|
||||||
|
const newExpandedState = artifact === item ? !currentExpandedState : currentExpandedState;
|
||||||
|
|
||||||
|
if (newExpandedState) {
|
||||||
|
expanded.push(artifact);
|
||||||
|
} else {
|
||||||
|
collapsed.push(artifact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExpandCollapse({ expanded, collapsed });
|
||||||
|
},
|
||||||
|
[callerDefinedCardProps, onExpandCollapse]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Full list of card props that includes the actual artifact and the callbacks
|
||||||
|
type FullCardProps = Map<AnyArtifact, ArtifactEntryCollapsibleCardProps>;
|
||||||
|
const fullCardProps = useMemo<FullCardProps>(() => {
|
||||||
|
const newFullCardProps: FullCardProps = new Map();
|
||||||
|
|
||||||
|
for (const [artifact, cardProps] of callerDefinedCardProps) {
|
||||||
|
newFullCardProps.set(artifact, {
|
||||||
|
...cardProps,
|
||||||
|
item: artifact,
|
||||||
|
onExpandCollapse: () => handleCardExpandCollapse(artifact),
|
||||||
|
'data-test-subj': cardProps['data-test-subj'] ?? getTestId('card'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return newFullCardProps;
|
||||||
|
}, [callerDefinedCardProps, getTestId, handleCardExpandCollapse]);
|
||||||
|
|
||||||
|
const handleItemComponentProps = useCallback(
|
||||||
|
(item: AnyArtifact): ArtifactEntryCollapsibleCardProps => {
|
||||||
|
return fullCardProps.get(item)!;
|
||||||
|
},
|
||||||
|
[fullCardProps]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<GridHeader data-test-subj={getTestId('header')} />
|
||||||
|
|
||||||
|
<PaginatedContent
|
||||||
|
{...paginatedContentProps}
|
||||||
|
data-test-subj={dataTestSubj}
|
||||||
|
items={items}
|
||||||
|
ItemComponent={ArtifactEntryCollapsibleCard}
|
||||||
|
itemComponentProps={handleItemComponentProps}
|
||||||
|
onChange={onPageChange}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ArtifactCardGrid.displayName = 'ArtifactCardGrid';
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo, useMemo } from 'react';
|
||||||
|
import { CommonProps, EuiText } from '@elastic/eui';
|
||||||
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { CardCompressedHeaderLayout, CardSectionPanel } from '../../artifact_entry_card';
|
||||||
|
import { useTestIdGenerator } from '../../hooks/use_test_id_generator';
|
||||||
|
|
||||||
|
const GridHeaderContainer = styled(CardSectionPanel)`
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: ${({ theme }) => theme.eui.paddingSizes.s};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export type GridHeaderProps = Pick<CommonProps, 'data-test-subj'>;
|
||||||
|
export const GridHeader = memo<GridHeaderProps>(({ 'data-test-subj': dataTestSubj }) => {
|
||||||
|
const getTestId = useTestIdGenerator(dataTestSubj);
|
||||||
|
|
||||||
|
const expandToggleElement = useMemo(
|
||||||
|
() => <div data-test-subj={getTestId('expandCollapsePlaceHolder')} style={{ width: '24px' }} />,
|
||||||
|
[getTestId]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GridHeaderContainer data-test-subj={dataTestSubj}>
|
||||||
|
<CardCompressedHeaderLayout
|
||||||
|
expanded={false}
|
||||||
|
expandToggle={expandToggleElement}
|
||||||
|
data-test-subj={getTestId('layout')}
|
||||||
|
flushTop={true}
|
||||||
|
name={
|
||||||
|
<EuiText size="xs" data-test-subj={getTestId('name')}>
|
||||||
|
<strong>
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.securitySolution.artifactCardGrid.nameColumn"
|
||||||
|
defaultMessage="Name"
|
||||||
|
/>
|
||||||
|
</strong>
|
||||||
|
</EuiText>
|
||||||
|
}
|
||||||
|
description={
|
||||||
|
<EuiText size="xs" data-test-subj={getTestId('description')}>
|
||||||
|
<strong>
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.securitySolution.artifactCardGrid.DescriptionColumn"
|
||||||
|
defaultMessage="Description"
|
||||||
|
/>
|
||||||
|
</strong>
|
||||||
|
</EuiText>
|
||||||
|
}
|
||||||
|
effectScope={
|
||||||
|
<EuiText size="xs" data-test-subj={getTestId('assignment')}>
|
||||||
|
<strong>
|
||||||
|
<FormattedMessage
|
||||||
|
id="xpack.securitySolution.artifactCardGrid.assignmentColumn"
|
||||||
|
defaultMessage="Assignment"
|
||||||
|
/>
|
||||||
|
</strong>
|
||||||
|
</EuiText>
|
||||||
|
}
|
||||||
|
actionMenu={false}
|
||||||
|
/>
|
||||||
|
</GridHeaderContainer>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
GridHeader.displayName = 'GridHeader';
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './artifact_card_grid';
|
|
@ -10,7 +10,7 @@ import { AppContextTestRender, createAppRootMockRenderer } from '../../../common
|
||||||
import { ArtifactEntryCard, ArtifactEntryCardProps } from './artifact_entry_card';
|
import { ArtifactEntryCard, ArtifactEntryCardProps } from './artifact_entry_card';
|
||||||
import { act, fireEvent, getByTestId } from '@testing-library/react';
|
import { act, fireEvent, getByTestId } from '@testing-library/react';
|
||||||
import { AnyArtifact } from './types';
|
import { AnyArtifact } from './types';
|
||||||
import { isTrustedApp } from './hooks/use_normalized_artifact';
|
import { isTrustedApp } from './utils';
|
||||||
import { getTrustedAppProvider, getExceptionProvider } from './test_utils';
|
import { getTrustedAppProvider, getExceptionProvider } from './test_utils';
|
||||||
|
|
||||||
describe.each([
|
describe.each([
|
||||||
|
|
|
@ -5,27 +5,22 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { memo, useMemo } from 'react';
|
import React, { memo } from 'react';
|
||||||
import { CommonProps, EuiHorizontalRule, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui';
|
import { CommonProps, EuiHorizontalRule, EuiSpacer, EuiText } from '@elastic/eui';
|
||||||
import styled from 'styled-components';
|
|
||||||
import { CardHeader, CardHeaderProps } from './components/card_header';
|
import { CardHeader, CardHeaderProps } from './components/card_header';
|
||||||
import { CardSubHeader } from './components/card_sub_header';
|
import { CardSubHeader } from './components/card_sub_header';
|
||||||
import { getEmptyValue } from '../../../common/components/empty_value';
|
import { getEmptyValue } from '../../../common/components/empty_value';
|
||||||
import { CriteriaConditions, CriteriaConditionsProps } from './components/criteria_conditions';
|
import { CriteriaConditions, CriteriaConditionsProps } from './components/criteria_conditions';
|
||||||
import { EffectScopeProps } from './components/effect_scope';
|
import { AnyArtifact, MenuItemPropsByPolicyId } from './types';
|
||||||
import { ContextMenuItemNavByRouterProps } from '../context_menu_with_router_support/context_menu_item_nav_by_rotuer';
|
|
||||||
import { AnyArtifact } from './types';
|
|
||||||
import { useNormalizedArtifact } from './hooks/use_normalized_artifact';
|
import { useNormalizedArtifact } from './hooks/use_normalized_artifact';
|
||||||
import { useTestIdGenerator } from '../hooks/use_test_id_generator';
|
import { useTestIdGenerator } from '../hooks/use_test_id_generator';
|
||||||
|
import { CardContainerPanel } from './components/card_container_panel';
|
||||||
const CardContainerPanel = styled(EuiPanel)`
|
import { CardSectionPanel } from './components/card_section_panel';
|
||||||
&.artifactEntryCard + &.artifactEntryCard {
|
import { usePolicyNavLinks } from './hooks/use_policy_nav_links';
|
||||||
margin-top: ${({ theme }) => theme.eui.spacerSizes.l};
|
import { MaybeImmutable } from '../../../../common/endpoint/types';
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export interface ArtifactEntryCardProps extends CommonProps {
|
export interface ArtifactEntryCardProps extends CommonProps {
|
||||||
item: AnyArtifact;
|
item: MaybeImmutable<AnyArtifact>;
|
||||||
/**
|
/**
|
||||||
* The list of actions for the card. Will display an icon with the actions in a menu if defined.
|
* The list of actions for the card. Will display an icon with the actions in a menu if defined.
|
||||||
*/
|
*/
|
||||||
|
@ -33,52 +28,25 @@ export interface ArtifactEntryCardProps extends CommonProps {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about the policies that are assigned to the `item`'s `effectScope` and that will be
|
* Information about the policies that are assigned to the `item`'s `effectScope` and that will be
|
||||||
* use to create a navigation link
|
* used to create the items in the popup context menu. This is a
|
||||||
|
* `Record<policyId: string, ContextMenuItemNavByRouterProps>`.
|
||||||
*/
|
*/
|
||||||
policies?: {
|
policies?: MenuItemPropsByPolicyId;
|
||||||
[policyId: string]: ContextMenuItemNavByRouterProps;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display Artifact Items (ex. Trusted App, Event Filter, etc) as a card.
|
* Display Artifact Items (ex. Trusted App, Event Filter, etc) as a card.
|
||||||
* This component is a TS Generic that allows you to set what the Item type is
|
* This component is a TS Generic that allows you to set what the Item type is
|
||||||
*/
|
*/
|
||||||
export const ArtifactEntryCard = memo(
|
export const ArtifactEntryCard = memo<ArtifactEntryCardProps>(
|
||||||
({
|
({ item, policies, actions, 'data-test-subj': dataTestSubj, ...commonProps }) => {
|
||||||
item,
|
const artifact = useNormalizedArtifact(item as AnyArtifact);
|
||||||
policies,
|
|
||||||
actions,
|
|
||||||
'data-test-subj': dataTestSubj,
|
|
||||||
...commonProps
|
|
||||||
}: ArtifactEntryCardProps) => {
|
|
||||||
const artifact = useNormalizedArtifact(item);
|
|
||||||
const getTestId = useTestIdGenerator(dataTestSubj);
|
const getTestId = useTestIdGenerator(dataTestSubj);
|
||||||
|
const policyNavLinks = usePolicyNavLinks(artifact, policies);
|
||||||
// create the policy links for each policy listed in the artifact record by grabbing the
|
|
||||||
// navigation data from the `policies` prop (if any)
|
|
||||||
const policyNavLinks = useMemo<EffectScopeProps['policies']>(() => {
|
|
||||||
return artifact.effectScope.type === 'policy'
|
|
||||||
? artifact?.effectScope.policies.map((id) => {
|
|
||||||
return policies && policies[id]
|
|
||||||
? policies[id]
|
|
||||||
: // else, unable to build a nav link, so just show id
|
|
||||||
{
|
|
||||||
children: id,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
: undefined;
|
|
||||||
}, [artifact.effectScope, policies]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardContainerPanel
|
<CardContainerPanel {...commonProps} data-test-subj={dataTestSubj}>
|
||||||
hasBorder={true}
|
<CardSectionPanel>
|
||||||
{...commonProps}
|
|
||||||
data-test-subj={dataTestSubj}
|
|
||||||
paddingSize="none"
|
|
||||||
className="artifactEntryCard"
|
|
||||||
>
|
|
||||||
<EuiPanel hasBorder={false} hasShadow={false} paddingSize="l">
|
|
||||||
<CardHeader
|
<CardHeader
|
||||||
name={artifact.name}
|
name={artifact.name}
|
||||||
createdDate={artifact.created_at}
|
createdDate={artifact.created_at}
|
||||||
|
@ -100,17 +68,17 @@ export const ArtifactEntryCard = memo(
|
||||||
{artifact.description || getEmptyValue()}
|
{artifact.description || getEmptyValue()}
|
||||||
</p>
|
</p>
|
||||||
</EuiText>
|
</EuiText>
|
||||||
</EuiPanel>
|
</CardSectionPanel>
|
||||||
|
|
||||||
<EuiHorizontalRule margin="none" />
|
<EuiHorizontalRule margin="none" />
|
||||||
|
|
||||||
<EuiPanel hasBorder={false} hasShadow={false} paddingSize="l">
|
<CardSectionPanel>
|
||||||
<CriteriaConditions
|
<CriteriaConditions
|
||||||
os={artifact.os as CriteriaConditionsProps['os']}
|
os={artifact.os as CriteriaConditionsProps['os']}
|
||||||
entries={artifact.entries}
|
entries={artifact.entries}
|
||||||
data-test-subj={getTestId('criteriaConditions')}
|
data-test-subj={getTestId('criteriaConditions')}
|
||||||
/>
|
/>
|
||||||
</EuiPanel>
|
</CardSectionPanel>
|
||||||
</CardContainerPanel>
|
</CardContainerPanel>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import { EuiHorizontalRule } from '@elastic/eui';
|
||||||
|
import { ArtifactEntryCardProps } from './artifact_entry_card';
|
||||||
|
import { CardContainerPanel } from './components/card_container_panel';
|
||||||
|
import { useNormalizedArtifact } from './hooks/use_normalized_artifact';
|
||||||
|
import { useTestIdGenerator } from '../hooks/use_test_id_generator';
|
||||||
|
import { CardSectionPanel } from './components/card_section_panel';
|
||||||
|
import { CriteriaConditions, CriteriaConditionsProps } from './components/criteria_conditions';
|
||||||
|
import { CardCompressedHeader } from './components/card_compressed_header';
|
||||||
|
|
||||||
|
export interface ArtifactEntryCollapsibleCardProps extends ArtifactEntryCardProps {
|
||||||
|
onExpandCollapse: () => void;
|
||||||
|
expanded?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ArtifactEntryCollapsibleCard = memo<ArtifactEntryCollapsibleCardProps>(
|
||||||
|
({
|
||||||
|
item,
|
||||||
|
onExpandCollapse,
|
||||||
|
policies,
|
||||||
|
actions,
|
||||||
|
expanded = false,
|
||||||
|
'data-test-subj': dataTestSubj,
|
||||||
|
...commonProps
|
||||||
|
}) => {
|
||||||
|
const artifact = useNormalizedArtifact(item);
|
||||||
|
const getTestId = useTestIdGenerator(dataTestSubj);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CardContainerPanel {...commonProps} data-test-subj={dataTestSubj}>
|
||||||
|
<CardSectionPanel>
|
||||||
|
<CardCompressedHeader
|
||||||
|
artifact={artifact}
|
||||||
|
actions={actions}
|
||||||
|
policies={policies}
|
||||||
|
expanded={expanded}
|
||||||
|
onExpandCollapse={onExpandCollapse}
|
||||||
|
data-test-subj={getTestId('header')}
|
||||||
|
/>
|
||||||
|
</CardSectionPanel>
|
||||||
|
{expanded && (
|
||||||
|
<>
|
||||||
|
<EuiHorizontalRule margin="xs" />
|
||||||
|
|
||||||
|
<CardSectionPanel>
|
||||||
|
<CriteriaConditions
|
||||||
|
os={artifact.os as CriteriaConditionsProps['os']}
|
||||||
|
entries={artifact.entries}
|
||||||
|
data-test-subj={getTestId('criteriaConditions')}
|
||||||
|
/>
|
||||||
|
</CardSectionPanel>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</CardContainerPanel>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ArtifactEntryCollapsibleCard.displayName = 'ArtifactEntryCollapsibleCard';
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import { CommonProps, EuiFlexItem } from '@elastic/eui';
|
||||||
|
import { ActionsContextMenu, ActionsContextMenuProps } from '../../actions_context_menu';
|
||||||
|
|
||||||
|
export interface CardActionsFlexItemProps extends Pick<CommonProps, 'data-test-subj'> {
|
||||||
|
/** If defined, then an overflow menu will be shown with the actions provided */
|
||||||
|
actions?: ActionsContextMenuProps['items'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CardActionsFlexItem = memo<CardActionsFlexItemProps>(
|
||||||
|
({ actions, 'data-test-subj': dataTestSubj }) => {
|
||||||
|
return actions && actions.length > 0 ? (
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<ActionsContextMenu items={actions} icon="boxesHorizontal" data-test-subj={dataTestSubj} />
|
||||||
|
</EuiFlexItem>
|
||||||
|
) : null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
CardActionsFlexItem.displayName = 'CardActionsFlexItem';
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo, ReactNode, useCallback } from 'react';
|
||||||
|
import { CommonProps, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { CardExpandButton } from './card_expand_button';
|
||||||
|
import { TextValueDisplay } from './text_value_display';
|
||||||
|
import { EffectScope } from './effect_scope';
|
||||||
|
import { CardActionsFlexItem } from './card_actions_flex_item';
|
||||||
|
import { ArtifactInfo } from '../types';
|
||||||
|
import { ArtifactEntryCollapsibleCardProps } from '../artifact_entry_collapsible_card';
|
||||||
|
import { useTestIdGenerator } from '../../hooks/use_test_id_generator';
|
||||||
|
import { useCollapsedCssClassNames } from '../hooks/use_collapsed_css_class_names';
|
||||||
|
import { usePolicyNavLinks } from '../hooks/use_policy_nav_links';
|
||||||
|
import { getEmptyValue } from '../../../../common/components/empty_value';
|
||||||
|
|
||||||
|
export interface CardCompressedHeaderProps
|
||||||
|
extends Pick<CommonProps, 'data-test-subj'>,
|
||||||
|
Pick<
|
||||||
|
ArtifactEntryCollapsibleCardProps,
|
||||||
|
'onExpandCollapse' | 'expanded' | 'actions' | 'policies'
|
||||||
|
> {
|
||||||
|
artifact: ArtifactInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CardCompressedHeader = memo<CardCompressedHeaderProps>(
|
||||||
|
({
|
||||||
|
artifact,
|
||||||
|
onExpandCollapse,
|
||||||
|
policies,
|
||||||
|
actions,
|
||||||
|
expanded = false,
|
||||||
|
'data-test-subj': dataTestSubj,
|
||||||
|
}) => {
|
||||||
|
const getTestId = useTestIdGenerator(dataTestSubj);
|
||||||
|
const cssClassNames = useCollapsedCssClassNames(expanded);
|
||||||
|
const policyNavLinks = usePolicyNavLinks(artifact, policies);
|
||||||
|
|
||||||
|
const handleExpandCollapseClick = useCallback(() => {
|
||||||
|
onExpandCollapse();
|
||||||
|
}, [onExpandCollapse]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiFlexGroup responsive={false} alignItems="center" data-test-subj={dataTestSubj}>
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<CardExpandButton
|
||||||
|
expanded={expanded}
|
||||||
|
onClick={handleExpandCollapseClick}
|
||||||
|
data-test-subj={getTestId('expandCollapse')}
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem className={cssClassNames}>
|
||||||
|
<EuiFlexGroup alignItems="center">
|
||||||
|
<EuiFlexItem grow={2} className={cssClassNames} data-test-subj={getTestId('title')}>
|
||||||
|
<TextValueDisplay bold truncate={!expanded}>
|
||||||
|
{artifact.name}
|
||||||
|
</TextValueDisplay>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={3}
|
||||||
|
className={cssClassNames}
|
||||||
|
data-test-subj={getTestId('description')}
|
||||||
|
>
|
||||||
|
<TextValueDisplay truncate={!expanded}>
|
||||||
|
{artifact.description || getEmptyValue()}
|
||||||
|
</TextValueDisplay>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem grow={1}>
|
||||||
|
<EffectScope policies={policyNavLinks} data-test-subj={getTestId('effectScope')} />
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<CardActionsFlexItem actions={actions} data-test-subj={getTestId('actions')} />
|
||||||
|
</EuiFlexGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
CardCompressedHeader.displayName = 'CardCompressedHeader';
|
||||||
|
|
||||||
|
const ButtonIconPlaceHolder = styled.div`
|
||||||
|
display: inline-block;
|
||||||
|
// Sizes below should match that of the Eui's Button Icon, so that it holds the same space.
|
||||||
|
width: ${({ theme }) => theme.eui.euiIconSizes.large};
|
||||||
|
height: ${({ theme }) => theme.eui.euiIconSizes.large};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledEuiFlexGroup = styled(EuiFlexGroup)`
|
||||||
|
&.flushTop,
|
||||||
|
.flushTop {
|
||||||
|
padding-top: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layout used for the compressed card header. Used also in the ArtifactGrid for creating the grid header row
|
||||||
|
*/
|
||||||
|
export interface CardCompressedHeaderLayoutProps extends Pick<CommonProps, 'data-test-subj'> {
|
||||||
|
expanded: boolean;
|
||||||
|
expandToggle: ReactNode;
|
||||||
|
name: ReactNode;
|
||||||
|
description: ReactNode;
|
||||||
|
effectScope: ReactNode;
|
||||||
|
/** If no menu is shown, but you want the space for it be preserved, set prop to `false` */
|
||||||
|
actionMenu?: ReactNode | false;
|
||||||
|
/**
|
||||||
|
* When set to `true`, all padding and margin values will be set to zero for the top of the header
|
||||||
|
* layout, so that all content is flushed to the top
|
||||||
|
*/
|
||||||
|
flushTop?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CardCompressedHeaderLayout = memo<CardCompressedHeaderLayoutProps>(
|
||||||
|
({
|
||||||
|
expanded,
|
||||||
|
name,
|
||||||
|
expandToggle,
|
||||||
|
effectScope,
|
||||||
|
actionMenu,
|
||||||
|
description,
|
||||||
|
'data-test-subj': dataTestSubj,
|
||||||
|
flushTop,
|
||||||
|
}) => {
|
||||||
|
const getTestId = useTestIdGenerator(dataTestSubj);
|
||||||
|
const cssClassNames = useCollapsedCssClassNames(expanded);
|
||||||
|
const flushTopCssClassname = flushTop ? ' flushTop' : '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledEuiFlexGroup
|
||||||
|
responsive={false}
|
||||||
|
alignItems="center"
|
||||||
|
data-test-subj={dataTestSubj}
|
||||||
|
className={flushTopCssClassname}
|
||||||
|
>
|
||||||
|
<EuiFlexItem grow={false} className={flushTopCssClassname}>
|
||||||
|
{expandToggle}
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem className={cssClassNames + flushTopCssClassname}>
|
||||||
|
<EuiFlexGroup alignItems="center" className={flushTopCssClassname}>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={2}
|
||||||
|
className={cssClassNames + flushTopCssClassname}
|
||||||
|
data-test-subj={getTestId('title')}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={3}
|
||||||
|
className={cssClassNames + flushTopCssClassname}
|
||||||
|
data-test-subj={getTestId('description')}
|
||||||
|
>
|
||||||
|
{description}
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={1}
|
||||||
|
data-test-subj={getTestId('effectScope')}
|
||||||
|
className={flushTopCssClassname}
|
||||||
|
>
|
||||||
|
{effectScope}
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
</EuiFlexItem>
|
||||||
|
{actionMenu === false ? (
|
||||||
|
<EuiFlexItem
|
||||||
|
grow={false}
|
||||||
|
data-test-subj={getTestId('cardActionsPlaceholder')}
|
||||||
|
className={flushTopCssClassname}
|
||||||
|
>
|
||||||
|
<ButtonIconPlaceHolder />
|
||||||
|
</EuiFlexItem>
|
||||||
|
) : (
|
||||||
|
actionMenu
|
||||||
|
)}
|
||||||
|
</StyledEuiFlexGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
CardCompressedHeaderLayout.displayName = 'CardCompressedHeaderLayout';
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 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 styled from 'styled-components';
|
||||||
|
import { EuiPanel } from '@elastic/eui';
|
||||||
|
import { EuiPanelProps } from '@elastic/eui/src/components/panel/panel';
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
|
||||||
|
export const EuiPanelStyled = styled(EuiPanel)`
|
||||||
|
&.artifactEntryCard + &.artifactEntryCard {
|
||||||
|
margin-top: ${({ theme }) => theme.eui.spacerSizes.l};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export type CardContainerPanelProps = Exclude<EuiPanelProps, 'hasBorder' | 'paddingSize'>;
|
||||||
|
|
||||||
|
export const CardContainerPanel = memo<CardContainerPanelProps>(({ className, ...props }) => {
|
||||||
|
return (
|
||||||
|
<EuiPanelStyled
|
||||||
|
{...props}
|
||||||
|
hasBorder={true}
|
||||||
|
paddingSize="none"
|
||||||
|
className={`artifactEntryCard ${className ?? ''}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CardContainerPanel.displayName = 'CardContainerPanel';
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import { CommonProps, EuiButtonIcon, EuiButtonIconPropsForButton } from '@elastic/eui';
|
||||||
|
import { COLLAPSE_ACTION, EXPAND_ACTION } from './translations';
|
||||||
|
|
||||||
|
export interface CardExpandButtonProps extends Pick<CommonProps, 'data-test-subj'> {
|
||||||
|
expanded: boolean;
|
||||||
|
onClick: EuiButtonIconPropsForButton['onClick'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CardExpandButton = memo<CardExpandButtonProps>(
|
||||||
|
({ expanded, onClick, 'data-test-subj': dataTestSubj }) => {
|
||||||
|
return (
|
||||||
|
<EuiButtonIcon
|
||||||
|
iconType={expanded ? 'arrowUp' : 'arrowDown'}
|
||||||
|
onClick={onClick}
|
||||||
|
data-test-subj={dataTestSubj}
|
||||||
|
aria-label={expanded ? COLLAPSE_ACTION : EXPAND_ACTION}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
CardExpandButton.displayName = 'CardExpandButton';
|
|
@ -8,15 +8,15 @@
|
||||||
import React, { memo } from 'react';
|
import React, { memo } from 'react';
|
||||||
import { CommonProps, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
|
import { CommonProps, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
|
||||||
import { DateFieldValue } from './date_field_value';
|
import { DateFieldValue } from './date_field_value';
|
||||||
import { ActionsContextMenu, ActionsContextMenuProps } from '../../actions_context_menu';
|
|
||||||
import { useTestIdGenerator } from '../../hooks/use_test_id_generator';
|
import { useTestIdGenerator } from '../../hooks/use_test_id_generator';
|
||||||
|
import { CardActionsFlexItem, CardActionsFlexItemProps } from './card_actions_flex_item';
|
||||||
|
|
||||||
export interface CardHeaderProps extends Pick<CommonProps, 'data-test-subj'> {
|
export interface CardHeaderProps
|
||||||
|
extends CardActionsFlexItemProps,
|
||||||
|
Pick<CommonProps, 'data-test-subj'> {
|
||||||
name: string;
|
name: string;
|
||||||
createdDate: string;
|
createdDate: string;
|
||||||
updatedDate: string;
|
updatedDate: string;
|
||||||
/** If defined, then an overflow menu will be shown with the actions provided */
|
|
||||||
actions?: ActionsContextMenuProps['items'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CardHeader = memo<CardHeaderProps>(
|
export const CardHeader = memo<CardHeaderProps>(
|
||||||
|
@ -52,15 +52,7 @@ export const CardHeader = memo<CardHeaderProps>(
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
{actions && actions.length > 0 && (
|
<CardActionsFlexItem actions={actions} data-test-subj={getTestId('actions')} />
|
||||||
<EuiFlexItem grow={false}>
|
|
||||||
<ActionsContextMenu
|
|
||||||
items={actions}
|
|
||||||
icon="boxesHorizontal"
|
|
||||||
data-test-subj={getTestId('actions')}
|
|
||||||
/>
|
|
||||||
</EuiFlexItem>
|
|
||||||
)}
|
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import { EuiPanel, EuiPanelProps } from '@elastic/eui';
|
||||||
|
|
||||||
|
export type CardSectionPanelProps = Exclude<
|
||||||
|
EuiPanelProps,
|
||||||
|
'hasBorder' | 'hasShadow' | 'paddingSize'
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const CardSectionPanel = memo<CardSectionPanelProps>((props) => {
|
||||||
|
return <EuiPanel {...props} hasBorder={false} hasShadow={false} paddingSize="l" />;
|
||||||
|
});
|
||||||
|
CardSectionPanel.displayName = 'CardSectionPanel';
|
|
@ -20,7 +20,7 @@ export const CardSubHeader = memo<SubHeaderProps>(
|
||||||
const getTestId = useTestIdGenerator(dataTestSubj);
|
const getTestId = useTestIdGenerator(dataTestSubj);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiFlexGroup alignItems="center" responsive={false} data-test-subj={dataTestSubj}>
|
<EuiFlexGroup alignItems="center" responsive={true} data-test-subj={dataTestSubj}>
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<TouchedByUsers
|
<TouchedByUsers
|
||||||
createdBy={createdBy}
|
createdBy={createdBy}
|
||||||
|
|
|
@ -10,9 +10,15 @@ import { CommonProps, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon } from
|
||||||
import { GLOBAL_EFFECT_SCOPE, POLICY_EFFECT_SCOPE } from './translations';
|
import { GLOBAL_EFFECT_SCOPE, POLICY_EFFECT_SCOPE } from './translations';
|
||||||
import { TextValueDisplay } from './text_value_display';
|
import { TextValueDisplay } from './text_value_display';
|
||||||
import { ContextMenuWithRouterSupport } from '../../context_menu_with_router_support';
|
import { ContextMenuWithRouterSupport } from '../../context_menu_with_router_support';
|
||||||
import { ContextMenuItemNavByRouterProps } from '../../context_menu_with_router_support/context_menu_item_nav_by_rotuer';
|
import { ContextMenuItemNavByRouterProps } from '../../context_menu_with_router_support/context_menu_item_nav_by_router';
|
||||||
import { useTestIdGenerator } from '../../hooks/use_test_id_generator';
|
import { useTestIdGenerator } from '../../hooks/use_test_id_generator';
|
||||||
|
|
||||||
|
// FIXME:PT support being able to show per policy label for Artifacst that have >0 policies, but no menu
|
||||||
|
// the intent in this component was to also support to be able to display only text for artifacts
|
||||||
|
// by policy (>0), but **NOT** show the menu.
|
||||||
|
// So something like: `<EffectScope perPolicyCount={3} />`
|
||||||
|
// This should dispaly it as "Applied t o 3 policies", but NOT as a menu with links
|
||||||
|
|
||||||
export interface EffectScopeProps extends Pick<CommonProps, 'data-test-subj'> {
|
export interface EffectScopeProps extends Pick<CommonProps, 'data-test-subj'> {
|
||||||
/** If set (even if empty), then effect scope will be policy specific. Else, it shows as global */
|
/** If set (even if empty), then effect scope will be policy specific. Else, it shows as global */
|
||||||
policies?: ContextMenuItemNavByRouterProps[];
|
policies?: ContextMenuItemNavByRouterProps[];
|
||||||
|
|
|
@ -5,18 +5,30 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { memo, PropsWithChildren } from 'react';
|
import React, { memo, PropsWithChildren, useMemo } from 'react';
|
||||||
import { EuiText } from '@elastic/eui';
|
import { EuiText } from '@elastic/eui';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
export type TextValueDisplayProps = PropsWithChildren<{
|
export type TextValueDisplayProps = PropsWithChildren<{
|
||||||
bold?: boolean;
|
bold?: boolean;
|
||||||
|
truncate?: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common component for displaying consistent text across the card. Changes here could impact all of
|
* Common component for displaying consistent text across the card. Changes here could impact all of
|
||||||
* display of values on the card
|
* display of values on the card
|
||||||
*/
|
*/
|
||||||
export const TextValueDisplay = memo<TextValueDisplayProps>(({ bold, children }) => {
|
export const TextValueDisplay = memo<TextValueDisplayProps>(({ bold, truncate, children }) => {
|
||||||
return <EuiText size="s">{bold ? <strong>{children}</strong> : children}</EuiText>;
|
const cssClassNames = useMemo(() => {
|
||||||
|
return classNames({
|
||||||
|
'eui-textTruncate': truncate,
|
||||||
|
});
|
||||||
|
}, [truncate]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EuiText size="s" className={cssClassNames}>
|
||||||
|
{bold ? <strong>{children}</strong> : children}
|
||||||
|
</EuiText>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
TextValueDisplay.displayName = 'TextValueDisplay';
|
TextValueDisplay.displayName = 'TextValueDisplay';
|
||||||
|
|
|
@ -100,3 +100,17 @@ export const OS_LINUX = i18n.translate('xpack.securitySolution.artifactCard.cond
|
||||||
export const OS_MAC = i18n.translate('xpack.securitySolution.artifactCard.conditions.macos', {
|
export const OS_MAC = i18n.translate('xpack.securitySolution.artifactCard.conditions.macos', {
|
||||||
defaultMessage: 'Mac',
|
defaultMessage: 'Mac',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const EXPAND_ACTION = i18n.translate(
|
||||||
|
'xpack.securitySolution.artifactExpandableCard.expand',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Expand',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const COLLAPSE_ACTION = i18n.translate(
|
||||||
|
'xpack.securitySolution.artifactExpandableCard.collpase',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Collapse',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the css classnames that should be applied when the collapsible card is NOT expanded
|
||||||
|
* @param expanded
|
||||||
|
*/
|
||||||
|
export const useCollapsedCssClassNames = (expanded?: boolean): string => {
|
||||||
|
return useMemo(() => {
|
||||||
|
return classNames({
|
||||||
|
'eui-textTruncate': !expanded,
|
||||||
|
});
|
||||||
|
}, [expanded]);
|
||||||
|
};
|
|
@ -5,53 +5,18 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
|
||||||
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
|
||||||
import { AnyArtifact, ArtifactInfo } from '../types';
|
import { AnyArtifact, ArtifactInfo } from '../types';
|
||||||
import { EffectScope, TrustedApp } from '../../../../../common/endpoint/types';
|
import { mapToArtifactInfo } from '../utils';
|
||||||
import { tagsToEffectScope } from '../../../../../common/endpoint/service/trusted_apps/mapping';
|
import { MaybeImmutable } from '../../../../../common/endpoint/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes in any artifact and return back a new data structure used internally with by the card's components
|
* Takes in any artifact and return back a new data structure used internally with by the card's components
|
||||||
*
|
*
|
||||||
* @param item
|
* @param item
|
||||||
*/
|
*/
|
||||||
export const useNormalizedArtifact = (item: AnyArtifact): ArtifactInfo => {
|
export const useNormalizedArtifact = (item: MaybeImmutable<AnyArtifact>): ArtifactInfo => {
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const {
|
return mapToArtifactInfo(item);
|
||||||
name,
|
|
||||||
created_by,
|
|
||||||
created_at,
|
|
||||||
updated_at,
|
|
||||||
updated_by,
|
|
||||||
description = '',
|
|
||||||
entries,
|
|
||||||
} = item;
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
created_by,
|
|
||||||
created_at,
|
|
||||||
updated_at,
|
|
||||||
updated_by,
|
|
||||||
description,
|
|
||||||
entries: entries as unknown as ArtifactInfo['entries'],
|
|
||||||
os: isTrustedApp(item) ? item.os : getOsFromExceptionItem(item),
|
|
||||||
effectScope: isTrustedApp(item) ? item.effectScope : getEffectScopeFromExceptionItem(item),
|
|
||||||
};
|
|
||||||
}, [item]);
|
}, [item]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isTrustedApp = (item: AnyArtifact): item is TrustedApp => {
|
|
||||||
return 'effectScope' in item;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getOsFromExceptionItem = (item: ExceptionListItemSchema): string => {
|
|
||||||
// FYI: Exceptions seem to allow for items to be assigned to more than one OS, unlike Event Filters and Trusted Apps
|
|
||||||
return item.os_types.join(', ');
|
|
||||||
};
|
|
||||||
|
|
||||||
const getEffectScopeFromExceptionItem = (item: ExceptionListItemSchema): EffectScope => {
|
|
||||||
return tagsToEffectScope(item.tags);
|
|
||||||
};
|
|
||||||
|
|
|
@ -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 { useMemo } from 'react';
|
||||||
|
import { EffectScopeProps } from '../components/effect_scope';
|
||||||
|
import { ArtifactInfo, MenuItemPropsByPolicyId } from '../types';
|
||||||
|
import { ContextMenuItemNavByRouterProps } from '../../context_menu_with_router_support/context_menu_item_nav_by_router';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates the policy links for each policy listed in the artifact record by grabbing the
|
||||||
|
* navigation data from the `policies` prop (if any)
|
||||||
|
*/
|
||||||
|
export const usePolicyNavLinks = (
|
||||||
|
artifact: ArtifactInfo,
|
||||||
|
policies?: MenuItemPropsByPolicyId
|
||||||
|
): ContextMenuItemNavByRouterProps[] | undefined => {
|
||||||
|
return useMemo<EffectScopeProps['policies']>(() => {
|
||||||
|
return artifact.effectScope.type === 'policy'
|
||||||
|
? artifact?.effectScope.policies.map((id) => {
|
||||||
|
return policies && policies[id]
|
||||||
|
? policies[id]
|
||||||
|
: // else, unable to build a nav link, so just show id
|
||||||
|
{
|
||||||
|
children: id,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
}, [artifact.effectScope, policies]);
|
||||||
|
};
|
|
@ -7,3 +7,7 @@
|
||||||
|
|
||||||
export * from './artifact_entry_card';
|
export * from './artifact_entry_card';
|
||||||
export * from './artifact_entry_card_minified';
|
export * from './artifact_entry_card_minified';
|
||||||
|
export * from './artifact_entry_collapsible_card';
|
||||||
|
export * from './components/card_section_panel';
|
||||||
|
export * from './types';
|
||||||
|
export { CardCompressedHeaderLayout } from './components/card_compressed_header';
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||||
import { EffectScope, TrustedApp } from '../../../../common/endpoint/types';
|
import { EffectScope, TrustedApp } from '../../../../common/endpoint/types';
|
||||||
|
import { ContextMenuItemNavByRouterProps } from '../context_menu_with_router_support/context_menu_item_nav_by_router';
|
||||||
|
|
||||||
export type AnyArtifact = ExceptionListItemSchema | TrustedApp;
|
export type AnyArtifact = ExceptionListItemSchema | TrustedApp;
|
||||||
|
|
||||||
|
@ -27,3 +28,7 @@ export interface ArtifactInfo
|
||||||
value: string;
|
value: string;
|
||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MenuItemPropsByPolicyId {
|
||||||
|
[policyId: string]: ContextMenuItemNavByRouterProps;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './is_trusted_app';
|
||||||
|
export * from './map_to_artifact_info';
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* 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 { AnyArtifact } from '../types';
|
||||||
|
import { TrustedApp } from '../../../../../common/endpoint/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type guard for `AnyArtifact` to check if it is a trusted app entry
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
export const isTrustedApp = (item: AnyArtifact): item is TrustedApp => {
|
||||||
|
return 'effectScope' in item;
|
||||||
|
};
|
|
@ -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 { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||||
|
import { AnyArtifact, ArtifactInfo } from '../types';
|
||||||
|
import { EffectScope, MaybeImmutable } from '../../../../../common/endpoint/types';
|
||||||
|
import { tagsToEffectScope } from '../../../../../common/endpoint/service/trusted_apps/mapping';
|
||||||
|
import { isTrustedApp } from './is_trusted_app';
|
||||||
|
|
||||||
|
export const mapToArtifactInfo = (_item: MaybeImmutable<AnyArtifact>): ArtifactInfo => {
|
||||||
|
const item = _item as AnyArtifact;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
const { name, created_by, created_at, updated_at, updated_by, description = '', entries } = item;
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
created_by,
|
||||||
|
created_at,
|
||||||
|
updated_at,
|
||||||
|
updated_by,
|
||||||
|
description,
|
||||||
|
entries: entries as unknown as ArtifactInfo['entries'],
|
||||||
|
os: isTrustedApp(item) ? item.os : getOsFromExceptionItem(item),
|
||||||
|
effectScope: isTrustedApp(item) ? item.effectScope : getEffectScopeFromExceptionItem(item),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOsFromExceptionItem = (item: ExceptionListItemSchema): string => {
|
||||||
|
// FYI: Exceptions seem to allow for items to be assigned to more than one OS, unlike Event Filters and Trusted Apps
|
||||||
|
return item.os_types.join(', ');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEffectScopeFromExceptionItem = (item: ExceptionListItemSchema): EffectScope => {
|
||||||
|
return tagsToEffectScope(item.tags);
|
||||||
|
};
|
|
@ -15,6 +15,12 @@ export interface ContextMenuItemNavByRouterProps extends EuiContextMenuItemProps
|
||||||
navigateAppId?: string;
|
navigateAppId?: string;
|
||||||
/** Additional options for the navigation action via react-router */
|
/** Additional options for the navigation action via react-router */
|
||||||
navigateOptions?: NavigateToAppOptions;
|
navigateOptions?: NavigateToAppOptions;
|
||||||
|
/**
|
||||||
|
* if `true`, the `children` will be wrapped in a `div` that contains CSS Classname `eui-textTruncate`.
|
||||||
|
* **NOTE**: When this component is used in combination with `ContextMenuWithRouterSupport` and `maxWidth`
|
||||||
|
* is set on the menu component, this prop will be overridden
|
||||||
|
*/
|
||||||
|
textTruncate?: boolean;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +29,7 @@ export interface ContextMenuItemNavByRouterProps extends EuiContextMenuItemProps
|
||||||
* allow navigation to a URL path via React Router
|
* allow navigation to a URL path via React Router
|
||||||
*/
|
*/
|
||||||
export const ContextMenuItemNavByRouter = memo<ContextMenuItemNavByRouterProps>(
|
export const ContextMenuItemNavByRouter = memo<ContextMenuItemNavByRouterProps>(
|
||||||
({ navigateAppId, navigateOptions, onClick, children, ...otherMenuItemProps }) => {
|
({ navigateAppId, navigateOptions, onClick, textTruncate, children, ...otherMenuItemProps }) => {
|
||||||
const handleOnClickViaNavigateToApp = useNavigateToAppEventHandler(navigateAppId ?? '', {
|
const handleOnClickViaNavigateToApp = useNavigateToAppEventHandler(navigateAppId ?? '', {
|
||||||
...navigateOptions,
|
...navigateOptions,
|
||||||
onClick,
|
onClick,
|
||||||
|
@ -34,7 +40,19 @@ export const ContextMenuItemNavByRouter = memo<ContextMenuItemNavByRouterProps>(
|
||||||
{...otherMenuItemProps}
|
{...otherMenuItemProps}
|
||||||
onClick={navigateAppId ? handleOnClickViaNavigateToApp : onClick}
|
onClick={navigateAppId ? handleOnClickViaNavigateToApp : onClick}
|
||||||
>
|
>
|
||||||
{children}
|
{textTruncate ? (
|
||||||
|
<div
|
||||||
|
className="eui-textTruncate"
|
||||||
|
{
|
||||||
|
/* Add the html `title` prop if children is a string */
|
||||||
|
...('string' === typeof children ? { title: children } : {})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
)}
|
||||||
</EuiContextMenuItem>
|
</EuiContextMenuItem>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { memo, useCallback, useMemo, useState } from 'react';
|
import React, { CSSProperties, HTMLAttributes, memo, useCallback, useMemo, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
CommonProps,
|
CommonProps,
|
||||||
EuiContextMenuPanel,
|
EuiContextMenuPanel,
|
||||||
|
@ -16,13 +16,19 @@ import {
|
||||||
import {
|
import {
|
||||||
ContextMenuItemNavByRouter,
|
ContextMenuItemNavByRouter,
|
||||||
ContextMenuItemNavByRouterProps,
|
ContextMenuItemNavByRouterProps,
|
||||||
} from './context_menu_item_nav_by_rotuer';
|
} from './context_menu_item_nav_by_router';
|
||||||
import { useTestIdGenerator } from '../hooks/use_test_id_generator';
|
import { useTestIdGenerator } from '../hooks/use_test_id_generator';
|
||||||
|
|
||||||
export interface ContextMenuWithRouterSupportProps
|
export interface ContextMenuWithRouterSupportProps
|
||||||
extends CommonProps,
|
extends CommonProps,
|
||||||
Pick<EuiPopoverProps, 'button' | 'anchorPosition' | 'panelPaddingSize'> {
|
Pick<EuiPopoverProps, 'button' | 'anchorPosition' | 'panelPaddingSize'> {
|
||||||
items: ContextMenuItemNavByRouterProps[];
|
items: ContextMenuItemNavByRouterProps[];
|
||||||
|
/**
|
||||||
|
* The max width for the popup menu. Default is `32ch`.
|
||||||
|
* **Note** that when used (default behaviour), all menu item's `truncateText` prop will be
|
||||||
|
* overwritten to `true`. Setting this prop's value to `undefined` will suppress the default behaviour.
|
||||||
|
*/
|
||||||
|
maxWidth?: CSSProperties['maxWidth'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +37,7 @@ export interface ContextMenuWithRouterSupportProps
|
||||||
* Menu also supports automatically closing the popup when an item is clicked.
|
* Menu also supports automatically closing the popup when an item is clicked.
|
||||||
*/
|
*/
|
||||||
export const ContextMenuWithRouterSupport = memo<ContextMenuWithRouterSupportProps>(
|
export const ContextMenuWithRouterSupport = memo<ContextMenuWithRouterSupportProps>(
|
||||||
({ items, button, panelPaddingSize, anchorPosition, ...commonProps }) => {
|
({ items, button, panelPaddingSize, anchorPosition, maxWidth = '32ch', ...commonProps }) => {
|
||||||
const getTestId = useTestIdGenerator(commonProps['data-test-subj']);
|
const getTestId = useTestIdGenerator(commonProps['data-test-subj']);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
|
@ -47,6 +53,7 @@ export const ContextMenuWithRouterSupport = memo<ContextMenuWithRouterSupportPro
|
||||||
return (
|
return (
|
||||||
<ContextMenuItemNavByRouter
|
<ContextMenuItemNavByRouter
|
||||||
{...itemProps}
|
{...itemProps}
|
||||||
|
textTruncate={Boolean(maxWidth) || itemProps.textTruncate}
|
||||||
onClick={(ev) => {
|
onClick={(ev) => {
|
||||||
handleCloseMenu();
|
handleCloseMenu();
|
||||||
if (itemProps.onClick) {
|
if (itemProps.onClick) {
|
||||||
|
@ -56,7 +63,20 @@ export const ContextMenuWithRouterSupport = memo<ContextMenuWithRouterSupportPro
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}, [handleCloseMenu, items]);
|
}, [handleCloseMenu, items, maxWidth]);
|
||||||
|
|
||||||
|
type AdditionalPanelProps = Partial<EuiContextMenuPanelProps & HTMLAttributes<HTMLDivElement>>;
|
||||||
|
const additionalContextMenuPanelProps = useMemo<AdditionalPanelProps>(() => {
|
||||||
|
const newAdditionalProps: AdditionalPanelProps = {
|
||||||
|
style: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (maxWidth) {
|
||||||
|
newAdditionalProps.style!.maxWidth = maxWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newAdditionalProps;
|
||||||
|
}, [maxWidth]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiPopover
|
<EuiPopover
|
||||||
|
@ -73,7 +93,7 @@ export const ContextMenuWithRouterSupport = memo<ContextMenuWithRouterSupportPro
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
closePopover={handleCloseMenu}
|
closePopover={handleCloseMenu}
|
||||||
>
|
>
|
||||||
<EuiContextMenuPanel items={menuItems} />
|
<EuiContextMenuPanel {...additionalContextMenuPanelProps} items={menuItems} />
|
||||||
</EuiPopover>
|
</EuiPopover>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
EuiPopoverProps,
|
EuiPopoverProps,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { ContextMenuItemNavByRouter } from '../../../../components/context_menu_with_router_support/context_menu_item_nav_by_rotuer';
|
import { ContextMenuItemNavByRouter } from '../../../../components/context_menu_with_router_support/context_menu_item_nav_by_router';
|
||||||
import { HostMetadata } from '../../../../../../common/endpoint/types';
|
import { HostMetadata } from '../../../../../../common/endpoint/types';
|
||||||
import { useEndpointActionItems } from '../hooks';
|
import { useEndpointActionItems } from '../hooks';
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { EuiContextMenuPanel, EuiButton, EuiPopover } from '@elastic/eui';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
import { useEndpointActionItems, useEndpointSelector } from '../../hooks';
|
import { useEndpointActionItems, useEndpointSelector } from '../../hooks';
|
||||||
import { detailsData } from '../../../store/selectors';
|
import { detailsData } from '../../../store/selectors';
|
||||||
import { ContextMenuItemNavByRouter } from '../../../../../components/context_menu_with_router_support/context_menu_item_nav_by_rotuer';
|
import { ContextMenuItemNavByRouter } from '../../../../../components/context_menu_with_router_support/context_menu_item_nav_by_router';
|
||||||
|
|
||||||
export const ActionsMenu = React.memo<{}>(() => {
|
export const ActionsMenu = React.memo<{}>(() => {
|
||||||
const endpointDetails = useEndpointSelector(detailsData);
|
const endpointDetails = useEndpointSelector(detailsData);
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { HostMetadata, MaybeImmutable } from '../../../../../../common/endpoint/
|
||||||
import { useEndpointSelector } from './hooks';
|
import { useEndpointSelector } from './hooks';
|
||||||
import { agentPolicies, uiQueryParams } from '../../store/selectors';
|
import { agentPolicies, uiQueryParams } from '../../store/selectors';
|
||||||
import { useAppUrl } from '../../../../../common/lib/kibana/hooks';
|
import { useAppUrl } from '../../../../../common/lib/kibana/hooks';
|
||||||
import { ContextMenuItemNavByRouterProps } from '../../../../components/context_menu_with_router_support/context_menu_item_nav_by_rotuer';
|
import { ContextMenuItemNavByRouterProps } from '../../../../components/context_menu_with_router_support/context_menu_item_nav_by_router';
|
||||||
import { isEndpointHostIsolated } from '../../../../../common/utils/validators';
|
import { isEndpointHostIsolated } from '../../../../../common/utils/validators';
|
||||||
import { useLicense } from '../../../../../common/hooks/use_license';
|
import { useLicense } from '../../../../../common/hooks/use_license';
|
||||||
import { isIsolationSupported } from '../../../../../../common/endpoint/service/host_isolation/utils';
|
import { isIsolationSupported } from '../../../../../../common/endpoint/service/host_isolation/utils';
|
||||||
|
|
|
@ -5,14 +5,17 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Action } from 'redux';
|
||||||
import { AsyncResourceState } from '../../../../../state';
|
import { AsyncResourceState } from '../../../../../state';
|
||||||
import {
|
import {
|
||||||
PostTrustedAppCreateResponse,
|
PostTrustedAppCreateResponse,
|
||||||
GetTrustedListAppsResponse,
|
GetTrustedAppsListResponse,
|
||||||
} from '../../../../../../../common/endpoint/types';
|
} from '../../../../../../../common/endpoint/types';
|
||||||
|
import { PolicyArtifactsState } from '../../../types';
|
||||||
|
|
||||||
export interface PolicyArtifactsAssignableListPageDataChanged {
|
export interface PolicyArtifactsAssignableListPageDataChanged {
|
||||||
type: 'policyArtifactsAssignableListPageDataChanged';
|
type: 'policyArtifactsAssignableListPageDataChanged';
|
||||||
payload: AsyncResourceState<GetTrustedListAppsResponse>;
|
payload: AsyncResourceState<GetTrustedAppsListResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PolicyArtifactsUpdateTrustedApps {
|
export interface PolicyArtifactsUpdateTrustedApps {
|
||||||
|
@ -37,9 +40,28 @@ export interface PolicyArtifactsAssignableListPageDataFilter {
|
||||||
payload: { filter: string };
|
payload: { filter: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AssignedTrustedAppsListStateChanged
|
||||||
|
extends Action<'assignedTrustedAppsListStateChanged'> {
|
||||||
|
payload: PolicyArtifactsState['assignedList'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PolicyDetailsListOfAllPoliciesStateChanged
|
||||||
|
extends Action<'policyDetailsListOfAllPoliciesStateChanged'> {
|
||||||
|
payload: PolicyArtifactsState['policies'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PolicyDetailsTrustedAppsForceListDataRefresh =
|
||||||
|
Action<'policyDetailsTrustedAppsForceListDataRefresh'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All of the possible actions for Trusted Apps under the Policy Details store
|
||||||
|
*/
|
||||||
export type PolicyTrustedAppsAction =
|
export type PolicyTrustedAppsAction =
|
||||||
| PolicyArtifactsAssignableListPageDataChanged
|
| PolicyArtifactsAssignableListPageDataChanged
|
||||||
| PolicyArtifactsUpdateTrustedApps
|
| PolicyArtifactsUpdateTrustedApps
|
||||||
| PolicyArtifactsUpdateTrustedAppsChanged
|
| PolicyArtifactsUpdateTrustedAppsChanged
|
||||||
| PolicyArtifactsAssignableListExistDataChanged
|
| PolicyArtifactsAssignableListExistDataChanged
|
||||||
| PolicyArtifactsAssignableListPageDataFilter;
|
| PolicyArtifactsAssignableListPageDataFilter
|
||||||
|
| AssignedTrustedAppsListStateChanged
|
||||||
|
| PolicyDetailsListOfAllPoliciesStateChanged
|
||||||
|
| PolicyDetailsTrustedAppsForceListDataRefresh;
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ImmutableMiddlewareFactory } from '../../../../../../common/store';
|
import { ImmutableMiddlewareFactory } from '../../../../../../common/store';
|
||||||
import { PolicyDetailsState } from '../../../types';
|
import { MiddlewareRunnerContext, PolicyDetailsState } from '../../../types';
|
||||||
import { policyTrustedAppsMiddlewareRunner } from './policy_trusted_apps_middleware';
|
import { policyTrustedAppsMiddlewareRunner } from './policy_trusted_apps_middleware';
|
||||||
import { policySettingsMiddlewareRunner } from './policy_settings_middleware';
|
import { policySettingsMiddlewareRunner } from './policy_settings_middleware';
|
||||||
|
import { TrustedAppsHttpService } from '../../../../trusted_apps/service';
|
||||||
|
|
||||||
export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory<PolicyDetailsState> = (
|
export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory<PolicyDetailsState> = (
|
||||||
coreStart
|
coreStart
|
||||||
|
@ -16,7 +17,13 @@ export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory<PolicyDe
|
||||||
return (store) => (next) => async (action) => {
|
return (store) => (next) => async (action) => {
|
||||||
next(action);
|
next(action);
|
||||||
|
|
||||||
policySettingsMiddlewareRunner(coreStart, store, action);
|
const trustedAppsService = new TrustedAppsHttpService(coreStart.http);
|
||||||
policyTrustedAppsMiddlewareRunner(coreStart, store, action);
|
const middlewareContext: MiddlewareRunnerContext = {
|
||||||
|
coreStart,
|
||||||
|
trustedAppsService,
|
||||||
|
};
|
||||||
|
|
||||||
|
policySettingsMiddlewareRunner(middlewareContext, store, action);
|
||||||
|
policyTrustedAppsMiddlewareRunner(middlewareContext, store, action);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,7 +27,7 @@ import { NewPolicyData, PolicyData } from '../../../../../../../common/endpoint/
|
||||||
import { getPolicyDataForUpdate } from '../../../../../../../common/endpoint/service/policy';
|
import { getPolicyDataForUpdate } from '../../../../../../../common/endpoint/service/policy';
|
||||||
|
|
||||||
export const policySettingsMiddlewareRunner: MiddlewareRunner = async (
|
export const policySettingsMiddlewareRunner: MiddlewareRunner = async (
|
||||||
coreStart,
|
{ coreStart },
|
||||||
{ dispatch, getState },
|
{ dispatch, getState },
|
||||||
action
|
action
|
||||||
) => {
|
) => {
|
||||||
|
|
|
@ -7,30 +7,95 @@
|
||||||
|
|
||||||
import pMap from 'p-map';
|
import pMap from 'p-map';
|
||||||
import { find, isEmpty } from 'lodash/fp';
|
import { find, isEmpty } from 'lodash/fp';
|
||||||
import { PolicyDetailsState, MiddlewareRunner } from '../../../types';
|
import {
|
||||||
|
PolicyDetailsState,
|
||||||
|
MiddlewareRunner,
|
||||||
|
GetPolicyListResponse,
|
||||||
|
MiddlewareRunnerContext,
|
||||||
|
PolicyAssignedTrustedApps,
|
||||||
|
PolicyDetailsStore,
|
||||||
|
} from '../../../types';
|
||||||
import {
|
import {
|
||||||
policyIdFromParams,
|
policyIdFromParams,
|
||||||
isOnPolicyTrustedAppsPage,
|
|
||||||
getCurrentArtifactsLocation,
|
|
||||||
getAssignableArtifactsList,
|
getAssignableArtifactsList,
|
||||||
|
doesPolicyTrustedAppsListNeedUpdate,
|
||||||
|
getCurrentPolicyAssignedTrustedAppsState,
|
||||||
|
getLatestLoadedPolicyAssignedTrustedAppsState,
|
||||||
|
getTrustedAppsPolicyListState,
|
||||||
|
isPolicyTrustedAppListLoading,
|
||||||
|
getCurrentArtifactsLocation,
|
||||||
|
isOnPolicyTrustedAppsView,
|
||||||
|
getCurrentUrlLocationPaginationParams,
|
||||||
} from '../selectors';
|
} from '../selectors';
|
||||||
import {
|
import {
|
||||||
ImmutableArray,
|
ImmutableArray,
|
||||||
ImmutableObject,
|
ImmutableObject,
|
||||||
PostTrustedAppCreateRequest,
|
PostTrustedAppCreateRequest,
|
||||||
TrustedApp,
|
TrustedApp,
|
||||||
|
Immutable,
|
||||||
} from '../../../../../../../common/endpoint/types';
|
} from '../../../../../../../common/endpoint/types';
|
||||||
import { ImmutableMiddlewareAPI } from '../../../../../../common/store';
|
import { ImmutableMiddlewareAPI } from '../../../../../../common/store';
|
||||||
import { TrustedAppsHttpService, TrustedAppsService } from '../../../../trusted_apps/service';
|
import { TrustedAppsService } from '../../../../trusted_apps/service';
|
||||||
import {
|
import {
|
||||||
createLoadedResourceState,
|
createLoadedResourceState,
|
||||||
createLoadingResourceState,
|
createLoadingResourceState,
|
||||||
createUninitialisedResourceState,
|
createUninitialisedResourceState,
|
||||||
createFailedResourceState,
|
createFailedResourceState,
|
||||||
|
isLoadingResourceState,
|
||||||
|
isUninitialisedResourceState,
|
||||||
} from '../../../../../state';
|
} from '../../../../../state';
|
||||||
import { parseQueryFilterToKQL } from '../../../../../common/utils';
|
import { parseQueryFilterToKQL } from '../../../../../common/utils';
|
||||||
import { SEARCHABLE_FIELDS } from '../../../../trusted_apps/constants';
|
import { SEARCHABLE_FIELDS } from '../../../../trusted_apps/constants';
|
||||||
import { PolicyDetailsAction } from '../action';
|
import { PolicyDetailsAction } from '../action';
|
||||||
|
import { ServerApiError } from '../../../../../../common/types';
|
||||||
|
|
||||||
|
/** Runs all middleware actions associated with the Trusted Apps view in Policy Details */
|
||||||
|
export const policyTrustedAppsMiddlewareRunner: MiddlewareRunner = async (
|
||||||
|
context,
|
||||||
|
store,
|
||||||
|
action
|
||||||
|
) => {
|
||||||
|
const state = store.getState();
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------
|
||||||
|
If not on the Trusted Apps Policy view, then just return
|
||||||
|
----------------------------------------------------------- */
|
||||||
|
if (!isOnPolicyTrustedAppsView(state)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { trustedAppsService } = context;
|
||||||
|
|
||||||
|
switch (action.type) {
|
||||||
|
case 'userChangedUrl':
|
||||||
|
fetchPolicyTrustedAppsIfNeeded(context, store);
|
||||||
|
fetchAllPoliciesIfNeeded(context, store);
|
||||||
|
|
||||||
|
if (action.type === 'userChangedUrl' && getCurrentArtifactsLocation(state).show === 'list') {
|
||||||
|
await searchTrustedApps(store, trustedAppsService);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'policyDetailsTrustedAppsForceListDataRefresh':
|
||||||
|
fetchPolicyTrustedAppsIfNeeded(context, store, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'policyArtifactsUpdateTrustedApps':
|
||||||
|
if (getCurrentArtifactsLocation(state).show === 'list') {
|
||||||
|
await updateTrustedApps(store, trustedAppsService, action.payload.trustedAppIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'policyArtifactsAssignableListPageDataFilter':
|
||||||
|
if (getCurrentArtifactsLocation(state).show === 'list') {
|
||||||
|
await searchTrustedApps(store, trustedAppsService, action.payload.filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const checkIfThereAreAssignableTrustedApps = async (
|
const checkIfThereAreAssignableTrustedApps = async (
|
||||||
store: ImmutableMiddlewareAPI<PolicyDetailsState, PolicyDetailsAction>,
|
store: ImmutableMiddlewareAPI<PolicyDetailsState, PolicyDetailsAction>,
|
||||||
|
@ -172,6 +237,8 @@ const updateTrustedApps = async (
|
||||||
type: 'policyArtifactsUpdateTrustedAppsChanged',
|
type: 'policyArtifactsUpdateTrustedAppsChanged',
|
||||||
payload: createLoadedResourceState(updatedTrustedApps),
|
payload: createLoadedResourceState(updatedTrustedApps),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
store.dispatch({ type: 'policyDetailsTrustedAppsForceListDataRefresh' });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
type: 'policyArtifactsUpdateTrustedAppsChanged',
|
type: 'policyArtifactsUpdateTrustedAppsChanged',
|
||||||
|
@ -182,31 +249,89 @@ const updateTrustedApps = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const policyTrustedAppsMiddlewareRunner: MiddlewareRunner = async (
|
const fetchPolicyTrustedAppsIfNeeded = async (
|
||||||
coreStart,
|
{ trustedAppsService }: MiddlewareRunnerContext,
|
||||||
store,
|
{ getState, dispatch }: PolicyDetailsStore,
|
||||||
action
|
forceFetch: boolean = false
|
||||||
) => {
|
) => {
|
||||||
const http = coreStart.http;
|
const state = getState();
|
||||||
const trustedAppsService = new TrustedAppsHttpService(http);
|
|
||||||
const state = store.getState();
|
if (isPolicyTrustedAppListLoading(state)) {
|
||||||
if (
|
return;
|
||||||
action.type === 'userChangedUrl' &&
|
}
|
||||||
isOnPolicyTrustedAppsPage(state) &&
|
|
||||||
getCurrentArtifactsLocation(state).show === 'list'
|
if (forceFetch || doesPolicyTrustedAppsListNeedUpdate(state)) {
|
||||||
) {
|
dispatch({
|
||||||
await searchTrustedApps(store, trustedAppsService);
|
type: 'assignedTrustedAppsListStateChanged',
|
||||||
} else if (
|
// @ts-ignore will be fixed when AsyncResourceState is refactored (#830)
|
||||||
action.type === 'policyArtifactsUpdateTrustedApps' &&
|
payload: createLoadingResourceState(getCurrentPolicyAssignedTrustedAppsState(state)),
|
||||||
isOnPolicyTrustedAppsPage(state) &&
|
});
|
||||||
getCurrentArtifactsLocation(state).show === 'list'
|
|
||||||
) {
|
try {
|
||||||
await updateTrustedApps(store, trustedAppsService, action.payload.trustedAppIds);
|
const urlLocationData = getCurrentUrlLocationPaginationParams(state);
|
||||||
} else if (
|
const policyId = policyIdFromParams(state);
|
||||||
action.type === 'policyArtifactsAssignableListPageDataFilter' &&
|
const fetchResponse = await trustedAppsService.getTrustedAppsList({
|
||||||
isOnPolicyTrustedAppsPage(state) &&
|
page: urlLocationData.page_index + 1,
|
||||||
getCurrentArtifactsLocation(state).show === 'list'
|
per_page: urlLocationData.page_size,
|
||||||
) {
|
kuery: `((exception-list-agnostic.attributes.tags:"policy:${policyId}") OR (exception-list-agnostic.attributes.tags:"policy:all"))${
|
||||||
await searchTrustedApps(store, trustedAppsService, action.payload.filter);
|
urlLocationData.filter ? ` AND (${urlLocationData.filter})` : ''
|
||||||
|
}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: 'assignedTrustedAppsListStateChanged',
|
||||||
|
payload: createLoadedResourceState<Immutable<PolicyAssignedTrustedApps>>({
|
||||||
|
location: urlLocationData,
|
||||||
|
artifacts: fetchResponse,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: 'assignedTrustedAppsListStateChanged',
|
||||||
|
payload: createFailedResourceState<Immutable<PolicyAssignedTrustedApps>>(
|
||||||
|
error as ServerApiError,
|
||||||
|
getLatestLoadedPolicyAssignedTrustedAppsState(getState())
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchAllPoliciesIfNeeded = async (
|
||||||
|
{ trustedAppsService }: MiddlewareRunnerContext,
|
||||||
|
{ getState, dispatch }: PolicyDetailsStore
|
||||||
|
) => {
|
||||||
|
const state = getState();
|
||||||
|
const currentPoliciesState = getTrustedAppsPolicyListState(state);
|
||||||
|
const isLoading = isLoadingResourceState(currentPoliciesState);
|
||||||
|
const hasBeenLoaded = !isUninitialisedResourceState(currentPoliciesState);
|
||||||
|
|
||||||
|
if (isLoading || hasBeenLoaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: 'policyDetailsListOfAllPoliciesStateChanged',
|
||||||
|
// @ts-ignore will be fixed when AsyncResourceState is refactored (#830)
|
||||||
|
payload: createLoadingResourceState(currentPoliciesState),
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const policyList = await trustedAppsService.getPolicyList({
|
||||||
|
query: {
|
||||||
|
page: 1,
|
||||||
|
perPage: 1000,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: 'policyDetailsListOfAllPoliciesStateChanged',
|
||||||
|
payload: createLoadedResourceState(policyList),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: 'policyDetailsListOfAllPoliciesStateChanged',
|
||||||
|
payload: createFailedResourceState<GetPolicyListResponse>(error.body || error),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,5 +37,7 @@ export const initialPolicyDetailsState: () => Immutable<PolicyDetailsState> = ()
|
||||||
assignableList: createUninitialisedResourceState(),
|
assignableList: createUninitialisedResourceState(),
|
||||||
trustedAppsToUpdate: createUninitialisedResourceState(),
|
trustedAppsToUpdate: createUninitialisedResourceState(),
|
||||||
assignableListEntriesExist: createUninitialisedResourceState(),
|
assignableListEntriesExist: createUninitialisedResourceState(),
|
||||||
|
assignedList: createUninitialisedResourceState(),
|
||||||
|
policies: createUninitialisedResourceState(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
import { PolicyDetailsState } from '../../../types';
|
import { PolicyDetailsState } from '../../../types';
|
||||||
import { initialPolicyDetailsState } from '../reducer/initial_policy_details_state';
|
import { initialPolicyDetailsState } from './initial_policy_details_state';
|
||||||
import { policyTrustedAppsReducer } from './trusted_apps_reducer';
|
import { policyTrustedAppsReducer } from './trusted_apps_reducer';
|
||||||
|
|
||||||
import { ImmutableObject } from '../../../../../../../common/endpoint/types';
|
import { ImmutableObject } from '../../../../../../../common/endpoint/types';
|
||||||
|
@ -16,12 +16,20 @@ import {
|
||||||
createFailedResourceState,
|
createFailedResourceState,
|
||||||
} from '../../../../../state';
|
} from '../../../../../state';
|
||||||
import { getMockListResponse, getAPIError, getMockCreateResponse } from '../../../test_utils';
|
import { getMockListResponse, getAPIError, getMockCreateResponse } from '../../../test_utils';
|
||||||
|
import { getPolicyDetailsArtifactsListPath } from '../../../../../common/routing';
|
||||||
|
|
||||||
describe('policy trusted apps reducer', () => {
|
describe('policy trusted apps reducer', () => {
|
||||||
let initialState: ImmutableObject<PolicyDetailsState>;
|
let initialState: ImmutableObject<PolicyDetailsState>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
initialState = initialPolicyDetailsState();
|
initialState = {
|
||||||
|
...initialPolicyDetailsState(),
|
||||||
|
location: {
|
||||||
|
pathname: getPolicyDetailsArtifactsListPath('abc'),
|
||||||
|
search: '',
|
||||||
|
hash: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PolicyTrustedApps', () => {
|
describe('PolicyTrustedApps', () => {
|
||||||
|
|
|
@ -9,11 +9,28 @@ import { ImmutableReducer } from '../../../../../../common/store';
|
||||||
import { PolicyDetailsState } from '../../../types';
|
import { PolicyDetailsState } from '../../../types';
|
||||||
import { AppAction } from '../../../../../../common/store/actions';
|
import { AppAction } from '../../../../../../common/store/actions';
|
||||||
import { initialPolicyDetailsState } from './initial_policy_details_state';
|
import { initialPolicyDetailsState } from './initial_policy_details_state';
|
||||||
|
import { isUninitialisedResourceState } from '../../../../../state';
|
||||||
|
import { getCurrentPolicyAssignedTrustedAppsState, isOnPolicyTrustedAppsView } from '../selectors';
|
||||||
|
|
||||||
export const policyTrustedAppsReducer: ImmutableReducer<PolicyDetailsState, AppAction> = (
|
export const policyTrustedAppsReducer: ImmutableReducer<PolicyDetailsState, AppAction> = (
|
||||||
state = initialPolicyDetailsState(),
|
state = initialPolicyDetailsState(),
|
||||||
action
|
action
|
||||||
) => {
|
) => {
|
||||||
|
/* ----------------------------------------------------------
|
||||||
|
If not on the Trusted Apps Policy view, then just return
|
||||||
|
---------------------------------------------------------- */
|
||||||
|
if (!isOnPolicyTrustedAppsView(state)) {
|
||||||
|
// If the artifacts state namespace needs resetting, then do it now
|
||||||
|
if (!isUninitialisedResourceState(getCurrentPolicyAssignedTrustedAppsState(state))) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
artifacts: initialPolicyDetailsState().artifacts,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
if (action.type === 'policyArtifactsAssignableListPageDataChanged') {
|
if (action.type === 'policyArtifactsAssignableListPageDataChanged') {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
@ -44,5 +61,25 @@ export const policyTrustedAppsReducer: ImmutableReducer<PolicyDetailsState, AppA
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.type === 'assignedTrustedAppsListStateChanged') {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
artifacts: {
|
||||||
|
...state?.artifacts,
|
||||||
|
assignedList: action.payload,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === 'policyDetailsListOfAllPoliciesStateChanged') {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
artifacts: {
|
||||||
|
...state.artifacts,
|
||||||
|
policies: action.payload,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,3 +7,4 @@
|
||||||
|
|
||||||
export * from './policy_settings_selectors';
|
export * from './policy_settings_selectors';
|
||||||
export * from './trusted_apps_selectors';
|
export * from './trusted_apps_selectors';
|
||||||
|
export * from './policy_common_selectors';
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { matchPath } from 'react-router-dom';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { PolicyDetailsSelector, PolicyDetailsState } from '../../../types';
|
||||||
|
import {
|
||||||
|
MANAGEMENT_ROUTING_POLICY_DETAILS_FORM_PATH,
|
||||||
|
MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH,
|
||||||
|
} from '../../../../../common/constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current artifacts location
|
||||||
|
*/
|
||||||
|
export const getCurrentArtifactsLocation: PolicyDetailsSelector<
|
||||||
|
PolicyDetailsState['artifacts']['location']
|
||||||
|
> = (state) => state.artifacts.location;
|
||||||
|
|
||||||
|
export const getUrlLocationPathname: PolicyDetailsSelector<string | undefined> = (state) =>
|
||||||
|
state.location?.pathname;
|
||||||
|
|
||||||
|
/** Returns a boolean of whether the user is on the policy form page or not */
|
||||||
|
export const isOnPolicyFormView: PolicyDetailsSelector<boolean> = createSelector(
|
||||||
|
getUrlLocationPathname,
|
||||||
|
(pathname) => {
|
||||||
|
return (
|
||||||
|
matchPath(pathname ?? '', {
|
||||||
|
path: MANAGEMENT_ROUTING_POLICY_DETAILS_FORM_PATH,
|
||||||
|
exact: true,
|
||||||
|
}) !== null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Returns a boolean of whether the user is on the policy details page or not */
|
||||||
|
export const isOnPolicyTrustedAppsView: PolicyDetailsSelector<boolean> = createSelector(
|
||||||
|
getUrlLocationPathname,
|
||||||
|
(pathname) => {
|
||||||
|
return (
|
||||||
|
matchPath(pathname ?? '', {
|
||||||
|
path: MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH,
|
||||||
|
exact: true,
|
||||||
|
}) !== null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
|
@ -23,8 +23,8 @@ import {
|
||||||
MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH,
|
MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH,
|
||||||
} from '../../../../../common/constants';
|
} from '../../../../../common/constants';
|
||||||
import { ManagementRoutePolicyDetailsParams } from '../../../../../types';
|
import { ManagementRoutePolicyDetailsParams } from '../../../../../types';
|
||||||
import { getPolicyDataForUpdate } from '../../../../../../../common/endpoint/service/policy/get_policy_data_for_update';
|
import { getPolicyDataForUpdate } from '../../../../../../../common/endpoint/service/policy';
|
||||||
import { isOnPolicyTrustedAppsPage } from './trusted_apps_selectors';
|
import { isOnPolicyTrustedAppsView, isOnPolicyFormView } from './policy_common_selectors';
|
||||||
|
|
||||||
/** Returns the policy details */
|
/** Returns the policy details */
|
||||||
export const policyDetails = (state: Immutable<PolicyDetailsState>) => state.policyItem;
|
export const policyDetails = (state: Immutable<PolicyDetailsState>) => state.policyItem;
|
||||||
|
@ -81,19 +81,9 @@ export const needsToRefresh = (state: Immutable<PolicyDetailsState>): boolean =>
|
||||||
return !state.policyItem && !state.apiError;
|
return !state.policyItem && !state.apiError;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Returns a boolean of whether the user is on the policy form page or not */
|
|
||||||
export const isOnPolicyFormPage = (state: Immutable<PolicyDetailsState>) => {
|
|
||||||
return (
|
|
||||||
matchPath(state.location?.pathname ?? '', {
|
|
||||||
path: MANAGEMENT_ROUTING_POLICY_DETAILS_FORM_PATH,
|
|
||||||
exact: true,
|
|
||||||
}) !== null
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Returns a boolean of whether the user is on some of the policy details page or not */
|
/** Returns a boolean of whether the user is on some of the policy details page or not */
|
||||||
export const isOnPolicyDetailsPage = (state: Immutable<PolicyDetailsState>) =>
|
export const isOnPolicyDetailsPage = (state: Immutable<PolicyDetailsState>) =>
|
||||||
isOnPolicyFormPage(state) || isOnPolicyTrustedAppsPage(state);
|
isOnPolicyFormView(state) || isOnPolicyTrustedAppsView(state);
|
||||||
|
|
||||||
/** Returns the license info fetched from the license service */
|
/** Returns the license info fetched from the license service */
|
||||||
export const license = (state: Immutable<PolicyDetailsState>) => {
|
export const license = (state: Immutable<PolicyDetailsState>) => {
|
||||||
|
|
|
@ -6,9 +6,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { PolicyDetailsState } from '../../../types';
|
import { PolicyDetailsState } from '../../../types';
|
||||||
import { initialPolicyDetailsState } from '../reducer/initial_policy_details_state';
|
import { initialPolicyDetailsState } from '../reducer';
|
||||||
import {
|
import {
|
||||||
getCurrentArtifactsLocation,
|
|
||||||
getAssignableArtifactsList,
|
getAssignableArtifactsList,
|
||||||
getAssignableArtifactsListIsLoading,
|
getAssignableArtifactsListIsLoading,
|
||||||
getUpdateArtifactsIsLoading,
|
getUpdateArtifactsIsLoading,
|
||||||
|
@ -17,8 +16,8 @@ import {
|
||||||
getAssignableArtifactsListExist,
|
getAssignableArtifactsListExist,
|
||||||
getAssignableArtifactsListExistIsLoading,
|
getAssignableArtifactsListExistIsLoading,
|
||||||
getUpdateArtifacts,
|
getUpdateArtifacts,
|
||||||
isOnPolicyTrustedAppsPage,
|
|
||||||
} from './trusted_apps_selectors';
|
} from './trusted_apps_selectors';
|
||||||
|
import { getCurrentArtifactsLocation, isOnPolicyTrustedAppsView } from './policy_common_selectors';
|
||||||
|
|
||||||
import { ImmutableObject } from '../../../../../../../common/endpoint/types';
|
import { ImmutableObject } from '../../../../../../../common/endpoint/types';
|
||||||
import {
|
import {
|
||||||
|
@ -39,7 +38,7 @@ describe('policy trusted apps selectors', () => {
|
||||||
|
|
||||||
describe('isOnPolicyTrustedAppsPage()', () => {
|
describe('isOnPolicyTrustedAppsPage()', () => {
|
||||||
it('when location is on policy trusted apps page', () => {
|
it('when location is on policy trusted apps page', () => {
|
||||||
const isOnPage = isOnPolicyTrustedAppsPage({
|
const isOnPage = isOnPolicyTrustedAppsView({
|
||||||
...initialState,
|
...initialState,
|
||||||
location: {
|
location: {
|
||||||
pathname: MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH,
|
pathname: MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH,
|
||||||
|
@ -50,7 +49,7 @@ describe('policy trusted apps selectors', () => {
|
||||||
expect(isOnPage).toBeFalsy();
|
expect(isOnPage).toBeFalsy();
|
||||||
});
|
});
|
||||||
it('when location is not on policy trusted apps page', () => {
|
it('when location is not on policy trusted apps page', () => {
|
||||||
const isOnPage = isOnPolicyTrustedAppsPage({
|
const isOnPage = isOnPolicyTrustedAppsView({
|
||||||
...initialState,
|
...initialState,
|
||||||
location: { pathname: '', search: '', hash: '' },
|
location: { pathname: '', search: '', hash: '' },
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,35 +5,48 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { matchPath } from 'react-router-dom';
|
import { createSelector } from 'reselect';
|
||||||
import { PolicyDetailsArtifactsPageLocation, PolicyDetailsState } from '../../../types';
|
import { Pagination } from '@elastic/eui';
|
||||||
|
import {
|
||||||
|
PolicyArtifactsState,
|
||||||
|
PolicyAssignedTrustedApps,
|
||||||
|
PolicyDetailsArtifactsPageListLocationParams,
|
||||||
|
PolicyDetailsSelector,
|
||||||
|
PolicyDetailsState,
|
||||||
|
} from '../../../types';
|
||||||
import {
|
import {
|
||||||
Immutable,
|
Immutable,
|
||||||
ImmutableArray,
|
ImmutableArray,
|
||||||
PostTrustedAppCreateResponse,
|
PostTrustedAppCreateResponse,
|
||||||
GetTrustedListAppsResponse,
|
GetTrustedAppsListResponse,
|
||||||
|
PolicyData,
|
||||||
} from '../../../../../../../common/endpoint/types';
|
} from '../../../../../../../common/endpoint/types';
|
||||||
import { MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH } from '../../../../../common/constants';
|
import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../../../../common/constants';
|
||||||
import {
|
import {
|
||||||
getLastLoadedResourceState,
|
getLastLoadedResourceState,
|
||||||
isFailedResourceState,
|
isFailedResourceState,
|
||||||
isLoadedResourceState,
|
isLoadedResourceState,
|
||||||
isLoadingResourceState,
|
isLoadingResourceState,
|
||||||
|
LoadedResourceState,
|
||||||
} from '../../../../../state';
|
} from '../../../../../state';
|
||||||
|
import { getCurrentArtifactsLocation } from './policy_common_selectors';
|
||||||
|
|
||||||
/**
|
export const doesPolicyHaveTrustedApps = (
|
||||||
* Returns current artifacts location
|
state: PolicyDetailsState
|
||||||
*/
|
): { loading: boolean; hasTrustedApps: boolean } => {
|
||||||
export const getCurrentArtifactsLocation = (
|
// TODO: implement empty state (task #1645)
|
||||||
state: Immutable<PolicyDetailsState>
|
return {
|
||||||
): Immutable<PolicyDetailsArtifactsPageLocation> => state.artifacts.location;
|
loading: false,
|
||||||
|
hasTrustedApps: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns current assignable artifacts list
|
* Returns current assignable artifacts list
|
||||||
*/
|
*/
|
||||||
export const getAssignableArtifactsList = (
|
export const getAssignableArtifactsList = (
|
||||||
state: Immutable<PolicyDetailsState>
|
state: Immutable<PolicyDetailsState>
|
||||||
): Immutable<GetTrustedListAppsResponse> | undefined =>
|
): Immutable<GetTrustedAppsListResponse> | undefined =>
|
||||||
getLastLoadedResourceState(state.artifacts.assignableList)?.data;
|
getLastLoadedResourceState(state.artifacts.assignableList)?.data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,12 +105,79 @@ export const getUpdateArtifacts = (
|
||||||
: undefined;
|
: undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Returns a boolean of whether the user is on the policy details page or not */
|
export const getCurrentPolicyAssignedTrustedAppsState: PolicyDetailsSelector<
|
||||||
export const isOnPolicyTrustedAppsPage = (state: Immutable<PolicyDetailsState>) => {
|
PolicyArtifactsState['assignedList']
|
||||||
return (
|
> = (state) => {
|
||||||
matchPath(state.location?.pathname ?? '', {
|
return state.artifacts.assignedList;
|
||||||
path: MANAGEMENT_ROUTING_POLICY_DETAILS_TRUSTED_APPS_PATH,
|
|
||||||
exact: true,
|
|
||||||
}) !== null
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getLatestLoadedPolicyAssignedTrustedAppsState: PolicyDetailsSelector<
|
||||||
|
undefined | LoadedResourceState<PolicyAssignedTrustedApps>
|
||||||
|
> = createSelector(getCurrentPolicyAssignedTrustedAppsState, (currentAssignedTrustedAppsState) => {
|
||||||
|
return getLastLoadedResourceState(currentAssignedTrustedAppsState);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getCurrentUrlLocationPaginationParams: PolicyDetailsSelector<PolicyDetailsArtifactsPageListLocationParams> =
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
createSelector(getCurrentArtifactsLocation, ({ filter, page_index, page_size }) => {
|
||||||
|
return { filter, page_index, page_size };
|
||||||
|
});
|
||||||
|
|
||||||
|
export const doesPolicyTrustedAppsListNeedUpdate: PolicyDetailsSelector<boolean> = createSelector(
|
||||||
|
getCurrentPolicyAssignedTrustedAppsState,
|
||||||
|
getCurrentUrlLocationPaginationParams,
|
||||||
|
(assignedListState, locationData) => {
|
||||||
|
return (
|
||||||
|
!isLoadedResourceState(assignedListState) ||
|
||||||
|
(isLoadedResourceState(assignedListState) &&
|
||||||
|
(
|
||||||
|
Object.keys(locationData) as Array<keyof PolicyDetailsArtifactsPageListLocationParams>
|
||||||
|
).some((key) => assignedListState.data.location[key] !== locationData[key]))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const isPolicyTrustedAppListLoading: PolicyDetailsSelector<boolean> = createSelector(
|
||||||
|
getCurrentPolicyAssignedTrustedAppsState,
|
||||||
|
(assignedState) => isLoadingResourceState(assignedState)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getPolicyTrustedAppList: PolicyDetailsSelector<GetTrustedAppsListResponse['data']> =
|
||||||
|
createSelector(getLatestLoadedPolicyAssignedTrustedAppsState, (assignedState) => {
|
||||||
|
return assignedState?.data.artifacts.data ?? [];
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getPolicyTrustedAppsListPagination: PolicyDetailsSelector<Pagination> = createSelector(
|
||||||
|
getLatestLoadedPolicyAssignedTrustedAppsState,
|
||||||
|
(currentAssignedTrustedAppsState) => {
|
||||||
|
const trustedAppsApiResponse = currentAssignedTrustedAppsState?.data.artifacts;
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Trusted apps api is `1` based for page - need to subtract here for `Pagination` component
|
||||||
|
pageIndex: trustedAppsApiResponse?.page ? trustedAppsApiResponse.page - 1 : 0,
|
||||||
|
pageSize: trustedAppsApiResponse?.per_page ?? MANAGEMENT_PAGE_SIZE_OPTIONS[0],
|
||||||
|
totalItemCount: trustedAppsApiResponse?.total || 0,
|
||||||
|
pageSizeOptions: [...MANAGEMENT_PAGE_SIZE_OPTIONS],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getTrustedAppsPolicyListState: PolicyDetailsSelector<
|
||||||
|
PolicyDetailsState['artifacts']['policies']
|
||||||
|
> = (state) => state.artifacts.policies;
|
||||||
|
|
||||||
|
export const getTrustedAppsListOfAllPolicies: PolicyDetailsSelector<PolicyData[]> = createSelector(
|
||||||
|
getTrustedAppsPolicyListState,
|
||||||
|
(policyListState) => {
|
||||||
|
return getLastLoadedResourceState(policyListState)?.data.items ?? [];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getTrustedAppsAllPoliciesById: PolicyDetailsSelector<
|
||||||
|
Record<string, Immutable<PolicyData>>
|
||||||
|
> = createSelector(getTrustedAppsListOfAllPolicies, (allPolicies) => {
|
||||||
|
return allPolicies.reduce<Record<string, Immutable<PolicyData>>>((mapById, policy) => {
|
||||||
|
mapById[policy.id] = policy;
|
||||||
|
return mapById;
|
||||||
|
}, {}) as Immutable<Record<string, Immutable<PolicyData>>>;
|
||||||
|
});
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GetTrustedListAppsResponse,
|
GetTrustedAppsListResponse,
|
||||||
PostTrustedAppCreateResponse,
|
PostTrustedAppCreateResponse,
|
||||||
} from '../../../../../common/endpoint/types';
|
} from '../../../../../common/endpoint/types';
|
||||||
|
|
||||||
import { createSampleTrustedApps, createSampleTrustedApp } from '../../trusted_apps/test_utils';
|
import { createSampleTrustedApps, createSampleTrustedApp } from '../../trusted_apps/test_utils';
|
||||||
|
|
||||||
export const getMockListResponse: () => GetTrustedListAppsResponse = () => ({
|
export const getMockListResponse: () => GetTrustedAppsListResponse = () => ({
|
||||||
data: createSampleTrustedApps({}),
|
data: createSampleTrustedApps({}),
|
||||||
per_page: 100,
|
per_page: 100,
|
||||||
page: 1,
|
page: 1,
|
||||||
|
|
|
@ -14,58 +14,41 @@ import {
|
||||||
PolicyData,
|
PolicyData,
|
||||||
UIPolicyConfig,
|
UIPolicyConfig,
|
||||||
PostTrustedAppCreateResponse,
|
PostTrustedAppCreateResponse,
|
||||||
GetTrustedListAppsResponse,
|
|
||||||
MaybeImmutable,
|
MaybeImmutable,
|
||||||
|
GetTrustedAppsListResponse,
|
||||||
} from '../../../../common/endpoint/types';
|
} from '../../../../common/endpoint/types';
|
||||||
import { ServerApiError } from '../../../common/types';
|
import { ServerApiError } from '../../../common/types';
|
||||||
import {
|
import {
|
||||||
GetAgentStatusResponse,
|
GetAgentStatusResponse,
|
||||||
GetOnePackagePolicyResponse,
|
GetOnePackagePolicyResponse,
|
||||||
GetPackagePoliciesResponse,
|
GetPackagePoliciesResponse,
|
||||||
GetPackagesResponse,
|
|
||||||
UpdatePackagePolicyResponse,
|
UpdatePackagePolicyResponse,
|
||||||
} from '../../../../../fleet/common';
|
} from '../../../../../fleet/common';
|
||||||
import { AsyncResourceState } from '../../state';
|
import { AsyncResourceState } from '../../state';
|
||||||
import { ImmutableMiddlewareAPI } from '../../../common/store';
|
import { ImmutableMiddlewareAPI } from '../../../common/store';
|
||||||
import { AppAction } from '../../../common/store/actions';
|
import { AppAction } from '../../../common/store/actions';
|
||||||
|
import { TrustedAppsService } from '../trusted_apps/service';
|
||||||
|
|
||||||
|
export type PolicyDetailsStore = ImmutableMiddlewareAPI<PolicyDetailsState, AppAction>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that runs Policy Details middleware
|
* Function that runs Policy Details middleware
|
||||||
*/
|
*/
|
||||||
export type MiddlewareRunner = (
|
export type MiddlewareRunner = (
|
||||||
coreStart: CoreStart,
|
context: MiddlewareRunnerContext,
|
||||||
store: ImmutableMiddlewareAPI<PolicyDetailsState, AppAction>,
|
store: PolicyDetailsStore,
|
||||||
action: MaybeImmutable<AppAction>
|
action: MaybeImmutable<AppAction>
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
|
||||||
/**
|
export interface MiddlewareRunnerContext {
|
||||||
* Policy list store state
|
coreStart: CoreStart;
|
||||||
*/
|
trustedAppsService: TrustedAppsService;
|
||||||
export interface PolicyListState {
|
|
||||||
/** Array of policy items */
|
|
||||||
policyItems: PolicyData[];
|
|
||||||
/** Information about the latest endpoint package */
|
|
||||||
endpointPackageInfo?: GetPackagesResponse['response'][0];
|
|
||||||
/** API error if loading data failed */
|
|
||||||
apiError?: ServerApiError;
|
|
||||||
/** total number of policies */
|
|
||||||
total: number;
|
|
||||||
/** Number of policies per page */
|
|
||||||
pageSize: number;
|
|
||||||
/** page number (zero based) */
|
|
||||||
pageIndex: number;
|
|
||||||
/** data is being retrieved from server */
|
|
||||||
isLoading: boolean;
|
|
||||||
/** current location information */
|
|
||||||
location?: Immutable<AppLocation>;
|
|
||||||
/** policy is being deleted */
|
|
||||||
isDeleting: boolean;
|
|
||||||
/** Deletion status */
|
|
||||||
deleteStatus?: boolean;
|
|
||||||
/** A summary of stats for the agents associated with a given Fleet Agent Policy */
|
|
||||||
agentStatusSummary?: GetAgentStatusResponse['results'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PolicyDetailsSelector<T = unknown> = (
|
||||||
|
state: Immutable<PolicyDetailsState>
|
||||||
|
) => Immutable<T>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Policy details store state
|
* Policy details store state
|
||||||
*/
|
*/
|
||||||
|
@ -90,6 +73,11 @@ export interface PolicyDetailsState {
|
||||||
license?: ILicense;
|
license?: ILicense;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PolicyAssignedTrustedApps {
|
||||||
|
location: PolicyDetailsArtifactsPageListLocationParams;
|
||||||
|
artifacts: GetTrustedAppsListResponse;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Policy artifacts store state
|
* Policy artifacts store state
|
||||||
*/
|
*/
|
||||||
|
@ -97,11 +85,15 @@ export interface PolicyArtifactsState {
|
||||||
/** artifacts location params */
|
/** artifacts location params */
|
||||||
location: PolicyDetailsArtifactsPageLocation;
|
location: PolicyDetailsArtifactsPageLocation;
|
||||||
/** A list of artifacts can be linked to the policy */
|
/** A list of artifacts can be linked to the policy */
|
||||||
assignableList: AsyncResourceState<GetTrustedListAppsResponse>;
|
assignableList: AsyncResourceState<GetTrustedAppsListResponse>;
|
||||||
/** Represents if avaialble trusted apps entries exist, regardless of whether the list is showing results */
|
/** Represents if available trusted apps entries exist, regardless of whether the list is showing results */
|
||||||
assignableListEntriesExist: AsyncResourceState<boolean>;
|
assignableListEntriesExist: AsyncResourceState<boolean>;
|
||||||
/** A list of trusted apps going to be updated */
|
/** A list of trusted apps going to be updated */
|
||||||
trustedAppsToUpdate: AsyncResourceState<PostTrustedAppCreateResponse[]>;
|
trustedAppsToUpdate: AsyncResourceState<PostTrustedAppCreateResponse[]>;
|
||||||
|
/** List of artifacts currently assigned to the policy (body specific and global) */
|
||||||
|
assignedList: AsyncResourceState<PolicyAssignedTrustedApps>;
|
||||||
|
/** A list of all available polices */
|
||||||
|
policies: AsyncResourceState<GetPolicyListResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum OS {
|
export enum OS {
|
||||||
|
@ -110,13 +102,17 @@ export enum OS {
|
||||||
linux = 'linux',
|
linux = 'linux',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PolicyDetailsArtifactsPageLocation {
|
export interface PolicyDetailsArtifactsPageListLocationParams {
|
||||||
page_index: number;
|
page_index: number;
|
||||||
page_size: number;
|
page_size: number;
|
||||||
show?: 'list';
|
|
||||||
filter: string;
|
filter: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PolicyDetailsArtifactsPageLocation
|
||||||
|
extends PolicyDetailsArtifactsPageListLocationParams {
|
||||||
|
show?: 'list';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the keys of an object whose values meet a criteria.
|
* Returns the keys of an object whose values meet a criteria.
|
||||||
* Ex) interface largeNestedObject = {
|
* Ex) interface largeNestedObject = {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GetTrustedListAppsResponse,
|
GetTrustedAppsListResponse,
|
||||||
Immutable,
|
Immutable,
|
||||||
TrustedApp,
|
TrustedApp,
|
||||||
} from '../../../../../../../common/endpoint/types';
|
} from '../../../../../../../common/endpoint/types';
|
||||||
|
@ -16,7 +16,7 @@ import { Loader } from '../../../../../../common/components/loader';
|
||||||
import { ArtifactEntryCardMinified } from '../../../../../components/artifact_entry_card';
|
import { ArtifactEntryCardMinified } from '../../../../../components/artifact_entry_card';
|
||||||
|
|
||||||
export interface PolicyArtifactsAssignableListProps {
|
export interface PolicyArtifactsAssignableListProps {
|
||||||
artifacts: Immutable<GetTrustedListAppsResponse | undefined>; // Or other artifacts type like Event Filters or Endpoint Exceptions
|
artifacts: Immutable<GetTrustedAppsListResponse | undefined>; // Or other artifacts type like Event Filters or Endpoint Exceptions
|
||||||
selectedArtifactIds: string[];
|
selectedArtifactIds: string[];
|
||||||
selectedArtifactsUpdated: (id: string, selected: boolean) => void;
|
selectedArtifactsUpdated: (id: string, selected: boolean) => void;
|
||||||
isListLoading: boolean;
|
isListLoading: boolean;
|
||||||
|
|
|
@ -12,8 +12,8 @@ import { EuiTabbedContent, EuiSpacer, EuiTabbedContentTab } from '@elastic/eui';
|
||||||
|
|
||||||
import { usePolicyDetailsSelector } from '../policy_hooks';
|
import { usePolicyDetailsSelector } from '../policy_hooks';
|
||||||
import {
|
import {
|
||||||
isOnPolicyFormPage,
|
isOnPolicyFormView,
|
||||||
isOnPolicyTrustedAppsPage,
|
isOnPolicyTrustedAppsView,
|
||||||
policyIdFromParams,
|
policyIdFromParams,
|
||||||
} from '../../store/policy_details/selectors';
|
} from '../../store/policy_details/selectors';
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ import { getPolicyDetailPath, getPolicyTrustedAppsPath } from '../../../../commo
|
||||||
|
|
||||||
export const PolicyTabs = React.memo(() => {
|
export const PolicyTabs = React.memo(() => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const isInSettingsTab = usePolicyDetailsSelector(isOnPolicyFormPage);
|
const isInSettingsTab = usePolicyDetailsSelector(isOnPolicyFormView);
|
||||||
const isInTrustedAppsTab = usePolicyDetailsSelector(isOnPolicyTrustedAppsPage);
|
const isInTrustedAppsTab = usePolicyDetailsSelector(isOnPolicyTrustedAppsView);
|
||||||
const policyId = usePolicyDetailsSelector(policyIdFromParams);
|
const policyId = usePolicyDetailsSelector(policyIdFromParams);
|
||||||
|
|
||||||
const tabs = useMemo(
|
const tabs = useMemo(
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { PolicyTrustedAppsLayout } from './layout';
|
|
@ -17,6 +17,7 @@ import {
|
||||||
import { getCurrentArtifactsLocation } from '../../../store/policy_details/selectors';
|
import { getCurrentArtifactsLocation } from '../../../store/policy_details/selectors';
|
||||||
import { usePolicyDetailsNavigateCallback, usePolicyDetailsSelector } from '../../policy_hooks';
|
import { usePolicyDetailsNavigateCallback, usePolicyDetailsSelector } from '../../policy_hooks';
|
||||||
import { PolicyTrustedAppsFlyout } from '../flyout';
|
import { PolicyTrustedAppsFlyout } from '../flyout';
|
||||||
|
import { PolicyTrustedAppsList } from '../list/policy_trusted_apps_list';
|
||||||
|
|
||||||
export const PolicyTrustedAppsLayout = React.memo(() => {
|
export const PolicyTrustedAppsLayout = React.memo(() => {
|
||||||
const location = usePolicyDetailsSelector(getCurrentArtifactsLocation);
|
const location = usePolicyDetailsSelector(getCurrentArtifactsLocation);
|
||||||
|
@ -67,8 +68,7 @@ export const PolicyTrustedAppsLayout = React.memo(() => {
|
||||||
color="transparent"
|
color="transparent"
|
||||||
borderRadius="none"
|
borderRadius="none"
|
||||||
>
|
>
|
||||||
{/* TODO: To be implemented */}
|
<PolicyTrustedAppsList />
|
||||||
{'Policy trusted apps layout content'}
|
|
||||||
</EuiPageContent>
|
</EuiPageContent>
|
||||||
{showListFlyout ? <PolicyTrustedAppsFlyout /> : null}
|
{showListFlyout ? <PolicyTrustedAppsFlyout /> : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { EuiLoadingSpinner, EuiSpacer, EuiText, Pagination } from '@elastic/eui';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import {
|
||||||
|
ArtifactCardGrid,
|
||||||
|
ArtifactCardGridCardComponentProps,
|
||||||
|
ArtifactCardGridProps,
|
||||||
|
} from '../../../../../components/artifact_card_grid';
|
||||||
|
import { usePolicyDetailsSelector } from '../../policy_hooks';
|
||||||
|
import {
|
||||||
|
doesPolicyHaveTrustedApps,
|
||||||
|
getCurrentArtifactsLocation,
|
||||||
|
getPolicyTrustedAppList,
|
||||||
|
getPolicyTrustedAppsListPagination,
|
||||||
|
getTrustedAppsAllPoliciesById,
|
||||||
|
isPolicyTrustedAppListLoading,
|
||||||
|
policyIdFromParams,
|
||||||
|
} from '../../../store/policy_details/selectors';
|
||||||
|
import {
|
||||||
|
getPolicyDetailPath,
|
||||||
|
getPolicyDetailsArtifactsListPath,
|
||||||
|
getTrustedAppsListPath,
|
||||||
|
} from '../../../../../common/routing';
|
||||||
|
import { Immutable, TrustedApp } from '../../../../../../../common/endpoint/types';
|
||||||
|
import { useAppUrl } from '../../../../../../common/lib/kibana';
|
||||||
|
import { APP_ID } from '../../../../../../../common/constants';
|
||||||
|
import { ContextMenuItemNavByRouterProps } from '../../../../../components/context_menu_with_router_support/context_menu_item_nav_by_router';
|
||||||
|
import { ArtifactEntryCollapsibleCardProps } from '../../../../../components/artifact_entry_card';
|
||||||
|
|
||||||
|
export const PolicyTrustedAppsList = memo(() => {
|
||||||
|
const history = useHistory();
|
||||||
|
const { getAppUrl } = useAppUrl();
|
||||||
|
const policyId = usePolicyDetailsSelector(policyIdFromParams);
|
||||||
|
const hasTrustedApps = usePolicyDetailsSelector(doesPolicyHaveTrustedApps);
|
||||||
|
const isLoading = usePolicyDetailsSelector(isPolicyTrustedAppListLoading);
|
||||||
|
const trustedAppItems = usePolicyDetailsSelector(getPolicyTrustedAppList);
|
||||||
|
const pagination = usePolicyDetailsSelector(getPolicyTrustedAppsListPagination);
|
||||||
|
const urlParams = usePolicyDetailsSelector(getCurrentArtifactsLocation);
|
||||||
|
const allPoliciesById = usePolicyDetailsSelector(getTrustedAppsAllPoliciesById);
|
||||||
|
|
||||||
|
const [isCardExpanded, setCardExpanded] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
|
// TODO:PT show load errors if any
|
||||||
|
|
||||||
|
const handlePageChange = useCallback<ArtifactCardGridProps['onPageChange']>(
|
||||||
|
({ pageIndex, pageSize }) => {
|
||||||
|
history.push(
|
||||||
|
getPolicyDetailsArtifactsListPath(policyId, {
|
||||||
|
...urlParams,
|
||||||
|
// If user changed page size, then reset page index back to the first page
|
||||||
|
page_index: pageSize !== pagination.pageSize ? 0 : pageIndex,
|
||||||
|
page_size: pageSize,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[history, pagination.pageSize, policyId, urlParams]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleExpandCollapse = useCallback<ArtifactCardGridProps['onExpandCollapse']>(
|
||||||
|
({ expanded, collapsed }) => {
|
||||||
|
const newCardExpandedSettings: Record<string, boolean> = {};
|
||||||
|
|
||||||
|
for (const trustedApp of expanded) {
|
||||||
|
newCardExpandedSettings[trustedApp.id] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const trustedApp of collapsed) {
|
||||||
|
newCardExpandedSettings[trustedApp.id] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCardExpanded(newCardExpandedSettings);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const totalItemsCountLabel = useMemo<string>(() => {
|
||||||
|
return i18n.translate('xpack.securitySolution.endpoint.policy.trustedApps.list.totalCount', {
|
||||||
|
defaultMessage:
|
||||||
|
'Showing {totalItemsCount, plural, one {# trusted application} other {# trusted applications}}',
|
||||||
|
values: { totalItemsCount: pagination.totalItemCount },
|
||||||
|
});
|
||||||
|
}, [pagination.totalItemCount]);
|
||||||
|
|
||||||
|
const cardProps = useMemo<Map<Immutable<TrustedApp>, ArtifactCardGridCardComponentProps>>(() => {
|
||||||
|
const newCardProps = new Map();
|
||||||
|
|
||||||
|
for (const trustedApp of trustedAppItems) {
|
||||||
|
const viewUrlPath = getTrustedAppsListPath({ id: trustedApp.id, show: 'edit' });
|
||||||
|
const assignedPoliciesMenuItems: ArtifactEntryCollapsibleCardProps['policies'] =
|
||||||
|
trustedApp.effectScope.type === 'global'
|
||||||
|
? undefined
|
||||||
|
: trustedApp.effectScope.policies.reduce<
|
||||||
|
Required<ArtifactEntryCollapsibleCardProps>['policies']
|
||||||
|
>((byIdPolicies, trustedAppAssignedPolicyId) => {
|
||||||
|
if (!allPoliciesById[trustedAppAssignedPolicyId]) {
|
||||||
|
byIdPolicies[trustedAppAssignedPolicyId] = { children: trustedAppAssignedPolicyId };
|
||||||
|
return byIdPolicies;
|
||||||
|
}
|
||||||
|
|
||||||
|
const policyDetailsPath = getPolicyDetailPath(trustedAppAssignedPolicyId);
|
||||||
|
|
||||||
|
const thisPolicyMenuProps: ContextMenuItemNavByRouterProps = {
|
||||||
|
navigateAppId: APP_ID,
|
||||||
|
navigateOptions: {
|
||||||
|
path: policyDetailsPath,
|
||||||
|
},
|
||||||
|
href: getAppUrl({ path: policyDetailsPath }),
|
||||||
|
children: allPoliciesById[trustedAppAssignedPolicyId].name,
|
||||||
|
};
|
||||||
|
|
||||||
|
byIdPolicies[trustedAppAssignedPolicyId] = thisPolicyMenuProps;
|
||||||
|
|
||||||
|
return byIdPolicies;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const thisTrustedAppCardProps: ArtifactCardGridCardComponentProps = {
|
||||||
|
expanded: Boolean(isCardExpanded[trustedApp.id]),
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
icon: 'controlsHorizontal',
|
||||||
|
children: i18n.translate(
|
||||||
|
'xpack.securitySolution.endpoint.policy.trustedApps.list.viewAction',
|
||||||
|
{ defaultMessage: 'View full details' }
|
||||||
|
),
|
||||||
|
href: getAppUrl({ appId: APP_ID, path: viewUrlPath }),
|
||||||
|
navigateAppId: APP_ID,
|
||||||
|
navigateOptions: { path: viewUrlPath },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
policies: assignedPoliciesMenuItems,
|
||||||
|
};
|
||||||
|
|
||||||
|
newCardProps.set(trustedApp, thisTrustedAppCardProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newCardProps;
|
||||||
|
}, [allPoliciesById, getAppUrl, isCardExpanded, trustedAppItems]);
|
||||||
|
|
||||||
|
const provideCardProps = useCallback<Required<ArtifactCardGridProps>['cardComponentProps']>(
|
||||||
|
(item) => {
|
||||||
|
return cardProps.get(item as Immutable<TrustedApp>)!;
|
||||||
|
},
|
||||||
|
[cardProps]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Anytime a new set of data (trusted apps) is retrieved, reset the card expand state
|
||||||
|
useEffect(() => {
|
||||||
|
setCardExpanded({});
|
||||||
|
}, [trustedAppItems]);
|
||||||
|
|
||||||
|
if (hasTrustedApps.loading) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<EuiLoadingSpinner className="essentialAnimation" size="xl" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasTrustedApps.hasTrustedApps) {
|
||||||
|
// TODO: implement empty state (task #1645)
|
||||||
|
return <div>{'No trusted application'}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EuiText color="subdued" size="xs" data-test-subj="policyDetailsTrustedAppsCount">
|
||||||
|
{totalItemsCountLabel}
|
||||||
|
</EuiText>
|
||||||
|
|
||||||
|
<EuiSpacer size="m" />
|
||||||
|
|
||||||
|
<ArtifactCardGrid
|
||||||
|
items={trustedAppItems}
|
||||||
|
onPageChange={handlePageChange}
|
||||||
|
onExpandCollapse={handleExpandCollapse}
|
||||||
|
cardComponentProps={provideCardProps}
|
||||||
|
loading={isLoading}
|
||||||
|
pagination={pagination as Pagination}
|
||||||
|
data-test-subj="policyTrustedAppsGrid"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
PolicyTrustedAppsList.displayName = 'PolicyTrustedAppsList';
|
|
@ -18,7 +18,7 @@ import {
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DeleteTrustedAppsRequestParams,
|
DeleteTrustedAppsRequestParams,
|
||||||
GetTrustedListAppsResponse,
|
GetTrustedAppsListResponse,
|
||||||
GetTrustedAppsListRequest,
|
GetTrustedAppsListRequest,
|
||||||
PostTrustedAppCreateRequest,
|
PostTrustedAppCreateRequest,
|
||||||
PostTrustedAppCreateResponse,
|
PostTrustedAppCreateResponse,
|
||||||
|
@ -36,7 +36,7 @@ import { sendGetEndpointSpecificPackagePolicies } from '../../policy/store/servi
|
||||||
|
|
||||||
export interface TrustedAppsService {
|
export interface TrustedAppsService {
|
||||||
getTrustedApp(params: GetOneTrustedAppRequestParams): Promise<GetOneTrustedAppResponse>;
|
getTrustedApp(params: GetOneTrustedAppRequestParams): Promise<GetOneTrustedAppResponse>;
|
||||||
getTrustedAppsList(request: GetTrustedAppsListRequest): Promise<GetTrustedListAppsResponse>;
|
getTrustedAppsList(request: GetTrustedAppsListRequest): Promise<GetTrustedAppsListResponse>;
|
||||||
deleteTrustedApp(request: DeleteTrustedAppsRequestParams): Promise<void>;
|
deleteTrustedApp(request: DeleteTrustedAppsRequestParams): Promise<void>;
|
||||||
createTrustedApp(request: PostTrustedAppCreateRequest): Promise<PostTrustedAppCreateResponse>;
|
createTrustedApp(request: PostTrustedAppCreateRequest): Promise<PostTrustedAppCreateResponse>;
|
||||||
updateTrustedApp(
|
updateTrustedApp(
|
||||||
|
@ -58,7 +58,7 @@ export class TrustedAppsHttpService implements TrustedAppsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTrustedAppsList(request: GetTrustedAppsListRequest) {
|
async getTrustedAppsList(request: GetTrustedAppsListRequest) {
|
||||||
return this.http.get<GetTrustedListAppsResponse>(TRUSTED_APPS_LIST_API, {
|
return this.http.get<GetTrustedAppsListResponse>(TRUSTED_APPS_LIST_API, {
|
||||||
query: request,
|
query: request,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -407,7 +407,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
class="body-content undefined"
|
class="body-content undefined"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -563,7 +563,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -790,7 +790,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -946,7 +946,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -1173,7 +1173,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -1329,7 +1329,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -1556,7 +1556,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -1712,7 +1712,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -1939,7 +1939,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -2095,7 +2095,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -2322,7 +2322,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -2478,7 +2478,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -2705,7 +2705,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -2861,7 +2861,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -3088,7 +3088,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -3244,7 +3244,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -3471,7 +3471,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -3627,7 +3627,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -3854,7 +3854,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -4010,7 +4010,7 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -4532,7 +4532,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
class="body-content undefined"
|
class="body-content undefined"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -4688,7 +4688,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -4915,7 +4915,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -5071,7 +5071,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -5298,7 +5298,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -5454,7 +5454,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -5681,7 +5681,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -5837,7 +5837,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -6064,7 +6064,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -6220,7 +6220,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -6447,7 +6447,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -6603,7 +6603,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -6830,7 +6830,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -6986,7 +6986,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -7213,7 +7213,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -7369,7 +7369,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -7596,7 +7596,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -7752,7 +7752,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -7979,7 +7979,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -8135,7 +8135,7 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -8614,7 +8614,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
class="body-content undefined"
|
class="body-content undefined"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -8770,7 +8770,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -8997,7 +8997,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -9153,7 +9153,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -9380,7 +9380,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -9536,7 +9536,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -9763,7 +9763,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -9919,7 +9919,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -10146,7 +10146,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -10302,7 +10302,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -10529,7 +10529,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -10685,7 +10685,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -10912,7 +10912,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -11068,7 +11068,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -11295,7 +11295,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -11451,7 +11451,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -11678,7 +11678,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -11834,7 +11834,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -12061,7 +12061,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard"
|
class="euiPanel euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--hasBorder c2 c3 artifactEntryCard "
|
||||||
data-test-subj="trustedAppCard"
|
data-test-subj="trustedAppCard"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -12217,7 +12217,7 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
|
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow euiFlexGroup--responsive"
|
||||||
data-test-subj="trustedAppCard-subHeader"
|
data-test-subj="trustedAppCard-subHeader"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { fireEvent } from '@testing-library/dom';
|
||||||
import { MiddlewareActionSpyHelper } from '../../../../common/store/test_utils';
|
import { MiddlewareActionSpyHelper } from '../../../../common/store/test_utils';
|
||||||
import {
|
import {
|
||||||
ConditionEntryField,
|
ConditionEntryField,
|
||||||
GetTrustedListAppsResponse,
|
GetTrustedAppsListResponse,
|
||||||
NewTrustedApp,
|
NewTrustedApp,
|
||||||
OperatingSystem,
|
OperatingSystem,
|
||||||
PostTrustedAppCreateResponse,
|
PostTrustedAppCreateResponse,
|
||||||
|
@ -83,7 +83,7 @@ describe('When on the Trusted Apps Page', () => {
|
||||||
page: number = 1,
|
page: number = 1,
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
per_page: number = 20
|
per_page: number = 20
|
||||||
): GetTrustedListAppsResponse => {
|
): GetTrustedAppsListResponse => {
|
||||||
return {
|
return {
|
||||||
data: [getFakeTrustedApp()],
|
data: [getFakeTrustedApp()],
|
||||||
total: 50, // << Should be a value large enough to fulfill two pages
|
total: 50, // << Should be a value large enough to fulfill two pages
|
||||||
|
@ -683,7 +683,7 @@ describe('When on the Trusted Apps Page', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('and there are no trusted apps', () => {
|
describe('and there are no trusted apps', () => {
|
||||||
const releaseExistsResponse: jest.MockedFunction<() => Promise<GetTrustedListAppsResponse>> =
|
const releaseExistsResponse: jest.MockedFunction<() => Promise<GetTrustedAppsListResponse>> =
|
||||||
jest.fn(async () => {
|
jest.fn(async () => {
|
||||||
return {
|
return {
|
||||||
data: [],
|
data: [],
|
||||||
|
@ -692,7 +692,7 @@ describe('When on the Trusted Apps Page', () => {
|
||||||
per_page: 1,
|
per_page: 1,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const releaseListResponse: jest.MockedFunction<() => Promise<GetTrustedListAppsResponse>> =
|
const releaseListResponse: jest.MockedFunction<() => Promise<GetTrustedAppsListResponse>> =
|
||||||
jest.fn(async () => {
|
jest.fn(async () => {
|
||||||
return {
|
return {
|
||||||
data: [],
|
data: [],
|
||||||
|
|
|
@ -10,11 +10,17 @@ import { ToolingLog } from '@kbn/dev-utils';
|
||||||
import { KbnClient } from '@kbn/test';
|
import { KbnClient } from '@kbn/test';
|
||||||
import bluebird from 'bluebird';
|
import bluebird from 'bluebird';
|
||||||
import { basename } from 'path';
|
import { basename } from 'path';
|
||||||
|
import { AxiosResponse } from 'axios';
|
||||||
import { TRUSTED_APPS_CREATE_API, TRUSTED_APPS_LIST_API } from '../../../common/endpoint/constants';
|
import { TRUSTED_APPS_CREATE_API, TRUSTED_APPS_LIST_API } from '../../../common/endpoint/constants';
|
||||||
import { TrustedApp } from '../../../common/endpoint/types';
|
import { TrustedApp } from '../../../common/endpoint/types';
|
||||||
import { TrustedAppGenerator } from '../../../common/endpoint/data_generators/trusted_app_generator';
|
import { TrustedAppGenerator } from '../../../common/endpoint/data_generators/trusted_app_generator';
|
||||||
import { indexFleetEndpointPolicy } from '../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
|
import { indexFleetEndpointPolicy } from '../../../common/endpoint/data_loaders/index_fleet_endpoint_policy';
|
||||||
import { setupFleetForEndpoint } from '../../../common/endpoint/data_loaders/setup_fleet_for_endpoint';
|
import { setupFleetForEndpoint } from '../../../common/endpoint/data_loaders/setup_fleet_for_endpoint';
|
||||||
|
import { GetPolicyListResponse } from '../../../public/management/pages/policy/types';
|
||||||
|
import {
|
||||||
|
PACKAGE_POLICY_API_ROUTES,
|
||||||
|
PACKAGE_POLICY_SAVED_OBJECT_TYPE,
|
||||||
|
} from '../../../../fleet/common';
|
||||||
|
|
||||||
const defaultLogger = new ToolingLog({ level: 'info', writeTo: process.stdout });
|
const defaultLogger = new ToolingLog({ level: 'info', writeTo: process.stdout });
|
||||||
const separator = '----------------------------------------';
|
const separator = '----------------------------------------';
|
||||||
|
@ -83,21 +89,25 @@ export const run: (options?: RunOptions) => Promise<TrustedApp[]> = async ({
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Setup a list of read endpoint policies and return a method to randomly select one
|
// Setup a list of real endpoint policies and return a method to randomly select one
|
||||||
const randomPolicyId: () => string = await (async () => {
|
const randomPolicyId: () => string = await (async () => {
|
||||||
const randomN = (max: number): number => Math.floor(Math.random() * max);
|
const randomN = (max: number): number => Math.floor(Math.random() * max);
|
||||||
const policyIds: string[] = [];
|
const policyIds: string[] =
|
||||||
|
(await fetchEndpointPolicies(kbnClient)).data.items.map((policy) => policy.id) || [];
|
||||||
|
|
||||||
for (let i = 0, t = 5; i < t; i++) {
|
// If the number of existing policies is less than 5, then create some more policies
|
||||||
policyIds.push(
|
if (policyIds.length < 5) {
|
||||||
(
|
for (let i = 0, t = 5 - policyIds.length; i < t; i++) {
|
||||||
await indexFleetEndpointPolicy(
|
policyIds.push(
|
||||||
kbnClient,
|
(
|
||||||
`Policy for Trusted App assignment ${i + 1}`,
|
await indexFleetEndpointPolicy(
|
||||||
installedEndpointPackage.version
|
kbnClient,
|
||||||
)
|
`Policy for Trusted App assignment ${i + 1}`,
|
||||||
).integrationPolicies[0].id
|
installedEndpointPackage.version
|
||||||
);
|
)
|
||||||
|
).integrationPolicies[0].id
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => policyIds[randomN(policyIds.length)];
|
return () => policyIds[randomN(policyIds.length)];
|
||||||
|
@ -153,3 +163,16 @@ const createRunLogger = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchEndpointPolicies = (
|
||||||
|
kbnClient: KbnClient
|
||||||
|
): Promise<AxiosResponse<GetPolicyListResponse>> => {
|
||||||
|
return kbnClient.request<GetPolicyListResponse>({
|
||||||
|
method: 'GET',
|
||||||
|
path: PACKAGE_POLICY_API_ROUTES.LIST_PATTERN,
|
||||||
|
query: {
|
||||||
|
perPage: 100,
|
||||||
|
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name: endpoint`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
GetOneTrustedAppResponse,
|
GetOneTrustedAppResponse,
|
||||||
GetTrustedAppsListRequest,
|
GetTrustedAppsListRequest,
|
||||||
GetTrustedAppsSummaryResponse,
|
GetTrustedAppsSummaryResponse,
|
||||||
GetTrustedListAppsResponse,
|
GetTrustedAppsListResponse,
|
||||||
PostTrustedAppCreateRequest,
|
PostTrustedAppCreateRequest,
|
||||||
PostTrustedAppCreateResponse,
|
PostTrustedAppCreateResponse,
|
||||||
PutTrustedAppUpdateRequest,
|
PutTrustedAppUpdateRequest,
|
||||||
|
@ -124,7 +124,7 @@ export const getTrustedApp = async (
|
||||||
export const getTrustedAppsList = async (
|
export const getTrustedAppsList = async (
|
||||||
exceptionsListClient: ExceptionListClient,
|
exceptionsListClient: ExceptionListClient,
|
||||||
{ page, per_page: perPage, kuery }: GetTrustedAppsListRequest
|
{ page, per_page: perPage, kuery }: GetTrustedAppsListRequest
|
||||||
): Promise<GetTrustedListAppsResponse> => {
|
): Promise<GetTrustedAppsListResponse> => {
|
||||||
// Ensure list is created if it does not exist
|
// Ensure list is created if it does not exist
|
||||||
await exceptionsListClient.createTrustedAppsList();
|
await exceptionsListClient.createTrustedAppsList();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue