Compare commits

..

1 Commits

Author SHA1 Message Date
Kristina Demeshchik
eb8a2617a7 improve loggers 2026-01-07 13:22:36 -05:00
19 changed files with 26 additions and 232 deletions

View File

@@ -13,7 +13,7 @@ import (
// schema is unexported to prevent accidental overwrites
var (
schemaReceiver = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", NewReceiver(), &ReceiverList{}, resource.WithKind("Receiver"),
resource.WithPlural("receivers"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{resource.SelectableField{
resource.WithPlural("receivers"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{{
FieldSelector: "spec.title",
FieldValueFunc: func(o resource.Object) (string, error) {
cast, ok := o.(*Receiver)

View File

@@ -790,8 +790,6 @@ VariableOption: {
text: string | [...string]
// Value of the option
value: string | [...string]
// Additional properties for multi-props variables
properties?: {[string]: string}
}
// Query variable specification

View File

@@ -794,8 +794,6 @@ VariableOption: {
text: string | [...string]
// Value of the option
value: string | [...string]
// Additional properties for multi-props variables
properties?: {[string]: string}
}
// Query variable specification

View File

@@ -301,8 +301,6 @@ var _ resource.ListObject = &DashboardList{}
// Copy methods for all subresource types
// DeepCopy creates a full deep copy of DashboardStatus
func (s *DashboardStatus) DeepCopy() *DashboardStatus {
cpy := &DashboardStatus{}

View File

@@ -301,8 +301,6 @@ var _ resource.ListObject = &DashboardList{}
// Copy methods for all subresource types
// DeepCopy creates a full deep copy of DashboardStatus
func (s *DashboardStatus) DeepCopy() *DashboardStatus {
cpy := &DashboardStatus{}

View File

@@ -794,8 +794,6 @@ VariableOption: {
text: string | [...string]
// Value of the option
value: string | [...string]
// Additional properties for multi-props variables
properties?: {[string]: string}
}
// Query variable specification

View File

@@ -1411,8 +1411,6 @@ type DashboardVariableOption struct {
Text DashboardStringOrArrayOfString `json:"text"`
// Value of the option
Value DashboardStringOrArrayOfString `json:"value"`
// Additional properties for multi-props variables
Properties map[string]string `json:"properties,omitempty"`
}
// NewDashboardVariableOption creates a new DashboardVariableOption object.

View File

@@ -798,8 +798,6 @@ VariableOption: {
text: string | [...string]
// Value of the option
value: string | [...string]
// Additional properties for multi-props variables
properties?: {[string]: string}
}
// Query variable specification

View File

@@ -1414,8 +1414,6 @@ type DashboardVariableOption struct {
Text DashboardStringOrArrayOfString `json:"text"`
// Value of the option
Value DashboardStringOrArrayOfString `json:"value"`
// Additional properties for multi-props variables
Properties map[string]string `json:"properties,omitempty"`
}
// NewDashboardVariableOption creates a new DashboardVariableOption object.

File diff suppressed because one or more lines are too long

View File

@@ -76,29 +76,38 @@ func convertAPIVersionToFuncName(apiVersion string) string {
// It optionally runs a data loss check function after successful conversion
func withConversionMetrics(sourceVersionAPI, targetVersionAPI string, conversionFunc func(a, b interface{}, scope conversion.Scope) error) func(a, b interface{}, scope conversion.Scope) error {
return func(a, b interface{}, scope conversion.Scope) error {
// Extract dashboard UID and schema version from source
// Extract dashboard UID, title, and schema version from source
var dashboardUID string
var dashboardTitle string
var sourceSchemaVersion interface{}
var targetSchemaVersion interface{}
// Try to extract UID and schema version from source dashboard
// Try to extract UID, title, and schema version from source dashboard
// Only track schema versions for v0/v1 dashboards (v2+ info is redundant with API version)
switch source := a.(type) {
case *dashv0.Dashboard:
dashboardUID = source.Name
if source.Spec.Object != nil {
sourceSchemaVersion = schemaversion.GetSchemaVersion(source.Spec.Object)
if title, ok := source.Spec.Object["title"].(string); ok {
dashboardTitle = title
}
}
case *dashv1.Dashboard:
dashboardUID = source.Name
if source.Spec.Object != nil {
sourceSchemaVersion = schemaversion.GetSchemaVersion(source.Spec.Object)
if title, ok := source.Spec.Object["title"].(string); ok {
dashboardTitle = title
}
}
case *dashv2alpha1.Dashboard:
dashboardUID = source.Name
dashboardTitle = source.Spec.Title
// Don't track schema version for v2+ (redundant with API version)
case *dashv2beta1.Dashboard:
dashboardUID = source.Name
dashboardTitle = source.Spec.Title
// Don't track schema version for v2+ (redundant with API version)
}
@@ -167,6 +176,7 @@ func withConversionMetrics(sourceVersionAPI, targetVersionAPI string, conversion
"targetVersionAPI", targetVersionAPI,
"erroredConversionFunc", getErroredConversionFunc(err),
"dashboardUID", dashboardUID,
"dashboardTitle", dashboardTitle,
}
// Add schema version fields only if we have them (v0/v1 dashboards)
@@ -227,6 +237,7 @@ func withConversionMetrics(sourceVersionAPI, targetVersionAPI string, conversion
"sourceVersionAPI", sourceVersionAPI,
"targetVersionAPI", targetVersionAPI,
"dashboardUID", dashboardUID,
"dashboardTitle", dashboardTitle,
}
// Add schema version fields only if we have them (v0/v1 dashboards)

View File

@@ -18,8 +18,6 @@ import (
v1beta1 "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
)
var ()
var appManifestData = app.ManifestData{
AppName: "folder",
Group: "folder.grafana.app",

View File

@@ -1,174 +0,0 @@
import { useEffect, useState } from 'react';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import {
CustomVariableSupport,
DataQueryRequest,
DataQueryResponse,
QueryEditorProps,
Field,
DataFrame,
MetricFindValue,
} from '@grafana/data';
import { t } from '@grafana/i18n';
import { EditorMode, EditorRows, EditorRow, EditorField } from '@grafana/plugin-ui';
import { Combobox, ComboboxOption } from '@grafana/ui';
import { SqlQueryEditorLazy } from './components/QueryEditorLazy';
import { SqlDatasource } from './datasource/SqlDatasource';
import { applyQueryDefaults } from './defaults';
import { QueryFormat, type SQLQuery, type SQLOptions, type SQLQueryMeta } from './types';
type SQLVariableQuery = { query: string } & SQLQuery;
const refId = 'SQLVariableQueryEditor-VariableQuery';
export class SQLVariableSupport extends CustomVariableSupport<SqlDatasource, SQLQuery> {
constructor(readonly datasource: SqlDatasource) {
super();
}
editor = SQLVariablesQueryEditor;
query(request: DataQueryRequest<SQLQuery>): Observable<DataQueryResponse> {
if (request.targets.length < 1) {
throw new Error('no variable query found');
}
const updatedQuery = migrateVariableQuery(request.targets[0]);
return this.datasource.query({ ...request, targets: [updatedQuery] }).pipe(
map((d: DataQueryResponse) => {
const frames = d.data || [];
const metricFindValues = convertDataFramesToMetricFindValues(frames, updatedQuery.meta);
return { data: metricFindValues };
})
);
}
getDefaultQuery(): Partial<SQLQuery> {
return applyQueryDefaults({ refId, editorMode: EditorMode.Builder, format: QueryFormat.Table });
}
}
type SQLVariableQueryEditorProps = QueryEditorProps<SqlDatasource, SQLQuery, SQLOptions>;
const SQLVariablesQueryEditor = (props: SQLVariableQueryEditorProps) => {
const query = migrateVariableQuery(props.query);
return (
<>
<SqlQueryEditorLazy {...props} query={query} />
<FieldMapping {...props} query={query} />
</>
);
};
const FieldMapping = (props: SQLVariableQueryEditorProps) => {
const { query, datasource, onChange } = props;
const [choices, setChoices] = useState<ComboboxOption[]>([]);
useEffect(() => {
let isActive = true;
// eslint-disable-next-line
const subscription = datasource.query({ targets: [query] } as DataQueryRequest<SQLQuery>).subscribe({
next: (response) => {
if (!isActive) {
return;
}
const fieldNames = (response.data[0] || { fields: [] }).fields.map((f: Field) => f.name);
setChoices(fieldNames.map((f: Field) => ({ value: f, label: f })));
},
error: () => {
if (isActive) {
setChoices([]);
}
},
});
return () => {
isActive = false;
subscription.unsubscribe();
};
}, [datasource, query]);
const onMetaPropChange = <Key extends keyof SQLQueryMeta, Value extends SQLQueryMeta[Key]>(
key: Key,
value: Value,
meta = query.meta || {}
) => {
onChange({ ...query, meta: { ...meta, [key]: value } });
};
return (
<EditorRows>
<EditorRow>
<EditorField label={t('grafana-sql.components.query-meta.variables.valueField', 'Value Field')}>
<Combobox
isClearable
value={query.meta?.valueField}
onChange={(e) => onMetaPropChange('valueField', e?.value)}
width={40}
options={choices}
/>
</EditorField>
<EditorField label={t('grafana-sql.components.query-meta.variables.textField', 'Text Field')}>
<Combobox
isClearable
value={query.meta?.textField}
onChange={(e) => onMetaPropChange('textField', e?.value)}
width={40}
options={choices}
/>
</EditorField>
</EditorRow>
</EditorRows>
);
};
const migrateVariableQuery = (rawQuery: string | SQLQuery): SQLVariableQuery => {
if (typeof rawQuery !== 'string') {
return {
...rawQuery,
refId: rawQuery.refId || refId,
query: rawQuery.rawSql || '',
};
}
return {
...applyQueryDefaults({
refId,
rawSql: rawQuery,
editorMode: rawQuery ? EditorMode.Code : EditorMode.Builder,
}),
query: rawQuery,
};
};
const convertDataFramesToMetricFindValues = (frames: DataFrame[], meta?: SQLQueryMeta): MetricFindValue[] => {
if (!frames.length) {
throw new Error('no results found');
}
const frame = frames[0];
const fields = frame.fields;
if (fields.length < 1) {
throw new Error('no fields found in the response');
}
let textField = fields.find((f) => f.name === '__text');
let valueField = fields.find((f) => f.name === '__value');
if (meta?.textField) {
textField = fields.find((f) => f.name === meta.textField);
}
if (meta?.valueField) {
valueField = fields.find((f) => f.name === meta.valueField);
}
const resolvedTextField = textField || valueField || fields[0];
const resolvedValueField = valueField || textField || fields[0];
const results: MetricFindValue[] = [];
const rowCount = frame.length;
for (let i = 0; i < rowCount; i++) {
const text = String(resolvedTextField.values[i] ?? '');
const value = String(resolvedValueField.values[i] ?? '');
const properties: Record<string, string> = {};
for (const field of fields) {
properties[field.name] = String(field.values[i] ?? '');
}
results.push({ text, value, properties });
}
return results;
};

View File

@@ -21,7 +21,6 @@ export { TLSSecretsConfig } from './components/configuration/TLSSecretsConfig';
export { useMigrateDatabaseFields } from './components/configuration/useMigrateDatabaseFields';
export { SqlQueryEditorLazy } from './components/QueryEditorLazy';
export type { QueryHeaderProps } from './components/QueryHeader';
export { SQLVariableSupport } from './SQLVariableSupport';
export { createSelectClause, haveColumns } from './utils/sql.utils';
export { applyQueryDefaults } from './defaults';
export { makeVariable } from './utils/testHelpers';

View File

@@ -69,12 +69,6 @@
"placeholder-select-format": "Select format",
"run-query": "Run query"
},
"query-meta": {
"variables": {
"textField": "Text Field",
"valueField": "Value Field"
}
},
"query-toolbox": {
"content-hit-ctrlcmdreturn-to-run-query": "Hit CTRL/CMD+Return to run query",
"tooltip-collapse": "Collapse editor",

View File

@@ -50,8 +50,6 @@ export enum QueryFormat {
Table = 'table',
}
export type SQLQueryMeta = { valueField?: string; textField?: string };
export interface SQLQuery extends DataQuery {
alias?: string;
format?: QueryFormat;
@@ -61,7 +59,6 @@ export interface SQLQuery extends DataQuery {
sql?: SQLExpression;
editorMode?: EditorMode;
rawQuery?: boolean;
meta?: SQLQueryMeta;
}
export interface NameValue {

View File

@@ -284,7 +284,6 @@ function variableValueOptionsToVariableOptions(varState: MultiValueVariable['sta
value: String(o.value),
text: o.label,
selected: Array.isArray(varState.value) ? varState.value.includes(o.value) : varState.value === o.value,
...(o.properties && { properties: o.properties }),
}));
}

View File

@@ -80,18 +80,18 @@ const buildLabelPath = (label: string) => {
return label.includes('.') || label.trim().includes(' ') ? `["${label}"]` : `.${label}`;
};
const isRecord = (value: unknown): value is Record<string, unknown> => {
return typeof value === 'object' && value !== null && !Array.isArray(value);
};
const getVariableValueProperties = (variable: TypedVariableModel): string[] => {
function collectFieldPaths(option: Record<string, unknown>, currentPath: string): string[] {
if (!('valuesFormat' in variable) || variable.valuesFormat !== 'json') {
return [];
}
function collectFieldPaths(option: Record<string, string>, currentPath: string) {
let paths: string[] = [];
for (const field in option) {
if (option.hasOwnProperty(field)) {
const newPath = `${currentPath}.${field}`;
const value = option[field];
if (isRecord(value)) {
if (typeof value === 'object' && value !== null) {
paths = [...paths, ...collectFieldPaths(value, newPath)];
}
paths.push(newPath);
@@ -100,23 +100,11 @@ const getVariableValueProperties = (variable: TypedVariableModel): string[] => {
return paths;
}
if ('valuesFormat' in variable && variable.valuesFormat === 'json') {
try {
return collectFieldPaths(JSON.parse(variable.query)[0], variable.name);
} catch {
return [];
}
try {
return collectFieldPaths(JSON.parse(variable.query)[0], variable.name);
} catch {
return [];
}
if ('options' in variable && Array.isArray(variable.options) && variable.options.length > 0) {
for (const opt of variable.options) {
if ('properties' in opt && isRecord(opt.properties) && Object.keys(opt.properties).length > 0) {
return collectFieldPaths(opt.properties, variable.name);
}
}
}
return [];
};
export const getPanelLinksVariableSuggestions = (): VariableSuggestion[] => [

View File

@@ -11,7 +11,6 @@ import {
SQLQuery,
SQLSelectableValue,
SqlDatasource,
SQLVariableSupport,
formatSQL,
} from '@grafana/sql';
@@ -26,7 +25,6 @@ export class PostgresDatasource extends SqlDatasource {
constructor(instanceSettings: DataSourceInstanceSettings<PostgresOptions>) {
super(instanceSettings);
this.variables = new SQLVariableSupport(this);
}
getQueryModel(target?: SQLQuery, templateSrv?: TemplateSrv, scopedVars?: ScopedVars): PostgresQueryModel {