mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Synthetics] Monitor edit page 404 state (#153847)
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Abdul Wahab Zahid <awahab07@yahoo.com>
This commit is contained in:
parent
ef4afdd64d
commit
fcfd414ee1
11 changed files with 182 additions and 4 deletions
|
@ -21,4 +21,5 @@ export enum SYNTHETICS_API_URLS {
|
|||
JOURNEY_FAILED_STEPS = `/internal/synthetics/journeys/failed_steps`,
|
||||
NETWORK_EVENTS = `/internal/synthetics/network_events`,
|
||||
JOURNEY_SCREENSHOT = `/internal/synthetics/journey/screenshot/{checkGroup}/{stepIndex}`,
|
||||
DELETE_PACKAGE_POLICY = `/internal/synthetics/monitor/policy/{packagePolicyId}`,
|
||||
}
|
||||
|
|
|
@ -50,7 +50,9 @@ journey('AlertingDefaults', async ({ page, params }) => {
|
|||
step(
|
||||
'Fill text=Webhook URLCreate a Slack Webhook URL(opens in a new tab or window) >> input[type="text"]',
|
||||
async () => {
|
||||
await page.click(byTestId('webhookButton'));
|
||||
if (await page.isVisible(byTestId('webhookButton'))) {
|
||||
await page.click(byTestId('webhookButton'));
|
||||
}
|
||||
await page.fill(
|
||||
'text=Webhook URLCreate a Slack Webhook URL(opens in a new tab or window) >> input[type="text"]',
|
||||
'https://www.slack.com'
|
||||
|
|
|
@ -81,8 +81,12 @@ journey('AddPrivateLocationMonitor', async ({ page, params }) => {
|
|||
});
|
||||
|
||||
step('Integration edit button leads to correct Synthetics edit page', async () => {
|
||||
const url = page.url();
|
||||
const policyId = url.split('edit-integration/').pop();
|
||||
const btn = await page.locator(byTestId('syntheticsEditMonitorButton'));
|
||||
expect(await btn.getAttribute('href')).toBe(`/app/synthetics/edit-monitor/${monitorId}`);
|
||||
expect(await btn.getAttribute('href')).toBe(
|
||||
`/app/synthetics/edit-monitor/${monitorId}?packagePolicyId=${policyId}`
|
||||
);
|
||||
await page.click('text="Edit in Synthetics"');
|
||||
|
||||
await page.waitForSelector('h1:has-text("Edit Monitor")');
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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, { useEffect, useState } from 'react';
|
||||
import { EuiButton, EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui';
|
||||
import { useFetcher } from '@kbn/observability-plugin/public';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useGetUrlParams, useUrlParams } from '../../hooks';
|
||||
import { deletePackagePolicy } from '../../state/monitor_management/api';
|
||||
import { MonitorNotFoundPage } from '../monitor_details/monitor_not_found_page';
|
||||
|
||||
export const EditMonitorNotFound: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<LeftoverIntegrationFound />
|
||||
<EuiSpacer size="m" />
|
||||
<MonitorNotFoundPage />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const LeftoverIntegrationFound: React.FC = () => {
|
||||
const { packagePolicyId } = useGetUrlParams();
|
||||
const updateUrlParams = useUrlParams()[1];
|
||||
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
const { data, loading } = useFetcher(() => {
|
||||
if (!packagePolicyId || !isDeleting) return;
|
||||
return deletePackagePolicy(packagePolicyId);
|
||||
}, [isDeleting, packagePolicyId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isDeleting && data && !loading) {
|
||||
updateUrlParams({ packagePolicyId: undefined }, true);
|
||||
setIsDeleting(false);
|
||||
}
|
||||
}, [data, isDeleting, loading, updateUrlParams]);
|
||||
|
||||
if (!packagePolicyId) return null;
|
||||
|
||||
return (
|
||||
<EuiCallOut title="Leftover integration found" color="warning" iconType="help">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.synthetics.leftOver.errors.title"
|
||||
defaultMessage="Please click on the button below to delete the integration. Normally this should not happen.
|
||||
Since the monitor has been deleted, the integration was supposed to be deleted automatically. If
|
||||
this happens often, report it by "
|
||||
/>
|
||||
<EuiLink
|
||||
data-test-subj="syntheticsLeftoverIntegrationFoundCreatingAnIssueLink"
|
||||
href="https://github.com/elastic/kibana/issues/new/choose"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.synthetics.leftOver.errors.createIssue"
|
||||
defaultMessage="creating an issue."
|
||||
/>
|
||||
</EuiLink>
|
||||
</p>
|
||||
<EuiButton
|
||||
data-test-subj="syntheticsUseMonitorNotFoundDeleteIntegrationButton"
|
||||
color="danger"
|
||||
isLoading={loading && isDeleting}
|
||||
onClick={() => {
|
||||
setIsDeleting(true);
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.synthetics.leftOver.errors.delete"
|
||||
defaultMessage="Delete integration"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiCallOut>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 { useEffect } from 'react';
|
||||
import { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser';
|
||||
import { useGetUrlParams, useUrlParams } from '../../../hooks';
|
||||
|
||||
export const useMonitorNotFound = (error?: IHttpFetchError<ResponseErrorBody>, id?: string) => {
|
||||
const { packagePolicyId } = useGetUrlParams();
|
||||
const updateUrlParams = useUrlParams()[1];
|
||||
|
||||
useEffect(() => {
|
||||
if (id && packagePolicyId && !error) {
|
||||
updateUrlParams({ packagePolicyId: undefined }, true);
|
||||
}
|
||||
}, [error, id, packagePolicyId, updateUrlParams]);
|
||||
|
||||
if (!error) return null;
|
||||
return error.body?.statusCode === 404;
|
||||
};
|
|
@ -11,6 +11,8 @@ import { useDispatch, useSelector } from 'react-redux';
|
|||
import { EuiEmptyPrompt } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useTrackPageview, useFetcher } from '@kbn/observability-plugin/public';
|
||||
import { IHttpFetchError, ResponseErrorBody } from '@kbn/core-http-browser';
|
||||
import { EditMonitorNotFound } from './edit_monitor_not_found';
|
||||
import { LoadingState } from '../monitors_page/overview/overview/monitor_detail_flyout';
|
||||
import { ConfigKey, SourceType } from '../../../../../common/runtime_types';
|
||||
import { getServiceLocations, selectServiceLocationsState } from '../../state';
|
||||
|
@ -22,6 +24,7 @@ import { MonitorDetailsLinkPortal } from './monitor_details_portal';
|
|||
import { useMonitorAddEditBreadcrumbs } from './use_breadcrumbs';
|
||||
import { getMonitorAPI } from '../../state/monitor_management/api';
|
||||
import { EDIT_MONITOR_STEPS } from './steps/step_config';
|
||||
import { useMonitorNotFound } from './hooks/use_monitor_not_found';
|
||||
|
||||
export const MonitorEditPage: React.FC = () => {
|
||||
useTrackPageview({ app: 'synthetics', path: 'edit-monitor' });
|
||||
|
@ -41,6 +44,15 @@ export const MonitorEditPage: React.FC = () => {
|
|||
return getMonitorAPI({ id: monitorId });
|
||||
}, []);
|
||||
|
||||
const monitorNotFoundError = useMonitorNotFound(
|
||||
error as IHttpFetchError<ResponseErrorBody>,
|
||||
data?.id
|
||||
);
|
||||
|
||||
if (monitorNotFoundError) {
|
||||
return <EditMonitorNotFound />;
|
||||
}
|
||||
|
||||
const isReadOnly = data?.attributes[ConfigKey.MONITOR_SOURCE_TYPE] === SourceType.PROJECT;
|
||||
const projectId = data?.attributes[ConfigKey.PROJECT_ID];
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
SyntheticsMonitor,
|
||||
SyntheticsMonitorWithId,
|
||||
} from '../../../../../common/runtime_types';
|
||||
import { API_URLS } from '../../../../../common/constants';
|
||||
import { API_URLS, SYNTHETICS_API_URLS } from '../../../../../common/constants';
|
||||
import { DecryptedSyntheticsMonitorSavedObject } from '../../../../../common/types';
|
||||
|
||||
export const createMonitorAPI = async ({
|
||||
|
@ -46,3 +46,13 @@ export const fetchServiceAPIKey = async (): Promise<{
|
|||
}> => {
|
||||
return await apiService.get(API_URLS.SYNTHETICS_APIKEY);
|
||||
};
|
||||
|
||||
export const deletePackagePolicy = async (
|
||||
packagePolicyId: string
|
||||
): Promise<{
|
||||
apiKey: { encoded: string };
|
||||
}> => {
|
||||
return await apiService.delete(
|
||||
SYNTHETICS_API_URLS.DELETE_PACKAGE_POLICY.replace('{packagePolicyId}', packagePolicyId)
|
||||
);
|
||||
};
|
||||
|
|
|
@ -36,6 +36,7 @@ export interface SyntheticsUrlParams {
|
|||
schedules?: string[];
|
||||
groupBy?: MonitorOverviewState['groupBy']['field'];
|
||||
groupOrderBy?: MonitorOverviewState['groupBy']['order'];
|
||||
packagePolicyId?: string;
|
||||
}
|
||||
|
||||
const { ABSOLUTE_DATE_RANGE_START, ABSOLUTE_DATE_RANGE_END, SEARCH, FILTERS, STATUS_FILTER } =
|
||||
|
@ -93,9 +94,11 @@ export const getSupportedUrlParams = (params: {
|
|||
schedules,
|
||||
groupBy,
|
||||
groupOrderBy,
|
||||
packagePolicyId,
|
||||
} = filteredParams;
|
||||
|
||||
return {
|
||||
packagePolicyId: packagePolicyId || undefined,
|
||||
groupBy: groupBy as MonitorOverviewState['groupBy']['field'],
|
||||
groupOrderBy: groupOrderBy as MonitorOverviewState['groupBy']['order'],
|
||||
pagination,
|
||||
|
|
|
@ -45,7 +45,11 @@ export const SyntheticsPolicyEditExtensionWrapper = memo<PackagePolicyEditExtens
|
|||
return (
|
||||
<EuiCallOut>
|
||||
<p>{EDIT_IN_SYNTHETICS_DESC}</p>
|
||||
<EuiButton isLoading={!url} href={url} data-test-subj="syntheticsEditMonitorButton">
|
||||
<EuiButton
|
||||
isLoading={!url}
|
||||
href={url + `?packagePolicyId=${currentPolicy.id}`}
|
||||
data-test-subj="syntheticsEditMonitorButton"
|
||||
>
|
||||
{EDIT_IN_SYNTHETICS_LABEL}
|
||||
</EuiButton>
|
||||
</EuiCallOut>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { deletePackagePolicyRoute } from './monitor_cruds/delete_integration';
|
||||
import { createJourneyScreenshotRoute } from './pings/journey_screenshots';
|
||||
import { createJourneyScreenshotBlocksRoute } from './pings/journey_screenshot_blocks';
|
||||
import { createLastSuccessfulCheckRoute } from './pings/last_successful_check';
|
||||
|
@ -89,6 +90,7 @@ export const syntheticsAppRestApiRoutes: SyntheticsRestApiRouteFactory[] = [
|
|||
createJourneyFailedStepsRoute,
|
||||
createNetworkEventsRoute,
|
||||
createJourneyScreenshotRoute,
|
||||
deletePackagePolicyRoute,
|
||||
addPrivateLocationRoute,
|
||||
deletePrivateLocationRoute,
|
||||
getPrivateLocationsRoute,
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 { schema } from '@kbn/config-schema';
|
||||
import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types';
|
||||
import { SYNTHETICS_API_URLS } from '../../../common/constants';
|
||||
|
||||
export const deletePackagePolicyRoute: SyntheticsRestApiRouteFactory = () => ({
|
||||
method: 'DELETE',
|
||||
path: SYNTHETICS_API_URLS.DELETE_PACKAGE_POLICY,
|
||||
validate: {
|
||||
params: schema.object({
|
||||
packagePolicyId: schema.string({ minLength: 1, maxLength: 1024 }),
|
||||
}),
|
||||
},
|
||||
handler: async ({ request, savedObjectsClient, server, uptimeEsClient }): Promise<any> => {
|
||||
const { packagePolicyId } = request.params;
|
||||
|
||||
const response = await server.fleet.packagePolicyService.delete(
|
||||
savedObjectsClient,
|
||||
uptimeEsClient.baseESClient,
|
||||
[packagePolicyId],
|
||||
{
|
||||
force: true,
|
||||
}
|
||||
);
|
||||
if (response?.[0].success) {
|
||||
return response;
|
||||
} else {
|
||||
throw new Error(response?.[0].body?.message);
|
||||
}
|
||||
},
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue