mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Uptime] Use async search api for certificates (#111731)
This commit is contained in:
parent
ce2aac3763
commit
895747ad38
28 changed files with 432 additions and 434 deletions
|
@ -12,7 +12,7 @@ import { useKibana } from '../../../../../src/plugins/kibana_react/public';
|
|||
import { isCompleteResponse } from '../../../../../src/plugins/data/common';
|
||||
import { useFetcher } from './use_fetcher';
|
||||
|
||||
export const useEsSearch = <TParams extends estypes.SearchRequest>(
|
||||
export const useEsSearch = <DocumentSource extends unknown, TParams extends estypes.SearchRequest>(
|
||||
params: TParams,
|
||||
fnDeps: any[]
|
||||
) => {
|
||||
|
@ -43,7 +43,7 @@ export const useEsSearch = <TParams extends estypes.SearchRequest>(
|
|||
|
||||
const { rawResponse } = response as any;
|
||||
|
||||
return { data: rawResponse as ESSearchResponse<unknown, TParams>, loading };
|
||||
return { data: rawResponse as ESSearchResponse<DocumentSource, TParams>, loading };
|
||||
};
|
||||
|
||||
export function createEsParams<T extends estypes.SearchRequest>(params: T): T {
|
||||
|
|
|
@ -60,6 +60,7 @@ export {
|
|||
|
||||
export const LazyAlertsFlyout = lazy(() => import('./pages/alerts/alerts_flyout'));
|
||||
export { useFetcher, FETCH_STATUS } from './hooks/use_fetcher';
|
||||
export { useEsSearch, createEsParams } from './hooks/use_es_search';
|
||||
|
||||
export * from './typings';
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
export enum API_URLS {
|
||||
CERTS = '/api/uptime/certs',
|
||||
INDEX_PATTERN = `/api/uptime/index_pattern`,
|
||||
INDEX_STATUS = '/api/uptime/index_status',
|
||||
MONITOR_LIST = `/api/uptime/monitor/list`,
|
||||
|
|
183
x-pack/plugins/uptime/common/requests/get_certs_request_body.ts
Normal file
183
x-pack/plugins/uptime/common/requests/get_certs_request_body.ts
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* 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 { estypes } from '@elastic/elasticsearch';
|
||||
import { CertResult, GetCertsParams, Ping } from '../runtime_types';
|
||||
import { createEsQuery } from '../utils/es_search';
|
||||
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { CertificatesResults } from '../../server/lib/requests/get_certs';
|
||||
import { asMutableArray } from '../utils/as_mutable_array';
|
||||
|
||||
enum SortFields {
|
||||
'issuer' = 'tls.server.x509.issuer.common_name',
|
||||
'not_after' = 'tls.server.x509.not_after',
|
||||
'not_before' = 'tls.server.x509.not_before',
|
||||
'common_name' = 'tls.server.x509.subject.common_name',
|
||||
}
|
||||
|
||||
export const DEFAULT_SORT = 'not_after';
|
||||
export const DEFAULT_DIRECTION = 'asc';
|
||||
export const DEFAULT_SIZE = 20;
|
||||
export const DEFAULT_FROM = 'now-5m';
|
||||
export const DEFAULT_TO = 'now';
|
||||
|
||||
export const getCertsRequestBody = ({
|
||||
pageIndex,
|
||||
search,
|
||||
notValidBefore,
|
||||
notValidAfter,
|
||||
size = DEFAULT_SIZE,
|
||||
to = DEFAULT_TO,
|
||||
from = DEFAULT_FROM,
|
||||
sortBy = DEFAULT_SORT,
|
||||
direction = DEFAULT_DIRECTION,
|
||||
}: GetCertsParams) => {
|
||||
const sort = SortFields[sortBy as keyof typeof SortFields];
|
||||
|
||||
const searchRequest = createEsQuery({
|
||||
body: {
|
||||
from: pageIndex * size,
|
||||
size,
|
||||
sort: asMutableArray([
|
||||
{
|
||||
[sort]: {
|
||||
order: direction,
|
||||
},
|
||||
},
|
||||
]),
|
||||
query: {
|
||||
bool: {
|
||||
...(search
|
||||
? {
|
||||
minimum_should_match: 1,
|
||||
should: [
|
||||
{
|
||||
multi_match: {
|
||||
query: escape(search),
|
||||
type: 'phrase_prefix' as const,
|
||||
fields: [
|
||||
'monitor.id.text',
|
||||
'monitor.name.text',
|
||||
'url.full.text',
|
||||
'tls.server.x509.subject.common_name.text',
|
||||
'tls.server.x509.issuer.common_name.text',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
filter: [
|
||||
{
|
||||
exists: {
|
||||
field: 'tls.server.hash.sha256',
|
||||
},
|
||||
},
|
||||
{
|
||||
range: {
|
||||
'monitor.timespan': {
|
||||
gte: from,
|
||||
lte: to,
|
||||
},
|
||||
},
|
||||
},
|
||||
...(notValidBefore
|
||||
? [
|
||||
{
|
||||
range: {
|
||||
'tls.certificate_not_valid_before': {
|
||||
lte: notValidBefore,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(notValidAfter
|
||||
? [
|
||||
{
|
||||
range: {
|
||||
'tls.certificate_not_valid_after': {
|
||||
lte: notValidAfter,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
] as estypes.QueryDslQueryContainer,
|
||||
},
|
||||
},
|
||||
_source: [
|
||||
'monitor.id',
|
||||
'monitor.name',
|
||||
'tls.server.x509.issuer.common_name',
|
||||
'tls.server.x509.subject.common_name',
|
||||
'tls.server.hash.sha1',
|
||||
'tls.server.hash.sha256',
|
||||
'tls.server.x509.not_after',
|
||||
'tls.server.x509.not_before',
|
||||
],
|
||||
collapse: {
|
||||
field: 'tls.server.hash.sha256',
|
||||
inner_hits: {
|
||||
_source: {
|
||||
includes: ['monitor.id', 'monitor.name', 'url.full'],
|
||||
},
|
||||
collapse: {
|
||||
field: 'monitor.id',
|
||||
},
|
||||
name: 'monitors',
|
||||
sort: [{ 'monitor.id': 'asc' as const }],
|
||||
},
|
||||
},
|
||||
aggs: {
|
||||
total: {
|
||||
cardinality: {
|
||||
field: 'tls.server.hash.sha256',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return searchRequest.body;
|
||||
};
|
||||
|
||||
export const processCertsResult = (result: CertificatesResults): CertResult => {
|
||||
const certs = result.hits?.hits?.map((hit) => {
|
||||
const ping = hit._source;
|
||||
const server = ping.tls?.server!;
|
||||
|
||||
const notAfter = server?.x509?.not_after;
|
||||
const notBefore = server?.x509?.not_before;
|
||||
const issuer = server?.x509?.issuer?.common_name;
|
||||
const commonName = server?.x509?.subject?.common_name;
|
||||
const sha1 = server?.hash?.sha1;
|
||||
const sha256 = server?.hash?.sha256;
|
||||
|
||||
const monitors = hit.inner_hits!.monitors.hits.hits.map((monitor) => {
|
||||
const monitorPing = monitor._source as Ping;
|
||||
|
||||
return {
|
||||
name: monitorPing?.monitor.name,
|
||||
id: monitorPing?.monitor.id,
|
||||
url: monitorPing?.url?.full,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
monitors,
|
||||
issuer,
|
||||
sha1,
|
||||
sha256: sha256 as string,
|
||||
not_after: notAfter,
|
||||
not_before: notBefore,
|
||||
common_name: commonName,
|
||||
};
|
||||
});
|
||||
const total = result.aggregations?.total?.value ?? 0;
|
||||
return { certs, total };
|
||||
};
|
|
@ -9,10 +9,7 @@ import * as t from 'io-ts';
|
|||
|
||||
export const GetCertsParamsType = t.intersection([
|
||||
t.type({
|
||||
index: t.number,
|
||||
size: t.number,
|
||||
sortBy: t.string,
|
||||
direction: t.string,
|
||||
pageIndex: t.number,
|
||||
}),
|
||||
t.partial({
|
||||
search: t.string,
|
||||
|
@ -20,6 +17,9 @@ export const GetCertsParamsType = t.intersection([
|
|||
notValidAfter: t.string,
|
||||
from: t.string,
|
||||
to: t.string,
|
||||
sortBy: t.string,
|
||||
direction: t.string,
|
||||
size: t.number,
|
||||
}),
|
||||
]);
|
||||
|
||||
|
|
12
x-pack/plugins/uptime/common/utils/es_search.ts
Normal file
12
x-pack/plugins/uptime/common/utils/es_search.ts
Normal file
|
@ -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 { estypes } from '@elastic/elasticsearch';
|
||||
|
||||
export function createEsQuery<T extends estypes.SearchRequest>(params: T): T {
|
||||
return params;
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CertificateList shallow renders expected elements for valid props 1`] = `
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
"history": Object {
|
||||
"action": "POP",
|
||||
"block": [Function],
|
||||
"canGo": [Function],
|
||||
"createHref": [Function],
|
||||
"entries": Array [
|
||||
Object {
|
||||
"hash": "",
|
||||
"key": "TestKeyForTesting",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"state": undefined,
|
||||
},
|
||||
],
|
||||
"go": [Function],
|
||||
"goBack": [Function],
|
||||
"goForward": [Function],
|
||||
"index": 0,
|
||||
"length": 1,
|
||||
"listen": [Function],
|
||||
"location": Object {
|
||||
"hash": "",
|
||||
"key": "TestKeyForTesting",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"state": undefined,
|
||||
},
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
},
|
||||
"location": Object {
|
||||
"hash": "",
|
||||
"key": "TestKeyForTesting",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"state": undefined,
|
||||
},
|
||||
"match": Object {
|
||||
"isExact": true,
|
||||
"params": Object {},
|
||||
"path": "/",
|
||||
"url": "/",
|
||||
},
|
||||
"staticContext": undefined,
|
||||
}
|
||||
}
|
||||
>
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
"action": "POP",
|
||||
"block": [Function],
|
||||
"canGo": [Function],
|
||||
"createHref": [Function],
|
||||
"entries": Array [
|
||||
Object {
|
||||
"hash": "",
|
||||
"key": "TestKeyForTesting",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"state": undefined,
|
||||
},
|
||||
],
|
||||
"go": [Function],
|
||||
"goBack": [Function],
|
||||
"goForward": [Function],
|
||||
"index": 0,
|
||||
"length": 1,
|
||||
"listen": [Function],
|
||||
"location": Object {
|
||||
"hash": "",
|
||||
"key": "TestKeyForTesting",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"state": undefined,
|
||||
},
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<CertificateList
|
||||
onChange={[MockFunction]}
|
||||
page={
|
||||
Object {
|
||||
"index": 0,
|
||||
"size": 10,
|
||||
}
|
||||
}
|
||||
sort={
|
||||
Object {
|
||||
"direction": "asc",
|
||||
"field": "not_after",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</ContextProvider>
|
||||
</ContextProvider>
|
||||
`;
|
|
@ -5,9 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { ChangeEvent } from 'react';
|
||||
import React, { ChangeEvent, useState } from 'react';
|
||||
import { EuiFieldSearch } from '@elastic/eui';
|
||||
import styled from 'styled-components';
|
||||
import useDebounce from 'react-use/lib/useDebounce';
|
||||
import * as labels from './translations';
|
||||
|
||||
const WrapFieldSearch = styled('div')`
|
||||
|
@ -19,10 +20,20 @@ interface Props {
|
|||
}
|
||||
|
||||
export const CertificateSearch: React.FC<Props> = ({ setSearch }) => {
|
||||
const [debouncedValue, setDebouncedValue] = useState('');
|
||||
|
||||
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setSearch(e.target.value);
|
||||
setDebouncedValue(e.target.value);
|
||||
};
|
||||
|
||||
useDebounce(
|
||||
() => {
|
||||
setSearch(debouncedValue);
|
||||
},
|
||||
350,
|
||||
[debouncedValue]
|
||||
);
|
||||
|
||||
return (
|
||||
<WrapFieldSearch>
|
||||
<EuiFieldSearch
|
||||
|
|
|
@ -11,14 +11,14 @@ import { useSelector } from 'react-redux';
|
|||
import { certificatesSelector } from '../../state/certificates/certificates';
|
||||
|
||||
export const CertificateTitle = () => {
|
||||
const { data: certificates } = useSelector(certificatesSelector);
|
||||
const total = useSelector(certificatesSelector);
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.uptime.certificates.heading"
|
||||
defaultMessage="TLS Certificates ({total})"
|
||||
values={{
|
||||
total: <span data-test-subj="uptimeCertTotal">{certificates?.total ?? 0}</span>,
|
||||
total: <span data-test-subj="uptimeCertTotal">{total ?? 0}</span>,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallowWithRouter } from '../../lib';
|
||||
import { CertificateList, CertSort } from './certificates_list';
|
||||
import { render } from '../../lib/helper/rtl_helpers';
|
||||
|
||||
describe('CertificateList', () => {
|
||||
it('shallow renders expected elements for valid props', () => {
|
||||
it('render empty state', () => {
|
||||
const page = {
|
||||
index: 0,
|
||||
size: 10,
|
||||
|
@ -20,8 +20,59 @@ describe('CertificateList', () => {
|
|||
direction: 'asc',
|
||||
};
|
||||
|
||||
const { getByText } = render(
|
||||
<CertificateList
|
||||
page={page}
|
||||
sort={sort}
|
||||
onChange={jest.fn()}
|
||||
certificates={{ loading: false, total: 0, certs: [] }}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
shallowWithRouter(<CertificateList page={page} sort={sort} onChange={jest.fn()} />)
|
||||
).toMatchSnapshot();
|
||||
getByText('No Certificates found. Note: Certificates are only visible for Heartbeat 7.8+')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders certificates list', () => {
|
||||
const page = {
|
||||
index: 0,
|
||||
size: 10,
|
||||
};
|
||||
const sort: CertSort = {
|
||||
field: 'not_after',
|
||||
direction: 'asc',
|
||||
};
|
||||
|
||||
const { getByText } = render(
|
||||
<CertificateList
|
||||
page={page}
|
||||
sort={sort}
|
||||
onChange={jest.fn()}
|
||||
certificates={{
|
||||
loading: false,
|
||||
total: 1,
|
||||
certs: [
|
||||
{
|
||||
monitors: [
|
||||
{
|
||||
name: 'BadSSL Expired',
|
||||
id: 'expired-badssl',
|
||||
url: 'https://expired.badssl.com/',
|
||||
},
|
||||
],
|
||||
issuer: 'COMODO RSA Domain Validation Secure Server CA',
|
||||
sha1: '404bbd2f1f4cc2fdeef13aabdd523ef61f1c71f3',
|
||||
sha256: 'ba105ce02bac76888ecee47cd4eb7941653e9ac993b61b2eb3dcc82014d21b4f',
|
||||
not_after: '2015-04-12T23:59:59.000Z',
|
||||
not_before: '2015-04-09T00:00:00.000Z',
|
||||
common_name: '*.badssl.com',
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(getByText('BadSSL Expired')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,13 +7,11 @@
|
|||
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Direction, EuiBasicTable } from '@elastic/eui';
|
||||
import { certificatesSelector } from '../../state/certificates/certificates';
|
||||
import { CertStatus } from './cert_status';
|
||||
import { CertMonitors } from './cert_monitors';
|
||||
import * as labels from './translations';
|
||||
import { Cert, CertMonitor } from '../../../common/runtime_types';
|
||||
import { Cert, CertMonitor, CertResult } from '../../../common/runtime_types';
|
||||
import { FingerprintCol } from './fingerprint_col';
|
||||
import { LOADING_CERTIFICATES, NO_CERTS_AVAILABLE } from './translations';
|
||||
|
||||
|
@ -40,11 +38,10 @@ interface Props {
|
|||
page: Page;
|
||||
sort: CertSort;
|
||||
onChange: (page: Page, sort: CertSort) => void;
|
||||
certificates: CertResult & { loading?: boolean };
|
||||
}
|
||||
|
||||
export const CertificateList: React.FC<Props> = ({ page, sort, onChange }) => {
|
||||
const { data: certificates, loading } = useSelector(certificatesSelector);
|
||||
|
||||
export const CertificateList: React.FC<Props> = ({ page, certificates, sort, onChange }) => {
|
||||
const onTableChange = (newVal: Partial<Props>) => {
|
||||
onChange(newVal.page as Page, newVal.sort as CertSort);
|
||||
};
|
||||
|
@ -100,7 +97,7 @@ export const CertificateList: React.FC<Props> = ({ page, sort, onChange }) => {
|
|||
|
||||
return (
|
||||
<EuiBasicTable
|
||||
loading={loading}
|
||||
loading={certificates.loading}
|
||||
columns={columns}
|
||||
items={certificates?.certs ?? []}
|
||||
pagination={pagination}
|
||||
|
@ -112,7 +109,7 @@ export const CertificateList: React.FC<Props> = ({ page, sort, onChange }) => {
|
|||
},
|
||||
}}
|
||||
noItemsMessage={
|
||||
loading ? (
|
||||
certificates.loading ? (
|
||||
LOADING_CERTIFICATES
|
||||
) : (
|
||||
<span data-test-subj="uptimeCertsEmptyMessage">{NO_CERTS_AVAILABLE}</span>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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 { useSelector } from 'react-redux';
|
||||
import { useContext } from 'react';
|
||||
import { useEsSearch, createEsParams } from '../../../../observability/public';
|
||||
|
||||
import { CertResult, GetCertsParams, Ping } from '../../../common/runtime_types';
|
||||
|
||||
import { selectDynamicSettings } from '../../state/selectors';
|
||||
import {
|
||||
DEFAULT_DIRECTION,
|
||||
DEFAULT_FROM,
|
||||
DEFAULT_SIZE,
|
||||
DEFAULT_SORT,
|
||||
DEFAULT_TO,
|
||||
getCertsRequestBody,
|
||||
processCertsResult,
|
||||
} from '../../../common/requests/get_certs_request_body';
|
||||
import { UptimeRefreshContext } from '../../contexts';
|
||||
|
||||
export const useCertSearch = ({
|
||||
pageIndex,
|
||||
size = DEFAULT_SIZE,
|
||||
search,
|
||||
sortBy = DEFAULT_SORT,
|
||||
direction = DEFAULT_DIRECTION,
|
||||
}: GetCertsParams): CertResult & { loading?: boolean } => {
|
||||
const settings = useSelector(selectDynamicSettings);
|
||||
const { lastRefresh } = useContext(UptimeRefreshContext);
|
||||
|
||||
const searchBody = getCertsRequestBody({
|
||||
pageIndex,
|
||||
size,
|
||||
search,
|
||||
sortBy,
|
||||
direction,
|
||||
to: DEFAULT_TO,
|
||||
from: DEFAULT_FROM,
|
||||
});
|
||||
|
||||
const esParams = createEsParams({
|
||||
index: settings.settings?.heartbeatIndices,
|
||||
body: searchBody,
|
||||
});
|
||||
|
||||
const { data: result, loading } = useEsSearch<Ping, typeof esParams>(esParams, [
|
||||
settings.settings?.heartbeatIndices,
|
||||
size,
|
||||
pageIndex,
|
||||
lastRefresh,
|
||||
search,
|
||||
]);
|
||||
|
||||
return result ? { ...processCertsResult(result), loading } : { certs: [], total: 0, loading };
|
||||
};
|
|
@ -95,10 +95,7 @@ export const mockState: AppState = {
|
|||
},
|
||||
},
|
||||
certificates: {
|
||||
certs: {
|
||||
data: null,
|
||||
loading: false,
|
||||
},
|
||||
total: 0,
|
||||
},
|
||||
selectedFilters: null,
|
||||
alerts: {
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTrackPageview } from '../../../observability/public';
|
||||
import { useBreadcrumbs } from '../hooks/use_breadcrumbs';
|
||||
import { getDynamicSettings } from '../state/actions/dynamic_settings';
|
||||
import { UptimeRefreshContext } from '../contexts';
|
||||
import { getCertificatesAction } from '../state/certificates/certificates';
|
||||
import { CertificateList, CertificateSearch, CertSort } from '../components/certificates';
|
||||
import { useCertSearch } from '../components/certificates/use_cert_search';
|
||||
import { setCertificatesTotalAction } from '../state/certificates/certificates';
|
||||
|
||||
const DEFAULT_PAGE_SIZE = 10;
|
||||
const LOCAL_STORAGE_KEY = 'xpack.uptime.certList.pageSize';
|
||||
|
@ -40,22 +40,21 @@ export const CertificatesPage: React.FC = () => {
|
|||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { lastRefresh } = useContext(UptimeRefreshContext);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getDynamicSettings());
|
||||
}, [dispatch]);
|
||||
|
||||
const certificates = useCertSearch({
|
||||
search,
|
||||
size: page.size,
|
||||
pageIndex: page.index,
|
||||
sortBy: sort.field,
|
||||
direction: sort.direction,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
getCertificatesAction.get({
|
||||
search,
|
||||
...page,
|
||||
sortBy: sort.field,
|
||||
direction: sort.direction,
|
||||
})
|
||||
);
|
||||
}, [dispatch, page, search, sort.direction, sort.field, lastRefresh]);
|
||||
dispatch(setCertificatesTotalAction({ total: certificates.total }));
|
||||
}, [certificates.total, dispatch]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -70,6 +69,7 @@ export const CertificatesPage: React.FC = () => {
|
|||
localStorage.setItem(LOCAL_STORAGE_KEY, pageVal.size.toString());
|
||||
}}
|
||||
sort={sort}
|
||||
certificates={certificates}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,14 +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 { API_URLS } from '../../../common/constants';
|
||||
import { apiService } from './utils';
|
||||
import { CertResultType, GetCertsParams } from '../../../common/runtime_types';
|
||||
|
||||
export const fetchCertificates = async (params: GetCertsParams) => {
|
||||
return await apiService.get(API_URLS.CERTS, params, CertResultType);
|
||||
};
|
|
@ -5,40 +5,27 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { handleActions } from 'redux-actions';
|
||||
import { takeLatest } from 'redux-saga/effects';
|
||||
import { createAsyncAction } from '../actions/utils';
|
||||
import { asyncInitState, handleAsyncAction } from '../reducers/utils';
|
||||
import { CertResult, GetCertsParams } from '../../../common/runtime_types';
|
||||
import { Action, createAction, handleActions } from 'redux-actions';
|
||||
import { AppState } from '../index';
|
||||
import { AsyncInitState } from '../reducers/types';
|
||||
import { fetchEffectFactory } from '../effects/fetch_effect';
|
||||
import { fetchCertificates } from '../api/certificates';
|
||||
|
||||
export const getCertificatesAction = createAsyncAction<GetCertsParams, CertResult>(
|
||||
'GET_CERTIFICATES'
|
||||
);
|
||||
export const setCertificatesTotalAction = createAction<CertificatesState>('SET_CERTIFICATES_TOTAL');
|
||||
|
||||
export interface CertificatesState {
|
||||
certs: AsyncInitState<CertResult>;
|
||||
total: number;
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
certs: asyncInitState(),
|
||||
total: 0,
|
||||
};
|
||||
|
||||
export const certificatesReducer = handleActions<CertificatesState>(
|
||||
{
|
||||
...handleAsyncAction<CertificatesState>('certs', getCertificatesAction),
|
||||
[String(setCertificatesTotalAction)]: (state, action: Action<CertificatesState>) => ({
|
||||
...state,
|
||||
total: action.payload.total,
|
||||
}),
|
||||
},
|
||||
initialState
|
||||
);
|
||||
|
||||
export function* fetchCertificatesEffect() {
|
||||
yield takeLatest(
|
||||
getCertificatesAction.get,
|
||||
fetchEffectFactory(fetchCertificates, getCertificatesAction.success, getCertificatesAction.fail)
|
||||
);
|
||||
}
|
||||
|
||||
export const certificatesSelector = ({ certificates }: AppState) => certificates.certs;
|
||||
export const certificatesSelector = ({ certificates }: AppState) => certificates.total;
|
||||
|
|
|
@ -16,7 +16,6 @@ import { fetchPingsEffect, fetchPingHistogramEffect } from './ping';
|
|||
import { fetchMonitorDurationEffect } from './monitor_duration';
|
||||
import { fetchMLJobEffect } from './ml_anomaly';
|
||||
import { fetchIndexStatusEffect } from './index_status';
|
||||
import { fetchCertificatesEffect } from '../certificates/certificates';
|
||||
import { fetchAlertsEffect } from '../alerts/alerts';
|
||||
import { fetchJourneyStepsEffect } from './journey';
|
||||
import { fetchNetworkEventsEffect } from './network_events';
|
||||
|
@ -39,7 +38,6 @@ export function* rootEffect() {
|
|||
yield fork(fetchMLJobEffect);
|
||||
yield fork(fetchMonitorDurationEffect);
|
||||
yield fork(fetchIndexStatusEffect);
|
||||
yield fork(fetchCertificatesEffect);
|
||||
yield fork(fetchAlertsEffect);
|
||||
yield fork(fetchJourneyStepsEffect);
|
||||
yield fork(fetchNetworkEventsEffect);
|
||||
|
|
|
@ -93,10 +93,7 @@ describe('state selectors', () => {
|
|||
},
|
||||
},
|
||||
certificates: {
|
||||
certs: {
|
||||
data: null,
|
||||
loading: false,
|
||||
},
|
||||
total: 0,
|
||||
},
|
||||
selectedFilters: null,
|
||||
alerts: {
|
||||
|
|
|
@ -6,11 +6,10 @@
|
|||
*/
|
||||
import moment from 'moment';
|
||||
import { ALERT_SEVERITY_WARNING, ALERT_SEVERITY } from '@kbn/rule-data-utils';
|
||||
import { tlsAlertFactory, getCertSummary, DEFAULT_SIZE } from './tls';
|
||||
import { tlsAlertFactory, getCertSummary } from './tls';
|
||||
import { TLS } from '../../../common/constants/alerts';
|
||||
import { CertResult, DynamicSettings } from '../../../common/runtime_types';
|
||||
import { createRuleTypeMocks, bootstrapDependencies } from './test_utils';
|
||||
import { DEFAULT_FROM, DEFAULT_TO } from '../../rest_api/certs/certs';
|
||||
import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants';
|
||||
|
||||
import { savedObjectsAdapter, UMSavedObjectsAdapter } from '../saved_objects';
|
||||
|
@ -123,10 +122,8 @@ describe('tls alert', () => {
|
|||
});
|
||||
expect(mockGetter).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
from: DEFAULT_FROM,
|
||||
to: DEFAULT_TO,
|
||||
index: 0,
|
||||
size: DEFAULT_SIZE,
|
||||
pageIndex: 0,
|
||||
size: 1000,
|
||||
notValidAfter: `now+${DYNAMIC_SETTINGS_DEFAULTS.certExpirationThreshold}d`,
|
||||
notValidBefore: `now-${DYNAMIC_SETTINGS_DEFAULTS.certAgeThreshold}d`,
|
||||
sortBy: 'common_name',
|
||||
|
|
|
@ -13,7 +13,6 @@ import { TLS } from '../../../common/constants/alerts';
|
|||
import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants';
|
||||
import { Cert, CertResult } from '../../../common/runtime_types';
|
||||
import { commonStateTranslations, tlsTranslations } from './translations';
|
||||
import { DEFAULT_FROM, DEFAULT_TO } from '../../rest_api/certs/certs';
|
||||
import { TlsTranslations } from '../../../common/translations';
|
||||
|
||||
import { ActionGroupIdsOf } from '../../../../alerting/common';
|
||||
|
@ -23,8 +22,6 @@ import { createUptimeESClient } from '../lib';
|
|||
|
||||
export type ActionGroupIds = ActionGroupIdsOf<typeof TLS>;
|
||||
|
||||
export const DEFAULT_SIZE = 20;
|
||||
|
||||
interface TlsAlertState {
|
||||
commonName: string;
|
||||
issuer: string;
|
||||
|
@ -130,10 +127,8 @@ export const tlsAlertFactory: UptimeAlertTypeFactory<ActionGroupIds> = (_server,
|
|||
|
||||
const { certs, total }: CertResult = await libs.requests.getCerts({
|
||||
uptimeEsClient,
|
||||
from: DEFAULT_FROM,
|
||||
to: DEFAULT_TO,
|
||||
index: 0,
|
||||
size: DEFAULT_SIZE,
|
||||
pageIndex: 0,
|
||||
size: 1000,
|
||||
notValidAfter: `now+${
|
||||
dynamicSettings?.certExpirationThreshold ??
|
||||
DYNAMIC_SETTINGS_DEFAULTS.certExpirationThreshold
|
||||
|
|
|
@ -13,7 +13,6 @@ import { TLS_LEGACY } from '../../../common/constants/alerts';
|
|||
import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants';
|
||||
import { Cert, CertResult } from '../../../common/runtime_types';
|
||||
import { commonStateTranslations, tlsTranslations } from './translations';
|
||||
import { DEFAULT_FROM, DEFAULT_TO } from '../../rest_api/certs/certs';
|
||||
import { ActionGroupIdsOf } from '../../../../alerting/common';
|
||||
|
||||
import { AlertInstanceContext } from '../../../../alerting/common';
|
||||
|
@ -21,13 +20,16 @@ import { AlertInstance } from '../../../../alerting/server';
|
|||
|
||||
import { savedObjectsAdapter } from '../saved_objects';
|
||||
import { createUptimeESClient } from '../lib';
|
||||
import {
|
||||
DEFAULT_FROM,
|
||||
DEFAULT_SIZE,
|
||||
DEFAULT_TO,
|
||||
} from '../../../common/requests/get_certs_request_body';
|
||||
|
||||
export type ActionGroupIds = ActionGroupIdsOf<typeof TLS_LEGACY>;
|
||||
|
||||
type TLSAlertInstance = AlertInstance<Record<string, any>, AlertInstanceContext, ActionGroupIds>;
|
||||
|
||||
const DEFAULT_SIZE = 20;
|
||||
|
||||
interface TlsAlertState {
|
||||
count: number;
|
||||
agingCount: number;
|
||||
|
@ -125,7 +127,7 @@ export const tlsLegacyAlertFactory: UptimeAlertTypeFactory<ActionGroupIds> = (_s
|
|||
uptimeEsClient,
|
||||
from: DEFAULT_FROM,
|
||||
to: DEFAULT_TO,
|
||||
index: 0,
|
||||
pageIndex: 0,
|
||||
size: DEFAULT_SIZE,
|
||||
notValidAfter: `now+${
|
||||
dynamicSettings?.certExpirationThreshold ??
|
||||
|
|
|
@ -58,9 +58,9 @@ export function createUptimeESClient({
|
|||
|
||||
return {
|
||||
baseESClient: esClient,
|
||||
async search<TParams extends estypes.SearchRequest>(
|
||||
async search<DocumentSource extends unknown, TParams extends estypes.SearchRequest>(
|
||||
params: TParams
|
||||
): Promise<{ body: ESSearchResponse<unknown, TParams> }> {
|
||||
): Promise<{ body: ESSearchResponse<DocumentSource, TParams> }> {
|
||||
let res: any;
|
||||
let esError: any;
|
||||
const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(
|
||||
|
@ -155,7 +155,3 @@ export function debugESCall({
|
|||
}
|
||||
console.log(`\n`);
|
||||
}
|
||||
|
||||
export function createEsQuery<T extends estypes.SearchRequest>(params: T): T {
|
||||
return params;
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ describe('getCerts', () => {
|
|||
|
||||
const result = await getCerts({
|
||||
uptimeEsClient,
|
||||
index: 1,
|
||||
pageIndex: 1,
|
||||
from: 'now-2d',
|
||||
to: 'now+1h',
|
||||
search: 'my_common_name',
|
||||
|
|
|
@ -5,170 +5,37 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { PromiseType } from 'utility-types';
|
||||
import { UMElasticsearchQueryFn } from '../adapters';
|
||||
import { CertResult, GetCertsParams, Ping } from '../../../common/runtime_types';
|
||||
import {
|
||||
getCertsRequestBody,
|
||||
processCertsResult,
|
||||
} from '../../../common/requests/get_certs_request_body';
|
||||
import { UptimeESClient } from '../lib';
|
||||
|
||||
enum SortFields {
|
||||
'issuer' = 'tls.server.x509.issuer.common_name',
|
||||
'not_after' = 'tls.server.x509.not_after',
|
||||
'not_before' = 'tls.server.x509.not_before',
|
||||
'common_name' = 'tls.server.x509.subject.common_name',
|
||||
}
|
||||
export const getCerts: UMElasticsearchQueryFn<GetCertsParams, CertResult> = async (
|
||||
requestParams
|
||||
) => {
|
||||
const result = await getCertsResults(requestParams);
|
||||
|
||||
export const getCerts: UMElasticsearchQueryFn<GetCertsParams, CertResult> = async ({
|
||||
uptimeEsClient,
|
||||
index,
|
||||
from,
|
||||
to,
|
||||
size,
|
||||
search,
|
||||
notValidBefore,
|
||||
notValidAfter,
|
||||
sortBy,
|
||||
direction,
|
||||
}) => {
|
||||
const sort = SortFields[sortBy as keyof typeof SortFields];
|
||||
return processCertsResult(result);
|
||||
};
|
||||
|
||||
const searchBody = {
|
||||
from: index * size,
|
||||
size,
|
||||
sort: [
|
||||
{
|
||||
[sort]: {
|
||||
order: direction as 'asc' | 'desc',
|
||||
},
|
||||
},
|
||||
],
|
||||
query: {
|
||||
bool: {
|
||||
...(search
|
||||
? {
|
||||
minimum_should_match: 1,
|
||||
should: [
|
||||
{
|
||||
multi_match: {
|
||||
query: escape(search),
|
||||
type: 'phrase_prefix' as const,
|
||||
fields: [
|
||||
'monitor.id.text',
|
||||
'monitor.name.text',
|
||||
'url.full.text',
|
||||
'tls.server.x509.subject.common_name.text',
|
||||
'tls.server.x509.issuer.common_name.text',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
filter: [
|
||||
{
|
||||
exists: {
|
||||
field: 'tls.server.hash.sha256',
|
||||
},
|
||||
},
|
||||
{
|
||||
range: {
|
||||
'monitor.timespan': {
|
||||
gte: from,
|
||||
lte: to,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
_source: [
|
||||
'monitor.id',
|
||||
'monitor.name',
|
||||
'tls.server.x509.issuer.common_name',
|
||||
'tls.server.x509.subject.common_name',
|
||||
'tls.server.hash.sha1',
|
||||
'tls.server.hash.sha256',
|
||||
'tls.server.x509.not_after',
|
||||
'tls.server.x509.not_before',
|
||||
],
|
||||
collapse: {
|
||||
field: 'tls.server.hash.sha256',
|
||||
inner_hits: {
|
||||
_source: {
|
||||
includes: ['monitor.id', 'monitor.name', 'url.full'],
|
||||
},
|
||||
collapse: {
|
||||
field: 'monitor.id',
|
||||
},
|
||||
name: 'monitors',
|
||||
sort: [{ 'monitor.id': 'asc' as const }],
|
||||
},
|
||||
},
|
||||
aggs: {
|
||||
total: {
|
||||
cardinality: {
|
||||
field: 'tls.server.hash.sha256',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
export type CertificatesResults = PromiseType<ReturnType<typeof getCertsResults>>;
|
||||
|
||||
if (notValidBefore || notValidAfter) {
|
||||
const validityFilters: any = {
|
||||
bool: {
|
||||
should: [],
|
||||
},
|
||||
};
|
||||
if (notValidBefore) {
|
||||
validityFilters.bool.should.push({
|
||||
range: {
|
||||
'tls.certificate_not_valid_before': {
|
||||
lte: notValidBefore,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
if (notValidAfter) {
|
||||
validityFilters.bool.should.push({
|
||||
range: {
|
||||
'tls.certificate_not_valid_after': {
|
||||
lte: notValidAfter,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
const getCertsResults = async (
|
||||
requestParams: GetCertsParams & { uptimeEsClient: UptimeESClient }
|
||||
) => {
|
||||
const { uptimeEsClient } = requestParams;
|
||||
|
||||
searchBody.query.bool.filter.push(validityFilters);
|
||||
}
|
||||
const searchBody = getCertsRequestBody(requestParams);
|
||||
|
||||
const { body: result } = await uptimeEsClient.search({
|
||||
const request = { body: searchBody };
|
||||
|
||||
const { body: result } = await uptimeEsClient.search<Ping, typeof request>({
|
||||
body: searchBody,
|
||||
});
|
||||
|
||||
const certs = (result?.hits?.hits ?? []).map((hit) => {
|
||||
const ping = hit._source as Ping;
|
||||
const server = ping.tls?.server!;
|
||||
|
||||
const notAfter = server?.x509?.not_after;
|
||||
const notBefore = server?.x509?.not_before;
|
||||
const issuer = server?.x509?.issuer?.common_name;
|
||||
const commonName = server?.x509?.subject?.common_name;
|
||||
const sha1 = server?.hash?.sha1;
|
||||
const sha256 = server?.hash?.sha256;
|
||||
|
||||
const monitors = hit.inner_hits!.monitors.hits.hits.map((monitor: any) => ({
|
||||
name: monitor._source?.monitor.name,
|
||||
id: monitor._source?.monitor.id,
|
||||
url: monitor._source?.url?.full,
|
||||
}));
|
||||
|
||||
return {
|
||||
monitors,
|
||||
issuer,
|
||||
sha1,
|
||||
sha256: sha256 as string,
|
||||
not_after: notAfter,
|
||||
not_before: notBefore,
|
||||
common_name: commonName,
|
||||
};
|
||||
});
|
||||
const total = result?.aggregations?.total?.value ?? 0;
|
||||
return { certs, total };
|
||||
return result;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ import { GetPingHistogramParams, HistogramResult } from '../../../common/runtime
|
|||
import { QUERY } from '../../../common/constants';
|
||||
import { getHistogramInterval } from '../helper/get_histogram_interval';
|
||||
import { UMElasticsearchQueryFn } from '../adapters/framework';
|
||||
import { createEsQuery } from '../lib';
|
||||
import { createEsQuery } from '../../../common/utils/es_search';
|
||||
|
||||
export const getPingHistogram: UMElasticsearchQueryFn<
|
||||
GetPingHistogramParams,
|
||||
|
|
|
@ -1,54 +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 { schema } from '@kbn/config-schema';
|
||||
import { API_URLS } from '../../../common/constants';
|
||||
import { UMServerLibs } from '../../lib/lib';
|
||||
import { UMRestApiRouteFactory } from '../types';
|
||||
|
||||
export const DEFAULT_FROM = 'now-5m';
|
||||
export const DEFAULT_TO = 'now';
|
||||
|
||||
const DEFAULT_SIZE = 25;
|
||||
const DEFAULT_SORT = 'not_after';
|
||||
const DEFAULT_DIRECTION = 'asc';
|
||||
|
||||
export const createGetCertsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({
|
||||
method: 'GET',
|
||||
path: API_URLS.CERTS,
|
||||
validate: {
|
||||
query: schema.object({
|
||||
from: schema.maybe(schema.string()),
|
||||
to: schema.maybe(schema.string()),
|
||||
search: schema.maybe(schema.string()),
|
||||
index: schema.maybe(schema.number()),
|
||||
size: schema.maybe(schema.number()),
|
||||
sortBy: schema.maybe(schema.string()),
|
||||
direction: schema.maybe(schema.string()),
|
||||
}),
|
||||
},
|
||||
handler: async ({ uptimeEsClient, request }): Promise<any> => {
|
||||
const index = request.query?.index ?? 0;
|
||||
const size = request.query?.size ?? DEFAULT_SIZE;
|
||||
const from = request.query?.from ?? DEFAULT_FROM;
|
||||
const to = request.query?.to ?? DEFAULT_TO;
|
||||
const sortBy = request.query?.sortBy ?? DEFAULT_SORT;
|
||||
const direction = request.query?.direction ?? DEFAULT_DIRECTION;
|
||||
const { search } = request.query;
|
||||
|
||||
return await libs.requests.getCerts({
|
||||
uptimeEsClient,
|
||||
index,
|
||||
search,
|
||||
size,
|
||||
from,
|
||||
to,
|
||||
sortBy,
|
||||
direction,
|
||||
});
|
||||
},
|
||||
});
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { createGetCertsRoute } from './certs/certs';
|
||||
import { createGetOverviewFilters } from './overview_filters';
|
||||
import {
|
||||
createGetPingHistogramRoute,
|
||||
|
@ -35,7 +34,6 @@ export { createRouteWithAuth } from './create_route_with_auth';
|
|||
export { uptimeRouteWrapper } from './uptime_route_wrapper';
|
||||
|
||||
export const restApiRoutes: UMRestApiRouteFactory[] = [
|
||||
createGetCertsRoute,
|
||||
createGetOverviewFilters,
|
||||
createGetPingsRoute,
|
||||
createGetIndexPatternRoute,
|
||||
|
|
|
@ -9,9 +9,12 @@ import expect from '@kbn/expect';
|
|||
import moment from 'moment';
|
||||
import { isRight } from 'fp-ts/lib/Either';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
import { API_URLS } from '../../../../../plugins/uptime/common/constants';
|
||||
import { CertType } from '../../../../../plugins/uptime/common/runtime_types';
|
||||
import { makeChecksWithStatus } from './helper/make_checks';
|
||||
import {
|
||||
processCertsResult,
|
||||
getCertsRequestBody,
|
||||
} from '../../../../../plugins/uptime/common/requests/get_certs_request_body';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
|
@ -21,8 +24,18 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
describe('certs api', () => {
|
||||
describe('empty index', async () => {
|
||||
it('returns empty array for no data', async () => {
|
||||
const apiResponse = await supertest.get(API_URLS.CERTS);
|
||||
expect(JSON.stringify(apiResponse.body)).to.eql('{"certs":[],"total":0}');
|
||||
const apiResponse = await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
params: {
|
||||
index: 'heartbeat-*',
|
||||
body: getCertsRequestBody({ pageIndex: 0, size: 10 }),
|
||||
},
|
||||
});
|
||||
|
||||
const result = processCertsResult(apiResponse.body.rawResponse);
|
||||
expect(JSON.stringify(result)).to.eql('{"certs":[],"total":0}');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -67,19 +80,29 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('retrieves expected cert data', async () => {
|
||||
const apiResponse = await supertest.get(API_URLS.CERTS);
|
||||
const { body } = apiResponse;
|
||||
const { body } = await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.send({
|
||||
params: {
|
||||
index: 'heartbeat-*',
|
||||
body: getCertsRequestBody({ pageIndex: 0, size: 10 }),
|
||||
},
|
||||
});
|
||||
|
||||
expect(body.certs).not.to.be(undefined);
|
||||
expect(Array.isArray(body.certs)).to.be(true);
|
||||
expect(body.certs).to.have.length(1);
|
||||
const result = processCertsResult(body.rawResponse);
|
||||
|
||||
const decoded = CertType.decode(body.certs[0]);
|
||||
expect(result.certs).not.to.be(undefined);
|
||||
expect(Array.isArray(result.certs)).to.be(true);
|
||||
expect(result.certs).to.have.length(1);
|
||||
|
||||
const decoded = CertType.decode(result.certs[0]);
|
||||
expect(isRight(decoded)).to.be(true);
|
||||
|
||||
const cert = body.certs[0];
|
||||
const cert = result.certs[0];
|
||||
expect(Array.isArray(cert.monitors)).to.be(true);
|
||||
expect(cert.monitors[0]).to.eql({
|
||||
name: undefined,
|
||||
id: monitorId,
|
||||
url: 'http://localhost:5678/pattern?r=200x5,500x1',
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue