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 @@ -179,8 +179,6 @@ export function ServiceInventory() {
canCreateJob &&
!userHasDismissedCallout;

const isLoading = mainStatisticsStatus === FETCH_STATUS.LOADING;

return (
<>
<SearchBar showTimeComparison />
Expand All @@ -192,17 +190,10 @@ export function ServiceInventory() {
)}
<EuiFlexItem>
<ServiceList
isLoading={isLoading}
isLoading={mainStatisticsStatus === FETCH_STATUS.LOADING}
items={mainStatisticsData.items}
comparisonData={comparisonData}
noItemsMessage={
!isLoading && (
<NoServicesMessage
historicalDataFound={mainStatisticsData.hasHistoricalData}
status={mainStatisticsStatus}
/>
)
}
noItemsMessage={<NoServicesMessage status={mainStatisticsStatus} />}
/>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,76 +5,35 @@
* 2.0.
*/

import { EuiEmptyPrompt, EuiLink } from '@elastic/eui';
import { EuiEmptyPrompt } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { FETCH_STATUS } from '../../../hooks/use_fetcher';
import { ErrorStatePrompt } from '../../shared/ErrorStatePrompt';
import { useUpgradeAssistantHref } from '../../shared/Links/kibana';
import { SetupInstructionsLink } from '../../shared/Links/SetupInstructionsLink';

interface Props {
// any data submitted from APM agents found (not just in the given time range)
historicalDataFound: boolean;
status: FETCH_STATUS | undefined;
}

export function NoServicesMessage({ historicalDataFound, status }: Props) {
const upgradeAssistantHref = useUpgradeAssistantHref();

if (status === 'failure') {
return <ErrorStatePrompt />;
export function NoServicesMessage({ status }: Props) {
if (status === FETCH_STATUS.LOADING) {
return null;
}

if (historicalDataFound) {
return (
<EuiEmptyPrompt
title={
<div>
{i18n.translate('xpack.apm.servicesTable.notFoundLabel', {
defaultMessage: 'No services found',
})}
</div>
}
titleSize="s"
/>
);
if (status === FETCH_STATUS.FAILURE) {
return <ErrorStatePrompt />;
}

return (
<EuiEmptyPrompt
title={
<div>
{i18n.translate('xpack.apm.servicesTable.noServicesLabel', {
defaultMessage: `Looks like you don't have any APM services installed. Let's add some!`,
{i18n.translate('xpack.apm.servicesTable.notFoundLabel', {
defaultMessage: 'No services found',
})}
</div>
}
titleSize="s"
body={
<React.Fragment>
<p>
{i18n.translate('xpack.apm.servicesTable.7xUpgradeServerMessage', {
defaultMessage: `Upgrading from a pre-7.x version? Make sure you've also upgraded
your APM Server instance(s) to at least 7.0.`,
})}
</p>
<p>
{i18n.translate('xpack.apm.servicesTable.7xOldDataMessage', {
defaultMessage:
'You may also have old data that needs to be migrated.',
})}{' '}
<EuiLink href={upgradeAssistantHref}>
{i18n.translate('xpack.apm.servicesTable.UpgradeAssistantLink', {
defaultMessage:
'Learn more by visiting the Kibana Upgrade Assistant',
})}
</EuiLink>
.
</p>
</React.Fragment>
}
actions={<SetupInstructionsLink buttonFill={true} />}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
*/

import { EuiPageHeaderProps } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import {
useKibana,
KibanaPageTemplateProps,
} from '../../../../../../../src/plugins/kibana_react/public';
import { useFetcher } from '../../../hooks/use_fetcher';
import { ApmPluginStartDeps } from '../../../plugin';
import { ApmEnvironmentFilter } from '../../shared/EnvironmentFilter';
import { getNoDataConfig } from './no_data_config';

/*
* This template contains:
Expand Down Expand Up @@ -41,33 +42,16 @@ export function ApmMainTemplate({
const ObservabilityPageTemplate =
services.observability.navigation.PageTemplate;

// TODO: NEEDS A DATA CHECK
const hasData = true;
const noDataConfig: KibanaPageTemplateProps['noDataConfig'] = !hasData
? {
solution: i18n.translate('xpack.apm.noDataConfig.solutionName', {
defaultMessage: 'Observability',
}),
actions: {
beats: {
title: i18n.translate('xpack.apm.noDataConfig.beatsCard.title', {
defaultMessage: 'Add data with APM agents',
}),
description: i18n.translate(
'xpack.apm.noDataConfig.beatsCard.description',
{
defaultMessage:
'Use APM agents to collect APM data. We make it easy with agents for many popular languages.',
}
),
href: basePath + `/app/home#/tutorial/apm`,
},
},
docsLink: docLinks!.links.observability.guide,
}
: undefined;
const { data } = useFetcher((callApmApi) => {
return callApmApi({ endpoint: 'GET /api/apm/has_data' });
}, []);

const noDataConfig = getNoDataConfig({
basePath,
docsLink: docLinks!.links.observability.guide,
hasData: data?.hasData,
});

// TODO: GET A CHECK
return (
<ObservabilityPageTemplate
noDataConfig={noDataConfig}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { KibanaPageTemplateProps } from '../../../../../../../src/plugins/kibana_react/public';

export function getNoDataConfig({
docsLink,
basePath,
hasData,
}: {
docsLink: string;
basePath?: string;
hasData?: boolean;
}): KibanaPageTemplateProps['noDataConfig'] {
// Returns no data config when there is no historical data
if (hasData === false) {
return {
solution: i18n.translate('xpack.apm.noDataConfig.solutionName', {
defaultMessage: 'Observability',
}),
actions: {
beats: {
title: i18n.translate('xpack.apm.noDataConfig.beatsCard.title', {
defaultMessage: 'Add data with APM agents',
}),
description: i18n.translate(
'xpack.apm.noDataConfig.beatsCard.description',
{
defaultMessage:
'Use APM agents to collect APM data. We make it easy with agents for many popular languages.',
}
),
href: basePath + `/app/home#/tutorial/apm`,
},
},
docsLink,
};
}
}
8 changes: 0 additions & 8 deletions x-pack/plugins/apm/server/lib/services/get_services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@
*/

import { Logger } from '@kbn/logging';
import { isEmpty } from 'lodash';
import { withApmSpan } from '../../../utils/with_apm_span';
import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { getLegacyDataStatus } from './get_legacy_data_status';
import { getServicesItems } from './get_services_items';
import { hasHistoricalAgentData } from './has_historical_agent_data';

export async function getServices({
environment,
Expand All @@ -38,14 +36,8 @@ export async function getServices({
getLegacyDataStatus(setup),
]);

const noDataInCurrentTimeRange = isEmpty(items);
const hasHistoricalData = noDataInCurrentTimeRange
? await hasHistoricalAgentData(setup)
: true;

return {
items,
hasHistoricalData,
hasLegacyData,
};
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { sourceMapsRouteRepository } from './source_maps';
import { traceRouteRepository } from './traces';
import { transactionRouteRepository } from './transactions';
import { APMRouteHandlerResources } from './typings';
import { historicalDataRouteRepository } from './historical_data';

const getTypedGlobalApmServerRouteRepository = () => {
const repository = createApmServerRouteRepository()
Expand All @@ -56,7 +57,8 @@ const getTypedGlobalApmServerRouteRepository = () => {
.merge(sourceMapsRouteRepository)
.merge(apmFleetRouteRepository)
.merge(backendsRouteRepository)
.merge(fallbackToTransactionsRouteRepository);
.merge(fallbackToTransactionsRouteRepository)
.merge(historicalDataRouteRepository);

return repository;
};
Expand Down
25 changes: 25 additions & 0 deletions x-pack/plugins/apm/server/routes/historical_data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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 { setupRequest } from '../lib/helpers/setup_request';
import { hasHistoricalAgentData } from '../lib/services/get_services/has_historical_agent_data';
import { createApmServerRoute } from './create_apm_server_route';
import { createApmServerRouteRepository } from './create_apm_server_route_repository';

const hasDataRoute = createApmServerRoute({
endpoint: 'GET /api/apm/has_data',
options: { tags: ['access:apm'] },
handler: async (resources) => {
const setup = await setupRequest(resources);
const hasData = await hasHistoricalAgentData(setup);
return { hasData };
},
});

export const historicalDataRouteRepository = createApmServerRouteRepository().add(
hasDataRoute
);
41 changes: 41 additions & 0 deletions x-pack/test/apm_api_integration/tests/historical_data/has_data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../common/ftr_provider_context';
import { registry } from '../../common/registry';

export default function ApiTest({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const archiveName = 'apm_8.0.0';

registry.when(
'Historical data when data is not loaded',
{ config: 'basic', archives: [] },
() => {
it('handles the empty state', async () => {
const response = await supertest.get(`/api/apm/has_data`);

expect(response.status).to.be(200);
expect(response.body.hasData).to.be(false);
});
}
);

registry.when(
'Historical data when data is loaded',
{ config: 'basic', archives: [archiveName] },
() => {
it('returns hasData: true', async () => {
const response = await supertest.get(`/api/apm/has_data`);

expect(response.status).to.be(200);
expect(response.body.hasData).to.be(true);
});
}
);
}
4 changes: 4 additions & 0 deletions x-pack/test/apm_api_integration/tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ export default function apmApiIntegrationTests(providerContext: FtrProviderConte
loadTestFile(require.resolve('./csm/web_core_vitals'));
});

describe('historical_data/has_data', function () {
loadTestFile(require.resolve('./historical_data/has_data'));
});

registry.run(providerContext);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export default function ApiTest({ getService }: FtrProviderContext) {
);

expect(response.status).to.be(200);
expect(response.body.hasHistoricalData).to.be(false);
expect(response.body.hasLegacyData).to.be(false);
expect(response.body.items.length).to.be(0);
});
Expand Down Expand Up @@ -64,10 +63,6 @@ export default function ApiTest({ getService }: FtrProviderContext) {
expect(response.status).to.eql(200);
});

it('returns hasHistoricalData: true', () => {
expect(response.body.hasHistoricalData).to.be(true);
});

it('returns hasLegacyData: false', () => {
expect(response.body.hasLegacyData).to.be(false);
});
Expand Down