K8s/Dashboards: Replace multiple calls with a single endpoint (#89639)

pull/89598/head
Ryan McKinley 1 year ago committed by GitHub
parent 3ede2dba24
commit 4651506319
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 16
      pkg/apis/dashboard/v0alpha1/types.go
  2. 43
      pkg/apis/dashboard/v0alpha1/zz_generated.deepcopy.go
  3. 62
      pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go
  4. 7
      pkg/registry/apis/dashboard/register.go
  5. 46
      pkg/registry/apis/dashboard/sub_dto.go
  6. 4
      pkg/tests/apis/dashboard/dashboards_test.go
  7. 8
      public/app/features/dashboard-scene/scene/Scopes/ScopesScene.test.tsx
  8. 15
      public/app/features/dashboard/api/dashboard_api.ts

@ -89,11 +89,21 @@ type VersionsQueryOptions struct {
Version int64 `json:"version,omitempty"` Version int64 `json:"version,omitempty"`
} }
// Information about how the requesting user can use a given dashboard // This is like the legacy DTO where access and metadata are all returned in a single call
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type DashboardAccessInfo struct { type DashboardWithAccessInfo struct {
metav1.TypeMeta `json:",inline"` Dashboard `json:",inline"`
Access DashboardAccess `json:"access"`
}
// Information about how the requesting user can use a given dashboard
type DashboardAccess struct {
// Metadata fields
Slug string `json:"slug,omitempty"`
Url string `json:"url,omitempty"`
// The permissions part
CanSave bool `json:"canSave"` CanSave bool `json:"canSave"`
CanEdit bool `json:"canEdit"` CanEdit bool `json:"canEdit"`
CanAdmin bool `json:"canAdmin"` CanAdmin bool `json:"canAdmin"`

@ -73,9 +73,8 @@ func (in *Dashboard) DeepCopyObject() runtime.Object {
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DashboardAccessInfo) DeepCopyInto(out *DashboardAccessInfo) { func (in *DashboardAccess) DeepCopyInto(out *DashboardAccess) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta
if in.AnnotationsPermissions != nil { if in.AnnotationsPermissions != nil {
in, out := &in.AnnotationsPermissions, &out.AnnotationsPermissions in, out := &in.AnnotationsPermissions, &out.AnnotationsPermissions
*out = new(AnnotationPermission) *out = new(AnnotationPermission)
@ -84,24 +83,16 @@ func (in *DashboardAccessInfo) DeepCopyInto(out *DashboardAccessInfo) {
return return
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardAccessInfo. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardAccess.
func (in *DashboardAccessInfo) DeepCopy() *DashboardAccessInfo { func (in *DashboardAccess) DeepCopy() *DashboardAccess {
if in == nil { if in == nil {
return nil return nil
} }
out := new(DashboardAccessInfo) out := new(DashboardAccess)
in.DeepCopyInto(out) in.DeepCopyInto(out)
return out return out
} }
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *DashboardAccessInfo) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DashboardList) DeepCopyInto(out *DashboardList) { func (in *DashboardList) DeepCopyInto(out *DashboardList) {
*out = *in *out = *in
@ -263,6 +254,32 @@ func (in *DashboardVersionList) DeepCopyObject() runtime.Object {
return nil return nil
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DashboardWithAccessInfo) DeepCopyInto(out *DashboardWithAccessInfo) {
*out = *in
in.Dashboard.DeepCopyInto(&out.Dashboard)
in.Access.DeepCopyInto(&out.Access)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardWithAccessInfo.
func (in *DashboardWithAccessInfo) DeepCopy() *DashboardWithAccessInfo {
if in == nil {
return nil
}
out := new(DashboardWithAccessInfo)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *DashboardWithAccessInfo) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VersionsQueryOptions) DeepCopyInto(out *VersionsQueryOptions) { func (in *VersionsQueryOptions) DeepCopyInto(out *VersionsQueryOptions) {
*out = *in *out = *in

@ -19,13 +19,14 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.AnnotationActions": schema_pkg_apis_dashboard_v0alpha1_AnnotationActions(ref), "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.AnnotationActions": schema_pkg_apis_dashboard_v0alpha1_AnnotationActions(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.AnnotationPermission": schema_pkg_apis_dashboard_v0alpha1_AnnotationPermission(ref), "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.AnnotationPermission": schema_pkg_apis_dashboard_v0alpha1_AnnotationPermission(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.Dashboard": schema_pkg_apis_dashboard_v0alpha1_Dashboard(ref), "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.Dashboard": schema_pkg_apis_dashboard_v0alpha1_Dashboard(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardAccessInfo": schema_pkg_apis_dashboard_v0alpha1_DashboardAccessInfo(ref), "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardAccess": schema_pkg_apis_dashboard_v0alpha1_DashboardAccess(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardList": schema_pkg_apis_dashboard_v0alpha1_DashboardList(ref), "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardList": schema_pkg_apis_dashboard_v0alpha1_DashboardList(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardSummary": schema_pkg_apis_dashboard_v0alpha1_DashboardSummary(ref), "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardSummary": schema_pkg_apis_dashboard_v0alpha1_DashboardSummary(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardSummaryList": schema_pkg_apis_dashboard_v0alpha1_DashboardSummaryList(ref), "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardSummaryList": schema_pkg_apis_dashboard_v0alpha1_DashboardSummaryList(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardSummarySpec": schema_pkg_apis_dashboard_v0alpha1_DashboardSummarySpec(ref), "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardSummarySpec": schema_pkg_apis_dashboard_v0alpha1_DashboardSummarySpec(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardVersionInfo": schema_pkg_apis_dashboard_v0alpha1_DashboardVersionInfo(ref), "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardVersionInfo": schema_pkg_apis_dashboard_v0alpha1_DashboardVersionInfo(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardVersionList": schema_pkg_apis_dashboard_v0alpha1_DashboardVersionList(ref), "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardVersionList": schema_pkg_apis_dashboard_v0alpha1_DashboardVersionList(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardWithAccessInfo": schema_pkg_apis_dashboard_v0alpha1_DashboardWithAccessInfo(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.VersionsQueryOptions": schema_pkg_apis_dashboard_v0alpha1_VersionsQueryOptions(ref), "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.VersionsQueryOptions": schema_pkg_apis_dashboard_v0alpha1_VersionsQueryOptions(ref),
} }
} }
@ -133,29 +134,29 @@ func schema_pkg_apis_dashboard_v0alpha1_Dashboard(ref common.ReferenceCallback)
} }
} }
func schema_pkg_apis_dashboard_v0alpha1_DashboardAccessInfo(ref common.ReferenceCallback) common.OpenAPIDefinition { func schema_pkg_apis_dashboard_v0alpha1_DashboardAccess(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{ return common.OpenAPIDefinition{
Schema: spec.Schema{ Schema: spec.Schema{
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "Information about how the requesting user can use a given dashboard", Description: "Information about how the requesting user can use a given dashboard",
Type: []string{"object"}, Type: []string{"object"},
Properties: map[string]spec.Schema{ Properties: map[string]spec.Schema{
"kind": { "slug": {
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", Description: "Metadata fields",
Type: []string{"string"}, Type: []string{"string"},
Format: "", Format: "",
}, },
}, },
"apiVersion": { "url": {
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"}, Type: []string{"string"},
Format: "", Format: "",
}, },
}, },
"canSave": { "canSave": {
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "The permissions part",
Default: false, Default: false,
Type: []string{"boolean"}, Type: []string{"boolean"},
Format: "", Format: "",
@ -469,6 +470,55 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardVersionList(ref common.Referenc
} }
} }
func schema_pkg_apis_dashboard_v0alpha1_DashboardWithAccessInfo(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "This is like the legacy DTO where access and metadata are all returned in a single call",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Description: "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata",
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"spec": {
SchemaProps: spec.SchemaProps{
Description: "The dashboard body (unstructured for now)",
Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"),
},
},
"access": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardAccess"),
},
},
},
Required: []string{"spec", "access"},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured", "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardAccess", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
func schema_pkg_apis_dashboard_v0alpha1_VersionsQueryOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { func schema_pkg_apis_dashboard_v0alpha1_VersionsQueryOptions(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{ return common.OpenAPIDefinition{
Schema: spec.Schema{ Schema: spec.Schema{

@ -11,6 +11,8 @@ import (
common "k8s.io/kube-openapi/pkg/common" common "k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/spec3" "k8s.io/kube-openapi/pkg/spec3"
"github.com/prometheus/client_golang/prometheus"
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/pkg/apiserver/builder" "github.com/grafana/grafana/pkg/apiserver/builder"
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic" grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
@ -25,7 +27,6 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/provisioning" "github.com/grafana/grafana/pkg/services/provisioning"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/prometheus/client_golang/prometheus"
) )
var _ builder.APIGroupBuilder = (*DashboardsAPIBuilder)(nil) var _ builder.APIGroupBuilder = (*DashboardsAPIBuilder)(nil)
@ -84,7 +85,7 @@ func addKnownTypes(scheme *runtime.Scheme, gv schema.GroupVersion) {
scheme.AddKnownTypes(gv, scheme.AddKnownTypes(gv,
&v0alpha1.Dashboard{}, &v0alpha1.Dashboard{},
&v0alpha1.DashboardList{}, &v0alpha1.DashboardList{},
&v0alpha1.DashboardAccessInfo{}, &v0alpha1.DashboardWithAccessInfo{},
&v0alpha1.DashboardVersionList{}, &v0alpha1.DashboardVersionList{},
&v0alpha1.DashboardSummary{}, &v0alpha1.DashboardSummary{},
&v0alpha1.DashboardSummaryList{}, &v0alpha1.DashboardSummaryList{},
@ -135,7 +136,7 @@ func (b *DashboardsAPIBuilder) GetAPIGroupInfo(
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
storage[resourceInfo.StoragePath()] = legacyStore storage[resourceInfo.StoragePath()] = legacyStore
storage[resourceInfo.StoragePath("access")] = &AccessREST{ storage[resourceInfo.StoragePath("dto")] = &DTOConnector{
builder: b, builder: b,
} }
storage[resourceInfo.StoragePath("versions")] = &VersionsREST{ storage[resourceInfo.StoragePath("versions")] = &VersionsREST{

@ -11,43 +11,45 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/identity"
dashboard "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" dashboard "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/pkg/infra/appcontext" "github.com/grafana/grafana/pkg/infra/appcontext"
"github.com/grafana/grafana/pkg/infra/slugify"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
dashboardssvc "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/guardian" "github.com/grafana/grafana/pkg/services/guardian"
) )
type AccessREST struct { // The DTO returns everything the UI needs in a single request
type DTOConnector struct {
builder *DashboardsAPIBuilder builder *DashboardsAPIBuilder
} }
var _ = rest.Connecter(&AccessREST{}) var _ = rest.Connecter(&DTOConnector{})
var _ = rest.StorageMetadata(&AccessREST{}) var _ = rest.StorageMetadata(&DTOConnector{})
func (r *AccessREST) New() runtime.Object { func (r *DTOConnector) New() runtime.Object {
return &dashboard.DashboardAccessInfo{} return &dashboard.DashboardWithAccessInfo{}
} }
func (r *AccessREST) Destroy() { func (r *DTOConnector) Destroy() {
} }
func (r *AccessREST) ConnectMethods() []string { func (r *DTOConnector) ConnectMethods() []string {
return []string{"GET"} return []string{"GET"}
} }
func (r *AccessREST) NewConnectOptions() (runtime.Object, bool, string) { func (r *DTOConnector) NewConnectOptions() (runtime.Object, bool, string) {
return &dashboard.VersionsQueryOptions{}, false, "" return &dashboard.VersionsQueryOptions{}, false, ""
} }
func (r *AccessREST) ProducesMIMETypes(verb string) []string { func (r *DTOConnector) ProducesMIMETypes(verb string) []string {
return nil return nil
} }
func (r *AccessREST) ProducesObject(verb string) interface{} { func (r *DTOConnector) ProducesObject(verb string) interface{} {
return &dashboard.DashboardAccessInfo{} return &dashboard.DashboardWithAccessInfo{}
} }
func (r *AccessREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) { func (r *DTOConnector) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
info, err := request.NamespaceInfoFrom(ctx, true) info, err := request.NamespaceInfoFrom(ctx, true)
if err != nil { if err != nil {
return nil, err return nil, err
@ -58,7 +60,7 @@ func (r *AccessREST) Connect(ctx context.Context, name string, opts runtime.Obje
return nil, err return nil, err
} }
dto, err := r.builder.dashboardService.GetDashboard(ctx, &dashboardssvc.GetDashboardQuery{ dto, err := r.builder.dashboardService.GetDashboard(ctx, &dashboards.GetDashboardQuery{
UID: name, UID: name,
OrgID: info.OrgID, OrgID: info.OrgID,
}) })
@ -75,7 +77,7 @@ func (r *AccessREST) Connect(ctx context.Context, name string, opts runtime.Obje
return nil, fmt.Errorf("not allowed to view") return nil, fmt.Errorf("not allowed to view")
} }
access := &dashboard.DashboardAccessInfo{} access := dashboard.DashboardAccess{}
access.CanEdit, _ = guardian.CanEdit() access.CanEdit, _ = guardian.CanEdit()
access.CanSave, _ = guardian.CanSave() access.CanSave, _ = guardian.CanSave()
access.CanAdmin, _ = guardian.CanAdmin() access.CanAdmin, _ = guardian.CanAdmin()
@ -86,12 +88,22 @@ func (r *AccessREST) Connect(ctx context.Context, name string, opts runtime.Obje
r.getAnnotationPermissionsByScope(ctx, user, &access.AnnotationsPermissions.Dashboard, accesscontrol.ScopeAnnotationsTypeDashboard) r.getAnnotationPermissionsByScope(ctx, user, &access.AnnotationsPermissions.Dashboard, accesscontrol.ScopeAnnotationsTypeDashboard)
r.getAnnotationPermissionsByScope(ctx, user, &access.AnnotationsPermissions.Organization, accesscontrol.ScopeAnnotationsTypeOrganization) r.getAnnotationPermissionsByScope(ctx, user, &access.AnnotationsPermissions.Organization, accesscontrol.ScopeAnnotationsTypeOrganization)
dash, err := r.builder.access.GetDashboard(ctx, info.OrgID, name)
if err != nil {
return nil, err
}
access.Slug = slugify.Slugify(dash.Spec.GetNestedString("title"))
access.Url = dashboards.GetDashboardFolderURL(false, name, access.Slug)
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
responder.Object(http.StatusOK, access) responder.Object(http.StatusOK, &dashboard.DashboardWithAccessInfo{
Dashboard: *dash,
Access: access,
})
}), nil }), nil
} }
func (r *AccessREST) getAnnotationPermissionsByScope(ctx context.Context, user identity.Requester, actions *dashboard.AnnotationActions, scope string) { func (r *DTOConnector) getAnnotationPermissionsByScope(ctx context.Context, user identity.Requester, actions *dashboard.AnnotationActions, scope string) {
var err error var err error
evaluate := accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsCreate, scope) evaluate := accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsCreate, scope)

@ -66,10 +66,10 @@ func TestIntegrationDashboardsApp(t *testing.T) {
{ {
"responseKind": { "responseKind": {
"group": "", "group": "",
"kind": "DashboardAccessInfo", "kind": "DashboardWithAccessInfo",
"version": "" "version": ""
}, },
"subresource": "access", "subresource": "dto",
"verbs": [ "verbs": [
"get" "get"
] ]

@ -439,7 +439,9 @@ describe('ScopesScene', () => {
it('K8s API should not pass the scopes', () => { it('K8s API should not pass the scopes', () => {
config.featureToggles.kubernetesDashboards = true; config.featureToggles.kubernetesDashboards = true;
getDashboardAPI().getDashboardDTO('1'); getDashboardAPI().getDashboardDTO('1');
expect(getMock).toHaveBeenCalledWith('/apis/dashboard.grafana.app/v0alpha1/namespaces/default/dashboards/1'); expect(getMock).toHaveBeenCalledWith(
'/apis/dashboard.grafana.app/v0alpha1/namespaces/default/dashboards/1/dto'
);
}); });
}); });
@ -463,7 +465,9 @@ describe('ScopesScene', () => {
it('K8s API should not pass the scopes', () => { it('K8s API should not pass the scopes', () => {
config.featureToggles.kubernetesDashboards = true; config.featureToggles.kubernetesDashboards = true;
getDashboardAPI().getDashboardDTO('1'); getDashboardAPI().getDashboardDTO('1');
expect(getMock).toHaveBeenCalledWith('/apis/dashboard.grafana.app/v0alpha1/namespaces/default/dashboards/1'); expect(getMock).toHaveBeenCalledWith(
'/apis/dashboard.grafana.app/v0alpha1/namespaces/default/dashboards/1/dto'
);
}); });
}); });
}); });

@ -1,6 +1,6 @@
import { config, getBackendSrv } from '@grafana/runtime'; import { config, getBackendSrv } from '@grafana/runtime';
import { ScopedResourceClient } from 'app/features/apiserver/client'; import { ScopedResourceClient } from 'app/features/apiserver/client';
import { ResourceClient } from 'app/features/apiserver/types'; import { Resource, ResourceClient } from 'app/features/apiserver/types';
import { SaveDashboardCommand } from 'app/features/dashboard/components/SaveDashboard/types'; import { SaveDashboardCommand } from 'app/features/dashboard/components/SaveDashboard/types';
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher'; import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
import { DeleteDashboardResponse } from 'app/features/manage-dashboards/types'; import { DeleteDashboardResponse } from 'app/features/manage-dashboards/types';
@ -45,6 +45,10 @@ class LegacyDashboardAPI implements DashboardAPI {
} }
} }
interface DashboardWithAccessInfo extends Resource<DashboardDataDTO, 'DashboardWithAccessInfo'> {
access: Object; // TODO...
}
// Implemented using /apis/dashboards.grafana.app/* // Implemented using /apis/dashboards.grafana.app/*
class K8sDashboardAPI implements DashboardAPI { class K8sDashboardAPI implements DashboardAPI {
private client: ResourceClient<DashboardDataDTO>; private client: ResourceClient<DashboardDataDTO>;
@ -66,16 +70,15 @@ class K8sDashboardAPI implements DashboardAPI {
} }
async getDashboardDTO(uid: string): Promise<DashboardDTO> { async getDashboardDTO(uid: string): Promise<DashboardDTO> {
const d = await this.client.get(uid); const dto = await this.client.subresource<DashboardWithAccessInfo>(uid, 'dto');
const m = await this.client.subresource<object>(uid, 'access');
return { return {
meta: { meta: {
...m, ...dto.access,
isNew: false, isNew: false,
isFolder: false, isFolder: false,
uid: d.metadata.name, uid: dto.metadata.name,
}, },
dashboard: d.spec, dashboard: dto.spec,
}; };
} }
} }

Loading…
Cancel
Save