Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breakdowns: Remove service_name requirement #801

Merged
merged 22 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2157dff
chore: wip
gtk-grafana Sep 23, 2024
9005ea7
chore: fix types
gtk-grafana Sep 23, 2024
6e6171a
chore: wip
gtk-grafana Sep 23, 2024
b248887
chore: update primary label in url
gtk-grafana Sep 23, 2024
f12ed7d
chore: wip, stuff is broken
gtk-grafana Sep 24, 2024
0443387
Merge remote-tracking branch 'origin/main' into gtk-grafana/service-n…
gtk-grafana Sep 25, 2024
d63c05f
chore: remove service_name everywhere besides service selection, fiel…
gtk-grafana Sep 25, 2024
0c8e41f
Merge remote-tracking branch 'origin/main' into gtk-grafana/service-n…
gtk-grafana Sep 25, 2024
e546ed3
chore: todos
gtk-grafana Sep 25, 2024
606c4df
chore: fix fields queries with metadata, clean up, fix fields count w…
gtk-grafana Sep 26, 2024
57ac417
chore: remove flag placeholder
gtk-grafana Sep 26, 2024
85a7266
chore: update unit test
gtk-grafana Sep 26, 2024
406bd21
Merge remote-tracking branch 'origin/main' into gtk-grafana/service-n…
gtk-grafana Sep 26, 2024
ffbe1c2
Merge remote-tracking branch 'origin/main' into gtk-grafana/service-n…
gtk-grafana Sep 26, 2024
9020084
chore: clean up, document
gtk-grafana Sep 26, 2024
7fd1ca0
chore: clean up
gtk-grafana Sep 26, 2024
195f4d2
Merge branch 'main' into gtk-grafana/service-not-required-poc
gtk-grafana Sep 30, 2024
7c056dc
Merge remote-tracking branch 'origin/main' into gtk-grafana/service-n…
gtk-grafana Sep 30, 2024
976b6ca
chore: add back in change that was overwritten by merge
gtk-grafana Sep 30, 2024
3226cc7
chore: assert url contains primary label name and value when calling …
gtk-grafana Oct 2, 2024
48798c8
chore: add more verbose logging for unexpected urls, remove bad error…
gtk-grafana Oct 2, 2024
629eaf2
chore: remove optional chaining operator
gtk-grafana Oct 3, 2024
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
6 changes: 3 additions & 3 deletions src/Components/IndexScene/IndexScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
SceneObjectUrlSyncConfig,
SceneObjectUrlValues,
SceneRefreshPicker,
SceneRouteMatch,
SceneTimePicker,
SceneTimeRange,
SceneVariableSet,
Expand Down Expand Up @@ -54,6 +53,7 @@ import {
getUrlParamNameForVariable,
} from '../../services/variableGetters';
import { ToolbarScene } from './ToolbarScene';
import { OptionalRouteMatch } from '../Pages';

export interface AppliedPattern {
pattern: string;
Expand All @@ -67,7 +67,7 @@ export interface IndexSceneState extends SceneObjectState {
body?: LayoutScene;
initialFilters?: AdHocVariableFilter[];
patterns?: AppliedPattern[];
routeMatch?: SceneRouteMatch<{ service?: string; label?: string }>;
routeMatch?: OptionalRouteMatch;
}

export class IndexScene extends SceneObjectBase<IndexSceneState> {
Expand Down Expand Up @@ -122,7 +122,7 @@ export class IndexScene extends SceneObjectBase<IndexSceneState> {
const stateUpdate: Partial<IndexSceneState> = {};

if (!this.state.contentScene) {
stateUpdate.contentScene = getContentScene(this.state.routeMatch?.params.label);
stateUpdate.contentScene = getContentScene(this.state.routeMatch?.params.breakdownLabel);
}

this.setState(stateUpdate);
Expand Down
37 changes: 24 additions & 13 deletions src/Components/Pages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import {
import {
CHILD_ROUTE_DEFINITIONS,
ChildDrilldownSlugs,
ValueSlugs,
DRILLDOWN_URL_KEYS,
extractLabelNameFromRoute,
extractServiceFromRoute,
extractValuesFromRoute,
PageSlugs,
ParentDrilldownSlugs,
PLUGIN_BASE_URL,
Expand All @@ -21,12 +19,20 @@ import {
ROUTES,
SERVICE_URL_KEYS,
SUB_ROUTES,
ValueSlugs,
} from '../services/routing';
import { PageLayoutType } from '@grafana/data';
import { IndexScene } from './IndexScene/IndexScene';
import { navigateToIndex } from '../services/navigate';
import { logger } from '../services/logger';

function getServicesScene(routeMatch?: SceneRouteMatch<{ service?: string; label?: string }>) {
export type RouteProps = { labelName: string; labelValue: string; breakdownLabel?: string };
export type RouteMatch = SceneRouteMatch<RouteProps>;
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
export type OptionalRouteProps = Optional<RouteProps, 'labelName' | 'labelValue'>;
export type OptionalRouteMatch = SceneRouteMatch<OptionalRouteProps>;

function getServicesScene(routeMatch: OptionalRouteMatch) {
const DEFAULT_TIME_RANGE = { from: 'now-15m', to: 'now' };
return new EmbeddedScene({
body: new IndexScene({
Expand Down Expand Up @@ -70,7 +76,7 @@ export function makeIndexPage() {
},
{
routePath: CHILD_ROUTE_DEFINITIONS.field,
getPage: (routeMatch, parent) => makeBreakdownValuePage(routeMatch, parent, ValueSlugs.field),
getPage: (routeMatch: RouteMatch, parent) => makeBreakdownValuePage(routeMatch, parent, ValueSlugs.field),
},
{
routePath: '*',
Expand Down Expand Up @@ -107,33 +113,38 @@ function makeEmptyScene(): (routeMatch: SceneRouteMatch) => EmbeddedScene {
}

export function makeBreakdownPage(
routeMatch: SceneRouteMatch<{ service: string; label?: string }>,
routeMatch: RouteMatch,
parent: SceneAppPageLike,
slug: ParentDrilldownSlugs
): SceneAppPage {
const { service } = extractServiceFromRoute(routeMatch);
const { labelName, labelValue } = extractValuesFromRoute(routeMatch);
return new SceneAppPage({
title: slugToBreadcrumbTitle(slug),
layout: PageLayoutType.Custom,
url: ROUTES[slug](service),
url: ROUTES[slug](labelValue, labelName),
preserveUrlKeys: DRILLDOWN_URL_KEYS,
getParentPage: () => parent,
getScene: (routeMatch) => getServicesScene(routeMatch),
});
}

export function makeBreakdownValuePage(
routeMatch: SceneRouteMatch<{ service: string; label: string }>,
routeMatch: RouteMatch,
parent: SceneAppPageLike,
slug: ChildDrilldownSlugs
): SceneAppPage {
const { service } = extractServiceFromRoute(routeMatch);
const { label } = extractLabelNameFromRoute(routeMatch);
const { labelName, labelValue, breakdownLabel } = extractValuesFromRoute(routeMatch);

if (!breakdownLabel) {
matyax marked this conversation as resolved.
Show resolved Hide resolved
const e = new Error('Breakdown value missing!');
logger.error(e, { labelName, labelValue, breakdownLabel: breakdownLabel ?? '' });
throw e;
}

return new SceneAppPage({
title: slugToBreadcrumbTitle(label),
title: slugToBreadcrumbTitle(breakdownLabel),
layout: PageLayoutType.Custom,
url: SUB_ROUTES[slug](service, label),
url: SUB_ROUTES[slug](labelValue, labelName, breakdownLabel),
preserveUrlKeys: DRILLDOWN_URL_KEYS,
getParentPage: () => parent,
getScene: (routeMatch) => getServicesScene(routeMatch),
Expand Down
13 changes: 2 additions & 11 deletions src/Components/ServiceScene/ActionBarScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ import { getExplorationFor } from '../../services/scenes';
import { getDrilldownSlug, getDrilldownValueSlug, PageSlugs, ValueSlugs } from '../../services/routing';
import { GoToExploreButton } from './GoToExploreButton';
import { reportAppInteraction, USER_EVENTS_ACTIONS, USER_EVENTS_PAGES } from '../../services/analytics';
import { SERVICE_NAME } from '../../services/variables';
import { navigateToDrilldownPage, navigateToIndex } from '../../services/navigate';
import { navigateToDrilldownPage } from '../../services/navigate';
import React from 'react';
import { ServiceScene, ServiceSceneState } from './ServiceScene';
import { GrafanaTheme2 } from '@grafana/data';
import { css } from '@emotion/css';
import { BreakdownViewDefinition, breakdownViewsDefinitions } from './BreakdownViews';
import { getLabelsVariable } from '../../services/variableGetters';

export interface ActionBarSceneState extends SceneObjectState {}

Expand Down Expand Up @@ -67,14 +65,7 @@ export class ActionBarScene extends SceneObjectBase<ActionBarSceneState> {
);

const serviceScene = sceneGraph.getAncestor(model, ServiceScene);
const variable = getLabelsVariable(serviceScene);
const service = variable.state.filters.find((f) => f.key === SERVICE_NAME);

if (service?.value) {
navigateToDrilldownPage(tab.value, serviceScene);
} else {
navigateToIndex();
}
navigateToDrilldownPage(tab.value, serviceScene);
}
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,8 @@ export class FieldsAggregatedBreakdownScene extends SceneObjectBase<FieldsAggreg
const activeLayoutChildren = activeLayout?.state.children as SceneCSSGridItem[] | undefined;
const activePanels = activeLayoutChildren?.filter((child) => !child.state.isHidden);

if (activePanels) {
const fieldsBreakdownScene = sceneGraph.getAncestor(this, FieldsBreakdownScene);
fieldsBreakdownScene.state.changeFieldCount?.(activePanels.length);
}
const fieldsBreakdownScene = sceneGraph.getAncestor(this, FieldsBreakdownScene);
fieldsBreakdownScene.state.changeFieldCount?.(activePanels?.length ?? 0);
}

public static Selector({ model }: SceneComponentProps<FieldsAggregatedBreakdownScene>) {
Expand Down
23 changes: 17 additions & 6 deletions src/Components/ServiceScene/Breakdowns/FieldsBreakdownScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import {
import { Alert, Button, useStyles2 } from '@grafana/ui';
import { reportAppInteraction, USER_EVENTS_ACTIONS, USER_EVENTS_PAGES } from 'services/analytics';
import { getSortByPreference } from 'services/store';
import { ALL_VARIABLE_VALUE, SERVICE_NAME, VAR_FIELD_GROUP_BY, VAR_LABELS } from 'services/variables';
import { ALL_VARIABLE_VALUE, SERVICE_NAME, VAR_FIELD_GROUP_BY, VAR_LABELS, VAR_SERVICE } from 'services/variables';
import { areArraysEqual } from '../../../services/comparison';
import { CustomConstantVariable, CustomConstantVariableState } from '../../../services/CustomConstantVariable';
import { navigateToValueBreakdown } from '../../../services/navigate';
import { ValueSlugs } from '../../../services/routing';
import { checkPrimaryLabel, getPrimaryLabelFromUrl, ValueSlugs } from '../../../services/routing';
import { DEFAULT_SORT_BY } from '../../../services/sorting';
import { GrotError } from '../../GrotError';
import { IndexScene } from '../../IndexScene/IndexScene';
Expand Down Expand Up @@ -108,10 +108,12 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt
this._subs.add(
getLabelsVariable(this).subscribeToState((newState, prevState) => {
const variable = getFieldGroupByVariable(this);
const newService = newState.filters.find((filter) => filter.key === SERVICE_NAME);
const prevService = prevState.filters.find((filter) => filter.key === SERVICE_NAME);
let { labelName } = getPrimaryLabelFromUrl();

// If the user changes the service
const newService = newState.filters.find((filter) => filter.key === labelName);
const prevService = prevState.filters.find((filter) => filter.key === labelName);

// If the user changes the primary label
if (variable.state.value === ALL_VARIABLE_VALUE && newService !== prevService) {
this.setState({
loading: true,
Expand All @@ -138,6 +140,8 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt
if (detectedFieldsFrame) {
this.updateOptions(detectedFieldsFrame);
}

checkPrimaryLabel(this);
}

private variableChanged = (newState: CustomConstantVariableState, oldState: CustomConstantVariableState) => {
Expand All @@ -159,6 +163,7 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt

let body;
if (variablesToClear.length > 1) {
this.state.changeFieldCount?.(0);
body = this.buildClearFiltersLayout(() => this.clearVariables(variablesToClear));
} else {
body = new EmptyLayoutScene({ type: 'fields' });
Expand Down Expand Up @@ -220,6 +225,7 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt
const variablesToClear = this.getVariablesThatCanBeCleared(indexScene);

if (variablesToClear.length > 1) {
this.state.changeFieldCount?.(0);
stateUpdate.body = this.buildClearFiltersLayout(() => this.clearVariables(variablesToClear));
} else {
stateUpdate.body = new EmptyLayoutScene({ type: 'fields' });
Expand Down Expand Up @@ -270,8 +276,13 @@ export class FieldsBreakdownScene extends SceneObjectBase<FieldsBreakdownSceneSt

variablesToClear.forEach((variable) => {
if (variable instanceof AdHocFiltersVariable && variable.state.key === 'adhoc_service_filter') {
let { labelName } = getPrimaryLabelFromUrl();
// getPrimaryLabelFromUrl returns the label name that exists in the URL, which is "service" not "service_name"
if (labelName === VAR_SERVICE) {
labelName = SERVICE_NAME;
}
variable.setState({
filters: variable.state.filters.filter((filter) => filter.key === SERVICE_NAME),
filters: variable.state.filters.filter((filter) => filter.key === labelName),
});
} else if (variable instanceof AdHocFiltersVariable) {
variable.setState({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('buildLabelsQuery', () => {

const result = buildLabelsQuery({} as SceneObject, VAR_LABEL_GROUP_BY_EXPR, 'cluster');
expect(result).toMatchObject({
expr: `sum(count_over_time({\${filters} ,cluster != ""} \${levels} \${patterns} \${lineFilter} [$__auto])) by (${VAR_LABEL_GROUP_BY_EXPR})`,
expr: `sum(count_over_time({\${filters} ,cluster != ""} \${levels} \${patterns} \${lineFilter} \${fields} [$__auto])) by (${VAR_LABEL_GROUP_BY_EXPR})`,
gtk-grafana marked this conversation as resolved.
Show resolved Hide resolved
});
});
test('should build no-parser query with structured metadata filters', () => {
Expand All @@ -35,7 +35,7 @@ describe('buildLabelsQuery', () => {

const result = buildLabelsQuery({} as SceneObject, VAR_LABEL_GROUP_BY_EXPR, 'cluster');
expect(result).toMatchObject({
expr: `sum(count_over_time({\${filters} ,cluster != ""} \${levels} \${patterns} \${lineFilter} [$__auto])) by (${VAR_LABEL_GROUP_BY_EXPR})`,
expr: `sum(count_over_time({\${filters} ,cluster != ""} \${levels} \${patterns} \${lineFilter} \${fields} [$__auto])) by (${VAR_LABEL_GROUP_BY_EXPR})`,
});
});
test('should build logfmt-parser query with structured metadata filters', () => {
Expand Down
16 changes: 11 additions & 5 deletions src/Components/ServiceScene/Breakdowns/LabelBreakdownScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import {
} from '@grafana/scenes';
import { Alert, useStyles2 } from '@grafana/ui';
import { reportAppInteraction, USER_EVENTS_ACTIONS, USER_EVENTS_PAGES } from 'services/analytics';
import { ValueSlugs } from 'services/routing';
import { ALL_VARIABLE_VALUE, SERVICE_NAME, VAR_LABEL_GROUP_BY, VAR_LABELS } from 'services/variables';
import { checkPrimaryLabel, getPrimaryLabelFromUrl, ValueSlugs } from 'services/routing';
import { ALL_VARIABLE_VALUE, SERVICE_NAME, VAR_LABEL_GROUP_BY, VAR_LABELS, VAR_SERVICE } from 'services/variables';
import { ByFrameRepeater } from './ByFrameRepeater';
import { FieldSelector } from './FieldSelector';
import { StatusWrapper } from './StatusWrapper';
Expand Down Expand Up @@ -113,6 +113,8 @@ export class LabelBreakdownScene extends SceneObjectBase<LabelBreakdownSceneStat
if (detectedLabelsFrame) {
this.updateOptions(detectedLabelsFrame);
}

checkPrimaryLabel(this);
}

private onGroupByVariableChange(newState: CustomConstantVariableState, prevState: CustomConstantVariableState) {
Expand All @@ -131,12 +133,16 @@ export class LabelBreakdownScene extends SceneObjectBase<LabelBreakdownSceneStat
newState: SceneVariableState & { filters: AdHocVariableFilter[] },
prevState: SceneVariableState & { filters: AdHocVariableFilter[] }
) {
let { labelName } = getPrimaryLabelFromUrl();
if (labelName === VAR_SERVICE) {
labelName = SERVICE_NAME;
}
const variable = getLabelGroupByVariable(this);
const newService = newState.filters.find((filter) => filter.key === SERVICE_NAME);
const prevService = prevState.filters.find((filter) => filter.key === SERVICE_NAME);
const newPrimaryLabel = newState.filters.find((filter) => filter.key === labelName);
const prevPrimaryLabel = prevState.filters.find((filter) => filter.key === labelName);

// If the user changes the service
if (variable.state.value === ALL_VARIABLE_VALUE && newService !== prevService) {
if (variable.state.value === ALL_VARIABLE_VALUE && newPrimaryLabel !== prevPrimaryLabel) {
this.setState({
loading: true,
body: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import {
} from '@grafana/scenes';
import { getLogsPanelFrame, ServiceScene } from '../ServiceScene';
import { navigateToValueBreakdown } from '../../../services/navigate';
import { ValueSlugs } from '../../../services/routing';
import { getPrimaryLabelFromUrl, ValueSlugs } from '../../../services/routing';
import { Button } from '@grafana/ui';
import React from 'react';
import { addToFilters, VariableFilterType } from './AddToFiltersButton';
import { FilterButton } from '../../FilterButton';
import { EMPTY_VARIABLE_VALUE, LEVEL_VARIABLE_VALUE, SERVICE_NAME } from '../../../services/variables';
import { EMPTY_VARIABLE_VALUE, LEVEL_VARIABLE_VALUE } from '../../../services/variables';
import { AdHocVariableFilter, Field, Labels, LoadingState } from '@grafana/data';
import { FilterOp } from '../../../services/filters';
import {
Expand Down Expand Up @@ -78,7 +78,8 @@ export class SelectLabelActionScene extends SceneObjectBase<SelectLabelActionSce
};

private getExistingFilter(variable?: AdHocFiltersVariable): AdHocVariableFilter | undefined {
if (this.state.labelName !== SERVICE_NAME) {
let { labelName } = getPrimaryLabelFromUrl();
if (this.state.labelName !== labelName) {
return variable?.state.filters.find((filter) => {
const value = getValueFromAdHocVariableFilter(variable, filter);
return filter.key === this.state.labelName && value.value === EMPTY_VARIABLE_VALUE;
Expand Down
6 changes: 1 addition & 5 deletions src/Components/ServiceScene/LogsPanelScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { LogsListScene } from './LogsListScene';
import { LoadingPlaceholder } from '@grafana/ui';
import { addToFilters, FilterType } from './Breakdowns/AddToFiltersButton';
import { getFilterTypeFromLabelType, getLabelTypeFromFrame } from '../../services/fields';
import { SERVICE_NAME, VAR_FIELDS, VAR_LABELS, VAR_LEVELS } from '../../services/variables';
import { VAR_FIELDS, VAR_LABELS, VAR_LEVELS } from '../../services/variables';
import { reportAppInteraction, USER_EVENTS_ACTIONS, USER_EVENTS_PAGES } from '../../services/analytics';
import { getAdHocFiltersVariable } from '../../services/variableGetters';
import { LabelType } from '../../services/fieldsTypes';
Expand Down Expand Up @@ -169,10 +169,6 @@ export class LogsPanelScene extends SceneObjectBase<LogsPanelSceneState> {
};

private handleLabelFilter(key: string, value: string, frame: DataFrame | undefined, operator: FilterType) {
// @TODO: NOOP. We need a way to let the user know why this is not possible.
if (key === SERVICE_NAME) {
return;
}
const labelType = frame ? getLabelTypeFromFrame(key, frame) : LabelType.Parsed;
const variableName = getFilterTypeFromLabelType(labelType, key, value);
addToFilters(key, value, operator, this, variableName);
Expand Down
Loading
Loading