mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[fleet][integrations] Provide Deployment Details on Cloud (#114287)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
eaf25d64e4
commit
6d5354a99d
21 changed files with 436 additions and 45 deletions
|
@ -240,6 +240,7 @@ readonly links: {
|
|||
upgradeElasticAgent: string;
|
||||
upgradeElasticAgent712lower: string;
|
||||
learnMoreBlog: string;
|
||||
apiKeysLearnMore: string;
|
||||
}>;
|
||||
readonly ecs: {
|
||||
readonly guide: string;
|
||||
|
|
|
@ -482,6 +482,7 @@ export class DocLinksService {
|
|||
upgradeElasticAgent: `${FLEET_DOCS}upgrade-elastic-agent.html`,
|
||||
upgradeElasticAgent712lower: `${FLEET_DOCS}upgrade-elastic-agent.html#upgrade-7.12-lower`,
|
||||
learnMoreBlog: `${ELASTIC_WEBSITE_URL}blog/elastic-agent-and-fleet-make-it-easier-to-integrate-your-systems-with-elastic`,
|
||||
apiKeysLearnMore: `${KIBANA_DOCS}api-keys.html`,
|
||||
},
|
||||
ecs: {
|
||||
guide: `${ELASTIC_WEBSITE_URL}guide/en/ecs/current/index.html`,
|
||||
|
@ -741,6 +742,7 @@ export interface DocLinksStart {
|
|||
upgradeElasticAgent: string;
|
||||
upgradeElasticAgent712lower: string;
|
||||
learnMoreBlog: string;
|
||||
apiKeysLearnMore: string;
|
||||
}>;
|
||||
readonly ecs: {
|
||||
readonly guide: string;
|
||||
|
|
|
@ -709,6 +709,7 @@ export interface DocLinksStart {
|
|||
upgradeElasticAgent: string;
|
||||
upgradeElasticAgent712lower: string;
|
||||
learnMoreBlog: string;
|
||||
apiKeysLearnMore: string;
|
||||
}>;
|
||||
readonly ecs: {
|
||||
readonly guide: string;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"server": true,
|
||||
"ui": true,
|
||||
"configPath": ["xpack", "fleet"],
|
||||
"requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation", "customIntegrations"],
|
||||
"requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation", "customIntegrations", "share"],
|
||||
"optionalPlugins": ["security", "features", "cloud", "usageCollection", "home", "globalSearch"],
|
||||
"extraPublicDirs": ["common"],
|
||||
"requiredBundles": ["kibanaReact", "esUiShared", "home", "infra", "kibanaUtils"]
|
||||
|
|
|
@ -40,6 +40,7 @@ import { EPMApp } from './sections/epm';
|
|||
import { DefaultLayout } from './layouts';
|
||||
import { PackageInstallProvider } from './hooks';
|
||||
import { useBreadcrumbs, UIExtensionsContext } from './hooks';
|
||||
import { IntegrationsHeader } from './components/header';
|
||||
|
||||
const ErrorLayout = ({ children }: { children: JSX.Element }) => (
|
||||
<EuiErrorBoundary>
|
||||
|
@ -127,41 +128,53 @@ export const IntegrationsAppContext: React.FC<{
|
|||
history: AppMountParameters['history'];
|
||||
kibanaVersion: string;
|
||||
extensions: UIExtensionsStorage;
|
||||
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
|
||||
/** For testing purposes only */
|
||||
routerHistory?: History<any>; // TODO remove
|
||||
}> = memo(({ children, startServices, config, history, kibanaVersion, extensions }) => {
|
||||
const isDarkMode = useObservable<boolean>(startServices.uiSettings.get$('theme:darkMode'));
|
||||
}> = memo(
|
||||
({
|
||||
children,
|
||||
startServices,
|
||||
config,
|
||||
history,
|
||||
kibanaVersion,
|
||||
extensions,
|
||||
setHeaderActionMenu,
|
||||
}) => {
|
||||
const isDarkMode = useObservable<boolean>(startServices.uiSettings.get$('theme:darkMode'));
|
||||
|
||||
return (
|
||||
<RedirectAppLinks application={startServices.application}>
|
||||
<startServices.i18n.Context>
|
||||
<KibanaContextProvider services={{ ...startServices }}>
|
||||
<EuiErrorBoundary>
|
||||
<ConfigContext.Provider value={config}>
|
||||
<KibanaVersionContext.Provider value={kibanaVersion}>
|
||||
<EuiThemeProvider darkMode={isDarkMode}>
|
||||
<UIExtensionsContext.Provider value={extensions}>
|
||||
<FleetStatusProvider>
|
||||
<startServices.customIntegrations.ContextProvider>
|
||||
<Router history={history}>
|
||||
<AgentPolicyContextProvider>
|
||||
<PackageInstallProvider notifications={startServices.notifications}>
|
||||
{children}
|
||||
</PackageInstallProvider>
|
||||
</AgentPolicyContextProvider>
|
||||
</Router>
|
||||
</startServices.customIntegrations.ContextProvider>
|
||||
</FleetStatusProvider>
|
||||
</UIExtensionsContext.Provider>
|
||||
</EuiThemeProvider>
|
||||
</KibanaVersionContext.Provider>
|
||||
</ConfigContext.Provider>
|
||||
</EuiErrorBoundary>
|
||||
</KibanaContextProvider>
|
||||
</startServices.i18n.Context>
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<RedirectAppLinks application={startServices.application}>
|
||||
<startServices.i18n.Context>
|
||||
<KibanaContextProvider services={{ ...startServices }}>
|
||||
<EuiErrorBoundary>
|
||||
<ConfigContext.Provider value={config}>
|
||||
<KibanaVersionContext.Provider value={kibanaVersion}>
|
||||
<EuiThemeProvider darkMode={isDarkMode}>
|
||||
<UIExtensionsContext.Provider value={extensions}>
|
||||
<FleetStatusProvider>
|
||||
<startServices.customIntegrations.ContextProvider>
|
||||
<Router history={history}>
|
||||
<AgentPolicyContextProvider>
|
||||
<PackageInstallProvider notifications={startServices.notifications}>
|
||||
<IntegrationsHeader {...{ setHeaderActionMenu }} />
|
||||
{children}
|
||||
</PackageInstallProvider>
|
||||
</AgentPolicyContextProvider>
|
||||
</Router>
|
||||
</startServices.customIntegrations.ContextProvider>
|
||||
</FleetStatusProvider>
|
||||
</UIExtensionsContext.Provider>
|
||||
</EuiThemeProvider>
|
||||
</KibanaVersionContext.Provider>
|
||||
</ConfigContext.Provider>
|
||||
</EuiErrorBoundary>
|
||||
</KibanaContextProvider>
|
||||
</startServices.i18n.Context>
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const AppRoutes = memo(() => {
|
||||
const { modal, setModal } = useUrlModal();
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
EuiPopover,
|
||||
EuiText,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiFieldText,
|
||||
EuiCopy,
|
||||
EuiButtonIcon,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiButton,
|
||||
EuiLink,
|
||||
EuiHeaderLink,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export interface Props {
|
||||
endpointUrl: string;
|
||||
cloudId: string;
|
||||
managementUrl?: string;
|
||||
learnMoreUrl: string;
|
||||
}
|
||||
|
||||
const Description = styled(EuiText)`
|
||||
margin-bottom: ${({ theme }) => theme.eui.euiSizeL};
|
||||
`;
|
||||
|
||||
export const DeploymentDetails = ({ endpointUrl, cloudId, learnMoreUrl, managementUrl }: Props) => {
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
|
||||
const button = (
|
||||
<EuiHeaderLink onClick={() => setIsOpen(!isOpen)} iconType="iInCircle" iconSide="left" isActive>
|
||||
{i18n.translate('xpack.fleet.integrations.deploymentButton', {
|
||||
defaultMessage: 'View deployment details',
|
||||
})}
|
||||
</EuiHeaderLink>
|
||||
);
|
||||
|
||||
const management = managementUrl ? (
|
||||
<EuiFormRow label="API keys" fullWidth>
|
||||
<EuiFlexGroup gutterSize="m" alignItems="center">
|
||||
<EuiFlexItem>
|
||||
<EuiButton href={managementUrl}>Create and manage API keys</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiLink external href={learnMoreUrl} target="_blank">
|
||||
Learn more
|
||||
</EuiLink>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
isOpen={isOpen}
|
||||
closePopover={() => setIsOpen(false)}
|
||||
button={button}
|
||||
anchorPosition="downCenter"
|
||||
>
|
||||
<div style={{ width: 450 }}>
|
||||
<Description>
|
||||
Send data to Elastic from your applications by referencing your deployment and
|
||||
Elasticsearch information.
|
||||
</Description>
|
||||
<EuiForm component="div">
|
||||
<EuiFormRow label="Elasticsearch endpoint" fullWidth isDisabled>
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
<EuiFlexItem>
|
||||
<EuiFieldText value={endpointUrl} fullWidth disabled />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiCopy textToCopy={endpointUrl}>
|
||||
{(copy) => (
|
||||
<EuiButtonIcon
|
||||
onClick={copy}
|
||||
iconType="copyClipboard"
|
||||
display="base"
|
||||
size="m"
|
||||
/>
|
||||
)}
|
||||
</EuiCopy>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow label="Cloud ID" fullWidth>
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
<EuiFlexItem>
|
||||
<EuiFieldText value={cloudId} fullWidth disabled />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiCopy textToCopy={cloudId}>
|
||||
{(copy) => (
|
||||
<EuiButtonIcon
|
||||
onClick={copy}
|
||||
iconType="copyClipboard"
|
||||
display="base"
|
||||
size="m"
|
||||
/>
|
||||
)}
|
||||
</EuiCopy>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
{management}
|
||||
</EuiForm>
|
||||
</div>
|
||||
</EuiPopover>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import type { Meta } from '@storybook/react';
|
||||
import { EuiHeader } from '@elastic/eui';
|
||||
|
||||
import { DeploymentDetails as ConnectedComponent } from './deployment_details';
|
||||
import type { Props as PureComponentProps } from './deployment_details.component';
|
||||
import { DeploymentDetails as PureComponent } from './deployment_details.component';
|
||||
|
||||
export default {
|
||||
title: 'Sections/EPM/Deployment Details',
|
||||
description: '',
|
||||
decorators: [
|
||||
(storyFn) => {
|
||||
const sections = [{ items: [] }, { items: [storyFn()] }];
|
||||
return <EuiHeader sections={sections} />;
|
||||
},
|
||||
],
|
||||
} as Meta;
|
||||
|
||||
export const DeploymentDetails = () => {
|
||||
return <ConnectedComponent />;
|
||||
};
|
||||
|
||||
DeploymentDetails.args = {
|
||||
isCloudEnabled: true,
|
||||
};
|
||||
|
||||
DeploymentDetails.argTypes = {
|
||||
isCloudEnabled: {
|
||||
type: {
|
||||
name: 'boolean',
|
||||
},
|
||||
defaultValue: true,
|
||||
control: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Component = (props: PureComponentProps) => {
|
||||
return <PureComponent {...props} />;
|
||||
};
|
||||
|
||||
Component.args = {
|
||||
cloudId: 'cloud-id',
|
||||
endpointUrl: 'https://endpoint-url',
|
||||
learnMoreUrl: 'https://learn-more-url',
|
||||
managementUrl: 'https://management-url',
|
||||
};
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
|
||||
import { useStartServices } from '../../hooks';
|
||||
|
||||
import { DeploymentDetails as Component } from './deployment_details.component';
|
||||
|
||||
export const DeploymentDetails = () => {
|
||||
const { share, cloud, docLinks } = useStartServices();
|
||||
|
||||
// If the cloud plugin isn't enabled, we can't display the flyout.
|
||||
if (!cloud) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { isCloudEnabled, cloudId, cname } = cloud;
|
||||
|
||||
// If cloud isn't enabled, we don't have a cloudId or a cname, we can't display the flyout.
|
||||
if (!isCloudEnabled || !cloudId || !cname) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the cname doesn't start with a known prefix, we can't display the flyout.
|
||||
// TODO: dover - this is a short term solution, see https://github.com/elastic/kibana/pull/114287#issuecomment-940111026
|
||||
if (
|
||||
!(
|
||||
cname.endsWith('elastic-cloud.com') ||
|
||||
cname.endsWith('found.io') ||
|
||||
cname.endsWith('found.no')
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cnameNormalized = cname.startsWith('.') ? cname.substring(1) : cname;
|
||||
const endpointUrl = `https://${cloudId}.${cnameNormalized}`;
|
||||
|
||||
const managementUrl = share.url.locators
|
||||
.get('MANAGEMENT_APP_LOCATOR')
|
||||
?.useUrl({ sectionId: 'security', appId: 'api_keys' });
|
||||
|
||||
const learnMoreUrl = docLinks.links.fleet.apiKeysLearnMore;
|
||||
|
||||
return <Component {...{ cloudId, endpointUrl, managementUrl, learnMoreUrl }} />;
|
||||
};
|
|
@ -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 React from 'react';
|
||||
import { EuiHeaderSectionItem, EuiHeaderSection, EuiHeaderLinks } from '@elastic/eui';
|
||||
|
||||
import type { AppMountParameters } from 'kibana/public';
|
||||
|
||||
import { HeaderPortal } from './header_portal';
|
||||
import { DeploymentDetails } from './deployment_details';
|
||||
|
||||
export const IntegrationsHeader = ({
|
||||
setHeaderActionMenu,
|
||||
}: {
|
||||
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
|
||||
}) => {
|
||||
return (
|
||||
<HeaderPortal {...{ setHeaderActionMenu }}>
|
||||
<EuiHeaderSection grow={false}>
|
||||
<EuiHeaderSectionItem>
|
||||
<EuiHeaderLinks>
|
||||
<DeploymentDetails />
|
||||
</EuiHeaderLinks>
|
||||
</EuiHeaderSectionItem>
|
||||
</EuiHeaderSection>
|
||||
</HeaderPortal>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 type { AppMountParameters } from 'kibana/public';
|
||||
import type { FC } from 'react';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { createPortalNode, InPortal, OutPortal } from 'react-reverse-portal';
|
||||
|
||||
import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
|
||||
export interface Props {
|
||||
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
|
||||
}
|
||||
|
||||
export const HeaderPortal: FC<Props> = ({ children, setHeaderActionMenu }) => {
|
||||
const portalNode = useMemo(() => createPortalNode(), []);
|
||||
|
||||
useEffect(() => {
|
||||
setHeaderActionMenu((element) => {
|
||||
const mount = toMountPoint(<OutPortal node={portalNode} />);
|
||||
return mount(element);
|
||||
});
|
||||
|
||||
return () => {
|
||||
portalNode.unmount();
|
||||
setHeaderActionMenu(undefined);
|
||||
};
|
||||
}, [portalNode, setHeaderActionMenu]);
|
||||
|
||||
return <InPortal node={portalNode}>{children}</InPortal>;
|
||||
};
|
|
@ -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 { IntegrationsHeader } from './header';
|
|
@ -37,6 +37,7 @@ interface IntegrationsAppProps {
|
|||
history: AppMountParameters['history'];
|
||||
kibanaVersion: string;
|
||||
extensions: UIExtensionsStorage;
|
||||
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
|
||||
}
|
||||
const IntegrationsApp = ({
|
||||
basepath,
|
||||
|
@ -45,6 +46,7 @@ const IntegrationsApp = ({
|
|||
history,
|
||||
kibanaVersion,
|
||||
extensions,
|
||||
setHeaderActionMenu,
|
||||
}: IntegrationsAppProps) => {
|
||||
return (
|
||||
<IntegrationsAppContext
|
||||
|
@ -54,6 +56,7 @@ const IntegrationsApp = ({
|
|||
history={history}
|
||||
kibanaVersion={kibanaVersion}
|
||||
extensions={extensions}
|
||||
setHeaderActionMenu={setHeaderActionMenu}
|
||||
>
|
||||
<WithPermissionsAndSetup>
|
||||
<AppRoutes />
|
||||
|
@ -64,7 +67,7 @@ const IntegrationsApp = ({
|
|||
|
||||
export function renderApp(
|
||||
startServices: FleetStartServices,
|
||||
{ element, appBasePath, history }: AppMountParameters,
|
||||
{ element, appBasePath, history, setHeaderActionMenu }: AppMountParameters,
|
||||
config: FleetConfigType,
|
||||
kibanaVersion: string,
|
||||
extensions: UIExtensionsStorage
|
||||
|
@ -77,6 +80,7 @@ export function renderApp(
|
|||
history={history}
|
||||
kibanaVersion={kibanaVersion}
|
||||
extensions={extensions}
|
||||
setHeaderActionMenu={setHeaderActionMenu}
|
||||
/>,
|
||||
element
|
||||
);
|
||||
|
|
|
@ -41,6 +41,7 @@ export interface TestRenderer {
|
|||
kibanaVersion: string;
|
||||
AppWrapper: React.FC<any>;
|
||||
render: UiRender;
|
||||
setHeaderActionMenu: Function;
|
||||
}
|
||||
|
||||
export const createFleetTestRendererMock = (): TestRenderer => {
|
||||
|
@ -55,6 +56,7 @@ export const createFleetTestRendererMock = (): TestRenderer => {
|
|||
config: createConfigurationMock(),
|
||||
startInterface: createStartMock(extensions),
|
||||
kibanaVersion: '8.0.0',
|
||||
setHeaderActionMenu: jest.fn(),
|
||||
AppWrapper: memo(({ children }) => {
|
||||
return (
|
||||
<FleetAppContext
|
||||
|
@ -96,6 +98,7 @@ export const createIntegrationsTestRendererMock = (): TestRenderer => {
|
|||
config: createConfigurationMock(),
|
||||
startInterface: createStartMock(extensions),
|
||||
kibanaVersion: '8.0.0',
|
||||
setHeaderActionMenu: jest.fn(),
|
||||
AppWrapper: memo(({ children }) => {
|
||||
return (
|
||||
<IntegrationsAppContext
|
||||
|
@ -106,6 +109,7 @@ export const createIntegrationsTestRendererMock = (): TestRenderer => {
|
|||
kibanaVersion={testRendererMocks.kibanaVersion}
|
||||
extensions={extensions}
|
||||
routerHistory={testRendererMocks.history}
|
||||
setHeaderActionMenu={() => {}}
|
||||
>
|
||||
{children}
|
||||
</IntegrationsAppContext>
|
||||
|
|
|
@ -10,6 +10,7 @@ import { licensingMock } from '../../../licensing/public/mocks';
|
|||
import { homePluginMock } from '../../../../../src/plugins/home/public/mocks';
|
||||
import { navigationPluginMock } from '../../../../../src/plugins/navigation/public/mocks';
|
||||
import { customIntegrationsMock } from '../../../../../src/plugins/custom_integrations/public/mocks';
|
||||
import { sharePluginMock } from '../../../../../src/plugins/share/public/mocks';
|
||||
|
||||
import type { MockedFleetSetupDeps, MockedFleetStartDeps } from './types';
|
||||
|
||||
|
@ -27,5 +28,6 @@ export const createStartDepsMock = (): MockedFleetStartDeps => {
|
|||
data: dataPluginMock.createStartContract(),
|
||||
navigation: navigationPluginMock.createStartContract(),
|
||||
customIntegrations: customIntegrationsMock.createStart(),
|
||||
share: sharePluginMock.createStartContract(),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -21,6 +21,8 @@ import type {
|
|||
CustomIntegrationsSetup,
|
||||
} from 'src/plugins/custom_integrations/public';
|
||||
|
||||
import type { SharePluginStart } from 'src/plugins/share/public';
|
||||
|
||||
import { DEFAULT_APP_CATEGORIES, AppNavLinkStatus } from '../../../../src/core/public';
|
||||
|
||||
import type {
|
||||
|
@ -81,10 +83,12 @@ export interface FleetStartDeps {
|
|||
data: DataPublicPluginStart;
|
||||
navigation: NavigationPublicPluginStart;
|
||||
customIntegrations: CustomIntegrationsStart;
|
||||
share: SharePluginStart;
|
||||
}
|
||||
|
||||
export interface FleetStartServices extends CoreStart, FleetStartDeps {
|
||||
storage: Storage;
|
||||
share: SharePluginStart;
|
||||
cloud?: CloudSetup;
|
||||
}
|
||||
|
||||
|
@ -134,6 +138,7 @@ export class FleetPlugin implements Plugin<FleetSetup, FleetStart, FleetSetupDep
|
|||
...coreStartServices,
|
||||
...startDepsServices,
|
||||
storage: this.storage,
|
||||
cloud: deps.cloud,
|
||||
};
|
||||
const { renderApp, teardownIntegrations } = await import('./applications/integrations');
|
||||
const unmount = renderApp(startServices, params, config, kibanaVersion, extensions);
|
||||
|
|
23
x-pack/plugins/fleet/storybook/context/cloud.ts
Normal file
23
x-pack/plugins/fleet/storybook/context/cloud.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 type { CloudSetup } from '../../../cloud/public';
|
||||
|
||||
export const getCloud = ({ isCloudEnabled }: { isCloudEnabled: boolean }) => {
|
||||
const cloud: CloudSetup = {
|
||||
isCloudEnabled,
|
||||
baseUrl: 'https://base.url',
|
||||
cloudId: 'cloud-id',
|
||||
cname: 'found.io',
|
||||
deploymentUrl: 'https://deployment.url',
|
||||
organizationUrl: 'https://organization.url',
|
||||
profileUrl: 'https://profile.url',
|
||||
snapshotsUrl: 'https://snapshots.url',
|
||||
};
|
||||
|
||||
return cloud;
|
||||
};
|
|
@ -15,6 +15,7 @@ export const getDocLinks = () => {
|
|||
fleet: {
|
||||
learnMoreBlog:
|
||||
'https://www.elastic.co/blog/elastic-agent-and-fleet-make-it-easier-to-integrate-your-systems-with-elastic',
|
||||
apiKeysLearnMore: 'https://www.elastic.co/guide/en/kibana/master/api-keys.html',
|
||||
},
|
||||
},
|
||||
} as unknown as DocLinksStart;
|
||||
|
|
|
@ -28,6 +28,8 @@ import { getUiSettings } from './ui_settings';
|
|||
import { getNotifications } from './notifications';
|
||||
import { stubbedStartServices } from './stubs';
|
||||
import { getDocLinks } from './doc_links';
|
||||
import { getCloud } from './cloud';
|
||||
import { getShare } from './share';
|
||||
|
||||
// TODO: clintandrewhall - this is not ideal, or complete. The root context of Fleet applications
|
||||
// requires full start contracts of its dependencies. As a result, we have to mock all of those contracts
|
||||
|
@ -36,6 +38,7 @@ import { getDocLinks } from './doc_links';
|
|||
//
|
||||
// Expect this to grow as components that are given Stories need access to mocked services.
|
||||
export const StorybookContext: React.FC<{ storyContext?: StoryContext }> = ({
|
||||
storyContext,
|
||||
children: storyChildren,
|
||||
}) => {
|
||||
const basepath = '';
|
||||
|
@ -46,10 +49,12 @@ export const StorybookContext: React.FC<{ storyContext?: StoryContext }> = ({
|
|||
...stubbedStartServices,
|
||||
application: getApplication(),
|
||||
chrome: getChrome(),
|
||||
cloud: getCloud({ isCloudEnabled: storyContext?.args.isCloudEnabled }),
|
||||
customIntegrations: {
|
||||
ContextProvider: getStorybookContextProvider(),
|
||||
},
|
||||
docLinks: getDocLinks(),
|
||||
http: getHttp(),
|
||||
notifications: getNotifications(),
|
||||
uiSettings: getUiSettings(),
|
||||
i18n: {
|
||||
Context: function I18nContext({ children }) {
|
||||
return <I18nProvider>{children}</I18nProvider>;
|
||||
|
@ -58,9 +63,9 @@ export const StorybookContext: React.FC<{ storyContext?: StoryContext }> = ({
|
|||
injectedMetadata: {
|
||||
getInjectedVar: () => null,
|
||||
},
|
||||
customIntegrations: {
|
||||
ContextProvider: getStorybookContextProvider(),
|
||||
},
|
||||
notifications: getNotifications(),
|
||||
share: getShare(),
|
||||
uiSettings: getUiSettings(),
|
||||
};
|
||||
|
||||
setHttpClient(startServices.http);
|
||||
|
@ -81,12 +86,20 @@ export const StorybookContext: React.FC<{ storyContext?: StoryContext }> = ({
|
|||
} as unknown as FleetConfigType;
|
||||
|
||||
const extensions = {};
|
||||
|
||||
const kibanaVersion = '1.2.3';
|
||||
const setHeaderActionMenu = () => {};
|
||||
|
||||
return (
|
||||
<IntegrationsAppContext
|
||||
{...{ kibanaVersion, basepath, config, history, startServices, extensions }}
|
||||
{...{
|
||||
kibanaVersion,
|
||||
basepath,
|
||||
config,
|
||||
history,
|
||||
startServices,
|
||||
extensions,
|
||||
setHeaderActionMenu,
|
||||
}}
|
||||
>
|
||||
{storyChildren}
|
||||
</IntegrationsAppContext>
|
||||
|
|
22
x-pack/plugins/fleet/storybook/context/share.ts
Normal file
22
x-pack/plugins/fleet/storybook/context/share.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 type { SharePluginStart } from 'src/plugins/share/public';
|
||||
|
||||
export const getShare = () => {
|
||||
const share: SharePluginStart = {
|
||||
url: {
|
||||
locators: {
|
||||
get: () => ({
|
||||
useUrl: () => 'https://locator.url',
|
||||
}),
|
||||
},
|
||||
},
|
||||
} as unknown as SharePluginStart;
|
||||
|
||||
return share;
|
||||
};
|
|
@ -14,8 +14,7 @@ type Stubs =
|
|||
| 'fatalErrors'
|
||||
| 'navigation'
|
||||
| 'overlays'
|
||||
| 'savedObjects'
|
||||
| 'cloud';
|
||||
| 'savedObjects';
|
||||
|
||||
type StubbedStartServices = Pick<FleetStartServices, Stubs>;
|
||||
|
||||
|
@ -27,5 +26,4 @@ export const stubbedStartServices: StubbedStartServices = {
|
|||
navigation: {} as FleetStartServices['navigation'],
|
||||
overlays: {} as FleetStartServices['overlays'],
|
||||
savedObjects: {} as FleetStartServices['savedObjects'],
|
||||
cloud: {} as FleetStartServices['cloud'],
|
||||
};
|
||||
|
|
|
@ -11,5 +11,5 @@ import type { DecoratorFn } from '@storybook/react';
|
|||
import { StorybookContext } from './context';
|
||||
|
||||
export const decorator: DecoratorFn = (story, storybook) => {
|
||||
return <StorybookContext>{story()}</StorybookContext>;
|
||||
return <StorybookContext storyContext={storybook}>{story()}</StorybookContext>;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue