Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@
import * as t from 'io-ts';

// IO type for validation
export const ErrorType = t.partial({
export const MonitorErrorType = t.partial({
code: t.number,
message: t.string,
type: t.string,
});

// Typescript type for type checking
export type Error = t.TypeOf<typeof ErrorType>;
export type MonitorError = t.TypeOf<typeof MonitorErrorType>;

export const MonitorDetailsType = t.intersection([
t.type({ monitorId: t.string }),
t.partial({ error: ErrorType }),
t.partial({ error: MonitorErrorType }),
t.partial({ timestamp: t.string }),
]);
export type MonitorDetails = t.TypeOf<typeof MonitorDetailsType>;

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,52 @@

import { shallowWithIntl, renderWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
import moment from 'moment';
import { BrowserRouter as Router } from 'react-router-dom';
import { MostRecentError } from '../most_recent_error';
import { MonitorDetails, MonitorError } from '../../../../../../common/runtime_types';

describe('MostRecentError component', () => {
let monitorDetails: any;
let monitorDetails: MonitorDetails;
let monitorError: MonitorError;

beforeAll(() => {
moment.prototype.fromNow = jest.fn(() => '5 days ago');
});

beforeEach(() => {
monitorError = {
type: 'io',
message: 'Get https://expired.badssl.com: x509: certificate has expired or is not yet valid',
};
monitorDetails = {
monitorId: 'bad-ssl',
error: {
type: 'io',
message:
'Get https://expired.badssl.com: x509: certificate has expired or is not yet valid',
},
timestamp: '2019-11-30T01:57:37.792Z',
error: monitorError,
};
});

it('validates props with shallow render', () => {
const component = shallowWithIntl(
<Router>
<MostRecentError monitorId={monitorDetails.monitorId} error={monitorDetails.error} />
<MostRecentError
monitorId={monitorDetails.monitorId}
error={monitorDetails.error}
timestamp={monitorDetails.timestamp}
/>
</Router>
);
expect(component).toMatchSnapshot();
});

it('renders properly with empty data', () => {
it('renders properly with mock data', () => {
const component = renderWithIntl(
<Router>
<MostRecentError monitorId={monitorDetails.monitorId} error={monitorDetails.error} />
<MostRecentError
monitorId={monitorDetails.monitorId}
error={monitorDetails.error}
timestamp={monitorDetails.timestamp}
/>
</Router>
);
expect(component).toMatchSnapshot();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { fetchMonitorDetails } from '../../../../state/actions/monitor';
import { MostRecentError } from './most_recent_error';
import { getMonitorDetails } from '../../../../state/selectors';
import { MonitorStatusList } from './monitor_status_list';
import { MonitorDetails } from '../../../../../common/runtime_types';

const ContainerDiv = styled.div`
padding: 10px;
Expand All @@ -30,7 +31,7 @@ interface MonitorListDrawerProps {
/**
* Monitor details to be fetched from rest api using monitorId
*/
monitorDetails: any;
monitorDetails: MonitorDetails;

/**
* Redux action to trigger , loading monitor details
Expand Down Expand Up @@ -69,7 +70,11 @@ export function MonitorListDrawerComponent({
<EuiSpacer size="s" />
<MonitorStatusList checks={summary.state.checks} />
{monitorDetails && monitorDetails.error && (
<MostRecentError error={monitorDetails.error} monitorId={summary.monitor_id} />
<MostRecentError
error={monitorDetails.error}
monitorId={summary.monitor_id}
timestamp={monitorDetails.timestamp}
/>
)}
</ContainerDiv>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,51 @@
*/
import React from 'react';
import { EuiText } from '@elastic/eui';
import moment from 'moment';
import { i18n } from '@kbn/i18n';
import { MonitorPageLink } from '../monitor_page_link';
import { useUrlParams } from '../../../../hooks';
import { stringifyUrlParams } from '../../../../lib/helper/stringify_url_params';

interface RecentError {
message: string;
type: string;
}
import { MonitorError } from '../../../../../common/runtime_types';

interface MostRecentErrorProps {
/**
* error returned from API for monitor details
*/
error: RecentError;
error: MonitorError | undefined;

/**
* monitorId to be used for link to detail page
*/
monitorId: string;

/**
* Timestamp of error for the monitor
*/
timestamp: string | undefined;
}

export const MostRecentError = ({ error, monitorId }: MostRecentErrorProps) => {
export const MostRecentError = ({ error, monitorId, timestamp }: MostRecentErrorProps) => {
const [getUrlParams] = useUrlParams();
const { absoluteDateRangeStart, absoluteDateRangeEnd, ...params } = getUrlParams();
params.selectedPingStatus = 'down';
const linkParameters = stringifyUrlParams(params);

const timestampStr = timestamp ? moment(new Date(timestamp).valueOf()).fromNow() : '';

return (
<>
<EuiText size="xs">
<h3>
{i18n.translate('xpack.uptime.monitorList.mostRecentError.title', {
defaultMessage: 'Most recent error',
defaultMessage: 'Most recent error ({timestamp})',
values: { timestamp: timestampStr },
description: 'Most Recent Error title in Monitor List Expanded row',
})}
</h3>
</EuiText>
<MonitorPageLink monitorId={monitorId} linkParameters={linkParameters}>
{error.message}
{error?.message}
</MonitorPageLink>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { getHistogramIntervalFormatted } from '../../helper';
import { DatabaseAdapter } from '../database';
import { UMMonitorsAdapter } from './adapter_types';
import { MonitorDetails, Error } from '../../../../common/runtime_types';
import { MonitorDetails, MonitorError } from '../../../../common/runtime_types';

const formatStatusBuckets = (time: any, buckets: any, docCount: any) => {
let up = null;
Expand Down Expand Up @@ -278,6 +278,7 @@ export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter {
index: INDEX_NAMES.HEARTBEAT,
body: {
size: 1,
_source: ['error', '@timestamp'],
query: {
bool: {
must: [
Expand Down Expand Up @@ -308,11 +309,15 @@ export class ElasticsearchMonitorsAdapter implements UMMonitorsAdapter {

const result = await this.database.search(request, params);

const monitorError: Error | undefined = get(result, 'hits.hits[0]._source.error', undefined);
const data = result.hits.hits[0]?._source;

const monitorError: MonitorError | undefined = data?.error;
const errorTimeStamp: string | undefined = data?.['@timestamp'];

return {
monitorId,
error: monitorError,
timestamp: errorTimeStamp,
};
}
}