From 4651506319fa1e1d96487f4b7818ac86a77e553e Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 26 Jun 2024 07:27:52 +0300 Subject: [PATCH] K8s/Dashboards: Replace multiple calls with a single endpoint (#89639) --- pkg/apis/dashboard/v0alpha1/types.go | 16 +++- .../v0alpha1/zz_generated.deepcopy.go | 43 ++++++--- .../v0alpha1/zz_generated.openapi.go | 92 ++++++++++++++----- pkg/registry/apis/dashboard/register.go | 7 +- .../dashboard/{sub_access.go => sub_dto.go} | 46 ++++++---- pkg/tests/apis/dashboard/dashboards_test.go | 66 ++++++------- .../scene/Scopes/ScopesScene.test.tsx | 8 +- .../features/dashboard/api/dashboard_api.ts | 15 +-- 8 files changed, 195 insertions(+), 98 deletions(-) rename pkg/registry/apis/dashboard/{sub_access.go => sub_dto.go} (66%) diff --git a/pkg/apis/dashboard/v0alpha1/types.go b/pkg/apis/dashboard/v0alpha1/types.go index df69ef7c1ab..3b531127031 100644 --- a/pkg/apis/dashboard/v0alpha1/types.go +++ b/pkg/apis/dashboard/v0alpha1/types.go @@ -89,11 +89,21 @@ type VersionsQueryOptions struct { 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 -type DashboardAccessInfo struct { - metav1.TypeMeta `json:",inline"` +type DashboardWithAccessInfo struct { + 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"` CanEdit bool `json:"canEdit"` CanAdmin bool `json:"canAdmin"` diff --git a/pkg/apis/dashboard/v0alpha1/zz_generated.deepcopy.go b/pkg/apis/dashboard/v0alpha1/zz_generated.deepcopy.go index 2e0aa21fd20..acac319e53d 100644 --- a/pkg/apis/dashboard/v0alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/dashboard/v0alpha1/zz_generated.deepcopy.go @@ -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. -func (in *DashboardAccessInfo) DeepCopyInto(out *DashboardAccessInfo) { +func (in *DashboardAccess) DeepCopyInto(out *DashboardAccess) { *out = *in - out.TypeMeta = in.TypeMeta if in.AnnotationsPermissions != nil { in, out := &in.AnnotationsPermissions, &out.AnnotationsPermissions *out = new(AnnotationPermission) @@ -84,24 +83,16 @@ func (in *DashboardAccessInfo) DeepCopyInto(out *DashboardAccessInfo) { return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardAccessInfo. -func (in *DashboardAccessInfo) DeepCopy() *DashboardAccessInfo { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardAccess. +func (in *DashboardAccess) DeepCopy() *DashboardAccess { if in == nil { return nil } - out := new(DashboardAccessInfo) + out := new(DashboardAccess) in.DeepCopyInto(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. func (in *DashboardList) DeepCopyInto(out *DashboardList) { *out = *in @@ -263,6 +254,32 @@ func (in *DashboardVersionList) DeepCopyObject() runtime.Object { 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. func (in *VersionsQueryOptions) DeepCopyInto(out *VersionsQueryOptions) { *out = *in diff --git a/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go b/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go index e2fa02b3a91..330f7dd1255 100644 --- a/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go +++ b/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go @@ -16,17 +16,18 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "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.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.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.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.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.VersionsQueryOptions": schema_pkg_apis_dashboard_v0alpha1_VersionsQueryOptions(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.Dashboard": schema_pkg_apis_dashboard_v0alpha1_Dashboard(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.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.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.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), } } @@ -133,32 +134,32 @@ 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{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ Description: "Information about how the requesting user can use a given dashboard", Type: []string{"object"}, Properties: map[string]spec.Schema{ - "kind": { + "slug": { 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"}, Format: "", }, }, - "apiVersion": { + "url": { 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: "", + Type: []string{"string"}, + Format: "", }, }, "canSave": { SchemaProps: spec.SchemaProps{ - Default: false, - Type: []string{"boolean"}, - Format: "", + Description: "The permissions part", + Default: false, + Type: []string{"boolean"}, + Format: "", }, }, "canEdit": { @@ -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 { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/registry/apis/dashboard/register.go b/pkg/registry/apis/dashboard/register.go index fe60abed4fb..0fbf7dcc5c5 100644 --- a/pkg/registry/apis/dashboard/register.go +++ b/pkg/registry/apis/dashboard/register.go @@ -11,6 +11,8 @@ import ( common "k8s.io/kube-openapi/pkg/common" "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/apiserver/builder" 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/provisioning" "github.com/grafana/grafana/pkg/setting" - "github.com/prometheus/client_golang/prometheus" ) var _ builder.APIGroupBuilder = (*DashboardsAPIBuilder)(nil) @@ -84,7 +85,7 @@ func addKnownTypes(scheme *runtime.Scheme, gv schema.GroupVersion) { scheme.AddKnownTypes(gv, &v0alpha1.Dashboard{}, &v0alpha1.DashboardList{}, - &v0alpha1.DashboardAccessInfo{}, + &v0alpha1.DashboardWithAccessInfo{}, &v0alpha1.DashboardVersionList{}, &v0alpha1.DashboardSummary{}, &v0alpha1.DashboardSummaryList{}, @@ -135,7 +136,7 @@ func (b *DashboardsAPIBuilder) GetAPIGroupInfo( storage := map[string]rest.Storage{} storage[resourceInfo.StoragePath()] = legacyStore - storage[resourceInfo.StoragePath("access")] = &AccessREST{ + storage[resourceInfo.StoragePath("dto")] = &DTOConnector{ builder: b, } storage[resourceInfo.StoragePath("versions")] = &VersionsREST{ diff --git a/pkg/registry/apis/dashboard/sub_access.go b/pkg/registry/apis/dashboard/sub_dto.go similarity index 66% rename from pkg/registry/apis/dashboard/sub_access.go rename to pkg/registry/apis/dashboard/sub_dto.go index 21fbbbe480e..3a5a86cba94 100644 --- a/pkg/registry/apis/dashboard/sub_access.go +++ b/pkg/registry/apis/dashboard/sub_dto.go @@ -11,43 +11,45 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/identity" dashboard "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" "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/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" ) -type AccessREST struct { +// The DTO returns everything the UI needs in a single request +type DTOConnector struct { builder *DashboardsAPIBuilder } -var _ = rest.Connecter(&AccessREST{}) -var _ = rest.StorageMetadata(&AccessREST{}) +var _ = rest.Connecter(&DTOConnector{}) +var _ = rest.StorageMetadata(&DTOConnector{}) -func (r *AccessREST) New() runtime.Object { - return &dashboard.DashboardAccessInfo{} +func (r *DTOConnector) New() runtime.Object { + return &dashboard.DashboardWithAccessInfo{} } -func (r *AccessREST) Destroy() { +func (r *DTOConnector) Destroy() { } -func (r *AccessREST) ConnectMethods() []string { +func (r *DTOConnector) ConnectMethods() []string { return []string{"GET"} } -func (r *AccessREST) NewConnectOptions() (runtime.Object, bool, string) { +func (r *DTOConnector) NewConnectOptions() (runtime.Object, bool, string) { return &dashboard.VersionsQueryOptions{}, false, "" } -func (r *AccessREST) ProducesMIMETypes(verb string) []string { +func (r *DTOConnector) ProducesMIMETypes(verb string) []string { return nil } -func (r *AccessREST) ProducesObject(verb string) interface{} { - return &dashboard.DashboardAccessInfo{} +func (r *DTOConnector) ProducesObject(verb string) interface{} { + 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) if err != nil { return nil, err @@ -58,7 +60,7 @@ func (r *AccessREST) Connect(ctx context.Context, name string, opts runtime.Obje return nil, err } - dto, err := r.builder.dashboardService.GetDashboard(ctx, &dashboardssvc.GetDashboardQuery{ + dto, err := r.builder.dashboardService.GetDashboard(ctx, &dashboards.GetDashboardQuery{ UID: name, 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") } - access := &dashboard.DashboardAccessInfo{} + access := dashboard.DashboardAccess{} access.CanEdit, _ = guardian.CanEdit() access.CanSave, _ = guardian.CanSave() 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.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) { - responder.Object(http.StatusOK, access) + responder.Object(http.StatusOK, &dashboard.DashboardWithAccessInfo{ + Dashboard: *dash, + Access: access, + }) }), 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 evaluate := accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsCreate, scope) diff --git a/pkg/tests/apis/dashboard/dashboards_test.go b/pkg/tests/apis/dashboard/dashboards_test.go index 94c62a18988..cad2432d7b1 100644 --- a/pkg/tests/apis/dashboard/dashboards_test.go +++ b/pkg/tests/apis/dashboard/dashboards_test.go @@ -47,46 +47,46 @@ func TestIntegrationDashboardsApp(t *testing.T) { t.Run("Check discovery client", func(t *testing.T) { disco := helper.GetGroupVersionInfoJSON("dashboard.grafana.app") - // fmt.Printf("%s", string(disco)) + //fmt.Printf("%s", string(disco)) require.JSONEq(t, `[ { - "freshness": "Current", - "resources": [ + "freshness": "Current", + "resources": [ { - "resource": "dashboards", - "responseKind": { + "resource": "dashboards", + "responseKind": { "group": "", "kind": "Dashboard", "version": "" - }, - "scope": "Namespaced", - "singularResource": "dashboard", - "subresources": [ + }, + "scope": "Namespaced", + "singularResource": "dashboard", + "subresources": [ { - "responseKind": { + "responseKind": { "group": "", - "kind": "DashboardAccessInfo", + "kind": "DashboardWithAccessInfo", "version": "" - }, - "subresource": "access", - "verbs": [ + }, + "subresource": "dto", + "verbs": [ "get" - ] + ] }, { - "responseKind": { + "responseKind": { "group": "", "kind": "DashboardVersionList", "version": "" - }, - "subresource": "versions", - "verbs": [ + }, + "subresource": "versions", + "verbs": [ "get" - ] + ] } - ], - "verbs": [ + ], + "verbs": [ "create", "delete", "deletecollection", @@ -94,25 +94,25 @@ func TestIntegrationDashboardsApp(t *testing.T) { "list", "patch", "update" - ] + ] }, { - "resource": "summary", - "responseKind": { + "resource": "summary", + "responseKind": { "group": "", "kind": "DashboardSummary", "version": "" - }, - "scope": "Namespaced", - "singularResource": "summary", - "verbs": [ + }, + "scope": "Namespaced", + "singularResource": "summary", + "verbs": [ "get", "list" - ] + ] } - ], - "version": "v0alpha1" + ], + "version": "v0alpha1" } - ]`, disco) + ]`, disco) }) } diff --git a/public/app/features/dashboard-scene/scene/Scopes/ScopesScene.test.tsx b/public/app/features/dashboard-scene/scene/Scopes/ScopesScene.test.tsx index 60ed7d1aa2f..839c76d5933 100644 --- a/public/app/features/dashboard-scene/scene/Scopes/ScopesScene.test.tsx +++ b/public/app/features/dashboard-scene/scene/Scopes/ScopesScene.test.tsx @@ -439,7 +439,9 @@ describe('ScopesScene', () => { it('K8s API should not pass the scopes', () => { config.featureToggles.kubernetesDashboards = true; 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', () => { config.featureToggles.kubernetesDashboards = true; 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' + ); }); }); }); diff --git a/public/app/features/dashboard/api/dashboard_api.ts b/public/app/features/dashboard/api/dashboard_api.ts index 6ea255ad5e4..40bb77523ec 100644 --- a/public/app/features/dashboard/api/dashboard_api.ts +++ b/public/app/features/dashboard/api/dashboard_api.ts @@ -1,6 +1,6 @@ import { config, getBackendSrv } from '@grafana/runtime'; 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 { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher'; import { DeleteDashboardResponse } from 'app/features/manage-dashboards/types'; @@ -45,6 +45,10 @@ class LegacyDashboardAPI implements DashboardAPI { } } +interface DashboardWithAccessInfo extends Resource { + access: Object; // TODO... +} + // Implemented using /apis/dashboards.grafana.app/* class K8sDashboardAPI implements DashboardAPI { private client: ResourceClient; @@ -66,16 +70,15 @@ class K8sDashboardAPI implements DashboardAPI { } async getDashboardDTO(uid: string): Promise { - const d = await this.client.get(uid); - const m = await this.client.subresource(uid, 'access'); + const dto = await this.client.subresource(uid, 'dto'); return { meta: { - ...m, + ...dto.access, isNew: false, isFolder: false, - uid: d.metadata.name, + uid: dto.metadata.name, }, - dashboard: d.spec, + dashboard: dto.spec, }; } }