mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Transformations: Selectively apply transformation to queries (#61735)
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
GrafanaTheme2,
|
||||
transformDataFrame,
|
||||
TransformerRegistryItem,
|
||||
getFrameMatchers,
|
||||
} from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { Icon, JSONFormatter, useStyles2 } from '@grafana/ui';
|
||||
@@ -37,9 +38,16 @@ export const TransformationEditor = ({
|
||||
const config = useMemo(() => configs[index], [configs, index]);
|
||||
|
||||
useEffect(() => {
|
||||
const config = configs[index].transformation;
|
||||
const matcher = config.filter?.options ? getFrameMatchers(config.filter) : undefined;
|
||||
const inputTransforms = configs.slice(0, index).map((t) => t.transformation);
|
||||
const outputTransforms = configs.slice(index, index + 1).map((t) => t.transformation);
|
||||
const inputSubscription = transformDataFrame(inputTransforms, data).subscribe(setInput);
|
||||
const inputSubscription = transformDataFrame(inputTransforms, data).subscribe((v) => {
|
||||
if (matcher) {
|
||||
v = data.filter((v) => matcher(v));
|
||||
}
|
||||
setInput(v);
|
||||
});
|
||||
const outputSubscription = transformDataFrame(inputTransforms, data)
|
||||
.pipe(mergeMap((before) => transformDataFrame(outputTransforms, before)))
|
||||
.subscribe(setOutput);
|
||||
@@ -56,18 +64,13 @@ export const TransformationEditor = ({
|
||||
options: { ...uiConfig.transformation.defaultOptions, ...config.transformation.options },
|
||||
input,
|
||||
onChange: (opts) => {
|
||||
onChange(index, { id: config.transformation.id, options: opts });
|
||||
onChange(index, {
|
||||
...config.transformation,
|
||||
options: opts,
|
||||
});
|
||||
},
|
||||
}),
|
||||
[
|
||||
uiConfig.editor,
|
||||
uiConfig.transformation.defaultOptions,
|
||||
config.transformation.options,
|
||||
config.transformation.id,
|
||||
input,
|
||||
onChange,
|
||||
index,
|
||||
]
|
||||
[uiConfig.editor, uiConfig.transformation.defaultOptions, config.transformation, input, onChange, index]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import {
|
||||
DataFrame,
|
||||
DataTransformerConfig,
|
||||
GrafanaTheme2,
|
||||
StandardEditorContext,
|
||||
StandardEditorsRegistryItem,
|
||||
} from '@grafana/data';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { FrameSelectionEditor } from 'app/plugins/panel/geomap/editor/FrameSelectionEditor';
|
||||
|
||||
interface TransformationFilterProps {
|
||||
index: number;
|
||||
config: DataTransformerConfig;
|
||||
data: DataFrame[];
|
||||
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]);
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<h5>Apply tranformation to</h5>
|
||||
<FrameSelectionEditor
|
||||
value={config.filter!}
|
||||
context={context}
|
||||
// eslint-disable-next-line
|
||||
item={{} as StandardEditorsRegistryItem}
|
||||
onChange={(filter) => onChange(index, { ...config, filter })}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
const borderRadius = theme.shape.borderRadius();
|
||||
|
||||
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;
|
||||
`,
|
||||
};
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useToggle } from 'react-use';
|
||||
|
||||
import { DataFrame, DataTransformerConfig, TransformerRegistryItem } from '@grafana/data';
|
||||
import { DataFrame, DataTransformerConfig, TransformerRegistryItem, FrameMatcherID } from '@grafana/data';
|
||||
import { HorizontalGroup } from '@grafana/ui';
|
||||
import { OperationRowHelp } from 'app/core/components/QueryOperationRow/OperationRowHelp';
|
||||
import { QueryOperationAction } from 'app/core/components/QueryOperationRow/QueryOperationAction';
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { PluginStateInfo } from 'app/features/plugins/components/PluginStateInfo';
|
||||
|
||||
import { TransformationEditor } from './TransformationEditor';
|
||||
import { TransformationFilter } from './TransformationFilter';
|
||||
import { TransformationsEditorTransformation } from './types';
|
||||
|
||||
interface TransformationOperationRowProps {
|
||||
@@ -24,7 +25,7 @@ interface TransformationOperationRowProps {
|
||||
onChange: (index: number, config: DataTransformerConfig) => void;
|
||||
}
|
||||
|
||||
export const TransformationOperationRow: React.FC<TransformationOperationRowProps> = ({
|
||||
export const TransformationOperationRow = ({
|
||||
onRemove,
|
||||
index,
|
||||
id,
|
||||
@@ -32,10 +33,12 @@ export const TransformationOperationRow: React.FC<TransformationOperationRowProp
|
||||
configs,
|
||||
uiConfig,
|
||||
onChange,
|
||||
}) => {
|
||||
}: TransformationOperationRowProps) => {
|
||||
const [showDebug, toggleDebug] = useToggle(false);
|
||||
const [showHelp, toggleHelp] = useToggle(false);
|
||||
const disabled = configs[index].transformation.disabled;
|
||||
const filter = configs[index].transformation.filter != null;
|
||||
const showFilter = filter || data.length > 1;
|
||||
|
||||
const onDisableToggle = useCallback(
|
||||
(index: number) => {
|
||||
@@ -48,6 +51,20 @@ export const TransformationOperationRow: React.FC<TransformationOperationRowProp
|
||||
[onChange, configs]
|
||||
);
|
||||
|
||||
// Adds or removes the frame filter
|
||||
const toggleFilter = useCallback(() => {
|
||||
let current = { ...configs[index].transformation };
|
||||
if (current.filter) {
|
||||
delete current.filter;
|
||||
} else {
|
||||
current.filter = {
|
||||
id: FrameMatcherID.byRefId,
|
||||
options: '', // empty string will not do anything
|
||||
};
|
||||
}
|
||||
onChange(index, current);
|
||||
}, [onChange, index, configs]);
|
||||
|
||||
const renderActions = ({ isOpen }: QueryOperationRowRenderProps) => {
|
||||
return (
|
||||
<HorizontalGroup align="center" width="auto">
|
||||
@@ -58,6 +75,7 @@ export const TransformationOperationRow: React.FC<TransformationOperationRowProp
|
||||
onClick={toggleHelp}
|
||||
active={showHelp}
|
||||
/>
|
||||
{showFilter && <QueryOperationAction title="Filter" icon="filter" onClick={toggleFilter} active={filter} />}
|
||||
<QueryOperationAction title="Debug" disabled={!isOpen} icon="bug" onClick={toggleDebug} active={showDebug} />
|
||||
<QueryOperationAction
|
||||
title="Disable/Enable transformation"
|
||||
@@ -80,6 +98,9 @@ export const TransformationOperationRow: React.FC<TransformationOperationRowProp
|
||||
disabled={disabled}
|
||||
>
|
||||
{showHelp && <OperationRowHelp markdown={prepMarkdown(uiConfig)} />}
|
||||
{filter && (
|
||||
<TransformationFilter index={index} config={configs[index].transformation} data={data} onChange={onChange} />
|
||||
)}
|
||||
<TransformationEditor
|
||||
debugMode={showDebug}
|
||||
index={index}
|
||||
|
||||
Reference in New Issue
Block a user