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

Service selection: Starting with labels besides service_name #813

Merged
merged 55 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
55 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
f462fda
chore: wip
gtk-grafana Oct 2, 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
525db5f
Merge branch 'gtk-grafana/service-not-required-poc' into gtk-grafana/…
gtk-grafana Oct 2, 2024
718a7e6
chore: wip, mostly functional
gtk-grafana Oct 2, 2024
debec32
chore: clean up
gtk-grafana Oct 3, 2024
932884b
Merge remote-tracking branch 'origin/main' into gtk-grafana/service-s…
gtk-grafana Oct 3, 2024
b7ee755
chore: refactor tabs out into new scene
gtk-grafana Oct 3, 2024
f07952b
chore: clean up, refactor
gtk-grafana Oct 3, 2024
d61b594
chore: clean up
gtk-grafana Oct 3, 2024
67c2ad1
chore: remove redundant state
gtk-grafana Oct 3, 2024
31ffdf9
feat: switch to detected_labels and display cardinality in tabs and d…
gtk-grafana Oct 3, 2024
3502ad2
chore: add browser history
gtk-grafana Oct 3, 2024
378182c
test: add test for new url params
gtk-grafana Oct 3, 2024
0a29feb
fix: fix browser history when changing labels, datasource
gtk-grafana Oct 8, 2024
207f6a2
Merge remote-tracking branch 'origin/main' into gtk-grafana/service-s…
gtk-grafana Oct 8, 2024
9a0bb0b
chore: fix e2e tests
gtk-grafana Oct 8, 2024
de6a3c4
feat: add favorites which work with new tabs
gtk-grafana Oct 8, 2024
43d1162
feat: add favorites which work with new tabs
gtk-grafana Oct 8, 2024
6cf5f4e
chore: ui clean up, fix bad browser history bug on redirect back to i…
gtk-grafana Oct 9, 2024
0ee54f4
chore: ui review, update favorite icon
gtk-grafana Oct 9, 2024
909b74c
chore: truncate long tab names, add tooltip
gtk-grafana Oct 9, 2024
875eace
chore: spelling
gtk-grafana Oct 9, 2024
13174b9
chore: fix e2e tests, update tooltip copy
gtk-grafana Oct 9, 2024
66aaeef
chore: add e2e coverage
gtk-grafana Oct 10, 2024
5c518eb
chore: clean up
gtk-grafana Oct 10, 2024
001a038
Merge remote-tracking branch 'origin/main' into gtk-grafana/service-s…
gtk-grafana Oct 10, 2024
bbac060
chore: fix urls, remove uncessary test
gtk-grafana Oct 10, 2024
01e1c9b
chore: remove cryptic comment
gtk-grafana Oct 10, 2024
a682175
fix: prevent runtime error if primary label is defined in url but empty
gtk-grafana Oct 10, 2024
a31d9c4
chore: remove capitalization, change input copy
gtk-grafana Oct 15, 2024
d7df5dd
chore: remove loading indicator that pushes content
gtk-grafana Oct 15, 2024
4209473
chore: design tweaks and review with UX
gtk-grafana Oct 15, 2024
bcb107b
chore: sort tabs by order added
gtk-grafana Oct 15, 2024
058f1b0
chore: update playwright, update testids
gtk-grafana Oct 15, 2024
4607a65
chore: clean up, ellipsis
gtk-grafana Oct 16, 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
Next Next commit
chore: wip
  • Loading branch information
gtk-grafana committed Sep 23, 2024
commit 2157dff7d6623f82353f3cab8474b487b7e980e3
30 changes: 15 additions & 15 deletions src/Components/IndexScene/IndexScene.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

import { AdHocVariableFilter, SelectableValue } from '@grafana/data';
import {AdHocVariableFilter, SelectableValue} from '@grafana/data';
import {
AdHocFiltersVariable,
CustomVariable,
Expand All @@ -14,7 +14,6 @@ import {
SceneObjectUrlSyncConfig,
SceneObjectUrlValues,
SceneRefreshPicker,
SceneRouteMatch,
SceneTimePicker,
SceneTimeRange,
SceneVariableSet,
Expand All @@ -36,23 +35,24 @@ import {
VAR_PATTERNS,
} from 'services/variables';

import { addLastUsedDataSourceToStorage, getLastUsedDataSourceFromStorage } from 'services/store';
import { ServiceScene } from '../ServiceScene/ServiceScene';
import { LayoutScene } from './LayoutScene';
import { FilterOp } from 'services/filters';
import { getDrilldownSlug, PageSlugs } from '../../services/routing';
import { ServiceSelectionScene } from '../ServiceSelectionScene/ServiceSelectionScene';
import { LoadingPlaceholder } from '@grafana/ui';
import { config, locationService } from '@grafana/runtime';
import {addLastUsedDataSourceToStorage, getLastUsedDataSourceFromStorage} from 'services/store';
import {ServiceScene} from '../ServiceScene/ServiceScene';
import {LayoutScene} from './LayoutScene';
import {FilterOp} from 'services/filters';
import {getDrilldownSlug, PageSlugs} from '../../services/routing';
import {ServiceSelectionScene} from '../ServiceSelectionScene/ServiceSelectionScene';
import {LoadingPlaceholder} from '@grafana/ui';
import {config, locationService} from '@grafana/runtime';
import {
renderLogQLFieldFilters,
renderLogQLLabelFilters,
renderLogQLMetadataFilters,
renderPatternFilters,
} from 'services/query';
import { VariableHide } from '@grafana/schema';
import { CustomConstantVariable } from '../../services/CustomConstantVariable';
import { ToolbarScene } from './ToolbarScene';
import {VariableHide} from '@grafana/schema';
import {CustomConstantVariable} from '../../services/CustomConstantVariable';
import {ToolbarScene} from './ToolbarScene';
import {RouteMatch} from "../Pages";

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

export class IndexScene extends SceneObjectBase<IndexSceneState> {
Expand Down Expand Up @@ -121,7 +121,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
65 changes: 42 additions & 23 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 {PageLayoutType} from '@grafana/data';
import {IndexScene} from './IndexScene/IndexScene';
import {navigateToIndex} from '../services/navigate';
import {logger} from "../services/logger";

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 RouteMatchOptional = Optional<RouteProps, 'labelName' | 'labelValue' >

function getServicesScene(routeMatch?: SceneRouteMatch<{ service?: string; label?: string }>) {
function getServicesScene(routeMatch: RouteMatch) {
console.log('getServicesScene', routeMatch)
const DEFAULT_TIME_RANGE = { from: 'now-15m', to: 'now' };
return new EmbeddedScene({
body: new IndexScene({
Expand All @@ -49,28 +55,28 @@ export function makeIndexPage() {
drilldowns: [
{
routePath: ROUTE_DEFINITIONS.logs,
getPage: (routeMatch, parent) => makeBreakdownPage(routeMatch, parent, PageSlugs.logs),
getPage: (routeMatch: RouteMatch, parent) => makeBreakdownPage(routeMatch, parent, PageSlugs.logs),
defaultRoute: true,
},
{
routePath: ROUTE_DEFINITIONS.labels,
getPage: (routeMatch, parent) => makeBreakdownPage(routeMatch, parent, PageSlugs.labels),
getPage: (routeMatch: RouteMatch, parent) => makeBreakdownPage(routeMatch, parent, PageSlugs.labels),
},
{
routePath: ROUTE_DEFINITIONS.patterns,
getPage: (routeMatch, parent) => makeBreakdownPage(routeMatch, parent, PageSlugs.patterns),
getPage: (routeMatch: RouteMatch, parent) => makeBreakdownPage(routeMatch, parent, PageSlugs.patterns),
},
{
routePath: ROUTE_DEFINITIONS.fields,
getPage: (routeMatch, parent) => makeBreakdownPage(routeMatch, parent, PageSlugs.fields),
getPage: (routeMatch: RouteMatch, parent) => makeBreakdownPage(routeMatch, parent, PageSlugs.fields),
},
{
routePath: CHILD_ROUTE_DEFINITIONS.label,
getPage: (routeMatch, parent) => makeBreakdownValuePage(routeMatch, parent, ValueSlugs.label),
getPage: (routeMatch: RouteMatch, parent) => makeBreakdownValuePage(routeMatch, parent, ValueSlugs.label),
},
{
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,36 +113,49 @@ 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);
console.log('makeBreakdownPage', {
routeMatch, parent, slug, labelName
})
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),
getScene: (routeMatch: 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);
console.log('makeBreakdownValuePage', {
routeMatch,
parent,
slug
})
const { labelName, labelValue, breakdownLabel } = extractValuesFromRoute(routeMatch);

if(!breakdownLabel){
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),
getScene: (routeMatch: RouteMatch) => getServicesScene(routeMatch),
});
}

Expand Down
38 changes: 18 additions & 20 deletions src/Components/ServiceScene/ActionBarScene.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { SceneComponentProps, sceneGraph, SceneObjectBase, SceneObjectState } from '@grafana/scenes';
import { Box, Stack, Tab, TabsBar, useStyles2 } from '@grafana/ui';
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 { getLabelsVariable, SERVICE_NAME } from '../../services/variables';
import { navigateToDrilldownPage, navigateToIndex } from '../../services/navigate';
import {SceneComponentProps, sceneGraph, SceneObjectBase, SceneObjectState} from '@grafana/scenes';
import {Box, Stack, Tab, TabsBar, useStyles2} from '@grafana/ui';
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 {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 {ServiceScene, ServiceSceneState} from './ServiceScene';
import {GrafanaTheme2} from '@grafana/data';
import {css} from '@emotion/css';
import {BreakdownViewDefinition, breakdownViewsDefinitions} from './BreakdownViews';
import {logger} from "../../services/logger";

export interface ActionBarSceneState extends SceneObjectState {}

Expand Down Expand Up @@ -66,14 +66,12 @@ 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);
}else {
logger.warn('invalid tab!', {
'tab.value': tab.value,
'currentBreakdownViewSlug': currentBreakdownViewSlug,
})
}
}}
/>
Expand Down
8 changes: 5 additions & 3 deletions src/Components/ServiceScene/ServiceScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ type ServiceSceneLoadingStates = {
[name in TabNames]: boolean;
};

const placeholderServiceNameOptionalFlag = true

export interface ServiceSceneCustomState {
labelsCount?: number;
patternsCount?: number;
Expand Down Expand Up @@ -126,7 +128,7 @@ export class ServiceScene extends SceneObjectBase<ServiceSceneState> {

private setSubscribeToLabelsVariable() {
const variable = getLabelsVariable(this);
if (variable.state.filters.length === 0) {
if (variable.state.filters.length === 0 && !placeholderServiceNameOptionalFlag) {
this.redirectToStart();
return;
}
Expand All @@ -138,12 +140,12 @@ export class ServiceScene extends SceneObjectBase<ServiceSceneState> {
this.redirectToStart();
}
// If we remove the service name filter, we should redirect to the start
if (!newState.filters.some((f) => f.key === SERVICE_NAME)) {
if (!placeholderServiceNameOptionalFlag && !newState.filters.some((f) => f.key === SERVICE_NAME)) {
this.redirectToStart();
}

// Clear filters if changing service, they might not exist, or might have a different parser
if (prevServiceName !== newServiceName) {
if (!placeholderServiceNameOptionalFlag && prevServiceName !== newServiceName) {
const fields = getFieldsVariable(this);
fields.setState({
filters: [],
Expand Down
23 changes: 13 additions & 10 deletions src/services/extensions/links.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { PluginExtensionLinkConfig, PluginExtensionPanelContext, PluginExtensionPoints } from '@grafana/data';
import { LabelType } from 'services/fields';
import { getMatcherFromQuery } from 'services/logql';
import {PluginExtensionLinkConfig, PluginExtensionPanelContext, PluginExtensionPoints} from '@grafana/data';
import {LabelType} from 'services/fields';
import {getMatcherFromQuery} from 'services/logql';

import { LokiQuery } from 'services/query';
import { appendUrlParameter, createAppUrl, replaceSlash, setUrlParameter, UrlParameters } from 'services/routing';
import { SERVICE_NAME } from 'services/variables';
import {LokiQuery} from 'services/query';
import {appendUrlParameter, createAppUrl, replaceSlash, setUrlParameter, UrlParameters} from 'services/routing';
import {SERVICE_NAME} from 'services/variables';
import {FilterOp} from "../filters";

const title = 'Open in Explore Logs';
const description = 'Open current query in the Explore Logs view';
Expand Down Expand Up @@ -40,11 +41,11 @@ function contextToLink<T extends PluginExtensionPanelContext>(context?: T) {

const expr = lokiQuery.expr;
const labelFilters = getMatcherFromQuery(expr);
const serviceSelector = labelFilters.find((selector) => selector.key === SERVICE_NAME);
if (!serviceSelector) {
const labelSelector = labelFilters.find((selector) => selector.key === SERVICE_NAME) ?? labelFilters.find((selector) => selector.operator === FilterOp.Equal) ;
if (!labelSelector) {
return undefined;
}
const serviceName = replaceSlash(serviceSelector.value);
const labelName = replaceSlash(labelSelector.value);
// sort `service_name` first
labelFilters.sort((a, b) => (a.key === SERVICE_NAME ? -1 : 1));

Expand All @@ -65,7 +66,9 @@ function contextToLink<T extends PluginExtensionPanelContext>(context?: T) {
);
}

console.log('path', createAppUrl(`/explore/${labelSelector.key}/${labelName}/logs`, params))

return {
path: createAppUrl(`/explore/service/${serviceName}/logs`, params),
path: createAppUrl(`/explore/${labelSelector.key}/${labelName}/logs`, params),
};
}
8 changes: 5 additions & 3 deletions src/services/navigate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ describe('navigate', () => {
isExact: true,
url: '',
params: {
service: serviceLabel,
label: drillDownLabel,
labelValue: serviceLabel,
labelName: 'service',
breakdownLabel: drillDownLabel,
},
},
},
Expand Down Expand Up @@ -69,7 +70,8 @@ describe('navigate', () => {
isExact: true,
url: '',
params: {
service: serviceLabel,
labelName: 'service',
labelValue: serviceLabel,
},
},
},
Expand Down
Loading