- {"absoluteDateRangeStart":20,"absoluteDateRangeEnd":20,"autorefreshInterval":60000,"autorefreshIsPaused":false,"dateRangeStart":"now-12","dateRangeEnd":"now","filters":"","search":"","selectedPingStatus":"","statusFilter":"","pagination":"foo","focusConnectorField":false}
+ {"pagination":"foo","absoluteDateRangeStart":20,"absoluteDateRangeEnd":20,"autorefreshInterval":60000,"autorefreshIsPaused":false,"dateRangeStart":"now-12","dateRangeEnd":"now","filters":"","search":"","statusFilter":"","focusConnectorField":false}
- {"absoluteDateRangeStart":20,"absoluteDateRangeEnd":20,"autorefreshInterval":60000,"autorefreshIsPaused":false,"dateRangeStart":"now-15m","dateRangeEnd":"now","filters":"","search":"","selectedPingStatus":"","statusFilter":"","focusConnectorField":false}
+ {"absoluteDateRangeStart":20,"absoluteDateRangeEnd":20,"autorefreshInterval":60000,"autorefreshIsPaused":false,"dateRangeStart":"now-15m","dateRangeEnd":"now","filters":"","search":"","statusFilter":"","focusConnectorField":false}
;
-}
-
export const useFilterUpdate = (
fieldName?: string,
values?: string[],
shouldUpdateUrl: boolean = true
-): SelectedFilters => {
+) => {
const [getUrlParams, updateUrl] = useUrlParams();
const { filters: currentFilters } = getUrlParams();
@@ -62,12 +49,4 @@ export const useFilterUpdate = (
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fieldName, values]);
-
- return {
- selectedTags: filterKueries.get('tags') || [],
- selectedPorts: filterKueries.get('url.port') || [],
- selectedSchemes: filterKueries.get('monitor.type') || [],
- selectedLocations: filterKueries.get('observer.geo.name') || [],
- selectedFilters: filterKueries,
- };
};
diff --git a/x-pack/plugins/uptime/public/hooks/use_selected_filters.ts b/x-pack/plugins/uptime/public/hooks/use_selected_filters.ts
new file mode 100644
index 000000000000..ac4658f9a229
--- /dev/null
+++ b/x-pack/plugins/uptime/public/hooks/use_selected_filters.ts
@@ -0,0 +1,28 @@
+/*
+ * 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 { useMemo } from 'react';
+import { useGetUrlParams } from './use_url_params';
+
+export const useSelectedFilters = () => {
+ const { filters } = useGetUrlParams();
+
+ return useMemo(() => {
+ let selectedFilters: Map;
+ try {
+ selectedFilters = new Map(JSON.parse(filters));
+ } catch {
+ selectedFilters = new Map();
+ }
+
+ return {
+ selectedTags: selectedFilters.get('tags') || [],
+ selectedPorts: selectedFilters.get('url.port') || [],
+ selectedSchemes: selectedFilters.get('monitor.type') || [],
+ selectedLocations: selectedFilters.get('observer.geo.name') || [],
+ };
+ }, [filters]);
+};
diff --git a/x-pack/plugins/uptime/public/lib/__mocks__/uptime_store.mock.ts b/x-pack/plugins/uptime/public/lib/__mocks__/uptime_store.mock.ts
index 313120639b38..f985fd42f31c 100644
--- a/x-pack/plugins/uptime/public/lib/__mocks__/uptime_store.mock.ts
+++ b/x-pack/plugins/uptime/public/lib/__mocks__/uptime_store.mock.ts
@@ -66,7 +66,6 @@ export const mockStore = {
loading: false,
pingList: {
total: 0,
- locations: [],
pings: [],
},
},
diff --git a/x-pack/plugins/uptime/public/lib/helper/__tests__/stringify_url_params.test.ts b/x-pack/plugins/uptime/public/lib/helper/__tests__/stringify_url_params.test.ts
index 8cf35c728fc0..2320b1011177 100644
--- a/x-pack/plugins/uptime/public/lib/helper/__tests__/stringify_url_params.test.ts
+++ b/x-pack/plugins/uptime/public/lib/helper/__tests__/stringify_url_params.test.ts
@@ -16,11 +16,10 @@ describe('stringifyUrlParams', () => {
filters: 'monitor.id: bar',
focusConnectorField: true,
search: 'monitor.id: foo',
- selectedPingStatus: 'down',
statusFilter: 'up',
});
expect(result).toMatchInlineSnapshot(
- `"?autorefreshInterval=50000&autorefreshIsPaused=false&dateRangeStart=now-15m&dateRangeEnd=now&filters=monitor.id%3A%20bar&focusConnectorField=true&search=monitor.id%3A%20foo&selectedPingStatus=down&statusFilter=up"`
+ `"?autorefreshInterval=50000&autorefreshIsPaused=false&dateRangeStart=now-15m&dateRangeEnd=now&filters=monitor.id%3A%20bar&focusConnectorField=true&search=monitor.id%3A%20foo&statusFilter=up"`
);
});
@@ -34,7 +33,6 @@ describe('stringifyUrlParams', () => {
filters: 'monitor.id: bar',
focusConnectorField: false,
search: undefined,
- selectedPingStatus: undefined,
statusFilter: '',
pagination: undefined,
},
@@ -46,6 +44,5 @@ describe('stringifyUrlParams', () => {
expect(result.includes('pagination')).toBeFalsy();
expect(result.includes('search')).toBeFalsy();
- expect(result.includes('selectedPingStatus')).toBeFalsy();
});
});
diff --git a/x-pack/plugins/uptime/public/lib/helper/test_helpers.ts b/x-pack/plugins/uptime/public/lib/helper/test_helpers.ts
index d18f2aa2a4e7..faa1b968d254 100644
--- a/x-pack/plugins/uptime/public/lib/helper/test_helpers.ts
+++ b/x-pack/plugins/uptime/public/lib/helper/test_helpers.ts
@@ -8,6 +8,7 @@
import moment from 'moment';
import { Moment } from 'moment-timezone';
+import * as redux from 'react-redux';
export function mockMoment() {
// avoid timezone issues
@@ -20,3 +21,9 @@ export function mockMoment() {
return `15 minutes ago`;
});
}
+
+export function mockReduxHooks(response?: any) {
+ jest.spyOn(redux, 'useDispatch').mockReturnValue(jest.fn());
+
+ jest.spyOn(redux, 'useSelector').mockReturnValue(response);
+}
diff --git a/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/__snapshots__/get_supported_url_params.test.ts.snap b/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/__snapshots__/get_supported_url_params.test.ts.snap
index 7452915d9407..c9e0167d9c21 100644
--- a/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/__snapshots__/get_supported_url_params.test.ts.snap
+++ b/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/__snapshots__/get_supported_url_params.test.ts.snap
@@ -12,7 +12,6 @@ Object {
"focusConnectorField": false,
"pagination": undefined,
"search": "",
- "selectedPingStatus": "",
"statusFilter": "",
}
`;
@@ -29,7 +28,6 @@ Object {
"focusConnectorField": false,
"pagination": undefined,
"search": "",
- "selectedPingStatus": "",
"statusFilter": "",
}
`;
@@ -46,7 +44,6 @@ Object {
"focusConnectorField": false,
"pagination": undefined,
"search": "monitor.status: down",
- "selectedPingStatus": "up",
"statusFilter": "",
}
`;
@@ -63,7 +60,6 @@ Object {
"focusConnectorField": false,
"pagination": undefined,
"search": "",
- "selectedPingStatus": "",
"statusFilter": "",
}
`;
@@ -80,7 +76,6 @@ Object {
"focusConnectorField": false,
"pagination": undefined,
"search": "",
- "selectedPingStatus": "",
"statusFilter": "",
}
`;
diff --git a/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/get_supported_url_params.test.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/get_supported_url_params.test.ts
index bdbac9086846..4e7e0c7a6aec 100644
--- a/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/get_supported_url_params.test.ts
+++ b/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/get_supported_url_params.test.ts
@@ -46,7 +46,6 @@ describe('getSupportedUrlParams', () => {
DATE_RANGE_END,
FILTERS,
SEARCH,
- SELECTED_PING_LIST_STATUS,
STATUS_FILTER,
} = CLIENT_DEFAULTS;
const result = getSupportedUrlParams({});
@@ -62,7 +61,6 @@ describe('getSupportedUrlParams', () => {
focusConnectorField: false,
pagination: undefined,
search: SEARCH,
- selectedPingStatus: SELECTED_PING_LIST_STATUS,
statusFilter: STATUS_FILTER,
});
});
diff --git a/x-pack/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.ts
index ff621f994bf4..f57d9983a84e 100644
--- a/x-pack/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.ts
+++ b/x-pack/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.ts
@@ -19,7 +19,6 @@ export interface UptimeUrlParams {
pagination?: string;
filters: string;
search: string;
- selectedPingStatus: string;
statusFilter: string;
focusConnectorField?: boolean;
}
@@ -32,7 +31,6 @@ const {
DATE_RANGE_START,
DATE_RANGE_END,
SEARCH,
- SELECTED_PING_LIST_STATUS,
FILTERS,
STATUS_FILTER,
} = CLIENT_DEFAULTS;
@@ -73,13 +71,13 @@ export const getSupportedUrlParams = (params: {
dateRangeEnd,
filters,
search,
- selectedPingStatus,
statusFilter,
pagination,
focusConnectorField,
} = filteredParams;
return {
+ pagination,
absoluteDateRangeStart: parseAbsoluteDate(
dateRangeStart || DATE_RANGE_START,
ABSOLUTE_DATE_RANGE_START
@@ -95,10 +93,7 @@ export const getSupportedUrlParams = (params: {
dateRangeEnd: dateRangeEnd || DATE_RANGE_END,
filters: filters || FILTERS,
search: search || SEARCH,
- selectedPingStatus:
- selectedPingStatus === undefined ? SELECTED_PING_LIST_STATUS : selectedPingStatus,
statusFilter: statusFilter || STATUS_FILTER,
- pagination,
focusConnectorField: !!focusConnectorField,
};
};
diff --git a/x-pack/plugins/uptime/public/pages/monitor.tsx b/x-pack/plugins/uptime/public/pages/monitor.tsx
index d0173d2d9622..3fd102c5eb60 100644
--- a/x-pack/plugins/uptime/public/pages/monitor.tsx
+++ b/x-pack/plugins/uptime/public/pages/monitor.tsx
@@ -82,7 +82,7 @@ export const MonitorPage: React.FC = () => {
-
+
>
);
};
diff --git a/x-pack/plugins/uptime/public/state/api/__tests__/snapshot.test.ts b/x-pack/plugins/uptime/public/state/api/__tests__/snapshot.test.ts
index a0a12be5715d..f3ddbd8be02b 100644
--- a/x-pack/plugins/uptime/public/state/api/__tests__/snapshot.test.ts
+++ b/x-pack/plugins/uptime/public/state/api/__tests__/snapshot.test.ts
@@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { HttpFetchError } from 'src/core/public';
import { fetchSnapshotCount } from '../snapshot';
import { apiService } from '../utils';
-import { HttpFetchError } from 'src/core/public';
describe('snapshot API', () => {
let fetchMock: jest.SpyInstance>;
@@ -15,8 +15,9 @@ describe('snapshot API', () => {
beforeEach(() => {
apiService.http = {
get: jest.fn(),
+ fetch: jest.fn(),
} as any;
- fetchMock = jest.spyOn(apiService.http, 'get');
+ fetchMock = jest.spyOn(apiService.http, 'fetch');
mockResponse = { up: 3, down: 12, total: 15 };
});
@@ -31,7 +32,9 @@ describe('snapshot API', () => {
dateRangeEnd: 'now',
filters: 'monitor.id:"auto-http-0X21EE76EAC459873F"',
});
- expect(fetchMock).toHaveBeenCalledWith('/api/uptime/snapshot/count', {
+ expect(fetchMock).toHaveBeenCalledWith({
+ asResponse: false,
+ path: '/api/uptime/snapshot/count',
query: {
dateRangeEnd: 'now',
dateRangeStart: 'now-15m',
diff --git a/x-pack/plugins/uptime/public/state/api/journey.ts b/x-pack/plugins/uptime/public/state/api/journey.ts
index e9ea9d8744bc..1aeeb485e481 100644
--- a/x-pack/plugins/uptime/public/state/api/journey.ts
+++ b/x-pack/plugins/uptime/public/state/api/journey.ts
@@ -20,3 +20,40 @@ export async function fetchJourneySteps(
SyntheticsJourneyApiResponseType
)) as SyntheticsJourneyApiResponse;
}
+
+export async function fetchJourneysFailedSteps({
+ checkGroups,
+}: {
+ checkGroups: string[];
+}): Promise {
+ return (await apiService.get(
+ `/api/uptime/journeys/failed_steps`,
+ { checkGroups },
+ SyntheticsJourneyApiResponseType
+ )) as SyntheticsJourneyApiResponse;
+}
+
+export async function getJourneyScreenshot(imgSrc: string) {
+ try {
+ const imgRequest = new Request(imgSrc);
+
+ const response = await fetch(imgRequest);
+
+ if (response.status !== 200) {
+ return null;
+ }
+
+ const imgBlob = await response.blob();
+
+ const stepName = response.headers.get('caption-name');
+ const maxSteps = response.headers.get('max-steps');
+
+ return {
+ stepName,
+ maxSteps: Number(maxSteps ?? 0),
+ src: URL.createObjectURL(imgBlob),
+ };
+ } catch (e) {
+ return null;
+ }
+}
diff --git a/x-pack/plugins/uptime/public/state/api/utils.ts b/x-pack/plugins/uptime/public/state/api/utils.ts
index e0cec56dd52c..965cbefd1311 100644
--- a/x-pack/plugins/uptime/public/state/api/utils.ts
+++ b/x-pack/plugins/uptime/public/state/api/utils.ts
@@ -59,8 +59,8 @@ class ApiService {
return ApiService.instance;
}
- public async get(apiUrl: string, params?: HttpFetchQuery, decodeType?: any) {
- const response = await this._http!.get(apiUrl, { query: params });
+ public async get(apiUrl: string, params?: HttpFetchQuery, decodeType?: any, asResponse = false) {
+ const response = await this._http!.fetch({ path: apiUrl, query: params, asResponse });
if (decodeType) {
const decoded = decodeType.decode(response);
diff --git a/x-pack/plugins/uptime/public/state/reducers/ping_list.ts b/x-pack/plugins/uptime/public/state/reducers/ping_list.ts
index 291051be9af6..1fbdc6302f11 100644
--- a/x-pack/plugins/uptime/public/state/reducers/ping_list.ts
+++ b/x-pack/plugins/uptime/public/state/reducers/ping_list.ts
@@ -17,7 +17,6 @@ export interface PingListState {
const initialState: PingListState = {
pingList: {
total: 0,
- locations: [],
pings: [],
},
loading: false,
@@ -36,6 +35,7 @@ export const pingListReducer = handleActions(
...state,
pingList: { ...action.payload },
loading: false,
+ error: undefined,
}),
[String(getPingsFail)]: (state, action: Action) => ({
diff --git a/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts b/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts
index a59e0be5cdf3..93f757b6baa8 100644
--- a/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts
+++ b/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts
@@ -65,7 +65,6 @@ describe('state selectors', () => {
loading: false,
pingList: {
total: 0,
- locations: [],
pings: [],
},
},
diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts
index 9b28d58c7e8c..709c13c40f9e 100644
--- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts
@@ -127,15 +127,6 @@ describe('getAll', () => {
Array [
Object {
"body": Object {
- "aggs": Object {
- "locations": Object {
- "terms": Object {
- "field": "observer.geo.name",
- "missing": "N/A",
- "size": 1000,
- },
- },
- },
"query": Object {
"bool": Object {
"filter": Array [
@@ -205,15 +196,6 @@ describe('getAll', () => {
Array [
Object {
"body": Object {
- "aggs": Object {
- "locations": Object {
- "terms": Object {
- "field": "observer.geo.name",
- "missing": "N/A",
- "size": 1000,
- },
- },
- },
"query": Object {
"bool": Object {
"filter": Array [
@@ -283,15 +265,6 @@ describe('getAll', () => {
Array [
Object {
"body": Object {
- "aggs": Object {
- "locations": Object {
- "terms": Object {
- "field": "observer.geo.name",
- "missing": "N/A",
- "size": 1000,
- },
- },
- },
"query": Object {
"bool": Object {
"filter": Array [
@@ -361,15 +334,6 @@ describe('getAll', () => {
Array [
Object {
"body": Object {
- "aggs": Object {
- "locations": Object {
- "terms": Object {
- "field": "observer.geo.name",
- "missing": "N/A",
- "size": 1000,
- },
- },
- },
"query": Object {
"bool": Object {
"filter": Array [
@@ -444,15 +408,6 @@ describe('getAll', () => {
Array [
Object {
"body": Object {
- "aggs": Object {
- "locations": Object {
- "terms": Object {
- "field": "observer.geo.name",
- "missing": "N/A",
- "size": 1000,
- },
- },
- },
"query": Object {
"bool": Object {
"filter": Array [
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_failed_steps.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_failed_steps.ts
new file mode 100644
index 000000000000..09c5f9d778ba
--- /dev/null
+++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_failed_steps.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { UMElasticsearchQueryFn } from '../adapters/framework';
+import { Ping } from '../../../common/runtime_types';
+
+interface GetJourneyStepsParams {
+ checkGroups: string[];
+}
+
+export const getJourneyFailedSteps: UMElasticsearchQueryFn = async ({
+ uptimeEsClient,
+ checkGroups,
+}) => {
+ const params = {
+ query: {
+ bool: {
+ filter: [
+ {
+ terms: {
+ 'synthetics.type': ['step/end'],
+ },
+ },
+ {
+ exists: {
+ field: 'synthetics.error',
+ },
+ },
+ {
+ terms: {
+ 'monitor.check_group': checkGroups,
+ },
+ },
+ ],
+ },
+ },
+ sort: [{ 'synthetics.step.index': { order: 'asc' } }, { '@timestamp': { order: 'asc' } }],
+ _source: {
+ excludes: ['synthetics.blob'],
+ },
+ size: 500,
+ };
+
+ const { body: result } = await uptimeEsClient.search({ body: params });
+
+ return (result.hits.hits.map((h) => {
+ const source = h._source as Ping & { '@timestamp': string };
+ return {
+ ...source,
+ timestamp: source['@timestamp'],
+ };
+ }) as unknown) as Ping;
+};
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts
index dacdcaff7dfd..38e8682bc7f5 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts
@@ -5,7 +5,7 @@
*/
import { UMElasticsearchQueryFn } from '../adapters/framework';
-import { ESSearchBody } from '../../../../../typings/elasticsearch';
+import { Ping } from '../../../common/runtime_types/ping';
interface GetJourneyScreenshotParams {
checkGroup: string;
@@ -16,7 +16,9 @@ export const getJourneyScreenshot: UMElasticsearchQueryFn<
GetJourneyScreenshotParams,
any
> = async ({ uptimeEsClient, checkGroup, stepIndex }) => {
- const params: ESSearchBody = {
+ const params = {
+ track_total_hits: true,
+ size: 0,
query: {
bool: {
filter: [
@@ -30,19 +32,38 @@ export const getJourneyScreenshot: UMElasticsearchQueryFn<
'synthetics.type': 'step/screenshot',
},
},
- {
- term: {
- 'synthetics.step.index': stepIndex,
- },
- },
],
},
},
- _source: ['synthetics.blob'],
+ aggs: {
+ step: {
+ filter: {
+ term: {
+ 'synthetics.step.index': stepIndex,
+ },
+ },
+ aggs: {
+ image: {
+ top_hits: {
+ size: 1,
+ _source: ['synthetics.blob', 'synthetics.step.name'],
+ },
+ },
+ },
+ },
+ },
};
const { body: result } = await uptimeEsClient.search({ body: params });
- if (!Array.isArray(result?.hits?.hits) || result.hits.hits.length < 1) {
+
+ if (result?.hits?.total.value < 1) {
return null;
}
- return result.hits.hits.map(({ _source }: any) => _source?.synthetics?.blob ?? null)[0];
+
+ const stepHit = result?.aggregations?.step.image.hits.hits[0]._source as Ping;
+
+ return {
+ blob: stepHit.synthetics?.blob ?? null,
+ stepName: stepHit?.synthetics?.step?.name ?? '',
+ totalSteps: result?.hits?.total.value,
+ };
};
diff --git a/x-pack/plugins/uptime/server/lib/requests/get_pings.ts b/x-pack/plugins/uptime/server/lib/requests/get_pings.ts
index 3b852ad1a738..cf7d6542a32b 100644
--- a/x-pack/plugins/uptime/server/lib/requests/get_pings.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/get_pings.ts
@@ -59,7 +59,7 @@ export const getPings: UMElasticsearchQueryFn = a
status,
sort,
size: sizeParam,
- location,
+ locations,
}) => {
const size = sizeParam ?? DEFAULT_PAGE_SIZE;
@@ -77,27 +77,17 @@ export const getPings: UMElasticsearchQueryFn = a
},
},
sort: [{ '@timestamp': { order: (sort ?? 'desc') as 'asc' | 'desc' } }],
- aggs: {
- locations: {
- terms: {
- field: 'observer.geo.name',
- missing: 'N/A',
- size: 1000,
- },
- },
- },
- ...(location ? { post_filter: { term: { 'observer.geo.name': location } } } : {}),
+ ...((locations ?? []).length > 0
+ ? { post_filter: { terms: { 'observer.geo.name': locations } } }
+ : {}),
};
const {
body: {
hits: { hits, total },
- aggregations: aggs,
},
} = await uptimeEsClient.search({ body: searchBody });
- const locations = aggs?.locations ?? { buckets: [{ key: 'N/A', doc_count: 0 }] };
-
const pings: Ping[] = hits.map((doc: any) => {
const { _id, _source } = doc;
// Calculate here the length of the content string in bytes, this is easier than in client JS, where
@@ -113,7 +103,6 @@ export const getPings: UMElasticsearchQueryFn = a
return {
total: total.value,
- locations: locations.buckets.map((bucket) => bucket.key as string),
pings,
};
};
diff --git a/x-pack/plugins/uptime/server/lib/requests/index.ts b/x-pack/plugins/uptime/server/lib/requests/index.ts
index 1806495d14cc..fd7e5f604171 100644
--- a/x-pack/plugins/uptime/server/lib/requests/index.ts
+++ b/x-pack/plugins/uptime/server/lib/requests/index.ts
@@ -20,6 +20,7 @@ import { getSnapshotCount } from './get_snapshot_counts';
import { getIndexStatus } from './get_index_status';
import { getJourneySteps } from './get_journey_steps';
import { getJourneyScreenshot } from './get_journey_screenshot';
+import { getJourneyFailedSteps } from './get_journey_failed_steps';
export const requests = {
getCerts,
@@ -37,6 +38,7 @@ export const requests = {
getSnapshotCount,
getIndexStatus,
getJourneySteps,
+ getJourneyFailedSteps,
getJourneyScreenshot,
};
diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts
index de44b2565a2f..a2475792edfb 100644
--- a/x-pack/plugins/uptime/server/rest_api/index.ts
+++ b/x-pack/plugins/uptime/server/rest_api/index.ts
@@ -24,6 +24,7 @@ import {
} from './monitors';
import { createGetMonitorDurationRoute } from './monitors/monitors_durations';
import { createGetIndexPatternRoute, createGetIndexStatusRoute } from './index_state';
+import { createJourneyFailedStepsRoute } from './pings/journeys';
export * from './types';
export { createRouteWithAuth } from './create_route_with_auth';
@@ -47,4 +48,5 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [
createGetMonitorDurationRoute,
createJourneyRoute,
createJourneyScreenshotRoute,
+ createJourneyFailedStepsRoute,
];
diff --git a/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts b/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts
index d00386d06c74..2a1a401ec315 100644
--- a/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts
+++ b/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts
@@ -5,12 +5,9 @@
*/
import { schema } from '@kbn/config-schema';
-import { isLeft } from 'fp-ts/lib/Either';
-import { PathReporter } from 'io-ts/lib/PathReporter';
import { UMServerLibs } from '../../lib/lib';
import { UMRestApiRouteFactory } from '../types';
import { API_URLS } from '../../../common/constants';
-import { GetPingsParamsType } from '../../../common/runtime_types';
export const createGetPingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({
method: 'GET',
@@ -19,7 +16,7 @@ export const createGetPingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) =
query: schema.object({
from: schema.string(),
to: schema.string(),
- location: schema.maybe(schema.string()),
+ locations: schema.maybe(schema.string()),
monitorId: schema.maybe(schema.string()),
index: schema.maybe(schema.number()),
size: schema.maybe(schema.number()),
@@ -28,17 +25,17 @@ export const createGetPingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) =
}),
},
handler: async ({ uptimeEsClient }, _context, request, response): Promise => {
- const { from, to, ...optional } = request.query;
- const params = GetPingsParamsType.decode({ dateRange: { from, to }, ...optional });
- if (isLeft(params)) {
- // eslint-disable-next-line no-console
- console.error(new Error(PathReporter.report(params).join(';')));
- return response.badRequest({ body: { message: 'Received invalid request parameters.' } });
- }
+ const { from, to, index, monitorId, status, sort, size, locations } = request.query;
const result = await libs.requests.getPings({
uptimeEsClient,
- ...params.right,
+ dateRange: { from, to },
+ index,
+ monitorId,
+ status,
+ sort,
+ size,
+ locations: locations ? JSON.parse(locations) : [],
});
return response.ok({
diff --git a/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts b/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts
index 161d7dd558fd..080fc8ab8f8e 100644
--- a/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts
+++ b/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts
@@ -29,10 +29,12 @@ export const createJourneyScreenshotRoute: UMRestApiRouteFactory = (libs: UMServ
return response.notFound();
}
return response.ok({
- body: Buffer.from(result, 'base64'),
+ body: Buffer.from(result.blob, 'base64'),
headers: {
'content-type': 'image/png',
'cache-control': 'max-age=600',
+ 'caption-name': result.stepName,
+ 'max-steps': result.totalSteps,
},
});
},
diff --git a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts
index 5b8583ea0322..f46cf3b99095 100644
--- a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts
+++ b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts
@@ -31,3 +31,27 @@ export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) =>
});
},
});
+
+export const createJourneyFailedStepsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({
+ method: 'GET',
+ path: '/api/uptime/journeys/failed_steps',
+ validate: {
+ query: schema.object({
+ checkGroups: schema.arrayOf(schema.string()),
+ }),
+ },
+ handler: async ({ uptimeEsClient }, _context, request, response) => {
+ const { checkGroups } = request.query;
+ const result = await libs.requests.getJourneyFailedSteps({
+ uptimeEsClient,
+ checkGroups,
+ });
+
+ return response.ok({
+ body: {
+ checkGroups,
+ steps: result,
+ },
+ });
+ },
+});
diff --git a/x-pack/test/api_integration/apis/uptime/rest/ping_list.ts b/x-pack/test/api_integration/apis/uptime/rest/ping_list.ts
index 7371742e74cf..a00010c0d94b 100644
--- a/x-pack/test/api_integration/apis/uptime/rest/ping_list.ts
+++ b/x-pack/test/api_integration/apis/uptime/rest/ping_list.ts
@@ -30,10 +30,9 @@ export default function ({ getService }: FtrProviderContext) {
const apiResponse = await supertest.get(`/api/uptime/pings?from=${from}&to=${to}&size=10`);
- const { total, locations, pings } = decodePingsResponseData(apiResponse.body);
+ const { total, pings } = decodePingsResponseData(apiResponse.body);
expect(total).to.be(2000);
- expect(locations).to.eql(['mpls']);
expect(pings).length(10);
expect(pings.map(({ monitor: { id } }) => id)).to.eql([
'0074-up',
@@ -58,10 +57,9 @@ export default function ({ getService }: FtrProviderContext) {
`/api/uptime/pings?from=${from}&to=${to}&size=${size}`
);
- const { total, locations, pings } = decodePingsResponseData(apiResponse.body);
+ const { total, pings } = decodePingsResponseData(apiResponse.body);
expect(total).to.be(2000);
- expect(locations).to.eql(['mpls']);
expect(pings).length(50);
expect(pings.map(({ monitor: { id } }) => id)).to.eql([
'0074-up',
@@ -127,10 +125,9 @@ export default function ({ getService }: FtrProviderContext) {
`/api/uptime/pings?from=${from}&to=${to}&monitorId=${monitorId}&size=${size}`
);
- const { total, locations, pings } = decodePingsResponseData(apiResponse.body);
+ const { total, pings } = decodePingsResponseData(apiResponse.body);
expect(total).to.be(20);
- expect(locations).to.eql(['mpls']);
pings.forEach(({ monitor: { id } }) => expect(id).to.eql('0001-up'));
expect(pings.map(({ timestamp }) => timestamp)).to.eql([
'2019-09-11T03:40:34.371Z',
@@ -162,10 +159,9 @@ export default function ({ getService }: FtrProviderContext) {
`/api/uptime/pings?from=${from}&to=${to}&monitorId=${monitorId}&size=${size}&sort=${sort}`
);
- const { total, locations, pings } = decodePingsResponseData(apiResponse.body);
+ const { total, pings } = decodePingsResponseData(apiResponse.body);
expect(total).to.be(20);
- expect(locations).to.eql(['mpls']);
expect(pings.map(({ timestamp }) => timestamp)).to.eql([
'2019-09-11T03:31:04.380Z',
'2019-09-11T03:31:34.366Z',
diff --git a/x-pack/test/functional/apps/uptime/monitor.ts b/x-pack/test/functional/apps/uptime/monitor.ts
index 5ecb10f4bf57..635c34bd4c12 100644
--- a/x-pack/test/functional/apps/uptime/monitor.ts
+++ b/x-pack/test/functional/apps/uptime/monitor.ts
@@ -32,23 +32,27 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await uptime.loadDataAndGoToMonitorPage(dateStart, dateEnd, monitorId);
});
+ it('should select the ping list location filter', async () => {
+ await uptimeService.common.selectFilterItem('location', 'mpls');
+ });
+
+ it('should set the status filter', async () => {
+ await uptimeService.common.setStatusFilterUp();
+ });
+
it('displays ping data as expected', async () => {
- await uptime.checkPingListInteractions(
- [
- 'XZtoHm0B0I9WX_CznN-6',
- '7ZtoHm0B0I9WX_CzJ96M',
- 'pptnHm0B0I9WX_Czst5X',
- 'I5tnHm0B0I9WX_CzPd46',
- 'y5tmHm0B0I9WX_Czx93x',
- 'XZtmHm0B0I9WX_CzUt3H',
- '-JtlHm0B0I9WX_Cz3dyX',
- 'k5tlHm0B0I9WX_CzaNxm',
- 'NZtkHm0B0I9WX_Cz89w9',
- 'zJtkHm0B0I9WX_CzftsN',
- ],
- 'mpls',
- 'up'
- );
+ await uptime.checkPingListInteractions([
+ 'XZtoHm0B0I9WX_CznN-6',
+ '7ZtoHm0B0I9WX_CzJ96M',
+ 'pptnHm0B0I9WX_Czst5X',
+ 'I5tnHm0B0I9WX_CzPd46',
+ 'y5tmHm0B0I9WX_Czx93x',
+ 'XZtmHm0B0I9WX_CzUt3H',
+ '-JtlHm0B0I9WX_Cz3dyX',
+ 'k5tlHm0B0I9WX_CzaNxm',
+ 'NZtkHm0B0I9WX_Cz89w9',
+ 'zJtkHm0B0I9WX_CzftsN',
+ ]);
});
});
});
diff --git a/x-pack/test/functional/page_objects/uptime_page.ts b/x-pack/test/functional/page_objects/uptime_page.ts
index fd4747890958..34d2eeb1df88 100644
--- a/x-pack/test/functional/page_objects/uptime_page.ts
+++ b/x-pack/test/functional/page_objects/uptime_page.ts
@@ -109,17 +109,7 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo
return commonService.clickPageSizeSelectPopoverItem(size);
}
- public async checkPingListInteractions(
- timestamps: string[],
- location?: string,
- status?: string
- ): Promise {
- if (location) {
- await monitor.setPingListLocation(location);
- }
- if (status) {
- await monitor.setPingListStatus(status);
- }
+ public async checkPingListInteractions(timestamps: string[]): Promise {
return monitor.checkForPingListTimestamps(timestamps);
}