mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Uptime] Fix synthetics detail step count (#89940)
* Add parameter to allow filtering by step type. Write tests. * Delete unneeded fields. * PR feedback.
This commit is contained in:
parent
2e5341d3db
commit
c38d9b0011
6 changed files with 221 additions and 3 deletions
|
@ -32,7 +32,7 @@ export const StepDetailContainer: React.FC<Props> = ({ checkGroup, stepIndex })
|
|||
|
||||
useEffect(() => {
|
||||
if (checkGroup) {
|
||||
dispatch(getJourneySteps({ checkGroup }));
|
||||
dispatch(getJourneySteps({ checkGroup, syntheticEventTypes: ['step/end'] }));
|
||||
}
|
||||
}, [dispatch, checkGroup]);
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import { SyntheticsJourneyApiResponse } from '../../../common/runtime_types';
|
|||
|
||||
export interface FetchJourneyStepsParams {
|
||||
checkGroup: string;
|
||||
syntheticEventTypes?: string[];
|
||||
}
|
||||
|
||||
export interface GetJourneyFailPayload {
|
||||
|
|
|
@ -16,7 +16,7 @@ export async function fetchJourneySteps(
|
|||
): Promise<SyntheticsJourneyApiResponse> {
|
||||
return (await apiService.get(
|
||||
`/api/uptime/journey/${params.checkGroup}`,
|
||||
undefined,
|
||||
{ syntheticEventTypes: params.syntheticEventTypes },
|
||||
SyntheticsJourneyApiResponseType
|
||||
)) as SyntheticsJourneyApiResponse;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* 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 { getJourneySteps, formatSyntheticEvents } from './get_journey_steps';
|
||||
import { getUptimeESMockClient } from './helper';
|
||||
|
||||
describe('getJourneySteps request module', () => {
|
||||
describe('formatStepTypes', () => {
|
||||
it('returns default steps if none are provided', () => {
|
||||
expect(formatSyntheticEvents()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"step/end",
|
||||
"stderr",
|
||||
"cmd/status",
|
||||
"step/screenshot",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('returns provided step array if isArray', () => {
|
||||
expect(formatSyntheticEvents(['step/end', 'stderr'])).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"step/end",
|
||||
"stderr",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('returns provided step string in an array', () => {
|
||||
expect(formatSyntheticEvents('step/end')).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"step/end",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getJourneySteps', () => {
|
||||
let data: any;
|
||||
beforeEach(() => {
|
||||
data = {
|
||||
body: {
|
||||
hits: {
|
||||
hits: [
|
||||
{
|
||||
_id: 'o6myXncBFt2V8m6r6z-r',
|
||||
_source: {
|
||||
'@timestamp': '2021-02-01T17:45:19.001Z',
|
||||
synthetics: {
|
||||
package_version: '0.0.1-alpha.8',
|
||||
journey: {
|
||||
name: 'inline',
|
||||
id: 'inline',
|
||||
},
|
||||
step: {
|
||||
name: 'load homepage',
|
||||
index: 1,
|
||||
},
|
||||
type: 'step/end',
|
||||
},
|
||||
monitor: {
|
||||
name: 'My Monitor',
|
||||
id: 'my-monitor',
|
||||
check_group: '2bf952dc-64b5-11eb-8b3b-42010a84000d',
|
||||
type: 'browser',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
_id: 'IjqzXncBn2sjqrYxYoCG',
|
||||
_source: {
|
||||
'@timestamp': '2021-02-01T17:45:49.944Z',
|
||||
synthetics: {
|
||||
package_version: '0.0.1-alpha.8',
|
||||
journey: {
|
||||
name: 'inline',
|
||||
id: 'inline',
|
||||
},
|
||||
step: {
|
||||
name: 'hover over products menu',
|
||||
index: 2,
|
||||
},
|
||||
type: 'step/end',
|
||||
},
|
||||
monitor: {
|
||||
name: 'My Monitor',
|
||||
timespan: {
|
||||
lt: '2021-02-01T17:46:49.945Z',
|
||||
gte: '2021-02-01T17:45:49.945Z',
|
||||
},
|
||||
id: 'my-monitor',
|
||||
check_group: '2bf952dc-64b5-11eb-8b3b-42010a84000d',
|
||||
type: 'browser',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('formats ES result', async () => {
|
||||
const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient();
|
||||
|
||||
mockEsClient.search.mockResolvedValueOnce(data as any);
|
||||
const result: any = await getJourneySteps({
|
||||
uptimeEsClient,
|
||||
checkGroup: '2bf952dc-64b5-11eb-8b3b-42010a84000d',
|
||||
});
|
||||
expect(mockEsClient.search).toHaveBeenCalledTimes(1);
|
||||
const call: any = mockEsClient.search.mock.calls[0][0];
|
||||
|
||||
// check that default `synthetics.type` value is supplied,
|
||||
expect(call.body.query.bool.filter[0]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"terms": Object {
|
||||
"synthetics.type": Array [
|
||||
"step/end",
|
||||
"stderr",
|
||||
"cmd/status",
|
||||
"step/screenshot",
|
||||
],
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
// given check group is used for the terms filter
|
||||
expect(call.body.query.bool.filter[1]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"term": Object {
|
||||
"monitor.check_group": "2bf952dc-64b5-11eb-8b3b-42010a84000d",
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
// should sort by step index, then timestamp
|
||||
expect(call.body.sort).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"synthetics.step.index": Object {
|
||||
"order": "asc",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"@timestamp": Object {
|
||||
"order": "asc",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
|
||||
expect(result).toHaveLength(2);
|
||||
// `getJourneySteps` is responsible for formatting these fields, so we need to check them
|
||||
result.forEach((step: any) => {
|
||||
expect(['2021-02-01T17:45:19.001Z', '2021-02-01T17:45:49.944Z']).toContain(step.timestamp);
|
||||
expect(['o6myXncBFt2V8m6r6z-r', 'IjqzXncBn2sjqrYxYoCG']).toContain(step.docId);
|
||||
expect(step.synthetics.screenshotExists).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('notes screenshot exists when a document of type step/screenshot is included', async () => {
|
||||
const { esClient: mockEsClient, uptimeEsClient } = getUptimeESMockClient();
|
||||
|
||||
data.body.hits.hits[0]._source.synthetics.type = 'step/screenshot';
|
||||
data.body.hits.hits[0]._source.synthetics.step.index = 2;
|
||||
mockEsClient.search.mockResolvedValueOnce(data as any);
|
||||
|
||||
const result: any = await getJourneySteps({
|
||||
uptimeEsClient,
|
||||
checkGroup: '2bf952dc-64b5-11eb-8b3b-42010a84000d',
|
||||
syntheticEventTypes: ['stderr', 'step/end'],
|
||||
});
|
||||
|
||||
const call: any = mockEsClient.search.mock.calls[0][0];
|
||||
|
||||
// assert that filters for only the provided step types are used
|
||||
expect(call.body.query.bool.filter[0]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"terms": Object {
|
||||
"synthetics.type": Array [
|
||||
"stderr",
|
||||
"step/end",
|
||||
],
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].synthetics.screenshotExists).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,11 +9,23 @@ import { Ping } from '../../../common/runtime_types';
|
|||
|
||||
interface GetJourneyStepsParams {
|
||||
checkGroup: string;
|
||||
syntheticEventTypes?: string | string[];
|
||||
}
|
||||
|
||||
const defaultEventTypes = ['step/end', 'stderr', 'cmd/status', 'step/screenshot'];
|
||||
|
||||
export const formatSyntheticEvents = (eventTypes?: string | string[]) => {
|
||||
if (!eventTypes) {
|
||||
return defaultEventTypes;
|
||||
} else {
|
||||
return Array.isArray(eventTypes) ? eventTypes : [eventTypes];
|
||||
}
|
||||
};
|
||||
|
||||
export const getJourneySteps: UMElasticsearchQueryFn<GetJourneyStepsParams, Ping> = async ({
|
||||
uptimeEsClient,
|
||||
checkGroup,
|
||||
syntheticEventTypes,
|
||||
}) => {
|
||||
const params = {
|
||||
query: {
|
||||
|
@ -21,7 +33,7 @@ export const getJourneySteps: UMElasticsearchQueryFn<GetJourneyStepsParams, Ping
|
|||
filter: [
|
||||
{
|
||||
terms: {
|
||||
'synthetics.type': ['step/end', 'stderr', 'cmd/status', 'step/screenshot'],
|
||||
'synthetics.type': formatSyntheticEvents(syntheticEventTypes),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -16,12 +16,21 @@ export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) =>
|
|||
checkGroup: schema.string(),
|
||||
_debug: schema.maybe(schema.boolean()),
|
||||
}),
|
||||
query: schema.object({
|
||||
// provides a filter for the types of synthetic events to include
|
||||
// when fetching a journey's data
|
||||
syntheticEventTypes: schema.maybe(
|
||||
schema.oneOf([schema.arrayOf(schema.string()), schema.string()])
|
||||
),
|
||||
}),
|
||||
},
|
||||
handler: async ({ uptimeEsClient, request }): Promise<any> => {
|
||||
const { checkGroup } = request.params;
|
||||
const { syntheticEventTypes } = request.query;
|
||||
const result = await libs.requests.getJourneySteps({
|
||||
uptimeEsClient,
|
||||
checkGroup,
|
||||
syntheticEventTypes,
|
||||
});
|
||||
|
||||
const details = await libs.requests.getJourneyDetails({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue