mirror of
https://github.com/grafana/grafana.git
synced 2024-11-30 12:44:10 -06:00
Cloud Monitoring: Update Alignment fields to use experimental UI components (#50536)
* Cloud Monitoring: Update Alignment fields to use experimental UI components * remove unreachable code * remove alias * remove custom label style * add tests
This commit is contained in:
parent
7c175e019c
commit
d88108a3b7
@ -1,12 +1,19 @@
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { TemplateSrvMock } from 'app/features/templating/template_srv.mock';
|
||||
|
||||
import Datasource from '../datasource';
|
||||
|
||||
export const createMockDatasource = () => {
|
||||
export const createMockDatasource = (overrides?: Partial<Datasource>) => {
|
||||
const templateSrv = new TemplateSrvMock({ ALIGN_DELTA: 'delta' }) as unknown as TemplateSrv;
|
||||
|
||||
const datasource: Partial<Datasource> = {
|
||||
intervalMs: 0,
|
||||
getVariables: jest.fn().mockReturnValue([]),
|
||||
getMetricTypes: jest.fn().mockResolvedValue([]),
|
||||
getProjects: jest.fn().mockResolvedValue([]),
|
||||
getDefaultProject: jest.fn().mockReturnValue('cloud-monitoring-default-project'),
|
||||
templateSrv,
|
||||
...overrides,
|
||||
};
|
||||
|
||||
return jest.mocked(datasource as Datasource, true);
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { CloudMonitoringQuery, EditorMode, MetricQuery, QueryType } from '../types';
|
||||
|
||||
export const createMockMetricQuery: () => MetricQuery = () => {
|
||||
export const createMockMetricQuery: (overrides?: Partial<MetricQuery>) => MetricQuery = (
|
||||
overrides?: Partial<MetricQuery>
|
||||
) => {
|
||||
return {
|
||||
editorMode: EditorMode.Visual,
|
||||
metricType: '',
|
||||
crossSeriesReducer: 'REDUCE_NONE',
|
||||
query: '',
|
||||
projectName: 'cloud-monitoring-default-project',
|
||||
...overrides,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,102 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { openMenu } from 'react-select-event';
|
||||
|
||||
import { TemplateSrvMock } from 'app/features/templating/template_srv.mock';
|
||||
|
||||
import { createMockDatasource } from '../../__mocks__/cloudMonitoringDatasource';
|
||||
import { createMockMetricQuery } from '../../__mocks__/cloudMonitoringQuery';
|
||||
import { MetricKind, ValueTypes } from '../../types';
|
||||
|
||||
import { Alignment } from './Alignment';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getTemplateSrv: () => new TemplateSrvMock({}),
|
||||
}));
|
||||
|
||||
describe('Alignment', () => {
|
||||
it('renders alignment fields', () => {
|
||||
const datasource = createMockDatasource();
|
||||
const query = createMockMetricQuery();
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(
|
||||
<Alignment
|
||||
refId="refId"
|
||||
customMetaData={{}}
|
||||
datasource={datasource}
|
||||
query={query}
|
||||
onChange={onChange}
|
||||
templateVariableOptions={[]}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByLabelText('Alignment function')).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('Alignment period')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('can set the alignment function', async () => {
|
||||
const datasource = createMockDatasource();
|
||||
const query = createMockMetricQuery({ metricKind: MetricKind.GAUGE, valueType: ValueTypes.INT64 });
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(
|
||||
<Alignment
|
||||
refId="refId"
|
||||
customMetaData={{}}
|
||||
datasource={datasource}
|
||||
query={query}
|
||||
onChange={onChange}
|
||||
templateVariableOptions={[]}
|
||||
/>
|
||||
);
|
||||
|
||||
const alignmentFunction = screen.getByLabelText('Alignment function');
|
||||
openMenu(alignmentFunction);
|
||||
await userEvent.click(screen.getByText('percent change'));
|
||||
expect(onChange).toBeCalledWith(expect.objectContaining({ perSeriesAligner: 'ALIGN_PERCENT_CHANGE' }));
|
||||
});
|
||||
|
||||
it('can set the alignment period', async () => {
|
||||
const datasource = createMockDatasource();
|
||||
const query = createMockMetricQuery();
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(
|
||||
<Alignment
|
||||
refId="refId"
|
||||
customMetaData={{}}
|
||||
datasource={datasource}
|
||||
query={query}
|
||||
onChange={onChange}
|
||||
templateVariableOptions={[]}
|
||||
/>
|
||||
);
|
||||
|
||||
const alignmentPeriod = screen.getByLabelText('Alignment period');
|
||||
openMenu(alignmentPeriod);
|
||||
await userEvent.click(screen.getByText('1m'));
|
||||
expect(onChange).toBeCalledWith(expect.objectContaining({ alignmentPeriod: '+60s' }));
|
||||
});
|
||||
|
||||
it('renders period label if alignment period and per series aligner is set', () => {
|
||||
const datasource = createMockDatasource();
|
||||
const query = createMockMetricQuery();
|
||||
const onChange = jest.fn();
|
||||
|
||||
render(
|
||||
<Alignment
|
||||
refId="refId"
|
||||
customMetaData={{ perSeriesAligner: 'ALIGN_DELTA', alignmentPeriod: '10' }}
|
||||
datasource={datasource}
|
||||
query={query}
|
||||
onChange={onChange}
|
||||
templateVariableOptions={[]}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('10s interval (delta)'));
|
||||
});
|
||||
});
|
@ -0,0 +1,61 @@
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { EditorRow, EditorField, EditorFieldGroup, Stack } from '@grafana/experimental';
|
||||
|
||||
import { ALIGNMENT_PERIODS, SELECT_WIDTH } from '../../constants';
|
||||
import CloudMonitoringDatasource from '../../datasource';
|
||||
import { CustomMetaData, MetricQuery, SLOQuery } from '../../types';
|
||||
|
||||
import { AlignmentFunction } from './AlignmentFunction';
|
||||
import { AlignmentPeriodLabel } from './AlignmentPeriodLabel';
|
||||
import { PeriodSelect } from './PeriodSelect';
|
||||
|
||||
export interface Props {
|
||||
refId: string;
|
||||
onChange: (query: MetricQuery | SLOQuery) => void;
|
||||
query: MetricQuery;
|
||||
templateVariableOptions: Array<SelectableValue<string>>;
|
||||
customMetaData: CustomMetaData;
|
||||
datasource: CloudMonitoringDatasource;
|
||||
}
|
||||
|
||||
export const Alignment: FC<Props> = ({
|
||||
refId,
|
||||
templateVariableOptions,
|
||||
onChange,
|
||||
query,
|
||||
customMetaData,
|
||||
datasource,
|
||||
}) => {
|
||||
return (
|
||||
<EditorRow>
|
||||
<EditorFieldGroup>
|
||||
<EditorField
|
||||
label="Alignment function"
|
||||
tooltip="The process of alignment consists of collecting all data points received in a fixed length of time, applying a function to combine those data points, and assigning a timestamp to the result."
|
||||
>
|
||||
<AlignmentFunction
|
||||
inputId={`${refId}-alignment-function`}
|
||||
templateVariableOptions={templateVariableOptions}
|
||||
query={query}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</EditorField>
|
||||
<EditorField label="Alignment period">
|
||||
<PeriodSelect
|
||||
inputId={`${refId}-alignment-period`}
|
||||
selectWidth={SELECT_WIDTH}
|
||||
templateVariableOptions={templateVariableOptions}
|
||||
current={query.alignmentPeriod}
|
||||
onChange={(period) => onChange({ ...query, alignmentPeriod: period })}
|
||||
aligmentPeriods={ALIGNMENT_PERIODS}
|
||||
/>
|
||||
</EditorField>
|
||||
<Stack alignItems="flex-end">
|
||||
<AlignmentPeriodLabel datasource={datasource} customMetaData={customMetaData} />
|
||||
</Stack>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
);
|
||||
};
|
@ -0,0 +1,44 @@
|
||||
import React, { FC, useMemo } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { Select } from '@grafana/ui';
|
||||
|
||||
import { SELECT_WIDTH } from '../../constants';
|
||||
import { getAlignmentPickerData } from '../../functions';
|
||||
import { MetricQuery } from '../../types';
|
||||
|
||||
export interface Props {
|
||||
inputId: string;
|
||||
onChange: (query: MetricQuery) => void;
|
||||
query: MetricQuery;
|
||||
templateVariableOptions: Array<SelectableValue<string>>;
|
||||
}
|
||||
|
||||
export const AlignmentFunction: FC<Props> = ({ inputId, query, templateVariableOptions, onChange }) => {
|
||||
const { valueType, metricKind, perSeriesAligner: psa, preprocessor } = query;
|
||||
const { perSeriesAligner, alignOptions } = useMemo(
|
||||
() => getAlignmentPickerData(valueType, metricKind, psa, preprocessor),
|
||||
[valueType, metricKind, psa, preprocessor]
|
||||
);
|
||||
|
||||
return (
|
||||
<Select
|
||||
width={SELECT_WIDTH}
|
||||
onChange={({ value }) => onChange({ ...query, perSeriesAligner: value! })}
|
||||
value={[...alignOptions, ...templateVariableOptions].find((s) => s.value === perSeriesAligner)}
|
||||
options={[
|
||||
{
|
||||
label: 'Template Variables',
|
||||
options: templateVariableOptions,
|
||||
},
|
||||
{
|
||||
label: 'Alignment options',
|
||||
expanded: true,
|
||||
options: alignOptions,
|
||||
},
|
||||
]}
|
||||
placeholder="Select Alignment"
|
||||
inputId={inputId}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
import React, { FC, useMemo } from 'react';
|
||||
|
||||
import { rangeUtil } from '@grafana/data';
|
||||
|
||||
import { ALIGNMENTS } from '../../constants';
|
||||
import CloudMonitoringDatasource from '../../datasource';
|
||||
import { CustomMetaData } from '../../types';
|
||||
|
||||
export interface Props {
|
||||
customMetaData: CustomMetaData;
|
||||
datasource: CloudMonitoringDatasource;
|
||||
}
|
||||
|
||||
export const AlignmentPeriodLabel: FC<Props> = ({ customMetaData, datasource }) => {
|
||||
const { perSeriesAligner, alignmentPeriod } = customMetaData;
|
||||
const formatAlignmentText = useMemo(() => {
|
||||
if (!alignmentPeriod || !perSeriesAligner) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const alignment = ALIGNMENTS.find((ap) => ap.value === datasource.templateSrv.replace(perSeriesAligner));
|
||||
const seconds = parseInt(alignmentPeriod, 10);
|
||||
const hms = rangeUtil.secondsToHms(seconds);
|
||||
return `${hms} interval (${alignment?.text ?? ''})`;
|
||||
}, [datasource, perSeriesAligner, alignmentPeriod]);
|
||||
|
||||
return <label>{formatAlignmentText}</label>;
|
||||
};
|
@ -16,11 +16,12 @@ import {
|
||||
SLOQuery,
|
||||
ValueTypes,
|
||||
} from '../../types';
|
||||
import { Project, VisualMetricQueryEditor } from '../index';
|
||||
import { Project } from '../index';
|
||||
|
||||
import { GraphPeriod } from './../GraphPeriod';
|
||||
import { MQLQueryEditor } from './../MQLQueryEditor';
|
||||
import { AliasBy } from './AliasBy';
|
||||
import { VisualMetricQueryEditor } from './VisualMetricQueryEditor';
|
||||
|
||||
export interface Props {
|
||||
refId: string;
|
||||
|
@ -0,0 +1,60 @@
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { Select } from '@grafana/ui';
|
||||
|
||||
import { periodOption } from '../../constants';
|
||||
|
||||
export interface Props {
|
||||
inputId: string;
|
||||
onChange: (period: string) => void;
|
||||
templateVariableOptions: Array<SelectableValue<string>>;
|
||||
aligmentPeriods: periodOption[];
|
||||
selectWidth?: number;
|
||||
category?: string;
|
||||
disabled?: boolean;
|
||||
current?: string;
|
||||
}
|
||||
|
||||
export function PeriodSelect({
|
||||
inputId,
|
||||
templateVariableOptions,
|
||||
onChange,
|
||||
current,
|
||||
selectWidth,
|
||||
disabled,
|
||||
aligmentPeriods,
|
||||
}: Props) {
|
||||
const options = useMemo(
|
||||
() =>
|
||||
aligmentPeriods.map((ap) => ({
|
||||
...ap,
|
||||
label: ap.text,
|
||||
})),
|
||||
[aligmentPeriods]
|
||||
);
|
||||
const visibleOptions = useMemo(() => options.filter((ap) => !ap.hidden), [options]);
|
||||
|
||||
return (
|
||||
<Select
|
||||
width={selectWidth}
|
||||
onChange={({ value }) => onChange(value!)}
|
||||
value={[...options, ...templateVariableOptions].find((s) => s.value === current)}
|
||||
options={[
|
||||
{
|
||||
label: 'Template Variables',
|
||||
options: templateVariableOptions,
|
||||
},
|
||||
{
|
||||
label: 'Aggregations',
|
||||
expanded: true,
|
||||
options: visibleOptions,
|
||||
},
|
||||
]}
|
||||
placeholder="Select Period"
|
||||
inputId={inputId}
|
||||
disabled={disabled}
|
||||
allowCustomValue
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
import React from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
|
||||
import CloudMonitoringDatasource from '../../datasource';
|
||||
import { CustomMetaData, MetricDescriptor, MetricQuery, SLOQuery } from '../../types';
|
||||
import { GroupBy, LabelFilter, Metrics, Preprocessor } from '../index';
|
||||
|
||||
import { Alignment } from './Alignment';
|
||||
|
||||
export interface Props {
|
||||
refId: string;
|
||||
customMetaData: CustomMetaData;
|
||||
variableOptionGroup: SelectableValue<string>;
|
||||
onMetricTypeChange: (query: MetricDescriptor) => void;
|
||||
onChange: (query: MetricQuery | SLOQuery) => void;
|
||||
query: MetricQuery;
|
||||
datasource: CloudMonitoringDatasource;
|
||||
labels: any;
|
||||
}
|
||||
|
||||
function Editor({
|
||||
refId,
|
||||
query,
|
||||
labels,
|
||||
datasource,
|
||||
onChange,
|
||||
onMetricTypeChange,
|
||||
customMetaData,
|
||||
variableOptionGroup,
|
||||
}: React.PropsWithChildren<Props>) {
|
||||
return (
|
||||
<Metrics
|
||||
refId={refId}
|
||||
templateSrv={datasource.templateSrv}
|
||||
projectName={query.projectName}
|
||||
metricType={query.metricType}
|
||||
templateVariableOptions={variableOptionGroup.options}
|
||||
datasource={datasource}
|
||||
onChange={onMetricTypeChange}
|
||||
>
|
||||
{(metric) => (
|
||||
<>
|
||||
<LabelFilter
|
||||
labels={labels}
|
||||
filters={query.filters!}
|
||||
onChange={(filters: string[]) => onChange({ ...query, filters })}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
/>
|
||||
<Preprocessor metricDescriptor={metric} query={query} onChange={onChange} />
|
||||
<GroupBy
|
||||
refId={refId}
|
||||
labels={Object.keys(labels)}
|
||||
query={query}
|
||||
onChange={onChange}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
metricDescriptor={metric}
|
||||
/>
|
||||
<Alignment
|
||||
refId={refId}
|
||||
datasource={datasource}
|
||||
templateVariableOptions={variableOptionGroup.options}
|
||||
query={query}
|
||||
customMetaData={customMetaData}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Metrics>
|
||||
);
|
||||
}
|
||||
|
||||
export const VisualMetricQueryEditor = React.memo(Editor);
|
Loading…
Reference in New Issue
Block a user