From 79d8349eaf261395a7c1fdd1cf2199762fd4f541 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Wed, 21 Jun 2023 22:04:13 +0200 Subject: [PATCH] [Synthetics] Filters tls certs by saved objects (#160113) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/constants/synthetics/rest_api.ts | 2 + .../common/requests/get_certs_request_body.ts | 2 +- .../synthetics/common/runtime_types/certs.ts | 30 ++++----- .../components/certificates/certificates.tsx | 4 +- .../certificates/certificates_list.test.tsx | 4 +- .../certificates/certificates_list.tsx | 6 +- .../certificates/use_cert_search.ts | 49 ++++++-------- .../apps/synthetics/state/certs/actions.ts | 11 ++++ .../public/apps/synthetics/state/certs/api.ts | 24 +++++++ .../apps/synthetics/state/certs/effects.ts | 29 +++++++++ .../apps/synthetics/state/certs/index.ts | 45 +++++++++++++ .../apps/synthetics/state/certs/selectors.ts | 10 +++ .../apps/synthetics/state/root_effect.ts | 2 + .../apps/synthetics/state/root_reducer.ts | 3 + .../__mocks__/synthetics_store.mock.ts | 7 ++ .../server/legacy_uptime/routes/types.ts | 18 ++--- .../server/routes/certs/get_certificates.ts | 65 +++++++++++++++++++ .../plugins/synthetics/server/routes/index.ts | 2 + 18 files changed, 250 insertions(+), 63 deletions(-) create mode 100644 x-pack/plugins/synthetics/public/apps/synthetics/state/certs/actions.ts create mode 100644 x-pack/plugins/synthetics/public/apps/synthetics/state/certs/api.ts create mode 100644 x-pack/plugins/synthetics/public/apps/synthetics/state/certs/effects.ts create mode 100644 x-pack/plugins/synthetics/public/apps/synthetics/state/certs/index.ts create mode 100644 x-pack/plugins/synthetics/public/apps/synthetics/state/certs/selectors.ts create mode 100644 x-pack/plugins/synthetics/server/routes/certs/get_certificates.ts diff --git a/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts b/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts index d83cecdf567bc..22842e1c174c7 100644 --- a/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts +++ b/x-pack/plugins/synthetics/common/constants/synthetics/rest_api.ts @@ -39,6 +39,8 @@ export enum SYNTHETICS_API_URLS { DELETE_PACKAGE_POLICY = `/internal/synthetics/monitor/policy/{packagePolicyId}`, FILTERS = '/internal/synthetics/monitor/filters', + CERTS = '/internal/synthetics/certs', + // Project monitor public endpoint SYNTHETICS_MONITORS_PROJECT = '/api/synthetics/project/{projectName}/monitors', SYNTHETICS_MONITORS_PROJECT_UPDATE = '/api/synthetics/project/{projectName}/monitors/_bulk_update', diff --git a/x-pack/plugins/synthetics/common/requests/get_certs_request_body.ts b/x-pack/plugins/synthetics/common/requests/get_certs_request_body.ts index 7a57f8ab13d41..3c1c0d48d633a 100644 --- a/x-pack/plugins/synthetics/common/requests/get_certs_request_body.ts +++ b/x-pack/plugins/synthetics/common/requests/get_certs_request_body.ts @@ -47,7 +47,7 @@ export const getCertsRequestBody = ({ const searchRequest = createEsQuery({ body: { - from: pageIndex * size, + from: (pageIndex ?? 0) * size, size, sort: asMutableArray([ { diff --git a/x-pack/plugins/synthetics/common/runtime_types/certs.ts b/x-pack/plugins/synthetics/common/runtime_types/certs.ts index bb854930f8e4d..8f5f589970c09 100644 --- a/x-pack/plugins/synthetics/common/runtime_types/certs.ts +++ b/x-pack/plugins/synthetics/common/runtime_types/certs.ts @@ -7,23 +7,19 @@ import * as t from 'io-ts'; -export const GetCertsParamsType = t.intersection([ - t.type({ - pageIndex: t.number, - }), - t.partial({ - search: t.string, - notValidBefore: t.string, - notValidAfter: t.string, - from: t.string, - to: t.string, - sortBy: t.string, - direction: t.string, - size: t.number, - filters: t.unknown, - monitorIds: t.array(t.string), - }), -]); +export const GetCertsParamsType = t.partial({ + pageIndex: t.number, + search: t.string, + notValidBefore: t.string, + notValidAfter: t.string, + from: t.string, + to: t.string, + sortBy: t.string, + direction: t.string, + size: t.number, + filters: t.unknown, + monitorIds: t.array(t.string), +}); export type GetCertsParams = t.TypeOf; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/certificates.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/certificates.tsx index 4a8272a81785c..7cf8abfe13d83 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/certificates.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/certificates.tsx @@ -26,8 +26,8 @@ const getPageSizeValue = () => { }; export const CertificatesPage: React.FC = () => { - useTrackPageview({ app: 'uptime', path: 'certificates' }); - useTrackPageview({ app: 'uptime', path: 'certificates', delay: 15000 }); + useTrackPageview({ app: 'synthetics', path: 'certificates' }); + useTrackPageview({ app: 'synthetics', path: 'certificates', delay: 15000 }); useBreadcrumbs([{ text: 'Certificates' }]); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/certificates_list.test.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/certificates_list.test.tsx index 64e06ab30aaac..92f9ccb77f381 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/certificates_list.test.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/certificates_list.test.tsx @@ -25,7 +25,7 @@ describe('CertificateList', () => { page={page} sort={sort} onChange={jest.fn()} - certificates={{ loading: false, total: 0, certs: [] }} + certificates={{ isLoading: false, total: 0, certs: [] }} /> ); @@ -48,7 +48,7 @@ describe('CertificateList', () => { sort={sort} onChange={jest.fn()} certificates={{ - loading: false, + isLoading: false, total: 1, certs: [ { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/certificates_list.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/certificates_list.tsx index 1832d9a8796d9..a40cca511238c 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/certificates_list.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/certificates_list.tsx @@ -42,7 +42,7 @@ interface Props { page: Page; sort: CertSort; onChange: (page: Page, sort: CertSort) => void; - certificates: CertResult & { loading?: boolean }; + certificates: CertResult & { isLoading?: boolean }; } export const CertificateList: React.FC = ({ page, certificates, sort, onChange }) => { @@ -101,7 +101,7 @@ export const CertificateList: React.FC = ({ page, certificates, sort, onC return ( = ({ page, certificates, sort, onC }, }} noItemsMessage={ - certificates.loading ? ( + certificates.isLoading ? ( LOADING_CERTIFICATES ) : ( {NO_CERTS_AVAILABLE} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/use_cert_search.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/use_cert_search.ts index c441ffae8a540..3da60a0c21126 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/use_cert_search.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/certificates/use_cert_search.ts @@ -5,20 +5,16 @@ * 2.0. */ -import { useContext } from 'react'; -import { createEsParams, useEsSearch } from '@kbn/observability-shared-plugin/public'; +import { useContext, useEffect } from 'react'; -import { SYNTHETICS_INDEX_PATTERN } from '../../../../../common/constants'; +import { useDispatch, useSelector } from 'react-redux'; +import { getCertsListAction, selectCertsListState } from '../../state/certs'; import { DEFAULT_DIRECTION, - DEFAULT_FROM, DEFAULT_SIZE, DEFAULT_SORT, - DEFAULT_TO, - getCertsRequestBody, - processCertsResult, } from '../../../../../common/requests/get_certs_request_body'; -import { CertResult, GetCertsParams, Ping } from '../../../../../common/runtime_types'; +import { CertResult, GetCertsParams } from '../../../../../common/runtime_types'; import { SyntheticsRefreshContext } from '../../contexts'; export const useCertSearch = ({ @@ -27,31 +23,24 @@ export const useCertSearch = ({ search, sortBy = DEFAULT_SORT, direction = DEFAULT_DIRECTION, -}: GetCertsParams): CertResult & { loading?: boolean } => { +}: GetCertsParams): CertResult & { isLoading?: boolean } => { const { lastRefresh } = useContext(SyntheticsRefreshContext); - const searchBody = getCertsRequestBody({ - pageIndex, - size, - search, - sortBy, - direction, - to: DEFAULT_TO, - from: DEFAULT_FROM, - }); + const dispatch = useDispatch(); - const esParams = createEsParams({ - index: SYNTHETICS_INDEX_PATTERN, - body: searchBody, - }); + useEffect(() => { + dispatch( + getCertsListAction.get({ + pageIndex, + size, + search, + sortBy, + direction, + }) + ); + }, [direction, dispatch, lastRefresh, pageIndex, search, size, sortBy]); - const { data: result, loading } = useEsSearch( - esParams, - [size, pageIndex, lastRefresh, search, sortBy, direction], - { - name: 'getTLSCertificates', - } - ); + const { data, isLoading } = useSelector(selectCertsListState); - return result ? { ...processCertsResult(result), loading } : { certs: [], total: 0, loading }; + return { ...(data ?? { certs: [], total: 0 }), isLoading }; }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/actions.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/actions.ts new file mode 100644 index 0000000000000..7ae894da6eec2 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/actions.ts @@ -0,0 +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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CertResult, GetCertsParams } from '../../../../../common/runtime_types'; +import { createAsyncAction } from '../utils/actions'; + +export const getCertsListAction = createAsyncAction('GET CERTS LIST'); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/api.ts new file mode 100644 index 0000000000000..0c300a2fb0ba6 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/api.ts @@ -0,0 +1,24 @@ +/* + * 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 { SYNTHETICS_API_URLS } from '../../../../../common/constants'; +import { CertResult, GetCertsParams } from '../../../../../common/runtime_types'; +import { apiService } from '../../../../utils/api_service/api_service'; + +export const getCertsList = async (queryParams: GetCertsParams): Promise => { + const { pageIndex, size, search, sortBy, direction } = queryParams; + const result = (await apiService.get(SYNTHETICS_API_URLS.CERTS, { + pageIndex, + size, + search, + sortBy, + direction, + })) as { + data: CertResult; + }; + return result.data; +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/effects.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/effects.ts new file mode 100644 index 0000000000000..7d84dc7a8c291 --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/effects.ts @@ -0,0 +1,29 @@ +/* + * 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 { takeLeading } from 'redux-saga/effects'; +import { i18n } from '@kbn/i18n'; +import { fetchEffectFactory } from '../utils/fetch_effect'; +import { getCertsList } from './api'; +import { getCertsListAction } from './actions'; + +export function* getCertsListEffect() { + yield takeLeading( + getCertsListAction.get, + fetchEffectFactory( + getCertsList, + getCertsListAction.success, + getCertsListAction.fail, + undefined, + getFailMessage + ) + ); +} + +const getFailMessage = i18n.translate('xpack.synthetics.getCerts.failed', { + defaultMessage: 'Failed to get TLS certificates.', +}); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/index.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/index.ts new file mode 100644 index 0000000000000..f21df69bcbbce --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/index.ts @@ -0,0 +1,45 @@ +/* + * 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 { createReducer } from '@reduxjs/toolkit'; +import { CertResult, SyntheticsParamSO } from '../../../../../common/runtime_types'; +import { IHttpSerializedFetchError } from '..'; +import { getCertsListAction } from './actions'; + +export interface CertsListState { + isLoading?: boolean; + data?: CertResult; + error: IHttpSerializedFetchError | null; + isSaving?: boolean; + savedData?: SyntheticsParamSO; +} + +const initialState: CertsListState = { + isLoading: false, + error: null, + data: { certs: [], total: 0 }, +}; + +export const certsListReducer = createReducer(initialState, (builder) => { + builder + .addCase(getCertsListAction.get, (state) => { + state.isLoading = true; + }) + .addCase(getCertsListAction.success, (state, action) => { + state.isLoading = false; + state.data = action.payload; + }) + .addCase(getCertsListAction.fail, (state, action) => { + state.isLoading = false; + state.error = action.payload; + }); +}); + +export * from './actions'; +export * from './effects'; +export * from './selectors'; +export * from './api'; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/selectors.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/selectors.ts new file mode 100644 index 0000000000000..2fbe4f713b77c --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/certs/selectors.ts @@ -0,0 +1,10 @@ +/* + * 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 { AppState } from '..'; + +export const selectCertsListState = (state: AppState) => state.certsList; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/root_effect.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/root_effect.ts index d99837e3fc93a..de1a710a317ab 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/root_effect.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/root_effect.ts @@ -6,6 +6,7 @@ */ import { all, fork } from 'redux-saga/effects'; +import { getCertsListEffect } from './certs'; import { addGlobalParamEffect, editGlobalParamEffect, getGlobalParamEffect } from './global_params'; import { fetchManualTestRunsEffect } from './manual_test_runs/effects'; import { enableDefaultAlertingEffect, updateDefaultAlertingEffect } from './alert_rules/effects'; @@ -61,5 +62,6 @@ export const rootEffect = function* root(): Generator { fork(addGlobalParamEffect), fork(editGlobalParamEffect), fork(getGlobalParamEffect), + fork(getCertsListEffect), ]); }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/root_reducer.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/root_reducer.ts index 4bcc1861cf535..a17749de498ff 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/root_reducer.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/root_reducer.ts @@ -7,6 +7,7 @@ import { combineReducers } from '@reduxjs/toolkit'; +import { certsListReducer, CertsListState } from './certs'; import { certificatesReducer, CertificatesState } from './certificates/certificates'; import { globalParamsReducer, GlobalParamsState } from './global_params'; import { overviewStatusReducer, OverviewStatusStateReducer } from './overview_status'; @@ -45,6 +46,7 @@ export interface SyntheticsAppState { manualTestRuns: ManualTestRunsState; monitorDetails: MonitorDetailsState; browserJourney: BrowserJourneyState; + certsList: CertsListState; defaultAlerting: DefaultAlertingState; dynamicSettings: DynamicSettingsState; serviceLocations: ServiceLocationsState; @@ -71,4 +73,5 @@ export const rootReducer = combineReducers({ serviceLocations: serviceLocationsReducer, syntheticsEnablement: syntheticsEnablementReducer, certificates: certificatesReducer, + certsList: certsListReducer, }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts index a5c656d253fa3..92149d84347fa 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts @@ -156,6 +156,13 @@ export const mockState: SyntheticsAppState = { certificates: { total: 0, }, + certsList: { + error: null, + data: { + total: 0, + certs: [], + }, + }, }; function getBrowserJourneyMockSlice() { diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/routes/types.ts b/x-pack/plugins/synthetics/server/legacy_uptime/routes/types.ts index 1099166f8ab77..eb7835e4af797 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/routes/types.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/routes/types.ts @@ -71,9 +71,10 @@ export type UptimeRoute = UMRouteDefinition< export type UMRestApiRouteFactory = ( libs: UMServerLibs ) => UptimeRoute; -export type SyntheticsRestApiRouteFactory = ( - libs: UMServerLibs -) => SyntheticsRoute; +export type SyntheticsRestApiRouteFactory< + ClientContract = any, + QueryParams = Record +> = (libs: UMServerLibs) => SyntheticsRoute; export type SyntheticsStreamingRouteFactory = (libs: UMServerLibs) => SyntheticsStreamingRoute; /** @@ -85,9 +86,10 @@ export type UMKibanaRouteWrapper = ( server: UptimeServerSetup ) => UMKibanaRoute; -export type SyntheticsRoute = UMRouteDefinition< - SyntheticsRouteHandler ->; +export type SyntheticsRoute< + ClientContract = unknown, + QueryParams = Record +> = UMRouteDefinition>; export type SyntheticsStreamingRoute = UMRouteDefinition; export type SyntheticsRouteWrapper = ( @@ -131,7 +133,7 @@ export interface RouteContext> { spaceId: string; } -export type SyntheticsRouteHandler = ({ +export type SyntheticsRouteHandler> = ({ uptimeEsClient, context, request, @@ -139,7 +141,7 @@ export type SyntheticsRouteHandler = ({ server, savedObjectsClient, subject: Subject, -}: RouteContext) => Promise | ClientContract>; +}: RouteContext) => Promise | ClientContract>; export type SyntheticsStreamingRouteHandler = ({ uptimeEsClient, diff --git a/x-pack/plugins/synthetics/server/routes/certs/get_certificates.ts b/x-pack/plugins/synthetics/server/routes/certs/get_certificates.ts new file mode 100644 index 0000000000000..93b95738e1b8c --- /dev/null +++ b/x-pack/plugins/synthetics/server/routes/certs/get_certificates.ts @@ -0,0 +1,65 @@ +/* + * 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 { + getAllMonitors, + processMonitors, +} from '../../saved_objects/synthetics_monitor/get_all_monitors'; +import { monitorAttributes } from '../../../common/types/saved_objects'; +import { getCerts } from '../../legacy_uptime/lib/requests/get_certs'; +import { SYNTHETICS_API_URLS } from '../../../common/constants'; +import { CertResult, GetCertsParams } from '../../../common/runtime_types'; +import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes'; +import { AlertConfigKey } from '../../../common/constants/monitor_management'; + +export const getSyntheticsCertsRoute: SyntheticsRestApiRouteFactory< + { data: CertResult }, + GetCertsParams +> = () => ({ + method: 'GET', + path: SYNTHETICS_API_URLS.CERTS, + validate: { + query: schema.object({ + pageIndex: schema.maybe(schema.number()), + size: schema.maybe(schema.number()), + sortBy: schema.maybe(schema.string()), + direction: schema.maybe(schema.string()), + search: schema.maybe(schema.string()), + from: schema.maybe(schema.string()), + to: schema.maybe(schema.string()), + }), + }, + handler: async ({ + request, + uptimeEsClient, + savedObjectsClient, + server, + syntheticsMonitorClient, + }) => { + const queryParams = request.query; + + const monitors = await getAllMonitors({ + soClient: savedObjectsClient, + filter: `${monitorAttributes}.${AlertConfigKey.STATUS_ENABLED}: true`, + }); + + const { enabledMonitorQueryIds } = await processMonitors( + monitors, + server, + savedObjectsClient, + syntheticsMonitorClient + ); + + const data = await getCerts({ + ...queryParams, + uptimeEsClient, + monitorIds: enabledMonitorQueryIds, + }); + return { data }; + }, +}); diff --git a/x-pack/plugins/synthetics/server/routes/index.ts b/x-pack/plugins/synthetics/server/routes/index.ts index 0e86f8a0ea75e..4069da67f1e32 100644 --- a/x-pack/plugins/synthetics/server/routes/index.ts +++ b/x-pack/plugins/synthetics/server/routes/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { getSyntheticsCertsRoute } from './certs/get_certificates'; import { getAgentPoliciesRoute } from './settings/private_locations/get_agent_policies'; import { inspectSyntheticsMonitorRoute } from './monitor_cruds/inspect_monitor'; import { deletePackagePolicyRoute } from './monitor_cruds/delete_integration'; @@ -102,6 +103,7 @@ export const syntheticsAppRestApiRoutes: SyntheticsRestApiRouteFactory[] = [ getSyntheticsFilters, inspectSyntheticsMonitorRoute, getAgentPoliciesRoute, + getSyntheticsCertsRoute, ]; export const syntheticsAppStreamingApiRoutes: SyntheticsStreamingRouteFactory[] = [