Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Endpoint] Policy Details integration with Ingest APIs #61827

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0b2b40d
button + action + middleware + ingest service
paul-tavares Mar 23, 2020
3a8883a
stuff
paul-tavares Mar 23, 2020
df16b26
it works
paul-tavares Mar 24, 2020
4e220d3
Merge remote-tracking branch 'upstream/master' into task/emt-275-poli…
paul-tavares Mar 25, 2020
bb5167d
Merge remote-tracking branch 'upstream/master' into task/emt-275-poli…
paul-tavares Mar 25, 2020
9436893
PolicyAgentsSummary component
paul-tavares Mar 25, 2020
9f3369c
Add health to AgentsSummary
paul-tavares Mar 25, 2020
08aaae0
renamed file to match component
paul-tavares Mar 25, 2020
6a48297
Added fetch of agents status from API
paul-tavares Mar 25, 2020
78e35ed
Better policy mock + handle updates + failures to the store
paul-tavares Mar 25, 2020
c251cc6
starting to work on notifications
paul-tavares Mar 25, 2020
cfaa957
Merge remote-tracking branch 'upstream/master' into task/emt-275-poli…
paul-tavares Mar 26, 2020
c4d53b2
Adjust policy details following merge from master
paul-tavares Mar 26, 2020
da31cba
Added success/failure toast notifications
paul-tavares Mar 26, 2020
c96c8d2
fix reset of state when user navigates out of details page
paul-tavares Mar 26, 2020
0cbf623
Add save confirmation modal dialog
paul-tavares Mar 26, 2020
81f3a9b
alter page_view and export components used for display header title a…
paul-tavares Mar 26, 2020
2ed3a07
Handle fetch Errors + back to list button
paul-tavares Mar 26, 2020
553b9e2
Add handler for button to navigate to policy list
paul-tavares Mar 26, 2020
0530e9f
add vertical spacer to header agent summary
paul-tavares Mar 27, 2020
864776e
change vertical divider to use values from spacerSize for `spacing` prop
paul-tavares Mar 27, 2020
be5f4a3
Adjusted PolicyConfig type + new Policy model generator
paul-tavares Mar 27, 2020
25a8222
first attempt at refactoring selectors/reducer (not working yet)
paul-tavares Mar 27, 2020
e57e18d
Updates are persisted (selectors working) + `viewType` prop added to …
paul-tavares Mar 30, 2020
a18b91a
Sync PageView `viewType` to design for lists + details
paul-tavares Mar 30, 2020
8e534c1
clean up of TODO items
paul-tavares Mar 30, 2020
f6a6268
Merge remote-tracking branch 'upstream/master' into task/emt-275-poli…
paul-tavares Mar 30, 2020
a6c55ee
Replace use of PolicyConfig with UIPolicyConfig + cleanup
paul-tavares Mar 31, 2020
3b45458
Fix test for PageView ++ policy details store
paul-tavares Mar 31, 2020
bdccd82
refactor policy `windows.eventing` to `events` ++ fix types
paul-tavares Mar 31, 2020
3f5e3d9
Fix policy model generator
paul-tavares Mar 31, 2020
4be1e2b
Merge remote-tracking branch 'upstream/master' into task/emt-275-poli…
paul-tavares Mar 31, 2020
a63b191
Delete sneaky file
paul-tavares Mar 31, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,67 @@ describe('PageView component', () => {
mount(ui, { wrappingComponent: EuiThemeProvider });

it('should display only body if not header props used', () => {
expect(render(<PageView>body content</PageView>)).toMatchSnapshot();
expect(render(<PageView viewType="list">body content</PageView>)).toMatchSnapshot();
});
it('should display header left and right', () => {
expect(
render(
<PageView headerLeft="page title" headerRight="right side actions">
<PageView viewType="list" headerLeft="page title" headerRight="right side actions">
body content
</PageView>
)
).toMatchSnapshot();
});
it('should display only header left', () => {
expect(render(<PageView headerLeft="page title">body content</PageView>)).toMatchSnapshot();
expect(
render(
<PageView viewType="list" headerLeft="page title">
body content
</PageView>
)
).toMatchSnapshot();
});
it('should display only header right but include an empty left side', () => {
expect(
render(<PageView headerRight="right side actions">body content</PageView>)
render(
<PageView viewType="list" headerRight="right side actions">
body content
</PageView>
)
).toMatchSnapshot();
});
it(`should use custom element for header left and not wrap in EuiTitle`, () => {
expect(
render(<PageView headerLeft={<p>title here</p>}>body content</PageView>)
render(
<PageView viewType="list" headerLeft={<p>title here</p>}>
body content
</PageView>
)
).toMatchSnapshot();
});
it('should display body header wrapped in EuiTitle', () => {
expect(render(<PageView bodyHeader="body header">body content</PageView>)).toMatchSnapshot();
expect(
render(
<PageView viewType="list" bodyHeader="body header">
body content
</PageView>
)
).toMatchSnapshot();
});
it('should display body header custom element', () => {
expect(
render(<PageView bodyHeader={<p>body header</p>}>body content</PageView>)
render(
<PageView viewType="list" bodyHeader={<p>body header</p>}>
body content
</PageView>
)
).toMatchSnapshot();
});
it('should pass through EuiPage props', () => {
expect(
render(
<PageView
viewType="list"
restrictWidth="1000"
className="test-class-name-here"
aria-label="test-aria-label-here"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,65 @@ import React, { memo, ReactNode } from 'react';
import styled from 'styled-components';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also just general question on our code organization: do we need to have a separate components folder? or does it make sense to just have all view related stuff just in the view folder? maybe common components can just be top level in the view folder? 🤷‍♀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats what I was thinking - top level components folder was reusable across all views.
my approach also was that if a component from one view needs to be re-used in another View, we would elevate that component here for reuse - so that we're not importing from a sibling, only from a parent or a child of the given view/component.


const StyledEuiPage = styled(EuiPage)`
padding: 0;
&.endpoint--isListView {
padding: 0;

.endpoint-header {
padding: ${props => props.theme.eui.euiSizeL};
.endpoint-header {
padding: ${props => props.theme.eui.euiSizeL};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we also want to do margin: 0; here. from the mockies i believe the header words are evensteven in the middle
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GOOOD EYE 👏
I will make this change in a subsequent PR.

FYI: The extra white space here is noticeable because a bodyHeader is also being used with the PageView component.

}
.endpoint-page-content {
border-left: none;
border-right: none;
}
}
.endpoint-page-content {
border-left: none;
border-right: none;
&.endpoint--isDetailsView {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see this -- pattern a lot in other css stuffs, does it have a standard meaning? -- pliss teach me

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:)
Ohhh. I hope I get this right from memory.
In CSS there are a few patterns for managing styles, one is called BEM - Block-Element-Modifier. in that pattern, a Modifier classname is normally defined using the double dash -- (in a past project, the _ was used, but same concept) . Its purpose is to normally drive how other classnames below it will adjust based on its presence.

(there are a few other patterns out there - google BEM vs and you should see the others popup)
😃

.endpoint-page-content {
padding: 0;
border: none;
background: none;
}
}
`;

const isStringOrNumber = /(string|number)/;

/**
* The `PageView` component used to render `headerLeft` when it is set as a `string`
* Can be used when wanting to customize the `headerLeft` value but still use the standard
* title component
*/
export const PageViewHeaderTitle = memo<{ children: ReactNode }>(({ children }) => {
return (
<EuiTitle size="l">
<h1 data-test-subj="pageViewHeaderLeftTitle">{children}</h1>
</EuiTitle>
);
});

/**
* The `PageView` component used to render `bodyHeader` when it is set as a `string`
* Can be used when wanting to customize the `bodyHeader` value but still use the standard
* title component
*/
export const PageViewBodyHeaderTitle = memo<{ children: ReactNode }>(
({ children, ...otherProps }) => {
return (
<EuiTitle {...otherProps}>
<h2 data-test-subj="pageViewBodyTitle">{children}</h2>
</EuiTitle>
);
}
);

/**
* Page View layout for use in Endpoint
*/
export const PageView = memo<
EuiPageProps & {
/**
* The type of view
*/
viewType: 'list' | 'details';
/**
* content to be placed on the left side of the header. If a `string` is used, then it will
* be wrapped with `<EuiTitle><h1></h1></EuiTitle>`, else it will just be used as is.
Expand All @@ -52,17 +93,18 @@ export const PageView = memo<
bodyHeader?: ReactNode;
children?: ReactNode;
}
>(({ children, headerLeft, headerRight, bodyHeader, ...otherProps }) => {
>(({ viewType, children, headerLeft, headerRight, bodyHeader, ...otherProps }) => {
return (
<StyledEuiPage {...otherProps}>
<StyledEuiPage
className={(viewType === 'list' && 'endpoint--isListView') || 'endpoint--isDetailsView'}
{...otherProps}
>
<EuiPageBody>
{(headerLeft || headerRight) && (
<EuiPageHeader className="endpoint-header">
<EuiPageHeaderSection data-test-subj="pageViewHeaderLeft">
{isStringOrNumber.test(typeof headerLeft) ? (
<EuiTitle size="l">
<h1>{headerLeft}</h1>
</EuiTitle>
<PageViewHeaderTitle>{headerLeft}</PageViewHeaderTitle>
) : (
headerLeft
)}
Expand All @@ -77,11 +119,9 @@ export const PageView = memo<
<EuiPageContent className="endpoint-page-content">
{bodyHeader && (
<EuiPageContentHeader>
<EuiPageContentHeaderSection data-test-subj="pageViewBodyTitle">
<EuiPageContentHeaderSection data-test-subj="pageViewBodyTitleArea">
{isStringOrNumber.test(typeof bodyHeader) ? (
<EuiTitle>
<h2>{bodyHeader}</h2>
</EuiTitle>
<PageViewBodyHeaderTitle>{bodyHeader}</PageViewBodyHeaderTitle>
) : (
bodyHeader
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { PolicyConfig } from '../types';

/**
* Generate a new Policy model.
* NOTE: in the near future, this will likely be removed and an API call to EPM will be used to retrieve
* the latest from the Endpoint package
*/
export const generatePolicy = (): PolicyConfig => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is essentially our defaults for now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinlog Yes, but this will move to the package registry (as part of the Endpoint package) (here: https://github.com/elastic/package-registry/blob/master/dev/packages/example/endpoint-1.0.0/manifest.yml). Once there, it should not be necessary for us to have this generator here (ok - maybe for UT), and if needed, we can use EPM APIs to retrieve it from the package.

return {
windows: {
events: {
process: true,
network: true,
},
malware: {
mode: 'prevent',
},
logging: {
stdout: 'debug',
file: 'info',
},
advanced: {
elasticsearch: {
indices: {
control: 'control-index',
event: 'event-index',
logging: 'logging-index',
},
kernel: {
connect: true,
process: true,
},
},
},
},
mac: {
events: {
process: true,
},
malware: {
mode: 'detect',
},
logging: {
stdout: 'debug',
file: 'info',
},
advanced: {
elasticsearch: {
indices: {
control: 'control-index',
event: 'event-index',
logging: 'logging-index',
},
kernel: {
connect: true,
process: true,
},
},
},
},
linux: {
events: {
process: true,
},
logging: {
stdout: 'debug',
file: 'info',
},
advanced: {
elasticsearch: {
indices: {
control: 'control-index',
event: 'event-index',
logging: 'logging-index',
},
kernel: {
connect: true,
process: true,
},
},
},
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { PolicyConfig } from '../types';
import { UIPolicyConfig } from '../types';

/**
* A typed Object.entries() function where the keys and values are typed based on the given object
Expand All @@ -14,10 +14,10 @@ const entries = <T extends object>(o: T): Array<[keyof T, T[keyof T]]> =>
type DeepPartial<T> = { [K in keyof T]?: DeepPartial<T[K]> };

/**
* Returns a deep copy of PolicyDetailsConfig
* Returns a deep copy of `UIPolicyConfig` object
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wew did it werk?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - just needed to get the types right

*/
export function clone(policyDetailsConfig: PolicyConfig): PolicyConfig {
const clonedConfig: DeepPartial<PolicyConfig> = {};
export function clone(policyDetailsConfig: UIPolicyConfig): UIPolicyConfig {
const clonedConfig: DeepPartial<UIPolicyConfig> = {};
for (const [key, val] of entries(policyDetailsConfig)) {
if (typeof val === 'object') {
const valClone: Partial<typeof val> = {};
Expand All @@ -41,5 +41,5 @@ export function clone(policyDetailsConfig: PolicyConfig): PolicyConfig {
/**
* clonedConfig is typed as DeepPartial so we can construct the copy from an empty object
*/
return clonedConfig as PolicyConfig;
return clonedConfig as UIPolicyConfig;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@
*/

import { HttpFetchOptions, HttpStart } from 'kibana/public';
import { GetDatasourcesRequest } from '../../../../../ingest_manager/common/types/rest_spec';
import { PolicyData } from '../types';
import {
CreateDatasourceResponse,
GetAgentStatusResponse,
GetDatasourcesRequest,
} from '../../../../../ingest_manager/common/types/rest_spec';
import { NewPolicyData, PolicyData } from '../types';

const INGEST_API_ROOT = `/api/ingest_manager`;
const INGEST_API_DATASOURCES = `${INGEST_API_ROOT}/datasources`;
const INGEST_API_FLEET = `${INGEST_API_ROOT}/fleet`;
const INGEST_API_FLEET_AGENT_STATUS = `${INGEST_API_FLEET}/agent-status`;

// FIXME: Import from ingest after - https://github.com/elastic/kibana/issues/60677
export interface GetDatasourcesResponse {
Expand All @@ -26,6 +32,11 @@ export interface GetDatasourceResponse {
success: boolean;
}

// FIXME: Import from Ingest after - https://github.com/elastic/kibana/issues/60677
export type UpdateDatasourceResponse = CreateDatasourceResponse & {
item: PolicyData;
};

/**
* Retrieves a list of endpoint specific datasources (those created with a `package.name` of
* `endpoint`) from Ingest
Expand Down Expand Up @@ -60,3 +71,44 @@ export const sendGetDatasource = (
) => {
return http.get<GetDatasourceResponse>(`${INGEST_API_DATASOURCES}/${datasourceId}`, options);
};

/**
* Updates a datasources
*
* @param http
* @param datasourceId
* @param datasource
* @param options
*/
export const sendPutDatasource = (
http: HttpStart,
datasourceId: string,
datasource: NewPolicyData,
options: Exclude<HttpFetchOptions, 'body'> = {}
): Promise<UpdateDatasourceResponse> => {
return http.put(`${INGEST_API_DATASOURCES}/${datasourceId}`, {
...options,
body: JSON.stringify(datasource),
});
};

/**
* Get a status summary for all Agents that are currently assigned to a given agent configuration
*
* @param http
* @param configId
* @param options
*/
export const sendGetFleetAgentStatusForConfig = (
http: HttpStart,
/** the Agent (fleet) configuration id */
configId: string,
options: Exclude<HttpFetchOptions, 'query'> = {}
): Promise<GetAgentStatusResponse> => {
return http.get(INGEST_API_FLEET_AGENT_STATUS, {
...options,
query: {
configId,
},
});
};
Loading