sql: extract frontend code into separate package (#81109)
* sql: extract frontend code into separate package * updated package version
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
// @ts-ignore
|
||||
import sqlFormatter from 'sql-formatter-plus';
|
||||
|
||||
export function formatSQL(q: string) {
|
||||
return sqlFormatter.format(q).replace(/(\$ \{ .* \})|(\$ __)|(\$ \w+)/g, (m: string) => {
|
||||
return m.replace(/\s/g, '');
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { QueryFormat } from '../types';
|
||||
|
||||
import migrateAnnotation from './migration';
|
||||
|
||||
describe('Annotation migration', () => {
|
||||
const annotation = {
|
||||
datasource: {
|
||||
uid: 'P4FDCC188E688367F',
|
||||
type: 'mysql',
|
||||
},
|
||||
enable: false,
|
||||
hide: false,
|
||||
iconColor: 'rgba(0, 211, 255, 1)',
|
||||
limit: 100,
|
||||
name: 'Single',
|
||||
rawQuery:
|
||||
"SELECT\n createdAt as time,\n 'single' as text,\n hostname as tags\nFROM\n grafana_metric\nWHERE\n $__timeFilter(createdAt)\nORDER BY time\nLIMIT 1\n",
|
||||
showIn: 0,
|
||||
tags: [],
|
||||
type: 'tags',
|
||||
};
|
||||
|
||||
it('should migrate from old format to new', () => {
|
||||
const newAnnotationFormat = migrateAnnotation(annotation);
|
||||
expect(newAnnotationFormat.target?.format).toBe(QueryFormat.Table);
|
||||
expect(newAnnotationFormat.target?.rawSql).toBe(annotation.rawQuery);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { AnnotationQuery } from '@grafana/data';
|
||||
|
||||
import { applyQueryDefaults } from '../defaults';
|
||||
import { SQLQuery } from '../types';
|
||||
|
||||
export default function migrateAnnotation(annotation: AnnotationQuery<SQLQuery>) {
|
||||
const oldQuery = typeof annotation.rawQuery === 'string' ? annotation.rawQuery : null;
|
||||
|
||||
if (!oldQuery) {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
const newQuery = applyQueryDefaults({ refId: 'Annotation', ...(annotation.target ?? {}), rawSql: oldQuery });
|
||||
|
||||
return {
|
||||
...annotation,
|
||||
rawQuery: undefined,
|
||||
workspace: undefined,
|
||||
subscription: undefined,
|
||||
queryType: undefined,
|
||||
target: newQuery,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
QueryEditorExpressionType,
|
||||
QueryEditorFunctionExpression,
|
||||
QueryEditorGroupByExpression,
|
||||
QueryEditorPropertyExpression,
|
||||
QueryEditorPropertyType,
|
||||
} from '../expressions';
|
||||
import { SQLExpression } from '../types';
|
||||
|
||||
export function createSelectClause(sqlColumns: NonNullable<SQLExpression['columns']>): string {
|
||||
const columns = sqlColumns.map((c) => {
|
||||
let rawColumn = '';
|
||||
if (c.name && c.alias) {
|
||||
rawColumn += `${c.name}(${c.parameters?.map((p) => `${p.name}`)}) AS ${c.alias}`;
|
||||
} else if (c.name) {
|
||||
rawColumn += `${c.name}(${c.parameters?.map((p) => `${p.name}`)})`;
|
||||
} else if (c.alias) {
|
||||
rawColumn += `${c.parameters?.map((p) => `${p.name}`)} AS ${c.alias}`;
|
||||
} else {
|
||||
rawColumn += `${c.parameters?.map((p) => `${p.name}`)}`;
|
||||
}
|
||||
return rawColumn;
|
||||
});
|
||||
return `SELECT ${columns.join(', ')} `;
|
||||
}
|
||||
|
||||
export const haveColumns = (columns: SQLExpression['columns']): columns is NonNullable<SQLExpression['columns']> => {
|
||||
if (!columns) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const haveColumn = columns.some((c) => c.parameters?.length || c.parameters?.some((p) => p.name));
|
||||
const haveFunction = columns.some((c) => c.name);
|
||||
return haveColumn || haveFunction;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a GroupByExpression for a specified field
|
||||
*/
|
||||
export function setGroupByField(field?: string): QueryEditorGroupByExpression {
|
||||
return {
|
||||
type: QueryEditorExpressionType.GroupBy,
|
||||
property: {
|
||||
type: QueryEditorPropertyType.String,
|
||||
name: field,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PropertyExpression for a specified field
|
||||
*/
|
||||
export function setPropertyField(field?: string): QueryEditorPropertyExpression {
|
||||
return {
|
||||
type: QueryEditorExpressionType.Property,
|
||||
property: {
|
||||
type: QueryEditorPropertyType.String,
|
||||
name: field,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createFunctionField(functionName?: string): QueryEditorFunctionExpression {
|
||||
return {
|
||||
type: QueryEditorExpressionType.Function,
|
||||
name: functionName,
|
||||
parameters: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { CustomVariableModel, LoadingState, VariableHide } from '@grafana/data';
|
||||
|
||||
export function makeVariable(id: string, name: string, attributes?: Partial<CustomVariableModel>): CustomVariableModel {
|
||||
return {
|
||||
multi: false,
|
||||
type: 'custom',
|
||||
includeAll: false,
|
||||
current: {},
|
||||
options: [],
|
||||
query: '',
|
||||
rootStateKey: null,
|
||||
global: false,
|
||||
hide: VariableHide.dontHide,
|
||||
skipUrlSync: false,
|
||||
index: -1,
|
||||
state: LoadingState.NotStarted,
|
||||
error: null,
|
||||
description: null,
|
||||
...attributes,
|
||||
id,
|
||||
name,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { DB, SQLExpression, SQLQuery } from '../types';
|
||||
|
||||
interface UseSqlChange {
|
||||
db: DB;
|
||||
query: SQLQuery;
|
||||
onQueryChange: (query: SQLQuery) => void;
|
||||
}
|
||||
|
||||
export function useSqlChange({ query, onQueryChange, db }: UseSqlChange) {
|
||||
const onSqlChange = useCallback(
|
||||
(sql: SQLExpression) => {
|
||||
const toRawSql = db.toRawSql;
|
||||
const rawSql = toRawSql({ sql, dataset: query.dataset, table: query.table, refId: query.refId });
|
||||
const newQuery: SQLQuery = { ...query, sql, rawSql };
|
||||
onQueryChange(newQuery);
|
||||
},
|
||||
[db, onQueryChange, query]
|
||||
);
|
||||
|
||||
return { onSqlChange };
|
||||
}
|
||||
Reference in New Issue
Block a user