From 97e81ecbde87992b21de753edc51f98129d0300e Mon Sep 17 00:00:00 2001 From: owensmallwood Date: Thu, 1 Dec 2022 10:02:10 -0600 Subject: [PATCH] PublicDashboards: Modal warns when using unsupported datasources (#58926) Adds warning to pubdash modal when dashboard uses datasources not supported by pubdash --- .../src/selectors/pages.ts | 1 + .../commands/generate_datasources/main.go | 19 +++--- .../supported_datasources_gen.go | 65 ------------------- .../SharePublicDashboard.test.tsx | 19 +++++- .../SharePublicDashboard.tsx | 18 +++++ .../SharePublicDashboardUtils.test.tsx | 29 +++++++++ .../SharePublicDashboardUtils.ts | 22 +++++++ .../SupportedPubdashDatasources.ts | 63 ++++++++++++++++++ 8 files changed, 159 insertions(+), 77 deletions(-) delete mode 100644 pkg/services/publicdashboards/supported_datasources_gen.go create mode 100644 public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SupportedPubdashDatasources.ts diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts index 8fa94cb8d9c..9e20696760a 100644 --- a/packages/grafana-e2e-selectors/src/selectors/pages.ts +++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts @@ -193,6 +193,7 @@ export const Pages = { CopyUrlInput: 'data-testid public dashboard copy url input', CopyUrlButton: 'data-testid public dashboard copy url button', TemplateVariablesWarningAlert: 'data-testid public dashboard disabled template variables alert', + UnsupportedDatasourcesWarningAlert: 'data-testid public dashboard unsupported datasources', }, }, Explore: { diff --git a/pkg/services/publicdashboards/commands/generate_datasources/main.go b/pkg/services/publicdashboards/commands/generate_datasources/main.go index f18fa4685ab..62ce8f15884 100644 --- a/pkg/services/publicdashboards/commands/generate_datasources/main.go +++ b/pkg/services/publicdashboards/commands/generate_datasources/main.go @@ -10,30 +10,27 @@ import ( "github.com/grafana/grafana/pkg/services/publicdashboards/commands/generate_datasources" ) -var goTemplate = ` +var tsDatasourcesTemplate = ` // Code generated by go generate; DO NOT EDIT. -package publicdashboards - -var SupportedDatasources = map[string]bool{ +export const supportedDatasources = new Set([ {{- range . }} - "{{ printf "%s" . }}": true, + '{{ printf "%s" . }}', {{- end }} -} +]) ` func main() { baseUrl := "https://grafana.com" slugs, err := generate_datasources.GetCompatibleDatasources(baseUrl) + tsTemplate := template.Must(template.New("").Parse(tsDatasourcesTemplate)) - myTemplate := template.Must(template.New("").Parse(goTemplate)) - - file, err := os.Create("supported_datasources_gen.go") + // Generate supported datasources for Typescript + tsFile, err := os.Create("./../../../public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SupportedPubdashDatasources.ts") if err != nil { fmt.Println(err) } - - err = myTemplate.Execute(file, slugs) + err = tsTemplate.Execute(tsFile, slugs) if err != nil { fmt.Println(err) } diff --git a/pkg/services/publicdashboards/supported_datasources_gen.go b/pkg/services/publicdashboards/supported_datasources_gen.go deleted file mode 100644 index 5540138c0e1..00000000000 --- a/pkg/services/publicdashboards/supported_datasources_gen.go +++ /dev/null @@ -1,65 +0,0 @@ -// Code generated by go generate; DO NOT EDIT. - -package publicdashboards - -var SupportedDatasources = map[string]bool{ - "aquaqanalytics-kdbbackend-datasource": true, - "cloudwatch": true, - "dlopes7-appdynamics-datasource": true, - "doitintl-bigquery-datasource": true, - "elasticsearch": true, - "frser-sqlite-datasource": true, - "grafadruid-druid-datasource": true, - "grafana-athena-datasource": true, - "grafana-azure-data-explorer-datasource": true, - "grafana-azure-monitor-datasource": true, - "grafana-bigquery-datasource": true, - "grafana-clickhouse-datasource": true, - "grafana-databricks-datasource": true, - "grafana-datadog-datasource": true, - "grafana-db2-datasource": true, - "grafana-dynatrace-datasource": true, - "grafana-es-open-distro-datasource": true, - "grafana-github-datasource": true, - "grafana-honeycomb-datasource": true, - "grafana-iot-sitewise-datasource": true, - "grafana-jira-datasource": true, - "grafana-mock-datasource": true, - "grafana-mongodb-datasource": true, - "grafana-newrelic-datasource": true, - "grafana-odbc-datasource": true, - "grafana-opcua-datasource": true, - "grafana-opensearch-datasource": true, - "grafana-oracle-datasource": true, - "grafana-orbit-datasource": true, - "grafana-redshift-datasource": true, - "grafana-salesforce-datasource": true, - "grafana-saphana-datasource": true, - "grafana-sentry-datasource": true, - "grafana-servicenow-datasource": true, - "grafana-snowflake-datasource": true, - "grafana-splunk-datasource": true, - "grafana-splunk-monitoring-datasource": true, - "grafana-timestream-datasource": true, - "grafana-wavefront-datasource": true, - "grafana-x-ray-datasource": true, - "graphite": true, - "hadesarchitect-cassandra-datasource": true, - "influxdb": true, - "innius-grpc-datasource": true, - "kniepdennis-neo4j-datasource": true, - "loki": true, - "marcusolsson-csv-datasource": true, - "marcusolsson-ynab-datasource": true, - "mssql": true, - "mysql": true, - "opentsdb": true, - "postgres": true, - "prometheus": true, - "redis-datasource": true, - "sentinelone-dataset-datasource": true, - "tdengine-datasource": true, - "vertamedia-clickhouse-datasource": true, - "vertica-grafana-datasource": true, - "yesoreyeram-infinity-datasource": true, -} diff --git a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.test.tsx b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.test.tsx index 808826c68ba..d4691d4fd33 100644 --- a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.test.tsx +++ b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.test.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { Provider } from 'react-redux'; import 'whatwg-fetch'; -import { BootData } from '@grafana/data/src'; +import { BootData, DataQuery } from '@grafana/data/src'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { setEchoSrv } from '@grafana/runtime/src'; import config from 'app/core/config'; @@ -192,6 +192,23 @@ describe('SharePublic - New config setup', () => { await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} }); expect(screen.getByTestId(selectors.SaveConfigButton)).toBeDisabled(); }); + it('when dashboard has unsupported datasources, warning is shown', async () => { + const panelModel = { + targets: [ + { + datasource: { type: 'notSupportedDatasource', uid: 'abc123' }, + } as DataQuery, + ] as DataQuery[], + } as PanelModel; + const dashboard = new DashboardModel({ + id: 1, + panels: [panelModel], + }); + + await renderSharePublicDashboard({ panel: mockPanel, dashboard, onDismiss: () => {} }); + + expect(screen.queryByTestId(selectors.UnsupportedDatasourcesWarningAlert)).toBeInTheDocument(); + }); it('when fetch is done, then no loader spinner appears, inputs are enabled and save button is disabled', async () => { await renderSharePublicDashboard({ panel: mockPanel, dashboard: mockDashboard, onDismiss: () => {} }); expect(screen.queryByTestId('Spinner')).not.toBeInTheDocument(); diff --git a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.tsx b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.tsx index cb5972ba1c3..8e116400498 100644 --- a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.tsx +++ b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard.tsx @@ -31,6 +31,7 @@ import { Acknowledgements, dashboardHasTemplateVariables, generatePublicDashboardUrl, + getUnsupportedDashboardDatasources, publicDashboardPersisted, } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils'; import { ShareModalTabProps } from 'app/features/dashboard/components/ShareModal/types'; @@ -161,6 +162,23 @@ export const SharePublicDashboard = (props: Props) => { {(isGetLoading || isFetching) && }
+ {getUnsupportedDashboardDatasources(props.dashboard.panels).length > 0 ? ( + +
+ {`There are datasources in this dashboard that are unsupported for public dashboards. Panels that use these datasources may not function properly: ${getUnsupportedDashboardDatasources( + props.dashboard.panels + ).join(', ')}. See the `} + + docs + {' '} + for supported datasources. +
+
+ ) : null} {dashboardHasTemplateVariables(dashboardVariables) && !publicDashboardPersisted(publicDashboard) ? ( { @@ -45,3 +48,29 @@ describe('publicDashboardPersisted', () => { expect(publicDashboardPersisted(pubdash)).toBe(false); }); }); + +describe('getUnsupportedDashboardDatasources', () => { + it('itIsSupported', () => { + const pm = { + targets: [ + { + datasource: { type: 'prometheus' } as DataSourceRef, + } as DataQuery, + ] as DataQuery[], + } as PanelModel; + const panelArray: PanelModel[] = [pm]; + expect(getUnsupportedDashboardDatasources(panelArray)).toEqual([]); + }); + + it('itIsNotSupported', () => { + const pm = { + targets: [ + { + datasource: { type: 'blah' } as DataSourceRef, + } as DataQuery, + ] as DataQuery[], + } as PanelModel; + const panelArray: PanelModel[] = [pm]; + expect(getUnsupportedDashboardDatasources(panelArray)).toEqual(['blah']); + }); +}); diff --git a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils.ts b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils.ts index f2409cb4624..f3cf9bdaf84 100644 --- a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils.ts +++ b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils.ts @@ -2,6 +2,10 @@ import { getConfig } from 'app/core/config'; import { VariableModel } from 'app/features/variables/types'; import { DashboardDataDTO, DashboardMeta } from 'app/types/dashboard'; +import { PanelModel } from '../../../state'; + +import { supportedDatasources } from './SupportedPubdashDatasources'; + export interface PublicDashboard { accessToken?: string; annotationsEnabled: boolean; @@ -31,6 +35,24 @@ export const publicDashboardPersisted = (publicDashboard?: PublicDashboard): boo return publicDashboard?.uid !== '' && publicDashboard?.uid !== undefined; }; +/** + * Get unique datasource names from all panels that are not currently supported by public dashboards. + */ +export const getUnsupportedDashboardDatasources = (panels: PanelModel[]): string[] => { + let unsupportedDS = new Set(); + + for (const panel of panels) { + for (const target of panel.targets) { + let ds = target?.datasource?.type; + if (ds && !supportedDatasources.has(ds)) { + unsupportedDS.add(ds); + } + } + } + + return Array.from(unsupportedDS).sort(); +}; + /** * Generate the public dashboard url. Uses the appUrl from the Grafana boot config, so urls will also be correct * when Grafana is hosted on a subpath. diff --git a/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SupportedPubdashDatasources.ts b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SupportedPubdashDatasources.ts new file mode 100644 index 00000000000..63b05bf2347 --- /dev/null +++ b/public/app/features/dashboard/components/ShareModal/SharePublicDashboard/SupportedPubdashDatasources.ts @@ -0,0 +1,63 @@ +// Code generated by go generate; DO NOT EDIT. + +export const supportedDatasources = new Set([ + 'aquaqanalytics-kdbbackend-datasource', + 'cloudwatch', + 'dlopes7-appdynamics-datasource', + 'doitintl-bigquery-datasource', + 'elasticsearch', + 'frser-sqlite-datasource', + 'grafadruid-druid-datasource', + 'grafana-athena-datasource', + 'grafana-azure-data-explorer-datasource', + 'grafana-azure-monitor-datasource', + 'grafana-bigquery-datasource', + 'grafana-clickhouse-datasource', + 'grafana-databricks-datasource', + 'grafana-datadog-datasource', + 'grafana-db2-datasource', + 'grafana-dynatrace-datasource', + 'grafana-es-open-distro-datasource', + 'grafana-github-datasource', + 'grafana-honeycomb-datasource', + 'grafana-iot-sitewise-datasource', + 'grafana-jira-datasource', + 'grafana-mock-datasource', + 'grafana-mongodb-datasource', + 'grafana-newrelic-datasource', + 'grafana-odbc-datasource', + 'grafana-opcua-datasource', + 'grafana-opensearch-datasource', + 'grafana-oracle-datasource', + 'grafana-orbit-datasource', + 'grafana-redshift-datasource', + 'grafana-salesforce-datasource', + 'grafana-saphana-datasource', + 'grafana-sentry-datasource', + 'grafana-servicenow-datasource', + 'grafana-snowflake-datasource', + 'grafana-splunk-datasource', + 'grafana-splunk-monitoring-datasource', + 'grafana-timestream-datasource', + 'grafana-wavefront-datasource', + 'grafana-x-ray-datasource', + 'graphite', + 'hadesarchitect-cassandra-datasource', + 'influxdb', + 'innius-grpc-datasource', + 'kniepdennis-neo4j-datasource', + 'loki', + 'marcusolsson-csv-datasource', + 'marcusolsson-ynab-datasource', + 'mssql', + 'mysql', + 'opentsdb', + 'postgres', + 'prometheus', + 'redis-datasource', + 'sentinelone-dataset-datasource', + 'tdengine-datasource', + 'vertamedia-clickhouse-datasource', + 'vertica-grafana-datasource', + 'yesoreyeram-infinity-datasource', +]);