mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Make SQLOptions and SQLQuery in SQLDatasource and in Editor generic
This commit is contained in:
parent
f14b45bc86
commit
1d15b4061a
@ -13,9 +13,19 @@ import { QueryHeader } from './QueryHeader';
|
||||
import { RawEditor } from './query-editor-raw/RawEditor';
|
||||
import { VisualEditor } from './visual-query-builder/VisualEditor';
|
||||
|
||||
type Props = QueryEditorProps<SqlDatasource, SQLQuery, SQLOptions>;
|
||||
type Props<TSQLQuery extends SQLQuery, TSQLOptions extends SQLOptions> = QueryEditorProps<
|
||||
SqlDatasource<TSQLQuery, TSQLOptions>,
|
||||
TSQLQuery,
|
||||
TSQLOptions
|
||||
>;
|
||||
|
||||
export function SqlQueryEditor({ datasource, query, onChange, onRunQuery, range }: Props) {
|
||||
export function SqlQueryEditor<TSQLQuery extends SQLQuery, TSQLOptions extends SQLOptions>({
|
||||
datasource,
|
||||
query,
|
||||
onChange,
|
||||
onRunQuery,
|
||||
range,
|
||||
}: Props<TSQLQuery, TSQLOptions>) {
|
||||
const [isQueryRunnable, setIsQueryRunnable] = useState(true);
|
||||
const db = datasource.getDB();
|
||||
const { loading, error } = useAsync(async () => {
|
||||
@ -52,7 +62,7 @@ export function SqlQueryEditor({ datasource, query, onChange, onRunQuery, range
|
||||
[onRunQuery]
|
||||
);
|
||||
|
||||
const onQueryChange = (q: SQLQuery, process = true) => {
|
||||
const onQueryChange = (q: TSQLQuery, process = true) => {
|
||||
setQueryToValidate(q);
|
||||
onChange(q);
|
||||
|
||||
@ -65,7 +75,7 @@ export function SqlQueryEditor({ datasource, query, onChange, onRunQuery, range
|
||||
}
|
||||
};
|
||||
|
||||
const onQueryHeaderChange = (q: SQLQuery) => {
|
||||
const onQueryHeaderChange = (q: TSQLQuery) => {
|
||||
setQueryToValidate(q);
|
||||
onChange(q);
|
||||
};
|
||||
@ -76,7 +86,7 @@ export function SqlQueryEditor({ datasource, query, onChange, onRunQuery, range
|
||||
|
||||
return (
|
||||
<>
|
||||
<QueryHeader
|
||||
<QueryHeader<TSQLQuery>
|
||||
db={db}
|
||||
onChange={onQueryHeaderChange}
|
||||
onRunQuery={onRunQuery}
|
||||
@ -92,7 +102,7 @@ export function SqlQueryEditor({ datasource, query, onChange, onRunQuery, range
|
||||
<VisualEditor
|
||||
db={db}
|
||||
query={queryWithDefaults}
|
||||
onChange={(q: SQLQuery) => onQueryChange(q, false)}
|
||||
onChange={(q: TSQLQuery) => onQueryChange(q, false)}
|
||||
queryRowFilter={queryRowFilter}
|
||||
onValidate={setIsQueryRunnable}
|
||||
range={range}
|
||||
|
@ -5,7 +5,6 @@ import { SelectableValue } from '@grafana/data';
|
||||
import { EditorField, EditorHeader, EditorMode, EditorRow, FlexItem, InlineSelect, Space } from '@grafana/experimental';
|
||||
import { Button, InlineField, InlineSwitch, RadioButtonGroup, Select, Tooltip } from '@grafana/ui';
|
||||
|
||||
import { QueryWithDefaults } from '../defaults';
|
||||
import { SQLQuery, QueryFormat, QueryRowFilter, QUERY_FORMAT_OPTIONS, DB } from '../types';
|
||||
import { defaultToRawSql } from '../utils/sql.utils';
|
||||
|
||||
@ -14,10 +13,10 @@ import { DatasetSelector } from './DatasetSelector';
|
||||
import { ErrorBoundary } from './ErrorBoundary';
|
||||
import { TableSelector } from './TableSelector';
|
||||
|
||||
interface QueryHeaderProps {
|
||||
interface QueryHeaderProps<T extends SQLQuery> {
|
||||
db: DB;
|
||||
query: QueryWithDefaults;
|
||||
onChange: (query: SQLQuery) => void;
|
||||
query: T;
|
||||
onChange: (query: T) => void;
|
||||
onRunQuery: () => void;
|
||||
onQueryRowChange: (queryRowFilter: QueryRowFilter) => void;
|
||||
queryRowFilter: QueryRowFilter;
|
||||
@ -29,7 +28,7 @@ const editorModes = [
|
||||
{ label: 'Code', value: EditorMode.Code },
|
||||
];
|
||||
|
||||
export function QueryHeader({
|
||||
export function QueryHeader<T extends SQLQuery>({
|
||||
db,
|
||||
query,
|
||||
queryRowFilter,
|
||||
@ -37,7 +36,7 @@ export function QueryHeader({
|
||||
onRunQuery,
|
||||
onQueryRowChange,
|
||||
isQueryRunnable,
|
||||
}: QueryHeaderProps) {
|
||||
}: QueryHeaderProps<T>) {
|
||||
const { editorMode } = query;
|
||||
const [_, copyToClipboard] = useCopyToClipboard();
|
||||
const [showConfirm, setShowConfirm] = useState(false);
|
||||
@ -80,7 +79,7 @@ export function QueryHeader({
|
||||
return;
|
||||
}
|
||||
|
||||
const next: SQLQuery = {
|
||||
const next = {
|
||||
...query,
|
||||
table: e.value,
|
||||
sql: undefined,
|
||||
|
@ -4,13 +4,12 @@ import { useAsync } from 'react-use';
|
||||
import { SelectableValue, toOption } from '@grafana/data';
|
||||
import { Select } from '@grafana/ui';
|
||||
|
||||
import { QueryWithDefaults } from '../defaults';
|
||||
import { DB, ResourceSelectorProps } from '../types';
|
||||
import { DB, ResourceSelectorProps, SQLQuery } from '../types';
|
||||
|
||||
interface TableSelectorProps extends ResourceSelectorProps {
|
||||
db: DB;
|
||||
value: string | null;
|
||||
query: QueryWithDefaults;
|
||||
query: SQLQuery;
|
||||
onChange: (v: SelectableValue) => void;
|
||||
}
|
||||
|
||||
|
@ -5,18 +5,25 @@ import { LanguageCompletionProvider, SQLEditor } from '@grafana/experimental';
|
||||
import { SQLQuery } from '../../types';
|
||||
import { formatSQL } from '../../utils/formatSQL';
|
||||
|
||||
type Props = {
|
||||
query: SQLQuery;
|
||||
onChange: (value: SQLQuery, processQuery: boolean) => void;
|
||||
type Props<T extends SQLQuery> = {
|
||||
query: T;
|
||||
onChange: (value: T, processQuery: boolean) => void;
|
||||
children?: (props: { formatQuery: () => void }) => React.ReactNode;
|
||||
width?: number;
|
||||
height?: number;
|
||||
completionProvider: LanguageCompletionProvider;
|
||||
};
|
||||
|
||||
export function QueryEditorRaw({ children, onChange, query, width, height, completionProvider }: Props) {
|
||||
export function QueryEditorRaw<T extends SQLQuery>({
|
||||
children,
|
||||
onChange,
|
||||
query,
|
||||
width,
|
||||
height,
|
||||
completionProvider,
|
||||
}: Props<T>) {
|
||||
// We need to pass query via ref to SQLEditor as onChange is executed via monacoEditor.onDidChangeModelContent callback, not onChange property
|
||||
const queryRef = useRef<SQLQuery>(query);
|
||||
const queryRef = useRef<T>(query);
|
||||
useEffect(() => {
|
||||
queryRef.current = query;
|
||||
}, [query]);
|
||||
|
@ -11,14 +11,22 @@ import { SQLQuery, QueryEditorProps } from '../../types';
|
||||
import { QueryEditorRaw } from './QueryEditorRaw';
|
||||
import { QueryToolbox } from './QueryToolbox';
|
||||
|
||||
interface RawEditorProps extends Omit<QueryEditorProps, 'onChange'> {
|
||||
interface RawEditorProps<T extends SQLQuery> extends Omit<QueryEditorProps<T>, 'onChange'> {
|
||||
onRunQuery: () => void;
|
||||
onChange: (q: SQLQuery, processQuery: boolean) => void;
|
||||
onChange: (q: T, processQuery: boolean) => void;
|
||||
onValidate: (isValid: boolean) => void;
|
||||
queryToValidate: SQLQuery;
|
||||
queryToValidate: T;
|
||||
}
|
||||
|
||||
export function RawEditor({ db, query, onChange, onRunQuery, onValidate, queryToValidate, range }: RawEditorProps) {
|
||||
export function RawEditor<T extends SQLQuery>({
|
||||
db,
|
||||
query,
|
||||
onChange,
|
||||
onRunQuery,
|
||||
onValidate,
|
||||
queryToValidate,
|
||||
range,
|
||||
}: RawEditorProps<T>) {
|
||||
const theme = useTheme2();
|
||||
const styles = useStyles2(getStyles);
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
@ -2,21 +2,20 @@ import React from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
|
||||
import { QueryWithDefaults } from '../../defaults';
|
||||
import { DB, SQLQuery } from '../../types';
|
||||
import { useSqlChange } from '../../utils/useSqlChange';
|
||||
|
||||
import { GroupByRow } from './GroupByRow';
|
||||
|
||||
interface SQLGroupByRowProps {
|
||||
interface SQLGroupByRowProps<T extends SQLQuery> {
|
||||
fields: SelectableValue[];
|
||||
query: QueryWithDefaults;
|
||||
onQueryChange: (query: SQLQuery) => void;
|
||||
query: T;
|
||||
onQueryChange: (query: T) => void;
|
||||
db: DB;
|
||||
}
|
||||
|
||||
export function SQLGroupByRow({ fields, query, onQueryChange, db }: SQLGroupByRowProps) {
|
||||
export function SQLGroupByRow<T extends SQLQuery>({ fields, query, onQueryChange, db }: SQLGroupByRowProps<T>) {
|
||||
const { onSqlChange } = useSqlChange({ query, onQueryChange, db });
|
||||
|
||||
return <GroupByRow columns={fields} sql={query.sql!} onSqlChange={onSqlChange} />;
|
||||
return <GroupByRow columns={fields} sql={query.sql || {}} onSqlChange={onSqlChange} />;
|
||||
}
|
||||
|
@ -2,20 +2,19 @@ import React from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
|
||||
import { QueryWithDefaults } from '../../defaults';
|
||||
import { DB, SQLQuery } from '../../types';
|
||||
import { useSqlChange } from '../../utils/useSqlChange';
|
||||
|
||||
import { OrderByRow } from './OrderByRow';
|
||||
|
||||
type SQLOrderByRowProps = {
|
||||
type SQLOrderByRowProps<T extends SQLQuery> = {
|
||||
fields: SelectableValue[];
|
||||
query: QueryWithDefaults;
|
||||
onQueryChange: (query: SQLQuery) => void;
|
||||
query: T;
|
||||
onQueryChange: (query: T) => void;
|
||||
db: DB;
|
||||
};
|
||||
|
||||
export function SQLOrderByRow({ fields, query, onQueryChange, db }: SQLOrderByRowProps) {
|
||||
export function SQLOrderByRow<T extends SQLQuery>({ fields, query, onQueryChange, db }: SQLOrderByRowProps<T>) {
|
||||
const { onSqlChange } = useSqlChange({ query, onQueryChange, db });
|
||||
let columnsWithIndices: SelectableValue[] = [];
|
||||
|
||||
@ -36,5 +35,5 @@ export function SQLOrderByRow({ fields, query, onQueryChange, db }: SQLOrderByRo
|
||||
];
|
||||
}
|
||||
|
||||
return <OrderByRow sql={query.sql!} onSqlChange={onSqlChange} columns={columnsWithIndices} />;
|
||||
return <OrderByRow sql={query.sql || {}} onSqlChange={onSqlChange} columns={columnsWithIndices} />;
|
||||
}
|
||||
|
@ -2,21 +2,20 @@ import React from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
|
||||
import { QueryWithDefaults } from '../../defaults';
|
||||
import { DB, SQLQuery } from '../../types';
|
||||
import { useSqlChange } from '../../utils/useSqlChange';
|
||||
|
||||
import { SelectRow } from './SelectRow';
|
||||
|
||||
interface SQLSelectRowProps {
|
||||
interface SQLSelectRowProps<T extends SQLQuery> {
|
||||
fields: SelectableValue[];
|
||||
query: QueryWithDefaults;
|
||||
onQueryChange: (query: SQLQuery) => void;
|
||||
query: T;
|
||||
onQueryChange: (query: T) => void;
|
||||
db: DB;
|
||||
}
|
||||
|
||||
export function SQLSelectRow({ fields, query, onQueryChange, db }: SQLSelectRowProps) {
|
||||
export function SQLSelectRow<T extends SQLQuery>({ fields, query, onQueryChange, db }: SQLSelectRowProps<T>) {
|
||||
const { onSqlChange } = useSqlChange({ query, onQueryChange, db });
|
||||
|
||||
return <SelectRow columns={fields} sql={query.sql!} onSqlChange={onSqlChange} />;
|
||||
return <SelectRow columns={fields} sql={query.sql || {}} onSqlChange={onSqlChange} />;
|
||||
}
|
||||
|
@ -3,21 +3,20 @@ import useAsync from 'react-use/lib/useAsync';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
|
||||
import { QueryWithDefaults } from '../../defaults';
|
||||
import { DB, SQLExpression, SQLQuery, SQLSelectableValue } from '../../types';
|
||||
import { useSqlChange } from '../../utils/useSqlChange';
|
||||
|
||||
import { Config } from './AwesomeQueryBuilder';
|
||||
import { WhereRow } from './WhereRow';
|
||||
|
||||
interface WhereRowProps {
|
||||
query: QueryWithDefaults;
|
||||
interface WhereRowProps<T extends SQLQuery> {
|
||||
query: T;
|
||||
fields: SelectableValue[];
|
||||
onQueryChange: (query: SQLQuery) => void;
|
||||
onQueryChange: (query: T) => void;
|
||||
db: DB;
|
||||
}
|
||||
|
||||
export function SQLWhereRow({ query, fields, onQueryChange, db }: WhereRowProps) {
|
||||
export function SQLWhereRow<T extends SQLQuery>({ query, fields, onQueryChange, db }: WhereRowProps<T>) {
|
||||
const state = useAsync(async () => {
|
||||
return mapFieldsToTypes(fields);
|
||||
}, [fields]);
|
||||
@ -29,7 +28,7 @@ export function SQLWhereRow({ query, fields, onQueryChange, db }: WhereRowProps)
|
||||
// TODO: fix key that's used to force clean render or SQLWhereRow - otherwise it doesn't render operators correctly
|
||||
key={JSON.stringify(state.value)}
|
||||
config={{ fields: state.value || {} }}
|
||||
sql={query.sql!}
|
||||
sql={query.sql || {}}
|
||||
onSqlChange={(val: SQLExpression) => {
|
||||
onSqlChange(val);
|
||||
}}
|
||||
|
@ -3,7 +3,7 @@ import { useAsync } from 'react-use';
|
||||
|
||||
import { EditorField, EditorRow, EditorRows } from '@grafana/experimental';
|
||||
|
||||
import { DB, QueryEditorProps, QueryRowFilter } from '../../types';
|
||||
import { DB, QueryEditorProps, QueryRowFilter, SQLQuery } from '../../types';
|
||||
import { QueryToolbox } from '../query-editor-raw/QueryToolbox';
|
||||
|
||||
import { Preview } from './Preview';
|
||||
@ -12,20 +12,20 @@ import { SQLOrderByRow } from './SQLOrderByRow';
|
||||
import { SQLSelectRow } from './SQLSelectRow';
|
||||
import { SQLWhereRow } from './SQLWhereRow';
|
||||
|
||||
interface VisualEditorProps extends QueryEditorProps {
|
||||
interface VisualEditorProps<T extends SQLQuery> extends QueryEditorProps<T> {
|
||||
db: DB;
|
||||
queryRowFilter: QueryRowFilter;
|
||||
onValidate: (isValid: boolean) => void;
|
||||
}
|
||||
|
||||
export const VisualEditor: React.FC<VisualEditorProps> = ({
|
||||
export function VisualEditor<T extends SQLQuery>({
|
||||
query,
|
||||
db,
|
||||
queryRowFilter,
|
||||
onChange,
|
||||
onValidate,
|
||||
range,
|
||||
}) => {
|
||||
}: VisualEditorProps<T>) {
|
||||
const state = useAsync(async () => {
|
||||
const fields = await db.fields(query);
|
||||
return fields;
|
||||
@ -65,4 +65,4 @@ export const VisualEditor: React.FC<VisualEditorProps> = ({
|
||||
<QueryToolbox db={db} query={query} onValidate={onValidate} range={range} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
@ -25,17 +25,12 @@ import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { VariableWithMultiSupport } from '../../../variables/types';
|
||||
import { getSearchFilterScopedVar, SearchFilterOptions } from '../../../variables/utils';
|
||||
import { MACRO_NAMES } from '../constants';
|
||||
import {
|
||||
DB,
|
||||
SQLQuery,
|
||||
SQLOptions,
|
||||
SqlQueryForInterpolation,
|
||||
ResponseParser,
|
||||
SqlQueryModel,
|
||||
QueryFormat,
|
||||
} from '../types';
|
||||
import { DB, SQLQuery, SQLOptions, ResponseParser, SqlQueryModel, QueryFormat } from '../types';
|
||||
|
||||
export abstract class SqlDatasource extends DataSourceWithBackend<SQLQuery, SQLOptions> {
|
||||
export abstract class SqlDatasource<
|
||||
TSQLQuery extends SQLQuery,
|
||||
TSQLOptions extends SQLOptions
|
||||
> extends DataSourceWithBackend<TSQLQuery, TSQLOptions> {
|
||||
id: number;
|
||||
name: string;
|
||||
interval: string;
|
||||
@ -43,7 +38,7 @@ export abstract class SqlDatasource extends DataSourceWithBackend<SQLQuery, SQLO
|
||||
annotations = {};
|
||||
|
||||
constructor(
|
||||
instanceSettings: DataSourceInstanceSettings<SQLOptions>,
|
||||
instanceSettings: DataSourceInstanceSettings<TSQLOptions>,
|
||||
protected readonly templateSrv: TemplateSrv = getTemplateSrv()
|
||||
) {
|
||||
super(instanceSettings);
|
||||
@ -82,10 +77,7 @@ export abstract class SqlDatasource extends DataSourceWithBackend<SQLQuery, SQLO
|
||||
return value;
|
||||
};
|
||||
|
||||
interpolateVariablesInQueries(
|
||||
queries: SqlQueryForInterpolation[],
|
||||
scopedVars: ScopedVars
|
||||
): SqlQueryForInterpolation[] {
|
||||
interpolateVariablesInQueries(queries: TSQLQuery[], scopedVars: ScopedVars): TSQLQuery[] {
|
||||
let expandedQueries = queries;
|
||||
if (queries && queries.length > 0) {
|
||||
expandedQueries = queries.map((query) => {
|
||||
|
@ -3,7 +3,7 @@ import { EditorMode } from '@grafana/experimental';
|
||||
import { QueryFormat, SQLQuery } from './types';
|
||||
import { createFunctionField, setGroupByField } from './utils/sql.utils';
|
||||
|
||||
export function applyQueryDefaults(q?: SQLQuery): SQLQuery {
|
||||
export function applyQueryDefaults<T extends SQLQuery>(q: T): T {
|
||||
let editorMode = q?.editorMode || EditorMode.Builder;
|
||||
|
||||
// Switching to code editor if the query was created before visual query builder was introduced.
|
||||
@ -11,7 +11,7 @@ export function applyQueryDefaults(q?: SQLQuery): SQLQuery {
|
||||
editorMode = EditorMode.Code;
|
||||
}
|
||||
|
||||
const result: SQLQuery = {
|
||||
const result: T = {
|
||||
...q,
|
||||
refId: q?.refId || 'A',
|
||||
format: q?.format !== undefined ? q.format : QueryFormat.Table,
|
||||
@ -26,5 +26,3 @@ export function applyQueryDefaults(q?: SQLQuery): SQLQuery {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export type QueryWithDefaults = ReturnType<typeof applyQueryDefaults>;
|
||||
|
@ -11,22 +11,12 @@ import {
|
||||
} from '@grafana/data';
|
||||
import { CompletionItemKind, EditorMode, LanguageCompletionProvider } from '@grafana/experimental';
|
||||
|
||||
import { QueryWithDefaults } from './defaults';
|
||||
import {
|
||||
QueryEditorFunctionExpression,
|
||||
QueryEditorGroupByExpression,
|
||||
QueryEditorPropertyExpression,
|
||||
} from './expressions';
|
||||
|
||||
export interface SqlQueryForInterpolation {
|
||||
dataset?: string;
|
||||
alias?: string;
|
||||
format?: QueryFormat;
|
||||
rawSql?: string;
|
||||
refId: string;
|
||||
hide?: boolean;
|
||||
}
|
||||
|
||||
export interface SQLOptions extends DataSourceJsonData {
|
||||
timeInterval: string;
|
||||
database: string;
|
||||
@ -122,10 +112,10 @@ export interface DB {
|
||||
toRawSql?: (query: SQLQuery) => string;
|
||||
}
|
||||
|
||||
export interface QueryEditorProps {
|
||||
export interface QueryEditorProps<T extends SQLQuery> {
|
||||
db: DB;
|
||||
query: QueryWithDefaults;
|
||||
onChange: (query: SQLQuery) => void;
|
||||
query: T;
|
||||
onChange: (query: T) => void;
|
||||
range?: TimeRange;
|
||||
}
|
||||
|
||||
|
@ -4,18 +4,18 @@ import { DB, SQLExpression, SQLQuery } from '../types';
|
||||
|
||||
import { defaultToRawSql } from './sql.utils';
|
||||
|
||||
interface UseSqlChange {
|
||||
interface UseSqlChange<T extends SQLQuery> {
|
||||
db: DB;
|
||||
query: SQLQuery;
|
||||
onQueryChange: (query: SQLQuery) => void;
|
||||
query: T;
|
||||
onQueryChange: (query: T) => void;
|
||||
}
|
||||
|
||||
export function useSqlChange({ query, onQueryChange, db }: UseSqlChange) {
|
||||
export function useSqlChange<T extends SQLQuery>({ query, onQueryChange, db }: UseSqlChange<T>) {
|
||||
const onSqlChange = useCallback(
|
||||
(sql: SQLExpression) => {
|
||||
const toRawSql = db.toRawSql || defaultToRawSql;
|
||||
const rawSql = toRawSql({ sql, dataset: query.dataset, table: query.table, refId: query.refId });
|
||||
const newQuery: SQLQuery = { ...query, sql, rawSql };
|
||||
const newQuery: T = { ...query, sql, rawSql };
|
||||
onQueryChange(newQuery);
|
||||
},
|
||||
[db, onQueryChange, query]
|
||||
|
Loading…
Reference in New Issue
Block a user