Compare commits

...

22 Commits

Author SHA1 Message Date
Dafydd
f96c50c4b7 update openapi snapshots 2026-01-06 09:52:46 +00:00
Dafydd
c4d0f6fb18 go mod tidy 2026-01-06 09:52:46 +00:00
Dafydd
c10c983dd9 inline function that was only used once 2026-01-06 09:52:45 +00:00
Dafydd
c04396c81f wip: update tests and frontend to reflect api group name changes 2026-01-06 09:52:45 +00:00
Dafydd
3d1ad8a791 wip: update comments 2026-01-06 09:52:45 +00:00
Dafydd
ebd62c91eb wip checkpoint: can register two api groups now, with use-explicit-group-name flag 2026-01-06 09:52:44 +00:00
Dafydd
16afacd859 wip: checkpoint: some tidy up after rebase. It runs for single runtime config, but not confident on functionality being complete 2026-01-06 09:52:44 +00:00
Dafydd
fa54279281 rm conflict artifact 2026-01-06 09:52:44 +00:00
Dafydd
d366270b1f use shorter name in scoped plugin provider 2026-01-06 09:52:43 +00:00
Dafydd
c69b5250ec wip: update experimental client logic to remove the reliance on the group name suffix 2026-01-06 09:52:43 +00:00
Dafydd
2a36a004b7 rm unnecessary change 2026-01-06 09:52:42 +00:00
Dafydd
b2b719a03f linting 2026-01-06 09:52:42 +00:00
Dafydd
7e8f0a4819 reset go.mod 2026-01-06 09:52:41 +00:00
Dafydd
26d299227a re-add enterprise imports 2026-01-06 09:52:41 +00:00
Dafydd
c63d3ed437 make update-workspace 2026-01-06 09:52:41 +00:00
Dafydd
1f4bc62831 update openAPI integration tests and TestIntegrationTestDatasource 2026-01-06 09:52:41 +00:00
Dafydd
8515c59650 nolint 2026-01-06 09:52:40 +00:00
Dafydd
0d2cac1ee8 use config structu in ds api builder 2026-01-06 09:52:40 +00:00
Dafydd
bc1b7250dd tidy up and only register the short names for the ds endpoints, since we are not using those 2026-01-06 09:52:40 +00:00
Dafydd
d4b572c234 add nolint check for global feature 2026-01-06 09:52:40 +00:00
Dafydd
2f29676d76 use testdata full id, and dont bother sanitising plugin IDs for apiGroup names 2026-01-06 09:52:39 +00:00
Dafydd
e329521c49 also register short apiGroup names for core datasources 2026-01-06 09:52:38 +00:00
7 changed files with 52 additions and 60 deletions

View File

@@ -71,7 +71,6 @@ type cachingDatasourceProvider struct {
}
func (q *cachingDatasourceProvider) GetDatasourceProvider(pluginJson plugins.JSONData) PluginDatasourceProvider {
group, _ := plugins.GetDatasourceGroupNameFromPluginID(pluginJson.ID)
return &scopedDatasourceProvider{
plugin: pluginJson,
dsService: q.dsService,
@@ -81,7 +80,7 @@ func (q *cachingDatasourceProvider) GetDatasourceProvider(pluginJson plugins.JSO
mapper: q.converter.mapper,
plugin: pluginJson.ID,
alias: pluginJson.AliasIDs,
group: group,
group: pluginJson.ID,
},
}
}

View File

@@ -36,6 +36,11 @@ var (
_ builder.APIGroupBuilder = (*DataSourceAPIBuilder)(nil)
)
type DataSourceAPIBuilderConfig struct {
LoadQueryTypes bool
UseDualWriter bool
}
// DataSourceAPIBuilder is used just so wire has something unique to return
type DataSourceAPIBuilder struct {
datasourceResourceInfo utils.ResourceInfo
@@ -45,7 +50,7 @@ type DataSourceAPIBuilder struct {
contextProvider PluginContextWrapper
accessControl accesscontrol.AccessControl
queryTypes *queryV0.QueryTypeDefinitionList
configCrudUseNewApis bool
cfg DataSourceAPIBuilderConfig
dataSourceCRUDMetric *prometheus.HistogramVec
}
@@ -88,20 +93,24 @@ func RegisterAPIService(
return nil, fmt.Errorf("plugin client is not a PluginClient: %T", pluginClient)
}
groupName := pluginJSON.ID + ".datasource.grafana.app"
builder, err = NewDataSourceAPIBuilder(
groupName,
pluginJSON,
client,
datasources.GetDatasourceProvider(pluginJSON),
contextProvider,
accessControl,
//nolint:staticcheck // not yet migrated to OpenFeature
features.IsEnabledGlobally(featuremgmt.FlagDatasourceQueryTypes),
//nolint:staticcheck // not yet migrated to OpenFeature
features.IsEnabledGlobally(featuremgmt.FlagQueryServiceWithConnections),
DataSourceAPIBuilderConfig{
//nolint:staticcheck // not yet migrated to OpenFeature
LoadQueryTypes: features.IsEnabledGlobally(featuremgmt.FlagDatasourceQueryTypes),
UseDualWriter: false,
},
)
if err != nil {
return nil, err
}
builder.SetDataSourceCRUDMetrics(dataSourceCRUDMetric)
apiRegistrar.RegisterAPI(builder)
@@ -119,31 +128,27 @@ type PluginClient interface {
}
func NewDataSourceAPIBuilder(
groupName string,
plugin plugins.JSONData,
client PluginClient,
datasources PluginDatasourceProvider,
contextProvider PluginContextWrapper,
accessControl accesscontrol.AccessControl,
loadQueryTypes bool,
configCrudUseNewApis bool,
cfg DataSourceAPIBuilderConfig,
) (*DataSourceAPIBuilder, error) {
group, err := plugins.GetDatasourceGroupNameFromPluginID(plugin.ID)
if err != nil {
return nil, err
}
builder := &DataSourceAPIBuilder{
datasourceResourceInfo: datasourceV0.DataSourceResourceInfo.WithGroupAndShortName(group, plugin.ID),
datasourceResourceInfo: datasourceV0.DataSourceResourceInfo.WithGroupAndShortName(groupName, plugin.ID),
pluginJSON: plugin,
client: client,
datasources: datasources,
contextProvider: contextProvider,
accessControl: accessControl,
configCrudUseNewApis: configCrudUseNewApis,
cfg: cfg,
}
if loadQueryTypes {
var err error
if cfg.LoadQueryTypes {
// In the future, this will somehow come from the plugin
builder.queryTypes, err = getHardcodedQueryTypes(group)
builder.queryTypes, err = getHardcodedQueryTypes(groupName)
}
return builder, err
}
@@ -153,9 +158,9 @@ func getHardcodedQueryTypes(group string) (*queryV0.QueryTypeDefinitionList, err
var err error
var raw json.RawMessage
switch group {
case "testdata.datasource.grafana.app":
case "testdata.datasource.grafana.app", "grafana-testdata-datasource":
raw, err = kinds.QueryTypeDefinitionListJSON()
case "prometheus.datasource.grafana.app":
case "prometheus.datasource.grafana.app", "prometheus":
raw, err = models.QueryTypeDefinitionListJSON()
}
if err != nil {
@@ -232,7 +237,7 @@ func (b *DataSourceAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver
storage["connections"] = &noopREST{} // hidden from openapi
storage["connections/query"] = storage[ds.StoragePath("query")] // deprecated in openapi
if b.configCrudUseNewApis {
if b.cfg.UseDualWriter {
legacyStore := &legacyStorage{
datasources: b.datasources,
resourceInfo: &ds,

View File

@@ -62,7 +62,7 @@ func TestIntegrationTestDatasource(t *testing.T) {
t.Run("Admin configs", func(t *testing.T) {
client := helper.Org1.Admin.ResourceClient(t, schema.GroupVersionResource{
Group: "testdata.datasource.grafana.app",
Group: "grafana-testdata-datasource.datasource.grafana.app",
Version: "v0alpha1",
Resource: "datasources",
}).Namespace("default")
@@ -92,7 +92,7 @@ func TestIntegrationTestDatasource(t *testing.T) {
t.Run("Call subresources", func(t *testing.T) {
client := helper.Org1.Admin.ResourceClient(t, schema.GroupVersionResource{
Group: "testdata.datasource.grafana.app",
Group: "grafana-testdata-datasource.datasource.grafana.app",
Version: "v0alpha1",
Resource: "datasources",
}).Namespace("default")
@@ -128,7 +128,7 @@ func TestIntegrationTestDatasource(t *testing.T) {
raw := apis.DoRequest[any](helper, apis.RequestParams{
User: helper.Org1.Admin,
Method: "GET",
Path: "/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/default/datasources/test/resource",
Path: "/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/default/datasources/test/resource",
}, nil)
// endpoint is disabled currently because it has not been
// sufficiently tested.

View File

@@ -2,10 +2,10 @@
"openapi": "3.0.0",
"info": {
"description": "Generates test data in different forms",
"title": "testdata.datasource.grafana.app/v0alpha1"
"title": "grafana-testdata-datasource.datasource.grafana.app/v0alpha1"
},
"paths": {
"/apis/testdata.datasource.grafana.app/v0alpha1/": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/": {
"get": {
"tags": [
"API Discovery"
@@ -36,7 +36,7 @@
}
}
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/connections/{name}/query": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/connections/{name}/query": {
"post": {
"tags": [
"Connections (deprecated)"
@@ -68,7 +68,7 @@
"deprecated": true,
"x-kubernetes-action": "connect",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "QueryDataResponse"
}
@@ -96,7 +96,7 @@
}
]
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources": {
"get": {
"tags": [
"DataSource"
@@ -137,7 +137,7 @@
},
"x-kubernetes-action": "list",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "DataSource"
}
@@ -254,7 +254,7 @@
}
]
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}": {
"get": {
"tags": [
"DataSource"
@@ -285,7 +285,7 @@
},
"x-kubernetes-action": "get",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "DataSource"
}
@@ -322,7 +322,7 @@
}
]
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/health": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/health": {
"get": {
"tags": [
"DataSource"
@@ -343,7 +343,7 @@
},
"x-kubernetes-action": "connect",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "HealthCheckResult"
}
@@ -371,7 +371,7 @@
}
]
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/query": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/query": {
"post": {
"tags": [
"DataSource"
@@ -401,7 +401,7 @@
},
"x-kubernetes-action": "connect",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "QueryDataResponse"
}
@@ -429,7 +429,7 @@
}
]
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/resource": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/resource": {
"get": {
"tags": [
"DataSource"
@@ -450,7 +450,7 @@
},
"x-kubernetes-action": "connect",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "Status"
}
@@ -478,7 +478,7 @@
}
]
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/queryconvert/{name}": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/queryconvert/{name}": {
"post": {
"tags": [
"QueryDataRequest"
@@ -499,7 +499,7 @@
},
"x-kubernetes-action": "connect",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "QueryDataRequest"
}
@@ -620,7 +620,7 @@
"apiVersion": {
"type": "string",
"enum": [
"testdata.datasource.grafana.app/v0alpha1"
"grafana-testdata-datasource.datasource.grafana.app/v0alpha1"
]
},
"kind": {
@@ -660,7 +660,7 @@
},
"x-kubernetes-group-version-kind": [
{
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"kind": "DataSource",
"version": "v0alpha1"
}
@@ -703,7 +703,7 @@
},
"x-kubernetes-group-version-kind": [
{
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"kind": "DataSourceList",
"version": "v0alpha1"
}
@@ -744,7 +744,7 @@
},
"x-kubernetes-group-version-kind": [
{
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"kind": "HealthCheckResult",
"version": "v0alpha1"
}
@@ -833,7 +833,7 @@
},
"x-kubernetes-group-version-kind": [
{
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"kind": "QueryDataResponse",
"version": "v0alpha1"
}

View File

@@ -128,7 +128,7 @@ func TestIntegrationOpenAPIs(t *testing.T) {
Group: "shorturl.grafana.app",
Version: "v1beta1",
}, {
Group: "testdata.datasource.grafana.app",
Group: "grafana-testdata-datasource.datasource.grafana.app",
Version: "v0alpha1",
}, {
Group: "logsdrilldown.grafana.app",

View File

@@ -28,18 +28,17 @@ describe('DatasourceAPIVersions', () => {
it('get', async () => {
const getMock = jest.fn().mockResolvedValue({
groups: [
{ name: 'testdata.datasource.grafana.app', preferredVersion: { version: 'v1' } },
{ name: 'grafana-testdata-datasource.datasource.grafana.app', preferredVersion: { version: 'v1' } },
{ name: 'prometheus.datasource.grafana.app', preferredVersion: { version: 'v2' } },
{ name: 'myorg-myplugin.datasource.grafana.app', preferredVersion: { version: 'v3' } },
],
});
getBackendSrv().get = getMock;
const apiVersions = new DatasourceAPIVersions();
expect(await apiVersions.get('testdata')).toBe('v1');
expect(await apiVersions.get('grafana-testdata-datasource')).toBe('v1');
expect(await apiVersions.get('prometheus')).toBe('v2');
expect(await apiVersions.get('graphite')).toBeUndefined();
expect(await apiVersions.get('myorg-myplugin-datasource')).toBe('v3');
expect(await apiVersions.get('myorg-myplugin')).toBe('v3');
expect(getMock).toHaveBeenCalledTimes(1);
expect(getMock).toHaveBeenCalledWith('/apis');
});

View File

@@ -162,17 +162,6 @@ export class DatasourceAPIVersions {
if (group.name.includes('datasource.grafana.app')) {
const id = group.name.split('.')[0];
apiVersions[id] = group.preferredVersion.version;
// workaround for plugins that don't append '-datasource' for the group name
// e.g. org-plugin-datasource uses org-plugin.datasource.grafana.app
if (!id.endsWith('-datasource')) {
if (!id.includes('-')) {
// workaroud for Grafana plugins that don't include the org either
// e.g. testdata uses testdata.datasource.grafana.app
apiVersions[`grafana-${id}-datasource`] = group.preferredVersion.version;
} else {
apiVersions[`${id}-datasource`] = group.preferredVersion.version;
}
}
}
});
this.apiVersions = apiVersions;