mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
chore: onboarding integration flow initial setup
This commit is contained in:
parent
6a84cccbde
commit
637369f012
29 changed files with 726 additions and 223 deletions
|
@ -28,11 +28,15 @@ interface UseCancelParams {
|
|||
}
|
||||
|
||||
export const useCancelAddPackagePolicy = (params: UseCancelParams) => {
|
||||
const { from, pkgkey, agentPolicyId } = params;
|
||||
const { from, pkgkey: pkgkeyParam, agentPolicyId } = params;
|
||||
const {
|
||||
application: { navigateToApp },
|
||||
} = useStartServices();
|
||||
const routeState = useIntraAppState<CreatePackagePolicyRouteState>();
|
||||
const pkgkey = useMemo(
|
||||
() => pkgkeyParam || routeState?.pkgkey,
|
||||
[pkgkeyParam, routeState?.pkgkey]
|
||||
);
|
||||
const { getHref } = useLink();
|
||||
|
||||
const cancelClickHandler = useCallback(
|
||||
|
|
|
@ -15,11 +15,28 @@ import type { AddToPolicyParams, EditPackagePolicyFrom } from './types';
|
|||
import { CreatePackagePolicySinglePage } from './single_page_layout';
|
||||
import { CreatePackagePolicyMultiPage } from './multi_page_layout';
|
||||
|
||||
export const CreatePackagePolicyPage: React.FC<{}> = () => {
|
||||
export const CreatePackagePolicyPage: React.FC<{
|
||||
useMultiPageLayoutProp?: boolean;
|
||||
originFrom?: EditPackagePolicyFrom;
|
||||
propPolicyId?: string;
|
||||
integrationName?: string;
|
||||
setIntegrationStep?: (step: number) => void;
|
||||
onCanceled?: () => void;
|
||||
}> = ({
|
||||
useMultiPageLayoutProp,
|
||||
originFrom,
|
||||
propPolicyId,
|
||||
integrationName,
|
||||
setIntegrationStep,
|
||||
onCanceled,
|
||||
}) => {
|
||||
const { search } = useLocation();
|
||||
const { params } = useRouteMatch<AddToPolicyParams>();
|
||||
const queryParams = useMemo(() => new URLSearchParams(search), [search]);
|
||||
const useMultiPageLayout = useMemo(() => queryParams.has('useMultiPageLayout'), [queryParams]);
|
||||
const useMultiPageLayout = useMemo(
|
||||
() => useMultiPageLayoutProp ?? queryParams.has('useMultiPageLayout'),
|
||||
[queryParams, useMultiPageLayoutProp]
|
||||
);
|
||||
const queryParamsPolicyId = useMemo(
|
||||
() => queryParams.get('policyId') ?? undefined,
|
||||
[queryParams]
|
||||
|
@ -47,12 +64,16 @@ export const CreatePackagePolicyPage: React.FC<{}> = () => {
|
|||
* creation possible if a user has not chosen one from the packages UI.
|
||||
*/
|
||||
const from: EditPackagePolicyFrom =
|
||||
'policyId' in params || queryParamsPolicyId ? 'policy' : 'package';
|
||||
originFrom ?? ('policyId' in params || queryParamsPolicyId ? 'policy' : 'package');
|
||||
|
||||
const pageParams = {
|
||||
from,
|
||||
queryParamsPolicyId,
|
||||
propPolicyId,
|
||||
integrationName,
|
||||
prerelease,
|
||||
setIntegrationStep,
|
||||
onCanceled,
|
||||
};
|
||||
|
||||
if (useMultiPageLayout) {
|
||||
|
|
|
@ -64,7 +64,6 @@ export const MultiPageStepsLayout: React.FunctionComponent<MultiPageStepLayoutPr
|
|||
restrictWidth={maxWidth}
|
||||
>
|
||||
<StepComponent {...props} />
|
||||
|
||||
{packageInfo && (
|
||||
<IntegrationBreadcrumb
|
||||
pkgTitle={integrationInfo?.title || packageInfo.title}
|
||||
|
|
|
@ -103,6 +103,9 @@ export const InstallElasticAgentManagedPageStep: React.FC<InstallAgentPageProps>
|
|||
agentCount: enrolledAgentIds.length,
|
||||
showLoading: true,
|
||||
poll: commandCopied,
|
||||
onClickViewAgents: () => {
|
||||
onNext(); // Fixme: Wording does not match what it does.
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useEffect, useState, useMemo } from 'react';
|
||||
import { useEffect, useState, useMemo, useRef } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { generateNewAgentPolicyWithDefaults } from '../../../../../../../../common/services/generate_new_agent_policy';
|
||||
|
||||
|
|
|
@ -18,6 +18,12 @@ import {
|
|||
|
||||
import type { AddToPolicyParams, CreatePackagePolicyParams } from '../types';
|
||||
|
||||
import { useIntegrationsStateContext } from '../../../../../integrations/hooks';
|
||||
|
||||
import { CreatePackagePolicySinglePage } from '../single_page_layout';
|
||||
|
||||
import type { AgentPolicy } from '../../../../types';
|
||||
|
||||
import { useGetAgentPolicyOrDefault } from './hooks';
|
||||
|
||||
import {
|
||||
|
@ -42,6 +48,13 @@ const addIntegrationStep = {
|
|||
component: AddIntegrationPageStep,
|
||||
};
|
||||
|
||||
const addIntegrationSingleLayoutStep = {
|
||||
title: i18n.translate('xpack.fleet.createFirstPackagePolicy.addIntegrationStepTitle', {
|
||||
defaultMessage: 'Add the integration',
|
||||
}),
|
||||
component: CreatePackagePolicySinglePage,
|
||||
};
|
||||
|
||||
const confirmDataStep = {
|
||||
title: i18n.translate('xpack.fleet.createFirstPackagePolicy.confirmDataStepTitle', {
|
||||
defaultMessage: 'Confirm incoming data',
|
||||
|
@ -53,23 +66,35 @@ const fleetManagedSteps = [installAgentStep, addIntegrationStep, confirmDataStep
|
|||
|
||||
const standaloneSteps = [addIntegrationStep, installAgentStep, confirmDataStep];
|
||||
|
||||
const onboardingSteps = [addIntegrationSingleLayoutStep, installAgentStep, confirmDataStep];
|
||||
|
||||
export const CreatePackagePolicyMultiPage: CreatePackagePolicyParams = ({
|
||||
queryParamsPolicyId,
|
||||
prerelease,
|
||||
from,
|
||||
integrationName,
|
||||
setIntegrationStep,
|
||||
onCanceled,
|
||||
}) => {
|
||||
const { params } = useRouteMatch<AddToPolicyParams>();
|
||||
const { pkgkey, policyId, integration } = params;
|
||||
// fixme
|
||||
const { pkgkey: pkgkeyParam, policyId, integration: integrationParam } = params;
|
||||
const { pkgkey: pkgKeyContext } = useIntegrationsStateContext();
|
||||
const pkgkey = pkgkeyParam || pkgKeyContext;
|
||||
const { pkgName, pkgVersion } = splitPkgKey(pkgkey);
|
||||
const [onSplash, setOnSplash] = useState(true);
|
||||
const [onSplash, setOnSplash] = useState(from !== 'onboarding-integration');
|
||||
const [currentStep, setCurrentStep] = useState(0);
|
||||
const [isManaged, setIsManaged] = useState(true);
|
||||
const { getHref } = useLink();
|
||||
const [enrolledAgentIds, setEnrolledAgentIds] = useState<string[]>([]);
|
||||
const [selectedAgentPolicies, setSelectedAgentPolicies] = useState<AgentPolicy[]>();
|
||||
const toggleIsManaged = (newIsManaged: boolean) => {
|
||||
setIsManaged(newIsManaged);
|
||||
setCurrentStep(0);
|
||||
};
|
||||
const agentPolicyId = policyId || queryParamsPolicyId;
|
||||
|
||||
const integration = integrationName || integrationParam;
|
||||
const agentPolicyId = selectedAgentPolicies?.[0]?.id || policyId || queryParamsPolicyId;
|
||||
const {
|
||||
data: packageInfoData,
|
||||
error: packageInfoError,
|
||||
|
@ -119,13 +144,23 @@ export const CreatePackagePolicyMultiPage: CreatePackagePolicyParams = ({
|
|||
);
|
||||
}
|
||||
|
||||
const steps = isManaged ? fleetManagedSteps : standaloneSteps;
|
||||
const stepsNext = () => {
|
||||
const steps =
|
||||
from === 'onboarding-integration'
|
||||
? onboardingSteps
|
||||
: isManaged
|
||||
? fleetManagedSteps
|
||||
: standaloneSteps;
|
||||
|
||||
const stepsNext = (props?: { selectedAgentPolicies: AgentPolicy[] }) => {
|
||||
if (currentStep === steps.length - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentStep(currentStep + 1);
|
||||
setIntegrationStep(currentStep + 1);
|
||||
if (props?.selectedAgentPolicies) {
|
||||
setSelectedAgentPolicies(props?.selectedAgentPolicies);
|
||||
}
|
||||
};
|
||||
|
||||
const stepsBack = () => {
|
||||
|
@ -154,6 +189,7 @@ export const CreatePackagePolicyMultiPage: CreatePackagePolicyParams = ({
|
|||
setIsManaged={toggleIsManaged}
|
||||
setEnrolledAgentIds={setEnrolledAgentIds}
|
||||
enrolledAgentIds={enrolledAgentIds}
|
||||
onCanceled={onCanceled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -76,6 +76,8 @@ import { generateNewAgentPolicyWithDefaults } from '../../../../../../../common/
|
|||
|
||||
import { packageHasAtLeastOneSecret } from '../utils';
|
||||
|
||||
import { useIntegrationsStateContext } from '../../../../../integrations/hooks';
|
||||
|
||||
import { CreatePackagePolicySinglePageLayout, PostInstallAddAgentModal } from './components';
|
||||
import { useDevToolsRequest, useOnSubmit, useSetupTechnology } from './hooks';
|
||||
import { PostInstallCloudFormationModal } from './components/cloud_security_posture/post_install_cloud_formation_modal';
|
||||
|
@ -105,12 +107,17 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
from,
|
||||
queryParamsPolicyId,
|
||||
prerelease,
|
||||
onNext,
|
||||
onCanceled,
|
||||
}) => {
|
||||
const {
|
||||
agents: { enabled: isFleetEnabled },
|
||||
} = useConfig();
|
||||
const hasFleetAddAgentsPrivileges = useAuthz().fleet.addAgents;
|
||||
const { params } = useRouteMatch<AddToPolicyParams>();
|
||||
const { pkgkey: pkgKeyContext } = useIntegrationsStateContext();
|
||||
const pkgkey = params.pkgkey || pkgKeyContext;
|
||||
|
||||
const fleetStatus = useFleetStatus();
|
||||
const { docLinks } = useStartServices();
|
||||
const spaceSettings = useSpaceSettingsContext();
|
||||
|
@ -130,7 +137,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
queryParamsPolicyId ? SelectedPolicyTab.EXISTING : SelectedPolicyTab.NEW
|
||||
);
|
||||
|
||||
const { pkgName, pkgVersion } = splitPkgKey(params.pkgkey);
|
||||
const { pkgName, pkgVersion } = splitPkgKey(pkgkey);
|
||||
// Fetch package info
|
||||
const {
|
||||
data: packageInfoData,
|
||||
|
@ -187,6 +194,24 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
hasFleetAddAgentsPrivileges,
|
||||
});
|
||||
|
||||
const handleNavigateAddAgent = useCallback(() => {
|
||||
if (onNext) {
|
||||
onNext({ selectedAgentPolicies: agentPolicies });
|
||||
} else {
|
||||
if (savedPackagePolicy) {
|
||||
navigateAddAgent(savedPackagePolicy);
|
||||
}
|
||||
}
|
||||
}, [onNext, agentPolicies, savedPackagePolicy, navigateAddAgent]);
|
||||
|
||||
const handleCancellation = useCallback(() => {
|
||||
if (onCanceled) {
|
||||
onCanceled();
|
||||
} else {
|
||||
navigateAddAgentHelp(savedPackagePolicy);
|
||||
}
|
||||
}, [onCanceled, savedPackagePolicy, navigateAddAgentHelp]);
|
||||
|
||||
const setPolicyValidation = useCallback(
|
||||
(selectedTab: SelectedPolicyTab, updatedAgentPolicy: NewAgentPolicy) => {
|
||||
if (selectedTab === SelectedPolicyTab.NEW) {
|
||||
|
@ -230,7 +255,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
|
||||
const { cancelClickHandler, cancelUrl } = useCancelAddPackagePolicy({
|
||||
from,
|
||||
pkgkey: params.pkgkey,
|
||||
pkgkey,
|
||||
agentPolicyId: agentPolicyIds[0],
|
||||
});
|
||||
useEffect(() => {
|
||||
|
@ -504,7 +529,10 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
agentCount={agentCount}
|
||||
agentPolicies={agentPolicies}
|
||||
onConfirm={onSubmit}
|
||||
onCancel={() => setFormState('VALID')}
|
||||
onCancel={() => {
|
||||
setFormState('VALID');
|
||||
onCanceled?.();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{formState === 'SUBMITTED_NO_AGENTS' &&
|
||||
|
@ -513,8 +541,8 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
savedPackagePolicy && (
|
||||
<PostInstallAddAgentModal
|
||||
packageInfo={packageInfo}
|
||||
onConfirm={() => navigateAddAgent(savedPackagePolicy)}
|
||||
onCancel={() => navigateAddAgentHelp(savedPackagePolicy)}
|
||||
onConfirm={handleNavigateAddAgent}
|
||||
onCancel={handleCancellation}
|
||||
/>
|
||||
)}
|
||||
{formState === 'SUBMITTED_AZURE_ARM_TEMPLATE' &&
|
||||
|
@ -523,8 +551,8 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
<PostInstallAzureArmTemplateModal
|
||||
agentPolicy={agentPolicies[0]}
|
||||
packagePolicy={savedPackagePolicy}
|
||||
onConfirm={() => navigateAddAgent(savedPackagePolicy)}
|
||||
onCancel={() => navigateAddAgentHelp(savedPackagePolicy)}
|
||||
onConfirm={handleNavigateAddAgent}
|
||||
onCancel={handleCancellation}
|
||||
/>
|
||||
)}
|
||||
{formState === 'SUBMITTED_CLOUD_FORMATION' &&
|
||||
|
@ -533,8 +561,8 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
<PostInstallCloudFormationModal
|
||||
agentPolicy={agentPolicies[0]}
|
||||
packagePolicy={savedPackagePolicy}
|
||||
onConfirm={() => navigateAddAgent(savedPackagePolicy)}
|
||||
onCancel={() => navigateAddAgentHelp(savedPackagePolicy)}
|
||||
onConfirm={handleNavigateAddAgent}
|
||||
onCancel={handleCancellation}
|
||||
/>
|
||||
)}
|
||||
{formState === 'SUBMITTED_GOOGLE_CLOUD_SHELL' &&
|
||||
|
@ -543,8 +571,8 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
<PostInstallGoogleCloudShellModal
|
||||
agentPolicy={agentPolicies[0]}
|
||||
packagePolicy={savedPackagePolicy}
|
||||
onConfirm={() => navigateAddAgent(savedPackagePolicy)}
|
||||
onCancel={() => navigateAddAgentHelp(savedPackagePolicy)}
|
||||
onConfirm={handleNavigateAddAgent}
|
||||
onCancel={handleCancellation}
|
||||
/>
|
||||
)}
|
||||
{packageInfo && (
|
||||
|
@ -603,7 +631,12 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
|
|||
<StepsWithLessPadding steps={steps} />
|
||||
<EuiSpacer size="xl" />
|
||||
<EuiSpacer size="xl" />
|
||||
<CustomEuiBottomBar data-test-subj="integrationsBottomBar">
|
||||
{/* Only show render button bar in portal when enableRouts is false*/}
|
||||
<CustomEuiBottomBar
|
||||
data-test-subj="integrationsBottomBar"
|
||||
usePortal={false}
|
||||
position="sticky"
|
||||
>
|
||||
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
{packageInfo && (formState === 'INVALID' || hasAgentPolicyError) ? (
|
||||
|
|
|
@ -13,7 +13,8 @@ export type EditPackagePolicyFrom =
|
|||
| 'edit'
|
||||
| 'upgrade-from-fleet-policy-list'
|
||||
| 'upgrade-from-integrations-policy-list'
|
||||
| 'upgrade-from-extension';
|
||||
| 'upgrade-from-extension'
|
||||
| 'onboarding-integration';
|
||||
|
||||
export type PackagePolicyFormState =
|
||||
| 'VALID'
|
||||
|
@ -35,5 +36,8 @@ export interface AddToPolicyParams {
|
|||
export type CreatePackagePolicyParams = React.FunctionComponent<{
|
||||
from: EditPackagePolicyFrom;
|
||||
queryParamsPolicyId?: string;
|
||||
propPolicyId?: string;
|
||||
integrationName?: string;
|
||||
prerelease: boolean;
|
||||
onNext?: () => void;
|
||||
}>;
|
||||
|
|
|
@ -38,7 +38,11 @@ import { INTEGRATIONS_ROUTING_PATHS, pagePathGetters } from './constants';
|
|||
import type { UIExtensionsStorage } from './types';
|
||||
|
||||
import { EPMApp } from './sections/epm';
|
||||
import { PackageInstallProvider, UIExtensionsContext, FlyoutContextProvider } from './hooks';
|
||||
import {
|
||||
PackageInstallProvider,
|
||||
UIExtensionsContextProvider,
|
||||
FlyoutContextProvider,
|
||||
} from './hooks';
|
||||
import { IntegrationsHeader } from './components/header';
|
||||
import { AgentEnrollmentFlyout } from './components';
|
||||
import { ReadOnlyContextProvider } from './hooks/use_read_only_context';
|
||||
|
@ -103,7 +107,7 @@ export const IntegrationsAppContext: React.FC<{
|
|||
<EuiThemeProvider darkMode={isDarkMode}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
<UIExtensionsContext.Provider value={extensions}>
|
||||
<UIExtensionsContextProvider values={extensions}>
|
||||
<FleetStatusProvider defaultFleetStatus={fleetStatus}>
|
||||
<SpaceSettingsContextProvider>
|
||||
<startServices.customIntegrations.ContextProvider>
|
||||
|
@ -126,7 +130,7 @@ export const IntegrationsAppContext: React.FC<{
|
|||
</startServices.customIntegrations.ContextProvider>
|
||||
</SpaceSettingsContextProvider>
|
||||
</FleetStatusProvider>
|
||||
</UIExtensionsContext.Provider>
|
||||
</UIExtensionsContextProvider>
|
||||
</QueryClientProvider>
|
||||
</EuiThemeProvider>
|
||||
</KibanaVersionContext.Provider>
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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, { createContext } from 'react';
|
||||
|
||||
import {
|
||||
FleetStatusProvider,
|
||||
KibanaVersionContext,
|
||||
UIExtensionsContextProvider,
|
||||
} from '../../../hooks';
|
||||
|
||||
import type { FleetStartServices } from '../../../plugin';
|
||||
|
||||
import { PackageInstallProvider } from './use_package_install';
|
||||
import { IntegrationsStateContextProvider } from './use_integrations_state';
|
||||
|
||||
interface FleetIntegrationsStateContextValue {
|
||||
pkgkey: string | undefined;
|
||||
startServices: FleetStartServices | undefined;
|
||||
}
|
||||
|
||||
const FleetIntegrationsStateContext = createContext<FleetIntegrationsStateContextValue>({
|
||||
pkgkey: undefined,
|
||||
startServices: undefined,
|
||||
});
|
||||
|
||||
export const FleetIntegrationsStateContextProvider: React.FC<{
|
||||
children?: React.ReactNode;
|
||||
values: FleetIntegrationsStateContextValue;
|
||||
/* fix hard coded KibanaVersion */
|
||||
}> = ({ children, values: { startServices, kibanaVersion = '8.16.0' } }) => {
|
||||
return (
|
||||
<FleetIntegrationsStateContext.Provider value={{ fleet: startServices.fleet }}>
|
||||
<KibanaVersionContext.Provider value={kibanaVersion}>
|
||||
<UIExtensionsContextProvider values={{}}>
|
||||
<FleetStatusProvider>
|
||||
<PackageInstallProvider startServices={startServices}>
|
||||
<IntegrationsStateContextProvider>{children}</IntegrationsStateContextProvider>
|
||||
</PackageInstallProvider>
|
||||
</FleetStatusProvider>
|
||||
</UIExtensionsContextProvider>
|
||||
</KibanaVersionContext.Provider>
|
||||
</FleetIntegrationsStateContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useFleetIntegrationsStateContext = () => {
|
||||
const ctx = React.useContext(FleetIntegrationsStateContext);
|
||||
if (!ctx) {
|
||||
throw new Error(
|
||||
'useFleetIntegrationsStateContext can only be used inside of FleetIntegrationsStateContextProvider'
|
||||
);
|
||||
}
|
||||
return ctx;
|
||||
};
|
|
@ -23,13 +23,19 @@ export const IntegrationsStateContextProvider: FunctionComponent<{
|
|||
children?: React.ReactNode;
|
||||
}> = ({ children }) => {
|
||||
const maybeState = useIntraAppState<undefined | IntegrationsAppBrowseRouteState>();
|
||||
const fromIntegrationsRef = useRef<undefined | string>(maybeState?.fromIntegrations);
|
||||
|
||||
const stateRef = useRef(maybeState);
|
||||
console.log('myState---', maybeState);
|
||||
const getFromIntegrations = useCallback(() => {
|
||||
return fromIntegrationsRef.current;
|
||||
return stateRef.current?.fromIntegrations;
|
||||
}, []);
|
||||
return (
|
||||
<IntegrationsStateContext.Provider value={{ getFromIntegrations }}>
|
||||
<IntegrationsStateContext.Provider
|
||||
value={{
|
||||
getFromIntegrations,
|
||||
pkgkey: maybeState?.pkgkey,
|
||||
panel: maybeState?.panel,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</IntegrationsStateContext.Provider>
|
||||
);
|
||||
|
|
|
@ -261,16 +261,18 @@ function usePackageInstall({ startServices }: { startServices: StartServices })
|
|||
};
|
||||
}
|
||||
|
||||
export const [
|
||||
PackageInstallProvider,
|
||||
useInstallPackage,
|
||||
useSetPackageInstallStatus,
|
||||
useGetPackageInstallStatus,
|
||||
useUninstallPackage,
|
||||
] = createContainer(
|
||||
export const packageInstallContainer = createContainer(
|
||||
usePackageInstall,
|
||||
(value) => value.installPackage,
|
||||
(value) => value.setPackageInstallStatus,
|
||||
(value) => value.getPackageInstallStatus,
|
||||
(value) => value.uninstallPackage
|
||||
);
|
||||
|
||||
export const [
|
||||
PackageInstallProvider,
|
||||
useInstallPackage,
|
||||
useSetPackageInstallStatus,
|
||||
useGetPackageInstallStatus,
|
||||
useUninstallPackage,
|
||||
] = packageInstallContainer;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { EuiButtonWithTooltip } from '../../../../../components';
|
||||
|
@ -14,8 +14,8 @@ interface AddIntegrationButtonProps {
|
|||
userCanInstallPackages?: boolean;
|
||||
missingSecurityConfiguration: boolean;
|
||||
packageName: string;
|
||||
href: string;
|
||||
onClick: Function;
|
||||
href: string | undefined;
|
||||
onClick: Function | undefined;
|
||||
}
|
||||
|
||||
export function AddIntegrationButton(props: AddIntegrationButtonProps) {
|
||||
|
@ -38,15 +38,22 @@ export function AddIntegrationButton(props: AddIntegrationButtonProps) {
|
|||
}
|
||||
: undefined;
|
||||
|
||||
const optionalProps = useMemo(
|
||||
() => ({
|
||||
...(href ? { href } : {}),
|
||||
...(onClick ? { onClick: (e: React.MouseEvent) => onClick(e) } : {}),
|
||||
}),
|
||||
[href, onClick]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiButtonWithTooltip
|
||||
fill
|
||||
isDisabled={!userCanInstallPackages}
|
||||
iconType="plusInCircle"
|
||||
href={href}
|
||||
onClick={(e) => onClick(e)}
|
||||
data-test-subj="addIntegrationPolicyButton"
|
||||
tooltip={tooltip}
|
||||
{...optionalProps}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.epm.addPackagePolicyButtonText"
|
||||
|
|
|
@ -91,6 +91,7 @@ import { Configs } from './configs';
|
|||
import './index.scss';
|
||||
import type { InstallPkgRouteOptions } from './utils/get_install_route_options';
|
||||
import { InstallButton } from './settings/install_button';
|
||||
import { TabsContent } from './tabs_content';
|
||||
|
||||
export type DetailViewPanelName =
|
||||
| 'overview'
|
||||
|
@ -129,10 +130,25 @@ function Breadcrumbs({ packageTitle }: { packageTitle: string }) {
|
|||
return null;
|
||||
}
|
||||
|
||||
export function Detail() {
|
||||
export function Detail({
|
||||
originFrom,
|
||||
routesEnabled = true,
|
||||
onAddIntegrationPolicyClick,
|
||||
}: {
|
||||
originFrom?: string;
|
||||
routesEnabled?: boolean;
|
||||
onAddIntegrationPolicyClick?: () => void;
|
||||
}) {
|
||||
const { getId: getAgentPolicyId } = useAgentPolicyContext();
|
||||
const { getFromIntegrations } = useIntegrationsStateContext();
|
||||
const { pkgkey, panel } = useParams<DetailParams>();
|
||||
const {
|
||||
getFromIntegrations,
|
||||
pkgkey: pkgKeyContext,
|
||||
panel: panelContext,
|
||||
} = useIntegrationsStateContext();
|
||||
const [selectedPanel, setSelectedPanel] = useState<DetailViewPanelName>(panelContext);
|
||||
const { pkgkey: pkgkeyParam, panel: panelParam } = useParams<DetailParams>();
|
||||
const pkgkey = pkgkeyParam || pkgKeyContext;
|
||||
const panel = panelParam || selectedPanel;
|
||||
const { getHref, getPath } = useLink();
|
||||
const history = useHistory();
|
||||
const { pathname, search, hash } = useLocation();
|
||||
|
@ -144,7 +160,6 @@ export function Detail() {
|
|||
*/
|
||||
const onboardingLink = useMemo(() => queryParams.get('onboardingLink'), [queryParams]);
|
||||
const onboardingAppId = useMemo(() => queryParams.get('onboardingAppId'), [queryParams]);
|
||||
|
||||
const authz = useAuthz();
|
||||
const canAddAgent = authz.fleet.addAgents;
|
||||
const canInstallPackages = authz.integrations.installPackages;
|
||||
|
@ -178,7 +193,7 @@ export function Detail() {
|
|||
if (packageInfo === null || !packageInfo.name) {
|
||||
return undefined;
|
||||
}
|
||||
return getPackageInstallStatus(packageInfo?.name)?.status;
|
||||
return getPackageInstallStatus?.(packageInfo?.name)?.status;
|
||||
}, [packageInfo, getPackageInstallStatus]);
|
||||
const isInstalled = useMemo(
|
||||
() =>
|
||||
|
@ -401,6 +416,11 @@ export function Detail() {
|
|||
ev.preventDefault();
|
||||
// The object below, given to `createHref` is explicitly accessing keys of `location` in order
|
||||
// to ensure that dependencies to this `useCallback` is set correctly (because `location` is mutable)
|
||||
if (onAddIntegrationPolicyClick) {
|
||||
onAddIntegrationPolicyClick();
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPath = history.createHref({
|
||||
pathname,
|
||||
search,
|
||||
|
@ -448,6 +468,7 @@ export function Detail() {
|
|||
isExperimentalAddIntegrationPageEnabled,
|
||||
isFirstTimeAgentUser,
|
||||
isGuidedOnboardingActive,
|
||||
onAddIntegrationPolicyClick,
|
||||
onboardingAppId,
|
||||
onboardingLink,
|
||||
pathname,
|
||||
|
@ -559,13 +580,17 @@ export function Detail() {
|
|||
>
|
||||
<AddIntegrationButton
|
||||
userCanInstallPackages={userCanInstallPackages}
|
||||
href={getHref('add_integration_to_policy', {
|
||||
pkgkey,
|
||||
...(integration ? { integration } : {}),
|
||||
...(agentPolicyIdFromContext
|
||||
? { agentPolicyId: agentPolicyIdFromContext }
|
||||
: {}),
|
||||
})}
|
||||
href={
|
||||
onAddIntegrationPolicyClick
|
||||
? undefined
|
||||
: getHref('add_integration_to_policy', {
|
||||
pkgkey,
|
||||
...(integration ? { integration } : {}),
|
||||
...(agentPolicyIdFromContext
|
||||
? { agentPolicyId: agentPolicyIdFromContext }
|
||||
: {}),
|
||||
})
|
||||
}
|
||||
missingSecurityConfiguration={missingSecurityConfiguration}
|
||||
packageName={integrationInfo?.title || packageInfo.title}
|
||||
onClick={handleAddIntegrationPolicyClick}
|
||||
|
@ -593,12 +618,16 @@ export function Detail() {
|
|||
) : undefined,
|
||||
[
|
||||
packageInfo,
|
||||
showVersionSelect,
|
||||
versionLabel,
|
||||
versionOptions,
|
||||
updateAvailable,
|
||||
isInstalled,
|
||||
pkgkey,
|
||||
isOverviewPage,
|
||||
isGuidedOnboardingActive,
|
||||
userCanInstallPackages,
|
||||
onAddIntegrationPolicyClick,
|
||||
getHref,
|
||||
integration,
|
||||
agentPolicyIdFromContext,
|
||||
|
@ -606,9 +635,6 @@ export function Detail() {
|
|||
integrationInfo?.title,
|
||||
handleAddIntegrationPolicyClick,
|
||||
onVersionChange,
|
||||
showVersionSelect,
|
||||
versionLabel,
|
||||
versionOptions,
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -629,10 +655,17 @@ export function Detail() {
|
|||
),
|
||||
isSelected: panel === 'overview',
|
||||
'data-test-subj': `tab-overview`,
|
||||
href: getHref('integration_details_overview', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
}),
|
||||
href:
|
||||
originFrom !== 'onboarding-integration'
|
||||
? getHref('integration_details_overview', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
})
|
||||
: undefined,
|
||||
onClick: (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
e.preventDefault();
|
||||
setSelectedPanel('overview');
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -647,10 +680,16 @@ export function Detail() {
|
|||
),
|
||||
isSelected: panel === 'policies',
|
||||
'data-test-subj': `tab-policies`,
|
||||
href: getHref('integration_details_policies', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
}),
|
||||
href: routesEnabled
|
||||
? getHref('integration_details_policies', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
})
|
||||
: undefined,
|
||||
onClick: (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
e.preventDefault();
|
||||
setSelectedPanel('policies');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -671,10 +710,16 @@ export function Detail() {
|
|||
),
|
||||
isSelected: panel === 'assets',
|
||||
'data-test-subj': `tab-assets`,
|
||||
href: getHref('integration_details_assets', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
}),
|
||||
href: routesEnabled
|
||||
? getHref('integration_details_assets', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
})
|
||||
: undefined,
|
||||
onClick: (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
e.preventDefault();
|
||||
setSelectedPanel('assets');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -689,10 +734,16 @@ export function Detail() {
|
|||
),
|
||||
isSelected: panel === 'settings',
|
||||
'data-test-subj': `tab-settings`,
|
||||
href: getHref('integration_details_settings', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
}),
|
||||
href: routesEnabled
|
||||
? getHref('integration_details_settings', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
})
|
||||
: undefined,
|
||||
onClick: (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
e.preventDefault();
|
||||
setSelectedPanel('settings');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -707,10 +758,16 @@ export function Detail() {
|
|||
),
|
||||
isSelected: panel === 'configs',
|
||||
'data-test-subj': `tab-configs`,
|
||||
href: getHref('integration_details_configs', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
}),
|
||||
href: routesEnabled
|
||||
? getHref('integration_details_configs', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
})
|
||||
: undefined,
|
||||
onClick: (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
e.preventDefault();
|
||||
setSelectedPanel('configs');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -725,10 +782,16 @@ export function Detail() {
|
|||
),
|
||||
isSelected: panel === 'custom',
|
||||
'data-test-subj': `tab-custom`,
|
||||
href: getHref('integration_details_custom', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
}),
|
||||
href: routesEnabled
|
||||
? getHref('integration_details_custom', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
})
|
||||
: undefined,
|
||||
onClick: (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
e.preventDefault();
|
||||
setSelectedPanel('custom');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -743,10 +806,16 @@ export function Detail() {
|
|||
),
|
||||
isSelected: panel === 'api-reference',
|
||||
'data-test-subj': `tab-api-reference`,
|
||||
href: getHref('integration_details_api_reference', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
}),
|
||||
href: routesEnabled
|
||||
? getHref('integration_details_api_reference', {
|
||||
pkgkey: packageInfoKey,
|
||||
...(integration ? { integration } : {}),
|
||||
})
|
||||
: undefined,
|
||||
onClick: (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
e.preventDefault();
|
||||
setSelectedPanel('api-reference');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -754,6 +823,7 @@ export function Detail() {
|
|||
}, [
|
||||
packageInfo,
|
||||
panel,
|
||||
originFrom,
|
||||
getHref,
|
||||
integration,
|
||||
canReadIntegrationPolicies,
|
||||
|
@ -763,6 +833,7 @@ export function Detail() {
|
|||
showConfigTab,
|
||||
showCustomTab,
|
||||
showDocumentationTab,
|
||||
routesEnabled,
|
||||
numOfDeferredInstallations,
|
||||
]);
|
||||
|
||||
|
@ -823,45 +894,17 @@ export function Detail() {
|
|||
) : isLoading || !packageInfo ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<Routes>
|
||||
<Route path={INTEGRATIONS_ROUTING_PATHS.integration_details_overview}>
|
||||
<OverviewPage
|
||||
packageInfo={packageInfo}
|
||||
integrationInfo={integrationInfo}
|
||||
latestGAVersion={latestGAVersion}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={INTEGRATIONS_ROUTING_PATHS.integration_details_settings}>
|
||||
<SettingsPage
|
||||
packageInfo={packageInfo}
|
||||
packageMetadata={packageInfoData?.metadata}
|
||||
startServices={services}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={INTEGRATIONS_ROUTING_PATHS.integration_details_assets}>
|
||||
<AssetsPage packageInfo={packageInfo} refetchPackageInfo={refetchPackageInfo} />
|
||||
</Route>
|
||||
<Route path={INTEGRATIONS_ROUTING_PATHS.integration_details_configs}>
|
||||
<Configs packageInfo={packageInfo} />
|
||||
</Route>
|
||||
<Route path={INTEGRATIONS_ROUTING_PATHS.integration_details_policies}>
|
||||
{canReadIntegrationPolicies ? (
|
||||
<PackagePoliciesPage packageInfo={packageInfo} />
|
||||
) : (
|
||||
<PermissionsError
|
||||
error="MISSING_PRIVILEGES"
|
||||
requiredFleetRole="Agent Policies Read and Integrations Read"
|
||||
/>
|
||||
)}
|
||||
</Route>
|
||||
<Route path={INTEGRATIONS_ROUTING_PATHS.integration_details_custom}>
|
||||
<CustomViewPage packageInfo={packageInfo} />
|
||||
</Route>
|
||||
<Route path={INTEGRATIONS_ROUTING_PATHS.integration_details_api_reference}>
|
||||
<DocumentationPage packageInfo={packageInfo} integration={integrationInfo?.name} />
|
||||
</Route>
|
||||
<Redirect to={INTEGRATIONS_ROUTING_PATHS.integration_details_overview} />
|
||||
</Routes>
|
||||
<TabsContent
|
||||
canReadIntegrationPolicies={canReadIntegrationPolicies}
|
||||
integrationInfo={integrationInfo}
|
||||
latestGAVersion={latestGAVersion}
|
||||
packageInfo={packageInfo}
|
||||
packageInfoData={packageInfoData}
|
||||
panel={panel}
|
||||
refetchPackageInfo={refetchPackageInfo}
|
||||
routesEnabled={routesEnabled}
|
||||
services={services}
|
||||
/>
|
||||
)}
|
||||
</WithHeaderLayout>
|
||||
);
|
||||
|
|
|
@ -12,7 +12,12 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
import { InstallStatus } from '../../../../../types';
|
||||
import type { PackageInfo } from '../../../../../types';
|
||||
|
||||
import { useAuthz, useGetPackageInstallStatus, useUninstallPackage } from '../../../../../hooks';
|
||||
import {
|
||||
useAuthz,
|
||||
useGetPackageInstallStatus,
|
||||
useIntegrationsStateContext,
|
||||
useUninstallPackage,
|
||||
} from '../../../../../hooks';
|
||||
|
||||
import { ConfirmPackageUninstall } from './confirm_package_uninstall';
|
||||
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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 { Routes, Route } from '@kbn/shared-ux-router';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
import { INTEGRATIONS_ROUTING_PATHS } from '../../../../constants';
|
||||
import { PermissionsError } from '../../../../../fleet/layouts';
|
||||
|
||||
import type { FleetStart, FleetStartServices } from '../../../../../../plugin';
|
||||
|
||||
import { AssetsPage } from './assets';
|
||||
import { OverviewPage } from './overview';
|
||||
import { PackagePoliciesPage } from './policies';
|
||||
import { SettingsPage } from './settings';
|
||||
import { CustomViewPage } from './custom';
|
||||
import { DocumentationPage } from './documentation';
|
||||
import { Configs } from './configs';
|
||||
|
||||
export const TabsContent: React.FC<{
|
||||
canReadIntegrationPolicies: boolean;
|
||||
integrationInfo: any;
|
||||
latestGAVersion: string | undefined;
|
||||
packageInfo: any;
|
||||
packageInfoData: any;
|
||||
panel: string;
|
||||
refetchPackageInfo: () => void;
|
||||
routesEnabled: boolean;
|
||||
services: FleetStartServices & {
|
||||
fleet?: FleetStart | undefined;
|
||||
};
|
||||
}> = ({
|
||||
canReadIntegrationPolicies,
|
||||
integrationInfo,
|
||||
latestGAVersion,
|
||||
packageInfo,
|
||||
packageInfoData,
|
||||
panel,
|
||||
refetchPackageInfo,
|
||||
routesEnabled,
|
||||
services,
|
||||
}) => {
|
||||
const routesMap = {
|
||||
overview: {
|
||||
path: INTEGRATIONS_ROUTING_PATHS.integration_details_overview,
|
||||
component: (
|
||||
<OverviewPage
|
||||
packageInfo={packageInfo}
|
||||
integrationInfo={integrationInfo}
|
||||
latestGAVersion={latestGAVersion}
|
||||
/>
|
||||
),
|
||||
},
|
||||
settings: {
|
||||
path: INTEGRATIONS_ROUTING_PATHS.integration_details_settings,
|
||||
component: (
|
||||
<SettingsPage
|
||||
packageInfo={packageInfo}
|
||||
packageMetadata={packageInfoData?.metadata}
|
||||
startServices={services}
|
||||
/>
|
||||
),
|
||||
},
|
||||
assets: {
|
||||
path: INTEGRATIONS_ROUTING_PATHS.integration_details_assets,
|
||||
component: <AssetsPage packageInfo={packageInfo} refetchPackageInfo={refetchPackageInfo} />,
|
||||
},
|
||||
configs: {
|
||||
path: INTEGRATIONS_ROUTING_PATHS.integration_details_configs,
|
||||
component: <Configs packageInfo={packageInfo} />,
|
||||
},
|
||||
policies: {
|
||||
path: INTEGRATIONS_ROUTING_PATHS.integration_details_policies,
|
||||
component: canReadIntegrationPolicies ? (
|
||||
<PackagePoliciesPage name={packageInfo.name} version={packageInfo.version} />
|
||||
) : (
|
||||
<PermissionsError
|
||||
error="MISSING_PRIVILEGES"
|
||||
requiredFleetRole="Agent Policies Read and Integrations Read"
|
||||
/>
|
||||
),
|
||||
},
|
||||
custom: {
|
||||
path: INTEGRATIONS_ROUTING_PATHS.integration_details_custom,
|
||||
component: <CustomViewPage packageInfo={packageInfo} />,
|
||||
},
|
||||
apiReference: {
|
||||
path: INTEGRATIONS_ROUTING_PATHS.integration_details_api_reference,
|
||||
component: (
|
||||
<DocumentationPage packageInfo={packageInfo} integration={integrationInfo?.name} />
|
||||
),
|
||||
},
|
||||
};
|
||||
return routesEnabled ? (
|
||||
<Routes>
|
||||
{Object.values(routesMap).map(({ path, component }) => (
|
||||
<Route key={path} path={path}>
|
||||
{component}
|
||||
</Route>
|
||||
))}
|
||||
<Redirect to={INTEGRATIONS_ROUTING_PATHS.integration_details_overview} />
|
||||
</Routes>
|
||||
) : (
|
||||
routesMap[panel].component
|
||||
);
|
||||
};
|
||||
|
||||
TabsContent.displayName = 'TabsRoute';
|
|
@ -66,6 +66,7 @@ export interface IntegrationCardItem {
|
|||
url: string;
|
||||
version: string;
|
||||
type?: string;
|
||||
pkgkey?: string;
|
||||
}
|
||||
|
||||
export const mapToCard = ({
|
||||
|
@ -91,6 +92,9 @@ export const mapToCard = ({
|
|||
|
||||
let isUpdateAvailable = false;
|
||||
let isReauthorizationRequired = false;
|
||||
|
||||
const pkgkey = item.name ? `${item.name}-${version}` : undefined;
|
||||
|
||||
if (item.type === 'ui_link') {
|
||||
uiInternalPathUrl = item.id.includes('language_client.')
|
||||
? addBasePath(item.uiInternalPath)
|
||||
|
@ -103,9 +107,8 @@ export const mapToCard = ({
|
|||
|
||||
isReauthorizationRequired = hasDeferredInstallations(item);
|
||||
}
|
||||
|
||||
const url = getHref('integration_details_overview', {
|
||||
pkgkey: `${item.name}-${version}`,
|
||||
pkgkey,
|
||||
...(item.integration ? { integration: item.integration } : {}),
|
||||
});
|
||||
|
||||
|
@ -136,6 +139,7 @@ export const mapToCard = ({
|
|||
isUnverified,
|
||||
isUpdateAvailable,
|
||||
extraLabelsBadges,
|
||||
pkgkey,
|
||||
};
|
||||
|
||||
if (item.type === 'integration') {
|
||||
|
|
|
@ -116,9 +116,12 @@ export const ConfirmAgentEnrollment: React.FunctionComponent<Props> = ({
|
|||
);
|
||||
|
||||
const onButtonClick = () => {
|
||||
if (onClickViewAgents) onClickViewAgents();
|
||||
const href = getHref('agent_list');
|
||||
application.navigateToUrl(href);
|
||||
if (onClickViewAgents) {
|
||||
onClickViewAgents();
|
||||
} else {
|
||||
const href = getHref('agent_list');
|
||||
application.navigateToUrl(href);
|
||||
}
|
||||
};
|
||||
|
||||
if (!policyId || (agentCount === 0 && !showLoading)) {
|
||||
|
|
|
@ -5,10 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useFleetIntegrationsStateContext } from '../applications/integrations/hooks/use_fleet_integration_context';
|
||||
|
||||
import { useStartServices } from './use_core';
|
||||
|
||||
// Expose authz object, containing the privileges for Fleet and Integrations
|
||||
export function useAuthz() {
|
||||
const core = useStartServices();
|
||||
return core.authz;
|
||||
const { fleet } = useFleetIntegrationsStateContext();
|
||||
return core.authz ?? fleet.authz;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,5 @@ import type { AnyIntraAppRouteState } from '../types';
|
|||
*/
|
||||
export function useIntraAppState<S = AnyIntraAppRouteState>(): S | undefined {
|
||||
const location = useLocation();
|
||||
|
||||
return location.state as S;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,16 @@ import type { UIExtensionPoint, UIExtensionsStorage } from '../types';
|
|||
|
||||
export const UIExtensionsContext = React.createContext<UIExtensionsStorage>({});
|
||||
|
||||
export const UIExtensionsContextProvider = ({
|
||||
values,
|
||||
children,
|
||||
}: {
|
||||
values: UIExtensionsStorage;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
return <UIExtensionsContext.Provider value={values}>{children}</UIExtensionsContext.Provider>;
|
||||
};
|
||||
|
||||
type NarrowExtensionPoint<V extends UIExtensionPoint['view'], A = UIExtensionPoint> = A extends {
|
||||
view: V;
|
||||
}
|
|
@ -10,6 +10,7 @@ import type { PluginInitializerContext } from '@kbn/core/public';
|
|||
import { lazy } from 'react';
|
||||
|
||||
import { FleetPlugin } from './plugin';
|
||||
import type { UIExtensionsStorage } from './types';
|
||||
export type { GetPackagesResponse } from './types';
|
||||
export { installationStatuses } from '../common/constants';
|
||||
|
||||
|
@ -89,3 +90,17 @@ export const AvailablePackagesHook = () => {
|
|||
'./applications/integrations/sections/epm/screens/home/hooks/use_available_packages'
|
||||
);
|
||||
};
|
||||
export const Detail = () => {
|
||||
return import('./applications/integrations/sections/epm/screens/detail');
|
||||
};
|
||||
export const UseIntegrationsState = () => {
|
||||
return import('./applications/integrations/hooks/use_integrations_state');
|
||||
};
|
||||
|
||||
export const CreatePackagePolicyPage = () => {
|
||||
return import('./applications/fleet/sections/agent_policy/create_package_policy_page');
|
||||
};
|
||||
|
||||
export const FleetIntegrationsStateContextProvider = () => {
|
||||
return import('./applications/integrations/hooks/use_fleet_integration_context');
|
||||
};
|
||||
|
|
|
@ -20,12 +20,13 @@ import { OnboardingContextProvider } from './onboarding_context';
|
|||
import { OnboardingAVCBanner } from './onboarding_banner';
|
||||
import { OnboardingRoute } from './onboarding_route';
|
||||
import { OnboardingFooter } from './onboarding_footer';
|
||||
import type { StartPlugins } from '../../types';
|
||||
|
||||
const topicPathParam = `:topicId(${Object.values(OnboardingTopicId) // any topics
|
||||
.filter((val) => val !== OnboardingTopicId.default) // except "default"
|
||||
.join('|')})?`; // optional parameter
|
||||
|
||||
export const OnboardingPage = React.memo(() => {
|
||||
export const OnboardingPage = React.memo((plugins: StartPlugins) => {
|
||||
const spaceId = useSpaceId();
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
|
@ -38,7 +39,7 @@ export const OnboardingPage = React.memo(() => {
|
|||
}
|
||||
|
||||
return (
|
||||
<OnboardingContextProvider spaceId={spaceId}>
|
||||
<OnboardingContextProvider spaceId={spaceId} fleet={plugins.fleet}>
|
||||
<PluginTemplateWrapper paddingSize="none" data-test-subj="onboarding-hub-page">
|
||||
<OnboardingAVCBanner />
|
||||
<KibanaPageTemplate.Section
|
||||
|
|
|
@ -4,12 +4,26 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { lazy, Suspense, useMemo, useCallback, useEffect, useRef } from 'react';
|
||||
import { EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiSkeletonText } from '@elastic/eui';
|
||||
import React, { lazy, Suspense, useMemo, useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import {
|
||||
EuiButton,
|
||||
EuiButtonGroup,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiModal,
|
||||
EuiModalBody,
|
||||
EuiModalFooter,
|
||||
EuiModalHeader,
|
||||
EuiPortal,
|
||||
EuiSkeletonText,
|
||||
useGeneratedHtmlId,
|
||||
} from '@elastic/eui';
|
||||
import type { AvailablePackagesHookType, IntegrationCardItem } from '@kbn/fleet-plugin/public';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { withLazyHook } from '../../../../../common/components/with_lazy_hook';
|
||||
import {
|
||||
useStoredIntegrationSearchTerm,
|
||||
|
@ -45,9 +59,37 @@ export const PackageListGrid = lazy(async () => ({
|
|||
.then((pkg) => pkg.PackageListGrid),
|
||||
}));
|
||||
|
||||
const Detail = lazy(async () => ({
|
||||
default: await import('@kbn/fleet-plugin/public')
|
||||
.then((module) => module.Detail())
|
||||
.then((pkg) => pkg.Detail),
|
||||
}));
|
||||
|
||||
const CreatePackagePolicyPage = lazy(async () => ({
|
||||
default: await import('@kbn/fleet-plugin/public')
|
||||
.then((module) => module.CreatePackagePolicyPage())
|
||||
.then((pkg) => pkg.CreatePackagePolicyPage),
|
||||
}));
|
||||
|
||||
const FleetIntegrationsStateContextProvider = lazy(async () => ({
|
||||
default: await import('@kbn/fleet-plugin/public')
|
||||
.then((module) => module.FleetIntegrationsStateContextProvider())
|
||||
.then((pkg) => pkg.FleetIntegrationsStateContextProvider),
|
||||
}));
|
||||
|
||||
const integrationStepMap = {
|
||||
0: 'Add integration',
|
||||
1: 'Install Elastic Agent',
|
||||
2: 'Confirm incoming data',
|
||||
}
|
||||
|
||||
export const IntegrationsCardGridTabsComponent = React.memo<IntegrationsCardGridTabsProps>(
|
||||
({ installedIntegrationsCount, isAgentRequired, useAvailablePackages }) => {
|
||||
const { spaceId } = useOnboardingContext();
|
||||
const startServices = useKibana().services;
|
||||
const {
|
||||
services: { fleet },
|
||||
} = useKibana();
|
||||
const scrollElement = useRef<HTMLDivElement>(null);
|
||||
const [toggleIdSelected, setSelectedTabIdToStorage] = useStoredIntegrationTabId(
|
||||
spaceId,
|
||||
|
@ -65,6 +107,25 @@ export const IntegrationsCardGridTabsComponent = React.memo<IntegrationsCardGrid
|
|||
[setSelectedTabIdToStorage]
|
||||
);
|
||||
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [integrationName, setIntegrationName] = useState();
|
||||
const [modalView, setModalView] = useState<'overview' | 'configure-integration' | 'add-agent'>(
|
||||
'overview'
|
||||
);
|
||||
const [integrationStep, setIntegrationStep] = useState(0);
|
||||
const onAddIntegrationPolicyClick = useCallback(() => {
|
||||
setModalView('configure-integration');
|
||||
}, []);
|
||||
const closeModal = useCallback(() => {
|
||||
setIsModalVisible(false);
|
||||
setModalView('overview');
|
||||
setIntegrationStep(0);
|
||||
}, []);
|
||||
const onCardClicked = useCallback((name: string) => {
|
||||
setIsModalVisible(true);
|
||||
setIntegrationName(name);
|
||||
}, []);
|
||||
const modalTitleId = useGeneratedHtmlId();
|
||||
const {
|
||||
filteredCards,
|
||||
isLoading,
|
||||
|
@ -77,7 +138,6 @@ export const IntegrationsCardGridTabsComponent = React.memo<IntegrationsCardGrid
|
|||
});
|
||||
|
||||
const selectedTab = useMemo(() => INTEGRATION_TABS_BY_ID[toggleIdSelected], [toggleIdSelected]);
|
||||
|
||||
const onSearchTermChanged = useCallback(
|
||||
(searchQuery: string) => {
|
||||
setSearchTerm(searchQuery);
|
||||
|
@ -116,10 +176,10 @@ export const IntegrationsCardGridTabsComponent = React.memo<IntegrationsCardGrid
|
|||
setSelectedSubCategory,
|
||||
toggleIdSelected,
|
||||
]);
|
||||
|
||||
const list: IntegrationCardItem[] = useIntegrationCardList({
|
||||
integrationsList: filteredCards,
|
||||
featuredCardIds: selectedTab.featuredCardIds,
|
||||
onCardClicked,
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
|
@ -132,68 +192,108 @@ export const IntegrationsCardGridTabsComponent = React.memo<IntegrationsCardGrid
|
|||
);
|
||||
}
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
direction="column"
|
||||
className="step-paragraph"
|
||||
gutterSize={selectedTab.showSearchTools ? 'm' : 'none'}
|
||||
css={css`
|
||||
height: ${selectedTab.showSearchTools
|
||||
? WITH_SEARCH_BOX_HEIGHT
|
||||
: WITHOUT_SEARCH_BOX_HEIGHT};
|
||||
`}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonGroup
|
||||
buttonSize="compressed"
|
||||
color="primary"
|
||||
idSelected={toggleIdSelected}
|
||||
isFullWidth
|
||||
legend="Categories"
|
||||
onChange={onTabChange}
|
||||
options={INTEGRATION_TABS}
|
||||
type="single"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
<>
|
||||
<EuiFlexGroup
|
||||
direction="column"
|
||||
className="step-paragraph"
|
||||
gutterSize={selectedTab.showSearchTools ? 'm' : 'none'}
|
||||
css={css`
|
||||
overflow-y: ${selectedTab.overflow ?? 'auto'};
|
||||
height: ${selectedTab.showSearchTools
|
||||
? WITH_SEARCH_BOX_HEIGHT
|
||||
: WITHOUT_SEARCH_BOX_HEIGHT};
|
||||
`}
|
||||
grow={1}
|
||||
id={SCROLL_ELEMENT_ID}
|
||||
ref={scrollElement}
|
||||
>
|
||||
<Suspense
|
||||
fallback={<EuiSkeletonText isLoading={true} lines={LOADING_SKELETON_TEXT_LINES} />}
|
||||
>
|
||||
<PackageListGrid
|
||||
callout={
|
||||
<IntegrationCardTopCallout
|
||||
isAgentRequired={isAgentRequired}
|
||||
installedIntegrationsCount={installedIntegrationsCount}
|
||||
selectedTabId={toggleIdSelected}
|
||||
/>
|
||||
}
|
||||
calloutTopSpacerSize="m"
|
||||
categories={SEARCH_FILTER_CATEGORIES} // We do not want to show categories and subcategories as the search bar filter
|
||||
emptyStateStyles={emptyStateStyles}
|
||||
list={list}
|
||||
scrollElementId={SCROLL_ELEMENT_ID}
|
||||
searchTerm={searchTerm}
|
||||
selectedCategory={selectedTab.category ?? ''}
|
||||
selectedSubCategory={selectedTab.subCategory}
|
||||
setCategory={setCategory}
|
||||
setSearchTerm={onSearchTermChanged}
|
||||
setUrlandPushHistory={noop}
|
||||
setUrlandReplaceHistory={noop}
|
||||
showCardLabels={false}
|
||||
showControls={false}
|
||||
showSearchTools={selectedTab.showSearchTools}
|
||||
sortByFeaturedIntegrations={selectedTab.sortByFeaturedIntegrations}
|
||||
spacer={false}
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonGroup
|
||||
buttonSize="compressed"
|
||||
color="primary"
|
||||
idSelected={toggleIdSelected}
|
||||
isFullWidth
|
||||
legend="Categories"
|
||||
onChange={onTabChange}
|
||||
options={INTEGRATION_TABS}
|
||||
type="single"
|
||||
/>
|
||||
</Suspense>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
css={css`
|
||||
overflow-y: ${selectedTab.overflow ?? 'auto'};
|
||||
`}
|
||||
grow={1}
|
||||
id={SCROLL_ELEMENT_ID}
|
||||
ref={scrollElement}
|
||||
>
|
||||
<Suspense
|
||||
fallback={<EuiSkeletonText isLoading={true} lines={LOADING_SKELETON_TEXT_LINES} />}
|
||||
>
|
||||
<PackageListGrid
|
||||
callout={
|
||||
<IntegrationCardTopCallout
|
||||
isAgentRequired={isAgentRequired}
|
||||
installedIntegrationsCount={installedIntegrationsCount}
|
||||
selectedTabId={toggleIdSelected}
|
||||
/>
|
||||
}
|
||||
calloutTopSpacerSize="m"
|
||||
categories={SEARCH_FILTER_CATEGORIES} // We do not want to show categories and subcategories as the search bar filter
|
||||
emptyStateStyles={emptyStateStyles}
|
||||
list={list}
|
||||
scrollElementId={SCROLL_ELEMENT_ID}
|
||||
searchTerm={searchTerm}
|
||||
selectedCategory={selectedTab.category ?? ''}
|
||||
selectedSubCategory={selectedTab.subCategory}
|
||||
setCategory={setCategory}
|
||||
setSearchTerm={onSearchTermChanged}
|
||||
setUrlandPushHistory={noop}
|
||||
setUrlandReplaceHistory={noop}
|
||||
showCardLabels={false}
|
||||
showControls={false}
|
||||
showSearchTools={selectedTab.showSearchTools}
|
||||
sortByFeaturedIntegrations={selectedTab.sortByFeaturedIntegrations}
|
||||
spacer={false}
|
||||
/>
|
||||
</Suspense>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{isModalVisible && fleet && (
|
||||
<EuiPortal>
|
||||
<EuiModal
|
||||
aria-labelledby={modalTitleId}
|
||||
onClose={closeModal}
|
||||
css={css`
|
||||
width: 85%;
|
||||
`}
|
||||
maxWidth="90%"
|
||||
>
|
||||
{modalView === 'configure-integration' && (<EuiModalHeader>{`step indicator place holder. Integration step: ${integrationStepMap[integrationStep]}`}</EuiModalHeader>)}
|
||||
<EuiModalBody>
|
||||
<FleetIntegrationsStateContextProvider
|
||||
values={{ startServices, useMultiPageLayoutProp: true }}
|
||||
>
|
||||
{modalView === 'overview' && (
|
||||
<Detail
|
||||
onAddIntegrationPolicyClick={onAddIntegrationPolicyClick}
|
||||
originFrom="onboarding-integration"
|
||||
routesEnabled={false}
|
||||
/>
|
||||
)}
|
||||
{modalView === 'configure-integration' && (
|
||||
<CreatePackagePolicyPage
|
||||
useMultiPageLayoutProp={true}
|
||||
originFrom="onboarding-integration"
|
||||
propPolicyId=""
|
||||
integrationName={integrationName}
|
||||
setIntegrationStep={setIntegrationStep}
|
||||
onCanceled={closeModal}
|
||||
/>
|
||||
)}
|
||||
</FleetIntegrationsStateContextProvider>
|
||||
</EuiModalBody>
|
||||
{/* <EuiModalFooter><EuiButton onClick={closeModal}>Close</EuiButton></EuiModalFooter> */}
|
||||
</EuiModal>
|
||||
</EuiPortal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -16,7 +16,6 @@ import {
|
|||
import {
|
||||
CARD_DESCRIPTION_LINE_CLAMP,
|
||||
CARD_TITLE_LINE_CLAMP,
|
||||
INTEGRATION_APP_ID,
|
||||
MAX_CARD_HEIGHT_IN_PX,
|
||||
ONBOARDING_APP_ID,
|
||||
ONBOARDING_LINK,
|
||||
|
@ -50,15 +49,23 @@ const getFilteredCards = ({
|
|||
installedIntegrationList,
|
||||
integrationsList,
|
||||
navigateTo,
|
||||
onCardClicked,
|
||||
}: {
|
||||
featuredCardIds?: string[];
|
||||
getAppUrl: GetAppUrl;
|
||||
installedIntegrationList?: IntegrationCardItem[];
|
||||
integrationsList: IntegrationCardItem[];
|
||||
navigateTo: NavigateTo;
|
||||
onCardClicked?: (integrationName: string) => void;
|
||||
}) => {
|
||||
const securityIntegrationsList = integrationsList.map((card) =>
|
||||
addSecuritySpecificProps({ navigateTo, getAppUrl, card, installedIntegrationList })
|
||||
addSecuritySpecificProps({
|
||||
navigateTo,
|
||||
getAppUrl,
|
||||
card,
|
||||
installedIntegrationList,
|
||||
onCardClicked,
|
||||
})
|
||||
);
|
||||
if (!featuredCardIds) {
|
||||
return { featuredCards: [], integrationCards: securityIntegrationsList };
|
||||
|
@ -74,23 +81,32 @@ const addSecuritySpecificProps = ({
|
|||
navigateTo,
|
||||
getAppUrl,
|
||||
card,
|
||||
onCardClicked,
|
||||
}: {
|
||||
navigateTo: NavigateTo;
|
||||
getAppUrl: GetAppUrl;
|
||||
card: IntegrationCardItem;
|
||||
installedIntegrationList?: IntegrationCardItem[];
|
||||
onCardClicked?: (integrationName: string) => void;
|
||||
}): IntegrationCardItem => {
|
||||
const onboardingLink = getAppUrl({ appId: SECURITY_UI_APP_ID, path: ONBOARDING_PATH });
|
||||
const integrationRootUrl = getAppUrl({ appId: INTEGRATION_APP_ID });
|
||||
const state = {
|
||||
onCancelNavigateTo: [APP_UI_ID, { path: ONBOARDING_PATH }],
|
||||
onCancelUrl: onboardingLink,
|
||||
onSaveNavigateTo: [APP_UI_ID, { path: ONBOARDING_PATH }],
|
||||
};
|
||||
|
||||
const url =
|
||||
card.url.indexOf(APP_INTEGRATIONS_PATH) >= 0 && onboardingLink
|
||||
? addPathParamToUrl(card.url, onboardingLink)
|
||||
: card.url;
|
||||
|
||||
const state = {
|
||||
onCancelNavigateTo: [
|
||||
APP_UI_ID,
|
||||
{ path: ONBOARDING_PATH, state: { pkgkey: card.pkgkey, onCancelUrl: onboardingLink } },
|
||||
],
|
||||
onCancelUrl: onboardingLink,
|
||||
onSaveNavigateTo: [APP_UI_ID, { path: ONBOARDING_PATH, state: { pkgkey: card.pkgkey } }],
|
||||
pkgkey: card.pkgkey,
|
||||
panel: 'overview', // Default to the overview tab on modal opened
|
||||
};
|
||||
|
||||
return {
|
||||
...card,
|
||||
titleLineClamp: CARD_TITLE_LINE_CLAMP,
|
||||
|
@ -101,10 +117,12 @@ const addSecuritySpecificProps = ({
|
|||
onCardClick: () => {
|
||||
const trackId = `${TELEMETRY_INTEGRATION_CARD}_${card.id}`;
|
||||
trackOnboardingLinkClick(trackId);
|
||||
|
||||
if (url.startsWith(APP_INTEGRATIONS_PATH)) {
|
||||
onCardClicked?.(card.name); // fix me: type error
|
||||
|
||||
navigateTo({
|
||||
appId: INTEGRATION_APP_ID,
|
||||
path: url.slice(integrationRootUrl.length),
|
||||
path: `${addPathParamToUrl(ONBOARDING_PATH, onboardingLink)}#integrations`,
|
||||
state,
|
||||
});
|
||||
} else if (url.startsWith('http') || url.startsWith('https')) {
|
||||
|
@ -119,15 +137,24 @@ const addSecuritySpecificProps = ({
|
|||
export const useIntegrationCardList = ({
|
||||
integrationsList,
|
||||
featuredCardIds,
|
||||
onCardClicked,
|
||||
}: {
|
||||
integrationsList: IntegrationCardItem[];
|
||||
featuredCardIds?: string[] | undefined;
|
||||
onCardClicked?: (integrationName: string) => void;
|
||||
}): IntegrationCardItem[] => {
|
||||
const { navigateTo, getAppUrl } = useNavigation();
|
||||
|
||||
const { featuredCards, integrationCards } = useMemo(
|
||||
() => getFilteredCards({ navigateTo, getAppUrl, integrationsList, featuredCardIds }),
|
||||
[navigateTo, getAppUrl, integrationsList, featuredCardIds]
|
||||
() =>
|
||||
getFilteredCards({
|
||||
navigateTo,
|
||||
getAppUrl,
|
||||
integrationsList,
|
||||
featuredCardIds,
|
||||
onCardClicked,
|
||||
}),
|
||||
[navigateTo, getAppUrl, integrationsList, featuredCardIds, onCardClicked]
|
||||
);
|
||||
|
||||
if (featuredCardIds && featuredCardIds.length > 0) {
|
||||
|
|
|
@ -35,18 +35,19 @@ export interface OnboardingContextValue {
|
|||
}
|
||||
const OnboardingContext = createContext<OnboardingContextValue | null>(null);
|
||||
|
||||
export const OnboardingContextProvider: React.FC<PropsWithChildren<{ spaceId: string }>> =
|
||||
React.memo(({ children, spaceId }) => {
|
||||
const config = useFilteredConfig();
|
||||
const telemetry = useOnboardingTelemetry();
|
||||
export const OnboardingContextProvider: React.FC<
|
||||
PropsWithChildren<{ spaceId: string; fleet: FleetStart }>
|
||||
> = React.memo(({ children, spaceId, fleet }) => {
|
||||
const config = useFilteredConfig();
|
||||
const telemetry = useOnboardingTelemetry();
|
||||
|
||||
const value = useMemo<OnboardingContextValue>(
|
||||
() => ({ spaceId, telemetry, config }),
|
||||
[spaceId, telemetry, config]
|
||||
);
|
||||
const value = useMemo<OnboardingContextValue>(
|
||||
() => ({ spaceId, telemetry, config }),
|
||||
[spaceId, telemetry, config]
|
||||
);
|
||||
|
||||
return <OnboardingContext.Provider value={value}>{children}</OnboardingContext.Provider>;
|
||||
});
|
||||
return <OnboardingContext.Provider value={value}>{children}</OnboardingContext.Provider>;
|
||||
});
|
||||
OnboardingContextProvider.displayName = 'OnboardingContextProvider';
|
||||
|
||||
export const useOnboardingContext = () => {
|
||||
|
|
|
@ -6,14 +6,15 @@
|
|||
*/
|
||||
|
||||
import type { SecuritySubPlugin } from '../app/types';
|
||||
import type { StartPlugins } from '../types';
|
||||
import { routes } from './routes';
|
||||
|
||||
export class Onboarding {
|
||||
public setup() {}
|
||||
|
||||
public start(): SecuritySubPlugin {
|
||||
public start(plugins: StartPlugins): SecuritySubPlugin {
|
||||
return {
|
||||
routes,
|
||||
routes: routes(plugins),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
import { ONBOARDING_PATH, SecurityPageName } from '../../common/constants';
|
||||
import type { SecuritySubPluginRoutes } from '../app/types';
|
||||
import { withSecurityRoutePageWrapper } from '../common/components/security_route_page_wrapper';
|
||||
import type { StartPlugins } from '../types';
|
||||
import { OnboardingPage } from './components/onboarding';
|
||||
|
||||
export const routes: SecuritySubPluginRoutes = [
|
||||
export const routes: (plugins: StartPlugins) => SecuritySubPluginRoutes = (plugins) => [
|
||||
{
|
||||
path: ONBOARDING_PATH,
|
||||
component: withSecurityRoutePageWrapper(OnboardingPage, SecurityPageName.landing),
|
||||
|
|
|
@ -61,7 +61,7 @@ const createMultipassVm = async ({
|
|||
log.info(`Creating VM [${name}] using multipass`);
|
||||
|
||||
const createResponse = await execa.command(
|
||||
`multipass launch --name ${name} --disk ${disk} --cpus ${cpus} --memory ${memory}`
|
||||
`multipass launch --name ${name} --disk ${disk} --cpus ${cpus} --memory ${memory} --network en0`
|
||||
);
|
||||
|
||||
log.verbose(`VM [${name}] created successfully using multipass.`, createResponse);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue