Skip to content

Commit

Permalink
[Alerting] adds lazy loading of alerting UI components (#65060) (#65726)
Browse files Browse the repository at this point in the history
This PR adds code splitting into the Alerting Trigger UI by lazy loading the React Components needed for this plugin.

This include two key changes:

1. The plugin lazy loads its routes so that they don't get loaded at setup time.
2. This changes the way in which plugins can register their ActionType Components so they have to specify a lazy loaded component rather than a regular React component - this allows us to avoid loading all these components at setup time and defer this to when the Flyout/Triggers UI is loaded.
  • Loading branch information
gmmorris authored May 7, 2020
1 parent 03aba59 commit 50e0e2f
Show file tree
Hide file tree
Showing 63 changed files with 2,577 additions and 2,356 deletions.
3 changes: 3 additions & 0 deletions x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,6 @@ export const JiraConnectorFlyout = withConnectorFlyout<JiraActionConnector>({
configKeys: ['projectKey'],
connectorActionTypeId: '.jira',
});

// eslint-disable-next-line import/no-default-export
export { JiraConnectorFlyout as default };
4 changes: 2 additions & 2 deletions x-pack/plugins/siem/public/lib/connectors/jira/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { lazy } from 'react';
import {
ValidationResult,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
Expand All @@ -13,7 +14,6 @@ import { connector } from './config';
import { createActionType } from '../utils';
import logo from './logo.svg';
import { JiraActionConnector } from './types';
import { JiraConnectorFlyout } from './flyout';
import * as i18n from './translations';

interface Errors {
Expand Down Expand Up @@ -50,5 +50,5 @@ export const getActionType = createActionType({
selectMessage: i18n.JIRA_DESC,
actionTypeTitle: connector.name,
validateConnector,
actionConnectorFields: JiraConnectorFlyout,
actionConnectorFields: lazy(() => import('./flyout')),
});
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,6 @@ export const ServiceNowConnectorFlyout = withConnectorFlyout<ServiceNowActionCon
secretKeys: ['username', 'password'],
connectorActionTypeId: '.servicenow',
});

// eslint-disable-next-line import/no-default-export
export { ServiceNowConnectorFlyout as default };
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { lazy } from 'react';
import {
ValidationResult,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../triggers_actions_ui/public/types';

import { connector } from './config';
import { createActionType } from '../utils';
import logo from './logo.svg';
import { ServiceNowActionConnector } from './types';
import { ServiceNowConnectorFlyout } from './flyout';
import * as i18n from './translations';

interface Errors {
Expand Down Expand Up @@ -44,5 +43,5 @@ export const getActionType = createActionType({
selectMessage: i18n.SERVICENOW_DESC,
actionTypeTitle: connector.name,
validateConnector,
actionConnectorFields: ServiceNowConnectorFlyout,
actionConnectorFields: lazy(() => import('./flyout')),
});
3 changes: 2 additions & 1 deletion x-pack/plugins/siem/public/lib/connectors/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/* eslint-disable @kbn/eslint/no-restricted-paths */

import { ActionType } from '../../../../triggers_actions_ui/public';
import { IErrorObject } from '../../../../triggers_actions_ui/public/types';
import { ExternalIncidentServiceConfiguration } from '../../../../actions/server/builtin_action_types/case/types';

import { ActionType as ThirdPartySupportedActions, CaseField } from '../../../../case/common/api';
Expand Down Expand Up @@ -42,7 +43,7 @@ export interface ActionConnectorValidationErrors {
export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<T>;

export interface ConnectorFlyoutFormProps<T> {
errors: { [key: string]: string[] };
errors: IErrorObject;
action: T;
onChangeSecret: (key: string, value: string) => void;
onBlurSecret: (key: string) => void;
Expand Down
12 changes: 1 addition & 11 deletions x-pack/plugins/siem/public/lib/connectors/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import {
ActionTypeModel,
ValidationResult,
ActionParamsProps,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../triggers_actions_ui/public/types';

Expand All @@ -31,7 +30,7 @@ export const createActionType = ({
validateConnector,
validateParams = connectorParamsValidator,
actionConnectorFields,
actionParamsFields = ConnectorParamsFields,
actionParamsFields = null,
}: Optional<ActionTypeModel, 'validateParams' | 'actionParamsFields'>) => (): ActionTypeModel => {
return {
id,
Expand Down Expand Up @@ -59,15 +58,6 @@ export const createActionType = ({
};
};

const ConnectorParamsFields: React.FunctionComponent<ActionParamsProps<ActionConnectorParams>> = ({
actionParams,
editAction,
index,
errors,
}) => {
return null;
};

const connectorParamsValidator = (actionParams: ActionConnectorParams): ValidationResult => {
return { errors: {} };
};
Expand Down
14 changes: 10 additions & 4 deletions x-pack/plugins/triggers_actions_ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -985,8 +985,8 @@ Each action type should be defined as an `ActionTypeModel` object with the follo
|selectMessage|Short description of action type responsibility, that will be displayed on the select card in UI.|
|validateConnector|Validation function for action connector.|
|validateParams|Validation function for action params.|
|actionConnectorFields|React functional component for building UI of current action type connector.|
|actionParamsFields|React functional component for building UI of current action type params. Displayed as a part of Create Alert flyout.|
|actionConnectorFields|A lazy loaded React component for building UI of current action type connector.|
|actionParamsFields|A lazy loaded React component for building UI of current action type params. Displayed as a part of Create Alert flyout.|

## Register action type model

Expand Down Expand Up @@ -1082,8 +1082,8 @@ export function getActionType(): ActionTypeModel {
}
return validationResult;
},
actionConnectorFields: ExampleConnectorFields,
actionParamsFields: ExampleParamsFields,
actionConnectorFields: lazy(() => import('./example_connector_fields')),
actionParamsFields: lazy(() => import('./example_params_fields')),
};
}
```
Expand Down Expand Up @@ -1130,6 +1130,9 @@ const ExampleConnectorFields: React.FunctionComponent<ActionConnectorFieldsProps
</Fragment>
);
};
// Export as default in order to support lazy loading
export {ExampleConnectorFields as default};
```

3. Define action type params fields using the property of `ActionTypeModel` `actionParamsFields`:
Expand Down Expand Up @@ -1175,6 +1178,9 @@ const ExampleParamsFields: React.FunctionComponent<ActionParamsProps<ExampleActi
</Fragment>
);
};
// Export as default in order to support lazy loading
export {ExampleParamsFields as default};
```

4. Extend registration code with the new action type register in the file `x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts`
Expand Down
39 changes: 33 additions & 6 deletions x-pack/plugins/triggers_actions_ui/public/application/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { Switch, Route, Redirect, HashRouter } from 'react-router-dom';
import React, { lazy, Suspense } from 'react';
import { Switch, Route, Redirect, HashRouter, RouteComponentProps } from 'react-router-dom';
import {
ChromeStart,
DocLinksStart,
Expand All @@ -15,17 +15,21 @@ import {
ChromeBreadcrumb,
CoreStart,
} from 'kibana/public';
import { EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { BASE_PATH, Section, routeToAlertDetails } from './constants';
import { TriggersActionsUIHome } from './home';
import { AppContextProvider, useAppDependencies } from './app_context';
import { hasShowAlertsCapability } from './lib/capabilities';
import { ActionTypeModel, AlertTypeModel } from '../types';
import { TypeRegistry } from './type_registry';
import { AlertDetailsRouteWithApi as AlertDetailsRoute } from './sections/alert_details/components/alert_details_route';
import { ChartsPluginStart } from '../../../../../src/plugins/charts/public';
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
import { PluginStartContract as AlertingStart } from '../../../alerting/public';

const TriggersActionsUIHome = lazy(async () => import('./home'));
const AlertDetailsRoute = lazy(() =>
import('./sections/alert_details/components/alert_details_route')
);

export interface AppDeps {
dataPlugin: DataPublicPluginStart;
charts: ChartsPluginStart;
Expand Down Expand Up @@ -62,9 +66,32 @@ export const AppWithoutRouter = ({ sectionsRegex }: { sectionsRegex: string }) =
const DEFAULT_SECTION: Section = canShowAlerts ? 'alerts' : 'connectors';
return (
<Switch>
<Route path={`${BASE_PATH}/:section(${sectionsRegex})`} component={TriggersActionsUIHome} />
{canShowAlerts && <Route path={routeToAlertDetails} component={AlertDetailsRoute} />}
<Route
path={`${BASE_PATH}/:section(${sectionsRegex})`}
component={suspendedRouteComponent(TriggersActionsUIHome)}
/>
{canShowAlerts && (
<Route path={routeToAlertDetails} component={suspendedRouteComponent(AlertDetailsRoute)} />
)}
<Redirect from={`${BASE_PATH}`} to={`${BASE_PATH}/${DEFAULT_SECTION}`} />
</Switch>
);
};

function suspendedRouteComponent<T = unknown>(
RouteComponent: React.ComponentType<RouteComponentProps<T>>
) {
return (props: RouteComponentProps<T>) => (
<Suspense
fallback={
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="xl" />
</EuiFlexItem>
</EuiFlexGroup>
}
>
<RouteComponent {...props} />
</Suspense>
);
}
Loading

0 comments on commit 50e0e2f

Please sign in to comment.