Merge branch 'main' into matyax/monaco-loki-from-hackathon

This commit is contained in:
Matias Chomicki 2022-09-22 14:46:28 +02:00
commit 458bda6eb1
709 changed files with 20467 additions and 10873 deletions

View File

@ -1,5 +1,5 @@
// BETTERER RESULTS V2.
//
//
// If this file contains merge conflicts, use `betterer merge` to automatically resolve them:
// https://phenomnomnominal.github.io/betterer/docs/results-file/#merge
//
@ -23,9 +23,6 @@ exports[`no enzyme tests`] = {
"packages/grafana-ui/src/slate-plugins/suggestions.test.tsx:2682912140": [
[0, 18, 13, "RegExp match", "2409514259"]
],
"packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/TickLabels.test.js:2931161174": [
[14, 19, 13, "RegExp match", "2409514259"]
],
"packages/jaeger-ui-components/src/TracePageHeader/SpanGraph/ViewingLayer.test.js:1676554632": [
[14, 19, 13, "RegExp match", "2409514259"]
],
@ -59,7 +56,7 @@ exports[`no enzyme tests`] = {
"public/app/features/dimensions/editors/ThresholdsEditor/ThresholdsEditor.test.tsx:145048794": [
[0, 17, 13, "RegExp match", "2409514259"]
],
"public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx:4057721851": [
"public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx:2983010995": [
[1, 19, 13, "RegExp match", "2409514259"]
]
}`
@ -860,13 +857,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
],
"packages/grafana-data/src/utils/logs.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
],
"packages/grafana-data/src/utils/logs.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
@ -2778,17 +2768,6 @@ exports[`better eslint`] = {
"public/app/core/history/richHistoryLocalStorageUtils.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/core/logsModel.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"]
],
"public/app/core/logsModel.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
@ -2803,8 +2782,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
],
"public/app/core/navigation/RouterDebugger.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
@ -2921,34 +2899,17 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
],
"public/app/core/services/echo/backends/analytics/ApplicationInsightsBackend.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Do not use any type assertions.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Do not use any type assertions.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"]
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
],
"public/app/core/services/echo/backends/analytics/RudderstackBackend.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Do not use any type assertions.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Do not use any type assertions.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Do not use any type assertions.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Do not use any type assertions.", "12"],
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
[0, 0, 0, "Do not use any type assertions.", "14"],
[0, 0, 0, "Unexpected any. Specify a different type.", "15"]
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
],
"public/app/core/services/echo/backends/sentry/transports/CustomEndpointTransport.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@ -3690,6 +3651,9 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/features/dashboard/components/HelpWizard/randomizer.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/dashboard/components/Inspector/PanelInspectActions.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
@ -3818,7 +3782,7 @@ exports[`better eslint`] = {
"public/app/features/dashboard/components/ShareModal/ShareModal.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/features/dashboard/components/ShareModal/SharePublicDashboard.test.tsx:5381": [
"public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.test.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/features/dashboard/components/ShareModal/ShareSnapshot.tsx:5381": [
@ -3834,9 +3798,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/features/dashboard/components/SupportSnapshot/randomizer.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/dashboard/components/TransformationsEditor/TransformationEditor.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
@ -3941,11 +3902,9 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/features/dashboard/services/PublicDashboardDataSource.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
],
"public/app/features/dashboard/services/TimeSrv.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@ -4605,10 +4564,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/features/library-panels/components/LibraryPanelsSearch/reducer.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/features/library-panels/components/LibraryPanelsView/actions.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
@ -4691,6 +4646,19 @@ exports[`better eslint`] = {
"public/app/features/live/pages/utils.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/features/logs/components/LogRowContextProvider.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"]
],
"public/app/features/logs/components/LogRows.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/features/logs/components/logParser.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/manage-dashboards/DashboardImportPage.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
@ -5072,11 +5040,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"],
[0, 0, 0, "Do not use any type assertions.", "5"],
[0, 0, 0, "Do not use any type assertions.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Do not use any type assertions.", "8"],
[0, 0, 0, "Do not use any type assertions.", "9"]
[0, 0, 0, "Do not use any type assertions.", "5"]
],
"public/app/features/query/state/runRequest.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
@ -5913,14 +5877,6 @@ exports[`better eslint`] = {
"public/app/plugins/datasource/cloud-monitoring/types.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/datasource/cloudwatch/__mocks__/CloudWatchDataSource.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
],
"public/app/plugins/datasource/cloudwatch/__mocks__/monarch/Monaco.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
@ -5965,54 +5921,8 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
],
"public/app/plugins/datasource/cloudwatch/datasource.d.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/datasource/cloudwatch/datasource.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Unexpected any. Specify a different type.", "12"]
],
"public/app/plugins/datasource/cloudwatch/datasource.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Do not use any type assertions.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Do not use any type assertions.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Unexpected any. Specify a different type.", "12"],
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
[0, 0, 0, "Do not use any type assertions.", "14"],
[0, 0, 0, "Unexpected any. Specify a different type.", "15"],
[0, 0, 0, "Unexpected any. Specify a different type.", "16"],
[0, 0, 0, "Unexpected any. Specify a different type.", "17"],
[0, 0, 0, "Unexpected any. Specify a different type.", "18"],
[0, 0, 0, "Unexpected any. Specify a different type.", "19"],
[0, 0, 0, "Unexpected any. Specify a different type.", "20"],
[0, 0, 0, "Unexpected any. Specify a different type.", "21"],
[0, 0, 0, "Unexpected any. Specify a different type.", "22"],
[0, 0, 0, "Unexpected any. Specify a different type.", "23"],
[0, 0, 0, "Do not use any type assertions.", "24"],
[0, 0, 0, "Do not use any type assertions.", "25"],
[0, 0, 0, "Do not use any type assertions.", "26"],
[0, 0, 0, "Do not use any type assertions.", "27"],
[0, 0, 0, "Unexpected any. Specify a different type.", "28"]
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/datasource/cloudwatch/guards.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
@ -6036,26 +5946,9 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/datasource/cloudwatch/metric-math/completion/CompletionItemProvider.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/datasource/cloudwatch/specs/datasource.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Unexpected any. Specify a different type.", "12"],
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
[0, 0, 0, "Unexpected any. Specify a different type.", "14"],
[0, 0, 0, "Unexpected any. Specify a different type.", "15"]
"public/app/plugins/datasource/cloudwatch/query-runner/CloudWatchLogsQueryRunner.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/datasource/cloudwatch/types.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@ -6080,14 +5973,49 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/datasource/dashboard/DashboardQueryEditor.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"]
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/plugins/datasource/dashboard/runSharedRequest.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
],
"public/app/plugins/datasource/elasticsearch/LanguageProvider.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
],
"public/app/plugins/datasource/elasticsearch/QueryBuilder.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"]
],
"public/app/plugins/datasource/elasticsearch/QueryBuilder.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Unexpected any. Specify a different type.", "12"],
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
[0, 0, 0, "Unexpected any. Specify a different type.", "14"],
[0, 0, 0, "Do not use any type assertions.", "15"],
[0, 0, 0, "Unexpected any. Specify a different type.", "16"],
[0, 0, 0, "Unexpected any. Specify a different type.", "17"],
[0, 0, 0, "Unexpected any. Specify a different type.", "18"]
],
"public/app/plugins/datasource/elasticsearch/components/AddRemove.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
@ -6217,33 +6145,6 @@ exports[`better eslint`] = {
"public/app/plugins/datasource/elasticsearch/hooks/useStatelessReducer.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/plugins/datasource/elasticsearch/language_provider.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
],
"public/app/plugins/datasource/elasticsearch/query_builder.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
[0, 0, 0, "Unexpected any. Specify a different type.", "8"],
[0, 0, 0, "Unexpected any. Specify a different type.", "9"],
[0, 0, 0, "Unexpected any. Specify a different type.", "10"],
[0, 0, 0, "Unexpected any. Specify a different type.", "11"],
[0, 0, 0, "Unexpected any. Specify a different type.", "12"],
[0, 0, 0, "Unexpected any. Specify a different type.", "13"],
[0, 0, 0, "Unexpected any. Specify a different type.", "14"],
[0, 0, 0, "Do not use any type assertions.", "15"],
[0, 0, 0, "Unexpected any. Specify a different type.", "16"],
[0, 0, 0, "Unexpected any. Specify a different type.", "17"],
[0, 0, 0, "Unexpected any. Specify a different type.", "18"]
],
"public/app/plugins/datasource/elasticsearch/specs/elastic_response.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
@ -6264,16 +6165,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "16"],
[0, 0, 0, "Unexpected any. Specify a different type.", "17"]
],
"public/app/plugins/datasource/elasticsearch/specs/query_builder.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"]
],
"public/app/plugins/datasource/elasticsearch/test-helpers/render.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],

View File

@ -2,6 +2,8 @@
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: pr-verify-drone
node:
@ -54,6 +56,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: pr-test-frontend
node:
@ -86,17 +90,6 @@ steps:
CGO_ENABLED: 0
image: golang:1.19.1
name: compile-build-cmd
- commands:
- yarn run prettier:check
- yarn run lint
- yarn run i18n:compile
- yarn run typecheck
depends_on:
- yarn-install
environment:
TEST_MAX_WORKERS: 50%
image: grafana/build-container:1.6.2
name: lint-frontend
- commands:
- yarn betterer ci
depends_on:
@ -133,6 +126,60 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: pr-lint-frontend
node:
type: no-parallel
platform:
arch: amd64
os: linux
services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
image: alpine:3.15.6
name: identify-runner
- commands:
- yarn install --immutable
depends_on: []
image: grafana/build-container:1.6.2
name: yarn-install
- commands:
- yarn run prettier:check
- yarn run lint
- yarn run i18n:compile
- yarn run typecheck
depends_on:
- yarn-install
environment:
TEST_MAX_WORKERS: 50%
image: grafana/build-container:1.6.2
name: lint-frontend
trigger:
event:
- pull_request
paths:
exclude:
- docs/**
- '*.md'
- pkg/**
- packaging/**
- go.sum
- go.mod
include: []
type: docker
volumes:
- host:
path: /var/run/docker.sock
name: docker
---
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: pr-test-backend
node:
@ -146,12 +193,6 @@ steps:
- echo $DRONE_RUNNER_NAME
image: alpine:3.15.6
name: identify-runner
- commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.7/grabpl
- chmod +x bin/grabpl
image: byrnedo/alpine-curl:0.1.8
name: grabpl
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
depends_on: []
@ -174,14 +215,6 @@ steps:
- verify-gen-cue
image: grafana/build-container:1.6.2
name: wire-install
- commands:
- make lint-go
depends_on:
- wire-install
environment:
CGO_ENABLED: "1"
image: grafana/build-container:1.6.2
name: lint-backend
- commands:
- go test -short -covermode=atomic -timeout=30m ./pkg/...
depends_on:
@ -219,6 +252,68 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: pr-lint-backend
node:
type: no-parallel
platform:
arch: amd64
os: linux
services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
image: alpine:3.15.6
name: identify-runner
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.19.1
name: compile-build-cmd
- commands:
- make gen-go
depends_on: []
image: grafana/build-container:1.6.2
name: wire-install
- commands:
- apt-get update && apt-get install make
- make lint-go
depends_on:
- wire-install
environment:
CGO_ENABLED: "1"
image: golang:1.19.1
name: lint-backend
trigger:
event:
- pull_request
paths:
exclude:
- docs/**
- '*.md'
include:
- pkg/**
- packaging/**
- conf/**
- go.sum
- go.mod
- public/app/plugins/**/plugin.json
- devenv/**
type: docker
volumes:
- host:
path: /var/run/docker.sock
name: docker
---
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: pr-build-e2e
node:
@ -466,6 +561,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: pr-integration-tests
node:
@ -589,6 +686,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: pr-docs
node:
@ -673,6 +772,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: pr-shellcheck
node:
@ -714,6 +815,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: main-docs
node:
@ -799,6 +902,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: main-test-frontend
node:
@ -831,17 +936,6 @@ steps:
CGO_ENABLED: 0
image: golang:1.19.1
name: compile-build-cmd
- commands:
- yarn run prettier:check
- yarn run lint
- yarn run i18n:compile
- yarn run typecheck
depends_on:
- yarn-install
environment:
TEST_MAX_WORKERS: 50%
image: grafana/build-container:1.6.2
name: lint-frontend
- commands:
- yarn betterer ci
depends_on:
@ -875,6 +969,57 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: main-lint-frontend
node:
type: no-parallel
platform:
arch: amd64
os: linux
services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
image: alpine:3.15.6
name: identify-runner
- commands:
- yarn install --immutable
depends_on: []
image: grafana/build-container:1.6.2
name: yarn-install
- commands:
- yarn run prettier:check
- yarn run lint
- yarn run i18n:compile
- yarn run typecheck
depends_on:
- yarn-install
environment:
TEST_MAX_WORKERS: 50%
image: grafana/build-container:1.6.2
name: lint-frontend
trigger:
branch: main
event:
- push
paths:
exclude:
- '*.md'
- docs/**
- latest.json
type: docker
volumes:
- host:
path: /var/run/docker.sock
name: docker
---
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: main-test-backend
node:
@ -888,12 +1033,6 @@ steps:
- echo $DRONE_RUNNER_NAME
image: alpine:3.15.6
name: identify-runner
- commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v3.0.7/grabpl
- chmod +x bin/grabpl
image: byrnedo/alpine-curl:0.1.8
name: grabpl
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
depends_on: []
@ -916,14 +1055,6 @@ steps:
- verify-gen-cue
image: grafana/build-container:1.6.2
name: wire-install
- commands:
- make lint-go
depends_on:
- wire-install
environment:
CGO_ENABLED: "1"
image: grafana/build-container:1.6.2
name: lint-backend
- commands:
- go test -short -covermode=atomic -timeout=30m ./pkg/...
depends_on:
@ -936,6 +1067,60 @@ steps:
- wire-install
image: grafana/build-container:1.6.2
name: test-backend-integration
trigger:
branch: main
event:
- push
paths:
exclude:
- '*.md'
- docs/**
- latest.json
type: docker
volumes:
- host:
path: /var/run/docker.sock
name: docker
---
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: main-lint-backend
node:
type: no-parallel
platform:
arch: amd64
os: linux
services: []
steps:
- commands:
- echo $DRONE_RUNNER_NAME
image: alpine:3.15.6
name: identify-runner
- commands:
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.19.1
name: compile-build-cmd
- commands:
- make gen-go
depends_on: []
image: grafana/build-container:1.6.2
name: wire-install
- commands:
- apt-get update && apt-get install make
- make lint-go
depends_on:
- wire-install
environment:
CGO_ENABLED: "1"
image: golang:1.19.1
name: lint-backend
- commands:
- ./bin/build verify-drone
depends_on:
@ -960,6 +1145,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: main-build-e2e-publish
node:
@ -1035,17 +1222,6 @@ steps:
- pkg/build/**
repo:
- grafana/grafana
- image: grafana/drone-downstream
name: trigger-enterprise-downstream
settings:
params:
- SOURCE_BUILD_NUMBER=${DRONE_COMMIT}
- SOURCE_COMMIT=${DRONE_COMMIT}
repositories:
- grafana/grafana-enterprise@main
server: https://drone.grafana.net
token:
from_secret: drone_token
- commands:
- ./bin/build build-backend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER}
depends_on:
@ -1378,6 +1554,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: main-integration-tests
node:
@ -1499,6 +1677,8 @@ depends_on:
- main-test-backend
- main-build-e2e-publish
- main-integration-tests
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: main-windows
platform:
@ -1597,6 +1777,8 @@ depends_on:
- main-build-e2e-publish
- main-integration-tests
- main-windows
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: main-publish
node:
@ -1661,6 +1843,47 @@ volumes:
clone:
retries: 3
depends_on:
- main-publish
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: main-trigger-downstream
node:
type: no-parallel
platform:
arch: amd64
os: linux
services: []
steps:
- image: grafana/drone-downstream
name: trigger-enterprise-downstream
settings:
params:
- SOURCE_BUILD_NUMBER=${DRONE_COMMIT}
- SOURCE_COMMIT=${DRONE_COMMIT}
repositories:
- grafana/grafana-enterprise@main
server: https://drone.grafana.net
token:
from_secret: drone_token
trigger:
branch: main
event:
- push
paths:
exclude:
- '*.md'
- docs/**
- latest.json
type: docker
volumes:
- host:
path: /var/run/docker.sock
name: docker
---
clone:
retries: 3
depends_on:
- main-test-frontend
- main-test-backend
- main-build-e2e-publish
@ -1699,6 +1922,8 @@ type: docker
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: release-oss-build-e2e-publish
node:
@ -2005,6 +2230,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: release-oss-test
node:
@ -2053,12 +2280,13 @@ steps:
image: golang:1.19.1
name: compile-build-cmd
- commands:
- apt-get update && apt-get install make
- make lint-go
depends_on:
- wire-install
environment:
CGO_ENABLED: "1"
image: grafana/build-container:1.6.2
image: golang:1.19.1
name: lint-backend
- commands:
- yarn run prettier:check
@ -2109,6 +2337,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: release-oss-integration-tests
node:
@ -2222,6 +2452,8 @@ depends_on:
- release-oss-build-e2e-publish
- release-oss-test
- release-oss-integration-tests
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: release-oss-windows
platform:
@ -2706,12 +2938,13 @@ steps:
image: grafana/build-container:1.6.2
name: verify-gen-cue
- commands:
- apt-get update && apt-get install make
- make lint-go
depends_on:
- wire-install
environment:
CGO_ENABLED: "1"
image: grafana/build-container:1.6.2
image: golang:1.19.1
name: lint-backend
- commands:
- yarn run prettier:check
@ -2745,12 +2978,13 @@ steps:
image: grafana/build-container:1.6.2
name: test-frontend
- commands:
- apt-get update && apt-get install make
- make lint-go
depends_on:
- wire-install
environment:
CGO_ENABLED: "1"
image: grafana/build-container:1.6.2
image: golang:1.19.1
name: lint-backend-enterprise2
- commands:
- go test -tags=pro -covermode=atomic -timeout=30m ./pkg/...
@ -3022,6 +3256,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: publish-docker-oss-public
node:
@ -3038,10 +3274,16 @@ steps:
image: byrnedo/alpine-curl:0.1.8
name: grabpl
- commands:
- ./bin/grabpl artifacts docker fetch --version-tag ${TAG} --edition oss --base
alpine --base ubuntu --arch amd64 --arch arm64 --arch armv7
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.19.1
name: compile-build-cmd
- commands:
- ./bin/build artifacts docker fetch --edition oss
depends_on:
- grabpl
- compile-build-cmd
environment:
DOCKER_PASSWORD:
from_secret: docker_password
@ -3102,6 +3344,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: publish-docker-enterprise-public
node:
@ -3118,10 +3362,16 @@ steps:
image: byrnedo/alpine-curl:0.1.8
name: grabpl
- commands:
- ./bin/grabpl artifacts docker fetch --version-tag ${TAG} --edition enterprise
--base alpine --base ubuntu --arch amd64 --arch arm64 --arch armv7
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.19.1
name: compile-build-cmd
- commands:
- ./bin/build artifacts docker fetch --edition enterprise
depends_on:
- grabpl
- compile-build-cmd
environment:
DOCKER_PASSWORD:
from_secret: docker_password
@ -3165,6 +3415,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: publish-docker-oss-security
node:
@ -3181,10 +3433,16 @@ steps:
image: byrnedo/alpine-curl:0.1.8
name: grabpl
- commands:
- ./bin/grabpl artifacts docker fetch --version-tag ${TAG} --edition oss --base
alpine --base ubuntu --arch amd64 --arch arm64 --arch armv7
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.19.1
name: compile-build-cmd
- commands:
- ./bin/build artifacts docker fetch --edition oss
depends_on:
- grabpl
- compile-build-cmd
environment:
DOCKER_PASSWORD:
from_secret: docker_password
@ -3246,6 +3504,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: publish-docker-enterprise-security
node:
@ -3262,10 +3522,16 @@ steps:
image: byrnedo/alpine-curl:0.1.8
name: grabpl
- commands:
- ./bin/grabpl artifacts docker fetch --version-tag ${TAG} --edition enterprise
--base alpine --base ubuntu --arch amd64 --arch arm64 --arch armv7
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.19.1
name: compile-build-cmd
- commands:
- ./bin/build artifacts docker fetch --edition enterprise
depends_on:
- grabpl
- compile-build-cmd
environment:
DOCKER_PASSWORD:
from_secret: docker_password
@ -3310,6 +3576,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: publish-artifacts-security
node:
@ -3350,6 +3618,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: publish-artifacts-public
node:
@ -3390,6 +3660,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: publish-npm-packages-public
node:
@ -3450,6 +3722,8 @@ depends_on:
- publish-artifacts-public
- publish-docker-oss-public
- publish-docker-enterprise-public
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: publish-packages-oss
node:
@ -3529,6 +3803,8 @@ depends_on:
- publish-artifacts-public
- publish-docker-oss-public
- publish-docker-enterprise-public
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: publish-packages-enterprise
node:
@ -3606,6 +3882,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: publish-artifacts-page
node:
@ -3643,6 +3921,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: release-branch-oss-build-e2e-publish
node:
@ -3918,6 +4198,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: release-branch-oss-test
node:
@ -3966,12 +4248,13 @@ steps:
image: golang:1.19.1
name: compile-build-cmd
- commands:
- apt-get update && apt-get install make
- make lint-go
depends_on:
- wire-install
environment:
CGO_ENABLED: "1"
image: grafana/build-container:1.6.2
image: golang:1.19.1
name: lint-backend
- commands:
- yarn run prettier:check
@ -4016,6 +4299,8 @@ volumes:
clone:
retries: 3
depends_on: []
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: release-branch-oss-integration-tests
node:
@ -4123,6 +4408,8 @@ depends_on:
- release-branch-oss-build-e2e-publish
- release-branch-oss-test
- release-branch-oss-integration-tests
image_pull_secrets:
- dockerconfigjson
kind: pipeline
name: release-branch-oss-windows
platform:
@ -4583,12 +4870,13 @@ steps:
image: grafana/build-container:1.6.2
name: verify-gen-cue
- commands:
- apt-get update && apt-get install make
- make lint-go
depends_on:
- wire-install
environment:
CGO_ENABLED: "1"
image: grafana/build-container:1.6.2
image: golang:1.19.1
name: lint-backend
- commands:
- yarn run prettier:check
@ -4622,12 +4910,13 @@ steps:
image: grafana/build-container:1.6.2
name: test-frontend
- commands:
- apt-get update && apt-get install make
- make lint-go
depends_on:
- wire-install
environment:
CGO_ENABLED: "1"
image: grafana/build-container:1.6.2
image: golang:1.19.1
name: lint-backend-enterprise2
- commands:
- go test -tags=pro -covermode=atomic -timeout=30m ./pkg/...
@ -5060,6 +5349,6 @@ kind: secret
name: packages_secret_access_key
---
kind: signature
hmac: 1bbfd995ded7c2d1c0330ef2009691577a6613ec98df296fc8ec4388b9898e2d
hmac: 51941c93ec6753cf788db7ecefc8ffc63549ec675b23ae98fc27bcd7a22cddbb
...

View File

@ -19,6 +19,18 @@
"alphabetize": { "order": "asc" }
}
],
"no-restricted-imports": [
"warn",
{
"paths": [
{
"name": "react-redux",
"importNames": ["useDispatch", "useSelector"],
"message": "Please import from app/types instead."
}
]
}
],
// Use typescript's no-redeclare for compatibility with overrides
"no-redeclare": "off",

3
.github/CODEOWNERS vendored
View File

@ -191,3 +191,6 @@ lerna.json @grafana/frontend-ops
/pkg/services/oauthtoken @grafana/grafana-authnz-team
/pkg/services/teamguardian @grafana/grafana-authnz-team
/pkg/services/serviceaccounts @grafana/grafana-authnz-team
# Grafana Partnerships Team
/pkg/infra/httpclient/httpclientprovider/sigv4_middleware.go @grafana/grafana-partnerships-team

View File

@ -16,6 +16,10 @@
"name": "needs_more_info",
"query": "label:\"needs more info\" is:open"
},
{
"name": "triage_needs_confirmation",
"query": "label:\"triage/needs-confirmation\" is:open"
},
{
"name": "unlabeled",
"query": "is:open is:issue no:label"
@ -23,10 +27,6 @@
{
"name": "open_prs",
"query": "is:open is:pr"
},
{
"name": "milestone_7_4_open",
"query": "is:open is:issue milestone:7.4"
}
]
}

View File

@ -14,7 +14,7 @@ on:
jobs:
workflow-call:
uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.10
uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.11
with:
frontend-path-regexp: public\/app\/plugins\/datasource\/(grafana-azure-monitor-datasource|cloud-monitoring|cloudwatch)
backend-path-regexp: pkg\/tsdb\/(azuremonitor|cloudmonitoring|cloudwatch)

17
.github/workflows/ox-code-coverage.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Observability Experience test code coverage
on:
pull_request:
paths:
- 'pkg/services/queryhistory/**'
- 'public/app/features/explore/**'
- 'public/app/features/correlations/**'
branches-ignore:
- dependabot/**
- backport-*
jobs:
workflow-call:
uses: grafana/code-coverage/.github/workflows/code-coverage.yml@v0.1.11
with:
frontend-path-regexp: public\/app\/features\/(explore|correlations)
backend-path-regexp: pkg\/services\/(queryhistory)

3
.gitignore vendored
View File

@ -40,6 +40,8 @@ tsconfig.tsbuildinfo
# Enterprise devenv
/devenv/docker/blocks/grafana-enterprise
/devenv/docker/blocks/saml-enterprise
# This is the new place of the block, but I leave the previous here for a while
/devenv/docker/blocks/auth/saml-enterprise
/tmp
tools/phantomjs/phantomjs
@ -73,6 +75,7 @@ public/css/*.min.css
# devenv
/devenv/docker-compose.yaml
/devenv/docker-compose.override.yaml
/devenv/.env
/devenv/docker/blocks/tempo/tempo-data/

View File

@ -1,3 +1,29 @@
<!-- 9.1.6 START -->
# 9.1.6 (2022-09-20)
### Features and enhancements
- **Auth:** Trigger auth token cleanup job. (Enterprise)
- **DataSource:** Adding possibility to hide queries from the inspector. [#54892](https://github.com/grafana/grafana/pull/54892), [@mckn](https://github.com/mckn)
- **Inspect:** Hide Actions tab when it is empty. [#55272](https://github.com/grafana/grafana/pull/55272), [@ryantxu](https://github.com/ryantxu)
- **PanelMenu:** Remove hide legend action as it was showing on all panel types. [#54876](https://github.com/grafana/grafana/pull/54876), [@torkelo](https://github.com/torkelo)
- **Provisioning Contact points:** Support disableResolveMessage via YAML. [#54122](https://github.com/grafana/grafana/pull/54122), [@mmusenbr](https://github.com/mmusenbr)
- **PublicDashboards:** Support subpaths when generating pubdash url. [#55204](https://github.com/grafana/grafana/pull/55204), [@owensmallwood](https://github.com/owensmallwood)
### Bug fixes
- **Alerting:** Fix legacy migration crash when rule name is too long. [#55053](https://github.com/grafana/grafana/pull/55053), [@alexweav](https://github.com/alexweav)
- **Alerting:** Fix send resolved notifications. [#54793](https://github.com/grafana/grafana/pull/54793), [@grobinson-grafana](https://github.com/grobinson-grafana)
- **Azure Monitor:** Fix migration issue with MetricDefinitionsQuery template variable query types. [#55262](https://github.com/grafana/grafana/pull/55262), [@yaelleC](https://github.com/yaelleC)
- **Browse:** Hide dashboard actions if user does not have enough permission. [#55218](https://github.com/grafana/grafana/pull/55218), [@lpskdl](https://github.com/lpskdl)
- **ElasticSearch:** Fix dispatching queries at a wrong time. [#55225](https://github.com/grafana/grafana/pull/55225), [@svennergr](https://github.com/svennergr)
- **Panel:** Disable legends when showLegend is false prior to schema v37. [#55126](https://github.com/grafana/grafana/pull/55126), [@ivanortegaalba](https://github.com/ivanortegaalba)
- **Prometheus:** Fix metadata requests for browser access mode. [#55403](https://github.com/grafana/grafana/pull/55403), [@itsmylife](https://github.com/itsmylife)
- **Search:** Avoid requesting all dashboards when in Folder View. [#55169](https://github.com/grafana/grafana/pull/55169), [@JoaoSilvaGrafana](https://github.com/JoaoSilvaGrafana)
- **TablePanel/StatPanel:** Fix values not being visible when background transparent. [#55092](https://github.com/grafana/grafana/pull/55092), [@mdvictor](https://github.com/mdvictor)
<!-- 9.1.6 END -->
<!-- 9.1.5 START -->
# 9.1.5 (2022-09-12)
@ -392,6 +418,18 @@ In Prometheus, browser access mode was deprecated in Grafana 7.4.0 and removed i
- **Grafana/UI:** Add ColorPickerInput component. [#52222](https://github.com/grafana/grafana/pull/52222), [@Clarity-89](https://github.com/Clarity-89)
- **Plugins:** Validate root URLs when signing private plugins via grafana-toolkit. [#51968](https://github.com/grafana/grafana/pull/51968), [@wbrowne](https://github.com/wbrowne)
<!-- 9.0.9 START -->
# 9.0.9 (2022-09-20)
### Bug fixes
- **AngularPanels:** Fixing changing angular panel options not taking having affect when coming back from panel edit. [#54834](https://github.com/grafana/grafana/pull/54834), [@grafanabot](https://github.com/grafanabot)
- **AuthNZ:** Security fixes for CVE-2022-35957 and CVE-2022-36062. [#55498](https://github.com/grafana/grafana/pull/55498), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
- **FIX:** RBAC prevents deleting empty snapshots (#54385). [#54509](https://github.com/grafana/grafana/pull/54509), [@gamab](https://github.com/gamab)
<!-- 9.0.9 END -->
<!-- 9.0.8 START -->
# 9.0.8 (2022-08-30)
@ -1124,6 +1162,20 @@ In the Loki data source, for consistency and performance reasons, we changed how
The dependency to [grafana/aws-sdk](https://github.com/grafana/grafana-aws-sdk-react) is moved from [grafana/ui](https://github.com/grafana/grafana/blob/main/packages/grafana-ui/package.json) to the plugin. This means that any plugin that use SIGV4 auth need to pass a SIGV4 editor component as a prop to the `DataSourceHttpSettings` component. Issue [#43559](https://github.com/grafana/grafana/issues/43559)
<!-- 8.5.13 START -->
# 8.5.13 (2022-09-20)
### Features and enhancements
- **Plugins:** Expose @emotion/react to plugins to prevent load failures. [#55297](https://github.com/grafana/grafana/pull/55297), [@jackw](https://github.com/jackw)
### Bug fixes
- **AuthNZ:** Security fixes for CVE-2022-35957 and CVE-2022-36062. [#55495](https://github.com/grafana/grafana/pull/55495), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
<!-- 8.5.13 END -->
<!-- 8.5.11 START -->
# 8.5.11 (2022-08-30)

View File

@ -210,7 +210,10 @@ check_for_updates = true
check_for_plugin_updates = true
# Google Analytics universal tracking code, only enabled if you specify an id here
google_analytics_ua_id =
google_analytics_ua_id =
# Google Analytics 4 tracking code, only enabled if you specify an id here
google_analytics_4_id =
# Google Tag Manager ID, only enabled if you specify an id here
google_tag_manager_id =
@ -1039,6 +1042,11 @@ zipkin_propagation = false
# Not disabling is the most common setting when using Zipkin elsewhere in your infrastructure.
disable_shared_zipkin_spans = false
[tracing.opentelemetry]
# attributes that will always be included in when creating new spans. ex (key1:value1,key2:value2)
custom_attributes =
[tracing.opentelemetry.jaeger]
# jaeger destination (ex http://localhost:14268/api/traces)
address =
@ -1288,3 +1296,19 @@ scheduler_interval =
[storage]
# Allow uploading SVG files without sanitization.
allow_unsanitized_svg_upload = false
#################################### Search ################################################
[search]
# Defines the number of dashboards loaded at once in a batch during a full reindex.
# This is a temporary settings that might be removed in the future.
dashboard_loading_batch_size = 200
# Defines the frequency of a full search reindex.
# This is a temporary settings that might be removed in the future.
full_reindex_interval = 5m
# Defines the frequency of partial index updates based on recent changes such as dashboard updates.
# This is a temporary settings that might be removed in the future.
index_update_interval = 10s

View File

@ -218,6 +218,9 @@
# Google Analytics universal tracking code, only enabled if you specify an id here
;google_analytics_ua_id =
# Google Analytics 4 tracking code, only enabled if you specify an id here
;google_analytics_4_id =
# Google Tag Manager ID, only enabled if you specify an id here
;google_tag_manager_id =
@ -1008,6 +1011,10 @@
# Not disabling is the most common setting when using Zipkin elsewhere in your infrastructure.
;disable_shared_zipkin_spans = false
[tracing.opentelemetry]
# attributes that will always be included in when creating new spans. ex (key1:value1,key2:value2)
;custom_attributes = key1:value1,key2:value2
[tracing.opentelemetry.jaeger]
# jaeger destination (ex http://localhost:14268/api/traces)
; address = http://localhost:14268/api/traces

View File

@ -0,0 +1,276 @@
# Instrumenting Grafana
Guidance, conventions and best practices for instrumenting Grafana using logs, metrics and traces.
## Logs
Logs are files that record events, warnings and errors as they occur within a software environment. Most logs include contextual information, such as the time an event occurred and which user or endpoint was associated with it.
### Usage
Use the _pkg/infra/log_ package to create a named structured logger. Example:
```go
import (
"fmt"
"github.com/grafana/grafana/pkg/infra/log"
)
logger := log.New("my-logger")
logger.Debug("Debug msg")
logger.Info("Info msg")
logger.Warning("Warning msg")
logger.Error("Error msg", "error", fmt.Errorf("BOOM"))
```
### Naming conventions
Name the logger using lowercase characters, e.g. `log.New("my-logger")` using snake_case or kebab-case styling.
Prefix the logger name with an area name when using different loggers across a feature or related packages, e.g. `log.New("plugin.loader")` and `log.New("plugin.client")`.
Start the log message with a capital letter, e.g. `logger.Info("Hello world")` instead of `logger.Info("hello world")`. The log message should be an identifier for the log entry, avoid parameterization in favor of key-value pairs for additional data.
Prefer using camelCase style when naming log keys, e.g. _remoteAddr_, to be consistent with Go identifiers.
Use the key _error_ when logging Go errors, e.g. `logger.Error("Something failed", "error", fmt.Errorf("BOOM"))`.
### Validate and sanitize input coming from user input
If log messages or key/value pairs originates from user input they **should** be validated and sanitized.
Be **careful** to not expose any sensitive information in log messages e.g. secrets, credentials etc. It's especially easy to do by mistake when including a struct as value.
### Log levels
When to use which log level?
- **Debug:** Informational messages of high frequency and/or less-important messages during normal operations.
- **Info:** Informational messages of low frequency and/or important messages.
- **Warning:** Should in normal cases not be used/needed. If used should be actionable.
- **Error:** Error messages indicating some operation failed (with an error) and the program didn't have a way of handle the error.
### Contextual logging
Use a contextual logger to include additional key/value pairs attached to `context.Context`, e.g. `traceID`, to allow correlating logs with traces and/or correlate logs with a common identifier.
Example:
```go
import (
"context"
"fmt"
"github.com/grafana/grafana/pkg/infra/log"
)
var logger = log.New("my-logger")
func doSomething(ctx context.Context) {
ctxLogger := logger.FromContext(ctx)
ctxLogger.Debug("Debug msg")
ctxLogger.Info("Info msg")
ctxLogger.Warning("Warning msg")
ctxLogger.Error("Error msg", "error", fmt.Errorf("BOOM"))
}
```
### Enable certain log levels for certain loggers
During development it's convenient to enable certain log level, e.g. debug, for certain loggers to minimize the generated log output and make it easier to find things. See [[log.filters]](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#filters) for information how to configure this.
It's also possible to configure multiple loggers:
```ini
[log]
filters = rendering:debug \
; alerting.notifier:debug \
oauth.generic_oauth:debug \
; oauth.okta:debug \
; tsdb.postgres:debug \
; tsdb.mssql:debug \
; provisioning.plugins:debug \
; provisioning:debug \
; provisioning.dashboard:debug \
; provisioning.datasources:debug \
datasources:debug \
data-proxy-log:debug
```
## Metrics
Metrics are quantifiable measurements that reflect the health and performance of applications or infrastructure.
Consider using metrics to provide real-time insight into the state of resources. If you want to know how responsive your application is or identify anomalies that could be early signs of a performance issue, metrics are a key source of visibility.
### Metric types
See [Prometheus metric types](https://prometheus.io/docs/concepts/metric_types/) for a list and description of the different metric types you can use and when to use them.
There are many possible types of metrics that can be tracked. One popular method for defining metrics is the [RED method](https://grafana.com/blog/2018/08/02/the-red-method-how-to-instrument-your-services/).
### Naming conventions
Use the namespace _grafana_ as that would prefix any defined metric names with `grafana_`. This will make it clear for operators that any metric named `grafana_*` belongs to Grafana.
Use snake*case style when naming metrics, e.g. \_http_request_duration_seconds* instead of _httpRequestDurationSeconds_.
Use snake*case style when naming labels, e.g. \_status_code* instead of _statusCode_.
If metric type is a _counter_, name it with a `_total` suffix, e.g. _http_requests_total_.
If metric type is a _histogram_ and you're measuring duration, name it with a `_<unit>` suffix, e.g. _http_request_duration_seconds_.
If metric type is a _gauge_, name it to denote it's a value that can increase and decrease , e.g. _http_request_in_flight_.
### Label values and high cardinality
Be careful with what label values you add/accept. Using/allowing too many label values could result in [high cardinality problems](https://grafana.com/blog/2022/02/15/what-are-cardinality-spikes-and-why-do-they-matter/).
If label values originates from user input they **should** be validated. Use `metricutil.SanitizeLabelName(<label value>`) from _pkg/infra/metrics/metricutil_ package to sanitize label names. Very **important** to only allow a pre-defined set of labels to minimize the risk of high cardinality problems.
Be **careful** to not expose any sensitive information in label values, e.g. secrets, credentials etc.
### Guarantee the existence of metrics
If you want to guarantee the existence of metrics before any observations has happened there's a couple of helper methods available in the _pkg/infra/metrics/metricutil_ package.
### How to collect and visualize metrics locally
1. Start Prometheus
```bash
make devenv sources=prometheus
```
2. Use Grafana Explore or dashboards to query any exported Grafana metrics
## Traces
A distributed trace is data that tracks an application request as it flows through the various parts of an application. The trace records how long it takes each application component to process the request and pass the result to the next component. Traces can also identify which parts of the application trigger an error.
### Usage
Grafana currently supports two tracing implementations, [OpenTelemetry](https://opentelemetry.io/) and [OpenTracing](https://opentracing.io/). OpenTracing is deprecated, but still supported until we remove it. The two different implementations implements the `Tracer` and `Span` interfaces, defined in the _pkg/infra/tracing_ package, which you can use to create traces and spans. To get a hold of a `Tracer` you would need to get it injected as dependency into your service, see [Services](../../architecture/backend/services.md) for more details.
Example:
```go
import (
"fmt"
"github.com/grafana/grafana/pkg/infra/tracing"
"go.opentelemetry.io/otel/attribute"
)
type MyService struct {
tracer tracing.Tracer
}
func ProvideService(tracer tracing.Tracer) *MyService {
return &MyService{
tracer: tracer,
}
}
func (s *MyService) Hello(ctx context.Context, name string) (string, error) {
ctx, span := s.tracer.Start(ctx, "MyService.Hello")
// this make sure the span is marked as finished when this
// method ends to allow the span to be flushed and sent to
// storage backend.
defer span.End()
// Add some event to show Events usage
span.AddEvents(
[]string{"message"},
[]tracing.EventValue{
{Str: "checking name..."},
})
if name == "" {
err := fmt.Errorf("name cannot be empty")
// record err as an exception span event for this span
span.RecordError(err)
return "", err
}
// Add some other event to show Events usage
span.AddEvents(
[]string{"message"},
[]tracing.EventValue{
{Str: "name checked"},
})
// Add attribute to show Attributes usage
span.SetAttributes("my_service.name", name, attribute.Key("my_service.name").String(name))
return fmt.Sprintf("Hello %s", name), nil
}
```
### Naming conventions
Span names should follow the [guidelines from OpenTelemetry](https://opentelemetry.io/docs/reference/specification/trace/api/#span).
| Span Name | Guidance |
| ----------------------- | -------------------------------------------------------- |
| get | Too general |
| get_account/42 | Too specific |
| get_account | Good, and account_id=42 would make a nice Span attribute |
| get_account/{accountId} | Also good (using the “HTTP route”) |
Span attribute and span event attributes should follow the [Attribute naming specification from OpenTelemetry](https://opentelemetry.io/docs/reference/specification/common/attribute-naming/). Good attribute key examples:
- service.version
- http.status_code
See [Trace semantic conventions from OpenTelemetry](https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/) for additional conventions regarding well-known protocols and operations.
### Span names and high cardinality
Be careful with what span names you add/accept. Using/allowing too many span names could result in high cardinality problems.
### Validate and sanitize input coming from user input
If span names, attribute or event values originates from user input they **should** be validated and sanitized. It's very **important** to only allow a pre-defined set of span names to minimize the risk of high cardinality problems.
Be **careful** to not expose any sensitive information in span names, attribute or event values, e.g. secrets, credentials etc.
### How to collect, visualize and query traces (and correlate logs with traces) locally
1. Start Jaeger
```bash
make devenv sources=jaeger
```
2. Enable tracing in Grafana
opentelemetry tracing (recommended):
```ini
[tracing.opentelemetry.jaeger]
address = http://localhost:14268/api/traces
```
opentracing tracing (deprecated/not recommended):
```ini
[tracing.jaeger]
address = localhost:6831
```
3. Search/browse collected logs and traces in Grafana Explore
You need provisioned gdev-jaeger and gdev-loki datasources, see [developer dashboard and data sources](https://github.com/grafana/grafana/tree/main/devenv#developer-dashboards-and-data-sources) for setup instructions.
Open Grafana explore and select gdev-loki datasource and use the query `{filename="/var/log/grafana/grafana.log"} | logfmt`.
You can then inspect any log message that includes a `traceID` and from there click on `gdev-jaeger` to split view and inspect the trace in question.
4. Search/browse collected traces in Jaeger UI
You can open http://localhost:16686 to use the Jaeger UI for browsing and searching traces.

View File

@ -0,0 +1,536 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 1394,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "testdata",
"uid": "PD8C576611E62080A"
},
"gridPos": {
"h": 3,
"w": 24,
"x": 0,
"y": 0
},
"id": 9,
"options": {
"content": "Dashboard queries allow re-using the same results from one panel in another panel context.\n\nThis dashboard shows a single panel that makes a real query and applies transformations. The other panels, all use the same results rather than make their own query requests.",
"mode": "markdown"
},
"pluginVersion": "9.2.0-pre",
"type": "text"
},
{
"datasource": {
"type": "testdata",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 18,
"w": 7,
"x": 0,
"y": 3
},
"id": 2,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "9.2.0-pre",
"targets": [
{
"csvFileName": "flight_info_by_state.csv",
"datasource": {
"type": "testdata",
"uid": "PD8C576611E62080A"
},
"refId": "A",
"scenarioId": "csv_file"
},
{
"csvFileName": "population_by_state.csv",
"datasource": {
"type": "testdata",
"uid": "PD8C576611E62080A"
},
"refId": "B",
"scenarioId": "csv_file"
}
],
"title": "Raw data -- with outer join",
"transformations": [
{
"id": "seriesToColumns",
"options": {
"byField": "State",
"mode": "outer"
}
},
{
"id": "organize",
"options": {
"excludeByName": {
"1980 population_by_state.csv": true,
"2000 population_by_state.csv": true,
"DestLocation flight_info_by_state.csv": true,
"Lat flight_info_by_state.csv": true,
"Lng flight_info_by_state.csv": true,
"Price flight_info_by_state.csv": true
},
"indexByName": {},
"renameByName": {
"2020 population_by_state.csv": "2020 population",
"Count flight_info_by_state.csv": "Flight count",
"Price flight_info_by_state.csv": ""
}
}
}
],
"type": "table"
},
{
"datasource": {
"type": "datasource",
"uid": "-- Dashboard --"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 11,
"x": 7,
"y": 3
},
"id": 4,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "9.2.0-pre",
"targets": [
{
"datasource": {
"type": "datasource",
"uid": "-- Dashboard --"
},
"panelId": 2,
"refId": "A"
}
],
"title": "Reused data (without transform)",
"type": "table"
},
{
"datasource": {
"type": "datasource",
"uid": "-- Dashboard --"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 6,
"x": 18,
"y": 3
},
"id": 5,
"options": {
"basemap": {
"config": {},
"name": "Layer 0",
"type": "default"
},
"controls": {
"mouseWheelZoom": true,
"showAttribution": true,
"showDebug": false,
"showMeasure": false,
"showScale": false,
"showZoom": true
},
"layers": [
{
"config": {
"showLegend": true,
"style": {
"color": {
"fixed": "dark-green"
},
"opacity": 0.4,
"rotation": {
"fixed": 0,
"max": 360,
"min": -360,
"mode": "mod"
},
"size": {
"field": "Flight count",
"fixed": 5,
"max": 15,
"min": 2
},
"symbol": {
"fixed": "img/icons/marker/circle.svg",
"mode": "fixed"
},
"textConfig": {
"fontSize": 12,
"offsetX": 0,
"offsetY": 0,
"textAlign": "center",
"textBaseline": "middle"
}
}
},
"location": {
"gazetteer": "public/gazetteer/usa-states.json",
"lookup": "State",
"mode": "lookup"
},
"name": "Flight count",
"tooltip": true,
"type": "markers"
}
],
"tooltip": {
"mode": "details"
},
"view": {
"id": "coords",
"lat": 35.70008,
"lon": -93.558296,
"zoom": 3.09
}
},
"pluginVersion": "9.2.0-pre",
"targets": [
{
"datasource": {
"type": "datasource",
"uid": "-- Dashboard --"
},
"panelId": 2,
"refId": "A",
"withTransforms": true
}
],
"title": "Reused data (without transform)",
"type": "geomap"
},
{
"datasource": {
"type": "datasource",
"uid": "-- Dashboard --"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 11,
"x": 7,
"y": 12
},
"id": 6,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "9.2.0-pre",
"targets": [
{
"datasource": {
"type": "datasource",
"uid": "-- Dashboard --"
},
"panelId": 2,
"refId": "A",
"withTransforms": true
}
],
"title": "Reused data (with transform)",
"type": "table"
},
{
"datasource": {
"type": "datasource",
"uid": "-- Dashboard --"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 6,
"x": 18,
"y": 12
},
"id": 7,
"options": {
"basemap": {
"config": {},
"name": "Layer 0",
"type": "default"
},
"controls": {
"mouseWheelZoom": true,
"showAttribution": true,
"showDebug": false,
"showMeasure": false,
"showScale": false,
"showZoom": true
},
"layers": [
{
"config": {
"showLegend": true,
"style": {
"color": {
"fixed": "dark-green"
},
"opacity": 0.4,
"rotation": {
"fixed": 0,
"max": 360,
"min": -360,
"mode": "mod"
},
"size": {
"field": "2020 population",
"fixed": 5,
"max": 15,
"min": 2
},
"symbol": {
"fixed": "img/icons/marker/circle.svg",
"mode": "fixed"
},
"textConfig": {
"fontSize": 12,
"offsetX": 0,
"offsetY": 0,
"textAlign": "center",
"textBaseline": "middle"
}
}
},
"location": {
"gazetteer": "public/gazetteer/usa-states.json",
"lookup": "State",
"mode": "lookup"
},
"name": "2022 Population",
"tooltip": true,
"type": "markers"
}
],
"tooltip": {
"mode": "details"
},
"view": {
"id": "coords",
"lat": 35.70008,
"lon": -93.558296,
"zoom": 3.09
}
},
"pluginVersion": "9.2.0-pre",
"targets": [
{
"datasource": {
"type": "datasource",
"uid": "-- Dashboard --"
},
"panelId": 2,
"refId": "A",
"withTransforms": true
}
],
"title": "Reused data (with transform)",
"type": "geomap"
}
],
"schemaVersion": 37,
"style": "dark",
"tags": ["devenv"],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Reuse dashboard queries",
"uid": "fYGWTVW4k"
}

View File

@ -80,6 +80,9 @@ async function elasticSetupIndexTemplate() {
location: {
type: 'geo_point',
},
shapes: {
type: 'nested',
}
},
},
},
@ -108,6 +111,15 @@ function getRandomLogItem(counter, timestamp) {
level: chooseRandomElement(['info', 'info', 'error']),
// location: chooseRandomElement(LOCATIONS),
location: makeRandomPoint(),
shapes: Math.random() < 0.5 ? [
{"type": "triangle"},
{"type": "square"},
] : [
{"type": "triangle"},
{"type": "triangle"},
{"type": "triangle"},
{"type": "square"},
],
};
}

View File

@ -1,42 +0,0 @@
# You need to run 'sysctl -w vm.max_map_count=262144' on the host machine
elasticsearch7:
image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2
command: elasticsearch -E "discovery.type=single-node"
ports:
- "12200:9200"
- "12300:9300"
fake-elastic7-data:
image: grafana/fake-data-gen
links:
- elasticsearch7
environment:
FD_SERVER: elasticsearch7
FD_DATASOURCE: elasticsearch7
FD_PORT: 9200
filebeat7:
image: docker.elastic.co/beats/filebeat-oss:7.17.0
command: filebeat -e -strict.perms=false
volumes:
- ./docker/blocks/elastic7/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- /var/log:/var/log:ro
- ../data/log:/var/log/grafana:ro
metricbeat7:
image: docker.elastic.co/beats/metricbeat-oss:7.17.0
command: metricbeat -e -strict.perms=false
user: root
volumes:
- ./docker/blocks/elastic7/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
kibana7:
image: docker.elastic.co/kibana/kibana-oss:7.0.0
ports:
- "5601:5601"
links:
- elasticsearch7
environment:
ELASTICSEARCH_HOSTS: http://elasticsearch7:9200

View File

@ -1,2 +0,0 @@
script.inline: on
script.indexed: on

View File

@ -1,44 +0,0 @@
# You need to run 'sysctl -w vm.max_map_count=262144' on the host machine
elasticsearch77:
image: docker.elastic.co/elasticsearch/elasticsearch:7.7.1
command: elasticsearch
environment:
- "discovery.type=single-node"
ports:
- "13200:9200"
- "13300:9300"
fake-elastic77-data:
image: grafana/fake-data-gen
links:
- elasticsearch77
environment:
FD_SERVER: elasticsearch77
FD_DATASOURCE: elasticsearch7
FD_PORT: 9200
filebeat77:
image: docker.elastic.co/beats/filebeat:7.7.1
command: filebeat -e -strict.perms=false
volumes:
- ./docker/blocks/elastic77/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- /var/log:/var/log:ro
- ../data/log:/var/log/grafana:ro
metricbeat77:
image: docker.elastic.co/beats/metricbeat:7.7.1
command: metricbeat -e -strict.perms=false
user: root
volumes:
- ./docker/blocks/elastic77/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
kibana77:
image: docker.elastic.co/kibana/kibana:7.7.1
ports:
- "5601:5601"
links:
- elasticsearch77
environment:
ELASTICSEARCH_HOSTS: http://elasticsearch77:9200

View File

@ -1,3 +0,0 @@
script.inline: on
script.indexed: on
xpack.license.self_generated.type: basic

File diff suppressed because it is too large Load Diff

View File

@ -1,38 +0,0 @@
metricbeat.config:
modules:
path: ${path.config}/modules.d/*.yml
# Reload module configs as they change:
reload.enabled: false
metricbeat.autodiscover:
providers:
- type: docker
hints.enabled: true
metricbeat.modules:
- module: docker
metricsets:
- "container"
- "cpu"
- "diskio"
- "healthcheck"
- "info"
#- "image"
- "memory"
- "network"
hosts: ["unix:///var/run/docker.sock"]
period: 10s
enabled: true
processors:
- add_cloud_metadata: ~
output.elasticsearch:
hosts: ["elasticsearch77:9200"]
index: "metricbeat-%{+yyyy.MM.dd}"
setup.template.name: "metricbeat"
setup.template.pattern: "metricbeat-*"
setup.template.settings:
index.number_of_shards: 1
index.number_of_replicas: 1

View File

@ -0,0 +1 @@
elastic_version=8.4.1

View File

@ -0,0 +1,32 @@
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:${elastic_version}
environment:
- "discovery.type=single-node"
- "xpack.license.self_generated.type=basic"
- "xpack.security.enabled=false"
ports:
- 9200:9200
kibana:
image: docker.elastic.co/kibana/kibana:${elastic_version}
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- 5601:5601
metricbeat-host:
image: docker.elastic.co/beats/metricbeat-oss:${elastic_version}
command: metricbeat -e -strict.perms=false
user: root
volumes:
- ./docker/blocks/elasticstack/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
filebeat-host:
image: docker.elastic.co/beats/filebeat-oss:${elastic_version}
command: filebeat -e -strict.perms=false
volumes:
- ./docker/blocks/elasticstack/filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
# - /var/log:/var/log:ro
- ../data/log:/var/log/grafana:ro

View File

@ -1080,13 +1080,13 @@ filebeat.inputs:
#-------------------------- Elasticsearch output -------------------------------
output.elasticsearch:
# Boolean flag to enable or disable the output module.
#enabled: true
enabled: true
# Array of hosts to connect to.
# Scheme and port can be left out and will be set to the default (http and 9200)
# In case you specify and additional path, the scheme is required: http://localhost:9200/path
# IPv6 addresses should always be defined as: https://[2001:db8::1]:9200
hosts: ["elasticsearch7:9200"]
hosts: ["elasticsearch:9200"]
# Enabled ilm (beta) to use index lifecycle management instead daily indices.
#ilm.enabled: false

View File

@ -28,7 +28,7 @@ processors:
- add_cloud_metadata: ~
output.elasticsearch:
hosts: ["elasticsearch7:9200"]
hosts: ["elasticsearch:9200"]
index: "metricbeat-%{+yyyy.MM.dd}"
setup.template.name: "metricbeat"

View File

@ -25,7 +25,7 @@
promtail:
image: grafana/promtail:master
volumes:
- ./docker/blocks/loki/config.yaml:/etc/promtail/docker-config.yaml
- ./docker/blocks/loki-promtail/promtail-config.yaml:/etc/promtail/docker-config.yaml
- /var/log:/var/log
- ../data/log:/var/log/grafana
command:

View File

@ -3,7 +3,7 @@ aliases:
- /docs/grafana/latest/alerting/fundamentals/
- /docs/grafana/latest/alerting/metrics/
- /docs/grafana/latest/alerting/unified-alerting/fundamentals/
title: Alerting fundamentals
title: Explore Grafana Alerting
weight: 105
---

View File

@ -0,0 +1,16 @@
---
aliases:
- /docs/grafana/latest/alerting/set-up/
- /docs/grafana/latest/alerting/set-up/
- /docs/grafana/latest/alerting/unified-alerting/set-up/
title: Set up Grafana Alerting
weight: 107
---
# Set up Grafana Alerting
Configure the features and integrations that you need to create and manage your alerts.
- [Configure Alertmanager](https://grafana.com/docs/grafana/latest/alerting/set-up/configure-alertmanager/)
- [Provision Grafana Alerting resources](https://grafana.com/docs/grafana/latest/alerting/set-up/provision-alerting-resources/)
- [Connect Grafana Alerting to Grafana OnCall](https://grafana.com/docs/oncall/latest/integrations/available-integrations/add-grafana-alerting/)

View File

@ -0,0 +1,36 @@
---
aliases:
- /docs/grafana/latest/alerting/configure-alertmanager
- /docs/grafana/latest/alerting/configure-alertmanager
description: Configure Alertmanager
keywords:
- grafana
- alerting
- set up
- configure
- Alertmanager
title: Configure Alertmanager
weight: 100
---
# Configure Alertmanager
Configure Alertmanager from Grafana Alerting to group and manage alert rules, adding a layer of orchestration on top of your external alerting engine.
## Add a new external Alertmanager
1. In the Grafana menu, click the Alerting (bell) icon to open the Alerting page listing existing alerts.
2. Click **Admin** and then scroll down to the External Alertmanager section.
3. Click **Add Alertmanager** and a modal opens.
4. Add the URL and the port for the external Alertmanager. You do not need to specify the path suffix, for example, `/api/v(1|2)/alerts`. Grafana automatically adds this.
The external URL is listed in the table with a pending status. Once Grafana verifies that the Alertmanager is discovered, the status changes to active. No requests are made to the external Alertmanager at this point; the verification signals that alerts are ready to be sent.
### Edit an external Alertmanager
1. Click the pen symbol to the right of the Alertmanager row in the table.
2. When the edit modal opens, you can view all the URLs that were added.
The edited URL will be pending until Grafana verifies it again.
{{< figure max-width="40%" src="/static/img/docs/alerting/unified/ext-alertmanager-active.png" max-width="650px" caption="External Alertmanagers" >}}

View File

@ -0,0 +1,726 @@
---
aliases:
- /docs/grafana/latest/alerting/provision-alerting-resources
- /docs/grafana/latest/alerting/provision-alerting-resources
description: Provision alerting resources
keywords:
- grafana
- alerting
- set up
- configure
- provisioning
title: Provision Grafana Alerting resources
weight: 200
---
# Provision Grafana Alerting resources
Alerting infrastructure is often complex, with many pieces of the pipeline that often live in different places. Scaling this across multiple teams and organizations is an especially challenging task. Grafana Alerting provisioning makes this process easier by enabling you to create, manage, and maintain your alerting data in a way that best suits your organization.
There are three options to choose from:
1. Use file provisioning to provision your Grafana Alerting resources, such as alert rules and contact points, through files on disk.
1. Provision your alerting resources using the Grafana HTTP API.
For more information on the Grafana Alerting provisioning API, refer to [Alerting provisioning API](https://grafana.com/docs/grafana/latest/developers/http_api/alerting_provisioning/).
1. Provision your alerting resources using Terraform.
**Note:**
Currently, provisioning for Grafana Alerting supports alert rules, contact points, mute timings, and templates. Provisioned alerting resources can only be edited in the source that created them and not from within Grafana or any other source. For example, if you provision your alerting resources using files from disk, you cannot edit the data in Terraform or from within Grafana.
**Useful Links:**
[Grafana provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/)
[Grafana Cloud provisioning](https://grafana.com/docs/grafana-cloud/infrastructure-as-code/terraform/)
[Grafana Alerting provisioning API](https://grafana.com/docs/grafana/latest/developers/http_api/alerting_provisioning)
## Create and manage alerting resources using file provisioning
Provision your alerting resources using files from disk. When you start Grafana, the data from these files is created in your Grafana system. Grafana adds any new resources you created, updates any that you changed, and deletes old ones.
Arrange your files in a directory in a way that best suits your use case. For example, you can choose a team-based layout where every team has its own file, you can have one big file for all your teams; or you can have one file per resource type.
Details on how to set up the files and which fields are required for each object are listed below depending on which resource you are provisioning.
**Note:**
Provisioning takes place during the initial set up of your Grafana system, but you can re-run it at any time using the [Grafana Alerting provisioning API](https://grafana.com/docs/grafana/latest/developers/http_api/admin/#reload-provisioning-configurations).
### Provision alert rules
Create or delete alert rules in your Grafana instance(s).
1. Create an alert rule in Grafana.
1. Use the [Alerting provisioning API](https://grafana.com/docs/grafana/latest/developers/http_api/admin/#reload-provisioning-configurations) to extract the alert rule.
1. Copy the contents into a YAML or JSON configuration file.
Example configuration files can be found below.
1. Add the file(s) to your GitOps workflow, so that they deploy alongside your Grafana instance(s).
1. Delete the alert rule in Grafana.
**Note:**
If you do not delete the alert rule, it will clash with the provisioned alert rule once uploaded.
Here is an example of a configuration file for creating alert rules.
```yaml
# config file version
apiVersion: 1
# List of rule groups to import or update
groups:
# <int> organization ID, default = 1
- orgId: 1
# <string, required> name of the rule group
name: my_rule_group
# <string, required> name of the folder the rule group will be stored in
folder: my_first_folder
# <duration, required> interval that the rule group should evaluated at
interval: 60s
# <list, required> list of rules that are part of the rule group
rules:
# <string, required> unique identifier for the rule
- uid: my_id_1
# <string, required> title of the rule that will be displayed in the UI
title: my_first_rule
# <string, required> which query should be used for the condition
condition: A
# <list, required> list of query objects that should be executed on each
# evaluation - should be obtained trough the API
data:
- refId: A
datasourceUid: '-100'
model:
conditions:
- evaluator:
params:
- 3
type: gt
operator:
type: and
query:
params:
- A
reducer:
type: last
type: query
datasource:
type: __expr__
uid: '-100'
expression: 1==0
intervalMs: 1000
maxDataPoints: 43200
refId: A
type: math
# <string> UID of a dashboard that the alert rule should be linked to
dashboardUid: my_dashboard
# <int> ID of the panel that the alert rule should be linked to
panelId: 123
# <string> the state the alert rule will have when no data is returned
# possible values: "NoData", "Alerting", "OK", default = NoData
noDataState: Alerting
# <string> the state the alert rule will have when the query execution
# failed - possible values: "Error", "Alerting", "OK"
# default = Alerting
# <duration, required> for how long should the alert fire before alerting
for: 60s
# <map<string, string>> a map of strings to pass around any data
annotations:
some_key: some_value
# <map<string, string> a map of strings that can be used to filter and
# route alerts
labels:
team: sre_team_1
```
Here is an example of a configuration file for deleting alert rules.
```yaml
# config file version
apiVersion: 1
# List of alert rule UIDs that should be deleted
deleteRules:
# <int> organization ID, default = 1
- orgId: 1
# <string, required> unique identifier for the rule
uid: my_id_1
```
### Provision contact points
Create or delete contact points in your Grafana instance(s).
1. Create a YAML or JSON configuration file.
Example configuration files can be found below.
1. Add the file(s) to your GitOps workflow, so that they deploy alongside your Grafana instance(s).
Here is an example of a configuration file for creating contact points.
```yaml
# config file version
apiVersion: 1
# List of contact points to import or update
contactPoints:
# <int> organization ID, default = 1
- orgId: 1
# <string, required> name of the contact point
name: cp_1
receivers:
# <string, required> unique identifier for the receiver
- uid: first_uid
# <string, required> type of the receiver
type: prometheus-alertmanager
# <object, required> settings for the specific receiver type
settings:
url: http://test:9000
```
Here is an example of a configuration file for deleting contact points.
```yaml
# config file version
apiVersion: 1
# List of receivers that should be deleted
deleteContactPoints:
# <int> organization ID, default = 1
- orgId: 1
# <string, required> unique identifier for the receiver
uid: first_uid
```
#### Settings
Here are some examples of settings you can use for the different
contact point types.
##### Alertmanager
```yaml
type: prometheus-alertmanager
settings:
# <string, required>
url: http://localhost:9093
# <string>
basicAuthUser: abc
# <string>
basicAuthPassword: abc123
```
##### DingDing
```yaml
type: dingding
settings:
# <string, required>
url: https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx
# <string> options: link, actionCard
msgType: link
# <string>
message: |
{{ template "default.message" . }}
```
##### Discord
```yaml
type: discord
settings:
# <string, required>
url: https://discord/webhook
# <string>
avatar_url: https://my_avatar
# <string>
use_discord_username: Grafana
# <string>
message: |
{{ template "default.message" . }}
```
##### E-Mail
```yaml
type: email
settings:
# <string, required>
addresses: me@example.com;you@example.com
# <bool>
singleEmail: false
# <string>
message: my optional message to include
# <string>
subject: |
{{ template "default.title" . }}
```
##### Google Hangouts Chat
```yaml
type: googlechat
settings:
# <string, required>
url: https://google/webhook
# <string>
message: |
{{ template "default.message" . }}
```
##### Kafka
```yaml
type: kafka
settings:
# <string, required>
kafkaRestProxy: http://localhost:8082
# <string, required>
kafkaTopic: topic1
```
##### LINE
```yaml
type: line
settings:
# <string, required>
token: xxx
```
##### Microsoft Teams
```yaml
type: teams
settings:
# <string, required>
url: https://ms_teams_url
# <string>
title: |
{{ template "default.title" . }}
# <string>
sectiontitle: ''
# <string>
message: |
{{ template "default.message" . }}
```
##### OpsGenie
```yaml
type: opsgenie
settings:
# <string, required>
apiKey: xxx
# <string, required>
apiUrl: https://api.opsgenie.com/v2/alerts
# <string>
message: |
{{ template "default.title" . }}
# <string>
description: some descriptive description
# <bool>
autoClose: false
# <bool>
overridePriority: false
# <string> options: tags, details, both
sendTagsAs: both
```
##### PagerDuty
```yaml
type: pagerduty
settings:
# <string, required>
integrationKey: XXX
# <string> options: critical, error, warning, info
severity: critical
# <string>
class: ping failure
# <string>
component: Grafana
# <string>
group: app-stack
# <string>
summary: |
{{ template "default.message" . }}
```
##### Pushover
```yaml
type: pushover
settings:
# <string, required>
apiToken: XXX
# <string, required>
userKey: user1,user2
# <string>
device: device1,device2
# <string> options (high to low): 2,1,0,-1,-2
priority: '2'
# <string>
retry: '30'
# <string>
expire: '120'
# <string>
sound: siren
# <string>
okSound: magic
# <string>
message: |
{{ template "default.message" . }}
```
##### Slack
```yaml
type: slack
settings:
# <string, required>
recipient: alerting-dev
# <string, required>
token: xxx
# <string>
username: grafana_bot
# <string>
icon_emoji: heart
# <string>
icon_url: https://icon_url
# <string>
mentionUsers: user_1,user_2
# <string>
mentionGroups: group_1,group_2
# <string> options: here, channel
mentionChannel: here
# <string> Optionally provide a Slack incoming webhook URL for sending messages, in this case the token isn't necessary
url: https://some_webhook_url
# <string>
endpointUrl: https://custom_url/api/chat.postMessage
# <string>
title: |
{{ template "slack.default.title" . }}
text: |
{{ template "slack.default.text" . }}
```
##### Sensu Go
```yaml
type: sensugo
settings:
# <string, required>
url: http://sensu-api.local:8080
# <string, required>
apikey: xxx
# <string>
entity: default
# <string>
check: default
# <string>
handler: some_handler
# <string>
namespace: default
# <string>
message: |
{{ template "default.message" . }}
```
##### Telegram
```yaml
type: telegram
settings:
# <string, required>
bottoken: xxx
# <string, required>
chatid: some_chat_id
# <string>
message: |
{{ template "default.message" . }}
```
##### Threema Gateway
```yaml
type: threema
settings:
# <string, required>
api_secret: xxx
# <string, required>
gateway_id: A5K94S9
# <string, required>
recipient_id: A9R4KL4S
```
##### VictorOps
```yaml
type: victorops
settings:
# <string, required>
url: XXX
# <string> options: CRITICAL, WARNING
messageType: CRITICAL
```
##### Webhook
```yaml
type: webhook
settings:
# <string, required>
url: https://endpoint_url
# <string> options: POST, PUT
httpMethod: POST
# <string>
username: abc
# <string>
password: abc123
# <string>
authorization_scheme: Bearer
# <string>
authorization_credentials: abc123
# <string>
maxAlerts: '10'
```
##### WeCom
```yaml
type: wecom
settings:
# <string, required>
url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxx
# <string>
message: |
{{ template "default.message" . }}
# <string>
title: |
{{ template "default.title" . }}
```
### Provision notification policies
Create or reset notification policies in your Grafana instance(s).
1. Create a YAML or JSON configuration file.
Example configuration files can be found below.
2. Add the file(s) to your GitOps workflow, so that they deploy alongside your Grafana instance(s).
Here is an example of a configuration file for creating notification policiies.
```yaml
# config file version
apiVersion: 1
# List of notification policies
policies:
# <int> organization ID, default = 1
- orgId: 1
# <string> name of the contact point that should be used for this route
receiver: grafana-default-email
# <list> The labels by which incoming alerts are grouped together. For example,
# multiple alerts coming in for cluster=A and alertname=LatencyHigh would
# be batched into a single group.
#
# To aggregate by all possible labels use the special value '...' as
# the sole label name, for example:
# group_by: ['...']
# This effectively disables aggregation entirely, passing through all
# alerts as-is. This is unlikely to be what you want, unless you have
# a very low alert volume or your upstream notification system performs
# its own grouping.
group_by: ['...']
# <list> a list of matchers that an alert has to fulfill to match the node
matchers:
- alertname = Watchdog
- severity =~ "warning|critical"
# <list> Times when the route should be muted. These must match the name of a
# mute time interval.
# Additionally, the root node cannot have any mute times.
# When a route is muted it will not send any notifications, but
# otherwise acts normally (including ending the route-matching process
# if the `continue` option is not set)
mute_time_intervals:
- abc
# <duration> How long to initially wait to send a notification for a group
# of alerts. Allows to collect more initial alerts for the same group.
# (Usually ~0s to few minutes), default = 30s
group_wait: 30s
# <duration> How long to wait before sending a notification about new alerts that
# are added to a group of alerts for which an initial notification has
# already been sent. (Usually ~5m or more), default = 5m
group_internval: 5m
# <duration> How long to wait before sending a notification again if it has already
# been sent successfully for an alert. (Usually ~3h or more), default = 4h
repeat_interval: 4h
# <list> Zero or more child routes
# routes:
# ...
```
Here is an example of a configuration file for resetting notification policies.
```yaml
# config file version
apiVersion: 1
# List of orgIds that should be reset to the default policy
resetPolicies:
- 1
```
### Provision templates
Create or delete templates in your Grafana instance(s).
1. Create a YAML or JSON configuration file.
Example configuration files can be found below.
2. Add the file(s) to your GitOps workflow, so that they deploy alongside your Grafana instance(s).
Here is an example of a configuration file for creating templates.
```yaml
# config file version
apiVersion: 1
# List of templates to import or update
templates:
# <int> organization ID, default = 1
- orgID: 1
# <string, required> name of the template, must be unique
name: my_first_template
# <string, required> content of the the template
template: Alerting with a custom text template
```
Here is an example of a configuration file for deleting templates.
```yaml
# config file version
apiVersion: 1
# List of alert rule UIDs that should be deleted
deleteTemplates:
# <int> organization ID, default = 1
- orgId: 1
# <string, required> name of the template, must be unique
name: my_first_template
```
### Provision mute timings
Create or delete mute timings in your Grafana instance(s).
1. Create a YAML or JSON configuration file.
Example configuration files can be found below.
1. Add the file(s) to your GitOps workflow, so that they deploy alongside your Grafana instance(s).
Here is an example of a configuration file for creating mute timings.
```yaml
# config file version
apiVersion: 1
# List of mute time intervals to import or update
muteTimes:
# <int> organization ID, default = 1
- orgId: 1
# <string, required> name of the mute time interval, must be unique
name: mti_1
# <list> time intervals that should trigger the muting
# refer to https://prometheus.io/docs/alerting/latest/configuration/#time_interval-0
time_intervals:
- times:
- start_time: '06:00'
end_time: '23:59'
weekdays: ['monday:wednesday', 'saturday', 'sunday']
months: ['1:3', 'may:august', 'december']
years: ['2020:2022', '2030']
days_of_month: ['1:5', '-3:-1']
```
Here is an example of a configuration file for deleting mute timings.
```yaml
# config file version
apiVersion: 1
# List of mute time intervals that should be deleted
deleteMuteTimes:
# <int> organization ID, default = 1
- orgId: 1
# <string, required> name of the mute time interval, must be unique
name: mti_1
```
### File provisioning using Kubernetes
If you are a Kubernetes user, you can leverage file provisioning using Kubernetes configuration maps.
1. Create one or more configuration maps as follows.
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-alerting
data:
provisioning.yaml: |
templates:
- name: my_first_template
template: the content for my template
```
2. Add the file(s) to your GitOps workflow, so that they deploy alongside your Grafana instance(s).
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana
spec:
replicas: 1
selector:
matchLabels:
app: grafana
template:
metadata:
name: grafana
labels:
app: grafana
spec:
containers:
- name: grafana
image: grafana/grafana:latest
ports:
- name: grafana
containerPort: 3000
volumeMounts:
- mountPath: /etc/grafana/provisioning/alerting
name: grafana-alerting
readOnly: false
volumes:
- name: grafana-alerting
configMap:
defaultMode: 420
name: grafana-alerting
```
This eliminates the need for a persistent database to use Grafana Alerting in Kubernetes; all your provisioned resources appear after each restart or re-deployment.

View File

@ -21,7 +21,7 @@ weight: 200
Usage insights enables you to have a better understanding of how your Grafana instance is used.
> **Note:** Available in [Grafana Enterprise]({{< relref "../" >}}) and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). Grafana Cloud insights logs include additional fields with their own dashboards. Read more in the [Grafana Cloud documentation](/docs/grafana-cloud/usage-insights/).
The usage insights feature collects a number of aggregated data and stores them in the database:
@ -34,6 +34,7 @@ The aggregated data provides you access to several features:
- [Dashboard and data source insights]({{< relref "#dashboard-and-data-source-insights" >}})
- [Presence indicator]({{< relref "#presence-indicator" >}})
- [Sort dashboards by using insights data]({{< relref "#sort-dashboards-by-using-insights-data" >}})
- [Visualize usage insight data in a dashboard]({{< relref "#visualize-usage-insights-data" >}})
This feature also generates detailed logs that can be exported to Loki. Refer to [Export logs of usage insights]({{< relref "../../setup-grafana/configure-security/export-logs/" >}}).
@ -43,7 +44,7 @@ For every dashboard and data source, you can access usage information.
### Dashboard insights
> **Note:** Available in [Grafana Enterprise]({{< relref "../" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
To see dashboard usage information, click **Dashboard insights** in the top bar.
@ -58,7 +59,7 @@ Dashboard insights show the following information:
### Data source insights
> **Note:** Available in [Grafana Enterprise]({{< relref "../" >}}) version 7.3 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.3 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
Data source insights provides information about how a data source has been used in the past 30 days, such as:
@ -76,7 +77,7 @@ To find data source insights:
## Presence indicator
> **Note:** Available in [Grafana Enterprise]({{< relref "../" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
When you are signed in and looking at a dashboard, you can know who is looking at the same dashboard as you are via a presence indicator, which displays avatars of users who have recently interacted with the dashboard. The default timeframe is 10 minutes. To see the user's name, hover over the user's avatar. The avatars come from [Gravatar](https://gravatar.com) based on the user's email.
@ -94,7 +95,7 @@ recent_users_age = 10m
## Sort dashboards by using insights data
> **Note:** Available in [Grafana Enterprise]({{< relref "../" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
In the search view, you can use insights data to help you find most-used, broken, and unused dashbaords.
@ -106,3 +107,14 @@ You can sort the dashboards by:
- Views 30 days
{{< figure src="/static/img/docs/enterprise/improved-search-7-5.png" max-width="650px" class="docs-image--no-shadow" >}}
## Visualize usage insights data
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
If you set up your installation to [export logs of usage insights]({{< relref "../../setup-grafana/configure-security/export-logs/" >}}), we've created two dashboards to help you take advantage of this data.
1. [Usage Insights overview](/grafana/dashboards/13785) provides a top-level perspective of user activity.
1. [Data source details](/grafana/dashboards/13786) dashboard provides a view of data source activity and health.
You can click the previous links to download the respective dashboard JSON, then import into your Grafana installation.

View File

@ -31,7 +31,9 @@ This topic explains configuring, querying, and other options specific to the Azu
To access Azure Monitor configuration, hover your mouse over the **Configuration** (gear) icon, click **Data Sources**, and then select the Azure Monitor data source. If you haven't already, you'll need to [add the Azure Monitor data source]({{< relref "../add-a-data-source/" >}}).
You must create an app registration and service principal in Azure AD to authenticate the data source. See the [Azure documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal#get-tenant-and-app-id-values-for-signing-in) for configuration details. Alternatively, if you are hosting Grafana in Azure (e.g. App Service, or Azure Virtual Machines) you can configure the Azure Monitor data source to use Managed Identity to securely authenticate without entering credentials into Grafana. Refer to [Configuring using Managed Identity](#configuring-using-managed-identity) for more details.
You must create an app registration and service principal in Azure AD to authenticate the data source. For more information, refer to [Azure documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal#get-tenant-and-app-id-values-for-signing-in) for configuration details. The app registration you create will need to have the `Reader` role assigned on the subscription. For more information, refer to [Azure docs for role assignments](https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal?tabs=current).
Alternatively, if you host Grafana in Azure (such as in App Service or Azure Virtual Machines), you can configure the Azure Monitor data source to use Managed Identity to securely authenticate without entering credentials into Grafana. Refer to [Configuring using Managed Identity](#configuring-using-managed-identity) for details.
| Name | Description |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

View File

@ -88,7 +88,7 @@ On the left side of the query field, click **Metrics** to open the Metric Explor
### Query field
The Query field supports autocomplete for metric names, function and works mostly the same way as the standard Prometheus query editor. Press the enter key to execute a query.
The Query field supports autocomplete for metric names and functions, comparable to the standard Prometheus query editor. Press the Enter key to create a new line and Shift+Enter to execute a query.
The autocomplete menu can be triggered by pressing Ctrl+Space. The Autocomplete menu contains a new History section with a list of recently executed queries.

View File

@ -67,7 +67,7 @@ You can also paste a native emoji in the unit picker and pick it as a custom uni
#### String units
Grafana can sometime be too aggressive in parsing strings and displaying them as numbers. To make Grafana show the original string create a field override and add a unit property with the `string` unit.
Grafana can sometimes be too aggressive in parsing strings and displaying them as numbers. To configure Grafana to show the original string value, create a field override and add a unit property with the `String` unit.
### Min
@ -79,9 +79,9 @@ Lets you set the maximum value used in percentage threshold calculations. Leave
### Decimals
Number of decimals to render value with. Leave empty for Grafana to use the number of decimals provided by the data source.
Specify the number of decimals Grafana includes in the rendered value. If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. For example 1.1234 will display as 1.12 and 100.456 will display as 100.
To change this setting, type a number in the field and then click outside the field or press Enter.
To display all decimals, set the unit to `String`.
### Display name

View File

@ -10,6 +10,7 @@ weight: 10000
Here you can find detailed release notes that list everything that is included in every release as well as notices
about deprecations, breaking changes as well as changes that relate to plugin development.
- [Release notes for 9.1.6]({{< relref "release-notes-9-1-6" >}})
- [Release notes for 9.1.5]({{< relref "release-notes-9-1-5" >}})
- [Release notes for 9.1.4]({{< relref "release-notes-9-1-4" >}})
- [Release notes for 9.1.3]({{< relref "release-notes-9-1-3" >}})
@ -17,6 +18,7 @@ about deprecations, breaking changes as well as changes that relate to plugin de
- [Release notes for 9.1.1]({{< relref "release-notes-9-1-1" >}})
- [Release notes for 9.1.0]({{< relref "release-notes-9-1-0" >}})
- [Release notes for 9.1.0-beta1]({{< relref "release-notes-9-1-0-beta1" >}})
- [Release notes for 9.0.9]({{< relref "release-notes-9-0-9" >}})
- [Release notes for 9.0.8]({{< relref "release-notes-9-0-8" >}})
- [Release notes for 9.0.7]({{< relref "release-notes-9-0-7" >}})
- [Release notes for 9.0.6]({{< relref "release-notes-9-0-6" >}})
@ -29,6 +31,7 @@ about deprecations, breaking changes as well as changes that relate to plugin de
- [Release notes for 9.0.0-beta3]({{< relref "release-notes-9-0-0-beta3/" >}})
- [Release notes for 9.0.0-beta2]({{< relref "release-notes-9-0-0-beta2/" >}})
- [Release notes for 9.0.0-beta1]({{< relref "release-notes-9-0-0-beta1/" >}})
- [Release notes for 8.5.13]({{< relref "release-notes-8-5-13" >}})
- [Release notes for 8.5.11]({{< relref "release-notes-8-5-11" >}})
- [Release notes for 8.5.10]({{< relref "release-notes-8-5-10" >}})
- [Release notes for 8.5.9]({{< relref "release-notes-8-5-9" >}})

View File

@ -0,0 +1,16 @@
+++
title = "Release notes for Grafana 8.5.13"
hide_menu = true
+++
<!-- Auto generated by update changelog github action -->
# Release notes for Grafana 8.5.13
### Features and enhancements
- **Plugins:** Expose @emotion/react to plugins to prevent load failures. [#55297](https://github.com/grafana/grafana/pull/55297), [@jackw](https://github.com/jackw)
### Bug fixes
- **AuthNZ:** Security fixes for CVE-2022-35957 and CVE-2022-36062. [#55495](https://github.com/grafana/grafana/pull/55495), [@IevaVasiljeva](https://github.com/IevaVasiljeva)

View File

@ -0,0 +1,14 @@
+++
title = "Release notes for Grafana 9.0.9"
hide_menu = true
+++
<!-- Auto generated by update changelog github action -->
# Release notes for Grafana 9.0.9
### Bug fixes
- **AngularPanels:** Fixing changing angular panel options not taking having affect when coming back from panel edit. [#54834](https://github.com/grafana/grafana/pull/54834), [@grafanabot](https://github.com/grafanabot)
- **AuthNZ:** Security fixes for CVE-2022-35957 and CVE-2022-36062. [#55498](https://github.com/grafana/grafana/pull/55498), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
- **FIX:** RBAC prevents deleting empty snapshots (#54385). [#54509](https://github.com/grafana/grafana/pull/54509), [@gamab](https://github.com/gamab)

View File

@ -0,0 +1,29 @@
+++
title = "Release notes for Grafana 9.1.6"
hide_menu = true
+++
<!-- Auto generated by update changelog github action -->
# Release notes for Grafana 9.1.6
### Features and enhancements
- **Auth:** Trigger auth token cleanup job. (Enterprise)
- **DataSource:** Adding possibility to hide queries from the inspector. [#54892](https://github.com/grafana/grafana/pull/54892), [@mckn](https://github.com/mckn)
- **Inspect:** Hide Actions tab when it is empty. [#55272](https://github.com/grafana/grafana/pull/55272), [@ryantxu](https://github.com/ryantxu)
- **PanelMenu:** Remove hide legend action as it was showing on all panel types. [#54876](https://github.com/grafana/grafana/pull/54876), [@torkelo](https://github.com/torkelo)
- **Provisioning Contact points:** Support disableResolveMessage via YAML. [#54122](https://github.com/grafana/grafana/pull/54122), [@mmusenbr](https://github.com/mmusenbr)
- **PublicDashboards:** Support subpaths when generating pubdash url. [#55204](https://github.com/grafana/grafana/pull/55204), [@owensmallwood](https://github.com/owensmallwood)
### Bug fixes
- **Alerting:** Fix legacy migration crash when rule name is too long. [#55053](https://github.com/grafana/grafana/pull/55053), [@alexweav](https://github.com/alexweav)
- **Alerting:** Fix send resolved notifications. [#54793](https://github.com/grafana/grafana/pull/54793), [@grobinson-grafana](https://github.com/grobinson-grafana)
- **Azure Monitor:** Fix migration issue with MetricDefinitionsQuery template variable query types. [#55262](https://github.com/grafana/grafana/pull/55262), [@yaelleC](https://github.com/yaelleC)
- **Browse:** Hide dashboard actions if user does not have enough permission. [#55218](https://github.com/grafana/grafana/pull/55218), [@lpskdl](https://github.com/lpskdl)
- **ElasticSearch:** Fix dispatching queries at a wrong time. [#55225](https://github.com/grafana/grafana/pull/55225), [@svennergr](https://github.com/svennergr)
- **Panel:** Disable legends when showLegend is false prior to schema v37. [#55126](https://github.com/grafana/grafana/pull/55126), [@ivanortegaalba](https://github.com/ivanortegaalba)
- **Prometheus:** Fix metadata requests for browser access mode. [#55403](https://github.com/grafana/grafana/pull/55403), [@itsmylife](https://github.com/itsmylife)
- **Search:** Avoid requesting all dashboards when in Folder View. [#55169](https://github.com/grafana/grafana/pull/55169), [@JoaoSilvaGrafana](https://github.com/JoaoSilvaGrafana)
- **TablePanel/StatPanel:** Fix values not being visible when background transparent. [#55092](https://github.com/grafana/grafana/pull/55092), [@mdvictor](https://github.com/mdvictor)

View File

@ -474,6 +474,10 @@ Set to false disables checking for new versions of installed plugins from https:
If you want to track Grafana usage via Google analytics specify _your_ Universal
Analytics ID here. By default this feature is disabled.
### google_analytics_4_id
If you want to track Grafana usage via Google Analytics 4 specify _your_ GA4 ID here. By default this feature is disabled.
### google_tag_manager_id
Google Tag Manager ID, only enabled if you enter an ID here.
@ -1571,6 +1575,18 @@ Setting this to `true` turns off shared RPC spans. Leaving this available is the
<hr>
## [tracing.opentelemetry]
Configure general parameters shared between OpenTelemetry providers.
### custom_attributes
Comma-separated list of attributes to include in all new spans, such as `key1:value1,key2:value2`.
Can be set with the environment variable `OTEL_RESOURCE_ATTRIBUTES` (use `=` instead of `:` with the environment variable).
<hr>
## [tracing.opentelemetry.jaeger]
Configure Grafana's Jaeger client for distributed tracing.

View File

@ -9,7 +9,7 @@ weight: 100
# Configure Grafana Enterprise
This page describes Grafana Enterprise-specific configuration options that you can specify in a `.ini` configuration file or using environment variables. Refer to [Configuration]({{< relref "/" >}}) for more information about available configuration options.
This page describes Grafana Enterprise-specific configuration options that you can specify in a `.ini` configuration file or using environment variables. Refer to [Configuration]({{< relref "../" >}}) for more information about available configuration options.
## [enterprise]

View File

@ -202,3 +202,26 @@ the correct teams.
You can reference Azure AD groups by group object ID, like `8bab1c86-8fba-33e5-2089-1d1c80ec267d`.
To learn more, refer to the [Team Sync]({{< relref "../configure-team-sync/" >}}) documentation.
## Common troubleshooting
Here are some common issues and particulars you can run into when
configuring Azure AD authentication in Grafana.
### Users with over 200 Group assignments
> Supported in Grafana v8.5 and later versions.
To ensure that the token size doesn't exceed HTTP header size limits,
Azure AD limits the number of object IDs that it includes in the groups claim.
If a user is member of more groups than the
overage limit (200), then
Azure AD does not emit the groups claim in the token and emits a group overage claim instead.
> More information in [Groups overage claim](https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens#groups-overage-claim)
If Grafana receives a token with a group overage claim instead of a groups claim,
Grafana attempts to retrieve the user's group membership by calling the included endpoint.
> Note: The token must include the `GroupMember.Read.All` permission for group overage claim calls to succeed.
> Admin consent may be required for this permission.

View File

@ -383,7 +383,9 @@ To troubleshoot and get more log information, enable SAML debug logging in the c
filters = saml.auth:debug
```
## Known issues
## Troubleshooting
Following are common issues found in configuring SAML authentication in Grafana and how to resolve them.
### SAML authentication fails with error:
@ -438,3 +440,31 @@ csrf_trusted_origins = https://grafana.example.com
csrf_additional_headers = X-Forwarded-Host
...
```
### SAML login attempts fail with request response "login session has expired"
Accessing the Grafana login page from a URL that is not the root URL of the
Grafana server can cause the instance to return the following error: "login session has expired".
If you are accessing grafana through a proxy server, ensure that cookies are correctly
rewritten to the root URL of Grafana.
Cookies must be set on the same url as the `root_url` of Grafana. This is normally the reverse proxy's domain/address.
Review the cookie settings in your proxy server configuration to ensure that cookies are
not being discarded
Review the following settings in your grafana config:
```ini
[security]
cookie_samesite = none
```
This setting should be set to none to allow grafana session cookies to work correctly with redirects.
```ini
[security]
cookie_secure = true
```
Ensure cookie_secure is set to true to ensure that cookies are only sent over HTTPS.

View File

@ -13,9 +13,9 @@ weight: 900
# Export logs of usage insights
> **Note:** Available in [Grafana Enterprise]({{< relref "../" >}}) version 7.4 and later, and [Grafana Cloud Pro and Advanced]({{< ref "/docs/grafana-cloud" >}}).
> **Note:** Available in [Grafana Enterprise]({{< relref "../../grafana-enterprise" >}}) version 7.4 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
By exporting usage logs to Loki, you can directly query them and create dashboards of the information that matters to you most, such as dashboard errors, most active organizations, or your top-10 most-used queries.
By exporting usage logs to Loki, you can directly query them and create dashboards of the information that matters to you most, such as dashboard errors, most active organizations, or your top-10 most-used queries. This configuration is done for you in Grafana Cloud, with provisioned dashboards. Read about them in the [Grafana Cloud documentation](/docs/grafana-cloud/usage-insights/).
## Usage insights logs

View File

@ -28,8 +28,11 @@ e2e.scenario({
e2e.pages.ShareDashboardModal.PublicDashboard.LimitedDSCheckbox().should('be.enabled').click({ force: true });
e2e.pages.ShareDashboardModal.PublicDashboard.CostIncreaseCheckbox().should('be.enabled').click({ force: true });
e2e.pages.ShareDashboardModal.PublicDashboard.SaveConfigButton().should('be.disabled');
// Switch on enabling toggle
e2e.pages.ShareDashboardModal.PublicDashboard.EnableSwitch().should('be.enabled').click({ force: true });
e2e.pages.ShareDashboardModal.PublicDashboard.SaveConfigButton().should('be.enabled');
// Save configuration
e2e().intercept('POST', '/api/dashboards/uid/ZqZnVvFZz/public-config').as('save');
@ -69,6 +72,8 @@ e2e.scenario({
e2e.pages.ShareDashboardModal.PublicDashboard.Tab().click();
e2e().wait('@query-public-config');
e2e.pages.ShareDashboardModal.PublicDashboard.SaveConfigButton().should('be.enabled');
// Make a request to public dashboards api endpoint without authentication
e2e.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput()
.invoke('val')

21
go.mod
View File

@ -1,6 +1,6 @@
module github.com/grafana/grafana
go 1.17
go 1.18
// Override xorm's outdated go-mssqldb dependency, since we can't upgrade to current xorm (due to breaking changes).
// We need a more current go-mssqldb so we get rid of a version of apache/thrift with vulnerabilities.
@ -13,6 +13,9 @@ replace k8s.io/client-go => k8s.io/client-go v0.22.1
replace github.com/russellhaering/goxmldsig@v1.1.0 => github.com/russellhaering/goxmldsig v1.1.1
// Avoid using v2.0.0+incompatible Redigo used by dependencies as the latest maintained branch of Redigo is v1.
replace github.com/gomodule/redigo => github.com/gomodule/redigo v1.8.9
require (
cloud.google.com/go/storage v1.21.0
cuelang.org/go v0.4.3
@ -25,7 +28,7 @@ require (
github.com/beevik/etree v1.1.0
github.com/benbjohnson/clock v1.1.0
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
github.com/centrifugal/centrifuge v0.19.0
github.com/centrifugal/centrifuge v0.25.0
github.com/cortexproject/cortex v1.10.1-0.20211014125347-85c378182d0d
github.com/crewjam/saml v0.4.8
github.com/davecgh/go-spew v1.1.1
@ -42,14 +45,14 @@ require (
github.com/go-sql-driver/mysql v1.6.0
github.com/go-stack/stack v1.8.1
github.com/gobwas/glob v0.2.3
github.com/gofrs/uuid v4.2.0+incompatible
github.com/gofrs/uuid v4.2.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2
github.com/golang/mock v1.6.0
github.com/golang/snappy v0.0.4
github.com/google/go-cmp v0.5.8
github.com/google/uuid v1.3.0
github.com/google/wire v0.5.0
github.com/gorilla/websocket v1.4.2
github.com/gorilla/websocket v1.5.0
github.com/gosimple/slug v1.12.0
github.com/grafana/cuetsy v0.0.4-0.20220714174355-ebd987fdab27
github.com/grafana/grafana-aws-sdk v0.10.8
@ -132,7 +135,7 @@ require (
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/FZambia/eagle v0.0.1 // indirect
github.com/FZambia/eagle v0.0.2 // indirect
github.com/FZambia/sentinel v1.1.0 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
@ -142,7 +145,7 @@ require (
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
github.com/centrifugal/protocol v0.7.6 // indirect
github.com/centrifugal/protocol v0.8.10 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
@ -185,7 +188,7 @@ require (
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 // indirect
github.com/igm/sockjs-go/v3 v3.0.1 // indirect
github.com/igm/sockjs-go/v3 v3.0.2 // indirect
github.com/jessevdk/go-flags v1.5.0 // indirect
github.com/jonboulle/clockwork v0.3.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
@ -213,7 +216,7 @@ require (
github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b // indirect
github.com/rs/cors v1.8.2 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/segmentio/encoding v0.3.2
github.com/segmentio/encoding v0.3.5
github.com/sercand/kuberesolver v2.4.0+incompatible // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
@ -275,6 +278,7 @@ require (
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/segmentio/asm v1.1.4 // indirect
go.starlark.net v0.0.0-20201118183435-e55f603d8c79 // indirect
)
@ -323,7 +327,6 @@ require (
github.com/mschoch/smat v0.2.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
github.com/pierrec/lz4/v4 v4.1.8 // indirect
github.com/segmentio/asm v1.1.1 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
github.com/wk8/go-ordered-map v1.0.0
github.com/xanzy/ssh-agent v0.3.0 // indirect

37
go.sum
View File

@ -212,8 +212,8 @@ github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/FZambia/eagle v0.0.1 h1:FN1yTkPihMb5nE8SrlRjoCf7T9H9bTKJFQOm6ach2YU=
github.com/FZambia/eagle v0.0.1/go.mod h1:xq6u/JeNZ5/8mrAQ76MMhzNTodASh9FavQlCgg4j48w=
github.com/FZambia/eagle v0.0.2 h1:35qHDuXSQevZ4w9A51k4wU7OE/tPHTEWXoywA93hvkY=
github.com/FZambia/eagle v0.0.2/go.mod h1:xq6u/JeNZ5/8mrAQ76MMhzNTodASh9FavQlCgg4j48w=
github.com/FZambia/sentinel v1.1.0 h1:qrCBfxc8SvJihYNjBWgwUI93ZCvFe/PJIPTHKmlp8a8=
github.com/FZambia/sentinel v1.1.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
github.com/GoogleCloudPlatform/cloudsql-proxy v1.29.0/go.mod h1:spvB9eLJH9dutlbPSRmHvSXXHOwGRyeXh1jVdquA2G8=
@ -485,10 +485,10 @@ github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuD
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/centrifugal/centrifuge v0.19.0 h1:YHws0dRpgsBiI73tRl1wwaB13gzuaI1AM4IFcQQQqcw=
github.com/centrifugal/centrifuge v0.19.0/go.mod h1:O2elf8q3Qkie3z97wkqVqxB52pnOpPsfFUa7L88Lpy0=
github.com/centrifugal/protocol v0.7.6 h1:AfMwTZfwnFwZslIzQL4QtRnWSVO32RPSuk4iNS/V9tg=
github.com/centrifugal/protocol v0.7.6/go.mod h1:cJo0/BuXglhPfg0fgSgTXvBZ7y+9rdg4+nPbIDOVmlA=
github.com/centrifugal/centrifuge v0.25.0 h1:QivFZRPWcN8w3I/gZ8Zs9rMe/KGZoKIS9Kgo1/bY4JE=
github.com/centrifugal/centrifuge v0.25.0/go.mod h1:bFcSFalnROq/wcFeRiTG+wIbHsxEMW66QUnq95RY1d0=
github.com/centrifugal/protocol v0.8.10 h1:eezzBIU/4pWyl7a+NUnANYojJBASqbkPZcQh9b8YQRI=
github.com/centrifugal/protocol v0.8.10/go.mod h1:dlHBjKakr0r+f1pkfwSMfZ+cnpvidN7pQe1ZrsKfhtE=
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/xxhash v0.0.0-20181017004759-096ff4a8a059/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@ -1227,10 +1227,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219 h1:utua3L2IbQJmauC5IXdEA547bcoU5dozgQAfc8Onsg4=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -1363,8 +1361,9 @@ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gosimple/slug v1.12.0 h1:xzuhj7G7cGtd34NXnW/yF0l+AGNfWqwgh/IXgFy7dnc=
github.com/gosimple/slug v1.12.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
@ -1529,8 +1528,8 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK
github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/igm/sockjs-go/v3 v3.0.1 h1:rmgEkeKqBHCFf7uIAipYrYSX8x9LBB2nOxAac2sooak=
github.com/igm/sockjs-go/v3 v3.0.1/go.mod h1:UqchsOjeagIBFHvd+RZpLaVRbCwGilEC08EDHsD1jYE=
github.com/igm/sockjs-go/v3 v3.0.2 h1:2m0k53w0DBiGozeQUIEPR6snZFmpFpYvVsGnfLPNXbE=
github.com/igm/sockjs-go/v3 v3.0.2/go.mod h1:UqchsOjeagIBFHvd+RZpLaVRbCwGilEC08EDHsD1jYE=
github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
@ -1701,7 +1700,6 @@ github.com/klauspost/compress v1.15.2/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHU
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/knadh/koanf v1.2.0/go.mod h1:xpPTwMhsA/aaQLAilyCCqfpEiY1gpa160AiCuWHJUjY=
@ -2287,11 +2285,11 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/securego/gosec v0.0.0-20200203094520-d13bb6d2420c/go.mod h1:gp0gaHj0WlmPh9BdsTmo1aq6C27yIPWdxCKGFGdVKBE=
github.com/segmentio/asm v1.1.0/go.mod h1:4EUJGaKsB8ImLUwOGORVsNd9vTRDeh44JGsY4aKp5I4=
github.com/segmentio/asm v1.1.1 h1:WzUpP9BRnRgoP+v8qywthpSe9U5KLq1uDGdJBEcSeQo=
github.com/segmentio/asm v1.1.1/go.mod h1:VCkA6aQH8usgIAUp4QMLQsAeaMl6iW7+VMLrW9Vkv0Y=
github.com/segmentio/encoding v0.3.2 h1:gkXXteOfNaPPlrXTEf/e5tWvaQGVJWnvT3LqMzUeH7U=
github.com/segmentio/encoding v0.3.2/go.mod h1:waft2p6XI4z2pk07M0YzZV4wEiqaRvsBSyWNHxVx4gU=
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
github.com/segmentio/asm v1.1.4 h1:Q/FKBtrgnmDc0YMrurLROqG9mXE6Ndn276EtDnoWtMM=
github.com/segmentio/asm v1.1.4/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
github.com/segmentio/encoding v0.3.5 h1:UZEiaZ55nlXGDL92scoVuw00RmiRCazIEmvPSbSvt8Y=
github.com/segmentio/encoding v0.3.5/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM=
github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e/go.mod h1:tm/wZFQ8e24NYaBGIlnO2WGCAi67re4HHuOm0sftE/M=
github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo=
@ -3097,6 +3095,7 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@ -1,4 +1,4 @@
{
"stable": "9.1.5",
"testing": "9.1.5"
"stable": "9.1.6",
"testing": "9.1.6"
}

View File

@ -81,7 +81,7 @@
"@babel/plugin-proposal-optional-chaining": "7.18.9",
"@babel/plugin-syntax-dynamic-import": "7.8.3",
"@babel/plugin-transform-react-constant-elements": "7.18.9",
"@babel/plugin-transform-runtime": "7.18.9",
"@babel/plugin-transform-runtime": "7.19.1",
"@babel/plugin-transform-typescript": "7.19.0",
"@babel/preset-env": "7.19.0",
"@babel/preset-react": "7.18.6",
@ -124,6 +124,7 @@
"@types/eslint": "8.4.5",
"@types/file-saver": "2.0.5",
"@types/google.analytics": "^0.0.42",
"@types/gtag.js": "^0.0.11",
"@types/history": "4.7.11",
"@types/hoist-non-react-statics": "3.3.1",
"@types/jest": "28.1.6",
@ -145,7 +146,6 @@
"@types/react-dom": "17.0.14",
"@types/react-grid-layout": "1.3.2",
"@types/react-highlight-words": "0.16.4",
"@types/react-loadable": "5.5.6",
"@types/react-redux": "7.1.24",
"@types/react-router-dom": "5.3.3",
"@types/react-table": "7.7.12",
@ -207,6 +207,7 @@
"lerna": "5.2.0",
"lint-staged": "13.0.3",
"mini-css-extract-plugin": "2.6.1",
"msw": "^0.47.3",
"mutationobserver-shim": "0.3.7",
"ngtemplate-loader": "2.1.0",
"node-notifier": "10.0.1",
@ -222,6 +223,7 @@
"react-test-renderer": "17.0.2",
"redux-mock-store": "1.5.4",
"rimraf": "3.0.2",
"rudder-sdk-js": "^2.13.0",
"sass": "1.54.0",
"sass-loader": "13.0.2",
"sinon": "14.0.0",
@ -244,7 +246,7 @@
"webpack-merge": "5.8.0"
},
"dependencies": {
"@daybrush/utils": "^1.9.1",
"@daybrush/utils": "1.10.0",
"@emotion/css": "11.9.0",
"@emotion/react": "11.9.3",
"@grafana/agent-core": "0.4.0",
@ -263,12 +265,12 @@
"@kusto/monaco-kusto": "5.1.8",
"@lezer/common": "1.0.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "1.0.0",
"@lezer/lr": "1.2.3",
"@lingui/core": "3.14.0",
"@lingui/react": "3.14.0",
"@opentelemetry/api": "1.2.0",
"@opentelemetry/exporter-collector": "0.25.0",
"@opentelemetry/semantic-conventions": "1.6.0",
"@opentelemetry/semantic-conventions": "1.7.0",
"@popperjs/core": "2.11.5",
"@prometheus-io/lezer-promql": "^0.37.0-rc.1",
"@react-aria/button": "3.6.1",
@ -299,11 +301,12 @@
"angular-bindonce": "0.3.1",
"angular-route": "1.8.3",
"angular-sanitize": "1.8.3",
"ansicolor": "1.1.100",
"app": "link:./public/app",
"baron": "3.0.3",
"brace": "0.11.1",
"calculate-size": "1.1.1",
"centrifuge": "2.8.5",
"centrifuge": "3.0.1",
"classnames": "2.3.1",
"comlink": "4.3.1",
"common-tags": "1.8.2",
@ -362,7 +365,6 @@
"react-highlight-words": "0.18.0",
"react-hook-form": "7.5.3",
"react-inlinesvg": "3.0.0",
"react-loadable": "5.5.0",
"react-moveable": "0.38.4",
"react-popper": "2.3.0",
"react-popper-tooltip": "4.4.2",
@ -405,7 +407,7 @@
"resolutions": {
"underscore": "1.13.4",
"@types/slate": "0.47.9",
"@rushstack/node-core-library": "3.51.1",
"@rushstack/node-core-library": "3.52.0",
"@rushstack/rig-package": "0.3.13",
"@rushstack/ts-command-line": "4.12.1",
"@storybook/react/webpack": "5.74.0",

View File

@ -210,6 +210,7 @@ export interface GrafanaConfig {
feedbackLinksEnabled: boolean;
secretsManagerPluginEnabled: boolean;
googleAnalyticsId: string | undefined;
googleAnalytics4Id: string | undefined;
rudderstackWriteKey: string | undefined;
rudderstackDataPlaneUrl: string | undefined;
rudderstackSdkUrl: string | undefined;
@ -219,4 +220,5 @@ export interface GrafanaConfig {
export interface AuthSettings {
OAuthSkipOrgRoleUpdateSync?: boolean;
SAMLSkipOrgRoleSync?: boolean;
DisableSyncLock?: boolean;
}

View File

@ -21,9 +21,14 @@ export interface ExploreTracePanelState {
spanId?: string;
}
export interface SplitOpenOptions<T> {
datasourceUid: string;
query: T;
range?: TimeRange;
panelsState?: ExplorePanelsState;
}
/**
* SplitOpen type is used in Explore and related components.
*/
export type SplitOpen = <T extends DataQuery = any>(
options?: { datasourceUid: string; query: T; range?: TimeRange; panelsState?: ExplorePanelsState } | undefined
) => void;
export type SplitOpen = <T extends DataQuery = any>(options?: SplitOpenOptions<T> | undefined) => void;

View File

@ -46,6 +46,7 @@ export interface FeatureToggles {
exploreMixedDatasource?: boolean;
tracing?: boolean;
commandPalette?: boolean;
correlations?: boolean;
cloudWatchDynamicLabels?: boolean;
datasourceQueryMultiStatus?: boolean;
traceToMetrics?: boolean;

View File

@ -20,9 +20,9 @@ export enum LiveChannelScope {
* @alpha
*/
export enum LiveChannelType {
DataStream = 'stream', // each message contains a batch of rows that will be appened to previous values
DataStream = 'stream', // each message contains a batch of rows that will be appended to previous values
DataFrame = 'frame', // each message is an entire data frame and should *replace* previous content
JSON = 'json', // arbitray json message
JSON = 'json', // arbitrary json message
}
export enum LiveChannelConnectionState {
@ -30,11 +30,13 @@ export enum LiveChannelConnectionState {
Pending = 'pending',
/** Connected to the channel */
Connected = 'connected',
/** Connecting to a channel */
Connecting = 'connecting',
/** Disconnected from the channel. The channel will reconnect when possible */
Disconnected = 'disconnected',
/** Was at some point connected, and will not try to reconnect */
Shutdown = 'shutdown',
/** Channel configuraiton was invalid and will not connect */
/** Channel configuration was invalid and will not connect */
Invalid = 'invalid',
}
@ -139,7 +141,7 @@ export interface LiveChannelAddress {
path: string;
/**
* Additional metadata passed to a channel. The backend will propigate this JSON object to
* Additional metadata passed to a channel. The backend will propagate this JSON object to
* each OnSubscribe and RunStream calls. This value should be constant across multiple requests
* to the same channel path
*/

View File

@ -1,5 +1,6 @@
import { ComponentType } from 'react';
import { LinkTarget } from './dataLink';
import { IconName } from './icon';
export interface NavLinkDTO {
@ -11,7 +12,7 @@ export interface NavLinkDTO {
icon?: IconName;
img?: string;
url?: string;
target?: string;
target?: LinkTarget;
sortWeight?: number;
divider?: boolean;
hideFromMenu?: boolean;

View File

@ -69,6 +69,7 @@ export interface PluginMeta<T extends KeyValue = {}> {
// Filled in by the backend
jsonData?: T;
secureJsonData?: KeyValue;
secureJsonFields?: KeyValue<boolean>;
enabled?: boolean;
defaultNavUrl?: string;
hasUpdate?: boolean;

View File

@ -1,4 +1,5 @@
import { MutableDataFrame } from '../dataframe/MutableDataFrame';
import { Labels } from '../types/data';
import { LogLevel, LogsModel, LogRowModel, LogsSortOrder } from '../types/logs';
import {
@ -66,11 +67,11 @@ describe('calculateLogsLabelStats()', () => {
entry: 'foo 1',
labels: {
foo: 'bar',
},
} as Labels,
},
];
] as LogRowModel[];
expect(calculateLogsLabelStats(rows as any, 'baz')).toEqual([]);
expect(calculateLogsLabelStats(rows, 'baz')).toEqual([]);
});
test('should return stats for found labels', () => {
@ -79,23 +80,23 @@ describe('calculateLogsLabelStats()', () => {
entry: 'foo 1',
labels: {
foo: 'bar',
},
} as Labels,
},
{
entry: 'foo 0',
labels: {
foo: 'xxx',
},
} as Labels,
},
{
entry: 'foo 2',
labels: {
foo: 'bar',
},
} as Labels,
},
];
] as LogRowModel[];
expect(calculateLogsLabelStats(rows as any, 'foo')).toMatchObject([
expect(calculateLogsLabelStats(rows, 'foo')).toMatchObject([
{
value: 'bar',
count: 2,
@ -215,9 +216,9 @@ describe('calculateFieldStats()', () => {
{
entry: 'foo=bar',
},
];
] as LogRowModel[];
expect(calculateFieldStats(rows as any, /baz=(.*)/)).toEqual([]);
expect(calculateFieldStats(rows, /baz=(.*)/)).toEqual([]);
});
test('should return stats for found field', () => {
@ -234,9 +235,9 @@ describe('calculateFieldStats()', () => {
{
entry: 't=2018-12-05T07:44:59+0000 foo=503',
},
];
] as LogRowModel[];
expect(calculateFieldStats(rows as any, /foo=("[^"]*"|\S+)/)).toMatchObject([
expect(calculateFieldStats(rows, /foo=("[^"]*"|\S+)/)).toMatchObject([
{
value: '"42 + 1"',
count: 2,
@ -363,8 +364,8 @@ describe('checkLogsError()', () => {
labels: {
__error__: 'Error Message',
foo: 'boo',
},
} as any as LogRowModel;
} as Labels,
} as LogRowModel;
test('should return correct error if error is present', () => {
expect(checkLogsError(log)).toStrictEqual({ hasError: true, errorMessage: 'Error Message' });
});

View File

@ -134,6 +134,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
enabled: true,
};
googleAnalyticsId: undefined;
googleAnalytics4Id: undefined;
rudderstackWriteKey: undefined;
rudderstackDataPlaneUrl: undefined;
rudderstackSdkUrl: undefined;

View File

@ -1,6 +1,6 @@
import React, { PureComponent } from 'react';
import { DisplayValue, DisplayValueAlignmentFactors, FieldSparkline } from '@grafana/data';
import { DisplayValue, DisplayValueAlignmentFactors, FieldSparkline, VizOrientation } from '@grafana/data';
import { VizTextDisplayOptions } from '@grafana/schema';
import { Themeable2 } from '../../types';
@ -63,6 +63,8 @@ export interface Props extends Themeable2 {
textMode?: BigValueTextMode;
/** If true disables the tooltip */
hasLinks?: boolean;
/** The orientation of the parent container */
parentOrientation?: VizOrientation;
/**
* If part of a series of stat panes, this is the total number.

View File

@ -1,10 +1,10 @@
import React, { CSSProperties } from 'react';
import tinycolor from 'tinycolor2';
import { formattedValueToString, DisplayValue, FieldConfig, FieldType } from '@grafana/data';
import { formattedValueToString, DisplayValue, FieldConfig, FieldType, VizOrientation } from '@grafana/data';
import { GraphDrawStyle, GraphFieldConfig } from '@grafana/schema';
import { getTextColorForBackground } from '../../utils';
import { getTextColorForAlphaBackground } from '../../utils';
import { calculateFontSize } from '../../utils/measureText';
import { Sparkline } from '../Sparkline/Sparkline';
@ -62,8 +62,12 @@ export abstract class BigValueLayout {
lineHeight: LINE_HEIGHT,
};
if (this.props.parentOrientation === VizOrientation.Horizontal && this.justifyCenter) {
styles.paddingRight = '0.75ch';
}
if (this.props.colorMode === BigValueColorMode.Background) {
styles.color = getTextColorForBackground(this.valueColor);
styles.color = getTextColorForAlphaBackground(this.valueColor, this.props.theme.isDark);
}
return styles;
@ -87,7 +91,7 @@ export abstract class BigValueLayout {
styles.color = this.valueColor;
break;
case BigValueColorMode.Background:
styles.color = getTextColorForBackground(this.valueColor);
styles.color = getTextColorForAlphaBackground(this.valueColor, this.props.theme.isDark);
break;
case BigValueColorMode.None:
styles.color = this.props.theme.colors.text.primary;

View File

@ -97,7 +97,7 @@ const Heading = ({ children, className, 'aria-label': ariaLabel }: ChildProps &
{children}
</a>
) : onClick ? (
<button onClick={onClick} className={styles.linkHack} aria-label={ariaLabel}>
<button onClick={onClick} className={styles.linkHack} aria-label={ariaLabel} type="button">
{children}
</button>
) : (

View File

@ -1,6 +1,6 @@
import { css } from '@emotion/css';
import { FocusScope } from '@react-aria/focus';
import React, { useState } from 'react';
import React, { useRef, useState } from 'react';
import { usePopperTooltip } from 'react-popper-tooltip';
import { CSSTransition } from 'react-transition-group';
@ -16,6 +16,7 @@ export interface Props {
export const Dropdown = React.memo(({ children, overlay, placement }: Props) => {
const [show, setShow] = useState(false);
const transitionRef = useRef(null);
const { getArrowProps, getTooltipProps, setTooltipRef, setTriggerRef, visible } = usePopperTooltip({
visible: show,
@ -46,12 +47,13 @@ export const Dropdown = React.memo(({ children, overlay, placement }: Props) =>
<div ref={setTooltipRef} {...getTooltipProps()} onClick={onOverlayClicked}>
<div {...getArrowProps({ className: 'tooltip-arrow' })} />
<CSSTransition
nodeRef={transitionRef}
appear={true}
in={true}
timeout={{ appear: animationDuration, exit: 0, enter: 0 }}
classNames={animationStyles}
>
{ReactUtils.renderOrCallToRender(overlay)}
<div ref={transitionRef}>{ReactUtils.renderOrCallToRender(overlay)}</div>
</CSSTransition>
</div>
</FocusScope>

View File

@ -9,10 +9,11 @@ export interface Props extends Omit<HTMLProps<HTMLInputElement>, 'onChange'> {
value: string | undefined;
width?: number;
onChange: (value: string) => void;
escapeRegex?: boolean;
}
export const FilterInput = React.forwardRef<HTMLInputElement, Props>(
({ value, width, onChange, ...restProps }, ref) => {
({ value, width, onChange, escapeRegex = true, ...restProps }, ref) => {
const innerRef = React.useRef<HTMLInputElement>(null);
const combinedRef = useCombinedRefs(ref, innerRef) as React.Ref<HTMLInputElement>;
@ -38,8 +39,10 @@ export const FilterInput = React.forwardRef<HTMLInputElement, Props>(
suffix={suffix}
width={width}
type="text"
value={value ? unEscapeStringFromRegex(value) : ''}
onChange={(event) => onChange(escapeStringForRegex(event.currentTarget.value))}
value={escapeRegex ? unEscapeStringFromRegex(value ?? '') : value}
onChange={(event) =>
onChange(escapeRegex ? escapeStringForRegex(event.currentTarget.value) : event.currentTarget.value)
}
{...restProps}
ref={combinedRef}
/>

View File

@ -22,8 +22,7 @@ import { LogDetailsRow } from './LogDetailsRow';
import { getLogRowStyles } from './getLogRowStyles';
import { getAllFields } from './logParser';
//Components
/** @deprecated will be removed in the next major version */
export interface Props extends Themeable2 {
row: LogRowModel;
showDuplicates: boolean;
@ -176,5 +175,6 @@ class UnThemedLogDetails extends PureComponent<Props> {
}
}
/** @deprecated will be removed in the next major version */
export const LogDetails = withTheme2(UnThemedLogDetails);
LogDetails.displayName = 'LogDetails';

View File

@ -12,8 +12,7 @@ import { IconButton } from '../IconButton/IconButton';
import { LogLabelStats } from './LogLabelStats';
import { getLogRowStyles } from './getLogRowStyles';
//Components
/** @deprecated will be removed in the next major version */
export interface Props extends Themeable2 {
parsedValue: string;
parsedKey: string;
@ -220,5 +219,6 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
}
}
/** @deprecated will be removed in the next major version */
export const LogDetailsRow = withTheme2(UnThemedLogDetailsRow);
LogDetailsRow.displayName = 'LogDetailsRow';

View File

@ -96,5 +96,6 @@ class UnThemedLogLabelStats extends PureComponent<Props> {
}
}
/** @deprecated will be removed in the next major version */
export const LogLabelStats = withTheme2(UnThemedLogLabelStats);
LogLabelStats.displayName = 'LogLabelStats';

View File

@ -51,6 +51,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
`,
});
/** @deprecated will be removed in the next major version */
export interface Props {
active?: boolean;
count: number;
@ -58,6 +59,7 @@ export interface Props {
value?: string;
}
/** @deprecated will be removed in the next major version */
export const LogLabelStatsRow: FunctionComponent<Props> = ({ active, count, proportion, value }) => {
const style = useStyles2(getStyles);
const percent = `${Math.round(proportion * 100)}%`;

View File

@ -12,6 +12,7 @@ interface Props {
labels: Labels;
}
/** @deprecated will be removed in the next major version */
export const LogLabels: FunctionComponent<Props> = ({ labels }) => {
const styles = useStyles2(getStyles);
const displayLabels = Object.keys(labels).filter((label) => !label.startsWith('_') && !HIDDEN_LABELS.includes(label));

View File

@ -49,6 +49,7 @@ interface State {
prevValue: string;
}
/** @deprecated will be removed in the next major version */
export class UnThemedLogMessageAnsi extends PureComponent<Props, State> {
state: State = {
chunks: [],
@ -101,5 +102,6 @@ export class UnThemedLogMessageAnsi extends PureComponent<Props, State> {
}
}
/** @deprecated will be removed in the next major version */
export const LogMessageAnsi = withTheme2(UnThemedLogMessageAnsi);
LogMessageAnsi.displayName = 'LogMessageAnsi';

View File

@ -256,5 +256,6 @@ class UnThemedLogRow extends PureComponent<Props, State> {
}
}
/** @deprecated will be removed in the next major version */
export const LogRow = withTheme2(UnThemedLogRow);
LogRow.displayName = 'LogRow';

View File

@ -123,6 +123,7 @@ const LogRowContextGroupHeader: React.FunctionComponent<LogRowContextGroupHeader
);
};
/** @deprecated will be removed in the next major version */
export const LogRowContextGroup: React.FunctionComponent<LogRowContextGroupProps> = ({
row,
rows,
@ -184,6 +185,7 @@ export const LogRowContextGroup: React.FunctionComponent<LogRowContextGroupProps
);
};
/** @deprecated will be removed in the next major version */
export const LogRowContext: React.FunctionComponent<LogRowContextProps> = ({
row,
context,

View File

@ -11,20 +11,25 @@ import {
DataQueryError,
} from '@grafana/data';
/** @deprecated will be removed in the next major version */
export interface RowContextOptions {
direction?: 'BACKWARD' | 'FORWARD';
limit?: number;
}
/** @deprecated will be removed in the next major version */
export interface LogRowContextRows {
before?: string[];
after?: string[];
}
/** @deprecated will be removed in the next major version */
export interface LogRowContextQueryErrors {
before?: string;
after?: string;
}
/** @deprecated will be removed in the next major version */
export interface HasMoreContextRows {
before: boolean;
after: boolean;
@ -48,6 +53,7 @@ interface LogRowContextProviderProps {
}) => JSX.Element;
}
/** @deprecated will be removed in the next major version */
export const getRowContexts = async (
getRowContext: (row: LogRowModel, options?: RowContextOptions) => Promise<DataQueryResponse>,
row: LogRowModel,
@ -130,6 +136,7 @@ export const getRowContexts = async (
};
};
/** @deprecated will be removed in the next major version */
export const LogRowContextProvider: React.FunctionComponent<LogRowContextProviderProps> = ({
getRowContext,
row,

View File

@ -16,8 +16,7 @@ import { LogRowContext } from './LogRowContext';
import { LogRowContextQueryErrors, HasMoreContextRows, LogRowContextRows } from './LogRowContextProvider';
import { getLogRowStyles } from './getLogRowStyles';
//Components
/** @deprecated will be removed in the next major version */
export const MAX_CHARACTERS = 100000;
interface Props extends Themeable2 {
@ -187,5 +186,6 @@ class UnThemedLogRowMessage extends PureComponent<Props> {
}
}
/** @deprecated will be removed in the next major version */
export const LogRowMessage = withTheme2(UnThemedLogRowMessage);
LogRowMessage.displayName = 'LogRowMessage';

View File

@ -8,6 +8,7 @@ import { Themeable2 } from '../../types/theme';
import { getAllFields } from './logParser';
/** @deprecated will be removed in the next major version */
export interface Props extends Themeable2 {
row: LogRowModel;
showDetectedFields: string[];
@ -45,5 +46,6 @@ class UnThemedLogRowMessageDetectedFields extends PureComponent<Props> {
}
}
/** @deprecated will be removed in the next major version */
export const LogRowMessageDetectedFields = withTheme2(UnThemedLogRowMessageDetectedFields);
LogRowMessageDetectedFields.displayName = 'LogRowMessageDetectedFields';

View File

@ -6,13 +6,14 @@ import { TimeZone, LogsDedupStrategy, LogRowModel, Field, LinkModel, LogsSortOrd
import { withTheme2 } from '../../themes/index';
import { Themeable2 } from '../../types/theme';
//Components
import { LogRow } from './LogRow';
import { RowContextOptions } from './LogRowContextProvider';
import { getLogRowStyles } from './getLogRowStyles';
/** @deprecated will be removed in the next major version */
export const PREVIEW_LIMIT = 100;
/** @deprecated will be removed in the next major version */
export interface Props extends Themeable2 {
logRows?: LogRowModel[];
deduplicatedRows?: LogRowModel[];
@ -188,5 +189,6 @@ class UnThemedLogRows extends PureComponent<Props, State> {
}
}
/** @deprecated will be removed in the next major version */
export const LogRows = withTheme2(UnThemedLogRows);
LogRows.displayName = 'LogsRows';

View File

@ -4,6 +4,7 @@ import { GrafanaTheme2, LogLevel } from '@grafana/data';
import { styleMixins } from '../../themes';
/** @deprecated will be removed in the next major version */
export const getLogRowStyles = (theme: GrafanaTheme2, logLevel?: LogLevel) => {
let logColor = theme.isLight ? theme.v1.palette.gray5 : theme.v1.palette.gray2;
const hoverBgColor = styleMixins.hoverColor(theme.colors.background.secondary, theme);

View File

@ -16,6 +16,8 @@ type FieldDef = {
/**
* Returns all fields for log row which consists of fields we parse from the message itself and additional fields
* found in the dataframe (they may contain links).
*
* @deprecated will be removed in the next major version
*/
export const getAllFields = memoizeOne(
(row: LogRowModel, getFieldLinks?: (field: Field, rowIndex: number) => Array<LinkModel<Field>>) => {

View File

@ -30,7 +30,7 @@ export interface MenuItemProps<T = any> {
/** Url of the menu item */
url?: string;
/** Handler for the click behaviour */
onClick?: (event?: React.SyntheticEvent<HTMLElement>, payload?: T) => void;
onClick?: (event?: React.MouseEvent<HTMLElement>, payload?: T) => void;
/** Custom MenuItem styles*/
className?: string;
/** Active */
@ -115,17 +115,7 @@ export const MenuItem = React.memo(
className={itemStyle}
rel={target === '_blank' ? 'noopener noreferrer' : undefined}
href={url}
onClick={
onClick
? (event) => {
if (!(event.ctrlKey || event.metaKey || event.shiftKey) && onClick) {
event.preventDefault();
event.stopPropagation();
onClick(event);
}
}
: undefined
}
onClick={onClick}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onKeyDown={handleKeys}
@ -136,8 +126,11 @@ export const MenuItem = React.memo(
aria-checked={ariaChecked}
tabIndex={tabIndex}
>
{icon && <Icon name={icon} className={styles.icon} aria-hidden />}
{label}
<>
{icon && <Icon name={icon} className={styles.icon} aria-hidden />}
{label}
</>
{hasSubMenu && (
<SubMenu
items={childItems}

View File

@ -10,7 +10,9 @@ import { getSegmentStyles } from './styles';
import { useExpandableLabel, SegmentProps } from '.';
export interface SegmentInputProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLInputElement>, 'value' | 'onChange'> {
export interface SegmentInputProps<T>
extends Omit<SegmentProps<T>, 'allowCustomValue' | 'allowEmptyValue'>,
Omit<HTMLProps<HTMLInputElement>, 'value' | 'onChange'> {
value: string | number;
onChange: (text: string | number) => void;
}

View File

@ -4,7 +4,7 @@ import tinycolor from 'tinycolor2';
import { DisplayValue, Field, formattedValueToString } from '@grafana/data';
import { getTextColorForBackground, getCellLinks } from '../../utils';
import { getCellLinks, getTextColorForAlphaBackground } from '../../utils';
import { DataLinksContextMenu } from '../DataLinks/DataLinksContextMenu';
import { CellActions } from './CellActions';
@ -63,7 +63,7 @@ function getCellStyle(
if (field.config.custom?.displayMode === TableCellDisplayMode.ColorBackgroundSolid) {
const bgColor = tinycolor(displayValue.color);
const textColor = getTextColorForBackground(displayValue.color!);
const textColor = getTextColorForAlphaBackground(displayValue.color!, tableStyles.theme.isDark);
return tableStyles.buildCellContainerStyle(textColor, bgColor.toRgbString(), !disableOverflowOnHover);
}
@ -74,7 +74,7 @@ function getCellStyle(
.spin(5)
.toRgbString();
const textColor = getTextColorForBackground(displayValue.color!);
const textColor = getTextColorForAlphaBackground(displayValue.color!, tableStyles.theme.isDark);
return tableStyles.buildCellContainerStyle(
textColor,

View File

@ -23,7 +23,7 @@ export const FooterRow = (props: FooterRowProps) => {
const tableStyles = useStyles2(getTableStyles);
return (
<table
<div
style={{
position: isPaginationVisible ? 'relative' : 'absolute',
width: totalColumnsWidth ? `${totalColumnsWidth}px` : '100%',
@ -33,22 +33,20 @@ export const FooterRow = (props: FooterRowProps) => {
{footerGroups.map((footerGroup: HeaderGroup) => {
const { key, ...footerGroupProps } = footerGroup.getFooterGroupProps();
return (
<tfoot
<div
className={tableStyles.tfoot}
{...footerGroupProps}
key={key}
data-testid={e2eSelectorsTable.footer}
style={height ? { height: `${height}px` } : undefined}
>
<tr>
{footerGroup.headers.map((column: ColumnInstance, index: number) =>
renderFooterCell(column, tableStyles, height)
)}
</tr>
</tfoot>
{footerGroup.headers.map((column: ColumnInstance, index: number) =>
renderFooterCell(column, tableStyles, height)
)}
</div>
);
})}
</table>
</div>
);
};
@ -67,9 +65,9 @@ function renderFooterCell(column: ColumnInstance, tableStyles: TableStyles, heig
}
return (
<th className={tableStyles.headerCell} {...footerProps}>
<div className={tableStyles.headerCell} {...footerProps}>
{column.render('Footer')}
</th>
</div>
);
}

View File

@ -269,7 +269,10 @@ export class UPlotConfigBuilder {
const xAxis = axes.find((a) => a.props.scaleKey === 'x');
const axesWithoutGridSet = axes.filter((a) => a.props.grid?.show === undefined);
const firstValueAxisIdx = axesWithoutGridSet.findIndex(
(a) => a.props.placement === AxisPlacement.Left || (a.props.placement === AxisPlacement.Bottom && a !== xAxis)
(a) =>
a.props.placement === AxisPlacement.Left ||
a.props.placement === AxisPlacement.Right ||
(a.props.placement === AxisPlacement.Bottom && a !== xAxis)
);
// For all axes with no grid set, set the grid automatically (grid only for first left axis )

View File

@ -143,6 +143,8 @@ export function SuggestionsPlugin({
const preserveSuffix = suggestion.kind === 'function';
const move = suggestion.move || 0;
const moveForward = move > 0 ? move : 0;
const moveBackward = move < 0 ? -move : 0;
const { typeaheadPrefix, typeaheadText, typeaheadContext } = state;
@ -180,7 +182,8 @@ export function SuggestionsPlugin({
.deleteBackward(backward)
.deleteForward(forward)
.insertText(suggestionText)
.moveForward(move)
.moveForward(moveForward)
.moveBackward(moveBackward)
.focus();
return editor;

View File

@ -114,6 +114,19 @@ function hslToHex(color: tinycolor.ColorFormats.HSLA) {
export function getTextColorForBackground(color: string) {
const b = tinycolor(color).getBrightness();
return b > 180 ? 'rgb(32, 34, 38)' : 'rgb(247, 248, 250)';
}
export function getTextColorForAlphaBackground(color: string, themeIsDark: boolean) {
const tcolor = tinycolor(color);
const b = tcolor.getBrightness();
const a = tcolor.getAlpha();
if (a < 0.3) {
return themeIsDark ? 'rgb(247, 248, 250)' : 'rgb(32, 34, 38)';
}
return b > 180 ? 'rgb(32, 34, 38)' : 'rgb(247, 248, 250)';
}

View File

@ -16,20 +16,19 @@ const ThemeableStory: React.FunctionComponent<{ handleSassThemeChange: SassTheme
handleSassThemeChange(theme);
const css = `#root {
width: 100%;
padding: 20px;
display: flex;
min-height: 100%;
background: ${theme.colors.background.primary};
}`;
return (
<ThemeContext.Provider value={theme}>
<div
style={{
width: '100%',
padding: '20px',
display: 'flex',
minHeight: '100%',
background: `${theme.colors.background.primary}`,
}}
>
<GlobalStyles />
{children}
</div>
<GlobalStyles />
<style>{css}</style>
{children}
</ThemeContext.Provider>
);
};

View File

@ -11,8 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { shallow } from 'enzyme';
import { render, screen } from '@testing-library/react';
import React from 'react';
import TickLabels from './TickLabels';
@ -23,37 +22,36 @@ describe('<TickLabels>', () => {
duration: 5000,
};
let wrapper;
let ticks;
beforeEach(() => {
wrapper = shallow(<TickLabels {...defaultProps} />);
ticks = wrapper.find('[data-test="tick"]');
render(<TickLabels {...defaultProps} />);
ticks = screen.getAllByTestId('tick');
});
it('renders the right number of ticks', () => {
expect(ticks.length).toBe(defaultProps.numTicks + 1);
expect(ticks).toHaveLength(defaultProps.numTicks + 1);
});
it('places the first tick on the left', () => {
const firstTick = ticks.first();
expect(firstTick.prop('style')).toEqual({ left: '0%' });
const firstTick = ticks[0];
expect(firstTick).toHaveStyle(`left: 0%;`);
});
it('places the last tick on the right', () => {
const lastTick = ticks.last();
expect(lastTick.prop('style')).toEqual({ right: '0%' });
const lastTick = ticks[ticks.length - 1];
expect(lastTick).toHaveStyle(`right: 0%;`);
});
it('places middle ticks at proper intervals', () => {
const positions = ['25%', '50%', '75%'];
positions.forEach((pos, i) => {
const tick = ticks.at(i + 1);
expect(tick.prop('style')).toEqual({ left: pos });
expect(tick).toHaveStyle(`left: ${pos};`);
});
});
it("doesn't explode if no trace is present", () => {
expect(() => shallow(<TickLabels {...defaultProps} trace={null} />)).not.toThrow();
expect(() => render(<TickLabels {...defaultProps} trace={null} />)).not.toThrow();
});
});

View File

@ -50,7 +50,7 @@ export default function TickLabels(props: TickLabelsProps) {
const portion = i / numTicks;
const style = portion === 1 ? { right: '0%' } : { left: `${portion * 100}%` };
ticks.push(
<div key={portion} className={styles.TickLabelsLabel} style={style} data-test="tick">
<div key={portion} className={styles.TickLabelsLabel} style={style} data-testid="tick">
{formatDuration(duration * portion)}
</div>
);

View File

@ -63,9 +63,7 @@ func (hs *HTTPServer) GetAnnotations(c *models.ReqContext) response.Response {
}
}
repo := annotations.GetRepository()
items, err := repo.Find(c.Req.Context(), query)
items, err := hs.annotationsRepo.Find(c.Req.Context(), query)
if err != nil {
return response.Error(500, "Failed to get annotations", err)
}
@ -135,8 +133,6 @@ func (hs *HTTPServer) PostAnnotation(c *models.ReqContext) response.Response {
return dashboardGuardianResponse(err)
}
repo := annotations.GetRepository()
if cmd.Text == "" {
err := &AnnotationError{"text field should not be empty"}
return response.Error(400, "Failed to save annotation", err)
@ -154,7 +150,7 @@ func (hs *HTTPServer) PostAnnotation(c *models.ReqContext) response.Response {
Tags: cmd.Tags,
}
if err := repo.Save(&item); err != nil {
if err := hs.annotationsRepo.Save(c.Req.Context(), &item); err != nil {
if errors.Is(err, annotations.ErrTimerangeMissing) {
return response.Error(400, "Failed to save annotation", err)
}
@ -194,8 +190,6 @@ func (hs *HTTPServer) PostGraphiteAnnotation(c *models.ReqContext) response.Resp
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
repo := annotations.GetRepository()
if cmd.What == "" {
err := &AnnotationError{"what field should not be empty"}
return response.Error(400, "Failed to save Graphite annotation", err)
@ -234,7 +228,7 @@ func (hs *HTTPServer) PostGraphiteAnnotation(c *models.ReqContext) response.Resp
Tags: tagsArray,
}
if err := repo.Save(&item); err != nil {
if err := hs.annotationsRepo.Save(c.Req.Context(), &item); err != nil {
return response.Error(500, "Failed to save Graphite annotation", err)
}
@ -267,9 +261,7 @@ func (hs *HTTPServer) UpdateAnnotation(c *models.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
}
repo := annotations.GetRepository()
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
annotation, resp := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, annotationID, c.SignedInUser)
if resp != nil {
return resp
}
@ -288,7 +280,7 @@ func (hs *HTTPServer) UpdateAnnotation(c *models.ReqContext) response.Response {
Tags: cmd.Tags,
}
if err := repo.Update(c.Req.Context(), &item); err != nil {
if err := hs.annotationsRepo.Update(c.Req.Context(), &item); err != nil {
return response.Error(500, "Failed to update annotation", err)
}
@ -319,9 +311,7 @@ func (hs *HTTPServer) PatchAnnotation(c *models.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
}
repo := annotations.GetRepository()
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
annotation, resp := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, annotationID, c.SignedInUser)
if resp != nil {
return resp
}
@ -356,7 +346,7 @@ func (hs *HTTPServer) PatchAnnotation(c *models.ReqContext) response.Response {
existing.EpochEnd = cmd.TimeEnd
}
if err := repo.Update(c.Req.Context(), &existing); err != nil {
if err := hs.annotationsRepo.Update(c.Req.Context(), &existing); err != nil {
return response.Error(500, "Failed to update annotation", err)
}
@ -391,7 +381,6 @@ func (hs *HTTPServer) MassDeleteAnnotations(c *models.ReqContext) response.Respo
return response.Error(http.StatusBadRequest, "bad request data", err)
}
repo := annotations.GetRepository()
var deleteParams *annotations.DeleteParams
// validations only for RBAC. A user can mass delete all annotations in a (dashboard + panel) or a specific annotation
@ -400,7 +389,7 @@ func (hs *HTTPServer) MassDeleteAnnotations(c *models.ReqContext) response.Respo
var dashboardId int64
if cmd.AnnotationId != 0 {
annotation, respErr := findAnnotationByID(c.Req.Context(), repo, cmd.AnnotationId, c.SignedInUser)
annotation, respErr := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, cmd.AnnotationId, c.SignedInUser)
if respErr != nil {
return respErr
}
@ -431,7 +420,7 @@ func (hs *HTTPServer) MassDeleteAnnotations(c *models.ReqContext) response.Respo
}
}
err = repo.Delete(c.Req.Context(), deleteParams)
err = hs.annotationsRepo.Delete(c.Req.Context(), deleteParams)
if err != nil {
return response.Error(500, "Failed to delete annotations", err)
@ -454,9 +443,7 @@ func (hs *HTTPServer) GetAnnotationByID(c *models.ReqContext) response.Response
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
}
repo := annotations.GetRepository()
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
annotation, resp := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, annotationID, c.SignedInUser)
if resp != nil {
return resp
}
@ -485,9 +472,7 @@ func (hs *HTTPServer) DeleteAnnotationByID(c *models.ReqContext) response.Respon
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
}
repo := annotations.GetRepository()
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
annotation, resp := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, annotationID, c.SignedInUser)
if resp != nil {
return resp
}
@ -496,7 +481,7 @@ func (hs *HTTPServer) DeleteAnnotationByID(c *models.ReqContext) response.Respon
return dashboardGuardianResponse(err)
}
err = repo.Delete(c.Req.Context(), &annotations.DeleteParams{
err = hs.annotationsRepo.Delete(c.Req.Context(), &annotations.DeleteParams{
OrgId: c.OrgID,
Id: annotationID,
})
@ -563,8 +548,7 @@ func (hs *HTTPServer) GetAnnotationTags(c *models.ReqContext) response.Response
Limit: c.QueryInt64("limit"),
}
repo := annotations.GetRepository()
result, err := repo.FindTags(c.Req.Context(), query)
result, err := hs.annotationsRepo.FindTags(c.Req.Context(), query)
if err != nil {
return response.Error(500, "Failed to find annotation tags", err)
}
@ -575,7 +559,7 @@ func (hs *HTTPServer) GetAnnotationTags(c *models.ReqContext) response.Response
// AnnotationTypeScopeResolver provides an ScopeAttributeResolver able to
// resolve annotation types. Scope "annotations:id:<id>" will be translated to "annotations:type:<type>,
// where <type> is the type of annotation with id <id>.
func AnnotationTypeScopeResolver() (string, accesscontrol.ScopeAttributeResolver) {
func AnnotationTypeScopeResolver(annotationsRepo annotations.Repository) (string, accesscontrol.ScopeAttributeResolver) {
prefix := accesscontrol.ScopeAnnotationsProvider.GetResourceScope("")
return prefix, accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, initialScope string) ([]string, error) {
scopeParts := strings.Split(initialScope, ":")
@ -601,7 +585,7 @@ func AnnotationTypeScopeResolver() (string, accesscontrol.ScopeAttributeResolver
},
}
annotation, resp := findAnnotationByID(ctx, annotations.GetRepository(), int64(annotationId), tempUser)
annotation, resp := findAnnotationByID(ctx, annotationsRepo, int64(annotationId), tempUser)
if resp != nil {
return nil, errors.New("could not resolve annotation type")
}

View File

@ -17,11 +17,13 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/services/team/teamtest"
)
func TestAnnotationsAPIEndpoint(t *testing.T) {
@ -73,8 +75,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
mock := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
fakeAnnoRepo = NewFakeAnnotationsRepo()
annotations.SetRepository(fakeAnnoRepo)
sc.handlerFunc = hs.DeleteAnnotationByID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
assert.Equal(t, 403, sc.resp.Code)
@ -103,8 +103,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
mock := mockstore.NewSQLStoreMock()
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
fakeAnnoRepo = NewFakeAnnotationsRepo()
annotations.SetRepository(fakeAnnoRepo)
sc.handlerFunc = hs.DeleteAnnotationByID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
assert.Equal(t, 200, sc.resp.Code)
@ -178,8 +176,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
setUpACL()
fakeAnnoRepo = NewFakeAnnotationsRepo()
annotations.SetRepository(fakeAnnoRepo)
sc.handlerFunc = hs.DeleteAnnotationByID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
assert.Equal(t, 403, sc.resp.Code)
@ -211,8 +207,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
setUpACL()
fakeAnnoRepo = NewFakeAnnotationsRepo()
annotations.SetRepository(fakeAnnoRepo)
sc.handlerFunc = hs.DeleteAnnotationByID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
assert.Equal(t, 200, sc.resp.Code)
@ -286,59 +280,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
})
}
type fakeAnnotationsRepo struct {
annotations map[int64]annotations.Item
}
func NewFakeAnnotationsRepo() *fakeAnnotationsRepo {
return &fakeAnnotationsRepo{
annotations: map[int64]annotations.Item{},
}
}
func (repo *fakeAnnotationsRepo) Delete(_ context.Context, params *annotations.DeleteParams) error {
if params.Id != 0 {
delete(repo.annotations, params.Id)
} else {
for _, v := range repo.annotations {
if params.DashboardId == v.DashboardId && params.PanelId == v.PanelId {
delete(repo.annotations, v.Id)
}
}
}
return nil
}
func (repo *fakeAnnotationsRepo) Save(item *annotations.Item) error {
if item.Id == 0 {
item.Id = int64(len(repo.annotations) + 1)
}
repo.annotations[item.Id] = *item
return nil
}
func (repo *fakeAnnotationsRepo) Update(_ context.Context, item *annotations.Item) error {
return nil
}
func (repo *fakeAnnotationsRepo) Find(_ context.Context, query *annotations.ItemQuery) ([]*annotations.ItemDTO, error) {
if annotation, has := repo.annotations[query.AnnotationId]; has {
return []*annotations.ItemDTO{{Id: annotation.Id, DashboardId: annotation.DashboardId}}, nil
}
annotations := []*annotations.ItemDTO{{Id: 1, DashboardId: 0}}
return annotations, nil
}
func (repo *fakeAnnotationsRepo) FindTags(_ context.Context, query *annotations.TagsQuery) (annotations.FindTagsResult, error) {
result := annotations.FindTagsResult{
Tags: []*annotations.TagsDTO{},
}
return result, nil
}
func (repo *fakeAnnotationsRepo) LoadItems() {
}
var fakeAnnoRepo *fakeAnnotationsRepo
func postAnnotationScenario(t *testing.T, desc string, url string, routePattern string, role org.RoleType,
cmd dtos.PostAnnotationsCmd, store sqlstore.Store, dashSvc dashboards.DashboardService, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
@ -358,9 +299,6 @@ func postAnnotationScenario(t *testing.T, desc string, url string, routePattern
return hs.PostAnnotation(c)
})
fakeAnnoRepo = NewFakeAnnotationsRepo()
annotations.SetRepository(fakeAnnoRepo)
sc.m.Post(routePattern, sc.defaultHandler)
fn(sc)
@ -387,9 +325,6 @@ func putAnnotationScenario(t *testing.T, desc string, url string, routePattern s
return hs.UpdateAnnotation(c)
})
fakeAnnoRepo = NewFakeAnnotationsRepo()
annotations.SetRepository(fakeAnnoRepo)
sc.m.Put(routePattern, sc.defaultHandler)
fn(sc)
@ -415,9 +350,6 @@ func patchAnnotationScenario(t *testing.T, desc string, url string, routePattern
return hs.PatchAnnotation(c)
})
fakeAnnoRepo = NewFakeAnnotationsRepo()
annotations.SetRepository(fakeAnnoRepo)
sc.m.Patch(routePattern, sc.defaultHandler)
fn(sc)
@ -443,9 +375,6 @@ func deleteAnnotationsScenario(t *testing.T, desc string, url string, routePatte
return hs.MassDeleteAnnotations(c)
})
fakeAnnoRepo = NewFakeAnnotationsRepo()
annotations.SetRepository(fakeAnnoRepo)
sc.m.Post(routePattern, sc.defaultHandler)
fn(sc)
@ -461,11 +390,8 @@ func TestAPI_Annotations_AccessControl(t *testing.T) {
dashboardAnnotation := &annotations.Item{Id: 1, DashboardId: 1}
organizationAnnotation := &annotations.Item{Id: 2, DashboardId: 0}
fakeAnnoRepo = NewFakeAnnotationsRepo()
_ = fakeAnnoRepo.Save(dashboardAnnotation)
_ = fakeAnnoRepo.Save(organizationAnnotation)
annotations.SetRepository(fakeAnnoRepo)
_ = sc.hs.annotationsRepo.Save(context.Background(), dashboardAnnotation)
_ = sc.hs.annotationsRepo.Save(context.Background(), organizationAnnotation)
postOrganizationCmd := dtos.PostAnnotationsCmd{
Time: 1000,
@ -788,7 +714,7 @@ func TestAPI_Annotations_AccessControl(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
setUpRBACGuardian(t)
sc.acmock.
RegisterScopeAttributeResolver(AnnotationTypeScopeResolver())
RegisterScopeAttributeResolver(AnnotationTypeScopeResolver(sc.hs.annotationsRepo))
setAccessControlPermissions(sc.acmock, tt.args.permissions, sc.initCtx.OrgID)
r := callAPI(sc.server, tt.args.method, tt.args.url, tt.args.body, t)
@ -835,13 +761,11 @@ func TestService_AnnotationTypeScopeResolver(t *testing.T) {
dashboardAnnotation := annotations.Item{Id: 1, DashboardId: 1}
organizationAnnotation := annotations.Item{Id: 2}
fakeAnnoRepo = NewFakeAnnotationsRepo()
_ = fakeAnnoRepo.Save(&dashboardAnnotation)
_ = fakeAnnoRepo.Save(&organizationAnnotation)
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
_ = fakeAnnoRepo.Save(context.Background(), &dashboardAnnotation)
_ = fakeAnnoRepo.Save(context.Background(), &organizationAnnotation)
annotations.SetRepository(fakeAnnoRepo)
prefix, resolver := AnnotationTypeScopeResolver()
prefix, resolver := AnnotationTypeScopeResolver(fakeAnnoRepo)
require.Equal(t, "annotations:id:", prefix)
for _, tc := range testCases {
@ -986,11 +910,8 @@ func TestAPI_MassDeleteAnnotations_AccessControl(t *testing.T) {
dashboardAnnotation := &annotations.Item{Id: 1, DashboardId: 1}
organizationAnnotation := &annotations.Item{Id: 2, DashboardId: 0}
fakeAnnoRepo = NewFakeAnnotationsRepo()
_ = fakeAnnoRepo.Save(dashboardAnnotation)
_ = fakeAnnoRepo.Save(organizationAnnotation)
annotations.SetRepository(fakeAnnoRepo)
_ = sc.hs.annotationsRepo.Save(context.Background(), dashboardAnnotation)
_ = sc.hs.annotationsRepo.Save(context.Background(), organizationAnnotation)
r := callAPI(sc.server, tt.args.method, tt.args.url, tt.args.body, t)
assert.Equalf(t, tt.want, r.Code, "Annotations API(%v)", tt.args.url)
@ -1002,7 +923,7 @@ func setUpACL() {
viewerRole := org.RoleViewer
editorRole := org.RoleEditor
store := mockstore.NewSQLStoreMock()
store.ExpectedTeamsByUser = []*models.TeamDTO{}
teamSvc := &teamtest.FakeService{}
dashSvc := &dashboards.FakeDashboardService{}
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
@ -1012,7 +933,7 @@ func setUpACL() {
}
}).Return(nil)
guardian.InitLegacyGuardian(store, dashSvc)
guardian.InitLegacyGuardian(store, dashSvc, teamSvc)
}
func setUpRBACGuardian(t *testing.T) {

View File

@ -58,7 +58,7 @@ func (hs *HTTPServer) registerRoutes() {
reqGrafanaAdmin := middleware.ReqGrafanaAdmin
reqEditorRole := middleware.ReqEditorRole
reqOrgAdmin := middleware.ReqOrgAdmin
reqOrgAdminDashOrFolderAdminOrTeamAdmin := middleware.OrgAdminDashOrFolderAdminOrTeamAdmin(hs.SQLStore, hs.DashboardService)
reqOrgAdminDashOrFolderAdminOrTeamAdmin := middleware.OrgAdminDashOrFolderAdminOrTeamAdmin(hs.SQLStore, hs.DashboardService, hs.teamService)
reqCanAccessTeams := middleware.AdminOrEditorAndFeatureEnabled(hs.Cfg.EditorsCanAdmin)
reqSnapshotPublicModeOrSignedIn := middleware.SnapshotPublicModeOrSignedIn(hs.Cfg)
redirectFromLegacyPanelEditURL := middleware.RedirectFromLegacyPanelEditURL(hs.Cfg)

View File

@ -26,6 +26,7 @@ import (
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/contexthandler"
"github.com/grafana/grafana/pkg/services/contexthandler/authproxy"
@ -41,6 +42,7 @@ import (
"github.com/grafana/grafana/pkg/services/login/loginservice"
"github.com/grafana/grafana/pkg/services/login/logintest"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgtest"
"github.com/grafana/grafana/pkg/services/preference/preftest"
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
"github.com/grafana/grafana/pkg/services/rendering"
@ -49,6 +51,10 @@ import (
"github.com/grafana/grafana/pkg/services/searchusers/filters"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/team"
"github.com/grafana/grafana/pkg/services/team/teamimpl"
"github.com/grafana/grafana/pkg/services/team/teamtest"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/usertest"
"github.com/grafana/grafana/pkg/setting"
@ -295,6 +301,7 @@ type accessControlScenarioContext struct {
cfg *setting.Cfg
dashboardsStore dashboards.Store
teamService team.Service
}
func setAccessControlPermissions(acmock *accesscontrolmock.Mock, perms []accesscontrol.Permission, org int64) {
@ -337,10 +344,11 @@ func setupSimpleHTTPServer(features *featuremgmt.FeatureManager) *HTTPServer {
cfg.IsFeatureToggleEnabled = features.IsEnabled
return &HTTPServer{
Cfg: cfg,
Features: features,
License: &licensing.OSSLicensingService{},
AccessControl: accesscontrolmock.New().WithDisabled(),
Cfg: cfg,
Features: features,
License: &licensing.OSSLicensingService{},
AccessControl: accesscontrolmock.New().WithDisabled(),
annotationsRepo: annotationstest.NewFakeAnnotationsRepo(),
}
}
@ -363,7 +371,8 @@ func setupHTTPServerWithCfgDb(
license := &licensing.OSSLicensingService{}
routeRegister := routing.NewRouteRegister()
dashboardsStore := dashboardsstore.ProvideDashboardStore(db, featuremgmt.WithFeatures())
teamService := teamimpl.ProvideService(db)
dashboardsStore := dashboardsstore.ProvideDashboardStore(db, featuremgmt.WithFeatures(), tagimpl.ProvideService(db))
var acmock *accesscontrolmock.Mock
var ac accesscontrol.AccessControl
@ -408,6 +417,9 @@ func setupHTTPServerWithCfgDb(
),
preferenceService: preftest.NewPreferenceServiceFake(),
userService: userMock,
orgService: orgtest.NewOrgServiceFake(),
teamService: teamService,
annotationsRepo: annotationstest.NewFakeAnnotationsRepo(),
}
for _, o := range options {
@ -442,6 +454,7 @@ func setupHTTPServerWithCfgDb(
db: db,
cfg: cfg,
dashboardsStore: dashboardsStore,
teamService: teamService,
usermock: userMock,
}
}
@ -519,11 +532,12 @@ func setUp(confs ...setUpConf) *HTTPServer {
}
}
store.ExpectedTeamsByUser = []*models.TeamDTO{}
teamSvc := &teamtest.FakeService{}
dashSvc := &dashboards.FakeDashboardService{}
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
q.Result = aclMockResp
}).Return(nil)
guardian.InitLegacyGuardian(store, dashSvc)
guardian.InitLegacyGuardian(store, dashSvc, teamSvc)
return hs
}

View File

@ -189,7 +189,7 @@ func (hs *HTTPServer) GetDashboard(c *models.ReqContext) response.Response {
meta.FolderUrl = query.Result.GetUrl()
}
provisioningData, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardID(dash.Id)
provisioningData, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardID(c.Req.Context(), dash.Id)
if err != nil {
return response.Error(500, "Error while checking if dashboard is provisioned", err)
}
@ -407,13 +407,13 @@ func (hs *HTTPServer) postDashboard(c *models.ReqContext, cmd models.SaveDashboa
var provisioningData *models.DashboardProvisioning
if dash.Id != 0 {
data, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardID(dash.Id)
data, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardID(c.Req.Context(), dash.Id)
if err != nil {
return response.Error(500, "Error while checking if dashboard is provisioned using ID", err)
}
provisioningData = data
} else if dash.Uid != "" {
data, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardUID(dash.OrgId, dash.Uid)
data, err := hs.dashboardProvisioningService.GetProvisionedDashboardDataByDashboardUID(c.Req.Context(), dash.OrgId, dash.Uid)
if err != nil && !errors.Is(err, dashboards.ErrProvisionedDashboardNotFound) && !errors.Is(err, dashboards.ErrDashboardNotFound) {
return response.Error(500, "Error while checking if dashboard is provisioned", err)
}

View File

@ -20,6 +20,7 @@ import (
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/services/team/teamtest"
)
func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) {
@ -70,10 +71,11 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) {
hs := &HTTPServer{dashboardsnapshotsService: setUpSnapshotTest(t, 0, "")}
sc.handlerFunc = hs.DeleteDashboardSnapshot
teamSvc := &teamtest.FakeService{}
dashSvc := dashboards.NewFakeDashboardService(t)
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Return(nil).Maybe()
guardian.InitLegacyGuardian(sc.sqlStore, dashSvc)
guardian.InitLegacyGuardian(sc.sqlStore, dashSvc, teamSvc)
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
assert.Equal(t, 403, sc.resp.Code)
@ -107,6 +109,7 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) {
})
t.Run("When user is editor and dashboard has default ACL", func(t *testing.T) {
teamSvc := &teamtest.FakeService{}
dashSvc := &dashboards.FakeDashboardService{}
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
@ -118,7 +121,7 @@ func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) {
loggedInUserScenarioWithRole(t, "Should be able to delete a snapshot when calling DELETE on", "DELETE",
"/api/snapshots/12345", "/api/snapshots/:key", org.RoleEditor, func(sc *scenarioContext) {
guardian.InitLegacyGuardian(sc.sqlStore, dashSvc)
guardian.InitLegacyGuardian(sc.sqlStore, dashSvc, teamSvc)
var externalRequest *http.Request
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(200)

View File

@ -22,6 +22,7 @@ import (
"github.com/grafana/grafana/pkg/models"
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/dashboards/service"
@ -38,6 +39,8 @@ import (
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
"github.com/grafana/grafana/pkg/services/team/teamtest"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web"
@ -109,7 +112,7 @@ func newTestLive(t *testing.T, store *sqlstore.SQLStore) *live.GrafanaLive {
nil,
&usagestats.UsageStatsMock{T: t},
nil,
features, accesscontrolmock.New(), &dashboards.FakeDashboardService{})
features, accesscontrolmock.New(), &dashboards.FakeDashboardService{}, annotationstest.NewFakeAnnotationsRepo(), nil)
require.NoError(t, err)
return gLive
}
@ -128,6 +131,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
fakeDash.HasACL = false
fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
fakeDashboardVersionService.ExpectedDashboardVersion = &dashver.DashboardVersion{}
teamService := &teamtest.FakeService{}
dashboardService := dashboards.NewFakeDashboardService(t)
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
q := args.Get(1).(*models.GetDashboardQuery)
@ -156,7 +160,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
{Role: &editorRole, Permission: models.PERMISSION_EDIT},
}
}).Return(nil)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
}
// This tests two scenarios:
@ -234,6 +238,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
fakeDash.HasACL = true
fakeDashboardVersionService := dashvertest.NewDashboardVersionServiceFake()
fakeDashboardVersionService.ExpectedDashboardVersion = &dashver.DashboardVersion{}
teamService := &teamtest.FakeService{}
dashboardService := dashboards.NewFakeDashboardService(t)
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) {
q := args.Get(1).(*models.GetDashboardQuery)
@ -273,7 +278,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
setting.ViewersCanEdit = origCanEdit
})
setting.ViewersCanEdit = false
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
}
// This tests six scenarios:
@ -377,7 +382,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_EDIT},
}
}).Return(nil)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
}
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi",
@ -439,7 +444,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_VIEW},
}
}).Return(nil)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
}
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
@ -479,7 +484,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_ADMIN},
}
}).Return(nil)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
}
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
@ -532,7 +537,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
{OrgId: 1, DashboardId: 2, UserId: 1, Permission: models.PERMISSION_VIEW},
}
}).Return(nil)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
}
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
@ -742,9 +747,10 @@ func TestDashboardAPIEndpoint(t *testing.T) {
}
sqlmock := mockstore.SQLStoreMock{}
setUp := func() {
teamSvc := &teamtest.FakeService{}
dashSvc := dashboards.NewFakeDashboardService(t)
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Return(nil)
guardian.InitLegacyGuardian(&sqlmock, dashSvc)
guardian.InitLegacyGuardian(&sqlmock, dashSvc, teamSvc)
}
cmd := dtos.CalculateDiffOptions{
@ -860,6 +866,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
dashboardStore := dashboards.NewFakeDashboardStore(t)
dashboardStore.On("GetProvisionedDataByDashboardID", mock.Anything).Return(&models.DashboardProvisioning{ExternalId: "/dashboard1.json"}, nil).Once()
teamService := &teamtest.FakeService{}
dashboardService := dashboards.NewFakeDashboardService(t)
dataValue, err := simplejson.NewJson([]byte(`{"id": 1, "editable": true, "style": "dark"}`))
@ -872,7 +879,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
q.Result = []*models.DashboardACLInfoDTO{{OrgId: testOrgID, DashboardId: 1, UserId: testUserID, Permission: models.PERMISSION_EDIT}}
}).Return(nil)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService)
guardian.InitLegacyGuardian(mockSQLStore, dashboardService, teamService)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/uid/dash", "/api/dashboards/uid/:uid", org.RoleEditor, func(sc *scenarioContext) {
fakeProvisioningService := provisioning.NewProvisioningServiceMock(context.Background())
@ -929,7 +936,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
if dashboardStore == nil {
sql := sqlstore.InitTestDB(t)
dashboardStore = database.ProvideDashboardStore(sql, featuremgmt.WithFeatures())
dashboardStore = database.ProvideDashboardStore(sql, featuremgmt.WithFeatures(), tagimpl.ProvideService(sql))
}
libraryPanelsService := mockLibraryPanelService{}
@ -1134,7 +1141,7 @@ type mockDashboardProvisioningService struct {
dashboards.DashboardProvisioningService
}
func (s mockDashboardProvisioningService) GetProvisionedDashboardDataByDashboardID(dashboardID int64) (
func (s mockDashboardProvisioningService) GetProvisionedDashboardDataByDashboardID(ctx context.Context, dashboardID int64) (
*models.DashboardProvisioning, error) {
return nil, nil
}

View File

@ -8,18 +8,21 @@ import (
"net/http"
"sort"
"strconv"
"strings"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/api/datasource"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins/adapters"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/datasources/permissions"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/proxyutil"
"github.com/grafana/grafana/pkg/web"
@ -326,6 +329,26 @@ func validateURL(cmdType string, url string) response.Response {
return nil
}
// validateJSONData prevents the user from adding a custom header with name that matches the auth proxy header name.
// This is done to prevent data source proxy from being used to circumvent auth proxy.
// For more context take a look at CVE-2022-35957
func validateJSONData(jsonData *simplejson.Json, cfg *setting.Cfg) error {
if jsonData == nil || !cfg.AuthProxyEnabled {
return nil
}
for key, value := range jsonData.MustMap() {
if strings.HasPrefix(key, "httpHeaderName") {
header := fmt.Sprint(value)
if http.CanonicalHeaderKey(header) == http.CanonicalHeaderKey(cfg.AuthProxyHeaderName) {
datasourcesLogger.Error("Forbidden to add a data source header with a name equal to auth proxy header name", "headerName", key)
return errors.New("validation error, invalid header name specified")
}
}
}
return nil
}
// swagger:route POST /datasources datasources addDataSource
//
// Create a data source.
@ -357,6 +380,9 @@ func (hs *HTTPServer) AddDataSource(c *models.ReqContext) response.Response {
return resp
}
}
if err := validateJSONData(cmd.JsonData, hs.Cfg); err != nil {
return response.Error(http.StatusBadRequest, "Failed to add datasource", err)
}
if err := hs.DataSourcesService.AddDataSource(c.Req.Context(), &cmd); err != nil {
if errors.Is(err, datasources.ErrDataSourceNameExists) || errors.Is(err, datasources.ErrDataSourceUidExists) {
@ -414,6 +440,9 @@ func (hs *HTTPServer) UpdateDataSourceByID(c *models.ReqContext) response.Respon
if resp := validateURL(cmd.Type, cmd.Url); resp != nil {
return resp
}
if err := validateJSONData(cmd.JsonData, hs.Cfg); err != nil {
return response.Error(http.StatusBadRequest, "Failed to update datasource", err)
}
ds, err := hs.getRawDataSourceById(c.Req.Context(), cmd.Id, cmd.OrgId)
if err != nil {
@ -451,6 +480,9 @@ func (hs *HTTPServer) UpdateDataSourceByUID(c *models.ReqContext) response.Respo
if resp := validateURL(cmd.Type, cmd.Url); resp != nil {
return resp
}
if err := validateJSONData(cmd.JsonData, hs.Cfg); err != nil {
return response.Error(http.StatusBadRequest, "Failed to update datasource", err)
}
ds, err := hs.getRawDataSourceByUID(c.Req.Context(), web.Params(c.Req)[":uid"], c.OrgID)
if err != nil {

View File

@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/datasources"
@ -83,6 +84,7 @@ func TestAddDataSource_InvalidURL(t *testing.T) {
sc := setupScenarioContext(t, "/api/datasources")
hs := &HTTPServer{
DataSourcesService: &dataSourcesServiceMock{},
Cfg: setting.NewCfg(),
}
sc.m.Post(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
@ -109,6 +111,7 @@ func TestAddDataSource_URLWithoutProtocol(t *testing.T) {
DataSourcesService: &dataSourcesServiceMock{
expectedDatasource: &datasources.DataSource{},
},
Cfg: setting.NewCfg(),
}
sc := setupScenarioContext(t, "/api/datasources")
@ -128,10 +131,42 @@ func TestAddDataSource_URLWithoutProtocol(t *testing.T) {
assert.Equal(t, 200, sc.resp.Code)
}
// Using a custom header whose name matches the name specified for auth proxy header should fail
func TestAddDataSource_InvalidJSONData(t *testing.T) {
hs := &HTTPServer{
DataSourcesService: &dataSourcesServiceMock{},
Cfg: setting.NewCfg(),
}
sc := setupScenarioContext(t, "/api/datasources")
hs.Cfg = setting.NewCfg()
hs.Cfg.AuthProxyEnabled = true
hs.Cfg.AuthProxyHeaderName = "X-AUTH-PROXY-HEADER"
jsonData := simplejson.New()
jsonData.Set("httpHeaderName1", hs.Cfg.AuthProxyHeaderName)
sc.m.Post(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(datasources.AddDataSourceCommand{
Name: "Test",
Url: "localhost:5432",
Access: "direct",
Type: "test",
JsonData: jsonData,
})
return hs.AddDataSource(c)
}))
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
assert.Equal(t, 400, sc.resp.Code)
}
// Updating data sources with invalid URLs should lead to an error.
func TestUpdateDataSource_InvalidURL(t *testing.T) {
hs := &HTTPServer{
DataSourcesService: &dataSourcesServiceMock{},
Cfg: setting.NewCfg(),
}
sc := setupScenarioContext(t, "/api/datasources/1234")
@ -150,6 +185,35 @@ func TestUpdateDataSource_InvalidURL(t *testing.T) {
assert.Equal(t, 400, sc.resp.Code)
}
// Using a custom header whose name matches the name specified for auth proxy header should fail
func TestUpdateDataSource_InvalidJSONData(t *testing.T) {
hs := &HTTPServer{
DataSourcesService: &dataSourcesServiceMock{},
Cfg: setting.NewCfg(),
}
sc := setupScenarioContext(t, "/api/datasources/1234")
hs.Cfg.AuthProxyEnabled = true
hs.Cfg.AuthProxyHeaderName = "X-AUTH-PROXY-HEADER"
jsonData := simplejson.New()
jsonData.Set("httpHeaderName1", hs.Cfg.AuthProxyHeaderName)
sc.m.Put(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(datasources.AddDataSourceCommand{
Name: "Test",
Url: "localhost:5432",
Access: "direct",
Type: "test",
JsonData: jsonData,
})
return hs.AddDataSource(c)
}))
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
assert.Equal(t, 400, sc.resp.Code)
}
// Updating data sources with URLs not specifying protocol should work.
func TestUpdateDataSource_URLWithoutProtocol(t *testing.T) {
const name = "Test"
@ -159,6 +223,7 @@ func TestUpdateDataSource_URLWithoutProtocol(t *testing.T) {
DataSourcesService: &dataSourcesServiceMock{
expectedDatasource: &datasources.DataSource{},
},
Cfg: setting.NewCfg(),
}
sc := setupScenarioContext(t, "/api/datasources/1234")

View File

@ -12,6 +12,7 @@ type IndexViewData struct {
AppUrl string
AppSubUrl string
GoogleAnalyticsId string
GoogleAnalytics4Id string
GoogleTagManagerId string
NavTree []*NavLink
BuildVersion string

View File

@ -6,18 +6,19 @@ import (
)
type PluginSetting struct {
Name string `json:"name"`
Type string `json:"type"`
Id string `json:"id"`
Enabled bool `json:"enabled"`
Pinned bool `json:"pinned"`
Module string `json:"module"`
BaseUrl string `json:"baseUrl"`
Info plugins.Info `json:"info"`
Includes []*plugins.Includes `json:"includes"`
Dependencies plugins.Dependencies `json:"dependencies"`
JsonData map[string]interface{} `json:"jsonData"`
DefaultNavUrl string `json:"defaultNavUrl"`
Name string `json:"name"`
Type string `json:"type"`
Id string `json:"id"`
Enabled bool `json:"enabled"`
Pinned bool `json:"pinned"`
Module string `json:"module"`
BaseUrl string `json:"baseUrl"`
Info plugins.Info `json:"info"`
Includes []*plugins.Includes `json:"includes"`
Dependencies plugins.Dependencies `json:"dependencies"`
JsonData map[string]interface{} `json:"jsonData"`
SecureJsonFields map[string]bool `json:"secureJsonFields"`
DefaultNavUrl string `json:"defaultNavUrl"`
LatestVersion string `json:"latestVersion"`
HasUpdate bool `json:"hasUpdate"`

View File

@ -86,10 +86,16 @@ type fakePluginSettings struct {
}
// GetPluginSettings returns all Plugin Settings for the provided Org
func (ps *fakePluginSettings) GetPluginSettings(ctx context.Context, args *pluginsettings.GetArgs) ([]*pluginsettings.DTO, error) {
res := []*pluginsettings.DTO{}
func (ps *fakePluginSettings) GetPluginSettings(_ context.Context, _ *pluginsettings.GetArgs) ([]*pluginsettings.InfoDTO, error) {
res := []*pluginsettings.InfoDTO{}
for _, dto := range ps.plugins {
res = append(res, dto)
res = append(res, &pluginsettings.InfoDTO{
PluginID: dto.PluginID,
OrgID: dto.OrgID,
Enabled: dto.Enabled,
Pinned: dto.Pinned,
PluginVersion: dto.PluginVersion,
})
}
return res, nil
}

View File

@ -22,6 +22,7 @@ import (
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/services/team/teamtest"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web/webtest"
@ -237,13 +238,14 @@ func createFolderScenario(t *testing.T, desc string, url string, routePattern st
setUpRBACGuardian(t)
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
aclMockResp := []*models.DashboardACLInfoDTO{}
teamSvc := &teamtest.FakeService{}
dashSvc := &dashboards.FakeDashboardService{}
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
q.Result = aclMockResp
}).Return(nil)
store := mockstore.NewSQLStoreMock()
guardian.InitLegacyGuardian(store, dashSvc)
guardian.InitLegacyGuardian(store, dashSvc, teamSvc)
hs := HTTPServer{
AccessControl: acmock.New(),
folderService: folderService,

View File

@ -117,6 +117,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
"profileEnabled": setting.ProfileEnabled,
"queryHistoryEnabled": hs.Cfg.QueryHistoryEnabled,
"googleAnalyticsId": setting.GoogleAnalyticsId,
"googleAnalytics4Id": setting.GoogleAnalytics4Id,
"rudderstackWriteKey": setting.RudderstackWriteKey,
"rudderstackDataPlaneUrl": setting.RudderstackDataPlaneUrl,
"rudderstackSdkUrl": setting.RudderstackSdkUrl,
@ -139,6 +140,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
"auth": map[string]interface{}{
"OAuthSkipOrgRoleUpdateSync": hs.Cfg.OAuthSkipOrgRoleUpdateSync,
"SAMLSkipOrgRoleSync": hs.Cfg.SectionWithEnvOverrides("auth.saml").Key("skip_org_role_sync").MustBool(false),
"DisableSyncLock": hs.Cfg.DisableSyncLock,
},
"buildInfo": map[string]interface{}{
"hideVersion": hideVersion,
@ -429,8 +431,8 @@ func (hs *HTTPServer) enabledPlugins(ctx context.Context, orgID int64) (EnabledP
return ep, nil
}
func (hs *HTTPServer) pluginSettings(ctx context.Context, orgID int64) (map[string]*pluginsettings.DTO, error) {
pluginSettings := make(map[string]*pluginsettings.DTO)
func (hs *HTTPServer) pluginSettings(ctx context.Context, orgID int64) (map[string]*pluginsettings.InfoDTO, error) {
pluginSettings := make(map[string]*pluginsettings.InfoDTO)
// fill settings from database
if pss, err := hs.PluginSettings.GetPluginSettings(ctx, &pluginsettings.GetArgs{OrgID: orgID}); err != nil {
@ -449,11 +451,12 @@ func (hs *HTTPServer) pluginSettings(ctx context.Context, orgID int64) (map[stri
}
// add new setting which is enabled depending on if AutoEnabled: true
pluginSetting := &pluginsettings.DTO{
PluginID: plugin.ID,
OrgID: orgID,
Enabled: plugin.AutoEnabled,
Pinned: plugin.AutoEnabled,
pluginSetting := &pluginsettings.InfoDTO{
PluginID: plugin.ID,
OrgID: orgID,
Enabled: plugin.AutoEnabled,
Pinned: plugin.AutoEnabled,
PluginVersion: plugin.Info.Version,
}
pluginSettings[plugin.ID] = pluginSetting
@ -467,10 +470,12 @@ func (hs *HTTPServer) pluginSettings(ctx context.Context, orgID int64) (map[stri
}
// add new setting which is enabled by default
pluginSetting := &pluginsettings.DTO{
PluginID: plugin.ID,
OrgID: orgID,
Enabled: true,
pluginSetting := &pluginsettings.InfoDTO{
PluginID: plugin.ID,
OrgID: orgID,
Enabled: true,
Pinned: false,
PluginVersion: plugin.Info.Version,
}
// if plugin is included in an app, check app settings

View File

@ -37,6 +37,7 @@ import (
"github.com/grafana/grafana/pkg/plugins/plugincontext"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/cleanup"
"github.com/grafana/grafana/pkg/services/comments"
@ -83,6 +84,8 @@ import (
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/star"
"github.com/grafana/grafana/pkg/services/store"
"github.com/grafana/grafana/pkg/services/tag"
"github.com/grafana/grafana/pkg/services/team"
"github.com/grafana/grafana/pkg/services/teamguardian"
tempUser "github.com/grafana/grafana/pkg/services/temp_user"
"github.com/grafana/grafana/pkg/services/thumbs"
@ -188,7 +191,10 @@ type HTTPServer struct {
dashboardThumbsService dashboardThumbs.Service
loginAttemptService loginAttempt.Service
orgService org.Service
teamService team.Service
accesscontrolService accesscontrol.Service
annotationsRepo annotations.Repository
tagService tag.Service
}
type ServerOptions struct {
@ -226,8 +232,9 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
playlistService playlist.Service, apiKeyService apikey.Service, kvStore kvstore.KVStore,
secretsMigrator secrets.Migrator, secretsPluginManager plugins.SecretsPluginManager, secretsService secrets.Service,
secretsPluginMigrator spm.SecretMigrationProvider, secretsStore secretsKV.SecretsKVStore,
publicDashboardsApi *publicdashboardsApi.Api, userService user.Service, tempUserService tempUser.Service, loginAttemptService loginAttempt.Service, orgService org.Service,
accesscontrolService accesscontrol.Service, dashboardThumbsService dashboardThumbs.Service,
publicDashboardsApi *publicdashboardsApi.Api, userService user.Service, tempUserService tempUser.Service,
loginAttemptService loginAttempt.Service, orgService org.Service, teamService team.Service,
accesscontrolService accesscontrol.Service, dashboardThumbsService dashboardThumbs.Service, annotationRepo annotations.Repository, tagService tag.Service,
) (*HTTPServer, error) {
web.Env = cfg.Env
m := web.New()
@ -322,7 +329,10 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
dashboardThumbsService: dashboardThumbsService,
loginAttemptService: loginAttemptService,
orgService: orgService,
teamService: teamService,
accesscontrolService: accesscontrolService,
annotationsRepo: annotationRepo,
tagService: tagService,
}
if hs.Listener != nil {
hs.log.Debug("Using provided listener")
@ -330,7 +340,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
hs.registerRoutes()
// Register access control scope resolver for annotations
hs.AccessControl.RegisterScopeAttributeResolver(AnnotationTypeScopeResolver())
hs.AccessControl.RegisterScopeAttributeResolver(AnnotationTypeScopeResolver(hs.annotationsRepo))
if err := hs.declareFixedRoles(); err != nil {
return nil, err

View File

@ -105,7 +105,7 @@ func (hs *HTTPServer) getAppLinks(c *models.ReqContext) ([]*dtos.NavLink, error)
}
if hs.Features.IsEnabled(featuremgmt.FlagTopnav) {
appLink.Url = path.Join(hs.Cfg.AppSubURL, "a", plugin.ID)
appLink.Url = hs.Cfg.AppSubURL + "/a/" + plugin.ID
} else {
appLink.Url = path.Join(hs.Cfg.AppSubURL, plugin.DefaultNavURL)
}
@ -356,7 +356,7 @@ func (hs *HTTPServer) setupConfigNodes(c *models.ReqContext) ([]*dtos.NavLink, e
})
}
if hasAccess(ac.ReqOrgAdmin, correlations.ConfigurationPageAccess) {
if hs.Features.IsEnabled(featuremgmt.FlagCorrelations) && hasAccess(ac.ReqOrgAdmin, correlations.ConfigurationPageAccess) {
configNodes = append(configNodes, &dtos.NavLink{
Text: "Correlations",
Icon: "gf-glue",
@ -521,6 +521,7 @@ func (hs *HTTPServer) buildDashboardNavLinks(c *models.ReqContext, hasEditPerm b
Text: "Browse", Id: "dashboards/browse", Url: hs.Cfg.AppSubURL + "/dashboards", Icon: "sitemap",
})
}
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{
Text: "Playlists", Id: "dashboards/playlists", Url: hs.Cfg.AppSubURL + "/playlists", Icon: "presentation-play",
})
@ -840,6 +841,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
AppUrl: appURL,
AppSubUrl: appSubURL,
GoogleAnalyticsId: setting.GoogleAnalyticsId,
GoogleAnalytics4Id: setting.GoogleAnalytics4Id,
GoogleTagManagerId: setting.GoogleTagManagerId,
BuildVersion: setting.BuildVersion,
BuildCommit: setting.BuildCommit,

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