Make SQLOptions and SQLQuery in SQLDatasource and in Editor generic

pull/51765/head
Zoltán Bedi 3 years ago
parent f14b45bc86
commit 1d15b4061a
  1. 24
      public/app/features/plugins/sql/components/QueryEditor.tsx
  2. 13
      public/app/features/plugins/sql/components/QueryHeader.tsx
  3. 5
      public/app/features/plugins/sql/components/TableSelector.tsx
  4. 17
      public/app/features/plugins/sql/components/query-editor-raw/QueryEditorRaw.tsx
  5. 16
      public/app/features/plugins/sql/components/query-editor-raw/RawEditor.tsx
  6. 11
      public/app/features/plugins/sql/components/visual-query-builder/SQLGroupByRow.tsx
  7. 11
      public/app/features/plugins/sql/components/visual-query-builder/SQLOrderByRow.tsx
  8. 11
      public/app/features/plugins/sql/components/visual-query-builder/SQLSelectRow.tsx
  9. 11
      public/app/features/plugins/sql/components/visual-query-builder/SQLWhereRow.tsx
  10. 10
      public/app/features/plugins/sql/components/visual-query-builder/VisualEditor.tsx
  11. 24
      public/app/features/plugins/sql/datasource/SqlDatasource.ts
  12. 6
      public/app/features/plugins/sql/defaults.ts
  13. 16
      public/app/features/plugins/sql/types.ts
  14. 10
      public/app/features/plugins/sql/utils/useSqlChange.ts

@ -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>;
export function SqlQueryEditor({ datasource, query, onChange, onRunQuery, range }: Props) {
type Props<TSQLQuery extends SQLQuery, TSQLOptions extends SQLOptions> = QueryEditorProps<
SqlDatasource<TSQLQuery, TSQLOptions>,
TSQLQuery,
TSQLOptions
>;
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';
export abstract class SqlDatasource extends DataSourceWithBackend<SQLQuery, SQLOptions> {
import { DB, SQLQuery, SQLOptions, ResponseParser, SqlQueryModel, QueryFormat } from '../types';
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…
Cancel
Save