diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
index 49e6c99098d..2bd6d4eef14 100644
--- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
+++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
@@ -235,6 +235,7 @@ Experimental features might be changed or removed without prior notice.
| `queryLibraryDashboards` | Enables Query Library feature in Dashboards |
| `grafanaAdvisor` | Enables Advisor app |
| `elasticsearchImprovedParsing` | Enables less memory intensive Elasticsearch result parsing |
+| `datasourceConnectionsTab` | Shows defined connections for a data source in the plugins detail page |
## Development feature toggles
diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts
index 3691d40e4df..ec63d6381f3 100644
--- a/packages/grafana-data/src/types/featureToggles.gen.ts
+++ b/packages/grafana-data/src/types/featureToggles.gen.ts
@@ -254,4 +254,5 @@ export interface FeatureToggles {
queryLibraryDashboards?: boolean;
grafanaAdvisor?: boolean;
elasticsearchImprovedParsing?: boolean;
+ datasourceConnectionsTab?: boolean;
}
diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go
index ac80cb89c2c..81203ded268 100644
--- a/pkg/services/featuremgmt/registry.go
+++ b/pkg/services/featuremgmt/registry.go
@@ -1759,6 +1759,14 @@ var (
Stage: FeatureStageExperimental,
Owner: awsDatasourcesSquad,
},
+ {
+ Name: "datasourceConnectionsTab",
+ Description: "Shows defined connections for a data source in the plugins detail page",
+ Stage: FeatureStageExperimental,
+ Owner: grafanaPluginsPlatformSquad,
+ RequiresDevMode: false,
+ FrontendOnly: true,
+ },
}
)
diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv
index cc379d9878e..071c07a971e 100644
--- a/pkg/services/featuremgmt/toggles_gen.csv
+++ b/pkg/services/featuremgmt/toggles_gen.csv
@@ -235,3 +235,4 @@ ABTestFeatureToggleB,experimental,@grafana/sharing-squad,false,false,false
queryLibraryDashboards,experimental,@grafana/grafana-frontend-platform,false,false,false
grafanaAdvisor,experimental,@grafana/plugins-platform-backend,false,false,false
elasticsearchImprovedParsing,experimental,@grafana/aws-datasources,false,false,false
+datasourceConnectionsTab,experimental,@grafana/plugins-platform-backend,false,false,true
diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go
index 02f3e7d062d..d7676dca557 100644
--- a/pkg/services/featuremgmt/toggles_gen.go
+++ b/pkg/services/featuremgmt/toggles_gen.go
@@ -950,4 +950,8 @@ const (
// FlagElasticsearchImprovedParsing
// Enables less memory intensive Elasticsearch result parsing
FlagElasticsearchImprovedParsing = "elasticsearchImprovedParsing"
+
+ // FlagDatasourceConnectionsTab
+ // Shows defined connections for a data source in the plugins detail page
+ FlagDatasourceConnectionsTab = "datasourceConnectionsTab"
)
diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json
index 6616e393d35..e310dd9a6a6 100644
--- a/pkg/services/featuremgmt/toggles_gen.json
+++ b/pkg/services/featuremgmt/toggles_gen.json
@@ -1155,6 +1155,22 @@
"requiresRestart": true
}
},
+ {
+ "metadata": {
+ "name": "datasourceConnectionsTab",
+ "resourceVersion": "1737049826022",
+ "creationTimestamp": "2025-01-16T17:36:09Z",
+ "annotations": {
+ "grafana.app/updatedTimestamp": "2025-01-16 17:50:26.022636488 +0000 UTC"
+ }
+ },
+ "spec": {
+ "description": "Shows defined connections for a data source in the plugins detail page",
+ "stage": "experimental",
+ "codeowner": "@grafana/plugins-platform-backend",
+ "frontend": true
+ }
+ },
{
"metadata": {
"name": "datasourceProxyDisableRBAC",
diff --git a/public/app/features/plugins/admin/__mocks__/catalogPlugin.mock.ts b/public/app/features/plugins/admin/__mocks__/catalogPlugin.mock.ts
index 251523db9ee..9ced4f20a84 100644
--- a/public/app/features/plugins/admin/__mocks__/catalogPlugin.mock.ts
+++ b/public/app/features/plugins/admin/__mocks__/catalogPlugin.mock.ts
@@ -339,3 +339,389 @@ export default {
],
},
} as CatalogPlugin;
+
+export const datasourcePlugin = {
+ description: 'Use Amazon Redshift in Grafana',
+ downloads: 3019577,
+ id: 'grafana-redshift-datasource',
+ info: {
+ logos: {
+ small: 'https://grafana.com/api/plugins/grafana-redshift-datasource/versions/1.20.0/logos/small',
+ large: 'https://grafana.com/api/plugins/grafana-redshift-datasource/versions/1.20.0/logos/large',
+ },
+ keywords: ['datasource', 'redshift', 'aws', 'amazon', 'cloud provider', 'database', 'data warehouse', 'sql'],
+ },
+ name: 'Amazon Redshift',
+ orgName: 'Grafana Labs',
+ popularity: 0.2146,
+ publishedAt: '2021-11-24T08:37:47.000Z',
+ signature: 'valid',
+ signatureOrg: 'Grafana Labs',
+ signatureType: 'grafana',
+ updatedAt: '2025-01-10T20:23:27.000Z',
+ installedVersion: '1.20.0',
+ hasUpdate: false,
+ isInstalled: true,
+ isDisabled: false,
+ isCore: false,
+ isPublished: true,
+ isDeprecated: false,
+ isDev: false,
+ isEnterprise: false,
+ isManaged: false,
+ isPreinstalled: {
+ found: false,
+ withVersion: false,
+ },
+ type: 'datasource',
+ accessControl: {
+ 'plugins.app:access': true,
+ 'plugins:write': true,
+ },
+ angularDetected: false,
+ isFullyInstalled: true,
+ latestVersion: '1.20.0',
+ details: {
+ grafanaDependency: '>=8.0.0',
+ pluginDependencies: [],
+ links: [
+ {
+ name: 'Website',
+ url: 'https://github.com/grafana/redshift-datasource',
+ },
+ {
+ name: 'License',
+ url: 'https://github.com/redshift-datasource/blob/master/LICENSE',
+ },
+ ],
+ readme:
+ '
Grafana 10 breaking change: update Redshift datasource plugin to >=1.8.3
\nGrafana 10.0.0 was shipped with the new React 18 upgrade. Changes in batching of state updates in React 18 cause a bug in the query editor in Redshift versions <=1.8.2 If you’re using Grafana@>=10.0.0, please update your plugin to version 1.8.3 or higher in your Grafana instance management console.
\nRedshift data source for Grafana
\nThe Redshift data source plugin allows you to query and visualize Redshift data metrics from within Grafana.
\nThis topic explains options, variables, querying, and other options specific to this data source. Refer to Add a data source for instructions on how to add a data source to Grafana.
\nConfigure the data source in Grafana
\nTo access data source settings, hover your mouse over the Configuration (gear) icon, then click Data Sources, and then click the Amazon Redshift data source.
\n\n\n\n| Name | \nDescription | \n
\n\n\nName | \nThe data source name. This is how you refer to the data source in panels and queries. | \n
\n\nDefault | \nDefault data source means that it will be pre-selected for new panels. | \n
\n\nAuthentication Provider | \nSpecify the provider to get credentials. | \n
\n\nAccess Key ID | \nIf Access & secret key is selected, specify the Access Key of the security credentials to use. | \n
\n\nSecret Access Key | \nIf Access & secret key is selected, specify the Secret Key of the security credentials to use. | \n
\n\nCredentials Profile Name | \nSpecify the name of the profile to use (if you use ~/.aws/credentials file), leave blank for default. | \n
\n\nAssume Role Arn (optional) | \nSpecify the ARN of the role to assume. | \n
\n\nExternal ID (optional) | \nIf you are assuming a role in another account, that has been created with an external ID, specify the external ID here. | \n
\n\nEndpoint (optional) | \nOptionally, specify a custom endpoint for the service. | \n
\n\nDefault Region | \nRegion in which the cluster is deployed. | \n
\n\nAWS Secrets Manager | \nTo authenticate with Amazon Redshift using AWS Secrets Manager. | \n
\n\nTemporary credentials | \nTo authenticate with Amazon Redshift using temporary database credentials. | \n
\n\nServerless | \nTo use a Redshift Serverless workgroup. | \n
\n\nCluster Identifier | \nRedshift Provisioned Cluster to use (automatically set if using AWS Secrets Manager). | \n
\n\nWorkgroup | \nRedshift Serverless Workgroup to use. | \n
\n\nManaged Secret | \nWhen using AWS Secrets Manager, select the secret containing the credentials to access the database. Note that Provisioned and Serverless stores credentials in a different format. Refer to Storing database credentials in AWS Secrets Manager for instructions. | \n
\n\nDatabase User | \nUser of the database. Automatically set if using AWS Secrets Manager. | \n
\n\nDatabase | \nName of the database within the cluster or workgroup. | \n
\n\nSend events to Amazon EventBridge | \nTo send Data API events to Amazon EventBridge for monitoring purpose. | \n
\n
\nAuthentication
\nFor authentication options and configuration details, see AWS authentication topic.
\nIAM policies
\nGrafana needs permissions granted via IAM to be able to read Redshift metrics. You can attach these permissions to IAM roles and utilize Grafana's built-in support for assuming roles. Note that you will need to configure the required policy before adding the data source to Grafana. You can check some predefined policies by AWS here.
\nHere is a minimal policy example:
\n{\n "Version": "2012-10-17",\n "Statement": [\n {\n "Sid": "AllowReadingMetricsFromRedshift",\n "Effect": "Allow",\n "Action": [\n "redshift-data:ListTables",\n "redshift-data:DescribeTable",\n "redshift-data:GetStatementResult",\n "redshift-data:DescribeStatement",\n "redshift-data:ListStatements",\n "redshift-data:ListSchemas",\n "redshift-data:ExecuteStatement",\n "redshift-data:CancelStatement",\n "redshift:GetClusterCredentials",\n "redshift:DescribeClusters",\n "redshift-serverless:ListWorkgroups",\n "redshift-serverless:GetCredentials",\n "secretsmanager:ListSecrets"\n ],\n "Resource": "*"\n },\n {\n "Sid": "AllowReadingRedshiftQuerySecrets",\n "Effect": "Allow",\n "Action": ["secretsmanager:GetSecretValue"],\n "Resource": "*",\n "Condition": {\n "Null": {\n "secretsmanager:ResourceTag/RedshiftQueryOwner": "false"\n }\n }\n }\n ]\n}\n
\nQuery Redshift data
\nThe provided query editor is a standard SQL query editor. Grafana includes some macros to help with writing more complex timeseries queries.
\nMacros
\n\n\n\n| Macro | \nDescription | \nOutput example | \n
\n\n\n$__timeEpoch(column) | \n$__timeEpoch will be replaced by an expression to convert to a UNIX timestamp and rename the column to time | \nUNIX_TIMESTAMP(dateColumn) as "time" | \n
\n\n$__timeFilter(column) | \n$__timeFilter creates a conditional that filters the data (using column) based on the time range of the panel | \ntime BETWEEN '2017-07-18T11:15:52Z' AND '2017-07-18T11:15:52Z' | \n
\n\n$__timeFrom() | \n$__timeFrom outputs the current starting time of the range of the panel with quotes | \n'2017-07-18T11:15:52Z' | \n
\n\n$__timeTo() | \n$__timeTo outputs the current ending time of the range of the panel with quotes | \n'2017-07-18T11:15:52Z' | \n
\n\n$__timeGroup(column, '1m') | \n$__timeGroup groups timestamps so that there is only 1 point for every period on the graph | \nfloor(extract(epoch from time)/60)*60 AS "time" | \n
\n\n$__schema | \n$__schema uses the selected schema | \npublic | \n
\n\n$__table | \n$__table outputs a table from the given $__schema (it uses the public schema by default) | \nsales | \n
\n\n$__column | \n$__column outputs a column from the current $__table | \ndate | \n
\n\n$__unixEpochFilter(column) | \n$__unixEpochFilter be replaced by a time range filter using the specified column name with times represented as Unix timestamp | \ncolumn >= 1624406400 AND column <= 1624410000 | \n
\n\n$__unixEpochGroup(column) | \n$__unixEpochGroup is the same as $__timeGroup but for times stored as Unix timestamp | \nfloor(time/60)*60 AS "time" | \n
\n
\nTable Visualization
\nMost queries in Redshift will be best represented by a table visualization. Any query will display data in a table. If it can be queried, then it can be put in a table.
\nThis example returns results for a table visualization:
\nSELECT {column_1}, {column_2} FROM {table};\n
\nTimeseries / Graph visualizations
\nFor timeseries / graph visualizations, there are a few requirements:
\n\n- A column with a
date or datetime type must be selected \n- The
date column must be in ascending order (using ORDER BY column ASC) \n- A numeric column must also be selected
\n
\nTo make a more reasonable graph, be sure to use the $__timeFilter and $__timeGroup macros.
\nExample timeseries query:
\nSELECT\n avg(execution_time) AS average_execution_time,\n $__timeGroup(start_time, 'hour'),\n query_type\nFROM\n account_usage.query_history\nWHERE\n $__timeFilter(start_time)\ngroup by\n query_type,start_time\norder by\n start_time,query_type ASC;\n
\nFill value
\nWhen data frames are formatted as time series, you can choose how missing values should be filled. This in turn affects how they are rendered: with connected or disconnected values. To configure this value, change the "Fill Value" in the query editor.
\nInspecting the query
\nBecause Grafana supports macros that Redshift does not, the fully rendered query, which can be copy/pasted directly into Redshift, is visible in the Query Inspector. To view the full interpolated query, click the Query Inspector button, and the full query will be visible under the "Query" tab.
\nTemplates and variables
\nTo add a new Redshift query variable, refer to Add a query variable. Use your Redshift data source as your data source for the following available queries:
\nAny value queried from a Redshift table can be used as a variable. Be sure to avoid selecting too many values, as this can cause performance issues.
\nTo display a custom display name for a variable, you can use a query such as SELECT hostname AS text, id AS value FROM MyTable. In this case the variable value field must be a string type or cast to a string type.
\nAfter creating a variable, you can use it in your Redshift queries by using Variable syntax. For more information about variables, refer to Templates and variables.
\nAnnotations
\nAnnotations allow you to overlay rich event information on top of graphs. You can add annotations by clicking on panels or by adding annotation queries via the Dashboard menu / Annotations view.
\nExample query to automatically add annotations:
\nSELECT\n time as time,\n environment as tags,\n humidity as text\nFROM\n $__table\nWHERE\n $__timeFilter(time) and humidity > 95\n
\nThe following table represents the values of the columns taken into account to render annotations:
\n\n\n\n| Name | \nDescription | \n
\n\n\ntime | \nThe name of the date/time field. Could be a column with a native SQL date/time data type or epoch value. | \n
\n\ntimeend | \nOptional name of the end date/time field. Could be a column with a native SQL date/time data type or epoch value. (Grafana v6.6+) | \n
\n\ntext | \nEvent description field. | \n
\n\ntags | \nOptional field name to use for event tags as a comma separated string. | \n
\n
\nProvision Redshift data source
\nYou can configure the Redshift data source using configuration files with Grafana's provisioning system. For more information, refer to the provisioning docs page.
\nHere are some provisioning examples.
\nUsing AWS SDK (default)
\napiVersion: 1\ndatasources:\n - name: Redshift\n type: redshift\n jsonData:\n authType: default\n defaultRegion: eu-west-2\n
\nUsing credentials' profile name (non-default)
\napiVersion: 1\n\ndatasources:\n - name: Redshift\n type: redshift\n jsonData:\n authType: credentials\n defaultRegion: eu-west-2\n profile: secondary\n
\nUsing accessKey and secretKey
\napiVersion: 1\n\ndatasources:\n - name: Redshift\n type: grafana-redshift-datasource\n jsonData:\n authType: keys\n defaultRegion: eu-west-2\n secureJsonData:\n accessKey: '<your access key>'\n secretKey: '<your secret key>'\n
\nUsing AWS SDK Default and ARN of IAM Role to Assume
\napiVersion: 1\ndatasources:\n - name: Redshift\n type: grafana-redshift-datasource\n jsonData:\n authType: default\n assumeRoleArn: arn:aws:iam::123456789012:root\n defaultRegion: eu-west-2\n
\nPre-configured Redshift dashboards
\nRedshift data source ships with a pre-configured dashboard for some advanced monitoring parameters. This curated dashboard is based on similar dashboards in the AWS Labs repository for Redshift. Check it out for more details.
\nFollow these instructions for importing a dashboard in Grafana.
\nImported dashboards can be found in Configuration > Data Sources > select your Redshift data source > select the Dashboards tab to see available pre-made dashboards.
\nGet the most out of the plugin
\n\nAsync Query Data Support
\nAsync Query Data support enables an asynchronous query handling flow. With Async Query Data support enabled, queries will be handled over multiple requests (starting, checking its status, and fetching the results) instead of having a query be started and resolved over a single request. This is useful for queries that can potentially run for a long time and timeout. You'll need to ensure the IAM policy used by Grafana allows the following actions redshift-data:ListStatements and redshift-data:CancelStatement.
\nAsync query data support is enabled by default in all Redshift datasources.
\nAsync Query Caching
\nTo enable query caching for async queries, you need to be on Grafana version 10.1 or above, and to set the feature toggles useCachingService and awsAsyncQueryCaching to true. You'll also need to configure query caching for the specific Redshift datasource.
\n',
+ versions: [
+ {
+ version: '1.20.0',
+ createdAt: '2025-01-10T20:23:26.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.19.1',
+ createdAt: '2024-12-06T10:39:33.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.19.0',
+ createdAt: '2024-10-21T20:15:45.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.18.0',
+ createdAt: '2024-08-20T13:12:42.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.17.0',
+ createdAt: '2024-08-12T10:48:02.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.16.0',
+ createdAt: '2024-07-05T08:51:15.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.15.2',
+ createdAt: '2024-06-26T18:30:12.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.15.1',
+ createdAt: '2024-06-10T19:12:04.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.15.0',
+ createdAt: '2024-05-31T16:38:26.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.14.0',
+ createdAt: '2024-03-19T16:14:51.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.13.3',
+ createdAt: '2024-02-16T19:14:40.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.13.2',
+ createdAt: '2024-02-12T10:45:23.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.13.1',
+ createdAt: '2023-11-24T04:48:20.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.13.0',
+ createdAt: '2023-10-31T14:50:23.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.12.2',
+ createdAt: '2023-10-05T13:53:53.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.12.1',
+ createdAt: '2023-08-30T14:43:16.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.12.0',
+ createdAt: '2023-08-23T15:49:16.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.11.1',
+ createdAt: '2023-08-15T13:14:00.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.11.0',
+ createdAt: '2023-08-08T10:20:47.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.10.0',
+ createdAt: '2023-07-19T20:26:45.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.9.0',
+ createdAt: '2023-06-20T19:24:48.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.8.4',
+ createdAt: '2023-05-15T08:29:05.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.8.3',
+ createdAt: '2023-05-10T10:00:53.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.8.2',
+ createdAt: '2023-05-05T19:24:43.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.8.1',
+ createdAt: '2023-04-25T15:32:50.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.8.0',
+ createdAt: '2023-04-20T08:14:30.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.7.0',
+ createdAt: '2023-03-02T21:22:15.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.6.0',
+ createdAt: '2023-02-01T12:17:23.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.5.0',
+ createdAt: '2023-01-25T17:07:23.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.4.1',
+ createdAt: '2022-12-05T21:55:26.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.4.0',
+ createdAt: '2022-12-02T16:57:08.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.3.3',
+ createdAt: '2022-11-30T20:49:43.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.3.2',
+ createdAt: '2022-11-04T20:07:33.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.3.1',
+ createdAt: '2022-10-24T07:51:39.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.3.0',
+ createdAt: '2022-10-04T09:01:57.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.2.0',
+ createdAt: '2022-09-22T07:36:27.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.1.0',
+ createdAt: '2022-08-24T09:33:58.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.0.7',
+ createdAt: '2022-07-21T19:50:20.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.0.6',
+ createdAt: '2022-07-12T09:08:36.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.0.5',
+ createdAt: '2022-04-01T10:02:36.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.0.4',
+ createdAt: '2022-03-24T09:53:08.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.0.3',
+ createdAt: '2022-02-22T15:31:54.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.0.2',
+ createdAt: '2022-01-25T12:40:17.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.0.1',
+ createdAt: '2021-12-01T16:55:29.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '1.0.0',
+ createdAt: '2021-11-30T09:16:07.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ {
+ version: '0.4.1',
+ createdAt: '2021-11-24T08:43:33.000Z',
+ isCompatible: true,
+ grafanaDependency: '>=8.0.0',
+ angularDetected: false,
+ },
+ ],
+ statusContext: '',
+ changelog:
+ 'Changelog
\n1.20.0
\n\n- Add PDC support in #333
\n- Bump node dependencies with 29 updates, ignore react and react-dom major updates in dependabot in #336
\n- Bump the all-go-dependencies group across 1 directory with 3 updates in #335
\n- Add pre-commit hook in #327
\n
\n1.19.1
\n\n- Dependabot: Update dependencies in #302, #303, #313,\n#308, #323, #322:\n
\n- path-to-regexp from 1.8.0 to 1.9.0 in the npm_and_yarn group
\n- micromatch from 4.0.5 to 4.0.8 in the npm_and_yarn group
\n- actions/checkout from 2 to 4
\n- actions/setup-node from 3 to 4
\n- tibdex/github-app-token from 1.8.0 to 2.1.0
\n- github.com/aws/aws-sdk-go from 1.51.31 to 1.55.5
\n- github.com/grafana/grafana-plugin-sdk-go from 0.254.0 to 0.259.4
\n- github.com/grafana/grafana-aws-sdk from 0.31.3 to 0.31.4
\n- github.com/grafana/sqlds/v4 from 4.1.2 to 4.1.4
\n- braces from 3.0.2 to 3.0.3 in the npm_and_yarn group
\n- github.com/stretchr/testify from 1.9.0 to 1.10.0(#321)
\n- @emotion/css 11.13.4 11.13.5
\n- @grafana/async-query-data from 0.2.0 to 0.3.0
\n- @grafana/data from 11.2.2 to 11.3.1
\n- @grafana/experimental from 2.1.2 to 2.1.4
\n- @grafana/runtime from 11.2.2 to 11.3.1
\n- tslib from 2.8.0 to 2.8.1
\n- @babel/core from 7.25.8 to 7.26.0
\n- @grafana/eslint-config from 7.0.0 to 8.0.0
\n- @swc/core from 1.7.36 to 1.9.3
\n- @swc/helpers from 0.5.13 to 0.5.15
\n- @swc/jest from 0.2.36 to 0.2.37
\n- @testing-library/jest-dom from 6.6.0 to 6.6.3
\n- @types/jest from 29.5.13 to 29.5.14
\n- @types/lodash from 4.17.10 to 4.17.13
\n- @types/node from 22.7.5 to 22.10.1
\n- cspell from 8.15.2 to 8.16.1
\n- cypress from 7.7.0 to 13.16.0
\n- prettier from 3.3.3 to 3.4.1
\n- sass from 1.79.5 to 1.81.0
\n- sass-loader from 16.0.2 to 16.0.3
\n- typescript from 5.6.3 to 5.7.2
\n- webpack from 5.95.0 to 5.96.1
\n- cross-spawn from 7.0.3 to 7.0.6 in the npm_and_yarn group
\n
\n
\n1.19.0
\n\n- Chore: update dependencies #300
\n- Chore: bump dependencies #299
\n- Chore: Update plugin.json keywords #298
\n- Add dependabot for grafana/plugin-sdk-go #296
\n- Fix: don\'t check slice nilness before checking length #294
\n
\n1.18.0
\n\n- Add errorsource in #292
\n
\n1.17.0
\n\n- Update grafana/aws-sdk to get new regions
\n
\n1.16.0
\n\n- Migrate to new form styling in config and query editors in #287
\n
\n1.15.2
\n\n- Fix: use ReadAuthSettings to get authSettings in #288
\n
\n1.15.1
\n\n- Upgrade grafana-aws-sdk to replace
GetSession usages with GetSessionWithAuthSettings #284 \n
\n1.15.0
\n\n- Add keywords by @kevinwcyu in https://github.com/grafana/redshift-datasource/pull/273
\n- Add missing regions and use the region resource handler in the frontend by @iwysiu in https://github.com/grafana/redshift-datasource/pull/276
\n- Plugin.json: update schema reference URL by @leventebalogh in https://github.com/grafana/redshift-datasource/pull/277
\n- Fix E2E: Update region before sending the /secrets resource request by @idastambuk in https://github.com/grafana/redshift-datasource/pull/280
\n- Update for added context in grafana-aws-sdk by @njvrzm in https://github.com/grafana/redshift-datasource/pull/279
\n
\nNew Contributors
\n\n- @leventebalogh made their first contribution in https://github.com/grafana/redshift-datasource/pull/277
\n- @njvrzm made their first contribution in https://github.com/grafana/redshift-datasource/pull/279
\n
\n1.14.0
\n\n- Remove the redshiftAsyncQuerySupport feature toggle + styling improvements in https://github.com/grafana/redshift-datasource/pull/272
\n
\n1.13.3
\n\n- Upgrade @grafana/async-query-data from 0.1.10 to 0.1.11 https://github.com/grafana/redshift-datasource/pull/269
\n
\n1.13.2
\n\n- Update grafana/aws-sdk-go to 0.20.0 https://github.com/grafana/redshift-datasource/pull/268
\n
\n1.13.1
\n\n- Bump go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace from 0.37.0 to 0.44.0 by @dependabot in https://github.com/grafana/redshift-datasource/pull/257
\n- Upgrade grafana-plugin-sdk-go; add underscore, debug to package resolutions by @fridgepoet in https://github.com/grafana/redshift-datasource/pull/265
\n
\nFull Changelog: https://github.com/grafana/redshift-datasource/compare/v1.13.0...v1.13.1
\n1.13.0
\n\n- Migrate Query and config editors to new form styling under feature toggle #255
\n- Support Node 18 #249
\n- Fix datasource type in provisioning docs in #246
\n
\n1.12.2
\n\n- Fix async queries by not calling ListStatements in GetQueryID #252
\n
\n1.12.1
\n\n- upgrade @grafana/aws-sdk to fix a bug in temporary credentials
\n
\n1.12.0
\n\n- Update grafana-aws-sdk to v0.19.1 to add
il-central-1 to opt-in region list \n
\n1.11.1
\n\n- Upgrade @grafana/async-query-data to reduce minimum query time https://github.com/grafana/redshift-datasource/pull/237
\n
\n1.11.0
\n\n- Upgrade grafana/aws-sdk-react dependency #239
\n- Fix connection error when changing access and secret key #235
\n- Support async query caching #233
\n
\n1.10.0
\n\n- Add support for Redshift Serverless https://github.com/grafana/redshift-datasource/pull/228 by @yota-p
\n
\n1.9.0
\n\n- Upgrade @grafana/aws-sdk to v0.0.47 to support numeric values when applying template variables to SQL queries
\n- Fix async queries and expressions https://github.com/grafana/redshift-datasource/pull/225
\n
\n1.8.4
\n\n- Upgrade Readme.md re: Grafana 10 https://github.com/grafana/redshift-datasource/pull/224
\n
\n1.8.3
\n\n- Upgrade grafana/aws-sdk-react to 0.0.46 https://github.com/grafana/redshift-datasource/pull/223
\n
\n1.8.2
\n\n- Update grafana-aws-sdk version to include new region in opt-in region list https://github.com/grafana/grafana-aws-sdk/pull/80
\n- Security: Upgrade Go in build process to 1.20.4
\n- Update grafana-plugin-sdk-go version to 0.161.0 to avoid a potential http header problem. https://github.com/grafana/athena-datasource/issues/233
\n
\n1.8.1
\n\n- Update async-query-data with a fix for errors in #220
\n
\n1.8.0
\n\n- Update backend dependencies
\n
\n1.7.0
\n\n- Fix converting rows with FLOAT, FLOAT4, and BOOLEAN by @iwysiu in #213
\n- Add header component to Query Editor by @idastambuk in #214
\n- Use organization ISSUE_COMMANDS_TOKEN with reduced scope by @iwysiu in #210
\n
\n1.6.0
\n\n- Remove run and cancel buttons in annotations editor in https://github.com/grafana/redshift-datasource/pull/206
\n
\n1.5.0
\n\n- Migrate to create-plugin by @iwysiu in https://github.com/grafana/redshift-datasource/pull/195
\n- Update code coverage in workflow to latest by @idastambuk in https://github.com/grafana/redshift-datasource/pull/198
\n- Update @grafana/aws-sdk by @kevinwcyu in https://github.com/grafana/redshift-datasource/pull/199
\n- Update @grafana/ packages by @idastambuk in https://github.com/grafana/redshift-datasource/pull/201
\n- Upgrade grafana-aws-sdk to v0.12.0 by @fridgepoet in https://github.com/grafana/redshift-datasource/pull/202
\n
\n1.4.1
\n\n- Hide the stop button when async query data support is not enabled https://github.com/grafana/redshift-datasource/pull/196
\n
\n1.4.0
\n\n- Add Async Query Data Support https://github.com/grafana/redshift-datasource/pull/177
\n
\n1.3.3
\n\n- Update @grafana dependencies to v8.5.10 https://github.com/grafana/redshift-datasource/pull/194
\n
\n1.3.2
\n\n- Security: Upgrade Go in build process to 1.19.3
\n
\n1.3.1
\n\n- Security: Upgrade Go in build process to 1.19.2
\n
\n1.3.0
\n\n- Upgrade to grafana-aws-sdk v0.11.0 by @fridgepoet in https://github.com/grafana/redshift-datasource/pull/183
\n
\n1.2.0
\n\n- Add database security monitoring dashboards by @yota-p in https://github.com/grafana/redshift-datasource/pull/175
\n
\n1.1.0
\n\n- Add support for context aware autocompletion by @sunker in https://github.com/grafana/redshift-datasource/pull/174
\n
\n1.0.7
\n\n- Bug fix for auth issues with when using keys and dependency upgrades (https://github.com/grafana/redshift-datasource/pull/165)
\n- Updates to code coverage
\n
\n1.0.6
\nWhat\'s Changed
\n\n- Update grafana-aws-sdk by @andresmgot in https://github.com/grafana/redshift-datasource/pull/146
\n- Autocomplete: Render SQL editor in case feature toggle is enabled by @sunker in https://github.com/grafana/redshift-datasource/pull/151
\n- fix: WLM panels query fix by @vgkowski in https://github.com/grafana/redshift-datasource/pull/152
\n- Custom redshift language by @sunker in https://github.com/grafana/redshift-datasource/pull/154
\n- Align Monaco language with official language ref by @sunker in https://github.com/grafana/redshift-datasource/pull/156
\n
\nFull Changelog: https://github.com/grafana/redshift-datasource/compare/v1.0.5...v1.0.6
\n1.0.5
\n\n- Reduces backoff time factor to retrieve results.
\n- Upgrades internal dependencies.
\n
\n1.0.4
\n\n- Add details in the datasource card #130
\n- Enable WithEvent to send an event to the AWS EventBridge #132
\n
\n1.0.3
\nFixes bugs for Endpoint and Assume Role settings.
\n1.0.2
\nFixes a bug preventing from getting null values in a query.
\n1.0.1
\nFixes a bug preventing from creating several data sources of the plugin in the same instance.
\n1.0.0
\nInitial release.
\n0.4.1
\nImproved curated dashboard.
\n0.4.0
\nAllow to authenticate using AWS Secret Manager. More bug fixes.
\n0.3.0
\nThird preview release. Includes curated dashboard.
\n0.2.0
\nSecond release.
\n0.1.0
\nInitial release.
\n',
+ },
+} as CatalogPlugin;
diff --git a/public/app/features/plugins/admin/components/ConnectionsTab.test.tsx b/public/app/features/plugins/admin/components/ConnectionsTab.test.tsx
new file mode 100644
index 00000000000..2aa6f6510db
--- /dev/null
+++ b/public/app/features/plugins/admin/components/ConnectionsTab.test.tsx
@@ -0,0 +1,127 @@
+import { screen } from '@testing-library/react';
+import { render } from 'test/test-utils';
+
+import { DataSourceSettings } from '@grafana/data';
+import { config } from '@grafana/runtime';
+import { ContextSrv, setContextSrv } from 'app/core/services/context_srv';
+import { getMockDataSources } from 'app/features/datasources/__mocks__';
+import { AccessControlAction } from 'app/types';
+
+import { datasourcePlugin } from '../__mocks__/catalogPlugin.mock';
+
+import ConnectionsTab, { ConnectionsList } from './ConnectionsTab';
+
+jest.mock('app/features/datasources/state', () => ({
+ ...jest.requireActual('app/features/datasources/state'),
+ useLoadDataSource: jest.fn().mockReturnValue({ isLoading: false }),
+ getDataSources: jest.fn(() => 'getDataSources mock implementation'),
+}));
+
+const setupContextSrv = () => {
+ const testContextSrv = new ContextSrv();
+ testContextSrv.user.permissions = {
+ [AccessControlAction.DataSourcesCreate]: true,
+ [AccessControlAction.DataSourcesWrite]: true,
+ [AccessControlAction.DataSourcesExplore]: true,
+ };
+
+ setContextSrv(testContextSrv);
+};
+
+describe('', () => {
+ const oldExporeEnabled = config.exploreEnabled;
+ const olddatasourceConnectionsTab = config.featureToggles.datasourceConnectionsTab;
+ config.exploreEnabled = true;
+ config.featureToggles.datasourceConnectionsTab = true;
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ afterAll(() => {
+ config.exploreEnabled = oldExporeEnabled;
+ config.featureToggles.datasourceConnectionsTab = olddatasourceConnectionsTab;
+ });
+
+ it('should onnly render list of datasources with type=plugin.id', async () => {
+ setupContextSrv();
+ const mockedConnections = getMockDataSources(3, { type: datasourcePlugin.id });
+ mockedConnections[2].type = 'other-plugin-id';
+ jest.requireMock('app/features/datasources/state').getDataSources.mockReturnValue(mockedConnections);
+
+ render();
+
+ expect(await screen.findAllByRole('listitem')).toHaveLength(2);
+ expect(await screen.findAllByRole('heading')).toHaveLength(2);
+ expect(await screen.findByRole('link', { name: /Connections - Data sources/i })).toBeVisible();
+ expect(await screen.findAllByRole('link', { name: /Build a dashboard/i })).toHaveLength(2);
+ expect(await screen.findAllByRole('link', { name: 'Explore' })).toHaveLength(2);
+ });
+
+ it('should render add new datasource button when no datasources are defined', async () => {
+ setupContextSrv();
+ jest.requireMock('app/features/datasources/state').getDataSources.mockReturnValue(getMockDataSources(1));
+ render();
+
+ expect(screen.getByText('Add new data source')).toBeVisible();
+ expect(screen.getByText(`No data sources defined`)).toBeVisible();
+ });
+
+ describe('', () => {
+ it('should render list of datasources', async () => {
+ const dss = getMockDataSources(2, { type: datasourcePlugin.id });
+ render(
+
+ );
+
+ expect(await screen.findAllByRole('listitem')).toHaveLength(2);
+ expect(await screen.findAllByRole('heading')).toHaveLength(2);
+ expect(await screen.findByRole('link', { name: /Connections - Data sources/i })).toBeVisible();
+ expect(await screen.findAllByRole('link', { name: /Build a dashboard/i })).toHaveLength(2);
+ expect(await screen.findAllByRole('link', { name: 'Explore' })).toHaveLength(2);
+ });
+
+ it('should not render Explore button when user has no access', async () => {
+ const dss = getMockDataSources(2, { type: datasourcePlugin.id });
+ render(
+
+ );
+
+ expect(await screen.findAllByRole('listitem')).toHaveLength(2);
+ expect(await screen.findAllByRole('heading')).toHaveLength(2);
+ expect(await screen.findByRole('link', { name: /Connections - Data sources/i })).toBeVisible();
+ expect(await screen.findAllByRole('link', { name: /Build a dashboard/i })).toHaveLength(2);
+ expect(screen.queryAllByRole('link', { name: 'Explore' })).toHaveLength(0);
+ });
+
+ it('should render add new datasource button when no datasources are defined', async () => {
+ const dss = [] as DataSourceSettings[];
+ render(
+
+ );
+
+ expect(screen.getByText('Add new data source')).toBeVisible();
+ expect(screen.getByText(`No data sources defined`)).toBeVisible();
+ });
+ });
+});
diff --git a/public/app/features/plugins/admin/components/ConnectionsTab.tsx b/public/app/features/plugins/admin/components/ConnectionsTab.tsx
new file mode 100644
index 00000000000..d4896232277
--- /dev/null
+++ b/public/app/features/plugins/admin/components/ConnectionsTab.tsx
@@ -0,0 +1,107 @@
+import { css } from '@emotion/css';
+
+import { GrafanaTheme2 } from '@grafana/data';
+import { EmptyState, Stack, TextLink, useStyles2 } from '@grafana/ui';
+import { t, Trans } from 'app/core/internationalization';
+import { contextSrv } from 'app/core/services/context_srv';
+import { ViewProps } from 'app/features/datasources/components/DataSourcesList';
+import { DataSourcesListCard } from 'app/features/datasources/components/DataSourcesListCard';
+import { getDataSources, useLoadDataSources } from 'app/features/datasources/state';
+import { AccessControlAction, useSelector } from 'app/types';
+
+import { CatalogPlugin } from '../types';
+
+import { GetStartedWithDataSource } from './GetStartedWithPlugin/GetStartedWithDataSource';
+
+interface Props {
+ plugin: CatalogPlugin;
+}
+
+export default function ConnectionsTab({ plugin }: Props) {
+ const { isLoading } = useLoadDataSources();
+
+ const allDataSources = useSelector((state) => getDataSources(state.dataSources));
+ const dataSources = allDataSources.filter((ds) => ds.type === plugin.id);
+ const hasWriteRights = contextSrv.hasPermission(AccessControlAction.DataSourcesWrite);
+ const hasExploreRights = contextSrv.hasAccessToExplore();
+
+ return (
+
+ );
+}
+
+type ListProps = Omit & {
+ plugin: CatalogPlugin;
+};
+
+export function ConnectionsList({
+ dataSources,
+ dataSourcesCount,
+ isLoading,
+ hasWriteRights,
+ hasExploreRights,
+ plugin,
+}: ListProps) {
+ const styles = useStyles2(getStyles);
+
+ if (!isLoading && dataSourcesCount === 0) {
+ return (
+ }
+ message={t('data-source-list.empty-state.title', 'No data sources defined')}
+ />
+ );
+ }
+
+ const getDataSourcesList = () => {
+ if (isLoading) {
+ return new Array(5)
+ .fill(null)
+ .map((_, index) => );
+ }
+
+ return dataSources.map((dataSource) => (
+
+
+
+ ));
+ };
+
+ return (
+
+
+
+ The data source connections below are all {'{{pluginName}}'}. You can find all of your data source connections
+ of all types in{' '}
+
+ Connections -{' '}
+ Data sources.
+
+
+
+
+
+ );
+}
+
+const getStyles = (theme: GrafanaTheme2) => {
+ return {
+ list: css({
+ listStyle: 'none',
+ display: 'grid',
+ // gap: '8px', Add back when legacy support for old Card interface is dropped
+ }),
+ };
+};
diff --git a/public/app/features/plugins/admin/components/PluginDetailsBody.test.tsx b/public/app/features/plugins/admin/components/PluginDetailsBody.test.tsx
index 39770882e47..cceb1058832 100644
--- a/public/app/features/plugins/admin/components/PluginDetailsBody.test.tsx
+++ b/public/app/features/plugins/admin/components/PluginDetailsBody.test.tsx
@@ -99,4 +99,22 @@ describe('PluginDetailsBody', () => {
});
});
});
+
+ it('should render data source connections tab content for installed data source plugin', async () => {
+ const plugin = getCatalogPluginMock({ type: PluginType.datasource });
+ config.featureToggles.datasourceConnectionsTab = true;
+ await act(async () => {
+ renderWithStore(
+
+ );
+ });
+
+ expect(screen.getByText('No data sources defined')).toBeVisible();
+ });
});
diff --git a/public/app/features/plugins/admin/components/PluginDetailsBody.tsx b/public/app/features/plugins/admin/components/PluginDetailsBody.tsx
index d45024f59b7..6a1c3259321 100644
--- a/public/app/features/plugins/admin/components/PluginDetailsBody.tsx
+++ b/public/app/features/plugins/admin/components/PluginDetailsBody.tsx
@@ -1,7 +1,7 @@
import { css } from '@emotion/css';
import { useMemo } from 'react';
-import { AppPlugin, GrafanaTheme2, PluginContextProvider, UrlQueryMap } from '@grafana/data';
+import { AppPlugin, GrafanaTheme2, PluginContextProvider, UrlQueryMap, PluginType } from '@grafana/data';
import { config } from '@grafana/runtime';
import { PageInfoItem } from '@grafana/runtime/src/components/PluginPage';
import { CellProps, Column, InteractiveTable, Stack, useStyles2 } from '@grafana/ui';
@@ -14,6 +14,7 @@ import { usePluginConfig } from '../hooks/usePluginConfig';
import { CatalogPlugin, Permission, PluginTabIds } from '../types';
import { AppConfigCtrlWrapper } from './AppConfigWrapper';
+import Connections from './ConnectionsTab';
import { PluginDashboards } from './PluginDashboards';
import { PluginUsage } from './PluginUsage';
@@ -30,7 +31,6 @@ type Cell = CellProps> = useMemo(
() => [
{
@@ -91,6 +91,18 @@ export function PluginDetailsBody({ plugin, queryParams, pageId, info, showDetai
);
}
+ if (
+ config.featureToggles.datasourceConnectionsTab &&
+ pageId === PluginTabIds.DATASOURCE_CONNECTIONS &&
+ plugin.type === PluginType.datasource
+ ) {
+ return (
+
+
+
+ );
+ }
+
// Permissions will be returned in the iam field for installed plugins and in the details.iam field when fetching details from gcom
const permissions = plugin.iam?.permissions || plugin.details?.iam?.permissions;
diff --git a/public/app/features/plugins/admin/components/PluginDetailsPage.test.tsx b/public/app/features/plugins/admin/components/PluginDetailsPage.test.tsx
index c256b7f412a..4f1bde85393 100644
--- a/public/app/features/plugins/admin/components/PluginDetailsPage.test.tsx
+++ b/public/app/features/plugins/admin/components/PluginDetailsPage.test.tsx
@@ -76,7 +76,12 @@ jest.mock('../state/hooks', () => ({
useFetchDetailsLazy: () => jest.fn(),
}));
+jest.mock('../hooks/usePluginConfig', () => ({
+ usePluginConfig: jest.fn().mockReturnValue({ value: {}, loading: false }),
+}));
+
const mockUseGetSingle = jest.requireMock('../state/hooks').useGetSingle;
+const mockUsePluginConfig = jest.requireMock('../hooks/usePluginConfig').usePluginConfig;
describe('PluginDetailsPage', () => {
beforeEach(() => {
@@ -141,4 +146,12 @@ describe('PluginDetailsPage', () => {
render();
expect(screen.getByRole('tab', { name: 'Plugin details' })).toBeInTheDocument();
});
+
+ it('should show "Datasource connections" tab when plugin is type of datasource', () => {
+ config.featureToggles.datasourceConnectionsTab = true;
+ mockUseGetSingle.mockReturnValue({ ...plugin, type: PluginType.datasource });
+ mockUsePluginConfig.mockReturnValue({ value: {}, loading: false });
+ render();
+ expect(screen.getByRole('tab', { name: 'Data source connections' })).toBeVisible();
+ });
});
diff --git a/public/app/features/plugins/admin/hooks/usePluginConfig.tsx b/public/app/features/plugins/admin/hooks/usePluginConfig.tsx
index bf28f69ecf9..79feffc09e9 100644
--- a/public/app/features/plugins/admin/hooks/usePluginConfig.tsx
+++ b/public/app/features/plugins/admin/hooks/usePluginConfig.tsx
@@ -15,7 +15,6 @@ export const usePluginConfig = (plugin?: CatalogPlugin) => {
config.pluginAdminExternalManageEnabled && config.featureToggles.managedPluginsInstall
? plugin.isFullyInstalled
: plugin.isInstalled;
-
if (isPluginInstalled && !plugin.isDisabled) {
return loadPlugin(plugin.id);
}
diff --git a/public/app/features/plugins/admin/hooks/usePluginDetailsTabs.tsx b/public/app/features/plugins/admin/hooks/usePluginDetailsTabs.tsx
index eebb94e5748..e9c50269d15 100644
--- a/public/app/features/plugins/admin/hooks/usePluginDetailsTabs.tsx
+++ b/public/app/features/plugins/admin/hooks/usePluginDetailsTabs.tsx
@@ -99,6 +99,16 @@ export const usePluginDetailsTabs = (
});
}
+ if (config.featureToggles.datasourceConnectionsTab && plugin?.type === PluginType.datasource) {
+ navModelChildren.push({
+ text: PluginTabLabels.DATASOURCE_CONNECTIONS,
+ icon: 'database',
+ id: PluginTabIds.DATASOURCE_CONNECTIONS,
+ url: `${pathname}?page=${PluginTabIds.DATASOURCE_CONNECTIONS}`,
+ active: PluginTabIds.DATASOURCE_CONNECTIONS === currentPageId,
+ });
+ }
+
if (!canConfigurePlugins) {
return navModelChildren;
}
diff --git a/public/app/features/plugins/admin/types.ts b/public/app/features/plugins/admin/types.ts
index ea61ba34cb0..082d3cdf941 100644
--- a/public/app/features/plugins/admin/types.ts
+++ b/public/app/features/plugins/admin/types.ts
@@ -257,6 +257,7 @@ export enum PluginTabLabels {
IAM = 'IAM',
CHANGELOG = 'Changelog',
PLUGINDETAILS = 'Plugin details',
+ DATASOURCE_CONNECTIONS = 'Data source connections',
}
export enum PluginTabIds {
@@ -268,6 +269,7 @@ export enum PluginTabIds {
IAM = 'iam',
CHANGELOG = 'changelog',
PLUGINDETAILS = 'right-panel',
+ DATASOURCE_CONNECTIONS = 'datasource-connections',
}
export enum RequestStatus {
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index b372b474e2d..7f6844d6779 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -2540,6 +2540,9 @@
}
},
"details": {
+ "connections-tab": {
+ "description": "The data source connections below are all {{pluginName}}. You can find all of your data source connections of all types in <4><0>Connections0> - <3>Data sources3>.4>"
+ },
"labels": {
"contactGrafanaLabs": "Contact Grafana Labs",
"dependencies": "Dependencies",
diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json
index 0766bbfa14c..c87e26d6a1e 100644
--- a/public/locales/pseudo-LOCALE/grafana.json
+++ b/public/locales/pseudo-LOCALE/grafana.json
@@ -2540,6 +2540,9 @@
}
},
"details": {
+ "connections-tab": {
+ "description": "Ŧĥę đäŧä şőūřčę čőʼnʼnęčŧįőʼnş þęľőŵ äřę äľľ {{pluginName}}. Ÿőū čäʼn ƒįʼnđ äľľ őƒ yőūř đäŧä şőūřčę čőʼnʼnęčŧįőʼnş őƒ äľľ ŧypęş įʼn <4><0>Cőʼnʼnęčŧįőʼnş0> - <3>Đäŧä şőūřčęş3>.4>"
+ },
"labels": {
"contactGrafanaLabs": "Cőʼnŧäčŧ Ğřäƒäʼnä Ŀäþş",
"dependencies": "Đępęʼnđęʼnčįęş",