sql: extract frontend code into separate package (#81109)

* sql: extract frontend code into separate package

* updated package version
This commit is contained in:
Gábor Farkas
2024-01-26 11:38:29 +01:00
committed by GitHub
parent 2febbec758
commit 29e8a355cb
87 changed files with 187 additions and 104 deletions
@@ -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 };
}