Scopes: Select scope even without data retrieval (#87988)

* Scopes: Select scope even without data retrieval

* Pass entire scope and not only the spec to Prometheus

* Enrich ScopeSpec that is sent to Prometheus

* add name to BE

---------

Co-authored-by: Kyle Brandt <kyle@grafana.com>
This commit is contained in:
Bogdan Matei 2024-05-29 20:09:27 +03:00 committed by GitHub
parent 8b5c9e3e2a
commit 55ea077c3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 56 additions and 17 deletions

View File

@ -1,5 +1,5 @@
// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/dataquery.ts // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/dataquery.ts
import { ScopeSpec, ScopeSpecFilter } from '@grafana/data'; import { Scope, ScopeSpec, ScopeSpecFilter } from '@grafana/data';
import * as common from '@grafana/schema'; import * as common from '@grafana/schema';
export enum QueryEditorMode { export enum QueryEditorMode {
@ -43,7 +43,7 @@ export interface Prometheus extends common.DataQuery {
* Returns a Range vector, comprised of a set of time series containing a range of data points over time for each time series * Returns a Range vector, comprised of a set of time series containing a range of data points over time for each time series
*/ */
range?: boolean; range?: boolean;
scopes?: ScopeSpec[]; scopes?: Array<ScopeSpec & Pick<Scope['metadata'], 'name'>>;
adhocFilters?: ScopeSpecFilter[]; adhocFilters?: ScopeSpecFilter[];
groupByKeys?: string[]; groupByKeys?: string[];
} }

View File

@ -374,7 +374,10 @@ export class PrometheusDatasource
}; };
if (config.featureToggles.promQLScope) { if (config.featureToggles.promQLScope) {
processedTarget.scopes = (request.scopes ?? []).map((scope) => scope.spec); processedTarget.scopes = (request.scopes ?? []).map((scope) => ({
name: scope.metadata.name,
...scope.spec,
}));
} }
if (config.featureToggles.groupByVariable) { if (config.featureToggles.groupByVariable) {

View File

@ -76,8 +76,9 @@ type PrometheusQueryProperties struct {
} }
// ScopeSpec is a hand copy of the ScopeSpec struct from pkg/apis/scope/v0alpha1/types.go // ScopeSpec is a hand copy of the ScopeSpec struct from pkg/apis/scope/v0alpha1/types.go
// to avoid import (temp fix) // to avoid import (temp fix). This also has metadata.name inlined.
type ScopeSpec struct { type ScopeSpec struct {
Name string `json:"name"` // This is the identifier from metadata.name of the scope model.
Title string `json:"title"` Title string `json:"title"`
Type string `json:"type"` Type string `json:"type"`
Description string `json:"description"` Description string `json:"description"`

View File

@ -177,9 +177,10 @@
"description": "A set of filters applied to apply to the query", "description": "A set of filters applied to apply to the query",
"type": "array", "type": "array",
"items": { "items": {
"description": "ScopeSpec is a hand copy of the ScopeSpec struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix)", "description": "ScopeSpec is a hand copy of the ScopeSpec struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix).",
"type": "object", "type": "object",
"required": [ "required": [
"name",
"title", "title",
"type", "type",
"description", "description",
@ -217,6 +218,10 @@
"additionalProperties": false "additionalProperties": false
} }
}, },
"name": {
"description": "This is the identifier from metadata.name of the scope model.",
"type": "string"
},
"title": { "title": {
"type": "string" "type": "string"
}, },

View File

@ -187,9 +187,10 @@
"description": "A set of filters applied to apply to the query", "description": "A set of filters applied to apply to the query",
"type": "array", "type": "array",
"items": { "items": {
"description": "ScopeSpec is a hand copy of the ScopeSpec struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix)", "description": "ScopeSpec is a hand copy of the ScopeSpec struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix).",
"type": "object", "type": "object",
"required": [ "required": [
"name",
"title", "title",
"type", "type",
"description", "description",
@ -227,6 +228,10 @@
"additionalProperties": false "additionalProperties": false
} }
}, },
"name": {
"description": "This is the identifier from metadata.name of the scope model.",
"type": "string"
},
"title": { "title": {
"type": "string" "type": "string"
}, },

View File

@ -8,7 +8,7 @@
{ {
"metadata": { "metadata": {
"name": "default", "name": "default",
"resourceVersion": "1715781995240", "resourceVersion": "1715871691891",
"creationTimestamp": "2024-03-25T13:19:04Z" "creationTimestamp": "2024-03-25T13:19:04Z"
}, },
"spec": { "spec": {
@ -96,7 +96,7 @@
"description": "A set of filters applied to apply to the query", "description": "A set of filters applied to apply to the query",
"items": { "items": {
"additionalProperties": false, "additionalProperties": false,
"description": "ScopeSpec is a hand copy of the ScopeSpec struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix)", "description": "ScopeSpec is a hand copy of the ScopeSpec struct from pkg/apis/scope/v0alpha1/types.go to avoid import (temp fix).",
"properties": { "properties": {
"category": { "category": {
"type": "string" "type": "string"
@ -128,6 +128,10 @@
}, },
"type": "array" "type": "array"
}, },
"name": {
"description": "This is the identifier from metadata.name of the scope model.",
"type": "string"
},
"title": { "title": {
"type": "string" "type": "string"
}, },
@ -136,6 +140,7 @@
} }
}, },
"required": [ "required": [
"name",
"title", "title",
"type", "type",
"description", "description",

View File

@ -53,13 +53,17 @@ export class ScopesFiltersScene extends SceneObjectBase<ScopesFiltersSceneState>
} }
updateFromUrl(values: SceneObjectUrlValues) { updateFromUrl(values: SceneObjectUrlValues) {
let scopes = values.scopes ?? []; let scopesNames = values.scopes ?? [];
scopes = Array.isArray(scopes) ? scopes : [scopes]; scopesNames = Array.isArray(scopesNames) ? scopesNames : [scopesNames];
const scopesPromises = scopes.map((scopeName) => this.server.get(scopeName)); const scopesPromises = scopesNames.map((scopeName) => this.server.get(scopeName));
Promise.all(scopesPromises).then((scopes) => { Promise.all(scopesPromises).then((scopes) => {
this.setState({ scopes }); this.setState({
scopes: scopesNames.map((scopeName, scopeNameIdx) =>
this.mergeScopeNameWithScopes(scopeName, scopes[scopeNameIdx] ?? {})
),
});
}); });
} }
@ -134,21 +138,37 @@ export class ScopesFiltersScene extends SceneObjectBase<ScopesFiltersSceneState>
} }
public async toggleScope(linkId: string) { public async toggleScope(linkId: string) {
let scopes = this.state.scopes; let scopes = [...this.state.scopes];
const selectedIdx = scopes.findIndex((scope) => scope.metadata.name === linkId); const selectedIdx = scopes.findIndex((scope) => scope.metadata.name === linkId);
if (selectedIdx === -1) { if (selectedIdx === -1) {
const scope = await this.server.get(linkId); const receivedScope = await this.server.get(linkId);
if (scope) { scopes.push(this.mergeScopeNameWithScopes(linkId, receivedScope ?? {}));
scopes = [...scopes, scope];
}
} else { } else {
scopes.splice(selectedIdx, 1); scopes.splice(selectedIdx, 1);
} }
this.setState({ scopes }); this.setState({ scopes });
} }
private mergeScopeNameWithScopes(scopeName: string, scope: Partial<Scope>): Scope {
return {
...scope,
metadata: {
name: scopeName,
...scope.metadata,
},
spec: {
filters: [],
title: scopeName,
type: '',
category: '',
description: '',
...scope.spec,
},
};
}
} }
export function ScopesFiltersSceneRenderer({ model }: SceneComponentProps<ScopesFiltersScene>) { export function ScopesFiltersSceneRenderer({ model }: SceneComponentProps<ScopesFiltersScene>) {