From 8ba5f539d26f561bbb664bdd5e1f5bebfa97aafb Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 9 Feb 2021 10:56:19 +0100 Subject: [PATCH 01/30] wip --- x-pack/plugins/uptime/common/constants/ui.ts | 2 + .../synthetics/check_steps/steps_list.tsx | 136 ++++++++++++++++++ .../synthetics/check_steps/use_check_steps.ts | 27 ++++ .../components/synthetics/translations.ts | 12 ++ .../uptime/public/hooks/use_telemetry.ts | 1 + x-pack/plugins/uptime/public/pages/index.ts | 2 +- .../{ => synthetics}/step_detail_page.tsx | 6 +- .../pages/synthetics/synthetics_checks.tsx | 77 ++++++++++ x-pack/plugins/uptime/public/routes.tsx | 9 ++ .../lib/requests/get_journey_details.ts | 2 +- .../uptime/server/rest_api/pings/journeys.ts | 20 +-- 11 files changed, 280 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts create mode 100644 x-pack/plugins/uptime/public/components/synthetics/translations.ts rename x-pack/plugins/uptime/public/pages/{ => synthetics}/step_detail_page.tsx (75%) create mode 100644 x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx diff --git a/x-pack/plugins/uptime/common/constants/ui.ts b/x-pack/plugins/uptime/common/constants/ui.ts index 0c8ff6d3f1ed6..0b0cb4d43a05a 100644 --- a/x-pack/plugins/uptime/common/constants/ui.ts +++ b/x-pack/plugins/uptime/common/constants/ui.ts @@ -15,6 +15,8 @@ export const CERTIFICATES_ROUTE = '/certificates'; export const STEP_DETAIL_ROUTE = '/journey/:checkGroupId/step/:stepIndex'; +export const SYNTHETIC_CHECK_STEPs_ROUTE = '/journey/:checkGroupId/steps'; + export enum STATUS { UP = 'up', DOWN = 'down', diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx new file mode 100644 index 0000000000000..2e7c7cb64e1c3 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx @@ -0,0 +1,136 @@ +/* + * 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 { EuiBasicTable, EuiPanel } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useCallback, useState, useEffect } from 'react'; +import styled from 'styled-components'; +import { useDispatch } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import { Ping } from '../../../../common/runtime_types'; +import { pruneJourneyState } from '../../../state/actions/journey'; +import { clearPings } from '../../../state/actions'; +import { STATUS_LABEL } from '../../monitor/ping_list/translations'; +import { PingStatusColumn } from '../../monitor/ping_list/columns/ping_status'; +import { PingTimestamp } from '../../monitor/ping_list/columns/ping_timestamp'; +import { ExpandRowColumn } from '../../monitor/ping_list/columns/expand_row'; +import { STEP_NAME_LABEL } from '../translations'; + +export const SpanWithMargin = styled.span` + margin-right: 16px; +`; + +export const StepsList = ({ data, error, loading }) => { + const dispatch = useDispatch(); + + const history = useHistory(); + + const pruneJourneysCallback = useCallback( + (checkGroups: string[]) => dispatch(pruneJourneyState(checkGroups)), + [dispatch] + ); + + const [expandedRows, setExpandedRows] = useState>({}); + + const expandedIdsToRemove = JSON.stringify( + Object.keys(expandedRows).filter((e) => !data.some(({ docId }) => docId === e)) + ); + + useEffect(() => { + return () => { + dispatch(clearPings()); + }; + }, [dispatch]); + + useEffect(() => { + const parsed = JSON.parse(expandedIdsToRemove); + if (parsed.length) { + parsed.forEach((docId: string) => { + delete expandedRows[docId]; + }); + setExpandedRows(expandedRows); + } + }, [expandedIdsToRemove, expandedRows]); + + const expandedCheckGroups = data + .filter((p: Ping) => Object.keys(expandedRows).some((f) => p.docId === f)) + .map(({ monitor: { check_group: cg } }) => cg); + + const expandedCheckGroupsStr = JSON.stringify(expandedCheckGroups); + + useEffect(() => { + pruneJourneysCallback(JSON.parse(expandedCheckGroupsStr)); + }, [pruneJourneysCallback, expandedCheckGroupsStr]); + + const columns = [ + { + field: 'monitor.status', + name: STATUS_LABEL, + render: (pingStatus: string, item: Ping) => ( + + ), + }, + + { + align: 'left', + field: 'timestamp', + name: STEP_NAME_LABEL, + render: (timestamp: string, item: Ping) => ( + + ), + }, + + { + align: 'right', + width: '24px', + isExpander: true, + render: (item: Ping) => ( + + ), + }, + ]; + + const getRowProps = (item: Ping) => { + const { monitor } = item; + return { + 'data-test-subj': `row-${monitor.check_group}`, + onClick: () => { + history.push(`/journey/${monitor.check_group}/steps`); + }, + }; + }; + + return ( + + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts new file mode 100644 index 0000000000000..91c0891ed9a27 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts @@ -0,0 +1,27 @@ +/* + * 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 { useParams } from 'react-router-dom'; +import { FETCH_STATUS, useFetcher } from '../../../../../observability/public'; +import { fetchJourneySteps } from '../../../state/api/journey'; + +export const useCheckSteps = () => { + const { checkGroupId } = useParams<{ checkGroupId: string; stepIndex: string }>(); + + const { data, status } = useFetcher(() => { + return fetchJourneySteps({ + checkGroup: checkGroupId, + }); + }, [checkGroupId]); + + return { + steps: data?.steps ?? [], + ping: data?.details.journey, + timestamp: data?.details.timestamp, + loading: status == FETCH_STATUS.LOADING || status === FETCH_STATUS.PENDING, + }; +}; diff --git a/x-pack/plugins/uptime/public/components/synthetics/translations.ts b/x-pack/plugins/uptime/public/components/synthetics/translations.ts new file mode 100644 index 0000000000000..7fbf3e8619bc5 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/synthetics/translations.ts @@ -0,0 +1,12 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const STEP_NAME_LABEL = i18n.translate('xpack.uptime.stepList.stepName', { + defaultMessage: 'Step name', +}); diff --git a/x-pack/plugins/uptime/public/hooks/use_telemetry.ts b/x-pack/plugins/uptime/public/hooks/use_telemetry.ts index da0f109747758..b9ec9cc5e5516 100644 --- a/x-pack/plugins/uptime/public/hooks/use_telemetry.ts +++ b/x-pack/plugins/uptime/public/hooks/use_telemetry.ts @@ -16,6 +16,7 @@ export enum UptimePage { Settings = 'Settings', Certificates = 'Certificates', StepDetail = 'StepDetail', + SyntheticCheckStepsPage = 'SyntheticCheckStepsPage', NotFound = '__not-found__', } diff --git a/x-pack/plugins/uptime/public/pages/index.ts b/x-pack/plugins/uptime/public/pages/index.ts index 828942bc1eb1e..5624f61c3abb5 100644 --- a/x-pack/plugins/uptime/public/pages/index.ts +++ b/x-pack/plugins/uptime/public/pages/index.ts @@ -6,6 +6,6 @@ */ export { MonitorPage } from './monitor'; -export { StepDetailPage } from './step_detail_page'; +export { StepDetailPage } from './synthetics/step_detail_page'; export { SettingsPage } from './settings'; export { NotFoundPage } from './not_found'; diff --git a/x-pack/plugins/uptime/public/pages/step_detail_page.tsx b/x-pack/plugins/uptime/public/pages/synthetics/step_detail_page.tsx similarity index 75% rename from x-pack/plugins/uptime/public/pages/step_detail_page.tsx rename to x-pack/plugins/uptime/public/pages/synthetics/step_detail_page.tsx index aa81ddd0eae3d..de38d2d663523 100644 --- a/x-pack/plugins/uptime/public/pages/step_detail_page.tsx +++ b/x-pack/plugins/uptime/public/pages/synthetics/step_detail_page.tsx @@ -7,9 +7,9 @@ import React from 'react'; import { useParams } from 'react-router-dom'; -import { useTrackPageview } from '../../../observability/public'; -import { useInitApp } from '../hooks/use_init_app'; -import { StepDetailContainer } from '../components/monitor/synthetics/step_detail/step_detail_container'; +import { useTrackPageview } from '../../../../observability/public'; +import { useInitApp } from '../../hooks/use_init_app'; +import { StepDetailContainer } from '../../components/monitor/synthetics/step_detail/step_detail_container'; export const StepDetailPage: React.FC = () => { useInitApp(); diff --git a/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx b/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx new file mode 100644 index 0000000000000..dc622c5f4a855 --- /dev/null +++ b/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx @@ -0,0 +1,77 @@ +/* + * 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 from 'react'; +import { useParams } from 'react-router-dom'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPage, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import moment from 'moment'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { useTrackPageview } from '../../../../observability/public'; +import { useInitApp } from '../../hooks/use_init_app'; +import { StepsList } from '../../components/synthetics/check_steps/steps_list'; +import { useCheckSteps } from '../../components/synthetics/check_steps/use_check_steps'; +import { useUiSetting$ } from '../../../../../../src/plugins/kibana_react/public'; + +export const SyntheticsCheckSteps: React.FC = () => { + useInitApp(); + useTrackPageview({ app: 'uptime', path: 'syntheticCheckSteps' }); + useTrackPageview({ app: 'uptime', path: 'syntheticCheckSteps', delay: 15000 }); + + const { checkGroupId } = useParams<{ checkGroupId: string; stepIndex: string }>(); + + const { error, loading, steps, ping, timestamp } = useCheckSteps(); + + const [dateFormat] = useUiSetting$('dateFormat'); + + return ( + <> + + + +

{ping?.monitor.name}

+
+
+ + + + + + + + + {moment(timestamp).format(dateFormat).toString()} + + + + + + + + +
+ + + + + ); +}; diff --git a/x-pack/plugins/uptime/public/routes.tsx b/x-pack/plugins/uptime/public/routes.tsx index 82aa09c3293e6..c5fddd2b34931 100644 --- a/x-pack/plugins/uptime/public/routes.tsx +++ b/x-pack/plugins/uptime/public/routes.tsx @@ -15,10 +15,12 @@ import { OVERVIEW_ROUTE, SETTINGS_ROUTE, STEP_DETAIL_ROUTE, + SYNTHETIC_CHECK_STEPs_ROUTE, } from '../common/constants'; import { MonitorPage, StepDetailPage, NotFoundPage, SettingsPage } from './pages'; import { CertificatesPage } from './pages/certificates'; import { UptimePage, useUptimeTelemetry } from './hooks'; +import { SyntheticsCheckSteps } from './pages/synthetics/synthetics_checks'; interface RouteProps { path: string; @@ -71,6 +73,13 @@ const Routes: RouteProps[] = [ dataTestSubj: 'uptimeStepDetailPage', telemetryId: UptimePage.StepDetail, }, + { + title: baseTitle, + path: SYNTHETIC_CHECK_STEPs_ROUTE, + component: SyntheticsCheckSteps, + dataTestSubj: 'uptimeSyntheticCheckStepsPage', + telemetryId: UptimePage.SyntheticCheckStepsPage, + }, { title: baseTitle, path: OVERVIEW_ROUTE, diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts index c942c3a8f69fd..8bc6dc33290d5 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts @@ -33,7 +33,6 @@ export const getJourneyDetails: UMElasticsearchQueryFn< ], }, }, - _source: ['@timestamp', 'monitor.id'], size: 1, }; @@ -109,6 +108,7 @@ export const getJourneyDetails: UMElasticsearchQueryFn< nextJourneyResult?.hits?.hits.length > 0 ? nextJourneyResult?.hits?.hits[0] : null; return { timestamp: thisJourneySource['@timestamp'], + journey: thisJourneySource, previous: previousJourney ? { checkGroup: previousJourney._source.monitor.check_group, 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 def373e88ae16..3994149c4b2aa 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts @@ -28,16 +28,18 @@ export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => handler: async ({ uptimeEsClient, request }): Promise => { const { checkGroup } = request.params; const { syntheticEventTypes } = request.query; - const result = await libs.requests.getJourneySteps({ - uptimeEsClient, - checkGroup, - syntheticEventTypes, - }); - const details = await libs.requests.getJourneyDetails({ - uptimeEsClient, - checkGroup, - }); + const [result, details] = await Promise.all([ + await libs.requests.getJourneySteps({ + uptimeEsClient, + checkGroup, + syntheticEventTypes, + }), + await libs.requests.getJourneyDetails({ + uptimeEsClient, + checkGroup, + }), + ]); return { checkGroup, From 6b31192ebe9ae3d4bce232d2f912bb91ed45b0e4 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Wed, 10 Feb 2021 17:54:21 +0100 Subject: [PATCH 02/30] Addew check steps list view --- .../uptime/common/runtime_types/ping/ping.ts | 1 + .../components/common/step_detail_link.tsx | 12 +- .../columns/ping_timestamp/nav_buttons.tsx | 8 +- .../ping_timestamp/ping_timestamp.test.tsx | 12 +- .../columns/ping_timestamp/ping_timestamp.tsx | 21 +- .../step_image_caption.test.tsx | 4 +- .../ping_timestamp/step_image_caption.tsx | 17 +- .../monitor/ping_list/expanded_row.test.tsx | 37 ++- .../monitor/ping_list/expanded_row.tsx | 19 -- .../monitor/ping_list/ping_list.tsx | 53 +++- .../synthetics/browser_expanded_row.test.tsx | 203 -------------- .../synthetics/browser_expanded_row.tsx | 65 ----- .../synthetics/executed_journey.test.tsx | 265 ------------------ .../monitor/synthetics/executed_journey.tsx | 88 ------ .../monitor/synthetics/executed_step.tsx | 110 -------- .../monitor/synthetics/status_badge.test.tsx | 42 --- .../step_detail/step_detail_container.tsx | 2 +- .../step_detail/use_monitor_breadcrumb.tsx | 28 +- .../use_monitor_breadcrumbs.test.tsx | 5 +- .../synthetics/check_steps/expand_row_col.tsx | 59 ++++ .../synthetics/check_steps/step_image.tsx | 26 ++ .../synthetics/check_steps/step_list.test.tsx | 144 ++++++++++ .../synthetics/check_steps/steps_list.tsx | 121 ++++++-- .../synthetics/check_steps/use_check_steps.ts | 10 +- .../synthetics/code_block_accordion.tsx | 4 +- .../synthetics/console_event.test.tsx | 0 .../synthetics/console_event.tsx | 4 +- .../console_output_event_list.test.tsx | 0 .../synthetics/console_output_event_list.tsx | 4 +- .../synthetics/empty_journey.test.tsx | 0 .../synthetics/empty_journey.tsx | 0 .../synthetics/executed_step.test.tsx | 37 +-- .../components/synthetics/executed_step.tsx | 89 ++++++ .../synthetics/status_badge.test.tsx | 33 +++ .../{monitor => }/synthetics/status_badge.tsx | 20 +- .../step_screenshot_display.test.tsx | 2 +- .../synthetics/step_screenshot_display.tsx | 12 +- .../pages/synthetics/checks_navigation.tsx | 63 +++++ .../pages/synthetics/synthetics_checks.tsx | 51 +--- 39 files changed, 692 insertions(+), 979 deletions(-) delete mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.test.tsx delete mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx delete mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.test.tsx delete mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx delete mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx delete mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/expand_row_col.tsx create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/step_list.test.tsx rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/code_block_accordion.tsx (87%) rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/console_event.test.tsx (100%) rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/console_event.tsx (89%) rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/console_output_event_list.test.tsx (100%) rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/console_output_event_list.tsx (92%) rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/empty_journey.test.tsx (100%) rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/empty_journey.tsx (100%) rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/executed_step.test.tsx (56%) create mode 100644 x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx create mode 100644 x-pack/plugins/uptime/public/components/synthetics/status_badge.test.tsx rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/status_badge.tsx (66%) rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/step_screenshot_display.test.tsx (96%) rename x-pack/plugins/uptime/public/components/{monitor => }/synthetics/step_screenshot_display.tsx (94%) create mode 100644 x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx diff --git a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts index 8991d52f6a920..d3365456e09f1 100644 --- a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts @@ -251,6 +251,7 @@ export const SyntheticsJourneyApiResponseType = t.intersection([ t.intersection([ t.type({ timestamp: t.string, + journey: PingType, }), t.partial({ next: t.type({ diff --git a/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx b/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx index 313dd18e67c11..fa6d0b4c3f8bb 100644 --- a/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx +++ b/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx @@ -6,7 +6,7 @@ */ import React, { FC } from 'react'; -import { ReactRouterEuiButton } from './react_router_helpers'; +import { ReactRouterEuiButtonEmpty } from './react_router_helpers'; interface StepDetailLinkProps { /** @@ -23,14 +23,8 @@ export const StepDetailLink: FC = ({ children, checkGroupId const to = `/journey/${checkGroupId}/step/${stepIndex}`; return ( - + {children} - + ); }; diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/nav_buttons.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/nav_buttons.tsx index 390a133b1819b..3b0aad721be8a 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/nav_buttons.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/nav_buttons.tsx @@ -6,7 +6,7 @@ */ import { EuiButtonIcon, EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; -import React from 'react'; +import React, { MouseEvent } from 'react'; import { nextAriaLabel, prevAriaLabel } from './translations'; export interface NavButtonsProps { @@ -34,8 +34,9 @@ export const NavButtons: React.FC = ({ disabled={stepNumber === 1} color="subdued" size="s" - onClick={() => { + onClick={(evt: MouseEvent) => { setStepNumber(stepNumber - 1); + evt.stopPropagation(); }} iconType="arrowLeft" aria-label={prevAriaLabel} @@ -46,8 +47,9 @@ export const NavButtons: React.FC = ({ disabled={stepNumber === maxSteps} color="subdued" size="s" - onClick={() => { + onClick={(evt: MouseEvent) => { setStepNumber(stepNumber + 1); + evt.stopPropagation(); }} iconType="arrowRight" aria-label={nextAriaLabel} diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.test.tsx index 20fab4238952b..bc9a70339fa45 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.test.tsx @@ -12,6 +12,8 @@ import { mockReduxHooks } from '../../../../../lib/helper/test_helpers'; import { render } from '../../../../../lib/helper/rtl_helpers'; import { Ping } from '../../../../../../common/runtime_types/ping'; import * as observabilityPublic from '../../../../../../../observability/public'; +import { getShortTimeStamp } from '../../../../overview/monitor_list/columns/monitor_status_column'; +import moment from 'moment'; mockReduxHooks(); @@ -68,7 +70,7 @@ describe('Ping Timestamp component', () => { .spyOn(observabilityPublic, 'useFetcher') .mockReturnValue({ status: fetchStatus, data: null, refetch: () => null }); const { getByTestId } = render( - + ); expect(getByTestId('pingTimestampSpinner')).toBeInTheDocument(); } @@ -79,7 +81,7 @@ describe('Ping Timestamp component', () => { .spyOn(observabilityPublic, 'useFetcher') .mockReturnValue({ status: FETCH_STATUS.SUCCESS, data: null, refetch: () => null }); const { getByTestId } = render( - + ); expect(getByTestId('pingTimestampNoImageAvailable')).toBeInTheDocument(); }); @@ -91,7 +93,9 @@ describe('Ping Timestamp component', () => { data: { src }, refetch: () => null, }); - const { container } = render(); + const { container } = render( + + ); expect(container.querySelector('img')?.src).toBe(src); }); @@ -103,7 +107,7 @@ describe('Ping Timestamp component', () => { refetch: () => null, }); const { getByAltText, getByText, queryByAltText } = render( - + ); const caption = getByText('Nov 26, 2020 10:28:56 AM'); fireEvent.mouseEnter(caption); diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx index 60c01765c68b8..7e6b353877515 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx @@ -42,11 +42,12 @@ const StepDiv = styled.div` `; interface Props { - timestamp: string; + label?: string; ping: Ping; + showNavBtn?: boolean; } -export const PingTimestamp = ({ timestamp, ping }: Props) => { +export const PingTimestamp = ({ label, ping, showNavBtn = true }: Props) => { const [stepNumber, setStepNumber] = useState(1); const [isImagePopoverOpen, setIsImagePopoverOpen] = useState(false); @@ -86,7 +87,7 @@ export const PingTimestamp = ({ timestamp, ping }: Props) => { maxSteps={data?.maxSteps} setStepNumber={setStepNumber} stepNumber={stepNumber} - timestamp={timestamp} + label={label} /> ); @@ -110,12 +111,14 @@ export const PingTimestamp = ({ timestamp, ping }: Props) => { isPending={status === FETCH_STATUS.PENDING} /> )} - + {showNavBtn && ( + + )} ); }; diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx index 80ed3ca46b8aa..d2b694479f85c 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx @@ -9,6 +9,8 @@ import { fireEvent, waitFor } from '@testing-library/react'; import React from 'react'; import { render } from '../../../../../lib/helper/rtl_helpers'; import { StepImageCaption, StepImageCaptionProps } from './step_image_caption'; +import { getShortTimeStamp } from '../../../../overview/monitor_list/columns/monitor_status_column'; +import moment from 'moment'; describe('StepImageCaption', () => { let defaultProps: StepImageCaptionProps; @@ -20,7 +22,7 @@ describe('StepImageCaption', () => { maxSteps: 3, setStepNumber: jest.fn(), stepNumber: 2, - timestamp: '2020-11-26T15:28:56.896Z', + label: getShortTimeStamp(moment('2020-11-26T15:28:56.896Z')), }; }); diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx index c9f4efa53fc07..08bf972ba8df5 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx @@ -6,10 +6,8 @@ */ import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; -import React from 'react'; -import moment from 'moment'; +import React, { MouseEvent } from 'react'; import { nextAriaLabel, prevAriaLabel } from './translations'; -import { getShortTimeStamp } from '../../../../overview/monitor_list/columns/monitor_status_column'; export interface StepImageCaptionProps { captionContent: string; @@ -17,7 +15,7 @@ export interface StepImageCaptionProps { maxSteps?: number; setStepNumber: React.Dispatch>; stepNumber: number; - timestamp: string; + label?: string; } export const StepImageCaption: React.FC = ({ @@ -26,7 +24,7 @@ export const StepImageCaption: React.FC = ({ maxSteps, setStepNumber, stepNumber, - timestamp, + label, }) => { return ( <> @@ -37,8 +35,9 @@ export const StepImageCaption: React.FC = ({ { + onClick={(evt: MouseEvent) => { setStepNumber(stepNumber - 1); + evt.preventDefault(); }} iconType="arrowLeft" aria-label={prevAriaLabel} @@ -51,8 +50,9 @@ export const StepImageCaption: React.FC = ({ { + onClick={(evt: MouseEvent) => { setStepNumber(stepNumber + 1); + evt.stopPropagation(); }} iconType="arrowRight" aria-label={nextAriaLabel} @@ -61,8 +61,7 @@ export const StepImageCaption: React.FC = ({ )} - {/* TODO: Add link to details page once it's available */} - {getShortTimeStamp(moment(timestamp))} + {label && {label}} ); diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.test.tsx index 5dee3e5b1e14a..dd42a14890793 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.test.tsx @@ -98,14 +98,35 @@ describe('PingListExpandedRow', () => { > - - - + color="primary" + > + + The Title", + "hash": "testhash", + } + } + /> + + + , + "title": "Response Body", + }, + ] + } + /> + `); diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx index 2599b8ed9fdca..df0d273d3bc3a 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx @@ -21,7 +21,6 @@ import { i18n } from '@kbn/i18n'; import { Ping, HttpResponseBody } from '../../../../common/runtime_types'; import { DocLinkForBody } from './doc_link_body'; import { PingRedirects } from './ping_redirects'; -import { BrowserExpandedRow } from '../synthetics/browser_expanded_row'; import { PingHeaders } from './headers'; interface Props { @@ -57,24 +56,6 @@ const BodyExcerpt = ({ content }: { content: string }) => export const PingListExpandedRowComponent = ({ ping }: Props) => { const listItems = []; - if (ping.monitor.type === 'browser') { - return ( - - - - - - - - - ); - } - // Show the error block if (ping.error) { listItems.push({ diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx index 110c46eca31d1..2b05cf699ff73 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx @@ -7,8 +7,9 @@ import { EuiBasicTable, EuiPanel, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useCallback, useState, useEffect } from 'react'; +import React, { useCallback, useState, useEffect, MouseEvent } from 'react'; import styled from 'styled-components'; +import { useHistory } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { Ping } from '../../../../common/runtime_types'; import { convertMicrosecondsToMilliseconds as microsToMillis } from '../../../lib/helper'; @@ -27,6 +28,8 @@ import { FailedStep } from './columns/failed_step'; import { usePingsList } from './use_pings'; import { PingListHeader } from './ping_list_header'; import { clearPings } from '../../../state/actions'; +import { getShortTimeStamp } from '../../overview/monitor_list/columns/monitor_status_column'; +import moment from 'moment'; export const SpanWithMargin = styled.span` margin-right: 16px; @@ -40,6 +43,8 @@ export const PingList = () => { const dispatch = useDispatch(); + const history = useHistory(); + const pruneJourneysCallback = useCallback( (checkGroups: string[]) => dispatch(pruneJourneyState(checkGroups)), [dispatch] @@ -111,7 +116,7 @@ export const PingList = () => { field: 'timestamp', name: TIMESTAMP_LABEL, render: (timestamp: string, item: Ping) => ( - + ), }, ] @@ -172,20 +177,39 @@ export const PingList = () => { }, ] : []), - { - align: 'right', - width: '24px', - isExpander: true, - render: (item: Ping) => ( - - ), - }, + ...(monitorType !== MONITOR_TYPES.BROWSER + ? [ + { + align: 'right', + width: '24px', + isExpander: true, + render: (item: Ping) => ( + + ), + }, + ] + : []), ]; + const getRowProps = (item: Ping) => { + const { monitor } = item; + return { + 'data-test-subj': `row-${monitor.check_group}`, + onClick: (evt: MouseEvent) => { + const targetElem = evt.target as HTMLElement; + + // we dont want to capture image click event + if (targetElem.tagName !== 'IMG' && targetElem.tagName !== 'path') { + history.push(`/journey/${monitor.check_group}/steps`); + } + }, + }; + }; + const pagination: Pagination = { initialPageSize: DEFAULT_PAGE_SIZE, pageIndex, @@ -222,6 +246,7 @@ export const PingList = () => { setPageIndex(criteria.page!.index); }} tableLayout={'auto'} + rowProps={getRowProps} /> ); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.test.tsx deleted file mode 100644 index 396d51e3002b2..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.test.tsx +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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 { shallowWithIntl } from '@kbn/test/jest'; -import React from 'react'; -import { BrowserExpandedRowComponent } from './browser_expanded_row'; -import { Ping } from '../../../../common/runtime_types'; - -describe('BrowserExpandedRowComponent', () => { - let defStep: Ping; - beforeEach(() => { - defStep = { - docId: 'doc-id', - timestamp: '123', - monitor: { - duration: { - us: 100, - }, - id: 'mon-id', - status: 'up', - type: 'browser', - }, - }; - }); - - it('returns empty step state when no journey', () => { - expect(shallowWithIntl()).toMatchInlineSnapshot( - `` - ); - }); - - it('returns empty step state when journey has no steps', () => { - expect( - shallowWithIntl( - - ) - ).toMatchInlineSnapshot(``); - }); - - it('displays loading spinner when loading', () => { - expect( - shallowWithIntl( - - ) - ).toMatchInlineSnapshot(` -
- -
- `); - }); - - it('renders executed journey when step/end is present', () => { - expect( - shallowWithIntl( - - ) - ).toMatchInlineSnapshot(` - - `); - }); - - it('handles case where synth type is somehow missing', () => { - expect( - shallowWithIntl( - - ) - ).toMatchInlineSnapshot(`""`); - }); - - it('renders console output step list when only console steps are present', () => { - expect( - shallowWithIntl( - - ) - ).toMatchInlineSnapshot(` - - `); - }); - - it('renders null when only unsupported steps are present', () => { - expect( - shallowWithIntl( - - ) - ).toMatchInlineSnapshot(`""`); - }); -}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx deleted file mode 100644 index 2ceaa2d1b68ef..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 { EuiLoadingSpinner } from '@elastic/eui'; -import React, { useEffect, FC } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { Ping } from '../../../../common/runtime_types'; -import { getJourneySteps } from '../../../state/actions/journey'; -import { JourneyState } from '../../../state/reducers/journey'; -import { journeySelector } from '../../../state/selectors'; -import { EmptyJourney } from './empty_journey'; -import { ExecutedJourney } from './executed_journey'; -import { ConsoleOutputEventList } from './console_output_event_list'; - -interface BrowserExpandedRowProps { - checkGroup?: string; -} - -export const BrowserExpandedRow: React.FC = ({ checkGroup }) => { - const dispatch = useDispatch(); - useEffect(() => { - if (checkGroup) { - dispatch(getJourneySteps({ checkGroup })); - } - }, [dispatch, checkGroup]); - - const journeys = useSelector(journeySelector); - const journey = journeys[checkGroup ?? '']; - - return ; -}; - -type ComponentProps = BrowserExpandedRowProps & { - journey?: JourneyState; -}; - -const stepEnd = (step: Ping) => step.synthetics?.type === 'step/end'; -const stepConsole = (step: Ping) => - ['stderr', 'cmd/status'].indexOf(step.synthetics?.type ?? '') !== -1; - -export const BrowserExpandedRowComponent: FC = ({ checkGroup, journey }) => { - if (!!journey && journey.loading) { - return ( -
- -
- ); - } - - if (!journey || journey.steps.length === 0) { - return ; - } - - if (journey.steps.some(stepEnd)) return ; - - if (journey.steps.some(stepConsole)) return ; - - // TODO: should not happen, this means that the journey has no step/end and no console logs, but some other steps; filmstrip, screenshot, etc. - // we should probably create an error prompt letting the user know this step is not supported yet - return null; -}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.test.tsx deleted file mode 100644 index 2fbc19d245826..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.test.tsx +++ /dev/null @@ -1,265 +0,0 @@ -/* - * 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 { shallowWithIntl } from '@kbn/test/jest'; -import React from 'react'; -import { ExecutedJourney } from './executed_journey'; -import { Ping } from '../../../../common/runtime_types'; - -const MONITOR_BOILERPLATE = { - id: 'MON_ID', - duration: { - us: 10, - }, - status: 'down', - type: 'browser', -}; - -describe('ExecutedJourney component', () => { - let steps: Ping[]; - - beforeEach(() => { - steps = [ - { - docId: '1', - timestamp: '123', - monitor: MONITOR_BOILERPLATE, - synthetics: { - payload: { - status: 'failed', - }, - type: 'step/end', - }, - }, - { - docId: '2', - timestamp: '124', - monitor: MONITOR_BOILERPLATE, - synthetics: { - payload: { - status: 'failed', - }, - type: 'step/end', - }, - }, - ]; - }); - - it('creates expected message for all failed', () => { - const wrapper = shallowWithIntl( - - ); - expect(wrapper.find('EuiText')).toMatchInlineSnapshot(` - -

- -

-

- 2 Steps - all failed or skipped -

-
- `); - }); - - it('creates expected message for all succeeded', () => { - steps[0].synthetics!.payload!.status = 'succeeded'; - steps[1].synthetics!.payload!.status = 'succeeded'; - const wrapper = shallowWithIntl( - - ); - expect(wrapper.find('EuiText')).toMatchInlineSnapshot(` - -

- -

-

- 2 Steps - all succeeded -

-
- `); - }); - - it('creates appropriate message for mixed results', () => { - steps[0].synthetics!.payload!.status = 'succeeded'; - const wrapper = shallowWithIntl( - - ); - expect(wrapper.find('EuiText')).toMatchInlineSnapshot(` - -

- -

-

- 2 Steps - 1 succeeded -

-
- `); - }); - - it('tallies skipped steps', () => { - steps[0].synthetics!.payload!.status = 'succeeded'; - steps[1].synthetics!.payload!.status = 'skipped'; - const wrapper = shallowWithIntl( - - ); - expect(wrapper.find('EuiText')).toMatchInlineSnapshot(` - -

- -

-

- 2 Steps - 1 succeeded -

-
- `); - }); - - it('uses appropriate count when non-step/end steps are included', () => { - steps[0].synthetics!.payload!.status = 'succeeded'; - steps.push({ - docId: '3', - timestamp: '125', - monitor: MONITOR_BOILERPLATE, - synthetics: { - type: 'stderr', - error: { - message: `there was an error, that's all we know`, - stack: 'your.error.happened.here', - }, - }, - }); - const wrapper = shallowWithIntl( - - ); - expect(wrapper.find('EuiText')).toMatchInlineSnapshot(` - -

- -

-

- 2 Steps - 1 succeeded -

-
- `); - }); - - it('renders a component per step', () => { - expect( - shallowWithIntl( - - ).find('EuiFlexGroup') - ).toMatchInlineSnapshot(` - - - - - - `); - }); -}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx deleted file mode 100644 index 1ded7f065d8ab..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 { EuiFlexGroup, EuiSpacer, EuiText } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React, { FC } from 'react'; -import { Ping } from '../../../../common/runtime_types'; -import { JourneyState } from '../../../state/reducers/journey'; -import { ExecutedStep } from './executed_step'; - -interface StepStatusCount { - failed: number; - skipped: number; - succeeded: number; -} - -function statusMessage(count: StepStatusCount) { - const total = count.succeeded + count.failed + count.skipped; - if (count.failed + count.skipped === total) { - return i18n.translate('xpack.uptime.synthetics.journey.allFailedMessage', { - defaultMessage: '{total} Steps - all failed or skipped', - values: { total }, - }); - } else if (count.succeeded === total) { - return i18n.translate('xpack.uptime.synthetics.journey.allSucceededMessage', { - defaultMessage: '{total} Steps - all succeeded', - values: { total }, - }); - } - return i18n.translate('xpack.uptime.synthetics.journey.partialSuccessMessage', { - defaultMessage: '{total} Steps - {succeeded} succeeded', - values: { succeeded: count.succeeded, total }, - }); -} - -function reduceStepStatus(prev: StepStatusCount, cur: Ping): StepStatusCount { - if (cur.synthetics?.payload?.status === 'succeeded') { - prev.succeeded += 1; - return prev; - } else if (cur.synthetics?.payload?.status === 'skipped') { - prev.skipped += 1; - return prev; - } - prev.failed += 1; - return prev; -} - -function isStepEnd(step: Ping) { - return step.synthetics?.type === 'step/end'; -} - -interface ExecutedJourneyProps { - journey: JourneyState; -} - -export const ExecutedJourney: FC = ({ journey }) => { - return ( -
- -

- -

-

- {statusMessage( - journey.steps - .filter(isStepEnd) - .reduce(reduceStepStatus, { failed: 0, skipped: 0, succeeded: 0 }) - )} -

-
- - - {journey.steps.filter(isStepEnd).map((step, index) => ( - - ))} - - -
- ); -}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx deleted file mode 100644 index 991aa8fefba0a..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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 { EuiFlexItem, EuiFlexGroup, EuiSpacer, EuiText } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React, { FC } from 'react'; -import { i18n } from '@kbn/i18n'; -import { CodeBlockAccordion } from './code_block_accordion'; -import { StepScreenshotDisplay } from './step_screenshot_display'; -import { StatusBadge } from './status_badge'; -import { Ping } from '../../../../common/runtime_types'; -import { StepDetailLink } from '../../common/step_detail_link'; -import { VIEW_PERFORMANCE } from './translations'; - -const CODE_BLOCK_OVERFLOW_HEIGHT = 360; - -interface ExecutedStepProps { - step: Ping; - index: number; - checkGroup: string; -} - -export const ExecutedStep: FC = ({ step, index, checkGroup }) => { - return ( - <> -
- - - - - - - - -
- -
-
-
- -
- - - - - - {step.synthetics?.step?.index && ( - - - {VIEW_PERFORMANCE} - - - - )} - - {step.synthetics?.payload?.source} - - - {step.synthetics?.error?.message} - - - {step.synthetics?.error?.stack} - - - -
-
- - ); -}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.test.tsx deleted file mode 100644 index 304787e96818f..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.test.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 { shallowWithIntl } from '@kbn/test/jest'; -import React from 'react'; -import { StatusBadge } from './status_badge'; - -describe('StatusBadge', () => { - it('displays success message', () => { - expect(shallowWithIntl()).toMatchInlineSnapshot(` - - Succeeded - - `); - }); - - it('displays failed message', () => { - expect(shallowWithIntl()).toMatchInlineSnapshot(` - - Failed - - `); - }); - - it('displays skipped message', () => { - expect(shallowWithIntl()).toMatchInlineSnapshot(` - - Skipped - - `); - }); -}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx index 346af9d31a28b..ef0d001ac905e 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx @@ -48,7 +48,7 @@ export const StepDetailContainer: React.FC = ({ checkGroup, stepIndex }) }; }, [stepIndex, journey]); - useMonitorBreadcrumb({ journey, activeStep }); + useMonitorBreadcrumb({ details: journey?.details, activeStep, performanceBreakDownView: true }); const handleNextStep = useCallback(() => { history.push(`/journey/${checkGroup}/step/${stepIndex + 1}`); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx index c51b85f76d605..7ba0d5b3b3efa 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx @@ -11,13 +11,19 @@ import { useKibana, useUiSetting$ } from '../../../../../../../../src/plugins/ki import { JourneyState } from '../../../../state/reducers/journey'; import { Ping } from '../../../../../common/runtime_types/ping'; import { PLUGIN } from '../../../../../common/constants/plugin'; +import { i18n } from '@kbn/i18n'; interface Props { - journey: JourneyState; + details: JourneyState['details']; activeStep?: Ping; + performanceBreakDownView?: boolean; } -export const useMonitorBreadcrumb = ({ journey, activeStep }: Props) => { +export const useMonitorBreadcrumb = ({ + details, + activeStep, + performanceBreakDownView = false, +}: Props) => { const [dateFormat] = useUiSetting$('dateFormat'); const kibana = useKibana(); @@ -32,8 +38,22 @@ export const useMonitorBreadcrumb = ({ journey, activeStep }: Props) => { }, ] : []), - ...(journey?.details?.timestamp - ? [{ text: moment(journey?.details?.timestamp).format(dateFormat) }] + ...(details?.journey?.monitor?.check_group + ? [ + { + text: moment(details?.timestamp).format(dateFormat), + href: `${appPath}/journey/${details.journey.monitor.check_group}/steps`, + }, + ] + : []), + ...(performanceBreakDownView + ? [ + { + text: i18n.translate('xpack.uptime.synthetics.performanceBreakDown.label', { + defaultMessage: 'Performance breakdown', + }), + }, + ] : []), ]); }; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx index ac79d7f4c2a8a..843693346f0d6 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx @@ -44,7 +44,7 @@ describe('useMonitorBreadcrumbs', () => { const Component = () => { useMonitorBreadcrumb({ activeStep: { monitor: { id: 'test-monitor' } } as Ping, - journey: { details: { timestamp: '2021-01-04T11:25:19.104Z' } } as JourneyState, + details: { timestamp: '2021-01-04T11:25:19.104Z' } as JourneyState['details'], }); return <>Step Water Fall; }; @@ -68,9 +68,6 @@ describe('useMonitorBreadcrumbs', () => { "onClick": [Function], "text": "test-monitor", }, - Object { - "text": "Jan 4, 2021 @ 06:25:19.104", - }, ] `); }); diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/expand_row_col.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/expand_row_col.tsx new file mode 100644 index 0000000000000..5afeef7790fdb --- /dev/null +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/expand_row_col.tsx @@ -0,0 +1,59 @@ +/* + * 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 from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButtonIcon } from '@elastic/eui'; +import { Ping } from '../../../../common/runtime_types/ping'; +import { ExecutedStep } from '../executed_step'; + +export const toggleExpand = ( + ping: Ping, + expandedRows: Record, + setExpandedRows: (update: Record) => any +) => { + // If already expanded, collapse + if (expandedRows[ping.docId]) { + delete expandedRows[ping.docId]; + setExpandedRows({ ...expandedRows }); + return; + } + + // Otherwise expand this row + setExpandedRows({ + ...expandedRows, + [ping.docId]: ( + + ), + }); +}; + +interface Props { + item: Ping; + expandedRows: Record; + setExpandedRows: (val: Record) => void; +} +export const ExpandRowColumn = ({ item, expandedRows, setExpandedRows }: Props) => { + return ( + toggleExpand(item, expandedRows, setExpandedRows)} + aria-label={ + expandedRows[item.docId] + ? i18n.translate('xpack.uptime.stepList.collapseRow', { + defaultMessage: 'Collapse', + }) + : i18n.translate('xpack.uptime.stepList.expandRow', { defaultMessage: 'Expand' }) + } + iconType={expandedRows[item.docId] ? 'arrowUp' : 'arrowDown'} + /> + ); +}; diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx new file mode 100644 index 0000000000000..e3768190f7a06 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx @@ -0,0 +1,26 @@ +/* + * 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 from 'react'; +import { Ping } from '../../../../common/runtime_types/ping'; +import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { PingTimestamp } from '../../monitor/ping_list/columns/ping_timestamp'; + +interface Props { + step: Ping; +} +export const StepImage = ({ step }: Props) => { + return ( + + + + + + {step.synthetics?.step?.name} + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_list.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_list.test.tsx new file mode 100644 index 0000000000000..959bf0f644580 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_list.test.tsx @@ -0,0 +1,144 @@ +/* + * 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 from 'react'; +import { Ping } from '../../../../common/runtime_types/ping'; +import { StepsList } from './steps_list'; +import { render } from '../../../lib/helper/rtl_helpers'; + +describe('StepList component', () => { + let steps: Ping[]; + + beforeEach(() => { + steps = [ + { + docId: '1', + timestamp: '123', + monitor: { + id: 'MON_ID', + duration: { + us: 10, + }, + status: 'down', + type: 'browser', + check_group: 'fake-group', + }, + synthetics: { + payload: { + status: 'failed', + }, + type: 'step/end', + step: { + name: 'load page', + index: 1, + }, + }, + }, + { + docId: '2', + timestamp: '124', + monitor: { + id: 'MON_ID', + duration: { + us: 10, + }, + status: 'down', + type: 'browser', + check_group: 'fake-group-1', + }, + synthetics: { + payload: { + status: 'failed', + }, + type: 'step/end', + step: { + name: 'go to login', + index: 2, + }, + }, + }, + ]; + }); + + it('creates expected message for all failed', () => { + const { getByText } = render(); + expect(getByText('2 Steps - all failed or skipped')); + }); + + it('renders a link to the step detail view', () => { + const { getByTitle, getByTestId } = render(); + expect(getByTestId('step-detail-link')).toHaveAttribute('href', '/journey/fake-group/step/1'); + expect(getByTitle(`Failed`)); + }); + + it.each([ + ['succeeded', 'Succeeded'], + ['failed', 'Failed'], + ['skipped', 'Skipped'], + ])('supplies status badge correct status', (status, expectedStatus) => { + const step = steps[0]; + step.synthetics!.payload!.status = status; + const { getByText } = render(); + expect(getByText(expectedStatus)); + }); + + it('creates expected message for all succeeded', () => { + steps[0].synthetics!.payload!.status = 'succeeded'; + steps[1].synthetics!.payload!.status = 'succeeded'; + + const { getByText } = render(); + expect(getByText('2 Steps - all succeeded')); + }); + + it('creates appropriate message for mixed results', () => { + steps[0].synthetics!.payload!.status = 'succeeded'; + + const { getByText } = render(); + expect(getByText('2 Steps - 1 succeeded')); + }); + + it('tallies skipped steps', () => { + steps[0].synthetics!.payload!.status = 'succeeded'; + steps[1].synthetics!.payload!.status = 'skipped'; + + const { getByText } = render(); + expect(getByText('2 Steps - 1 succeeded')); + }); + + it('uses appropriate count when non-step/end steps are included', () => { + steps[0].synthetics!.payload!.status = 'succeeded'; + steps.push({ + docId: '3', + timestamp: '125', + monitor: { + id: 'MON_ID', + duration: { + us: 10, + }, + status: 'down', + type: 'browser', + check_group: 'fake-group-2', + }, + synthetics: { + type: 'stderr', + error: { + message: `there was an error, that's all we know`, + stack: 'your.error.happened.here', + }, + }, + }); + + const { getByText } = render(); + expect(getByText('2 Steps - 1 succeeded')); + }); + + it('renders a row per step', () => { + const { getByTestId } = render(); + expect(getByTestId('row-fake-group')); + expect(getByTestId('row-fake-group-1')); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx index 2e7c7cb64e1c3..f3d84ed0950ab 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx @@ -5,29 +5,75 @@ * 2.0. */ -import { EuiBasicTable, EuiPanel } from '@elastic/eui'; +import { EuiBasicTable, EuiPanel, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useCallback, useState, useEffect } from 'react'; +import React, { useCallback, useState, useEffect, MouseEvent } from 'react'; import styled from 'styled-components'; import { useDispatch } from 'react-redux'; -import { useHistory } from 'react-router-dom'; import { Ping } from '../../../../common/runtime_types'; import { pruneJourneyState } from '../../../state/actions/journey'; import { clearPings } from '../../../state/actions'; import { STATUS_LABEL } from '../../monitor/ping_list/translations'; -import { PingStatusColumn } from '../../monitor/ping_list/columns/ping_status'; -import { PingTimestamp } from '../../monitor/ping_list/columns/ping_timestamp'; -import { ExpandRowColumn } from '../../monitor/ping_list/columns/expand_row'; import { STEP_NAME_LABEL } from '../translations'; +import { ExpandRowColumn, toggleExpand } from './expand_row_col'; +import { StatusBadge } from '../status_badge'; +import { StepDetailLink } from '../../common/step_detail_link'; +import { VIEW_PERFORMANCE } from '../../monitor/synthetics/translations'; +import { StepImage } from './step_image'; export const SpanWithMargin = styled.span` margin-right: 16px; `; - -export const StepsList = ({ data, error, loading }) => { +interface Props { + data: any[]; + error?: Error; + loading: boolean; +} + +function isStepEnd(step: Ping) { + return step.synthetics?.type === 'step/end'; +} +interface StepStatusCount { + failed: number; + skipped: number; + succeeded: number; +} + +function statusMessage(count: StepStatusCount) { + const total = count.succeeded + count.failed + count.skipped; + if (count.failed + count.skipped === total) { + return i18n.translate('xpack.uptime.synthetics.journey.allFailedMessage', { + defaultMessage: '{total} Steps - all failed or skipped', + values: { total }, + }); + } else if (count.succeeded === total) { + return i18n.translate('xpack.uptime.synthetics.journey.allSucceededMessage', { + defaultMessage: '{total} Steps - all succeeded', + values: { total }, + }); + } + return i18n.translate('xpack.uptime.synthetics.journey.partialSuccessMessage', { + defaultMessage: '{total} Steps - {succeeded} succeeded', + values: { succeeded: count.succeeded, total }, + }); +} + +function reduceStepStatus(prev: StepStatusCount, cur: Ping): StepStatusCount { + if (cur.synthetics?.payload?.status === 'succeeded') { + prev.succeeded += 1; + return prev; + } else if (cur.synthetics?.payload?.status === 'skipped') { + prev.skipped += 1; + return prev; + } + prev.failed += 1; + return prev; +} + +export const StepsList = ({ data, error, loading }: Props) => { const dispatch = useDispatch(); - const history = useHistory(); + const steps = data.filter(isStepEnd); const pruneJourneysCallback = useCallback( (checkGroups: string[]) => dispatch(pruneJourneyState(checkGroups)), @@ -66,57 +112,80 @@ export const StepsList = ({ data, error, loading }) => { pruneJourneysCallback(JSON.parse(expandedCheckGroupsStr)); }, [pruneJourneysCallback, expandedCheckGroupsStr]); - const columns = [ + const columns: any[] = [ { - field: 'monitor.status', + field: 'synthetics.payload.status', name: STATUS_LABEL, render: (pingStatus: string, item: Ping) => ( - + ), }, - { align: 'left', field: 'timestamp', name: STEP_NAME_LABEL, - render: (timestamp: string, item: Ping) => ( - + render: (timestamp: string, item: Ping) => , + }, + { + align: 'left', + field: 'timestamp', + name: '', + width: '250px', + render: (val: string, item: Ping) => ( + + {VIEW_PERFORMANCE} + ), }, - { align: 'right', width: '24px', isExpander: true, - render: (item: Ping) => ( - - ), + render: (item: Ping) => { + return ( + + ); + }, }, ]; const getRowProps = (item: Ping) => { const { monitor } = item; + return { 'data-test-subj': `row-${monitor.check_group}`, - onClick: () => { - history.push(`/journey/${monitor.check_group}/steps`); + onClick: (evt: MouseEvent) => { + const targetElem = evt.target as HTMLElement; + + // we dont want to capture image click event + if (targetElem.tagName !== 'IMG') { + toggleExpand(item, expandedRows, setExpandedRows); + } }, }; }; return ( + +

+ {statusMessage(steps.reduce(reduceStepStatus, { failed: 0, skipped: 0, succeeded: 0 }))} +

+
{ +export const useCheckSteps = (): JourneyState => { const { checkGroupId } = useParams<{ checkGroupId: string; stepIndex: string }>(); - const { data, status } = useFetcher(() => { + const { data, status, error } = useFetcher(() => { return fetchJourneySteps({ checkGroup: checkGroupId, }); }, [checkGroupId]); return { + error, + checkGroup: checkGroupId, steps: data?.steps ?? [], - ping: data?.details.journey, - timestamp: data?.details.timestamp, + details: data?.details, loading: status == FETCH_STATUS.LOADING || status === FETCH_STATUS.PENDING, }; }; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx b/x-pack/plugins/uptime/public/components/synthetics/code_block_accordion.tsx similarity index 87% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx rename to x-pack/plugins/uptime/public/components/synthetics/code_block_accordion.tsx index 18aeb7a236ca8..225ba1041c263 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/code_block_accordion.tsx @@ -13,6 +13,7 @@ interface Props { id?: string; language: 'html' | 'javascript'; overflowHeight: number; + initialIsOpen?: boolean; } /** @@ -25,9 +26,10 @@ export const CodeBlockAccordion: FC = ({ id, language, overflowHeight, + initialIsOpen = false, }) => { return children && id ? ( - + {children} diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/console_event.test.tsx similarity index 100% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.test.tsx rename to x-pack/plugins/uptime/public/components/synthetics/console_event.test.tsx diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx b/x-pack/plugins/uptime/public/components/synthetics/console_event.tsx similarity index 89% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx rename to x-pack/plugins/uptime/public/components/synthetics/console_event.tsx index dc7b6ce9ea123..19672f953607b 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/console_event.tsx @@ -7,8 +7,8 @@ import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import React, { useContext, FC } from 'react'; -import { Ping } from '../../../../common/runtime_types'; -import { UptimeThemeContext } from '../../../contexts'; +import { UptimeThemeContext } from '../../contexts'; +import { Ping } from '../../../common/runtime_types/ping'; interface Props { event: Ping; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/console_output_event_list.test.tsx similarity index 100% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.test.tsx rename to x-pack/plugins/uptime/public/components/synthetics/console_output_event_list.test.tsx diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/console_output_event_list.tsx similarity index 92% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx rename to x-pack/plugins/uptime/public/components/synthetics/console_output_event_list.tsx index df1f6aeb3623b..df4314e5ccf1c 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/console_output_event_list.tsx @@ -8,9 +8,9 @@ import { EuiCodeBlock, EuiSpacer, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { FC } from 'react'; -import { Ping } from '../../../../common/runtime_types'; -import { JourneyState } from '../../../state/reducers/journey'; import { ConsoleEvent } from './console_event'; +import { Ping } from '../../../common/runtime_types/ping'; +import { JourneyState } from '../../state/reducers/journey'; interface Props { journey: JourneyState; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/empty_journey.test.tsx similarity index 100% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.test.tsx rename to x-pack/plugins/uptime/public/components/synthetics/empty_journey.test.tsx diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.tsx b/x-pack/plugins/uptime/public/components/synthetics/empty_journey.tsx similarity index 100% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.tsx rename to x-pack/plugins/uptime/public/components/synthetics/empty_journey.tsx diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx similarity index 56% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.test.tsx rename to x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx index 225ccb884ad00..dd4e367232b46 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.test.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { ExecutedStep } from './executed_step'; -import { Ping } from '../../../../common/runtime_types'; -import { render } from '../../../lib/helper/rtl_helpers'; +import { render } from '../../lib/helper/rtl_helpers'; +import { Ping } from '../../../common/runtime_types/ping'; describe('ExecutedStep', () => { let step: Ping; @@ -34,33 +34,6 @@ describe('ExecutedStep', () => { }; }); - it('renders correct step heading', () => { - const { getByText } = render(); - - expect(getByText(`${step?.synthetics?.step?.index}. ${step?.synthetics?.step?.name}`)); - }); - - it('renders a link to the step detail view', () => { - const { getByRole, getByText } = render( - - ); - expect(getByRole('link')).toHaveAttribute('href', '/journey/fake-group/step/4'); - expect(getByText('4. STEP_NAME')); - }); - - it.each([ - ['succeeded', 'Succeeded'], - ['failed', 'Failed'], - ['skipped', 'Skipped'], - ['somegarbage', '4.'], - ])('supplies status badge correct status', (status, expectedStatus) => { - step.synthetics = { - payload: { status }, - }; - const { getByText } = render(); - expect(getByText(expectedStatus)); - }); - it('renders accordion for step', () => { step.synthetics = { payload: { @@ -74,8 +47,7 @@ describe('ExecutedStep', () => { const { getByText } = render(); - expect(getByText('4. STEP_NAME')); - expect(getByText('Step script')); + expect(getByText('Script executed at this step')); expect(getByText(`const someVar = "the var"`)); }); @@ -89,8 +61,7 @@ describe('ExecutedStep', () => { const { getByText } = render(); - expect(getByText('4.')); - expect(getByText('Error')); + expect(getByText('Error message')); expect(getByText('There was an error executing the step.')); expect(getByText('some.stack.trace.string')); }); diff --git a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx new file mode 100644 index 0000000000000..3ff8a7709e183 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx @@ -0,0 +1,89 @@ +/* + * 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 { EuiSpacer, EuiText } from '@elastic/eui'; +import React, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { CodeBlockAccordion } from './code_block_accordion'; +import { StepScreenshotDisplay } from './step_screenshot_display'; +import { Ping } from '../../../common/runtime_types/ping'; +import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; + +const CODE_BLOCK_OVERFLOW_HEIGHT = 360; + +interface ExecutedStepProps { + step: Ping; + index: number; + checkGroup: string; +} + +const Label = euiStyled.div` + margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs}; + font-size: ${({ theme }) => theme.eui.euiFontSizeS}; + color: ${({ theme }) => theme.eui.euiColorDarkShade}; +`; + +const Message = euiStyled.div` + font-weight: bold; + font-size:${({ theme }) => theme.eui.euiFontSizeM}; + margin-bottom: ${(props) => props.theme.eui.paddingSizes.m}; +`; + +export const ExecutedStep: FC = ({ step, index, checkGroup }) => { + return ( + <> +
+ + {step.synthetics?.error?.message && ( + + + {step.synthetics?.error?.message} + + )} + + + {step.synthetics?.payload?.source} + + + + + + {step.synthetics?.error?.stack} + +
+ + ); +}; diff --git a/x-pack/plugins/uptime/public/components/synthetics/status_badge.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/status_badge.test.tsx new file mode 100644 index 0000000000000..500c680b91bf6 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/synthetics/status_badge.test.tsx @@ -0,0 +1,33 @@ +/* + * 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 from 'react'; +import { StatusBadge } from './status_badge'; +import { render } from '../../lib/helper/rtl_helpers'; + +describe('StatusBadge', () => { + it('displays success message', () => { + const { getByText } = render(); + + expect(getByText('1.')); + expect(getByText('Succeeded')); + }); + + it('displays failed message', () => { + const { getByText } = render(); + + expect(getByText('2.')); + expect(getByText('Failed')); + }); + + it('displays skipped message', () => { + const { getByText } = render(); + + expect(getByText('3.')); + expect(getByText('Skipped')); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx b/x-pack/plugins/uptime/public/components/synthetics/status_badge.tsx similarity index 66% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx rename to x-pack/plugins/uptime/public/components/synthetics/status_badge.tsx index 0cf9e5477d0db..b4c4e310abe6b 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/status_badge.tsx @@ -5,14 +5,15 @@ * 2.0. */ -import { EuiBadge } from '@elastic/eui'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useContext, FC } from 'react'; -import { UptimeAppColors } from '../../../apps/uptime_app'; -import { UptimeThemeContext } from '../../../contexts'; +import { UptimeAppColors } from '../../apps/uptime_app'; +import { UptimeThemeContext } from '../../contexts'; interface StatusBadgeProps { status?: string; + stepNo: number; } export function colorFromStatus(color: UptimeAppColors, status?: string) { @@ -45,9 +46,18 @@ export function textFromStatus(status?: string) { } } -export const StatusBadge: FC = ({ status }) => { +export const StatusBadge: FC = ({ status, stepNo }) => { const theme = useContext(UptimeThemeContext); return ( - {textFromStatus(status)} + + + + {stepNo}. + + + + {textFromStatus(status)} + + ); }; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.test.tsx similarity index 96% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.test.tsx rename to x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.test.tsx index 29dca39c34bf2..52d2eacaf0e52 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.test.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.test.tsx @@ -5,9 +5,9 @@ * 2.0. */ -import { render } from '../../../lib/helper/rtl_helpers'; import React from 'react'; import { StepScreenshotDisplay } from './step_screenshot_display'; +import { render } from '../../lib/helper/rtl_helpers'; jest.mock('react-use/lib/useIntersection', () => () => ({ isIntersecting: true, diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx b/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx similarity index 94% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx rename to x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx index 654193de72a9c..7b7f3ffadfc65 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx @@ -11,17 +11,18 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useContext, useEffect, useRef, useState, FC } from 'react'; import useIntersection from 'react-use/lib/useIntersection'; -import { UptimeSettingsContext, UptimeThemeContext } from '../../../contexts'; +import { UptimeSettingsContext, UptimeThemeContext } from '../../contexts'; interface StepScreenshotDisplayProps { screenshotExists?: boolean; checkGroup?: string; stepIndex?: number; stepName?: string; + allowPopover?: boolean; } -const THUMBNAIL_WIDTH = 320; -const THUMBNAIL_HEIGHT = 180; +const THUMBNAIL_WIDTH = 640; +const THUMBNAIL_HEIGHT = 360; const POPOVER_IMG_WIDTH = 640; const POPOVER_IMG_HEIGHT = 360; @@ -42,6 +43,7 @@ export const StepScreenshotDisplay: FC = ({ screenshotExists, stepIndex, stepName, + allowPopover = true, }) => { const containerRef = useRef(null); const { @@ -92,8 +94,8 @@ export const StepScreenshotDisplay: FC = ({ caption={`Step:${stepIndex} ${stepName}`} hasShadow url={imgSrc} - onMouseEnter={() => setIsImagePopoverOpen(true)} - onMouseLeave={() => setIsImagePopoverOpen(false)} + onMouseEnter={allowPopover ? () => setIsImagePopoverOpen(true) : undefined} + onMouseLeave={allowPopover ? () => setIsImagePopoverOpen(false) : undefined} /> } closePopover={() => setIsImagePopoverOpen(false)} diff --git a/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx b/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx new file mode 100644 index 0000000000000..3615574fcf6bb --- /dev/null +++ b/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx @@ -0,0 +1,63 @@ +/* + * 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 from 'react'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { useHistory } from 'react-router-dom'; +import moment from 'moment'; +import { useUiSetting$ } from '../../../../../../src/plugins/kibana_react/public'; +import { SyntheticsJourneyApiResponse } from '../../../common/runtime_types/ping'; + +interface Props { + timestamp: string; + details: SyntheticsJourneyApiResponse['details']; +} + +export const ChecksNavigation = ({ timestamp, details }: Props) => { + const [dateFormat] = useUiSetting$('dateFormat'); + + const history = useHistory(); + + return ( + + + { + history.push(`/journey/${details?.previous?.checkGroup}/steps`); + }} + > + + + + + + {moment(timestamp).format(dateFormat).toString()} + + + + { + history.push(`/journey/${details?.next?.checkGroup}/steps`); + }} + > + + + + + ); +}; diff --git a/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx b/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx index dc622c5f4a855..f1e8c8b020c31 100644 --- a/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx +++ b/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx @@ -6,72 +6,39 @@ */ import React from 'react'; -import { useParams } from 'react-router-dom'; -import { - EuiButton, - EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPage, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; -import moment from 'moment'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; import { useTrackPageview } from '../../../../observability/public'; import { useInitApp } from '../../hooks/use_init_app'; import { StepsList } from '../../components/synthetics/check_steps/steps_list'; import { useCheckSteps } from '../../components/synthetics/check_steps/use_check_steps'; -import { useUiSetting$ } from '../../../../../../src/plugins/kibana_react/public'; +import { ChecksNavigation } from './checks_navigation'; +import { useMonitorBreadcrumb } from '../../components/monitor/synthetics/step_detail/use_monitor_breadcrumb'; +import { EmptyJourney } from '../../components/synthetics/empty_journey'; export const SyntheticsCheckSteps: React.FC = () => { useInitApp(); useTrackPageview({ app: 'uptime', path: 'syntheticCheckSteps' }); useTrackPageview({ app: 'uptime', path: 'syntheticCheckSteps', delay: 15000 }); - const { checkGroupId } = useParams<{ checkGroupId: string; stepIndex: string }>(); + const { error, loading, steps, details, checkGroup } = useCheckSteps(); - const { error, loading, steps, ping, timestamp } = useCheckSteps(); - - const [dateFormat] = useUiSetting$('dateFormat'); + useMonitorBreadcrumb({ details, activeStep: details?.journey }); return ( <> -

{ping?.monitor.name}

+

{details?.journey?.monitor.name}

- - - - - - - - {moment(timestamp).format(dateFormat).toString()} - - - - - - - + {details && }
- + {(!steps || steps.length === 0) && } ); }; From 8797f45208a743f104a45b4da567cfd3aaf1fce0 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Wed, 10 Feb 2021 17:55:19 +0100 Subject: [PATCH 03/30] Addew check steps list view --- .../public/components/monitor/ping_list/ping_list.tsx | 2 +- .../synthetics/step_detail/use_monitor_breadcrumb.tsx | 2 +- .../components/synthetics/check_steps/step_image.tsx | 8 +++++--- .../components/synthetics/check_steps/use_check_steps.ts | 2 +- .../uptime/public/pages/synthetics/checks_navigation.tsx | 5 +++-- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx index 2b05cf699ff73..148b340fdd2d5 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import React, { useCallback, useState, useEffect, MouseEvent } from 'react'; import styled from 'styled-components'; import { useHistory } from 'react-router-dom'; +import moment from 'moment'; import { useDispatch } from 'react-redux'; import { Ping } from '../../../../common/runtime_types'; import { convertMicrosecondsToMilliseconds as microsToMillis } from '../../../lib/helper'; @@ -29,7 +30,6 @@ import { usePingsList } from './use_pings'; import { PingListHeader } from './ping_list_header'; import { clearPings } from '../../../state/actions'; import { getShortTimeStamp } from '../../overview/monitor_list/columns/monitor_status_column'; -import moment from 'moment'; export const SpanWithMargin = styled.span` margin-right: 16px; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx index 7ba0d5b3b3efa..220640e2f488f 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumb.tsx @@ -6,12 +6,12 @@ */ import moment from 'moment'; +import { i18n } from '@kbn/i18n'; import { useBreadcrumbs } from '../../../../hooks/use_breadcrumbs'; import { useKibana, useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/public'; import { JourneyState } from '../../../../state/reducers/journey'; import { Ping } from '../../../../../common/runtime_types/ping'; import { PLUGIN } from '../../../../../common/constants/plugin'; -import { i18n } from '@kbn/i18n'; interface Props { details: JourneyState['details']; diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx index e3768190f7a06..4c9101ebccb00 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx @@ -1,17 +1,19 @@ /* * 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. + * 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 from 'react'; -import { Ping } from '../../../../common/runtime_types/ping'; import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { Ping } from '../../../../common/runtime_types/ping'; import { PingTimestamp } from '../../monitor/ping_list/columns/ping_timestamp'; interface Props { step: Ping; } + export const StepImage = ({ step }: Props) => { return ( diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts index 66ab2a73b2644..c95115cc3d854 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts @@ -24,6 +24,6 @@ export const useCheckSteps = (): JourneyState => { checkGroup: checkGroupId, steps: data?.steps ?? [], details: data?.details, - loading: status == FETCH_STATUS.LOADING || status === FETCH_STATUS.PENDING, + loading: status === FETCH_STATUS.LOADING || status === FETCH_STATUS.PENDING, }; }; diff --git a/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx b/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx index 3615574fcf6bb..5ecd872aee6a7 100644 --- a/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx +++ b/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx @@ -1,7 +1,8 @@ /* * 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. + * 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 from 'react'; From e548040a0cbcd84dc7402958e410f5b029cd65f9 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Wed, 10 Feb 2021 18:56:50 +0100 Subject: [PATCH 04/30] fix i18n --- .../uptime/public/pages/synthetics/checks_navigation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx b/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx index 5ecd872aee6a7..88096d62f9f43 100644 --- a/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx +++ b/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx @@ -54,7 +54,7 @@ export const ChecksNavigation = ({ timestamp, details }: Props) => { }} > From 7d92edb0e7a9e41d144fa0398193f93d0b3e3a24 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 11 Feb 2021 09:52:37 +0100 Subject: [PATCH 05/30] wip --- .../ping_timestamp/step_image_popover.tsx | 1 + .../synthetics/check_steps/expand_row_col.tsx | 33 +++++---- .../step_expanded_row/step_screenshots.tsx | 64 +++++++++++++++++ .../synthetics/check_steps/steps_list.tsx | 11 ++- .../synthetics/executed_step.test.tsx | 16 ++++- .../components/synthetics/executed_step.tsx | 36 +++++++--- .../synthetics/step_screenshot_display.tsx | 4 +- .../lib/requests/get_journey_steps.test.ts | 2 + .../server/lib/requests/get_journey_steps.ts | 8 ++- .../lib/requests/get_step_screenshot.ts | 70 +++++++++++++++++++ .../uptime/server/lib/requests/index.ts | 2 + .../plugins/uptime/server/rest_api/index.ts | 2 + .../rest_api/pings/journey_screenshots.ts | 41 +++++++++++ .../uptime/server/rest_api/pings/journeys.ts | 24 +++++++ 14 files changed, 282 insertions(+), 32 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx create mode 100644 x-pack/plugins/uptime/server/lib/requests/get_step_screenshot.ts diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx index 1e6c97cd131a4..b1332827dec5b 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx @@ -52,6 +52,7 @@ export const StepImagePopover: React.FC = ({ /> } isOpen={isImagePopoverOpen} + closePopover={() => {}} > , - setExpandedRows: (update: Record) => any -) => { +interface Props { + ping: Ping; + browserConsole?: string; + expandedRows: Record; + setExpandedRows: (val: Record) => void; +} + +export const toggleExpand = ({ + ping, + browserConsole = '', + expandedRows, + setExpandedRows, +}: Props) => { // If already expanded, collapse if (expandedRows[ping.docId]) { delete expandedRows[ping.docId]; @@ -29,31 +37,26 @@ export const toggleExpand = ( [ping.docId]: ( ), }); }; -interface Props { - item: Ping; - expandedRows: Record; - setExpandedRows: (val: Record) => void; -} -export const ExpandRowColumn = ({ item, expandedRows, setExpandedRows }: Props) => { +export const ExpandRowColumn = ({ ping, browserConsole, expandedRows, setExpandedRows }: Props) => { return ( toggleExpand(item, expandedRows, setExpandedRows)} + onClick={() => toggleExpand({ ping, browserConsole, expandedRows, setExpandedRows })} aria-label={ - expandedRows[item.docId] + expandedRows[ping.docId] ? i18n.translate('xpack.uptime.stepList.collapseRow', { defaultMessage: 'Collapse', }) : i18n.translate('xpack.uptime.stepList.expandRow', { defaultMessage: 'Expand' }) } - iconType={expandedRows[item.docId] ? 'arrowUp' : 'arrowDown'} + iconType={expandedRows[ping.docId] ? 'arrowUp' : 'arrowDown'} /> ); }; diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx new file mode 100644 index 0000000000000..e6ba2bd707278 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx @@ -0,0 +1,64 @@ +/* + * 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, { useContext } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { StepScreenshotDisplay } from '../../step_screenshot_display'; +import { Ping } from '../../../../../common/runtime_types/ping'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; +import { UptimeSettingsContext } from '../../../../contexts'; + +const Label = euiStyled.div` + margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs}; + font-size: ${({ theme }) => theme.eui.euiFontSizeS}; + color: ${({ theme }) => theme.eui.euiColorDarkShade}; +`; + +interface Props { + step: Ping; +} + +export const StepScreenshots = ({ step }: Props) => { + const { basePath } = useContext(UptimeSettingsContext); + + + return ( + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx index f3d84ed0950ab..feac6766eb960 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx @@ -147,7 +147,14 @@ export const StepsList = ({ data, error, loading }: Props) => { render: (item: Ping) => { return ( + step.synthetics.type === 'journey/browserconsole' && + step.synthetics?.step?.index! === item.synthetics?.step?.index + )?.synthetics?.payload?.text + } expandedRows={expandedRows} setExpandedRows={setExpandedRows} /> @@ -166,7 +173,7 @@ export const StepsList = ({ data, error, loading }: Props) => { // we dont want to capture image click event if (targetElem.tagName !== 'IMG') { - toggleExpand(item, expandedRows, setExpandedRows); + toggleExpand({ ping: item, expandedRows, setExpandedRows }); } }, }; diff --git a/x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx index dd4e367232b46..8d3c0ecda6610 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx @@ -45,7 +45,7 @@ describe('ExecutedStep', () => { }, }; - const { getByText } = render(); + const { getByText } = render(); expect(getByText('Script executed at this step')); expect(getByText(`const someVar = "the var"`)); @@ -59,10 +59,22 @@ describe('ExecutedStep', () => { }, }; - const { getByText } = render(); + const { getByText } = render(); expect(getByText('Error message')); expect(getByText('There was an error executing the step.')); expect(getByText('some.stack.trace.string')); }); + + it('renders accordions for console output', () => { + const browserConsole = + "Refused to execute script from because its MIME type ('image/gif') is not executable"; + + const { getByText } = render( + + ); + + expect(getByText('Console output')); + expect(getByText(browserConsole)); + }); }); diff --git a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx index 3ff8a7709e183..fd0e13f92e157 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx @@ -9,16 +9,16 @@ import { EuiSpacer, EuiText } from '@elastic/eui'; import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; import { CodeBlockAccordion } from './code_block_accordion'; -import { StepScreenshotDisplay } from './step_screenshot_display'; import { Ping } from '../../../common/runtime_types/ping'; import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; +import { StepScreenshots } from './check_steps/step_expanded_row/step_screenshots'; const CODE_BLOCK_OVERFLOW_HEIGHT = 360; interface ExecutedStepProps { step: Ping; index: number; - checkGroup: string; + browserConsole?: string; } const Label = euiStyled.div` @@ -33,10 +33,10 @@ const Message = euiStyled.div` margin-bottom: ${(props) => props.theme.eui.paddingSizes.m}; `; -export const ExecutedStep: FC = ({ step, index, checkGroup }) => { +export const ExecutedStep: FC = ({ step, index, browserConsole = '' }) => { return ( <> -
+
{step.synthetics?.error?.message && ( @@ -62,15 +62,29 @@ export const ExecutedStep: FC = ({ step, index, checkGroup }) initialIsOpen={true} > {step.synthetics?.payload?.source} + {' '} + + + {browserConsole} - + + = ({ + srcPath, checkGroup, screenshotExists, stepIndex, @@ -69,7 +71,7 @@ export const StepScreenshotDisplay: FC = ({ }, [hasIntersected, isIntersecting, setHasIntersected]); let content: JSX.Element | null = null; - const imgSrc = basePath + `/api/uptime/journey/screenshot/${checkGroup}/${stepIndex}`; + const imgSrc = srcPath || basePath + `/api/uptime/journey/screenshot/${checkGroup}/${stepIndex}`; if (hasIntersected && screenshotExists) { content = ( diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.test.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.test.ts index 1034318257f66..2f1426dffe121 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.test.ts @@ -17,6 +17,7 @@ describe('getJourneySteps request module', () => { "stderr", "cmd/status", "step/screenshot", + "journey/browserconsole", ] `); }); @@ -124,6 +125,7 @@ describe('getJourneySteps request module', () => { "stderr", "cmd/status", "step/screenshot", + "journey/browserconsole", ], }, } diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts index 3055f169fc495..4c561d99eb76c 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts @@ -13,7 +13,13 @@ export interface GetJourneyStepsParams { syntheticEventTypes?: string | string[]; } -const defaultEventTypes = ['step/end', 'stderr', 'cmd/status', 'step/screenshot']; +const defaultEventTypes = [ + 'step/end', + 'stderr', + 'cmd/status', + 'step/screenshot', + 'journey/browserconsole', +]; export const formatSyntheticEvents = (eventTypes?: string | string[]) => { if (!eventTypes) { diff --git a/x-pack/plugins/uptime/server/lib/requests/get_step_screenshot.ts b/x-pack/plugins/uptime/server/lib/requests/get_step_screenshot.ts new file mode 100644 index 0000000000000..39700a23bd7c9 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/requests/get_step_screenshot.ts @@ -0,0 +1,70 @@ +/* + * 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 { UMElasticsearchQueryFn } from '../adapters/framework'; +import { Ping } from '../../../common/runtime_types/ping'; + +export interface GetStepScreenshotParams { + monitorId: string; + timestamp: string; + stepIndex: number; +} + +export const getStepLastSuccessfulScreenshot: UMElasticsearchQueryFn< + GetStepScreenshotParams, + any +> = async ({ uptimeEsClient, monitorId, stepIndex, timestamp }) => { + const params = { + size: 1, + query: { + bool: { + filter: [ + { + range: { + '@timestamp': { + lte: timestamp, + }, + }, + }, + { + term: { + 'monitor.id': monitorId, + }, + }, + { + term: { + 'synthetics.type': 'step/screenshot', + }, + }, + { + term: { + 'synthetics.payload.status': 'succeeded', + }, + }, + { + term: { + 'synthetics.step.index': stepIndex, + }, + }, + ], + }, + }, + _source: ['synthetics.blob', 'synthetics.step.name'], + }; + const { body: result } = await uptimeEsClient.search({ body: params }); + + if (result?.hits?.total.value < 1) { + return null; + } + + const stepHit = result?.hits.hits[0]._source as Ping; + + return { + blob: stepHit.synthetics?.blob ?? null, + stepName: stepHit?.synthetics?.step?.name ?? '', + }; +}; diff --git a/x-pack/plugins/uptime/server/lib/requests/index.ts b/x-pack/plugins/uptime/server/lib/requests/index.ts index 9e665fb8bbdb0..3b1e6628b17cf 100644 --- a/x-pack/plugins/uptime/server/lib/requests/index.ts +++ b/x-pack/plugins/uptime/server/lib/requests/index.ts @@ -24,6 +24,7 @@ import { getJourneyScreenshot } from './get_journey_screenshot'; import { getJourneyDetails } from './get_journey_details'; import { getNetworkEvents } from './get_network_events'; import { getJourneyFailedSteps } from './get_journey_failed_steps'; +import { getStepLastSuccessfulScreenshot } from './get_step_screenshot'; export const requests = { getCerts, @@ -42,6 +43,7 @@ export const requests = { getIndexStatus, getJourneySteps, getJourneyFailedSteps, + getStepLastSuccessfulScreenshot, getJourneyScreenshot, getJourneyDetails, getNetworkEvents, diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts index 41556d3c8d513..8ab408a125b1a 100644 --- a/x-pack/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index.ts @@ -27,6 +27,7 @@ import { createGetMonitorDurationRoute } from './monitors/monitors_durations'; import { createGetIndexPatternRoute, createGetIndexStatusRoute } from './index_state'; import { createNetworkEventsRoute } from './network_events'; import { createJourneyFailedStepsRoute } from './pings/journeys'; +import { createLastSuccessfulStepScreenshotRoute } from './pings/journey_screenshots'; export * from './types'; export { createRouteWithAuth } from './create_route_with_auth'; @@ -52,4 +53,5 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [ createJourneyScreenshotRoute, createNetworkEventsRoute, createJourneyFailedStepsRoute, + createLastSuccessfulStepScreenshotRoute, ]; 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 cda078da01539..ee5ab971fa72a 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 @@ -42,3 +42,44 @@ export const createJourneyScreenshotRoute: UMRestApiRouteFactory = (libs: UMServ }); }, }); + +export const createLastSuccessfulStepScreenshotRoute: UMRestApiRouteFactory = ( + libs: UMServerLibs +) => ({ + method: 'GET', + path: '/api/uptime/step/screenshot/{monitorId}/{stepIndex}', + validate: { + params: schema.object({ + monitorId: schema.string(), + stepIndex: schema.number(), + }), + query: schema.object({ + timestamp: schema.number(), + _debug: schema.maybe(schema.boolean()), + }), + }, + handler: async ({ uptimeEsClient, request, response }) => { + const { monitorId, stepIndex } = request.params; + const { timestamp } = request.query; + + const result = await libs.requests.getStepLastSuccessfulScreenshot({ + uptimeEsClient, + monitorId, + stepIndex, + timestamp, + }); + + if (result === null) { + return response.notFound(); + } + return response.ok({ + 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 3994149c4b2aa..1df4dd9d91367 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts @@ -70,3 +70,27 @@ export const createJourneyFailedStepsRoute: UMRestApiRouteFactory = (libs: UMSer }; }, }); + +export const createJourneyLastSuccessfulStepRoute: UMRestApiRouteFactory = ( + libs: UMServerLibs +) => ({ + method: 'GET', + path: '/api/uptime/journeys/failed_steps', + validate: { + query: schema.object({ + checkGroups: schema.arrayOf(schema.string()), + }), + }, + handler: async ({ uptimeEsClient, request }): Promise => { + const { checkGroups } = request.query; + const result = await libs.requests.getJourneyFailedSteps({ + uptimeEsClient, + checkGroups, + }); + + return { + checkGroups, + steps: result, + }; + }, +}); From cd4b69da9d67367a854961a91808aa6f4774a53f Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 11 Feb 2021 14:29:15 +0100 Subject: [PATCH 06/30] Added last successful check image --- .../translations/translations/ja-JP.json | 4 -- .../translations/translations/zh-CN.json | 4 -- x-pack/plugins/uptime/common/constants/ui.ts | 2 +- .../uptime/common/runtime_types/ping/ping.ts | 1 + .../step_expanded_row/step_screenshots.tsx | 57 ++++++++++++------- .../synthetics/check_steps/steps_list.tsx | 6 +- .../components/synthetics/executed_step.tsx | 5 -- .../pages/synthetics/synthetics_checks.tsx | 2 +- x-pack/plugins/uptime/public/routes.tsx | 4 +- .../lib/requests/get_step_screenshot.ts | 50 ++++++++++++++-- .../rest_api/pings/journey_screenshots.ts | 5 +- .../uptime/server/rest_api/pings/journeys.ts | 27 +-------- 12 files changed, 93 insertions(+), 74 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 5e0bf7501eb11..289cf84684fd7 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -22122,12 +22122,8 @@ "xpack.uptime.synthetics.emptyJourney.message.footer": "表示する詳細情報はありません。", "xpack.uptime.synthetics.emptyJourney.message.heading": "ステップが含まれていませんでした。", "xpack.uptime.synthetics.emptyJourney.title": "ステップがありません。", - "xpack.uptime.synthetics.executedJourney.heading": "概要情報", "xpack.uptime.synthetics.executedStep.errorHeading": "エラー", - "xpack.uptime.synthetics.executedStep.scriptHeading": "スクリプトのステップ", "xpack.uptime.synthetics.executedStep.stackTrace": "スタックトレース", - "xpack.uptime.synthetics.executedStep.stepName": "{stepNumber}. {stepName}", - "xpack.uptime.synthetics.experimentalCallout.title": "実験的機能", "xpack.uptime.synthetics.journey.allFailedMessage": "{total}ステップ - すべて失敗またはスキップされました", "xpack.uptime.synthetics.journey.allSucceededMessage": "{total}ステップ - すべて成功しました", "xpack.uptime.synthetics.journey.partialSuccessMessage": "{total}ステップ - {succeeded}成功しました", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index d0dbd750853a2..c912b44570578 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -22172,12 +22172,8 @@ "xpack.uptime.synthetics.emptyJourney.message.footer": "没有更多可显示的信息。", "xpack.uptime.synthetics.emptyJourney.message.heading": "此过程不包含任何步骤。", "xpack.uptime.synthetics.emptyJourney.title": "没有此过程的任何步骤", - "xpack.uptime.synthetics.executedJourney.heading": "摘要信息", "xpack.uptime.synthetics.executedStep.errorHeading": "错误", - "xpack.uptime.synthetics.executedStep.scriptHeading": "步骤脚本", "xpack.uptime.synthetics.executedStep.stackTrace": "堆栈跟踪", - "xpack.uptime.synthetics.executedStep.stepName": "{stepNumber}:{stepName}", - "xpack.uptime.synthetics.experimentalCallout.title": "实验功能", "xpack.uptime.synthetics.journey.allFailedMessage": "{total} 个步骤 - 全部失败或跳过", "xpack.uptime.synthetics.journey.allSucceededMessage": "{total} 个步骤 - 全部成功", "xpack.uptime.synthetics.journey.partialSuccessMessage": "{total} 个步骤 - {succeeded} 个成功", diff --git a/x-pack/plugins/uptime/common/constants/ui.ts b/x-pack/plugins/uptime/common/constants/ui.ts index 0b0cb4d43a05a..49ce4c934df31 100644 --- a/x-pack/plugins/uptime/common/constants/ui.ts +++ b/x-pack/plugins/uptime/common/constants/ui.ts @@ -15,7 +15,7 @@ export const CERTIFICATES_ROUTE = '/certificates'; export const STEP_DETAIL_ROUTE = '/journey/:checkGroupId/step/:stepIndex'; -export const SYNTHETIC_CHECK_STEPs_ROUTE = '/journey/:checkGroupId/steps'; +export const SYNTHETIC_CHECK_STEPS_ROUTE = '/journey/:checkGroupId/steps'; export enum STATUS { UP = 'up', diff --git a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts index d3365456e09f1..77b9473f2912e 100644 --- a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts @@ -216,6 +216,7 @@ export const PingType = t.intersection([ type: t.string, url: t.string, end: t.number, + text: t.string, }), }), tags: t.array(t.string), diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx index e6ba2bd707278..87c763dbb401a 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx @@ -1,7 +1,8 @@ /* * 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. + * 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, { useContext } from 'react'; @@ -25,15 +26,22 @@ interface Props { export const StepScreenshots = ({ step }: Props) => { const { basePath } = useContext(UptimeSettingsContext); - return ( { stepName={step.synthetics?.step?.name} /> - - - - + + )} ); }; diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx index feac6766eb960..fdc1b305f6771 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx @@ -25,7 +25,7 @@ export const SpanWithMargin = styled.span` margin-right: 16px; `; interface Props { - data: any[]; + data: Ping[]; error?: Error; loading: boolean; } @@ -151,7 +151,7 @@ export const StepsList = ({ data, error, loading }: Props) => { browserConsole={ data.find( (step) => - step.synthetics.type === 'journey/browserconsole' && + step.synthetics?.type === 'journey/browserconsole' && step.synthetics?.step?.index! === item.synthetics?.step?.index )?.synthetics?.payload?.text } @@ -172,7 +172,7 @@ export const StepsList = ({ data, error, loading }: Props) => { const targetElem = evt.target as HTMLElement; // we dont want to capture image click event - if (targetElem.tagName !== 'IMG') { + if (targetElem.tagName !== 'IMG' && targetElem.tagName !== 'BUTTON') { toggleExpand({ ping: item, expandedRows, setExpandedRows }); } }, diff --git a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx index fd0e13f92e157..3693eb1098158 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx @@ -79,11 +79,6 @@ export const ExecutedStep: FC = ({ step, index, browserConsol {browserConsole} - { - {(!steps || steps.length === 0) && } + {(!steps || steps.length === 0) && !loading && } ); }; diff --git a/x-pack/plugins/uptime/public/routes.tsx b/x-pack/plugins/uptime/public/routes.tsx index c5fddd2b34931..dcfb21955f219 100644 --- a/x-pack/plugins/uptime/public/routes.tsx +++ b/x-pack/plugins/uptime/public/routes.tsx @@ -15,7 +15,7 @@ import { OVERVIEW_ROUTE, SETTINGS_ROUTE, STEP_DETAIL_ROUTE, - SYNTHETIC_CHECK_STEPs_ROUTE, + SYNTHETIC_CHECK_STEPS_ROUTE, } from '../common/constants'; import { MonitorPage, StepDetailPage, NotFoundPage, SettingsPage } from './pages'; import { CertificatesPage } from './pages/certificates'; @@ -75,7 +75,7 @@ const Routes: RouteProps[] = [ }, { title: baseTitle, - path: SYNTHETIC_CHECK_STEPs_ROUTE, + path: SYNTHETIC_CHECK_STEPS_ROUTE, component: SyntheticsCheckSteps, dataTestSubj: 'uptimeSyntheticCheckStepsPage', telemetryId: UptimePage.SyntheticCheckStepsPage, diff --git a/x-pack/plugins/uptime/server/lib/requests/get_step_screenshot.ts b/x-pack/plugins/uptime/server/lib/requests/get_step_screenshot.ts index 39700a23bd7c9..ad266867bab40 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_step_screenshot.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_step_screenshot.ts @@ -18,8 +18,15 @@ export const getStepLastSuccessfulScreenshot: UMElasticsearchQueryFn< GetStepScreenshotParams, any > = async ({ uptimeEsClient, monitorId, stepIndex, timestamp }) => { - const params = { + const lastSuccessCheckParams = { size: 1, + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], query: { bool: { filter: [ @@ -37,7 +44,7 @@ export const getStepLastSuccessfulScreenshot: UMElasticsearchQueryFn< }, { term: { - 'synthetics.type': 'step/screenshot', + 'synthetics.type': 'step/end', }, }, { @@ -53,9 +60,10 @@ export const getStepLastSuccessfulScreenshot: UMElasticsearchQueryFn< ], }, }, - _source: ['synthetics.blob', 'synthetics.step.name'], + _source: ['monitor'], }; - const { body: result } = await uptimeEsClient.search({ body: params }); + + const { body: result } = await uptimeEsClient.search({ body: lastSuccessCheckParams }); if (result?.hits?.total.value < 1) { return null; @@ -63,8 +71,38 @@ export const getStepLastSuccessfulScreenshot: UMElasticsearchQueryFn< const stepHit = result?.hits.hits[0]._source as Ping; + const lastSucessedCheckScreenshot = { + size: 1, + query: { + bool: { + filter: [ + { + term: { + 'monitor.check_group': stepHit.monitor.check_group, + }, + }, + { + term: { + 'synthetics.type': 'step/screenshot', + }, + }, + { + term: { + 'synthetics.step.index': stepIndex, + }, + }, + ], + }, + }, + _source: ['synthetics.blob', 'synthetics.step.name'], + }; + + const { body: result1 } = await uptimeEsClient.search({ body: lastSucessedCheckScreenshot }); + + const stepScreenshotHit = result1?.hits.hits[0]._source as Ping; + return { - blob: stepHit.synthetics?.blob ?? null, - stepName: stepHit?.synthetics?.step?.name ?? '', + blob: stepScreenshotHit.synthetics?.blob ?? null, + stepName: stepScreenshotHit?.synthetics?.step?.name ?? '', }; }; 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 ee5ab971fa72a..21b787c3d747c 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 @@ -18,6 +18,9 @@ export const createJourneyScreenshotRoute: UMRestApiRouteFactory = (libs: UMServ stepIndex: schema.number(), _debug: schema.maybe(schema.boolean()), }), + query: schema.object({ + _debug: schema.maybe(schema.boolean()), + }), }, handler: async ({ uptimeEsClient, request, response }) => { const { checkGroup, stepIndex } = request.params; @@ -54,7 +57,7 @@ export const createLastSuccessfulStepScreenshotRoute: UMRestApiRouteFactory = ( stepIndex: schema.number(), }), query: schema.object({ - timestamp: schema.number(), + timestamp: schema.string(), _debug: schema.maybe(schema.boolean()), }), }, 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 1df4dd9d91367..9b5bffc380c27 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts @@ -15,7 +15,6 @@ export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => validate: { params: schema.object({ checkGroup: schema.string(), - _debug: schema.maybe(schema.boolean()), }), query: schema.object({ // provides a filter for the types of synthetic events to include @@ -23,6 +22,7 @@ export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => syntheticEventTypes: schema.maybe( schema.oneOf([schema.arrayOf(schema.string()), schema.string()]) ), + _debug: schema.maybe(schema.boolean()), }), }, handler: async ({ uptimeEsClient, request }): Promise => { @@ -55,30 +55,7 @@ export const createJourneyFailedStepsRoute: UMRestApiRouteFactory = (libs: UMSer validate: { query: schema.object({ checkGroups: schema.arrayOf(schema.string()), - }), - }, - handler: async ({ uptimeEsClient, request }): Promise => { - const { checkGroups } = request.query; - const result = await libs.requests.getJourneyFailedSteps({ - uptimeEsClient, - checkGroups, - }); - - return { - checkGroups, - steps: result, - }; - }, -}); - -export const createJourneyLastSuccessfulStepRoute: UMRestApiRouteFactory = ( - libs: UMServerLibs -) => ({ - method: 'GET', - path: '/api/uptime/journeys/failed_steps', - validate: { - query: schema.object({ - checkGroups: schema.arrayOf(schema.string()), + _debug: schema.maybe(schema.boolean()), }), }, handler: async ({ uptimeEsClient, request }): Promise => { From 4efe69a967a2efc71dedd02e3482967fac9be1d8 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 11 Feb 2021 15:03:13 +0100 Subject: [PATCH 07/30] collapse accordion on success step --- .../uptime/public/components/synthetics/executed_step.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx index 3693eb1098158..4c9275654b9ef 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx @@ -34,6 +34,8 @@ const Message = euiStyled.div` `; export const ExecutedStep: FC = ({ step, index, browserConsole = '' }) => { + const isSucceeded = step.synthetics?.payload?.status === 'succeeded'; + return ( <>
@@ -59,7 +61,7 @@ export const ExecutedStep: FC = ({ step, index, browserConsol )} overflowHeight={CODE_BLOCK_OVERFLOW_HEIGHT} language="javascript" - initialIsOpen={true} + initialIsOpen={!isSucceeded} > {step.synthetics?.payload?.source} {' '} @@ -74,7 +76,7 @@ export const ExecutedStep: FC = ({ step, index, browserConsol )} overflowHeight={CODE_BLOCK_OVERFLOW_HEIGHT} language="javascript" - initialIsOpen={true} + initialIsOpen={!isSucceeded} > {browserConsole} @@ -88,7 +90,7 @@ export const ExecutedStep: FC = ({ step, index, browserConsol })} language="html" overflowHeight={CODE_BLOCK_OVERFLOW_HEIGHT} - initialIsOpen={true} + initialIsOpen={!isSucceeded} > {step.synthetics?.error?.stack} From 870707502fcad7a8d85e52a5e3858a7913d41b68 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 11 Feb 2021 15:28:26 +0100 Subject: [PATCH 08/30] update breadcrumb test --- .../use_monitor_breadcrumbs.test.tsx | 85 ++++++++++++++++++- 1 file changed, 82 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx index 843693346f0d6..56694ff93dfb4 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx @@ -17,7 +17,7 @@ import { JourneyState } from '../../../../state/reducers/journey'; import { chromeServiceMock, uiSettingsServiceMock } from 'src/core/public/mocks'; describe('useMonitorBreadcrumbs', () => { - it('sets the given breadcrumbs', () => { + it('sets the given breadcrumbs for steps list view', () => { let breadcrumbObj: ChromeBreadcrumb[] = []; const getBreadcrumbs = () => { return breadcrumbObj; @@ -43,8 +43,13 @@ describe('useMonitorBreadcrumbs', () => { const Component = () => { useMonitorBreadcrumb({ - activeStep: { monitor: { id: 'test-monitor' } } as Ping, - details: { timestamp: '2021-01-04T11:25:19.104Z' } as JourneyState['details'], + activeStep: { monitor: { id: 'test-monitor', check_group: 'fake-test-group' } } as Ping, + details: { + timestamp: '2021-01-04T11:25:19.104Z', + journey: { + monitor: { id: 'test-monitor', check_group: 'fake-test-group' }, + }, + } as JourneyState['details'], }); return <>Step Water Fall; }; @@ -68,6 +73,80 @@ describe('useMonitorBreadcrumbs', () => { "onClick": [Function], "text": "test-monitor", }, + Object { + "href": "/app/uptime/journey/fake-test-group/steps", + "onClick": [Function], + "text": "Jan 4, 2021 @ 06:25:19.104", + }, + ] + `); + }); + + it('sets the given breadcrumbs for performance breakdown page', () => { + let breadcrumbObj: ChromeBreadcrumb[] = []; + const getBreadcrumbs = () => { + return breadcrumbObj; + }; + + const core = { + chrome: { + ...chromeServiceMock.createStartContract(), + setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => { + breadcrumbObj = newBreadcrumbs; + }, + }, + uiSettings: { + ...uiSettingsServiceMock.createSetupContract(), + get(key: string, defaultOverride?: any): any { + return `MMM D, YYYY @ HH:mm:ss.SSS` || defaultOverride; + }, + get$(key: string, defaultOverride?: any): any { + return of(`MMM D, YYYY @ HH:mm:ss.SSS`) || of(defaultOverride); + }, + }, + }; + + const Component = () => { + useMonitorBreadcrumb({ + activeStep: { monitor: { id: 'test-monitor', check_group: 'fake-test-group' } } as Ping, + details: { + timestamp: '2021-01-04T11:25:19.104Z', + journey: { + monitor: { id: 'test-monitor', check_group: 'fake-test-group' }, + }, + } as JourneyState['details'], + performanceBreakDownView: true, + }); + return <>Step Water Fall; + }; + + render( + + + , + { core } + ); + + expect(getBreadcrumbs()).toMatchInlineSnapshot(` + Array [ + Object { + "href": "/app/uptime", + "onClick": [Function], + "text": "Uptime", + }, + Object { + "href": "/app/uptime/monitor/dGVzdC1tb25pdG9y", + "onClick": [Function], + "text": "test-monitor", + }, + Object { + "href": "/app/uptime/journey/fake-test-group/steps", + "onClick": [Function], + "text": "Jan 4, 2021 @ 06:25:19.104", + }, + Object { + "text": "Performance breakdown", + }, ] `); }); From 135fbd2428ebf63538b73adb83b990c599ec140e Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 11 Feb 2021 18:51:09 +0100 Subject: [PATCH 09/30] update last successful step timestamp --- .../step_expanded_row/step_screenshots.tsx | 41 ++++--- .../synthetics/check_steps/steps_list.tsx | 8 +- .../components/synthetics/executed_step.tsx | 110 ++++++++---------- .../synthetics/step_screenshot_display.tsx | 4 +- .../pages/synthetics/synthetics_checks.tsx | 2 +- .../uptime/public/state/api/journey.ts | 17 +++ ...eenshot.ts => get_last_successful_step.ts} | 39 +------ .../uptime/server/lib/requests/index.ts | 4 +- .../plugins/uptime/server/rest_api/index.ts | 4 +- .../rest_api/pings/journey_screenshots.ts | 41 ------- .../synthetics/last_successful_step.ts | 33 ++++++ 11 files changed, 144 insertions(+), 159 deletions(-) rename x-pack/plugins/uptime/server/lib/requests/{get_step_screenshot.ts => get_last_successful_step.ts} (61%) create mode 100644 x-pack/plugins/uptime/server/rest_api/synthetics/last_successful_step.ts diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx index 87c763dbb401a..260ce385e30f0 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx @@ -5,13 +5,16 @@ * 2.0. */ -import React, { useContext } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import moment from 'moment'; +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { StepScreenshotDisplay } from '../../step_screenshot_display'; import { Ping } from '../../../../../common/runtime_types/ping'; import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; -import { UptimeSettingsContext } from '../../../../contexts'; +import { useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/public'; +import { useFetcher } from '../../../../../../observability/public'; +import { fetchLastSuccessfulStep } from '../../../../state/api/journey'; const Label = euiStyled.div` margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs}; @@ -24,7 +27,19 @@ interface Props { } export const StepScreenshots = ({ step }: Props) => { - const { basePath } = useContext(UptimeSettingsContext); + const [dateFormat] = useUiSetting$('dateFormat'); + + const isSucceeded = step.synthetics?.payload?.status === 'succeeded'; + + const { data: lastSuccessfulStep } = useFetcher(() => { + if (!isSucceeded) { + return fetchLastSuccessfulStep({ + timestamp: step.timestamp, + monitorId: step.monitor.id, + stepIndex: step.synthetics?.step?.index!, + }); + } + }, [step.docId]); return ( @@ -50,8 +65,10 @@ export const StepScreenshots = ({ step }: Props) => { stepIndex={step.synthetics?.step?.index} stepName={step.synthetics?.step?.name} /> + + - {step.synthetics?.payload?.status !== 'succeeded' && ( + {!isSucceeded && lastSuccessfulStep && ( + + )} diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx index fdc1b305f6771..cda14e7829ee6 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx @@ -24,21 +24,23 @@ import { StepImage } from './step_image'; export const SpanWithMargin = styled.span` margin-right: 16px; `; + interface Props { data: Ping[]; error?: Error; loading: boolean; } -function isStepEnd(step: Ping) { - return step.synthetics?.type === 'step/end'; -} interface StepStatusCount { failed: number; skipped: number; succeeded: number; } +function isStepEnd(step: Ping) { + return step.synthetics?.type === 'step/end'; +} + function statusMessage(count: StepStatusCount) { const total = count.succeeded + count.failed + count.skipped; if (count.failed + count.skipped === total) { diff --git a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx index 4c9275654b9ef..500c6af07fa25 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx @@ -37,64 +37,56 @@ export const ExecutedStep: FC = ({ step, index, browserConsol const isSucceeded = step.synthetics?.payload?.status === 'succeeded'; return ( - <> -
- - {step.synthetics?.error?.message && ( - - - {step.synthetics?.error?.message} - - )} - - - {step.synthetics?.payload?.source} - {' '} - - - {browserConsole} - - - - - - {step.synthetics?.error?.stack} - -
- +
+ + {step.synthetics?.error?.message && ( + + + {step.synthetics?.error?.message} + + )} + + + {step.synthetics?.payload?.source} + {' '} + + + {browserConsole} + + + + + + {step.synthetics?.error?.stack} + +
); }; diff --git a/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx b/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx index eaff6e7ece703..7b7f3ffadfc65 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx @@ -14,7 +14,6 @@ import useIntersection from 'react-use/lib/useIntersection'; import { UptimeSettingsContext, UptimeThemeContext } from '../../contexts'; interface StepScreenshotDisplayProps { - srcPath?: string; screenshotExists?: boolean; checkGroup?: string; stepIndex?: number; @@ -40,7 +39,6 @@ const StepImage = styled(EuiImage)` `; export const StepScreenshotDisplay: FC = ({ - srcPath, checkGroup, screenshotExists, stepIndex, @@ -71,7 +69,7 @@ export const StepScreenshotDisplay: FC = ({ }, [hasIntersected, isIntersecting, setHasIntersected]); let content: JSX.Element | null = null; - const imgSrc = srcPath || basePath + `/api/uptime/journey/screenshot/${checkGroup}/${stepIndex}`; + const imgSrc = basePath + `/api/uptime/journey/screenshot/${checkGroup}/${stepIndex}`; if (hasIntersected && screenshotExists) { content = ( diff --git a/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx b/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx index 0f19ecf2410a3..edfd7ae24f91b 100644 --- a/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx +++ b/x-pack/plugins/uptime/public/pages/synthetics/synthetics_checks.tsx @@ -29,7 +29,7 @@ export const SyntheticsCheckSteps: React.FC = () => { -

{details?.journey?.monitor.name}

+

{details?.journey?.monitor.name || details?.journey?.monitor.id}

diff --git a/x-pack/plugins/uptime/public/state/api/journey.ts b/x-pack/plugins/uptime/public/state/api/journey.ts index 5c4c7c7149792..63796a66d1c5c 100644 --- a/x-pack/plugins/uptime/public/state/api/journey.ts +++ b/x-pack/plugins/uptime/public/state/api/journey.ts @@ -8,6 +8,7 @@ import { apiService } from './utils'; import { FetchJourneyStepsParams } from '../actions/journey'; import { + Ping, SyntheticsJourneyApiResponse, SyntheticsJourneyApiResponseType, } from '../../../common/runtime_types'; @@ -34,6 +35,22 @@ export async function fetchJourneysFailedSteps({ )) as SyntheticsJourneyApiResponse; } +export async function fetchLastSuccessfulStep({ + monitorId, + timestamp, + stepIndex, +}: { + monitorId: string; + timestamp: string; + stepIndex: number; +}): Promise { + return (await apiService.get(`/api/uptime/synthetics/step/success/`, { + monitorId, + timestamp, + stepIndex, + })) as Ping; +} + export async function getJourneyScreenshot(imgSrc: string) { try { const imgRequest = new Request(imgSrc); diff --git a/x-pack/plugins/uptime/server/lib/requests/get_step_screenshot.ts b/x-pack/plugins/uptime/server/lib/requests/get_last_successful_step.ts similarity index 61% rename from x-pack/plugins/uptime/server/lib/requests/get_step_screenshot.ts rename to x-pack/plugins/uptime/server/lib/requests/get_last_successful_step.ts index ad266867bab40..ab9189115dc9e 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_step_screenshot.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_last_successful_step.ts @@ -14,7 +14,7 @@ export interface GetStepScreenshotParams { stepIndex: number; } -export const getStepLastSuccessfulScreenshot: UMElasticsearchQueryFn< +export const getStepLastSuccessfulStep: UMElasticsearchQueryFn< GetStepScreenshotParams, any > = async ({ uptimeEsClient, monitorId, stepIndex, timestamp }) => { @@ -60,7 +60,6 @@ export const getStepLastSuccessfulScreenshot: UMElasticsearchQueryFn< ], }, }, - _source: ['monitor'], }; const { body: result } = await uptimeEsClient.search({ body: lastSuccessCheckParams }); @@ -69,40 +68,10 @@ export const getStepLastSuccessfulScreenshot: UMElasticsearchQueryFn< return null; } - const stepHit = result?.hits.hits[0]._source as Ping; - - const lastSucessedCheckScreenshot = { - size: 1, - query: { - bool: { - filter: [ - { - term: { - 'monitor.check_group': stepHit.monitor.check_group, - }, - }, - { - term: { - 'synthetics.type': 'step/screenshot', - }, - }, - { - term: { - 'synthetics.step.index': stepIndex, - }, - }, - ], - }, - }, - _source: ['synthetics.blob', 'synthetics.step.name'], - }; - - const { body: result1 } = await uptimeEsClient.search({ body: lastSucessedCheckScreenshot }); - - const stepScreenshotHit = result1?.hits.hits[0]._source as Ping; + const step = result?.hits.hits[0]._source as Ping & { '@timestamp': string }; return { - blob: stepScreenshotHit.synthetics?.blob ?? null, - stepName: stepScreenshotHit?.synthetics?.step?.name ?? '', + ...step, + timestamp: step['@timestamp'], }; }; diff --git a/x-pack/plugins/uptime/server/lib/requests/index.ts b/x-pack/plugins/uptime/server/lib/requests/index.ts index 3b1e6628b17cf..24109245c2902 100644 --- a/x-pack/plugins/uptime/server/lib/requests/index.ts +++ b/x-pack/plugins/uptime/server/lib/requests/index.ts @@ -24,7 +24,7 @@ import { getJourneyScreenshot } from './get_journey_screenshot'; import { getJourneyDetails } from './get_journey_details'; import { getNetworkEvents } from './get_network_events'; import { getJourneyFailedSteps } from './get_journey_failed_steps'; -import { getStepLastSuccessfulScreenshot } from './get_step_screenshot'; +import { getStepLastSuccessfulStep } from './get_last_successful_step'; export const requests = { getCerts, @@ -43,7 +43,7 @@ export const requests = { getIndexStatus, getJourneySteps, getJourneyFailedSteps, - getStepLastSuccessfulScreenshot, + getStepLastSuccessfulStep, getJourneyScreenshot, getJourneyDetails, getNetworkEvents, diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts index 8ab408a125b1a..91b5597321ed0 100644 --- a/x-pack/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index.ts @@ -27,7 +27,7 @@ import { createGetMonitorDurationRoute } from './monitors/monitors_durations'; import { createGetIndexPatternRoute, createGetIndexStatusRoute } from './index_state'; import { createNetworkEventsRoute } from './network_events'; import { createJourneyFailedStepsRoute } from './pings/journeys'; -import { createLastSuccessfulStepScreenshotRoute } from './pings/journey_screenshots'; +import { createLastSuccessfulStepRoute } from './synthetics/last_successful_step'; export * from './types'; export { createRouteWithAuth } from './create_route_with_auth'; @@ -53,5 +53,5 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [ createJourneyScreenshotRoute, createNetworkEventsRoute, createJourneyFailedStepsRoute, - createLastSuccessfulStepScreenshotRoute, + createLastSuccessfulStepRoute, ]; 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 21b787c3d747c..6e908d2ea4332 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 @@ -45,44 +45,3 @@ export const createJourneyScreenshotRoute: UMRestApiRouteFactory = (libs: UMServ }); }, }); - -export const createLastSuccessfulStepScreenshotRoute: UMRestApiRouteFactory = ( - libs: UMServerLibs -) => ({ - method: 'GET', - path: '/api/uptime/step/screenshot/{monitorId}/{stepIndex}', - validate: { - params: schema.object({ - monitorId: schema.string(), - stepIndex: schema.number(), - }), - query: schema.object({ - timestamp: schema.string(), - _debug: schema.maybe(schema.boolean()), - }), - }, - handler: async ({ uptimeEsClient, request, response }) => { - const { monitorId, stepIndex } = request.params; - const { timestamp } = request.query; - - const result = await libs.requests.getStepLastSuccessfulScreenshot({ - uptimeEsClient, - monitorId, - stepIndex, - timestamp, - }); - - if (result === null) { - return response.notFound(); - } - return response.ok({ - 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/synthetics/last_successful_step.ts b/x-pack/plugins/uptime/server/rest_api/synthetics/last_successful_step.ts new file mode 100644 index 0000000000000..a1523fae9d4a1 --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/synthetics/last_successful_step.ts @@ -0,0 +1,33 @@ +/* + * 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 { UMServerLibs } from '../../lib/lib'; +import { UMRestApiRouteFactory } from '../types'; + +export const createLastSuccessfulStepRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ + method: 'GET', + path: '/api/uptime/synthetics/step/success/', + validate: { + query: schema.object({ + monitorId: schema.string(), + stepIndex: schema.number(), + timestamp: schema.string(), + _debug: schema.maybe(schema.boolean()), + }), + }, + handler: async ({ uptimeEsClient, request, response }) => { + const { timestamp, monitorId, stepIndex } = request.query; + + return await libs.requests.getStepLastSuccessfulStep({ + uptimeEsClient, + monitorId, + stepIndex, + timestamp, + }); + }, +}); From 2cc8591c9d747afdcbed03c17df3e14a3a618da3 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 11 Feb 2021 19:45:21 +0100 Subject: [PATCH 10/30] remove unused popover code --- .../step_expanded_row/step_screenshots.tsx | 4 +- .../synthetics/step_screenshot_display.tsx | 85 ++++++------------- 2 files changed, 25 insertions(+), 64 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx index 260ce385e30f0..86150ddde1b1e 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx @@ -59,7 +59,6 @@ export const StepScreenshots = ({ step }: Props) => { )} { - {!isSucceeded && lastSuccessfulStep && ( + {!isSucceeded && lastSuccessfulStep?.monitor && ( = ({ screenshotExists, stepIndex, stepName, - allowPopover = true, }) => { const containerRef = useRef(null); const { @@ -52,8 +48,6 @@ export const StepScreenshotDisplay: FC = ({ const { basePath } = useContext(UptimeSettingsContext); - const [isImagePopoverOpen, setIsImagePopoverOpen] = useState(false); - const intersection = useIntersection(containerRef, { root: null, rootMargin: '0px', @@ -73,55 +67,24 @@ export const StepScreenshotDisplay: FC = ({ if (hasIntersected && screenshotExists) { content = ( - <> - setIsImagePopoverOpen(true) : undefined} - onMouseLeave={allowPopover ? () => setIsImagePopoverOpen(false) : undefined} - /> - } - closePopover={() => setIsImagePopoverOpen(false)} - isOpen={isImagePopoverOpen} - > - - - + ); } else if (screenshotExists === false) { content = ( @@ -150,7 +113,7 @@ export const StepScreenshotDisplay: FC = ({ return (
{content}
From 639d6793c31d4355c5d6650547b5bf311a66ff29 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Sat, 13 Feb 2021 22:19:59 +0100 Subject: [PATCH 11/30] fix epand row --- .../synthetics/check_steps/expand_row_col.tsx | 62 --------------- .../step_expanded_row/step_screenshots.tsx | 24 ++++-- .../synthetics/check_steps/steps_list.tsx | 71 +++-------------- .../synthetics/check_steps/use_check_steps.ts | 2 +- .../check_steps/use_expanded_row.tsx | 76 +++++++++++++++++++ .../synthetics/step_screenshot_display.tsx | 4 +- .../components/synthetics/translations.ts | 8 ++ 7 files changed, 117 insertions(+), 130 deletions(-) delete mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/expand_row_col.tsx create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/expand_row_col.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/expand_row_col.tsx deleted file mode 100644 index 2e74158b85031..0000000000000 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/expand_row_col.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiButtonIcon } from '@elastic/eui'; -import { Ping } from '../../../../common/runtime_types/ping'; -import { ExecutedStep } from '../executed_step'; - -interface Props { - ping: Ping; - browserConsole?: string; - expandedRows: Record; - setExpandedRows: (val: Record) => void; -} - -export const toggleExpand = ({ - ping, - browserConsole = '', - expandedRows, - setExpandedRows, -}: Props) => { - // If already expanded, collapse - if (expandedRows[ping.docId]) { - delete expandedRows[ping.docId]; - setExpandedRows({ ...expandedRows }); - return; - } - - // Otherwise expand this row - setExpandedRows({ - ...expandedRows, - [ping.docId]: ( - - ), - }); -}; - -export const ExpandRowColumn = ({ ping, browserConsole, expandedRows, setExpandedRows }: Props) => { - return ( - toggleExpand({ ping, browserConsole, expandedRows, setExpandedRows })} - aria-label={ - expandedRows[ping.docId] - ? i18n.translate('xpack.uptime.stepList.collapseRow', { - defaultMessage: 'Collapse', - }) - : i18n.translate('xpack.uptime.stepList.expandRow', { defaultMessage: 'Expand' }) - } - iconType={expandedRows[ping.docId] ? 'arrowUp' : 'arrowDown'} - /> - ); -}; diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx index 86150ddde1b1e..0cc437dbb9bea 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx @@ -15,6 +15,7 @@ import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/comm import { useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/public'; import { useFetcher } from '../../../../../../observability/public'; import { fetchLastSuccessfulStep } from '../../../../state/api/journey'; +import { ReactRouterEuiLink } from '../../../common/react_router_helpers'; const Label = euiStyled.div` margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs}; @@ -22,6 +23,11 @@ const Label = euiStyled.div` color: ${({ theme }) => theme.eui.euiColorDarkShade}; `; +const LabelLink = euiStyled.div` + margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs}; + font-size: ${({ theme }) => theme.eui.euiFontSizeS}; +`; + interface Props { step: Ping; } @@ -39,7 +45,7 @@ export const StepScreenshots = ({ step }: Props) => { stepIndex: step.synthetics?.step?.index!, }); } - }, [step.docId]); + }, [step.docId, step.timestamp]); return ( @@ -63,23 +69,27 @@ export const StepScreenshots = ({ step }: Props) => { screenshotExists={step.synthetics?.screenshotExists} stepIndex={step.synthetics?.step?.index} stepName={step.synthetics?.step?.name} + lazyLoad={false} />
{!isSucceeded && lastSuccessfulStep?.monitor && ( - + + + + + diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx index cda14e7829ee6..632455202fdb2 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx @@ -5,21 +5,18 @@ * 2.0. */ -import { EuiBasicTable, EuiPanel, EuiTitle } from '@elastic/eui'; +import { EuiBasicTable, EuiButtonIcon, EuiPanel, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useCallback, useState, useEffect, MouseEvent } from 'react'; +import React, { MouseEvent } from 'react'; import styled from 'styled-components'; -import { useDispatch } from 'react-redux'; import { Ping } from '../../../../common/runtime_types'; -import { pruneJourneyState } from '../../../state/actions/journey'; -import { clearPings } from '../../../state/actions'; import { STATUS_LABEL } from '../../monitor/ping_list/translations'; -import { STEP_NAME_LABEL } from '../translations'; -import { ExpandRowColumn, toggleExpand } from './expand_row_col'; +import { COLLAPSE_LABEL, EXPAND_LABEL, STEP_NAME_LABEL } from '../translations'; import { StatusBadge } from '../status_badge'; import { StepDetailLink } from '../../common/step_detail_link'; import { VIEW_PERFORMANCE } from '../../monitor/synthetics/translations'; import { StepImage } from './step_image'; +import { useExpandedRow } from './use_expanded_row'; export const SpanWithMargin = styled.span` margin-right: 16px; @@ -73,46 +70,9 @@ function reduceStepStatus(prev: StepStatusCount, cur: Ping): StepStatusCount { } export const StepsList = ({ data, error, loading }: Props) => { - const dispatch = useDispatch(); - const steps = data.filter(isStepEnd); - const pruneJourneysCallback = useCallback( - (checkGroups: string[]) => dispatch(pruneJourneyState(checkGroups)), - [dispatch] - ); - - const [expandedRows, setExpandedRows] = useState>({}); - - const expandedIdsToRemove = JSON.stringify( - Object.keys(expandedRows).filter((e) => !data.some(({ docId }) => docId === e)) - ); - - useEffect(() => { - return () => { - dispatch(clearPings()); - }; - }, [dispatch]); - - useEffect(() => { - const parsed = JSON.parse(expandedIdsToRemove); - if (parsed.length) { - parsed.forEach((docId: string) => { - delete expandedRows[docId]; - }); - setExpandedRows(expandedRows); - } - }, [expandedIdsToRemove, expandedRows]); - - const expandedCheckGroups = data - .filter((p: Ping) => Object.keys(expandedRows).some((f) => p.docId === f)) - .map(({ monitor: { check_group: cg } }) => cg); - - const expandedCheckGroupsStr = JSON.stringify(expandedCheckGroups); - - useEffect(() => { - pruneJourneysCallback(JSON.parse(expandedCheckGroupsStr)); - }, [pruneJourneysCallback, expandedCheckGroupsStr]); + const { expandedRows, toggleExpand } = useExpandedRow({ steps, allPings: data }); const columns: any[] = [ { @@ -146,19 +106,13 @@ export const StepsList = ({ data, error, loading }: Props) => { align: 'right', width: '24px', isExpander: true, - render: (item: Ping) => { + render: (ping: Ping) => { return ( - - step.synthetics?.type === 'journey/browserconsole' && - step.synthetics?.step?.index! === item.synthetics?.step?.index - )?.synthetics?.payload?.text - } - expandedRows={expandedRows} - setExpandedRows={setExpandedRows} + toggleExpand({ ping })} + aria-label={expandedRows[ping.docId] ? COLLAPSE_LABEL : EXPAND_LABEL} + iconType={expandedRows[ping.docId] ? 'arrowUp' : 'arrowDown'} /> ); }, @@ -175,7 +129,7 @@ export const StepsList = ({ data, error, loading }: Props) => { // we dont want to capture image click event if (targetElem.tagName !== 'IMG' && targetElem.tagName !== 'BUTTON') { - toggleExpand({ ping: item, expandedRows, setExpandedRows }); + toggleExpand({ ping: item }); } }, }; @@ -195,7 +149,6 @@ export const StepsList = ({ data, error, loading }: Props) => { isExpandable={true} hasActions={true} items={steps} - itemId="docId" itemIdToExpandedRowMap={expandedRows} noItemsMessage={ loading diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts index c95115cc3d854..da40b900fdcc2 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_check_steps.ts @@ -11,7 +11,7 @@ import { fetchJourneySteps } from '../../../state/api/journey'; import { JourneyState } from '../../../state/reducers/journey'; export const useCheckSteps = (): JourneyState => { - const { checkGroupId } = useParams<{ checkGroupId: string; stepIndex: string }>(); + const { checkGroupId } = useParams<{ checkGroupId: string }>(); const { data, status, error } = useFetcher(() => { return fetchJourneySteps({ diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx new file mode 100644 index 0000000000000..1d5b1926a8167 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx @@ -0,0 +1,76 @@ +/* + * 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, { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { ExecutedStep } from '../executed_step'; +import { Ping } from '../../../../common/runtime_types/ping'; + +interface HookProps { + allPings: Ping[]; + steps: Ping[]; +} + +type ExpandRowType = Record; + +export const useExpandedRow = ({ steps, allPings }: HookProps) => { + const [expandedRows, setExpandedRows] = useState({}); + // eui table uses index from 0, synthetics uses 1 + + const { checkGroupId } = useParams<{ checkGroupId: string }>(); + + const getBrowserConsole = (index: number) => { + return allPings.find( + (stepF) => + stepF.synthetics?.type === 'journey/browserconsole' && + stepF.synthetics?.step?.index! === index + )?.synthetics?.payload?.text; + }; + + useEffect(() => { + const expandedRowsN: ExpandRowType = {}; + for (let expandedRowKeyStr in expandedRows) { + const expandedRowKey = Number(expandedRowKeyStr); + + const step = steps.find((stepF) => stepF.synthetics?.step?.index !== expandedRowKey)!; + + expandedRowsN[expandedRowKey] = ( + + ); + } + + setExpandedRows(expandedRowsN); + }, [checkGroupId, allPings]); + + const toggleExpand = ({ ping }: { ping: Ping }) => { + // eui table uses index from 0, synthetics uses 1 + const stepIndex = ping.synthetics?.step?.index! - 1; + + // If already expanded, collapse + if (expandedRows[stepIndex]) { + delete expandedRows[stepIndex]; + setExpandedRows({ ...expandedRows }); + } else { + // Otherwise expand this row + setExpandedRows({ + ...expandedRows, + [stepIndex]: ( + + ), + }); + } + }; + + return { expandedRows, toggleExpand }; +}; diff --git a/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx b/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx index 332c17eec4b84..78c65b7d40803 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/step_screenshot_display.tsx @@ -18,6 +18,7 @@ interface StepScreenshotDisplayProps { checkGroup?: string; stepIndex?: number; stepName?: string; + lazyLoad?: boolean; } const IMAGE_WIDTH = 640; @@ -40,6 +41,7 @@ export const StepScreenshotDisplay: FC = ({ screenshotExists, stepIndex, stepName, + lazyLoad = true, }) => { const containerRef = useRef(null); const { @@ -65,7 +67,7 @@ export const StepScreenshotDisplay: FC = ({ let content: JSX.Element | null = null; const imgSrc = basePath + `/api/uptime/journey/screenshot/${checkGroup}/${stepIndex}`; - if (hasIntersected && screenshotExists) { + if ((hasIntersected || !lazyLoad) && screenshotExists) { content = ( Date: Sat, 13 Feb 2021 22:20:14 +0100 Subject: [PATCH 12/30] fix epand row --- .../check_steps/use_expanded_row.tsx | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx index 1d5b1926a8167..2bec84466dfdb 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx @@ -1,10 +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. + * 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 React, { useEffect, useState, useCallback } from 'react'; import { useParams } from 'react-router-dom'; import { ExecutedStep } from '../executed_step'; import { Ping } from '../../../../common/runtime_types/ping'; @@ -22,32 +23,37 @@ export const useExpandedRow = ({ steps, allPings }: HookProps) => { const { checkGroupId } = useParams<{ checkGroupId: string }>(); - const getBrowserConsole = (index: number) => { - return allPings.find( - (stepF) => - stepF.synthetics?.type === 'journey/browserconsole' && - stepF.synthetics?.step?.index! === index - )?.synthetics?.payload?.text; - }; + const getBrowserConsole = useCallback( + (index: number) => { + return allPings.find( + (stepF) => + stepF.synthetics?.type === 'journey/browserconsole' && + stepF.synthetics?.step?.index! === index + )?.synthetics?.payload?.text; + }, + [allPings] + ); useEffect(() => { const expandedRowsN: ExpandRowType = {}; - for (let expandedRowKeyStr in expandedRows) { - const expandedRowKey = Number(expandedRowKeyStr); + for (const expandedRowKeyStr in expandedRows) { + if (expandedRows.hasOwnProperty(expandedRowKeyStr)) { + const expandedRowKey = Number(expandedRowKeyStr); - const step = steps.find((stepF) => stepF.synthetics?.step?.index !== expandedRowKey)!; + const step = steps.find((stepF) => stepF.synthetics?.step?.index !== expandedRowKey)!; - expandedRowsN[expandedRowKey] = ( - - ); + expandedRowsN[expandedRowKey] = ( + + ); + } } setExpandedRows(expandedRowsN); - }, [checkGroupId, allPings]); + }, [checkGroupId, allPings, expandedRows, steps, getBrowserConsole]); const toggleExpand = ({ ping }: { ping: Ping }) => { // eui table uses index from 0, synthetics uses 1 From c4928bc71d82904a40aed262dea34c7c442c6682 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Sun, 14 Feb 2021 00:21:16 +0100 Subject: [PATCH 13/30] added tests --- .../synthetics/check_steps/steps_list.tsx | 2 +- .../check_steps/use_expanded_row.test.tsx | 153 ++++++++++++++++++ .../check_steps/use_expanded_row.tsx | 8 +- 3 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.test.tsx diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx index 632455202fdb2..018a8957ea8fd 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx @@ -72,7 +72,7 @@ function reduceStepStatus(prev: StepStatusCount, cur: Ping): StepStatusCount { export const StepsList = ({ data, error, loading }: Props) => { const steps = data.filter(isStepEnd); - const { expandedRows, toggleExpand } = useExpandedRow({ steps, allPings: data }); + const { expandedRows, toggleExpand } = useExpandedRow({ steps, allPings: data, loading }); const columns: any[] = [ { diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.test.tsx new file mode 100644 index 0000000000000..023f6f532fb19 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.test.tsx @@ -0,0 +1,153 @@ +/* + * 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 from 'react'; +import { Route } from 'react-router-dom'; +import { fireEvent, screen } from '@testing-library/dom'; +import { EuiButtonIcon } from '@elastic/eui'; +import { createMemoryHistory } from 'history'; + +import { useExpandedRow } from './use_expanded_row'; +import { render } from '../../../lib/helper/rtl_helpers'; +import { Ping } from '../../../../common/runtime_types/ping'; +import { SYNTHETIC_CHECK_STEPS_ROUTE } from '../../../../common/constants'; +import { COLLAPSE_LABEL, EXPAND_LABEL } from '../translations'; +import { act } from 'react-dom/test-utils'; + +describe('useExpandedROw', () => { + const steps: Ping[]; + let expandedRowsObj = {}; + const TEST_ID = 'uptimeStepListExpandBtn'; + + const history = createMemoryHistory({ + initialEntries: ['/journey/fake-group/steps'], + }); + steps = [ + { + docId: '1', + timestamp: '123', + monitor: { + id: 'MON_ID', + duration: { + us: 10, + }, + status: 'down', + type: 'browser', + check_group: 'fake-group', + }, + synthetics: { + payload: { + status: 'failed', + }, + type: 'step/end', + step: { + name: 'load page', + index: 1, + }, + }, + }, + { + docId: '2', + timestamp: '124', + monitor: { + id: 'MON_ID', + duration: { + us: 10, + }, + status: 'down', + type: 'browser', + check_group: 'fake-group', + }, + synthetics: { + payload: { + status: 'failed', + }, + type: 'step/end', + step: { + name: 'go to login', + index: 2, + }, + }, + }, + ]; + + const Component = () => { + const { expandedRows, toggleExpand } = useExpandedRow({ + steps, + allPings: steps, + loading: false, + }); + + expandedRowsObj = expandedRows; + + return ( + + Step list + {steps.map((ping, index) => ( + toggleExpand({ ping })} + aria-label={expandedRows[ping.docId] ? COLLAPSE_LABEL : EXPAND_LABEL} + iconType={expandedRows[ping.docId] ? 'arrowUp' : 'arrowDown'} + /> + ))} + + ); + }; + + it('it toggles rows on expand click', async () => { + render(, { + history, + }); + + fireEvent.click(await screen.findByTestId(TEST_ID + '1')); + + expect(Object.keys(expandedRowsObj)).toStrictEqual(['1']); + + expect(JSON.stringify(expandedRowsObj)).toContain('fake-group'); + + await act(async () => { + fireEvent.click(await screen.findByTestId(TEST_ID + '1')); + }); + + expect(Object.keys(expandedRowsObj)).toStrictEqual([]); + }); + + it('it can expand both rows at same time', async () => { + render(, { + history, + }); + + // let's expand both rows + fireEvent.click(await screen.findByTestId(TEST_ID + '1')); + fireEvent.click(await screen.findByTestId(TEST_ID + '0')); + + expect(Object.keys(expandedRowsObj)).toStrictEqual(['0', '1']); + }); + + it('it updates already expanded rows on new check group monitor', async () => { + render(, { + history, + }); + + // let's expand both rows + fireEvent.click(await screen.findByTestId(TEST_ID + '1')); + fireEvent.click(await screen.findByTestId(TEST_ID + '0')); + + const newFakeGroup = 'new-fake-group-1'; + + steps[0].monitor.check_group = newFakeGroup; + steps[1].monitor.check_group = newFakeGroup; + + act(() => { + history.push(`/journey/${newFakeGroup}/steps`); + }); + + expect(JSON.stringify(expandedRowsObj)).toContain(newFakeGroup); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx index 2bec84466dfdb..eab6f96c3996f 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx @@ -11,13 +11,14 @@ import { ExecutedStep } from '../executed_step'; import { Ping } from '../../../../common/runtime_types/ping'; interface HookProps { + loading: boolean; allPings: Ping[]; steps: Ping[]; } type ExpandRowType = Record; -export const useExpandedRow = ({ steps, allPings }: HookProps) => { +export const useExpandedRow = ({ loading, steps, allPings }: HookProps) => { const [expandedRows, setExpandedRows] = useState({}); // eui table uses index from 0, synthetics uses 1 @@ -53,7 +54,10 @@ export const useExpandedRow = ({ steps, allPings }: HookProps) => { } setExpandedRows(expandedRowsN); - }, [checkGroupId, allPings, expandedRows, steps, getBrowserConsole]); + + // we only want to update when checkGroupId changes + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [checkGroupId, loading]); const toggleExpand = ({ ping }: { ping: Ping }) => { // eui table uses index from 0, synthetics uses 1 From 28ff3a37e7e44846ad5bf907e0eb2dc2db9ae44d Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 15 Feb 2021 10:50:04 +0100 Subject: [PATCH 14/30] update loading view --- .../check_steps/use_expanded_row.tsx | 2 + .../components/synthetics/executed_step.tsx | 126 +++++++++++------- .../server/lib/requests/get_journey_steps.ts | 8 +- 3 files changed, 78 insertions(+), 58 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx index eab6f96c3996f..bb56b237dfbd2 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.tsx @@ -48,6 +48,7 @@ export const useExpandedRow = ({ loading, steps, allPings }: HookProps) => { step={step} browserConsole={getBrowserConsole(expandedRowKey)} index={step.synthetics?.step?.index!} + loading={loading} /> ); } @@ -76,6 +77,7 @@ export const useExpandedRow = ({ loading, steps, allPings }: HookProps) => { step={ping} browserConsole={getBrowserConsole(stepIndex)} index={ping.synthetics?.step?.index!} + loading={loading} /> ), }); diff --git a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx index 500c6af07fa25..2aae4146d2167 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiLoadingSpinner, EuiSpacer, EuiText } from '@elastic/eui'; import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; import { CodeBlockAccordion } from './code_block_accordion'; @@ -18,6 +18,7 @@ const CODE_BLOCK_OVERFLOW_HEIGHT = 360; interface ExecutedStepProps { step: Ping; index: number; + loading: boolean; browserConsole?: string; } @@ -33,60 +34,83 @@ const Message = euiStyled.div` margin-bottom: ${(props) => props.theme.eui.paddingSizes.m}; `; -export const ExecutedStep: FC = ({ step, index, browserConsole = '' }) => { +const ExpandedRow = euiStyled.div` + padding: '8px'; + max-width: 1000px; + width: 100%; +`; + +export const ExecutedStep: FC = ({ + loading, + step, + index, + browserConsole = '', +}) => { const isSucceeded = step.synthetics?.payload?.status === 'succeeded'; return ( -
- - {step.synthetics?.error?.message && ( - - + language="html" + overflowHeight={CODE_BLOCK_OVERFLOW_HEIGHT} + initialIsOpen={!isSucceeded} + > + {step.synthetics?.error?.stack} + + )} - - - {step.synthetics?.payload?.source} - {' '} - - - {browserConsole} - - - - - - {step.synthetics?.error?.stack} - -
+ ); }; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts index 4c561d99eb76c..43d17cb938159 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts @@ -13,13 +13,7 @@ export interface GetJourneyStepsParams { syntheticEventTypes?: string | string[]; } -const defaultEventTypes = [ - 'step/end', - 'stderr', - 'cmd/status', - 'step/screenshot', - 'journey/browserconsole', -]; +const defaultEventTypes = ['step/end', 'cmd/status', 'step/screenshot', 'journey/browserconsole']; export const formatSyntheticEvents = (eventTypes?: string | string[]) => { if (!eventTypes) { From a720eea52eeed26946700d3f66b5fa80f6ea14b9 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 15 Feb 2021 11:09:20 +0100 Subject: [PATCH 15/30] fix types --- .../synthetics/check_steps/use_expanded_row.test.tsx | 3 +-- .../public/components/synthetics/executed_step.test.tsx | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.test.tsx index 023f6f532fb19..d94122a7311ca 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.test.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/use_expanded_row.test.tsx @@ -19,14 +19,13 @@ import { COLLAPSE_LABEL, EXPAND_LABEL } from '../translations'; import { act } from 'react-dom/test-utils'; describe('useExpandedROw', () => { - const steps: Ping[]; let expandedRowsObj = {}; const TEST_ID = 'uptimeStepListExpandBtn'; const history = createMemoryHistory({ initialEntries: ['/journey/fake-group/steps'], }); - steps = [ + const steps: Ping[] = [ { docId: '1', timestamp: '123', diff --git a/x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx b/x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx index 8d3c0ecda6610..24b52e09adbf9 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/executed_step.test.tsx @@ -45,7 +45,7 @@ describe('ExecutedStep', () => { }, }; - const { getByText } = render(); + const { getByText } = render(); expect(getByText('Script executed at this step')); expect(getByText(`const someVar = "the var"`)); @@ -59,7 +59,7 @@ describe('ExecutedStep', () => { }, }; - const { getByText } = render(); + const { getByText } = render(); expect(getByText('Error message')); expect(getByText('There was an error executing the step.')); @@ -71,7 +71,7 @@ describe('ExecutedStep', () => { "Refused to execute script from because its MIME type ('image/gif') is not executable"; const { getByText } = render( - + ); expect(getByText('Console output')); From 82cb947310fc6002c3cca33f51cd9aab1f2bf225 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 15 Feb 2021 11:12:50 +0100 Subject: [PATCH 16/30] update tests --- .../uptime/server/lib/requests/get_journey_steps.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.test.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.test.ts index 2f1426dffe121..af7752b05997e 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.test.ts @@ -14,7 +14,6 @@ describe('getJourneySteps request module', () => { expect(formatSyntheticEvents()).toMatchInlineSnapshot(` Array [ "step/end", - "stderr", "cmd/status", "step/screenshot", "journey/browserconsole", @@ -122,7 +121,6 @@ describe('getJourneySteps request module', () => { "terms": Object { "synthetics.type": Array [ "step/end", - "stderr", "cmd/status", "step/screenshot", "journey/browserconsole", From 4661313dbaa81c8e9a03fb81ea8668600e28c873 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 15 Feb 2021 11:47:44 +0100 Subject: [PATCH 17/30] i18fix --- x-pack/plugins/translations/translations/ja-JP.json | 2 -- x-pack/plugins/translations/translations/zh-CN.json | 2 -- 2 files changed, 4 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 728f84dbf3230..b922832ce7f54 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -22069,8 +22069,6 @@ "xpack.uptime.synthetics.screenshot.noImageMessage": "画像がありません", "xpack.uptime.synthetics.screenshotDisplay.altText": "名前「{stepName}」のステップのスクリーンショット", "xpack.uptime.synthetics.screenshotDisplay.altTextWithoutName": "スクリーンショット", - "xpack.uptime.synthetics.screenshotDisplay.thumbnailAltText": "名前「{stepName}」のステップのサムネイルスクリーンショット", - "xpack.uptime.synthetics.screenshotDisplay.thumbnailAltTextWithoutName": "サムネイルスクリーンショット", "xpack.uptime.synthetics.statusBadge.failedMessage": "失敗", "xpack.uptime.synthetics.statusBadge.skippedMessage": "スキップ", "xpack.uptime.synthetics.statusBadge.succeededMessage": "成功", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index cdda58680d2df..d148040790fb3 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -22119,8 +22119,6 @@ "xpack.uptime.synthetics.screenshot.noImageMessage": "没有可用图像", "xpack.uptime.synthetics.screenshotDisplay.altText": "名称为“{stepName}”的步骤的屏幕截图", "xpack.uptime.synthetics.screenshotDisplay.altTextWithoutName": "屏幕截图", - "xpack.uptime.synthetics.screenshotDisplay.thumbnailAltText": "名称为“{stepName}”的步骤的缩略屏幕截图", - "xpack.uptime.synthetics.screenshotDisplay.thumbnailAltTextWithoutName": "缩略屏幕截图", "xpack.uptime.synthetics.statusBadge.failedMessage": "失败", "xpack.uptime.synthetics.statusBadge.skippedMessage": "已跳过", "xpack.uptime.synthetics.statusBadge.succeededMessage": "成功", From 7a89640143f2bbf2479ef104474a3c3366c37980 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 15 Feb 2021 14:50:46 +0100 Subject: [PATCH 18/30] fix ping list --- .../uptime/public/components/monitor/ping_list/ping_list.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx index 0e41504aef700..44ee02829d078 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx @@ -221,6 +221,9 @@ export const PingList = () => { ]; const getRowProps = (item: Ping) => { + if (monitorType !== MONITOR_TYPES.BROWSER) { + return {}; + } const { monitor } = item; return { 'data-test-subj': `row-${monitor.check_group}`, From a56556634a180c71902700c86a9171d71b37b7e8 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 15 Feb 2021 15:07:46 +0100 Subject: [PATCH 19/30] added loading state --- .../components/synthetics/check_steps/steps_list.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx index 018a8957ea8fd..4afddcbe772eb 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx @@ -38,7 +38,12 @@ function isStepEnd(step: Ping) { return step.synthetics?.type === 'step/end'; } -function statusMessage(count: StepStatusCount) { +function statusMessage(count: StepStatusCount, loading?: boolean) { + if (loading) { + return i18n.translate('xpack.uptime.synthetics.journey.loadingSteps', { + defaultMessage: 'Loading steps ...', + }); + } const total = count.succeeded + count.failed + count.skipped; if (count.failed + count.skipped === total) { return i18n.translate('xpack.uptime.synthetics.journey.allFailedMessage', { @@ -139,7 +144,10 @@ export const StepsList = ({ data, error, loading }: Props) => {

- {statusMessage(steps.reduce(reduceStepStatus, { failed: 0, skipped: 0, succeeded: 0 }))} + {statusMessage( + steps.reduce(reduceStepStatus, { failed: 0, skipped: 0, succeeded: 0 }), + loading + )}

Date: Mon, 15 Feb 2021 19:09:22 +0100 Subject: [PATCH 20/30] fixed icon in console logs --- .../uptime/public/components/synthetics/executed_step.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx index 2aae4146d2167..a77b3dfe3ba21 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/executed_step.tsx @@ -79,7 +79,7 @@ export const ExecutedStep: FC = ({ initialIsOpen={!isSucceeded} > {step.synthetics?.payload?.source} - {' '} + = ({ language="javascript" initialIsOpen={!isSucceeded} > - {browserConsole} + <> + {browserConsole} + From 65ea39c045414eb3c11698e9c12ab4dfac6fcda2 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 16 Feb 2021 20:23:12 +0100 Subject: [PATCH 21/30] update link --- .../step_expanded_row/screenshot_link.tsx | 47 +++++++++++++++++++ .../step_expanded_row/step_screenshots.tsx | 16 +------ 2 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx new file mode 100644 index 0000000000000..9accc457aa79f --- /dev/null +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx @@ -0,0 +1,47 @@ +/* + * 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 from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ReactRouterEuiLink } from '../../../common/react_router_helpers'; +import { Ping } from '../../../../../common/runtime_types/ping'; +import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; + +const LabelLink = euiStyled.div` + margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs}; + font-size: ${({ theme }) => theme.eui.euiFontSizeS}; +`; + +interface Props { + lastSuccessfulStep: Ping; +} + +export const ScreenshotLink = ({ lastSuccessfulStep }: Props) => { + return ( + + + + + + + ), + }} + /> + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx index 0cc437dbb9bea..fc1eaddbbf080 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx @@ -15,7 +15,7 @@ import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/comm import { useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/public'; import { useFetcher } from '../../../../../../observability/public'; import { fetchLastSuccessfulStep } from '../../../../state/api/journey'; -import { ReactRouterEuiLink } from '../../../common/react_router_helpers'; +import { ScreenshotLink } from './screenshot_link'; const Label = euiStyled.div` margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs}; @@ -23,11 +23,6 @@ const Label = euiStyled.div` color: ${({ theme }) => theme.eui.euiColorDarkShade}; `; -const LabelLink = euiStyled.div` - margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs}; - font-size: ${({ theme }) => theme.eui.euiFontSizeS}; -`; - interface Props { step: Ping; } @@ -76,14 +71,7 @@ export const StepScreenshots = ({ step }: Props) => {
{!isSucceeded && lastSuccessfulStep?.monitor && ( - - - - - + Date: Wed, 17 Feb 2021 15:15:14 +0100 Subject: [PATCH 22/30] fixed showing first step image in all rows --- .../ping_list/columns/ping_timestamp/ping_timestamp.tsx | 5 +++-- .../columns/ping_timestamp/step_image_popover.tsx | 2 +- .../synthetics/step_detail/use_monitor_breadcrumb.tsx | 7 +++---- .../monitor_list/columns/monitor_status_column.tsx | 2 +- .../check_steps/step_expanded_row/step_screenshots.tsx | 8 +++----- .../components/synthetics/check_steps/step_image.tsx | 6 +++++- .../uptime/public/pages/synthetics/checks_navigation.tsx | 8 ++------ 7 files changed, 18 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx index b3f05957b8c85..7d9b294bf1ff6 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx @@ -41,10 +41,11 @@ interface Props { label?: string; ping: Ping; showNavBtn?: boolean; + initialStepNo?: number; } -export const PingTimestamp = ({ label, ping, showNavBtn = true }: Props) => { - const [stepNumber, setStepNumber] = useState(1); +export const PingTimestamp = ({ label, ping, showNavBtn = true, initialStepNo = 1 }: Props) => { + const [stepNumber, setStepNumber] = useState(initialStepNo); const [isImagePopoverOpen, setIsImagePopoverOpen] = useState(false); const [stepImages, setStepImages] = useState([]); diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx index a986ae8f98086..d3dce3a2505b2 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_popover.tsx @@ -38,7 +38,7 @@ export const StepImagePopover: React.FC = ({ isImagePopoverOpen, }) => ( { - const [dateFormat] = useUiSetting$('dateFormat'); - const kibana = useKibana(); const appPath = kibana.services.application?.getUrlForApp(PLUGIN.ID) ?? ''; @@ -41,7 +40,7 @@ export const useMonitorBreadcrumb = ({ ...(details?.journey?.monitor?.check_group ? [ { - text: moment(details?.timestamp).format(dateFormat), + text: getShortTimeStamp(moment(details?.timestamp)), href: `${appPath}/journey/${details.journey.monitor.check_group}/steps`, }, ] diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx index c6476a5bf2e53..f5581f75b3759 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_status_column.tsx @@ -67,7 +67,7 @@ export const getShortTimeStamp = (timeStamp: moment.Moment, relative = false) => moment.locale(prevLocale); return shortTimestamp; } else { - if (moment().diff(timeStamp, 'd') > 1) { + if (moment().diff(timeStamp, 'd') >= 1) { return timeStamp.format('ll LTS'); } return timeStamp.format('LTS'); diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx index fc1eaddbbf080..eb7bc95751557 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/step_screenshots.tsx @@ -12,10 +12,10 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { StepScreenshotDisplay } from '../../step_screenshot_display'; import { Ping } from '../../../../../common/runtime_types/ping'; import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; -import { useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/public'; import { useFetcher } from '../../../../../../observability/public'; import { fetchLastSuccessfulStep } from '../../../../state/api/journey'; import { ScreenshotLink } from './screenshot_link'; +import { getShortTimeStamp } from '../../../overview/monitor_list/columns/monitor_status_column'; const Label = euiStyled.div` margin-bottom: ${(props) => props.theme.eui.paddingSizes.xs}; @@ -28,8 +28,6 @@ interface Props { } export const StepScreenshots = ({ step }: Props) => { - const [dateFormat] = useUiSetting$('dateFormat'); - const isSucceeded = step.synthetics?.payload?.status === 'succeeded'; const { data: lastSuccessfulStep } = useFetcher(() => { @@ -67,7 +65,7 @@ export const StepScreenshots = ({ step }: Props) => { lazyLoad={false} /> - + {!isSucceeded && lastSuccessfulStep?.monitor && ( @@ -80,7 +78,7 @@ export const StepScreenshots = ({ step }: Props) => { lazyLoad={false} /> - + )}
diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx index 4c9101ebccb00..9b7fc9ad8ee67 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx @@ -18,7 +18,11 @@ export const StepImage = ({ step }: Props) => { return ( - + {step.synthetics?.step?.name} diff --git a/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx b/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx index 88096d62f9f43..291019d93c398 100644 --- a/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx +++ b/x-pack/plugins/uptime/public/pages/synthetics/checks_navigation.tsx @@ -10,8 +10,8 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui import { FormattedMessage } from '@kbn/i18n/react'; import { useHistory } from 'react-router-dom'; import moment from 'moment'; -import { useUiSetting$ } from '../../../../../../src/plugins/kibana_react/public'; import { SyntheticsJourneyApiResponse } from '../../../common/runtime_types/ping'; +import { getShortTimeStamp } from '../../components/overview/monitor_list/columns/monitor_status_column'; interface Props { timestamp: string; @@ -19,8 +19,6 @@ interface Props { } export const ChecksNavigation = ({ timestamp, details }: Props) => { - const [dateFormat] = useUiSetting$('dateFormat'); - const history = useHistory(); return ( @@ -40,9 +38,7 @@ export const ChecksNavigation = ({ timestamp, details }: Props) => { - - {moment(timestamp).format(dateFormat).toString()} - + {getShortTimeStamp(moment(timestamp))} Date: Wed, 17 Feb 2021 17:09:08 +0100 Subject: [PATCH 23/30] always show relevant step in thumbnail --- .../columns/ping_timestamp/ping_timestamp.tsx | 37 ++++++++----------- .../ping_timestamp/step_image_caption.tsx | 14 ++++++- .../synthetics/check_steps/step_image.tsx | 6 +-- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx index 7d9b294bf1ff6..352ab5a644c43 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/ping_timestamp.tsx @@ -13,7 +13,6 @@ import { Ping } from '../../../../../../common/runtime_types/ping'; import { useFetcher, FETCH_STATUS } from '../../../../../../../observability/public'; import { getJourneyScreenshot } from '../../../../../state/api/journey'; import { UptimeSettingsContext } from '../../../../../contexts'; -import { NavButtons } from './nav_buttons'; import { NoImageDisplay } from './no_image_display'; import { StepImageCaption } from './step_image_caption'; import { StepImagePopover } from './step_image_popover'; @@ -25,26 +24,15 @@ const StepDiv = styled.div` display: none; } } - - position: relative; - div.stepArrows { - display: none; - } - :hover { - div.stepArrows { - display: flex; - } - } `; interface Props { label?: string; ping: Ping; - showNavBtn?: boolean; initialStepNo?: number; } -export const PingTimestamp = ({ label, ping, showNavBtn = true, initialStepNo = 1 }: Props) => { +export const PingTimestamp = ({ label, ping, initialStepNo = 1 }: Props) => { const [stepNumber, setStepNumber] = useState(initialStepNo); const [isImagePopoverOpen, setIsImagePopoverOpen] = useState(false); @@ -77,6 +65,8 @@ export const PingTimestamp = ({ label, ping, showNavBtn = true, initialStepNo = const captionContent = formatCaptionContent(stepNumber, data?.maxSteps); + const [numberOfCaptions, setNumberOfCaptions] = useState(0); + const ImageCaption = ( setNumberOfCaptions((prevVal) => (val ? prevVal + 1 : prevVal - 1))} /> ); + useEffect(() => { + // This is a hack to get state if image is in full screen, we should refactor + // it once eui image exposes it's full screen state + // we are checking if number of captions are 2, that means + // image is in full screen mode since caption is also rendered on + // full screen image + // we dont want to change image displayed in thumbnail + if (numberOfCaptions === 1 && stepNumber !== initialStepNo) { + setStepNumber(initialStepNo); + } + }, [numberOfCaptions, initialStepNo, stepNumber]); + return ( @@ -110,14 +113,6 @@ export const PingTimestamp = ({ label, ping, showNavBtn = true, initialStepNo = isPending={status === FETCH_STATUS.PENDING} /> )} - {showNavBtn && ( - - )} diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx index 2f41859435800..b535cd7b300bf 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.tsx @@ -5,8 +5,8 @@ * 2.0. */ -import React, { MouseEvent } from 'react'; -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText, EuiSpacer } from '@elastic/eui'; +import React, { MouseEvent, useEffect } from 'react'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { nextAriaLabel, prevAriaLabel } from './translations'; import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; @@ -17,6 +17,7 @@ export interface StepImageCaptionProps { setStepNumber: React.Dispatch>; stepNumber: number; label?: string; + onVisible: (val: boolean) => void; } const ImageCaption = euiStyled.div` @@ -33,7 +34,16 @@ export const StepImageCaption: React.FC = ({ setStepNumber, stepNumber, label, + onVisible, }) => { + useEffect(() => { + onVisible(true); + return () => { + onVisible(false); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return ( { diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx index 9b7fc9ad8ee67..69a5ef91a5925 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_image.tsx @@ -18,11 +18,7 @@ export const StepImage = ({ step }: Props) => { return ( - + {step.synthetics?.step?.name} From 48623d005c3a5807332bf7bb86f2b451e9576878 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Wed, 17 Feb 2021 17:49:09 +0100 Subject: [PATCH 24/30] fix test --- .../columns/ping_timestamp/step_image_caption.test.tsx | 1 + .../synthetics/step_detail/use_monitor_breadcrumbs.test.tsx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx index d2b694479f85c..5e39fde5fcc67 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/columns/ping_timestamp/step_image_caption.test.tsx @@ -23,6 +23,7 @@ describe('StepImageCaption', () => { setStepNumber: jest.fn(), stepNumber: 2, label: getShortTimeStamp(moment('2020-11-26T15:28:56.896Z')), + onVisible: jest.fn(), }; }); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx index 56694ff93dfb4..4aed073424788 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/use_monitor_breadcrumbs.test.tsx @@ -76,7 +76,7 @@ describe('useMonitorBreadcrumbs', () => { Object { "href": "/app/uptime/journey/fake-test-group/steps", "onClick": [Function], - "text": "Jan 4, 2021 @ 06:25:19.104", + "text": "Jan 4, 2021 6:25:19 AM", }, ] `); @@ -142,7 +142,7 @@ describe('useMonitorBreadcrumbs', () => { Object { "href": "/app/uptime/journey/fake-test-group/steps", "onClick": [Function], - "text": "Jan 4, 2021 @ 06:25:19.104", + "text": "Jan 4, 2021 6:25:19 AM", }, Object { "text": "Performance breakdown", From 8116b82c7a0ebb862fa5b0516d4a184d4370507b Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 18 Feb 2021 11:38:02 +0100 Subject: [PATCH 25/30] fix i18n --- .../check_steps/step_expanded_row/screenshot_link.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx index 9accc457aa79f..1aa9ff6d9f89f 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx @@ -24,7 +24,7 @@ export const ScreenshotLink = ({ lastSuccessfulStep }: Props) => { return ( Date: Thu, 18 Feb 2021 12:00:44 +0100 Subject: [PATCH 26/30] added row height --- .../uptime/public/components/monitor/ping_list/ping_list.tsx | 1 + .../public/components/synthetics/check_steps/steps_list.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx index 44ee02829d078..65644ce493906 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx @@ -226,6 +226,7 @@ export const PingList = () => { } const { monitor } = item; return { + height: '85px', 'data-test-subj': `row-${monitor.check_group}`, onClick: (evt: MouseEvent) => { const targetElem = evt.target as HTMLElement; diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx index 4afddcbe772eb..0c3a7270f91e5 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx @@ -128,6 +128,7 @@ export const StepsList = ({ data, error, loading }: Props) => { const { monitor } = item; return { + height: '85px', 'data-test-subj': `row-${monitor.check_group}`, onClick: (evt: MouseEvent) => { const targetElem = evt.target as HTMLElement; From 5659d5f1caa040f7f2912f7ece0fea188d77b921 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Fri, 19 Feb 2021 11:24:31 +0100 Subject: [PATCH 27/30] removed fixed width --- .../public/components/synthetics/check_steps/steps_list.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx index 0c3a7270f91e5..47bf3ae0a1784 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/steps_list.tsx @@ -97,7 +97,6 @@ export const StepsList = ({ data, error, loading }: Props) => { align: 'left', field: 'timestamp', name: '', - width: '250px', render: (val: string, item: Ping) => ( Date: Fri, 19 Feb 2021 17:13:39 +0100 Subject: [PATCH 28/30] update link text --- .../check_steps/step_expanded_row/screenshot_link.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx index 1aa9ff6d9f89f..16068e0d72b46 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_expanded_row/screenshot_link.tsx @@ -25,7 +25,7 @@ export const ScreenshotLink = ({ lastSuccessfulStep }: Props) => { { From 1debf85cbbc74d2f442b241aa09b518cff4ca07d Mon Sep 17 00:00:00 2001 From: Shahzad Date: Wed, 10 Mar 2021 16:31:50 +0100 Subject: [PATCH 29/30] resolve conflicts --- x-pack/plugins/translations/translations/ja-JP.json | 6 ------ x-pack/plugins/translations/translations/zh-CN.json | 6 ------ 2 files changed, 12 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 92bbcc230662b..abad69f35d43a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -23315,12 +23315,8 @@ "xpack.uptime.synthetics.emptyJourney.message.footer": "表示する詳細情報はありません。", "xpack.uptime.synthetics.emptyJourney.message.heading": "ステップが含まれていませんでした。", "xpack.uptime.synthetics.emptyJourney.title": "ステップがありません。", - "xpack.uptime.synthetics.executedJourney.heading": "概要情報", "xpack.uptime.synthetics.executedStep.errorHeading": "エラー", - "xpack.uptime.synthetics.executedStep.scriptHeading": "スクリプトのステップ", "xpack.uptime.synthetics.executedStep.stackTrace": "スタックトレース", - "xpack.uptime.synthetics.executedStep.stepName": "{stepNumber}. {stepName}", - "xpack.uptime.synthetics.experimentalCallout.title": "実験的機能", "xpack.uptime.synthetics.imageLoadingSpinner.ariaLabel": "画像を示すアニメーションスピナーを読み込んでいます", "xpack.uptime.synthetics.journey.allFailedMessage": "{total}ステップ - すべて失敗またはスキップされました", "xpack.uptime.synthetics.journey.allSucceededMessage": "{total}ステップ - すべて成功しました", @@ -23331,8 +23327,6 @@ "xpack.uptime.synthetics.screenshot.noImageMessage": "画像がありません", "xpack.uptime.synthetics.screenshotDisplay.altText": "名前「{stepName}」のステップのスクリーンショット", "xpack.uptime.synthetics.screenshotDisplay.altTextWithoutName": "スクリーンショット", - "xpack.uptime.synthetics.screenshotDisplay.thumbnailAltText": "名前「{stepName}」のステップのサムネイルスクリーンショット", - "xpack.uptime.synthetics.screenshotDisplay.thumbnailAltTextWithoutName": "サムネイルスクリーンショット", "xpack.uptime.synthetics.statusBadge.failedMessage": "失敗", "xpack.uptime.synthetics.statusBadge.skippedMessage": "スキップ", "xpack.uptime.synthetics.statusBadge.succeededMessage": "成功", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index e189c53adf8c3..b43062032c33b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -23673,12 +23673,8 @@ "xpack.uptime.synthetics.emptyJourney.message.footer": "没有更多可显示的信息。", "xpack.uptime.synthetics.emptyJourney.message.heading": "此过程不包含任何步骤。", "xpack.uptime.synthetics.emptyJourney.title": "没有此过程的任何步骤", - "xpack.uptime.synthetics.executedJourney.heading": "摘要信息", "xpack.uptime.synthetics.executedStep.errorHeading": "错误", - "xpack.uptime.synthetics.executedStep.scriptHeading": "步骤脚本", "xpack.uptime.synthetics.executedStep.stackTrace": "堆栈跟踪", - "xpack.uptime.synthetics.executedStep.stepName": "{stepNumber}:{stepName}", - "xpack.uptime.synthetics.experimentalCallout.title": "实验功能", "xpack.uptime.synthetics.imageLoadingSpinner.ariaLabel": "表示图像正在加载的动画旋转图标", "xpack.uptime.synthetics.journey.allFailedMessage": "{total} 个步骤 - 全部失败或跳过", "xpack.uptime.synthetics.journey.allSucceededMessage": "{total} 个步骤 - 全部成功", @@ -23689,8 +23685,6 @@ "xpack.uptime.synthetics.screenshot.noImageMessage": "没有可用图像", "xpack.uptime.synthetics.screenshotDisplay.altText": "名称为“{stepName}”的步骤的屏幕截图", "xpack.uptime.synthetics.screenshotDisplay.altTextWithoutName": "屏幕截图", - "xpack.uptime.synthetics.screenshotDisplay.thumbnailAltText": "名称为“{stepName}”的步骤的缩略屏幕截图", - "xpack.uptime.synthetics.screenshotDisplay.thumbnailAltTextWithoutName": "缩略屏幕截图", "xpack.uptime.synthetics.statusBadge.failedMessage": "失败", "xpack.uptime.synthetics.statusBadge.skippedMessage": "已跳过", "xpack.uptime.synthetics.statusBadge.succeededMessage": "成功", From d1b4167df0c2be08c42f2e660c7869df7bb6aa18 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 11 Mar 2021 18:36:02 +0100 Subject: [PATCH 30/30] fix breadcrumb and last successfull step screenshot --- .../plugins/uptime/server/lib/requests/get_journey_details.ts | 4 ++-- .../uptime/server/lib/requests/get_journey_screenshot.ts | 4 ++-- .../uptime/server/lib/requests/get_last_successful_step.ts | 2 +- .../uptime/server/rest_api/pings/journey_screenshots.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts index ab06f0a30dcd2..de37688b155f5 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts @@ -27,7 +27,7 @@ export const getJourneyDetails: UMElasticsearchQueryFn< }, { term: { - 'synthetics.type': 'journey/end', + 'synthetics.type': 'journey/start', }, }, ], @@ -52,7 +52,7 @@ export const getJourneyDetails: UMElasticsearchQueryFn< }, { term: { - 'synthetics.type': 'journey/end', + 'synthetics.type': 'journey/start', }, }, ], 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 9cb5e1eedb6b0..faa260eb9abd4 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 @@ -60,10 +60,10 @@ export const getJourneyScreenshot: UMElasticsearchQueryFn< return null; } - const stepHit = result?.aggregations?.step.image.hits.hits[0]._source as Ping; + const stepHit = result?.aggregations?.step.image.hits.hits[0]?._source as Ping; return { - blob: stepHit.synthetics?.blob ?? null, + 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_last_successful_step.ts b/x-pack/plugins/uptime/server/lib/requests/get_last_successful_step.ts index ab9189115dc9e..82958167341c0 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_last_successful_step.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_last_successful_step.ts @@ -49,7 +49,7 @@ export const getStepLastSuccessfulStep: UMElasticsearchQueryFn< }, { term: { - 'synthetics.payload.status': 'succeeded', + 'synthetics.step.status': 'succeeded', }, }, { 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 6e908d2ea4332..2b056498d7f10 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 @@ -31,7 +31,7 @@ export const createJourneyScreenshotRoute: UMRestApiRouteFactory = (libs: UMServ stepIndex, }); - if (result === null) { + if (result === null || !result.blob) { return response.notFound(); } return response.ok({