Skip to content

Commit

Permalink
Merge branch 'main' into 150985-infrastructure-ui-hosts-view-add-link…
Browse files Browse the repository at this point in the history
…s-to-apm-and-uptime-for-a-single-host
  • Loading branch information
jennypavlova committed Apr 4, 2023
2 parents 789fb3d + 711b8e0 commit 069acdd
Show file tree
Hide file tree
Showing 16 changed files with 272 additions and 42 deletions.
61 changes: 39 additions & 22 deletions x-pack/plugins/observability/public/pages/rules/rules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ export function RulesPage() {
const { ObservabilityPageTemplate } = usePluginContext();
const history = useHistory();

useBreadcrumbs([
{
text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', {
defaultMessage: 'Alerts',
}),
href: http.basePath.prepend('/app/observability/alerts'),
},
{
text: i18n.translate('xpack.observability.breadcrumbs.rulesLinkText', {
defaultMessage: 'Rules',
}),
},
]);

const filteredRuleTypes = useGetFilteredRuleTypes();
const { ruleTypes } = useLoadRuleTypes({
filteredRuleTypes,
Expand All @@ -48,42 +62,41 @@ export function RulesPage() {
useHashQuery: false,
});

const { status, lastResponse } = urlStateStorage.get<{
status: RuleStatus[];
const { lastResponse, search, status, type } = urlStateStorage.get<{
lastResponse: string[];
}>('_a') || { status: [], lastResponse: [] };
search: string;
status: RuleStatus[];
type: string[];
}>('_a') || { lastResponse: [], search: '', status: [], type: [] };

const [stateStatus, setStatus] = useState<RuleStatus[]>(status);
const [stateLastResponse, setLastResponse] = useState<string[]>(lastResponse);
const [stateSearch, setSearch] = useState<string>(search);
const [stateStatus, setStatus] = useState<RuleStatus[]>(status);
const [stateType, setType] = useState<string[]>(type);

const [stateRefresh, setRefresh] = useState(new Date());

const [addRuleFlyoutVisibility, setAddRuleFlyoutVisibility] = useState(false);

useBreadcrumbs([
{
text: i18n.translate('xpack.observability.breadcrumbs.alertsLinkText', {
defaultMessage: 'Alerts',
}),
href: http.basePath.prepend('/app/observability/alerts'),
},
{
text: i18n.translate('xpack.observability.breadcrumbs.rulesLinkText', {
defaultMessage: 'Rules',
}),
},
]);

const handleStatusFilterChange = (newStatus: RuleStatus[]) => {
setStatus(newStatus);
urlStateStorage.set('_a', { status: newStatus, lastResponse });
return { lastResponse: stateLastResponse || [], status: newStatus };
urlStateStorage.set('_a', { lastResponse, search, status: newStatus, type });
};

const handleLastRunOutcomeFilterChange = (newLastResponse: string[]) => {
setRefresh(new Date());
setLastResponse(newLastResponse);
urlStateStorage.set('_a', { status, lastResponse: newLastResponse });
return { lastResponse: newLastResponse, status: stateStatus || [] };
urlStateStorage.set('_a', { lastResponse: newLastResponse, search, status, type });
};

const handleTypeFilterChange = (newType: string[]) => {
setType(newType);
urlStateStorage.set('_a', { lastResponse, search, status, type: newType });
};

const handleSearchFilterChange = (newSearch: string) => {
setSearch(newSearch);
urlStateStorage.set('_a', { lastResponse, search: newSearch, status, type });
};

return (
Expand Down Expand Up @@ -132,6 +145,8 @@ export function RulesPage() {
rulesListKey="observability_rulesListColumns"
showActionFilter={false}
statusFilter={stateStatus}
searchFilter={stateSearch}
typeFilter={stateType}
visibleColumns={[
'ruleName',
'ruleExecutionStatusLastDate',
Expand All @@ -140,7 +155,9 @@ export function RulesPage() {
'ruleExecutionState',
]}
onLastRunOutcomeFilterChange={handleLastRunOutcomeFilterChange}
onSearchFilterChange={handleSearchFilterChange}
onStatusFilterChange={handleStatusFilterChange}
onTypeFilterChange={handleTypeFilterChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
}
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")');
Expand Down
Original file line number Diff line number Diff line change
@@ -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>
);
};
Original file line number Diff line number Diff line change
@@ -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;
};
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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' });
Expand All @@ -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];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 ({
Expand Down Expand Up @@ -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)
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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 } =
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/synthetics/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -89,6 +90,7 @@ export const syntheticsAppRestApiRoutes: SyntheticsRestApiRouteFactory[] = [
createJourneyFailedStepsRoute,
createNetworkEventsRoute,
createJourneyScreenshotRoute,
deletePackagePolicyRoute,
addPrivateLocationRoute,
deletePrivateLocationRoute,
getPrivateLocationsRoute,
Expand Down
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 069acdd

Please sign in to comment.