[FLEET] New Integration Policy Details page for use in Integrations section (#85355)

* new UI route to show Edit Package Policy page
* Package policy List items point to new Integration Policy details page
* Refactor to use common service to generate pkgKey
* add breadcrumb for edit policy under integrations
This commit is contained in:
Paul Tavares 2020-12-09 17:50:33 -05:00 committed by GitHub
parent 5bb47d48b0
commit 6c52ac84c6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 352 additions and 40 deletions

View file

@ -8,6 +8,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { EuiText, EuiLink, EuiSpacer } from '@elastic/eui';
import { TutorialModuleNoticeComponent } from 'src/plugins/home/public';
import { useGetPackages, useLink, useCapabilities } from '../../hooks';
import { pkgKeyFromPackageInfo } from '../../services/pkg_key_from_package_info';
const TutorialModuleNotice: TutorialModuleNoticeComponent = memo(({ moduleName }) => {
const { getHref } = useLink();
@ -41,7 +42,7 @@ const TutorialModuleNotice: TutorialModuleNoticeComponent = memo(({ moduleName }
availableAsIntegrationLink: (
<EuiLink
href={getHref('integration_details', {
pkgkey: `${pkgInfo.name}-${pkgInfo.version}`,
pkgkey: pkgKeyFromPackageInfo(pkgInfo),
})}
>
<FormattedMessage

View file

@ -18,6 +18,7 @@ export type StaticPage =
export type DynamicPage =
| 'integration_details'
| 'integration_policy_edit'
| 'policy_details'
| 'add_integration_from_policy'
| 'add_integration_to_policy'
@ -41,6 +42,7 @@ export const PAGE_ROUTING_PATHS = {
integrations_all: '/integrations',
integrations_installed: '/integrations/installed',
integration_details: '/integrations/detail/:pkgkey/:panel?',
integration_policy_edit: '/integrations/edit-integration/:packagePolicyId',
policies: '/policies',
policies_list: '/policies',
policy_details: '/policies/:policyId/:tabId?',
@ -69,6 +71,8 @@ export const pagePathGetters: {
integrations_installed: () => '/integrations/installed',
integration_details: ({ pkgkey, panel }) =>
`/integrations/detail/${pkgkey}${panel ? `/${panel}` : ''}`,
integration_policy_edit: ({ packagePolicyId }) =>
`/integrations/edit-integration/${packagePolicyId}`,
policies: () => '/policies',
policies_list: () => '/policies',
policy_details: ({ policyId, tabId }) => `/policies/${policyId}${tabId ? `/${tabId}` : ''}`,

View file

@ -73,6 +73,20 @@ const breadcrumbGetters: {
},
{ text: pkgTitle },
],
integration_policy_edit: ({ pkgTitle, pkgkey, policyName }) => [
BASE_BREADCRUMB,
{
href: pagePathGetters.integrations(),
text: i18n.translate('xpack.fleet.breadcrumbs.integrationPageTitle', {
defaultMessage: 'Integration',
}),
},
{
href: pagePathGetters.integration_details({ pkgkey, panel: 'policies' }),
text: pkgTitle,
},
{ text: policyName },
],
policies: () => [
BASE_BREADCRUMB,
{

View file

@ -38,7 +38,7 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{
'data-test-subj': dataTestSubj,
}) => {
const pageTitle = useMemo(() => {
if ((from === 'package' || from === 'edit') && packageInfo) {
if ((from === 'package' || from === 'package-edit' || from === 'edit') && packageInfo) {
return (
<EuiFlexGroup alignItems="center" gutterSize="m">
<EuiFlexItem grow={false}>
@ -76,7 +76,7 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{
);
}
return from === 'edit' ? (
return from === 'edit' || from === 'package-edit' ? (
<EuiText>
<h1>
<FormattedMessage
@ -98,7 +98,7 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{
}, [from, packageInfo]);
const pageDescription = useMemo(() => {
return from === 'edit' ? (
return from === 'edit' || from === 'package-edit' ? (
<FormattedMessage
id="xpack.fleet.editPackagePolicy.pageDescription"
defaultMessage="Modify integration settings and deploy changes to the selected agent policy."

View file

@ -51,6 +51,7 @@ import { useUIExtension } from '../../../hooks/use_ui_extension';
import { ExtensionWrapper } from '../../../components/extension_wrapper';
import { PackagePolicyEditExtensionComponentProps } from '../../../types';
import { PLUGIN_ID } from '../../../../../../common/constants';
import { pkgKeyFromPackageInfo } from '../../../services/pkg_key_from_package_info';
const StepsWithLessPadding = styled(EuiSteps)`
.euiStep__content {
@ -404,7 +405,7 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => {
? packageInfo && (
<IntegrationBreadcrumb
pkgTitle={packageInfo.title}
pkgkey={`${packageInfo.name}-${packageInfo.version}`}
pkgkey={pkgKeyFromPackageInfo(packageInfo)}
/>
)
: agentPolicy && (

View file

@ -20,6 +20,7 @@ import { AgentPolicy, PackageInfo, PackagePolicy, NewPackagePolicy } from '../..
import { packageToPackagePolicyInputs } from '../../../services';
import { Loading } from '../../../components';
import { PackagePolicyValidationResults } from './services';
import { pkgKeyFromPackageInfo } from '../../../services/pkg_key_from_package_info';
export const StepDefinePackagePolicy: React.FunctionComponent<{
agentPolicy: AgentPolicy;
@ -34,8 +35,8 @@ export const StepDefinePackagePolicy: React.FunctionComponent<{
// Update package policy's package and agent policy info
useEffect(() => {
const pkg = packagePolicy.package;
const currentPkgKey = pkg ? `${pkg.name}-${pkg.version}` : '';
const pkgKey = `${packageInfo.name}-${packageInfo.version}`;
const currentPkgKey = pkg ? pkgKeyFromPackageInfo(pkg) : '';
const pkgKey = pkgKeyFromPackageInfo(packageInfo);
// If package has changed, create shell package policy with input&stream values based on package info
if (currentPkgKey !== pkgKey) {

View file

@ -16,6 +16,7 @@ import {
sendGetPackageInfoByKey,
} from '../../../hooks';
import { PackageIcon } from '../../../components/package_icon';
import { pkgKeyFromPackageInfo } from '../../../services/pkg_key_from_package_info';
export const StepSelectPackage: React.FunctionComponent<{
agentPolicyId: string;
@ -32,7 +33,7 @@ export const StepSelectPackage: React.FunctionComponent<{
}) => {
// Selected package state
const [selectedPkgKey, setSelectedPkgKey] = useState<string | undefined>(
packageInfo ? `${packageInfo.name}-${packageInfo.version}` : undefined
packageInfo ? pkgKeyFromPackageInfo(packageInfo) : undefined
);
const [selectedPkgError, setSelectedPkgError] = useState<Error>();
@ -92,7 +93,7 @@ export const StepSelectPackage: React.FunctionComponent<{
updatePackageInfo(undefined);
}
};
if (!packageInfo || selectedPkgKey !== `${packageInfo.name}-${packageInfo.version}`) {
if (!packageInfo || selectedPkgKey !== pkgKeyFromPackageInfo(packageInfo)) {
fetchPackageInfo();
}
}, [selectedPkgKey, packageInfo, updatePackageInfo, setIsLoadingSecondStep]);

View file

@ -4,5 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/
export type CreatePackagePolicyFrom = 'package' | 'policy' | 'edit';
export type CreatePackagePolicyFrom = 'package' | 'package-edit' | 'policy' | 'edit';
export type PackagePolicyFormState = 'VALID' | 'INVALID' | 'CONFIRM' | 'LOADING' | 'SUBMITTED';

View file

@ -3,7 +3,7 @@
* 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, { useState, useEffect, useCallback, useMemo } from 'react';
import React, { useState, useEffect, useCallback, useMemo, memo } from 'react';
import { useRouteMatch, useHistory } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
@ -45,15 +45,24 @@ import { useUIExtension } from '../../../hooks/use_ui_extension';
import { ExtensionWrapper } from '../../../components/extension_wrapper';
import { GetOnePackagePolicyResponse } from '../../../../../../common/types/rest_spec';
import { PackagePolicyEditExtensionComponentProps } from '../../../types';
import { pkgKeyFromPackageInfo } from '../../../services/pkg_key_from_package_info';
export const EditPackagePolicyPage: React.FunctionComponent = () => {
export const EditPackagePolicyPage = memo(() => {
const {
params: { packagePolicyId },
} = useRouteMatch<{ policyId: string; packagePolicyId: string }>();
return <EditPackagePolicyForm packagePolicyId={packagePolicyId} />;
});
export const EditPackagePolicyForm = memo<{
packagePolicyId: string;
from?: CreatePackagePolicyFrom;
}>(({ packagePolicyId, from = 'edit' }) => {
const { notifications } = useStartServices();
const {
agents: { enabled: isFleetEnabled },
} = useConfig();
const {
params: { policyId, packagePolicyId },
} = useRouteMatch<{ policyId: string; packagePolicyId: string }>();
const history = useHistory();
const { getHref, getPath } = useLink();
@ -76,16 +85,31 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => {
GetOnePackagePolicyResponse['item']
>();
const policyId = agentPolicy?.id ?? '';
// Retrieve agent policy, package, and package policy info
useEffect(() => {
const getData = async () => {
setIsLoadingData(true);
setLoadingError(undefined);
try {
const [{ data: agentPolicyData }, { data: packagePolicyData }] = await Promise.all([
sendGetOneAgentPolicy(policyId),
sendGetOnePackagePolicy(packagePolicyId),
]);
const {
data: packagePolicyData,
error: packagePolicyError,
} = await sendGetOnePackagePolicy(packagePolicyId);
if (packagePolicyError) {
throw packagePolicyError;
}
const { data: agentPolicyData, error: agentPolicyError } = await sendGetOneAgentPolicy(
packagePolicyData!.item.policy_id
);
if (agentPolicyError) {
throw agentPolicyError;
}
if (agentPolicyData?.item) {
setAgentPolicy(agentPolicyData.item);
}
@ -123,7 +147,7 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => {
setPackagePolicy(newPackagePolicy);
if (packagePolicyData.item.package) {
const { data: packageData } = await sendGetPackageInfoByKey(
`${packagePolicyData.item.package.name}-${packagePolicyData.item.package.version}`
pkgKeyFromPackageInfo(packagePolicyData.item.package)
);
if (packageData?.response) {
setPackageInfo(packageData.response);
@ -150,7 +174,7 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => {
}
};
if (isFleetEnabled) {
if (isFleetEnabled && policyId) {
getAgentCount();
}
}, [policyId, isFleetEnabled]);
@ -214,8 +238,32 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => {
[updatePackagePolicy]
);
// Cancel url
const cancelUrl = getHref('policy_details', { policyId });
// Cancel url + Success redirect Path:
// if `from === 'edit'` then it links back to Policy Details
// if `from === 'package-edit'` then it links back to the Integration Policy List
const cancelUrl = useMemo((): string => {
if (packageInfo && policyId) {
return from === 'package-edit'
? getHref('integration_details', {
pkgkey: pkgKeyFromPackageInfo(packageInfo!),
panel: 'policies',
})
: getHref('policy_details', { policyId });
}
return '/';
}, [from, getHref, packageInfo, policyId]);
const successRedirectPath = useMemo(() => {
if (packageInfo && policyId) {
return from === 'package-edit'
? getPath('integration_details', {
pkgkey: pkgKeyFromPackageInfo(packageInfo!),
panel: 'policies',
})
: getPath('policy_details', { policyId });
}
return '/';
}, [from, getPath, packageInfo, policyId]);
// Save package policy
const [formState, setFormState] = useState<PackagePolicyFormState>('INVALID');
@ -237,7 +285,7 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => {
}
const { error } = await savePackagePolicy();
if (!error) {
history.push(getPath('policy_details', { policyId }));
history.push(successRedirectPath);
notifications.toasts.addSuccess({
title: i18n.translate('xpack.fleet.editPackagePolicy.updatedNotificationTitle', {
defaultMessage: `Successfully updated '{packagePolicyName}'`,
@ -287,7 +335,7 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => {
};
const layoutProps = {
from: 'edit' as CreatePackagePolicyFrom,
from,
cancelUrl,
agentPolicy,
packageInfo,
@ -363,13 +411,21 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => {
error={
loadingError ||
i18n.translate('xpack.fleet.editPackagePolicy.errorLoadingDataMessage', {
defaultMessage: 'There was an error loading this intergration information',
defaultMessage: 'There was an error loading this integration information',
})
}
/>
) : (
<>
<Breadcrumb policyName={agentPolicy.name} policyId={policyId} />
{from === 'package' || from === 'package-edit' ? (
<IntegrationsBreadcrumb
pkgkey={pkgKeyFromPackageInfo(packageInfo)}
pkgTitle={packageInfo.title}
policyName={packagePolicy.name}
/>
) : (
<PoliciesBreadcrumb policyName={agentPolicy.name} policyId={policyId} />
)}
{formState === 'CONFIRM' && (
<ConfirmDeployAgentPolicyModal
agentCount={agentCount}
@ -424,12 +480,21 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => {
)}
</CreatePackagePolicyPageLayout>
);
};
});
const Breadcrumb: React.FunctionComponent<{ policyName: string; policyId: string }> = ({
const PoliciesBreadcrumb: React.FunctionComponent<{ policyName: string; policyId: string }> = ({
policyName,
policyId,
}) => {
useBreadcrumbs('edit_integration', { policyName, policyId });
return null;
};
const IntegrationsBreadcrumb = memo<{
pkgTitle: string;
policyName: string;
pkgkey: string;
}>(({ pkgTitle, policyName, pkgkey }) => {
useBreadcrumbs('integration_policy_edit', { policyName, pkgTitle, pkgkey });
return null;
});

View file

@ -21,6 +21,7 @@ import { Loading } from '../../../components';
import { PackageList } from '../../../types';
import { useLocalSearch, searchIdField } from '../hooks';
import { PackageCard } from './package_card';
import { pkgKeyFromPackageInfo } from '../../../services/pkg_key_from_package_info';
interface ListProps {
isLoading?: boolean;
@ -118,7 +119,7 @@ function GridColumn({ list }: GridColumnProps) {
<EuiFlexGrid gutterSize="l" columns={3}>
{list.length ? (
list.map((item) => (
<EuiFlexItem key={`${item.name}-${item.version}`}>
<EuiFlexItem key={pkgKeyFromPackageInfo(item)}>
<PackageCard {...item} />
</EuiFlexItem>
))

View file

@ -11,6 +11,7 @@ import { useBreadcrumbs } from '../../hooks';
import { CreatePackagePolicyPage } from '../agent_policy/create_package_policy_page';
import { EPMHomePage } from './screens/home';
import { Detail } from './screens/detail';
import { Policy } from './screens/policy';
export const EPMApp: React.FunctionComponent = () => {
useBreadcrumbs('integrations');
@ -20,6 +21,9 @@ export const EPMApp: React.FunctionComponent = () => {
<Route path={PAGE_ROUTING_PATHS.add_integration_to_policy}>
<CreatePackagePolicyPage />
</Route>
<Route path={PAGE_ROUTING_PATHS.integration_policy_edit}>
<Policy />
</Route>
<Route path={PAGE_ROUTING_PATHS.integration_details}>
<Detail />
</Route>

View file

@ -10,17 +10,26 @@ import React, { lazy, memo } from 'react';
import { PAGE_ROUTING_PATHS, pagePathGetters } from '../../../../constants';
import { Route } from 'react-router-dom';
import {
GetAgentPoliciesResponse,
GetFleetStatusResponse,
GetInfoResponse,
GetPackagePoliciesResponse,
} from '../../../../../../../common/types/rest_spec';
import { DetailViewPanelName, KibanaAssetType } from '../../../../../../../common/types/models';
import { epmRouteService, fleetSetupRouteService } from '../../../../../../../common/services';
import { act } from '@testing-library/react';
import {
agentPolicyRouteService,
epmRouteService,
fleetSetupRouteService,
packagePolicyRouteService,
} from '../../../../../../../common/services';
import { act, cleanup } from '@testing-library/react';
describe('when on integration detail', () => {
const detailPageUrlPath = pagePathGetters.integration_details({ pkgkey: 'nginx-0.3.7' });
const pkgkey = 'nginx-0.3.7';
const detailPageUrlPath = pagePathGetters.integration_details({ pkgkey });
let testRenderer: TestRenderer;
let renderResult: ReturnType<typeof testRenderer.render>;
let mockedApi: MockedApi;
const render = () =>
(renderResult = testRenderer.render(
<Route path={PAGE_ROUTING_PATHS.integration_details}>
@ -30,10 +39,15 @@ describe('when on integration detail', () => {
beforeEach(() => {
testRenderer = createTestRendererMock();
mockApiCalls(testRenderer.startServices.http);
mockedApi = mockApiCalls(testRenderer.startServices.http);
testRenderer.history.push(detailPageUrlPath);
});
afterEach(() => {
cleanup();
window.location.hash = '#/';
});
describe('and a custom UI extension is NOT registered', () => {
beforeEach(() => render());
@ -137,9 +151,48 @@ describe('when on integration detail', () => {
});
});
});
describe('and on the Policies Tab', () => {
const policiesTabURLPath = pagePathGetters.integration_details({ pkgkey, panel: 'policies' });
beforeEach(() => {
testRenderer.history.push(policiesTabURLPath);
render();
});
it('should display policies list', () => {
const table = renderResult.getByTestId('integrationPolicyTable');
expect(table).not.toBeNull();
});
it('should link to integration policy detail when an integration policy is clicked', async () => {
await mockedApi.waitForApi();
const firstPolicy = renderResult.getByTestId('integrationNameLink') as HTMLAnchorElement;
expect(firstPolicy.href).toEqual(
'http://localhost/mock/app/fleet#/integrations/edit-integration/e8a37031-2907-44f6-89d2-98bd493f60dc'
);
});
});
});
const mockApiCalls = (http: MockedFleetStartServices['http']) => {
interface MockedApi {
/** Will return a promise that resolves when triggered APIs are complete */
waitForApi: () => Promise<void>;
}
const mockApiCalls = (http: MockedFleetStartServices['http']): MockedApi => {
let inflightApiCalls = 0;
const apiDoneListeners: Array<() => void> = [];
const markApiCallAsHandled = async () => {
inflightApiCalls++;
await new Promise((r) => setTimeout(r, 1));
inflightApiCalls--;
// If no more pending API calls, then notify listeners
if (inflightApiCalls === 0 && apiDoneListeners.length > 0) {
apiDoneListeners.splice(0).forEach((listener) => listener());
}
};
// @ts-ignore
const epmPackageResponse: GetInfoResponse = {
response: {
@ -369,7 +422,7 @@ const mockApiCalls = (http: MockedFleetStartServices['http']) => {
owner: { github: 'elastic/integrations-services' },
latestVersion: '0.3.7',
removable: true,
status: 'not_installed',
status: 'installed',
},
} as GetInfoResponse;
@ -388,24 +441,162 @@ On Windows, the module was tested with Nginx installed from the Chocolatey repos
const agentsSetupResponse: GetFleetStatusResponse = { isReady: true, missing_requirements: [] };
const packagePoliciesResponse: GetPackagePoliciesResponse = {
items: [
{
id: 'e8a37031-2907-44f6-89d2-98bd493f60dc',
version: 'WzgzMiwxXQ==',
name: 'nginx-1',
description: '',
namespace: 'default',
policy_id: '521c1b70-3976-11eb-ad1c-3baa423084d9',
enabled: true,
output_id: '',
inputs: [
{
type: 'logfile',
enabled: true,
streams: [
{
enabled: true,
data_stream: { type: 'logs', dataset: 'nginx.access' },
vars: { paths: { value: ['/var/log/nginx/access.log*'], type: 'text' } },
id: 'logfile-nginx.access-e8a37031-2907-44f6-89d2-98bd493f60dc',
compiled_stream: {
paths: ['/var/log/nginx/access.log*'],
exclude_files: ['.gz$'],
processors: [{ add_locale: null }],
},
},
{
enabled: true,
data_stream: { type: 'logs', dataset: 'nginx.error' },
vars: { paths: { value: ['/var/log/nginx/error.log*'], type: 'text' } },
id: 'logfile-nginx.error-e8a37031-2907-44f6-89d2-98bd493f60dc',
compiled_stream: {
paths: ['/var/log/nginx/error.log*'],
exclude_files: ['.gz$'],
multiline: {
pattern: '^\\d{4}\\/\\d{2}\\/\\d{2} ',
negate: true,
match: 'after',
},
processors: [{ add_locale: null }],
},
},
{
enabled: false,
data_stream: { type: 'logs', dataset: 'nginx.ingress_controller' },
vars: { paths: { value: ['/var/log/nginx/ingress.log*'], type: 'text' } },
id: 'logfile-nginx.ingress_controller-e8a37031-2907-44f6-89d2-98bd493f60dc',
},
],
},
{
type: 'nginx/metrics',
enabled: true,
streams: [
{
enabled: true,
data_stream: { type: 'metrics', dataset: 'nginx.stubstatus' },
vars: {
period: { value: '10s', type: 'text' },
server_status_path: { value: '/nginx_status', type: 'text' },
},
id: 'nginx/metrics-nginx.stubstatus-e8a37031-2907-44f6-89d2-98bd493f60dc',
compiled_stream: {
metricsets: ['stubstatus'],
hosts: ['http://127.0.0.1:80'],
period: '10s',
server_status_path: '/nginx_status',
},
},
],
vars: { hosts: { value: ['http://127.0.0.1:80'], type: 'text' } },
},
],
package: { name: 'nginx', title: 'Nginx', version: '0.3.7' },
revision: 1,
created_at: '2020-12-09T13:46:31.013Z',
created_by: 'elastic',
updated_at: '2020-12-09T13:46:31.013Z',
updated_by: 'elastic',
},
],
total: 1,
page: 1,
perPage: 20,
};
const agentPoliciesResponse: GetAgentPoliciesResponse = {
items: [
{
id: '521c1b70-3976-11eb-ad1c-3baa423084d9',
name: 'Default',
namespace: 'default',
description: 'Default agent policy created by Kibana',
status: 'active',
package_policies: [
'4d09bd78-b0ad-4238-9fa3-d87d3c887c73',
'2babac18-eb8e-4ce4-b53b-4b7c5f507019',
'e8a37031-2907-44f6-89d2-98bd493f60dc',
],
is_default: true,
monitoring_enabled: ['logs', 'metrics'],
revision: 6,
updated_at: '2020-12-09T13:46:31.840Z',
updated_by: 'elastic',
agents: 0,
},
],
total: 1,
page: 1,
perPage: 100,
};
http.get.mockImplementation(async (path) => {
if (typeof path === 'string') {
if (path === epmRouteService.getInfoPath(`nginx-0.3.7`)) {
markApiCallAsHandled();
return epmPackageResponse;
}
if (path === epmRouteService.getFilePath('/package/nginx/0.3.7/docs/README.md')) {
markApiCallAsHandled();
return packageReadMe;
}
if (path === fleetSetupRouteService.getFleetSetupPath()) {
markApiCallAsHandled();
return agentsSetupResponse;
}
if (path === packagePolicyRouteService.getListPath()) {
markApiCallAsHandled();
return packagePoliciesResponse;
}
if (path === agentPolicyRouteService.getListPath()) {
markApiCallAsHandled();
return agentPoliciesResponse;
}
const err = new Error(`API [GET ${path}] is not MOCKED!`);
// eslint-disable-next-line no-console
console.log(err);
console.error(err);
throw err;
}
});
return {
waitForApi() {
return new Promise((resolve) => {
if (inflightApiCalls > 0) {
apiDoneListeners.push(resolve);
} else {
resolve();
}
});
},
};
};

View file

@ -43,6 +43,7 @@ import { Content } from './content';
import './index.scss';
import { useUIExtension } from '../../../../hooks/use_ui_extension';
import { PLUGIN_ID } from '../../../../../../../common/constants';
import { pkgKeyFromPackageInfo } from '../../../../services/pkg_key_from_package_info';
export const DEFAULT_PANEL: DetailViewPanelName = 'overview';
@ -315,7 +316,7 @@ export function Detail() {
isSelected: panelId === panel,
'data-test-subj': `tab-${panelId}`,
href: getHref('integration_details', {
pkgkey: `${packageInfo?.name}-${packageInfo?.version}`,
pkgkey: pkgKeyFromPackageInfo(packageInfo || {}),
panel: panelId,
}),
};

View file

@ -36,8 +36,8 @@ const IntegrationDetailsLink = memo<{
return (
<EuiLink
className="eui-textTruncate"
href={getHref('edit_integration', {
policyId: packagePolicy.policy_id,
data-test-subj="integrationNameLink"
href={getHref('integration_policy_edit', {
packagePolicyId: packagePolicy.id,
})}
>

View file

@ -0,0 +1,17 @@
/*
* 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 { useRouteMatch } from 'react-router-dom';
import { EditPackagePolicyForm } from '../../../agent_policy/edit_package_policy_page';
export const Policy = memo(() => {
const {
params: { packagePolicyId },
} = useRouteMatch<{ packagePolicyId: string }>();
return <EditPackagePolicyForm packagePolicyId={packagePolicyId} from="package-edit" />;
});

View file

@ -0,0 +1,11 @@
/*
* 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.
*/
export const pkgKeyFromPackageInfo = <T extends { name: string; version: string }>(
packageInfo: T
): string => {
return `${packageInfo.name}-${packageInfo.version}`;
};