From 1d9211bd46c8b623cd310f527791837115bd1cb8 Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Tue, 7 Jul 2020 13:41:07 -0700 Subject: [PATCH] [Metrics UI] Performance improvements for Observability Homepage (#70869) (#70995) --- .../public/metrics_overview_fetchers.test.ts | 12 +-- .../infra/public/metrics_overview_fetchers.ts | 7 +- .../infra/server/routes/source/index.ts | 52 +++++++++--- .../apis/metrics_ui/http_source.ts | 79 +++++++++++++++++++ .../api_integration/apis/metrics_ui/index.js | 1 + 5 files changed, 127 insertions(+), 24 deletions(-) create mode 100644 x-pack/test/api_integration/apis/metrics_ui/http_source.ts diff --git a/x-pack/plugins/infra/public/metrics_overview_fetchers.test.ts b/x-pack/plugins/infra/public/metrics_overview_fetchers.test.ts index 11348dab18b82..24c51598ad257 100644 --- a/x-pack/plugins/infra/public/metrics_overview_fetchers.test.ts +++ b/x-pack/plugins/infra/public/metrics_overview_fetchers.test.ts @@ -29,11 +29,7 @@ describe('Metrics UI Observability Homepage Functions', () => { it('should return true when true', async () => { const { core, mockedGetStartServices } = setup(); core.http.get.mockResolvedValue({ - status: { - indexFields: [], - logIndicesExist: false, - metricIndicesExist: true, - }, + hasData: true, }); const hasData = createMetricsHasData(mockedGetStartServices); const response = await hasData(); @@ -43,11 +39,7 @@ describe('Metrics UI Observability Homepage Functions', () => { it('should return false when false', async () => { const { core, mockedGetStartServices } = setup(); core.http.get.mockResolvedValue({ - status: { - indexFields: [], - logIndicesExist: false, - metricIndicesExist: false, - }, + hasData: false, }); const hasData = createMetricsHasData(mockedGetStartServices); const response = await hasData(); diff --git a/x-pack/plugins/infra/public/metrics_overview_fetchers.ts b/x-pack/plugins/infra/public/metrics_overview_fetchers.ts index 50b1269641510..15751fab39abc 100644 --- a/x-pack/plugins/infra/public/metrics_overview_fetchers.ts +++ b/x-pack/plugins/infra/public/metrics_overview_fetchers.ts @@ -16,15 +16,16 @@ import { } from '../common/http_api/snapshot_api'; import { SnapshotMetricType } from '../common/inventory_models/types'; import { InfraClientCoreSetup } from './types'; -import { SourceResponse } from '../common/http_api/source_api'; export const createMetricsHasData = ( getStartServices: InfraClientCoreSetup['getStartServices'] ) => async () => { const [coreServices] = await getStartServices(); const { http } = coreServices; - const results = await http.get('/api/metrics/source/default/metrics'); - return results.status.metricIndicesExist; + const results = await http.get<{ hasData: boolean }>( + '/api/metrics/source/default/metrics/hasData' + ); + return results.hasData; }; export const average = (values: number[]) => (values.length ? sum(values) / values.length : 0); diff --git a/x-pack/plugins/infra/server/routes/source/index.ts b/x-pack/plugins/infra/server/routes/source/index.ts index 62b7fd7ba902f..2843897071e19 100644 --- a/x-pack/plugins/infra/server/routes/source/index.ts +++ b/x-pack/plugins/infra/server/routes/source/index.ts @@ -37,22 +37,21 @@ export const initSourceRoute = (libs: InfraBackendLibs) => { try { const { type, sourceId } = request.params; - const source = await libs.sources.getSourceConfiguration( - requestContext.core.savedObjects.client, - sourceId - ); + const [source, logIndicesExist, metricIndicesExist, indexFields] = await Promise.all([ + libs.sources.getSourceConfiguration(requestContext.core.savedObjects.client, sourceId), + libs.sourceStatus.hasLogIndices(requestContext, sourceId), + libs.sourceStatus.hasMetricIndices(requestContext, sourceId), + libs.fields.getFields(requestContext, sourceId, typeToInfraIndexType(type)), + ]); + if (!source) { return response.notFound(); } const status = { - logIndicesExist: await libs.sourceStatus.hasLogIndices(requestContext, sourceId), - metricIndicesExist: await libs.sourceStatus.hasMetricIndices(requestContext, sourceId), - indexFields: await libs.fields.getFields( - requestContext, - sourceId, - typeToInfraIndexType(type) - ), + logIndicesExist, + metricIndicesExist, + indexFields, }; return response.ok({ @@ -65,4 +64,35 @@ export const initSourceRoute = (libs: InfraBackendLibs) => { } } ); + + framework.registerRoute( + { + method: 'get', + path: '/api/metrics/source/{sourceId}/{type}/hasData', + validate: { + params: schema.object({ + sourceId: schema.string(), + type: schema.string(), + }), + }, + }, + async (requestContext, request, response) => { + try { + const { type, sourceId } = request.params; + + const hasData = + type === 'metrics' + ? await libs.sourceStatus.hasMetricIndices(requestContext, sourceId) + : await libs.sourceStatus.hasLogIndices(requestContext, sourceId); + + return response.ok({ + body: { hasData }, + }); + } catch (error) { + return response.internalError({ + body: error.message, + }); + } + } + ); }; diff --git a/x-pack/test/api_integration/apis/metrics_ui/http_source.ts b/x-pack/test/api_integration/apis/metrics_ui/http_source.ts new file mode 100644 index 0000000000000..7e92caf0e37d7 --- /dev/null +++ b/x-pack/test/api_integration/apis/metrics_ui/http_source.ts @@ -0,0 +1,79 @@ +/* + * 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 expect from '@kbn/expect'; + +import { SourceResponse } from '../../../../plugins/infra/server/lib/sources'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + const fetchSource = async (): Promise => { + const response = await supertest + .get('/api/metrics/source/default/metrics') + .set('kbn-xsrf', 'xxx') + .expect(200); + return response.body; + }; + const fetchHasData = async ( + type: 'logs' | 'metrics' + ): Promise<{ hasData: boolean } | undefined> => { + const response = await supertest + .get(`/api/metrics/source/default/${type}/hasData`) + .set('kbn-xsrf', 'xxx') + .expect(200); + return response.body; + }; + + describe('Source API via HTTP', () => { + describe('8.0.0', () => { + before(() => esArchiver.load('infra/8.0.0/logs_and_metrics')); + after(() => esArchiver.unload('infra/8.0.0/logs_and_metrics')); + describe('/api/metrics/source/default/metrics', () => { + it('should just work', () => { + const resp = fetchSource(); + return resp.then((data) => { + expect(data).to.have.property('source'); + expect(data?.source.configuration.metricAlias).to.equal('metrics-*,metricbeat-*'); + expect(data?.source.configuration.logAlias).to.equal( + 'logs-*,filebeat-*,kibana_sample_data_logs*' + ); + expect(data?.source.configuration.fields).to.eql({ + container: 'container.id', + host: 'host.name', + message: ['message', '@message'], + pod: 'kubernetes.pod.uid', + tiebreaker: '_doc', + timestamp: '@timestamp', + }); + expect(data).to.have.property('status'); + expect(data?.status.metricIndicesExist).to.equal(true); + expect(data?.status.logIndicesExist).to.equal(true); + }); + }); + }); + describe('/api/metrics/source/default/metrics/hasData', () => { + it('should just work', () => { + const resp = fetchHasData('metrics'); + return resp.then((data) => { + expect(data).to.have.property('hasData'); + expect(data?.hasData).to.be(true); + }); + }); + }); + describe('/api/metrics/source/default/logs/hasData', () => { + it('should just work', () => { + const resp = fetchHasData('logs'); + return resp.then((data) => { + expect(data).to.have.property('hasData'); + expect(data?.hasData).to.be(true); + }); + }); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/metrics_ui/index.js b/x-pack/test/api_integration/apis/metrics_ui/index.js index eb8ee77da582b..fdd37fa4c335c 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/index.js +++ b/x-pack/test/api_integration/apis/metrics_ui/index.js @@ -21,5 +21,6 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./metrics_explorer')); loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./ip_to_hostname')); + loadTestFile(require.resolve('./http_source')); }); }