diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_column.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_column.tsx index 2d89c560ef193..94913c1678d21 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_column.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_column.tsx @@ -82,7 +82,7 @@ export function getColumns({ { defaultMessage: `{occurrencesCount} occ.`, values: { - occurrencesCount: asInteger(occurrences.value), + occurrencesCount: asInteger(occurrences), }, } )} diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx index 379e93f56870e..8fe02db4e24be 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx @@ -14,7 +14,6 @@ import { import { i18n } from '@kbn/i18n'; import { orderBy } from 'lodash'; import React, { useState } from 'react'; -import uuid from 'uuid'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; @@ -36,6 +35,10 @@ const DEFAULT_SORT = { field: 'occurrences' as const, }; +const INITIAL_STATE = { + items: [], +}; + export function ServiceOverviewErrorsTable({ serviceName }: Props) { const { urlParams: { start, end }, @@ -55,18 +58,11 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { const { pageIndex, sort } = tableOptions; - const { - data = { - items: [], - requestId: '', - }, - status, - } = useFetcher( + const { data = INITIAL_STATE, status } = useFetcher( (callApmApi) => { if (!start || !end || !transactionType) { return; } - return callApmApi({ endpoint: 'GET /api/apm/services/{serviceName}/error_groups/primary_statistics', @@ -81,7 +77,6 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { }, }).then((response) => { return { - requestId: uuid(), items: response.error_groups, }; }); @@ -89,15 +84,10 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { [start, end, serviceName, uiFilters, transactionType] ); - const { items, requestId } = data; + const { items } = data; const currentPageErrorGroups = orderBy( items, - (group) => { - if (sort.field === 'occurrences') { - return group.occurrences.value; - } - return group[sort.field]; - }, + sort.field, sort.direction ).slice(pageIndex * PAGE_SIZE, (pageIndex + 1) * PAGE_SIZE); @@ -109,7 +99,13 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { status: errorGroupComparisonStatisticsStatus, } = useFetcher( (callApmApi) => { - if (currentPageErrorGroups.length && start && end && transactionType) { + if ( + status === FETCH_STATUS.SUCCESS && + currentPageErrorGroups.length && + start && + end && + transactionType + ) { return callApmApi({ endpoint: 'GET /api/apm/services/{serviceName}/error_groups/comparison_statistics', @@ -127,9 +123,9 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { }); } }, - // only fetches agg results when requestId changes or group ids change + // only fetches agg results when status changes or group ids change // eslint-disable-next-line react-hooks/exhaustive-deps - [requestId, groupIds], + [status, groupIds], { preservePreviousData: false } ); diff --git a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_primary_statistics.ts b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_primary_statistics.ts index 8287b24fb8b0e..973b3cf4d4e89 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_primary_statistics.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_error_groups/get_service_error_group_primary_statistics.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { orderBy } from 'lodash'; import { ERROR_EXC_MESSAGE, ERROR_GROUP_ID, @@ -82,21 +81,13 @@ export function getServiceErrorGroupPrimaryStatistics({ last_seen: new Date( bucket.sample.hits.hits[0]?._source['@timestamp'] ).getTime(), - occurrences: { - value: bucket.doc_count, - }, + occurrences: bucket.doc_count, })) ?? []; - const sortedErrorGroups = orderBy( - errorGroups, - (group) => group.occurrences.value, - 'desc' - ); - return { is_aggregation_accurate: (response.aggregations?.error_groups.sum_other_doc_count ?? 0) === 0, - error_groups: sortedErrorGroups, + error_groups: errorGroups, }; }); } diff --git a/x-pack/test/apm_api_integration/tests/services/__snapshots__/error_groups_comparison_statistics.snap b/x-pack/test/apm_api_integration/tests/services/__snapshots__/error_groups_comparison_statistics.snap new file mode 100644 index 0000000000000..a536a6de67ff3 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/services/__snapshots__/error_groups_comparison_statistics.snap @@ -0,0 +1,133 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`APM API tests basic apm_8.0.0 Error groups comparison statistics when data is loaded returns the correct data 1`] = ` +Object { + "groupId": "051f95eabf120ebe2f8b0399fe3e54c5", + "timeseries": Array [ + Object { + "x": 1607435820000, + "y": 0, + }, + Object { + "x": 1607435880000, + "y": 0, + }, + Object { + "x": 1607435940000, + "y": 0, + }, + Object { + "x": 1607436000000, + "y": 0, + }, + Object { + "x": 1607436060000, + "y": 0, + }, + Object { + "x": 1607436120000, + "y": 0, + }, + Object { + "x": 1607436180000, + "y": 0, + }, + Object { + "x": 1607436240000, + "y": 0, + }, + Object { + "x": 1607436300000, + "y": 1, + }, + Object { + "x": 1607436360000, + "y": 0, + }, + Object { + "x": 1607436420000, + "y": 0, + }, + Object { + "x": 1607436480000, + "y": 0, + }, + Object { + "x": 1607436540000, + "y": 0, + }, + Object { + "x": 1607436600000, + "y": 1, + }, + Object { + "x": 1607436660000, + "y": 0, + }, + Object { + "x": 1607436720000, + "y": 0, + }, + Object { + "x": 1607436780000, + "y": 0, + }, + Object { + "x": 1607436840000, + "y": 0, + }, + Object { + "x": 1607436900000, + "y": 0, + }, + Object { + "x": 1607436960000, + "y": 0, + }, + Object { + "x": 1607437020000, + "y": 0, + }, + Object { + "x": 1607437080000, + "y": 0, + }, + Object { + "x": 1607437140000, + "y": 0, + }, + Object { + "x": 1607437200000, + "y": 2, + }, + Object { + "x": 1607437260000, + "y": 0, + }, + Object { + "x": 1607437320000, + "y": 1, + }, + Object { + "x": 1607437380000, + "y": 0, + }, + Object { + "x": 1607437440000, + "y": 0, + }, + Object { + "x": 1607437500000, + "y": 0, + }, + Object { + "x": 1607437560000, + "y": 0, + }, + Object { + "x": 1607437620000, + "y": 0, + }, + ], +} +`; diff --git a/x-pack/test/apm_api_integration/tests/services/__snapshots__/error_groups_primary_statistics.snap b/x-pack/test/apm_api_integration/tests/services/__snapshots__/error_groups_primary_statistics.snap new file mode 100644 index 0000000000000..cd744a3e50494 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/services/__snapshots__/error_groups_primary_statistics.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`APM API tests basic apm_8.0.0 Error groups primary statistics when data is loaded returns the correct data 1`] = ` +Object { + "error_groups": Array [ + Object { + "group_id": "051f95eabf120ebe2f8b0399fe3e54c5", + "last_seen": 1607437366098, + "name": "Could not write JSON: Null return value from advice does not match primitive return type for: public abstract double co.elastic.apm.opbeans.repositories.Numbers.getRevenue(); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Null return value from advice does not match primitive return type for: public abstract double co.elastic.apm.opbeans.repositories.Numbers.getRevenue() (through reference chain: co.elastic.apm.opbeans.repositories.Stats[\\"numbers\\"]->com.sun.proxy.$Proxy132[\\"revenue\\"])", + "occurrences": 5, + }, + Object { + "group_id": "3bb34b98031a19c277bf59c3db82d3f3", + "last_seen": 1607436860546, + "name": "java.io.IOException: Connection reset by peer", + "occurrences": 3, + }, + Object { + "group_id": "b1c3ff13ec52de11187facf9c6a82538", + "last_seen": 1607437482385, + "name": "java.io.IOException: Connection reset by peer", + "occurrences": 2, + }, + Object { + "group_id": "9581687a53eac06aba50ba17cbd959c5", + "last_seen": 1607437468244, + "name": "Could not write JSON: Unable to find co.elastic.apm.opbeans.model.Customer with id 7173; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unable to find co.elastic.apm.opbeans.model.Customer with id 7173 (through reference chain: co.elastic.apm.opbeans.model.Customer_$$_jvst101_3[\\"email\\"])", + "occurrences": 1, + }, + Object { + "group_id": "97c2eef51fec10d177ade955670a2f15", + "last_seen": 1607437475563, + "name": "Request method 'POST' not supported", + "occurrences": 1, + }, + ], + "is_aggregation_accurate": true, +} +`; diff --git a/x-pack/test/apm_api_integration/tests/services/error_groups_comparison_statistics.ts b/x-pack/test/apm_api_integration/tests/services/error_groups_comparison_statistics.ts index db7a86758747b..50690897eba7d 100644 --- a/x-pack/test/apm_api_integration/tests/services/error_groups_comparison_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/services/error_groups_comparison_statistics.ts @@ -10,6 +10,9 @@ import expect from '@kbn/expect'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { registry } from '../../common/registry'; +import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; + +type ErrorGroupsComparisonStatistics = APIReturnType<'GET /api/apm/services/{serviceName}/error_groups/comparison_statistics'>; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -69,12 +72,22 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(response.status).to.be(200); - expect(Object.keys(response.body).length).to.be(5); - const errorMetric = response.body[groupIds[0]]; - expect(errorMetric.timeseries.length).to.be(31); + + const errorGroupsComparisonStatistics = response.body as ErrorGroupsComparisonStatistics; + expect(Object.keys(errorGroupsComparisonStatistics).length).to.be.eql(groupIds.length); + + groupIds.map((groupId) => { + expect(errorGroupsComparisonStatistics[groupId]).not.to.be.empty(); + }); + + const errorgroupsComparisonStatistics = errorGroupsComparisonStatistics[groupIds[0]]; + expect( + errorgroupsComparisonStatistics.timeseries.map(({ y }) => isFinite(y)).length + ).to.be.greaterThan(0); + expectSnapshot(errorgroupsComparisonStatistics).toMatch(); }); - it('returns empty data', async () => { + it('returns an empty list when requested groupIds are not available in the given time range', async () => { const response = await supertest.get( url.format({ pathname: `/api/apm/services/opbeans-java/error_groups/comparison_statistics`, diff --git a/x-pack/test/apm_api_integration/tests/services/error_groups_primary_statistics.ts b/x-pack/test/apm_api_integration/tests/services/error_groups_primary_statistics.ts index f3de86315b1fc..51394935c1c2b 100644 --- a/x-pack/test/apm_api_integration/tests/services/error_groups_primary_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/services/error_groups_primary_statistics.ts @@ -58,7 +58,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.status).to.be(200); expect(response.body.is_aggregation_accurate).to.eql(true); - expect(response.body.error_groups.length).to.be(5); + expect(response.body.error_groups.length).to.be.greaterThan(0); + expectSnapshot(response.body).toMatch(); }); } );