Skip to content
Merged
1 change: 0 additions & 1 deletion x-pack/plugins/apm/common/service_groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export interface ServiceGroup {
groupName: string;
kuery: string;
description?: string;
serviceNames: string[];
color?: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import datemath from '@kbn/datemath';
import { EuiModal } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useHistory } from 'react-router-dom';
Expand Down Expand Up @@ -60,14 +59,9 @@ export function SaveGroupModal({ onClose, savedServiceGroup }: Props) {
async function (newServiceGroup: StagedServiceGroup) {
setIsLoading(true);
try {
const start = datemath.parse('now-24h')?.toISOString();
const end = datemath.parse('now', { roundUp: true })?.toISOString();
if (!start || !end) {
throw new Error('Unable to determine start/end time range.');
}
await callApmApi('POST /internal/apm/service-group', {
params: {
query: { start, end, serviceGroupId: savedServiceGroup?.id },
query: { serviceGroupId: savedServiceGroup?.id },
body: {
groupName: newServiceGroup.groupName,
kuery: newServiceGroup.kuery,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ const MAX_CONTAINER_HEIGHT = 600;
const MODAL_HEADER_HEIGHT = 122;
const MODAL_FOOTER_HEIGHT = 80;

const suggestedFieldsWhitelist = [
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use allowlist here?

'agent.name',
'service.name',
'service.language.name',
'service.environment',
];

const Container = styled.div`
width: 600px;
height: ${MAX_CONTAINER_HEIGHT}px;
Expand Down Expand Up @@ -144,6 +151,21 @@ export function SelectServices({
setStagedKuery(value);
}}
value={kuery}
suggestionFilter={(querySuggestion) => {
if ('field' in querySuggestion) {
const {
field: {
spec: { name: fieldName },
},
} = querySuggestion;
Comment on lines +156 to +160
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this is 100% personal preference but my brain always struggle a bit with nested destructurings. Feel free to leave as-is if you prefer that.

Suggested change
const {
field: {
spec: { name: fieldName },
},
} = querySuggestion;
const fieldName = querySuggestion.field.spec.name;


return (
fieldName.startsWith('label') ||
suggestedFieldsWhitelist.includes(fieldName)
);
}
return true;
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { isEmpty, sortBy } from 'lodash';
import React, { useState, useCallback } from 'react';
import React, { useState, useCallback, useMemo } from 'react';
import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher';
import { ServiceGroupsListItems } from './service_groups_list';
import { Sort } from './sort';
import { RefreshServiceGroupsSubscriber } from '../refresh_service_groups_subscriber';
import { getDateRange } from '../../../../context/url_params_context/helpers';

export type ServiceGroupsSortType = 'recently_added' | 'alphabetical';

Expand All @@ -38,6 +39,31 @@ export function ServiceGroupsList() {
[]
);

const { start, end } = useMemo(
() =>
getDateRange({
rangeFrom: 'now-24h',
rangeTo: 'now',
}),
[]
);
Comment on lines +42 to +49
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is on the service group overview page (aka the page listing all the service groups), right?

@gbamparop and I talked about that we didn't currently have a timepicker, and we didn't see any need to add any. It also means that the lookback time-period for calculating the service counts is hardcoded to 24 hours.

I think this is a fine compromise between simplicity and functionality. But we should remember to flag this to product (and possible docs to document it).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, this is on the service group overview page.


const { data: servicesCountData = { servicesCounts: {} } } = useFetcher(
(callApmApi) => {
if (start && end) {
return callApmApi('GET /internal/apm/service_groups/services_count', {
params: {
query: {
start,
end,
},
},
});
}
},
[start, end]
);

const { serviceGroups } = data;

const isLoading =
Expand Down Expand Up @@ -133,7 +159,11 @@ export function ServiceGroupsList() {
</EuiFlexItem>
<EuiFlexItem>
{items.length ? (
<ServiceGroupsListItems items={items} isLoading={isLoading} />
<ServiceGroupsListItems
items={items}
servicesCounts={servicesCountData.servicesCounts}
isLoading={isLoading}
/>
) : (
<EuiEmptyPrompt
iconType="layers"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface Props {
onClick?: () => void;
href?: string;
withTour?: boolean;
servicesCount?: number;
}

export function ServiceGroupsCard({
Expand All @@ -35,6 +36,7 @@ export function ServiceGroupsCard({
onClick,
href,
withTour,
servicesCount,
}: Props) {
const { tourEnabled, dismissTour } = useServiceGroupsTour('serviceGroupCard');

Expand Down Expand Up @@ -62,13 +64,17 @@ export function ServiceGroupsCard({
{!hideServiceCount && (
<EuiFlexItem>
<EuiText size="s">
{i18n.translate(
'xpack.apm.serviceGroups.cardsList.serviceCount',
{
defaultMessage:
'{servicesCount} {servicesCount, plural, one {service} other {services}}',
values: { servicesCount: serviceGroup.serviceNames.length },
}
{servicesCount === undefined ? (
<>&nbsp;</>
) : (
i18n.translate(
'xpack.apm.serviceGroups.cardsList.serviceCount',
{
defaultMessage:
'{servicesCount} {servicesCount, plural, one {service} other {services}}',
values: { servicesCount },
}
)
)}
</EuiText>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import { useDefaultEnvironment } from '../../../../hooks/use_default_environment

interface Props {
items: SavedServiceGroup[];
servicesCounts: Record<string, number>;
isLoading: boolean;
}

export function ServiceGroupsListItems({ items }: Props) {
export function ServiceGroupsListItems({ items, servicesCounts }: Props) {
const router = useApmRouter();
const { query } = useApmParams('/service-groups');

Expand All @@ -30,6 +31,7 @@ export function ServiceGroupsListItems({ items }: Props) {
{items.map((item) => (
<ServiceGroupsCard
serviceGroup={item}
servicesCount={servicesCounts[item.id]}
href={router.link('/services', {
query: {
...query,
Expand All @@ -52,7 +54,6 @@ export function ServiceGroupsListItems({ items }: Props) {
'xpack.apm.serviceGroups.list.allServices.description',
{ defaultMessage: 'View all services' }
),
serviceNames: [],
color: SERVICE_GROUP_COLOR_DEFAULT,
}}
hideServiceCount
Expand Down
16 changes: 12 additions & 4 deletions x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function KueryBar(props: {
onSubmit?: (value: string) => void;
onChange?: (value: string) => void;
value?: string;
suggestionFilter?: (querySuggestion: QuerySuggestion) => boolean;
}) {
const { path, query } = useApmParams('/*');

Expand Down Expand Up @@ -102,7 +103,7 @@ export function KueryBar(props: {
currentRequestCheck = currentRequest;

try {
const suggestions = (
const suggestions =
(await unifiedSearch.autocomplete.getQuerySuggestions({
language: 'kuery',
indexPatterns: [dataView],
Expand All @@ -120,14 +121,21 @@ export function KueryBar(props: {
selectionEnd: selectionStart,
useTimeRange: true,
method: 'terms_agg',
})) || []
).slice(0, 15);
})) || [];

const filteredSuggestions = props.suggestionFilter
? suggestions.filter(props.suggestionFilter)
: suggestions;

if (currentRequest !== currentRequestCheck) {
return;
}

setState({ ...state, suggestions, isLoadingSuggestions: false });
setState({
...state,
suggestions: filteredSuggestions.slice(0, 15),
isLoadingSuggestions: false,
});
} catch (e) {
console.error('Error while fetching suggestions', e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,11 @@
*/

import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { SERVICE_NAME } from '../elasticsearch_fieldnames';
import { ServiceGroup } from '../service_groups';
import { kqlQuery } from '@kbn/observability-plugin/server';
import { ServiceGroup } from '../../common/service_groups';

export function serviceGroupQuery(
serviceGroup?: ServiceGroup | null
): QueryDslQueryContainer[] {
if (!serviceGroup) {
return [];
}

return serviceGroup?.serviceNames
? [{ terms: { [SERVICE_NAME]: serviceGroup.serviceNames } }]
: [];
return serviceGroup ? kqlQuery(serviceGroup?.kuery) : [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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 { ProcessorEvent } from '@kbn/observability-plugin/common';
import { rangeQuery, kqlQuery } from '@kbn/observability-plugin/server';
import { Setup } from '../../lib/helpers/setup_request';
import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames';

export async function getServicesCounts({
setup,
kuery,
maxNumberOfServices,
start,
end,
}: {
setup: Setup;
kuery: string;
maxNumberOfServices: number;
Copy link
Copy Markdown
Contributor

@sorenlouv sorenlouv Sep 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is maxNumberOfServices unused here?

Copy link
Copy Markdown
Contributor Author

@gbamparop gbamparop Sep 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is unused, we can add a size (service inventory and service map already have max size specified).

start: number;
end: number;
}) {
const { apmEventClient } = setup;

const response = await apmEventClient.search('get_services_count', {
apm: {
events: [
ProcessorEvent.metric,
ProcessorEvent.transaction,
ProcessorEvent.span,
ProcessorEvent.error,
],
},
body: {
size: 0,
query: {
bool: {
filter: [...rangeQuery(start, end), ...kqlQuery(kuery)],
},
},
aggs: {
services_count: {
cardinality: {
field: SERVICE_NAME,
},
},
},
},
});

return response?.aggregations?.services_count.value ?? 0;
}
Loading