Compare commits

...

73 Commits

Author SHA1 Message Date
Dominik Prokop
6668161a88 Release 7.3.1 2020-10-30 11:13:21 +01:00
Grot (@grafanabot)
f81257c2f2 Loki: Fix error when some queries return zero results (#28645) (#28689)
* Fix logSeriesToLogsModel when some data frames have empty fields

* Update test

* Update public/app/core/logs_model.ts

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
(cherry picked from commit 05644e7042)

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
2020-10-30 10:05:27 +01:00
Grot (@grafanabot)
e8d399ef94 TextPanel: Fixes problems where text panel would show old content (#28643) (#28687)
(cherry picked from commit 16a1d2f744)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-10-30 08:44:14 +01:00
Grot (@grafanabot)
03563abba0 PanelMenu: Fixes panel submenu not being accessible for panels close to the right edge of the screen (#28666) (#28676)
* Dropdowns: Trying to fix dropdown menus

* Dropdowns: Trying to fix dropdown menus

* removed now unnessary wrapper ref

* Upodates

* Remove export

(cherry picked from commit 5a83fc574a)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-10-29 20:33:16 +01:00
Grot (@grafanabot)
43928c4c88 Cloudwatch: Fix duplicate metric data (#28642) (#28668)
* Cloudwatch: Fix duplicate metric data

* Refactor reduce function to for of

(cherry picked from commit 6dbf1f830a)

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
2020-10-29 17:31:14 +01:00
Grot (@grafanabot)
3069d764f8 Plugins: Fix descendent frontend plugin signature validation (#28638) (#28662)
* move plugin root check to earlier in validation process

* remove comment

* only check root if necessary

(cherry picked from commit b9d71f5cdd)

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
2020-10-29 15:28:20 +01:00
Grot (@grafanabot)
de9ac280b2 StatPanel: Fixed value being under graph and reduced likley hood for white and dark value text mixing (#28641) (#28655)
* StatPanel: Fixed value being under graph and reduced likley hood for white and dark value text mixing

* Updated snapshot

* Updated storybook config

(cherry picked from commit b46ac2891d)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-10-29 14:59:00 +01:00
Grot (@grafanabot)
4ff80d541b Table: Fix image cell mode so that it works with value mappings (#28644) (#28651)
(cherry picked from commit 1e51d33d85)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-10-29 14:58:38 +01:00
Grot (@grafanabot)
4f4fcf6331 Plugin signing: Fix copy on signed plugin notice (#28633) (#28637)
* Fix copy on signed plugin notice

* Update public/app/features/plugins/PluginPage.tsx

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update public/app/features/plugins/PluginPage.tsx

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit a2b3e63705)

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
2020-10-29 11:32:12 +01:00
kyle
bf8837f6f8 Release v7.3.0 2020-10-28 08:03:43 -04:00
Grot (@grafanabot)
95a3c6a024 Live: updated the reference to use lazy loaded Monaco in code editor. (#28597) (#28599)
(cherry picked from commit c258ee86eb)

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
2020-10-28 07:59:22 -04:00
Grot (@grafanabot)
1ff5718af9 AddDatasource: Improve plugin categories (#28584) (#28589)
* add IoT category to datasources

* add more enterprise plugins

* add more enterprise plugins

(cherry picked from commit 33ef71d81e)

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2020-10-28 10:55:19 +01:00
Grot (@grafanabot)
e123e50d27 StatPanel: Fixes BizChart error max: yyy should not be less than min zzz (#28587) (#28588)
(cherry picked from commit 0d803613d6)

Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
2020-10-28 08:24:31 +01:00
Kyle Brandt
44f691ff91 Alerting: Log alert warnings for obsolete notifiers when extracting alerts and remove spammy error (#28162) (#28582)
* Lower level of notification translation messages

* API: Log alert warnings when saving dashboard

* Remove spammy error

* Rename function parameter

* Apply suggestions from code review

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Apply suggestions from code review

* Fix test

* Change alertValidator return type

* Small fix

* Rename symbol

* Revert "Rename symbol"

This reverts commit 40b619b21a.

* Revert "Small fix"

This reverts commit 2df8319d1d.

* Revert "Change alertValidator return type"

This reverts commit ad933863e3.

* Revert "Fix test"

This reverts commit f728ece2db.

* Revert "Apply suggestions from code review"

This reverts commit f35c5f52af.

* Revert "Apply suggestions from code review"

This reverts commit 7f95800c5f.

* Revert "Rename function parameter"

This reverts commit 95d3e75b00.

* Revert "API: Log alert warnings when saving dashboard"

This reverts commit 1ac5c3f281.

* Conditionally log translation failures

* Fix issue causing test to fail

* Fix test

* Log instead of propagating translations failures due to database errors

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-27 15:57:01 -04:00
Grot (@grafanabot)
12a69876c7 Plugin signing: UI information (#28469) (#28563)
* first pass

* return list

* types and cleanup

* add to plugin page and add styles

* update comment

* update comment

* fix component path

* simplify error component

* simplify error struct

* fix tests

* don't export and fix string()

* update naming

* remove frontend

* introduce phantom loader

* track single error

* remove error from base

* remove unused struct

* remove unnecessary filter

* add errors endpoint

* Update set log to use id field

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* skip adding BE plugins

* remove errs from plugin + ds list

* remove unnecessary fields

* add signature state to panels

* Fetch plugins errors

* grafana/ui component tweaks

* DS Picker - add unsigned badge

* VizPicker - add unsigned badge

* PluginSignatureBadge tweaks

* Plugins list - add signatures info box

* New datasource page - add signatures info box

* Plugin page - add signatures info box

* Fix test

* Do not show Core label in viz picker

* Update public/app/features/plugins/PluginsErrorsInfo.tsx

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>

* Update public/app/features/plugins/PluginListPage.test.tsx

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

* Update public/app/features/plugins/PluginListPage.tsx

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

* Update public/app/features/datasources/NewDataSourcePage.tsx

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

* Review comments 1

* Review comments 2

* Update public/app/features/plugins/PluginsErrorsInfo.tsx

* Update public/app/features/plugins/PluginPage.tsx

* Prettier fix

* remove stale backend code

* Docs issues fix

Co-authored-by: Will Browne <will.browne@grafana.com>
Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
(cherry picked from commit 4468d41417)

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
2020-10-27 15:28:56 -04:00
Grot (@grafanabot)
5bb203df99 Short URL: Update last seen at when visiting a short URL (#28565) (#28573)
Ref #28248

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
(cherry picked from commit 60d40fa99b)

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
2020-10-27 12:16:56 -04:00
Grot (@grafanabot)
23ae63913f Grafana/ui: auto focus threshold editor input (#28360) (#28538)
* feat(grafana-ui): autofocus threshold editor input

* refactor(grafana-ui): remove commented out css

* feat(grafana-ui): use ref for autofocus new thresholds editor input

* refactor(grafana-ui): conditionally set input ref for latest threshold

* refactor(grafana-ui): put back createRef for input ref

(cherry picked from commit a8a3686785)

Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com>
2020-10-27 09:04:55 +01:00
Grot (@grafanabot)
9b3981fb3c API: add login username in SendLoginLogCommand (#28544) (#28545)
* API: add login username in Login actions

* LoginUser -> LoginUsername

* fix test

(cherry picked from commit 65b5086a59)

Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com>
2020-10-26 18:52:48 +02:00
Will Browne
c933d58893 Docs: SigV4 What's New and AWS Elasticsearch documentation (#28506) (#28543)
* add what's new entry and elastic docs

* update docs

* remove caps

* fix image path

* fix md link

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>

* fix link case

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>

* fix md link

* docs fixes

* update config ref

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
2020-10-26 14:52:29 +01:00
Grot (@grafanabot)
1c61f3fd45 AzureMonitor: Support decimal (as float64) type in analytics/logs (#28480) (#28540)
loss of precision but will make the response work instead of erroring
follows work already done in the ADX plugin
fixes #28278

(cherry picked from commit 96e6524a7d)

Co-authored-by: Kyle Brandt <kyle@grafana.com>
2020-10-26 08:46:08 -04:00
Grot (@grafanabot)
be4b32270a Auth: Fix SigV4 request verification step for Amazon Elasticsearch Service (#28481) (#28539)
* remove forward header and add extra steps for encoding

* add comment

* re-use forwarded header

* fix service param

* Make SDK the default auth option in UI

* use SDK code instead

* propagate err

* refactor flow

* fix prom service namespace

(cherry picked from commit 4d2b20f727)

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
2020-10-26 13:09:03 +01:00
Grot (@grafanabot)
1e027f5289 Drone: Upgrade build pipeline tool (#28533) (#28536)
* Drone: Upgrade grabpl

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 1571579c1d)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-26 12:16:41 +01:00
Grot (@grafanabot)
99613f86f8 Drone: Upgrade build-pipeline tool (#28520) (#28530)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit c3917e6525)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-26 09:49:10 +01:00
Grot (@grafanabot)
43a1cb25b9 Elasticsearch: Fix handling of errors when testing data source (#28498) (#28504)
#24999 changed the error response payload, but the test data source
was not adapted to this change and broke the feature of displaying
any errors to the user in the UI. This change should resolve this
problem.

Ref #24999
Ref #28481

(cherry picked from commit c8b7ccc66c)

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
2020-10-24 01:53:42 +02:00
Grot (@grafanabot)
648a149813 Plugins: Track plugin signing errors and expose them to the frontend (#28219) (#28505)
* first pass

* return list

* types and cleanup

* add to plugin page and add styles

* update comment

* update comment

* fix component path

* simplify error component

* simplify error struct

* fix tests

* don't export and fix string()

* update naming

* remove frontend

* introduce phantom loader

* track single error

* remove error from base

* remove unused struct

* remove unnecessary filter

* add errors endpoint

* Update set log to use id field

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* skip adding BE plugins

* remove errs from plugin + ds list

* remove unnecessary fields

* add signature state to panels

* remove unused code

* apply PR feedback

* update comment

* merge dto with model

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 1bee9f4168)

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
2020-10-23 17:54:34 +02:00
Grot (@grafanabot)
d8ed14f5a5 Cloud Monitoring: Fix help section for aliases (#28499) (#28509)
(cherry picked from commit 8327dd500f)

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
2020-10-23 18:43:25 +03:00
Grot (@grafanabot)
8f5c9b192f Auth: Should redirect to login when anonymous enabled and URL with different org than anonymous specified (#28158) (#28503)
If anonymous access is enabled for an org and there are multiple
orgs. When requesting a page that requires user to be logged in
and orgId query string is set in the request url to an org not
equal the anonymous org, if the user is not logged in should
be redirected to the login page.

Fixes #26120

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
(cherry picked from commit 3be82ecd4e)

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
2020-10-23 16:57:19 +02:00
Grot (@grafanabot)
0a184cb84f Drone: Don't build Windows installer for version branches (#28494) (#28495)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 090c683fea)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-23 12:30:11 +02:00
Grot (@grafanabot)
be4ef1dcd8 Drone: Add version branch pipeline (#28490) (#28492)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 07897c7e1e)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-23 10:37:26 +02:00
Grot (@grafanabot)
de86b51fb5 Explore: parse time range fix (#28467) (#28470)
* Explore: parse time range fix

* Remove commented out code

* Fix cases for string epoch and ISO strings

Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
(cherry picked from commit ce1f7908c8)

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
2020-10-22 17:02:58 +02:00
Grot (@grafanabot)
25653e15a6 Circle: Remove release pipeline (#28474) (#28477)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 77cbb4f0f9)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-22 16:42:12 +02:00
Arve Knudsen
fce221a7d3 Release v7.3.0-beta2
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-22 12:47:29 +02:00
Krzysztof Warunek
6c5e139d8f CloudWatch/Athena - valid metrics and dimensions. (#28436)
* CloudWatch/Athena - valid metrics and dimensions.
In accordance with https://docs.aws.amazon.com/athena/latest/ug/query-metrics-viewing.html.

* Athena: add ProcessedBytes dimension instead of DataScannedInBytes

(cherry picked from commit a71eadf379)
2020-10-22 12:47:29 +02:00
Carl Bergquist
194362c302 Database; Remove database metric feature flag and update changelog (#28438)
* adds note about broken feature flag

* remove code to enable feature flag

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
(cherry picked from commit 62f5641aa9)
2020-10-22 12:47:29 +02:00
Aliaksei Tuzik
11eb920e87 Prometheus: fix parsing of infinite sample values (#28287) (#28288)
* Prometheus: fix parsing of infinite sample values (#28287)

* Prometheus: Use common function to parse both sample values and histogram "le" label

(cherry picked from commit f3c09e8bcc)
2020-10-22 12:47:29 +02:00
Jack Westbrook
bb555684b1 Grafana/ui: pass html attributes to segment (#28316)
* feat(grafana-ui): introduce rest props to segment components

* docs(grafana-ui): add segment stories for html attributes

(cherry picked from commit 04c06f2286)
2020-10-22 12:47:29 +02:00
Hugo Häggmark
f2fac78c0b Docs: Adds basic frontend data request concepts (#28253)
* Docs: Adds frontend data request docs

* Update contribute/architecture/frontend-data-requests.md

Co-authored-by: Marcus Olsson <marcus.olsson@hey.com>

* Update contribute/architecture/frontend-data-requests.md

Co-authored-by: Marcus Olsson <marcus.olsson@hey.com>

* Update contribute/architecture/frontend-data-requests.md

Co-authored-by: Marcus Olsson <marcus.olsson@hey.com>

* Update contribute/architecture/frontend-data-requests.md

Co-authored-by: Marcus Olsson <marcus.olsson@hey.com>

* Docs: changes after PR comments

* Docs: changes after PR comments

* Docs: changes after PR comments

* Update contribute/architecture/frontend-data-requests.md

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>

Co-authored-by: Marcus Olsson <marcus.olsson@hey.com>
Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: achatterjee-grafana <34888589+ashishagarwal06@users.noreply.github.com>
(cherry picked from commit b497063f64)
2020-10-22 12:47:29 +02:00
Carl Bergquist
08be99fa45 Instrumentation: Add histogram for request duration (#28364)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
(cherry picked from commit edbaa9d681)
2020-10-22 12:47:29 +02:00
Carl Bergquist
df2fda3f88 remove status label from histogram (#28387)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
(cherry picked from commit b036112444)
2020-10-22 12:47:29 +02:00
Andrej Ocenas
983d6c8ef8 Explore: Fix date formatting in url for trace logs link (#28381)
* Fix url formatting

* Reverse the overhang buffers

* Fix range when opening split

(cherry picked from commit ad657dcdc3)
2020-10-22 12:47:29 +02:00
Carl Bergquist
12dc0ea111 Instrumentation: Add counters and histograms for database queries (#28236)
Signed-off-by: bergquist <carl.bergquist@gmail.com>

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 74d1d3c6a8)
2020-10-22 12:47:29 +02:00
Marcus Efraimsson
103765c349 CloudWatch: Adding support for additional Amazon CloudFront metrics (#28378)
Follow up to #28069 where some metrics was missed.

Ref #28069

(cherry picked from commit 0e17a15fbd)
2020-10-22 12:47:29 +02:00
Alex Khomenko
6b4fd2d33c Add unique ids to query editor fields (#28376)
* Add unique ids to query editor fields

* Update public/app/plugins/datasource/testdata/components/RandomWalkEditor.tsx

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
(cherry picked from commit 1cce13b501)
2020-10-22 12:47:29 +02:00
maknik
1a0500bbeb Dashboard links: Places drop down list so it's always visible (#28330)
* calculating whether to place the list on the right or left edge of the parent

* change naming and add import of createRef

(cherry picked from commit cdab6028e1)
2020-10-22 12:47:29 +02:00
Andrej Ocenas
f1b9c6cde1 Loki: Run instant query only when doing metric query (#28325)
* Run instant query only when doing metric query

* Update public/app/plugins/datasource/loki/datasource.ts

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
(cherry picked from commit 8e9181e7d1)
2020-10-22 12:47:29 +02:00
Andrej Ocenas
fd851af389 Loki: Base maxDataPoints limits on query type (#28298)
* Base maxLines and maxDataPoints based on query type

* Allow overriding the limit to higher value

(cherry picked from commit 8db5d750d0)
2020-10-22 12:47:29 +02:00
Elliot Pryde
8f7eb69db6 Explore: respect min_refresh_interval (#27988)
* Explore: respect min_refresh_interval

Fixes #27494

* fixup! Explore: respect min_refresh_interval

* fixup! Explore: respect min_refresh_interval

* UI: export defaultIntervals from refresh picker

* fixup! Explore: respect min_refresh_interval

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
(cherry picked from commit 1760fdd55d)
2020-10-22 12:47:29 +02:00
Arve Knudsen
bf74c1fe3f Drone: Use ${DRONE_TAG} in release pipelines, since it should work (#28299)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 392c5bdf73)
2020-10-22 12:47:29 +02:00
Jack Westbrook
c60ea5f25f fix: for graph size not taking up full height or width
(cherry picked from commit 448114f649)
2020-10-22 12:47:29 +02:00
Arve Knudsen
12dc9ea49f Drone: Fix grafana-mixin linting (#28308)
* Drone: Fix Starlark script

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* grafana-mixin: Move build logic to scripts

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Drone: Use mixin scripts

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* CI build image: Install jsonnetfmt and mixtool

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Makefile: Print commands

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 847dc0bec7)
2020-10-22 12:47:29 +02:00
Sofia Papagiannaki
e5f12afda2 SQLStore: Run tests as integration tests (#28265)
* sqlstore: Run tests as integration tests

* Truncate database instead of re-creating it on each test

* Fix test description

See https://github.com/grafana/grafana/pull/12129

* Fix lint issues

* Fix postgres dialect after review suggestion

* Rename and document functions after review suggestion

* Add periods

* Fix auto-increment value for mysql dialect

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
(cherry picked from commit 4937f0daab)
2020-10-22 12:47:29 +02:00
Arve Knudsen
77ab9f4331 API: Fix short URLs (#28300)
* API: Fix short URLs

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit a2c7c5b912)
2020-10-22 12:47:29 +02:00
The Rock Guy
0f0d860ed3 CloudWatch: Add EC2CapacityReservations Namespace (#28309)
(cherry picked from commit c9cc82ea55)
2020-10-22 12:47:29 +02:00
Zoltán Bedi
ec0383c9ff Jaeger: timeline collapser to show icons (#28284)
* Fix: timeline collapser to show icons

* Use IconButton

* Export named component instead of default

(cherry picked from commit e93bd23353)
2020-10-22 12:47:29 +02:00
Carl Bergquist
c510f30eb3 Add monitoring mixing for Grafana (#28285)
Co-authored-by: Tom Wilkie <tom.wilkie@gmail.com>
(cherry picked from commit 6002df580f)
2020-10-22 12:47:29 +02:00
Grot (@grafanabot)
f0dafad5cb SAML: IdP-initiated SSO docs (#28280) (#28462)
* SAML: IdP-initiated SSO docs

* Update docs/sources/enterprise/saml.md

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>

* Apply suggestions from code review

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
(cherry picked from commit 2087ff6003)

Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com>
2020-10-22 12:12:00 +02:00
Grot (@grafanabot)
6b056c527f AzureMonitor: Fix capitalization of NetApp 'volumes' namespace (#28369) (#28459)
'Microsoft.NetApp/netAppAccounts/capacityPools/Volumes'  -> 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes'

(cherry picked from commit d2a792ea3b)

Co-authored-by: Sean Luce <lucesean@gmail.com>
2020-10-22 12:11:44 +02:00
Grot (@grafanabot)
c9513c5e81 Loki: Visually distinguish error logs for LogQL2 (#28359) (#28460)
* Loki: Add errored logs and update UI

* Update messaging

* Add icon and tooltip for errored logs

* Update name of variable for more semantic meaning

* Add tests

* Update test

* Refactor, remove unnecessary state

* Update packages/grafana-data/src/types/logs.ts

* Update packages/grafana-ui/src/components/Logs/LogDetails.tsx

Co-authored-by: Giordano Ricci <gio.ricci@grafana.com>

Co-authored-by: Giordano Ricci <gio.ricci@grafana.com>
(cherry picked from commit 3f39b4b601)

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
2020-10-22 12:11:02 +02:00
Grot (@grafanabot)
9153d2146b Explore: Support wide data frames (#28393) (#28454)
* Change how isTimeSeries work

* Simplify the decorators and update tests

(cherry picked from commit 8f4be08b00)

Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
2020-10-22 11:52:16 +02:00
Grot (@grafanabot)
f3fa16706c Live: support real time measurements (alpha) (#28022) (#28451)
* improve reduce transformer

* add measurment classes

* sync with new grafana measure format

* use address for live

* use plural in URL

* set the field name

* fix build

* find changes

* POST http to channel

* Yarn: Update lock file (#28014)

* Loki: Run instant query only in Explore (#27974)

* Run instant query only in Explore

* Replace forEach with for loop

* don't cast

* Docs: Fixed row display in table (#28031)

* Plugins: Let descendant plugins inherit their root's signature (#27970)

* plugins: Let descendant plugins inherit their root's signature

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Registry: Fix service shutdown mode trigger location (#28025)

* Add Alex Khomenko as member (#28032)

* show history

* fix confirm

* fix confirm

* add tests

* fix lint

* add more errors

* set values

* remove unrelated changes

* unrelated changes

* Update pkg/models/live.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/models/live.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/services/live/live.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/services/live/pluginHandler.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/services/live/pluginHandler.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/services/live/pluginHandler.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* use measurments for testdata endpoints

* add live to testdata

* add live to testdata

* Update pkg/services/live/channel.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Apply suggestions from code review

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* update comment formats

* uprevert testdata

* Apply suggestions from code review

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>

* Apply suggestions from code review

* CloudWatch: Add EC2CapacityReservations Namespace (#28309)

* API: Fix short URLs (#28300)

* API: Fix short URLs

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Chore: Add cloud-middleware as code owners (#28310)

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* SQLStore: Run tests as integration tests (#28265)

* sqlstore: Run tests as integration tests

* Truncate database instead of re-creating it on each test

* Fix test description

See https://github.com/grafana/grafana/pull/12129

* Fix lint issues

* Fix postgres dialect after review suggestion

* Rename and document functions after review suggestion

* Add periods

* Fix auto-increment value for mysql dialect

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>

* Drone: Fix grafana-mixin linting (#28308)

* Drone: Fix Starlark script

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* grafana-mixin: Move build logic to scripts

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Drone: Use mixin scripts

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* CI build image: Install jsonnetfmt and mixtool

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Makefile: Print commands

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* should only ignore the file in the grafana mixin root folder (#28306)

Signed-off-by: bergquist <carl.bergquist@gmail.com>

* fix: for graph size not taking up full height or width

* Graph NG: fix toggling queries and extract Graph component from graph3 panel (#28290)

* Fix issue when data and config is not in sync

* Extract GraphNG component from graph panel and add some tests coverage

* Update packages/grafana-ui/src/components/uPlot/hooks.test.ts

* Update packages/grafana-ui/src/components/uPlot/hooks.test.ts

* Update packages/grafana-ui/src/components/uPlot/hooks.test.ts

* Fix grid color and annotations refresh

* Drone: Use ${DRONE_TAG} in release pipelines, since it should work (#28299)

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Explore: respect min_refresh_interval (#27988)

* Explore: respect min_refresh_interval

Fixes #27494

* fixup! Explore: respect min_refresh_interval

* fixup! Explore: respect min_refresh_interval

* UI: export defaultIntervals from refresh picker

* fixup! Explore: respect min_refresh_interval

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>

* Loki: Base maxDataPoints limits on query type (#28298)

* Base maxLines and maxDataPoints based on query type

* Allow overriding the limit to higher value

* Bump tree-kill from 1.2.1 to 1.2.2 (#27405)

Bumps [tree-kill](https://github.com/pkrumins/node-tree-kill) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/pkrumins/node-tree-kill/releases)
- [Commits](https://github.com/pkrumins/node-tree-kill/compare/v1.2.1...v1.2.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump handlebars from 4.4.3 to 4.7.6 (#27416)

Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.4.3 to 4.7.6.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.4.3...v4.7.6)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Build(deps): Bump http-proxy from 1.18.0 to 1.18.1 (#27507)

Bumps [http-proxy](https://github.com/http-party/node-http-proxy) from 1.18.0 to 1.18.1.
- [Release notes](https://github.com/http-party/node-http-proxy/releases)
- [Changelog](https://github.com/http-party/node-http-proxy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/http-party/node-http-proxy/compare/1.18.0...1.18.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Automation: Add backport github action (#28318)

* BackendSrv: Fixes queue countdown when unsubscribe is before response (#28323)

* GraphNG: Use AxisSide enum (#28320)

* IssueTriage: Needs more info automation and messages (#28137)

* IssueTriage: Needs more info automation and messages

* Updated

* Updated

* Updated wording

* SAML: IdP-initiated SSO docs (#28280)

* SAML: IdP-initiated SSO docs

* Update docs/sources/enterprise/saml.md

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>

* Apply suggestions from code review

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>

* Loki: Run instant query only when doing metric query (#28325)

* Run instant query only when doing metric query

* Update public/app/plugins/datasource/loki/datasource.ts

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

* Automation: Tweaks to more info message (#28332)

* AlertingNG: remove warn/crit from eval prototype (#28334)

and misc cleanup

* area/grafana/toolkit: update e2e docker image (#28335)

* add xvfb to image

* comment out toolkit inclusion

* add latest tag

* update packages for cypress

* cleanup script

* Update auth-proxy.md (#28339)

Fix a minor grammar mistake: 'handling' to 'handle'.

* Git: Create .gitattributes for windows line endings (#28340)

With this set, Windows users will have text files converted from Windows style line endings (\r\n) to Unix style line endings (\n) when they’re added to the repository.
https://www.edwardthomson.com/blog/git_for_windows_line_endings.html

* Docs: Add docs for valuepicker (#28327)

* Templating: Replace all '$tag' in tag values query (#28343)

* Docs: Add missing records from grafana-ui 7.2.1 CHANGELOG (#28302)

* Dashboard links: Places drop down list so it's always visible (#28330)

* calculating whether to place the list on the right or left edge of the parent

* change naming and add import of createRef

* Automation: Update backport github action trigger (#28352)

It seems like GitHub has solved the problem of running github actions on PRs from forks with access to secrets.

https://github.blog/2020-08-03-github-actions-improvements-for-fork-and-pull-request-workflows/#improvements-for-public-repository-forks

If I change the event that triggers it to pull_request_target the action is run in the context of the base instead of the merged PR branch

* ColorSchemes: Adds more color schemes and text colors that depend on the background (#28305)

* Adding more color modes and text colors that depend on the background color

* Updates

* Updated

* Another big value fix

* Fixing unit tests

* Updated

* Updated test

* Update

* Updated

* Updated

* Updated

* Updated

* Added new demo dashboard

* Updated

* updated

* Updated

* Updateed

* added beta notice

* Fixed e2e test

* Fix typos

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* revert pseduo code

* apply feedback

* remove HTTP for now

* fix backend test

* change to datasource

* clear input for streams

* fix docs?

* consistent measure vs measurements

* better jsdocs

* fix a few jsdoc errors

* fix comment style

* Remove commented out code

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Clean up code

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Clean up code

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Clean up code

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Clean up code

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Clean up code

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Clean up code

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Clean up code

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/models/live.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix build

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* set the stringField

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
Co-authored-by: ozhuang <ozhuang.95@gmail.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Amos Law <ahlaw.dev@gmail.com>
Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
Co-authored-by: The Rock Guy <fabian.bracco@gvcgroup.com.au>
Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
Co-authored-by: Carl Bergquist <carl@grafana.com>
Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com>
Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: Elliot Pryde <elliot.pryde@elliotpryde.com>
Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com>
Co-authored-by: Kyle Brandt <kyle@grafana.com>
Co-authored-by: Brian Gann <briangann@users.noreply.github.com>
Co-authored-by: J-F-Far <joel.f.farthing@gmail.com>
Co-authored-by: acoder77 <73009264+acoder77@users.noreply.github.com>
Co-authored-by: Peter Holmberg <peterholmberg@users.noreply.github.com>
Co-authored-by: Krzysztof Dąbrowski <krzysdabro@live.com>
Co-authored-by: maknik <mooniczkam@gmail.com>
(cherry picked from commit 2aafa39879)

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2020-10-22 09:27:49 +02:00
Grot (@grafanabot)
9d7eaedb2b TestData: multiple arrow requests should return multiple frames (#28417) (#28441)
(cherry picked from commit f32d47a535)

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2020-10-21 18:16:06 +02:00
Grot (@grafanabot)
2221c2bbad Plugins: do not remount app plugin on nav change (#28105) (#28426)
* do not remount app plugin on nav change

* test for not mounting app plugin twice

(cherry picked from commit 97526fc492)

Co-authored-by: Domas <domas.lapinskas@grafana.com>
2020-10-21 16:22:47 +02:00
Grot (@grafanabot)
363d0a9588 plugins: Don't exit on duplicate plugin (#28390) (#28430)
* plugins: Don't exit on duplicate plugin

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Add missing files

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix test

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 4084b53f91)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-21 13:04:25 +02:00
Grot (@grafanabot)
d18ac27126 App Plugins: Add backend support (#28272) (#28423)
* Add backend support for app plugins

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 68efedfa88)

Co-authored-by: Joan López de la Franca Beltran <joanjan14@gmail.com>
2020-10-21 10:52:08 +02:00
Grot (@grafanabot)
37327d74f5 FieldColor: Remove inverted color scheme (#28408) (#28418)
(cherry picked from commit 84992adf2a)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-10-21 09:02:49 +02:00
Torkel Ödegaard
5757bd80d3 ColorSchemes: Adds more color schemes and text colors that depend on the background (#28305) (#28414)
* Adding more color modes and text colors that depend on the background color

* Updates

* Updated

* Another big value fix

* Fixing unit tests

* Updated

* Updated test

* Update

* Updated

* Updated

* Updated

* Updated

* Added new demo dashboard

* Updated

* updated

* Updated

* Updateed

* added beta notice

* Fixed e2e test

(cherry picked from commit 566cd2c6af)
2020-10-21 07:23:13 +02:00
Grot (@grafanabot)
f071817b95 CloudWatch: Fix custom metrics (#28391) (#28401)
* CloudWatch: Fix querying for custom metrics

Co-authored by Mitch McKenzie <mitch.mckenzie@outlook.com>

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit af17f9fd9b)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-20 15:31:17 +02:00
Grot (@grafanabot)
2110d4e9b4 Instrumentation: Adds environment_info metric (#28355) (#28388)
Signed-off-by: bergquist <carl.bergquist@gmail.com>

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 89ebb97fca)

Co-authored-by: Carl Bergquist <carl@grafana.com>
2020-10-20 09:59:52 +02:00
Grot (@grafanabot)
5ac4ae37a2 BackendSrv: Fixes queue countdown when unsubscribe is before response (#28323) (#28328)
(cherry picked from commit 9305117902)

Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
2020-10-16 16:45:38 +02:00
Arve Knudsen
c11c8b0b4a Drone: Fixes
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-15 13:56:46 +02:00
Arve Knudsen
0df7b25a49 Drone: Fixes
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-15 13:47:14 +02:00
Arve Knudsen
68a3631ed0 Release 7.3.0-beta1
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-15 13:14:47 +02:00
Arve Knudsen
2e9a0d4755 Chore: Update what's new and release notes URL in package.json
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-15 13:09:34 +02:00
274 changed files with 6879 additions and 3196 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,9 @@
load('scripts/pr.star', 'pr_pipelines')
load('scripts/master.star', 'master_pipelines')
load('scripts/release.star', 'release_pipelines', 'test_release_pipelines')
load('scripts/version.star', 'version_branch_pipelines')
def main(ctx):
edition = 'oss'
return pr_pipelines(edition=edition) + master_pipelines(edition=edition) + release_pipelines() + \
test_release_pipelines()
test_release_pipelines() + version_branch_pipelines()

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,73 @@
# 7.3.0-beta1 (2020-10-14)
# 7.3.0-beta1 (2020-10-15)
### Breaking changes
- **CloudWatch**: The AWS CloudWatch data source's authentication scheme has changed. See the [upgrade notes](https://grafana.com/docs/grafana/latest/installation/upgrading/#upgrading-to-v73) for details and how this may affect you.
### Features / Enhancements
* **Alerting**: Add labels to name when converting data frame to series. [#28085](https://github.com/grafana/grafana/pull/28085), [@kylebrandt](https://github.com/kylebrandt)
* **Alerting**: Ensuring LINE Notify notifications are sent for all alert states. [#27639](https://github.com/grafana/grafana/pull/27639), [@haraldkubota](https://github.com/haraldkubota)
* **Auth**: Add SigV4 auth option to datasources. [#27552](https://github.com/grafana/grafana/pull/27552), [@wbrowne](https://github.com/wbrowne)
* **AzureMonitor**: Pass through null values instead of setting 0. [#28126](https://github.com/grafana/grafana/pull/28126), [@kylebrandt](https://github.com/kylebrandt)
* **Cloud Monitoring**: Out-of-the-box dashboards. [#27864](https://github.com/grafana/grafana/pull/27864), [@papagian](https://github.com/papagian)
* **CloudWatch**: Add support for AWS DirectConnect virtual interface metrics and add missing dimensions. [#28008](https://github.com/grafana/grafana/pull/28008), [@jgulick48](https://github.com/jgulick48)
* **CloudWatch**: Adding support for Amazon ElastiCache Redis metrics. [#28040](https://github.com/grafana/grafana/pull/28040), [@jgulick48](https://github.com/jgulick48)
* **CloudWatch**: Adding support for additional Amazon CloudFront metrics. [#28069](https://github.com/grafana/grafana/pull/28069), [@darrylsepeda](https://github.com/darrylsepeda)
* **CloudWatch**: Re-implement authentication. [#25548](https://github.com/grafana/grafana/pull/25548), [@aknuds1](https://github.com/aknuds1),[@patstrom](https://github.com/patstrom)
* **Dashboard**: Allow shortlink generation. [#27409](https://github.com/grafana/grafana/pull/27409), [@MisterSquishy](https://github.com/MisterSquishy)
* **Docker**: OpenShift compatability. [#27813](https://github.com/grafana/grafana/pull/27813), [@xlson](https://github.com/xlson)
* **Elasticsearch**: Support multiple pipeline aggregations for a query. [#27945](https://github.com/grafana/grafana/pull/27945), [@simianhacker](https://github.com/simianhacker)
* **Explore**: Allow shortlink generation. [#28222](https://github.com/grafana/grafana/pull/28222), [@ivanahuckova](https://github.com/ivanahuckova)
* **Explore**: Remove collapsing of visualisations. [#27026](https://github.com/grafana/grafana/pull/27026), [@ivanahuckova](https://github.com/ivanahuckova)
* **FieldColor**: Adds new standard color option for color. [#28039](https://github.com/grafana/grafana/pull/28039), [@torkelo](https://github.com/torkelo)
* **Gauge**: Improve text sizing and support non threshold color modes. [#28256](https://github.com/grafana/grafana/pull/28256), [@torkelo](https://github.com/torkelo)
* **NamedColors**: Named colors refactors. [#28235](https://github.com/grafana/grafana/pull/28235), [@torkelo](https://github.com/torkelo)
* **Panel Inspect**: Allow CSV download for Excel. [#27284](https://github.com/grafana/grafana/pull/27284), [@tomdaly](https://github.com/tomdaly)
* **Prometheus**: Add time range parameters to labels API. [#27548](https://github.com/grafana/grafana/pull/27548), [@kakkoyun](https://github.com/kakkoyun)
* **Snapshots**: Store dashboard data encrypted in the database. [#28129](https://github.com/grafana/grafana/pull/28129), [@wbrowne](https://github.com/wbrowne)
* **Table**: New cell hover behavior and image cell display mode. [#27669](https://github.com/grafana/grafana/pull/27669), [@torkelo](https://github.com/torkelo)
* **Timezones**: Include IANA timezone canonical name in TimeZoneInfo. [#27591](https://github.com/grafana/grafana/pull/27591), [@dprokop](https://github.com/dprokop)
* **Tracing**: Add Tempo data source. [#28204](https://github.com/grafana/grafana/pull/28204), [@aocenas](https://github.com/aocenas)
* **Transformations**: Add Concatenate fields transformer. [#28237](https://github.com/grafana/grafana/pull/28237), [@ryantxu](https://github.com/ryantxu)
* **Transformations**: improve the reduce transformer. [#27875](https://github.com/grafana/grafana/pull/27875), [@ryantxu](https://github.com/ryantxu)
* **Users**: Expire old user invites. [#27361](https://github.com/grafana/grafana/pull/27361), [@wbrowne](https://github.com/wbrowne)
* **Variables**: Adds loading state and indicators. [#27917](https://github.com/grafana/grafana/pull/27917), [@hugohaggmark](https://github.com/hugohaggmark)
* **Variables**: Adds support for key/value mapping in Custom variable. [#27829](https://github.com/grafana/grafana/pull/27829), [@sartaj10](https://github.com/sartaj10)
* **grafana/toolkit**: expose Jest maxWorkers arg for plugin test & build tasks. [#27724](https://github.com/grafana/grafana/pull/27724), [@domasx2](https://github.com/domasx2)
### Bug Fixes
* **Azure Analytics**: FormatAs Time series groups bool columns wrong. [#27713](https://github.com/grafana/grafana/issues/27713)
* **Azure**: Fixes cancellation of requests with different Azure sources. [#28180](https://github.com/grafana/grafana/pull/28180), [@hugohaggmark](https://github.com/hugohaggmark)
* **BackendSrv**: Reloads page instead of redirect on Unauthorized Error. [#28276](https://github.com/grafana/grafana/pull/28276), [@hugohaggmark](https://github.com/hugohaggmark)
* **Dashboard**: Do not allow users without edit permission to a folder to see new dashboard page. [#28249](https://github.com/grafana/grafana/pull/28249), [@torkelo](https://github.com/torkelo)
* **Dashboard**: Fixed issue accessing horizontal table scrollbar when placed at bottom of dashboard. [#28250](https://github.com/grafana/grafana/pull/28250), [@torkelo](https://github.com/torkelo)
* **DataProxy**: Add additional settings for dataproxy to help with network proxy timeouts. [#27841](https://github.com/grafana/grafana/pull/27841), [@kahinton](https://github.com/kahinton)
* **Database**: Adds new indices to alert_notification_state and alert_rule_tag tables. [#28166](https://github.com/grafana/grafana/pull/28166), [@KarineValenca](https://github.com/KarineValenca)
* **Explore**: Fix showing of Prometheus data in Query inspector. [#28128](https://github.com/grafana/grafana/pull/28128), [@ivanahuckova](https://github.com/ivanahuckova)
* **Explore**: Show results of Prometheus instant queries in formatted table. [#27767](https://github.com/grafana/grafana/pull/27767), [@ivanahuckova](https://github.com/ivanahuckova)
* **Graph**: Prevent legend from overflowing container. [#28254](https://github.com/grafana/grafana/pull/28254), [@jackw](https://github.com/jackw)
* **OAuth**: Fix token refresh failure when custom SSL settings are configured for OAuth provider. [#27523](https://github.com/grafana/grafana/pull/27523), [@billoley](https://github.com/billoley)
* **Plugins**: Let descendant plugins inherit their root's signature. [#27970](https://github.com/grafana/grafana/pull/27970), [@aknuds1](https://github.com/aknuds1)
* **Runtime**: Fix handling of short-lived background services. [#28025](https://github.com/grafana/grafana/pull/28025), [@ahlaw](https://github.com/ahlaw)
* **TemplateSrv**: Fix interpolating strings with object variables. [#28171](https://github.com/grafana/grafana/pull/28171), [@torkelo](https://github.com/torkelo)
* **Variables**: Fixes so constants set from url get completed state. [#28257](https://github.com/grafana/grafana/pull/28257), [@hugohaggmark](https://github.com/hugohaggmark)
* **Variables**: Prevent adhoc filters from crashing when they are not loaded properly. [#28226](https://github.com/grafana/grafana/pull/28226), [@mckn](https://github.com/mckn)
# 7.2.2 (2020-10-21)
### Features / Enhancements
**Caution:** Please do not use/enable the `database_metrics` feature flag. It will corrupt MySQL database tables. See [#28440](https://github.com/grafana/grafana/issues/28440) for more information.
~~**Instrumentation**: Add counters and histograms for database queries. [#28236](https://github.com/grafana/grafana/pull/28236), [@bergquist](https://github.com/bergquist)~~
- **Instrumentation**: Add histogram for request duration. [#28364](https://github.com/grafana/grafana/pull/28364), [@bergquist](https://github.com/bergquist)
- **Instrumentation**: Adds environment_info metric. [#28355](https://github.com/grafana/grafana/pull/28355), [@bergquist](https://github.com/bergquist)
### Bug Fixes
- **CloudWatch**: Fix custom metrics. [#28391](https://github.com/grafana/grafana/pull/28391), [@aknuds1](https://github.com/aknuds1)
# 7.2.1 (2020-10-08)
### Features / Enhancements

View File

@@ -670,6 +670,12 @@ disable_total_stats = false
basic_auth_username =
basic_auth_password =
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
# can expose more information about the Grafana instance.
[metrics.environment_info]
#exampleLabel1 = exampleValue1
#exampleLabel2 = exampleValue2
# Send internal Grafana metrics to graphite
[metrics.graphite]
# Enable by setting the address setting (ex localhost:2003)

View File

@@ -664,6 +664,12 @@
; basic_auth_username =
; basic_auth_password =
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
# can expose more information about the Grafana instance.
[metrics.environment_info]
#exampleLabel1 = exampleValue1
#exampleLabel2 = exampleValue2
# Send internal metrics to Graphite
[metrics.graphite]
# Enable by setting the address setting (ex localhost:2003)

View File

@@ -8,3 +8,6 @@ Learn more about the backend architecture:
- Part 2: [Communication](communication.md)
- Part 3: [Database](database.md)
Learn more about the frontend architecture:
- Part 1: [Data requests](frontend-data-requests.md)

View File

@@ -0,0 +1,41 @@
# Data requests
[BackendSrv](https://grafana.com/docs/grafana/latest/packages_api/runtime/backendsrv) handles all outgoing HTTP requests from Grafana. This document explains the high-level concepts used by `BackendSrv`.
## Canceling requests
This section describes how canceling requests work in Grafana. While data sources can implement their own cancellation concept, we recommend that you use the method we describe here.
A data request can take a long time to finish. During the time between when a request starts and finishes, the user can change context. For example, the user may navigate away or issue the same request again.
If we wait for canceled requests to complete, it might create unnecessary load on data sources.
Grafana uses a concept called _request cancelation_ to cancel any ongoing request that Grafana doesn't need.
#### Before Grafana 7.2
Before Grafana can cancel any data request, it has to identify that request. Grafana identifies a request using the property `requestId` [passed as options](https://github.com/grafana/grafana/blob/master/docs/sources/packages_api/runtime/backendsrvrequest.md) when you use [BackendSrv](https://grafana.com/docs/grafana/latest/packages_api/runtime/backendsrv).
The cancellation logic is as follows:
- When an ongoing request discovers that an additional request with the same `requestId` has started, then Grafana will cancel the ongoing request.
- When an ongoing request discovers that the special "cancel all requests" `requestId` was sent, then Grafana will cancel the ongoing request.
#### After Grafana 7.2
Grafana 7.2 introduced an additional way of canceling requests using [RxJs](https://github.com/ReactiveX/rxjs). To support the new cancellation functionality, the data source needs to use the new `fetch` function in [BackendSrv](https://grafana.com/docs/grafana/latest/packages_api/runtime/backendsrv).
Migrating the core data sources to the new `fetch` function [is an ongoing process that you can read about in this issue.](https://github.com/grafana/grafana/issues/27222)
## Request queue
Depending on how the web browser implements the protocol for HTTP 1.1, it will limit the number of parallel requests, lets call this limit _max_parallel_browser_request_.
Unless you have configured Grafana to use HTTP2, the browser limits parallel data requests according to the browser's implementation. For more information on how to enable HTTP2, refer to [Configuration](https://grafana.com/docs/grafana/latest/administration/configuration/#protocol).
Because there is a _max_parallel_browser_request_ limit, if some of the requests take a long time, they will block later requests and make interacting with Grafana very slow.
#### Before Grafana 7.2
Not supported.
#### After Grafana 7.2
Grafana uses a _request queue_ to process all incoming data requests in order while reserving a free "spot" for any requests to the Grafana API.
Since the first implementation of the request queue doesn't take into account what browser the user uses, the _request queue_ limit for parallel data source requests is hard-coded to 5.
> **Note:** Grafana instances [configured with HTTP2 ](https://grafana.com/docs/grafana/latest/administration/configuration/#protocol) will have a hard coded limit of 1000.

View File

@@ -0,0 +1,338 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": "gdev-testdata",
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-BlYlRd"
},
"custom": {
"align": "center",
"displayMode": "color-background",
"filterable": false
},
"mappings": [],
"thresholds": {
"mode": "percentage",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "blue",
"value": 20
},
{
"color": "orange",
"value": 60
},
{
"color": "red",
"value": 70
}
]
},
"unit": "degree"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Field"
},
"properties": [
{
"id": "custom.displayMode"
}
]
}
]
},
"gridPos": {
"h": 16,
"w": 19,
"x": 0,
"y": 0
},
"id": 4,
"options": {
"showHeader": true,
"sortBy": [
{
"desc": true,
"displayName": "Last"
}
]
},
"pluginVersion": "7.4.0-pre",
"targets": [
{
"alias": "",
"csvWave": {
"timeStep": 60,
"valuesCSV": "0,0,2,2,1,1"
},
"lines": 10,
"points": [],
"pulseWave": {
"offCount": 3,
"offValue": 1,
"onCount": 3,
"onValue": 2,
"timeStep": 60
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 15,
"stream": {
"bands": 1,
"noise": 2.2,
"speed": 250,
"spread": 3.5,
"type": "signal"
},
"stringInput": ""
}
],
"timeFrom": null,
"timeShift": null,
"title": "Gradient color schemes",
"transformations": [
{
"id": "reduce",
"options": {
"reducers": ["max", "mean", "last", "min"]
}
},
{
"id": "organize",
"options": {
"excludeByName": {
"Field": false
},
"indexByName": {},
"renameByName": {}
}
}
],
"type": "table"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-blues"
},
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 20
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 26,
"w": 5,
"x": 19,
"y": 0
},
"id": 2,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": ["mean"],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "7.4.0-pre",
"targets": [
{
"alias": "",
"csvWave": {
"timeStep": 60,
"valuesCSV": "0,0,2,2,1,1"
},
"labels": "",
"lines": 10,
"points": [],
"pulseWave": {
"offCount": 3,
"offValue": 1,
"onCount": 3,
"onValue": 2,
"timeStep": 60
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 30,
"stream": {
"bands": 1,
"noise": 2.2,
"speed": 250,
"spread": 3.5,
"type": "signal"
},
"stringInput": ""
}
],
"timeFrom": null,
"timeShift": null,
"title": "Stats",
"type": "stat"
},
{
"datasource": "gdev-testdata",
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-GrYlRd"
},
"custom": {
"align": "center",
"displayMode": "color-background",
"filterable": false
},
"mappings": [],
"thresholds": {
"mode": "percentage",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "blue",
"value": 20
},
{
"color": "orange",
"value": 60
},
{
"color": "red",
"value": 70
}
]
},
"unit": "degree"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Field"
},
"properties": [
{
"id": "custom.displayMode"
}
]
}
]
},
"gridPos": {
"h": 10,
"w": 19,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"displayMode": "lcd",
"orientation": "auto",
"reduceOptions": {
"calcs": ["mean"],
"fields": "",
"values": false
},
"showUnfilled": true
},
"pluginVersion": "7.4.0-pre",
"targets": [
{
"alias": "",
"csvWave": {
"timeStep": 60,
"valuesCSV": "0,0,2,2,1,1"
},
"lines": 10,
"points": [],
"pulseWave": {
"offCount": 3,
"offValue": 1,
"onCount": 3,
"onValue": 2,
"timeStep": 60
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 15,
"stream": {
"bands": 1,
"noise": 2.2,
"speed": 250,
"spread": 3.5,
"type": "signal"
},
"stringInput": ""
}
],
"timeFrom": null,
"timeShift": null,
"title": "Bar Gauge LCD",
"transformations": [],
"type": "bargauge"
}
],
"schemaVersion": 26,
"style": "dark",
"tags": ["gdev", "demo"],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Gradient Color modes",
"uid": "inxsweKGz",
"version": 17
}

View File

@@ -399,7 +399,7 @@ The length of time that Grafana will wait for a successful TLS handshake with th
### expect_continue_timeout_seconds
The length of time that Grafana will wait for a datasources first response headers after fully writing the request headers, if the request has an “Expect: 100-continue” header. A value of `0` will result in the body being sent immediately. Default is `1` second. For more details check the [Transport.ExpectContinueTimeout](https://golang.org/pkg/net/http/#Transport.ExpectContinueTimeout) documentation.
The length of time that Grafana will wait for a datasources first response headers after fully writing the request headers, if the request has an “Expect: 100-continue” header. A value of `0` will result in the body being sent immediately. Default is `1` second. For more details check the [Transport.ExpectContinueTimeout](https://golang.org/pkg/net/http/#Transport.ExpectContinueTimeout) documentation.
### max_idle_connections
@@ -549,9 +549,11 @@ Number dashboard versions to keep (per dashboard). Default: `20`, Minimum: `1`.
> Only available in Grafana v6.7+.
This prevents users from setting the dashboard refresh interval of a lower than given interval. Per default this is 5 seconds.
This feature prevents users from setting the dashboard refresh interval to a lower value than a given interval value. The default interval value is 5 seconds.
The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. `30s` or `1m`.
As of Grafana v7.3, this also limits the refresh interval options in Explore.
### default_home_dashboard_path
Path to the default home dashboard. If this value is empty, then Grafana uses StaticRootPath + "dashboards/home.json"
@@ -623,7 +625,7 @@ Default is `false`.
### user_invite_max_lifetime_duration
The duration in time a user invitation remains valid before expiring.
The duration in time a user invitation remains valid before expiring.
This setting should be expressed as a duration. Examples: 6h (hours), 2d (days), 1w (week).
Default is `24h` (24 hours). The minimum supported duration is `15m` (15 minutes).
@@ -1073,6 +1075,15 @@ If both are set, then basic authentication is required to access the metrics end
<hr>
## [metrics.environment_info]
Adds dimensions to the `grafana_environment_info` metric, which can expose more information about the Grafana instance.
```
; exampleLabel1 = exampleValue1
; exampleLabel2 = exampleValue2
```
## [metrics.graphite]
Use these options if you want to send internal Grafana metrics to Graphite.

View File

@@ -248,3 +248,19 @@ datasources:
logMessageField: message
logLevelField: fields.level
```
## Amazon Elasticsearch Service
AWS users using Amazon's Elasticsearch Service can use Grafana's Elasticsearch data source to visualize Elasticsearch data.
If you are using an AWS Identity and Access Management (IAM) policy to control access to your Amazon Elasticsearch Service domain, then you must use AWS Signature Version 4 (AWS SigV4) to sign all requests to that domain.
For more details on AWS SigV4, refer to the [AWS documentation](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html).
### AWS Signature Version 4 authentication
> **Note:** Only available in Grafana v7.3+.
In order to sign requests to your Amazon Elasticsearch Service domain, SigV4 can be enabled in the Grafana [configuration]({{< relref "../administration/configuration.md#sigv4_auth_enabled" >}}).
Once AWS SigV4 is enabled, it can be configured on the Elasticsearch data source configuration page. Refer to [Cloudwatch authentication]({{<relref "./cloudwatch.md#authentication" >}}) for more information about authentication options.
{{< docs-imagebox img="/img/docs/v73/elasticsearch-sigv4-config-editor.png" max-width="500px" class="docs-image--no-shadow" caption="SigV4 configuration for AWS Elasticsearch Service" >}}

View File

@@ -14,7 +14,7 @@ weight = 500
SAML authentication integration allows your Grafana users to log in by using an external SAML 2.0 Identity Provider (IdP). To enable this, Grafana becomes a Service Provider (SP) in the authentication flow, interacting with the IdP to exchange user information.
The SAML single-sign-on (SSO) standard is varied and flexible. Our implementation contains the subset of features needed to provide a smooth authentication experience into Grafana.
The SAML single sign-on (SSO) standard is varied and flexible. Our implementation contains a subset of features needed to provide a smooth authentication experience into Grafana.
> Only available in Grafana Enterprise v6.3+. If you encounter any problems with our implementation, please don't hesitate to contact us.
@@ -45,12 +45,14 @@ The table below describes all SAML configuration options. Continue reading below
| ----------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------- | ------------- |
| `enabled` | No | Whether SAML authentication is allowed | `false` |
| `single_logout` | No | Whether SAML Single Logout enabled | `false` |
| `allow_idp_initiated` | No | Whether SAML IdP-initiated login is allowed | `false` |
| `certificate` or `certificate_path` | Yes | Base64-encoded string or Path for the SP X.509 certificate | |
| `private_key` or `private_key_path` | Yes | Base64-encoded string or Path for the SP private key | |
| `signature_algorithm` | No | Signature algorithm used for signing requests to the IdP. Supported values are rsa-sha1, rsa-sha256, rsa-sha512. | |
| `idp_metadata`, `idp_metadata_path`, or `idp_metadata_url` | Yes | Base64-encoded string, Path or URL for the IdP SAML metadata XML | |
| `max_issue_delay` | No | Duration, since the IdP issued a response and the SP is allowed to process it | `90s` |
| `metadata_valid_duration` | No | Duration, for how long the SP metadata is valid | `48h` |
| `relay_state` | No | Relay state for IdP-initiated login. Should match relay state configured in IdP | |
| `assertion_attribute_name` | No | Friendly name or name of the attribute within the SAML assertion to use as the user name | `displayName` |
| `assertion_attribute_login` | No | Friendly name or name of the attribute within the SAML assertion to use as the user login handle | `mail` |
| `assertion_attribute_email` | No | Friendly name or name of the attribute within the SAML assertion to use as the user email | `mail` |
@@ -81,7 +83,9 @@ You can only use one form of each configuration option. Using multiple forms, su
### Signature algorithm
The SAML standard recommends using digital signature for some types of messages, like authentication or logout requests. If `signature_algorithm` option configured, Grafana will put digital signature into SAML requests. Supported signature types are `rsa-sha1`, `rsa-sha256`, `rsa-sha512`. This option should match your IdP configuration, otherwise, signature won't be validated by the IdP. Grafana uses key and certificate configured with `private_key` and `certificate` options for signing SAML requests.
> Only available in Grafana v7.3+
The SAML standard recommends using a digital signature for some types of messages, like authentication or logout requests. If the `signature_algorithm` option is configured, Grafana will put a digital signature into SAML requests. Supported signature types are `rsa-sha1`, `rsa-sha256`, `rsa-sha512`. This option should match your IdP configuration, otherwise, signature validation will fail. Grafana uses key and certificate configured with `private_key` and `certificate` options for signing SAML requests.
### IdP metadata
@@ -113,9 +117,19 @@ The integration provides two key endpoints as part of Grafana:
- The `/saml/metadata` endpoint, which contains the SP metadata. You can either download and upload it manually, or youmake the IdP request it directly from the endpoint. Some providers name it Identifier or Entity ID.
- The `/saml/acs` endpoint, which is intended to receive the ACS (Assertion Customer Service) callback. Some providers name it SSO URL or Reply URL.
### Single Logout
### IdP-initiated Single Sign-On (SSO)
Single Logout feature allows user to log out from all applications associated with current IdP session established via SAML SSO. If `single_logout` option set to `true` and user logs out, Grafana requests IdP to terminate user session. Then IdP triggers logout process for all other applications which user logged in with the same IdP session (application should support single logout). And conversely, if another application connected to the same IdP initiates single logout, Grafana gets logout request from IdP and terminates user session.
> Only available in Grafana v7.3+
By default, Grafana allows only service provider (SP) initiated logins (when the user logs in with SAML via Grafanas login page). If you want users to log in into Grafana directly from your identity provider (IdP), set the `allow_idp_initiated` configuration option to `true` and configure `relay_state` with the same value specified in the IdP configuration.
IdP-initiated SSO has some security risks, so make sure you understand the risks before enabling this feature. When using IdP-initiated SSO, Grafana receives unsolicited SAML requests and can't verify that login flow was started by the user. This makes it hard to detect whether SAML message has been stolen or replaced. Because of this, IdP-initiated SSO is vulnerable to login cross-site request forgery (CSRF) and man in the middle (MITM) attacks. We do not recommend using IdP-initiated SSO and keeping it disabled whenever possible.
### Single logout
> Only available in Grafana v7.3+
SAML's single logout feature allows users to log out from all applications associated with the current IdP session established via SAML SSO. If the `single_logout` option is set to `true` and a user logs out, Grafana requests IdP to end the user session which in turn triggers logout from all other applications the user is logged into using the same IdP session (applications should support single logout). Conversely, if another application connected to the same IdP logs out using single logout, Grafana receives a logout request from IdP and ends the user session.
### Assertion mapping

View File

@@ -15,16 +15,53 @@ weight = -17
This topic includes the release notes for the Grafana v7.3. For all details, read the full [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md).
## Highlights
Grafana 7.3 comes with a number of features and enhancements:
The main highlights are:
- [**Google Cloud Monitoring:** Out of the box dashboards]({{< relref "#cloud-monitoring-out-of-the-box-dashboards" >}})
- [**Shorten URL for dashboards and Explore**]({{< relref "#shorten-url-for-dashboards-and-explore" >}})
- [**Table improvements and new image cell mode**]({{< relref "#table-improvements-and-new-image-cell-mode" >}})
- [**New color scheme option**]({{< relref "#new-color-scheme-option" >}})
- [**SigV4 Authentication for Amazon Elasticsearch Service**]({{< relref "#sigv4-authentication-for-aws-users" >}})
#### Cloud monitoring out-of-the-box dashboards
## Table improvements and new image cell mode
The updated Cloud monitoring data source is shipped with pre-configured dashboards for five of the most popular GCP services:
The table has been updated with improved hover behavior for cells that have longer content than what fits the current column width. As you can see
in the animated gif below the cell will automatically expand to show you full content of the cell.
{{< figure src="/img/docs/v73/table_hover.gif" max-width="900px" caption="Table hover" >}}
Another new feature that can be seen in the image above is the new image cell display mode. If you have a field value that is an image URL or a base64 encoded image you can configure the table to display it as an image.
## New color scheme option
{{< figure src="/img/docs/v73/color_scheme_dropdown.png" max-width="450px" caption="Color scheme" class="pull-right" >}}
A new standard field [color scheme]({{< relref "../panels/field-options/standard-field-options.md#color-scheme" >}}) option has been added. This new option will provide a unified way for all new panels to specify how colors should be assigned.
* **Single color**: Specify a single color, useful in an override rule.
* **From thresholds**: Informs Grafana to take the color from the matching threshold.
* **Classic palette**: Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations.
* **Green-Yellow-Red (by value)**: This is a continuous color scheme where Grafana will interpolate a color based on the value being displayed and the field min & max values.
* **Blue-Yellow-Red (by value)**: Same as above but different colors.
* **Blues (by value)**: Same as above but color scheme go from panel background to blue.
<div class="clearfix"></div>
As you can see this adds new continuous color schemes where Grafana will interpolate colors. A great use of these new color schemes is the table panel where you can color the background and get a heatmap like effect.
{{< figure src="/img/docs/v73/table_color_scheme.png" max-width="900px" caption="table color scheme" >}}
Another thing to highlight is that all these new color schemes are theme aware and adapt to the current theme. For example here is how the new monochrome color scheme look like in the light theme:
{{< figure src="/img/docs/v73/table_color_scheme_mono_light.png" max-width="900px" caption="table color monochrome scheme" >}}
As this new option is a standard field option it works in every panel. Here is another example from the [Bar Gauge]({{< relref "../panels/visualizations/bar-gauge-panel.md" >}}) panel.
{{< figure src="/img/docs/v73/bar_gauge_gradient_color_scheme.png" max-width="900px" caption="bar gauge color scheme" >}}
## Google Cloud monitoring out-of-the-box dashboards
The updated Google Cloud monitoring data source is shipped with pre-configured dashboards for five of the most popular Google Cloud Platform (GCP) services:
- BigQuery
- Cloud Load Balancing
@@ -38,7 +75,13 @@ For more details, see the [Google Cloud Monitoring docs]({{<relref "../datasourc
## Shorten URL for dashboards and Explore
This is an amazing new feature that was created in cooperation with one of our community members. The new **share shortened link** capability allows you to create smaller and simpler URLs of the format `/goto/:uid` instead of using longer URLs that can contain complex query parameters. In Explore, you can create a shortened link by clicking on the share button in Explore toolbar. In the dashboards, a shortened url option is available through the share panel or dashboard button.
This is an amazing new feature that was created in cooperation with one of our community members. The new share shortened link capability allows you to create smaller and simpler URLs of the format `/goto/:uid` instead of using longer URLs that can contain complex query parameters. In Explore, you can create a shortened link by clicking on the share button in Explore toolbar. In the dashboards, a shortened url option is available through the share panel or dashboard button.
## SigV4 authentication for AWS users
You can now configure your Elasticsearch data source to access your Amazon Elasticsearch Service domain directly from Grafana.
For more details, refer to the [Elasticsearch docs]({{<relref "../datasources/elasticsearch/#aws-signature-version-4-authentication">}}).
## Grafana Enterprise features

View File

@@ -29,7 +29,7 @@ e2e.scenario({
e2e.components.DashboardLinks.link()
.should('be.visible')
.and(links => {
expect(links).to.have.length(13);
expect(links).to.have.length.greaterThan(13);
for (let index = 0; index < links.length; index++) {
expect(Cypress.$(links[index]).attr('href')).contains(`var-custom=${variableValue}`);

1
go.mod
View File

@@ -30,6 +30,7 @@ require (
github.com/facebookgo/structtag v0.0.0-20150214074306-217e25fb9691 // indirect
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
github.com/fatih/color v1.9.0
github.com/gchaincl/sqlhooks v1.3.0
github.com/go-macaron/binding v0.0.0-20190806013118-0b4f37bab25b
github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07
github.com/go-macaron/session v0.0.0-20190805070824-1a3cdc6f5659

2
go.sum
View File

@@ -317,6 +317,8 @@ github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03D
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk=
github.com/gchaincl/sqlhooks v1.3.0 h1:yKPXxW9a5CjXaVf2HkQn6wn7TZARvbAOAelr3H8vK2Y=
github.com/gchaincl/sqlhooks v1.3.0/go.mod h1:9BypXnereMT0+Ys8WGWHqzgkkOfHIhyeUCqXC24ra34=
github.com/getkin/kin-openapi v0.2.0/go.mod h1:V1z9xl9oF5Wt7v32ne4FmiF1alpS4dM6mNzoywPOXlk=
github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=

3
grafana-mixin/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
alerts.yaml
rules.yaml
dashboards_out

13
grafana-mixin/Makefile Normal file
View File

@@ -0,0 +1,13 @@
all: fmt lint build clean
fmt:
./scripts/format.sh
lint:
./scripts/lint.sh
build:
./scripts/build.sh
clean:
rm -rf dashboards_out alerts.yaml rules.yaml

28
grafana-mixin/README.md Normal file
View File

@@ -0,0 +1,28 @@
# Grafana Mixin
_This is a work in progress. We aim for it to become a good role model for alerts
and dashboards eventually, but it is not quite there yet._
The Grafana Mixin is a set of configurable, reusable, and extensible alerts and
dashboards based on the metrics exported by Grafana. The mixin creates
recording and alerting rules for Prometheus and suitable dashboard descriptions
for Grafana.
To use them, you need to have `mixtool` and `jsonnetfmt` installed. If you
have a working Go development environment, it's easiest to run the following:
```bash
$ go get github.com/monitoring-mixins/mixtool/cmd/mixtool
$ go get github.com/google/go-jsonnet/cmd/jsonnetfmt
```
You can then build the Prometheus rules files `alerts.yaml` and
`rules.yaml` and a directory `dashboard_out` with the JSON dashboard files
for Grafana:
```bash
$ make build
```
For more advanced uses of mixins, see
https://github.com/monitoring-mixins/docs.

View File

@@ -0,0 +1,14 @@
groups:
- name: GrafanaAlerts
rules:
- alert: GrafanaRequestsFailing
for: 5m
expr: |
100 * namespace_job_handler_statuscode:http_request_total:rate5m{handler!~"/datasources/proxy/:id.*|/ds/query|/tsdb/query", statuscode=~"5.."}
/
namespace_job_handler_statuscode:http_request_total:rate5m{handler!~"/datasources/proxy/:id.*|/ds/query|/tsdb/query"}
> 0.5
labels:
severity: 'critical'
annotations:
message: "'{{ $labels.namespace }}' / '{{ $labels.job }}' / '{{ $labels.handler }}' is experiencing {{ $value | humanize }}% errors"

View File

@@ -0,0 +1,528 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": 35,
"iteration": 1602761142538,
"links": [],
"panels": [
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [],
"noValue": "0",
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 0,
"y": 0
},
"id": 6,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"mean"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.4",
"targets": [
{
"expr": "grafana_alerting_result_total{job=~\"$job\", instance=~\"$instance\", state=\"alerting\"}",
"instant": true,
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Firing Alerts",
"type": "stat"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 6,
"y": 0
},
"id": 8,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"mean"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.4",
"targets": [
{
"expr": "sum(grafana_stat_totals_dashboard{job=~\"$job\", instance=~\"$instance\"})",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Dashboards",
"type": "stat"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"custom": {
"align": null
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 12,
"x": 12,
"y": 0
},
"id": 10,
"options": {
"showHeader": true
},
"pluginVersion": "7.0.4",
"targets": [
{
"expr": "grafana_build_info{job=~\"$job\", instance=~\"$instance\"}",
"instant": true,
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Build Info",
"transformations": [
{
"id": "labelsToFields",
"options": {}
},
{
"id": "organize",
"options": {
"excludeByName": {
"Time": true,
"Value": true,
"branch": true,
"container": true,
"goversion": true,
"namespace": true,
"pod": true,
"revision": true
},
"indexByName": {
"Time": 7,
"Value": 11,
"branch": 4,
"container": 8,
"edition": 2,
"goversion": 6,
"instance": 1,
"job": 0,
"namespace": 9,
"pod": 10,
"revision": 5,
"version": 3
},
"renameByName": {}
}
}
],
"type": "table"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 5
},
"hiddenSeries": false,
"id": 2,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": true,
"steppedLine": false,
"targets": [
{
"expr": "sum by (statuscode) (irate(http_request_total{job=~\"$job\", instance=~\"$instance\"}[1m])) ",
"interval": "",
"legendFormat": "{{statuscode}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "RPS",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:157",
"format": "reqps",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:158",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": false
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 5
},
"hiddenSeries": false,
"id": 4,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "max(http_request_duration_milliseconds{job=~\"$job\", instance=~\"$instance\", quantile=\"0.99\"})",
"interval": "",
"legendFormat": "max-99th",
"refId": "A"
},
{
"expr": "max(http_request_duration_milliseconds{job=~\"$job\", instance=~\"$instance\", quantile=\"0.9\"})",
"interval": "",
"legendFormat": "max-90th",
"refId": "B"
},
{
"expr": "sum(irate(http_request_duration_milliseconds_sum{job=~\"$job\", instance=~\"$instance\"}[$__interval])) / sum(irate(http_request_duration_milliseconds_count{job=~\"$job\", instance=~\"$instance\"}[$__interval])) ",
"interval": "",
"legendFormat": "avg",
"refId": "C"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Request Latency",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:210",
"format": "ms",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:211",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"schemaVersion": 25,
"style": "dark",
"tags": [],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "prometheus",
"value": "prometheus"
},
"hide": 0,
"includeAll": false,
"label": null,
"multi": false,
"name": "datasource",
"options": [],
"query": "prometheus",
"queryValue": "",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"type": "datasource"
},
{
"allValue": ".*",
"current": {
"selected": true,
"tags": [],
"text": "All",
"value": [
"$__all"
]
},
"datasource": "$datasource",
"definition": "label_values(grafana_build_info, job)",
"hide": 0,
"includeAll": true,
"label": null,
"multi": true,
"name": "job",
"options": [],
"query": "label_values(grafana_build_info, job)",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"allValue": ".*",
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"datasource": "$datasource",
"definition": "label_values(grafana_build_info, instance)",
"hide": 0,
"includeAll": true,
"label": null,
"multi": true,
"name": "instance",
"options": [],
"query": "label_values(grafana_build_info, instance)",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "",
"title": "Grafana Overview",
"uid": "6be0s85Mk",
"version": 4
}

View File

@@ -0,0 +1,15 @@
{
grafanaDashboards: {
'grafana-overview.json': (import 'dashboards/grafana-overview.json'),
},
// Helper function to ensure that we don't override other rules, by forcing
// the patching of the groups list, and not the overall rules object.
local importRules(rules) = {
groups+: std.native('parseYaml')(rules)[0].groups,
},
prometheusRules+: importRules(importstr 'rules/rules.yaml'),
prometheusAlerts+: importRules(importstr 'alerts/alerts.yaml'),
}

View File

@@ -0,0 +1,7 @@
groups:
- name: grafana_rules
rules:
# Record error rate of http requests excluding dataproxy, /ds/query and /tsdb/query requests
- record: namespace_job_handler_statuscode:http_request_total:rate5m
expr: |
sum by (namespace, job, handler, statuscode) (rate(http_request_total[5m]))

6
grafana-mixin/scripts/build.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
set -eo pipefail
cd "$(dirname "$0")"/..
mixtool generate all mixin.libsonnet

View File

@@ -0,0 +1 @@
JSONNET_FMT="jsonnetfmt -n 2 --max-blank-lines 2 --string-style s --comment-style s"

View File

@@ -0,0 +1,9 @@
#!/bin/bash
set -eo pipefail
cd "$(dirname "$0")"/..
. scripts/common.sh
find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \
xargs -n 1 -- ${JSONNET_FMT} -i

13
grafana-mixin/scripts/lint.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
set -eo pipefail
cd "$(dirname "$0")"/..
. scripts/common.sh
find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \
while read f; do \
${JSONNET_FMT} "$f" | diff -u "$f" -; \
done
mixtool lint mixin.libsonnet

View File

@@ -2,5 +2,5 @@
"npmClient": "yarn",
"useWorkspaces": true,
"packages": ["packages/*"],
"version": "7.3.0-pre.0"
"version": "7.3.1"
}

View File

@@ -3,7 +3,7 @@
"license": "Apache-2.0",
"private": true,
"name": "grafana",
"version": "7.3.0-pre",
"version": "7.3.1",
"repository": "github:grafana/grafana",
"scripts": {
"api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js",
@@ -45,8 +45,8 @@
"ci:test-frontend": "yarn run prettier:check && yarn run packages:typecheck && yarn run typecheck && yarn run test"
},
"grafana": {
"whatsNewUrl": "https://grafana.com/docs/grafana/latest/guides/whats-new-in-v7-2/",
"releaseNotesUrl": "https://community.grafana.com/t/release-notes-v7-2-x/36321"
"whatsNewUrl": "https://grafana.com/docs/grafana/latest/guides/whats-new-in-v7-3/",
"releaseNotesUrl": "https://community.grafana.com/t/release-notes-v7-3-x/37993"
},
"husky": {
"hooks": {
@@ -269,6 +269,7 @@
"react-loadable": "5.5.0",
"react-popper": "1.3.3",
"react-redux": "7.2.0",
"react-reverse-portal": "^2.0.1",
"react-sizeme": "2.6.12",
"react-split-pane": "0.1.89",
"react-transition-group": "4.3.0",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/data",
"version": "7.3.0-pre.0",
"version": "7.3.1",
"description": "Grafana Data Library",
"keywords": [
"typescript"

View File

@@ -3,13 +3,14 @@ import _ from 'lodash';
// Types
import { Field, FieldType } from '../types/dataFrame';
import { GrafanaTheme, GrafanaThemeType } from '../types/theme';
import { GrafanaTheme } from '../types/theme';
import { DecimalCount, DecimalInfo, DisplayProcessor, DisplayValue } from '../types/displayValue';
import { getValueFormat } from '../valueFormats/valueFormats';
import { getMappedValue } from '../utils/valueMappings';
import { dateTime } from '../datetime';
import { KeyValue, TimeZone } from '../types';
import { getScaleCalculator } from './scale';
import { getTestTheme } from '../utils/testdata/testTheme';
interface DisplayProcessorOptions {
field: Partial<Field>;
@@ -41,7 +42,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
const config = field.config ?? {};
// Theme should be required or we need access to default theme instance from here
const theme = options.theme ?? ({ type: GrafanaThemeType.Dark } as GrafanaTheme);
const theme = options.theme ?? getTestTheme();
let unit = config.unit;
let hasDateUnit = unit && (timeFormats[unit] || unit.startsWith('time:'));

View File

@@ -1,4 +1,5 @@
import { Field, GrafanaThemeType, GrafanaTheme, FieldColorModeId } from '../types';
import { Field, FieldColorModeId } from '../types';
import { getTestTheme } from '../utils/testdata/testTheme';
import { fieldColorModeRegistry, FieldValueColorCalculator } from './fieldColor';
describe('fieldColorModeRegistry', () => {
@@ -9,10 +10,7 @@ describe('fieldColorModeRegistry', () => {
function getCalculator(options: GetCalcOptions): FieldValueColorCalculator {
const mode = fieldColorModeRegistry.get(options.mode);
return mode.getCalculator(
{ state: { seriesIndex: options.seriesIndex } } as Field,
{ type: GrafanaThemeType.Dark } as GrafanaTheme
);
return mode.getCalculator({ state: { seriesIndex: options.seriesIndex } } as Field, getTestTheme());
}
it('Schemes should interpolate', () => {

View File

@@ -54,20 +54,74 @@ export const fieldColorModeRegistry = new Registry<FieldColorMode>(() => {
// }),
new FieldColorSchemeMode({
id: FieldColorModeId.PaletteClassic,
name: 'By series / Classic palette',
//description: 'Assigns color based on series or field index',
name: 'Classic palette',
isContinuous: false,
isByValue: false,
colors: classicColors,
}),
new FieldColorSchemeMode({
id: 'continuous-GrYlRd',
name: 'By value / Green Yellow Red (gradient)',
//description: 'Interpolated colors based value, min and max',
name: 'Green-Yellow-Red',
isContinuous: true,
isByValue: true,
colors: ['green', 'yellow', 'red'],
}),
new FieldColorSchemeMode({
id: 'continuous-BlYlRd',
name: 'Blue-Yellow-Red',
isContinuous: true,
isByValue: true,
colors: ['dark-blue', 'super-light-yellow', 'dark-red'],
}),
new FieldColorSchemeMode({
id: 'continuous-YlRd',
name: 'Yellow-Red',
isContinuous: true,
isByValue: true,
colors: ['super-light-yellow', 'dark-red'],
}),
new FieldColorSchemeMode({
id: 'continuous-BlPu',
name: 'Blue-Purple',
isContinuous: true,
isByValue: true,
colors: ['blue', 'purple'],
}),
new FieldColorSchemeMode({
id: 'continuous-YlBl',
name: 'Yellow-Blue',
isContinuous: true,
isByValue: true,
colors: ['super-light-yellow', 'dark-blue'],
}),
new FieldColorSchemeMode({
id: 'continuous-blues',
name: 'Blues',
isContinuous: true,
isByValue: true,
colors: ['panel-bg', 'dark-blue'],
}),
new FieldColorSchemeMode({
id: 'continuous-reds',
name: 'Reds',
isContinuous: true,
isByValue: true,
colors: ['panel-bg', 'dark-red'],
}),
new FieldColorSchemeMode({
id: 'continuous-greens',
name: 'Greens',
isContinuous: true,
isByValue: true,
colors: ['panel-bg', 'dark-green'],
}),
new FieldColorSchemeMode({
id: 'continuous-purples',
name: 'Purples',
isContinuous: true,
isByValue: true,
colors: ['panel-bg', 'dark-purple'],
}),
];
});

View File

@@ -2,9 +2,9 @@ import merge from 'lodash/merge';
import { getFieldDisplayValues, GetFieldDisplayValuesOptions } from './fieldDisplay';
import { toDataFrame } from '../dataframe/processDataFrame';
import { ReducerID } from '../transformations/fieldReducer';
import { GrafanaTheme } from '../types/theme';
import { MappingType } from '../types';
import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry';
import { getTestTheme } from '../utils/testdata/testTheme';
describe('FieldDisplay', () => {
beforeAll(() => {
@@ -241,7 +241,7 @@ function createDisplayOptions(extend: Partial<GetFieldDisplayValuesOptions> = {}
overrides: [],
defaults: {},
},
theme: {} as GrafanaTheme,
theme: getTestTheme(),
};
return merge<GetFieldDisplayValuesOptions, any>(options, extend);

View File

@@ -11,15 +11,14 @@ import { MutableDataFrame, toDataFrame } from '../dataframe';
import {
DataFrame,
Field,
FieldColorModeId,
FieldConfig,
FieldConfigPropertyItem,
FieldConfigSource,
FieldType,
GrafanaTheme,
InterpolateFunction,
ThresholdsMode,
FieldColorModeId,
ScopedVars,
ThresholdsMode,
} from '../types';
import { locationUtil, Registry } from '../utils';
import { mockStandardProperties } from '../utils/tests/mockStandardProperties';
@@ -28,6 +27,7 @@ import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
import { getFieldDisplayName } from './fieldState';
import { ArrayVector } from '../vector';
import { getDisplayProcessor } from './displayProcessor';
import { getTestTheme } from '../utils/testdata/testTheme';
const property1: any = {
id: 'custom.property1', // Match field properties
@@ -87,6 +87,21 @@ describe('Global MinMax', () => {
expect(minmax.min).toEqual(-20);
expect(minmax.max).toEqual(1234);
});
describe('when value is null', () => {
it('then global min max should be null', () => {
const frame = toDataFrame({
fields: [
{ name: 'Time', type: FieldType.time, values: [1] },
{ name: 'Value', type: FieldType.number, values: [null] },
],
});
const { min, max } = findNumericFieldMinMax([frame]);
expect(min).toBeNull();
expect(max).toBeNull();
});
});
});
describe('applyFieldOverrides', () => {
@@ -136,7 +151,7 @@ describe('applyFieldOverrides', () => {
},
replaceVariables: (value: any) => value,
getDataSourceSettingsByUid: undefined as any,
theme: {} as GrafanaTheme,
theme: getTestTheme(),
fieldConfigRegistry: new FieldConfigOptionsRegistry(),
});
@@ -199,7 +214,7 @@ describe('applyFieldOverrides', () => {
fieldConfigRegistry: customFieldRegistry,
getDataSourceSettingsByUid: undefined as any,
replaceVariables: v => v,
theme: {} as GrafanaTheme,
theme: getTestTheme(),
})[0];
const outField = processed.fields[0];
@@ -216,7 +231,7 @@ describe('applyFieldOverrides', () => {
fieldConfig: src as FieldConfigSource, // defaults + overrides
replaceVariables: (undefined as any) as InterpolateFunction,
getDataSourceSettingsByUid: undefined as any,
theme: (undefined as any) as GrafanaTheme,
theme: getTestTheme(),
fieldConfigRegistry: customFieldRegistry,
})[0];
const valueColumn = data.fields[1];
@@ -244,7 +259,7 @@ describe('applyFieldOverrides', () => {
fieldConfig: src as FieldConfigSource, // defaults + overrides
replaceVariables: (undefined as any) as InterpolateFunction,
getDataSourceSettingsByUid: undefined as any,
theme: (undefined as any) as GrafanaTheme,
theme: getTestTheme(),
autoMinMax: true,
})[0];
const valueColumn = data.fields[1];
@@ -268,7 +283,7 @@ describe('applyFieldOverrides', () => {
return value;
}) as InterpolateFunction,
getDataSourceSettingsByUid: undefined as any,
theme: (undefined as any) as GrafanaTheme,
theme: getTestTheme(),
autoMinMax: true,
fieldConfigRegistry: customFieldRegistry,
})[0];
@@ -521,7 +536,7 @@ describe('getLinksSupplier', () => {
// this is used only for internal links so isn't needed here
() => ({} as any),
{
theme: {} as GrafanaTheme,
theme: getTestTheme(),
}
);
supplier({});
@@ -568,7 +583,7 @@ describe('getLinksSupplier', () => {
// We do not need to interpolate anything for this test
(value, vars, format) => value,
uid => ({ name: 'testDS' } as any),
{ theme: {} as GrafanaTheme }
{ theme: getTestTheme() }
);
const links = supplier({ valueRowIndex: 0 });
expect(links.length).toBe(1);

View File

@@ -55,11 +55,23 @@ export function findNumericFieldMinMax(data: DataFrame[]): GlobalMinMax {
for (const field of frame.fields) {
if (field.type === FieldType.number) {
const stats = reduceField({ field, reducers });
if (stats[ReducerID.min] < min) {
min = stats[ReducerID.min];
const statsMin = stats[ReducerID.min];
const statsMax = stats[ReducerID.max];
if (!statsMin) {
min = statsMin;
}
if (stats[ReducerID.max] > max) {
max = stats[ReducerID.max];
if (!statsMax) {
max = statsMax;
}
if (statsMin && statsMin < min) {
min = statsMin;
}
if (statsMax && statsMax > max) {
max = statsMax;
}
}
}

View File

@@ -2,6 +2,7 @@ import { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy';
import { applyFieldOverrides } from './fieldOverrides';
import { toDataFrame } from '../dataframe';
import { GrafanaTheme } from '../types';
import { getTestTheme } from '../utils/testdata/testTheme';
describe('getFieldDisplayValuesProxy', () => {
const data = applyFieldOverrides({
@@ -30,7 +31,7 @@ describe('getFieldDisplayValuesProxy', () => {
replaceVariables: (val: string) => val,
getDataSourceSettingsByUid: (val: string) => ({} as any),
timeZone: 'utc',
theme: {} as GrafanaTheme,
theme: getTestTheme(),
autoMinMax: true,
})[0];

View File

@@ -1,7 +1,8 @@
import { ThresholdsMode, Field, FieldType, GrafanaThemeType, GrafanaTheme } from '../types';
import { ThresholdsMode, Field, FieldType } from '../types';
import { sortThresholds } from './thresholds';
import { ArrayVector } from '../vector/ArrayVector';
import { getScaleCalculator } from './scale';
import { getTestTheme } from '../utils/testdata/testTheme';
describe('getScaleCalculator', () => {
it('should return percent, threshold and color', () => {
@@ -18,7 +19,7 @@ describe('getScaleCalculator', () => {
values: new ArrayVector([0, 50, 100]),
};
const calc = getScaleCalculator(field, { type: GrafanaThemeType.Dark } as GrafanaTheme);
const calc = getScaleCalculator(field, getTestTheme());
expect(calc(70)).toEqual({
percent: 0.7,
threshold: thresholds[1],

View File

@@ -2,7 +2,7 @@ import { map } from 'rxjs/operators';
import { DataTransformerID } from './ids';
import { DataTransformerInfo } from '../../types/transformations';
import { DataFrame, Field } from '../../types/dataFrame';
import { DataFrame, Field, TIME_SERIES_VALUE_FIELD_NAME } from '../../types/dataFrame';
import { ArrayVector } from '../../vector';
export enum ConcatenateFrameNameMode {
@@ -73,7 +73,7 @@ export function concatenateFields(data: DataFrame[], opts: ConcatenateTransforme
} else if (opts.frameNameMode === ConcatenateFrameNameMode.Label) {
copy.labels = { ...f.labels };
copy.labels[frameNameLabel] = frame.name;
} else if (!copy.name || copy.name === 'Value') {
} else if (!copy.name || copy.name === TIME_SERIES_VALUE_FIELD_NAME) {
copy.name = frame.name;
} else {
copy.name = `${frame.name} · ${f.name}`;

View File

@@ -1,4 +1,3 @@
import { SelectableValue } from './select';
import { Observable } from 'rxjs';
/**
@@ -17,7 +16,7 @@ export enum LiveChannelScope {
/**
* @alpha -- experimental
*/
export interface LiveChannelConfig<TMessage = any> {
export interface LiveChannelConfig<TMessage = any, TController = any> {
/**
* The path definition. either static, or it may contain variables identifed with {varname}
*/
@@ -28,11 +27,6 @@ export interface LiveChannelConfig<TMessage = any> {
*/
description?: string;
/**
* When variables exist, this list will identify each one
*/
variables?: Array<SelectableValue<string>>;
/**
* The channel keeps track of who else is connected to the same channel
*/
@@ -46,6 +40,9 @@ export interface LiveChannelConfig<TMessage = any> {
/** convert the raw stream message into a message that should be broadcast */
processMessage?: (msg: any) => TMessage;
/** some channels are managed by an explicit interface */
getController?: () => TController;
}
export enum LiveChannelConnectionState {

View File

@@ -27,10 +27,12 @@ export enum LogLevel {
unknown = 'unknown',
}
// Used for meta information such as common labels or returned log rows in logs view in Explore
export enum LogsMetaKind {
Number,
String,
LabelsMap,
Error,
}
export enum LogsSortOrder {

View File

@@ -2,12 +2,14 @@ import { ComponentClass } from 'react';
import { KeyValue } from './data';
import { LiveChannelSupport } from './live';
/** Describes plugins life cycle status */
export enum PluginState {
alpha = 'alpha', // Only included it `enable_alpha` is true
alpha = 'alpha', // Only included if `enable_alpha` config option is true
beta = 'beta', // Will show a warning banner
deprecated = 'deprecated', // Will continue to work -- but not show up in the options to add
}
/** Describes {@link https://grafana.com/docs/grafana/latest/plugins | type of plugin} */
export enum PluginType {
panel = 'panel',
datasource = 'datasource',
@@ -15,12 +17,26 @@ export enum PluginType {
renderer = 'renderer',
}
/** Describes status of {@link https://grafana.com/docs/grafana/latest/plugins/plugin-signature-verification/ | plugin signature} */
export enum PluginSignatureStatus {
internal = 'internal', // core plugin, no signature
valid = 'valid', // signed and accurate MANIFEST
invalid = 'invalid', // invalid signature
modified = 'modified', // valid signature, but content mismatch
unsigned = 'unsigned', // no MANIFEST file
missing = 'missing', // missing signature file
}
/** Describes error code returned from Grafana plugins API call */
export enum PluginErrorCode {
missingSignature = 'signatureMissing',
invalidSignature = 'signatureInvalid',
modifiedSignature = 'signatureModified',
}
/** Describes error returned from Grafana plugins API call */
export interface PluginError {
errorCode: PluginErrorCode;
pluginId: string;
}
export interface PluginMeta<T extends KeyValue = {}> {

View File

@@ -9,6 +9,7 @@ import {
calculateStats,
getLogLevelFromKey,
sortLogsResult,
checkLogsError,
} from './logs';
describe('getLoglevel()', () => {
@@ -352,3 +353,15 @@ describe('sortLogsResult', () => {
});
});
});
describe('checkLogsError()', () => {
const log = ({
labels: {
__error__: 'Error Message',
foo: 'boo',
},
} as any) as LogRowModel;
test('should return correct error if error is present', () => {
expect(checkLogsError(log)).toStrictEqual({ hasError: true, errorMessage: 'Error Message' });
});
});

View File

@@ -210,3 +210,16 @@ export const sortLogsResult = (logsResult: LogsModel | null, sortOrder: LogsSort
export const sortLogRows = (logRows: LogRowModel[], sortOrder: LogsSortOrder) =>
sortOrder === LogsSortOrder.Ascending ? logRows.sort(sortInAscendingOrder) : logRows.sort(sortInDescendingOrder);
// Currently supports only error condition in Loki logs
export const checkLogsError = (logRow: LogRowModel): { hasError: boolean; errorMessage?: string } => {
if (logRow.labels.__error__) {
return {
hasError: true,
errorMessage: logRow.labels.__error__,
};
}
return {
hasError: false,
};
};

View File

@@ -34,7 +34,8 @@ export type Color =
| 'dark-purple'
| 'semi-dark-purple'
| 'light-purple'
| 'super-light-purple';
| 'super-light-purple'
| 'panel-bg';
type ThemeVariants = {
dark: string;
@@ -82,6 +83,8 @@ export function buildColorsMapForTheme(theme: GrafanaTheme): Record<Color, strin
}
}
colorsMap['panel-bg'] = theme.colors.panelBg;
return colorsMap;
}
@@ -118,7 +121,25 @@ export function getColorForTheme(color: string, theme: GrafanaTheme): string {
export function getColorFromHexRgbOrName(color: string, type?: GrafanaThemeType): string {
const themeType = type ?? GrafanaThemeType.Dark;
return getColorForTheme(color, ({ type: themeType } as unknown) as GrafanaTheme);
if (themeType === GrafanaThemeType.Dark) {
const darkTheme = ({
type: themeType,
colors: {
panelBg: '#141619',
},
} as unknown) as GrafanaTheme;
return getColorForTheme(color, darkTheme);
}
const lightTheme = ({
type: themeType,
colors: {
panelBg: '#000000',
},
} as unknown) as GrafanaTheme;
return getColorForTheme(color, lightTheme);
}
const buildNamedColorsPalette = () => {

View File

@@ -0,0 +1,12 @@
import { GrafanaTheme, GrafanaThemeType } from '../../types/theme';
export function getTestTheme(type: GrafanaThemeType = GrafanaThemeType.Dark): GrafanaTheme {
return ({
type,
isDark: type === GrafanaThemeType.Dark,
isLight: type === GrafanaThemeType.Light,
colors: {
panelBg: 'white',
},
} as unknown) as GrafanaTheme;
}

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/e2e-selectors",
"version": "7.3.0-pre.0",
"version": "7.3.1",
"description": "Grafana End-to-End Test Selectors Library",
"keywords": [
"cli",

View File

@@ -135,4 +135,14 @@ export const Pages = {
SoloPanel: {
url: (page: string) => `/d-solo/${page}`,
},
PluginsList: {
page: 'Plugins list page',
list: 'Plugins list',
listItem: 'Plugins list item',
signatureErrorNotice: 'Unsigned plugins notice',
},
PluginPage: {
page: 'Plugin page',
signatureInfo: 'Plugin signature info',
},
};

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/e2e",
"version": "7.3.0-pre.0",
"version": "7.3.1",
"description": "Grafana End-to-End Test Library",
"keywords": [
"cli",
@@ -44,7 +44,7 @@
"types": "src/index.ts",
"dependencies": {
"@cypress/webpack-preprocessor": "4.1.3",
"@grafana/e2e-selectors": "7.3.0-pre.0",
"@grafana/e2e-selectors": "7.3.1",
"@grafana/tsconfig": "^1.0.0-rc1",
"@mochajs/json-file-reporter": "^1.2.0",
"blink-diff": "1.0.13",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/runtime",
"version": "7.3.0-pre.0",
"version": "7.3.1",
"description": "Grafana Runtime Library",
"keywords": [
"grafana",
@@ -22,8 +22,8 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@grafana/data": "7.3.0-pre.0",
"@grafana/ui": "7.3.0-pre.0",
"@grafana/data": "7.3.1",
"@grafana/ui": "7.3.1",
"systemjs": "0.20.19",
"systemjs-plugin-css": "0.1.37"
},

View File

@@ -6,6 +6,7 @@
export * from './services';
export * from './config';
export * from './types';
export * from './measurement';
export { loadPluginCss, SystemJS, PluginCssOptions } from './utils/plugin';
export { reportMetaAnalytics } from './utils/analytics';
export { DataSourceWithBackend, HealthCheckResult, HealthStatus } from './utils/DataSourceWithBackend';

View File

@@ -0,0 +1,250 @@
import { MeasurementCollector } from './collector';
import { MeasurementAction } from './types';
describe('MeasurementCollector', () => {
it('should collect values', () => {
const collector = new MeasurementCollector();
collector.addBatch({
measurements: [
{
name: 'test',
labels: { host: 'a' },
time: 100,
values: {
f0: 0,
f1: 1,
f2: 'hello',
},
},
{
name: 'test',
labels: { host: 'b' },
time: 101,
values: {
f0: 0,
f1: 1,
f2: 'hello',
},
config: {
f2: {
unit: 'mph',
},
},
},
{
name: 'test',
time: 102,
labels: { host: 'a' }, // should append to first value
values: {
// note the missing values for f0/1
f2: 'world',
},
},
],
});
const frames = collector.getData();
expect(frames.length).toEqual(2);
expect(frames[0]).toMatchInlineSnapshot(`
Object {
"fields": Array [
Object {
"config": Object {},
"labels": undefined,
"name": "time",
"type": "time",
"values": Array [
100,
102,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "a",
},
"name": "f0",
"type": "number",
"values": Array [
0,
null,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "a",
},
"name": "f1",
"type": "number",
"values": Array [
1,
null,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "a",
},
"name": "f2",
"type": "string",
"values": Array [
"hello",
"world",
],
},
],
"meta": Object {
"custom": Object {
"labels": Object {
"host": "a",
},
},
},
"name": "test",
"refId": undefined,
}
`);
expect(frames[1]).toMatchInlineSnapshot(`
Object {
"fields": Array [
Object {
"config": Object {},
"labels": undefined,
"name": "time",
"type": "time",
"values": Array [
101,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "b",
},
"name": "f0",
"type": "number",
"values": Array [
0,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "b",
},
"name": "f1",
"type": "number",
"values": Array [
1,
],
},
Object {
"config": Object {
"unit": "mph",
},
"labels": Object {
"host": "b",
},
"name": "f2",
"type": "string",
"values": Array [
"hello",
],
},
],
"meta": Object {
"custom": Object {
"labels": Object {
"host": "b",
},
},
},
"name": "test",
"refId": undefined,
}
`);
collector.addBatch({
action: MeasurementAction.Replace,
measurements: [
{
name: 'test',
time: 105,
labels: { host: 'a' },
values: {
f1: 10,
},
},
],
});
const frames2 = collector.getData();
expect(frames2.length).toEqual(2);
expect(frames2[0].length).toEqual(1); // not three!
expect(frames2[0]).toMatchInlineSnapshot(`
Object {
"fields": Array [
Object {
"config": Object {},
"labels": undefined,
"name": "time",
"type": "time",
"values": Array [
105,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "a",
},
"name": "f0",
"type": "number",
"values": Array [
null,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "a",
},
"name": "f1",
"type": "number",
"values": Array [
10,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "a",
},
"name": "f2",
"type": "string",
"values": Array [
null,
],
},
],
"meta": Object {
"custom": Object {
"labels": Object {
"host": "a",
},
},
},
"name": "test",
"refId": undefined,
}
`);
collector.addBatch({
action: MeasurementAction.Clear,
measurements: [],
});
expect(collector.getData().length).toEqual(0);
});
});

View File

@@ -0,0 +1,209 @@
import {
CircularDataFrame,
Labels,
formatLabels,
FieldType,
DataFrame,
matchAllLabels,
parseLabels,
CircularVector,
ArrayVector,
} from '@grafana/data';
import { Measurement, MeasurementBatch, LiveMeasurements, MeasurementsQuery, MeasurementAction } from './types';
interface MeasurementCacheConfig {
append?: 'head' | 'tail';
capacity?: number;
}
/** This is a cache scoped to a the measurement name
*
* @alpha -- experimental
*/
export class MeasurementCache {
readonly frames: Record<string, CircularDataFrame> = {}; // key is the labels
constructor(public name: string, private config: MeasurementCacheConfig) {
if (!this.config) {
this.config = {
append: 'tail',
capacity: 600, // Default capacity 10min @ 1hz
};
}
}
getFrames(match?: Labels): DataFrame[] {
const frames = Object.values(this.frames);
if (!match) {
return frames;
}
return frames.filter(f => {
return matchAllLabels(match, f.meta?.custom?.labels);
});
}
addMeasurement(m: Measurement, action: MeasurementAction): DataFrame {
const key = m.labels ? formatLabels(m.labels) : '';
let frame = this.frames[key];
if (!frame) {
frame = new CircularDataFrame(this.config);
frame.name = this.name;
frame.addField({
name: 'time',
type: FieldType.time,
});
for (const [key, value] of Object.entries(m.values)) {
frame.addFieldFor(value, key).labels = m.labels;
}
frame.meta = {
custom: {
labels: m.labels,
},
};
this.frames[key] = frame;
}
// Clear existing values
if (action === MeasurementAction.Replace) {
for (const field of frame.fields) {
(field.values as ArrayVector).buffer.length = 0; // same buffer, but reset to empty length
}
}
// Add the timestamp
frame.values['time'].add(m.time || Date.now());
// Attach field config to the current fields
if (m.config) {
for (const [key, value] of Object.entries(m.config)) {
const f = frame.fields.find(f => f.name === key);
if (f) {
f.config = value;
}
}
}
// Append all values (a row)
for (const [key, value] of Object.entries(m.values)) {
let v = frame.values[key];
if (!v) {
const f = frame.addFieldFor(value, key);
f.labels = m.labels;
v = f.values;
}
v.add(value);
}
// Make sure all fields have the same length
frame.validate();
return frame;
}
}
/**
* @alpha -- experimental
*/
export class MeasurementCollector implements LiveMeasurements {
measurements = new Map<string, MeasurementCache>();
config: MeasurementCacheConfig = {
append: 'tail',
capacity: 600, // Default capacity 10min @ 1hz
};
//------------------------------------------------------
// Public
//------------------------------------------------------
getData(query?: MeasurementsQuery): DataFrame[] {
const { name, labels, fields } = query || {};
let data: DataFrame[] = [];
if (name) {
// for now we only match exact names
const m = this.measurements.get(name);
if (m) {
data = m.getFrames(labels);
}
} else {
for (const f of this.measurements.values()) {
data.push.apply(data, f.getFrames(labels));
}
}
if (fields && fields.length) {
let filtered: DataFrame[] = [];
for (const frame of data) {
const match = frame.fields.filter(f => fields.includes(f.name));
if (match.length > 0) {
filtered.push({ ...frame, fields: match }); // Copy the frame with fewer fields
}
}
}
return data;
}
getDistinctNames(): string[] {
return Object.keys(this.measurements);
}
getDistinctLabels(name: string): Labels[] {
const m = this.measurements.get(name);
if (m) {
return Object.keys(m.frames).map(k => parseLabels(k));
}
return [];
}
setCapacity(size: number) {
this.config.capacity = size;
// Now update all the circular buffers
for (const wrap of this.measurements.values()) {
for (const frame of Object.values(wrap.frames)) {
for (const field of frame.fields) {
(field.values as CircularVector).setCapacity(size);
}
}
}
}
getCapacity() {
return this.config.capacity!;
}
clear() {
this.measurements.clear();
}
//------------------------------------------------------
// Collector
//------------------------------------------------------
addBatch = (batch: MeasurementBatch) => {
let action = batch.action ?? MeasurementAction.Append;
if (action === MeasurementAction.Clear) {
this.measurements.clear();
action = MeasurementAction.Append;
}
// Change the local buffer size
if (batch.capacity && batch.capacity !== this.config.capacity) {
this.setCapacity(batch.capacity);
}
for (const measure of batch.measurements) {
const name = measure.name || '';
let m = this.measurements.get(name);
if (!m) {
m = new MeasurementCache(name, this.config);
this.measurements.set(name, m);
}
if (measure.values) {
m.addMeasurement(measure, action);
} else {
console.log('invalid measurement', measure);
}
}
return this;
};
}

View File

@@ -0,0 +1,3 @@
export * from './types';
export * from './collector';
export * from './query';

View File

@@ -0,0 +1,77 @@
import {
DataQueryResponse,
isLiveChannelMessageEvent,
isLiveChannelStatusEvent,
isValidLiveChannelAddress,
LiveChannelAddress,
LoadingState,
} from '@grafana/data';
import { LiveMeasurements, MeasurementsQuery } from './types';
import { getGrafanaLiveSrv } from '../services/live';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
/**
* @alpha -- experimental
*/
export function getLiveMeasurements(addr: LiveChannelAddress): LiveMeasurements | undefined {
if (!isValidLiveChannelAddress(addr)) {
return undefined;
}
const live = getGrafanaLiveSrv();
if (!live) {
return undefined;
}
const channel = live.getChannel<LiveMeasurements>(addr);
const getController = channel?.config?.getController;
return getController ? getController() : undefined;
}
/**
* When you know the stream will be managed measurements
*
* @alpha -- experimental
*/
export function getLiveMeasurementsObserver(
addr: LiveChannelAddress,
requestId: string,
query?: MeasurementsQuery
): Observable<DataQueryResponse> {
const rsp: DataQueryResponse = { data: [] };
if (!addr || !addr.path) {
return of(rsp); // Address not configured yet
}
const live = getGrafanaLiveSrv();
if (!live) {
// This will only happen with the feature flag is not enabled
rsp.error = { message: 'Grafana live is not initalized' };
return of(rsp);
}
rsp.key = requestId;
return live
.getChannel<LiveMeasurements>(addr)
.getStream()
.pipe(
map(evt => {
if (isLiveChannelMessageEvent(evt)) {
rsp.data = evt.message.getData(query);
if (!rsp.data.length) {
// ?? skip when data is empty ???
}
delete rsp.error;
rsp.state = LoadingState.Streaming;
} else if (isLiveChannelStatusEvent(evt)) {
if (evt.error != null) {
rsp.error = rsp.error;
rsp.state = LoadingState.Error;
}
}
return { ...rsp }; // send event on all status messages
})
);
}

View File

@@ -0,0 +1,73 @@
import { DataFrame, Labels, FieldConfig } from '@grafana/data';
/**
* the raw channel events are batches of Measurements
*
* @alpha -- experimental
*/
export interface Measurement {
name: string;
time?: number; // Missing will use the browser time
values: Record<string, any>;
config?: Record<string, FieldConfig>;
labels?: Labels;
}
/**
* @alpha -- experimental
*/
export enum MeasurementAction {
/** The measurements will be added to the client buffer */
Append = 'append',
/** The measurements will replace the client buffer */
Replace = 'replace',
/** All measurements will be removed from the client buffer before processing */
Clear = 'clear',
}
/**
* List of Measurements sent in a batch
*
* @alpha -- experimental
*/
export interface MeasurementBatch {
/**
* The default action is to append values to the client buffer
*/
action?: MeasurementAction;
/**
* List of measurements to process
*/
measurements: Measurement[];
/**
* This will set the capacity on the client buffer for everything
* in the measurement channel
*/
capacity?: number;
}
/**
* @alpha -- experimental
*/
export interface MeasurementsQuery {
name?: string;
labels?: Labels;
fields?: string[]; // only include the fields with these names
}
/**
* Channels that receive Measurements can collect them into frames
*
* @alpha -- experimental
*/
export interface LiveMeasurements {
getData(query?: MeasurementsQuery): DataFrame[];
getDistinctNames(): string[];
getDistinctLabels(name: string): Labels[];
setCapacity(size: number): void;
getCapacity(): number;
}

View File

@@ -6,6 +6,9 @@
*/
import { UrlQueryMap } from '@grafana/data';
/**
* @public
*/
export interface LocationUpdate {
/**
* Target path where you automatically wants to navigate the user.

View File

@@ -39,7 +39,7 @@ export const setGrafanaLiveSrv = (instance: GrafanaLiveSrv) => {
};
/**
* Used to retrieve the {@link GrafanaLiveSrv} that allows you to subscribe to
* Used to retrieve the GrafanaLiveSrv that allows you to subscribe to
* server side events and streams
*
* @alpha -- experimental

View File

@@ -43,7 +43,7 @@ get_file "https://codeclimate.com/downloads/test-reporter/test-reporter-latest-l
"b4138199aa755ebfe171b57cc46910b13258ace5fbc4eaa099c42607cd0bff32"
chmod +x /usr/local/bin/cc-test-reporter
curl -fL -o /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.16/grabpl"
curl -fL -o /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl"
apk add --no-cache git
# Install Mage

View File

@@ -44,7 +44,7 @@ get_file "https://codeclimate.com/downloads/test-reporter/test-reporter-latest-l
"b4138199aa755ebfe171b57cc46910b13258ace5fbc4eaa099c42607cd0bff32"
chmod 755 /usr/local/bin/cc-test-reporter
wget -O /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.16/grabpl"
wget -O /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl"
chmod +x /usr/local/bin/grabpl
# Install Mage

View File

@@ -27,7 +27,7 @@ get_file "https://codeclimate.com/downloads/test-reporter/test-reporter-latest-l
"b4138199aa755ebfe171b57cc46910b13258ace5fbc4eaa099c42607cd0bff32"
chmod +x /usr/local/bin/cc-test-reporter
wget -O /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.16/grabpl"
wget -O /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl"
chmod +x /usr/local/bin/grabpl
# Install Mage

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/toolkit",
"version": "7.3.0-pre.0",
"version": "7.3.1",
"description": "Grafana Toolkit",
"keywords": [
"grafana",

View File

@@ -103,7 +103,7 @@ module.exports = ({ config, mode }) => {
minimize: isProductionBuild,
minimizer: isProductionBuild
? [
new TerserPlugin({ cache: false, parallel: false, sourceMap: false, exclude: /monaco/ }),
new TerserPlugin({ cache: false, parallel: false, sourceMap: false, exclude: /monaco|bizcharts/ }),
new OptimizeCSSAssetsPlugin({}),
]
: [],

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/ui",
"version": "7.3.0-pre.0",
"version": "7.3.1",
"description": "Grafana Components Library",
"keywords": [
"grafana",
@@ -27,15 +27,15 @@
},
"dependencies": {
"@emotion/core": "^10.0.27",
"@grafana/data": "7.3.0-pre.0",
"@grafana/e2e-selectors": "7.3.0-pre.0",
"@grafana/data": "7.3.1",
"@grafana/e2e-selectors": "7.3.1",
"@grafana/slate-react": "0.22.9-grafana",
"@grafana/tsconfig": "^1.0.0-rc1",
"@iconscout/react-unicons": "1.1.4",
"@torkelo/react-select": "3.0.8",
"@types/hoist-non-react-statics": "3.3.1",
"@types/react-beautiful-dnd": "12.1.2",
"@types/react-color": "3.0.1",
"@types/hoist-non-react-statics": "3.3.1",
"@types/react-select": "3.0.8",
"@types/react-table": "7.0.12",
"@types/slate": "0.47.1",
@@ -43,9 +43,8 @@
"bizcharts": "^3.5.8",
"classnames": "2.2.6",
"d3": "5.15.0",
"hoist-non-react-statics": "3.3.2",
"immutable": "3.8.2",
"emotion": "10.0.27",
"hoist-non-react-statics": "3.3.2",
"immutable": "3.8.2",
"jquery": "3.5.1",
"lodash": "4.17.19",

View File

@@ -5,6 +5,7 @@ import { selectors } from '@grafana/e2e-selectors';
import { useTheme } from '../../themes';
import { Icon } from '../Icon/Icon';
import { IconName } from '../../types/icon';
import { getColorsFromSeverity } from '../../utils/colors';
export type AlertVariant = 'success' | 'warning' | 'error' | 'info';
@@ -76,21 +77,11 @@ export const Alert: FC<Props> = ({
};
const getStyles = (theme: GrafanaTheme, severity: AlertVariant, outline: boolean) => {
const { redBase, redShade, greenBase, greenShade, blue80, blue77, white } = theme.palette;
const backgrounds = {
error: css`
background: linear-gradient(90deg, ${redBase}, ${redShade});
`,
warning: css`
background: linear-gradient(90deg, ${redBase}, ${redShade});
`,
info: css`
background: linear-gradient(100deg, ${blue80}, ${blue77});
`,
success: css`
background: linear-gradient(100deg, ${greenBase}, ${greenShade});
`,
};
const { white } = theme.palette;
const severityColors = getColorsFromSeverity(severity, theme);
const background = css`
background: linear-gradient(90deg, ${severityColors[0]}, ${severityColors[0]});
`;
return {
container: css`
@@ -106,7 +97,7 @@ const getStyles = (theme: GrafanaTheme, severity: AlertVariant, outline: boolean
display: flex;
flex-direction: row;
align-items: center;
${backgrounds[severity]}
${background}
`,
icon: css`
padding: 0 ${theme.spacing.md} 0 0;

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { HTMLAttributes } from 'react';
import { Icon } from '../Icon/Icon';
import { useTheme } from '../../themes/ThemeContext';
import { stylesFactory } from '../../themes/stylesFactory';
@@ -6,23 +6,23 @@ import { IconName } from '../../types';
import { Tooltip } from '../Tooltip/Tooltip';
import { getColorForTheme, GrafanaTheme } from '@grafana/data';
import tinycolor from 'tinycolor2';
import { css } from 'emotion';
import { HorizontalGroup } from '..';
import { css, cx } from 'emotion';
import { HorizontalGroup } from '../Layout/Layout';
export type BadgeColor = 'blue' | 'red' | 'green' | 'orange' | 'purple';
export interface BadgeProps {
export interface BadgeProps extends HTMLAttributes<HTMLDivElement> {
text: string;
color: BadgeColor;
icon?: IconName;
tooltip?: string;
}
export const Badge = React.memo<BadgeProps>(({ icon, color, text, tooltip }) => {
export const Badge = React.memo<BadgeProps>(({ icon, color, text, tooltip, className, ...otherProps }) => {
const theme = useTheme();
const styles = getStyles(theme, color);
const badge = (
<div className={styles.wrapper}>
<div className={cx(styles.wrapper, className)} {...otherProps}>
<HorizontalGroup align="center" spacing="xs">
{icon && <Icon name={icon} size="sm" />}
<span>{text}</span>

View File

@@ -9,6 +9,7 @@ import { calculateFontSize } from '../../utils/measureText';
// Types
import { BigValueColorMode, Props, BigValueJustifyMode, BigValueTextMode } from './BigValue';
import { getTextColorForBackground } from '../../utils';
const LINE_HEIGHT = 1.2;
const MAX_TITLE_SIZE = 30;
@@ -51,7 +52,7 @@ export abstract class BigValueLayout {
};
if (this.props.colorMode === BigValueColorMode.Background) {
styles.color = 'white';
styles.color = getTextColorForBackground(this.valueColor);
}
return styles;
@@ -62,6 +63,8 @@ export abstract class BigValueLayout {
fontSize: this.valueFontSize,
fontWeight: 500,
lineHeight: LINE_HEIGHT,
position: 'relative',
zIndex: 1,
};
switch (this.props.colorMode) {
@@ -69,7 +72,7 @@ export abstract class BigValueLayout {
styles.color = this.valueColor;
break;
case BigValueColorMode.Background:
styles.color = 'white';
styles.color = getTextColorForBackground(this.valueColor);
}
return styles;

View File

@@ -30,10 +30,12 @@ exports[`BigValue Render with basic options should render 1`] = `
<FormattedDisplayValue
style={
Object {
"color": "white",
"color": "#f7f8fa",
"fontSize": 230,
"fontWeight": 500,
"lineHeight": 1.2,
"position": "relative",
"zIndex": 1,
}
}
value={

View File

@@ -13,7 +13,7 @@ It is used in a `ConfigEditor` for data source plugins. You can find more exampl
### Example usage
```jsx
export const ConfigEditor = (props: Props) => {
const { options, onOptionsChange, config } = props;
const { options, onOptionsChange } = props;
return (
<>
<DataSourceHttpSettings

View File

@@ -100,7 +100,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
authProvider => authProvider.value === dataSourceConfig.jsonData.sigV4AuthType
)}
options={authProviderOptions}
defaultValue={dataSourceConfig.jsonData.sigV4AuthType || 'keys'}
defaultValue={dataSourceConfig.jsonData.sigV4AuthType || authProviderOptions[0]}
onChange={option => {
onJsonDataChange('sigV4AuthType', option.value);
}}

View File

@@ -7,13 +7,22 @@ import { IconButton } from '../IconButton/IconButton';
import { HorizontalGroup } from '../Layout/Layout';
import panelArtDark from './panelArt_dark.svg';
import panelArtLight from './panelArt_light.svg';
import { AlertVariant } from '../Alert/Alert';
import { getColorsFromSeverity } from '../../utils/colors';
export interface InfoBoxProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
children: React.ReactNode;
/** Title of the box */
title?: string | JSX.Element;
/** Url of the read more link */
url?: string;
/** Text of the read more link */
urlTitle?: string;
/** Indicates whether or not box should be rendered with Grafana branding background */
branded?: boolean;
/** Color variant of the box */
severity?: AlertVariant;
/** Call back to be performed when box is dismissed */
onDismiss?: () => void;
}
@@ -24,9 +33,9 @@ export interface InfoBoxProps extends Omit<React.HTMLAttributes<HTMLDivElement>,
*/
export const InfoBox = React.memo(
React.forwardRef<HTMLDivElement, InfoBoxProps>(
({ title, className, children, branded, url, urlTitle, onDismiss, ...otherProps }, ref) => {
({ title, className, children, branded, url, urlTitle, onDismiss, severity = 'info', ...otherProps }, ref) => {
const theme = useTheme();
const styles = getInfoBoxStyles(theme);
const styles = getInfoBoxStyles(theme, severity);
const wrapperClassName = branded ? cx(styles.wrapperBranded, className) : cx(styles.wrapper, className);
return (
@@ -49,18 +58,15 @@ export const InfoBox = React.memo(
)
);
const getInfoBoxStyles = stylesFactory((theme: GrafanaTheme) => ({
const getInfoBoxStyles = stylesFactory((theme: GrafanaTheme, severity: AlertVariant) => ({
wrapper: css`
position: relative;
padding: ${theme.spacing.md};
background-color: ${theme.colors.bg2};
border-top: 3px solid ${theme.palette.blue80};
border-top: 3px solid ${getColorsFromSeverity(severity, theme)[0]};
margin-bottom: ${theme.spacing.md};
flex-grow: 1;
ul {
padding-left: ${theme.spacing.lg};
}
color: ${theme.colors.textSemiWeak};
code {
@include font-family-monospace();
@@ -109,5 +115,6 @@ const getInfoBoxStyles = stylesFactory((theme: GrafanaTheme) => ({
display: inline-block;
margin-top: ${theme.spacing.md};
font-size: ${theme.typography.size.sm};
color: ${theme.colors.textSemiWeak};
`,
}));

View File

@@ -45,6 +45,12 @@ describe('LogDetails', () => {
expect(wrapper.text().includes('key1label1key2label2')).toBe(true);
});
});
describe('when log row has error', () => {
it('should not render log level border', () => {
const wrapper = setup({ hasError: true }, undefined);
expect(wrapper.find({ 'aria-label': 'Log level' }).html()).not.toContain('logs-row__level');
});
});
describe('when row entry has parsable fields', () => {
it('should render heading ', () => {
const wrapper = setup(undefined, { entry: 'test=successful' });

View File

@@ -28,6 +28,7 @@ export interface Props extends Themeable {
showDuplicates: boolean;
getRows: () => LogRowModel[];
className?: string;
hasError?: boolean;
onMouseEnter?: () => void;
onMouseLeave?: () => void;
onClickFilterLabel?: (key: string, value: string) => void;
@@ -70,6 +71,7 @@ class UnThemedLogDetails extends PureComponent<Props> {
const {
row,
theme,
hasError,
onClickFilterOutLabel,
onClickFilterLabel,
getRows,
@@ -88,6 +90,8 @@ class UnThemedLogDetails extends PureComponent<Props> {
const labelsAvailable = Object.keys(labels).length > 0;
const fields = getAllFields(row, getFieldLinks);
const parsedFieldsAvailable = fields && fields.length > 0;
// If logs with error, we are not showing the level color
const levelClassName = cx(!hasError && [style.logsRowLevel, styles.logsRowLevelDetails]);
return (
<tr
@@ -96,7 +100,7 @@ class UnThemedLogDetails extends PureComponent<Props> {
onMouseLeave={onMouseLeave}
>
{showDuplicates && <td />}
<td className={cx(style.logsRowLevel, styles.logsRowLevelDetails)} />
<td className={levelClassName} aria-label="Log level" />
<td colSpan={4}>
<div className={style.logDetailsContainer}>
<table className={style.logDetailsTable}>

View File

@@ -8,8 +8,10 @@ import {
DataQueryResponse,
GrafanaTheme,
dateTimeFormat,
checkLogsError,
} from '@grafana/data';
import { Icon } from '../Icon/Icon';
import { Tooltip } from '../Tooltip/Tooltip';
import { cx, css } from 'emotion';
import {
@@ -72,6 +74,10 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
label: hoverBackground;
background-color: ${bgColor};
`,
errorLogRow: css`
label: erroredLogRow;
color: ${theme.colors.textWeak};
`,
};
});
/**
@@ -160,22 +166,27 @@ class UnThemedLogRow extends PureComponent<Props, State> {
const { showDetails, showContext, hasHoverBackground } = this.state;
const style = getLogRowStyles(theme, row.logLevel);
const styles = getStyles(theme);
const hoverBackground = cx(style.logsRow, { [styles.hoverBackground]: hasHoverBackground });
const { errorMessage, hasError } = checkLogsError(row);
const logRowBackground = cx(style.logsRow, {
[styles.hoverBackground]: hasHoverBackground,
[styles.errorLogRow]: hasError,
});
return (
<>
<tr
className={hoverBackground}
onMouseEnter={this.addHoverBackground}
onMouseLeave={this.clearHoverBackground}
onClick={this.toggleDetails}
>
<tr className={logRowBackground} onClick={this.toggleDetails}>
{showDuplicates && (
<td className={style.logsRowDuplicates}>
{row.duplicates && row.duplicates > 0 ? `${row.duplicates + 1}x` : null}
</td>
)}
<td className={style.logsRowLevel} />
<td className={cx({ [style.logsRowLevel]: !hasError })}>
{hasError && (
<Tooltip content={`Error: ${errorMessage}`} placement="right" theme="error">
<Icon className={style.logIconError} name="exclamation-triangle" size="sm" />
</Tooltip>
)}
</td>
{!allowDetails && (
<td title={showDetails ? 'Hide log details' : 'See log details'} className={style.logsRowToggleDetails}>
<Icon className={styles.topVerticalAlign} name={showDetails ? 'angle-down' : 'angle-right'} />
@@ -207,7 +218,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
</tr>
{this.state.showDetails && (
<LogDetails
className={hoverBackground}
className={logRowBackground}
onMouseEnter={this.addHoverBackground}
onMouseLeave={this.clearHoverBackground}
showDuplicates={showDuplicates}
@@ -218,6 +229,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
onClickHideParsedField={onClickHideParsedField}
getRows={getRows}
row={row}
hasError={hasError}
showParsedFields={showParsedFields}
/>
)}

View File

@@ -9,6 +9,7 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
let logColor = selectThemeVariant({ light: theme.palette.gray5, dark: theme.palette.gray2 }, theme.type);
const borderColor = selectThemeVariant({ light: theme.palette.gray5, dark: theme.palette.gray2 }, theme.type);
const bgColor = selectThemeVariant({ light: theme.palette.gray5, dark: theme.palette.dark4 }, theme.type);
const hoverBgColor = selectThemeVariant({ light: theme.palette.gray7, dark: theme.palette.dark2 }, theme.type);
const context = css`
label: context;
visibility: hidden;
@@ -92,7 +93,7 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
}
&:hover {
background: ${theme.colors.bodyBg};
background: ${hoverBgColor};
}
`,
logsRowDuplicates: css`
@@ -116,6 +117,10 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
background-color: ${logColor};
}
`,
logIconError: css`
color: ${theme.palette.red};
margin-left: -5px;
`,
logsRowToggleDetails: css`
label: logs-row-toggle-details__level;
position: relative;

View File

@@ -23,9 +23,11 @@ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | unde
const styles = useStyles(getStyles);
const options = fieldColorModeRegistry.list().map(mode => {
let suffix = mode.isByValue ? ' (by value)' : '';
return {
value: mode.id,
label: mode.name,
label: `${mode.name}${suffix}`,
description: mode.description,
isContinuous: mode.isContinuous,
isByValue: mode.isByValue,

View File

@@ -9,6 +9,7 @@ import memoizeOne from 'memoize-one';
import { GrafanaTheme } from '@grafana/data';
import { withTheme } from '../../themes';
// Default intervals used in the refresh picker component
export const defaultIntervals = ['5s', '10s', '30s', '1m', '5m', '15m', '30m', '1h', '2h', '1d'];
const getStyles = memoizeOne((theme: GrafanaTheme) => {

View File

@@ -131,3 +131,21 @@ export const CustomLabelField = () => {
</SegmentFrame>
);
};
export const HtmlAttributes = () => {
const [value, setValue] = useState<any>(options[0]);
return (
<SegmentFrame options={options}>
<Segment
data-testid="segment-test"
id="segment-id"
value={value}
options={groupedOptions}
onChange={({ value }) => {
setValue(value);
action('Segment value changed')(value);
}}
/>
</SegmentFrame>
);
};

View File

@@ -1,10 +1,10 @@
import React from 'react';
import React, { HTMLProps } from 'react';
import { cx } from 'emotion';
import _ from 'lodash';
import { SelectableValue } from '@grafana/data';
import { SegmentSelect, useExpandableLabel, SegmentProps } from './';
export interface SegmentSyncProps<T> extends SegmentProps<T> {
export interface SegmentSyncProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLDivElement>, 'value' | 'onChange'> {
value?: T | SelectableValue<T>;
onChange: (item: SelectableValue<T>) => void;
options: Array<SelectableValue<T>>;
@@ -18,6 +18,7 @@ export function Segment<T>({
className,
allowCustomValue,
placeholder,
...rest
}: React.PropsWithChildren<SegmentSyncProps<T>>) {
const [Label, width, expanded, setExpanded] = useExpandableLabel(false);
@@ -38,6 +39,7 @@ export function Segment<T>({
return (
<SegmentSelect
{...rest}
value={value && !_.isObject(value) ? { value } : value}
options={options}
width={width}

View File

@@ -123,3 +123,21 @@ export const CustomLabel = () => {
</SegmentFrame>
);
};
export const HtmlAttributes = () => {
const [value, setValue] = useState<any>(options[0]);
return (
<SegmentFrame loadOptions={() => loadOptions(options)}>
<SegmentAsync
data-testid="segment-async-test"
id="segment-async"
value={value}
loadOptions={() => loadOptions(options)}
onChange={item => {
setValue(item);
action('Segment value changed')(item.value);
}}
/>
</SegmentFrame>
);
};

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { HTMLProps } from 'react';
import { cx } from 'emotion';
import _ from 'lodash';
import { SegmentSelect } from './SegmentSelect';
@@ -7,7 +7,7 @@ import { useExpandableLabel, SegmentProps } from '.';
import { useAsyncFn } from 'react-use';
import { AsyncState } from 'react-use/lib/useAsync';
export interface SegmentAsyncProps<T> extends SegmentProps<T> {
export interface SegmentAsyncProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLDivElement>, 'value' | 'onChange'> {
value?: T | SelectableValue<T>;
loadOptions: (query?: string) => Promise<Array<SelectableValue<T>>>;
onChange: (item: SelectableValue<T>) => void;
@@ -21,6 +21,7 @@ export function SegmentAsync<T>({
className,
allowCustomValue,
placeholder,
...rest
}: React.PropsWithChildren<SegmentAsyncProps<T>>) {
const [state, fetchOptions] = useAsyncFn(loadOptions, [loadOptions]);
const [Label, width, expanded, setExpanded] = useExpandableLabel(false);
@@ -43,6 +44,7 @@ export function SegmentAsync<T>({
return (
<SegmentSelect
{...rest}
value={value && !_.isObject(value) ? { value } : value}
options={state.value ?? []}
width={width}

View File

@@ -49,6 +49,23 @@ export const BasicInputWithPlaceholder = () => {
);
};
export const BasicInputWithHtmlAttributes = () => {
const [value, setValue] = useState('some text');
return (
<SegmentFrame>
<SegmentInput
data-testid="segment-input-test"
id="segment-input"
value={value}
onChange={text => {
setValue(text as string);
action('Segment value changed')(text);
}}
/>
</SegmentFrame>
);
};
const InputComponent = ({ initialValue }: any) => {
const [value, setValue] = useState(initialValue);
return (

View File

@@ -1,10 +1,10 @@
import React, { useRef, useState } from 'react';
import React, { HTMLProps, useRef, useState } from 'react';
import { cx, css } from 'emotion';
import useClickAway from 'react-use/lib/useClickAway';
import { measureText } from '../../utils/measureText';
import { useExpandableLabel, SegmentProps } from '.';
export interface SegmentInputProps<T> extends SegmentProps<T> {
export interface SegmentInputProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLInputElement>, 'value' | 'onChange'> {
value: string | number;
onChange: (text: string | number) => void;
autofocus?: boolean;
@@ -19,6 +19,7 @@ export function SegmentInput<T>({
className,
placeholder,
autofocus = false,
...rest
}: React.PropsWithChildren<SegmentInputProps<T>>) {
const ref = useRef<HTMLInputElement>(null);
const [value, setValue] = useState<number | string>(initialValue);
@@ -50,6 +51,7 @@ export function SegmentInput<T>({
return (
<input
{...rest}
ref={ref}
autoFocus
className={cx(`gf-form gf-form-input`, inputWidthStyle)}

View File

@@ -1,10 +1,10 @@
import React, { useRef } from 'react';
import React, { HTMLProps, useRef } from 'react';
import { css, cx } from 'emotion';
import useClickAway from 'react-use/lib/useClickAway';
import { SelectableValue } from '@grafana/data';
import { Select } from '../Forms/Legacy/Select/Select';
export interface Props<T> {
export interface Props<T> extends Omit<HTMLProps<HTMLDivElement>, 'value' | 'onChange'> {
value?: SelectableValue<T>;
options: Array<SelectableValue<T>>;
onChange: (item: SelectableValue<T>) => void;
@@ -22,6 +22,7 @@ export function SegmentSelect<T>({
width,
noOptionsMessage = '',
allowCustomValue = false,
...rest
}: React.PropsWithChildren<Props<T>>) {
const ref = useRef<HTMLDivElement>(null);
@@ -39,7 +40,7 @@ export function SegmentSelect<T>({
});
return (
<div ref={ref}>
<div {...rest} ref={ref}>
<Select
className={cx(
css`

View File

@@ -20,7 +20,7 @@ export interface SelectCommonProps<T> {
filterOption?: (option: SelectableValue, searchQuery: string) => boolean;
/** Function for formatting the text that is displayed when creating a new value*/
formatCreateLabel?: (input: string) => string;
getOptionLabel?: (item: SelectableValue<T>) => string;
getOptionLabel?: (item: SelectableValue<T>) => React.ReactNode;
getOptionValue?: (item: SelectableValue<T>) => string;
inputValue?: string;
invalid?: boolean;

View File

@@ -5,6 +5,7 @@ import { TableCellDisplayMode, TableCellProps } from './types';
import tinycolor from 'tinycolor2';
import { TableStyles } from './styles';
import { FilterActions } from './FilterActions';
import { getTextColorForBackground } from '../../utils';
export const DefaultCell: FC<TableCellProps> = props => {
const { field, cell, tableStyles, row, cellProps } = props;
@@ -65,7 +66,12 @@ function getCellStyle(tableStyles: TableStyles, field: Field, displayValue: Disp
.spin(5)
.toRgbString();
return tableStyles.buildCellContainerStyle('white', `linear-gradient(120deg, ${bgColor2}, ${displayValue.color})`);
const textColor = getTextColorForBackground(displayValue.color!);
return tableStyles.buildCellContainerStyle(
textColor,
`linear-gradient(120deg, ${bgColor2}, ${displayValue.color})`
);
}
return tableStyles.cellContainer;

View File

@@ -2,11 +2,13 @@ import React, { FC } from 'react';
import { TableCellProps } from './types';
export const ImageCell: FC<TableCellProps> = props => {
const { cell, tableStyles, cellProps } = props;
const { field, cell, tableStyles, cellProps } = props;
const displayValue = field.display!(cell.value);
return (
<div {...cellProps} className={tableStyles.cellContainer}>
<img src={cell.value} className={tableStyles.imageCell} />
<img src={displayValue.text} className={tableStyles.imageCell} />
</div>
);
};

View File

@@ -38,6 +38,8 @@ interface State {
}
export class ThresholdsEditor extends PureComponent<Props, State> {
private latestThresholdInputRef: React.RefObject<HTMLInputElement>;
constructor(props: Props) {
super(props);
@@ -45,6 +47,7 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
steps[0].value = -Infinity;
this.state = { steps };
this.latestThresholdInputRef = React.createRef();
}
onAddThreshold = () => {
@@ -67,7 +70,12 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
const newThresholds = [...steps, add];
sortThresholds(newThresholds);
this.setState({ steps: newThresholds }, this.onChange);
this.setState({ steps: newThresholds }, () => {
if (this.latestThresholdInputRef.current) {
this.latestThresholdInputRef.current.focus();
}
this.onChange();
});
};
onRemoveThreshold = (threshold: ThresholdWithKey) => {
@@ -136,7 +144,7 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
});
};
renderInput(threshold: ThresholdWithKey, styles: ThresholdStyles) {
renderInput(threshold: ThresholdWithKey, styles: ThresholdStyles, idx: number) {
const isPercent = this.props.thresholds.mode === ThresholdsMode.Percentage;
if (!isFinite(threshold.value)) {
@@ -167,6 +175,7 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
key={isPercent.toString()}
onChange={(event: ChangeEvent<HTMLInputElement>) => this.onChangeThresholdValue(event, threshold)}
value={threshold.value}
ref={idx === 0 ? this.latestThresholdInputRef : null}
onBlur={this.onBlur}
prefix={
<div className={styles.inputPrefix}>
@@ -208,13 +217,11 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
{steps
.slice(0)
.reverse()
.map(threshold => {
return (
<div className={styles.item} key={`${threshold.key}`}>
{this.renderInput(threshold, styles)}
</div>
);
})}
.map((threshold, idx) => (
<div className={styles.item} key={`${threshold.key}`}>
{this.renderInput(threshold, styles, idx)}
</div>
))}
</div>
<div>
@@ -279,7 +286,6 @@ const getStyles = stylesFactory(
wrapper: css`
display: flex;
flex-direction: column;
// margin-bottom: -${theme.spacing.formSpacingBase * 2}px;
`,
thresholds: css`
display: flex;

View File

@@ -22,7 +22,7 @@ export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult';
export { PieChart, PieChartType } from './PieChart/PieChart';
export { UnitPicker } from './UnitPicker/UnitPicker';
export { StatsPicker } from './StatsPicker/StatsPicker';
export { RefreshPicker } from './RefreshPicker/RefreshPicker';
export { RefreshPicker, defaultIntervals } from './RefreshPicker/RefreshPicker';
export { TimeRangePicker } from './TimePicker/TimeRangePicker';
export { TimeOfDayPicker } from './TimePicker/TimeOfDayPicker';
export { TimeZonePicker } from './TimePicker/TimeZonePicker';

View File

@@ -12,6 +12,8 @@ export type IconName =
| 'filter'
| 'angle-left'
| 'angle-right'
| 'angle-double-right'
| 'angle-double-down'
| 'pen'
| 'envelope'
| 'percentage'
@@ -128,6 +130,8 @@ export const getAvailableIcons = (): IconName[] => [
'filter',
'angle-left',
'angle-right',
'angle-double-right',
'angle-double-down',
'pen',
'envelope',
'percentage',

View File

@@ -4,6 +4,10 @@ import flattenDeep from 'lodash/flattenDeep';
import chunk from 'lodash/chunk';
import zip from 'lodash/zip';
import tinycolor from 'tinycolor2';
import lightTheme from '../themes/light';
import darkTheme from '../themes/dark';
import { GrafanaTheme } from '@grafana/data';
import { AlertVariant } from '../components/Alert/Alert';
export const PALETTE_ROWS = 4;
export const PALETTE_COLUMNS = 14;
@@ -93,4 +97,27 @@ function hslToHex(color: any) {
return tinycolor(color).toHexString();
}
export function getTextColorForBackground(color: string) {
const b = tinycolor(color).getBrightness();
return b > 180 ? lightTheme.colors.textStrong : darkTheme.colors.textStrong;
}
export let sortedColors = sortColorsByHue(colors);
/**
* Returns colors used for severity color coding. Use for single color retrievel(0 index) or gradient definition
* @internal
**/
export function getColorsFromSeverity(severity: AlertVariant, theme: GrafanaTheme): [string, string] {
switch (severity) {
case 'error':
case 'warning':
return [theme.palette.redBase, theme.palette.redShade];
case 'info':
return [theme.palette.blue80, theme.palette.blue77];
case 'success':
return [theme.palette.greenBase, theme.palette.greenShade];
default:
return [theme.palette.blue80, theme.palette.blue77];
}
}

View File

@@ -1,6 +1,9 @@
let canvas: HTMLCanvasElement | null = null;
const cache: Record<string, TextMetrics> = {};
/**
* @beta
*/
export function measureText(text: string, fontSize: number): TextMetrics {
const fontStyle = `${fontSize}px 'Roboto'`;
const cacheKey = text + fontStyle;
@@ -26,6 +29,9 @@ export function measureText(text: string, fontSize: number): TextMetrics {
return metrics;
}
/**
* @beta
*/
export function calculateFontSize(text: string, width: number, height: number, lineHeight: number, maxSize?: number) {
// calculate width in 14px
const textSize = measureText(text, 14);

View File

@@ -1,6 +1,6 @@
{
"name": "@jaegertracing/jaeger-ui-components",
"version": "7.3.0-pre.0",
"version": "7.3.1",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -14,8 +14,8 @@
"typescript": "4.0.2"
},
"dependencies": {
"@grafana/data": "7.3.0-pre.0",
"@grafana/ui": "7.3.0-pre.0",
"@grafana/data": "7.3.1",
"@grafana/ui": "7.3.1",
"@types/classnames": "^2.2.7",
"@types/deep-freeze": "^0.1.1",
"@types/hoist-non-react-statics": "^3.3.1",

View File

@@ -15,7 +15,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import TimelineCollapser from './TimelineCollapser';
import { TimelineCollapser } from './TimelineCollapser';
describe('<TimelineCollapser>', () => {
it('renders without exploding', () => {

View File

@@ -12,18 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import React from 'react';
import { IconButton } from '@grafana/ui';
import { css } from 'emotion';
import cx from 'classnames';
import { UITooltip, UIIcon } from '../../uiElementsContext';
import React from 'react';
import { createStyle } from '../../Theme';
const getStyles = createStyle(() => {
return {
TraceTimelineViewer: css`
border-bottom: 1px solid #bbb;
`,
TimelineCollapser: css`
align-items: center;
display: flex;
@@ -31,21 +26,6 @@ const getStyles = createStyle(() => {
justify-content: center;
margin-right: 0.5rem;
`,
tooltipTitle: css`
white-space: pre;
`,
btn: css`
color: rgba(0, 0, 0, 0.5);
cursor: pointer;
margin-right: 0.3rem;
padding: 0.1rem;
&:hover {
color: rgba(0, 0, 0, 0.85);
}
`,
btnExpanded: css`
transform: rotate(90deg);
`,
};
});
@@ -56,40 +36,27 @@ type CollapserProps = {
onExpandAll: () => void;
};
function getTitle(value: string) {
export function TimelineCollapser(props: CollapserProps) {
const { onExpandAll, onExpandOne, onCollapseAll, onCollapseOne } = props;
const styles = getStyles();
return <span className={styles.tooltipTitle}>{value}</span>;
}
export default class TimelineCollapser extends React.PureComponent<CollapserProps> {
containerRef: React.RefObject<HTMLDivElement>;
constructor(props: CollapserProps) {
super(props);
this.containerRef = React.createRef();
}
// TODO: Something less hacky than createElement to help TypeScript / AntD
getContainer = () => this.containerRef.current || document.createElement('div');
render() {
const { onExpandAll, onExpandOne, onCollapseAll, onCollapseOne } = this.props;
const styles = getStyles();
return (
<div className={styles.TimelineCollapser} ref={this.containerRef} data-test-id="TimelineCollapser">
<UITooltip title={getTitle('Expand +1')} getPopupContainer={this.getContainer}>
<UIIcon type="right" onClick={onExpandOne} className={cx(styles.btn, styles.btnExpanded)} />
</UITooltip>
<UITooltip title={getTitle('Collapse +1')} getPopupContainer={this.getContainer}>
<UIIcon type="right" onClick={onCollapseOne} className={styles.btn} />
</UITooltip>
<UITooltip title={getTitle('Expand All')} getPopupContainer={this.getContainer}>
<UIIcon type="double-right" onClick={onExpandAll} className={cx(styles.btn, styles.btnExpanded)} />
</UITooltip>
<UITooltip title={getTitle('Collapse All')} getPopupContainer={this.getContainer}>
<UIIcon type="double-right" onClick={onCollapseAll} className={styles.btn} />
</UITooltip>
</div>
);
}
return (
<div className={styles.TimelineCollapser} data-test-id="TimelineCollapser">
<IconButton tooltip="Expand +1" size="xl" tooltipPlacement="top" name="angle-down" onClick={onExpandOne} />
<IconButton tooltip="Collapse +1" size="xl" tooltipPlacement="top" name="angle-right" onClick={onCollapseOne} />
<IconButton
tooltip="Expand All"
size="xl"
tooltipPlacement="top"
name="angle-double-down"
onClick={onExpandAll}
/>
<IconButton
tooltip="Collapse All"
size="xl"
tooltipPlacement="top"
name="angle-double-right"
onClick={onCollapseAll}
/>
</div>
);
}

View File

@@ -19,7 +19,7 @@ import TimelineHeaderRow from './TimelineHeaderRow';
import TimelineColumnResizer from './TimelineColumnResizer';
import TimelineViewingLayer from './TimelineViewingLayer';
import Ticks from '../Ticks';
import TimelineCollapser from './TimelineCollapser';
import { TimelineCollapser } from './TimelineCollapser';
describe('<TimelineHeaderRow>', () => {
let wrapper;

View File

@@ -16,7 +16,7 @@ import * as React from 'react';
import { css } from 'emotion';
import cx from 'classnames';
import TimelineCollapser from './TimelineCollapser';
import { TimelineCollapser } from './TimelineCollapser';
import TimelineColumnResizer from './TimelineColumnResizer';
import TimelineViewingLayer from './TimelineViewingLayer';
import Ticks from '../Ticks';

View File

@@ -256,6 +256,7 @@ func (hs *HTTPServer) registerRoutes() {
apiRoute.Get("/plugins/:pluginId/health", Wrap(hs.CheckHealth))
apiRoute.Any("/plugins/:pluginId/resources", hs.CallResource)
apiRoute.Any("/plugins/:pluginId/resources/*", hs.CallResource)
apiRoute.Any("/plugins/errors", Wrap(hs.GetPluginErrorsList))
apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) {
pluginRoute.Get("/:pluginId/dashboards/", Wrap(GetPluginDashboards))

View File

@@ -21,7 +21,7 @@ type URLValidationError struct {
// Error returns the error message.
func (e URLValidationError) Error() string {
return fmt.Sprintf("Validation of data source URL %q failed: %s", e.URL, e.Err.Error())
return fmt.Sprintf("validation of data source URL %q failed: %s", e.URL, e.Err.Error())
}
// Unwrap returns the wrapped error.

View File

@@ -173,6 +173,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
"sort": getPanelSort(panel.Id),
"skipDataQuery": panel.SkipDataQuery,
"state": panel.State,
"signature": panel.Signature,
}
}

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