diff --git a/.github/workflows/is-compatible.yml b/.github/workflows/is-compatible.yml index 9b87dba6f..e07d9d405 100644 --- a/.github/workflows/is-compatible.yml +++ b/.github/workflows/is-compatible.yml @@ -35,4 +35,4 @@ jobs: - name: Build plugin run: npm run build - name: Compatibility check - run: npx @grafana/levitate@latest is-compatible --path src/module.ts --target @grafana/data,@grafana/ui,@grafana/runtime + run: npx @grafana/levitate@latest is-compatible --path src/module.tsx --target @grafana/data,@grafana/ui,@grafana/runtime diff --git a/package.json b/package.json index 5a9fcc8df..355d66105 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "tsconfig-paths": "^4.2.0", "typescript": "4.8.4", "webpack": "^5.86.0", + "webpack-bundle-analyzer": "^4.10.2", "webpack-cli": "^5.1.4", "webpack-livereload-plugin": "^3.0.2" }, diff --git a/src/Components/App.tsx b/src/Components/App.tsx index 2565ce9ac..4e85be61b 100644 --- a/src/Components/App.tsx +++ b/src/Components/App.tsx @@ -1,10 +1,10 @@ -import React from 'react'; +import React, { lazy } from 'react'; import { AppRootProps } from '@grafana/data'; -import { LogExplorationView } from './LogExplorationPage'; +const LogExplorationView = lazy(() => import('./LogExplorationPage')); const PluginPropsContext = React.createContext(null); -export class App extends React.PureComponent { +class App extends React.PureComponent { render() { return ( @@ -13,3 +13,5 @@ export class App extends React.PureComponent { ); } } + +export default App; diff --git a/src/Components/IndexScene/IndexScene.tsx b/src/Components/IndexScene/IndexScene.tsx index a32b6fdd2..a99ce1d7e 100644 --- a/src/Components/IndexScene/IndexScene.tsx +++ b/src/Components/IndexScene/IndexScene.tsx @@ -22,10 +22,6 @@ import { } from '@grafana/scenes'; import { EXPLORATION_DS, - getFieldsVariable, - getLevelsVariable, - getPatternsVariable, - getUrlParamNameForVariable, MIXED_FORMAT_EXPR, VAR_DATASOURCE, VAR_FIELDS, @@ -52,6 +48,12 @@ import { } from 'services/query'; import { VariableHide } from '@grafana/schema'; import { CustomConstantVariable } from '../../services/CustomConstantVariable'; +import { + getFieldsVariable, + getLevelsVariable, + getPatternsVariable, + getUrlParamNameForVariable, +} from '../../services/variableGetters'; export interface AppliedPattern { pattern: string; diff --git a/src/Components/LogExplorationPage.tsx b/src/Components/LogExplorationPage.tsx index 074e1db15..a4ffbed01 100644 --- a/src/Components/LogExplorationPage.tsx +++ b/src/Components/LogExplorationPage.tsx @@ -11,7 +11,7 @@ const getSceneApp = () => pages: [makeIndexPage(), makeRedirectPage()], }); -export function LogExplorationView() { +function LogExplorationView() { const [isInitialized, setIsInitialized] = React.useState(false); initializeMetadataService(); @@ -37,3 +37,5 @@ export function LogExplorationView() { return ; } + +export default LogExplorationView; diff --git a/src/Components/ServiceScene/ActionBarScene.tsx b/src/Components/ServiceScene/ActionBarScene.tsx index 8577aaba4..94b59ea7a 100644 --- a/src/Components/ServiceScene/ActionBarScene.tsx +++ b/src/Components/ServiceScene/ActionBarScene.tsx @@ -4,13 +4,14 @@ 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 { SERVICE_NAME } from '../../services/variables'; import { navigateToDrilldownPage, navigateToIndex } 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 {} diff --git a/src/Components/ServiceScene/Breakdowns/AddToFiltersButton.test.tsx b/src/Components/ServiceScene/Breakdowns/AddToFiltersButton.test.tsx index bbb3a5a92..5d2c37b36 100644 --- a/src/Components/ServiceScene/Breakdowns/AddToFiltersButton.test.tsx +++ b/src/Components/ServiceScene/Breakdowns/AddToFiltersButton.test.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; -import { addAdHocFilter, addToFilters, AddToFiltersButton, FieldValue, FilterType } from './AddToFiltersButton'; +import { addAdHocFilter, addToFilters, AddToFiltersButton, FilterType } from './AddToFiltersButton'; import { BusEvent, createDataFrame, Field, FieldType, LoadingState, PanelData } from '@grafana/data'; import userEvent from '@testing-library/user-event'; import { AdHocFiltersVariable, sceneGraph, SceneObject, SceneQueryRunner } from '@grafana/scenes'; -import { LEVEL_VARIABLE_VALUE, VAR_FIELDS, VAR_LABELS, VAR_LEVELS } from 'services/variables'; +import { FieldValue, LEVEL_VARIABLE_VALUE, VAR_FIELDS, VAR_LABELS, VAR_LEVELS } from 'services/variables'; import { ServiceScene, ServiceSceneState } from '../ServiceScene'; const scene = { publishEvent(event: BusEvent, bubble?: boolean) {} } as SceneObject; diff --git a/src/Components/ServiceScene/Breakdowns/AddToFiltersButton.tsx b/src/Components/ServiceScene/Breakdowns/AddToFiltersButton.tsx index 527609d4d..e1ee5305e 100644 --- a/src/Components/ServiceScene/Breakdowns/AddToFiltersButton.tsx +++ b/src/Components/ServiceScene/Breakdowns/AddToFiltersButton.tsx @@ -4,19 +4,12 @@ import { AdHocVariableFilter, BusEventBase, DataFrame } from '@grafana/data'; import { SceneComponentProps, SceneObject, SceneObjectBase, SceneObjectState } from '@grafana/scenes'; import { VariableHide } from '@grafana/schema'; import { reportAppInteraction, USER_EVENTS_ACTIONS, USER_EVENTS_PAGES } from 'services/analytics'; -import { - getAdHocFiltersVariable, - getValueFromAdHocVariableFilter, - LEVEL_VARIABLE_VALUE, - ParserType, - VAR_FIELDS, - VAR_LABELS, - VAR_LEVELS, -} from 'services/variables'; +import { LEVEL_VARIABLE_VALUE, VAR_FIELDS, VAR_LABELS, VAR_LEVELS } from 'services/variables'; import { FilterButton } from 'Components/FilterButton'; import { FilterOp } from 'services/filters'; import { getDetectedLabelsFrame } from '../ServiceScene'; import { getParserForField } from '../../../services/fields'; +import { getAdHocFiltersVariable, getValueFromAdHocVariableFilter } from '../../../services/variableGetters'; export interface AddToFiltersButtonState extends SceneObjectState { frame: DataFrame; @@ -43,16 +36,6 @@ export function addAdHocFilter(filter: AdHocVariableFilter, scene: SceneObject, addToFilters(filter.key, filter.value, type, scene, variableType); } -export interface FieldValue { - value: string; - parser: ParserType; -} - -export interface AdHocFieldValue { - value?: string; - parser?: ParserType; -} - export type VariableFilterType = typeof VAR_LABELS | typeof VAR_FIELDS | typeof VAR_LEVELS; export function addToFilters( diff --git a/src/Components/ServiceScene/Breakdowns/FieldValuesBreakdownScene.tsx b/src/Components/ServiceScene/Breakdowns/FieldValuesBreakdownScene.tsx index 25fd55136..a16d6a4ad 100644 --- a/src/Components/ServiceScene/Breakdowns/FieldValuesBreakdownScene.tsx +++ b/src/Components/ServiceScene/Breakdowns/FieldValuesBreakdownScene.tsx @@ -11,7 +11,7 @@ import { SceneObjectState, SceneReactObject, } from '@grafana/scenes'; -import { buildDataQuery, LokiQuery } from '../../../services/query'; +import { buildDataQuery } from '../../../services/query'; import { getSortByPreference } from '../../../services/store'; import { DataQueryError, LoadingState } from '@grafana/data'; import { LayoutSwitcher } from './LayoutSwitcher'; @@ -20,7 +20,7 @@ import { ByFrameRepeater } from './ByFrameRepeater'; import { Alert, DrawStyle, LoadingPlaceholder } from '@grafana/ui'; import { buildFieldsQueryString, getFilterBreakdownValueScene } from '../../../services/fields'; import { getLabelValue } from './SortByScene'; -import { getFieldGroupByVariable, getFieldsVariable, VAR_FIELDS } from '../../../services/variables'; +import { VAR_FIELDS } from '../../../services/variables'; import React from 'react'; import { FIELDS_BREAKDOWN_GRID_TEMPLATE_COLUMNS, FieldsBreakdownScene } from './FieldsBreakdownScene'; import { AddFilterEvent } from './AddToFiltersButton'; @@ -28,6 +28,8 @@ import { navigateToDrilldownPage } from '../../../services/navigate'; import { PageSlugs } from '../../../services/routing'; import { getDetectedFieldsFrame, ServiceScene } from '../ServiceScene'; import { DEFAULT_SORT_BY } from '../../../services/sorting'; +import { getFieldGroupByVariable, getFieldsVariable } from '../../../services/variableGetters'; +import { LokiQuery } from '../../../services/lokiQuery'; export interface FieldValuesBreakdownSceneState extends SceneObjectState { body?: LayoutSwitcher | SceneReactObject; diff --git a/src/Components/ServiceScene/Breakdowns/FieldsAggregatedBreakdownScene.tsx b/src/Components/ServiceScene/Breakdowns/FieldsAggregatedBreakdownScene.tsx index 8afe135c4..0e583c307 100644 --- a/src/Components/ServiceScene/Breakdowns/FieldsAggregatedBreakdownScene.tsx +++ b/src/Components/ServiceScene/Breakdowns/FieldsAggregatedBreakdownScene.tsx @@ -10,7 +10,7 @@ import { SceneObjectState, VizPanel, } from '@grafana/scenes'; -import { ALL_VARIABLE_VALUE, getFieldGroupByVariable, getFieldsVariable } from '../../../services/variables'; +import { ALL_VARIABLE_VALUE } from '../../../services/variables'; import { buildDataQuery } from '../../../services/query'; import { getQueryRunner, setLevelColorOverrides } from '../../../services/panel'; import { DrawStyle, LoadingPlaceholder, StackingMode } from '@grafana/ui'; @@ -30,6 +30,7 @@ import { DataFrame, LoadingState } from '@grafana/data'; import { limitMaxNumberOfSeriesForPanel, MAX_NUMBER_OF_TIME_SERIES } from './TimeSeriesLimitSeriesTitleItem'; import { map, Observable } from 'rxjs'; import { buildFieldsQueryString, isAvgField } from '../../../services/fields'; +import { getFieldGroupByVariable, getFieldsVariable } from '../../../services/variableGetters'; export interface FieldsAggregatedBreakdownSceneState extends SceneObjectState { body?: LayoutSwitcher; diff --git a/src/Components/ServiceScene/Breakdowns/FieldsBreakdownScene.tsx b/src/Components/ServiceScene/Breakdowns/FieldsBreakdownScene.tsx index d75e97afa..3c7e84b67 100644 --- a/src/Components/ServiceScene/Breakdowns/FieldsBreakdownScene.tsx +++ b/src/Components/ServiceScene/Breakdowns/FieldsBreakdownScene.tsx @@ -19,14 +19,7 @@ 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, - getFieldGroupByVariable, - getLabelsVariable, - SERVICE_NAME, - VAR_FIELD_GROUP_BY, - VAR_LABELS, -} from 'services/variables'; +import { ALL_VARIABLE_VALUE, SERVICE_NAME, VAR_FIELD_GROUP_BY, VAR_LABELS } from 'services/variables'; import { areArraysEqual } from '../../../services/comparison'; import { CustomConstantVariable, CustomConstantVariableState } from '../../../services/CustomConstantVariable'; import { navigateToValueBreakdown } from '../../../services/navigate'; @@ -45,6 +38,7 @@ import { SortByScene, SortCriteriaChanged } from './SortByScene'; import { StatusWrapper } from './StatusWrapper'; import { getFieldOptions } from 'services/filters'; import { EmptyLayoutScene } from './EmptyLayoutScene'; +import { getFieldGroupByVariable, getLabelsVariable } from '../../../services/variableGetters'; export const averageFields = ['duration', 'count', 'total', 'bytes']; export const FIELDS_BREAKDOWN_GRID_TEMPLATE_COLUMNS = 'repeat(auto-fit, minmax(400px, 1fr))'; diff --git a/src/Components/ServiceScene/Breakdowns/LabelBreakdownScene.tsx b/src/Components/ServiceScene/Breakdowns/LabelBreakdownScene.tsx index 453f6227d..3051b2c12 100644 --- a/src/Components/ServiceScene/Breakdowns/LabelBreakdownScene.tsx +++ b/src/Components/ServiceScene/Breakdowns/LabelBreakdownScene.tsx @@ -17,14 +17,7 @@ import { 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, - getLabelGroupByVariable, - getLabelsVariable, - SERVICE_NAME, - VAR_LABEL_GROUP_BY, - VAR_LABELS, -} from 'services/variables'; +import { ALL_VARIABLE_VALUE, SERVICE_NAME, VAR_LABEL_GROUP_BY, VAR_LABELS } from 'services/variables'; import { ByFrameRepeater } from './ByFrameRepeater'; import { FieldSelector } from './FieldSelector'; import { StatusWrapper } from './StatusWrapper'; @@ -40,6 +33,7 @@ import { LabelValuesBreakdownScene } from './LabelValuesBreakdownScene'; import { LabelsAggregatedBreakdownScene } from './LabelsAggregatedBreakdownScene'; import { DEFAULT_SORT_BY } from '../../../services/sorting'; import { EmptyLayoutScene } from './EmptyLayoutScene'; +import { getLabelGroupByVariable, getLabelsVariable } from '../../../services/variableGetters'; export interface LabelBreakdownSceneState extends SceneObjectState { body?: SceneObject; diff --git a/src/Components/ServiceScene/Breakdowns/LabelValuesBreakdownScene.tsx b/src/Components/ServiceScene/Breakdowns/LabelValuesBreakdownScene.tsx index 813ad5e38..940092204 100644 --- a/src/Components/ServiceScene/Breakdowns/LabelValuesBreakdownScene.tsx +++ b/src/Components/ServiceScene/Breakdowns/LabelValuesBreakdownScene.tsx @@ -19,12 +19,7 @@ import { getSortByPreference } from '../../../services/store'; import { LoadingState } from '@grafana/data'; import { ByFrameRepeater } from './ByFrameRepeater'; import { getFilterBreakdownValueScene } from '../../../services/fields'; -import { - ALL_VARIABLE_VALUE, - getLabelGroupByVariable, - VAR_LABEL_GROUP_BY_EXPR, - VAR_LABELS, -} from '../../../services/variables'; +import { ALL_VARIABLE_VALUE, VAR_LABEL_GROUP_BY_EXPR, VAR_LABELS } from '../../../services/variables'; import React from 'react'; import { LabelBreakdownScene } from './LabelBreakdownScene'; import { navigateToDrilldownPage } from '../../../services/navigate'; @@ -33,6 +28,7 @@ import { ServiceScene } from '../ServiceScene'; import { AddFilterEvent } from './AddToFiltersButton'; import { DEFAULT_SORT_BY } from '../../../services/sorting'; import { buildLabelsQuery, LABEL_BREAKDOWN_GRID_TEMPLATE_COLUMNS } from '../../../services/labels'; +import { getLabelGroupByVariable } from '../../../services/variableGetters'; export interface LabelValueBreakdownSceneState extends SceneObjectState { body?: LayoutSwitcher; diff --git a/src/Components/ServiceScene/Breakdowns/LabelsAggregatedBreakdownScene.tsx b/src/Components/ServiceScene/Breakdowns/LabelsAggregatedBreakdownScene.tsx index 67b87e771..db63a9871 100644 --- a/src/Components/ServiceScene/Breakdowns/LabelsAggregatedBreakdownScene.tsx +++ b/src/Components/ServiceScene/Breakdowns/LabelsAggregatedBreakdownScene.tsx @@ -15,20 +15,16 @@ import { import { LayoutSwitcher } from './LayoutSwitcher'; import { DrawStyle, LoadingPlaceholder, StackingMode } from '@grafana/ui'; import { getQueryRunner, setLevelColorOverrides } from '../../../services/panel'; -import { - ALL_VARIABLE_VALUE, - getFieldsVariable, - getLabelGroupByVariable, - LEVEL_VARIABLE_VALUE, -} from '../../../services/variables'; +import { ALL_VARIABLE_VALUE, LEVEL_VARIABLE_VALUE } from '../../../services/variables'; import React from 'react'; import { LabelBreakdownScene } from './LabelBreakdownScene'; import { SelectLabelActionScene } from './SelectLabelActionScene'; import { ValueSlugs } from '../../../services/routing'; import { limitMaxNumberOfSeriesForPanel, MAX_NUMBER_OF_TIME_SERIES } from './TimeSeriesLimitSeriesTitleItem'; import { limitFramesTransformation } from './FieldsAggregatedBreakdownScene'; -import { LokiQuery } from '../../../services/query'; import { buildLabelsQuery, LABEL_BREAKDOWN_GRID_TEMPLATE_COLUMNS } from '../../../services/labels'; +import { getFieldsVariable, getLabelGroupByVariable } from '../../../services/variableGetters'; +import { LokiQuery } from '../../../services/lokiQuery'; import { ServiceScene } from '../ServiceScene'; import { DataFrame, LoadingState } from '@grafana/data'; diff --git a/src/Components/ServiceScene/Breakdowns/Patterns/PatternNameLabel.tsx b/src/Components/ServiceScene/Breakdowns/Patterns/PatternNameLabel.tsx index 43c1fc1c8..318665874 100644 --- a/src/Components/ServiceScene/Breakdowns/Patterns/PatternNameLabel.tsx +++ b/src/Components/ServiceScene/Breakdowns/Patterns/PatternNameLabel.tsx @@ -3,12 +3,12 @@ import { AdHocFiltersVariable, sceneGraph } from '@grafana/scenes'; import { Spinner, Toggletip, useStyles2 } from '@grafana/ui'; import { getLokiDatasource } from 'services/scenes'; import { IndexScene } from 'Components/IndexScene/IndexScene'; -import { getLabelsVariable } from 'services/variables'; import { buildDataQuery } from 'services/query'; import { PatternFieldLabelStats } from './PatternFieldLabelStats'; import { GrafanaTheme2, LoadingState, LogLabelStatsModel, TimeRange } from '@grafana/data'; import { reportAppInteraction, USER_EVENTS_ACTIONS, USER_EVENTS_PAGES } from 'services/analytics'; import { css } from '@emotion/css'; +import { getLabelsVariable } from '../../../../services/variableGetters'; interface PatternNameLabelProps { exploration: IndexScene; diff --git a/src/Components/ServiceScene/Breakdowns/Patterns/PatternsLogsSampleScene.tsx b/src/Components/ServiceScene/Breakdowns/Patterns/PatternsLogsSampleScene.tsx index 8ef02d262..58419031d 100644 --- a/src/Components/ServiceScene/Breakdowns/Patterns/PatternsLogsSampleScene.tsx +++ b/src/Components/ServiceScene/Breakdowns/Patterns/PatternsLogsSampleScene.tsx @@ -15,18 +15,17 @@ import React from 'react'; import { LoadingState } from '@grafana/data'; import { Alert, Button } from '@grafana/ui'; import { - getFieldsVariable, - getLevelsVariable, - getLineFilterVariable, LOG_STREAM_SELECTOR_EXPR, PATTERNS_SAMPLE_SELECTOR_EXPR, VAR_PATTERNS_EXPR, } from '../../../../services/variables'; -import { buildDataQuery, LokiQuery, renderPatternFilters } from '../../../../services/query'; +import { buildDataQuery, renderPatternFilters } from '../../../../services/query'; import { getQueryRunner } from '../../../../services/panel'; import { AppliedPattern } from '../../../IndexScene/IndexScene'; import { PatternsViewTableScene } from './PatternsViewTableScene'; import { emptyStateStyles } from '../FieldsBreakdownScene'; +import { getFieldsVariable, getLevelsVariable, getLineFilterVariable } from '../../../../services/variableGetters'; +import { LokiQuery } from '../../../../services/lokiQuery'; interface PatternsLogsSampleSceneState extends SceneObjectState { pattern: string; diff --git a/src/Components/ServiceScene/Breakdowns/PatternsLogsSampleScene.tsx b/src/Components/ServiceScene/Breakdowns/PatternsLogsSampleScene.tsx index 24c1e230b..23a3aaa53 100644 --- a/src/Components/ServiceScene/Breakdowns/PatternsLogsSampleScene.tsx +++ b/src/Components/ServiceScene/Breakdowns/PatternsLogsSampleScene.tsx @@ -12,11 +12,8 @@ import { } from '@grafana/scenes'; import React from 'react'; import { getQueryRunner } from '../../../services/panel'; -import { buildDataQuery, LokiQuery, renderPatternFilters } from '../../../services/query'; +import { buildDataQuery, renderPatternFilters } from '../../../services/query'; import { - getFieldsVariable, - getLevelsVariable, - getLineFilterVariable, LOG_STREAM_SELECTOR_EXPR, PATTERNS_SAMPLE_SELECTOR_EXPR, VAR_PATTERNS_EXPR, @@ -26,6 +23,8 @@ import { LoadingState } from '@grafana/data'; import { Alert, Button } from '@grafana/ui'; import { PatternsViewTableScene } from './Patterns/PatternsViewTableScene'; import { emptyStateStyles } from './FieldsBreakdownScene'; +import { getFieldsVariable, getLevelsVariable, getLineFilterVariable } from '../../../services/variableGetters'; +import { LokiQuery } from '../../../services/lokiQuery'; interface PatternsLogsSampleSceneState extends SceneObjectState { pattern: string; diff --git a/src/Components/ServiceScene/Breakdowns/SelectLabelActionScene.tsx b/src/Components/ServiceScene/Breakdowns/SelectLabelActionScene.tsx index 8e58074cd..86f065487 100644 --- a/src/Components/ServiceScene/Breakdowns/SelectLabelActionScene.tsx +++ b/src/Components/ServiceScene/Breakdowns/SelectLabelActionScene.tsx @@ -13,17 +13,15 @@ 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 { AdHocVariableFilter, Field, Labels, LoadingState } from '@grafana/data'; +import { FilterOp } from '../../../services/filters'; import { - EMPTY_VARIABLE_VALUE, getFieldsVariable, getLabelsVariable, getLevelsVariable, getValueFromAdHocVariableFilter, - LEVEL_VARIABLE_VALUE, - SERVICE_NAME, -} from '../../../services/variables'; -import { AdHocVariableFilter, Field, Labels, LoadingState } from '@grafana/data'; -import { FilterOp } from '../../../services/filters'; +} from '../../../services/variableGetters'; interface SelectLabelActionSceneState extends SceneObjectState { labelName: string; diff --git a/src/Components/ServiceScene/LineFilterScene.tsx b/src/Components/ServiceScene/LineFilterScene.tsx index 50b46bfe9..cc918057c 100644 --- a/src/Components/ServiceScene/LineFilterScene.tsx +++ b/src/Components/ServiceScene/LineFilterScene.tsx @@ -3,11 +3,11 @@ import { SceneComponentProps, SceneObjectBase, SceneObjectState } from '@grafana import { Field } from '@grafana/ui'; import { debounce, escapeRegExp } from 'lodash'; import React, { ChangeEvent } from 'react'; -import { getLineFilterVariable } from 'services/variables'; import { testIds } from 'services/testIds'; import { reportAppInteraction, USER_EVENTS_ACTIONS, USER_EVENTS_PAGES } from 'services/analytics'; import { SearchInput } from './Breakdowns/SearchInput'; import { LineFilterIcon } from './LineFilterIcon'; +import { getLineFilterVariable } from '../../services/variableGetters'; interface LineFilterState extends SceneObjectState { lineFilter: string; diff --git a/src/Components/ServiceScene/LogsPanelScene.tsx b/src/Components/ServiceScene/LogsPanelScene.tsx index a8f8e8cad..278e664bd 100644 --- a/src/Components/ServiceScene/LogsPanelScene.tsx +++ b/src/Components/ServiceScene/LogsPanelScene.tsx @@ -14,9 +14,11 @@ import React from 'react'; import { LogsListScene } from './LogsListScene'; import { LoadingPlaceholder } from '@grafana/ui'; import { addToFilters, FilterType } from './Breakdowns/AddToFiltersButton'; -import { getFilterTypeFromLabelType, getLabelTypeFromFrame, LabelType } from '../../services/fields'; -import { getAdHocFiltersVariable, SERVICE_NAME, VAR_FIELDS, VAR_LABELS, VAR_LEVELS } from '../../services/variables'; +import { getFilterTypeFromLabelType, getLabelTypeFromFrame } from '../../services/fields'; +import { SERVICE_NAME, 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'; interface LogsPanelSceneState extends SceneObjectState { body?: VizPanel; diff --git a/src/Components/ServiceScene/LogsTableScene.tsx b/src/Components/ServiceScene/LogsTableScene.tsx index 932965d49..b9a847b8b 100644 --- a/src/Components/ServiceScene/LogsTableScene.tsx +++ b/src/Components/ServiceScene/LogsTableScene.tsx @@ -9,7 +9,8 @@ import { css } from '@emotion/css'; import { addAdHocFilter } from './Breakdowns/AddToFiltersButton'; import { areArraysEqual } from '../../services/comparison'; import { getLogsPanelFrame } from './ServiceScene'; -import { getFilterTypeFromLabelType, getLabelTypeFromFrame, LabelType } from '../../services/fields'; +import { getFilterTypeFromLabelType, getLabelTypeFromFrame } from '../../services/fields'; +import { LabelType } from '../../services/fieldsTypes'; export class LogsTableScene extends SceneObjectBase { public static Component = ({ model }: SceneComponentProps) => { diff --git a/src/Components/ServiceScene/LogsVolumePanel.tsx b/src/Components/ServiceScene/LogsVolumePanel.tsx index 21dfc6a5a..918bbf970 100644 --- a/src/Components/ServiceScene/LogsVolumePanel.tsx +++ b/src/Components/ServiceScene/LogsVolumePanel.tsx @@ -4,11 +4,12 @@ import { PanelBuilders, SceneComponentProps, SceneObjectBase, SceneObjectState, import { LegendDisplayMode, PanelContext, SeriesVisibilityChangeMode } from '@grafana/ui'; import { getQueryRunner, setLogsVolumeFieldConfigs, syncLogsPanelVisibleSeries } from 'services/panel'; import { buildDataQuery } from 'services/query'; -import { getFieldsVariable, getLabelsVariable, getLevelsVariable, LEVEL_VARIABLE_VALUE } from 'services/variables'; +import { LEVEL_VARIABLE_VALUE } from 'services/variables'; import { reportAppInteraction, USER_EVENTS_ACTIONS, USER_EVENTS_PAGES } from 'services/analytics'; import { getTimeSeriesExpr } from '../../services/expressions'; import { toggleLevelFromFilter } from 'services/levels'; import { LoadingState } from '@grafana/data'; +import { getFieldsVariable, getLabelsVariable, getLevelsVariable } from '../../services/variableGetters'; export interface LogsVolumePanelState extends SceneObjectState { panel?: VizPanel; diff --git a/src/Components/ServiceScene/ServiceScene.tsx b/src/Components/ServiceScene/ServiceScene.tsx index 551f28c8a..5395d92c5 100644 --- a/src/Components/ServiceScene/ServiceScene.tsx +++ b/src/Components/ServiceScene/ServiceScene.tsx @@ -20,11 +20,6 @@ import { getQueryRunner, getResourceQueryRunner } from 'services/panel'; import { buildDataQuery, buildResourceQuery } from 'services/query'; import { getDrilldownSlug, getDrilldownValueSlug, PageSlugs, ValueSlugs } from 'services/routing'; import { - getDataSourceVariable, - getFieldsVariable, - getLabelsVariable, - getLevelsVariable, - getServiceNameFromVariableState, LEVEL_VARIABLE_VALUE, LOG_STREAM_SELECTOR_EXPR, SERVICE_NAME, @@ -40,6 +35,13 @@ import { navigateToIndex } from '../../services/navigate'; import { areArraysEqual } from '../../services/comparison'; import { ActionBarScene } from './ActionBarScene'; import { breakdownViewsDefinitions, TabNames, valueBreakdownViews } from './BreakdownViews'; +import { + getDataSourceVariable, + getFieldsVariable, + getLabelsVariable, + getLevelsVariable, + getServiceNameFromVariableState, +} from '../../services/variableGetters'; const LOGS_PANEL_QUERY_REFID = 'logsPanelQuery'; const PATTERNS_QUERY_REFID = 'patterns'; diff --git a/src/Components/ServiceSelectionScene/SelectServiceButton.tsx b/src/Components/ServiceSelectionScene/SelectServiceButton.tsx index aa1ab12d9..8999718b6 100644 --- a/src/Components/ServiceSelectionScene/SelectServiceButton.tsx +++ b/src/Components/ServiceSelectionScene/SelectServiceButton.tsx @@ -4,10 +4,11 @@ import { SceneComponentProps, SceneObject, SceneObjectBase, SceneObjectState } f import { Button } from '@grafana/ui'; import { VariableHide } from '@grafana/schema'; import { addToFavoriteServicesInStorage } from 'services/store'; -import { getDataSourceVariable, getLabelsVariable, SERVICE_NAME } from 'services/variables'; +import { SERVICE_NAME } from 'services/variables'; import { reportAppInteraction, USER_EVENTS_ACTIONS, USER_EVENTS_PAGES } from 'services/analytics'; import { FilterOp } from 'services/filters'; import { navigateToInitialPageAfterServiceSelection } from '../../services/navigate'; +import { getDataSourceVariable, getLabelsVariable } from '../../services/variableGetters'; export interface SelectServiceButtonState extends SceneObjectState { service: string; diff --git a/src/Components/ServiceSelectionScene/ServiceSelectionScene.tsx b/src/Components/ServiceSelectionScene/ServiceSelectionScene.tsx index fc183b9a3..e6b2d65d3 100644 --- a/src/Components/ServiceSelectionScene/ServiceSelectionScene.tsx +++ b/src/Components/ServiceSelectionScene/ServiceSelectionScene.tsx @@ -26,15 +26,7 @@ import { useStyles2, } from '@grafana/ui'; import { getFavoriteServicesFromStorage } from 'services/store'; -import { - getDataSourceVariable, - getLabelsVariable, - getServiceSelectionStringVariable, - LEVEL_VARIABLE_VALUE, - SERVICE_NAME, - VAR_SERVICE, - VAR_SERVICE_EXPR, -} from 'services/variables'; +import { LEVEL_VARIABLE_VALUE, SERVICE_NAME, VAR_SERVICE, VAR_SERVICE_EXPR } from 'services/variables'; import { selectService, SelectServiceButton } from './SelectServiceButton'; import { buildDataQuery, buildResourceQuery } from 'services/query'; import { reportAppInteraction, USER_EVENTS_ACTIONS, USER_EVENTS_PAGES } from 'services/analytics'; @@ -45,6 +37,11 @@ import { getLabelsFromSeries, toggleLevelVisibility } from 'services/levels'; import { ServiceFieldSelector } from '../ServiceScene/Breakdowns/FieldSelector'; import { CustomConstantVariable } from '../../services/CustomConstantVariable'; import { areArraysEqual } from '../../services/comparison'; +import { + getDataSourceVariable, + getLabelsVariable, + getServiceSelectionStringVariable, +} from '../../services/variableGetters'; export const SERVICES_LIMIT = 20; diff --git a/src/module.ts b/src/module.ts deleted file mode 100644 index 3261ce89a..000000000 --- a/src/module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { AppPlugin } from '@grafana/data'; -import { App } from 'Components/App'; -import init from '@bsull/augurs'; -import { linkConfigs } from 'services/extensions/links'; -import { init as initRuntimeDs } from 'services/datasource'; -import { wasmSupported } from 'services/sorting'; - -if (wasmSupported()) { - init(); -} - -export const plugin = new AppPlugin<{}>().setRootPage(App); - -for (const linkConfig of linkConfigs) { - plugin.configureExtensionLink(linkConfig); -} - -initRuntimeDs(); diff --git a/src/module.tsx b/src/module.tsx new file mode 100644 index 000000000..e960e2257 --- /dev/null +++ b/src/module.tsx @@ -0,0 +1,24 @@ +import { lazy } from 'react'; +import { AppPlugin } from '@grafana/data'; +import { linkConfigs } from 'services/extensions/links'; + +const App = lazy(async () => { + const { wasmSupported } = await import('services/sorting'); + + const { default: initRuntimeDs } = await import('services/datasource'); + const { default: init } = await import('@bsull/augurs'); + + initRuntimeDs(); + + if (wasmSupported()) { + await init(); + } + + return import('Components/App'); +}); + +export const plugin = new AppPlugin<{}>().setRootPage(App); + +for (const linkConfig of linkConfigs) { + plugin.configureExtensionLink(linkConfig); +} diff --git a/src/services/datasource.test.ts b/src/services/datasource.test.ts index aff7a35df..dbf0d0663 100644 --- a/src/services/datasource.test.ts +++ b/src/services/datasource.test.ts @@ -1,4 +1,4 @@ -import { SceneDataQueryResourceRequest, WrappedLokiDatasource } from './datasource'; +import { WrappedLokiDatasource } from './datasource'; import { DataFrame, DataQueryRequest, @@ -7,10 +7,11 @@ import { dateTime, LoadingState, } from '@grafana/data'; -import { LokiQuery } from './query'; import { Observable } from 'rxjs'; import { DataSourceWithBackend } from '@grafana/runtime'; import { DetectedFieldsResponse } from './fields'; +import { LokiQuery } from './lokiQuery'; +import { SceneDataQueryResourceRequest } from './datasourceTypes'; jest.mock('@grafana/runtime', () => ({ ...jest.requireActual('@grafana/runtime'), diff --git a/src/services/datasource.ts b/src/services/datasource.ts index 81ddb61a1..4a831fb30 100644 --- a/src/services/datasource.ts +++ b/src/services/datasource.ts @@ -9,27 +9,21 @@ import { TestDataSourceResponse, } from '@grafana/data'; import { config, DataSourceWithBackend, getDataSourceSrv } from '@grafana/runtime'; -import { RuntimeDataSource, SceneObject, sceneUtils } from '@grafana/scenes'; +import { RuntimeDataSource, sceneUtils } from '@grafana/scenes'; import { DataQuery } from '@grafana/schema'; import { Observable, Subscriber } from 'rxjs'; import { getDataSource } from './scenes'; -import { LokiQuery } from './query'; import { PLUGIN_ID } from './routing'; import { DetectedFieldsResponse, DetectedLabelsResponse } from './fields'; import { FIELDS_TO_REMOVE, LABELS_TO_REMOVE, sortLabelsByCardinality } from './filters'; import { SERVICE_NAME } from './variables'; import { runShardSplitQuery } from './shardQuerySplitting'; import { requestSupportsSharding } from './logql'; +import { LokiQuery } from './lokiQuery'; +import { SceneDataQueryRequest, SceneDataQueryResourceRequest } from './datasourceTypes'; export const WRAPPED_LOKI_DS_UID = 'wrapped-loki-ds-uid'; -export type SceneDataQueryRequest = DataQueryRequest & { - scopedVars?: { __sceneObject?: { valueOf: () => SceneObject } }; -}; - -export type SceneDataQueryResourceRequest = { - resource: 'volume' | 'patterns' | 'detected_labels' | 'detected_fields'; -}; type TimeStampOfVolumeEval = number; type VolumeCount = string; type VolumeValue = [TimeStampOfVolumeEval, VolumeCount]; @@ -432,8 +426,10 @@ export class WrappedLokiDatasource extends RuntimeDataSource { } } -export function init() { +function init() { sceneUtils.registerRuntimeDataSource({ dataSource: new WrappedLokiDatasource('wrapped-loki-ds', WRAPPED_LOKI_DS_UID), }); } + +export default init; diff --git a/src/services/datasourceTypes.ts b/src/services/datasourceTypes.ts new file mode 100644 index 000000000..1ddbd4a94 --- /dev/null +++ b/src/services/datasourceTypes.ts @@ -0,0 +1,10 @@ +import { DataQueryRequest } from '@grafana/data'; +import { LokiQuery } from './lokiQuery'; +import { SceneObject } from '@grafana/scenes'; + +export type SceneDataQueryRequest = DataQueryRequest & { + scopedVars?: { __sceneObject?: { valueOf: () => SceneObject } }; +}; +export type SceneDataQueryResourceRequest = { + resource: 'volume' | 'patterns' | 'detected_labels' | 'detected_fields'; +}; diff --git a/src/services/expressions.ts b/src/services/expressions.ts index 3685a54c0..a0d00acfb 100644 --- a/src/services/expressions.ts +++ b/src/services/expressions.ts @@ -1,6 +1,4 @@ import { - getFieldsVariable, - getLabelsVariable, LEVEL_VARIABLE_VALUE, VAR_FIELDS_EXPR, JSON_FORMAT_EXPR, @@ -13,6 +11,7 @@ import { isDefined } from './scenes'; import { SceneObject } from '@grafana/scenes'; import { renderLogQLLabelFilters } from './query'; import { getParserFromFieldsFilters } from './fields'; +import { getFieldsVariable, getLabelsVariable } from './variableGetters'; /** * Crafts count over time query that excludes empty values for stream selector name diff --git a/src/services/extensions/links.test.ts b/src/services/extensions/links.test.ts index 1b30d2ba2..461c3fd21 100644 --- a/src/services/extensions/links.test.ts +++ b/src/services/extensions/links.test.ts @@ -1,6 +1,6 @@ import { linkConfigs } from './links'; -import { LokiQuery } from '../query'; import { dateTime } from '@grafana/data'; +import { LokiQuery } from '../lokiQuery'; describe('contextToLink', () => { it('should strip slashes', () => { diff --git a/src/services/extensions/links.ts b/src/services/extensions/links.ts index c1c3ccc06..14c1bd25d 100644 --- a/src/services/extensions/links.ts +++ b/src/services/extensions/links.ts @@ -1,10 +1,10 @@ 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 { SERVICE_NAME, VAR_DATASOURCE, VAR_FIELDS, VAR_LABELS } from 'services/variables'; +import pluginJson from '../../plugin.json'; +import { LokiQuery } from '../lokiQuery'; +import { getMatcherFromQuery } from '../logqlMatchers'; +import { LabelType } from '../fieldsTypes'; const title = 'Open in Explore Logs'; const description = 'Open current query in the Explore Logs view'; @@ -69,3 +69,38 @@ function contextToLink(context?: T) { path: createAppUrl(`/explore/service/${serviceName}/logs`, params), }; } + +export function createAppUrl(path = '/explore', urlParams?: URLSearchParams): string { + return `/a/${pluginJson.id}${path}${urlParams ? `?${urlParams.toString()}` : ''}`; +} + +export const UrlParameters = { + DatasourceId: `var-${VAR_DATASOURCE}`, + TimeRangeFrom: 'from', + TimeRangeTo: 'to', + Labels: `var-${VAR_LABELS}`, + Fields: `var-${VAR_FIELDS}`, +} as const; +export type UrlParameterType = (typeof UrlParameters)[keyof typeof UrlParameters]; + +export function setUrlParameter(key: UrlParameterType, value: string, initalParams?: URLSearchParams): URLSearchParams { + const searchParams = new URLSearchParams(initalParams?.toString() ?? location.search); + searchParams.set(key, value); + + return searchParams; +} + +export function appendUrlParameter( + key: UrlParameterType, + value: string, + initalParams?: URLSearchParams +): URLSearchParams { + const searchParams = new URLSearchParams(initalParams?.toString() ?? location.search); + searchParams.append(key, value); + + return searchParams; +} + +export function replaceSlash(parameter: string): string { + return parameter.replace(/\//g, '-'); +} diff --git a/src/services/fields.test.ts b/src/services/fields.test.ts index 5fd26c355..7c40af905 100644 --- a/src/services/fields.test.ts +++ b/src/services/fields.test.ts @@ -1,12 +1,13 @@ import { createDataFrame, FieldType, toDataFrame } from '@grafana/data'; -import { extractParserFromArray, getLabelTypeFromFrame, LabelType } from './fields'; +import { extractParserFromArray, getLabelTypeFromFrame } from './fields'; import { DETECTED_FIELDS_CARDINALITY_NAME, DETECTED_FIELDS_NAME_FIELD, DETECTED_FIELDS_PARSER_NAME, DETECTED_FIELDS_TYPE_NAME, } from './datasource'; +import { LabelType } from './fieldsTypes'; jest.mock('./variables'); diff --git a/src/services/fields.ts b/src/services/fields.ts index 306eaeebf..e3c95c5a2 100644 --- a/src/services/fields.ts +++ b/src/services/fields.ts @@ -9,21 +9,14 @@ import { } from '@grafana/scenes'; import { getColorByIndex } from './scenes'; import { AddToFiltersButton, VariableFilterType } from 'Components/ServiceScene/Breakdowns/AddToFiltersButton'; -import { - getLogsStreamSelector, - getValueFromFieldsFilter, - LEVEL_VARIABLE_VALUE, - LogsQueryOptions, - ParserType, - VAR_FIELDS, - VAR_LABELS, - VAR_LEVELS, -} from './variables'; +import { LEVEL_VARIABLE_VALUE, LogsQueryOptions, ParserType, VAR_FIELDS, VAR_LABELS, VAR_LEVELS } from './variables'; import { setLevelColorOverrides } from './panel'; import { map, Observable } from 'rxjs'; import { SortBy, SortByScene } from '../Components/ServiceScene/Breakdowns/SortByScene'; import { getDetectedFieldsFrame } from '../Components/ServiceScene/ServiceScene'; import { averageFields } from '../Components/ServiceScene/Breakdowns/FieldsBreakdownScene'; +import { getLogsStreamSelector, getValueFromFieldsFilter } from './variableGetters'; +import { LabelType } from './fieldsTypes'; export type DetectedLabel = { label: string; @@ -168,13 +161,6 @@ export function selectFrameTransformation(frame: DataFrame) { }; } -// copied from public/app/plugins/datasource/loki/types.ts -export enum LabelType { - Indexed = 'I', - StructuredMetadata = 'S', - Parsed = 'P', -} - export function getLabelTypeFromFrame(labelKey: string, frame: DataFrame, index = 0): null | LabelType { const typeField = frame.fields.find((field) => field.name === 'labelTypes')?.values[index]; if (!typeField) { diff --git a/src/services/fieldsTypes.ts b/src/services/fieldsTypes.ts new file mode 100644 index 000000000..6d2aad601 --- /dev/null +++ b/src/services/fieldsTypes.ts @@ -0,0 +1,6 @@ +// copied from public/app/plugins/datasource/loki/types.ts +export enum LabelType { + Indexed = 'I', + StructuredMetadata = 'S', + Parsed = 'P', +} diff --git a/src/services/filters.ts b/src/services/filters.ts index 91dfadc8a..a0522093d 100644 --- a/src/services/filters.ts +++ b/src/services/filters.ts @@ -1,6 +1,7 @@ -import { DetectedLabel, LabelType } from './fields'; +import { DetectedLabel } from './fields'; import { ALL_VARIABLE_VALUE, LEVEL_VARIABLE_VALUE, SERVICE_NAME } from './variables'; import { VariableValueOption } from '@grafana/scenes'; +import { LabelType } from './fieldsTypes'; export enum FilterOp { Equal = '=', diff --git a/src/services/labels.ts b/src/services/labels.ts index 53ee8268c..4165ccf92 100644 --- a/src/services/labels.ts +++ b/src/services/labels.ts @@ -1,8 +1,9 @@ import { SceneFlexItem, SceneFlexLayout, SceneObject } from '@grafana/scenes'; -import { getFieldsVariable, getLogsStreamSelector, LEVEL_VARIABLE_VALUE } from './variables'; +import { LEVEL_VARIABLE_VALUE } from './variables'; import { getParserFromFieldsFilters } from './fields'; import { buildDataQuery } from './query'; import { LabelBreakdownScene } from '../Components/ServiceScene/Breakdowns/LabelBreakdownScene'; +import { getFieldsVariable, getLogsStreamSelector } from './variableGetters'; export const LABEL_BREAKDOWN_GRID_TEMPLATE_COLUMNS = 'repeat(auto-fit, minmax(400px, 1fr))'; diff --git a/src/services/levels.test.ts b/src/services/levels.test.ts index 16d0403cf..925595655 100644 --- a/src/services/levels.test.ts +++ b/src/services/levels.test.ts @@ -1,13 +1,15 @@ import { SeriesVisibilityChangeMode } from '@grafana/ui'; import { getLabelsFromSeries, getVisibleLevels, toggleLevelFromFilter, toggleLevelVisibility } from './levels'; import { AdHocVariableFilter, FieldType, toDataFrame } from '@grafana/data'; -import { getLevelsVariable, VAR_LEVELS } from './variables'; +import { VAR_LEVELS } from './variables'; import { AdHocFiltersVariable, SceneObject } from '@grafana/scenes'; import { FilterOp } from './filters'; import { addToFilters, replaceFilter } from 'Components/ServiceScene/Breakdowns/AddToFiltersButton'; import { LEVEL_NAME } from 'Components/Table/constants'; +import { getLevelsVariable } from './variableGetters'; jest.mock('./variables'); +jest.mock('./variableGetters'); jest.mock('Components/ServiceScene/Breakdowns/AddToFiltersButton'); const ALL_LEVELS = ['logs', 'debug', 'info', 'warn', 'error', 'crit']; diff --git a/src/services/levels.ts b/src/services/levels.ts index 2cce91c27..5f0d0bebf 100644 --- a/src/services/levels.ts +++ b/src/services/levels.ts @@ -1,9 +1,10 @@ import { DataFrame } from '@grafana/data'; import { SeriesVisibilityChangeMode } from '@grafana/ui'; -import { getLevelsVariable, LEVEL_VARIABLE_VALUE } from './variables'; +import { LEVEL_VARIABLE_VALUE } from './variables'; import { SceneObject } from '@grafana/scenes'; import { FilterOp } from './filters'; import { addToFilters, replaceFilter } from 'Components/ServiceScene/Breakdowns/AddToFiltersButton'; +import { getLevelsVariable } from './variableGetters'; /** * Given a set of `visibleLevels` in a panel, it returns a list of the new visible levels diff --git a/src/services/logql.ts b/src/services/logql.ts index 018538443..965540711 100644 --- a/src/services/logql.ts +++ b/src/services/logql.ts @@ -1,98 +1,7 @@ -import { NodeType, SyntaxNode, Tree } from '@lezer/common'; - -import { Identifier, Matcher, MetricExpr, parser, Selector, String } from '@grafana/lezer-logql'; -import { Filter, FilterOp } from './filters'; -import { LabelType } from './fields'; -import { LokiQuery } from './query'; -import { SceneDataQueryRequest } from './datasource'; - -export class NodePosition { - from: number; - to: number; - type: NodeType; - syntaxNode: SyntaxNode; - - constructor(from: number, to: number, syntaxNode: SyntaxNode, type: NodeType) { - this.from = from; - this.to = to; - this.type = type; - this.syntaxNode = syntaxNode; - } - - static fromNode(node: SyntaxNode): NodePosition { - return new NodePosition(node.from, node.to, node, node.type); - } - - contains(position: NodePosition): boolean { - return this.from <= position.from && this.to >= position.to; - } - - getExpression(query: string): string { - return query.substring(this.from, this.to); - } -} - -export function getNodesFromQuery(query: string, nodeTypes?: number[]): SyntaxNode[] { - const nodes: SyntaxNode[] = []; - const tree: Tree = parser.parse(query); - tree.iterate({ - enter: (node): false | void => { - if (nodeTypes === undefined || nodeTypes.includes(node.type.id)) { - nodes.push(node.node); - } - }, - }); - return nodes; -} - -function getAllPositionsInNodeByType(node: SyntaxNode, type: number): NodePosition[] { - if (node.type.id === type) { - return [NodePosition.fromNode(node)]; - } - - const positions: NodePosition[] = []; - let pos = 0; - let child = node.childAfter(pos); - while (child) { - positions.push(...getAllPositionsInNodeByType(child, type)); - pos = child.to; - child = node.childAfter(pos); - } - return positions; -} - -export function getMatcherFromQuery(query: string): Filter[] { - const filter: Filter[] = []; - const selector = getNodesFromQuery(query, [Selector]); - if (selector.length === 0) { - return filter; - } - const selectorPosition = NodePosition.fromNode(selector[0]); - - const allMatcher = getNodesFromQuery(query, [Matcher]); - for (const matcher of allMatcher) { - const matcherPosition = NodePosition.fromNode(matcher); - const identifierPosition = getAllPositionsInNodeByType(matcher, Identifier); - const valuePosition = getAllPositionsInNodeByType(matcher, String); - const operation = query.substring(identifierPosition[0].to, valuePosition[0].from); - const op = operation === '=' ? FilterOp.Equal : FilterOp.NotEqual; - const key = identifierPosition[0].getExpression(query); - const value = valuePosition.map((position) => query.substring(position.from + 1, position.to - 1))[0]; - - if (!key || !value) { - continue; - } - - filter.push({ - key, - operator: op, - value, - type: selectorPosition.contains(matcherPosition) ? LabelType.Indexed : undefined, - }); - } - - return filter; -} +import { Identifier, Matcher, MetricExpr, parser, String } from '@grafana/lezer-logql'; +import { LokiQuery } from './lokiQuery'; +import { getNodesFromQuery } from './logqlMatchers'; +import { SceneDataQueryRequest } from './datasourceTypes'; export function isQueryWithNode(query: string, nodeType: number): boolean { let isQueryWithNode = false; diff --git a/src/services/logqlMatchers.ts b/src/services/logqlMatchers.ts new file mode 100644 index 000000000..6f42c39be --- /dev/null +++ b/src/services/logqlMatchers.ts @@ -0,0 +1,92 @@ +import { Filter, FilterOp } from './filters'; +import { Identifier, Matcher, parser, Selector, String } from '@grafana/lezer-logql'; +import { NodeType, SyntaxNode, Tree } from '@lezer/common'; +import { LabelType } from './fieldsTypes'; + +export class NodePosition { + from: number; + to: number; + type: NodeType; + syntaxNode: SyntaxNode; + + constructor(from: number, to: number, syntaxNode: SyntaxNode, type: NodeType) { + this.from = from; + this.to = to; + this.type = type; + this.syntaxNode = syntaxNode; + } + + static fromNode(node: SyntaxNode): NodePosition { + return new NodePosition(node.from, node.to, node, node.type); + } + + contains(position: NodePosition): boolean { + return this.from <= position.from && this.to >= position.to; + } + + getExpression(query: string): string { + return query.substring(this.from, this.to); + } +} + +export function getNodesFromQuery(query: string, nodeTypes?: number[]): SyntaxNode[] { + const nodes: SyntaxNode[] = []; + const tree: Tree = parser.parse(query); + tree.iterate({ + enter: (node): false | void => { + if (nodeTypes === undefined || nodeTypes.includes(node.type.id)) { + nodes.push(node.node); + } + }, + }); + return nodes; +} + +function getAllPositionsInNodeByType(node: SyntaxNode, type: number): NodePosition[] { + if (node.type.id === type) { + return [NodePosition.fromNode(node)]; + } + + const positions: NodePosition[] = []; + let pos = 0; + let child = node.childAfter(pos); + while (child) { + positions.push(...getAllPositionsInNodeByType(child, type)); + pos = child.to; + child = node.childAfter(pos); + } + return positions; +} + +export function getMatcherFromQuery(query: string): Filter[] { + const filter: Filter[] = []; + const selector = getNodesFromQuery(query, [Selector]); + if (selector.length === 0) { + return filter; + } + const selectorPosition = NodePosition.fromNode(selector[0]); + + const allMatcher = getNodesFromQuery(query, [Matcher]); + for (const matcher of allMatcher) { + const matcherPosition = NodePosition.fromNode(matcher); + const identifierPosition = getAllPositionsInNodeByType(matcher, Identifier); + const valuePosition = getAllPositionsInNodeByType(matcher, String); + const operation = query.substring(identifierPosition[0].to, valuePosition[0].from); + const op = operation === '=' ? FilterOp.Equal : FilterOp.NotEqual; + const key = identifierPosition[0].getExpression(query); + const value = valuePosition.map((position) => query.substring(position.from + 1, position.to - 1))[0]; + + if (!key || !value) { + continue; + } + + filter.push({ + key, + operator: op, + value, + type: selectorPosition.contains(matcherPosition) ? LabelType.Indexed : undefined, + }); + } + + return filter; +} diff --git a/src/services/lokiQuery.ts b/src/services/lokiQuery.ts new file mode 100644 index 000000000..96cd8b696 --- /dev/null +++ b/src/services/lokiQuery.ts @@ -0,0 +1,13 @@ +import { DataSourceRef } from '@grafana/schema'; + +export type LokiQuery = { + refId: string; + queryType: string; + editorMode: string; + supportingQueryType: string; + expr: string; + legendFormat?: string; + splitDuration?: string; + datasource?: DataSourceRef; + maxLines?: number; +}; diff --git a/src/services/navigate.ts b/src/services/navigate.ts index 3b04943ee..a2e730288 100644 --- a/src/services/navigate.ts +++ b/src/services/navigate.ts @@ -3,17 +3,10 @@ import { IndexScene } from '../Components/IndexScene/IndexScene'; import { ALL_VARIABLE_VALUE } from './variables'; import { getMetadataService } from './metadata'; import { locationService } from '@grafana/runtime'; -import { - buildServicesUrl, - DRILLDOWN_URL_KEYS, - PageSlugs, - prefixRoute, - replaceSlash, - ROUTES, - ValueSlugs, -} from './routing'; +import { buildServicesUrl, DRILLDOWN_URL_KEYS, PageSlugs, prefixRoute, ROUTES, ValueSlugs } from './routing'; import { sceneGraph } from '@grafana/scenes'; import { UrlQueryMap, urlUtil } from '@grafana/data'; +import { replaceSlash } from './extensions/links'; function buildValueBreakdownUrl(label: string, newPath: ValueSlugs, serviceString: string) { if (label === ALL_VARIABLE_VALUE && newPath === ValueSlugs.label) { diff --git a/src/services/panel.ts b/src/services/panel.ts index fea83adfb..b3fd1aa3f 100644 --- a/src/services/panel.ts +++ b/src/services/panel.ts @@ -11,12 +11,12 @@ import { VizPanel, } from '@grafana/scenes'; import { map, Observable } from 'rxjs'; -import { LokiQuery } from './query'; import { HideSeriesConfig } from '@grafana/schema'; import { WRAPPED_LOKI_DS_UID } from './datasource'; import { LogsSceneQueryRunner } from './LogsSceneQueryRunner'; import { DrawStyle, StackingMode } from '@grafana/ui'; import { getLabelsFromSeries, getVisibleLevels } from './levels'; +import { LokiQuery } from './lokiQuery'; const UNKNOWN_LEVEL_LOGS = 'logs'; export function setLevelColorOverrides(overrides: FieldConfigOverridesBuilder) { diff --git a/src/services/query.test.ts b/src/services/query.test.ts index 962ca936b..aa9812355 100644 --- a/src/services/query.test.ts +++ b/src/services/query.test.ts @@ -1,7 +1,8 @@ import { AdHocVariableFilter } from '@grafana/data'; import { buildDataQuery, renderLogQLFieldFilters, renderLogQLLabelFilters } from './query'; import { FilterOp } from './filters'; -import { FieldValue } from '../Components/ServiceScene/Breakdowns/AddToFiltersButton'; + +import { FieldValue } from './variables'; describe('buildDataQuery', () => { test('Given an expression outputs a Loki query', () => { diff --git a/src/services/query.ts b/src/services/query.ts index ce59b789b..8ff7bb49e 100644 --- a/src/services/query.ts +++ b/src/services/query.ts @@ -1,23 +1,12 @@ import { AdHocVariableFilter } from '@grafana/data'; -import { DataSourceRef } from '@grafana/schema'; import { AppliedPattern } from 'Components/IndexScene/IndexScene'; import { PLUGIN_ID } from './routing'; -import { SceneDataQueryResourceRequest } from './datasource'; -import { EMPTY_VARIABLE_VALUE, getValueFromFieldsFilter, VAR_DATASOURCE_EXPR } from './variables'; +import { EMPTY_VARIABLE_VALUE, VAR_DATASOURCE_EXPR } from './variables'; import { FilterOp } from './filters'; import { groupBy, trim } from 'lodash'; - -export type LokiQuery = { - refId: string; - queryType: string; - editorMode: string; - supportingQueryType: string; - expr: string; - legendFormat?: string; - splitDuration?: string; - datasource?: DataSourceRef; - maxLines?: number; -}; +import { getValueFromFieldsFilter } from './variableGetters'; +import { LokiQuery } from './lokiQuery'; +import { SceneDataQueryResourceRequest } from './datasourceTypes'; /** * Builds the resource query diff --git a/src/services/routing.ts b/src/services/routing.ts index 69d56c35c..d52e76296 100644 --- a/src/services/routing.ts +++ b/src/services/routing.ts @@ -12,6 +12,7 @@ import { } from './variables'; import { locationService } from '@grafana/runtime'; import { SceneRouteMatch } from '@grafana/scenes'; +import { replaceSlash } from './extensions/links'; export const PLUGIN_ID = pluginJson.id; export const PLUGIN_BASE_URL = `/a/${PLUGIN_ID}`; @@ -36,10 +37,6 @@ export type ParentDrilldownSlugs = | PageSlugs.patterns; export type ChildDrilldownSlugs = ValueSlugs.field | ValueSlugs.label; -export function replaceSlash(parameter: string): string { - return parameter.replace(/\//g, '-'); -} - export const ROUTES = { explore: () => prefixRoute(PageSlugs.explore), logs: (service: string) => prefixRoute(`${PageSlugs.explore}/service/${replaceSlash(service)}/${PageSlugs.logs}`), @@ -137,34 +134,3 @@ export function buildServicesRoute(extraQueryParams?: UrlQueryMap): UrlQueryMap ...extraQueryParams, }; } - -export function createAppUrl(path = '/explore', urlParams?: URLSearchParams): string { - return `/a/${pluginJson.id}${path}${urlParams ? `?${urlParams.toString()}` : ''}`; -} - -export const UrlParameters = { - DatasourceId: `var-${VAR_DATASOURCE}`, - TimeRangeFrom: 'from', - TimeRangeTo: 'to', - Labels: `var-${VAR_LABELS}`, - Fields: `var-${VAR_FIELDS}`, -} as const; -export type UrlParameterType = (typeof UrlParameters)[keyof typeof UrlParameters]; - -export function setUrlParameter(key: UrlParameterType, value: string, initalParams?: URLSearchParams): URLSearchParams { - const searchParams = new URLSearchParams(initalParams?.toString() ?? location.search); - searchParams.set(key, value); - - return searchParams; -} - -export function appendUrlParameter( - key: UrlParameterType, - value: string, - initalParams?: URLSearchParams -): URLSearchParams { - const searchParams = new URLSearchParams(initalParams?.toString() ?? location.search); - searchParams.append(key, value); - - return searchParams; -} diff --git a/src/services/shardQuerySplitting.test.ts b/src/services/shardQuerySplitting.test.ts index dd0ecaa1b..7e07e07c2 100644 --- a/src/services/shardQuerySplitting.test.ts +++ b/src/services/shardQuerySplitting.test.ts @@ -5,7 +5,7 @@ import { DataQueryRequest, DataQueryResponse, dateTime, LoadingState } from '@gr import { runShardSplitQuery } from './shardQuerySplitting'; import { DataSourceWithBackend } from '@grafana/runtime'; -import { LokiQuery } from './query'; +import { LokiQuery } from './lokiQuery'; jest.mock('uuid', () => ({ v4: jest.fn().mockReturnValue('uuid'), diff --git a/src/services/shardQuerySplitting.ts b/src/services/shardQuerySplitting.ts index 91ffe63e7..619e07e86 100644 --- a/src/services/shardQuerySplitting.ts +++ b/src/services/shardQuerySplitting.ts @@ -2,7 +2,6 @@ import { Observable, Subscriber, Subscription } from 'rxjs'; import { v4 as uuidv4 } from 'uuid'; import { DataQueryRequest, LoadingState, DataQueryResponse, TimeRange } from '@grafana/data'; -import { LokiQuery } from './query'; import { addShardingPlaceholderSelector, getServiceNameFromQuery, @@ -11,6 +10,7 @@ import { } from './logql'; import { combineResponses } from './combineResponses'; import { DataSourceWithBackend } from '@grafana/runtime'; +import { LokiQuery } from './lokiQuery'; /** * Query splitting by stream shards. diff --git a/src/services/store.ts b/src/services/store.ts index ae74c355b..0c90d4774 100644 --- a/src/services/store.ts +++ b/src/services/store.ts @@ -1,7 +1,7 @@ import pluginJson from '../plugin.json'; import { SortBy, SortDirection } from '../Components/ServiceScene/Breakdowns/SortByScene'; -import { getDataSourceName, getServiceName } from './variables'; import { SceneObject } from '@grafana/scenes'; +import { getDataSourceName, getServiceName } from './variableGetters'; const SERVICES_LOCALSTORAGE_KEY = `${pluginJson.id}.services.favorite`; const DS_LOCALSTORAGE_KEY = `${pluginJson.id}.datasource`; diff --git a/src/services/variableGetters.ts b/src/services/variableGetters.ts new file mode 100644 index 000000000..5d6876050 --- /dev/null +++ b/src/services/variableGetters.ts @@ -0,0 +1,171 @@ +import { + AdHocFiltersVariable, + CustomVariable, + DataSourceVariable, + sceneGraph, + SceneObject, + SceneVariableState, +} from '@grafana/scenes'; +import { CustomConstantVariable } from './CustomConstantVariable'; +import { + AdHocFieldValue, + FieldValue, + JSON_FORMAT_EXPR, + LOGS_FORMAT_EXPR, + LogsQueryOptions, + MIXED_FORMAT_EXPR, + SERVICE_NAME, + VAR_DATASOURCE, + VAR_FIELD_GROUP_BY, + VAR_FIELDS, + VAR_FIELDS_EXPR, + VAR_LABEL_GROUP_BY, + VAR_LABELS, + VAR_LABELS_EXPR, + VAR_LEVELS, + VAR_LEVELS_EXPR, + VAR_LINE_FILTER, + VAR_LINE_FILTER_EXPR, + VAR_PATTERNS, + VAR_PATTERNS_EXPR, + VAR_SERVICE, +} from './variables'; +import { AdHocVariableFilter } from '@grafana/data'; + +export function getServiceSelectionStringVariable(sceneRef: SceneObject) { + const variable = sceneGraph.lookupVariable(VAR_SERVICE, sceneRef); + if (!(variable instanceof CustomConstantVariable)) { + throw new Error('VAR_SERVICE not found'); + } + return variable; +} + +export function getPatternsVariable(scene: SceneObject) { + const variable = sceneGraph.lookupVariable(VAR_PATTERNS, scene); + if (!(variable instanceof CustomVariable)) { + throw new Error('VAR_PATTERNS not found'); + } + return variable; +} + +export function getLabelsVariable(scene: SceneObject) { + return getAdHocFiltersVariable(VAR_LABELS, scene); +} + +export function getFieldsVariable(scene: SceneObject) { + return getAdHocFiltersVariable(VAR_FIELDS, scene); +} + +export function getLevelsVariable(scene: SceneObject) { + return getAdHocFiltersVariable(VAR_LEVELS, scene); +} + +export function getLineFilterVariable(scene: SceneObject) { + const variable = sceneGraph.lookupVariable(VAR_LINE_FILTER, scene); + if (!(variable instanceof CustomVariable)) { + throw new Error('VAR_LINE_FILTER not found'); + } + return variable; +} + +export function getLabelGroupByVariable(scene: SceneObject) { + const variable = sceneGraph.lookupVariable(VAR_LABEL_GROUP_BY, scene); + if (!(variable instanceof CustomConstantVariable)) { + throw new Error('VAR_LABEL_GROUP_BY not found'); + } + return variable; +} + +export function getFieldGroupByVariable(scene: SceneObject) { + const variable = sceneGraph.lookupVariable(VAR_FIELD_GROUP_BY, scene); + if (!(variable instanceof CustomConstantVariable)) { + throw new Error('VAR_FIELD_GROUP_BY not found'); + } + return variable; +} + +export function getDataSourceVariable(scene: SceneObject) { + const variable = sceneGraph.lookupVariable(VAR_DATASOURCE, scene); + if (!(variable instanceof DataSourceVariable)) { + throw new Error('VAR_DATASOURCE not found'); + } + return variable; +} + +export function getAdHocFiltersVariable(variableName: string, scene: SceneObject) { + const variable = sceneGraph.lookupVariable(variableName, scene); + + if (!(variable instanceof AdHocFiltersVariable)) { + throw new Error(`Could not get AdHocFiltersVariable ${variableName}. Variable not found.`); + } + return variable; +} + +export function getLogsStreamSelector(options: LogsQueryOptions) { + const { + labelExpressionToAdd = '', + structuredMetadataToAdd = '', + fieldExpressionToAdd = '', + parser = undefined, + } = options; + + switch (parser) { + case 'structuredMetadata': + return `{${VAR_LABELS_EXPR}${labelExpressionToAdd}} ${structuredMetadataToAdd} ${VAR_LEVELS_EXPR} ${VAR_PATTERNS_EXPR} ${VAR_LINE_FILTER_EXPR}`; + case 'json': + return `{${VAR_LABELS_EXPR}${labelExpressionToAdd}} ${structuredMetadataToAdd} ${VAR_LEVELS_EXPR} ${VAR_PATTERNS_EXPR} ${VAR_LINE_FILTER_EXPR} ${JSON_FORMAT_EXPR} ${fieldExpressionToAdd} ${VAR_FIELDS_EXPR}`; + case 'logfmt': + return `{${VAR_LABELS_EXPR}${labelExpressionToAdd}} ${structuredMetadataToAdd} ${VAR_LEVELS_EXPR} ${VAR_PATTERNS_EXPR} ${VAR_LINE_FILTER_EXPR} ${LOGS_FORMAT_EXPR} ${fieldExpressionToAdd} ${VAR_FIELDS_EXPR}`; + default: + return `{${VAR_LABELS_EXPR}${labelExpressionToAdd}} ${structuredMetadataToAdd} ${VAR_LEVELS_EXPR} ${VAR_PATTERNS_EXPR} ${VAR_LINE_FILTER_EXPR} ${MIXED_FORMAT_EXPR} ${fieldExpressionToAdd} ${VAR_FIELDS_EXPR}`; + } +} + +export function getUrlParamNameForVariable(variableName: string) { + return `var-${variableName}`; +} + +export function getValueFromFieldsFilter(filter: AdHocVariableFilter, variableName: string = VAR_FIELDS): FieldValue { + try { + return JSON.parse(filter.value); + } catch (e) { + console.error(`Failed to parse ${variableName}`, e); + throw e; + } +} + +export function getValueFromAdHocVariableFilter( + variable: AdHocFiltersVariable, + filter?: AdHocVariableFilter +): AdHocFieldValue { + if (variable.state.name === VAR_FIELDS && filter) { + return getValueFromFieldsFilter(filter); + } + + return { + value: filter?.value, + }; +} + +export function getServiceName(scene: SceneObject) { + const labelsVariable = getLabelsVariable(scene); + return getServiceNameFromVariableState(labelsVariable.state); +} + +export function getServiceNameFromVariableState( + adHocFiltersVariableState: SceneVariableState & { filters: AdHocVariableFilter[] } +) { + const serviceName = adHocFiltersVariableState.filters + .filter((filter) => filter.key === SERVICE_NAME) + .map((filter) => filter.value); + + if (!serviceName) { + throw new Error('Service present in filters selected'); + } + return serviceName[0]; +} + +export function getDataSourceName(scene: SceneObject) { + const dsVariable = getDataSourceVariable(scene); + return dsVariable.getValue(); +} diff --git a/src/services/variables.ts b/src/services/variables.ts index eb6a9263a..70faa0f80 100644 --- a/src/services/variables.ts +++ b/src/services/variables.ts @@ -1,14 +1,21 @@ -import { - AdHocFiltersVariable, - CustomVariable, - DataSourceVariable, - sceneGraph, - SceneObject, - SceneVariableState, -} from '@grafana/scenes'; -import { CustomConstantVariable } from './CustomConstantVariable'; -import { AdHocVariableFilter } from '@grafana/data'; -import { AdHocFieldValue, FieldValue } from '../Components/ServiceScene/Breakdowns/AddToFiltersButton'; +export interface FieldValue { + value: string; + parser: ParserType; +} + +export interface AdHocFieldValue { + value?: string; + parser?: ParserType; +} + +export type ParserType = 'logfmt' | 'json' | 'mixed' | 'structuredMetadata'; + +export type LogsQueryOptions = { + labelExpressionToAdd?: string; + structuredMetadataToAdd?: string; + fieldExpressionToAdd?: string; + parser?: ParserType; +}; export const VAR_LABELS = 'filters'; export const VAR_LABELS_EXPR = '${filters}'; @@ -40,150 +47,3 @@ export const ALL_VARIABLE_VALUE = '$__all'; export const LEVEL_VARIABLE_VALUE = 'detected_level'; export const SERVICE_NAME = 'service_name'; export const EMPTY_VARIABLE_VALUE = '""'; - -export type ParserType = 'logfmt' | 'json' | 'mixed' | 'structuredMetadata'; - -export type LogsQueryOptions = { - labelExpressionToAdd?: string; - structuredMetadataToAdd?: string; - fieldExpressionToAdd?: string; - parser?: ParserType; -}; - -export function getLogsStreamSelector(options: LogsQueryOptions) { - const { - labelExpressionToAdd = '', - structuredMetadataToAdd = '', - fieldExpressionToAdd = '', - parser = undefined, - } = options; - - switch (parser) { - case 'structuredMetadata': - return `{${VAR_LABELS_EXPR}${labelExpressionToAdd}} ${structuredMetadataToAdd} ${VAR_LEVELS_EXPR} ${VAR_PATTERNS_EXPR} ${VAR_LINE_FILTER_EXPR}`; - case 'json': - return `{${VAR_LABELS_EXPR}${labelExpressionToAdd}} ${structuredMetadataToAdd} ${VAR_LEVELS_EXPR} ${VAR_PATTERNS_EXPR} ${VAR_LINE_FILTER_EXPR} ${JSON_FORMAT_EXPR} ${fieldExpressionToAdd} ${VAR_FIELDS_EXPR}`; - case 'logfmt': - return `{${VAR_LABELS_EXPR}${labelExpressionToAdd}} ${structuredMetadataToAdd} ${VAR_LEVELS_EXPR} ${VAR_PATTERNS_EXPR} ${VAR_LINE_FILTER_EXPR} ${LOGS_FORMAT_EXPR} ${fieldExpressionToAdd} ${VAR_FIELDS_EXPR}`; - default: - return `{${VAR_LABELS_EXPR}${labelExpressionToAdd}} ${structuredMetadataToAdd} ${VAR_LEVELS_EXPR} ${VAR_PATTERNS_EXPR} ${VAR_LINE_FILTER_EXPR} ${MIXED_FORMAT_EXPR} ${fieldExpressionToAdd} ${VAR_FIELDS_EXPR}`; - } -} - -export function getPatternsVariable(scene: SceneObject) { - const variable = sceneGraph.lookupVariable(VAR_PATTERNS, scene); - if (!(variable instanceof CustomVariable)) { - throw new Error('VAR_PATTERNS not found'); - } - return variable; -} - -export function getLabelsVariable(scene: SceneObject) { - return getAdHocFiltersVariable(VAR_LABELS, scene); -} - -export function getFieldsVariable(scene: SceneObject) { - return getAdHocFiltersVariable(VAR_FIELDS, scene); -} - -export function getLevelsVariable(scene: SceneObject) { - return getAdHocFiltersVariable(VAR_LEVELS, scene); -} - -export function getLineFilterVariable(scene: SceneObject) { - const variable = sceneGraph.lookupVariable(VAR_LINE_FILTER, scene); - if (!(variable instanceof CustomVariable)) { - throw new Error('VAR_LINE_FILTER not found'); - } - return variable; -} - -export function getLabelGroupByVariable(scene: SceneObject) { - const variable = sceneGraph.lookupVariable(VAR_LABEL_GROUP_BY, scene); - if (!(variable instanceof CustomConstantVariable)) { - throw new Error('VAR_LABEL_GROUP_BY not found'); - } - return variable; -} - -export function getFieldGroupByVariable(scene: SceneObject) { - const variable = sceneGraph.lookupVariable(VAR_FIELD_GROUP_BY, scene); - if (!(variable instanceof CustomConstantVariable)) { - throw new Error('VAR_FIELD_GROUP_BY not found'); - } - return variable; -} - -export function getDataSourceVariable(scene: SceneObject) { - const variable = sceneGraph.lookupVariable(VAR_DATASOURCE, scene); - if (!(variable instanceof DataSourceVariable)) { - throw new Error('VAR_DATASOURCE not found'); - } - return variable; -} - -export function getAdHocFiltersVariable(variableName: string, scene: SceneObject) { - const variable = sceneGraph.lookupVariable(variableName, scene); - - if (!(variable instanceof AdHocFiltersVariable)) { - throw new Error(`Could not get AdHocFiltersVariable ${variableName}. Variable not found.`); - } - return variable; -} - -export function getServiceSelectionStringVariable(sceneRef: SceneObject) { - const variable = sceneGraph.lookupVariable(VAR_SERVICE, sceneRef); - if (!(variable instanceof CustomConstantVariable)) { - throw new Error('VAR_SERVICE not found'); - } - return variable; -} - -export function getUrlParamNameForVariable(variableName: string) { - return `var-${variableName}`; -} - -export function getValueFromFieldsFilter(filter: AdHocVariableFilter, variableName: string = VAR_FIELDS): FieldValue { - try { - return JSON.parse(filter.value); - } catch (e) { - console.error(`Failed to parse ${variableName}`, e); - throw e; - } -} - -export function getValueFromAdHocVariableFilter( - variable: AdHocFiltersVariable, - filter?: AdHocVariableFilter -): AdHocFieldValue { - if (variable.state.name === VAR_FIELDS && filter) { - return getValueFromFieldsFilter(filter); - } - - return { - value: filter?.value, - }; -} - -export function getServiceName(scene: SceneObject) { - const labelsVariable = getLabelsVariable(scene); - return getServiceNameFromVariableState(labelsVariable.state); -} - -export function getServiceNameFromVariableState( - adHocFiltersVariableState: SceneVariableState & { filters: AdHocVariableFilter[] } -) { - const serviceName = adHocFiltersVariableState.filters - .filter((filter) => filter.key === SERVICE_NAME) - .map((filter) => filter.value); - - if (!serviceName) { - throw new Error('Service present in filters selected'); - } - return serviceName[0]; -} - -export function getDataSourceName(scene: SceneObject) { - const dsVariable = getDataSourceVariable(scene); - return dsVariable.getValue(); -} diff --git a/tests/exploreServicesBreakDown.spec.ts b/tests/exploreServicesBreakDown.spec.ts index bb94f9535..f7aee770d 100644 --- a/tests/exploreServicesBreakDown.spec.ts +++ b/tests/exploreServicesBreakDown.spec.ts @@ -2,7 +2,7 @@ import { expect, test } from '@grafana/plugin-e2e'; import { ExplorePage, PlaywrightRequest } from './fixtures/explore'; import { testIds } from '../src/services/testIds'; import { mockEmptyQueryApiResponse } from './mocks/mockEmptyQueryApiResponse'; -import { LokiQuery } from '../src/services/query'; +import { LokiQuery } from '../src/services/lokiQuery'; const fieldName = 'caller'; const levelName = 'detected_level'; diff --git a/tests/exploreServicesJsonBreakDown.spec.ts b/tests/exploreServicesJsonBreakDown.spec.ts index beba398f2..e5be0a3be 100644 --- a/tests/exploreServicesJsonBreakDown.spec.ts +++ b/tests/exploreServicesJsonBreakDown.spec.ts @@ -1,6 +1,7 @@ import { expect, test } from '@grafana/plugin-e2e'; import { ExplorePage, PlaywrightRequest } from './fixtures/explore'; -import { LokiQuery } from '../src/services/query'; + +import { LokiQuery } from '../src/services/lokiQuery'; const fieldName = 'method'; // const levelName = 'cluster' diff --git a/tests/exploreServicesJsonMixedBreakDown.spec.ts b/tests/exploreServicesJsonMixedBreakDown.spec.ts index b32a28125..8e7e38f09 100644 --- a/tests/exploreServicesJsonMixedBreakDown.spec.ts +++ b/tests/exploreServicesJsonMixedBreakDown.spec.ts @@ -1,6 +1,7 @@ import { expect, test } from '@grafana/plugin-e2e'; import { ExplorePage, PlaywrightRequest } from './fixtures/explore'; -import { LokiQuery } from '../src/services/query'; + +import { LokiQuery } from '../src/services/lokiQuery'; const mixedFieldName = 'method'; const logFmtFieldName = 'caller'; diff --git a/tests/fixtures/explore.ts b/tests/fixtures/explore.ts index bb0677782..17fffd538 100644 --- a/tests/fixtures/explore.ts +++ b/tests/fixtures/explore.ts @@ -2,7 +2,8 @@ import type { Locator, Page } from '@playwright/test'; import pluginJson from '../../src/plugin.json'; import { testIds } from '../../src/services/testIds'; import { expect } from '@grafana/plugin-e2e'; -import { LokiQuery } from '../../src/services/query'; + +import { LokiQuery } from '../../src/services/lokiQuery'; export interface PlaywrightRequest { post: any; diff --git a/webpack.config.ts b/webpack.config.ts index 623b503cc..9a053ec17 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -1,14 +1,18 @@ import type { Configuration } from 'webpack'; import { merge } from 'webpack-merge'; import grafanaConfig from './.config/webpack/webpack.config'; +// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; -const config = async (env): Promise => { +const config = async (env: any): Promise => { const baseConfig = await grafanaConfig(env); return merge(baseConfig, { experiments: { // Required to load WASM modules. asyncWebAssembly: true, }, + plugins: [ + // new BundleAnalyzerPlugin() + ] }); }; diff --git a/yarn.lock b/yarn.lock index 6cd2f72be..aea9e385f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -782,7 +782,7 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@discoveryjs/json-ext@^0.5.0": +"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== @@ -1717,6 +1717,11 @@ dependencies: playwright "1.43.1" +"@polka/url@^1.0.0-next.24": + version "1.0.0-next.25" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.25.tgz#f077fdc0b5d0078d30893396ff4827a13f99e817" + integrity sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ== + "@popperjs/core@2.11.8": version "2.11.8" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" @@ -2679,11 +2684,23 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== +acorn-walk@^8.0.0: + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + acorn-walk@^8.0.2, acorn-walk@^8.1.1: version "8.3.2" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== +acorn@^8.0.4, acorn@^8.11.0: + version "8.12.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== + acorn@^8.1.0, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" @@ -3285,7 +3302,7 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@7: +commander@7, commander@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== @@ -3962,6 +3979,11 @@ date-fns@3.6.0: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf" integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww== +debounce@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" + integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== + debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -4142,6 +4164,11 @@ dot-prop@^5.1.0, dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" +duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + earcut@^2.2.3: version "2.2.4" resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.4.tgz#6d02fd4d68160c114825d06890a92ecaae60343a" @@ -5071,6 +5098,13 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + harmony-reflect@^1.4.6: version "1.6.2" resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.2.tgz#31ecbd32e648a34d030d86adb67d4d47547fe710" @@ -5158,7 +5192,7 @@ html-encoding-sniffer@^3.0.0: dependencies: whatwg-encoding "^2.0.0" -html-escaper@^2.0.0: +html-escaper@^2.0.0, html-escaper@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== @@ -6577,6 +6611,11 @@ monaco-editor@0.34.1: resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.34.1.tgz#1b75c4ad6bc4c1f9da656d740d98e0b850a22f87" integrity sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ== +mrmime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" + integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -6789,6 +6828,11 @@ onetime@^6.0.0: dependencies: mimic-fn "^4.0.0" +opener@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + optionator@^0.9.1: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -8061,6 +8105,15 @@ signal-exit@^4.0.1, signal-exit@^4.1.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +sirv@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" + integrity sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ== + dependencies: + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" + totalist "^3.0.0" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -8598,6 +8651,11 @@ toggle-selection@^1.0.6: resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== + tough-cookie@^4.1.2: version "4.1.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" @@ -8966,6 +9024,24 @@ webidl-conversions@^7.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== +webpack-bundle-analyzer@^4.10.2: + version "4.10.2" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz#633af2862c213730be3dbdf40456db171b60d5bd" + integrity sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw== + dependencies: + "@discoveryjs/json-ext" "0.5.7" + acorn "^8.0.4" + acorn-walk "^8.0.0" + commander "^7.2.0" + debounce "^1.2.1" + escape-string-regexp "^4.0.0" + gzip-size "^6.0.0" + html-escaper "^2.0.2" + opener "^1.5.2" + picocolors "^1.0.0" + sirv "^2.0.3" + ws "^7.3.1" + webpack-cli@^5.1.4: version "5.1.4" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" @@ -9166,6 +9242,11 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" +ws@^7.3.1: + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== + ws@^8.11.0: version "8.16.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4"