Skip to content

Commit

Permalink
Endpoint: Move files, add README, replace implicit any with stricte…
Browse files Browse the repository at this point in the history
…r types. Reorganize types. (#63262)

* Removed `FIXME` comments and references to private repos. Please use Github issues to track work
* Added a README describing the modules in `applications/endpoint`
* Removed dead code
* Moved `AppRoot` component to its own module
* Moved `applications/endpoint/services` under `store`
* Moved some exported types to `applications/endpoint/types`
* Moved all React code to `view`
* Added types in some places that were implicitly `any`
* Moved `PageId` type from common directory (where it could be shared with the server) to the public directory
  • Loading branch information
Robert Austin authored Apr 13, 2020
1 parent 8cef945 commit d00c905
Show file tree
Hide file tree
Showing 26 changed files with 161 additions and 124 deletions.
1 change: 0 additions & 1 deletion x-pack/plugins/endpoint/common/generate_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import uuid from 'uuid';
import seedrandom from 'seedrandom';
import { AlertEvent, EndpointEvent, HostMetadata, OSFields, HostFields } from './types';
// FIXME: move types/model to top-level
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { PolicyData } from '../public/applications/endpoint/types';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
Expand Down
5 changes: 0 additions & 5 deletions x-pack/plugins/endpoint/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,11 +375,6 @@ export interface EndpointEvent {

export type ResolverEvent = EndpointEvent | LegacyEndpointEvent;

/**
* The PageId type is used for the payload when firing userNavigatedToPage actions
*/
export type PageId = 'alertsPage' | 'managementPage' | 'policyListPage';

/**
* Takes a @kbn/config-schema 'schema' type and returns a type that represents valid inputs.
* Similar to `TypeOf`, but allows strings as input for `schema.number()` (which is inline
Expand Down
28 changes: 28 additions & 0 deletions x-pack/plugins/endpoint/public/applications/endpoint/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Endpoint application
This application provides the user interface for the Elastic Endpoint

# Architecture
The application consists of a _view_ written in React and a _model_ written in Redux.

# Modules
We structure the modules to match the architecture. `view` contains the _view_ (all React) code. `store` contains the _model_.

This section covers the conventions of each top level module.

# `mocks`
This contains helper code for unit tests.

## `models`
This contains domain models. By convention, each submodule here contains methods for a single type. Domain model classes would also live here.

## `store`
This contains the _model_ of the application. All Redux and Redux middleware code (including API interactions) happen here. This module also contains the types and interfaces defining Redux actions. Each action type or interface should be commented and if it has fields, each field should be commented. Comments should be of `tsdoc` style.

## `view`
This contains the code which renders elements to the DOM. All React code goes here.

## `index.tsx`
This exports `renderApp` which instantiates the React view with the _model_.

## `types.ts`
This contains the types and interfaces. All `export`ed types or interfaces (except ones defining Redux actions, which live in `store`) should be here. Each type or interface should have a `tsdoc` style comment. Interfaces should have `tsdoc` comments on each field and types which have fields should do the same.

This file was deleted.

51 changes: 2 additions & 49 deletions x-pack/plugins/endpoint/public/applications/endpoint/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,10 @@

import * as React from 'react';
import ReactDOM from 'react-dom';
import { CoreStart, AppMountParameters, ScopedHistory } from 'kibana/public';
import { FormattedMessage } from '@kbn/i18n/react';
import { Route, Switch } from 'react-router-dom';
import { Store } from 'redux';
import { CoreStart, AppMountParameters } from 'kibana/public';
import { EndpointPluginStartDependencies } from '../../plugin';
import { appStoreFactory } from './store';
import { AlertIndex } from './view/alerts';
import { HostList } from './view/hosts';
import { PolicyList } from './view/policy';
import { PolicyDetails } from './view/policy';
import { HeaderNavigation } from './components/header_nav';
import { AppRootProvider } from './view/app_root_provider';
import { Setup } from './view/setup';
import { AppRoot } from './view/app_root';

/**
* This module will be loaded asynchronously to reduce the bundle size of your plugin's main bundle.
Expand All @@ -37,41 +28,3 @@ export function renderApp(
ReactDOM.unmountComponentAtNode(element);
};
}

interface RouterProps {
history: ScopedHistory;
store: Store;
coreStart: CoreStart;
depsStart: EndpointPluginStartDependencies;
}

const AppRoot: React.FunctionComponent<RouterProps> = React.memo(
({ history, store, coreStart, depsStart }) => {
return (
<AppRootProvider store={store} history={history} coreStart={coreStart} depsStart={depsStart}>
<Setup ingestManager={depsStart.ingestManager} notifications={coreStart.notifications} />
<HeaderNavigation />
<Switch>
<Route
exact
path="/"
render={() => (
<h1 data-test-subj="welcomeTitle">
<FormattedMessage id="xpack.endpoint.welcomeTitle" defaultMessage="Hello World" />
</h1>
)}
/>
<Route path="/hosts" component={HostList} />
<Route path="/alerts" component={AlertIndex} />
<Route path="/policy" exact component={PolicyList} />
<Route path="/policy/:id" exact component={PolicyDetails} />
<Route
render={() => (
<FormattedMessage id="xpack.endpoint.notFound" defaultMessage="Page Not Found" />
)}
/>
</Switch>
</AppRootProvider>
);
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@

export { alertListReducer } from './reducer';
export { AlertAction } from './action';
export * from '../../types';
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { MiddlewareFactory, PolicyData, PolicyDetailsState } from '../../types';
import {
MiddlewareFactory,
PolicyData,
PolicyDetailsState,
UpdateDatasourceResponse,
} from '../../types';
import { policyIdFromParams, isOnPolicyDetailsPage, policyDetails } from './selectors';
import { generatePolicy } from '../../models/policy';
import {
sendGetDatasource,
sendGetFleetAgentStatusForConfig,
sendPutDatasource,
UpdateDatasourceResponse,
} from '../../services/ingest';
import { generatePolicy } from '../../models/policy';
} from '../policy_list/services/ingest';

export const policyDetailsMiddlewareFactory: MiddlewareFactory<PolicyDetailsState> = coreStart => {
const http = coreStart.http;
Expand All @@ -35,7 +39,6 @@ export const policyDetailsMiddlewareFactory: MiddlewareFactory<PolicyDetailsStat
return;
}

// FIXME: remove this code once the Default Policy is available in the endpoint package - see: https://github.com/elastic/endpoint-app-team/issues/295
// Until we get the Default configuration into the Endpoint package so that the datasource has
// the expected data structure, we will add it here manually.
if (!policyItem.inputs.length) {
Expand All @@ -62,7 +65,6 @@ export const policyDetailsMiddlewareFactory: MiddlewareFactory<PolicyDetailsStat

// Agent summary is secondary data, so its ok for it to come after the details
// page is populated with the main content
// FIXME: need to only do this IF fleet is enabled - see: https://github.com/elastic/endpoint-app-team/issues/296
if (policyItem.config_id) {
const { results } = await sendGetFleetAgentStatusForConfig(http, policyItem.config_id);
dispatch({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { MiddlewareFactory, PolicyListState } from '../../types';
import { GetDatasourcesResponse, sendGetEndpointSpecificDatasources } from '../../services/ingest';
import { MiddlewareFactory, PolicyListState, GetDatasourcesResponse } from '../../types';
import { sendGetEndpointSpecificDatasources } from './services/ingest';

export const policyListMiddlewareFactory: MiddlewareFactory<PolicyListState> = coreStart => {
const http = coreStart.http;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { httpServiceMock } from '../../../../../../../src/core/public/mocks';
import { sendGetDatasource, sendGetEndpointSpecificDatasources } from './ingest';
import { httpServiceMock } from '../../../../../../../../../src/core/public/mocks';

describe('ingest service', () => {
let http: ReturnType<typeof httpServiceMock.createStartContract>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,21 @@

import { HttpFetchOptions, HttpStart } from 'kibana/public';
import {
CreateDatasourceResponse,
GetAgentStatusResponse,
GetDatasourcesRequest,
} from '../../../../../ingest_manager/common/types/rest_spec';
import { NewPolicyData, PolicyData } from '../types';
GetAgentStatusResponse,
} from '../../../../../../../ingest_manager/common';
import {
NewPolicyData,
GetDatasourcesResponse,
GetDatasourceResponse,
UpdateDatasourceResponse,
} 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 {
items: PolicyData[];
total: number;
page: number;
perPage: number;
success: boolean;
}

// FIXME: Import from Ingest after - https://github.com/elastic/kibana/issues/60677
export interface GetDatasourceResponse {
item: PolicyData;
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { PageId, Immutable } from '../../../../../common/types';
import { EndpointAppLocation } from '../alerts';
import { Immutable } from '../../../../../common/types';
import { EndpointAppLocation, PageId } from '../../types';

interface UserNavigatedToPage {
readonly type: 'userNavigatedToPage';
Expand Down
27 changes: 26 additions & 1 deletion x-pack/plugins/endpoint/public/applications/endpoint/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import { EndpointPluginStartDependencies } from '../../plugin';
import { AppAction } from './store/action';
import { CoreStart } from '../../../../../../src/core/public';
import { Datasource, NewDatasource } from '../../../../ingest_manager/common/types/models';
import { GetAgentStatusResponse } from '../../../../ingest_manager/common/types/rest_spec';
import {
GetAgentStatusResponse,
CreateDatasourceResponse,
} from '../../../../ingest_manager/common/types/rest_spec';

export { AppAction };
export type MiddlewareFactory<S = GlobalState> = (
Expand Down Expand Up @@ -317,3 +320,25 @@ export interface AlertingIndexUIQueryParams {
date_range?: string;
filters?: string;
}

export interface GetDatasourcesResponse {
items: PolicyData[];
total: number;
page: number;
perPage: number;
success: boolean;
}

export interface GetDatasourceResponse {
item: PolicyData;
success: boolean;
}

export type UpdateDatasourceResponse = CreateDatasourceResponse & {
item: PolicyData;
};

/**
* The PageId type is used for the payload when firing userNavigatedToPage actions
*/
export type PageId = 'alertsPage' | 'managementPage' | 'policyListPage';
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 * as React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { Route, Switch } from 'react-router-dom';
import { Store } from 'redux';
import { AlertIndex } from './alerts';
import { HostList } from './hosts';
import { PolicyList } from './policy';
import { PolicyDetails } from './policy';
import { HeaderNavigation } from './components/header_navigation';
import { AppRootProvider } from './app_root_provider';
import { Setup } from './setup';
import { EndpointPluginStartDependencies } from '../../../plugin';
import { ScopedHistory, CoreStart } from '../../../../../../../src/core/public';

interface RouterProps {
history: ScopedHistory;
store: Store;
coreStart: CoreStart;
depsStart: EndpointPluginStartDependencies;
}

/**
* The root of the Endpoint application react view.
*/
export const AppRoot: React.FunctionComponent<RouterProps> = React.memo(
({ history, store, coreStart, depsStart }) => {
return (
<AppRootProvider store={store} history={history} coreStart={coreStart} depsStart={depsStart}>
<Setup ingestManager={depsStart.ingestManager} notifications={coreStart.notifications} />
<HeaderNavigation />
<Switch>
<Route
exact
path="/"
render={() => (
<h1 data-test-subj="welcomeTitle">
<FormattedMessage id="xpack.endpoint.welcomeTitle" defaultMessage="Hello World" />
</h1>
)}
/>
<Route path="/hosts" component={HostList} />
<Route path="/alerts" component={AlertIndex} />
<Route path="/policy" exact component={PolicyList} />
<Route path="/policy/:id" exact component={PolicyDetails} />
<Route
render={() => (
<FormattedMessage id="xpack.endpoint.notFound" defaultMessage="Page Not Found" />
)}
/>
</Switch>
</AppRootProvider>
);
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import React, { MouseEvent, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiTabs, EuiTab } from '@elastic/eui';
import { useHistory, useLocation } from 'react-router-dom';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
import { Immutable } from '../../../../../common/types';

export interface NavTabs {
interface NavTabs {
name: string;
id: string;
href: string;
}

export const navTabs: NavTabs[] = [
const navTabs: Immutable<NavTabs[]> = [
{
id: 'home',
name: i18n.translate('xpack.endpoint.headerNav.home', {
Expand Down
Loading

0 comments on commit d00c905

Please sign in to comment.