[UX] Migrate visitor breakdown chart to lens embeddable (#134684)

* Migrate visitor breakdown chart to lens embeddable

* Remove visitor breakdown from APM

Co-authored-by: shahzad31 <shahzad.muhammad@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Emilio Alvarez Piñeiro 2022-06-27 17:57:52 +02:00 committed by GitHub
parent a7032ceebc
commit 56512e1d41
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 606 additions and 241 deletions

View file

@ -1,103 +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 { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
import { SetupUX } from './route';
import {
USER_AGENT_NAME,
USER_AGENT_OS,
} from '../../../common/elasticsearch_fieldnames';
export async function getVisitorBreakdown({
setup,
urlQuery,
start,
end,
}: {
setup: SetupUX;
urlQuery?: string;
start: number;
end: number;
}) {
const projection = getRumPageLoadTransactionsProjection({
setup,
urlQuery,
start,
end,
});
const params = mergeProjection(projection, {
body: {
size: 0,
track_total_hits: true,
query: {
bool: projection.body.query.bool,
},
aggs: {
browsers: {
terms: {
field: USER_AGENT_NAME,
size: 9,
},
},
os: {
terms: {
field: USER_AGENT_OS,
size: 9,
},
},
},
},
});
const { apmEventClient } = setup;
const response = await apmEventClient.search('get_visitor_breakdown', params);
const { browsers, os } = response.aggregations!;
const totalItems = response.hits.total.value;
const browserTotal = browsers.buckets.reduce(
(prevVal, item) => prevVal + item.doc_count,
0
);
const osTotal = os.buckets.reduce(
(prevVal, item) => prevVal + item.doc_count,
0
);
const browserItems = browsers.buckets.map((bucket) => ({
count: bucket.doc_count,
name: bucket.key as string,
}));
if (totalItems > 0) {
browserItems.push({
count: totalItems - browserTotal,
name: 'Others',
});
}
const osItems = os.buckets.map((bucket) => ({
count: bucket.doc_count,
name: bucket.key as string,
}));
if (totalItems > 0) {
osItems.push({
count: totalItems - osTotal,
name: 'Others',
});
}
return {
os: osItems,
browsers: browserItems,
};
}

View file

@ -10,7 +10,6 @@ import { setupRequest, Setup } from '../../lib/helpers/setup_request';
import { getPageLoadDistribution } from './get_page_load_distribution';
import { getPageViewTrends } from './get_page_view_trends';
import { getPageLoadDistBreakdown } from './get_pl_dist_breakdown';
import { getVisitorBreakdown } from './get_visitor_breakdown';
import { createApmServerRoute } from '../apm_routes/create_apm_server_route';
import { rangeRt } from '../default_api_types';
import { APMRouteHandlerResources } from '../typings';
@ -152,33 +151,6 @@ const rumPageViewsTrendRoute = createApmServerRoute({
},
});
const rumVisitorsBreakdownRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/ux/visitor-breakdown',
params: t.type({
query: uxQueryRt,
}),
options: { tags: ['access:apm'] },
handler: async (
resources
): Promise<{
os: Array<{ count: number; name: string }>;
browsers: Array<{ count: number; name: string }>;
}> => {
const setup = await setupUXRequest(resources);
const {
query: { urlQuery, start, end },
} = resources.params;
return getVisitorBreakdown({
setup,
urlQuery,
start,
end,
});
},
});
function decodeUiFilters(
logger: Logger,
uiFiltersEncoded?: string
@ -212,5 +184,4 @@ export const rumRouteRepository = {
...rumPageLoadDistributionRoute,
...rumPageLoadDistBreakdownRoute,
...rumPageViewsTrendRoute,
...rumVisitorsBreakdownRoute,
};

View file

@ -10,3 +10,4 @@ export * from './url_ux_query.journey';
export * from './ux_js_errors.journey';
export * from './ux_client_metrics.journey';
export * from './ux_long_task_metric_journey';
export * from './ux_visitor_breakdown.journey';

View file

@ -75,3 +75,7 @@ export const getQuerystring = (params: object) => {
export const delay = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));
export const byLensTestId = (id: string) => `[data-test-embeddable-id="${id}"]`;
export const byLensDataLayerId = (id: string) =>
`[data-ech-series-name="${id}"]`;

View file

@ -0,0 +1,58 @@
/*
* 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 { journey, step, before } from '@elastic/synthetics';
import { UXDashboardDatePicker } from '../page_objects/date_picker';
import { byLensTestId, loginToKibana, waitForLoadingToFinish } from './utils';
const osNameMetric = 'ux-visitor-breakdown-user_agent-os-name';
const uaNameMetric = 'ux-visitor-breakdown-user_agent-name';
const chartIds = [osNameMetric, uaNameMetric];
journey('UX Visitor Breakdown', async ({ page, params }) => {
before(async () => {
await waitForLoadingToFinish({ page });
});
const queryParams = {
percentile: '50',
rangeFrom: '2020-05-18T11:51:00.000Z',
rangeTo: '2021-10-30T06:37:15.536Z',
};
const queryString = new URLSearchParams(queryParams).toString();
const baseUrl = `${params.kibanaUrl}/app/ux`;
step('Go to UX Dashboard', async () => {
await page.goto(`${baseUrl}?${queryString}`, {
waitUntil: 'networkidle',
});
await loginToKibana({
page,
user: { username: 'elastic', password: 'changeme' },
});
});
step('Set date range', async () => {
const datePickerPage = new UXDashboardDatePicker(page);
await datePickerPage.setDefaultE2eRange();
});
step('Confirm charts are visible', async () => {
// Wait until chart data is loaded
await page.waitForLoadState('networkidle');
await Promise.all(
chartIds.map(
async (dataTestId) =>
// lens embeddable injects its own test attribute
await page.waitForSelector(byLensTestId(dataTestId))
)
);
});
});

View file

@ -21,7 +21,8 @@
"alerts",
"observability",
"security",
"maps"
"maps",
"lens"
],
"server": false,
"ui": true,

View file

@ -106,7 +106,15 @@ export function UXAppRoot({
appMountParameters,
core,
deps,
corePlugins: { embeddable, inspector, maps, observability, data, dataViews },
corePlugins: {
embeddable,
inspector,
maps,
observability,
data,
dataViews,
lens,
},
}: {
appMountParameters: AppMountParameters;
core: CoreStart;
@ -131,6 +139,7 @@ export function UXAppRoot({
embeddable,
data,
dataViews,
lens,
}}
>
<i18nCore.Context>

View file

@ -0,0 +1,124 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VisitorBreakdownChart getVisitorBreakdownLensAttributes generates expected lens attributes 1`] = `
Object {
"references": Array [
Object {
"id": "Required",
"name": "indexpattern-datasource-current-indexpattern",
"type": "index-pattern",
},
Object {
"id": "Required",
"name": "indexpattern-datasource-layer-layer1",
"type": "index-pattern",
},
],
"state": Object {
"datasourceStates": Object {
"indexpattern": Object {
"layers": Object {
"layer1": Object {
"columnOrder": Array [
"col1",
"col2",
],
"columns": Object {
"col1": Object {
"dataType": "string",
"isBucketed": true,
"label": "Top 9 values of user_agent.os.name",
"operationType": "terms",
"params": Object {
"orderBy": Object {
"columnId": "col2",
"type": "column",
},
"orderDirection": "desc",
"otherBucket": true,
"parentFormat": Object {
"id": "terms",
},
"size": 9,
},
"scale": "ordinal",
"sourceField": "user_agent.os.name",
},
"col2": Object {
"dataType": "number",
"isBucketed": false,
"label": "Count of records",
"operationType": "count",
"params": Object {
"emptyAsNull": true,
},
"scale": "ratio",
"sourceField": "___records___",
},
},
"incompleteColumns": Object {},
},
},
},
},
"filters": Array [
Object {
"meta": Object {},
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"transaction.type": "page-load",
},
},
Object {
"terms": Object {
"processor.event": Array [
"transaction",
],
},
},
Object {
"exists": Object {
"field": "transaction.marks.navigationTiming.fetchStart",
},
},
Object {
"wildcard": Object {
"url.full": "*elastic.co*",
},
},
],
"must_not": Array [],
},
},
},
],
"query": Object {
"language": "kuery",
"query": "",
},
"visualization": Object {
"layers": Array [
Object {
"categoryDisplay": "default",
"groups": Array [
"col1",
],
"layerId": "layer1",
"layerType": "data",
"legendDisplay": "hide",
"metric": "col2",
"nestedLegend": false,
"numberDisplay": "percent",
"showValuesInLegend": true,
},
],
"shape": "pie",
},
},
"title": "ux-visitor-breakdown-user_agent.os.name",
"visualizationType": "lnsPie",
}
`;

View file

@ -0,0 +1,74 @@
/*
* 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 React from 'react';
import { render } from '@testing-library/react';
import {
getVisitorBreakdownLensAttributes,
VisitorBreakdownChart,
VisitorBreakdownMetric,
} from './visitor_breakdown_chart';
import { useKibanaServices } from '../../../../hooks/use_kibana_services';
jest.mock('../../../../hooks/use_kibana_services');
describe('VisitorBreakdownChart', () => {
describe('getVisitorBreakdownLensAttributes', () => {
test('generates expected lens attributes', () => {
const props = {
metric: VisitorBreakdownMetric.OS_BREAKDOWN,
uiFilters: {
environment: 'ENVIRONMENT_ALL',
},
urlQuery: 'elastic.co',
dataView: 'Required',
};
expect(getVisitorBreakdownLensAttributes(props)).toMatchSnapshot();
});
});
describe('component', () => {
const mockEmbeddableComponent = jest.fn((_) => <></>);
beforeEach(() => {
jest.clearAllMocks();
(useKibanaServices as jest.Mock).mockReturnValue({
lens: {
EmbeddableComponent: mockEmbeddableComponent,
},
});
});
test('calls lens with original attributes', () => {
const props = {
start: '0',
end: '5000',
metric: VisitorBreakdownMetric.OS_BREAKDOWN,
uiFilters: {
environment: 'ENVIRONMENT_ALL',
},
urlQuery: 'elastic.co',
dataView: 'Required',
onFilter: (_m: VisitorBreakdownMetric, _e: any) => {},
};
const { container: _ } = render(<VisitorBreakdownChart {...props} />);
expect(mockEmbeddableComponent).toHaveBeenCalledTimes(1);
expect(mockEmbeddableComponent.mock.calls[0][0]).toEqual(
expect.objectContaining({
timeRange: {
from: props.start,
to: props.end,
},
attributes: getVisitorBreakdownLensAttributes(props),
})
);
});
});
});

View file

@ -5,90 +5,216 @@
* 2.0.
*/
import React from 'react';
import React, { useCallback, useMemo } from 'react';
import { ViewMode } from '@kbn/embeddable-plugin/public';
import {
Chart,
DARK_THEME,
Datum,
LIGHT_THEME,
PartialTheme,
Partition,
PartitionLayout,
Settings,
} from '@elastic/charts';
import styled from 'styled-components';
CountIndexPatternColumn,
PersistedIndexPatternLayer,
PieVisualizationState,
TermsIndexPatternColumn,
TypedLensByValueInput,
} from '@kbn/lens-plugin/public';
import { EuiText } from '@elastic/eui';
import { TRANSACTION_PAGE_LOAD } from '../../../../../common/transaction_types';
import {
EUI_CHARTS_THEME_DARK,
EUI_CHARTS_THEME_LIGHT,
} from '@elastic/eui/dist/eui_charts_theme';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { ChartWrapper } from '../chart_wrapper';
import { I18LABELS } from '../translations';
PROCESSOR_EVENT,
TRANSACTION_TYPE,
} from '../../../../../common/elasticsearch_fieldnames';
import { getEsFilter } from '../../../../services/data/get_es_filter';
import { useKibanaServices } from '../../../../hooks/use_kibana_services';
import type { UxUIFilters } from '../../../../../typings/ui_filters';
import { ProcessorEvent } from '../../../../../common/processor_event';
const StyleChart = styled.div`
height: 100%;
`;
const BUCKET_SIZE = 9;
interface Props {
options?: Array<{
count: number;
name: string;
}>;
loading: boolean;
export enum VisitorBreakdownMetric {
OS_BREAKDOWN = 'user_agent.os.name',
UA_BREAKDOWN = 'user_agent.name',
}
const theme: PartialTheme = {
chartMargins: { top: 0, bottom: 0, left: 0, right: 0 },
legend: {
verticalWidth: 100,
},
partition: {
linkLabel: { maximumSection: Infinity, maxCount: 0 },
outerSizeRatio: 1, // - 0.5 * Math.random(),
circlePadding: 4,
},
};
interface LensAttributes {
metric: VisitorBreakdownMetric;
uiFilters: UxUIFilters;
urlQuery?: string;
dataView: string;
}
export function VisitorBreakdownChart({ loading, options }: Props) {
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
type Props = {
start: string;
end: string;
onFilter: (metric: VisitorBreakdownMetric, event: any) => void;
} & LensAttributes;
const euiChartTheme = darkMode
? EUI_CHARTS_THEME_DARK
: EUI_CHARTS_THEME_LIGHT;
export function VisitorBreakdownChart({
start,
end,
onFilter,
uiFilters,
urlQuery,
metric,
dataView,
}: Props) {
const kibana = useKibanaServices();
const LensEmbeddableComponent = kibana.lens.EmbeddableComponent;
const lensAttributes = useMemo(
() =>
getVisitorBreakdownLensAttributes({
uiFilters,
urlQuery,
metric,
dataView,
}),
[uiFilters, urlQuery, metric, dataView]
);
const filterHandler = useCallback(
(event) => {
onFilter(metric, event);
},
[onFilter, metric]
);
if (!LensEmbeddableComponent) {
return <EuiText>No lens component</EuiText>;
}
return (
<ChartWrapper loading={loading} height="245px" maxWidth="430px">
<StyleChart>
<Chart>
<Settings
showLegend
baseTheme={darkMode ? DARK_THEME : LIGHT_THEME}
theme={theme}
/>
<Partition
id="spec_1"
data={
options?.length ? options : [{ count: 1, name: I18LABELS.noData }]
}
layout={PartitionLayout.sunburst}
clockwiseSectors={false}
valueAccessor={(d: Datum) => d.count as number}
valueGetter="percent"
percentFormatter={(d: number) =>
`${Math.round((d + Number.EPSILON) * 100) / 100}%`
}
layers={[
{
groupByRollup: (d: Datum) => d.name,
shape: {
fillColor: (d) =>
euiChartTheme.theme.colors?.vizColors?.[d.sortIndex]!,
},
},
]}
/>
</Chart>
</StyleChart>
</ChartWrapper>
<LensEmbeddableComponent
id={`ux-visitor-breakdown-${metric.replaceAll('.', '-')}`}
hidePanelTitles
withDefaultActions
style={{ minHeight: '250px', height: '100%' }}
attributes={lensAttributes}
timeRange={{
from: start ?? '',
to: end ?? '',
}}
viewMode={ViewMode.VIEW}
onFilter={filterHandler}
/>
);
}
const visConfig: PieVisualizationState = {
layers: [
{
layerId: 'layer1',
groups: ['col1'],
metric: 'col2',
categoryDisplay: 'default',
legendDisplay: 'hide',
numberDisplay: 'percent',
showValuesInLegend: true,
nestedLegend: false,
layerType: 'data',
},
],
shape: 'pie',
};
export function getVisitorBreakdownLensAttributes({
uiFilters,
urlQuery,
metric,
dataView,
}: LensAttributes): TypedLensByValueInput['attributes'] {
const dataLayer: PersistedIndexPatternLayer = {
incompleteColumns: {},
columnOrder: ['col1', 'col2'],
columns: {
col1: {
label: `Top ${BUCKET_SIZE} values of ${metric}`,
dataType: 'string',
operationType: 'terms',
scale: 'ordinal',
sourceField: metric,
isBucketed: true,
params: {
size: BUCKET_SIZE,
orderBy: {
type: 'column',
columnId: 'col2',
},
orderDirection: 'desc',
otherBucket: true,
parentFormat: {
id: 'terms',
},
},
} as TermsIndexPatternColumn,
col2: {
label: 'Count of records',
dataType: 'number',
operationType: 'count',
isBucketed: false,
scale: 'ratio',
sourceField: '___records___',
params: {
emptyAsNull: true,
},
} as CountIndexPatternColumn,
},
};
return {
visualizationType: 'lnsPie',
title: `ux-visitor-breakdown-${metric}`,
references: [
{
id: dataView,
name: 'indexpattern-datasource-current-indexpattern',
type: 'index-pattern',
},
{
id: dataView,
name: 'indexpattern-datasource-layer-layer1',
type: 'index-pattern',
},
],
state: {
datasourceStates: {
indexpattern: {
layers: {
layer1: dataLayer,
},
},
},
filters: [
{
meta: {},
query: {
bool: {
filter: [
{ term: { [TRANSACTION_TYPE]: TRANSACTION_PAGE_LOAD } },
{
terms: {
[PROCESSOR_EVENT]: [ProcessorEvent.transaction],
},
},
{
exists: {
field: 'transaction.marks.navigationTiming.fetchStart',
},
},
...getEsFilter(uiFilters),
...(urlQuery
? [
{
wildcard: {
'url.full': `*${urlQuery}*`,
},
},
]
: []),
],
must_not: [...getEsFilter(uiFilters, true)],
},
},
},
],
query: { language: 'kuery', query: '' },
visualization: visConfig,
},
};
}

View file

@ -5,39 +5,83 @@
* 2.0.
*/
import React from 'react';
import React, { useCallback } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSpacer } from '@elastic/eui';
import { VisitorBreakdownChart } from '../charts/visitor_breakdown_chart';
import { EuiLoadingChart } from '@elastic/eui';
import styled from 'styled-components';
import {
UxLocalUIFilterName,
uxLocalUIFilterNames,
} from '../../../../../common/ux_ui_filter';
import {
VisitorBreakdownChart,
VisitorBreakdownMetric,
} from '../charts/visitor_breakdown_chart';
import { I18LABELS, VisitorBreakdownLabel } from '../translations';
import { useFetcher } from '../../../../hooks/use_fetcher';
import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params';
import { useStaticDataView } from '../../../../hooks/use_static_data_view';
import { useLocalUIFilters } from '../hooks/use_local_uifilters';
import { getExcludedName } from '../local_uifilters';
type VisitorBreakdownFieldMap = Record<
VisitorBreakdownMetric,
UxLocalUIFilterName
>;
const visitorBreakdownFieldMap: VisitorBreakdownFieldMap = {
[VisitorBreakdownMetric.OS_BREAKDOWN]: 'os',
[VisitorBreakdownMetric.UA_BREAKDOWN]: 'browser',
};
const EuiLoadingEmbeddable = styled(EuiFlexGroup)`
& {
min-height: 100%;
min-width: 100%;
}
`;
const getInvertedFilterName = (filter: UxLocalUIFilterName, negate: boolean) =>
negate ? filter : getExcludedName(filter);
export function VisitorBreakdown() {
const { rangeId, urlParams, uxUiFilters } = useLegacyUrlParams();
const { urlParams, uxUiFilters } = useLegacyUrlParams();
const { start, end, searchTerm } = urlParams;
// static dataView is required for lens
const { dataView, loading } = useStaticDataView();
const { data, status } = useFetcher(
(callApmApi) => {
const { serviceName } = uxUiFilters;
const { filters, setFilterValue } = useLocalUIFilters({
filterNames: uxLocalUIFilterNames.filter((name) =>
['browser', 'browserExcluded', 'os', 'osExcluded'].includes(name)
),
});
if (start && end && serviceName) {
return callApmApi('GET /internal/apm/ux/visitor-breakdown', {
params: {
query: {
start,
end,
uiFilters: JSON.stringify(uxUiFilters),
urlQuery: searchTerm,
},
},
});
const onFilter = useCallback(
(metric: VisitorBreakdownMetric, event: any) => {
if (!visitorBreakdownFieldMap[metric]) {
return;
}
return Promise.resolve(null);
const filterValues = event?.data?.map((fdata: any) => fdata.value);
const invertedField = getInvertedFilterName(
visitorBreakdownFieldMap[metric],
event?.negate ?? false
);
const invertedFieldValues =
filters?.find((filter) => filter.name === invertedField)?.value ?? [];
setFilterValue(
invertedField,
invertedFieldValues.filter((value) => !filterValues.includes(value))
);
setFilterValue(
event?.negate
? getExcludedName(visitorBreakdownFieldMap[metric])
: visitorBreakdownFieldMap[metric],
filterValues
);
},
// `rangeId` acts as a cache buster for stable ranges like "Today"
// eslint-disable-next-line react-hooks/exhaustive-deps
[end, start, uxUiFilters, searchTerm, rangeId]
[filters, setFilterValue]
);
return (
@ -52,20 +96,52 @@ export function VisitorBreakdown() {
<h4>{I18LABELS.browser}</h4>
</EuiTitle>
<EuiSpacer size="s" />
<VisitorBreakdownChart
options={data?.browsers}
loading={status !== 'success'}
/>
{!!loading ? (
<EuiLoadingEmbeddable
justifyContent="spaceAround"
alignItems={'center'}
>
<EuiFlexItem grow={false}>
<EuiLoadingChart size="l" mono />
</EuiFlexItem>
</EuiLoadingEmbeddable>
) : (
<VisitorBreakdownChart
dataView={dataView?.id ?? ''}
start={start ?? ''}
end={end ?? ''}
uiFilters={uxUiFilters}
urlQuery={searchTerm}
metric={VisitorBreakdownMetric.UA_BREAKDOWN}
onFilter={onFilter}
/>
)}
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="xs">
<h4>{I18LABELS.operatingSystem}</h4>
</EuiTitle>
<EuiSpacer size="s" />
<VisitorBreakdownChart
options={data?.os}
loading={status !== 'success'}
/>
{!!loading ? (
<EuiLoadingEmbeddable
justifyContent="spaceAround"
alignItems={'center'}
>
<EuiFlexItem grow={false}>
<EuiLoadingChart size="l" mono />
</EuiFlexItem>
</EuiLoadingEmbeddable>
) : (
<VisitorBreakdownChart
dataView={dataView?.id ?? ''}
start={start ?? ''}
end={end ?? ''}
uiFilters={uxUiFilters}
urlQuery={searchTerm}
metric={VisitorBreakdownMetric.OS_BREAKDOWN}
onFilter={onFilter}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
</>

View file

@ -0,0 +1,22 @@
/*
* 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 { useFetcher } from '@kbn/observability-plugin/public';
import { useKibanaServices } from './use_kibana_services';
export function useStaticDataView() {
const { observability } = useKibanaServices();
const { data, loading } = useFetcher(async () => {
return observability.getAppDataView('ux');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return {
dataView: data ?? undefined,
loading,
};
}

View file

@ -33,6 +33,7 @@ import { EmbeddableStart } from '@kbn/embeddable-plugin/public';
import { MapsStartApi } from '@kbn/maps-plugin/public';
import { Start as InspectorPluginStart } from '@kbn/inspector-plugin/public';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { LensPublicStart } from '@kbn/lens-plugin/public';
export type UxPluginSetup = void;
export type UxPluginStart = void;
@ -54,6 +55,7 @@ export interface ApmPluginStartDeps {
inspector: InspectorPluginStart;
observability: ObservabilityPublicStart;
dataViews: DataViewsPublicPluginStart;
lens: LensPublicStart;
}
async function getDataStartPlugin(core: CoreSetup) {
@ -66,7 +68,6 @@ export class UxPlugin implements Plugin<UxPluginSetup, UxPluginStart> {
public setup(core: CoreSetup, plugins: ApmPluginSetupDeps) {
const pluginSetupDeps = plugins;
if (plugins.observability) {
const getUxDataHelper = async () => {
const { fetchUxOverviewDate, hasRumData, createCallApmApi } =

View file

@ -27,5 +27,6 @@
{ "path": "../licensing/tsconfig.json" },
{ "path": "../maps/tsconfig.json" },
{ "path": "../observability/tsconfig.json" },
{ "path": "../lens/tsconfig.json" },
]
}