[ENDPOINT][INGEST]Task/endpoint ingest update (#67234)
Custom endpoint Datasource configuration in ingest Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Jen Huang <its.jenetic@gmail.com>
This commit is contained in:
parent
77abbf257d
commit
dd86ccf747
12 changed files with 186 additions and 44 deletions
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
export { useCapabilities } from './use_capabilities';
|
||||
export { useCore, CoreContext } from './use_core';
|
||||
export { useCore } from './use_core';
|
||||
export { useConfig, ConfigContext } from './use_config';
|
||||
export { useSetupDeps, useStartDeps, DepsContext } from './use_deps';
|
||||
export { useBreadcrumbs } from './use_breadcrumbs';
|
||||
|
|
|
@ -4,15 +4,13 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { useContext } from 'react';
|
||||
import { CoreStart } from 'src/core/public';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
export const CoreContext = React.createContext<CoreStart | null>(null);
|
||||
|
||||
export function useCore() {
|
||||
const core = useContext(CoreContext);
|
||||
if (core === null) {
|
||||
throw new Error('CoreContext not initialized');
|
||||
export function useCore(): CoreStart {
|
||||
const { services } = useKibana();
|
||||
if (services === null) {
|
||||
throw new Error('KibanaContextProvider not initialized');
|
||||
}
|
||||
return core;
|
||||
return services;
|
||||
}
|
||||
|
|
|
@ -22,11 +22,12 @@ import { PAGE_ROUTING_PATHS } from './constants';
|
|||
import { DefaultLayout, WithoutHeaderLayout } from './layouts';
|
||||
import { Loading, Error } from './components';
|
||||
import { IngestManagerOverview, EPMApp, AgentConfigApp, FleetApp, DataStreamApp } from './sections';
|
||||
import { CoreContext, DepsContext, ConfigContext, setHttpClient, useConfig } from './hooks';
|
||||
import { DepsContext, ConfigContext, setHttpClient, useConfig } from './hooks';
|
||||
import { PackageInstallProvider } from './sections/epm/hooks';
|
||||
import { useCore, sendSetup, sendGetPermissionsCheck } from './hooks';
|
||||
import { FleetStatusProvider } from './hooks/use_fleet_status';
|
||||
import './index.scss';
|
||||
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
export interface ProtectedRouteProps extends RouteProps {
|
||||
isAllowed?: boolean;
|
||||
|
@ -229,7 +230,7 @@ const IngestManagerApp = ({
|
|||
const isDarkMode = useObservable<boolean>(coreStart.uiSettings.get$('theme:darkMode'));
|
||||
return (
|
||||
<coreStart.i18n.Context>
|
||||
<CoreContext.Provider value={coreStart}>
|
||||
<KibanaContextProvider services={{ ...coreStart }}>
|
||||
<DepsContext.Provider value={{ setup: setupDeps, start: startDeps }}>
|
||||
<ConfigContext.Provider value={config}>
|
||||
<EuiThemeProvider darkMode={isDarkMode}>
|
||||
|
@ -237,7 +238,7 @@ const IngestManagerApp = ({
|
|||
</EuiThemeProvider>
|
||||
</ConfigContext.Provider>
|
||||
</DepsContext.Provider>
|
||||
</CoreContext.Provider>
|
||||
</KibanaContextProvider>
|
||||
</coreStart.i18n.Context>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { EuiEmptyPrompt, EuiText } from '@elastic/eui';
|
||||
import { NewDatasource } from '../../../../types';
|
||||
import { CreateDatasourceFrom } from '../types';
|
||||
|
||||
export interface CustomConfigureDatasourceProps {
|
||||
packageName: string;
|
||||
from: CreateDatasourceFrom;
|
||||
datasource: NewDatasource | (NewDatasource & { id: string });
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom content type that external plugins can provide to Ingest's
|
||||
* Datasource configuration.
|
||||
*/
|
||||
export type CustomConfigureDatasourceContent = React.FC<CustomConfigureDatasourceProps>;
|
||||
|
||||
type AllowedDatasourceKey = 'endpoint';
|
||||
const ConfigureDatasourceMapping: {
|
||||
[key: string]: CustomConfigureDatasourceContent;
|
||||
} = {};
|
||||
|
||||
/**
|
||||
* Plugins can call this function from the start lifecycle to
|
||||
* register a custom component in the Ingest Datasource configuration.
|
||||
*/
|
||||
export function registerDatasource(
|
||||
key: AllowedDatasourceKey,
|
||||
value: CustomConfigureDatasourceContent
|
||||
) {
|
||||
ConfigureDatasourceMapping[key] = value;
|
||||
}
|
||||
|
||||
const EmptyConfigureDatasource: CustomConfigureDatasourceContent = () => (
|
||||
<EuiEmptyPrompt
|
||||
iconType="checkInCircleFilled"
|
||||
iconColor="secondary"
|
||||
body={
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.ingestManager.createDatasource.stepConfigure.noConfigOptionsMessage"
|
||||
defaultMessage="Nothing to configure"
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
export const CustomConfigureDatasource = (props: CustomConfigureDatasourceProps) => {
|
||||
const ConfigureDatasourceContent =
|
||||
ConfigureDatasourceMapping[props.packageName] || EmptyConfigureDatasource;
|
||||
return <ConfigureDatasourceContent {...props} />;
|
||||
};
|
|
@ -6,3 +6,4 @@
|
|||
export { CreateDatasourcePageLayout } from './layout';
|
||||
export { DatasourceInputPanel } from './datasource_input_panel';
|
||||
export { DatasourceInputVarField } from './datasource_input_var_field';
|
||||
export { CustomConfigureDatasource } from './custom_configure_datasource';
|
||||
|
|
|
@ -5,28 +5,29 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import {
|
||||
EuiPanel,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiSpacer,
|
||||
EuiEmptyPrompt,
|
||||
EuiText,
|
||||
EuiCallOut,
|
||||
} from '@elastic/eui';
|
||||
import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiCallOut } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { PackageInfo, NewDatasource, DatasourceInput } from '../../../types';
|
||||
import { Loading } from '../../../components';
|
||||
import { DatasourceValidationResults, validationHasErrors } from './services';
|
||||
import { DatasourceInputPanel } from './components';
|
||||
import { DatasourceInputPanel, CustomConfigureDatasource } from './components';
|
||||
import { CreateDatasourceFrom } from './types';
|
||||
|
||||
export const StepConfigureDatasource: React.FunctionComponent<{
|
||||
from?: CreateDatasourceFrom;
|
||||
packageInfo: PackageInfo;
|
||||
datasource: NewDatasource;
|
||||
datasource: NewDatasource | (NewDatasource & { id: string });
|
||||
updateDatasource: (fields: Partial<NewDatasource>) => void;
|
||||
validationResults: DatasourceValidationResults;
|
||||
submitAttempted: boolean;
|
||||
}> = ({ packageInfo, datasource, updateDatasource, validationResults, submitAttempted }) => {
|
||||
}> = ({
|
||||
from = 'config',
|
||||
packageInfo,
|
||||
datasource,
|
||||
updateDatasource,
|
||||
validationResults,
|
||||
submitAttempted,
|
||||
}) => {
|
||||
const hasErrors = validationResults ? validationHasErrors(validationResults) : false;
|
||||
|
||||
// Configure inputs (and their streams)
|
||||
|
@ -68,19 +69,10 @@ export const StepConfigureDatasource: React.FunctionComponent<{
|
|||
</EuiFlexGroup>
|
||||
) : (
|
||||
<EuiPanel>
|
||||
<EuiEmptyPrompt
|
||||
iconType="checkInCircleFilled"
|
||||
iconColor="secondary"
|
||||
body={
|
||||
<EuiText>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.ingestManager.createDatasource.stepConfigure.noConfigOptionsMessage"
|
||||
defaultMessage="Nothing to configure"
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
}
|
||||
<CustomConfigureDatasource
|
||||
packageName={packageInfo.name}
|
||||
datasource={datasource}
|
||||
from={from}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
|
|
|
@ -69,7 +69,8 @@ export const EditDatasourcePage: React.FunctionComponent = () => {
|
|||
const [loadingError, setLoadingError] = useState<Error>();
|
||||
const [agentConfig, setAgentConfig] = useState<AgentConfig>();
|
||||
const [packageInfo, setPackageInfo] = useState<PackageInfo>();
|
||||
const [datasource, setDatasource] = useState<NewDatasource>({
|
||||
const [datasource, setDatasource] = useState<NewDatasource & { id: string }>({
|
||||
id: '',
|
||||
name: '',
|
||||
description: '',
|
||||
config_id: '',
|
||||
|
@ -93,7 +94,6 @@ export const EditDatasourcePage: React.FunctionComponent = () => {
|
|||
}
|
||||
if (datasourceData?.item) {
|
||||
const {
|
||||
id,
|
||||
revision,
|
||||
inputs,
|
||||
created_by,
|
||||
|
@ -299,6 +299,7 @@ export const EditDatasourcePage: React.FunctionComponent = () => {
|
|||
),
|
||||
children: (
|
||||
<StepConfigureDatasource
|
||||
from={'edit'}
|
||||
packageInfo={packageInfo}
|
||||
datasource={datasource}
|
||||
updateDatasource={updateDatasource}
|
||||
|
|
|
@ -11,3 +11,11 @@ export { IngestManagerStart } from './plugin';
|
|||
export const plugin = (initializerContext: PluginInitializerContext) => {
|
||||
return new IngestManagerPlugin(initializerContext);
|
||||
};
|
||||
|
||||
export {
|
||||
CustomConfigureDatasourceContent,
|
||||
CustomConfigureDatasourceProps,
|
||||
registerDatasource,
|
||||
} from './applications/ingest_manager/sections/agent_config/create_datasource_page/components/custom_configure_datasource';
|
||||
|
||||
export { NewDatasource } from './applications/ingest_manager/types';
|
||||
|
|
|
@ -18,6 +18,7 @@ import { PLUGIN_ID } from '../common/constants';
|
|||
|
||||
import { IngestManagerConfigType } from '../common/types';
|
||||
import { setupRouteService, appRoutesService } from '../common';
|
||||
import { registerDatasource } from './applications/ingest_manager/sections/agent_config/create_datasource_page/components/custom_configure_datasource';
|
||||
|
||||
export { IngestManagerConfigType } from '../common/types';
|
||||
|
||||
|
@ -26,6 +27,7 @@ export type IngestManagerSetup = void;
|
|||
* Describes public IngestManager plugin contract returned at the `start` stage.
|
||||
*/
|
||||
export interface IngestManagerStart {
|
||||
registerDatasource: typeof registerDatasource;
|
||||
success: boolean;
|
||||
error?: {
|
||||
message: string;
|
||||
|
@ -80,12 +82,16 @@ export class IngestManagerPlugin
|
|||
const permissionsResponse = await core.http.get(appRoutesService.getCheckPermissionsPath());
|
||||
if (permissionsResponse.success) {
|
||||
const { isInitialized: success } = await core.http.post(setupRouteService.getSetupPath());
|
||||
return { success };
|
||||
return { success, registerDatasource };
|
||||
} else {
|
||||
throw new Error(permissionsResponse.error);
|
||||
}
|
||||
} catch (error) {
|
||||
return { success: false, error: { message: error.body?.message || 'Unknown error' } };
|
||||
return {
|
||||
success: false,
|
||||
error: { message: error.body?.message || 'Unknown error' },
|
||||
registerDatasource,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { IngestManagerStart } from '../../../../../ingest_manager/public';
|
||||
import { IngestManagerStart, registerDatasource } from '../../../../../ingest_manager/public';
|
||||
import {
|
||||
dataPluginMock,
|
||||
Start as DataPublicStartMock,
|
||||
|
@ -56,6 +56,6 @@ export const depsStartMock: () => DepsStartMock = () => {
|
|||
|
||||
return {
|
||||
data: dataMock,
|
||||
ingestManager: { success: true },
|
||||
ingestManager: { success: true, registerDatasource },
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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 React, { memo } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { EuiEmptyPrompt, EuiText } from '@elastic/eui';
|
||||
import { useKibana } from '../../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { LinkToApp } from '../../../../../common/components/endpoint/link_to_app';
|
||||
import {
|
||||
CustomConfigureDatasourceContent,
|
||||
CustomConfigureDatasourceProps,
|
||||
NewDatasource,
|
||||
} from '../../../../../../../ingest_manager/public';
|
||||
import { getManagementUrl } from '../../../..';
|
||||
|
||||
type DatasourceWithId = NewDatasource & { id: string };
|
||||
|
||||
/**
|
||||
* Exports Endpoint-specific datasource configuration instructions
|
||||
* for use in the Ingest app create / edit datasource config
|
||||
*/
|
||||
export const ConfigureEndpointDatasource = memo<CustomConfigureDatasourceContent>(
|
||||
({
|
||||
from,
|
||||
datasource,
|
||||
}: {
|
||||
from: string;
|
||||
datasource: CustomConfigureDatasourceProps['datasource'];
|
||||
}) => {
|
||||
const { services } = useKibana();
|
||||
let policyUrl = '';
|
||||
if (from === 'edit') {
|
||||
policyUrl = getManagementUrl({
|
||||
name: 'policyDetails',
|
||||
policyId: (datasource as DatasourceWithId).id,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
body={
|
||||
<EuiText>
|
||||
<p>
|
||||
{from === 'edit' ? (
|
||||
<LinkToApp
|
||||
appId="siem"
|
||||
appPath={policyUrl}
|
||||
href={`${services.application.getUrlForApp('siem')}${policyUrl}`}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.siem.endpoint.ingestManager.editDatasource.stepConfigure"
|
||||
defaultMessage="View and configure Security Policy"
|
||||
/>
|
||||
</LinkToApp>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.siem.endpoint.ingestManager.createDatasource.stepConfigure"
|
||||
defaultMessage="The recommended Security Policy has been associated with this data source. The Security Policy can be edited in the Security application once your data source has been saved."
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
</EuiText>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
ConfigureEndpointDatasource.displayName = 'ConfigureEndpointDatasource';
|
|
@ -20,6 +20,7 @@ import { KibanaServices } from './common/lib/kibana/services';
|
|||
import { serviceNowActionType, jiraActionType } from './common/lib/connectors';
|
||||
import { PluginSetup, PluginStart, SetupPlugins, StartPlugins, StartServices } from './types';
|
||||
import { APP_ID, APP_NAME, APP_ICON, APP_PATH } from '../common/constants';
|
||||
import { ConfigureEndpointDatasource } from './management/pages/policy/view/ingest_manager_integration/configure_datasource';
|
||||
|
||||
export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, StartPlugins> {
|
||||
private kibanaVersion: string;
|
||||
|
@ -131,6 +132,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
|
|||
|
||||
public start(core: CoreStart, plugins: StartPlugins) {
|
||||
KibanaServices.init({ ...core, ...plugins, kibanaVersion: this.kibanaVersion });
|
||||
plugins.ingestManager.registerDatasource('endpoint', ConfigureEndpointDatasource);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue