Compare commits

...

87 Commits

Author SHA1 Message Date
ismail simsek
e96b5a68c4 Prometheus: Support utf8 metrics and labels in scopes (#99015)
* support utf8 in scopes

* separate the utf8 tests

* remove redundant test after enabling utf8 validation in prometheus
2025-01-16 18:11:44 +02:00
kay delaney
c0b3932168 Graphite: Fix issue where duplicated panels share same query object (#99077) 2025-01-16 16:07:57 +00:00
Brendan O'Handley
1eb45dd0f8 Explore metrics: Do not show otel switch in related metrics tab (#99054)
do not show otel switch in related metrics tab
2025-01-16 10:00:39 -06:00
Ashley Harrison
cff07c9fb6 Chore: Replace rc-time-picker with rc-picker (#99022)
* replace rc-time-picker with rc-picker

* remove unnecessary stack

* remove TimePickerInput

* make props stricter

* fix type assertions

* remove outdated comment

* fix types

* fix clear icon

* fix styling

* fix z-index and 6 tests

* fix remaining unit tests

* betterer results

* styling tweaks

* don't show/hide scrollbars on hover
2025-01-16 15:58:02 +00:00
ismail simsek
106af5d0ee Prometheus: UTF8 support in metrics browser (#98612)
* utf8 metrics for prometheus devenv

* introduce utf8 support

* completions and suggestions

* don't wrap the utf8 label in quotes

* linting

* support utf8 labels and metrics on visual query builder

* lint

* update raw view for utf8 metric syntax

* betterer

* support utf8 metric names in explore metrics

* utf8 support in grouop by

* utf8 support in label break down

* utf8 metric and label support in metric_find_query for label values

* use the same regex to check label values

* no need to escape

* support series endpoint

* support series endpoint

* support series endpoint

* support series endpoint

* fix tests

* fix extracting labels from labelValuesQuery

* show explanation with right syntax

* support utf8 metrics and labels
2025-01-16 17:50:35 +02:00
ismail simsek
029edcb6be Prometheus: Render metrics and labels in explanation with right syntax (#98591)
* utf8 metrics for prometheus devenv

* introduce utf8 support

* completions and suggestions

* don't wrap the utf8 label in quotes

* linting

* support utf8 labels and metrics on visual query builder

* lint

* update raw view for utf8 metric syntax

* betterer

* support utf8 metric names in explore metrics

* utf8 support in grouop by

* utf8 support in label break down

* utf8 metric and label support in metric_find_query for label values

* use the same regex to check label values

* no need to escape

* support series endpoint

* support series endpoint

* support series endpoint

* support series endpoint

* fix tests

* fix extracting labels from labelValuesQuery

* show explanation with right syntax

* betterer
2025-01-16 17:30:29 +02:00
ismail simsek
4386cfc984 Prometheus: Support utf8 metrics and labels in variable editor (#98345)
* utf8 metrics for prometheus devenv

* introduce utf8 support

* completions and suggestions

* don't wrap the utf8 label in quotes

* linting

* support utf8 labels and metrics on visual query builder

* lint

* update raw view for utf8 metric syntax

* betterer

* support utf8 metric names in explore metrics

* utf8 support in grouop by

* utf8 support in label break down

* utf8 metric and label support in metric_find_query for label values

* use the same regex to check label values

* no need to escape

* support series endpoint

* support series endpoint

* support series endpoint

* support series endpoint

* fix tests

* fix extracting labels from labelValuesQuery

* betterer
2025-01-16 17:10:34 +02:00
Ihor Yeromin
019ee9c2d4 Table: Fix inspect drawer disappears unexpectedly (#99025)
fix(table): inspect drawer disappears unexpectedly
2025-01-16 15:58:56 +01:00
ismail simsek
b532df36c4 Explore Metrics: Support prometheus utf8 metrics names and labels (#98285)
* utf8 metrics for prometheus devenv

* introduce utf8 support

* completions and suggestions

* don't wrap the utf8 label in quotes

* linting

* support utf8 labels and metrics on visual query builder

* lint

* update raw view for utf8 metric syntax

* betterer

* support utf8 metric names in explore metrics

* utf8 support in grouop by

* utf8 support in label break down

* support series endpoint

* support series endpoint

* support series endpoint

* Explore metrics: Utf8 support in Explore metrics with OTel experience enabled (#98707)

* betterer

---------

Co-authored-by: Brendan O'Handley <brendan.ohandley@grafana.com>
2025-01-16 16:49:06 +02:00
Sergej-Vlasov
a32eed1d13 TransformationFilter: Include transformation outputs in transformation filtering options (#98323)
* wip: include transformation output as filtering option

* add refId to joinByField transformation

* clean up

* add refId to transformations that create new data frames

* adjust duplicate query removal for filtering options

* refactor transformation input/output subscription effect

* adjust input data frame filtering logic to include transformations as input for debug view

* transformation filter can only filter on output of previous transformation
2025-01-16 14:45:23 +00:00
Sergej-Vlasov
3df1fa86ae setDashboardPanelContext: Allow to add filters from the table with the same key (#99004)
* always add filters for elastic seach ds when filtering table value

* simplify update filter logic and restore behaviour from old arch

* remove unnecessary modifications

* adjust namig
2025-01-16 14:43:46 +00:00
Ryan McKinley
d95484fba6 K8s/Client: Add watch support (#99060)
* add watch client

* add watch client

* add support for selectors

* parse labels

* always send watch

* reuse decoder

* Update public/app/features/apiserver/client.ts

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

* Update public/app/features/apiserver/client.ts

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

* Update public/app/features/apiserver/client.ts

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

---------

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
2025-01-16 17:41:52 +03:00
ismail simsek
31deddafb6 Prometheus: Support utf8 labels and metrics on visual query builder (#98274)
* utf8 metrics for prometheus devenv

* introduce utf8 support

* completions and suggestions

* don't wrap the utf8 label in quotes

* linting

* support utf8 labels and metrics on visual query builder

* lint

* update raw view for utf8 metric syntax

* betterer

* support series endpoint

* support series endpoint

* betterer
2025-01-16 16:24:56 +02:00
ismail simsek
4c0fa629da Prometheus: Support utf8 metric/label/label value suggestions in code mode (#98253)
* utf8 metrics for prometheus devenv

* introduce utf8 support

* completions and suggestions

* don't wrap the utf8 label in quotes

* linting

* support series endpoint
2025-01-16 15:01:51 +01:00
Tom Ratcliffe
862c0ce9b5 Alerting: Fix check for contact point email in IRM essentials page (#98977) 2025-01-16 13:59:32 +00:00
Gabriel MABILLE
70ddf9cb76 Authenticator: Return gRPC errors (#99000) 2025-01-16 14:33:57 +01:00
Georges Chaudy
98e9f3a534 [unistore] wire authlib compile (#99027)
* Wire authz client compile method

* add verb to metric

* remove tipo
2025-01-16 14:11:55 +01:00
Sven Grossmann
4936c53072 chore: upgrade @grafana/lezer-logql and @grafana/monaco-logql (#99076) 2025-01-16 15:08:32 +02:00
Arati R.
a3cdad25a3 Docs: Fix broken link to provisioning instructions in elasticsearch doc page (#98871)
Fix broken link in elasticsearch data source doc page
2025-01-16 13:40:00 +01:00
Karl Persson
2187a66f2b Zanzana: Split up settings into client and server sections (#99066)
* Split up zanzana settings into client and server sections

* Update workspace
2025-01-16 13:39:39 +01:00
Gaurav Soni
ee4016f4bc Docs: add that traces panel works with traceID to show a singular trace (#98451)
* update that traces panel works with traceID to show a singular trace

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

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

---------

Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com>
Co-authored-by: Isabel Matwawana <76437239+imatwawana@users.noreply.github.com>
2025-01-16 13:21:11 +01:00
Kévin Gomez
77f7ab27be Use cog as a cli tool to generate code for kinds v2 schemas (#99044)
* Use cog as a cli tool to generate code for kinds v2 schemas

* Update imports to dashboard v2 package in frontend

* Ensure cog is installed before using it
2025-01-16 13:18:47 +01:00
Yaelle Chaudy
4fcd529d0e Dashboards Versions: Add event to dashboards restore version (#98855)
* Add event to dashboards restore version

* moved changes to scenes instead

* remove last non-scene change

* Move events to centralised file

* remove ? from properties copy pasta

* Add tests
2025-01-16 12:37:45 +01:00
Brendan O'Handley
76a7987427 Explore metrics: Support native histograms (#98894)
* identify native histograms by classic histograms

* use trail to expose ds helper

* identify native histograms for preview panel

* refactor ds helper to initialize all native histograms

* remove await

* add info message for native histograms

* hide button on show examples

* show nh in metric scene by passing check for nh and storing in url for url loads, bookmarks and recent explorations

* add badge for native histograms

* click native histogram examples in info message to see them

* add link for learn more

* close banner on select when selecting native histogram in info banner

* show message for newly selected data sources

* capitalize Native Histogram badge

* prettier

* fix badge ui width

* add padding for badge

* add images, styling and tests for native histogram banner

* move images to img folder

* fix store tests

* run i18n

* fix betterer

* fix betterer with translations

* cannot translate interpolated metric in button text

* Fix import

* do  not indent the > See examples section

* trans component interferes with text with special chars

* update sm text with 4px padding and 16px spacing between images

* do not show banner after closing then changing data sources

* prettier

* Update public/app/features/trails/helpers/MetricDatasourceHelper.ts

Co-authored-by: Nick Richmond <5732000+NWRichmond@users.noreply.github.com>

* Update public/app/features/trails/banners/NativeHistogramBanner.tsx

Co-authored-by: Nick Richmond <5732000+NWRichmond@users.noreply.github.com>

* Update public/app/features/trails/banners/NativeHistogramBanner.tsx

Co-authored-by: Nick Richmond <5732000+NWRichmond@users.noreply.github.com>

* update comments

* remove unnecessary code check

* add rudderstack types

* add close example functionality

* prettier

* add t() for betterer

* prettier

* fix betterer and trans issues

* fix test

---------

Co-authored-by: Nick Richmond <5732000+NWRichmond@users.noreply.github.com>
2025-01-16 03:36:10 +02:00
owensmallwood
7da6e48036 Unified Storage: Remove history stub (#99048)
remove history stub
2025-01-16 03:05:13 +02:00
Matias Chomicki
3641b28e84 Explore: fix logs no results and scanning states (#98605) 2025-01-15 22:08:19 +00:00
Yuri Tseretyan
d025523a8b Alerting: Log reason for taking image. (#99036) 2025-01-15 16:11:38 -05:00
Ryan McKinley
cd46f1ddb9 Search: Remove history query (#99026) 2025-01-15 12:49:47 -06:00
Andreas Christou
f9f341e9c9 Azure: Fix interface conversion (#99034) 2025-01-15 13:28:04 -05:00
Adam Yeats
bab55a4cb8 Elasticsearch: Process ES multisearch JSON response by streaming (#93689)
Co-authored-by: Isabella Siu <isabella.siu@grafana.com>
2025-01-15 12:05:54 -05:00
Matthew Jacobson
fc90a446c6 Alerting: Ensure long-lived repeat alerts keep images after 24h expiry (#98993)
Ensures we retake images after expiration on long-lived repeat alerts.
Otherwise, logs would show "Image not found in database" and notifications
would cease to contain an image after 24h of continuous firing.
2025-01-15 11:45:43 -05:00
Gabriel MABILLE
b6fc695598 ExtJwtClient: Use user namespace for k8s allowed namespace (#93687)
* ExtJwtClient: Use user namespace for k8s allowed namespace

* fix test
2025-01-15 17:38:46 +01:00
Andrej Ocenas
5d45af1110 I18n: Add doc comment for t() (#99024)
* Add comment doc

* Reorder information
2025-01-15 17:31:06 +01:00
owensmallwood
d00592ffa0 Unified Storage: Make all dashboard fields searchable (#98899)
* wip. adding sprinkles fields.

* some refactoring. Works with sprinkles now.

* exclude top level dashboard hit fields from hit "fields"

* adds unit test for DecodeCell helper

* test can search for specific dashboard fields on bleve index

* adds search handler tests for the fields and tests for fields when transforming the search req to a bleve search req

* fix panic when calling fields.Set() with int32

* adds regression test

* remove unneeded method on test mock client

* fix linter issues

* updates dashboard test data for bleve tests

* remove DASHBOARD_LEGACY_ID from bleve_tests

* dont cast twice

* updates test to sort by dashboard_views_last_1_days

* declare excludedFields outside of function

* fixes sorting by dashboard fields - prepends "fields." to any dashboard fields we try to sort by

* uses map for excludedFields

* expects fields to be array-style url param

* change method name

* fixes failing tests - needed to add column type to mocks
2025-01-15 10:23:05 -06:00
William Wernert
8415089534 Alerting: Use correct status code for UID conflict in contact point provisionining API (#98970)
Use correct status code for UID conflict in contact point provisioning API
2025-01-15 11:07:56 -05:00
Misi
125fdc8f21 Auth: Fix AzureAD public client configuration (#99019)
Auth: Add client auth none as default to ensure public clients can be configured on the UI
2025-01-15 16:49:58 +01:00
Jack Baldry
f142f12887 Ignore spelling errors in bold formatting (#99006) 2025-01-15 15:08:20 +00:00
Jean-Philippe Quéméner
c03586dfe8 fix(unified-storage): initialize blob storage when using grpc (#99020) 2025-01-15 16:04:41 +01:00
Jean-Philippe Quéméner
38927f0719 fix(indexer): reduce cardinality of indexer metric (#99018) 2025-01-15 16:01:31 +01:00
beejeebus
a290db6a7e Ensure we delete playlists when deleting an org, fixes #98736 (#98771) 2025-01-15 08:13:56 -06:00
Josh Hunt
e3e580edfa Chore: Prevent direct path imports from workspace grafana packages (#98940)
* restrict imports from @grafana/ui/src paths

* prevent path imports from all grafana packages

* just run on ui, runtime, data packages

* update
2025-01-15 15:47:44 +02:00
antonio
1dcff0a71f docs/alerting/tutorials/part4 (#98942)
* docs/alerting/tutorials/part4

* interactives

* refs

* links

* formatting

* links2

* alt text

* steps, new alert configuration sections

* feedback

* bypass spellcheck

* Update docs/sources/tutorials/alerting-get-started-pt4/index.md

Co-authored-by: brendamuir <100768211+brendamuir@users.noreply.github.com>

* Update docs/sources/tutorials/alerting-get-started-pt4/index.md

Co-authored-by: brendamuir <100768211+brendamuir@users.noreply.github.com>

* Update docs/sources/tutorials/alerting-get-started-pt4/index.md

Co-authored-by: brendamuir <100768211+brendamuir@users.noreply.github.com>

* Update docs/sources/tutorials/alerting-get-started-pt4/index.md

Co-authored-by: brendamuir <100768211+brendamuir@users.noreply.github.com>

* added note and minor formatting

* made links killercoda friendly

* all pretty no pity

---------

Co-authored-by: Jack Baldry <jack.baldry@grafana.com>
Co-authored-by: brendamuir <100768211+brendamuir@users.noreply.github.com>
2025-01-15 14:35:51 +01:00
Syerikjan Kh
dfe0712955 feat: pass gcom sso_api_token to repo created from install command (#98973)
* feat: pass gcom sso_api_token to repo created from install command

* fix

* fix: extract gcom section to a func

* Update pkg/cmd/grafana-cli/utils/command_line.go

Co-authored-by: Giuseppe Guerra <giuseppe.guerra@grafana.com>

* fix: only set gcom token when the request is to GCOM

---------

Co-authored-by: Giuseppe Guerra <giuseppe.guerra@grafana.com>
2025-01-15 08:15:18 -05:00
ismail simsek
79fc26ea87 Chore: Remove prometheus replace directive (#98966)
* remove replace directive

* have an adapter

* go mod tidy

* make update-workspace

* make update-workspace
2025-01-15 14:04:14 +01:00
Santiago
ea6cb8f139 Alerting: Panic when rule being evaluated has unexpected key (#99002) 2025-01-15 14:59:50 +02:00
Konrad Lalik
5aeaccadff Alerting: Add read-only GMA rules to the new list view (#98116)
* Reuse prom groups generator between GMA, external DS and list view

* Improve generators, add initial support for GMA in grouped view components

* Improve handling of GMA rules

* Split componentes into files

* Improve error handling, simplify groups grouping

* Extract grafana rules component

* Reset yarn.lock

* Reset yarn.lock 2

* Update filters, adjust file names, add folder display name to GMA rules

* Re-enable filtering for cloud rules

* Rename AlertRuleLoader

* Add missing translations, fix lint errors

* Remove unused imports, update translations

* Fix responses in BE tests

* Update backend tests

* Update integration test

* Tidy up group page size constants

* Add error throwing to getGroups endpoint to prevent grafana usage

* Refactor FilterView to remove exhaustive check

* Refactor common props for grafana rule rendering

* Unify identifiers' discriminators, add comments, minor refactor

* Update translations

* Remove unnecessary prev page condition, add a few explanations

---------

Co-authored-by: fayzal-g <fayzal.ghantiwala@grafana.com>
Co-authored-by: Tom Ratcliffe <tom.ratcliffe@grafana.com>
2025-01-15 11:36:32 +01:00
Igor Suleymanov
7f04f66137 Allow listing Dashboards through k8s APIs (#98997)
What

This PR changes the dashboard authorizer logic to no longer require
requester to be the super admin user in order to list dashboards using
k8s APIs.

Why

We no longer need this precondition - dashboards should be listable
based on the regular authorizer logic.

Signed-off-by: Igor Suleymanov <igor.suleymanov@grafana.com>
2025-01-15 11:50:28 +02:00
Leonor Oliveira
32790c6918 Remove dependency on dashboards table to get library element and library element connection (#98941)
* Remove dependency on dashboards table to get library element

* WIP

* Don't use dashboard id when getting library connections

* Lint

* Don;t include folder_uid in library element
2025-01-15 10:32:24 +01:00
Matias Chomicki
bbade6b011 Shard query splitting: run queries through time query splitting (#98126)
* Query splitting: add skipPartialUpdates option

* Shard query splitting: run queries through time splitting

* Query splitting: delegate error retry to shard splitting

* Shard query splitting: update unit tests

* Shard query splitting: test combined requests

* Formatting

* Query splitting: test new options

* Query splitting: update assertion

* Formatting
2025-01-15 10:26:23 +01:00
Jack Baldry
a6eb8abd05 Simplify language and lint for style (#98964) 2025-01-15 08:36:31 +00:00
Karl Persson
3f71a72c1a Authz: Remove "wrapper" interface and only check feature toggle for grpc mode (#98933)
* Remove "wrapper" interface and only check feature toggle for grpc and cloud mode

* Only set name for update checks

* Set dashboard permissions for admin user
2025-01-15 09:23:56 +01:00
Ryan McKinley
0d302a161a BackendSrv: Support streaming chunked responses (#98691) 2025-01-15 10:01:22 +03:00
Ryan McKinley
0fce8799eb Chore: go.mod cleanup (#98987) 2025-01-15 08:35:22 +03:00
Stephanie Hingtgen
2a08c9ed82 K8s Dashboards: Fix creating a dashboard inside a folder (#98982) 2025-01-15 07:15:58 +02:00
Stephanie Hingtgen
dbfc412ed8 K8s: Fix dashboard search (#98989) 2025-01-14 18:45:27 -05:00
Yuri Tseretyan
e15fc984c3 Alerting: Deprecate Alertmanager config and receivers APIs (#98918) 2025-01-14 17:22:01 -05:00
jackyin
69da0bb22c FieldReducers: Fix median (#98184)
Co-authored-by: Kristina Durivage <kristina.durivage@grafana.com>
Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
2025-01-14 23:38:34 +02:00
Piotr Jamróz
c5111b8132 Explore: Add missing translations (#98984)
Add missing translation
2025-01-14 21:55:58 +01:00
Scott Lepper
3b8477dcda Search: fix uid/name filter (#98981)
[search] fix uid/name filter
2025-01-14 15:48:14 -05:00
Isabel Matwawana
feb334cdbb Docs: update broken Play link (#98985) 2025-01-14 15:47:07 -05:00
Piotr Jamróz
f7e0710f53 Explore: Limit number of columns shown in Explore table (#98726) 2025-01-14 17:01:34 +01:00
Edvard Falkskär
e38bab43db Remove console.log from correlations service (#98969) 2025-01-14 16:09:32 +01:00
Gábor Farkas
1ff4053f03 go.mod: updated grafana-plugin-sdk-go from v0.261.0 to v0.262.0 (#98930) 2025-01-14 16:06:41 +01:00
Karl Persson
ce0d986673 Zanzana: Use cache for both streamed and non-stream version of list objects (#98882)
* Add prefix constants and use string builders / string concatinations

* Use cache for both streamed and non-stream versions of list objects

* Remove unused constants
2025-01-14 16:00:59 +01:00
Selene
7151ea6abc Codegen: Generate Golang code using cog (#98812)
* Use cog for Go types

* Delete old generation code

* Fix plugins generation

* workspaces update

* Update datasources with new generated code

* More fixes

* Update swagger and openapi specs

* Fixes

* More files...

* Update workspace

* More fixes...

* Remove unused functions
2025-01-14 15:58:38 +01:00
renovate[bot]
b96a752db3 Update dependency rc-tree to v5.13.0 (#98959)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 14:36:58 +00:00
Haris Rozajac
125a11ca99 Dashboard: Fix snapshots for the old arch (#98902)
Fix snapshots for the old arch
2025-01-14 07:33:29 -07:00
Jack Baldry
3884c0e896 Replace codespell with Vale spelling check (#98406) 2025-01-14 14:08:01 +00:00
Alexander Zobnin
037570b9c8 Chore: Add vscode config for zanzana server (#98957) 2025-01-14 15:01:10 +01:00
Leonor Oliveira
7c87ff1b84 Folders: Use folder service to count library panels (#98802)
* Use folder service to count library panels

---------

Co-authored-by: suntala <arati.rana@grafana.com>
2025-01-14 14:58:36 +01:00
renovate[bot]
2594b4f7af Update scenes to v5.37.0 (#98958)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 15:42:21 +02:00
renovate[bot]
0032e839ce Update dependency rc-tooltip to v6.4.0 (#98952)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 13:17:49 +00:00
Ashley Harrison
6b5146651f Select/Combobox: Tweak menu styling so hover states don't overflow (#98944)
tweak menu styling so hover states don't overflow
2025-01-14 13:16:42 +00:00
Pepe Cano
7771768363 Alerting docs: clarify data source-managed rules for Prometheus (#98378)
* Clarify DS managed rules support only the creation of Mimir and Loki rules

* additional copy changes

* Extend `Manage alerts via Alerting UI` description

* fix capital letter

* further details for `Manage alerts via Alerting UI`
2025-01-14 13:34:02 +01:00
renovate[bot]
0b5b21548b Update dependency rc-cascader to v3.33.0 (#98949)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 14:19:52 +02:00
Will Browne
70fb7b9545 Plugins: Remove bundled plugins feature (#96490)
* remove bundled plugins

* Add echo to command

Co-authored-by: Andres Martinez Gotor <andres.martinez@grafana.com>

* remove folder entirely

* remove folder from CODEOWNERS

---------

Co-authored-by: Andres Martinez Gotor <andres.martinez@grafana.com>
2025-01-14 12:04:01 +00:00
renovate[bot]
ff032a61d5 Update dependency postcss to v8.5.1 (#98945)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 11:44:11 +00:00
Jack Baldry
5570a7e42e Fix broken link (#98947) 2025-01-14 11:38:32 +00:00
Edvard Falkskär
e45eb95812 Correlations: Expose correlations logic through correlations service (#98124)
* Correlations: Expose correlations logic through correlations service

* Resolve remaining todos

* Marked new items as @alpha
2025-01-14 13:27:13 +02:00
renovate[bot]
33a91f22c0 Update dependency postcss to v8.5.0 (#98943)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 13:22:57 +02:00
renovate[bot]
dbd3bb7667 Update dependency papaparse to v5.5.1 (#98939)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 11:01:57 +00:00
Ashley Harrison
740cd22fe5 Query Library: Add feature toggle for query library in dashboards (#98938)
add feature toggle for query library in dashboards
2025-01-14 11:01:15 +00:00
renovate[bot]
64617886d9 Update dependency sass to v1.83.4 (#98936)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 10:30:40 +00:00
renovate[bot]
1281b0f094 Update dependency @types/node to v22.10.6 (#98934)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 12:09:54 +02:00
renovate[bot]
5c49b6cf73 Update dependency knip to v5.42.0 (#98892)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 09:46:18 +00:00
Josh Hunt
65f5a176a8 Chore: Import Combobox from main grafana-ui package (#98897) 2025-01-14 09:35:27 +00:00
Mariell Hoversholm
36d6fad421 Feat: Setup the App SDK logging module (#98927)
* Feat: Setup the App SDK logging module

* Build: Depend on logging pkg and update App SDK

* Chore: Assign owner to logging module
2025-01-14 10:34:57 +01:00
444 changed files with 14590 additions and 9509 deletions

View File

@@ -86,6 +86,10 @@ module.exports = [
importNames: ['Layout', 'HorizontalGroup', 'VerticalGroup'],
message: 'Use Stack component instead.',
},
{
group: ['@grafana/ui/src/*', '@grafana/runtime/src/*', '@grafana/data/src/*'],
message: 'Import from the public export instead.',
},
],
},
],

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,12 @@ $(BRA): $(BINGO_DIR)/bra.mod
@echo "(re)installing $(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=bra.mod -o=$(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8 "github.com/unknwon/bra"
COG := $(GOBIN)/cog-v0.0.15
$(COG): $(BINGO_DIR)/cog.mod
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
@echo "(re)installing $(GOBIN)/cog-v0.0.15"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=cog.mod -o=$(GOBIN)/cog-v0.0.15 "github.com/grafana/cog/cmd/cli"
CUE := $(GOBIN)/cue-v0.5.0
$(CUE): $(BINGO_DIR)/cue.mod
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.

5
.bingo/cog.mod Normal file
View File

@@ -0,0 +1,5 @@
module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT
go 1.23.4
require github.com/grafana/cog v0.0.15 // cmd/cli

View File

@@ -1,11 +1,10 @@
cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 h1:R5wwEcbEZSBmeyg91MJZTxfd7WpBo2jPof3AYjRbxwY=
cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg=
cuelang.org/go v0.11.1 h1:pV+49MX1mmvDm8Qh3Za3M786cty8VKPWzQ1Ho4gZRP0=
cuelang.org/go v0.11.1/go.mod h1:PBY6XvPUswPPJ2inpvUozP9mebDVTXaeehQikhZPBz0=
cuelang.org/go v0.11.0 h1:2af2nhipqlUHtXk2dtOP5xnMm1ObGvKqIsJUJL1sRE4=
cuelang.org/go v0.11.0/go.mod h1:PBY6XvPUswPPJ2inpvUozP9mebDVTXaeehQikhZPBz0=
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY=
github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
@@ -16,18 +15,16 @@ github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d h1:hrXbGJ5jgp6yNITzs5o+zXq0V5yT3siNJ+uM8LGwWKk=
github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s=
github.com/grafana/cog v0.0.5 h1:BCa+10i3KvV+KMSQuxlN1DS9cZEwN+EAFc7ZmXqHxQE=
github.com/grafana/cog v0.0.5/go.mod h1:lzetOuhGUl/JaSACiJoHvBokf9/fS6PEFaWZvnQu2vs=
github.com/grafana/cog v0.0.14 h1:sBK89oSu9BK4S9l3G9ewVJnGYnNQJTHFBC/01DZDRZs=
github.com/grafana/cog v0.0.14/go.mod h1:HwJbc60fZ+viayROClLGdDwO5w/JjBOpO9wjGnAfMLc=
github.com/grafana/cog v0.0.15 h1:e2pMY+Hf2nS22HcKJuguEzl0BVmV9DSINwCfWt+dFZQ=
github.com/grafana/cog v0.0.15/go.mod h1:jrS9indvWuDs60RHEZpLaAkmZdgyoLKMOEUT0jiB1t0=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -35,18 +32,12 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
@@ -61,36 +52,35 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d h1:HWfigq7lB31IeJL8iy7jkUmU/PG1Sr8jVGhS749dbUA=
github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY=
github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -10,6 +10,8 @@ fi
BRA="${GOBIN}/bra-v0.0.0-20200517080246-1e3013ecaff8"
COG="${GOBIN}/cog-v0.0.15"
CUE="${GOBIN}/cue-v0.5.0"
DRONE="${GOBIN}/drone-v1.5.0"

View File

@@ -1273,11 +1273,6 @@ steps:
depends_on: []
image: node:22.11.0-alpine
name: yarn-install
- commands:
- pip3 install codespell
- codespell -I docs/.codespellignore docs/
image: python:3.8
name: codespell
- commands:
- yarn run prettier:checkDocs
depends_on:
@@ -1635,11 +1630,6 @@ steps:
depends_on: []
image: node:22.11.0-alpine
name: yarn-install
- commands:
- pip3 install codespell
- codespell -I docs/.codespellignore docs/
image: python:3.8
name: codespell
- commands:
- yarn run prettier:checkDocs
depends_on:
@@ -5579,6 +5569,6 @@ kind: secret
name: gcr_credentials
---
kind: signature
hmac: bdf4cd3767ad13e5a8d7221731676e8ee1991dae0da9ae2cee92087d5c660ece
hmac: be82e983dfa15f85f82935674ec109056f519ff99c7ab1f4be71f9ce62b571a7
...

13
.github/CODEOWNERS vendored
View File

@@ -32,10 +32,10 @@
/devenv/README.md @grafana/docs-grafana
# START Technical documentation
/.vale.ini @grafana/docs-tooling
# `make docs` procedure and related workflows are owned @grafana/docs-tooling. Slack #docs.
/docs/ @grafana/docs-tooling
/docs/.codespellignore @grafana/docs-tooling
/docs/sources/ @irenerl24
/docs/sources/alerting/ @brendamuir
@@ -422,8 +422,6 @@
/packages/grafana-ui/src/graveyard/TimeSeries/ @grafana/dataviz-squad
/packages/grafana-ui/src/utils/storybook/ @grafana/plugins-platform-frontend
/plugins-bundled/ @grafana/plugins-platform-frontend
# root files, mostly frontend
/.browserslistrc @grafana/frontend-ops
/package.json @grafana/frontend-ops
@@ -759,6 +757,7 @@ embed.go @grafana/grafana-as-code
/.github/workflows/commands.yml @torkelo
/.github/workflows/community-release.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/detect-breaking-changes-* @grafana/plugins-platform-frontend
/.github/workflows/documentation-ci.yml @grafana/docs-tooling
/.github/workflows/doc-validator.yml @grafana/docs-tooling
/.github/workflows/deploy-pr-preview.yml @grafana/docs-tooling
/.github/workflows/epic-add-to-platform-ux-parent-project.yml @meanmina
@@ -781,10 +780,10 @@ embed.go @grafana/grafana-as-code
/.github/workflows/stale.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/update-changelog.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/update-make-docs.yml @grafana/docs-tooling
/.github/workflows/scripts/kinds/verify-kinds.go @grafana/platform-cat
/.github/workflows/publish-kinds-next.yml @grafana/platform-cat
/.github/workflows/publish-kinds-release.yml @grafana/platform-cat
/.github/workflows/verify-kinds.yml @grafana/platform-cat
/.github/workflows/scripts/kinds/verify-kinds.go @grafana/platform-monitoring
/.github/workflows/publish-kinds-next.yml @grafana/platform-monitoring
/.github/workflows/publish-kinds-release.yml @grafana/platform-monitoring
/.github/workflows/verify-kinds.yml @grafana/platform-monitoring
/.github/workflows/dashboards-issue-add-label.yml @grafana/dashboards-squad
/.github/workflows/ephemeral-instances-pr-comment.yml @grafana/grafana-backend-services-squad
/.github/workflows/create-security-patch-from-security-mirror.yml @grafana/grafana-developer-enablement-squad

View File

@@ -14,7 +14,6 @@
"public/**/*",
"packages/**/*",
"e2e/**/*",
"plugins-bundled/**/*",
"scripts/build/release-packages.sh",
"scripts/circle-release-next-packages.sh",
"scripts/ci-frontend-metrics.sh",
@@ -437,4 +436,4 @@
"action": "updateLabel",
"addLabel": "area/panel/table"
}
]
]

View File

@@ -19,7 +19,7 @@
"nx"
],
includePaths: ["package.json", "packages/**", "public/app/plugins/**"],
ignorePaths: ["emails/**", "plugins-bundled/**", "**/mocks/**"],
ignorePaths: ["emails/**", "**/mocks/**"],
labels: ["area/frontend", "dependencies", "no-changelog"],
postUpdateOptions: ["yarnDedupeHighest"],
packageRules: [

18
.github/workflows/documentation-ci.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Documentation CI
on:
pull_request:
paths: ["docs/sources/**"]
workflow_dispatch:
jobs:
vale:
runs-on: ubuntu-latest
container:
image: grafana/vale:latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: grafana/writers-toolkit/vale-action@13205961f20ad13843505a9b84fdf032f911a3f4 # vale-action/v1.1.0
with:
filter: '.Name in ["Grafana.WordList", "Grafana.Spelling", "Grafana.ProductPossessives"]'
token: ${{ secrets.GITHUB_TOKEN }}

5
.vale.ini Normal file
View File

@@ -0,0 +1,5 @@
MinAlertLevel = warning
[*]
BasedOnStyles = Grafana
TokenIgnores = (<http[^\n]+>+?), \*\*[^\n]+\*\*

19
.vscode/launch.json vendored
View File

@@ -9,12 +9,7 @@
"program": "${workspaceFolder}/pkg/cmd/grafana/",
"env": {},
"cwd": "${workspaceFolder}",
"args": [
"server",
"--homepath", "${workspaceFolder}",
"--packaging", "dev",
"cfg:app_mode=development",
]
"args": ["server", "--homepath", "${workspaceFolder}", "--packaging", "dev", "cfg:app_mode=development"]
},
{
"name": "Attach to Test Process",
@@ -23,7 +18,7 @@
"mode": "remote",
"host": "127.0.0.1",
"port": 50480,
"apiVersion": 2,
"apiVersion": 2
},
{
"name": "Run API Server (testdata)",
@@ -72,6 +67,16 @@
"cwd": "${workspaceFolder}",
"args": ["server", "target", "--homepath", "${workspaceFolder}", "--packaging", "dev"]
},
{
"name": "Run Authz server",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/pkg/cmd/grafana/",
"env": { "GF_DEFAULT_TARGET": "zanzana-server", "GF_SERVER_HTTP_PORT": "3001" },
"cwd": "${workspaceFolder}",
"args": ["server", "target", "--homepath", "${workspaceFolder}", "--packaging", "dev"]
},
{
"name": "Attach to Chrome",
"port": 9222,

View File

@@ -13,10 +13,6 @@ packageExtensions:
doctrine@3.0.0:
dependencies:
assert: 2.0.0
rc-time-picker@3.7.3:
peerDependencies:
react: 17.0.1
react-dom: 17.0.1
rc-trigger@2.6.5:
peerDependencies:
react: 17.0.1

View File

@@ -16,7 +16,6 @@ WORKDIR /tmp/grafana
COPY package.json project.json nx.json yarn.lock .yarnrc.yml ./
COPY .yarn .yarn
COPY packages packages
COPY plugins-bundled plugins-bundled
COPY public public
COPY LICENSE ./
COPY conf/defaults.ini ./conf/defaults.ini

View File

@@ -149,7 +149,7 @@ gen-cue: ## Do all CUE/Thema code generation
.PHONY: gen-cuev2
gen-cuev2: ## Do all CUE code generation
@echo "generate code from .cue files (v2)"
go generate ./kindsv2/gen.go
@$(MAKE) -C ./kindsv2 all
.PHONY: gen-feature-toggles
gen-feature-toggles:
@@ -211,7 +211,6 @@ build-cli: ## Build Grafana CLI application.
build-js: ## Build frontend assets.
@echo "build frontend"
yarn run build
yarn run plugins:build-bundled
PLUGIN_ID ?=

View File

@@ -6,7 +6,7 @@ replace github.com/grafana/grafana => ../../..
require (
github.com/grafana/grafana v0.0.0-00010101000000-000000000000
github.com/grafana/grafana-app-sdk v0.23.1
github.com/grafana/grafana-app-sdk v0.29.0
k8s.io/apimachinery v0.32.0
k8s.io/apiserver v0.32.0
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f

View File

@@ -71,8 +71,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grafana/grafana-app-sdk v0.23.1 h1:BRpUG0bA0oVxjthkmO2thuJBo3nbjaRSSmZJHw+mA8I=
github.com/grafana/grafana-app-sdk v0.23.1/go.mod h1:KzgPnTJfMeckGmMctv6CJb8Jr/o/5rwARDyjXoeR0Fc=
github.com/grafana/grafana-app-sdk v0.29.0 h1:LMSm/+0LOBPd13fe1bs/4sKJmuLiixYUX9T0oqDqp4I=
github.com/grafana/grafana-app-sdk v0.29.0/go.mod h1:XLt308EmK6kvqPlzjUyXxbwZKEk2vur/eiypUNDay5I=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da h1:2E3c/I3ayAy4Z1GwIPqXNZcpUccRapE1aBXA1ho4g7o=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da/go.mod h1:p09fvU5ujNL/Ig8HB7g4f+S0zyYbQq3x/f0jA4ujVOM=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=

View File

@@ -1,9 +1,9 @@
module github.com/grafana/grafana/apps/investigation
go 1.23.1
go 1.23.4
require (
github.com/grafana/grafana-app-sdk v0.23.1
github.com/grafana/grafana-app-sdk v0.29.0
k8s.io/apimachinery v0.32.0
k8s.io/klog/v2 v2.130.1
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f
@@ -29,7 +29,9 @@ require (
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grafana/grafana-app-sdk/logging v0.29.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
@@ -61,10 +63,12 @@ require (
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.29.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241216192217-9240e9c98484 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect

View File

@@ -29,6 +29,8 @@ github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -43,10 +45,14 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grafana/grafana-app-sdk v0.23.1 h1:BRpUG0bA0oVxjthkmO2thuJBo3nbjaRSSmZJHw+mA8I=
github.com/grafana/grafana-app-sdk v0.23.1/go.mod h1:KzgPnTJfMeckGmMctv6CJb8Jr/o/5rwARDyjXoeR0Fc=
github.com/grafana/grafana-app-sdk v0.29.0 h1:LMSm/+0LOBPd13fe1bs/4sKJmuLiixYUX9T0oqDqp4I=
github.com/grafana/grafana-app-sdk v0.29.0/go.mod h1:XLt308EmK6kvqPlzjUyXxbwZKEk2vur/eiypUNDay5I=
github.com/grafana/grafana-app-sdk/logging v0.29.0 h1:mgbXaAf33aFwqwGVeaX30l8rkeAJH0iACgX5Rn6YkN4=
github.com/grafana/grafana-app-sdk/logging v0.29.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -81,6 +87,10 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -150,6 +160,8 @@ golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbht
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -167,6 +179,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -1,9 +1,9 @@
module github.com/grafana/grafana/apps/playlist
go 1.23.1
go 1.23.4
require (
github.com/grafana/grafana-app-sdk v0.23.1
github.com/grafana/grafana-app-sdk v0.29.0
k8s.io/apimachinery v0.32.0
k8s.io/client-go v0.32.0
k8s.io/klog/v2 v2.130.1
@@ -30,7 +30,9 @@ require (
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grafana/grafana-app-sdk/logging v0.29.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
@@ -62,10 +64,12 @@ require (
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.29.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241216192217-9240e9c98484 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect

View File

@@ -29,6 +29,8 @@ github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -43,10 +45,14 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grafana/grafana-app-sdk v0.23.1 h1:BRpUG0bA0oVxjthkmO2thuJBo3nbjaRSSmZJHw+mA8I=
github.com/grafana/grafana-app-sdk v0.23.1/go.mod h1:KzgPnTJfMeckGmMctv6CJb8Jr/o/5rwARDyjXoeR0Fc=
github.com/grafana/grafana-app-sdk v0.29.0 h1:LMSm/+0LOBPd13fe1bs/4sKJmuLiixYUX9T0oqDqp4I=
github.com/grafana/grafana-app-sdk v0.29.0/go.mod h1:XLt308EmK6kvqPlzjUyXxbwZKEk2vur/eiypUNDay5I=
github.com/grafana/grafana-app-sdk/logging v0.29.0 h1:mgbXaAf33aFwqwGVeaX30l8rkeAJH0iACgX5Rn6YkN4=
github.com/grafana/grafana-app-sdk/logging v0.29.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -81,6 +87,10 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -150,6 +160,8 @@ golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbht
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -167,6 +179,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -60,6 +60,14 @@ func main() {
label: "label.with.spaß",
getNextValue: staticList([]string{"this_is_fun"}),
},
{
label: "instance",
getNextValue: staticList([]string{"instance"}),
},
{
label: "job",
getNextValue: staticList([]string{"job"}),
},
{
label: "site",
getNextValue: staticList([]string{"LA-EPI"}),
@@ -85,6 +93,12 @@ func main() {
Help: "a metric with utf8 labels",
}, dimensions)
target_info := promauto.NewGauge(prometheus.GaugeOpts{
Name: "target_info",
Help: "an info metric model for otel",
ConstLabels: map[string]string{"job": "job", "instance": "instance", "resource 1": "1", "resource 2": "2", "resource ę": "e", "deployment_environment": "prod"},
})
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
@@ -101,6 +115,7 @@ func main() {
utf8Metric.WithLabelValues(labels...).Inc()
opsProcessed.WithLabelValues(labels...).Inc()
target_info.Set(1)
time.Sleep(time.Second * 5)
}

View File

@@ -1,13 +0,0 @@
aks
eror
geomap
Geomap
grafanalib
grafonnet
iam
Jsonnet
[Operato Windrose](https://grafana.com/grafana/plugins/operato-windrose-panel/)
runbook
sergent
sparkline
wan

View File

@@ -20,6 +20,11 @@ labels:
title: Configure data source-managed alert rules
weight: 200
refs:
shared-configure-prometheus-data-source-alerting:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/prometheus/configure-prometheus-data-source/#alerting
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/prometheus/configure-prometheus-data-source/#alerting
configure-grafana-managed-rules:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-grafana-managed-rule/
@@ -74,17 +79,19 @@ refs:
# Configure data source-managed alert rules
Data source-managed alert rules can only query Prometheus-based data sources, such as Prometheus, Grafana Mimir, or Grafana Loki. They are one of the two [alert rule types](ref:alert-rules) supported in Grafana.
Data source-managed alert rules can only be created using Grafana Mimir or Grafana Loki data sources.
Data source-managed alert rules are stored within the data source. In a distributed architecture, they can scale horizontally to provide high-availability.
The rules are stored within the data source. In a distributed architecture, they can scale horizontally to provide high-availability. For more details, refer to [alert rule types](ref:alert-rules).
We recommend using [Grafana-managed alert rules](ref:configure-grafana-managed-rules) whenever possible and opting for data source-managed alert rules when scaling your alerting setup is necessary.
{{< docs/shared lookup="alerts/note-prometheus-ds-rules.md" source="grafana" version="<GRAFANA_VERSION>" >}}
To create or edit data source-managed alert rules, follow these instructions.
## Before you begin
Verify that you have write permission to the Prometheus, Mimir, or Loki data source. Otherwise, you cannot create or update data source-managed alert rules.
Verify that you have write permission to the Mimir or Loki data source. Otherwise, you cannot create or update data source-managed alert rules.
### Enable the Ruler API
@@ -96,7 +103,7 @@ For more information, refer to the [Mimir Ruler API](/docs/mimir/latest/referenc
### Permissions
Alert rules for Prometheus, Mimir, or Loki instances can be edited or deleted by users with **Editor** or **Admin** roles.
Alert rules for Mimir or Loki instances can be edited or deleted by users with **Editor** or **Admin** roles.
If you do not want to manage alert rules for a particular data source, go to its settings and clear the **Manage alerts via Alerting UI** checkbox.

View File

@@ -18,6 +18,11 @@ labels:
title: Alert rules
weight: 100
refs:
shared-configure-prometheus-data-source-alerting:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/prometheus/configure-prometheus-data-source/#alerting
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/prometheus/configure-prometheus-data-source/#alerting
queries-and-conditions:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/queries-conditions/
@@ -72,8 +77,9 @@ Grafana-managed alert rules are the most flexible alert rule type. They allow yo
{{< figure src="/media/docs/alerting/grafana-managed-alerting-architecture.png" max-width="750px" caption="How Grafana-managed alerting works by default" >}}
1. Alert rules are created within Grafana and query one or more data sources.
1. Alert rules are evaluated by the Alert Rule Evaluation Engine from within Grafana.
1. Alert rules are created and stored within Grafana.
1. Alert rules can query one or more supported data sources.
1. Alert rules are evaluated by the Alert Rule Evaluation Engine within Grafana.
1. Firing and resolved alert instances are forwarded to [handle their notifications](ref:notifications).
### Supported data sources
@@ -84,17 +90,17 @@ Find the public data sources supporting Alerting in the [Grafana Plugins directo
## Data source-managed alert rules
Data source-managed alert rules can only query Prometheus-based data sources, such as Prometheus, Grafana Mimir, or Grafana Loki.
Alert rules are stored within the data source. In this distributed architecture, the separation of components can provide high-availability and fault tolerance, enabling the scaling of your alerting setup.
Data source-managed alert rules can only be created using Grafana Mimir or Grafana Loki data sources. Both data source backends can provide high availability and fault tolerance, enabling you to scale your alerting setup.
{{< figure src="/media/docs/alerting/mimir-managed-alerting-architecture-v2.png" max-width="750px" caption="Mimir-managed alerting architecture" >}}
1. Alert rules are created and stored within the data source itself.
1. Alert rules can only query Prometheus-based data.
1. Alert rules are evaluated by the Alert Rule Evaluation Engine.
1. Alert rules are stored within the Mimir or Loki data source.
1. Alert rules can query only their specific data source.
1. Alert rules are evaluated by the Alert Rule Evaluation Engine within the data source.
1. Firing and resolved alert instances are forwarded to [handle their notifications](ref:notifications).
{{< docs/shared lookup="alerts/note-prometheus-ds-rules.md" source="grafana" version="<GRAFANA_VERSION>" >}}
## Comparison between alert rule types
We recommend using Grafana-managed alert rules whenever possible, and opting for data source-managed alert rules when you need to scale your alerting setup.
@@ -103,7 +109,7 @@ The table below compares Grafana-managed and data source-managed alert rules.
| <div style="width:200px">Feature</div> | <div style="width:200px">Grafana-managed alert rule</div> | <div style="width:200px">Data source-managed alert rule |
| ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Create alert rules<wbr /> that query [data sources supporting Alerting](#supported-data-sources) | Yes | No. Only query Prometheus-based data sources. |
| Create alert rules<wbr /> that query [data sources supporting Alerting](#supported-data-sources) | Yes | Only supports creating rules for Mimir and Loki. |
| Mix and match data sources | Yes | No |
| Add [expressions](ref:expression-queries) to transform<wbr /> your data and set [alert conditions](ref:alert-condition) | Yes | No |
| Use [images in alert notifications](ref:notification-images) | Yes | No |

View File

@@ -17,7 +17,9 @@ weight: 400
# Grafana CLI
Grafana CLI is a small executable that is bundled with Grafana server. It can be executed on the same machine Grafana server is running on. Grafana CLI has `plugins` and `admin` commands, as well as global options.
Grafana CLI is a small executable that's bundled with Grafana server.
You can run it on the same machine Grafana server is running on.
Grafana CLI has `plugins` and `admin` commands, as well as global options.
To list all commands and options:
@@ -25,13 +27,17 @@ To list all commands and options:
grafana cli -h
```
## Invoking Grafana CLI
## Run Grafana CLI
To invoke Grafana CLI, add the path to the grafana binaries in your `PATH` environment variable. Alternately, if your current directory is the `bin` directory, use `./grafana cli`. Otherwise, you can specify full path to the CLI. For example, on Linux `/usr/share/grafana/bin/grafana` and on Windows `C:\Program Files\GrafanaLabs\grafana\bin\grafana.exe`, and invoke it with `grafana cli`.
To run Grafana CLI, add the path to the Grafana binaries in your `PATH` environment variable.
Alternately, if your current directory is the `bin` directory, run `./grafana cli`.
Otherwise, you can specify full path to the binary.
For example, on Linux `/usr/share/grafana/bin/grafana` and on Windows `C:\Program Files\GrafanaLabs\grafana\bin\grafana.exe`, and run it with `grafana cli`.
{{% admonition type="note" %}}
Some commands, such as installing or removing plugins, require `sudo` on Linux. If you are on Windows, run Windows PowerShell as Administrator.
{{% /admonition %}}
{{< admonition type="note" >}}
Some commands, such as installing or removing plugins, require `sudo` on Linux.
If you're on Windows, run Windows PowerShell as Administrator.
{{< /admonition >}}
## Grafana CLI command syntax

View File

@@ -100,7 +100,7 @@ When sharing URLs with ad hoc filters, remember to encode the URL. In the preced
### Example
[This dashboard in Grafana Play](https://play.grafana.org/d/000000002/influxdb-templated?orgId=1&var-datacenter=America&var-host=All&var-summarize=1m&var-adhoc=datacenter%7C%3D%7CAmerica) passes the ad hoc filter variable `adhoc` with the filter value `datacenter = America`.
[This dashboard in Grafana Play](https://play.grafana.org/d/p-k6QtkGz/template-redux?var-interval=$__auto&orgId=1&from=now-5m&to=now&timezone=utc&var-query=$__all&var-query2=$__all&var-query3=$__all&var-Filters=job%7C%3D%7Cmetrictank%2Ftsdb-gw&var-textbox=foo&var-custom=lisa&var-datasource=grafanacloud-demoinfra-prom) passes the ad hoc filter variable `Filters` with the filter value `job = metrictank/tsdb-gw`.
## Time range control using the URL

View File

@@ -42,7 +42,7 @@ You can create a variety of queries to visualize logs or metrics stored in Elast
For instructions on how to add a data source to Grafana, refer to the [administration documentation](ref:administration-documentation).
Only users with the organization `administrator` role can add data sources.
Administrators can also [configure the data source via YAML](#provision-the-data-source) with Grafana's provisioning system.
Administrators can also [configure the data source via YAML](ref:provisioning-data-sources) with Grafana's provisioning system.
## Configuring permissions

View File

@@ -31,6 +31,11 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-data-links/#value-variables
- pattern: /docs/grafana-cloud/
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-data-links/#value-variables
alerting-alert-rules:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/
---
# Configure Prometheus
@@ -115,7 +120,7 @@ Following are additional configuration options.
### Alerting
- **Manage alerts via Alerting UI** - Toggle to enable `Alertmanager` integration for this data source.
- **Manage alerts via Alerting UI** - Toggle to enable [data source-managed rules in Grafana Alerting](ref:alerting-alert-rules) for this data source. For `Mimir`, it enables managing data source-managed rules and alerts. For `Prometheus`, it only supports viewing existing rules and alerts, which are displayed as data source-managed.
{{% admonition type="note" %}}

View File

@@ -43,7 +43,7 @@ In order to access Explore, you must have either the `editor` or `administrator`
Refer to [Role-based access control (RBAC)](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/access-control/) in Grafana Enterprise to understand how you can manage Explore with role-based permissions.
{{< admonition type="note" >}}
If you are using Grafana Cloud, open a [support ticket in the Cloud Portal](/https://grafana.com/auth/sign-in) to enable the `viewers_can_edit` option.
If you are using Grafana Cloud, open a [support ticket in the Cloud Portal](https://grafana.com/auth/sign-in) to enable the `viewers_can_edit` option.
{{< /admonition >}}
## Explore elements

View File

@@ -51,7 +51,7 @@ refs:
# Traces
Traces visualizations let you follow a request as it traverses the services in your infrastructure.
The traces visualization displays traces data in a diagram that allows you to easily interpret it.
The traces visualization displays traces data in a diagram that allows you to easily interpret it. Traces visualizations currently render one trace traversal based on the traceID used in TraceQL or using a variable.
For more information about traces and how to use them, refer to the following documentation:

View File

@@ -232,6 +232,8 @@ Experimental features might be changed or removed without prior notice.
| `k8SFolderCounts` | Enable folder's api server counts |
| `k8SFolderMove` | Enable folder's api server move |
| `teamHttpHeadersMimir` | Enables LBAC for datasources for Mimir to apply LBAC filtering of metrics to the client requests for users in teams |
| `queryLibraryDashboards` | Enables Query Library feature in Dashboards |
| `elasticsearchImprovedParsing` | Enables less memory intensive Elasticsearch result parsing |
## Development feature toggles

View File

@@ -0,0 +1,10 @@
---
labels:
products:
- oss
title: 'Note Prometheus data source-managed rules'
---
> Rules from a Prometheus data source appear in the **Data source-managed** section of the **Alert rules** page when [Manage alerts via Alerting UI](ref:shared-configure-prometheus-data-source-alerting) is enabled.
>
> However, Grafana can only create and edit data source-managed rules for Mimir and Loki, not for a Prometheus instance.

View File

@@ -10,10 +10,10 @@ labels:
- cloud
tags:
- beginner
title: Get started with Grafana Alerting - Part 2 of 3
title: Get started with Grafana Alerting - Part 2 of 4
weight: 50
killercoda:
title: Get started with Grafana Alerting - Part 2 of 3
title: Get started with Grafana Alerting - Part 2 of 4
description: Learn to use alert instances and route notifications by labels to contacts, building on your alerting skills in Grafana for more advanced workflows — Part 2.
backend:
imageid: ubuntu
@@ -21,7 +21,7 @@ killercoda:
<!-- INTERACTIVE page intro.md START -->
# Get started with Grafana Alerting - Part 2 of 3
# Get started with Grafana Alerting - Part 2 of 4
The Get started with Grafana Alerting tutorial Part 2 is a continuation of [Get started with Grafana Alerting tutorial Part 1](http://www.grafana.com/tutorials/alerting-get-started/).
@@ -222,7 +222,7 @@ The alert rule that you are about to create is meant to monitor web traffic page
### Create an alert rule
1. Navigate to **Alerting > Alert rules**.
1. Navigate to **Alerts & IRM > Alerting > Alert rules**.
1. Click **New alert rule**.
### Enter an alert rule name
@@ -260,18 +260,21 @@ It should return two series.`desktop` in Firing state, and `mobile` in Normal st
<!-- INTERACTIVE page step6.md END -->
<!-- INTERACTIVE page step7.md START -->
### Add folders and labels
1. In **Folder**, click **+ New folder** and enter a name. For example: `web-traffic-alerts` . This folder contains our alert rules.
### Set evaluation behavior
In the [life cycle](http://grafana.com/docs/grafana/next/alerting/fundamentals/alert-rule-evaluation/) of alert instances, when an alert condition (threshold) is not met, the alert instance state is **Normal**. Similarly, when the condition is breached (for longer than the pending period, which in this tutorial will be 0), the alert instance state switches back to **Alerting**, which means that the alert rule state is **Firing**, and a notification is sent.
To set up evaluation behavior:
1. In **Folder**, click **+ New folder** and enter a name. For example: `web-traffic-alerts`. This folder contains our alert rules.
1. In **Evaluation group**, repeat the above step to create a new evaluation group. Name it `1m` (referring to “1 minute”).
1. Choose an Evaluation interval (how often the alert will be evaluated). Choose `1m`.
1. Set the pending period to `0s` (zero seconds), so the alert rule fires the moment the condition is met.
1. In **Evaluation group and interval**, repeat the above step to create a new evaluation group. Name it `1m` (referring to “1 minute”).
1. Choose an **Evaluation interval** (how often the alert will be evaluated). Choose `1m`.
1. Set the **pending period** to `0s` (zero seconds), so the alert rule fires the moment the condition is met.
### Configure labels and notifications
### Configure notifications
In this section, you can select how you want to route your alert instances. Since we want to route by notification policy, we need to ensure that the labels match the alert instance.

View File

@@ -10,10 +10,10 @@ labels:
- cloud
tags:
- intermediate
title: Get started with Grafana Alerting - Part 3
title: Get started with Grafana Alerting - Part 3 of 4
weight: 60
killercoda:
title: Get started with Grafana Alerting - Part 3
title: Get started with Grafana Alerting - Part 3 of 4
description: Learn how to group alert notifications effectively to reduce noise and streamline communication in Grafana Alerting — Part 3.
backend:
imageid: ubuntu
@@ -37,7 +37,7 @@ refs:
<!-- INTERACTIVE page intro.md START -->
# Get started with Grafana Alerting - Part 3
# Get started with Grafana Alerting - Part 3 of 4
The Get started with Grafana Alerting tutorial Part 3 is a continuation of [Get started with Grafana Alerting tutorial Part 2](http://www.grafana.com/tutorials/alerting-get-started-pt2/).
@@ -338,7 +338,7 @@ Following the above example, [notification policies](ref:notification-policies)
In this section we configure an alert rule based on our application monitoring example.
1. Navigate to **Alerting > Alert rules**.
1. Navigate to **Alerts & IRM > Alerting > Alert rules**.
2. Click **New alert rule**.
### Enter an alert rule name
@@ -383,16 +383,19 @@ Grafana includes a [test data source](https://grafana.com/docs/grafana/latest/da
{{< figure src="/media/docs/alerting/regions-alert-instance-preview.png" max-width="750px" alt="Preview of a query returning alert instances." >}}
### Add folders and labels
1. In **Folder**, click **+ New folder** and enter a name. For example: `Multi-region alerts` . This folder contains our alert rules.
### Set evaluation behavior
Every alert rule is assigned to an evaluation group. You can assign the alert rule to an existing evaluation group or create a new one.
1. In **Folder**, click **+ New folder** and enter a name. For example: `Multi-region alerts`. This folder contains our alert rules.
1. In the **Evaluation group**, repeat the above step to create a new evaluation group. Name it `Multi-region group`.
1. In the **Evaluation group and interval**, repeat the above step to create a new evaluation group. Name it `Multi-region group`.
1. Choose an **Evaluation interval** (how often the alert are evaluated). Choose `1m`.
1. Set the pending period to `0s` (zero seconds), so the alert rule fires the moment the condition is met (this minimizes the waiting time for the demonstration).
1. Set the **pending period** to `0s` (zero seconds), so the alert rule fires the moment the condition is met (this minimizes the waiting time for the demonstration).
### Configure labels and notifications
### Configure notifications
Select who should receive a notification when an alert rule fires.
@@ -508,4 +511,24 @@ _Detail of memory alert instances grouped into a separate notification for us-ea
By configuring **notification policies** and using **labels** (such as _region_), you can group alert notifications based on specific criteria and route them to the appropriate teams. Fine-tuning **timing options**—including group wait, group interval, and repeat interval—further can reduce noise and ensures notifications remain actionable without overwhelming on-call engineers.
## Learn more in [Grafana Alerting Part 4](http://www.grafana.com/tutorials/alerting-get-started-pt4/)
<!-- INTERACTIVE ignore START -->
{{< admonition type="tip" >}}
In [Get started with Grafana Alerting - Part 4](http://www.grafana.com/tutorials/alerting-get-started-pt4/) you learn how to use templates to create customized and concise notifications.
{{< /admonition >}}
<!-- INTERACTIVE ignore END -->
{{< docs/ignore >}}
In [Get started with Grafana Alerting - Part 4](http://www.grafana.com/tutorials/alerting-get-started-pt4/) you learn how to use templates to create customized and concise notifications.
{{< /docs/ignore >}}
<!-- INTERACTIVE page finish.md END -->
<!-- INTERACTIVE page finish.md END -->

View File

@@ -0,0 +1,421 @@
---
Feedback Link: https://github.com/grafana/tutorials/issues/new
categories:
- alerting
description: Learn how to use templates to create customized and concise notifications — Part 4
labels:
products:
- enterprise
- oss
- cloud
tags:
- intermediate
title: Get started with Grafana Alerting - Part 4
weight: 60
killercoda:
title: Get started with Grafana Alerting - Part 4
description: Learn how to use templates to create customized and concise notifications — Part 4.
backend:
imageid: ubuntu
refs:
alert-labels:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/annotation-label/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/annotation-label/
template-labels-annotations:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/templates/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/
template-labels-annotations-ref:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/templates/reference/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/
template-labels-annotations-ref-labels-variable:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/templates/reference/#labels
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/#labels
template-labels-annotations-ref-values-variable:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/templates/reference/#values
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/reference/#values
template-labels-annotations-lang:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/templates/language/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/templates/language/
template-notifications:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/configure-notifications/template-notifications/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/
template-notifications-ref:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/configure-notifications/template-notifications/reference/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/reference/
template-notifications-lang:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/configure-notifications/template-notifications/language/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/language/
templates:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/templates/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/templates/
---
<!-- INTERACTIVE page intro.md START -->
# Get started with Grafana Alerting - Part 4
The Get started with Grafana Alerting tutorial Part 4 is a continuation of [Get started with Grafana Alerting tutorial Part 3](http://www.grafana.com/tutorials/alerting-get-started-pt3/).
In this tutorial, you will learn:
- The two types of templates in Grafana Alerting: labels and annotations and notification templates.
- How to configure alert rules with summary and description annotations.
- How to create a notification template that integrates with alert rule annotations.
- How to use a built-in notification template to group and format multiple alert instances.
- How to preview alert notifications by leveraging alert instances in the notification template payload.
<!-- INTERACTIVE page intro.md END -->
<!-- INTERACTIVE page step1.md START -->
<!-- INTERACTIVE ignore START -->
{{< docs/ignore >}}
## Set up the Grafana stack
{{< /docs/ignore >}}
## Before you begin
There are different ways you can follow along with this tutorial.
- **Grafana Cloud**
- As a Grafana Cloud user, you don't have to install anything. [Create your free account](http://www.grafana.com/auth/sign-up/create-user).
Continue to [how templating works](#how-templating-works).
- **Interactive learning environment**
- Alternatively, you can try out this example in our interactive learning environment: [Get started with Grafana Alerting - Part 4](https://killercoda.com/grafana-labs/course/grafana/alerting-get-started-pt4/). It's a fully configured environment with all the dependencies already installed.
- **Grafana OSS**
- If you opt to run a Grafana stack locally, ensure you have the following applications installed:
- [Docker Compose](https://docs.docker.com/get-docker/) (included in Docker for Desktop for macOS and Windows)
- [Git](https://git-scm.com/)
### Set up the Grafana stack (OSS users)
<!-- INTERACTIVE ignore END -->
To demonstrate the observation of data using the Grafana stack, download and run the following files.
1. Clone the [tutorial environment repository](https://www.github.com/grafana/tutorial-environment).
<!-- INTERACTIVE exec START -->
```
git clone https://github.com/grafana/tutorial-environment.git
```
<!-- INTERACTIVE exec END -->
1. Change to the directory where you cloned the repository:
<!-- INTERACTIVE exec START -->
```
cd tutorial-environment
```
<!-- INTERACTIVE exec END -->
1. Run the Grafana stack:
<!-- INTERACTIVE ignore START -->
```
docker compose up -d
```
<!-- INTERACTIVE ignore END -->
{{< docs/ignore >}}
<!-- INTERACTIVE exec START -->
```bash
docker-compose up -d
```
<!-- INTERACTIVE exec END -->
{{< /docs/ignore >}}
The first time you run `docker compose up -d`, Docker downloads all the necessary resources for the tutorial. This might take a few minutes, depending on your internet connection.
<!-- INTERACTIVE ignore START -->
{{< admonition type="note" >}}
If you already have Grafana, Loki, or Prometheus running on your system, you might see errors, because the Docker image is trying to use ports that your local installations are already using. If this is the case, stop the services, then run the command again.
{{< /admonition >}}
<!-- INTERACTIVE ignore END -->
{{< docs/ignore >}}
NOTE:
If you already have Grafana, Loki, or Prometheus running on your system, you might see errors, because the Docker image is trying to use ports that your local installations are already using. If this is the case, stop the services, then run the command again.
{{< /docs/ignore >}}
<!-- INTERACTIVE page step1.md END -->
<!-- INTERACTIVE page step2.md START -->
## How templating works
In Grafana, you can use [templates](https://grafana.com/docs/grafana/latest/alerting/fundamentals/templates/) to dynamically pull in specific data about the alert rule. This results in more flexible and informative alert notification messages. You can template either alert rule labels and annotations, or notification templates. Both use the Go template language.
{{< figure src="/media/docs/alerting/how-notification-templates-works.png" max-width="1200px" caption="How templating works" >}}
### Templating alert rule labels and annotations
[Labels and annotations](https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/) are key fields where templates are applied. One of the main advantages of using templating in annotations is the ability to incorporate dynamic data from queries, allowing alerts to reflect real-time information relevant to the triggered condition. By using templating in annotations, you can customize the content of each alert instance, such as including instance names and metric values, so the notification becomes more informative.
### Notification templates
The real power of templating lies in how it helps you format notifications with dynamic alert data. [Notification templates](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/template-notifications/) let you pull in details from annotations to create clear and consistent messages. They also make it simple to reuse the same format across different contact points, saving time and effort.
Notification templates allow you to customize how information is presented in each notification. For example, you can use templates to organize and format details about firing or resolved alerts, making it easier for recipients to understand the status of each alert at a glance—all within a single notification.
This particular notification template pulls in summary and description annotations for each alert instance and organizes them into separate sections, such as "firing" and "resolved." This way, instead of getting a long list of individual alert notifications, users can receive one well-structured message with all the relevant details grouped together.
This approach is helpful when you want to reduce notification noise, especially in situations where multiple instances of an alert are firing at the same time (e.g., high CPU usage across several instances). You can leverage templates to create a unified, easy-to-read notification that includes all the pertinent details.
<!-- INTERACTIVE page step2.md END -->
<!-- INTERACTIVE page step3.md START -->
## Step 1: Template labels and annotations
Now that we've introduced how templating works, lets move on to the next step. We guide you through creating an alert rule with a summary and description annotation. In doing so, we incorporate CPU usage and instance names, which we later use in our notification template.
### Create an alert rule
1. Sign in to Grafana:
- **Grafana Cloud** users: Log in via Grafana Cloud.
- **OSS users**: Go to [http://localhost:3000](http://localhost:3000).
1. Create an alert rule that includes a summary and description annotation:
- Navigate to **Alerts & IRM > Alerting > Alert rules**.
- Click **+ New alert rule**.
- Enter an **alert rule name**. Name it `High CPU usage`
1. **Define query an alert condition** section:
- Select TestData data source from the drop-down menu.
[TestData](https://grafana.com/docs/grafana/latest/datasources/testdata/) is included in the demo environment. If youre working in Grafana Cloud or your own local Grafana instance, you can add the data source through the Connections menu.
- From **Scenario** select **CSV Content**.
- Copy in the following CSV data:
```
region,cpu-usage,service,instance
us-west,88,web-server-1,server-01
us-west,81,web-server-1,server-02
us-east,79,web-server-2,server-03
us-east,52,web-server-2,server-04
```
This dataset simulates a data source returning multiple time series, with each time series generating a separate alert instance.
1. **Alert condition** section:
- Keep Last as the value for the reducer function (`WHEN`), and `75` as the threshold value, representing CPU usage above 75% .This is the value above which the alert rule should trigger.
- Click **Preview alert rule condition** to run the queries.
It should return 3 series in Firing state, and 1 in Normal state.
{{< figure src="/media/docs/alerting/part-4-firing-instances-preview.png" max-width="1200px" caption="Preview of a query returning alert instances" >}}
1. Add folders and labels section:
- In **Folder**, click **+ New folder** and enter a name. For example: `System metrics` . This folder contains our alert rules.
Note: while it's possible to template labels here, in this tutorial, we focus on templating the summary and annotations fields instead.
1. **Set evaluation behaviour** section:
- In the **Evaluation group and interval**, repeat the above step to create a new evaluation group. Name it `High usage`.
- Choose an **Evaluation interval** (how often the alert will be evaluated). Choose `1m`.
- Set the **pending period** to 0s (zero seconds), so the alert rule fires the moment the condition is met (this minimizes the waiting time for the demonstration.).
1. **Configure notifications** section:
Select who should receive a notification when an alert rule fires.
- Select a **Contact point**. If you dont have any contact points, click _View or create contact points_.
1. **Configure notification message** section:
In this step, youll configure the **summary** and **description** annotations to make your alert notifications informative and easy to understand. These annotations use templates to dynamically include key information about the alert.
- **Summary** annotation: Enter the following code as the value for the annotation.:
```go
{{- "\n" -}}
Instance: {{ index $labels "instance" }}
{{- "\t" -}} Usage: {{ index $values "A"}}%{{- "\n" -}}
```
This template automatically adds the instance name (from the [$labels](https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/reference/#labels) data) and its current CPU usage (from [$values["A"]](https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/reference/#values)) into the alert summary. `\t`: Adds a tab space between the instance name and the value. And, `\n`: Inserts a new line after the value.
Output example:
```
server-01 88
```
This output helps you quickly see which instance is affected and its usage level.
1. Optional: Add a description to help the on-call engineer to better understand what the alert rule is about. Eg. This alert monitors CPU usage across instances and triggers if any instance exceeds a usage threshold of 75%.
1. Click **Save rule and exit**.
Now that weve configured an alert rule with dynamic templates for the **summary** annotation, the next step is to customize the alert notifications themselves. While the default notification message includes the summary annotation and works well, it can often be too verbose.
{{< figure src="/media/docs/alerting/templated-annotation-alert.png" max-width="1200px" caption="Default email alert notification with templated annotation" >}}
To make our alert notifications more concise and tailored to our needs, well create a custom **notification template** that references the summary annotation we just set up. Notification templates are especially useful because they can be reused across multiple contact points, ensuring consistent alert messages.
<!-- INTERACTIVE page step3.md END -->
<!-- INTERACTIVE page step4.md START -->
## Step 2: Template notifications
In this step, we use a built-in notification template to format alert notifications in a clear and organized way. Notification templates allow us to customize the structure of alert messages, making them easier to read and more relevant.
Without a notification template, the alert messages would include the default Grafana formatting (`default.message`, see image above).
### Adding a notification template:
1. Navigate to **Alerts & IRM** > **Alerting** > **Contact point**s.
1. Select the **Notification Templates** tab.
1. Click **+ Add notification template group**.
1. Enter a name. E.g `instance-cpu-summary`.
1. From the **Add example** dropdown menu, choose `Print firing and resolved alerts`.
This template prints out alert instances into two sections: **firing alerts** and **resolved alerts**, and includes only the key details for each. In addition, it adds our summary and description annotations.
```
{{- /* Example displaying firing and resolved alerts separately in the notification. */ -}}
{{- /* Edit the template name and template content as needed. */ -}}
{{ define "custom.firing_and_resolved_alerts" -}}
{{ len .Alerts.Resolved }} resolved alert(s)
{{ range .Alerts.Resolved -}}
{{ template "alert.summary_and_description" . -}}
{{ end }}
{{ len .Alerts.Firing }} firing alert(s)
{{ range .Alerts.Firing -}}
{{ template "alert.summary_and_description" . -}}
{{ end -}}
{{ end -}}
{{ define "alert.summary_and_description" }}
Summary: {{.Annotations.summary}}
Status: {{ .Status }}
Description: {{.Annotations.description}}
{{ end -}}
```
{{< docs/ignore >}}
Note: Your notification template name (`{{define "<NAME>"}}`) must be unique. You cannot have two templates with the same name in the same notification template group or in different notification template groups.
{{< /docs/ignore >}}
<!-- INTERACTIVE ignore START -->
{{< admonition type="note" >}}
Your notification template name (`{{define "<NAME>"}}`) must be unique. You cannot have two templates with the same name in the same notification template group or in different notification template groups.
{{< /admonition >}}
<!-- INTERACTIVE ignore END -->
Heres a breakdown of the template:
- `{{ define "custom.firing_and_resolved_alerts" -}}` section: Displays the number of resolved alerts and their summaries, using the `alert.summary_and_description` template to include the summary, status, and description for each alert.
- `.Alerts.Firing` section: Similarly lists the number of firing alert instances and their details.
- `alert.summary_and_description`: This sub-template pulls the summary annotation you configured earlier.
In the **Preview** area, you can see a sample of how the notification would look. Since weve already created our alert rule, you can take it a step further by previewing how an actual alert instance from your rule would appear in the notification.
1. Click **Edit payload**.
1. Click **Use existing alert instance**.
You should see our alert rule listed on the left.
1. Click the alert rule.
1. Select an instance.
1. Click **Add alert data to payload**.
The alert instance is added to the bottom of the preview.
{{< figure src="/media/docs/alerting/alert-instance-preview-in-template.png" max-width="1200px" caption="Preview of an alert instance in a notification template" >}}
1. Click **Save**.
With the notification template ready, the next step is to apply it to your contact point to see it in action.
<!-- INTERACTIVE page step4.md END -->
<!-- INTERACTIVE page step5.md START -->
### Apply the template to your contact point
1. Apply the template to your contact point.
- Navigate to **Alerts & IRM** > **Alerting** > **Contact points**.
- Edit your contact point.
1. **Optional** [email] **settings** section:
- Click **Edit Message**.
- Under **Select notification template**, search `custom.firing_and_resolved_alerts`.
- Click **Save**.
1. Save your contact point.
<!-- INTERACTIVE page step5.md END -->
<!-- INTERACTIVE page step6.md START -->
### Receiving notifications
Now that the template has been applied to the contact point, you should receive notifications in the specified contact point.
Note: you might need to pause the alert rule evaluation and resume it to trigger the notification.
{{< figure src="/media/docs/alerting/templated-notification-cpu.png" max-width="1200px" caption="Templated email notification for CPU and memory usage" >}}
In the screen capture, you can see how the notification template groups the alert instances into two sections: **firing alerts** and **resolved alerts**. Each section includes only the key details for each alert, ensuring the message remains concise and focused. Additionally, the summary and description annotations we created earlier are included, providing affected instance and CPU usage.
<!-- INTERACTIVE page step6.md END -->
<!-- INTERACTIVE page finish.md START -->
## Conclusion
In this tutorial, we learned how to use templating in Grafana Alerting to create dynamic and actionable alert notifications. We explored how to configure alert rules with annotations, design custom notification templates, and apply them to contact points to enhance the clarity and efficiency of alert messages. By organizing alert instances into concise notifications, you can reduce noise and ensure on-call engineers quickly understand and address critical issues.
To deepen your understanding of Grafanas templating, explore the following resources:
- **Overview of the functions and operators used in templates**:
- [Notification template language](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/template-notifications/language/)
- [Alert rule template language](https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/language/)
- [**Notification template reference**](https://grafana.com/docs/grafana/latest/alerting/configure-notifications/template-notifications/reference/): Lists the data available for use in notification templates and explores specific functions.
- [**Alert rule template reference**](https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/reference/): Covers the specifics of creating dynamic labels and annotations for alert rules using elements such as variables and functions.
<!-- INTERACTIVE page finish.md END -->

View File

@@ -11,10 +11,10 @@ labels:
- cloud
tags:
- beginner
title: Get started with Grafana Alerting - Part 1 of 3
title: Get started with Grafana Alerting - Part 1 of 4
weight: 50
killercoda:
title: Get started with Grafana Alerting - Part 1 of 3
title: Get started with Grafana Alerting - Part 1 of 4
description: Get started with Grafana Alerting by creating your first alert rule, sending notifications to a webhook, and generating data to test it live — Part 1.
backend:
imageid: ubuntu
@@ -22,7 +22,7 @@ killercoda:
<!-- INTERACTIVE page intro.md START -->
# Get started with Grafana Alerting - Part 1 of 3
# Get started with Grafana Alerting - Part 1 of 4
In this guide, we walk you through the process of setting up your first alert in just a few minutes. You'll witness your alert in action with real-time data, as well as sending alert notifications.
@@ -181,7 +181,7 @@ We have created a dummy Webhook endpoint and created a new Alerting contact poin
Next, we establish an [alert rule](https://grafana.com/docs/grafana/latest/alerting/alerting-rules/create-grafana-managed-rule/) within Grafana Alerting to notify us whenever alert rules are triggered and resolved.
1. In Grafana, **navigate to Alerting** > **Alert rules**. Click on **New alert rule**.
1. In Grafana, navigate to **Alerts & IRM > Alerting > Alert rules**. Click on **New alert rule**.
1. Enter alert rule name for your alert rule. Make it short and descriptive as this appears in your alert notification. For instance, **database-metrics**
@@ -203,6 +203,10 @@ Grafana includes a [test data source](https://grafana.com/docs/grafana/latest/da
{{< figure src="/media/docs/alerting/random-walk-firing-alert-rule.png" max-width="1200px" caption="A preview of a firing alert" >}}
### Add folders and labels
1. In **Folder**, click **+ New folder** and enter a name. For example: `metric-alerts` . This folder contains our alert rules.
### Set evaluation behavior
The [alert rule evaluation](https://grafana.com/docs/grafana/latest/alerting/fundamentals/alert-rules/rule-evaluation/) defines the conditions under which an alert rule triggers, based on the following settings:
@@ -213,13 +217,12 @@ The [alert rule evaluation](https://grafana.com/docs/grafana/latest/alerting/fun
To set up the evaluation:
1. In **Folder**, click **+ New folder** and enter a name. For example: _metric-alerts_. This folder contains our alerts.
1. In the **Evaluation group**, repeat the above step to create a new evaluation group. Name it _1m-evaluation_.
1. In the **Evaluation group and interval**, repeat the above step to create a new evaluation group. Name it _1m-evaluation_.
1. Choose an **Evaluation interval** (how often the alert are evaluated).
For example, every `1m` (1 minute).
1. Set the pending period to, `0s` (zero seconds), so the alert rule fires the moment the condition is met.
1. Set the **pending period** to, `0s` (zero seconds), so the alert rule fires the moment the condition is met.
### Configure labels and notifications
### Configure notifications
Choose the contact point where you want to receive your alert notifications.

208
go.mod
View File

@@ -11,12 +11,14 @@ require (
cuelang.org/go v0.11.1 // @grafana/grafana-as-code
filippo.io/age v1.2.1 // @grafana/identity-access-team
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // @grafana/partner-datasources
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // @grafana/identity-access-team
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // @grafana/grafana-backend-group
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 // @grafana/grafana-backend-group
github.com/Azure/azure-storage-blob-go v0.15.0 // @grafana/grafana-backend-group
github.com/Azure/go-autorest/autorest v0.11.29 // @grafana/grafana-backend-group
github.com/Azure/go-autorest/autorest/adal v0.9.24 // @grafana/grafana-backend-group
github.com/BurntSushi/toml v1.4.0 // @grafana/identity-access-team
github.com/DATA-DOG/go-sqlmock v1.5.2 // @grafana/grafana-search-and-storage
github.com/Masterminds/semver v1.5.0 // @grafana/grafana-backend-group
github.com/Masterminds/semver/v3 v3.3.0 // @grafana/grafana-developer-enablement-squad
github.com/Masterminds/sprig/v3 v3.3.0 // @grafana/grafana-backend-group
@@ -41,6 +43,7 @@ require (
github.com/fatih/color v1.17.0 // @grafana/grafana-backend-group
github.com/fullstorydev/grpchan v1.1.1 // @grafana/grafana-backend-group
github.com/gchaincl/sqlhooks v1.3.0 // @grafana/grafana-search-and-storage
github.com/getkin/kin-openapi v0.128.0 // @grafana/grafana-app-platform-squad
github.com/go-jose/go-jose/v3 v3.0.3 // @grafana/identity-access-team
github.com/go-kit/log v0.2.1 // @grafana/grafana-backend-group
github.com/go-ldap/ldap/v3 v3.4.4 // @grafana/identity-access-team
@@ -59,6 +62,7 @@ require (
github.com/golang/protobuf v1.5.4 // @grafana/grafana-backend-group
github.com/golang/snappy v0.0.4 // @grafana/alerting-backend
github.com/google/go-cmp v0.6.0 // @grafana/grafana-backend-group
github.com/google/go-querystring v1.1.0 // indirect; @grafana/oss-big-tent
github.com/google/uuid v1.6.0 // @grafana/grafana-backend-group
github.com/google/wire v0.6.0 // @grafana/grafana-backend-group
github.com/googleapis/gax-go/v2 v2.14.1 // @grafana/grafana-backend-group
@@ -70,29 +74,25 @@ require (
github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics
github.com/grafana/dataplane/sdata v0.0.9 // @grafana/observability-metrics
github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 // @grafana/grafana-backend-group
github.com/grafana/e2e v0.1.1 // @grafana-app-platform-squad
github.com/grafana/gofpdf v0.0.0-20231002120153-857cc45be447 // @grafana/sharing-squad
github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana-app-sdk v0.23.1 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana-app-sdk v0.29.0 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana-app-sdk/logging v0.29.0 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana-aws-sdk v0.31.5 // @grafana/aws-datasources
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.2 // @grafana/partner-datasources
github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana-google-sdk-go v0.2.1 // @grafana/partner-datasources
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 // @grafana/grafana-backend-group
github.com/grafana/grafana-plugin-sdk-go v0.261.0 // @grafana/plugins-platform-backend
github.com/grafana/grafana/pkg/aggregator v0.0.0-20240813192817-1b0e6b5c09b2 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/apiserver v0.0.0-20240821155123-6891eb1d35da // @grafana/grafana-app-platform-squad
// This needs to be here for other projects that import grafana/grafana
// For local development grafana/grafana will always use the local files
// Check go.work file for details
github.com/grafana/grafana/pkg/promlib v0.0.7 // @grafana/oss-big-tent
github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana-plugin-sdk-go v0.262.0 // @grafana/plugins-platform-backend
github.com/grafana/loki/v3 v3.2.1 // @grafana/observability-logs
github.com/grafana/otel-profiling-go v0.5.1 // @grafana/grafana-backend-group
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // @grafana/observability-traces-and-profiling
github.com/grafana/pyroscope/api v1.0.0 // @grafana/observability-traces-and-profiling
github.com/grafana/tempo v1.5.1-0.20241001135150-ed943d7a56b2 // @grafana/observability-traces-and-profiling
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // @grafana/plugins-platform-backend
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 // @grafana/grafana-backend-group
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // @grafana/identity-access-team
github.com/hashicorp/go-hclog v1.6.3 // @grafana/plugins-platform-backend
github.com/hashicorp/go-multierror v1.1.1 // @grafana/alerting-squad
github.com/hashicorp/go-plugin v1.6.2 // @grafana/plugins-platform-backend
@@ -101,7 +101,10 @@ require (
github.com/hashicorp/hcl/v2 v2.17.0 // @grafana/alerting-backend
github.com/huandu/xstrings v1.5.0 // @grafana/partner-datasources
github.com/influxdata/influxdb-client-go/v2 v2.13.0 // @grafana/partner-datasources
github.com/influxdata/influxql v1.4.0 // @grafana/partner-datasources
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // @grafana/grafana-app-platform-squad
github.com/jeremywohl/flatten v1.0.1 // @grafana/grafana-app-platform-squad
github.com/jmespath-community/go-jmespath v1.1.1 // @grafana/identity-access-team
github.com/jmespath/go-jmespath v0.4.0 // indirect; @grafana/grafana-backend-group
github.com/jmoiron/sqlx v1.3.5 // @grafana/grafana-backend-group
github.com/json-iterator/go v1.1.12 // @grafana/grafana-backend-group
@@ -123,7 +126,9 @@ require (
github.com/openfga/api/proto v0.0.0-20240906203051-102620ef2a66 // @grafana/identity-access-team
github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20240926131254-992b301a003f // @grafana/identity-access-team
github.com/openfga/openfga v1.6.2 // @grafana/identity-access-team
github.com/openzipkin/zipkin-go v0.4.3 // @grafana/oss-big-tent
github.com/patrickmn/go-cache v2.1.0+incompatible // @grafana/alerting-backend
github.com/phpdave11/gofpdi v1.0.13 // @grafana/sharing-squad
github.com/prometheus/alertmanager v0.27.0 // @grafana/alerting-backend
github.com/prometheus/client_golang v1.20.5 // @grafana/alerting-backend
github.com/prometheus/client_model v0.6.1 // @grafana/grafana-backend-group
@@ -131,6 +136,7 @@ require (
github.com/prometheus/prometheus v0.301.0 // @grafana/alerting-backend
github.com/redis/go-redis/v9 v9.6.1 // @grafana/alerting-backend
github.com/robfig/cron/v3 v3.0.1 // @grafana/grafana-backend-group
github.com/rs/cors v1.11.1 // @grafana/identity-access-team
github.com/russellhaering/goxmldsig v1.4.0 // @grafana/grafana-backend-group
github.com/spf13/cobra v1.8.1 // @grafana/grafana-app-platform-squad
github.com/spf13/pflag v1.0.5 // @grafana-app-platform-squad
@@ -157,6 +163,7 @@ require (
go.opentelemetry.io/otel/trace v1.33.0 // @grafana/grafana-backend-group
go.uber.org/atomic v1.11.0 // @grafana/alerting-backend
go.uber.org/goleak v1.3.0 // @grafana/grafana-search-and-storage
go.uber.org/zap v1.27.0 // @grafana/identity-access-team
gocloud.dev v0.40.0 // @grafana/grafana-app-platform-squad
golang.org/x/crypto v0.32.0 // @grafana/grafana-backend-group
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // @grafana/alerting-backend
@@ -190,13 +197,33 @@ require (
)
require (
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20241209165425-c324376999f7 // @grafana/alerting-backend
github.com/grafana/grafana/apps/investigation v0.0.0-20241218083103-f46c07aba7b6 // @fcjack @matryer
github.com/grafana/grafana/apps/playlist v0.0.0-20241105090059-facca37f4d1f // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/aggregator v0.0.0-20240813192817-1b0e6b5c09b2 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240821155123-6891eb1d35da // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/apiserver v0.0.0-20240821155123-6891eb1d35da // @grafana/grafana-app-platform-squad
// This needs to be here for other projects that import grafana/grafana
// For local development grafana/grafana will always use the local files
// Check go.work file for details
github.com/grafana/grafana/pkg/promlib v0.0.7 // @grafana/oss-big-tent
github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20240821183201-2f012860344d // @grafana/grafana-search-and-storage
github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20240821161612-71f0dae39e9d // @grafana/grafana-search-and-storage
)
require (
cel.dev/expr v0.18.0 // indirect
cloud.google.com/go v0.116.0 // indirect
cloud.google.com/go/auth v0.13.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
cloud.google.com/go/iam v1.2.1 // indirect
cloud.google.com/go/longrunning v0.6.1 // indirect
cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // @grafana/identity-access-team
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
@@ -207,8 +234,8 @@ require (
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/DATA-DOG/go-sqlmock v1.5.2 // @grafana/grafana-search-and-storage
github.com/FZambia/eagle v0.1.0 // indirect
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
@@ -221,25 +248,42 @@ require (
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/at-wat/mqtt-go v0.19.4 // indirect
github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.12.0 // indirect
github.com/blevesearch/bleve_index_api v1.1.12 // indirect
github.com/blevesearch/geo v0.1.20 // indirect
github.com/blevesearch/go-faiss v1.0.23 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
github.com/blevesearch/vellum v1.0.10 // indirect
github.com/blevesearch/zapx/v11 v11.3.10 // indirect
github.com/blevesearch/zapx/v12 v12.3.10 // indirect
github.com/blevesearch/zapx/v13 v13.3.10 // indirect
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
github.com/blevesearch/zapx/v15 v15.3.16 // indirect
github.com/blevesearch/zapx/v16 v16.1.8 // indirect
github.com/blugelabs/ice v1.0.0 // indirect
github.com/blugelabs/ice/v2 v2.0.1 // indirect
github.com/bufbuild/protocompile v0.4.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 // indirect
github.com/caio/go-tdigest v3.1.0+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/centrifugal/protocol v0.13.4 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
@@ -248,18 +292,22 @@ require (
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dolthub/maphash v0.1.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/edsrzf/mmap-go v1.2.0 // indirect
github.com/elazarl/goproxy v1.2.6 // indirect
github.com/elazarl/goproxy v1.3.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/emicklei/proto v1.13.2 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gammazero/deque v0.2.1 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect; @grafana/grafana-app-platform-squad
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.23.0 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
@@ -275,6 +323,7 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.22.0 // indirect
@@ -283,23 +332,27 @@ require (
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20240821183201-2f012860344d // @grafana/grafana-search-and-storage
github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20240821161612-71f0dae39e9d // @grafana/grafana-search-and-storage
github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 // indirect
github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 // indirect
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
github.com/grafana/sqlds/v4 v4.1.3 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect; @grafana/plugins-platform-backend
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // @grafana/identity-access-team
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect
github.com/hashicorp/consul/api v1.30.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-msgpack v1.1.5 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-sockaddr v1.0.6 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/memberlist v0.5.0 // indirect
github.com/hashicorp/serf v0.10.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/invopop/jsonschema v0.12.0 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/invopop/yaml v0.3.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
@@ -311,13 +364,13 @@ require (
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jeremywohl/flatten v1.0.1 // @grafana/grafana-app-platform-squad
github.com/jessevdk/go-flags v1.5.0 // indirect
github.com/jhump/protoreflect v1.15.1 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/karlseguin/ccache/v3 v3.0.5 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
@@ -331,6 +384,9 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-ieproxy v0.0.12 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/maypok86/otter v1.2.2 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/mdlayher/vsock v1.2.1 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
github.com/miekg/dns v1.1.62 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
@@ -353,34 +409,44 @@ require (
github.com/oklog/run v1.1.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/oklog/ulid/v2 v2.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // indirect
github.com/opentracing-contrib/go-stdlib v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/pressly/goose/v3 v3.22.1 // indirect
github.com/prometheus/common/sigv4 v0.1.0 // indirect
github.com/prometheus/exporter-toolkit v0.13.2 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/sigv4 v0.1.0 // indirect
github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d // indirect
github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect
github.com/redis/rueidis v1.0.45 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/cors v1.11.1 // @grafana/identity-access-team
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/segmentio/encoding v0.4.0 // indirect
github.com/sercand/kuberesolver/v5 v5.1.1 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/sethvargo/go-retry v0.3.0 // indirect
github.com/shadowspore/fossil-delta v0.0.0-20240102155221-e3a8590b820b // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect
github.com/sony/gobreaker v0.5.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
@@ -395,34 +461,41 @@ require (
github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
github.com/yudai/pp v2.0.1+incompatible // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect
github.com/zclconf/go-cty v1.13.0 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
go.etcd.io/bbolt v1.3.11 // indirect
go.etcd.io/etcd/api/v3 v3.5.16 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect
go.etcd.io/etcd/client/v3 v3.5.16 // indirect
go.mongodb.org/mongo-driver v1.16.1 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect
go.opentelemetry.io/otel/metric v1.33.0 // indirect
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
go.uber.org/mock v0.4.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // @grafana/identity-access-team
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect; @grafana/grafana-backend-group
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241216192217-9240e9c98484 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect; @grafana/plugins-platform-backend
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/apiextensions-apiserver v0.32.0 // indirect
k8s.io/kms v0.32.0 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.55.3 // indirect
@@ -433,92 +506,7 @@ require (
modernc.org/token v1.1.0 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect; @grafana-app-platform-squad
)
require github.com/phpdave11/gofpdi v1.0.13 // @grafana/sharing-squad
require (
github.com/google/go-querystring v1.1.0 // indirect; @grafana/oss-big-tent
github.com/grafana/e2e v0.1.1 // @grafana-app-platform-squad
)
require (
github.com/fxamacker/cbor/v2 v2.7.0 // indirect; indirect0.0.0-20240809095826-8eb5495c0b2a
github.com/x448/float16 v0.8.4 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
)
require (
github.com/getkin/kin-openapi v0.128.0 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/apps/playlist v0.0.0-20241105090059-facca37f4d1f // @grafana/grafana-app-platform-squad
github.com/influxdata/influxql v1.4.0 // @grafana/partner-datasources
)
require github.com/jmespath-community/go-jmespath v1.1.1 // @grafana/identity-access-team
require github.com/grafana/loki/v3 v3.2.1 // @grafana/observability-logs
require github.com/openzipkin/zipkin-go v0.4.3 // @grafana/oss-big-tent
require github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20241209165425-c324376999f7 // @grafana/alerting-backend
require github.com/grafana/grafana/apps/investigation v0.0.0-20241218083103-f46c07aba7b6 // @fcjack @matryer
require (
cel.dev/expr v0.18.0 // indirect
cloud.google.com/go/longrunning v0.6.1 // indirect
cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
github.com/at-wat/mqtt-go v0.19.4 // indirect
github.com/blevesearch/bleve_index_api v1.1.12 // indirect
github.com/blevesearch/geo v0.1.20 // indirect
github.com/blevesearch/go-faiss v1.0.23 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
github.com/blevesearch/zapx/v11 v11.3.10 // indirect
github.com/blevesearch/zapx/v12 v12.3.10 // indirect
github.com/blevesearch/zapx/v13 v13.3.10 // indirect
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
github.com/blevesearch/zapx/v15 v15.3.16 // indirect
github.com/blevesearch/zapx/v16 v16.1.8 // indirect
github.com/blugelabs/ice/v2 v2.0.1 // indirect
github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/dolthub/maphash v0.1.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/gammazero/deque v0.2.1 // indirect
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 // indirect
github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 // indirect
github.com/grafana/sqlds/v4 v4.1.3 // indirect
github.com/hashicorp/consul/api v1.30.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/serf v0.10.1 // indirect
github.com/maypok86/otter v1.2.2 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/mdlayher/vsock v1.2.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/prometheus/sigv4 v0.1.0 // indirect
github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/sercand/kuberesolver/v5 v5.1.1 // indirect
github.com/shadowspore/fossil-delta v0.0.0-20240102155221-e3a8590b820b // indirect
github.com/sony/gobreaker v0.5.0 // indirect
go.etcd.io/bbolt v1.3.11 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
k8s.io/apiextensions-apiserver v0.32.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
// Use fork of crewjam/saml with fixes for some issues until changes get merged into upstream

21
go.sum
View File

@@ -1067,8 +1067,8 @@ github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8E
github.com/efficientgo/core v1.0.0-rc.3 h1:X6CdgycYWDcbYiJr1H1+lQGzx13o7bq3EUkbB9DsSPc=
github.com/efficientgo/core v1.0.0-rc.3/go.mod h1:FfGdkzWarkuzOlY04VY+bGfb1lWrjaL6x/GLcQ4vJps=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v1.2.6 h1:jk1cwYYz96HhB985lQ1FFv7UcYVQHV84w8lWtpxW7WE=
github.com/elazarl/goproxy v1.2.6/go.mod h1:yBhqz1/IaNA5tCayHGVfFmuzyanF6YeDNGIwPhfvtp8=
github.com/elazarl/goproxy v1.3.0 h1:hpDH1r1qJgM3eusz7lP+BiMPnLiWPa6hDjIFF5WVCjE=
github.com/elazarl/goproxy v1.3.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
@@ -1507,8 +1507,10 @@ github.com/grafana/gofpdf v0.0.0-20231002120153-857cc45be447 h1:jxJJ5z0GxqhWFbQU
github.com/grafana/gofpdf v0.0.0-20231002120153-857cc45be447/go.mod h1:IxsY6mns6Q5sAnWcrptrgUrSglTZJXH/kXr9nbpb/9I=
github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e h1:UlEET0InuoFautfaFp8lDrNF7rPHYXuBMrzwWx9XqFY=
github.com/grafana/gomemcache v0.0.0-20240805133030-fdaf6a95408e/go.mod h1:IGRj8oOoxwJbHBYl1+OhS9UjQR0dv6SQOep7HqmtyFU=
github.com/grafana/grafana-app-sdk v0.23.1 h1:BRpUG0bA0oVxjthkmO2thuJBo3nbjaRSSmZJHw+mA8I=
github.com/grafana/grafana-app-sdk v0.23.1/go.mod h1:KzgPnTJfMeckGmMctv6CJb8Jr/o/5rwARDyjXoeR0Fc=
github.com/grafana/grafana-app-sdk v0.29.0 h1:LMSm/+0LOBPd13fe1bs/4sKJmuLiixYUX9T0oqDqp4I=
github.com/grafana/grafana-app-sdk v0.29.0/go.mod h1:XLt308EmK6kvqPlzjUyXxbwZKEk2vur/eiypUNDay5I=
github.com/grafana/grafana-app-sdk/logging v0.29.0 h1:mgbXaAf33aFwqwGVeaX30l8rkeAJH0iACgX5Rn6YkN4=
github.com/grafana/grafana-app-sdk/logging v0.29.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM=
github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE=
github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM=
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.2 h1:fV6IgVtViXcYZ4VqTAMuVBTLuGAnI27HhQkaLttzbPE=
@@ -1519,8 +1521,8 @@ github.com/grafana/grafana-google-sdk-go v0.2.1 h1:XeFdKnkXBjOJjXc1gf4iMx4h5aCHT
github.com/grafana/grafana-google-sdk-go v0.2.1/go.mod h1:RiITSHwBhqVTTd3se3HQq5Ncs/wzzhTB9OK5N0J0PEU=
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 h1:r+mU5bGMzcXCRVAuOrTn54S80qbfVkvTdUJZfSfTNbs=
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79/go.mod h1:wc6Hbh3K2TgCUSfBC/BOzabItujtHMESZeFk5ZhdxhQ=
github.com/grafana/grafana-plugin-sdk-go v0.261.0 h1:pGGpPbKRWZcLxwNATEiVDhILbYGYwlWOEXFLhmUkBMo=
github.com/grafana/grafana-plugin-sdk-go v0.261.0/go.mod h1:QsLK0kAbmDXuX/QncFBTETPHCzw5g9hZnzqOPkoB3Yo=
github.com/grafana/grafana-plugin-sdk-go v0.262.0 h1:R2DV6lwBQE5zaogxX3PorD9Seo8CXA8YuStf84oqwkk=
github.com/grafana/grafana-plugin-sdk-go v0.262.0/go.mod h1:U43Cnrj/9DNYyvFcNdeUWNjMXTKNB0jcTcQGpWKd2gw=
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20241209165425-c324376999f7 h1:JFB5dvs0XwBh/RiDNA5OrqcF3eWCQmTYBm6Hy79PDMQ=
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20241209165425-c324376999f7/go.mod h1:AVvGgNqHsruJINRjKkhhY5NZMh5ke6Ei2bywuQ4Uuus=
github.com/grafana/grafana/apps/investigation v0.0.0-20241218083103-f46c07aba7b6 h1:KsHIuuPGww1U0G2CB1JYTasDaboJn0Cq91Je1aluEuc=
@@ -1690,8 +1692,8 @@ github.com/influxdata/influxql v1.4.0 h1:Lf62rbAF8KWQf+4Djqf4hVXgmQuGozUoSD6kNWj
github.com/influxdata/influxql v1.4.0/go.mod h1:VqxAKyQz5p8GzgGsxWalCWYGxEqw6kvJo2IickMQiQk=
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf h1:7JTmneyiNEwVBOHSjoMxiWAqB992atOeepeFYegn5RU=
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI=
github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
github.com/ionos-cloud/sdk-go/v6 v6.3.0 h1:/lTieTH9Mo/CWm3cTlFLnK10jgxjUGkAqRffGqvPteY=
@@ -1758,8 +1760,9 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6 h1:SwcnSwBR7X/5EHJQlXBockkJVIMRVt5yKaesBPMtyZQ=
github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6/go.mod h1:WrYiIuiXUMIvTDAQw97C+9l0CnBmCcvosPjN3XDqS/o=
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=

View File

@@ -8,7 +8,6 @@ use (
./apps/alerting/notifications
./apps/investigation
./apps/playlist
./kindsv2
./pkg/aggregator
./pkg/apimachinery
./pkg/apiserver

File diff suppressed because it is too large Load Diff

8
kindsv2/Makefile Normal file
View File

@@ -0,0 +1,8 @@
include ../.bingo/Variables.mk
.PHONY: all
all: dashboards
.PHONY: dashboards
dashboards: $(COG) ## Dashboards Typescript
@$(COG) generate --config ./dashboard-ts.yaml

22
kindsv2/dashboard-ts.yaml Normal file
View File

@@ -0,0 +1,22 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/pipeline.json
inputs:
- cue:
entrypoint: '%__config_dir%/../packages/grafana-schema/src/schema/dashboard/v2alpha0'
metadata:
kind: core
cue_imports:
- '%__config_dir%/../packages/grafana-schema/src/common:github.com/grafana/grafana/packages/grafana-schema/src/common'
output:
directory: '%__config_dir%/../packages/grafana-schema/src/schema/dashboard/'
types: true
languages:
- typescript:
skip_runtime: true
enums_as_union_types: true
path_prefix: ""
packages_import_map:
common: '@grafana/schema'

View File

@@ -1,53 +0,0 @@
//go:generate go run gen.go
package main
import (
"context"
"os"
"github.com/grafana/cog"
)
type codegenTargets struct {
modulePath string
outputPath string
cueImportsMap map[string]string
packagesImportMap map[string]string
}
func main() {
targets := []codegenTargets{
{
modulePath: "../packages/grafana-schema/src/schema/dashboard/v2alpha0/",
outputPath: "../packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.gen.ts",
cueImportsMap: map[string]string{
"github.com/grafana/grafana/packages/grafana-schema/src/common": "../packages/grafana-schema/src/common",
},
packagesImportMap: map[string]string{
"common": "@grafana/schema",
},
},
}
for _, target := range targets {
codegenPipeline := cog.TypesFromSchema().
CUEModule(
target.modulePath,
cog.CUEImports(target.cueImportsMap),
).
Typescript(cog.TypescriptConfig{
ImportsMap: target.packagesImportMap,
EnumsAsUnionTypes: true,
})
files, err := codegenPipeline.Run(context.Background())
if err != nil {
panic(err)
}
if err := os.WriteFile(target.outputPath, files[0].Data, 0644); err != nil {
panic(err)
}
}
}

View File

@@ -1,46 +0,0 @@
module github.com/grafana/grafana/kindsv2
go 1.23.1
require github.com/grafana/cog v0.0.5
require (
cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 // indirect
cuelang.org/go v0.11.1 // indirect
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/proto v1.13.2 // indirect
github.com/expr-lang/expr v1.16.9 // indirect
github.com/getkin/kin-openapi v0.128.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/invopop/yaml v0.3.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/yalue/merged_fs v1.3.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.29.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -50,7 +50,7 @@
"themes-generate": "esbuild --target=es6 ./scripts/cli/generateSassVariableFiles.ts --bundle --platform=node --tsconfig=./scripts/cli/tsconfig.json | node",
"themes:usage": "eslint . --ignore-pattern '*.test.ts*' --ignore-pattern '*.spec.ts*' --cache --plugin '@grafana' --rule '{ @grafana/theme-token-usage: \"error\" }'",
"typecheck": "tsc --noEmit && yarn run packages:typecheck",
"plugins:build-bundled": "find plugins-bundled -name package.json -not -path '*/node_modules/*' -execdir yarn build \\;",
"plugins:build-bundled": "echo 'bundled plugins are no longer supported'",
"watch": "yarn start -d watch,start core:start --watchTheme",
"ci:test-frontend": "yarn run test:ci",
"i18n:stats": "node ./scripts/cli/reportI18nStats.mjs",
@@ -123,7 +123,7 @@
"@types/lodash": "4.17.14",
"@types/logfmt": "^1.2.3",
"@types/lucene": "^2",
"@types/node": "22.10.5",
"@types/node": "22.10.6",
"@types/node-forge": "^1",
"@types/ol-ext": "npm:@siedlerchr/types-ol-ext@3.2.4",
"@types/pluralize": "^0.0.33",
@@ -213,7 +213,7 @@
"ngtemplate-loader": "2.1.0",
"node-notifier": "10.0.1",
"nx": "19.8.2",
"postcss": "8.4.49",
"postcss": "8.5.1",
"postcss-loader": "8.1.1",
"postcss-reporter": "7.1.0",
"postcss-scss": "4.0.9",
@@ -224,7 +224,7 @@
"redux-mock-store": "1.5.5",
"rimraf": "6.0.1",
"rudder-sdk-js": "2.48.43",
"sass": "1.83.1",
"sass": "1.83.4",
"sass-loader": "16.0.4",
"smtp-tester": "^2.1.0",
"style-loader": "4.0.0",
@@ -265,14 +265,14 @@
"@grafana/faro-web-tracing": "^1.8.2",
"@grafana/flamegraph": "workspace:*",
"@grafana/google-sdk": "0.1.2",
"@grafana/lezer-logql": "0.2.6",
"@grafana/monaco-logql": "^0.0.7",
"@grafana/lezer-logql": "0.2.7",
"@grafana/monaco-logql": "^0.0.8",
"@grafana/o11y-ds-frontend": "workspace:*",
"@grafana/prometheus": "workspace:*",
"@grafana/runtime": "workspace:*",
"@grafana/saga-icons": "workspace:*",
"@grafana/scenes": "5.36.4",
"@grafana/scenes-react": "5.36.4",
"@grafana/scenes": "5.37.0",
"@grafana/scenes-react": "5.37.0",
"@grafana/schema": "workspace:*",
"@grafana/sql": "workspace:*",
"@grafana/ui": "workspace:*",
@@ -359,8 +359,7 @@
"pluralize": "^8.0.0",
"prismjs": "1.29.0",
"rc-slider": "11.1.8",
"rc-time-picker": "3.7.3",
"rc-tree": "5.11.0",
"rc-tree": "5.13.0",
"re-resizable": "6.10.3",
"react": "18.2.0",
"react-diff-viewer-continued": "^3.4.0",
@@ -437,7 +436,6 @@
"packages": [
"packages/*",
"packages/!(grafana-icons)/**",
"plugins-bundled/internal/*",
"public/app/plugins/*/*",
"e2e/test-plugins/*"
]

View File

@@ -51,7 +51,7 @@
"moment": "2.30.1",
"moment-timezone": "0.5.46",
"ol": "7.4.0",
"papaparse": "5.4.1",
"papaparse": "5.5.1",
"react-use": "17.6.0",
"rxjs": "7.8.1",
"string-hash": "^1.1.3",
@@ -65,7 +65,7 @@
"@rollup/plugin-node-resolve": "16.0.0",
"@types/history": "4.7.11",
"@types/lodash": "4.17.14",
"@types/node": "22.10.5",
"@types/node": "22.10.6",
"@types/papaparse": "5.3.15",
"@types/react": "18.3.3",
"@types/react-dom": "18.2.25",

View File

@@ -254,6 +254,29 @@ describe('Stats Calculators', () => {
expect(reduce(someNulls, ReducerID.count)).toEqual(4);
});
it('median should ignoreNulls by default', () => {
const someNulls = createField('y', [3, null, 2, 1, 4]);
expect(reduce(someNulls, ReducerID.median)).toEqual(2.5);
});
it('median should use fieldConfig nullValueMode.Ignore and not count nulls', () => {
const someNulls = createField('y', [3, null, 2, 1, 4]);
someNulls.config.nullValueMode = NullValueMode.Ignore;
expect(reduce(someNulls, ReducerID.median)).toEqual(2.5);
});
it('median should use fieldConfig nullValueMode.Null and count nulls', () => {
const someNulls = createField('y', [3, null, 2, 1, 4]);
someNulls.config.nullValueMode = NullValueMode.Null;
expect(reduce(someNulls, ReducerID.median)).toEqual(2);
});
it('median should use fieldConfig nullValueMode.AsZero and count nulls as zero', () => {
const someNulls = createField('y', [3, null, 2, 1, 4]);
someNulls.config.nullValueMode = NullValueMode.AsZero;
expect(reduce(someNulls, ReducerID.median)).toEqual(2);
});
it('can reduce to percentiles', () => {
// This `Array.from` will build an array of elements from 1 to 99
const percentiles = [...Array.from({ length: 99 }, (_, i) => i + 1)];

View File

@@ -283,7 +283,8 @@ export const fieldReducers = new Registry<FieldReducerInfo>(() => [
id: ReducerID.median,
name: 'Median',
description: 'Median Value',
standard: true,
standard: false,
reduce: calculateMedian,
aliasIds: ['median'],
preservesUnits: true,
},
@@ -584,6 +585,7 @@ export function doStandardCalcs(field: Field, ignoreNulls: boolean, nullAsZero:
if (isNumber(calcs.firstNotNull) && isNumber(calcs.diff)) {
calcs.diffperc = (calcs.diff / calcs.firstNotNull) * 100;
}
return calcs;
}
@@ -703,3 +705,32 @@ function calculatePercentile(field: Field, percentile: number, ignoreNulls: bool
const index = Math.round((sorted.length - 1) * percentile);
return sorted[index];
}
function calculateMedian(field: Field<number>, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs {
const numbers: number[] = [];
for (let i = 0; i < field.values.length; i++) {
let currentValue = field.values[i];
if (currentValue == null) {
if (ignoreNulls) {
continue;
}
if (nullAsZero) {
currentValue = 0;
}
}
numbers.push(currentValue);
}
numbers.sort((a, b) => a - b);
const mid = Math.floor(numbers.length / 2);
if (numbers.length % 2 === 0) {
return { median: (numbers[mid - 1] + numbers[mid]) / 2 };
} else {
return { median: numbers[mid] };
}
}

View File

@@ -42,7 +42,10 @@ describe('ensureColumns transformer', () => {
options: {},
};
const data = [seriesA, seriesBC];
const data = [
{ refId: 'A', ...seriesA },
{ refId: 'B', ...seriesBC },
];
await expect(transformDataFrame([cfg], data)).toEmitValuesWith((received) => {
const filtered = received[0];
@@ -109,6 +112,7 @@ describe('ensureColumns transformer', () => {
},
],
"length": 2,
"refId": "joinByField-A-B",
}
`);
});

View File

@@ -592,5 +592,6 @@ export function histogramFieldsToFrame(info: HistogramFields, theme?: GrafanaThe
type: DataFrameType.Histogram,
},
fields: [info.xMin, info.xMax, ...info.counts],
refId: `${DataTransformerID.histogram}`,
};
}

View File

@@ -42,6 +42,7 @@ export const joinByFieldTransformer: SynchronousDataTransformerInfo<JoinByFieldO
}
const joined = joinDataFrames({ frames: data, joinBy, mode: options.mode });
if (joined) {
joined.refId = `${DataTransformerID.joinByField}-${data.map((frame) => frame.refId).join('-')}`;
return [joined];
}
}

View File

@@ -43,7 +43,10 @@ export const mergeTransformer: DataTransformerInfo<MergeTransformerOptions> = {
const fieldNames = new Set<string>();
const fieldIndexByName: Record<string, Record<number, number>> = {};
const fieldNamesForKey: string[] = [];
const dataFrame = new MutableDataFrame();
const dataFrame = new MutableDataFrame({
refId: `${DataTransformerID.merge}-${data.map((frame) => frame.refId).join('-')}`,
fields: [],
});
for (let frameIndex = 0; frameIndex < data.length; frameIndex++) {
const frame = data[frameIndex];

View File

@@ -56,7 +56,9 @@ export const reduceTransformer: DataTransformerInfo<ReduceTransformerOptions> =
// Add a row for each series
const res = reduceSeriesToRows(data, matcher, options.reducers, options.labelsToFields);
return res ? [res] : [];
return res
? [{ ...res, refId: `${DataTransformerID.reduce}-${data.map((frame) => frame.refId).join('-')}` }]
: [];
})
),
};

View File

@@ -37,7 +37,10 @@ export const seriesToRowsTransformer: DataTransformerInfo<SeriesToRowsTransforme
const timeFieldByIndex: Record<number, number> = {};
const targetFields = new Set<string>();
const dataFrame = new MutableDataFrame();
const dataFrame = new MutableDataFrame({
refId: `${DataTransformerID.seriesToRows}-${data.map((frame) => frame.refId).join('-')}`,
fields: [],
});
const metricField: Field = {
name: TIME_SERIES_METRIC_FIELD_NAME,
values: [],

View File

@@ -80,6 +80,7 @@ function transposeDataFrame(options: TransposeTransformerOptions, data: DataFram
...frame,
fields: newFields,
length: Math.max(...newFields.map((field) => field.values.length)),
refId: `${DataTransformerID.transpose}-${frame.refId}`,
};
});
}

View File

@@ -252,4 +252,6 @@ export interface FeatureToggles {
teamHttpHeadersMimir?: boolean;
ABTestFeatureToggleA?: boolean;
ABTestFeatureToggleB?: boolean;
queryLibraryDashboards?: boolean;
elasticsearchImprovedParsing?: boolean;
}

View File

@@ -40,7 +40,7 @@
},
"devDependencies": {
"@rollup/plugin-node-resolve": "16.0.0",
"@types/node": "22.10.5",
"@types/node": "22.10.6",
"@types/semver": "7.5.8",
"esbuild": "0.24.2",
"rimraf": "6.0.1",

View File

@@ -68,7 +68,7 @@
"@types/d3": "^7",
"@types/jest": "^29.5.4",
"@types/lodash": "4.17.14",
"@types/node": "22.10.5",
"@types/node": "22.10.6",
"@types/react": "18.3.3",
"@types/react-virtualized-auto-sizer": "1.0.4",
"@types/tinycolor2": "1.4.6",

View File

@@ -45,7 +45,7 @@
"@svgr/plugin-prettier": "^8.1.0",
"@svgr/plugin-svgo": "^8.1.0",
"@types/babel__core": "^7",
"@types/node": "22.10.5",
"@types/node": "22.10.6",
"@types/react": "18.3.3",
"@types/react-dom": "18.2.25",
"esbuild": "0.24.2",

View File

@@ -36,7 +36,7 @@
"@testing-library/react": "16.1.0",
"@testing-library/user-event": "14.5.2",
"@types/jest": "^29.5.4",
"@types/node": "22.10.5",
"@types/node": "22.10.6",
"@types/react": "18.3.3",
"@types/systemjs": "6.15.1",
"jest": "^29.6.4",

View File

@@ -92,7 +92,7 @@
"@types/jest": "29.5.14",
"@types/jquery": "3.5.32",
"@types/lodash": "4.17.14",
"@types/node": "22.10.5",
"@types/node": "22.10.6",
"@types/pluralize": "^0.0.33",
"@types/prismjs": "1.26.5",
"@types/react": "18.3.3",
@@ -129,7 +129,7 @@
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-esbuild": "6.1.1",
"rollup-plugin-node-externals": "^8.0.0",
"sass": "1.83.1",
"sass": "1.83.4",
"sass-loader": "16.0.4",
"style-loader": "4.0.0",
"testing-library-selector": "0.3.1",

View File

@@ -112,4 +112,24 @@ describe('addLabelToQuery()', () => {
it('should not add ad-hoc filter bool operator', () => {
expect(addLabelToQuery('ALERTS < bool 1', 'bar', 'baz')).toBe('ALERTS{bar="baz"} < bool 1');
});
it('should add a utf8 label', () => {
expect(addLabelToQuery('{"metric.name"}', 'cenk.erdem', 'muhabbet')).toBe(
'{"metric.name", "cenk.erdem"="muhabbet"}'
);
expect(addLabelToQuery('metric{label="val"}', 'cenk.erdem', 'muhabbet')).toBe(
'metric{label="val", "cenk.erdem"="muhabbet"}'
);
});
it('should not add a utf8 label when it is already applied', () => {
expect(addLabelToQuery('{"metric.name", "cenk.erdem"="muhabbet"}', 'cenk.erdem', 'muhabbet')).toBe(
'{"metric.name", "cenk.erdem"="muhabbet"}'
);
expect(addLabelToQuery('metric{label="val", "cenk.erdem"="muhabbet"}', 'cenk.erdem', 'muhabbet')).toBe(
'metric{label="val", "cenk.erdem"="muhabbet"}'
);
});
});

View File

@@ -54,6 +54,49 @@ describe('buildSelector()', () => {
];
expect(buildSelector(labels)).toEqual('foo{bar="baz"}');
});
describe('utf8 support', () => {
it('metric selector with utf8 metric', () => {
const labels: SelectableLabel[] = [
{ name: '__name__', selected: true, values: [{ name: 'utf8.metric', selected: true }] },
];
expect(buildSelector(labels)).toEqual('{"utf8.metric"}');
});
it('metric selector with utf8 labels', () => {
const labels: SelectableLabel[] = [
{ name: '__name__', selected: true, values: [{ name: 'foo', selected: true }] },
{ name: 'utf8.label', selected: true, values: [{ name: 'baz', selected: true }] },
];
expect(buildSelector(labels)).toEqual('foo{"utf8.label"="baz"}');
});
it('metric selector with utf8 labels and metrics', () => {
const labels: SelectableLabel[] = [
{ name: '__name__', selected: true, values: [{ name: 'utf8.metric', selected: true }] },
{ name: 'utf8.label', selected: true, values: [{ name: 'baz', selected: true }] },
];
expect(buildSelector(labels)).toEqual('{"utf8.metric","utf8.label"="baz"}');
});
it('metric selector with utf8 metric and with utf8/non-utf8 labels', () => {
const labels: SelectableLabel[] = [
{ name: '__name__', selected: true, values: [{ name: 'utf8.metric', selected: true }] },
{ name: 'utf8.label', selected: true, values: [{ name: 'uuu', selected: true }] },
{ name: 'bar', selected: true, values: [{ name: 'baz', selected: true }] },
];
expect(buildSelector(labels)).toEqual('{"utf8.metric","utf8.label"="uuu",bar="baz"}');
});
it('metric selector with non-utf8 metric with utf8/non-utf8 labels', () => {
const labels: SelectableLabel[] = [
{ name: '__name__', selected: true, values: [{ name: 'foo', selected: true }] },
{ name: 'utf8.label', selected: true, values: [{ name: 'uuu', selected: true }] },
{ name: 'bar', selected: true, values: [{ name: 'baz', selected: true }] },
];
expect(buildSelector(labels)).toEqual('foo{"utf8.label"="uuu",bar="baz"}');
});
});
});
describe('facetLabels()', () => {

View File

@@ -19,6 +19,7 @@ import {
import PromQlLanguageProvider from '../language_provider';
import { escapeLabelValueInExactSelector, escapeLabelValueInRegexSelector } from '../language_utils';
import { isValidLegacyName, utf8Support } from '../utf8_support';
// Hard limit on labels to render
const EMPTY_SELECTOR = '{}';
@@ -68,22 +69,38 @@ export interface SelectableLabel {
export function buildSelector(labels: SelectableLabel[]): string {
let singleMetric = '';
const selectedLabels = [];
const selectedLabels: string[] = [];
for (const label of labels) {
if ((label.name === METRIC_LABEL || label.selected) && label.values && label.values.length > 0) {
const selectedValues = label.values.filter((value) => value.selected).map((value) => value.name);
if (selectedValues.length > 1) {
selectedLabels.push(`${label.name}=~"${selectedValues.map(escapeLabelValueInRegexSelector).join('|')}"`);
selectedLabels.push(
`${utf8Support(label.name)}=~"${selectedValues.map(escapeLabelValueInRegexSelector).join('|')}"`
);
} else if (selectedValues.length === 1) {
if (label.name === METRIC_LABEL) {
singleMetric = selectedValues[0];
} else {
selectedLabels.push(`${label.name}="${escapeLabelValueInExactSelector(selectedValues[0])}"`);
selectedLabels.push(`${utf8Support(label.name)}="${escapeLabelValueInExactSelector(selectedValues[0])}"`);
}
}
}
}
return [singleMetric, '{', selectedLabels.join(','), '}'].join('');
const selectorParts: string[] = [];
const isLegacyName = singleMetric === '' || isValidLegacyName(singleMetric);
if (isLegacyName) {
selectorParts.push(singleMetric, '{');
} else {
selectorParts.push('{', `"${singleMetric}"`);
if (selectedLabels.length > 0) {
selectorParts.push(',');
}
}
selectorParts.push(selectedLabels.join(','), '}');
return selectorParts.join('');
}
export function facetLabels(

View File

@@ -1,11 +1,13 @@
// Core grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/completions.ts
import UFuzzy from '@leeoniya/ufuzzy';
import { languages } from 'monaco-editor';
import { config } from '@grafana/runtime';
import { prometheusRegularEscape } from '../../../datasource';
import { escapeLabelValueInExactSelector } from '../../../language_utils';
import { FUNCTIONS } from '../../../promql';
import { isValidLegacyName } from '../../../utf8_support';
import { DataProvider } from './data_provider';
import type { Label, Situation } from './situation';
@@ -14,10 +16,16 @@ import { NeverCaseError } from './util';
export type CompletionType = 'HISTORY' | 'FUNCTION' | 'METRIC_NAME' | 'DURATION' | 'LABEL_NAME' | 'LABEL_VALUE';
// We cannot use languages.CompletionItemInsertTextRule.InsertAsSnippet because grafana-prometheus package isn't compatible
// It should first change the moduleResolution to bundler for TS to correctly resolve the types
// https://github.com/grafana/grafana/pull/96450
const InsertAsSnippet = 4;
type Completion = {
type: CompletionType;
label: string;
insertText: string;
insertTextRules?: languages.CompletionItemInsertTextRule;
detail?: string;
documentation?: string;
triggerOnInsert?: boolean;
@@ -29,6 +37,11 @@ const metricNamesSearch = {
singleError: new UFuzzy({ intraMode: 1 }),
};
// Snippet Marker is telling monaco where to show the cursor and maybe a help text
// With help text example: ${1:labelName}
// labelName will be shown as selected. So user would know what to type next
const snippetMarker = '${1:}';
interface MetricFilterOptions {
metricNames: string[];
inputText: string;
@@ -74,9 +87,16 @@ function getAllMetricNamesCompletions(dataProvider: DataProvider): Completion[]
return dataProvider.metricNamesToMetrics(metricNames).map((metric) => ({
type: 'METRIC_NAME',
label: metric.name,
insertText: metric.name,
detail: `${metric.name} : ${metric.type}`,
documentation: metric.help,
...(metric.isUtf8
? {
insertText: `{"${metric.name}"${snippetMarker}}`,
insertTextRules: InsertAsSnippet,
}
: {
insertText: metric.name,
}),
}));
}
@@ -159,12 +179,22 @@ async function getLabelNamesForCompletions(
dataProvider: DataProvider
): Promise<Completion[]> {
const labelNames = await getLabelNames(metric, otherLabels, dataProvider);
return labelNames.map((text) => ({
type: 'LABEL_NAME',
label: text,
insertText: `${text}${suffix}`,
triggerOnInsert,
}));
return labelNames.map((text) => {
const isUtf8 = !isValidLegacyName(text);
return {
type: 'LABEL_NAME',
label: text,
...(isUtf8
? {
insertText: `"${text}"${suffix}`,
insertTextRules: InsertAsSnippet,
}
: {
insertText: `${text}${suffix}`,
}),
triggerOnInsert,
};
});
}
async function getLabelNamesForSelectorCompletions(

View File

@@ -3,6 +3,7 @@ import type { Monaco } from '@grafana/ui'; // used in TSDoc `@link` below
import PromQlLanguageProvider from '../../../language_provider';
import { PromQuery } from '../../../types';
import { isValidLegacyName } from '../../../utf8_support';
export const CODE_MODE_SUGGESTIONS_INCOMPLETE_EVENT = 'codeModeSuggestionsIncomplete';
@@ -26,6 +27,7 @@ interface Metric {
name: string;
help: string;
type: string;
isUtf8?: boolean;
}
export interface DataProviderParams {
@@ -78,6 +80,7 @@ export class DataProvider {
name: m,
help: metaItem?.help ?? '',
type: metaItem?.type ?? '',
isUtf8: !isValidLegacyName(m),
};
});

View File

@@ -95,6 +95,7 @@ export function getCompletionProvider(
kind: getMonacoCompletionItemKind(item.type, monaco),
label: item.label,
insertText: item.insertText,
insertTextRules: item.insertTextRules,
detail: item.detail,
documentation: item.documentation,
sortText: index.toString().padStart(maxIndexDigits, '0'), // to force the order we have

View File

@@ -56,6 +56,7 @@ describe('situation', () => {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
metricName: 'something',
otherLabels: [],
betweenQuotes: false,
});
assertSituation('sum(something) by (^)', {
@@ -79,34 +80,157 @@ describe('situation', () => {
{ name: 'three', value: 'val3', op: '=~' },
{ name: 'four', value: 'val4', op: '!~' },
],
betweenQuotes: false,
});
assertSituation('{^}', {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
otherLabels: [],
betweenQuotes: false,
});
assertSituation('{one="val1",^}', {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
otherLabels: [{ name: 'one', value: 'val1', op: '=' }],
betweenQuotes: false,
});
// single-quoted label-values with escape
assertSituation("{one='val\\'1',^}", {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
otherLabels: [{ name: 'one', value: "val'1", op: '=' }],
betweenQuotes: false,
});
// double-quoted label-values with escape
assertSituation('{one="val\\"1",^}', {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
otherLabels: [{ name: 'one', value: 'val"1', op: '=' }],
betweenQuotes: false,
});
// backticked label-values with escape (the escape should not be interpreted)
assertSituation('{one=`val\\"1`,^}', {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
otherLabels: [{ name: 'one', value: 'val\\"1', op: '=' }],
betweenQuotes: false,
});
});
describe('utf-8 metric name support', () => {
it('with utf8 metric name no label and no comma', () => {
assertSituation(`{"metric.name"^}`, null);
});
it('with utf8 metric name no label', () => {
assertSituation(`{"metric.name", ^}`, {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
metricName: 'metric.name',
otherLabels: [],
betweenQuotes: false,
});
});
it('with utf8 metric name requesting utf8 labels in quotes', () => {
assertSituation(`{"metric.name", "^"}`, {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
metricName: 'metric.name',
otherLabels: [],
betweenQuotes: true,
});
});
it('with utf8 metric name with a legacy label', () => {
assertSituation(`{"metric.name", label1="val", ^}`, {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
metricName: 'metric.name',
otherLabels: [{ name: 'label1', value: 'val', op: '=' }],
betweenQuotes: false,
});
});
it('with utf8 metric name with a legacy label and no value', () => {
assertSituation(`{"metric.name", label1="^"}`, {
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
metricName: 'metric.name',
labelName: 'label1',
betweenQuotes: true,
otherLabels: [],
});
});
it('with utf8 metric name with a utf8 label and no value', () => {
assertSituation(`{"metric.name", "utf8.label"="^"}`, {
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
metricName: 'metric.name',
labelName: '"utf8.label"',
betweenQuotes: true,
otherLabels: [],
});
});
it('with utf8 metric name with a legacy label and utf8 label', () => {
assertSituation(`{"metric.name", label1="val", "utf8.label"="^"}`, {
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
metricName: 'metric.name',
labelName: `"utf8.label"`,
betweenQuotes: true,
otherLabels: [{ name: 'label1', value: 'val', op: '=' }],
});
});
it('with utf8 metric name with a utf8 label and legacy label', () => {
assertSituation(`{"metric.name", "utf8.label"="val", label1="^"}`, {
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
metricName: 'metric.name',
labelName: `label1`,
betweenQuotes: true,
otherLabels: [{ name: '"utf8.label"', value: 'val', op: '=' }],
});
});
it('with utf8 metric name with grouping', () => {
assertSituation(`sum by (^)(rate({"metric.name", label1="val"}[1m]))`, {
type: 'IN_GROUPING',
metricName: 'metric.name',
otherLabels: [],
});
});
});
it('utf-8 label support', () => {
assertSituation(`metric{"label": "^"}`, null);
assertSituation(`metric{"label with space": "^"}`, null);
assertSituation(`metric{"label_🤖": "^"}`, null);
assertSituation(`metric{"Spaß": "^"}`, null);
assertSituation(`{"metric", "Spaß": "^"}`, null);
assertSituation('something{"job"=^}', {
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
metricName: 'something',
labelName: '"job"',
betweenQuotes: false,
otherLabels: [],
});
assertSituation('something{"job📈"=^}', {
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
metricName: 'something',
labelName: '"job📈"',
betweenQuotes: false,
otherLabels: [],
});
assertSituation('something{"job with space"=^,host="h1"}', {
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
metricName: 'something',
labelName: '"job with space"',
betweenQuotes: false,
otherLabels: [{ name: 'host', value: 'h1', op: '=' }],
});
});
@@ -194,6 +318,7 @@ describe('situation', () => {
{ name: 'three', value: 'val3', op: '=~' },
{ name: 'four', value: 'val4', op: '!~' },
],
betweenQuotes: false,
});
});
});

View File

@@ -18,6 +18,8 @@ import {
NumberDurationLiteralInDurationContext,
parser,
PromQL,
QuotedLabelMatcher,
QuotedLabelName,
StringLiteral,
UnquotedLabelMatcher,
VectorSelector,
@@ -35,8 +37,10 @@ type NodeTypeId =
| typeof GroupingLabels
| typeof Identifier
| typeof UnquotedLabelMatcher
| typeof QuotedLabelMatcher
| typeof LabelMatchers
| typeof LabelName
| typeof QuotedLabelName
| typeof PromQL
| typeof StringLiteral
| typeof VectorSelector
@@ -80,8 +84,10 @@ function walk(node: SyntaxNode, path: Path): SyntaxNode | null {
return current;
}
function getNodeText(node: SyntaxNode, text: string): string {
return text.slice(node.from, node.to);
function getNodeText(node: SyntaxNode, text: string, utf8?: boolean): string {
const nodeFrom = utf8 ? node.from + 1 : node.from;
const nodeTo = utf8 ? node.to - 1 : node.to;
return text.slice(nodeFrom, nodeTo);
}
function parsePromQLStringLiteral(text: string): string {
@@ -140,6 +146,8 @@ export type Situation =
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME';
metricName?: string;
otherLabels: Label[];
// utf8 labels must be in quotes
betweenQuotes: boolean;
}
| {
type: 'IN_GROUPING';
@@ -170,6 +178,10 @@ const RESOLVERS: Resolver[] = [
path: [LabelMatchers, VectorSelector],
fun: resolveLabelKeysWithEquals,
},
{
path: [StringLiteral, QuotedLabelName, LabelMatchers, VectorSelector],
fun: resolveUtf8LabelKeysWithEquals,
},
{
path: [PromQL],
fun: resolveTopLevel,
@@ -182,6 +194,10 @@ const RESOLVERS: Resolver[] = [
path: [StringLiteral, UnquotedLabelMatcher],
fun: resolveLabelMatcher,
},
{
path: [StringLiteral, QuotedLabelMatcher],
fun: resolveQuotedLabelMatcher,
},
{
path: [ERROR_NODE_NAME, BinaryExpr, PromQL],
fun: resolveTopLevel,
@@ -190,6 +206,10 @@ const RESOLVERS: Resolver[] = [
path: [ERROR_NODE_NAME, UnquotedLabelMatcher],
fun: resolveLabelMatcher,
},
{
path: [ERROR_NODE_NAME, QuotedLabelMatcher],
fun: resolveQuotedLabelMatcher,
},
{
path: [ERROR_NODE_NAME, NumberDurationLiteralInDurationContext, MatrixSelector],
fun: resolveDurations,
@@ -217,11 +237,13 @@ function getLabelOp(opNode: SyntaxNode): LabelOperator | null {
}
function getLabel(labelMatcherNode: SyntaxNode, text: string): Label | null {
if (labelMatcherNode.type.id !== UnquotedLabelMatcher) {
const allowedMatchers = new Set([UnquotedLabelMatcher, QuotedLabelMatcher]);
if (!allowedMatchers.has(labelMatcherNode.type.id)) {
return null;
}
const nameNode = walk(labelMatcherNode, [['firstChild', LabelName]]);
const nameNode =
walk(labelMatcherNode, [['firstChild', LabelName]]) ?? walk(labelMatcherNode, [['firstChild', QuotedLabelName]]);
if (nameNode === null) {
return null;
@@ -254,8 +276,17 @@ function getLabels(labelMatchersNode: SyntaxNode, text: string): Label[] {
return [];
}
const labelNodes = labelMatchersNode.getChildren(UnquotedLabelMatcher);
return labelNodes.map((ln) => getLabel(ln, text)).filter(notEmpty);
const matchers = [UnquotedLabelMatcher, QuotedLabelMatcher];
return matchers.reduce<Label[]>((acc, matcher) => {
labelMatchersNode.getChildren(matcher).forEach((ln) => {
const label = getLabel(ln, text);
if (notEmpty(label)) {
acc.push(label);
}
});
return acc;
}, []);
}
function getNodeChildren(node: SyntaxNode): SyntaxNode[] {
@@ -299,12 +330,20 @@ function resolveLabelsForGrouping(node: SyntaxNode, text: string, pos: number):
return null;
}
const metricIdNode = getNodeInSubtree(bodyNode, Identifier);
if (metricIdNode === null) {
const metricIdNode = getNodeInSubtree(bodyNode, Identifier) ?? getNodeInSubtree(bodyNode, StringLiteral);
if (!metricIdNode) {
return null;
}
const metricName = getNodeText(metricIdNode, text);
// Let's check whether it's a utf8 metric.
// A utf8 metric must be a StringLiteral and its parent must be a QuotedLabelName
if (metricIdNode.type.id === StringLiteral && metricIdNode.parent?.type.id !== QuotedLabelName) {
return null;
}
const metricName = getNodeText(metricIdNode, text, metricIdNode.type.id === StringLiteral);
return {
type: 'IN_GROUPING',
metricName,
@@ -341,29 +380,54 @@ function resolveLabelMatcher(node: SyntaxNode, text: string, pos: number): Situa
// we need to remove "our" label from all-labels, if it is in there
const otherLabels = allLabels.filter((label) => label.name !== labelName);
const metricNameNode = walk(labelMatchersNode, [
['parent', VectorSelector],
['firstChild', Identifier],
]);
if (metricNameNode === null) {
// we are probably in a situation without a metric name
return {
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
labelName,
betweenQuotes: inStringNode,
otherLabels,
};
}
const metricName = getNodeText(metricNameNode, text);
const metricName = getMetricName(labelMatchersNode, text);
// we are probably in a situation without a metric name
return {
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
metricName,
labelName,
betweenQuotes: inStringNode,
otherLabels,
...(metricName ? { metricName } : {}),
};
}
function resolveQuotedLabelMatcher(node: SyntaxNode, text: string, pos: number): Situation | null {
// we can arrive here in two situation. `node` is either:
// - a StringNode (like in `{"job"="^"}`)
// - or an error node (like in `{"job"=^}`)
const inStringNode = !node.type.isError;
const parent = walk(node, [['parent', QuotedLabelMatcher]]);
if (parent === null) {
return null;
}
const labelNameNode = walk(parent, [['firstChild', QuotedLabelName]]);
if (labelNameNode === null) {
return null;
}
const labelName = getNodeText(labelNameNode, text);
const labelMatchersNode = walk(parent, [['parent', LabelMatchers]]);
if (labelMatchersNode === null) {
return null;
}
// now we need to find the other names
const allLabels = getLabels(labelMatchersNode, text);
// we need to remove "our" label from all-labels, if it is in there
const otherLabels = allLabels.filter((label) => label.name !== labelName);
const metricName = getMetricName(parent.parent!, text);
return {
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
labelName,
betweenQuotes: inStringNode,
otherLabels,
...(metricName ? { metricName } : {}),
};
}
@@ -388,7 +452,7 @@ function resolveDurations(node: SyntaxNode, text: string, pos: number): Situatio
function resolveLabelKeysWithEquals(node: SyntaxNode, text: string, pos: number): Situation | null {
// next false positive:
// `something{a="1"^}`
const child = walk(node, [['firstChild', UnquotedLabelMatcher]]);
let child = walk(node, [['firstChild', UnquotedLabelMatcher]]);
if (child !== null) {
// means the label-matching part contains at least one label already.
//
@@ -403,28 +467,72 @@ function resolveLabelKeysWithEquals(node: SyntaxNode, text: string, pos: number)
}
}
const metricNameNode = walk(node, [
// next false positive:
// `{"utf8.metric"^}`
child = walk(node, [['firstChild', QuotedLabelName]]);
if (child !== null) {
// means the label-matching part contains a utf8 metric.
//
// in this case, we will need to have a `,` character at the end,
// to be able to suggest adding the next label.
// the area between the end-of-the-child-node and the cursor-pos
// must contain a `,` in this case.
const textToCheck = text.slice(child.to, pos);
if (!textToCheck.includes(',')) {
return null;
}
}
const otherLabels = getLabels(node, text);
const metricName = getMetricName(node, text);
return {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
otherLabels,
betweenQuotes: false,
...(metricName ? { metricName } : {}),
};
}
function resolveUtf8LabelKeysWithEquals(node: SyntaxNode, text: string, pos: number): Situation | null {
const otherLabels = getLabels(node, text);
const metricName = node.parent?.parent ? getMetricName(node.parent.parent, text) : null;
return {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
otherLabels,
betweenQuotes: true,
...(metricName ? { metricName } : {}),
};
}
function getMetricName(node: SyntaxNode, text: string): string | null {
// Legacy Metric metric_name{label="value"}
const legacyMetricNameNode = walk(node, [
['parent', VectorSelector],
['firstChild', Identifier],
]);
const otherLabels = getLabels(node, text);
if (metricNameNode === null) {
// we are probably in a situation without a metric name.
return {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
otherLabels,
};
if (legacyMetricNameNode) {
return getNodeText(legacyMetricNameNode, text);
}
const metricName = getNodeText(metricNameNode, text);
// check for a utf-8 metric
// utf-8 metric {"metric.name", label="value"}
const utf8MetricNameNode = walk(node, [
['parent', VectorSelector],
['firstChild', LabelMatchers],
['firstChild', QuotedLabelName],
['firstChild', StringLiteral],
]);
return {
type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
metricName,
otherLabels,
};
if (utf8MetricNameNode) {
return getNodeText(utf8MetricNameNode, text, true);
}
// no metric name
return null;
}
// we find the first error-node in the tree that is at the cursor-position.
@@ -461,10 +569,10 @@ export function getSituation(text: string, pos: number): Situation | null {
}
/**
PromQL
Expr
VectorSelector
LabelMatchers
PromQL
Expr
VectorSelector
LabelMatchers
*/
const tree = parser.parse(text);

View File

@@ -71,6 +71,7 @@ import {
RawRecordingRules,
RuleQueryMapping,
} from './types';
import { utf8Support, wrapUtf8Filters } from './utf8_support';
import { PrometheusVariableSupport } from './variables';
const ANNOTATION_QUERY_STEP_DEFAULT = '60s';
@@ -925,7 +926,21 @@ export class PrometheusDatasource
// We need a first replace to evaluate variables before applying adhoc filters
// This is required for an expression like `metric > $VAR` where $VAR is a float to which we must not add adhoc filters
const expr = this.templateSrv.replace(target.expr, variables, this.interpolateQueryExpr);
const expr = this.templateSrv.replace(
target.expr,
variables,
(value: string | string[] = [], variable: QueryVariableModel | CustomVariableModel) => {
if (typeof value === 'string' && target.fromExploreMetrics) {
if (variable.name === 'filters') {
return wrapUtf8Filters(value);
}
if (variable.name === 'groupby') {
return utf8Support(value);
}
}
return this.interpolateQueryExpr(value, variable);
}
);
// Apply ad-hoc filters
// When ad-hoc filters are applied, we replace again the variables in case the ad-hoc filters also reference a variable

View File

@@ -4,7 +4,7 @@ import { AbstractLabelOperator, dateTime, TimeRange } from '@grafana/data';
import { DEFAULT_SERIES_LIMIT } from './components/PrometheusMetricsBrowser';
import { Label } from './components/monaco-query-field/monaco-completion-provider/situation';
import { PrometheusDatasource } from './datasource';
import LanguageProvider from './language_provider';
import LanguageProvider, { removeQuotesIfExist } from './language_provider';
import { getClientCacheDurationInMinutes, getPrometheusTime, getRangeSnapInterval } from './language_utils';
import { PrometheusCacheLevel, PromQuery } from './types';
@@ -120,7 +120,13 @@ describe('Language completion provider', () => {
const labelName = 'job';
const labelValue = 'grafana';
getSeriesLabels(`{${labelName}="${labelValue}"}`, [{ name: labelName, value: labelValue, op: '=' }] as Label[]);
getSeriesLabels(`{${labelName}="${labelValue}"}`, [
{
name: labelName,
value: labelValue,
op: '=',
},
] as Label[]);
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
`/api/v1/labels`,
@@ -145,7 +151,13 @@ describe('Language completion provider', () => {
const labelName = 'job';
const labelValue = 'grafana';
getSeriesLabels(`{${labelName}="${labelValue}"}`, [{ name: labelName, value: labelValue, op: '=' }] as Label[]);
getSeriesLabels(`{${labelName}="${labelValue}"}`, [
{
name: labelName,
value: labelValue,
op: '=',
},
] as Label[]);
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/series',
@@ -174,7 +186,13 @@ describe('Language completion provider', () => {
const labelName = 'job';
const labelValue = 'grafana';
getSeriesLabels(`{${labelName}="${labelValue}"}`, [{ name: labelName, value: labelValue, op: '=' }] as Label[]);
getSeriesLabels(`{${labelName}="${labelValue}"}`, [
{
name: labelName,
value: labelValue,
op: '=',
},
] as Label[]);
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
`/api/v1/labels`,
@@ -569,14 +587,14 @@ describe('Language completion provider', () => {
it('should interpolate variable in series', () => {
const languageProvider = new LanguageProvider({
...defaultDatasource,
interpolateString: (string: string) => string.replace(/\$/, 'interpolated-'),
interpolateString: (string: string) => string.replace(/\$/g, 'interpolated_'),
} as PrometheusDatasource);
const fetchLabelValues = languageProvider.fetchLabelValues;
const requestSpy = jest.spyOn(languageProvider, 'request');
fetchLabelValues('$job');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/label/interpolated-job/values',
'/api/v1/label/interpolated_job/values',
[],
{
end: toPrometheusTimeString,
@@ -585,6 +603,69 @@ describe('Language completion provider', () => {
undefined
);
});
it('should fetch with encoded utf8 label', () => {
const languageProvider = new LanguageProvider({
...defaultDatasource,
interpolateString: (string: string) => string.replace(/\$/g, 'http.status:sum'),
} as PrometheusDatasource);
const fetchLabelValues = languageProvider.fetchLabelValues;
const requestSpy = jest.spyOn(languageProvider, 'request');
fetchLabelValues('"http.status:sum"');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/label/U__http_2e_status:sum/values',
[],
{
end: toPrometheusTimeString,
start: fromPrometheusTimeString,
},
undefined
);
});
});
describe('fetchSeriesValuesWithMatch', () => {
it('should fetch with encoded utf8 label', () => {
const languageProvider = new LanguageProvider({
...defaultDatasource,
interpolateString: (string: string) => string.replace(/\$/g, 'http.status:sum'),
} as PrometheusDatasource);
const fetchSeriesValuesWithMatch = languageProvider.fetchSeriesValuesWithMatch;
const requestSpy = jest.spyOn(languageProvider, 'request');
fetchSeriesValuesWithMatch('"http.status:sum"', '{__name__="a_utf8_http_requests_total"}');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/label/U__http_2e_status:sum/values',
[],
{
end: toPrometheusTimeString,
start: fromPrometheusTimeString,
'match[]': '{__name__="a_utf8_http_requests_total"}',
},
undefined
);
});
it('should fetch without encoding for standard prometheus labels', () => {
const languageProvider = new LanguageProvider({
...defaultDatasource,
} as PrometheusDatasource);
const fetchSeriesValuesWithMatch = languageProvider.fetchSeriesValuesWithMatch;
const requestSpy = jest.spyOn(languageProvider, 'request');
fetchSeriesValuesWithMatch('"http_status_sum"', '{__name__="a_utf8_http_requests_total"}');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/label/http_status_sum/values',
[],
{
end: toPrometheusTimeString,
start: fromPrometheusTimeString,
'match[]': '{__name__="a_utf8_http_requests_total"}',
},
undefined
);
});
});
describe('disabled metrics lookup', () => {
@@ -650,3 +731,59 @@ describe('Language completion provider', () => {
});
});
});
describe('removeQuotesIfExist', () => {
it('removes quotes from a string with double quotes', () => {
const input = '"hello"';
const result = removeQuotesIfExist(input);
expect(result).toBe('hello');
});
it('returns the original string if it does not start and end with quotes', () => {
const input = 'hello';
const result = removeQuotesIfExist(input);
expect(result).toBe('hello');
});
it('returns the original string if it has mismatched quotes', () => {
const input = '"hello';
const result = removeQuotesIfExist(input);
expect(result).toBe('"hello');
});
it('removes quotes for strings with special characters inside quotes', () => {
const input = '"hello, world!"';
const result = removeQuotesIfExist(input);
expect(result).toBe('hello, world!');
});
it('removes quotes for strings with spaces inside quotes', () => {
const input = '" "';
const result = removeQuotesIfExist(input);
expect(result).toBe(' ');
});
it('returns the original string for an empty string', () => {
const input = '';
const result = removeQuotesIfExist(input);
expect(result).toBe('');
});
it('returns the original string if the string only has a single quote character', () => {
const input = '"';
const result = removeQuotesIfExist(input);
expect(result).toBe('"');
});
it('handles strings with nested quotes correctly', () => {
const input = '"nested \"quotes\""';
const result = removeQuotesIfExist(input);
expect(result).toBe('nested \"quotes\"');
});
it('removes quotes from a numeric string wrapped in quotes', () => {
const input = '"12345"';
const result = removeQuotesIfExist(input);
expect(result).toBe('12345');
});
});

View File

@@ -29,6 +29,7 @@ import {
import PromqlSyntax from './promql';
import { buildVisualQueryFromString } from './querybuilder/parsing';
import { PrometheusCacheLevel, PromMetricsMetadata, PromQuery } from './types';
import { escapeForUtf8Support, isValidLegacyName } from './utf8_support';
const DEFAULT_KEYS = ['job', 'instance'];
const EMPTY_SELECTOR = '{}';
@@ -208,7 +209,8 @@ export default class PromQlLanguageProvider extends LanguageProvider {
fetchLabelValues = async (key: string): Promise<string[]> => {
const params = this.datasource.getAdjustedInterval(this.timeRange);
const interpolatedName = this.datasource.interpolateString(key);
const url = `/api/v1/label/${interpolatedName}/values`;
const interpolatedAndEscapedName = escapeForUtf8Support(removeQuotesIfExist(interpolatedName));
const url = `/api/v1/label/${interpolatedAndEscapedName}/values`;
const value = await this.request(url, [], params, this.getDefaultCacheHeaders());
return value ?? [];
};
@@ -232,10 +234,11 @@ export default class PromQlLanguageProvider extends LanguageProvider {
queries?.forEach((q) => {
const visualQuery = buildVisualQueryFromString(q.expr);
if (visualQuery.query.metric !== '') {
searchParams.append('match[]', visualQuery.query.metric);
const isUtf8Metric = !isValidLegacyName(visualQuery.query.metric);
searchParams.append('match[]', isUtf8Metric ? `{"${visualQuery.query.metric}"}` : visualQuery.query.metric);
if (visualQuery.query.binaryQueries) {
visualQuery.query.binaryQueries.forEach((bq) => {
searchParams.append('match[]', bq.query.metric);
searchParams.append('match[]', isUtf8Metric ? `{"${bq.query.metric}"}` : bq.query.metric);
});
}
}
@@ -263,7 +266,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
getSeriesValues = async (labelName: string, selector: string): Promise<string[]> => {
if (!this.datasource.hasLabelsMatchAPISupport()) {
const data = await this.getSeries(selector);
return data[labelName] ?? [];
return data[removeQuotesIfExist(labelName)] ?? [];
}
return await this.fetchSeriesValuesWithMatch(labelName, selector);
};
@@ -297,7 +300,14 @@ export default class PromQlLanguageProvider extends LanguageProvider {
requestOptions = undefined;
}
const value = await this.request(`/api/v1/label/${interpolatedName}/values`, [], urlParams, requestOptions);
const interpolatedAndEscapedName = escapeForUtf8Support(removeQuotesIfExist(interpolatedName ?? ''));
const value = await this.request(
`/api/v1/label/${interpolatedAndEscapedName}/values`,
[],
urlParams,
requestOptions
);
return value ?? [];
};
@@ -493,3 +503,10 @@ function isCancelledError(error: unknown): error is {
} {
return typeof error === 'object' && error !== null && 'cancelled' in error && error.cancelled === true;
}
// For utf8 labels we use quotes around the label
// While requesting the label values we must remove the quotes
export function removeQuotesIfExist(input: string): string {
const match = input.match(/^"(.*)"$/); // extract the content inside the quotes
return match?.[1] ?? input;
}

View File

@@ -11,6 +11,7 @@ import {
getPrometheusTime,
getRangeSnapInterval,
parseSelector,
processLabels,
toPromLikeQuery,
truncateResult,
} from './language_utils';
@@ -565,3 +566,54 @@ describe('truncateResult', () => {
expect(array[999]).toBe(999);
});
});
describe('processLabels', () => {
it('export abstract query to expr', () => {
const labels: Array<{ [key: string]: string }> = [
{ label1: 'value1' },
{ label2: 'value2' },
{ label3: 'value3' },
{ label1: 'value1' },
{ label1: 'value1b' },
];
expect(processLabels(labels)).toEqual({
keys: ['label1', 'label2', 'label3'],
values: { label1: ['value1', 'value1b'], label2: ['value2'], label3: ['value3'] },
});
});
it('dont wrap utf8 label values with quotes', () => {
const labels: Array<{ [key: string]: string }> = [
{ label1: 'value1' },
{ label2: 'value2' },
{ label3: 'value3 with space' },
{ label4: 'value4.with.dot' },
];
expect(processLabels(labels)).toEqual({
keys: ['label1', 'label2', 'label3', 'label4'],
values: {
label1: ['value1'],
label2: ['value2'],
label3: [`value3 with space`],
label4: [`value4.with.dot`],
},
});
});
it('dont wrap utf8 labels with quotes', () => {
const labels: Array<{ [key: string]: string }> = [
{ 'label1 with space': 'value1' },
{ 'label2.with.dot': 'value2' },
];
expect(processLabels(labels)).toEqual({
keys: ['label1 with space', 'label2.with.dot'],
values: {
'label1 with space': ['value1'],
'label2.with.dot': ['value2'],
},
});
});
});

View File

@@ -478,7 +478,10 @@ export function extractLabelMatchers(tokens: Array<string | Token>): AbstractLab
export function getRangeSnapInterval(
cacheLevel: PrometheusCacheLevel,
range: TimeRange
): { start: string; end: string } {
): {
start: string;
end: string;
} {
// Don't round the range if we're not caching
if (cacheLevel === PrometheusCacheLevel.None) {
return {

View File

@@ -8,6 +8,7 @@ import { PrometheusDatasource } from './datasource';
import { getPrometheusTime } from './language_utils';
import { PrometheusMetricFindQuery } from './metric_find_query';
import { PromApplication, PromOptions } from './types';
import { escapeForUtf8Support } from './utf8_support';
const fetchMock = jest.fn((options: BackendSrvRequest): Observable<FetchResponse<BackendDataSourceResponse>> => {
return of({} as unknown as FetchResponse);
@@ -413,5 +414,51 @@ describe('PrometheusMetricFindQuery', () => {
});
});
// </ ModernPrometheus>
describe('utf8 metric and label support', () => {
it('utf8 label - label_values(a_utf8_http_requests_total,instance.test) should generate label values query', () => {
const metricName = 'a_utf8_http_requests_total';
const label = 'instance.test';
const query = `label_values(${metricName},${label})`;
const metricFindQuery = new PrometheusMetricFindQuery(prometheusDatasource, query);
metricFindQuery.process(raw);
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith({
method: 'GET',
url: `/api/datasources/uid/ABCDEF/resources/api/v1/label/${escapeForUtf8Support(label)}/values?match%5B%5D=${metricName}&start=1524650400&end=1524654000`,
hideFromInspector: true,
headers: {},
});
});
it('utf8 metric - label_values(utf8.http_requests_total,instance_test) should generate label values query', () => {
const metricName = 'utf8.http_requests_total';
const label = 'instance_test';
const query = `label_values(${metricName},${label})`;
const metricFindQuery = new PrometheusMetricFindQuery(prometheusDatasource, query);
metricFindQuery.process(raw);
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith({
method: 'GET',
url: `/api/datasources/uid/ABCDEF/resources/api/v1/label/${label}/values?match%5B%5D=${metricName}&start=1524650400&end=1524654000`,
hideFromInspector: true,
headers: {},
});
});
it('utf8 metric and label - label_values(utf8.http_requests_total,instance.test) should generate label values query', () => {
const metricName = 'utf8.http_requests_total';
const label = 'instance.test';
const query = `label_values(${metricName},${label})`;
const metricFindQuery = new PrometheusMetricFindQuery(prometheusDatasource, query);
metricFindQuery.process(raw);
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith({
method: 'GET',
url: `/api/datasources/uid/ABCDEF/resources/api/v1/label/${escapeForUtf8Support(label)}/values?match%5B%5D=${metricName}&start=1524650400&end=1524654000`,
hideFromInspector: true,
headers: {},
});
});
});
});
});

View File

@@ -8,9 +8,11 @@ import { getPrometheusTime } from './language_utils';
import {
PrometheusLabelNamesRegex,
PrometheusLabelNamesRegexWithMatch,
PrometheusLabelValuesRegex,
PrometheusMetricNamesRegex,
PrometheusQueryResultRegex,
} from './migrations/variableMigration';
import { escapeForUtf8Support, isValidLegacyName } from './utf8_support';
export class PrometheusMetricFindQuery {
range: TimeRange;
@@ -28,7 +30,7 @@ export class PrometheusMetricFindQuery {
this.range = timeRange;
const labelNamesRegex = PrometheusLabelNamesRegex;
const labelNamesRegexWithMatch = PrometheusLabelNamesRegexWithMatch;
const labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]*)\)\s*$/;
const labelValuesRegex = PrometheusLabelValuesRegex;
const metricNamesRegex = PrometheusMetricNamesRegex;
const queryResultRegex = PrometheusQueryResultRegex;
const labelNamesQuery = this.query.match(labelNamesRegex);
@@ -83,8 +85,13 @@ export class PrometheusMetricFindQuery {
const end = getPrometheusTime(this.range.to, true);
const params = { ...(metric && { 'match[]': metric }), start: start.toString(), end: end.toString() };
let escapedLabel = label;
if (!isValidLegacyName(label)) {
escapedLabel = escapeForUtf8Support(label);
}
if (!metric || this.datasource.hasLabelsMatchAPISupport()) {
const url = `/api/v1/label/${label}/values`;
const url = `/api/v1/label/${escapedLabel}/values`;
return this.datasource.metadataRequest(url, params).then((result) => {
return _map(result.data.data, (value) => {

View File

@@ -4,8 +4,7 @@ import { buildVisualQueryFromString } from '../querybuilder/parsing';
import { PromVariableQuery, PromVariableQueryType as QueryType } from '../types';
export const PrometheusLabelNamesRegex = /^label_names\(\)\s*$/;
// Note that this regex is different from the one in metric_find_query.ts because this is used pre-interpolation
export const PrometheusLabelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_$][a-zA-Z0-9_]*)\)\s*$/;
export const PrometheusLabelValuesRegex = /^label_values\((?:(.+),\s*)?(.+)\)\s*$/;
export const PrometheusMetricNamesRegex = /^metrics\((.+)\)\s*$/;
export const PrometheusQueryResultRegex = /^query_result\((.+)\)\s*$/;
export const PrometheusLabelNamesRegexWithMatch = /^label_names\((.+)\)\s*$/;
@@ -97,7 +96,7 @@ export function migrateVariableQueryToEditor(rawQuery: string | PromVariableQuer
return queryBase;
}
// migrate it back to a string with the correct varialbes in place
// migrate it back to a string with the correct variables in place
export function migrateVariableEditorBackToVariableSupport(QueryVariable: PromVariableQuery): string {
switch (QueryVariable.qryType) {
case QueryType.LabelNames:

View File

@@ -334,3 +334,82 @@ describe('PromQueryModeller', () => {
).toBe('cluster_namespace_slug_dialer_name <= bool 2');
});
});
describe('PromQueryModeller with utf8 support', () => {
const modeller = new PromQueryModeller();
it('should render nothing if there is nothing', () => {
expect(
modeller.renderQuery({
metric: undefined,
labels: [],
operations: [],
})
).toBe('');
expect(
modeller.renderQuery({
metric: '',
labels: [],
operations: [],
})
).toBe('');
});
it('should render legacy metric name as usual', () => {
expect(
modeller.renderQuery({
metric: 'not_a_utf8_metric',
labels: [],
operations: [],
})
).toBe('not_a_utf8_metric');
});
it('can render utf8 metric name in curly braces', () => {
expect(
modeller.renderQuery({
metric: 'a.utf8.metric',
labels: [],
operations: [],
})
).toBe('{"a.utf8.metric"}');
});
it('can render utf8 metric name in curly braces with legacy labels', () => {
expect(
modeller.renderQuery({
metric: 'a.utf8.metric',
labels: [
{
label: 'label',
value: 'value',
op: '=',
},
],
operations: [],
})
).toBe('{"a.utf8.metric", label="value"}');
});
it('can render utf8 metric name in curly braces with legacy and utf8 labels', () => {
expect(
modeller.renderQuery({
metric: 'a.utf8.metric',
labels: [
{
label: 'label',
value: 'value',
op: '=',
},
{
label: 'utf8.label',
value: 'value',
op: '=',
},
],
operations: [],
})
).toBe('{"a.utf8.metric", label="value", "utf8.label"="value"}');
});
});

View File

@@ -2,8 +2,7 @@ import { useCallback, useState } from 'react';
import { SelectableValue } from '@grafana/data';
import { EditorField, EditorFieldGroup, InputGroup } from '@grafana/experimental';
import { Button, InlineField, InlineFieldRow } from '@grafana/ui';
import { Combobox, ComboboxOption } from '@grafana/ui/src/components/Combobox/Combobox';
import { Button, InlineField, InlineFieldRow, Combobox, ComboboxOption } from '@grafana/ui';
import { PrometheusDatasource } from '../../datasource';
import { regexifyLabelValuesQueryString } from '../parsingUtils';

View File

@@ -93,7 +93,7 @@ export const PromQueryBuilder = memo<PromQueryBuilderProps>((props) => {
{showExplain && (
<OperationExplainedBox
stepNumber={1}
title={<RawQuery query={`${query.metric} ${promQueryModeller.renderLabels(query.labels)}`} lang={lang} />}
title={<RawQuery query={`${promQueryModeller.renderQuery(query)}`} lang={lang} />}
>
{EXPLAIN_LABEL_FILTER_CONTENT}
</OperationExplainedBox>

View File

@@ -25,7 +25,7 @@ export const PromQueryBuilderExplained = memo<PromQueryBuilderExplainedProps>(({
<Stack gap={0.5} direction="column">
<OperationExplainedBox
stepNumber={1}
title={<RawQuery query={`${visQuery.metric} ${promQueryModeller.renderLabels(visQuery.labels)}`} lang={lang} />}
title={<RawQuery query={`${promQueryModeller.renderQuery(visQuery)}`} lang={lang} />}
>
{EXPLAIN_LABEL_FILTER_CONTENT}
</OperationExplainedBox>

View File

@@ -3,6 +3,80 @@ import { buildVisualQueryFromString } from './parsing';
import { PromOperationId, PromVisualQuery } from './types';
describe('buildVisualQueryFromString', () => {
describe('utf8 support', () => {
it('supports uts-8 label names', () => {
expect(buildVisualQueryFromString('{"glück:🍀.dot"="luck"} == 11')).toEqual({
query: {
labels: [
{
label: 'glück:🍀.dot',
op: '=',
value: 'luck',
},
],
metric: '',
operations: [
{
id: PromOperationId.EqualTo,
params: [11, false],
},
],
},
errors: [],
});
});
it('supports uts-8 metric names', () => {
expect(buildVisualQueryFromString('{"I am a metric"}')).toEqual({
query: {
labels: [],
metric: 'I am a metric',
operations: [],
},
errors: [],
});
});
it('supports uts-8 metric names with labels', () => {
expect(buildVisualQueryFromString('{"metric.name", label_field="label value"}')).toEqual({
query: {
labels: [
{
label: 'label_field',
op: '=',
value: 'label value',
},
],
metric: 'metric.name',
operations: [],
},
errors: [],
});
});
it('supports uts-8 metric names with utf8 labels', () => {
expect(buildVisualQueryFromString('{"metric.name", "glück:🍀.dot"="luck"} == 11')).toEqual({
query: {
labels: [
{
label: 'glück:🍀.dot',
op: '=',
value: 'luck',
},
],
metric: 'metric.name',
operations: [
{
id: PromOperationId.EqualTo,
params: [11, false],
},
],
},
errors: [],
});
});
});
it('creates no errors for empty query', () => {
expect(buildVisualQueryFromString('')).toEqual(
noErrors({
@@ -246,6 +320,31 @@ describe('buildVisualQueryFromString', () => {
);
});
it('parses query with aggregation by utf8 labels', () => {
const visQuery = {
metric: 'metric_name',
labels: [
{
label: 'instance',
op: '=',
value: 'internal:3000',
},
],
operations: [
{
id: '__sum_by',
params: ['cluster', '"app.version"'],
},
],
};
expect(
buildVisualQueryFromString('sum(metric_name{instance="internal:3000"}) by ("app.version", cluster)')
).toEqual(noErrors(visQuery));
expect(
buildVisualQueryFromString('sum by ("app.version", cluster)(metric_name{instance="internal:3000"})')
).toEqual(noErrors(visQuery));
});
it('parses aggregation with params', () => {
expect(buildVisualQueryFromString('topk(5, http_requests_total)')).toEqual(
noErrors({

View File

@@ -12,6 +12,7 @@ import {
GroupingLabels,
Identifier,
LabelName,
QuotedLabelName,
MatchingModifierClause,
MatchOp,
NumberDurationLiteral,
@@ -19,6 +20,7 @@ import {
ParenExpr,
parser,
StringLiteral,
QuotedLabelMatcher,
UnquotedLabelMatcher,
VectorSelector,
Without,
@@ -145,9 +147,33 @@ export function handleExpression(expr: string, node: SyntaxNode, context: Contex
break;
}
case QuotedLabelName: {
// Usually we got the metric name above in the Identifier case.
// If we didn't get the name that's potentially we have it in curly braces as quoted string.
// It must be quoted because that's how utf8 metric names should be defined
// See proposal https://github.com/prometheus/proposals/blob/main/proposals/2023-08-21-utf8.md
if (visQuery.metric === '') {
const strLiteral = node.getChild(StringLiteral);
const quotedMetric = getString(expr, strLiteral);
visQuery.metric = quotedMetric.slice(1, -1);
}
break;
}
case QuotedLabelMatcher: {
const quotedLabel = getLabel(expr, node, QuotedLabelName);
quotedLabel.label = quotedLabel.label.slice(1, -1);
visQuery.labels.push(quotedLabel);
const err = node.getChild(ErrorId);
if (err) {
context.errors.push(makeError(expr, err));
}
break;
}
case UnquotedLabelMatcher: {
// Same as MetricIdentifier should be just one per query.
visQuery.labels.push(getLabel(expr, node));
visQuery.labels.push(getLabel(expr, node, LabelName));
const err = node.getChild(ErrorId);
if (err) {
context.errors.push(makeError(expr, err));
@@ -202,8 +228,12 @@ function isIntervalVariableError(node: SyntaxNode) {
return node.prevSibling?.firstChild?.type.id === VectorSelector;
}
function getLabel(expr: string, node: SyntaxNode): QueryBuilderLabelFilter {
const label = getString(expr, node.getChild(LabelName));
function getLabel(
expr: string,
node: SyntaxNode,
labelType: typeof LabelName | typeof QuotedLabelName
): QueryBuilderLabelFilter {
const label = getString(expr, node.getChild(labelType));
const op = getString(expr, node.getChild(MatchOp));
const value = getString(expr, node.getChild(StringLiteral)).replace(/^["'`]|["'`]$/g, '');
return {
@@ -281,7 +311,7 @@ function handleAggregation(expr: string, node: SyntaxNode, context: Context) {
funcName = `__${funcName}_without`;
}
labels.push(...getAllByType(expr, modifier, LabelName));
labels.push(...getAllByType(expr, modifier, LabelName), ...getAllByType(expr, modifier, QuotedLabelName));
}
const body = node.getChild(FunctionCallBody);

View File

@@ -3,6 +3,7 @@ import { Registry } from '@grafana/data';
import { config } from '@grafana/runtime';
import { prometheusRegularEscape } from '../../datasource';
import { isValidLegacyName, utf8Support } from '../../utf8_support';
import { PromVisualQueryOperationCategory } from '../types';
import { QueryBuilderLabelFilter, QueryBuilderOperation, QueryBuilderOperationDef, VisualQueryModeller } from './types';
@@ -97,14 +98,28 @@ export abstract class LokiAndPromQueryModellerBase implements VisualQueryModelle
if (config.featureToggles.prometheusSpecialCharsInLabelValues && !usingRegexOperator) {
labelValue = prometheusRegularEscape(labelValue);
}
expr += `${filter.label}${filter.op}"${labelValue}"`;
expr += `${utf8Support(filter.label)}${filter.op}"${labelValue}"`;
}
return expr + `}`;
}
renderQuery(query: PromLokiVisualQuery, nested?: boolean) {
let queryString = `${query.metric ?? ''}${this.renderLabels(query.labels)}`;
let queryString = '';
const labels = this.renderLabels(query.labels);
if (query.metric) {
if (isValidLegacyName(query.metric)) {
// This is a legacy metric, put outside the curl legacy_query{label="value"}
queryString = `${query.metric}${labels}`;
} else {
// This is a utf8 metric, put inside the curly and quotes {"utf8.metric", label="value"}
queryString = `{"${query.metric}"${labels.length > 0 ? `, ${labels.substring(1)}` : `}`}`;
}
} else {
// No metric just use labels {label="value"}
queryString = labels;
}
queryString = this.renderOperations(queryString, query.operations);
if (!nested && this.hasBinaryOp(query) && Boolean(query.binaryQueries?.length)) {

View File

@@ -20,6 +20,7 @@ export interface PromQuery extends GenPromQuery, DataQuery {
disableTextWrap?: boolean;
fullMetaSearch?: boolean;
includeNullMetadata?: boolean;
fromExploreMetrics?: boolean;
}
export enum PrometheusCacheLevel {

View File

@@ -0,0 +1,127 @@
import { escapeForUtf8Support, utf8Support, wrapUtf8Filters } from './utf8_support';
describe('utf8 support', () => {
it('should return utf8 labels wrapped in quotes', () => {
const labels = ['valid:label', 'metric_label', 'utf8 label with space 🤘', ''];
const expected = ['valid:label', 'metric_label', `"utf8 label with space 🤘"`, ''];
const supportedLabels = labels.map(utf8Support);
expect(supportedLabels).toEqual(expected);
});
});
describe('applyValueEncodingEscaping', () => {
it('should return utf8 labels wrapped in quotes', () => {
const labels = [
'no:escaping_required',
'mysystem.prod.west.cpu.load',
'mysystem.prod.west.cpu.load_total',
'http.status:sum',
'my lovely_http.status:sum',
'花火',
'label with 😱',
];
const expected = [
'no:escaping_required',
'U__mysystem_2e_prod_2e_west_2e_cpu_2e_load',
'U__mysystem_2e_prod_2e_west_2e_cpu_2e_load__total',
'U__http_2e_status:sum',
'U__my_20_lovely__http_2e_status:sum',
'U___82b1__706b_',
'U__label_20_with_20__1f631_',
];
const excapedLabels = labels.map(escapeForUtf8Support);
expect(excapedLabels).toEqual(expected);
});
});
describe('wrapUtf8Filters', () => {
it('should correctly wrap UTF-8 labels and values for multiple key-value pairs', () => {
const result = wrapUtf8Filters('label.with.spaß="this_is_fun",instance="localhost:9112"');
const expected = '"label.with.spaß"="this_is_fun",instance="localhost:9112"';
expect(result).toEqual(expected);
});
it('should correctly wrap UTF-8 labels and values for a single key-value pair', () => {
const result = wrapUtf8Filters('label.with.spaß="this_is_fun"');
const expected = '"label.with.spaß"="this_is_fun"';
expect(result).toEqual(expected);
});
it('should correctly handle commas within values', () => {
const result = wrapUtf8Filters('label.with.spaß="this,is,fun",instance="localhost:9112"');
const expected = '"label.with.spaß"="this,is,fun",instance="localhost:9112"';
expect(result).toEqual(expected);
});
it('should correctly handle escaped quotes within values', () => {
const result = wrapUtf8Filters(`label.with.spaß="this_is_\\"fun\\"",instance="localhost:9112"`);
const expected = `"label.with.spaß"="this_is_\\"fun\\"",instance="localhost:9112"`;
expect(result).toEqual(expected);
});
it('should correctly handle spaces within keys', () => {
const result = wrapUtf8Filters('label with space="value with space",instance="localhost:9112"');
const expected = '"label with space"="value with space",instance="localhost:9112"';
expect(result).toEqual(expected);
});
it('should correctly process mixed inputs with various formats', () => {
const result = wrapUtf8Filters('key1="value1",key2="value,with,comma",key3="val3"');
const expected = 'key1="value1",key2="value,with,comma",key3="val3"';
expect(result).toEqual(expected);
});
it('should correctly handle empty values', () => {
const result = wrapUtf8Filters('key1="",key2="value2"');
const expected = 'key1="",key2="value2"';
expect(result).toEqual(expected);
});
it('should handle an empty input string', () => {
const result = wrapUtf8Filters('');
const expected = '';
expect(result).toEqual(expected);
});
it('should handle a single key with an empty value', () => {
const result = wrapUtf8Filters('key1=""');
const expected = 'key1=""';
expect(result).toEqual(expected);
});
it('should handle multiple consecutive commas in a value', () => {
const result = wrapUtf8Filters('key1="value1,,value2",key2="value3"');
const expected = 'key1="value1,,value2",key2="value3"';
expect(result).toEqual(expected);
});
it('should handle a key-value pair with special characters in the key', () => {
const result = wrapUtf8Filters('special@key#="value1",key2="value2"');
const expected = '"special@key#"="value1",key2="value2"';
expect(result).toEqual(expected);
});
it('should handle a key-value pair with special characters in the value', () => {
const result = wrapUtf8Filters('key1="value@#&*",key2="value2"');
const expected = 'key1="value@#&*",key2="value2"';
expect(result).toEqual(expected);
});
it('should correctly process keys without special characters', () => {
const result = wrapUtf8Filters('key1="value1",key2="value2"');
const expected = 'key1="value1",key2="value2"';
expect(result).toEqual(expected);
});
it('should handle nested escaped quotes correctly', () => {
const result = wrapUtf8Filters('key1="nested \\"escaped\\" quotes",key2="value2"');
const expected = 'key1="nested \\"escaped\\" quotes",key2="value2"';
expect(result).toEqual(expected);
});
it('should handle escaped quotes correctly', () => {
const result = wrapUtf8Filters('key1="nested \\"escaped\\" quotes",key2="value with \\"escaped\\" quotes"');
const expected = 'key1="nested \\"escaped\\" quotes",key2="value with \\"escaped\\" quotes"';
expect(result).toEqual(expected);
});
});

View File

@@ -0,0 +1,113 @@
export const utf8Support = (value: string) => {
if (value === '') {
return value;
}
const isLegacyLabel = isValidLegacyName(value);
if (isLegacyLabel) {
return value;
}
return `"${value}"`;
};
export const escapeForUtf8Support = (value: string) => {
const isLegacyLabel = isValidLegacyName(value);
if (isLegacyLabel) {
return value;
}
let escaped = 'U__';
for (let i = 0; i < value.length; i++) {
const char = value[i];
const codePoint = value.codePointAt(i);
if (char === '_') {
escaped += '__';
} else if (codePoint !== undefined && isValidLegacyRune(char, i)) {
escaped += char;
} else if (codePoint === undefined || !isValidCodePoint(codePoint)) {
escaped += '_FFFD_';
} else {
escaped += '_';
escaped += codePoint.toString(16); // Convert code point to hexadecimal
escaped += '_';
}
// Handle surrogate pairs for characters outside the Basic Multilingual Plane
if (codePoint !== undefined && codePoint > 0xffff) {
i++; // Skip the second half of the surrogate pair
}
}
return escaped;
};
export const isValidLegacyName = (name: string): boolean => {
if (name.length === 0) {
return false;
}
for (let i = 0; i < name.length; i++) {
const char = name[i];
if (!isValidLegacyRune(char, i)) {
return false;
}
}
return true;
};
// const labelNamePriorToUtf8Support = /^[a-zA-Z_:][a-zA-Z0-9_:]*$/;
// instead of regex we use rune check (converted from prometheus code)
// https://github.com/prometheus/common/blob/main/model/metric.go#L426-L428
const isValidLegacyRune = (char: string, index: number): boolean => {
const codePoint = char.codePointAt(0);
if (codePoint === undefined) {
return false;
}
return (
(codePoint >= 97 && codePoint <= 122) || // 'a' to 'z'
(codePoint >= 65 && codePoint <= 90) || // 'A' to 'Z'
codePoint === 95 || // '_'
codePoint === 58 || // ':'
(codePoint >= 48 && codePoint <= 57 && index > 0) // '0' to '9', but not at the start
);
};
const isValidCodePoint = (codePoint: number): boolean => {
// Validate the code point for UTF-8 compliance if needed.
return codePoint >= 0 && codePoint <= 0x10ffff;
};
export const wrapUtf8Filters = (filterStr: string): string => {
const resultArray: string[] = [];
let currentKey = '';
let currentValue = '';
let inQuotes = false;
let temp = '';
for (const char of filterStr) {
if (char === '"' && temp[temp.length - 1] !== '\\') {
// Toggle inQuotes when an unescaped quote is found
inQuotes = !inQuotes;
temp += char;
} else if (char === ',' && !inQuotes) {
// When outside quotes and encountering ',', finalize the current pair
[currentKey, currentValue] = temp.split('=');
resultArray.push(`${utf8Support(currentKey.trim())}="${currentValue.slice(1, -1)}"`);
temp = ''; // Reset for the next pair
} else {
// Collect characters
temp += char;
}
}
// Handle the last key-value pair
if (temp) {
[currentKey, currentValue] = temp.split('=');
resultArray.push(`${utf8Support(currentKey.trim())}="${currentValue.slice(1, -1)}"`);
}
return resultArray.join(',');
};

View File

@@ -57,3 +57,12 @@ export { hasPermission, hasPermissionInMetadata, hasAllPermissions, hasAnyPermis
export { QueryEditorWithMigration } from './components/QueryEditorWithMigration';
export { type MigrationHandler, isMigrationHandler, migrateQuery, migrateRequest } from './utils/migrationHandler';
export { usePluginUserStorage } from './utils/userStorage';
export {
type CorrelationsService,
type CorrelationData,
type CorrelationsData,
type CorrelationExternal,
type CorrelationQuery,
getCorrelationsService,
setCorrelationsService,
} from './services/CorrelationsService';

View File

@@ -0,0 +1,125 @@
import {
DataFrame,
DataLinkPostProcessor,
DataLinkTransformationConfig,
DataSourceInstanceSettings,
TimeRange,
} from '@grafana/data';
export type CorrelationConfigQuery = {
field: string;
target: object; // for queries, this contains anything that would go in the query editor, so any extension off DataQuery a datasource would have, and needs to be generic.
transformations?: DataLinkTransformationConfig[];
};
export type CorrelationConfigExternal = {
field: string;
target: {
url: string; // For external, this simply contains a URL
};
transformations?: DataLinkTransformationConfig[];
};
type CorrelationBase = {
uid: string;
sourceUID: string;
label?: string;
description?: string;
provisioned: boolean;
orgId?: number;
};
/**
* @alpha
*/
export type CorrelationExternal = CorrelationBase & {
type: 'external';
config: CorrelationConfigExternal;
};
/**
* @alpha
*/
export type CorrelationQuery = CorrelationBase & {
type: 'query';
config: CorrelationConfigQuery;
targetUID: string;
};
/**
* @alpha
*/
export type CorrelationData =
| (Omit<CorrelationExternal, 'sourceUID'> & {
source: DataSourceInstanceSettings;
})
| (Omit<CorrelationQuery, 'sourceUID' | 'targetUID'> & {
source: DataSourceInstanceSettings;
target: DataSourceInstanceSettings;
});
/**
* @alpha
*/
export interface CorrelationsData {
correlations: CorrelationData[];
page: number;
limit: number;
totalCount: number;
}
/**
* Used to work with user defined correlations.
* Should be accessed via {@link getCorrelationsService} function.
*
* @alpha
*/
export interface CorrelationsService {
/**
* Creates data links in data frames from provided correlations
*
* @param dataFrames list of data frames to be processed
* @param correlations list of of possible correlations that can be applied
* @param dataFrameRefIdToDataSourceUid a map that for provided refId references corresponding data source ui
*/
attachCorrelationsToDataFrames: (
dataFrames: DataFrame[],
correlations: CorrelationData[],
dataFrameRefIdToDataSourceUid: Record<string, string>
) => DataFrame[];
/**
* Creates a link post processor function that handles correlation transformations
*
* @param timeRange The current time range
*/
correlationsDataLinkPostProcessorFactory: (timeRange: TimeRange) => DataLinkPostProcessor;
/**
* Loads all the correlations defined for the given data sources.
*
* @param sourceUIDs Data source UIDs
*/
getCorrelationsBySourceUIDs: (sourceUIDs: string[]) => Promise<CorrelationsData>;
}
let singletonInstance: CorrelationsService;
/**
* Used during startup by Grafana to set the CorrelationsService so it is available
* via {@link getCorrelationsService} to the rest of the application.
*
* @internal
*/
export function setCorrelationsService(instance: CorrelationsService) {
singletonInstance = instance;
}
/**
* Used to retrieve the {@link CorrelationsService}.
*
* @alpha
*/
export function getCorrelationsService(): CorrelationsService {
return singletonInstance;
}

View File

@@ -70,6 +70,12 @@ export type BackendSrvRequest = {
*/
responseType?: 'json' | 'text' | 'arraybuffer' | 'blob';
/**
* Used to cancel an open connection
* https://developer.mozilla.org/en-US/docs/Web/API/AbortController
*/
abortSignal?: AbortSignal;
/**
* The credentials read-only property of the Request interface indicates whether the user agent should send cookies from the other domain in the case of cross-origin requests.
*/
@@ -173,6 +179,14 @@ export interface BackendSrv {
* Observable http request interface
*/
fetch<T>(options: BackendSrvRequest): Observable<FetchResponse<T>>;
/**
* Observe each raw chunk in the response. This is useful when reading values from
* a long living HTTP connection like the kubernetes WATCH command.
*
* Each chunk includes the full response headers and the `data` property is filled with the chunk.
*/
chunked(options: BackendSrvRequest): Observable<FetchResponse<Uint8Array | undefined>>;
}
let singletonInstance: BackendSrv;

View File

@@ -1,4 +1,4 @@
import { DashboardV2Spec } from './dashboard.gen';
import { DashboardV2Spec } from './types.gen';
export const handyTestingSchema: DashboardV2Spec = {
id: 1,

View File

@@ -0,0 +1,4 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
export * from './types.gen';
export type * from './types.gen';

View File

@@ -41,7 +41,7 @@
"@testing-library/user-event": "14.5.2",
"@types/jest": "^29.5.4",
"@types/lodash": "4.17.14",
"@types/node": "22.10.5",
"@types/node": "22.10.6",
"@types/react": "18.3.3",
"@types/react-dom": "18.2.25",
"@types/react-virtualized-auto-sizer": "1.0.4",

View File

@@ -83,11 +83,11 @@
"monaco-editor": "0.34.1",
"ol": "7.4.0",
"prismjs": "1.29.0",
"rc-cascader": "3.31.0",
"rc-cascader": "3.33.0",
"rc-drawer": "7.2.0",
"rc-picker": "4.9.2",
"rc-slider": "11.1.8",
"rc-time-picker": "^3.7.3",
"rc-tooltip": "6.3.2",
"rc-tooltip": "6.4.0",
"react-calendar": "^5.1.0",
"react-colorful": "5.6.1",
"react-custom-scrollbars-2": "4.5.0",
@@ -145,7 +145,7 @@
"@types/is-hotkey": "0.1.10",
"@types/jest": "29.5.14",
"@types/mock-raf": "1.0.6",
"@types/node": "22.10.5",
"@types/node": "22.10.6",
"@types/prismjs": "1.26.5",
"@types/react": "18.3.3",
"@types/react-color": "3.0.13",

View File

@@ -26,6 +26,7 @@ export const getComboboxStyles = (theme: GrafanaTheme2) => {
zIndex: theme.zIndex.dropdown,
position: 'relative',
borderRadius: theme.shape.radius.default,
overflow: 'hidden',
}),
menuUlContainer: css({
label: 'combobox-menu-ul-container',

View File

@@ -1,4 +1,4 @@
import { render, screen } from '@testing-library/react';
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { dateTime, dateTimeAsMoment, dateTimeForTimeZone, getTimeZone, setTimeZoneResolver } from '@grafana/data';
@@ -86,23 +86,16 @@ describe('Date time picker', () => {
// open the time of day overlay
await userEvent.click(screen.getAllByRole('textbox')[1]);
const hourList = screen.getAllByRole('list')[0];
// change the hour
await userEvent.click(
screen.getAllByRole('button', {
name: '00',
})[0]
);
await userEvent.click(within(hourList).getByText('00'));
// Check the active day is the 5th
expect(screen.getByRole('button', { name: 'May 5, 2021' })).toHaveClass('react-calendar__tile--active');
// change the hour
await userEvent.click(
screen.getAllByRole('button', {
name: '23',
})[0]
);
await userEvent.click(within(hourList).getByText('23'));
// Check the active day is the 5th
expect(screen.getByRole('button', { name: 'May 5, 2021' })).toHaveClass('react-calendar__tile--active');
@@ -123,7 +116,6 @@ describe('Date time picker', () => {
await userEvent.click(screen.getByRole('button', { name: 'May 15, 2021' }));
const timeInput = screen.getAllByRole('textbox')[1];
expect(timeInput).toHaveClass('rc-time-picker-input');
expect(timeInput).not.toHaveDisplayValue('00:00:00');
}
);
@@ -216,9 +208,7 @@ describe('Date time picker', () => {
await userEvent.click(screen.getAllByRole('textbox')[1]);
// check the hour element is visible
const hourElement = screen.getAllByRole('button', {
name: '00',
})[0];
const hourElement = screen.getAllByText('00')[0];
expect(hourElement).toBeVisible();
// select the hour value and check it's still visible
@@ -227,11 +217,7 @@ describe('Date time picker', () => {
// click outside the overlay and check the hour element is no longer visible
await userEvent.click(document.body);
expect(
screen.queryByRole('button', {
name: '00',
})
).not.toBeInTheDocument();
expect(screen.queryByText('00')).not.toBeInTheDocument();
}
);

View File

@@ -24,7 +24,7 @@ export const Basic: StoryFn<typeof TimeOfDayPicker> = (args) => {
return (
<TimeOfDayPicker
{...args}
onChange={(newValue) => {
onChange={(newValue?) => {
action('on selected')(newValue);
updateArgs({ value: newValue });
}}

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