Compare commits

...

26 Commits

Author SHA1 Message Date
grafakus 7f584d143f shrug... 2026-01-14 15:15:51 +01:00
grafakus e38240369b Update cue files++ 2026-01-14 14:58:36 +01:00
grafakus 26ac64cf1a Merge branch 'main' into grafakus/query-variable-support-multiprops 2026-01-14 14:20:50 +01:00
Fabrizio ba6a783997 Add Db2 plugin (#116190)
* Add new Db2 plugin

* Fix ID

* Fix ID

* Run `i18n-extract`

* Linting

* Fix ID

* Linting

* Rename plugin

* Fix i18n entry

* Run `yarn i18n-extract`
2026-01-14 14:20:24 +01:00
grafakus d8777012cb Fix lint issues 2026-01-14 14:19:18 +01:00
grafakus dfa07d8e10 Fix openapi tests 2026-01-14 14:17:43 +01:00
Andreas Christou f704b8aa79 Cloud Monitoring: Add support for Google Cloud universe_domain (#115931)
* feat(cloud-monitoring): add support for Google Cloud universe_domain (#110083)

This change introduces support for Google Cloud's `universe_domain`, enabling connections to sovereign cloud environments with custom API endpoints.

- Adds an optional "Universe Domain" field in the Google Cloud Monitoring data source configuration (frontend and backend).
- Allows specifying a custom API domain (e.g., `s3nsapis.fr`) for use in sovereign environments.
- Defaults to `googleapis.com` to ensure backward compatibility for existing configurations.

Signed-off-by: Andreas Christou <andreas.christou@grafana.com>

* Minor docs update

* Doc updates

* Update editor

* Update docs/sources/datasources/google-cloud-monitoring/_index.md

Co-authored-by: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com>

* Lint

* Review

---------

Signed-off-by: Andreas Christou <andreas.christou@grafana.com>
Co-authored-by: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com>
2026-01-14 13:16:09 +00:00
grafakus dc63eb3314 Regen 2026-01-14 14:03:13 +01:00
Andreas Christou c1a46fdcb5 Elasticsearch: Decoupling from core (#115900)
* Complete decoupling of backend

- Replace usage of featuremgmt
- Copy simplejson
- Add standalone logic

* Complete frontend decoupling

- Fix imports
- Copy store and reducer logic

* Add required files for full decoupling

* Regen cue

* Prettier

* Remove unneeded script

* Jest fix

* Add jest config

* Lint

* Lit

* Prune suppresions
2026-01-14 12:54:21 +00:00
Cauê Marcondes 7143324229 Elasticsearch: Add support for serverless connections (#114855)
* serverless connecction

* Adding api key

* fix

* addressing pr comments

* fixing tests

* refactoring

* changing to value semantic

* addressing pr comments

* minor changes

---------

Co-authored-by: Lucas Francisco Lopez <lucas.lopez@elastic.co>
2026-01-14 12:51:42 +00:00
grafakus 1e3c6fed55 Merge branch 'main' into grafakus/query-variable-support-multiprops
# Conflicts:
#	apps/dashboard/pkg/apis/dashboard_manifest.go
2026-01-14 13:50:54 +01:00
grafakus d02201f564 Update schemas 2026-01-14 13:41:21 +01:00
Ryan McKinley 48625d67e5 Chore: update blevesearch dependencies (#116251) 2026-01-14 12:15:19 +00:00
grafakus 2a9377ac02 QueryVariable: Support preview and autocomplete of multi-props 2026-01-14 13:14:32 +01:00
Jack Westbrook 8bad33de4c Grafana/data: Fix theme types schema resolution (#116240)
* fix(grafana-data): copy theme schema json to types so declaration resolves

* refactor(grafana-data): move node scripts out of source code

* feat(grafana-data): generate types for theme schema

* chore(codeowners): update for grafana-data/scripts file move

* feat(grafana-data): put back copy plugin for theme json files

* revert(grafana-data): remove definition output

* feat(grafana-data): make builds great again

* minor tidy up

---------

Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
2026-01-14 12:05:23 +00:00
Ryan McKinley 040854c8af Search: Allow query field selection (#116238) 2026-01-14 11:55:05 +00:00
Rafael Bortolon Paulovic 987c1fc6b6 feat(unified): add index scoring model config (#116210)
* feat(unified): add bm25 index scoring model

We want try BM25 scoring model since they have global scoring which we can probably re-use for fan-in/fan-out logic

https://github.com/blevesearch/bleve/blob/32d98823c4b7482c62cc6c847508ed7659c23c37/docs/scoring.md#global-scoring

* fix(plugins): update plugin test data
2026-01-14 12:07:53 +01:00
Alejandro Fraenkel 170ac31c5a Alerting: Add alertingNavigationV2 feature toggle (#116215)
feat(alerting): add alertingNavigationV2 feature toggle

Introduces a new feature toggle to enable the improved Alerting navigation
structure with grouped menu items. This toggle will allow:
- Safe incremental rollout of navigation changes
- Quick rollback if issues arise
- Handling BE/FE deployment timing differences

Toggle details:
- Name: alertingNavigationV2
- Stage: Experimental
- Owner: @grafana/alerting-squad
- Default: false (disabled)
- Affects: Both backend (navtree) and frontend (navigation hooks)
2026-01-14 11:58:11 +01:00
Dominik Prokop 0d1e0bc21c PanelMenu: use openInNewTab links extensions API correctly (#116200)
* Extensons: Make links use openInNewTab API

* Use openInNewTab api correctly in the UI

* Bump scenes

* Fx circular dep

* test

* Revert "test"

This reverts commit 8784a7992c.
2026-01-14 11:29:43 +01:00
Natalia Bernarte Oses afd84f0335 Datagrid: Deprecate panel (#116071)
* deprecate datagrid

* Update docs/sources/visualizations/panels-visualizations/visualizations/datagrid/index.md

Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>

---------

Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
2026-01-14 11:10:51 +01:00
Andres Martinez Gotor d680537ea1 Advisor: Simplify interface used (#116191) 2026-01-14 11:05:16 +01:00
Bogdan Matei 78d507d285 Dynamic Dashboards: Change the stage of the feature toggle (#116189) 2026-01-14 09:50:37 +00:00
Tito Lins 9d1d0e72c2 Alerting: add sync timer support (#114602)
- add new feature flag to support enabling the dispatcher sync timer on the alertmanager
- this attempts to synchronize the flushes across HA nodes to decrease amount of duplicate notifications

---------

Co-authored-by: Yuri Tseretyan <yuriy.tseretyan@grafana.com>
2026-01-14 10:04:29 +01:00
Konrad Lalik fd955f90ac Alerting: Enable server-side folder search for GMA rules (#116201)
* Alerting: Support backend filtering for folder search

Updates the Grafana managed rules API and filter logic to support
server-side filtering by folder (namespace).

Changes:
- Add `searchFolder` parameter to `getGrafanaGroups` API endpoint
- Map filter state `namespace` to `searchFolder` in backend filter
- Disable client-side namespace filtering when backend filtering is enabled
- Update tests to verify correct behavior for folder search with backend filters

* Add missing property in filter options

* Update tests
2026-01-14 09:48:07 +01:00
Sonia Aguilar ccb032f376 Alerting: Single alertmanager contact points versions (#116076)
* POC ssingle AM

* wip

* add query param ?version=2

* wip2

* wip3

* Update logic

* update badges and tests

* remove unsused import

* fix: update NewReceiverView snapshots to include version field

* update translations

* fix: delegate version determination to backend for new integrations

- Remove hardcoded version: 'v1' from defaultChannelValues
- Reset version to undefined when integration type changes
- Backend uses GetCurrentVersion() when no version is provided
- Update snapshots to reflect version handling changes
- Remove unused getDefaultVersionForNotifier function

* update snapshot

* fix(alerting): fix contact point form issues

- Fix empty info alert showing when notifier.dto.info is undefined
- Fix options not loading for new contact points by using default creatable version

* fix(alerting): only show version badge for legacy integrations

* update tests for version badge and getOptionsForVersion changes

* docs: add comment explaining currentVersion field in NotifierDTO

* Show user-friendly 'Legacy' label for legacy integrations

- Replace technical version strings (v0mimir1, v0mimir2) with user-friendly labels
- v0mimir1 -> 'Legacy', v0mimir2 -> 'Legacy v2', etc.
- Technical version is still shown in tooltip for reference
- Add getLegacyVersionLabel() utility function
- Update tests for badge display and utility function

* Add v0mimir2 to test mock for Legacy v2 badge test

* hasLegacyIntegrations now uses isLegacyVersion

- Accept notifiers array to properly check canCreate: false
- No longer relies on version string comparison (v1 check)
- Uses isLegacyVersion for consistent legacy detection
- Update tests to pass notifiers and test correct behavior

* update translations
2026-01-14 08:31:13 +01:00
Alex Khomenko cf452c167b Provisioning: Do not show the page when the toggle is off (#116206) 2026-01-14 07:41:10 +02:00
193 changed files with 4935 additions and 594 deletions
+1
View File
@@ -543,6 +543,7 @@ i18next.config.ts @grafana/grafana-frontend-platform
/packages/grafana-data/tsconfig.json @grafana/grafana-frontend-platform /packages/grafana-data/tsconfig.json @grafana/grafana-frontend-platform
/packages/grafana-data/test/ @grafana/grafana-frontend-platform /packages/grafana-data/test/ @grafana/grafana-frontend-platform
/packages/grafana-data/typings/ @grafana/grafana-frontend-platform /packages/grafana-data/typings/ @grafana/grafana-frontend-platform
/packages/grafana-data/scripts/ @grafana/grafana-frontend-platform
/packages/grafana-data/src/**/*logs* @grafana/observability-logs /packages/grafana-data/src/**/*logs* @grafana/observability-logs
/packages/grafana-data/src/context/plugins/ @grafana/plugins-platform-frontend /packages/grafana-data/src/context/plugins/ @grafana/plugins-platform-frontend
+2
View File
@@ -121,6 +121,8 @@ linters:
- '**/pkg/tsdb/zipkin/**/*' - '**/pkg/tsdb/zipkin/**/*'
- '**/pkg/tsdb/jaeger/*' - '**/pkg/tsdb/jaeger/*'
- '**/pkg/tsdb/jaeger/**/*' - '**/pkg/tsdb/jaeger/**/*'
- '**/pkg/tsdb/elasticsearch/*'
- '**/pkg/tsdb/elasticsearch/**/*'
deny: deny:
- pkg: github.com/grafana/grafana/pkg/api - pkg: github.com/grafana/grafana/pkg/api
desc: Core plugins are not allowed to depend on Grafana core packages desc: Core plugins are not allowed to depend on Grafana core packages
@@ -28,7 +28,7 @@ type check struct {
PluginStore pluginstore.Store PluginStore pluginstore.Store
PluginContextProvider PluginContextProvider PluginContextProvider PluginContextProvider
PluginClient plugins.Client PluginClient plugins.Client
PluginRepo repo.Service PluginRepo checks.PluginInfoGetter
GrafanaVersion string GrafanaVersion string
pluginCanBeInstalledCache map[string]bool pluginCanBeInstalledCache map[string]bool
pluginExistsCacheMu sync.RWMutex pluginExistsCacheMu sync.RWMutex
@@ -39,7 +39,7 @@ func New(
pluginStore pluginstore.Store, pluginStore pluginstore.Store,
pluginContextProvider PluginContextProvider, pluginContextProvider PluginContextProvider,
pluginClient plugins.Client, pluginClient plugins.Client,
pluginRepo repo.Service, pluginRepo checks.PluginInfoGetter,
grafanaVersion string, grafanaVersion string,
) checks.Check { ) checks.Check {
return &check{ return &check{
@@ -15,7 +15,7 @@ import (
type missingPluginStep struct { type missingPluginStep struct {
PluginStore pluginstore.Store PluginStore pluginstore.Store
PluginRepo repo.Service PluginRepo checks.PluginInfoGetter
GrafanaVersion string GrafanaVersion string
} }
+8
View File
@@ -5,6 +5,7 @@ import (
"github.com/grafana/grafana-app-sdk/logging" "github.com/grafana/grafana-app-sdk/logging"
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1" advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
"github.com/grafana/grafana/pkg/plugins/repo"
) )
// Check returns metadata about the check being executed and the list of Steps // Check returns metadata about the check being executed and the list of Steps
@@ -37,3 +38,10 @@ type Step interface {
// Run executes the step for an item and returns a report // Run executes the step for an item and returns a report
Run(ctx context.Context, log logging.Logger, obj *advisorv0alpha1.CheckSpec, item any) ([]advisorv0alpha1.CheckReportFailure, error) Run(ctx context.Context, log logging.Logger, obj *advisorv0alpha1.CheckSpec, item any) ([]advisorv0alpha1.CheckReportFailure, error)
} }
// PluginInfoGetter is a minimal interface for retrieving plugin information from a repository.
// It contains only the GetPluginsInfo method used by plugincheck and datasourcecheck.
type PluginInfoGetter interface {
// GetPluginsInfo will return a list of plugins from grafana.com/api/plugins.
GetPluginsInfo(ctx context.Context, options repo.GetPluginsInfoOptions, compatOpts repo.CompatOpts) ([]repo.PluginInfo, error)
}
@@ -17,7 +17,7 @@ const (
func New( func New(
pluginStore pluginstore.Store, pluginStore pluginstore.Store,
pluginRepo repo.Service, pluginRepo checks.PluginInfoGetter,
updateChecker pluginchecker.PluginUpdateChecker, updateChecker pluginchecker.PluginUpdateChecker,
pluginErrorResolver plugins.ErrorResolver, pluginErrorResolver plugins.ErrorResolver,
grafanaVersion string, grafanaVersion string,
@@ -33,7 +33,7 @@ func New(
type check struct { type check struct {
PluginStore pluginstore.Store PluginStore pluginstore.Store
PluginRepo repo.Service PluginRepo checks.PluginInfoGetter
updateChecker pluginchecker.PluginUpdateChecker updateChecker pluginchecker.PluginUpdateChecker
pluginErrorResolver plugins.ErrorResolver pluginErrorResolver plugins.ErrorResolver
GrafanaVersion string GrafanaVersion string
@@ -800,6 +800,8 @@ VariableOption: {
text: string | [...string] text: string | [...string]
// Value of the option // Value of the option
value: string | [...string] value: string | [...string]
// Additional properties for multi-props variables
properties?: {[string]: string}
} }
// Query variable specification // Query variable specification
@@ -804,6 +804,8 @@ VariableOption: {
text: string | [...string] text: string | [...string]
// Value of the option // Value of the option
value: string | [...string] value: string | [...string]
// Additional properties for multi-props variables
properties?: {[string]: string}
} }
// Query variable specification // Query variable specification
@@ -241,6 +241,8 @@ lineage: schemas: [{
text: string | [...string] text: string | [...string]
// Value of the option // Value of the option
value: string | [...string] value: string | [...string]
// Additional properties for multi-props variables
properties?: {[string]: string}
} @cuetsy(kind="interface") } @cuetsy(kind="interface")
// Options to config when to refresh a variable // Options to config when to refresh a variable
@@ -241,6 +241,8 @@ lineage: schemas: [{
text: string | [...string] text: string | [...string]
// Value of the option // Value of the option
value: string | [...string] value: string | [...string]
// Additional properties for multi-props variables
properties?: {[string]: string}
} @cuetsy(kind="interface") } @cuetsy(kind="interface")
// Options to config when to refresh a variable // Options to config when to refresh a variable
@@ -804,6 +804,8 @@ VariableOption: {
text: string | [...string] text: string | [...string]
// Value of the option // Value of the option
value: string | [...string] value: string | [...string]
// Additional properties for multi-props variables
properties?: {[string]: string}
} }
// Query variable specification // Query variable specification
@@ -1426,6 +1426,8 @@ type DashboardVariableOption struct {
Text DashboardStringOrArrayOfString `json:"text"` Text DashboardStringOrArrayOfString `json:"text"`
// Value of the option // Value of the option
Value DashboardStringOrArrayOfString `json:"value"` Value DashboardStringOrArrayOfString `json:"value"`
// Additional properties for multi-props variables
Properties map[string]string `json:"properties,omitempty"`
} }
// NewDashboardVariableOption creates a new DashboardVariableOption object. // NewDashboardVariableOption creates a new DashboardVariableOption object.
@@ -5133,6 +5133,22 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardVariableOption(ref common.Refer
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardStringOrArrayOfString"), Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardStringOrArrayOfString"),
}, },
}, },
"properties": {
SchemaProps: spec.SchemaProps{
Description: "Additional properties for multi-props variables",
Type: []string{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Allows: true,
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
}, },
Required: []string{"text", "value"}, Required: []string{"text", "value"},
}, },
@@ -808,6 +808,8 @@ VariableOption: {
text: string | [...string] text: string | [...string]
// Value of the option // Value of the option
value: string | [...string] value: string | [...string]
// Additional properties for multi-props variables
properties?: {[string]: string}
} }
// Query variable specification // Query variable specification
@@ -1429,6 +1429,8 @@ type DashboardVariableOption struct {
Text DashboardStringOrArrayOfString `json:"text"` Text DashboardStringOrArrayOfString `json:"text"`
// Value of the option // Value of the option
Value DashboardStringOrArrayOfString `json:"value"` Value DashboardStringOrArrayOfString `json:"value"`
// Additional properties for multi-props variables
Properties map[string]string `json:"properties,omitempty"`
} }
// NewDashboardVariableOption creates a new DashboardVariableOption object. // NewDashboardVariableOption creates a new DashboardVariableOption object.
@@ -5196,6 +5196,22 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardVariableOption(ref common.Refere
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardStringOrArrayOfString"), Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardStringOrArrayOfString"),
}, },
}, },
"properties": {
SchemaProps: spec.SchemaProps{
Description: "Additional properties for multi-props variables",
Type: []string{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Allows: true,
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
}, },
Required: []string{"text", "value"}, Required: []string{"text", "value"},
}, },
File diff suppressed because one or more lines are too long
@@ -103,10 +103,11 @@ To configure basic settings for the data source, complete the following steps:
1. Set the data source's basic configuration options: 1. Set the data source's basic configuration options:
| Name | Description | | Name | Description |
| ----------- | ------------------------------------------------------------------------ | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name** | Sets the name you use to refer to the data source in panels and queries. | | **Name** | Sets the name you use to refer to the data source in panels and queries. |
| **Default** | Sets whether the data source is pre-selected for new panels. | | **Default** | Sets whether the data source is pre-selected for new panels. |
| **Universe Domain** | The universe domain to connect to. For more information, refer to [Documentation on universe domains](https://docs.cloud.google.com/python/docs/reference/monitoring/latest/google.cloud.monitoring_v3.services.service_monitoring_service.ServiceMonitoringServiceAsyncClient#google_cloud_monitoring_v3_services_service_monitoring_service_ServiceMonitoringServiceAsyncClient_universe_domain). Defaults to `googleapis.com`. |
### Provision the data source ### Provision the data source
@@ -129,6 +130,7 @@ datasources:
clientEmail: stackdriver@myproject.iam.gserviceaccount.com clientEmail: stackdriver@myproject.iam.gserviceaccount.com
authenticationType: jwt authenticationType: jwt
defaultProject: my-project-name defaultProject: my-project-name
universeDomain: googleapis.com
secureJsonData: secureJsonData:
privateKey: | privateKey: |
-----BEGIN PRIVATE KEY----- -----BEGIN PRIVATE KEY-----
@@ -152,6 +154,7 @@ datasources:
clientEmail: stackdriver@myproject.iam.gserviceaccount.com clientEmail: stackdriver@myproject.iam.gserviceaccount.com
authenticationType: jwt authenticationType: jwt
defaultProject: my-project-name defaultProject: my-project-name
universeDomain: googleapis.com
privateKeyPath: /etc/secrets/gce.pem privateKeyPath: /etc/secrets/gce.pem
``` ```
@@ -166,6 +169,7 @@ datasources:
access: proxy access: proxy
jsonData: jsonData:
authenticationType: gce authenticationType: gce
universeDomain: googleapis.com
``` ```
## Import pre-configured dashboards ## Import pre-configured dashboards
@@ -87,6 +87,7 @@ With a Grafana Enterprise license, you also get access to premium data sources,
- [CockroachDB](/grafana/plugins/grafana-cockroachdb-datasource) - [CockroachDB](/grafana/plugins/grafana-cockroachdb-datasource)
- [Databricks](/grafana/plugins/grafana-databricks-datasource) - [Databricks](/grafana/plugins/grafana-databricks-datasource)
- [DataDog](/grafana/plugins/grafana-datadog-datasource) - [DataDog](/grafana/plugins/grafana-datadog-datasource)
- [IBM Db2](/grafana/plugins/grafana-ibmdb2-datasource)
- [Drone](/grafana/plugins/grafana-drone-datasource) - [Drone](/grafana/plugins/grafana-drone-datasource)
- [DynamoDB](/grafana/plugins/grafana-dynamodb-datasource/) - [DynamoDB](/grafana/plugins/grafana-dynamodb-datasource/)
- [Dynatrace](/grafana/plugins/grafana-dynatrace-datasource) - [Dynatrace](/grafana/plugins/grafana-dynatrace-datasource)
@@ -83,6 +83,7 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `reportingRetries` | Enables rendering retries for the reporting feature | | `reportingRetries` | Enables rendering retries for the reporting feature |
| `externalServiceAccounts` | Automatic service account and token setup for plugins | | `externalServiceAccounts` | Automatic service account and token setup for plugins |
| `cloudWatchBatchQueries` | Runs CloudWatch metrics queries as separate batches | | `cloudWatchBatchQueries` | Runs CloudWatch metrics queries as separate batches |
| `dashboardNewLayouts` | Enables new dashboard layouts |
| `pdfTables` | Enables generating table data as PDF in reporting | | `pdfTables` | Enables generating table data as PDF in reporting |
| `canvasPanelPanZoom` | Allow pan and zoom in canvas panel | | `canvasPanelPanZoom` | Allow pan and zoom in canvas panel |
| `alertingSaveStateCompressed` | Enables the compressed protobuf-based alert state storage. Default is enabled. | | `alertingSaveStateCompressed` | Enables the compressed protobuf-based alert state storage. Default is enabled. |
@@ -30,7 +30,9 @@ refs:
# Datagrid # Datagrid
{{< docs/experimental product="The datagrid visualization" featureFlag="`enableDatagridEditing`" >}} {{< admonition type="caution" >}}
Starting with Grafana 12.4, Datagrid is deprecated. It will be removed in version 13.0.
{{< /admonition >}}
Datagrids offer you the ability to create, edit, and fine-tune data within Grafana. As such, this panel can act as a data source for other panels Datagrids offer you the ability to create, edit, and fine-tune data within Grafana. As such, this panel can act as a data source for other panels
inside a dashboard. inside a dashboard.
-25
View File
@@ -3743,46 +3743,21 @@
"count": 1 "count": 1
} }
}, },
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/SettingsEditor/DateHistogramSettingsEditor.tsx": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1
}
},
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/SettingsEditor/TermsSettingsEditor.tsx": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1
}
},
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/aggregations.ts": { "public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/aggregations.ts": {
"@typescript-eslint/consistent-type-assertions": { "@typescript-eslint/consistent-type-assertions": {
"count": 1 "count": 1
} }
}, },
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/state/reducer.ts": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1
}
},
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/MetricEditor.tsx": { "public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/MetricEditor.tsx": {
"@typescript-eslint/consistent-type-assertions": { "@typescript-eslint/consistent-type-assertions": {
"count": 1 "count": 1
} }
}, },
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/SettingsEditor/SettingField.tsx": {
"@typescript-eslint/consistent-type-assertions": {
"count": 2
}
},
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/aggregations.ts": { "public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/aggregations.ts": {
"@typescript-eslint/consistent-type-assertions": { "@typescript-eslint/consistent-type-assertions": {
"count": 1 "count": 1
} }
}, },
"public/app/plugins/datasource/elasticsearch/components/QueryEditor/MetricAggregationsEditor/state/reducer.ts": {
"@typescript-eslint/consistent-type-assertions": {
"count": 1
}
},
"public/app/plugins/datasource/elasticsearch/configuration/DataLinks.tsx": { "public/app/plugins/datasource/elasticsearch/configuration/DataLinks.tsx": {
"no-restricted-syntax": { "no-restricted-syntax": {
"count": 1 "count": 1
+11 -12
View File
@@ -44,8 +44,8 @@ require (
github.com/beevik/etree v1.4.1 // @grafana/grafana-backend-group github.com/beevik/etree v1.4.1 // @grafana/grafana-backend-group
github.com/benbjohnson/clock v1.3.5 // @grafana/alerting-backend github.com/benbjohnson/clock v1.3.5 // @grafana/alerting-backend
github.com/blang/semver/v4 v4.0.0 // indirect; @grafana/grafana-developer-enablement-squad github.com/blang/semver/v4 v4.0.0 // indirect; @grafana/grafana-developer-enablement-squad
github.com/blevesearch/bleve/v2 v2.5.0 // @grafana/grafana-search-and-storage github.com/blevesearch/bleve/v2 v2.5.7 // @grafana/grafana-search-and-storage
github.com/blevesearch/bleve_index_api v1.2.7 // @grafana/grafana-search-and-storage github.com/blevesearch/bleve_index_api v1.3.0 // @grafana/grafana-search-and-storage
github.com/blugelabs/bluge v0.2.2 // @grafana/grafana-backend-group github.com/blugelabs/bluge v0.2.2 // @grafana/grafana-backend-group
github.com/blugelabs/bluge_segment_api v0.2.0 // @grafana/grafana-backend-group github.com/blugelabs/bluge_segment_api v0.2.0 // @grafana/grafana-backend-group
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // @grafana/grafana-backend-group github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // @grafana/grafana-backend-group
@@ -365,22 +365,22 @@ require (
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect github.com/blang/semver v3.5.1+incompatible // indirect
github.com/blevesearch/geo v0.1.20 // indirect github.com/blevesearch/geo v0.2.4 // indirect
github.com/blevesearch/go-faiss v1.0.25 // indirect github.com/blevesearch/go-faiss v1.0.26 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.3.9 // indirect github.com/blevesearch/scorch_segment_api/v2 v2.3.13 // indirect
github.com/blevesearch/segment v0.9.1 // indirect github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
github.com/blevesearch/vellum v1.1.0 // indirect github.com/blevesearch/vellum v1.1.0 // indirect
github.com/blevesearch/zapx/v11 v11.4.1 // indirect github.com/blevesearch/zapx/v11 v11.4.2 // indirect
github.com/blevesearch/zapx/v12 v12.4.1 // indirect github.com/blevesearch/zapx/v12 v12.4.2 // indirect
github.com/blevesearch/zapx/v13 v13.4.1 // indirect github.com/blevesearch/zapx/v13 v13.4.2 // indirect
github.com/blevesearch/zapx/v14 v14.4.1 // indirect github.com/blevesearch/zapx/v14 v14.4.2 // indirect
github.com/blevesearch/zapx/v15 v15.4.1 // indirect github.com/blevesearch/zapx/v15 v15.4.2 // indirect
github.com/blevesearch/zapx/v16 v16.2.2 // indirect github.com/blevesearch/zapx/v16 v16.2.8 // indirect
github.com/bluele/gcache v0.0.2 // indirect github.com/bluele/gcache v0.0.2 // indirect
github.com/blugelabs/ice v1.0.0 // indirect github.com/blugelabs/ice v1.0.0 // indirect
github.com/blugelabs/ice/v2 v2.0.1 // indirect github.com/blugelabs/ice/v2 v2.0.1 // indirect
@@ -443,7 +443,6 @@ require (
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
github.com/gomodule/redigo v1.8.9 // indirect github.com/gomodule/redigo v1.8.9 // indirect
github.com/google/btree v1.1.3 // indirect github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.26.1 // indirect github.com/google/cel-go v0.26.1 // indirect
+22 -24
View File
@@ -931,14 +931,14 @@ github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdn
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/blevesearch/bleve/v2 v2.5.0 h1:HzYqBy/5/M9Ul9ESEmXzN/3Jl7YpmWBdHM/+zzv/3k4= github.com/blevesearch/bleve/v2 v2.5.7 h1:2d9YrL5zrX5EBBW++GOaEKjE+NPWeZGaX77IM26m1Z8=
github.com/blevesearch/bleve/v2 v2.5.0/go.mod h1:PcJzTPnEynO15dCf9isxOga7YFRa/cMSsbnRwnszXUk= github.com/blevesearch/bleve/v2 v2.5.7/go.mod h1:yj0NlS7ocGC4VOSAedqDDMktdh2935v2CSWOCDMHdSA=
github.com/blevesearch/bleve_index_api v1.2.7 h1:c8r9vmbaYQroAMSGag7zq5gEVPiuXrUQDqfnj7uYZSY= github.com/blevesearch/bleve_index_api v1.3.0 h1:DsMpWVjFNlBw9/6pyWf59XoqcAkhHj3H0UWiQsavb6E=
github.com/blevesearch/bleve_index_api v1.2.7/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0= github.com/blevesearch/bleve_index_api v1.3.0/go.mod h1:xvd48t5XMeeioWQ5/jZvgLrV98flT2rdvEJ3l/ki4Ko=
github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM= github.com/blevesearch/geo v0.2.4 h1:ECIGQhw+QALCZaDcogRTNSJYQXRtC8/m8IKiA706cqk=
github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w= github.com/blevesearch/geo v0.2.4/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8=
github.com/blevesearch/go-faiss v1.0.25 h1:lel1rkOUGbT1CJ0YgzKwC7k+XH0XVBHnCVWahdCXk4U= github.com/blevesearch/go-faiss v1.0.26 h1:4dRLolFgjPyjkaXwff4NfbZFdE/dfywbzDqporeQvXI=
github.com/blevesearch/go-faiss v1.0.25/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= github.com/blevesearch/go-faiss v1.0.26/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y= github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
@@ -947,8 +947,8 @@ github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+
github.com/blevesearch/mmap-go v1.0.3/go.mod h1:pYvKl/grLQrBxuaRYgoTssa4rVujYYeenDp++2E+yvs= github.com/blevesearch/mmap-go v1.0.3/go.mod h1:pYvKl/grLQrBxuaRYgoTssa4rVujYYeenDp++2E+yvs=
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
github.com/blevesearch/scorch_segment_api/v2 v2.3.9 h1:X6nJXnNHl7nasXW+U6y2Ns2Aw8F9STszkYkyBfQ+p0o= github.com/blevesearch/scorch_segment_api/v2 v2.3.13 h1:ZPjv/4VwWvHJZKeMSgScCapOy8+DdmsmRyLmSB88UoY=
github.com/blevesearch/scorch_segment_api/v2 v2.3.9/go.mod h1:IrzspZlVjhf4X29oJiEhBxEteTqOY9RlYlk1lCmYHr4= github.com/blevesearch/scorch_segment_api/v2 v2.3.13/go.mod h1:ENk2LClTehOuMS8XzN3UxBEErYmtwkE7MAArFTXs9Vc=
github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ= github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
@@ -960,18 +960,18 @@ github.com/blevesearch/vellum v1.0.5/go.mod h1:atE0EH3fvk43zzS7t1YNdNC7DbmcC3uz+
github.com/blevesearch/vellum v1.0.7/go.mod h1:doBZpmRhwTsASB4QdUZANlJvqVAUdUyX0ZK7QJCTeBE= github.com/blevesearch/vellum v1.0.7/go.mod h1:doBZpmRhwTsASB4QdUZANlJvqVAUdUyX0ZK7QJCTeBE=
github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w= github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w=
github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y= github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y=
github.com/blevesearch/zapx/v11 v11.4.1 h1:qFCPlFbsEdwbbckJkysptSQOsHn4s6ZOHL5GMAIAVHA= github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs=
github.com/blevesearch/zapx/v11 v11.4.1/go.mod h1:qNOGxIqdPC1MXauJCD9HBG487PxviTUUbmChFOAosGs= github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc=
github.com/blevesearch/zapx/v12 v12.4.1 h1:K77bhypII60a4v8mwvav7r4IxWA8qxhNjgF9xGdb9eQ= github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE=
github.com/blevesearch/zapx/v12 v12.4.1/go.mod h1:QRPrlPOzAxBNMI0MkgdD+xsTqx65zbuPr3Ko4Re49II= github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58=
github.com/blevesearch/zapx/v13 v13.4.1 h1:EnkEMZFUK0lsW/jOJJF2xOcp+W8TjEsyeN5BeAZEYYE= github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks=
github.com/blevesearch/zapx/v13 v13.4.1/go.mod h1:e6duBMlCvgbH9rkzNMnUa9hRI9F7ri2BRcHfphcmGn8= github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk=
github.com/blevesearch/zapx/v14 v14.4.1 h1:G47kGCshknBZzZAtjcnIAMn3oNx8XBLxp8DMq18ogyE= github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0=
github.com/blevesearch/zapx/v14 v14.4.1/go.mod h1:O7sDxiaL2r2PnCXbhh1Bvm7b4sP+jp4unE9DDPWGoms= github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8=
github.com/blevesearch/zapx/v15 v15.4.1 h1:B5IoTMUCEzFdc9FSQbhVOxAY+BO17c05866fNruiI7g= github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k=
github.com/blevesearch/zapx/v15 v15.4.1/go.mod h1:b/MreHjYeQoLjyY2+UaM0hGZZUajEbE0xhnr1A2/Q6Y= github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw=
github.com/blevesearch/zapx/v16 v16.2.2 h1:MifKJVRTEhMTgSlle2bDRTb39BGc9jXFRLPZc6r0Rzk= github.com/blevesearch/zapx/v16 v16.2.8 h1:SlnzF0YGtSlrsOE3oE7EgEX6BIepGpeqxs1IjMbHLQI=
github.com/blevesearch/zapx/v16 v16.2.2/go.mod h1:B9Pk4G1CqtErgQV9DyCSA9Lb7WZe4olYfGw7fVDZ4sk= github.com/blevesearch/zapx/v16 v16.2.8/go.mod h1:murSoCJPCk25MqURrcJaBQ1RekuqSCSfMjXH4rHyA14=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/blugelabs/bluge v0.2.2 h1:gat8CqE6P6tOgeX30XGLOVNTC26cpM2RWVcreXWtYcM= github.com/blugelabs/bluge v0.2.2 h1:gat8CqE6P6tOgeX30XGLOVNTC26cpM2RWVcreXWtYcM=
@@ -1442,8 +1442,6 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2V
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
+27 -2
View File
@@ -520,14 +520,40 @@ github.com/benbjohnson/immutable v0.4.0 h1:CTqXbEerYso8YzVPxmWxh2gnoRQbbB9X1quUC
github.com/benbjohnson/immutable v0.4.0/go.mod h1:iAr8OjJGLnLmVUr9MZ/rz4PWUy6Ouc2JLYuMArmvAJM= github.com/benbjohnson/immutable v0.4.0/go.mod h1:iAr8OjJGLnLmVUr9MZ/rz4PWUy6Ouc2JLYuMArmvAJM=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/blevesearch/bleve/v2 v2.5.7 h1:2d9YrL5zrX5EBBW++GOaEKjE+NPWeZGaX77IM26m1Z8=
github.com/blevesearch/bleve/v2 v2.5.7/go.mod h1:yj0NlS7ocGC4VOSAedqDDMktdh2935v2CSWOCDMHdSA=
github.com/blevesearch/bleve_index_api v1.2.8/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/bleve_index_api v1.2.11 h1:bXQ54kVuwP8hdrXUSOnvTQfgK0KI1+f9A0ITJT8tX1s=
github.com/blevesearch/bleve_index_api v1.2.11/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0=
github.com/blevesearch/bleve_index_api v1.3.0 h1:DsMpWVjFNlBw9/6pyWf59XoqcAkhHj3H0UWiQsavb6E=
github.com/blevesearch/bleve_index_api v1.3.0/go.mod h1:xvd48t5XMeeioWQ5/jZvgLrV98flT2rdvEJ3l/ki4Ko=
github.com/blevesearch/geo v0.2.4 h1:ECIGQhw+QALCZaDcogRTNSJYQXRtC8/m8IKiA706cqk=
github.com/blevesearch/geo v0.2.4/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8=
github.com/blevesearch/go-faiss v1.0.26/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:kDy+zgJFJJoJYBvdfBSiZYBbdsUL0XcjHYWezpQBGPA= github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:kDy+zgJFJJoJYBvdfBSiZYBbdsUL0XcjHYWezpQBGPA=
github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:9eJDeqxJ3E7WnLebQUlPD7ZjSce7AnDb9vjGmMCbD0A= github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:9eJDeqxJ3E7WnLebQUlPD7ZjSce7AnDb9vjGmMCbD0A=
github.com/blevesearch/goleveldb v1.0.1 h1:iAtV2Cu5s0GD1lwUiekkFHe2gTMCCNVj2foPclDLIFI= github.com/blevesearch/goleveldb v1.0.1 h1:iAtV2Cu5s0GD1lwUiekkFHe2gTMCCNVj2foPclDLIFI=
github.com/blevesearch/goleveldb v1.0.1/go.mod h1:WrU8ltZbIp0wAoig/MHbrPCXSOLpe79nz5lv5nqfYrQ= github.com/blevesearch/goleveldb v1.0.1/go.mod h1:WrU8ltZbIp0wAoig/MHbrPCXSOLpe79nz5lv5nqfYrQ=
github.com/blevesearch/scorch_segment_api/v2 v2.3.10/go.mod h1:Z3e6ChN3qyN35yaQpl00MfI5s8AxUJbpTR/DL8QOQ+8=
github.com/blevesearch/scorch_segment_api/v2 v2.3.13 h1:ZPjv/4VwWvHJZKeMSgScCapOy8+DdmsmRyLmSB88UoY=
github.com/blevesearch/scorch_segment_api/v2 v2.3.13/go.mod h1:ENk2LClTehOuMS8XzN3UxBEErYmtwkE7MAArFTXs9Vc=
github.com/blevesearch/snowball v0.6.1 h1:cDYjn/NCH+wwt2UdehaLpr2e4BwLIjN4V/TdLsL+B5A= github.com/blevesearch/snowball v0.6.1 h1:cDYjn/NCH+wwt2UdehaLpr2e4BwLIjN4V/TdLsL+B5A=
github.com/blevesearch/snowball v0.6.1/go.mod h1:ZF0IBg5vgpeoUhnMza2v0A/z8m1cWPlwhke08LpNusg= github.com/blevesearch/snowball v0.6.1/go.mod h1:ZF0IBg5vgpeoUhnMza2v0A/z8m1cWPlwhke08LpNusg=
github.com/blevesearch/stempel v0.2.0 h1:CYzVPaScODMvgE9o+kf6D4RJ/VRomyi9uHF+PtB+Afc= github.com/blevesearch/stempel v0.2.0 h1:CYzVPaScODMvgE9o+kf6D4RJ/VRomyi9uHF+PtB+Afc=
github.com/blevesearch/stempel v0.2.0/go.mod h1:wjeTHqQv+nQdbPuJ/YcvOjTInA2EIc6Ks1FoSUzSLvc= github.com/blevesearch/stempel v0.2.0/go.mod h1:wjeTHqQv+nQdbPuJ/YcvOjTInA2EIc6Ks1FoSUzSLvc=
github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs=
github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc=
github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE=
github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58=
github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks=
github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk=
github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0=
github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8=
github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k=
github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw=
github.com/blevesearch/zapx/v16 v16.2.8 h1:SlnzF0YGtSlrsOE3oE7EgEX6BIepGpeqxs1IjMbHLQI=
github.com/blevesearch/zapx/v16 v16.2.8/go.mod h1:murSoCJPCk25MqURrcJaBQ1RekuqSCSfMjXH4rHyA14=
github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
@@ -998,8 +1024,6 @@ github.com/grafana/prometheus-alertmanager v0.25.1-0.20250331083058-4563aec7a975
github.com/grafana/prometheus-alertmanager v0.25.1-0.20250331083058-4563aec7a975/go.mod h1:FGdGvhI40Dq+CTQaSzK9evuve774cgOUdGfVO04OXkw= github.com/grafana/prometheus-alertmanager v0.25.1-0.20250331083058-4563aec7a975/go.mod h1:FGdGvhI40Dq+CTQaSzK9evuve774cgOUdGfVO04OXkw=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20250604130045-92c8f6389b36 h1:AjZ58JRw1ZieFH/SdsddF5BXtsDKt5kSrKNPWrzYz3Y= github.com/grafana/prometheus-alertmanager v0.25.1-0.20250604130045-92c8f6389b36 h1:AjZ58JRw1ZieFH/SdsddF5BXtsDKt5kSrKNPWrzYz3Y=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20250604130045-92c8f6389b36/go.mod h1:O/QP1BCm0HHIzbKvgMzqb5sSyH88rzkFk84F4TfJjBU= github.com/grafana/prometheus-alertmanager v0.25.1-0.20250604130045-92c8f6389b36/go.mod h1:O/QP1BCm0HHIzbKvgMzqb5sSyH88rzkFk84F4TfJjBU=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20260112162805-d29cc9cf7f0f h1:9tRhudagkQO2s61SLFLSziIdCm7XlkfypVKDxpcHokg=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20260112162805-d29cc9cf7f0f/go.mod h1:AsVdCBeDFN9QbgpJg+8voDAcgsW0RmNvBd70ecMMdC0=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grafana/pyroscope/api v1.2.1-0.20250415190842-3ff7247547ae/go.mod h1:6CJ1uXmLZ13ufpO9xE4pST+DyaBt0uszzrV0YnoaVLQ= github.com/grafana/pyroscope/api v1.2.1-0.20250415190842-3ff7247547ae/go.mod h1:6CJ1uXmLZ13ufpO9xE4pST+DyaBt0uszzrV0YnoaVLQ=
github.com/grafana/sqlds/v4 v4.2.4/go.mod h1:BQRjUG8rOqrBI4NAaeoWrIMuoNgfi8bdhCJ+5cgEfLU= github.com/grafana/sqlds/v4 v4.2.4/go.mod h1:BQRjUG8rOqrBI4NAaeoWrIMuoNgfi8bdhCJ+5cgEfLU=
@@ -1092,6 +1116,7 @@ github.com/jon-whit/go-grpc-prometheus v1.4.0/go.mod h1:iTPm+Iuhh3IIqR0iGZ91JJEg
github.com/joncrlsn/dque v0.0.0-20211108142734-c2ef48c5192a h1:sfe532Ipn7GX0V6mHdynBk393rDmqgI0QmjLK7ct7TU= github.com/joncrlsn/dque v0.0.0-20211108142734-c2ef48c5192a h1:sfe532Ipn7GX0V6mHdynBk393rDmqgI0QmjLK7ct7TU=
github.com/joncrlsn/dque v0.0.0-20211108142734-c2ef48c5192a/go.mod h1:dNKs71rs2VJGBAmttu7fouEsRQlRjxy0p1Sx+T5wbpY= github.com/joncrlsn/dque v0.0.0-20211108142734-c2ef48c5192a/go.mod h1:dNKs71rs2VJGBAmttu7fouEsRQlRjxy0p1Sx+T5wbpY=
github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY= github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY=
github.com/json-iterator/go v0.0.0-20171115153421-f7279a603ede/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jsternberg/zap-logfmt v1.3.0 h1:z1n1AOHVVydOOVuyphbOKyR4NICDQFiJMn1IK5hVQ5Y= github.com/jsternberg/zap-logfmt v1.3.0 h1:z1n1AOHVVydOOVuyphbOKyR4NICDQFiJMn1IK5hVQ5Y=
github.com/jsternberg/zap-logfmt v1.3.0/go.mod h1:N3DENp9WNmCZxvkBD/eReWwz1149BK6jEN9cQ4fNwZE= github.com/jsternberg/zap-logfmt v1.3.0/go.mod h1:N3DENp9WNmCZxvkBD/eReWwz1149BK6jEN9cQ4fNwZE=
+1
View File
@@ -82,6 +82,7 @@ module.exports = {
// Decoupled plugins run their own tests so ignoring them here. // Decoupled plugins run their own tests so ignoring them here.
'<rootDir>/public/app/plugins/datasource/azuremonitor', '<rootDir>/public/app/plugins/datasource/azuremonitor',
'<rootDir>/public/app/plugins/datasource/cloud-monitoring', '<rootDir>/public/app/plugins/datasource/cloud-monitoring',
'<rootDir>/public/app/plugins/datasource/elasticsearch',
'<rootDir>/public/app/plugins/datasource/grafana-postgresql-datasource', '<rootDir>/public/app/plugins/datasource/grafana-postgresql-datasource',
'<rootDir>/public/app/plugins/datasource/grafana-pyroscope-datasource', '<rootDir>/public/app/plugins/datasource/grafana-pyroscope-datasource',
'<rootDir>/public/app/plugins/datasource/grafana-testdata-datasource', '<rootDir>/public/app/plugins/datasource/grafana-testdata-datasource',
+2
View File
@@ -237,6 +237,8 @@ lineage: schemas: [{
text: string | [...string] text: string | [...string]
// Value of the option // Value of the option
value: string | [...string] value: string | [...string]
// Additional properties for multi-props variables
properties?: {[string]: string}
} @cuetsy(kind="interface") } @cuetsy(kind="interface")
// Options to config when to refresh a variable // Options to config when to refresh a variable
+2 -2
View File
@@ -293,8 +293,8 @@
"@grafana/plugin-ui": "^0.11.1", "@grafana/plugin-ui": "^0.11.1",
"@grafana/prometheus": "workspace:*", "@grafana/prometheus": "workspace:*",
"@grafana/runtime": "workspace:*", "@grafana/runtime": "workspace:*",
"@grafana/scenes": "v6.52.1", "@grafana/scenes": "6.52.2",
"@grafana/scenes-react": "v6.52.1", "@grafana/scenes-react": "6.52.2",
"@grafana/schema": "workspace:*", "@grafana/schema": "workspace:*",
"@grafana/sql": "workspace:*", "@grafana/sql": "workspace:*",
"@grafana/ui": "workspace:*", "@grafana/ui": "workspace:*",
+10 -1
View File
@@ -35,6 +35,14 @@
}, },
"./test": { "./test": {
"@grafana-app/source": "./test/index.ts" "@grafana-app/source": "./test/index.ts"
},
"./themes/schema.generated.json": {
"@grafana-app/source": "./src/themes/schema.generated.json",
"default": "./dist/esm/themes/schema.generated.json"
},
"./themes/definitions/*.json": {
"@grafana-app/source": "./src/themes/themeDefinitions/*.json",
"default": "./dist/esm/themes/themeDefinitions/*.json"
} }
}, },
"publishConfig": { "publishConfig": {
@@ -52,7 +60,7 @@
"typecheck": "tsc --emitDeclarationOnly false --noEmit", "typecheck": "tsc --emitDeclarationOnly false --noEmit",
"prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js", "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js",
"postpack": "mv package.json.bak package.json", "postpack": "mv package.json.bak package.json",
"themes-schema": "tsx ./src/themes/scripts/generateSchema.ts" "themes-schema": "tsx ./scripts/generateSchema.ts"
}, },
"dependencies": { "dependencies": {
"@braintree/sanitize-url": "7.0.1", "@braintree/sanitize-url": "7.0.1",
@@ -102,6 +110,7 @@
"react-dom": "18.3.1", "react-dom": "18.3.1",
"rimraf": "6.0.1", "rimraf": "6.0.1",
"rollup": "^4.22.4", "rollup": "^4.22.4",
"rollup-plugin-copy": "3.5.0",
"rollup-plugin-esbuild": "6.2.1", "rollup-plugin-esbuild": "6.2.1",
"rollup-plugin-node-externals": "^8.0.0", "rollup-plugin-node-externals": "^8.0.0",
"tsx": "^4.21.0", "tsx": "^4.21.0",
+21 -2
View File
@@ -1,21 +1,40 @@
import json from '@rollup/plugin-json'; import json from '@rollup/plugin-json';
import { createRequire } from 'node:module'; import { createRequire } from 'node:module';
import copy from 'rollup-plugin-copy';
import { entryPoint, plugins, esmOutput, cjsOutput } from '../rollup.config.parts'; import { entryPoint, plugins, esmOutput, cjsOutput } from '../rollup.config.parts';
const rq = createRequire(import.meta.url); const rq = createRequire(import.meta.url);
const pkg = rq('./package.json'); const pkg = rq('./package.json');
const grafanaDataPlugins = [
...plugins,
copy({
targets: [
{
src: 'src/themes/schema.generated.json',
dest: 'dist/esm/',
},
{
src: 'src/themes/themeDefinitions/*.json',
dest: 'dist/esm/',
},
],
flatten: false,
}),
json(),
];
export default [ export default [
{ {
input: entryPoint, input: entryPoint,
plugins: [...plugins, json()], plugins: grafanaDataPlugins,
output: [cjsOutput(pkg, 'grafana-data'), esmOutput(pkg, 'grafana-data')], output: [cjsOutput(pkg, 'grafana-data'), esmOutput(pkg, 'grafana-data')],
treeshake: false, treeshake: false,
}, },
{ {
input: 'src/unstable.ts', input: 'src/unstable.ts',
plugins: [...plugins, json()], plugins: grafanaDataPlugins,
output: [cjsOutput(pkg, 'grafana-data'), esmOutput(pkg, 'grafana-data')], output: [cjsOutput(pkg, 'grafana-data'), esmOutput(pkg, 'grafana-data')],
treeshake: false, treeshake: false,
}, },
@@ -0,0 +1,22 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { NewThemeOptionsSchema } from '../src/themes/createTheme';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const jsonOut = path.join(__dirname, '..', 'src', 'themes', 'schema.generated.json');
fs.writeFileSync(
jsonOut,
JSON.stringify(
NewThemeOptionsSchema.toJSONSchema({
target: 'draft-07',
}),
undefined,
2
)
);
console.log('Successfully generated theme schema');
+1 -1
View File
@@ -844,7 +844,6 @@ export {
DataLinkConfigOrigin, DataLinkConfigOrigin,
SupportedTransformationType, SupportedTransformationType,
type InternalDataLink, type InternalDataLink,
type LinkTarget,
type LinkModel, type LinkModel,
type LinkModelSupplier, type LinkModelSupplier,
VariableOrigin, VariableOrigin,
@@ -852,6 +851,7 @@ export {
VariableSuggestionsScope, VariableSuggestionsScope,
OneClickMode, OneClickMode,
} from './types/dataLink'; } from './types/dataLink';
export { type LinkTarget } from './types/linkTarget';
export { export {
type Action, type Action,
type ActionModel, type ActionModel,
@@ -93,7 +93,6 @@ export { DataTransformerID } from '../transformations/transformers/ids';
export { mergeTransformer } from '../transformations/transformers/merge'; export { mergeTransformer } from '../transformations/transformers/merge';
export { getThemeById } from '../themes/registry'; export { getThemeById } from '../themes/registry';
export * as experimentalThemeDefinitions from '../themes/themeDefinitions';
export { GrafanaEdition } from '../types/config'; export { GrafanaEdition } from '../types/config';
export { SIPrefix } from '../valueFormats/symbolFormatters'; export { SIPrefix } from '../valueFormats/symbolFormatters';
+27 -1
View File
@@ -1,7 +1,18 @@
import { Registry, RegistryItem } from '../utils/Registry'; import { Registry, RegistryItem } from '../utils/Registry';
import { createTheme, NewThemeOptionsSchema } from './createTheme'; import { createTheme, NewThemeOptionsSchema } from './createTheme';
import * as extraThemes from './themeDefinitions'; import aubergine from './themeDefinitions/aubergine.json';
import debug from './themeDefinitions/debug.json';
import desertbloom from './themeDefinitions/desertbloom.json';
import gildedgrove from './themeDefinitions/gildedgrove.json';
import gloom from './themeDefinitions/gloom.json';
import mars from './themeDefinitions/mars.json';
import matrix from './themeDefinitions/matrix.json';
import sapphiredusk from './themeDefinitions/sapphiredusk.json';
import synthwave from './themeDefinitions/synthwave.json';
import tron from './themeDefinitions/tron.json';
import victorian from './themeDefinitions/victorian.json';
import zen from './themeDefinitions/zen.json';
import { GrafanaTheme2 } from './types'; import { GrafanaTheme2 } from './types';
export interface ThemeRegistryItem extends RegistryItem { export interface ThemeRegistryItem extends RegistryItem {
@@ -9,6 +20,21 @@ export interface ThemeRegistryItem extends RegistryItem {
build: () => GrafanaTheme2; build: () => GrafanaTheme2;
} }
const extraThemes: { [key: string]: unknown } = {
aubergine,
debug,
desertbloom,
gildedgrove,
gloom,
mars,
matrix,
sapphiredusk,
synthwave,
tron,
victorian,
zen,
};
/** /**
* @internal * @internal
* Only for internal use, never use this from a plugin * Only for internal use, never use this from a plugin
@@ -1,19 +0,0 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { NewThemeOptionsSchema } from '../createTheme';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
fs.writeFileSync(
path.join(__dirname, '../schema.generated.json'),
JSON.stringify(
NewThemeOptionsSchema.toJSONSchema({
target: 'draft-07',
}),
undefined,
2
)
);
@@ -1,12 +0,0 @@
export { default as aubergine } from './aubergine.json';
export { default as debug } from './debug.json';
export { default as desertbloom } from './desertbloom.json';
export { default as gildedgrove } from './gildedgrove.json';
export { default as mars } from './mars.json';
export { default as matrix } from './matrix.json';
export { default as sapphiredusk } from './sapphiredusk.json';
export { default as synthwave } from './synthwave.json';
export { default as tron } from './tron.json';
export { default as victorian } from './victorian.json';
export { default as zen } from './zen.json';
export { default as gloom } from './gloom.json';
+1 -2
View File
@@ -1,5 +1,6 @@
import { ScopedVars } from './ScopedVars'; import { ScopedVars } from './ScopedVars';
import { ExploreCorrelationHelperData, ExplorePanelsState } from './explore'; import { ExploreCorrelationHelperData, ExplorePanelsState } from './explore';
import { LinkTarget } from './linkTarget';
import { InterpolateFunction } from './panel'; import { InterpolateFunction } from './panel';
import { DataQuery } from './query'; import { DataQuery } from './query';
import { TimeRange } from './time'; import { TimeRange } from './time';
@@ -88,8 +89,6 @@ export interface InternalDataLink<T extends DataQuery = any> {
range?: TimeRange; range?: TimeRange;
} }
export type LinkTarget = '_blank' | '_self' | undefined;
/** /**
* Processed Link Model. The values are ready to use * Processed Link Model. The values are ready to use
*/ */
+9 -1
View File
@@ -356,7 +356,7 @@ export interface FeatureToggles {
*/ */
dashboardScene?: boolean; dashboardScene?: boolean;
/** /**
* Enables experimental new dashboard layouts * Enables new dashboard layouts
*/ */
dashboardNewLayouts?: boolean; dashboardNewLayouts?: boolean;
/** /**
@@ -531,6 +531,10 @@ export interface FeatureToggles {
*/ */
alertingListViewV2?: boolean; alertingListViewV2?: boolean;
/** /**
* Enables the new Alerting navigation structure with improved menu grouping
*/
alertingNavigationV2?: boolean;
/**
* Enables saved searches for alert rules list * Enables saved searches for alert rules list
*/ */
alertingSavedSearches?: boolean; alertingSavedSearches?: boolean;
@@ -1251,4 +1255,8 @@ export interface FeatureToggles {
* Enables profiles exemplars support in profiles drilldown * Enables profiles exemplars support in profiles drilldown
*/ */
profilesExemplars?: boolean; profilesExemplars?: boolean;
/**
* Use synchronized dispatch timer to minimize duplicate notifications across alertmanager HA pods
*/
alertingSyncDispatchTimer?: boolean;
} }
@@ -0,0 +1,4 @@
/**
* Target for links - controls whether link opens in new tab or same tab
*/
export type LinkTarget = '_blank' | '_self' | undefined;
+1 -1
View File
@@ -1,7 +1,7 @@
import { ComponentType } from 'react'; import { ComponentType } from 'react';
import { LinkTarget } from './dataLink';
import { IconName } from './icon'; import { IconName } from './icon';
import { LinkTarget } from './linkTarget';
export interface NavLinkDTO { export interface NavLinkDTO {
id?: string; id?: string;
+2
View File
@@ -11,6 +11,7 @@ import { DataFrame } from './dataFrame';
import { DataQueryError, DataQueryRequest, DataQueryTimings } from './datasource'; import { DataQueryError, DataQueryRequest, DataQueryTimings } from './datasource';
import { FieldConfigSource } from './fieldOverrides'; import { FieldConfigSource } from './fieldOverrides';
import { IconName } from './icon'; import { IconName } from './icon';
import { LinkTarget } from './linkTarget';
import { OptionEditorConfig } from './options'; import { OptionEditorConfig } from './options';
import { PluginMeta } from './plugin'; import { PluginMeta } from './plugin';
import { AbsoluteTimeRange, TimeRange, TimeZone } from './time'; import { AbsoluteTimeRange, TimeRange, TimeZone } from './time';
@@ -191,6 +192,7 @@ export interface PanelMenuItem {
onClick?: (event: React.MouseEvent) => void; onClick?: (event: React.MouseEvent) => void;
shortcut?: string; shortcut?: string;
href?: string; href?: string;
target?: LinkTarget;
subMenu?: PanelMenuItem[]; subMenu?: PanelMenuItem[];
} }
@@ -91,6 +91,8 @@ export interface VariableOption {
text: string | string[]; text: string | string[];
value: string | string[]; value: string | string[];
isNone?: boolean; isNone?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
properties?: Record<string, any>;
} }
export interface IntervalVariableModel extends VariableWithOptions { export interface IntervalVariableModel extends VariableWithOptions {
@@ -118,6 +120,7 @@ export interface QueryVariableModel extends VariableWithMultiSupport {
definition: string; definition: string;
sort: VariableSort; sort: VariableSort;
queryValue?: string; queryValue?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
query: any; query: any;
regex: string; regex: string;
regexApplyTo?: VariableRegexApplyTo; regexApplyTo?: VariableRegexApplyTo;
@@ -193,6 +196,7 @@ export interface BaseVariableModel {
skipUrlSync: boolean; skipUrlSync: boolean;
index: number; index: number;
state: LoadingState; state: LoadingState;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error: any | null; error: any | null;
description: string | null; description: string | null;
usedInRepeat?: boolean; usedInRepeat?: boolean;
+1 -1
View File
@@ -9,4 +9,4 @@
* and be subject to the standard policies * and be subject to the standard policies
*/ */
export { default as themeJsonSchema } from './themes/schema.generated.json'; export {};
+2 -1
View File
@@ -8,7 +8,8 @@
"emitDeclarationOnly": true, "emitDeclarationOnly": true,
"isolatedModules": true, "isolatedModules": true,
"rootDirs": ["."], "rootDirs": ["."],
"moduleResolution": "bundler" "moduleResolution": "bundler",
"resolveJsonModule": true
}, },
"exclude": ["dist/**/*"], "exclude": ["dist/**/*"],
"include": [ "include": [
@@ -10,7 +10,7 @@
import * as common from '@grafana/schema'; import * as common from '@grafana/schema';
export const pluginVersion = "12.4.0-pre"; export const pluginVersion = "%VERSION%";
export type BucketAggregation = (DateHistogram | Histogram | Terms | Filters | GeoHashGrid | Nested); export type BucketAggregation = (DateHistogram | Histogram | Terms | Filters | GeoHashGrid | Nested);
@@ -231,6 +231,10 @@ export const defaultVariableModel: Partial<VariableModel> = {
* Option to be selected in a variable. * Option to be selected in a variable.
*/ */
export interface VariableOption { export interface VariableOption {
/**
* Additional properties for multi-props variables
*/
properties?: Record<string, string>;
/** /**
* Whether the option is selected or not * Whether the option is selected or not
*/ */
@@ -715,7 +715,9 @@ VariableOption: {
// Text to be displayed for the option // Text to be displayed for the option
text: string | [...string] text: string | [...string]
// Value of the option // Value of the option
value: string | [...string] value: string | [...string]
// Additional properties for multi-props variables
properties?: {[string]: string}
} }
// Query variable specification // Query variable specification
+2
View File
@@ -903,6 +903,8 @@ type VariableOption struct {
Text StringOrArrayOfString `json:"text"` Text StringOrArrayOfString `json:"text"`
// Value of the option // Value of the option
Value StringOrArrayOfString `json:"value"` Value StringOrArrayOfString `json:"value"`
// Additional properties for multi-props variables
Properties map[string]string `json:"properties,omitempty"`
} }
// NewVariableOption creates a new VariableOption object. // NewVariableOption creates a new VariableOption object.
+19 -3
View File
@@ -574,8 +574,8 @@ var (
}, },
{ {
Name: "dashboardNewLayouts", Name: "dashboardNewLayouts",
Description: "Enables experimental new dashboard layouts", Description: "Enables new dashboard layouts",
Stage: FeatureStageExperimental, Stage: FeatureStagePublicPreview,
FrontendOnly: false, // The restore backend feature changes behavior based on this flag FrontendOnly: false, // The restore backend feature changes behavior based on this flag
Owner: grafanaDashboardsSquad, Owner: grafanaDashboardsSquad,
}, },
@@ -879,6 +879,13 @@ var (
Owner: grafanaAlertingSquad, Owner: grafanaAlertingSquad,
FrontendOnly: true, FrontendOnly: true,
}, },
{
Name: "alertingNavigationV2",
Description: "Enables the new Alerting navigation structure with improved menu grouping",
Stage: FeatureStageExperimental,
Owner: grafanaAlertingSquad,
FrontendOnly: false,
},
{ {
Name: "alertingSavedSearches", Name: "alertingSavedSearches",
Description: "Enables saved searches for alert rules list", Description: "Enables saved searches for alert rules list",
@@ -981,7 +988,8 @@ var (
Stage: FeatureStageDeprecated, Stage: FeatureStageDeprecated,
Owner: grafanaPartnerPluginsSquad, Owner: grafanaPartnerPluginsSquad,
Expression: "true", // Enabled by default for now Expression: "true", // Enabled by default for now
}, { },
{
Name: "alertingFilterV2", Name: "alertingFilterV2",
Description: "Enable the new alerting search experience", Description: "Enable the new alerting search experience",
Stage: FeatureStageExperimental, Stage: FeatureStageExperimental,
@@ -2069,6 +2077,14 @@ var (
Owner: grafanaObservabilityTracesAndProfilingSquad, Owner: grafanaObservabilityTracesAndProfilingSquad,
FrontendOnly: false, FrontendOnly: false,
}, },
{
Name: "alertingSyncDispatchTimer",
Description: "Use synchronized dispatch timer to minimize duplicate notifications across alertmanager HA pods",
Stage: FeatureStageExperimental,
Owner: grafanaAlertingSquad,
RequiresRestart: true,
HideFromDocs: true,
},
} }
) )
+3 -1
View File
@@ -79,7 +79,7 @@ annotationPermissionUpdate,GA,@grafana/identity-access-team,false,false,false
dashboardSceneForViewers,GA,@grafana/dashboards-squad,false,false,true dashboardSceneForViewers,GA,@grafana/dashboards-squad,false,false,true
dashboardSceneSolo,GA,@grafana/dashboards-squad,false,false,true dashboardSceneSolo,GA,@grafana/dashboards-squad,false,false,true
dashboardScene,GA,@grafana/dashboards-squad,false,false,true dashboardScene,GA,@grafana/dashboards-squad,false,false,true
dashboardNewLayouts,experimental,@grafana/dashboards-squad,false,false,false dashboardNewLayouts,preview,@grafana/dashboards-squad,false,false,false
dashboardUndoRedo,experimental,@grafana/dashboards-squad,false,false,true dashboardUndoRedo,experimental,@grafana/dashboards-squad,false,false,true
unlimitedLayoutsNesting,experimental,@grafana/dashboards-squad,false,false,true unlimitedLayoutsNesting,experimental,@grafana/dashboards-squad,false,false,true
drilldownRecommendations,experimental,@grafana/dashboards-squad,false,false,true drilldownRecommendations,experimental,@grafana/dashboards-squad,false,false,true
@@ -121,6 +121,7 @@ dashboardLibrary,experimental,@grafana/sharing-squad,false,false,false
suggestedDashboards,experimental,@grafana/sharing-squad,false,false,false suggestedDashboards,experimental,@grafana/sharing-squad,false,false,false
dashboardTemplates,preview,@grafana/sharing-squad,false,false,false dashboardTemplates,preview,@grafana/sharing-squad,false,false,false
alertingListViewV2,privatePreview,@grafana/alerting-squad,false,false,true alertingListViewV2,privatePreview,@grafana/alerting-squad,false,false,true
alertingNavigationV2,experimental,@grafana/alerting-squad,false,false,false
alertingSavedSearches,experimental,@grafana/alerting-squad,false,false,true alertingSavedSearches,experimental,@grafana/alerting-squad,false,false,true
alertingDisableSendAlertsExternal,experimental,@grafana/alerting-squad,false,false,false alertingDisableSendAlertsExternal,experimental,@grafana/alerting-squad,false,false,false
preserveDashboardStateWhenNavigating,experimental,@grafana/dashboards-squad,false,false,false preserveDashboardStateWhenNavigating,experimental,@grafana/dashboards-squad,false,false,false
@@ -280,3 +281,4 @@ multiPropsVariables,experimental,@grafana/dashboards-squad,false,false,true
smoothingTransformation,experimental,@grafana/datapro,false,false,true smoothingTransformation,experimental,@grafana/datapro,false,false,true
secretsManagementAppPlatformAwsKeeper,experimental,@grafana/grafana-operator-experience-squad,false,false,false secretsManagementAppPlatformAwsKeeper,experimental,@grafana/grafana-operator-experience-squad,false,false,false
profilesExemplars,experimental,@grafana/observability-traces-and-profiling,false,false,false profilesExemplars,experimental,@grafana/observability-traces-and-profiling,false,false,false
alertingSyncDispatchTimer,experimental,@grafana/alerting-squad,false,true,false
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
79 dashboardSceneForViewers GA @grafana/dashboards-squad false false true
80 dashboardSceneSolo GA @grafana/dashboards-squad false false true
81 dashboardScene GA @grafana/dashboards-squad false false true
82 dashboardNewLayouts experimental preview @grafana/dashboards-squad false false false
83 dashboardUndoRedo experimental @grafana/dashboards-squad false false true
84 unlimitedLayoutsNesting experimental @grafana/dashboards-squad false false true
85 drilldownRecommendations experimental @grafana/dashboards-squad false false true
121 suggestedDashboards experimental @grafana/sharing-squad false false false
122 dashboardTemplates preview @grafana/sharing-squad false false false
123 alertingListViewV2 privatePreview @grafana/alerting-squad false false true
124 alertingNavigationV2 experimental @grafana/alerting-squad false false false
125 alertingSavedSearches experimental @grafana/alerting-squad false false true
126 alertingDisableSendAlertsExternal experimental @grafana/alerting-squad false false false
127 preserveDashboardStateWhenNavigating experimental @grafana/dashboards-squad false false false
281 smoothingTransformation experimental @grafana/datapro false false true
282 secretsManagementAppPlatformAwsKeeper experimental @grafana/grafana-operator-experience-squad false false false
283 profilesExemplars experimental @grafana/observability-traces-and-profiling false false false
284 alertingSyncDispatchTimer experimental @grafana/alerting-squad false true false
+9 -1
View File
@@ -260,7 +260,7 @@ const (
FlagAnnotationPermissionUpdate = "annotationPermissionUpdate" FlagAnnotationPermissionUpdate = "annotationPermissionUpdate"
// FlagDashboardNewLayouts // FlagDashboardNewLayouts
// Enables experimental new dashboard layouts // Enables new dashboard layouts
FlagDashboardNewLayouts = "dashboardNewLayouts" FlagDashboardNewLayouts = "dashboardNewLayouts"
// FlagPdfTables // FlagPdfTables
@@ -371,6 +371,10 @@ const (
// Enables a flow to get started with a new dashboard from a template // Enables a flow to get started with a new dashboard from a template
FlagDashboardTemplates = "dashboardTemplates" FlagDashboardTemplates = "dashboardTemplates"
// FlagAlertingNavigationV2
// Enables the new Alerting navigation structure with improved menu grouping
FlagAlertingNavigationV2 = "alertingNavigationV2"
// FlagAlertingDisableSendAlertsExternal // FlagAlertingDisableSendAlertsExternal
// Disables the ability to send alerts to an external Alertmanager datasource. // Disables the ability to send alerts to an external Alertmanager datasource.
FlagAlertingDisableSendAlertsExternal = "alertingDisableSendAlertsExternal" FlagAlertingDisableSendAlertsExternal = "alertingDisableSendAlertsExternal"
@@ -789,4 +793,8 @@ const (
// FlagProfilesExemplars // FlagProfilesExemplars
// Enables profiles exemplars support in profiles drilldown // Enables profiles exemplars support in profiles drilldown
FlagProfilesExemplars = "profilesExemplars" FlagProfilesExemplars = "profilesExemplars"
// FlagAlertingSyncDispatchTimer
// Use synchronized dispatch timer to minimize duplicate notifications across alertmanager HA pods
FlagAlertingSyncDispatchTimer = "alertingSyncDispatchTimer"
) )
+35 -5
View File
@@ -348,6 +348,18 @@
"expression": "true" "expression": "true"
} }
}, },
{
"metadata": {
"name": "alertingNavigationV2",
"resourceVersion": "1768320918269",
"creationTimestamp": "2026-01-13T16:15:18Z"
},
"spec": {
"description": "Enables the new Alerting navigation structure with improved menu grouping",
"stage": "experimental",
"codeowner": "@grafana/alerting-squad"
}
},
{ {
"metadata": { "metadata": {
"name": "alertingNotificationHistory", "name": "alertingNotificationHistory",
@@ -511,6 +523,20 @@
"frontend": true "frontend": true
} }
}, },
{
"metadata": {
"name": "alertingSyncDispatchTimer",
"resourceVersion": "1766161788928",
"creationTimestamp": "2025-12-19T16:29:48Z"
},
"spec": {
"description": "Use synchronized dispatch timer to minimize duplicate notifications across alertmanager HA pods",
"stage": "experimental",
"codeowner": "@grafana/alerting-squad",
"requiresRestart": true,
"hideFromDocs": true
}
},
{ {
"metadata": { "metadata": {
"name": "alertingTriage", "name": "alertingTriage",
@@ -662,7 +688,8 @@
"metadata": { "metadata": {
"name": "auditLoggingAppPlatform", "name": "auditLoggingAppPlatform",
"resourceVersion": "1767013056996", "resourceVersion": "1767013056996",
"creationTimestamp": "2025-12-29T12:57:36Z" "creationTimestamp": "2025-12-29T12:57:36Z",
"deletionTimestamp": "2026-01-06T09:18:36Z"
}, },
"spec": { "spec": {
"description": "Enable audit logging with Kubernetes under app platform", "description": "Enable audit logging with Kubernetes under app platform",
@@ -1015,12 +1042,15 @@
{ {
"metadata": { "metadata": {
"name": "dashboardNewLayouts", "name": "dashboardNewLayouts",
"resourceVersion": "1764664939750", "resourceVersion": "1768382835527",
"creationTimestamp": "2024-10-23T08:55:45Z" "creationTimestamp": "2024-10-23T08:55:45Z",
"annotations": {
"grafana.app/updatedTimestamp": "2026-01-14 09:27:15.527103 +0000 UTC"
}
}, },
"spec": { "spec": {
"description": "Enables experimental new dashboard layouts", "description": "Enables new dashboard layouts",
"stage": "experimental", "stage": "preview",
"codeowner": "@grafana/dashboards-squad" "codeowner": "@grafana/dashboards-squad"
} }
}, },
+1 -2
View File
@@ -54,8 +54,7 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
} }
//nolint:staticcheck // not yet migrated to OpenFeature //nolint:staticcheck // not yet migrated to OpenFeature
if c.HasRole(identity.RoleAdmin) && if c.HasRole(identity.RoleAdmin) &&
(s.cfg.StackID == "" || // show OnPrem even when provisioning is disabled s.features.IsEnabledGlobally(featuremgmt.FlagProvisioning) {
s.features.IsEnabledGlobally(featuremgmt.FlagProvisioning)) {
generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{ generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{
Text: "Provisioning", Text: "Provisioning",
Id: "provisioning", Id: "provisioning",
+4
View File
@@ -213,6 +213,9 @@ func (ng *AlertNG) init() error {
SkipVerify: ng.Cfg.Smtp.SkipVerify, SkipVerify: ng.Cfg.Smtp.SkipVerify,
StaticHeaders: ng.Cfg.Smtp.StaticHeaders, StaticHeaders: ng.Cfg.Smtp.StaticHeaders,
} }
runtimeConfig := remoteClient.RuntimeConfig{
DispatchTimer: notifier.GetDispatchTimer(ng.FeatureToggles).String(),
}
cfg := remote.AlertmanagerConfig{ cfg := remote.AlertmanagerConfig{
BasicAuthPassword: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.Password, BasicAuthPassword: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.Password,
@@ -222,6 +225,7 @@ func (ng *AlertNG) init() error {
ExternalURL: ng.Cfg.AppURL, ExternalURL: ng.Cfg.AppURL,
SmtpConfig: smtpCfg, SmtpConfig: smtpCfg,
Timeout: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.Timeout, Timeout: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.Timeout,
RuntimeConfig: runtimeConfig,
} }
autogenFn := func(ctx context.Context, logger log.Logger, orgID int64, cfg *definitions.PostableApiAlertingConfig, invalidReceiverAction notifier.InvalidReceiversAction) error { autogenFn := func(ctx context.Context, logger log.Logger, orgID int64, cfg *definitions.PostableApiAlertingConfig, invalidReceiverAction notifier.InvalidReceiversAction) error {
return notifier.AddAutogenConfig(ctx, logger, ng.store, orgID, cfg, invalidReceiverAction, ng.FeatureToggles) return notifier.AddAutogenConfig(ctx, logger, ng.store, orgID, cfg, invalidReceiverAction, ng.FeatureToggles)
@@ -33,6 +33,9 @@ const (
// How long we keep silences in the kvstore after they've expired. // How long we keep silences in the kvstore after they've expired.
silenceRetention = 5 * 24 * time.Hour silenceRetention = 5 * 24 * time.Hour
// How long we keep flushes in the kvstore after they've expired.
flushRetention = 5 * 24 * time.Hour
) )
type AlertingStore interface { type AlertingStore interface {
@@ -44,8 +47,10 @@ type AlertingStore interface {
type stateStore interface { type stateStore interface {
SaveSilences(ctx context.Context, st alertingNotify.State) (int64, error) SaveSilences(ctx context.Context, st alertingNotify.State) (int64, error)
SaveNotificationLog(ctx context.Context, st alertingNotify.State) (int64, error) SaveNotificationLog(ctx context.Context, st alertingNotify.State) (int64, error)
SaveFlushLog(ctx context.Context, st alertingNotify.State) (int64, error)
GetSilences(ctx context.Context) (string, error) GetSilences(ctx context.Context) (string, error)
GetNotificationLog(ctx context.Context) (string, error) GetNotificationLog(ctx context.Context) (string, error)
GetFlushLog(ctx context.Context) (string, error)
} }
type alertmanager struct { type alertmanager struct {
@@ -101,6 +106,10 @@ func NewAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store A
if err != nil { if err != nil {
return nil, err return nil, err
} }
flushLog, err := stateStore.GetFlushLog(ctx)
if err != nil {
return nil, err
}
silencesOptions := maintenanceOptions{ silencesOptions := maintenanceOptions{
initialState: silences, initialState: silences,
@@ -123,12 +132,29 @@ func NewAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store A
} }
l := log.New("ngalert.notifier") l := log.New("ngalert.notifier")
dispatchTimer := GetDispatchTimer(featureToggles)
var flushLogOptions *maintenanceOptions
if dispatchTimer == alertingNotify.DispatchTimerSync {
flushLogOptions = &maintenanceOptions{
initialState: flushLog,
retention: flushRetention,
maintenanceFrequency: maintenanceInterval,
maintenanceFunc: func(state alertingNotify.State) (int64, error) {
// Detached context here is to make sure that when the service is shut down the persist operation is executed.
return stateStore.SaveFlushLog(context.Background(), state)
},
}
}
opts := alertingNotify.GrafanaAlertmanagerOpts{ opts := alertingNotify.GrafanaAlertmanagerOpts{
ExternalURL: cfg.AppURL, ExternalURL: cfg.AppURL,
AlertStoreCallback: nil, AlertStoreCallback: nil,
PeerTimeout: cfg.UnifiedAlerting.HAPeerTimeout, PeerTimeout: cfg.UnifiedAlerting.HAPeerTimeout,
Silences: silencesOptions, Silences: silencesOptions,
Nflog: nflogOptions, Nflog: nflogOptions,
FlushLog: flushLogOptions,
DispatchTimer: dispatchTimer,
Limits: alertingNotify.Limits{ Limits: alertingNotify.Limits{
MaxSilences: cfg.UnifiedAlerting.AlertmanagerMaxSilencesCount, MaxSilences: cfg.UnifiedAlerting.AlertmanagerMaxSilencesCount,
MaxSilenceSizeBytes: cfg.UnifiedAlerting.AlertmanagerMaxSilenceSizeBytes, MaxSilenceSizeBytes: cfg.UnifiedAlerting.AlertmanagerMaxSilenceSizeBytes,
@@ -0,0 +1,16 @@
package notifier
import (
alertingNotify "github.com/grafana/alerting/notify"
"github.com/grafana/grafana/pkg/services/featuremgmt"
)
// GetDispatchTimer returns the appropriate dispatch timer based on feature toggles.
func GetDispatchTimer(features featuremgmt.FeatureToggles) (dt alertingNotify.DispatchTimer) {
//nolint:staticcheck // not yet migrated to OpenFeature
enabled := features.IsEnabledGlobally(featuremgmt.FlagAlertingSyncDispatchTimer)
if enabled {
dt = alertingNotify.DispatchTimerSync
}
return
}
@@ -0,0 +1,36 @@
package notifier
import (
"testing"
alertingNotify "github.com/grafana/alerting/notify"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/stretchr/testify/require"
)
func TestGetDispatchTimer(t *testing.T) {
tests := []struct {
name string
featureFlagValue bool
expected alertingNotify.DispatchTimer
}{
{
name: "feature flag enabled returns sync timer",
featureFlagValue: true,
expected: alertingNotify.DispatchTimerSync,
},
{
name: "feature flag disabled returns default timer",
featureFlagValue: false,
expected: alertingNotify.DispatchTimerDefault,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
features := featuremgmt.WithFeatures(featuremgmt.FlagAlertingSyncDispatchTimer, tt.featureFlagValue)
result := GetDispatchTimer(features)
require.Equal(t, tt.expected, result)
})
}
}
@@ -15,6 +15,7 @@ const (
KVNamespace = "alertmanager" KVNamespace = "alertmanager"
NotificationLogFilename = "notifications" NotificationLogFilename = "notifications"
SilencesFilename = "silences" SilencesFilename = "silences"
FlushLogFilename = "flushes"
) )
// FileStore is in charge of persisting the alertmanager files to the database. // FileStore is in charge of persisting the alertmanager files to the database.
@@ -42,6 +43,10 @@ func (fileStore *FileStore) GetNotificationLog(ctx context.Context) (string, err
return fileStore.contentFor(ctx, NotificationLogFilename) return fileStore.contentFor(ctx, NotificationLogFilename)
} }
func (fileStore *FileStore) GetFlushLog(ctx context.Context) (string, error) {
return fileStore.contentFor(ctx, FlushLogFilename)
}
// contentFor returns the content for the given Alertmanager kvstore key. // contentFor returns the content for the given Alertmanager kvstore key.
func (fileStore *FileStore) contentFor(ctx context.Context, filename string) (string, error) { func (fileStore *FileStore) contentFor(ctx context.Context, filename string) (string, error) {
// Then, let's attempt to read it from the database. // Then, let's attempt to read it from the database.
@@ -74,6 +79,11 @@ func (fileStore *FileStore) SaveNotificationLog(ctx context.Context, st alerting
return fileStore.persist(ctx, NotificationLogFilename, st) return fileStore.persist(ctx, NotificationLogFilename, st)
} }
// SaveFlushLog saves the flush log to the database and returns the size of the unencoded state.
func (fileStore *FileStore) SaveFlushLog(ctx context.Context, st alertingNotify.State) (int64, error) {
return fileStore.persist(ctx, FlushLogFilename, st)
}
// persist takes care of persisting the binary representation of internal state to the database as a base64 encoded string. // persist takes care of persisting the binary representation of internal state to the database as a base64 encoded string.
func (fileStore *FileStore) persist(ctx context.Context, filename string, st alertingNotify.State) (int64, error) { func (fileStore *FileStore) persist(ctx context.Context, filename string, st alertingNotify.State) (int64, error) {
var size int64 var size int64
@@ -106,3 +106,48 @@ func TestFileStore_NotificationLog(t *testing.T) {
t.Errorf("Unexpected Diff: %v", cmp.Diff(newState, decoded)) t.Errorf("Unexpected Diff: %v", cmp.Diff(newState, decoded))
} }
} }
func TestFileStore_FlushLog(t *testing.T) {
store := fakes.NewFakeKVStore(t)
ctx := context.Background()
var orgId int64 = 1
// Initialize kvstore with empty flush log state.
initialState := flushLogState{} // FlushLog uses the same structure as nflog
decodedState, err := initialState.MarshalBinary()
require.NoError(t, err)
encodedState := base64.StdEncoding.EncodeToString(decodedState)
err = store.Set(ctx, orgId, KVNamespace, FlushLogFilename, encodedState)
require.NoError(t, err)
fs := NewFileStore(orgId, store)
// Load initial (empty).
flushLog, err := fs.GetFlushLog(ctx)
require.NoError(t, err)
decoded, err := decodeFlushLogState(strings.NewReader(flushLog))
require.NoError(t, err)
if !cmp.Equal(initialState, decoded) {
t.Errorf("Unexpected Diff: %v", cmp.Diff(initialState, decoded))
}
// Save new flush log state.
now := time.Now()
oneHour := now.Add(time.Hour)
v1 := createFlushLog(1, now, oneHour)
v2 := createFlushLog(2, now, oneHour)
newState := flushLogState{1: v1, 2: v2}
size, err := fs.SaveFlushLog(ctx, newState)
require.NoError(t, err)
require.Greater(t, size, int64(0))
// Load new.
flushLog, err = fs.GetFlushLog(ctx)
require.NoError(t, err)
decoded, err = decodeFlushLogState(strings.NewReader(flushLog))
require.NoError(t, err)
if !cmp.Equal(newState, decoded) {
t.Errorf("Unexpected Diff: %v", cmp.Diff(newState, decoded))
}
}
@@ -82,6 +82,7 @@ type Alertmanager interface {
type ExternalState struct { type ExternalState struct {
Silences []byte Silences []byte
Nflog []byte Nflog []byte
FlushLog []byte
} }
// StateMerger describes a type that is able to merge external state (nflog, silences) with its own. // StateMerger describes a type that is able to merge external state (nflog, silences) with its own.
@@ -378,7 +379,7 @@ func (moa *MultiOrgAlertmanager) SyncAlertmanagersForOrgs(ctx context.Context, o
func (moa *MultiOrgAlertmanager) cleanupOrphanLocalOrgState(ctx context.Context, func (moa *MultiOrgAlertmanager) cleanupOrphanLocalOrgState(ctx context.Context,
activeOrganizations map[int64]struct{}, activeOrganizations map[int64]struct{},
) { ) {
storedFiles := []string{NotificationLogFilename, SilencesFilename} storedFiles := []string{NotificationLogFilename, SilencesFilename, FlushLogFilename}
for _, fileName := range storedFiles { for _, fileName := range storedFiles {
keys, err := moa.kvStore.Keys(ctx, kvstore.AllOrganizations, KVNamespace, fileName) keys, err := moa.kvStore.Keys(ctx, kvstore.AllOrganizations, KVNamespace, fileName)
if err != nil { if err != nil {
+4 -1
View File
@@ -5,5 +5,8 @@ func (am *alertmanager) MergeState(state ExternalState) error {
if err := am.Base.MergeNflog(state.Nflog); err != nil { if err := am.Base.MergeNflog(state.Nflog); err != nil {
return err return err
} }
return am.Base.MergeSilences(state.Silences) if err := am.Base.MergeSilences(state.Silences); err != nil {
return err
}
return am.Base.MergeFlushLog(state.FlushLog)
} }
+48 -4
View File
@@ -11,6 +11,7 @@ import (
"time" "time"
"github.com/matttproud/golang_protobuf_extensions/pbutil" "github.com/matttproud/golang_protobuf_extensions/pbutil"
"github.com/prometheus/alertmanager/flushlog/flushlogpb"
"github.com/prometheus/alertmanager/nflog/nflogpb" "github.com/prometheus/alertmanager/nflog/nflogpb"
"github.com/prometheus/alertmanager/silence/silencepb" "github.com/prometheus/alertmanager/silence/silencepb"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
@@ -228,15 +229,13 @@ func (f *FakeOrgStore) FetchOrgIds(_ context.Context) ([]int64, error) {
return f.orgs, nil return f.orgs, nil
} }
type NoValidation struct { type NoValidation struct{}
}
func (n NoValidation) Validate(_ models.NotificationSettings) error { func (n NoValidation) Validate(_ models.NotificationSettings) error {
return nil return nil
} }
type RejectingValidation struct { type RejectingValidation struct{}
}
func (n RejectingValidation) Validate(s models.NotificationSettings) error { func (n RejectingValidation) Validate(s models.NotificationSettings) error {
return ErrorReceiverDoesNotExist{ErrorReferenceInvalid: ErrorReferenceInvalid{Reference: s.Receiver}} return ErrorReceiverDoesNotExist{ErrorReferenceInvalid: ErrorReferenceInvalid{Reference: s.Receiver}}
@@ -365,6 +364,51 @@ func createNotificationLog(groupKey string, receiverName string, sentAt, expires
} }
} }
// https://github.com/grafana/prometheus-alertmanager/blob/main/flushlog/flushlog.go#L136-L136
type flushLogState map[uint64]*flushlogpb.MeshFlushLog
func (s flushLogState) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
for _, e := range s {
if _, err := pbutil.WriteDelimited(&buf, e); err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}
func createFlushLog(groupFingerprint uint64, ts, expiresAt time.Time) *flushlogpb.MeshFlushLog {
return &flushlogpb.MeshFlushLog{
FlushLog: &flushlogpb.FlushLog{
GroupFingerprint: groupFingerprint,
Timestamp: ts,
},
ExpiresAt: expiresAt,
}
}
// decodeFlushLogState copied from decodeState in prometheus-alertmanager/flushlog/flushlog.go
func decodeFlushLogState(r io.Reader) (flushLogState, error) {
st := flushLogState{}
for {
var e flushlogpb.MeshFlushLog
_, err := pbutil.ReadDelimited(r, &e)
if err == nil {
if e.FlushLog == nil || e.FlushLog.GroupFingerprint == 0 || e.FlushLog.Timestamp.IsZero() {
return nil, errInvalidState
}
st[e.FlushLog.GroupFingerprint] = &e
continue
}
if errors.Is(err, io.EOF) {
break
}
return nil, err
}
return st, nil
}
type call struct { type call struct {
Method string Method string
Args []interface{} Args []interface{}
+20 -4
View File
@@ -47,6 +47,7 @@ import (
type stateStore interface { type stateStore interface {
GetSilences(ctx context.Context) (string, error) GetSilences(ctx context.Context) (string, error)
GetNotificationLog(ctx context.Context) (string, error) GetNotificationLog(ctx context.Context) (string, error)
GetFlushLog(ctx context.Context) (string, error)
} }
// AutogenFn is a function that adds auto-generated routes to a configuration. // AutogenFn is a function that adds auto-generated routes to a configuration.
@@ -86,6 +87,8 @@ type Alertmanager struct {
promoteConfig bool promoteConfig bool
externalURL string externalURL string
runtimeConfig remoteClient.RuntimeConfig
} }
type AlertmanagerConfig struct { type AlertmanagerConfig struct {
@@ -111,6 +114,9 @@ type AlertmanagerConfig struct {
// Timeout for the HTTP client. // Timeout for the HTTP client.
Timeout time.Duration Timeout time.Duration
// RuntimeConfig specifies runtime behavior settings for the remote Alertmanager.
RuntimeConfig remoteClient.RuntimeConfig
} }
func (cfg *AlertmanagerConfig) Validate() error { func (cfg *AlertmanagerConfig) Validate() error {
@@ -203,6 +209,7 @@ func NewAlertmanager(ctx context.Context, cfg AlertmanagerConfig, store stateSto
externalURL: cfg.ExternalURL, externalURL: cfg.ExternalURL,
promoteConfig: cfg.PromoteConfig, promoteConfig: cfg.PromoteConfig,
smtp: cfg.SmtpConfig, smtp: cfg.SmtpConfig,
runtimeConfig: cfg.RuntimeConfig,
} }
// Parse the default configuration once and remember its hash so we can compare it later. // Parse the default configuration once and remember its hash so we can compare it later.
@@ -331,10 +338,11 @@ func (am *Alertmanager) buildConfiguration(ctx context.Context, raw []byte, crea
AlertmanagerConfig: mergeResult.Config, AlertmanagerConfig: mergeResult.Config,
Templates: templates, Templates: templates,
}, },
CreatedAt: createdAtEpoch, CreatedAt: createdAtEpoch,
Promoted: am.promoteConfig, Promoted: am.promoteConfig,
ExternalURL: am.externalURL, ExternalURL: am.externalURL,
SmtpConfig: am.smtp, SmtpConfig: am.smtp,
RuntimeConfig: am.runtimeConfig,
} }
cfgHash, err := calculateUserGrafanaConfigHash(payload) cfgHash, err := calculateUserGrafanaConfigHash(payload)
@@ -388,6 +396,8 @@ func (am *Alertmanager) GetRemoteState(ctx context.Context) (notifier.ExternalSt
rs.Silences = p.Data rs.Silences = p.Data
case "nfl": case "nfl":
rs.Nflog = p.Data rs.Nflog = p.Data
case "fls":
rs.FlushLog = p.Data
default: default:
return rs, fmt.Errorf("unknown part key %q", p.Key) return rs, fmt.Errorf("unknown part key %q", p.Key)
} }
@@ -677,6 +687,12 @@ func (am *Alertmanager) getFullState(ctx context.Context) (string, error) {
} }
parts = append(parts, alertingClusterPB.Part{Key: notifier.NotificationLogFilename, Data: []byte(notificationLog)}) parts = append(parts, alertingClusterPB.Part{Key: notifier.NotificationLogFilename, Data: []byte(notificationLog)})
flushLog, err := am.state.GetFlushLog(ctx)
if err != nil {
return "", fmt.Errorf("error getting flush log: %w", err)
}
parts = append(parts, alertingClusterPB.Part{Key: notifier.FlushLogFilename, Data: []byte(flushLog)})
fs := alertingClusterPB.FullState{ fs := alertingClusterPB.FullState{
Parts: parts, Parts: parts,
} }
@@ -29,6 +29,10 @@ func (u *GrafanaAlertmanagerConfig) MarshalJSON() ([]byte, error) {
return definition.MarshalJSONWithSecrets((*cfg)(u)) return definition.MarshalJSONWithSecrets((*cfg)(u))
} }
type RuntimeConfig struct {
DispatchTimer string `json:"dispatch_timer"`
}
type UserGrafanaConfig struct { type UserGrafanaConfig struct {
GrafanaAlertmanagerConfig GrafanaAlertmanagerConfig `json:"configuration"` GrafanaAlertmanagerConfig GrafanaAlertmanagerConfig `json:"configuration"`
Hash string `json:"configuration_hash"` Hash string `json:"configuration_hash"`
@@ -37,6 +41,7 @@ type UserGrafanaConfig struct {
Promoted bool `json:"promoted"` Promoted bool `json:"promoted"`
ExternalURL string `json:"external_url"` ExternalURL string `json:"external_url"`
SmtpConfig SmtpConfig `json:"smtp_config"` SmtpConfig SmtpConfig `json:"smtp_config"`
RuntimeConfig RuntimeConfig `json:"runtime_config"`
} }
func (mc *Mimir) GetGrafanaAlertmanagerConfig(ctx context.Context) (*UserGrafanaConfig, error) { func (mc *Mimir) GetGrafanaAlertmanagerConfig(ctx context.Context) (*UserGrafanaConfig, error) {
+1
View File
@@ -600,6 +600,7 @@ type Cfg struct {
IndexRebuildInterval time.Duration IndexRebuildInterval time.Duration
IndexCacheTTL time.Duration IndexCacheTTL time.Duration
IndexMinUpdateInterval time.Duration // Don't update index if it was updated less than this interval ago. IndexMinUpdateInterval time.Duration // Don't update index if it was updated less than this interval ago.
IndexScoringModel string // Note: Temporary config to switch the index scoring model and will be removed soon.
MaxFileIndexAge time.Duration // Max age of file-based indexes. Index older than this will be rebuilt asynchronously. MaxFileIndexAge time.Duration // Max age of file-based indexes. Index older than this will be rebuilt asynchronously.
MinFileIndexBuildVersion string // Minimum version of Grafana that built the file-based index. If index was built with older Grafana, it will be rebuilt asynchronously. MinFileIndexBuildVersion string // Minimum version of Grafana that built the file-based index. If index was built with older Grafana, it will be rebuilt asynchronously.
EnableSharding bool EnableSharding bool
+4
View File
@@ -123,6 +123,10 @@ func (cfg *Cfg) setUnifiedStorageConfig() {
cfg.IndexRebuildInterval = section.Key("index_rebuild_interval").MustDuration(24 * time.Hour) cfg.IndexRebuildInterval = section.Key("index_rebuild_interval").MustDuration(24 * time.Hour)
cfg.IndexCacheTTL = section.Key("index_cache_ttl").MustDuration(10 * time.Minute) cfg.IndexCacheTTL = section.Key("index_cache_ttl").MustDuration(10 * time.Minute)
cfg.IndexMinUpdateInterval = section.Key("index_min_update_interval").MustDuration(0) cfg.IndexMinUpdateInterval = section.Key("index_min_update_interval").MustDuration(0)
cfg.IndexScoringModel = section.Key("index_scoring_model").MustString("")
if cfg.IndexScoringModel != "" {
cfg.Logger.Info("Index scoring model set", "model", cfg.IndexScoringModel)
}
cfg.SprinklesApiServer = section.Key("sprinkles_api_server").String() cfg.SprinklesApiServer = section.Key("sprinkles_api_server").String()
cfg.SprinklesApiServerPageLimit = section.Key("sprinkles_api_server_page_limit").MustInt(10000) cfg.SprinklesApiServerPageLimit = section.Key("sprinkles_api_server_page_limit").MustInt(10000)
cfg.CACertPath = section.Key("ca_cert_path").String() cfg.CACertPath = section.Key("ca_cert_path").String()
+31
View File
@@ -9,11 +9,13 @@ import "resource.proto";
// Unlike the ResourceStore, this service can be exposed to clients directly // Unlike the ResourceStore, this service can be exposed to clients directly
// It should be implemented with efficient indexes and does not need read-after-write semantics // It should be implemented with efficient indexes and does not need read-after-write semantics
service ResourceIndex { service ResourceIndex {
// Query for documents
rpc Search(ResourceSearchRequest) returns (ResourceSearchResponse); rpc Search(ResourceSearchRequest) returns (ResourceSearchResponse);
// Get the resource stats // Get the resource stats
rpc GetStats(ResourceStatsRequest) returns (ResourceStatsResponse); rpc GetStats(ResourceStatsRequest) returns (ResourceStatsResponse);
// Rebuild the search index
rpc RebuildIndexes(RebuildIndexesRequest) returns (RebuildIndexesResponse); rpc RebuildIndexes(RebuildIndexesRequest) returns (RebuildIndexesResponse);
} }
@@ -49,6 +51,20 @@ message ResourceStatsResponse {
repeated Stats stats = 2; repeated Stats stats = 2;
} }
// This controls what query and analyzers are applied to the specified field
// See: https://blevesearch.com/docs/Analyzers/
enum QueryFieldType {
// Picks a reasonable analyzer given the input. Currently this always uses TEXT
// In the future, it may change to depend on the indexed field type
DEFAULT = 0;
// Use free text analyzer. The query is broken into a normalized set of tokens
TEXT = 1;
// The query must exactly match the indexed token
KEYWORD = 2;
// Like a text query, but the position and offsets influence the score
PHRASE = 3;
}
// Search within a single resource // Search within a single resource
message ResourceSearchRequest { message ResourceSearchRequest {
message Sort { message Sort {
@@ -64,6 +80,18 @@ message ResourceSearchRequest {
// date queries // date queries
} }
// Defines the field in the index to query
// Boost is optional, and allows weighting the field higher in the results
message QueryField {
// The field name in the index to query
string name = 1;
QueryFieldType type = 2;
// Boost value for this field
float boost = 3;
}
// The key must include namespace + group + resource // The key must include namespace + group + resource
ListOptions options = 1; ListOptions options = 1;
@@ -99,6 +127,9 @@ message ResourceSearchRequest {
int64 page = 11; int64 page = 11;
int64 permission = 12; int64 permission = 12;
// Optionally specify which fields are included in the query
repeated QueryField query_fields = 13;
} }
message ResourceSearchResponse { message ResourceSearchResponse {
-1
View File
@@ -290,7 +290,6 @@ const SEARCH_FIELD_NAMESPACE = "namespace"
const SEARCH_FIELD_NAME = "name" const SEARCH_FIELD_NAME = "name"
const SEARCH_FIELD_RV = "rv" const SEARCH_FIELD_RV = "rv"
const SEARCH_FIELD_TITLE = "title" const SEARCH_FIELD_TITLE = "title"
const SEARCH_FIELD_TITLE_NGRAM = "title_ngram"
const SEARCH_FIELD_TITLE_PHRASE = "title_phrase" // filtering/sorting on title by full phrase const SEARCH_FIELD_TITLE_PHRASE = "title_phrase" // filtering/sorting on title by full phrase
const SEARCH_FIELD_DESCRIPTION = "description" const SEARCH_FIELD_DESCRIPTION = "description"
const SEARCH_FIELD_TAGS = "tags" const SEARCH_FIELD_TAGS = "tags"
+293 -139
View File
@@ -21,6 +21,65 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
// This controls what query and analyzers are applied to the specified field
// See: https://blevesearch.com/docs/Analyzers/
type QueryFieldType int32
const (
// Picks a reasonable analyzer given the input. Currently this always uses TEXT
// In the future, it may change to depend on the indexed field type
QueryFieldType_DEFAULT QueryFieldType = 0
// Use free text analyzer. The query is broken into a normalized set of tokens
QueryFieldType_TEXT QueryFieldType = 1
// The query must exactly match the indexed token
QueryFieldType_KEYWORD QueryFieldType = 2
// Like a text query, but the position and offsets influence the score
QueryFieldType_PHRASE QueryFieldType = 3
)
// Enum value maps for QueryFieldType.
var (
QueryFieldType_name = map[int32]string{
0: "DEFAULT",
1: "TEXT",
2: "KEYWORD",
3: "PHRASE",
}
QueryFieldType_value = map[string]int32{
"DEFAULT": 0,
"TEXT": 1,
"KEYWORD": 2,
"PHRASE": 3,
}
)
func (x QueryFieldType) Enum() *QueryFieldType {
p := new(QueryFieldType)
*p = x
return p
}
func (x QueryFieldType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (QueryFieldType) Descriptor() protoreflect.EnumDescriptor {
return file_search_proto_enumTypes[0].Descriptor()
}
func (QueryFieldType) Type() protoreflect.EnumType {
return &file_search_proto_enumTypes[0]
}
func (x QueryFieldType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use QueryFieldType.Descriptor instead.
func (QueryFieldType) EnumDescriptor() ([]byte, []int) {
return file_search_proto_rawDescGZIP(), []int{0}
}
// Get statistics across multiple resources // Get statistics across multiple resources
// For these queries, we do not need authorization to see the actual values // For these queries, we do not need authorization to see the actual values
type ResourceStatsRequest struct { type ResourceStatsRequest struct {
@@ -165,10 +224,12 @@ type ResourceSearchRequest struct {
// the return fields (empty will return everything) // the return fields (empty will return everything)
Fields []string `protobuf:"bytes,8,rep,name=fields,proto3" json:"fields,omitempty"` Fields []string `protobuf:"bytes,8,rep,name=fields,proto3" json:"fields,omitempty"`
// explain each result (added to the each row) // explain each result (added to the each row)
Explain bool `protobuf:"varint,9,opt,name=explain,proto3" json:"explain,omitempty"` Explain bool `protobuf:"varint,9,opt,name=explain,proto3" json:"explain,omitempty"`
IsDeleted bool `protobuf:"varint,10,opt,name=is_deleted,json=isDeleted,proto3" json:"is_deleted,omitempty"` IsDeleted bool `protobuf:"varint,10,opt,name=is_deleted,json=isDeleted,proto3" json:"is_deleted,omitempty"`
Page int64 `protobuf:"varint,11,opt,name=page,proto3" json:"page,omitempty"` Page int64 `protobuf:"varint,11,opt,name=page,proto3" json:"page,omitempty"`
Permission int64 `protobuf:"varint,12,opt,name=permission,proto3" json:"permission,omitempty"` Permission int64 `protobuf:"varint,12,opt,name=permission,proto3" json:"permission,omitempty"`
// Optionally specify which fields are included in the query
QueryFields []*ResourceSearchRequest_QueryField `protobuf:"bytes,13,rep,name=query_fields,json=queryFields,proto3" json:"query_fields,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -287,6 +348,13 @@ func (x *ResourceSearchRequest) GetPermission() int64 {
return 0 return 0
} }
func (x *ResourceSearchRequest) GetQueryFields() []*ResourceSearchRequest_QueryField {
if x != nil {
return x.QueryFields
}
return nil
}
type ResourceSearchResponse struct { type ResourceSearchResponse struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
// Error details // Error details
@@ -670,6 +738,70 @@ func (x *ResourceSearchRequest_Facet) GetLimit() int64 {
return 0 return 0
} }
// Defines the field in the index to query
// Boost is optional, and allows weighting the field higher in the results
type ResourceSearchRequest_QueryField struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The field name in the index to query
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Type QueryFieldType `protobuf:"varint,2,opt,name=type,proto3,enum=resource.QueryFieldType" json:"type,omitempty"`
// Boost value for this field
Boost float32 `protobuf:"fixed32,3,opt,name=boost,proto3" json:"boost,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ResourceSearchRequest_QueryField) Reset() {
*x = ResourceSearchRequest_QueryField{}
mi := &file_search_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ResourceSearchRequest_QueryField) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ResourceSearchRequest_QueryField) ProtoMessage() {}
func (x *ResourceSearchRequest_QueryField) ProtoReflect() protoreflect.Message {
mi := &file_search_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ResourceSearchRequest_QueryField.ProtoReflect.Descriptor instead.
func (*ResourceSearchRequest_QueryField) Descriptor() ([]byte, []int) {
return file_search_proto_rawDescGZIP(), []int{2, 2}
}
func (x *ResourceSearchRequest_QueryField) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *ResourceSearchRequest_QueryField) GetType() QueryFieldType {
if x != nil {
return x.Type
}
return QueryFieldType_DEFAULT
}
func (x *ResourceSearchRequest_QueryField) GetBoost() float32 {
if x != nil {
return x.Boost
}
return 0
}
type ResourceSearchResponse_Facet struct { type ResourceSearchResponse_Facet struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"` Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"`
@@ -685,7 +817,7 @@ type ResourceSearchResponse_Facet struct {
func (x *ResourceSearchResponse_Facet) Reset() { func (x *ResourceSearchResponse_Facet) Reset() {
*x = ResourceSearchResponse_Facet{} *x = ResourceSearchResponse_Facet{}
mi := &file_search_proto_msgTypes[10] mi := &file_search_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -697,7 +829,7 @@ func (x *ResourceSearchResponse_Facet) String() string {
func (*ResourceSearchResponse_Facet) ProtoMessage() {} func (*ResourceSearchResponse_Facet) ProtoMessage() {}
func (x *ResourceSearchResponse_Facet) ProtoReflect() protoreflect.Message { func (x *ResourceSearchResponse_Facet) ProtoReflect() protoreflect.Message {
mi := &file_search_proto_msgTypes[10] mi := &file_search_proto_msgTypes[11]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -751,7 +883,7 @@ type ResourceSearchResponse_TermFacet struct {
func (x *ResourceSearchResponse_TermFacet) Reset() { func (x *ResourceSearchResponse_TermFacet) Reset() {
*x = ResourceSearchResponse_TermFacet{} *x = ResourceSearchResponse_TermFacet{}
mi := &file_search_proto_msgTypes[11] mi := &file_search_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@@ -763,7 +895,7 @@ func (x *ResourceSearchResponse_TermFacet) String() string {
func (*ResourceSearchResponse_TermFacet) ProtoMessage() {} func (*ResourceSearchResponse_TermFacet) ProtoMessage() {}
func (x *ResourceSearchResponse_TermFacet) ProtoReflect() protoreflect.Message { func (x *ResourceSearchResponse_TermFacet) ProtoReflect() protoreflect.Message {
mi := &file_search_proto_msgTypes[11] mi := &file_search_proto_msgTypes[12]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@@ -818,7 +950,7 @@ var file_search_proto_rawDesc = string([]byte{
0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x22, 0x8e, 0x05, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x74, 0x22, 0xc3, 0x06, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65,
0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x6f, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x6f,
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x70, 0x74, 0x69,
@@ -846,93 +978,109 @@ var file_search_proto_rawDesc = string([]byte{
0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x0b, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x0b,
0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65,
0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a,
0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x30, 0x0a, 0x04, 0x53, 0x6f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x0c, 0x71, 0x75,
0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x65, 0x72, 0x79, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b,
0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f,
0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x1a, 0x33, 0x0a, 0x05,
0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c,
0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69,
0x74, 0x1a, 0x5f, 0x0a, 0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
0x79, 0x12, 0x3b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x74, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0b, 0x71, 0x75,
0x38, 0x01, 0x22, 0xea, 0x04, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x30, 0x0a, 0x04, 0x53, 0x6f, 0x72,
0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x1a, 0x33, 0x0a, 0x05, 0x46,
0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x61, 0x63, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20,
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69,
0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74,
0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03, 0x1a, 0x64, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x07, 0x72, 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e,
0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x32, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x51, 0x75, 0x65, 0x72,
0x68, 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
0x6c, 0x48, 0x69, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x63, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52,
0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x05, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x1a, 0x5f, 0x0a, 0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45,
0x43, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x53, 0x63, 0x6f, 0x72, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
0x65, 0x12, 0x41, 0x0a, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xea, 0x04, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x66, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
0x61, 0x63, 0x65, 0x74, 0x1a, 0x8f, 0x01, 0x0a, 0x05, 0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x14, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72,
0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12,
0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72,
0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69,
0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x73,
0x73, 0x69, 0x6e, 0x67, 0x12, 0x40, 0x0a, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x18, 0x04, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52,
0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x1a, 0x35, 0x0a, 0x09, 0x54, 0x65, 0x72, 0x6d, 0x46, 0x61,
0x63, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x60, 0x0a,
0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3c, 0x0a,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75,
0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f,
0x60, 0x0a, 0x15, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x6c, 0x65, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74,
0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x68, 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x48, 0x69, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x65, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09,
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x71, 0x75, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78,
0x73, 0x22, 0x83, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x61,
0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x78, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x41, 0x0a, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x18,
0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x28, 0x03, 0x52, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52,
0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74,
0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x79, 0x52, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x1a, 0x8f, 0x01, 0x0a, 0x05, 0x46, 0x61,
0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x63, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01,
0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74,
0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0xfe, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12,
0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x4b, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
0x72, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x52, 0x07, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x40, 0x0a, 0x05, 0x74, 0x65, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72,
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x46,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x1a, 0x35, 0x0a, 0x09, 0x54,
0x74, 0x73, 0x12, 0x1e, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x65, 0x72, 0x6d, 0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x12, 0x14, 0x0a, 0x05,
0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x74, 0x1a, 0x60, 0x0a, 0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x65, 0x79, 0x12, 0x3c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x0b, 0x32, 0x26, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x65, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x60, 0x0a, 0x15, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x67, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a,
0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x67, 0x65, 0x2f, 0x75, 0x6e, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x6b,
0x72, 0x63, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79,
0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x83, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x62, 0x75, 0x69,
0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64,
0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12,
0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15,
0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52,
0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2a, 0x40, 0x0a, 0x0e,
0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b,
0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x54,
0x45, 0x58, 0x54, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x45, 0x59, 0x57, 0x4f, 0x52, 0x44,
0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x48, 0x52, 0x41, 0x53, 0x45, 0x10, 0x03, 0x32, 0xfe,
0x01, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78,
0x12, 0x4b, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65,
0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53,
0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a,
0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x72, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61,
0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61,
0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0e, 0x52, 0x65,
0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49,
0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e,
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64,
0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42,
0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72,
0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b,
0x67, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x75, 0x6e, 0x69, 0x66, 0x69, 0x65,
0x64, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}) })
var ( var (
@@ -947,53 +1095,58 @@ func file_search_proto_rawDescGZIP() []byte {
return file_search_proto_rawDescData return file_search_proto_rawDescData
} }
var file_search_proto_msgTypes = make([]protoimpl.MessageInfo, 13) var file_search_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_search_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
var file_search_proto_goTypes = []any{ var file_search_proto_goTypes = []any{
(*ResourceStatsRequest)(nil), // 0: resource.ResourceStatsRequest (QueryFieldType)(0), // 0: resource.QueryFieldType
(*ResourceStatsResponse)(nil), // 1: resource.ResourceStatsResponse (*ResourceStatsRequest)(nil), // 1: resource.ResourceStatsRequest
(*ResourceSearchRequest)(nil), // 2: resource.ResourceSearchRequest (*ResourceStatsResponse)(nil), // 2: resource.ResourceStatsResponse
(*ResourceSearchResponse)(nil), // 3: resource.ResourceSearchResponse (*ResourceSearchRequest)(nil), // 3: resource.ResourceSearchRequest
(*RebuildIndexesRequest)(nil), // 4: resource.RebuildIndexesRequest (*ResourceSearchResponse)(nil), // 4: resource.ResourceSearchResponse
(*RebuildIndexesResponse)(nil), // 5: resource.RebuildIndexesResponse (*RebuildIndexesRequest)(nil), // 5: resource.RebuildIndexesRequest
(*ResourceStatsResponse_Stats)(nil), // 6: resource.ResourceStatsResponse.Stats (*RebuildIndexesResponse)(nil), // 6: resource.RebuildIndexesResponse
(*ResourceSearchRequest_Sort)(nil), // 7: resource.ResourceSearchRequest.Sort (*ResourceStatsResponse_Stats)(nil), // 7: resource.ResourceStatsResponse.Stats
(*ResourceSearchRequest_Facet)(nil), // 8: resource.ResourceSearchRequest.Facet (*ResourceSearchRequest_Sort)(nil), // 8: resource.ResourceSearchRequest.Sort
nil, // 9: resource.ResourceSearchRequest.FacetEntry (*ResourceSearchRequest_Facet)(nil), // 9: resource.ResourceSearchRequest.Facet
(*ResourceSearchResponse_Facet)(nil), // 10: resource.ResourceSearchResponse.Facet (*ResourceSearchRequest_QueryField)(nil), // 10: resource.ResourceSearchRequest.QueryField
(*ResourceSearchResponse_TermFacet)(nil), // 11: resource.ResourceSearchResponse.TermFacet nil, // 11: resource.ResourceSearchRequest.FacetEntry
nil, // 12: resource.ResourceSearchResponse.FacetEntry (*ResourceSearchResponse_Facet)(nil), // 12: resource.ResourceSearchResponse.Facet
(*ErrorResult)(nil), // 13: resource.ErrorResult (*ResourceSearchResponse_TermFacet)(nil), // 13: resource.ResourceSearchResponse.TermFacet
(*ListOptions)(nil), // 14: resource.ListOptions nil, // 14: resource.ResourceSearchResponse.FacetEntry
(*ResourceKey)(nil), // 15: resource.ResourceKey (*ErrorResult)(nil), // 15: resource.ErrorResult
(*ResourceTable)(nil), // 16: resource.ResourceTable (*ListOptions)(nil), // 16: resource.ListOptions
(*ResourceKey)(nil), // 17: resource.ResourceKey
(*ResourceTable)(nil), // 18: resource.ResourceTable
} }
var file_search_proto_depIdxs = []int32{ var file_search_proto_depIdxs = []int32{
13, // 0: resource.ResourceStatsResponse.error:type_name -> resource.ErrorResult 15, // 0: resource.ResourceStatsResponse.error:type_name -> resource.ErrorResult
6, // 1: resource.ResourceStatsResponse.stats:type_name -> resource.ResourceStatsResponse.Stats 7, // 1: resource.ResourceStatsResponse.stats:type_name -> resource.ResourceStatsResponse.Stats
14, // 2: resource.ResourceSearchRequest.options:type_name -> resource.ListOptions 16, // 2: resource.ResourceSearchRequest.options:type_name -> resource.ListOptions
15, // 3: resource.ResourceSearchRequest.federated:type_name -> resource.ResourceKey 17, // 3: resource.ResourceSearchRequest.federated:type_name -> resource.ResourceKey
7, // 4: resource.ResourceSearchRequest.sortBy:type_name -> resource.ResourceSearchRequest.Sort 8, // 4: resource.ResourceSearchRequest.sortBy:type_name -> resource.ResourceSearchRequest.Sort
9, // 5: resource.ResourceSearchRequest.facet:type_name -> resource.ResourceSearchRequest.FacetEntry 11, // 5: resource.ResourceSearchRequest.facet:type_name -> resource.ResourceSearchRequest.FacetEntry
13, // 6: resource.ResourceSearchResponse.error:type_name -> resource.ErrorResult 10, // 6: resource.ResourceSearchRequest.query_fields:type_name -> resource.ResourceSearchRequest.QueryField
15, // 7: resource.ResourceSearchResponse.key:type_name -> resource.ResourceKey 15, // 7: resource.ResourceSearchResponse.error:type_name -> resource.ErrorResult
16, // 8: resource.ResourceSearchResponse.results:type_name -> resource.ResourceTable 17, // 8: resource.ResourceSearchResponse.key:type_name -> resource.ResourceKey
12, // 9: resource.ResourceSearchResponse.facet:type_name -> resource.ResourceSearchResponse.FacetEntry 18, // 9: resource.ResourceSearchResponse.results:type_name -> resource.ResourceTable
15, // 10: resource.RebuildIndexesRequest.keys:type_name -> resource.ResourceKey 14, // 10: resource.ResourceSearchResponse.facet:type_name -> resource.ResourceSearchResponse.FacetEntry
13, // 11: resource.RebuildIndexesResponse.error:type_name -> resource.ErrorResult 17, // 11: resource.RebuildIndexesRequest.keys:type_name -> resource.ResourceKey
8, // 12: resource.ResourceSearchRequest.FacetEntry.value:type_name -> resource.ResourceSearchRequest.Facet 15, // 12: resource.RebuildIndexesResponse.error:type_name -> resource.ErrorResult
11, // 13: resource.ResourceSearchResponse.Facet.terms:type_name -> resource.ResourceSearchResponse.TermFacet 0, // 13: resource.ResourceSearchRequest.QueryField.type:type_name -> resource.QueryFieldType
10, // 14: resource.ResourceSearchResponse.FacetEntry.value:type_name -> resource.ResourceSearchResponse.Facet 9, // 14: resource.ResourceSearchRequest.FacetEntry.value:type_name -> resource.ResourceSearchRequest.Facet
2, // 15: resource.ResourceIndex.Search:input_type -> resource.ResourceSearchRequest 13, // 15: resource.ResourceSearchResponse.Facet.terms:type_name -> resource.ResourceSearchResponse.TermFacet
0, // 16: resource.ResourceIndex.GetStats:input_type -> resource.ResourceStatsRequest 12, // 16: resource.ResourceSearchResponse.FacetEntry.value:type_name -> resource.ResourceSearchResponse.Facet
4, // 17: resource.ResourceIndex.RebuildIndexes:input_type -> resource.RebuildIndexesRequest 3, // 17: resource.ResourceIndex.Search:input_type -> resource.ResourceSearchRequest
3, // 18: resource.ResourceIndex.Search:output_type -> resource.ResourceSearchResponse 1, // 18: resource.ResourceIndex.GetStats:input_type -> resource.ResourceStatsRequest
1, // 19: resource.ResourceIndex.GetStats:output_type -> resource.ResourceStatsResponse 5, // 19: resource.ResourceIndex.RebuildIndexes:input_type -> resource.RebuildIndexesRequest
5, // 20: resource.ResourceIndex.RebuildIndexes:output_type -> resource.RebuildIndexesResponse 4, // 20: resource.ResourceIndex.Search:output_type -> resource.ResourceSearchResponse
18, // [18:21] is the sub-list for method output_type 2, // 21: resource.ResourceIndex.GetStats:output_type -> resource.ResourceStatsResponse
15, // [15:18] is the sub-list for method input_type 6, // 22: resource.ResourceIndex.RebuildIndexes:output_type -> resource.RebuildIndexesResponse
15, // [15:15] is the sub-list for extension type_name 20, // [20:23] is the sub-list for method output_type
15, // [15:15] is the sub-list for extension extendee 17, // [17:20] is the sub-list for method input_type
0, // [0:15] is the sub-list for field type_name 17, // [17:17] is the sub-list for extension type_name
17, // [17:17] is the sub-list for extension extendee
0, // [0:17] is the sub-list for field type_name
} }
func init() { file_search_proto_init() } func init() { file_search_proto_init() }
@@ -1007,13 +1160,14 @@ func file_search_proto_init() {
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_search_proto_rawDesc), len(file_search_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_search_proto_rawDesc), len(file_search_proto_rawDesc)),
NumEnums: 0, NumEnums: 1,
NumMessages: 13, NumMessages: 14,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },
GoTypes: file_search_proto_goTypes, GoTypes: file_search_proto_goTypes,
DependencyIndexes: file_search_proto_depIdxs, DependencyIndexes: file_search_proto_depIdxs,
EnumInfos: file_search_proto_enumTypes,
MessageInfos: file_search_proto_msgTypes, MessageInfos: file_search_proto_msgTypes,
}.Build() }.Build()
File_search_proto = out.File File_search_proto = out.File
@@ -31,9 +31,11 @@ const (
// Unlike the ResourceStore, this service can be exposed to clients directly // Unlike the ResourceStore, this service can be exposed to clients directly
// It should be implemented with efficient indexes and does not need read-after-write semantics // It should be implemented with efficient indexes and does not need read-after-write semantics
type ResourceIndexClient interface { type ResourceIndexClient interface {
// Query for documents
Search(ctx context.Context, in *ResourceSearchRequest, opts ...grpc.CallOption) (*ResourceSearchResponse, error) Search(ctx context.Context, in *ResourceSearchRequest, opts ...grpc.CallOption) (*ResourceSearchResponse, error)
// Get the resource stats // Get the resource stats
GetStats(ctx context.Context, in *ResourceStatsRequest, opts ...grpc.CallOption) (*ResourceStatsResponse, error) GetStats(ctx context.Context, in *ResourceStatsRequest, opts ...grpc.CallOption) (*ResourceStatsResponse, error)
// Rebuild the search index
RebuildIndexes(ctx context.Context, in *RebuildIndexesRequest, opts ...grpc.CallOption) (*RebuildIndexesResponse, error) RebuildIndexes(ctx context.Context, in *RebuildIndexesRequest, opts ...grpc.CallOption) (*RebuildIndexesResponse, error)
} }
@@ -82,9 +84,11 @@ func (c *resourceIndexClient) RebuildIndexes(ctx context.Context, in *RebuildInd
// Unlike the ResourceStore, this service can be exposed to clients directly // Unlike the ResourceStore, this service can be exposed to clients directly
// It should be implemented with efficient indexes and does not need read-after-write semantics // It should be implemented with efficient indexes and does not need read-after-write semantics
type ResourceIndexServer interface { type ResourceIndexServer interface {
// Query for documents
Search(context.Context, *ResourceSearchRequest) (*ResourceSearchResponse, error) Search(context.Context, *ResourceSearchRequest) (*ResourceSearchResponse, error)
// Get the resource stats // Get the resource stats
GetStats(context.Context, *ResourceStatsRequest) (*ResourceStatsResponse, error) GetStats(context.Context, *ResourceStatsRequest) (*ResourceStatsResponse, error)
// Rebuild the search index
RebuildIndexes(context.Context, *RebuildIndexesRequest) (*RebuildIndexesResponse, error) RebuildIndexes(context.Context, *RebuildIndexesRequest) (*RebuildIndexesResponse, error)
} }
+60 -34
View File
@@ -81,6 +81,11 @@ type BleveOptions struct {
// Indexes that are not owned by current instance are eligible for cleanup. // Indexes that are not owned by current instance are eligible for cleanup.
// If nil, all indexes are owned by the current instance. // If nil, all indexes are owned by the current instance.
OwnsIndex func(key resource.NamespacedResource) (bool, error) OwnsIndex func(key resource.NamespacedResource) (bool, error)
// ScoringModel defines the scoring model used for the bleve indexes
// Default: index.TFIDFScoring
// Supported values: index.TFIDFScoring and index.BM25Scoring
ScoringModel string
} }
type bleveBackend struct { type bleveBackend struct {
@@ -368,7 +373,7 @@ func (b *bleveBackend) BuildIndex(
attribute.String("reason", indexBuildReason), attribute.String("reason", indexBuildReason),
) )
mapper, err := GetBleveMappings(fields) mapper, err := GetBleveMappings(b.opts.ScoringModel, fields)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -1177,6 +1182,7 @@ func (b *bleveIndex) getIndex(
return b.index, nil return b.index, nil
} }
// nolint:gocyclo
func (b *bleveIndex) toBleveSearchRequest(ctx context.Context, req *resourcepb.ResourceSearchRequest, access authlib.AccessClient) (*bleve.SearchRequest, *resourcepb.ErrorResult) { func (b *bleveIndex) toBleveSearchRequest(ctx context.Context, req *resourcepb.ResourceSearchRequest, access authlib.AccessClient) (*bleve.SearchRequest, *resourcepb.ErrorResult) {
ctx, span := tracer.Start(ctx, "search.bleveIndex.toBleveSearchRequest") ctx, span := tracer.Start(ctx, "search.bleveIndex.toBleveSearchRequest")
defer span.End() defer span.End()
@@ -1235,42 +1241,62 @@ func (b *bleveIndex) toBleveSearchRequest(ctx context.Context, req *resourcepb.R
} }
} }
if len(req.Query) > 1 && strings.Contains(req.Query, "*") { if len(req.Query) > 1 {
// wildcard query is expensive - should be used with caution if strings.Contains(req.Query, "*") {
wildcard := bleve.NewWildcardQuery(req.Query) // wildcard query is expensive - should be used with caution
queries = append(queries, wildcard) wildcard := bleve.NewWildcardQuery(req.Query)
} queries = append(queries, wildcard)
} else {
// When using a
searchrequest.Fields = append(searchrequest.Fields, resource.SEARCH_FIELD_SCORE)
disjoin := bleve.NewDisjunctionQuery()
queries = append(queries, disjoin)
if req.Query != "" && !strings.Contains(req.Query, "*") { queryFields := req.QueryFields
// Add a text query if len(queryFields) == 0 {
searchrequest.Fields = append(searchrequest.Fields, resource.SEARCH_FIELD_SCORE) queryFields = []*resourcepb.ResourceSearchRequest_QueryField{
{
Name: resource.SEARCH_FIELD_TITLE,
Type: resourcepb.QueryFieldType_KEYWORD,
Boost: 10, // exact match -- includes ngrams! If they lived on their own field, we could score them differently
}, {
Name: resource.SEARCH_FIELD_TITLE,
Type: resourcepb.QueryFieldType_TEXT,
Boost: 2, // standard analyzer (with ngrams!)
}, {
Name: resource.SEARCH_FIELD_TITLE_PHRASE,
Type: resourcepb.QueryFieldType_TEXT,
Boost: 5, // standard analyzer
},
}
}
// There are multiple ways to match the query string to documents. The following queries are ordered by priority: for _, field := range queryFields {
switch field.Type {
case resourcepb.QueryFieldType_TEXT, resourcepb.QueryFieldType_DEFAULT:
q := bleve.NewMatchQuery(removeSmallTerms(req.Query)) // removeSmallTerms should be part of the analyzer
q.SetBoost(float64(field.Boost))
q.SetField(field.Name)
q.Analyzer = standard.Name // analyze the text
q.Operator = query.MatchQueryOperatorAnd // all terms must match
disjoin.AddQuery(q)
// Query 1: Match the exact query string case resourcepb.QueryFieldType_KEYWORD:
queryExact := bleve.NewMatchQuery(req.Query) q := bleve.NewMatchQuery(req.Query)
queryExact.SetBoost(10.0) q.SetBoost(float64(field.Boost))
queryExact.SetField(resource.SEARCH_FIELD_TITLE) q.SetField(field.Name)
queryExact.Analyzer = keyword.Name // don't analyze the query input - treat it as a single token q.Analyzer = keyword.Name // don't analyze the query input - treat it as a single token
queryExact.Operator = query.MatchQueryOperatorAnd // This doesn't make a difference for keyword analyzer, we add it just to be explicit. disjoin.AddQuery(q)
searchQuery := bleve.NewDisjunctionQuery(queryExact)
// Query 2: Phrase query with standard analyzer case resourcepb.QueryFieldType_PHRASE:
queryPhrase := bleve.NewMatchPhraseQuery(req.Query) q := bleve.NewMatchPhraseQuery(req.Query)
queryPhrase.SetBoost(5.0) q.SetBoost(float64(field.Boost))
queryPhrase.SetField(resource.SEARCH_FIELD_TITLE) q.SetField(field.Name)
queryPhrase.Analyzer = standard.Name q.Analyzer = standard.Name
searchQuery.AddQuery(queryPhrase) disjoin.AddQuery(q)
}
// Query 3: Match query with standard analyzer }
queryAnalyzed := bleve.NewMatchQuery(removeSmallTerms(req.Query)) }
queryAnalyzed.SetField(resource.SEARCH_FIELD_TITLE)
queryAnalyzed.SetBoost(2.0)
queryAnalyzed.Analyzer = standard.Name
queryAnalyzed.Operator = query.MatchQueryOperatorAnd // Make sure all terms from the query are matched
searchQuery.AddQuery(queryAnalyzed)
queries = append(queries, searchQuery)
} }
switch len(queries) { switch len(queries) {
@@ -1872,7 +1898,7 @@ func (q *permissionScopedQuery) Searcher(ctx context.Context, i index.IndexReade
if err != nil { if err != nil {
return nil, err return nil, err
} }
filteringSearcher := bleveSearch.NewFilteringSearcher(ctx, searcher, func(d *search.DocumentMatch) bool { filteringSearcher := bleveSearch.NewFilteringSearcher(ctx, searcher, func(_ *search.SearchContext, d *search.DocumentMatch) bool {
// The doc ID has the format: <namespace>/<group>/<resourceType>/<name> // The doc ID has the format: <namespace>/<group>/<resourceType>/<name>
// IndexInternalID will be the same as the doc ID when using an in-memory index, but when using a file-based // IndexInternalID will be the same as the doc ID when using an in-memory index, but when using a file-based
// index it becomes a binary encoded number that has some other internal meaning. Using ExternalID() will get the // index it becomes a binary encoded number that has some other internal meaning. Using ExternalID() will get the
@@ -4,6 +4,7 @@ import (
"context" "context"
"testing" "testing"
index "github.com/blevesearch/bleve_index_api"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/storage/unified/resource" "github.com/grafana/grafana/pkg/storage/unified/resource"
@@ -19,6 +20,7 @@ func TestBleveSearchBackend(t *testing.T) {
backend, err := NewBleveBackend(BleveOptions{ backend, err := NewBleveBackend(BleveOptions{
Root: tempDir, Root: tempDir,
FileThreshold: 5, FileThreshold: 5,
ScoringModel: index.BM25Scoring,
}, nil) }, nil)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, backend) require.NotNil(t, backend)
@@ -52,3 +54,32 @@ func TestSearchBackendBenchmark(t *testing.T) {
unitest.BenchmarkSearchBackend(t, backend, opts) unitest.BenchmarkSearchBackend(t, backend, opts)
} }
func BenchmarkScoringModels(b *testing.B) {
models := []string{index.TFIDFScoring, index.BM25Scoring}
for _, model := range models {
b.Run(model, func(b *testing.B) {
tempDir := b.TempDir()
backend, err := NewBleveBackend(BleveOptions{
Root: tempDir,
ScoringModel: model,
}, nil)
require.NoError(b, err)
require.NotNil(b, backend)
b.Cleanup(backend.Stop)
opts := &unitest.BenchmarkOptions{
NumResources: 1000,
Concurrency: 4,
NumNamespaces: 10,
NumGroups: 10,
NumResourceTypes: 10,
}
unitest.BenchmarkSearchBackend(b, backend, opts)
})
}
}
+4 -2
View File
@@ -5,13 +5,15 @@ import (
"github.com/blevesearch/bleve/v2/analysis/analyzer/keyword" "github.com/blevesearch/bleve/v2/analysis/analyzer/keyword"
"github.com/blevesearch/bleve/v2/analysis/analyzer/standard" "github.com/blevesearch/bleve/v2/analysis/analyzer/standard"
"github.com/blevesearch/bleve/v2/mapping" "github.com/blevesearch/bleve/v2/mapping"
"github.com/grafana/grafana/pkg/storage/unified/resource" "github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/grafana/grafana/pkg/storage/unified/resourcepb" "github.com/grafana/grafana/pkg/storage/unified/resourcepb"
) )
func GetBleveMappings(fields resource.SearchableDocumentFields) (mapping.IndexMapping, error) { func GetBleveMappings(scoringModel string, fields resource.SearchableDocumentFields) (mapping.IndexMapping, error) {
mapper := bleve.NewIndexMapping() mapper := bleve.NewIndexMapping()
if scoringModel != "" {
mapper.ScoringModel = scoringModel
}
err := RegisterCustomAnalyzers(mapper) err := RegisterCustomAnalyzers(mapper)
if err != nil { if err != nil {
@@ -13,7 +13,7 @@ import (
) )
func TestDocumentMapping(t *testing.T) { func TestDocumentMapping(t *testing.T) {
mappings, err := search.GetBleveMappings(nil) mappings, err := search.GetBleveMappings("", nil)
require.NoError(t, err) require.NoError(t, err)
data := resource.IndexableDocument{ data := resource.IndexableDocument{
Title: "title", Title: "title",
@@ -7,6 +7,7 @@ import (
"testing" "testing"
"github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2"
index "github.com/blevesearch/bleve_index_api"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/identity"
@@ -258,6 +259,7 @@ func newTestDashboardsIndex(t testing.TB, threshold int64, size int64, writer re
backend, err := search.NewBleveBackend(search.BleveOptions{ backend, err := search.NewBleveBackend(search.BleveOptions{
Root: t.TempDir(), Root: t.TempDir(),
FileThreshold: threshold, // use in-memory for tests FileThreshold: threshold, // use in-memory for tests
ScoringModel: index.BM25Scoring,
}, nil) }, nil)
require.NoError(t, err) require.NoError(t, err)
+3
View File
@@ -14,6 +14,7 @@ import (
"time" "time"
"github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2"
index "github.com/blevesearch/bleve_index_api"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil" "github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -50,6 +51,7 @@ func TestBleveBackend(t *testing.T) {
backend, err := NewBleveBackend(BleveOptions{ backend, err := NewBleveBackend(BleveOptions{
Root: tmpdir, Root: tmpdir,
FileThreshold: 5, // with more than 5 items we create a file on disk FileThreshold: 5, // with more than 5 items we create a file on disk
ScoringModel: index.BM25Scoring,
}, nil) }, nil)
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(backend.Stop) t.Cleanup(backend.Stop)
@@ -773,6 +775,7 @@ func setupBleveBackend(t *testing.T, options ...setupOption) (*bleveBackend, pro
IndexCacheTTL: defaultIndexCacheTTL, IndexCacheTTL: defaultIndexCacheTTL,
Logger: log.NewNopLogger(), Logger: log.NewNopLogger(),
BuildVersion: buildVersion, BuildVersion: buildVersion,
ScoringModel: index.BM25Scoring,
} }
for _, opt := range options { for _, opt := range options {
opt(&opts) opt(&opts)
+1
View File
@@ -46,6 +46,7 @@ func NewSearchOptions(
BuildVersion: cfg.BuildVersion, BuildVersion: cfg.BuildVersion,
OwnsIndex: ownsIndexFn, OwnsIndex: ownsIndexFn,
IndexMinUpdateInterval: cfg.IndexMinUpdateInterval, IndexMinUpdateInterval: cfg.IndexMinUpdateInterval,
ScoringModel: cfg.IndexScoringModel,
}, indexMetrics) }, indexMetrics)
if err != nil { if err != nil {
@@ -6,6 +6,7 @@ import (
"testing" "testing"
"time" "time"
index "github.com/blevesearch/bleve_index_api"
"github.com/go-jose/go-jose/v4/jwt" "github.com/go-jose/go-jose/v4/jwt"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -129,21 +130,28 @@ func TestIntegrationSearchAndStorage(t *testing.T) {
ctx := context.Background() ctx := context.Background()
// Create a new bleve backend scoringModels := []string{index.TFIDFScoring, index.BM25Scoring}
search, err := search.NewBleveBackend(search.BleveOptions{
FileThreshold: 0,
Root: t.TempDir(),
}, nil)
require.NoError(t, err)
require.NotNil(t, search)
t.Cleanup(search.Stop)
// Create a new resource backend for _, model := range scoringModels {
storage, _ := newTestBackend(t, false, 0) t.Run(model, func(t *testing.T) {
require.NotNil(t, storage) // Create a new bleve backend
search, err := search.NewBleveBackend(search.BleveOptions{
FileThreshold: 0,
Root: t.TempDir(),
ScoringModel: model,
}, nil)
require.NoError(t, err)
require.NotNil(t, search)
t.Cleanup(search.Stop)
// Run the shared storage and search tests // Create a new resource backend
unitest.RunTestSearchAndStorage(t, ctx, storage, search) storage, _ := newTestBackend(t, false, 0)
require.NotNil(t, storage)
// Run the shared storage and search tests
unitest.RunTestSearchAndStorage(t, ctx, storage, search)
})
}
} }
func TestClientServer(t *testing.T) { func TestClientServer(t *testing.T) {
@@ -24,6 +24,24 @@ func TestMain(m *testing.M) {
testsuite.Run(m) testsuite.Run(m)
} }
// mockElasticsearchHandler returns a handler that mocks Elasticsearch endpoints.
// It responds to GET / with cluster info (required for datasource initialization)
// and returns 401 Unauthorized for all other requests.
func mockElasticsearchHandler(onRequest func(r *http.Request)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodGet && r.URL.Path == "/":
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"version":{"build_flavor":"default","number":"8.0.0"}}`))
default:
if onRequest != nil {
onRequest(r)
}
w.WriteHeader(http.StatusUnauthorized)
}
}
}
func TestIntegrationElasticsearch(t *testing.T) { func TestIntegrationElasticsearch(t *testing.T) {
testutil.SkipIntegrationTestInShortMode(t) testutil.SkipIntegrationTestInShortMode(t)
@@ -35,9 +53,8 @@ func TestIntegrationElasticsearch(t *testing.T) {
ctx := context.Background() ctx := context.Background()
var outgoingRequest *http.Request var outgoingRequest *http.Request
outgoingServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { outgoingServer := httptest.NewServer(mockElasticsearchHandler(func(r *http.Request) {
outgoingRequest = r outgoingRequest = r
w.WriteHeader(http.StatusUnauthorized)
})) }))
t.Cleanup(outgoingServer.Close) t.Cleanup(outgoingServer.Close)
@@ -209,7 +209,7 @@
"path": "public/plugins/grafana-azure-monitor-datasource/img/azure_monitor_cpu.png" "path": "public/plugins/grafana-azure-monitor-datasource/img/azure_monitor_cpu.png"
} }
], ],
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": [ "keywords": [
"azure", "azure",
@@ -589,7 +589,7 @@
"hasUpdate": false, "hasUpdate": false,
"defaultNavUrl": "/plugins/datagrid/", "defaultNavUrl": "/plugins/datagrid/",
"category": "", "category": "",
"state": "beta", "state": "deprecated",
"signature": "internal", "signature": "internal",
"signatureType": "", "signatureType": "",
"signatureOrg": "", "signatureOrg": "",
@@ -639,7 +639,7 @@
] ]
}, },
"dependencies": { "dependencies": {
"grafanaDependency": "", "grafanaDependency": "\u003e=11.6.0",
"grafanaVersion": "*", "grafanaVersion": "*",
"plugins": [], "plugins": [],
"extensions": { "extensions": {
@@ -880,7 +880,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": null "keywords": null
}, },
@@ -934,7 +934,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": [ "keywords": [
"grafana", "grafana",
@@ -1000,7 +1000,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": null "keywords": null
}, },
@@ -1217,7 +1217,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": null "keywords": null
}, },
@@ -1325,7 +1325,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": null "keywords": null
}, },
@@ -1375,7 +1375,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": null "keywords": null
}, },
@@ -1425,7 +1425,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": null "keywords": null
}, },
@@ -1575,7 +1575,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": null "keywords": null
}, },
@@ -1629,7 +1629,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": [ "keywords": [
"grafana", "grafana",
@@ -1734,7 +1734,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": null "keywords": null
}, },
@@ -2042,7 +2042,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": null "keywords": null
}, },
@@ -2092,7 +2092,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": null "keywords": null
}, },
@@ -2445,7 +2445,7 @@
}, },
"build": {}, "build": {},
"screenshots": null, "screenshots": null,
"version": "12.3.0-pre", "version": "12.4.0-pre",
"updated": "", "updated": "",
"keywords": null "keywords": null
}, },
+39 -19
View File
@@ -97,7 +97,7 @@ func TestIntegrationSearchDevDashboards(t *testing.T) {
require.Equal(t, 16, fileCount, "file count from %s", devenv) require.Equal(t, 16, fileCount, "file count from %s", devenv)
// Helper to call search // Helper to call search
callSearch := func(user apis.User, params string) dashboardV0.SearchResults { callSearch := func(user apis.User, params map[string]string) dashboardV0.SearchResults {
require.NotNil(t, user) require.NotNil(t, user)
ns := user.Identity.GetNamespace() ns := user.Identity.GetNamespace()
cfg := dynamic.ConfigFor(user.NewRestConfig()) cfg := dynamic.ConfigFor(user.NewRestConfig())
@@ -107,17 +107,12 @@ func TestIntegrationSearchDevDashboards(t *testing.T) {
var statusCode int var statusCode int
req := restClient.Get().AbsPath("apis", "dashboard.grafana.app", "v0alpha1", "namespaces", ns, "search"). req := restClient.Get().AbsPath("apis", "dashboard.grafana.app", "v0alpha1", "namespaces", ns, "search").
//Param("explain", "true") // helpful to understand which field made things match
Param("limit", "1000"). Param("limit", "1000").
Param("type", "dashboard") // Only search dashboards Param("type", "dashboard") // Only search dashboards
for kv := range strings.SplitSeq(params, "&") { for k, v := range params {
if kv == "" { req = req.Param(k, v)
continue
}
parts := strings.SplitN(kv, "=", 2)
if len(parts) == 2 {
req = req.Param(parts[0], parts[1])
}
} }
res := req.Do(ctx).StatusCode(&statusCode) res := req.Do(ctx).StatusCode(&statusCode)
require.NoError(t, res.Error()) require.NoError(t, res.Error())
@@ -140,22 +135,47 @@ func TestIntegrationSearchDevDashboards(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
user apis.User user apis.User
params string params map[string]string
}{ }{
{ {
name: "all", name: "all",
user: helper.Org1.Admin, user: helper.Org1.Admin,
params: "", // only dashboards
}, },
{ {
name: "simple-query", name: "query-single-word",
user: helper.Org1.Admin, user: helper.Org1.Admin,
params: "query=stacking", params: map[string]string{
"query": "stacking",
},
}, },
{ {
name: "with-text-panel", name: "query-multiple-words",
user: helper.Org1.Admin, user: helper.Org1.Admin,
params: "field=panel_types&panelType=text", params: map[string]string{
"query": "graph softMin", // must match ALL terms
},
},
{
name: "with-text-panel",
user: helper.Org1.Admin,
params: map[string]string{
"field": "panel_types", // return panel types
"panelType": "text",
},
},
{
name: "title-ngram-prefix",
user: helper.Org1.Admin,
params: map[string]string{
"query": "zer", // should match "Zero Decimals Y Ticks"
},
},
{
name: "title-ngram-middle-word",
user: helper.Org1.Admin,
params: map[string]string{
"query": "decim", // should match "Zero Decimals Y Ticks"
},
}, },
} }
for i, tc := range testCases { for i, tc := range testCases {
@@ -10,7 +10,7 @@
"panel-tests", "panel-tests",
"graph-ng" "graph-ng"
], ],
"score": 0.658 "score": 0.284
}, },
{ {
"resource": "dashboards", "resource": "dashboards",
@@ -21,8 +21,8 @@
"panel-tests", "panel-tests",
"graph-ng" "graph-ng"
], ],
"score": 0.625 "score": 0.269
} }
], ],
"maxScore": 0.658 "maxScore": 0.284
} }
@@ -0,0 +1,17 @@
{
"totalHits": 1,
"hits": [
{
"resource": "dashboards",
"name": "timeseries-soft-limits",
"title": "Panel Tests - Graph NG - softMin/softMax",
"tags": [
"gdev",
"panel-tests",
"graph-ng"
],
"score": 0.024
}
],
"maxScore": 0.024
}
@@ -0,0 +1,17 @@
{
"totalHits": 1,
"hits": [
{
"resource": "dashboards",
"name": "timeseries-y-ticks-zero-decimals",
"title": "Zero Decimals Y Ticks",
"tags": [
"gdev",
"panel-tests",
"graph-ng"
],
"score": 0.35
}
],
"maxScore": 0.35
}
@@ -0,0 +1,17 @@
{
"totalHits": 1,
"hits": [
{
"resource": "dashboards",
"name": "timeseries-y-ticks-zero-decimals",
"title": "Zero Decimals Y Ticks",
"tags": [
"gdev",
"panel-tests",
"graph-ng"
],
"score": 0.35
}
],
"maxScore": 0.35
}
@@ -3912,6 +3912,13 @@
"value" "value"
], ],
"properties": { "properties": {
"properties": {
"description": "Additional properties for multi-props variables",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"selected": { "selected": {
"description": "Whether the option is selected or not", "description": "Whether the option is selected or not",
"type": "boolean" "type": "boolean"
@@ -3939,6 +3939,13 @@
"value" "value"
], ],
"properties": { "properties": {
"properties": {
"description": "Additional properties for multi-props variables",
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"selected": { "selected": {
"description": "Whether the option is selected or not", "description": "Whether the option is selected or not",
"type": "boolean" "type": "boolean"
+6 -3
View File
@@ -92,7 +92,7 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque
}, nil }, nil
} }
url := fmt.Sprintf("%v/v3/projects/%v/metricDescriptors", dsInfo.services[cloudMonitor].url, defaultProject) url := fmt.Sprintf("%s/v3/projects/%s/metricDescriptors", dsInfo.services[cloudMonitor].url, defaultProject)
request, err := http.NewRequest(http.MethodGet, url, nil) request, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -139,6 +139,7 @@ type datasourceInfo struct {
defaultProject string defaultProject string
clientEmail string clientEmail string
tokenUri string tokenUri string
universeDomain string
services map[string]datasourceService services map[string]datasourceService
privateKey string privateKey string
usingImpersonation bool usingImpersonation bool
@@ -150,6 +151,7 @@ type datasourceJSONData struct {
DefaultProject string `json:"defaultProject"` DefaultProject string `json:"defaultProject"`
ClientEmail string `json:"clientEmail"` ClientEmail string `json:"clientEmail"`
TokenURI string `json:"tokenUri"` TokenURI string `json:"tokenUri"`
UniverseDomain string `json:"universeDomain"`
UsingImpersonation bool `json:"usingImpersonation"` UsingImpersonation bool `json:"usingImpersonation"`
ServiceAccountToImpersonate string `json:"serviceAccountToImpersonate"` ServiceAccountToImpersonate string `json:"serviceAccountToImpersonate"`
} }
@@ -179,6 +181,7 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst
defaultProject: jsonData.DefaultProject, defaultProject: jsonData.DefaultProject,
clientEmail: jsonData.ClientEmail, clientEmail: jsonData.ClientEmail,
tokenUri: jsonData.TokenURI, tokenUri: jsonData.TokenURI,
universeDomain: jsonData.UniverseDomain,
usingImpersonation: jsonData.UsingImpersonation, usingImpersonation: jsonData.UsingImpersonation,
serviceAccountToImpersonate: jsonData.ServiceAccountToImpersonate, serviceAccountToImpersonate: jsonData.ServiceAccountToImpersonate,
services: map[string]datasourceService{}, services: map[string]datasourceService{},
@@ -194,13 +197,13 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst
return nil, err return nil, err
} }
for name, info := range routes { for name := range routes {
client, err := newHTTPClient(dsInfo, opts, &httpClientProvider, name) client, err := newHTTPClient(dsInfo, opts, &httpClientProvider, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
dsInfo.services[name] = datasourceService{ dsInfo.services[name] = datasourceService{
url: info.url, url: buildURL(name, dsInfo.universeDomain),
client: client, client: client,
} }
} }
+9 -2
View File
@@ -23,12 +23,12 @@ type routeInfo struct {
var routes = map[string]routeInfo{ var routes = map[string]routeInfo{
cloudMonitor: { cloudMonitor: {
method: "GET", method: "GET",
url: "https://monitoring.googleapis.com", url: "https://monitoring.",
scopes: []string{cloudMonitorScope}, scopes: []string{cloudMonitorScope},
}, },
resourceManager: { resourceManager: {
method: "GET", method: "GET",
url: "https://cloudresourcemanager.googleapis.com", url: "https://cloudresourcemanager.",
scopes: []string{resourceManagerScope}, scopes: []string{resourceManagerScope},
}, },
} }
@@ -68,6 +68,13 @@ func getMiddleware(model *datasourceInfo, routePath string) (httpclient.Middlewa
return tokenprovider.AuthMiddleware(provider), nil return tokenprovider.AuthMiddleware(provider), nil
} }
func buildURL(route string, universeDomain string) string {
if universeDomain == "" {
universeDomain = "googleapis.com"
}
return routes[route].url + universeDomain
}
func newHTTPClient(model *datasourceInfo, opts httpclient.Options, clientProvider *httpclient.Provider, route string) (*http.Client, error) { func newHTTPClient(model *datasourceInfo, opts httpclient.Options, clientProvider *httpclient.Provider, route string) (*http.Client, error) {
m, err := getMiddleware(model, route) m, err := getMiddleware(model, route)
if err != nil { if err != nil {
@@ -111,7 +111,7 @@ func Test_setRequestVariables(t *testing.T) {
im: &fakeInstance{ im: &fakeInstance{
services: map[string]datasourceService{ services: map[string]datasourceService{
cloudMonitor: { cloudMonitor: {
url: routes[cloudMonitor].url, url: buildURL(cloudMonitor, "googleapis.com"),
client: &http.Client{}, client: &http.Client{},
}, },
}, },
@@ -3,8 +3,8 @@ package elasticsearch
import ( import (
"regexp" "regexp"
"github.com/grafana/grafana/pkg/components/simplejson"
es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client" es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
) )
// addDateHistogramAgg adds a date histogram aggregation to the aggregation builder // addDateHistogramAgg adds a date histogram aggregation to the aggregation builder
+7 -3
View File
@@ -16,7 +16,6 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/backend/tracing" "github.com/grafana/grafana-plugin-sdk-go/backend/tracing"
"github.com/grafana/grafana/pkg/services/featuremgmt"
) )
// Used in logging to mark a stage // Used in logging to mark a stage
@@ -35,6 +34,7 @@ type DatasourceInfo struct {
Interval string Interval string
MaxConcurrentShardRequests int64 MaxConcurrentShardRequests int64
IncludeFrozen bool IncludeFrozen bool
ClusterInfo ClusterInfo
} }
type ConfiguredFields struct { type ConfiguredFields struct {
@@ -159,7 +159,7 @@ func (c *baseClientImpl) ExecuteMultisearch(r *MultiSearchRequest) (*MultiSearch
resSpan.End() resSpan.End()
}() }()
improvedParsingEnabled := isFeatureEnabled(c.ctx, featuremgmt.FlagElasticsearchImprovedParsing) improvedParsingEnabled := isFeatureEnabled(c.ctx, "elasticsearchImprovedParsing")
msr, err := c.parser.parseMultiSearchResponse(res.Body, improvedParsingEnabled) msr, err := c.parser.parseMultiSearchResponse(res.Body, improvedParsingEnabled)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -197,7 +197,11 @@ func (c *baseClientImpl) createMultiSearchRequests(searchRequests []*SearchReque
func (c *baseClientImpl) getMultiSearchQueryParameters() string { func (c *baseClientImpl) getMultiSearchQueryParameters() string {
var qs []string var qs []string
qs = append(qs, fmt.Sprintf("max_concurrent_shard_requests=%d", c.ds.MaxConcurrentShardRequests)) // if the build flavor is not serverless, we can use the max concurrent shard requests
// this is because serverless clusters do not support max concurrent shard requests
if !c.ds.ClusterInfo.IsServerless() && c.ds.MaxConcurrentShardRequests > 0 {
qs = append(qs, fmt.Sprintf("max_concurrent_shard_requests=%d", c.ds.MaxConcurrentShardRequests))
}
if c.ds.IncludeFrozen { if c.ds.IncludeFrozen {
qs = append(qs, "ignore_throttled=false") qs = append(qs, "ignore_throttled=false")
+1 -1
View File
@@ -15,7 +15,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
) )
func TestClient_ExecuteMultisearch(t *testing.T) { func TestClient_ExecuteMultisearch(t *testing.T) {
@@ -0,0 +1,51 @@
package es
import (
"encoding/json"
"fmt"
"net/http"
)
type VersionInfo struct {
BuildFlavor string `json:"build_flavor"`
}
// ClusterInfo represents Elasticsearch cluster information returned from the root endpoint.
// It is used to determine cluster capabilities and configuration like whether the cluster is serverless.
type ClusterInfo struct {
Version VersionInfo `json:"version"`
}
const (
BuildFlavorServerless = "serverless"
)
// GetClusterInfo fetches cluster information from the Elasticsearch root endpoint.
// It returns the cluster build flavor which is used to determine if the cluster is serverless.
func GetClusterInfo(httpCli *http.Client, url string) (clusterInfo ClusterInfo, err error) {
resp, err := httpCli.Get(url)
if err != nil {
return ClusterInfo{}, fmt.Errorf("error getting ES cluster info: %w", err)
}
if resp.StatusCode != http.StatusOK {
return ClusterInfo{}, fmt.Errorf("unexpected status code %d getting ES cluster info", resp.StatusCode)
}
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil && err == nil {
err = fmt.Errorf("error closing response body: %w", closeErr)
}
}()
err = json.NewDecoder(resp.Body).Decode(&clusterInfo)
if err != nil {
return ClusterInfo{}, fmt.Errorf("error decoding ES cluster info: %w", err)
}
return clusterInfo, nil
}
func (ci ClusterInfo) IsServerless() bool {
return ci.Version.BuildFlavor == BuildFlavorServerless
}
@@ -0,0 +1,188 @@
package es
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetClusterInfo(t *testing.T) {
t.Run("Should successfully get cluster info", func(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
_, err := rw.Write([]byte(`{
"name": "test-cluster",
"cluster_name": "elasticsearch",
"cluster_uuid": "abc123",
"version": {
"number": "8.0.0",
"build_flavor": "default",
"build_type": "tar",
"build_hash": "abc123",
"build_date": "2023-01-01T00:00:00.000Z",
"build_snapshot": false,
"lucene_version": "9.0.0"
}
}`))
require.NoError(t, err)
}))
t.Cleanup(func() {
ts.Close()
})
clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
require.NoError(t, err)
require.NotNil(t, clusterInfo)
assert.Equal(t, "default", clusterInfo.Version.BuildFlavor)
})
t.Run("Should successfully get serverless cluster info", func(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
_, err := rw.Write([]byte(`{
"name": "serverless-cluster",
"cluster_name": "elasticsearch",
"cluster_uuid": "def456",
"version": {
"number": "8.11.0",
"build_flavor": "serverless",
"build_type": "docker",
"build_hash": "def456",
"build_date": "2023-11-01T00:00:00.000Z",
"build_snapshot": false,
"lucene_version": "9.8.0"
}
}`))
require.NoError(t, err)
}))
t.Cleanup(func() {
ts.Close()
})
clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
require.NoError(t, err)
require.NotNil(t, clusterInfo)
assert.Equal(t, "serverless", clusterInfo.Version.BuildFlavor)
assert.True(t, clusterInfo.IsServerless())
})
t.Run("Should return error when HTTP request fails", func(t *testing.T) {
clusterInfo, err := GetClusterInfo(http.DefaultClient, "http://invalid-url-that-does-not-exist.local:9999")
require.Error(t, err)
require.Equal(t, ClusterInfo{}, clusterInfo)
assert.Contains(t, err.Error(), "error getting ES cluster info")
})
t.Run("Should return error when response body is invalid JSON", func(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
_, err := rw.Write([]byte(`{"invalid json`))
require.NoError(t, err)
}))
t.Cleanup(func() {
ts.Close()
})
clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
require.Error(t, err)
require.Equal(t, ClusterInfo{}, clusterInfo)
assert.Contains(t, err.Error(), "error decoding ES cluster info")
})
t.Run("Should handle empty version object", func(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
_, err := rw.Write([]byte(`{
"name": "test-cluster",
"version": {}
}`))
require.NoError(t, err)
}))
t.Cleanup(func() {
ts.Close()
})
clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
require.NoError(t, err)
require.Equal(t, ClusterInfo{}, clusterInfo)
assert.Equal(t, "", clusterInfo.Version.BuildFlavor)
assert.False(t, clusterInfo.IsServerless())
})
t.Run("Should handle HTTP error status codes", func(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusUnauthorized)
_, err := rw.Write([]byte(`{"error": "Unauthorized"}`))
require.NoError(t, err)
}))
t.Cleanup(func() {
ts.Close()
})
clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
require.Error(t, err)
require.Equal(t, ClusterInfo{}, clusterInfo)
assert.Contains(t, err.Error(), "unexpected status code 401 getting ES cluster info")
})
}
func TestClusterInfo_IsServerless(t *testing.T) {
t.Run("Should return true when build_flavor is serverless", func(t *testing.T) {
clusterInfo := ClusterInfo{
Version: VersionInfo{
BuildFlavor: BuildFlavorServerless,
},
}
assert.True(t, clusterInfo.IsServerless())
})
t.Run("Should return false when build_flavor is default", func(t *testing.T) {
clusterInfo := ClusterInfo{
Version: VersionInfo{
BuildFlavor: "default",
},
}
assert.False(t, clusterInfo.IsServerless())
})
t.Run("Should return false when build_flavor is empty", func(t *testing.T) {
clusterInfo := ClusterInfo{
Version: VersionInfo{
BuildFlavor: "",
},
}
assert.False(t, clusterInfo.IsServerless())
})
t.Run("Should return false when build_flavor is unknown value", func(t *testing.T) {
clusterInfo := ClusterInfo{
Version: VersionInfo{
BuildFlavor: "unknown",
},
}
assert.False(t, clusterInfo.IsServerless())
})
t.Run("should return false when cluster info is empty", func(t *testing.T) {
clusterInfo := ClusterInfo{}
assert.False(t, clusterInfo.IsServerless())
})
}
@@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
) )
func TestSearchRequest(t *testing.T) { func TestSearchRequest(t *testing.T) {
@@ -6,8 +6,8 @@ import (
"strconv" "strconv"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/components/simplejson"
es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client" es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
) )
// processQuery processes a single query and adds it to the multi-search request builder // processQuery processes a single query and adds it to the multi-search request builder
@@ -3,7 +3,7 @@ package elasticsearch
import ( import (
"strconv" "strconv"
"github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
) )
// setFloatPath converts a string value at the specified path to float64 // setFloatPath converts a string value at the specified path to float64
+14
View File
@@ -88,6 +88,14 @@ func newInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins
httpCliOpts.SigV4.Service = "es" httpCliOpts.SigV4.Service = "es"
} }
apiKeyAuth, ok := jsonData["apiKeyAuth"].(bool)
if ok && apiKeyAuth {
apiKey := settings.DecryptedSecureJSONData["apiKey"]
if apiKey != "" {
httpCliOpts.Header.Add("Authorization", "ApiKey "+apiKey)
}
}
httpCli, err := httpClientProvider.New(httpCliOpts) httpCli, err := httpClientProvider.New(httpCliOpts)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -151,6 +159,11 @@ func newInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins
includeFrozen = false includeFrozen = false
} }
clusterInfo, err := es.GetClusterInfo(httpCli, settings.URL)
if err != nil {
return nil, err
}
configuredFields := es.ConfiguredFields{ configuredFields := es.ConfiguredFields{
TimeField: timeField, TimeField: timeField,
LogLevelField: logLevelField, LogLevelField: logLevelField,
@@ -166,6 +179,7 @@ func newInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins
ConfiguredFields: configuredFields, ConfiguredFields: configuredFields,
Interval: interval, Interval: interval,
IncludeFrozen: includeFrozen, IncludeFrozen: includeFrozen,
ClusterInfo: clusterInfo,
} }
return model, nil return model, nil
} }

Some files were not shown because too many files have changed in this diff Show More