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

Transformations: Add frame source picker to allow transforming annotations #77842

Merged
merged 20 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 0 additions & 3 deletions .betterer.results
Original file line number Diff line number Diff line change
Expand Up @@ -2799,9 +2799,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Styles should be written using objects.", "9"],
[0, 0, 0, "Styles should be written using objects.", "10"]
],
"public/app/features/dashboard/components/TransformationsEditor/TransformationFilter.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"]
],
"public/app/features/dashboard/components/TransformationsEditor/TransformationsEditor.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ use the output of one transformation as the input to another transformation, etc
| `options` | | **Yes** | | Options to be passed to the transformer<br/>Valid options depend on the transformer id |
| `disabled` | boolean | No | | Disabled transformations are skipped |
| `filter` | [MatcherConfig](#matcherconfig) | No | | Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation.<br/>It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. |
| `topic` | string | No | | Where to pull DataFrames from as input to transformation<br/>Possible values are: `series`, `annotations`, `alertStates`. |

### MatcherConfig

Expand Down
2 changes: 2 additions & 0 deletions kinds/dashboard/dashboard_kind.cue
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,8 @@ lineage: schemas: [{
disabled?: bool
// Optional frame matcher. When missing it will be applied to all results
filter?: #MatcherConfig
// Where to pull DataFrames from as input to transformation
topic?: "series" | "annotations" | "alertStates" // replaced with common.DataTopic
// Options to be passed to the transformer
// Valid options depend on the transformer id
options: _
Expand Down
13 changes: 7 additions & 6 deletions packages/grafana-data/src/types/query.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { DataQuery as SchemaDataQuery, DataSourceRef as SchemaDataSourceRef } from '@grafana/schema';
import {
DataQuery as SchemaDataQuery,
DataSourceRef as SchemaDataSourceRef,
DataTopic as SchemaDataTopic,
} from '@grafana/schema';

/**
* @deprecated use the type from @grafana/schema
Expand All @@ -13,12 +17,9 @@ export interface DataSourceRef extends SchemaDataSourceRef {}
/**
* Attached to query results (not persisted)
*
* @public
* @deprecated use the type from @grafana/schema
*/
export enum DataTopic {
Annotations = 'annotations',
AlertStates = 'alertStates',
}
export { SchemaDataTopic as DataTopic };

/**
* Abstract representation of any label-based query
Expand Down
10 changes: 10 additions & 0 deletions packages/grafana-schema/src/common/common.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@
// Run 'make gen-cue' from repository root to regenerate.


/**
* A topic is attached to DataFrame metadata in query results.
* This specifies where the data should be used.
*/
export enum DataTopic {
AlertStates = 'alertStates',
Annotations = 'annotations',
Series = 'series',
}

/**
* TODO docs
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/grafana-schema/src/common/data.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package common

// A topic is attached to DataFrame metadata in query results.
// This specifies where the data should be used.
DataTopic: "series" | "annotations" | "alertStates" @cuetsy(kind="enum",memberNames="Series|Annotations|AlertStates")
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,10 @@ export interface DataTransformerConfig {
* Valid options depend on the transformer id
*/
options: unknown;
/**
* Where to pull DataFrames from as input to transformation
*/
topic?: ('series' | 'annotations' | 'alertStates'); // replaced with common.DataTopic
}

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/grafana-schema/src/veneer/dashboard.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DataSourceRef as CommonDataSourceRef, DataSourceRef } from '../common/common.gen';
import { DataSourceRef as CommonDataSourceRef, DataSourceRef, DataTopic } from '../common/common.gen';
import * as raw from '../raw/dashboard/x/dashboard_types.gen';

import { DataQuery } from './common.types';
Expand Down Expand Up @@ -59,6 +59,7 @@ export interface MatcherConfig<TConfig = any> extends raw.MatcherConfig {

export interface DataTransformerConfig<TOptions = any> extends raw.DataTransformerConfig {
options: TOptions;
topic?: DataTopic;
}

export interface TimePickerConfig extends raw.TimePickerConfig {}
Expand Down
13 changes: 13 additions & 0 deletions pkg/kinds/dashboard/dashboard_spec_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,69 @@ import { css } from '@emotion/css';
import React, { useMemo } from 'react';

import {
DataFrame,
DataTransformerConfig,
GrafanaTheme2,
StandardEditorContext,
StandardEditorsRegistryItem,
} from '@grafana/data';
import { Field, useStyles2 } from '@grafana/ui';
import { DataTopic } from '@grafana/schema';
import { Field, Select, useStyles2 } from '@grafana/ui';
import { FrameSelectionEditor } from 'app/plugins/panel/geomap/editor/FrameSelectionEditor';

import { TransformationData } from './TransformationsEditor';

interface TransformationFilterProps {
index: number;
config: DataTransformerConfig;
data: DataFrame[];
data: TransformationData;
onChange: (index: number, config: DataTransformerConfig) => void;
}

export const TransformationFilter = ({ index, data, config, onChange }: TransformationFilterProps) => {
const styles = useStyles2(getStyles);
const context = useMemo(() => {
// eslint-disable-next-line
return { data } as StandardEditorContext<unknown>;
}, [data]);

const opts = useMemo(() => {
return {
// eslint-disable-next-line
context: { data: data.series } as StandardEditorContext<unknown>,
showTopic: true || data.annotations?.length || config.topic?.length,
showFilter: config.topic !== DataTopic.Annotations,
source: [
{ value: DataTopic.Series, label: `Query results` },
{ value: DataTopic.Annotations, label: `Annotation data` },
],
};
}, [data, config.topic]);

return (
<div className={styles.wrapper}>
<Field label="Apply transformation to">
<FrameSelectionEditor
value={config.filter!}
context={context}
// eslint-disable-next-line
item={{} as StandardEditorsRegistryItem}
onChange={(filter) => onChange(index, { ...config, filter })}
/>
<>
{opts.showTopic && (
<Select
isClearable={true}
options={opts.source}
value={opts.source.find((v) => v.value === config.topic)}
placeholder={opts.source[0].label}
className={styles.padded}
onChange={(option) => {
onChange(index, {
...config,
topic: option?.value,
});
}}
/>
)}
{opts.showFilter && (
<FrameSelectionEditor
value={config.filter!}
context={opts.context}
// eslint-disable-next-line
item={{} as StandardEditorsRegistryItem}
onChange={(filter) => onChange(index, { ...config, filter })}
/>
)}
</>
</Field>
</div>
);
Expand All @@ -44,13 +74,16 @@ const getStyles = (theme: GrafanaTheme2) => {
const borderRadius = theme.shape.radius.default;

return {
wrapper: css`
padding: ${theme.spacing(2)};
border: 2px solid ${theme.colors.background.secondary};
border-top: none;
border-radius: 0 0 ${borderRadius} ${borderRadius};
position: relative;
top: -4px;
`,
wrapper: css({
padding: theme.spacing(2),
border: `2px solid ${theme.colors.background.secondary}`,
borderTop: `none`,
borderRadius: `0 0 ${borderRadius} ${borderRadius}`,
position: `relative`,
top: `-4px`,
}),
padded: css({
marginBottom: theme.spacing(1),
}),
};
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useCallback } from 'react';
import { useToggle } from 'react-use';

import { DataFrame, DataTransformerConfig, TransformerRegistryItem, FrameMatcherID } from '@grafana/data';
import { DataTransformerConfig, TransformerRegistryItem, FrameMatcherID, DataTopic } from '@grafana/data';
import { reportInteraction } from '@grafana/runtime';
import { ConfirmModal } from '@grafana/ui';
import {
Expand All @@ -15,12 +15,13 @@ import { PluginStateInfo } from 'app/features/plugins/components/PluginStateInfo
import { TransformationEditor } from './TransformationEditor';
import { TransformationEditorHelperModal } from './TransformationEditorHelperModal';
import { TransformationFilter } from './TransformationFilter';
import { TransformationData } from './TransformationsEditor';
import { TransformationsEditorTransformation } from './types';

interface TransformationOperationRowProps {
id: string;
index: number;
data: DataFrame[];
data: TransformationData;
uiConfig: TransformerRegistryItem<null>;
configs: TransformationsEditorTransformation[];
onRemove: (index: number) => void;
Expand All @@ -40,8 +41,9 @@ export const TransformationOperationRow = ({
const [showDebug, toggleShowDebug] = useToggle(false);
const [showHelp, toggleShowHelp] = useToggle(false);
const disabled = !!configs[index].transformation.disabled;
const filter = configs[index].transformation.filter != null;
const showFilter = filter || data.length > 1;
const topic = configs[index].transformation.topic;
const showFilterEditor = configs[index].transformation.filter != null || topic != null;
const showFilterToggle = showFilterEditor || data.series.length > 1 || (data.annotations?.length ?? 0) > 0;

const onDisableToggle = useCallback(
(index: number) => {
Expand Down Expand Up @@ -99,12 +101,12 @@ export const TransformationOperationRow = ({
onClick={instrumentToggleCallback(toggleShowHelp, 'help', showHelp)}
active={showHelp}
/>
{showFilter && (
{showFilterToggle && (
<QueryOperationToggleAction
title="Filter"
icon="filter"
onClick={instrumentToggleCallback(toggleFilter, 'filter', filter)}
active={filter}
onClick={instrumentToggleCallback(toggleFilter, 'filter', showFilterEditor)}
active={showFilterEditor}
/>
)}
<QueryOperationToggleAction
Expand Down Expand Up @@ -156,13 +158,14 @@ export const TransformationOperationRow = ({
open: 'Expand transformation row',
}}
>
{filter && (
{showFilterEditor && (
<TransformationFilter index={index} config={configs[index].transformation} data={data} onChange={onChange} />
)}

<TransformationEditor
debugMode={showDebug}
index={index}
data={data}
data={topic === DataTopic.Annotations ? data.annotations ?? [] : data.series}
configs={configs}
uiConfig={uiConfig}
onChange={onChange}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React from 'react';

import { DataFrame, DataTransformerConfig, standardTransformersRegistry } from '@grafana/data';
import { DataTransformerConfig, standardTransformersRegistry } from '@grafana/data';

import { TransformationOperationRow } from './TransformationOperationRow';
import { TransformationData } from './TransformationsEditor';
import { TransformationsEditorTransformation } from './types';

interface TransformationOperationRowsProps {
data: DataFrame[];
data: TransformationData;
configs: TransformationsEditorTransformation[];
onRemove: (index: number) => void;
onChange: (index: number, config: DataTransformerConfig) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,13 @@ const VIEW_ALL_VALUE = 'viewAll';
export type viewAllType = 'viewAll';
export type FilterCategory = TransformerCategory | viewAllType;

export interface TransformationData {
series: DataFrame[];
annotations?: DataFrame[];
}

interface State {
data: DataFrame[];
data: TransformationData;
transformations: TransformationsEditorTransformation[];
search: string;
showPicker?: boolean;
Expand All @@ -68,7 +73,9 @@ class UnThemedTransformationsEditor extends React.PureComponent<TransformationsE
transformation: t,
id: ids[i],
})),
data: [],
data: {
series: [],
},
search: '',
selectedFilter: VIEW_ALL_VALUE,
showIllustrations: true,
Expand Down Expand Up @@ -120,7 +127,7 @@ class UnThemedTransformationsEditor extends React.PureComponent<TransformationsE
.getQueryRunner()
.getData({ withTransforms: false, withFieldConfig: false })
.subscribe({
next: (panelData: PanelData) => this.setState({ data: panelData.series }),
next: (panelData: PanelData) => this.setState({ data: panelData }),
});
}

Expand Down Expand Up @@ -384,7 +391,7 @@ class UnThemedTransformationsEditor extends React.PureComponent<TransformationsE
onSearchChange={this.onSearchChange}
onSearchKeyDown={this.onSearchKeyDown}
onTransformationAdd={this.onTransformationAdd}
data={this.state.data}
data={this.state.data.series}
selectedFilter={this.state.selectedFilter}
showIllustrations={this.state.showIllustrations}
/>
Expand Down