K8s: Folders: Add v1 api (#103842)

pull/103862/head
Stephanie Hingtgen 3 months ago committed by GitHub
parent 50499fa749
commit f5ad1ef69b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 12
      apps/dashboard/kinds/dashboard.cue
  2. 90
      apps/dashboard/pkg/migration/conversion/conversion.go
  3. 14
      apps/dashboard/pkg/migration/conversion/conversion_test.go
  4. 2
      go.mod
  5. 4
      pkg/api/folder_test.go
  6. 2
      pkg/apis/folder/v1/doc.go
  7. 4
      pkg/apis/folder/v1/register.go
  8. 2
      pkg/apis/folder/v1/types.go
  9. 2
      pkg/apis/folder/v1/zz_generated.deepcopy.go
  10. 2
      pkg/apis/folder/v1/zz_generated.defaults.go
  11. 50
      pkg/apis/folder/v1/zz_generated.openapi.go
  12. 4
      pkg/apis/folder/v1/zz_generated.openapi_violation_exceptions.list
  13. 2
      pkg/cmd/grafana-cli/commands/datamigrations/to_unified_storage.go
  14. 8
      pkg/registry/apis/dashboard/large_test.go
  15. 4
      pkg/registry/apis/dashboard/legacy/migrate.go
  16. 4
      pkg/registry/apis/dashboard/legacy/sql_dashboards_test.go
  17. 12
      pkg/registry/apis/dashboard/legacysearcher/search_client.go
  18. 16
      pkg/registry/apis/dashboard/mutation_test.go
  19. 64
      pkg/registry/apis/dashboard/register.go
  20. 40
      pkg/registry/apis/dashboard/register_test.go
  21. 6
      pkg/registry/apis/dashboard/search.go
  22. 4
      pkg/registry/apis/folders/authorizer_test.go
  23. 12
      pkg/registry/apis/folders/conversions.go
  24. 4
      pkg/registry/apis/folders/folder_storage.go
  25. 4
      pkg/registry/apis/folders/folder_storage_test.go
  26. 12
      pkg/registry/apis/folders/legacy_storage.go
  27. 12
      pkg/registry/apis/folders/legacy_storage_test.go
  28. 49
      pkg/registry/apis/folders/register.go
  29. 134
      pkg/registry/apis/folders/register_test.go
  30. 8
      pkg/registry/apis/folders/sub_access.go
  31. 12
      pkg/registry/apis/folders/sub_count.go
  32. 22
      pkg/registry/apis/folders/sub_parent_test.go
  33. 22
      pkg/registry/apis/folders/sub_parents.go
  34. 2
      pkg/registry/apis/provisioning/controller/finalizers.go
  35. 2
      pkg/registry/apis/provisioning/register.go
  36. 2
      pkg/registry/apis/provisioning/resources/client.go
  37. 6
      pkg/registry/apis/provisioning/resources/folders.go
  38. 2
      pkg/registry/apis/provisioning/resources/object.go
  39. 2
      pkg/registry/apis/provisioning/resources/parser_test.go
  40. 2
      pkg/registry/apis/provisioning/resources/tree.go
  41. 3
      pkg/services/apiserver/standalone/runtime_test.go
  42. 8
      pkg/services/authn/authnimpl/service_test.go
  43. 4
      pkg/services/authz/rbac/store/folder_store.go
  44. 6
      pkg/services/authz/zanzana/common/info.go
  45. 12
      pkg/services/authz/zanzana/translations.go
  46. 8
      pkg/services/dashboards/service/client/client.go
  47. 4
      pkg/services/dashboards/service/client/client_test.go
  48. 34
      pkg/services/dashboards/service/dashboard_service.go
  49. 26
      pkg/services/dashboards/service/dashboard_service_test.go
  50. 4
      pkg/services/dashboardversion/dashverimpl/dashver.go
  51. 16
      pkg/services/folder/folderimpl/conversions_test.go
  52. 8
      pkg/services/folder/folderimpl/folder.go
  53. 14
      pkg/services/folder/folderimpl/folder_unifiedstorage.go
  54. 46
      pkg/services/folder/folderimpl/folder_unifiedstorage_test.go
  55. 4
      pkg/services/folder/folderimpl/unifiedstore.go
  56. 2
      pkg/storage/legacysql/dualwrite/utils.go
  57. 2
      pkg/storage/legacysql/dualwrite/utils_test.go
  58. 2
      pkg/storage/unified/README.md
  59. 4
      pkg/storage/unified/apistore/fake_large.go
  60. 1
      pkg/storage/unified/apistore/go.mod
  61. 1
      pkg/storage/unified/apistore/go.sum
  62. 56
      pkg/storage/unified/apistore/prepare_test.go
  63. 1
      pkg/storage/unified/resource/go.mod
  64. 2
      pkg/storage/unified/resource/go.sum
  65. 12
      pkg/storage/unified/resource/search.go
  66. 2
      pkg/storage/unified/resource/testdata/folder-resource.json
  67. 2
      pkg/storage/unified/resource/testdata/folder-resource2.json
  68. 4
      pkg/storage/unified/search/dashboard.go
  69. 2
      pkg/storage/unified/search/testdata/doc/folder-aaa.json
  70. 2
      pkg/storage/unified/search/testdata/doc/folder-bbb.json
  71. 28
      pkg/tests/apis/dashboard/integration/api_validation_test.go
  72. 30
      pkg/tests/apis/folder/folders_test.go
  73. 2
      pkg/tests/apis/folder/testdata/folder-generate.yaml
  74. 2
      pkg/tests/apis/folder/testdata/folder-test-create.yaml
  75. 2
      pkg/tests/apis/folder/testdata/folder-test-replace.yaml
  76. 1822
      pkg/tests/apis/openapi_snapshots/folder.grafana.app-v1.json
  77. 2
      pkg/tests/apis/openapi_test.go
  78. 2
      pkg/tests/apis/provisioning/helper_test.go

@ -1,9 +1,9 @@
package kinds
import (
"github.com/grafana/grafana/sdkkinds/dashboard/v0alpha1"
"github.com/grafana/grafana/sdkkinds/dashboard/v1alpha1"
"github.com/grafana/grafana/sdkkinds/dashboard/v2alpha1"
v0 "github.com/grafana/grafana/sdkkinds/dashboard/v0alpha1"
v1 "github.com/grafana/grafana/sdkkinds/dashboard/v1alpha1"
v2 "github.com/grafana/grafana/sdkkinds/dashboard/v2alpha1"
)
// Status is the shared status of all dashboard versions.
@ -51,19 +51,19 @@ dashboard: {
versions: {
"v0alpha1": {
schema: {
spec: v0alpha1.DashboardSpec
spec: v0.DashboardSpec
status: DashboardStatus
}
}
"v1alpha1": {
schema: {
spec: v1alpha1.DashboardSpec
spec: v1.DashboardSpec
status: DashboardStatus
}
}
"v2alpha1": {
schema: {
spec: v2alpha1.DashboardSpec
spec: v2.DashboardSpec
status: DashboardStatus
}
}

@ -4,55 +4,55 @@ import (
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashv2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/migration"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
)
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddConversionFunc((*v0alpha1.Dashboard)(nil), (*v1alpha1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_V0_to_V1(a.(*v0alpha1.Dashboard), b.(*v1alpha1.Dashboard), scope)
if err := s.AddConversionFunc((*dashv0.Dashboard)(nil), (*dashv1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_V0_to_V1(a.(*dashv0.Dashboard), b.(*dashv1.Dashboard), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*v0alpha1.Dashboard)(nil), (*v2alpha1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_V0_to_V2(a.(*v0alpha1.Dashboard), b.(*v2alpha1.Dashboard), scope)
if err := s.AddConversionFunc((*dashv0.Dashboard)(nil), (*dashv2.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_V0_to_V2(a.(*dashv0.Dashboard), b.(*dashv2.Dashboard), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*v1alpha1.Dashboard)(nil), (*v0alpha1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_V1_to_V0(a.(*v1alpha1.Dashboard), b.(*v0alpha1.Dashboard), scope)
if err := s.AddConversionFunc((*dashv1.Dashboard)(nil), (*dashv0.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_V1_to_V0(a.(*dashv1.Dashboard), b.(*dashv0.Dashboard), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*v1alpha1.Dashboard)(nil), (*v2alpha1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_V1_to_V2(a.(*v1alpha1.Dashboard), b.(*v2alpha1.Dashboard), scope)
if err := s.AddConversionFunc((*dashv1.Dashboard)(nil), (*dashv2.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_V1_to_V2(a.(*dashv1.Dashboard), b.(*dashv2.Dashboard), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*v2alpha1.Dashboard)(nil), (*v0alpha1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_V2_to_V0(a.(*v2alpha1.Dashboard), b.(*v0alpha1.Dashboard), scope)
if err := s.AddConversionFunc((*dashv2.Dashboard)(nil), (*dashv0.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_V2_to_V0(a.(*dashv2.Dashboard), b.(*dashv0.Dashboard), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*v2alpha1.Dashboard)(nil), (*v1alpha1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_V2_to_V1(a.(*v2alpha1.Dashboard), b.(*v1alpha1.Dashboard), scope)
if err := s.AddConversionFunc((*dashv2.Dashboard)(nil), (*dashv1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_V2_to_V1(a.(*dashv2.Dashboard), b.(*dashv1.Dashboard), scope)
}); err != nil {
return err
}
return nil
}
func Convert_V0_to_V1(in *v0alpha1.Dashboard, out *v1alpha1.Dashboard, scope conversion.Scope) error {
func Convert_V0_to_V1(in *dashv0.Dashboard, out *dashv1.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.Spec.Object = in.Spec.Object
out.Status = v1alpha1.DashboardStatus{
Conversion: &v1alpha1.DashboardConversionStatus{
StoredVersion: v0alpha1.VERSION,
out.Status = dashv1.DashboardStatus{
Conversion: &dashv1.DashboardConversionStatus{
StoredVersion: dashv0.VERSION,
},
}
@ -64,7 +64,7 @@ func Convert_V0_to_V1(in *v0alpha1.Dashboard, out *v1alpha1.Dashboard, scope con
return nil
}
func Convert_V0_to_V2(in *v0alpha1.Dashboard, out *v2alpha1.Dashboard, scope conversion.Scope) error {
func Convert_V0_to_V2(in *dashv0.Dashboard, out *dashv2.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
// TODO (@radiohead): implement V0 to V2 conversion
@ -77,16 +77,16 @@ func Convert_V0_to_V2(in *v0alpha1.Dashboard, out *v2alpha1.Dashboard, scope con
}
// We need to make sure the layout is set to some value, otherwise the JSON marshaling will fail.
out.Spec.Layout = v2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{
GridLayoutKind: &v2alpha1.DashboardGridLayoutKind{
out.Spec.Layout = dashv2.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{
GridLayoutKind: &dashv2.DashboardGridLayoutKind{
Kind: "GridLayout",
Spec: v2alpha1.DashboardGridLayoutSpec{},
Spec: dashv2.DashboardGridLayoutSpec{},
},
}
out.Status = v2alpha1.DashboardStatus{
Conversion: &v2alpha1.DashboardConversionStatus{
StoredVersion: v0alpha1.VERSION,
out.Status = dashv2.DashboardStatus{
Conversion: &dashv2.DashboardConversionStatus{
StoredVersion: dashv0.VERSION,
Failed: true,
Error: "backend conversion not yet implemented",
},
@ -95,21 +95,21 @@ func Convert_V0_to_V2(in *v0alpha1.Dashboard, out *v2alpha1.Dashboard, scope con
return nil
}
func Convert_V1_to_V0(in *v1alpha1.Dashboard, out *v0alpha1.Dashboard, scope conversion.Scope) error {
func Convert_V1_to_V0(in *dashv1.Dashboard, out *dashv0.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.Spec.Object = in.Spec.Object
out.Status = v0alpha1.DashboardStatus{
Conversion: &v0alpha1.DashboardConversionStatus{
StoredVersion: v1alpha1.VERSION,
out.Status = dashv0.DashboardStatus{
Conversion: &dashv0.DashboardConversionStatus{
StoredVersion: dashv1.VERSION,
},
}
return nil
}
func Convert_V1_to_V2(in *v1alpha1.Dashboard, out *v2alpha1.Dashboard, scope conversion.Scope) error {
func Convert_V1_to_V2(in *dashv1.Dashboard, out *dashv2.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
// TODO (@radiohead): implement V1 to V2 conversion
@ -122,16 +122,16 @@ func Convert_V1_to_V2(in *v1alpha1.Dashboard, out *v2alpha1.Dashboard, scope con
}
// We need to make sure the layout is set to some value, otherwise the JSON marshaling will fail.
out.Spec.Layout = v2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{
GridLayoutKind: &v2alpha1.DashboardGridLayoutKind{
out.Spec.Layout = dashv2.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{
GridLayoutKind: &dashv2.DashboardGridLayoutKind{
Kind: "GridLayout",
Spec: v2alpha1.DashboardGridLayoutSpec{},
Spec: dashv2.DashboardGridLayoutSpec{},
},
}
out.Status = v2alpha1.DashboardStatus{
Conversion: &v2alpha1.DashboardConversionStatus{
StoredVersion: v1alpha1.VERSION,
out.Status = dashv2.DashboardStatus{
Conversion: &dashv2.DashboardConversionStatus{
StoredVersion: dashv1.VERSION,
Failed: true,
Error: "backend conversion not yet implemented",
},
@ -140,14 +140,14 @@ func Convert_V1_to_V2(in *v1alpha1.Dashboard, out *v2alpha1.Dashboard, scope con
return nil
}
func Convert_V2_to_V0(in *v2alpha1.Dashboard, out *v0alpha1.Dashboard, scope conversion.Scope) error {
func Convert_V2_to_V0(in *dashv2.Dashboard, out *dashv0.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
// TODO: implement V2 to V0 conversion
out.Status = v0alpha1.DashboardStatus{
Conversion: &v0alpha1.DashboardConversionStatus{
StoredVersion: v2alpha1.VERSION,
out.Status = dashv0.DashboardStatus{
Conversion: &dashv0.DashboardConversionStatus{
StoredVersion: dashv2.VERSION,
Failed: true,
Error: "backend conversion not yet implemented",
},
@ -156,14 +156,14 @@ func Convert_V2_to_V0(in *v2alpha1.Dashboard, out *v0alpha1.Dashboard, scope con
return nil
}
func Convert_V2_to_V1(in *v2alpha1.Dashboard, out *v1alpha1.Dashboard, scope conversion.Scope) error {
func Convert_V2_to_V1(in *dashv2.Dashboard, out *dashv1.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
// TODO: implement V2 to V1 conversion
out.Status = v1alpha1.DashboardStatus{
Conversion: &v1alpha1.DashboardConversionStatus{
StoredVersion: v2alpha1.VERSION,
out.Status = dashv1.DashboardStatus{
Conversion: &dashv1.DashboardConversionStatus{
StoredVersion: dashv2.VERSION,
Failed: true,
Error: "backend conversion not yet implemented",
},

@ -9,18 +9,18 @@ import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashv2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
)
func TestConversionMatrixExist(t *testing.T) {
versions := []v1.Object{
&v0alpha1.Dashboard{Spec: common.Unstructured{Object: map[string]any{"title": "dashboardV0"}}},
&v1alpha1.Dashboard{Spec: common.Unstructured{Object: map[string]any{"title": "dashboardV1"}}},
&v2alpha1.Dashboard{Spec: v2alpha1.DashboardSpec{Title: "dashboardV2"}},
&dashv0.Dashboard{Spec: common.Unstructured{Object: map[string]any{"title": "dashboardV0"}}},
&dashv1.Dashboard{Spec: common.Unstructured{Object: map[string]any{"title": "dashboardV1"}}},
&dashv2.Dashboard{Spec: dashv2.DashboardSpec{Title: "dashboardV2"}},
}
scheme := runtime.NewScheme()
@ -47,7 +47,7 @@ func TestConversionMatrixExist(t *testing.T) {
}
func TestDeepCopyValid(t *testing.T) {
dash1 := &v0alpha1.Dashboard{}
dash1 := &dashv0.Dashboard{}
meta1, err := utils.MetaAccessor(dash1)
require.NoError(t, err)
meta1.SetFolder("f1")

@ -215,7 +215,7 @@ require (
github.com/grafana/grafana/apps/playlist v0.0.0-20250220164708-c8d4ff28a450 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/aggregator v0.0.0-20250220163425-b4c4b9abbdc8 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250401081501-6af5fbf3fff0 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/apis/folder v0.0.0-20250402082028-6781612335d9 // @grafana/grafana-search-and-storage
github.com/grafana/grafana/pkg/apis/folder v0.0.0-20250402082028-6781612335d9 // indirect; @grafana/grafana-search-and-storage
github.com/grafana/grafana/pkg/apis/secret v0.0.0-20250319110241-5a004939da2a // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana/pkg/apiserver v0.0.0-20250325075903-77fa2271be7a // @grafana/grafana-app-platform-squad

@ -15,7 +15,7 @@ import (
clientrest "k8s.io/client-go/rest"
"github.com/grafana/grafana/pkg/api/dtos"
folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
@ -618,7 +618,7 @@ func TestGetFolderLegacyAndUnifiedStorage(t *testing.T) {
cfg := setting.NewCfg()
cfg.UnifiedStorage = map[string]setting.UnifiedStorageConfig{
folderv0alpha1.RESOURCEGROUP: {
folders.RESOURCEGROUP: {
DualWriterMode: tc.unifiedStorageMode,
},
}

@ -3,4 +3,4 @@
// +k8s:defaulter-gen=TypeMeta
// +groupName=folder.grafana.app
package v0alpha1 // import "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
package v1 // import "github.com/grafana/grafana/pkg/apis/folder/v1"

@ -1,4 +1,4 @@
package v0alpha1
package v1
import (
"fmt"
@ -12,7 +12,7 @@ import (
const (
GROUP = "folder.grafana.app"
VERSION = "v0alpha1"
VERSION = "v1"
RESOURCE = "folders"
APIVERSION = GROUP + "/" + VERSION
RESOURCEGROUP = RESOURCE + "." + GROUP

@ -1,4 +1,4 @@
package v0alpha1
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

@ -5,7 +5,7 @@
// Code generated by deepcopy-gen. DO NOT EDIT.
package v0alpha1
package v1
import (
runtime "k8s.io/apimachinery/pkg/runtime"

@ -5,7 +5,7 @@
// Code generated by defaulter-gen. DO NOT EDIT.
package v0alpha1
package v1
import (
runtime "k8s.io/apimachinery/pkg/runtime"

@ -5,7 +5,7 @@
// Code generated by openapi-gen. DO NOT EDIT.
package v0alpha1
package v1
import (
common "k8s.io/kube-openapi/pkg/common"
@ -14,18 +14,18 @@ import (
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
return map[string]common.OpenAPIDefinition{
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1.DescendantCounts": schema_pkg_apis_folder_v0alpha1_DescendantCounts(ref),
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1.Folder": schema_pkg_apis_folder_v0alpha1_Folder(ref),
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1.FolderAccessInfo": schema_pkg_apis_folder_v0alpha1_FolderAccessInfo(ref),
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1.FolderInfo": schema_pkg_apis_folder_v0alpha1_FolderInfo(ref),
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1.FolderInfoList": schema_pkg_apis_folder_v0alpha1_FolderInfoList(ref),
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1.FolderList": schema_pkg_apis_folder_v0alpha1_FolderList(ref),
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1.ResourceStats": schema_pkg_apis_folder_v0alpha1_ResourceStats(ref),
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1.Spec": schema_pkg_apis_folder_v0alpha1_Spec(ref),
"github.com/grafana/grafana/pkg/apis/folder/v1.DescendantCounts": schema_pkg_apis_folder_v1_DescendantCounts(ref),
"github.com/grafana/grafana/pkg/apis/folder/v1.Folder": schema_pkg_apis_folder_v1_Folder(ref),
"github.com/grafana/grafana/pkg/apis/folder/v1.FolderAccessInfo": schema_pkg_apis_folder_v1_FolderAccessInfo(ref),
"github.com/grafana/grafana/pkg/apis/folder/v1.FolderInfo": schema_pkg_apis_folder_v1_FolderInfo(ref),
"github.com/grafana/grafana/pkg/apis/folder/v1.FolderInfoList": schema_pkg_apis_folder_v1_FolderInfoList(ref),
"github.com/grafana/grafana/pkg/apis/folder/v1.FolderList": schema_pkg_apis_folder_v1_FolderList(ref),
"github.com/grafana/grafana/pkg/apis/folder/v1.ResourceStats": schema_pkg_apis_folder_v1_ResourceStats(ref),
"github.com/grafana/grafana/pkg/apis/folder/v1.Spec": schema_pkg_apis_folder_v1_Spec(ref),
}
}
func schema_pkg_apis_folder_v0alpha1_DescendantCounts(ref common.ReferenceCallback) common.OpenAPIDefinition {
func schema_pkg_apis_folder_v1_DescendantCounts(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
@ -52,7 +52,7 @@ func schema_pkg_apis_folder_v0alpha1_DescendantCounts(ref common.ReferenceCallba
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/pkg/apis/folder/v0alpha1.ResourceStats"),
Ref: ref("github.com/grafana/grafana/pkg/apis/folder/v1.ResourceStats"),
},
},
},
@ -63,11 +63,11 @@ func schema_pkg_apis_folder_v0alpha1_DescendantCounts(ref common.ReferenceCallba
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1.ResourceStats"},
"github.com/grafana/grafana/pkg/apis/folder/v1.ResourceStats"},
}
}
func schema_pkg_apis_folder_v0alpha1_Folder(ref common.ReferenceCallback) common.OpenAPIDefinition {
func schema_pkg_apis_folder_v1_Folder(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
@ -96,18 +96,18 @@ func schema_pkg_apis_folder_v0alpha1_Folder(ref common.ReferenceCallback) common
"spec": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/pkg/apis/folder/v0alpha1.Spec"),
Ref: ref("github.com/grafana/grafana/pkg/apis/folder/v1.Spec"),
},
},
},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1.Spec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
"github.com/grafana/grafana/pkg/apis/folder/v1.Spec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
func schema_pkg_apis_folder_v0alpha1_FolderAccessInfo(ref common.ReferenceCallback) common.OpenAPIDefinition {
func schema_pkg_apis_folder_v1_FolderAccessInfo(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
@ -163,7 +163,7 @@ func schema_pkg_apis_folder_v0alpha1_FolderAccessInfo(ref common.ReferenceCallba
}
}
func schema_pkg_apis_folder_v0alpha1_FolderInfo(ref common.ReferenceCallback) common.OpenAPIDefinition {
func schema_pkg_apis_folder_v1_FolderInfo(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
@ -214,7 +214,7 @@ func schema_pkg_apis_folder_v0alpha1_FolderInfo(ref common.ReferenceCallback) co
}
}
func schema_pkg_apis_folder_v0alpha1_FolderInfoList(ref common.ReferenceCallback) common.OpenAPIDefinition {
func schema_pkg_apis_folder_v1_FolderInfoList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
@ -256,7 +256,7 @@ func schema_pkg_apis_folder_v0alpha1_FolderInfoList(ref common.ReferenceCallback
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/pkg/apis/folder/v0alpha1.FolderInfo"),
Ref: ref("github.com/grafana/grafana/pkg/apis/folder/v1.FolderInfo"),
},
},
},
@ -266,11 +266,11 @@ func schema_pkg_apis_folder_v0alpha1_FolderInfoList(ref common.ReferenceCallback
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1.FolderInfo", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
"github.com/grafana/grafana/pkg/apis/folder/v1.FolderInfo", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
}
}
func schema_pkg_apis_folder_v0alpha1_FolderList(ref common.ReferenceCallback) common.OpenAPIDefinition {
func schema_pkg_apis_folder_v1_FolderList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
@ -303,7 +303,7 @@ func schema_pkg_apis_folder_v0alpha1_FolderList(ref common.ReferenceCallback) co
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/pkg/apis/folder/v0alpha1.Folder"),
Ref: ref("github.com/grafana/grafana/pkg/apis/folder/v1.Folder"),
},
},
},
@ -313,11 +313,11 @@ func schema_pkg_apis_folder_v0alpha1_FolderList(ref common.ReferenceCallback) co
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1.Folder", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
"github.com/grafana/grafana/pkg/apis/folder/v1.Folder", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
}
}
func schema_pkg_apis_folder_v0alpha1_ResourceStats(ref common.ReferenceCallback) common.OpenAPIDefinition {
func schema_pkg_apis_folder_v1_ResourceStats(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
@ -351,7 +351,7 @@ func schema_pkg_apis_folder_v0alpha1_ResourceStats(ref common.ReferenceCallback)
}
}
func schema_pkg_apis_folder_v0alpha1_Spec(ref common.ReferenceCallback) common.OpenAPIDefinition {
func schema_pkg_apis_folder_v1_Spec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{

@ -1,2 +1,2 @@
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/folder/v0alpha1,DescendantCounts,Counts
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/folder/v0alpha1,FolderInfoList,Items
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/folder/v1,DescendantCounts,Counts
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/folder/v1,FolderInfoList,Items

@ -12,7 +12,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
authlib "github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/identity"

@ -10,7 +10,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
dashboardv1alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
)
func TestLargeDashboardSupport(t *testing.T) {
@ -21,7 +21,7 @@ func TestLargeDashboardSupport(t *testing.T) {
f, err := os.ReadFile(devdash)
require.NoError(t, err)
dash := &dashboardv1alpha1.Dashboard{
dash := &dashv1.Dashboard{
ObjectMeta: v1.ObjectMeta{
Name: "test",
Namespace: "test",
@ -38,7 +38,7 @@ func TestLargeDashboardSupport(t *testing.T) {
scheme := runtime.NewScheme()
err = dashboardv1alpha1.AddToScheme(scheme)
err = dashv1.AddToScheme(scheme)
require.NoError(t, err)
largeObject := NewDashboardLargeObjectSupport(scheme, 0)
@ -56,7 +56,7 @@ func TestLargeDashboardSupport(t *testing.T) {
}`, string(small))
// Now make it big again
rehydratedDash := &dashboardv1alpha1.Dashboard{
rehydratedDash := &dashv1.Dashboard{
ObjectMeta: v1.ObjectMeta{
Name: "test",
Namespace: "test",

@ -13,7 +13,7 @@ import (
authlib "github.com/grafana/authlib/types"
dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
folders "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/provisioning"
"github.com/grafana/grafana/pkg/services/search/sort"
@ -326,7 +326,7 @@ func (a *dashboardSqlAccess) migrateFolders(ctx context.Context, orgId int64, op
// Now send each dashboard
for i := 1; rows.Next(); i++ {
dash := rows.row.Dash
dash.APIVersion = "folder.grafana.app/v0alpha1"
dash.APIVersion = "folder.grafana.app/v1"
dash.Kind = "Folder"
dash.SetNamespace(opts.Namespace)
dash.SetResourceVersion("") // it will be filled in by the backend

@ -132,7 +132,7 @@ func TestBuildSaveDashboardCommand(t *testing.T) {
}
dash := &dashboard.Dashboard{
TypeMeta: metav1.TypeMeta{
APIVersion: "dashboard.grafana.app/v1alpha1",
APIVersion: dashboard.APIVERSION,
},
ObjectMeta: metav1.ObjectMeta{
Name: "test-dash",
@ -170,7 +170,7 @@ func TestBuildSaveDashboardCommand(t *testing.T) {
&dashboards.Dashboard{
ID: 1234,
Version: 2,
APIVersion: "dashboard.grafana.app/v1alpha1",
APIVersion: dashboard.APIVERSION,
}, nil).Once()
cmd, created, err = access.buildSaveDashboardCommand(ctx, 1, dash)
require.NoError(t, err)

@ -14,7 +14,7 @@ import (
dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
"github.com/grafana/grafana/pkg/services/search/sort"
@ -92,7 +92,7 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.Resour
switch req.Options.Key.Resource {
case dashboard.DASHBOARD_RESOURCE:
queryType = searchstore.TypeDashboard
case folderv0alpha1.RESOURCE:
case folders.RESOURCE:
queryType = searchstore.TypeFolder
default:
return nil, fmt.Errorf("bad type request")
@ -104,7 +104,7 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.Resour
if len(req.Federated) == 1 &&
((req.Federated[0].Resource == dashboard.DASHBOARD_RESOURCE && queryType == searchstore.TypeFolder) ||
(req.Federated[0].Resource == folderv0alpha1.RESOURCE && queryType == searchstore.TypeDashboard)) {
(req.Federated[0].Resource == folders.RESOURCE && queryType == searchstore.TypeDashboard)) {
queryType = "" // makes the legacy store search across both
}
@ -336,8 +336,8 @@ func getResourceKey(item *dashboards.DashboardSearchProjection, namespace string
if item.IsFolder {
return &resource.ResourceKey{
Namespace: namespace,
Group: folderv0alpha1.GROUP,
Resource: folderv0alpha1.RESOURCE,
Group: folders.GROUP,
Resource: folders.RESOURCE,
Name: item.UID,
}
}
@ -401,7 +401,7 @@ func (c *DashboardSearchClient) GetStats(ctx context.Context, req *resource.Reso
switch parts[0] {
case dashboard.GROUP:
count, err = c.dashboardStore.CountInOrg(ctx, info.OrgID, false)
case folderv0alpha1.GROUP:
case folders.GROUP:
count, err = c.dashboardStore.CountInOrg(ctx, info.OrgID, true)
default:
return nil, fmt.Errorf("invalid group")

@ -4,8 +4,8 @@ import (
"context"
"testing"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
@ -25,7 +25,7 @@ func TestDashboardAPIBuilder_Mutate(t *testing.T) {
}{
{
name: "should skip non-create/update operations",
inputObj: &v1alpha1.Dashboard{
inputObj: &dashv1.Dashboard{
Spec: common.Unstructured{
Object: map[string]interface{}{
"id": float64(123),
@ -37,7 +37,7 @@ func TestDashboardAPIBuilder_Mutate(t *testing.T) {
},
{
name: "v0 should extract id and set as label",
inputObj: &v0alpha1.Dashboard{
inputObj: &dashv0.Dashboard{
Spec: common.Unstructured{
Object: map[string]interface{}{
"id": float64(123),
@ -49,7 +49,7 @@ func TestDashboardAPIBuilder_Mutate(t *testing.T) {
},
{
name: "v1 should migrate dashboard to the latest version, if possible, and set as label",
inputObj: &v1alpha1.Dashboard{
inputObj: &dashv1.Dashboard{
Spec: common.Unstructured{
Object: map[string]interface{}{
"id": float64(456),
@ -63,7 +63,7 @@ func TestDashboardAPIBuilder_Mutate(t *testing.T) {
},
{
name: "v1 should not error mutation hook if migration fails",
inputObj: &v1alpha1.Dashboard{
inputObj: &dashv1.Dashboard{
Spec: common.Unstructured{
Object: map[string]interface{}{
"id": float64(456),
@ -100,10 +100,10 @@ func TestDashboardAPIBuilder_Mutate(t *testing.T) {
require.Equal(t, tt.expectedID, meta.GetDeprecatedInternalID()) //nolint:staticcheck
switch v := tt.inputObj.(type) {
case *v0alpha1.Dashboard:
case *dashv0.Dashboard:
_, exists := v.Spec.Object["id"]
require.False(t, exists, "id should be removed from spec")
case *v1alpha1.Dashboard:
case *dashv1.Dashboard:
_, exists := v.Spec.Object["id"]
require.False(t, exists, "id should be removed from spec")
schemaVersion, ok := v.Spec.Object["schemaVersion"].(int)

@ -20,9 +20,9 @@ import (
claims "github.com/grafana/authlib/types"
internal "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashv2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/conversion"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
@ -48,7 +48,7 @@ import (
"github.com/grafana/grafana/pkg/storage/unified/apistore"
"github.com/grafana/grafana/pkg/storage/unified/resource"
folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/services/apiserver"
"github.com/grafana/grafana/pkg/services/apiserver/client"
)
@ -113,7 +113,7 @@ func RegisterAPIService(
dbp := legacysql.NewDatabaseProvider(sql)
namespacer := request.GetNamespaceMapper(cfg)
legacyDashboardSearcher := legacysearcher.NewDashboardSearchClient(dashStore, sorter)
folderClient := client.NewK8sHandler(dual, request.GetNamespaceMapper(cfg), folderv0alpha1.FolderResourceInfo.GroupVersionResource(), restConfigProvider.GetRestConfig, dashStore, userService, unified, sorter)
folderClient := client.NewK8sHandler(dual, request.GetNamespaceMapper(cfg), folders.FolderResourceInfo.GroupVersionResource(), restConfigProvider.GetRestConfig, dashStore, userService, unified, sorter)
builder := &DashboardsAPIBuilder{
log: log.New("grafana-apiserver.dashboards"),
@ -146,28 +146,28 @@ func (b *DashboardsAPIBuilder) GetGroupVersions() []schema.GroupVersion {
if featuremgmt.AnyEnabled(b.features, featuremgmt.FlagDashboardNewLayouts) {
// If dashboards v2 is enabled, we want to use v2alpha1 as the default API version.
return []schema.GroupVersion{
v2alpha1.DashboardResourceInfo.GroupVersion(),
v0alpha1.DashboardResourceInfo.GroupVersion(),
v1alpha1.DashboardResourceInfo.GroupVersion(),
dashv2.DashboardResourceInfo.GroupVersion(),
dashv0.DashboardResourceInfo.GroupVersion(),
dashv1.DashboardResourceInfo.GroupVersion(),
}
}
return []schema.GroupVersion{
v1alpha1.DashboardResourceInfo.GroupVersion(),
v0alpha1.DashboardResourceInfo.GroupVersion(),
v2alpha1.DashboardResourceInfo.GroupVersion(),
dashv1.DashboardResourceInfo.GroupVersion(),
dashv0.DashboardResourceInfo.GroupVersion(),
dashv2.DashboardResourceInfo.GroupVersion(),
}
}
func (b *DashboardsAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
b.scheme = scheme
if err := v0alpha1.AddToScheme(scheme); err != nil {
if err := dashv0.AddToScheme(scheme); err != nil {
return err
}
if err := v1alpha1.AddToScheme(scheme); err != nil {
if err := dashv1.AddToScheme(scheme); err != nil {
return err
}
if err := v2alpha1.AddToScheme(scheme); err != nil {
if err := dashv2.AddToScheme(scheme); err != nil {
return err
}
@ -370,13 +370,13 @@ func getDashboardProperties(obj runtime.Object) (string, string, error) {
// Extract properties based on the object's type
switch d := obj.(type) {
case *v0alpha1.Dashboard:
case *dashv0.Dashboard:
title = d.Spec.GetNestedString(dashboardSpecTitle)
refresh = d.Spec.GetNestedString(dashboardSpecRefreshInterval)
case *v1alpha1.Dashboard:
case *dashv1.Dashboard:
title = d.Spec.GetNestedString(dashboardSpecTitle)
refresh = d.Spec.GetNestedString(dashboardSpecRefreshInterval)
case *v2alpha1.Dashboard:
case *dashv2.Dashboard:
title = d.Spec.Title
refresh = d.Spec.TimeSettings.AutoRefresh
default:
@ -401,15 +401,15 @@ func (b *DashboardsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver
largeObjects = NewDashboardLargeObjectSupport(opts.Scheme, opts.StorageOpts.BlobThresholdBytes)
storageOpts.LargeObjectSupport = largeObjects
}
opts.StorageOptsRegister(v0alpha1.DashboardResourceInfo.GroupResource(), storageOpts)
opts.StorageOptsRegister(dashv0.DashboardResourceInfo.GroupResource(), storageOpts)
// v0alpha1
if err := b.storageForVersion(apiGroupInfo, opts, largeObjects,
v0alpha1.DashboardResourceInfo,
&v0alpha1.LibraryPanelResourceInfo,
dashv0.DashboardResourceInfo,
&dashv0.LibraryPanelResourceInfo,
func(obj runtime.Object, access *internal.DashboardAccess) (v runtime.Object, err error) {
dto := &v0alpha1.DashboardWithAccessInfo{}
dash, ok := obj.(*v0alpha1.Dashboard)
dto := &dashv0.DashboardWithAccessInfo{}
dash, ok := obj.(*dashv0.Dashboard)
if ok {
dto.Dashboard = *dash
}
@ -423,11 +423,11 @@ func (b *DashboardsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver
// v1alpha1
if err := b.storageForVersion(apiGroupInfo, opts, largeObjects,
v1alpha1.DashboardResourceInfo,
dashv1.DashboardResourceInfo,
nil, // do not register library panel
func(obj runtime.Object, access *internal.DashboardAccess) (v runtime.Object, err error) {
dto := &v1alpha1.DashboardWithAccessInfo{}
dash, ok := obj.(*v1alpha1.Dashboard)
dto := &dashv1.DashboardWithAccessInfo{}
dash, ok := obj.(*dashv1.Dashboard)
if ok {
dto.Dashboard = *dash
}
@ -441,11 +441,11 @@ func (b *DashboardsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver
// v2alpha1
if err := b.storageForVersion(apiGroupInfo, opts, largeObjects,
v2alpha1.DashboardResourceInfo,
dashv2.DashboardResourceInfo,
nil, // do not register library panel
func(obj runtime.Object, access *internal.DashboardAccess) (v runtime.Object, err error) {
dto := &v2alpha1.DashboardWithAccessInfo{}
dash, ok := obj.(*v2alpha1.Dashboard)
dto := &dashv2.DashboardWithAccessInfo{}
dash, ok := obj.(*dashv2.Dashboard)
if ok {
dto.Dashboard = *dash
}
@ -515,9 +515,9 @@ func (b *DashboardsAPIBuilder) storageForVersion(
func (b *DashboardsAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
return func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
defs := v0alpha1.GetOpenAPIDefinitions(ref)
maps.Copy(defs, v1alpha1.GetOpenAPIDefinitions(ref))
maps.Copy(defs, v2alpha1.GetOpenAPIDefinitions(ref))
defs := dashv0.GetOpenAPIDefinitions(ref)
maps.Copy(defs, dashv1.GetOpenAPIDefinitions(ref))
maps.Copy(defs, dashv2.GetOpenAPIDefinitions(ref))
return defs
}
}
@ -528,7 +528,7 @@ func (b *DashboardsAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.Op
}
func (b *DashboardsAPIBuilder) GetAPIRoutes(gv schema.GroupVersion) *builder.APIRoutes {
if gv.Version != v0alpha1.VERSION {
if gv.Version != dashv0.VERSION {
return nil // Only show the custom routes for v0
}

@ -5,9 +5,9 @@ import (
"fmt"
"testing"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashv2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@ -24,7 +24,7 @@ func TestDashboardAPIBuilder_Validate(t *testing.T) {
zeroInt64 := int64(0)
tests := []struct {
name string
inputObj *v1alpha1.Dashboard
inputObj *dashv1.Dashboard
deletionOptions metav1.DeleteOptions
dashboardResponse *dashboards.DashboardProvisioning
dashboardErrorResponse error
@ -33,7 +33,7 @@ func TestDashboardAPIBuilder_Validate(t *testing.T) {
}{
{
name: "should return an error if data is found",
inputObj: &v1alpha1.Dashboard{
inputObj: &dashv1.Dashboard{
Spec: common.Unstructured{},
TypeMeta: metav1.TypeMeta{
Kind: "Dashboard",
@ -52,7 +52,7 @@ func TestDashboardAPIBuilder_Validate(t *testing.T) {
},
{
name: "should return an error if unable to check",
inputObj: &v1alpha1.Dashboard{
inputObj: &dashv1.Dashboard{
Spec: common.Unstructured{},
TypeMeta: metav1.TypeMeta{
Kind: "Dashboard",
@ -71,7 +71,7 @@ func TestDashboardAPIBuilder_Validate(t *testing.T) {
},
{
name: "should be okay if error is provisioned dashboard not found",
inputObj: &v1alpha1.Dashboard{
inputObj: &dashv1.Dashboard{
Spec: common.Unstructured{},
TypeMeta: metav1.TypeMeta{
Kind: "Dashboard",
@ -90,7 +90,7 @@ func TestDashboardAPIBuilder_Validate(t *testing.T) {
},
{
name: "Should still run the check for delete if grace period is not 0",
inputObj: &v1alpha1.Dashboard{
inputObj: &dashv1.Dashboard{
Spec: common.Unstructured{},
TypeMeta: metav1.TypeMeta{
Kind: "Dashboard",
@ -109,7 +109,7 @@ func TestDashboardAPIBuilder_Validate(t *testing.T) {
},
{
name: "should not run the check for delete if grace period is set to 0",
inputObj: &v1alpha1.Dashboard{
inputObj: &dashv1.Dashboard{
Spec: common.Unstructured{},
TypeMeta: metav1.TypeMeta{
Kind: "Dashboard",
@ -137,10 +137,10 @@ func TestDashboardAPIBuilder_Validate(t *testing.T) {
err := b.Validate(context.Background(), admission.NewAttributesRecord(
tt.inputObj,
nil,
v1alpha1.DashboardResourceInfo.GroupVersionKind(),
dashv1.DashboardResourceInfo.GroupVersionKind(),
"stacks-123",
tt.inputObj.Name,
v1alpha1.DashboardResourceInfo.GroupVersionResource(),
dashv1.DashboardResourceInfo.GroupVersionResource(),
"",
admission.Operation("DELETE"),
&tt.deletionOptions,
@ -173,9 +173,9 @@ func TestDashboardAPIBuilder_GetGroupVersions(t *testing.T) {
name: "should return v1alpha1 by default",
enabledFeatures: []string{},
expected: []schema.GroupVersion{
v1alpha1.DashboardResourceInfo.GroupVersion(),
v0alpha1.DashboardResourceInfo.GroupVersion(),
v2alpha1.DashboardResourceInfo.GroupVersion(),
dashv1.DashboardResourceInfo.GroupVersion(),
dashv0.DashboardResourceInfo.GroupVersion(),
dashv2.DashboardResourceInfo.GroupVersion(),
},
},
{
@ -184,9 +184,9 @@ func TestDashboardAPIBuilder_GetGroupVersions(t *testing.T) {
featuremgmt.FlagKubernetesDashboards,
},
expected: []schema.GroupVersion{
v1alpha1.DashboardResourceInfo.GroupVersion(),
v0alpha1.DashboardResourceInfo.GroupVersion(),
v2alpha1.DashboardResourceInfo.GroupVersion(),
dashv1.DashboardResourceInfo.GroupVersion(),
dashv0.DashboardResourceInfo.GroupVersion(),
dashv2.DashboardResourceInfo.GroupVersion(),
},
},
{
@ -195,9 +195,9 @@ func TestDashboardAPIBuilder_GetGroupVersions(t *testing.T) {
featuremgmt.FlagDashboardNewLayouts,
},
expected: []schema.GroupVersion{
v2alpha1.DashboardResourceInfo.GroupVersion(),
v0alpha1.DashboardResourceInfo.GroupVersion(),
v1alpha1.DashboardResourceInfo.GroupVersion(),
dashv2.DashboardResourceInfo.GroupVersion(),
dashv0.DashboardResourceInfo.GroupVersion(),
dashv1.DashboardResourceInfo.GroupVersion(),
},
},
}

@ -23,7 +23,7 @@ import (
dashboardv0alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
"github.com/grafana/grafana/pkg/services/dashboards"
@ -265,7 +265,7 @@ func (s *SearchHandler) DoSearch(w http.ResponseWriter, r *http.Request) {
searchRequest.Options.Key, err = asResourceKey(user.GetNamespace(), dashboardv0alpha1.DASHBOARD_RESOURCE)
// Currently a search query is across folders and dashboards
if err == nil {
federate, err = asResourceKey(user.GetNamespace(), folderv0alpha1.RESOURCE)
federate, err = asResourceKey(user.GetNamespace(), folders.RESOURCE)
}
case 1:
searchRequest.Options.Key, err = asResourceKey(user.GetNamespace(), types[0])
@ -473,7 +473,7 @@ func (s *SearchHandler) getDashboardsUIDsSharedWithUser(ctx context.Context, use
}
// only folders the user has access to will be returned here
folderKey, err := asResourceKey(user.GetNamespace(), folderv0alpha1.RESOURCE)
folderKey, err := asResourceKey(user.GetNamespace(), folders.RESOURCE)
if err != nil {
return sharedDashboards, err
}

@ -13,7 +13,7 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
folderv0aplha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@ -261,7 +261,7 @@ func TestMultiTenantAuthorizer(t *testing.T) {
authz := newMultiTenantAuthorizer(tt.input.client)
authorized, _, err := authz.Authorize(
types.WithAuthInfo(context.Background(), tt.input.info),
authorizer.AttributesRecord{User: tt.input.info, Verb: tt.input.verb, APIGroup: folderv0aplha1.GROUP, Resource: "folders", ResourceRequest: true, Name: "123", Namespace: "stacks-1"},
authorizer.AttributesRecord{User: tt.input.info, Verb: tt.input.verb, APIGroup: folders.GROUP, Resource: "folders", ResourceRequest: true, Name: "123", Namespace: "stacks-1"},
)
if tt.expeted.err {

@ -9,7 +9,7 @@ import (
claims "github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
"github.com/grafana/grafana/pkg/services/folder"
@ -40,20 +40,20 @@ func LegacyCreateCommandToUnstructured(cmd *folder.CreateFolderCommand) (*unstru
return obj, nil
}
func LegacyFolderToUnstructured(v *folder.Folder, namespacer request.NamespaceMapper) (*v0alpha1.Folder, error) {
func LegacyFolderToUnstructured(v *folder.Folder, namespacer request.NamespaceMapper) (*folders.Folder, error) {
return convertToK8sResource(v, namespacer)
}
func convertToK8sResource(v *folder.Folder, namespacer request.NamespaceMapper) (*v0alpha1.Folder, error) {
f := &v0alpha1.Folder{
TypeMeta: v0alpha1.FolderResourceInfo.TypeMeta(),
func convertToK8sResource(v *folder.Folder, namespacer request.NamespaceMapper) (*folders.Folder, error) {
f := &folders.Folder{
TypeMeta: folders.FolderResourceInfo.TypeMeta(),
ObjectMeta: metav1.ObjectMeta{
Name: v.UID,
ResourceVersion: fmt.Sprintf("%d", v.Updated.UnixMilli()),
CreationTimestamp: metav1.NewTime(v.Created),
Namespace: namespacer(v.OrgID),
},
Spec: v0alpha1.Spec{
Spec: folders.Spec{
Title: v.Title,
Description: v.Description,
},

@ -12,7 +12,7 @@ import (
claims "github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
@ -91,7 +91,7 @@ func (s *folderStorage) Create(ctx context.Context,
return nil, err
}
p, ok := obj.(*v0alpha1.Folder)
p, ok := obj.(*folders.Folder)
if !ok {
return nil, fmt.Errorf("expected folder?")
}

@ -5,7 +5,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/services/accesscontrol"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/user"
@ -50,7 +50,7 @@ func TestSetDefaultPermissionsWhenCreatingFolder(t *testing.T) {
store: &fakeStorage{},
cfg: cfg,
}
obj := &v0alpha1.Folder{}
obj := &folders.Folder{}
ctx := request.WithNamespace(context.Background(), "org-2")
ctx = identity.WithRequester(ctx, &user.SignedInUser{

@ -14,7 +14,7 @@ import (
"github.com/grafana/grafana/pkg/api/apierrors"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@ -116,7 +116,7 @@ func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListO
return nil, err
}
list := &v0alpha1.FolderList{}
list := &folders.FolderList{}
for _, v := range hits {
r, err := convertToK8sResource(v, s.namespacer)
if err != nil {
@ -178,7 +178,7 @@ func (s *legacyStorage) Create(ctx context.Context,
return nil, err
}
p, ok := obj.(*v0alpha1.Folder)
p, ok := obj.(*folders.Folder)
if !ok {
return nil, fmt.Errorf("expected folder?")
}
@ -248,11 +248,11 @@ func (s *legacyStorage) Update(ctx context.Context,
if err != nil {
return oldObj, created, err
}
f, ok := obj.(*v0alpha1.Folder)
f, ok := obj.(*folders.Folder)
if !ok {
return nil, created, fmt.Errorf("expected folder after update")
}
old, ok := oldObj.(*v0alpha1.Folder)
old, ok := oldObj.(*folders.Folder)
if !ok {
return nil, created, fmt.Errorf("expected old object to be a folder also")
}
@ -313,7 +313,7 @@ func (s *legacyStorage) Delete(ctx context.Context, name string, deleteValidatio
if err != nil {
return nil, false, err
}
p, ok := v.(*v0alpha1.Folder)
p, ok := v.(*folders.Folder)
if !ok {
return v, false, fmt.Errorf("expected a folder response from Get")
}

@ -14,7 +14,7 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folderv1 "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/org"
@ -57,7 +57,7 @@ func TestLegacyStorageList(t *testing.T) {
uidsReturnedByList := []string{}
for _, obj := range list {
f, ok := obj.(*v0alpha1.Folder)
f, ok := obj.(*folderv1.Folder)
require.Equal(t, true, ok)
uidsReturnedByList = append(uidsReturnedByList, f.Name)
}
@ -89,7 +89,7 @@ func TestLegacyStorage_List_Pagination(t *testing.T) {
result, err := storage.List(ctx, options)
require.NoError(t, err)
list, ok := result.(*v0alpha1.FolderList)
list, ok := result.(*folderv1.FolderList)
require.True(t, ok)
token, err := base64.StdEncoding.DecodeString(list.Continue)
require.NoError(t, err)
@ -113,7 +113,7 @@ func TestLegacyStorage_List_Pagination(t *testing.T) {
result, err := storage.List(ctx, options)
require.NoError(t, err)
list, ok := result.(*v0alpha1.FolderList)
list, ok := result.(*folderv1.FolderList)
require.True(t, ok)
token, err := base64.StdEncoding.DecodeString(list.Continue)
require.NoError(t, err)
@ -152,7 +152,7 @@ func TestLegacyStorage_List_LabelSelector(t *testing.T) {
require.False(t, folderService.LastQuery.WithFullpath)
require.False(t, folderService.LastQuery.WithFullpathUIDs)
list, ok := result.(*v0alpha1.FolderList)
list, ok := result.(*folderv1.FolderList)
require.True(t, ok)
require.Len(t, list.Items, 1)
})
@ -181,7 +181,7 @@ func TestLegacyStorage_List_LabelSelector(t *testing.T) {
require.True(t, folderService.LastQuery.WithFullpath)
require.True(t, folderService.LastQuery.WithFullpathUIDs)
list, ok := result.(*v0alpha1.FolderList)
list, ok := result.(*folderv1.FolderList)
require.True(t, ok)
require.Len(t, list.Items, 1)

@ -20,7 +20,7 @@ import (
authtypes "github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/accesscontrol"
@ -37,7 +37,7 @@ import (
var _ builder.APIGroupBuilder = (*FolderAPIBuilder)(nil)
var _ builder.APIGroupValidation = (*FolderAPIBuilder)(nil)
var resourceInfo = v0alpha1.FolderResourceInfo
var resourceInfo = folders.FolderResourceInfo
var errNoUser = errors.New("valid user is required")
var errNoResource = errors.New("resource name is required")
@ -67,13 +67,6 @@ func RegisterAPIService(cfg *setting.Cfg,
registerer prometheus.Registerer,
unified resource.ResourceClient,
) *FolderAPIBuilder {
if !featuremgmt.AnyEnabled(features,
featuremgmt.FlagKubernetesClientDashboardsFolders,
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs,
featuremgmt.FlagProvisioning) {
return nil // skip registration unless opting into Kubernetes folders or unless we want to customize registration when testing
}
builder := &FolderAPIBuilder{
gv: resourceInfo.GroupVersion(),
features: features,
@ -103,11 +96,11 @@ func (b *FolderAPIBuilder) GetGroupVersion() schema.GroupVersion {
func addKnownTypes(scheme *runtime.Scheme, gv schema.GroupVersion) {
scheme.AddKnownTypes(gv,
&v0alpha1.Folder{},
&v0alpha1.FolderList{},
&v0alpha1.FolderInfoList{},
&v0alpha1.DescendantCounts{},
&v0alpha1.FolderAccessInfo{},
&folders.Folder{},
&folders.FolderList{},
&folders.FolderInfoList{},
&folders.DescendantCounts{},
&folders.FolderAccessInfo{},
)
}
@ -142,7 +135,7 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API
return err
}
storage[resourceInfo.StoragePath()] = store
apiGroupInfo.VersionedResourcesStorageMap[v0alpha1.VERSION] = storage
apiGroupInfo.VersionedResourcesStorageMap[folders.VERSION] = storage
b.storage = storage[resourceInfo.StoragePath()].(grafanarest.Storage)
return nil
}
@ -187,13 +180,13 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API
storage[resourceInfo.StoragePath("counts")] = &subCountREST{searcher: b.searcher}
storage[resourceInfo.StoragePath("access")] = &subAccessREST{b.folderSvc}
apiGroupInfo.VersionedResourcesStorageMap[v0alpha1.VERSION] = storage
apiGroupInfo.VersionedResourcesStorageMap[folders.VERSION] = storage
b.storage = storage[resourceInfo.StoragePath()].(grafanarest.Storage)
return nil
}
func (b *FolderAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
return v0alpha1.GetOpenAPIDefinitions
return folders.GetOpenAPIDefinitions
}
func (b *FolderAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, error) {
@ -222,9 +215,9 @@ func (b *FolderAPIBuilder) Mutate(ctx context.Context, a admission.Attributes, _
verb := a.GetOperation()
if verb == admission.Create || verb == admission.Update {
obj := a.GetObject()
f, ok := obj.(*v0alpha1.Folder)
f, ok := obj.(*folders.Folder)
if !ok {
return fmt.Errorf("obj is not v0alpha1.Folder")
return fmt.Errorf("obj is not folders.Folder")
}
f.Spec.Title = strings.Trim(f.Spec.Title, "")
return nil
@ -239,9 +232,9 @@ func (b *FolderAPIBuilder) Validate(ctx context.Context, a admission.Attributes,
return nil // This is normal for sub-resource
}
f, ok := obj.(*v0alpha1.Folder)
f, ok := obj.(*folders.Folder)
if !ok {
return fmt.Errorf("obj is not v0alpha1.Folder")
return fmt.Errorf("obj is not folders.Folder")
}
verb := a.GetOperation()
@ -262,7 +255,7 @@ func (b *FolderAPIBuilder) Validate(ctx context.Context, a admission.Attributes,
return nil
}
func (b *FolderAPIBuilder) validateOnDelete(ctx context.Context, f *v0alpha1.Folder) error {
func (b *FolderAPIBuilder) validateOnDelete(ctx context.Context, f *folders.Folder) error {
resp, err := b.searcher.GetStats(ctx, &resource.ResourceStatsRequest{Namespace: f.Namespace, Folder: f.Name})
if err != nil {
return err
@ -292,9 +285,9 @@ func (b *FolderAPIBuilder) validateOnCreate(ctx context.Context, id string, obj
}
}
f, ok := obj.(*v0alpha1.Folder)
f, ok := obj.(*folders.Folder)
if !ok {
return fmt.Errorf("obj is not v0alpha1.Folder")
return fmt.Errorf("obj is not folders.Folder")
}
if f.Spec.Title == "" {
return dashboards.ErrFolderTitleEmpty
@ -342,14 +335,14 @@ func (b *FolderAPIBuilder) checkFolderMaxDepth(ctx context.Context, obj runtime.
}
func (b *FolderAPIBuilder) validateOnUpdate(ctx context.Context, obj, old runtime.Object) error {
f, ok := obj.(*v0alpha1.Folder)
f, ok := obj.(*folders.Folder)
if !ok {
return fmt.Errorf("obj is not v0alpha1.Folder")
return fmt.Errorf("obj is not folders.Folder")
}
fOld, ok := old.(*v0alpha1.Folder)
fOld, ok := old.(*folders.Folder)
if !ok {
return fmt.Errorf("obj is not v0alpha1.Folder")
return fmt.Errorf("obj is not folders.Folder")
}
var newParent = getParent(obj)
if newParent != getParent(fOld) {

@ -10,7 +10,7 @@ import (
"k8s.io/apiserver/pkg/admission"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboards"
@ -22,7 +22,7 @@ import (
func TestFolderAPIBuilder_Validate_Create(t *testing.T) {
type input struct {
obj *v0alpha1.Folder
obj *folders.Folder
annotations map[string]string
name string
}
@ -30,15 +30,15 @@ func TestFolderAPIBuilder_Validate_Create(t *testing.T) {
initialMaxDepth := folderValidationRules.maxDepth
folderValidationRules.maxDepth = 2
defer func() { folderValidationRules.maxDepth = initialMaxDepth }()
deepFolder := &v0alpha1.Folder{
Spec: v0alpha1.Spec{
deepFolder := &folders.Folder{
Spec: folders.Spec{
Title: "foo",
},
}
deepFolder.Name = "valid-parent"
deepFolder.Annotations = map[string]string{"grafana.app/folder": "valid-grandparent"}
parentFolder := &v0alpha1.Folder{
Spec: v0alpha1.Spec{
parentFolder := &folders.Folder{
Spec: folders.Spec{
Title: "foo-grandparent",
},
}
@ -53,8 +53,8 @@ func TestFolderAPIBuilder_Validate_Create(t *testing.T) {
{
name: "should return error when name is invalid",
input: input{
obj: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
obj: &folders.Folder{
Spec: folders.Spec{
Title: "foo",
},
},
@ -65,8 +65,8 @@ func TestFolderAPIBuilder_Validate_Create(t *testing.T) {
{
name: "should return no error if every validation passes",
input: input{
obj: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
obj: &folders.Folder{
Spec: folders.Spec{
Title: "foo",
},
},
@ -76,8 +76,8 @@ func TestFolderAPIBuilder_Validate_Create(t *testing.T) {
{
name: "should not allow creating a folder in a tree that is too deep",
input: input{
obj: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
obj: &folders.Folder{
Spec: folders.Spec{
Title: "foo",
},
},
@ -97,8 +97,8 @@ func TestFolderAPIBuilder_Validate_Create(t *testing.T) {
{
name: "should return error when title is empty",
input: input{
obj: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
obj: &folders.Folder{
Spec: folders.Spec{
Title: "",
},
},
@ -110,8 +110,8 @@ func TestFolderAPIBuilder_Validate_Create(t *testing.T) {
name: "should return error if folder is a parent of itself",
input: input{
annotations: map[string]string{utils.AnnoKeyFolder: "myself"},
obj: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
obj: &folders.Folder{
Spec: folders.Spec{
Title: "title",
},
},
@ -145,10 +145,10 @@ func TestFolderAPIBuilder_Validate_Create(t *testing.T) {
err := b.Validate(context.Background(), admission.NewAttributesRecord(
tt.input.obj,
nil,
v0alpha1.SchemeGroupVersion.WithKind("folder"),
folders.SchemeGroupVersion.WithKind("folder"),
"stacks-123",
tt.input.name,
v0alpha1.SchemeGroupVersion.WithResource("folders"),
folders.SchemeGroupVersion.WithResource("folders"),
"",
"CREATE",
nil,
@ -188,8 +188,8 @@ func TestFolderAPIBuilder_Validate_Delete(t *testing.T) {
us := storageMock{m, s}
sm := searcherMock{Mock: m}
obj := &v0alpha1.Folder{
Spec: v0alpha1.Spec{
obj := &folders.Folder{
Spec: folders.Spec{
Title: "foo",
},
ObjectMeta: metav1.ObjectMeta{
@ -221,10 +221,10 @@ func TestFolderAPIBuilder_Validate_Delete(t *testing.T) {
err := b.Validate(context.Background(), admission.NewAttributesRecord(
obj,
nil,
v0alpha1.SchemeGroupVersion.WithKind("folder"),
folders.SchemeGroupVersion.WithKind("folder"),
obj.Namespace,
obj.Name,
v0alpha1.SchemeGroupVersion.WithResource("folders"),
folders.SchemeGroupVersion.WithResource("folders"),
"",
"DELETE",
nil,
@ -243,7 +243,7 @@ func TestFolderAPIBuilder_Validate_Delete(t *testing.T) {
}
func TestFolderAPIBuilder_Validate_Update(t *testing.T) {
var circularObj = &v0alpha1.Folder{
var circularObj = &folders.Folder{
ObjectMeta: metav1.ObjectMeta{
Namespace: "stacks-123",
Name: "new-parent",
@ -253,15 +253,15 @@ func TestFolderAPIBuilder_Validate_Update(t *testing.T) {
tests := []struct {
name string
updatedObj *v0alpha1.Folder
expected *v0alpha1.Folder
updatedObj *folders.Folder
expected *folders.Folder
setupFn func(*mock.Mock)
wantErr bool
}{
{
name: "should allow updating a folder spec",
updatedObj: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
updatedObj: &folders.Folder{
Spec: folders.Spec{
Title: "different title",
},
ObjectMeta: metav1.ObjectMeta{
@ -270,8 +270,8 @@ func TestFolderAPIBuilder_Validate_Update(t *testing.T) {
Annotations: map[string]string{"grafana.app/folder": "valid-parent"},
},
},
expected: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
expected: &folders.Folder{
Spec: folders.Spec{
Title: "different title",
},
ObjectMeta: metav1.ObjectMeta{
@ -283,8 +283,8 @@ func TestFolderAPIBuilder_Validate_Update(t *testing.T) {
},
{
name: "updated title should not be empty",
updatedObj: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
updatedObj: &folders.Folder{
Spec: folders.Spec{
Title: "",
},
ObjectMeta: metav1.ObjectMeta{
@ -297,8 +297,8 @@ func TestFolderAPIBuilder_Validate_Update(t *testing.T) {
},
{
name: "should allow moving to a valid parent",
updatedObj: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
updatedObj: &folders.Folder{
Spec: folders.Spec{
Title: "foo",
},
ObjectMeta: metav1.ObjectMeta{
@ -309,14 +309,14 @@ func TestFolderAPIBuilder_Validate_Update(t *testing.T) {
},
setupFn: func(m *mock.Mock) {
m.On("Get", mock.Anything, "new-parent", mock.Anything).Return(
&v0alpha1.Folder{},
&folders.Folder{},
nil).Once()
},
},
{
name: "should not allow moving to a k6 folder",
updatedObj: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
updatedObj: &folders.Folder{
Spec: folders.Spec{
Title: "foo",
},
ObjectMeta: metav1.ObjectMeta{
@ -327,15 +327,15 @@ func TestFolderAPIBuilder_Validate_Update(t *testing.T) {
},
setupFn: func(m *mock.Mock) {
m.On("Get", mock.Anything, accesscontrol.K6FolderUID, mock.Anything).Return(
&v0alpha1.Folder{},
&folders.Folder{},
nil).Once()
},
wantErr: true,
},
{
name: "should not allow moving to a folder that is too deep",
updatedObj: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
updatedObj: &folders.Folder{
Spec: folders.Spec{
Title: "foo",
},
ObjectMeta: metav1.ObjectMeta{
@ -358,8 +358,8 @@ func TestFolderAPIBuilder_Validate_Update(t *testing.T) {
us := storageMock{m, s}
sm := searcherMock{Mock: m}
obj := &v0alpha1.Folder{
Spec: v0alpha1.Spec{
obj := &folders.Folder{
Spec: folders.Spec{
Title: "foo",
},
ObjectMeta: metav1.ObjectMeta{
@ -386,10 +386,10 @@ func TestFolderAPIBuilder_Validate_Update(t *testing.T) {
err := b.Validate(context.Background(), admission.NewAttributesRecord(
tt.updatedObj,
obj,
v0alpha1.SchemeGroupVersion.WithKind("folder"),
folders.SchemeGroupVersion.WithKind("folder"),
tt.updatedObj.Namespace,
tt.updatedObj.Name,
v0alpha1.SchemeGroupVersion.WithResource("folders"),
folders.SchemeGroupVersion.WithResource("folders"),
"",
"UPDATE",
nil,
@ -410,14 +410,14 @@ func TestFolderAPIBuilder_Validate_Update(t *testing.T) {
func TestFolderAPIBuilder_Mutate_Create(t *testing.T) {
tests := []struct {
name string
input *v0alpha1.Folder
expected *v0alpha1.Folder
input *folders.Folder
expected *folders.Folder
wantErr bool
}{
{
name: "should trim a title",
input: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
input: &folders.Folder{
Spec: folders.Spec{
Title: " foo ",
},
TypeMeta: metav1.TypeMeta{
@ -427,8 +427,8 @@ func TestFolderAPIBuilder_Mutate_Create(t *testing.T) {
Name: "valid-name",
},
},
expected: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
expected: &folders.Folder{
Spec: folders.Spec{
Title: "foo",
},
TypeMeta: metav1.TypeMeta{
@ -441,8 +441,8 @@ func TestFolderAPIBuilder_Mutate_Create(t *testing.T) {
},
{
name: "should return error if title doesnt exist",
input: &v0alpha1.Folder{
Spec: v0alpha1.Spec{},
input: &folders.Folder{
Spec: folders.Spec{},
TypeMeta: metav1.TypeMeta{
Kind: "Folder",
},
@ -454,7 +454,7 @@ func TestFolderAPIBuilder_Mutate_Create(t *testing.T) {
},
{
name: "should return error if spec doesnt exist",
input: &v0alpha1.Folder{
input: &folders.Folder{
TypeMeta: metav1.TypeMeta{
Kind: "Folder",
},
@ -482,10 +482,10 @@ func TestFolderAPIBuilder_Mutate_Create(t *testing.T) {
err := b.Validate(context.Background(), admission.NewAttributesRecord(
tt.input,
nil,
v0alpha1.SchemeGroupVersion.WithKind("folder"),
folders.SchemeGroupVersion.WithKind("folder"),
"stacks-123",
tt.input.Name,
v0alpha1.SchemeGroupVersion.WithResource("folders"),
folders.SchemeGroupVersion.WithResource("folders"),
"",
"CREATE",
nil,
@ -503,8 +503,8 @@ func TestFolderAPIBuilder_Mutate_Create(t *testing.T) {
}
func TestFolderAPIBuilder_Mutate_Update(t *testing.T) {
existingObj := &v0alpha1.Folder{
Spec: v0alpha1.Spec{
existingObj := &folders.Folder{
Spec: folders.Spec{
Title: "some title",
},
TypeMeta: metav1.TypeMeta{
@ -516,14 +516,14 @@ func TestFolderAPIBuilder_Mutate_Update(t *testing.T) {
}
tests := []struct {
name string
input *v0alpha1.Folder
expected *v0alpha1.Folder
input *folders.Folder
expected *folders.Folder
wantErr bool
}{
{
name: "should trim a title",
input: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
input: &folders.Folder{
Spec: folders.Spec{
Title: " foo ",
},
TypeMeta: metav1.TypeMeta{
@ -533,8 +533,8 @@ func TestFolderAPIBuilder_Mutate_Update(t *testing.T) {
Name: "valid-name",
},
},
expected: &v0alpha1.Folder{
Spec: v0alpha1.Spec{
expected: &folders.Folder{
Spec: folders.Spec{
Title: "foo",
},
TypeMeta: metav1.TypeMeta{
@ -547,8 +547,8 @@ func TestFolderAPIBuilder_Mutate_Update(t *testing.T) {
},
{
name: "should return error if title doesnt exist",
input: &v0alpha1.Folder{
Spec: v0alpha1.Spec{},
input: &folders.Folder{
Spec: folders.Spec{},
TypeMeta: metav1.TypeMeta{
Kind: "Folder",
},
@ -560,7 +560,7 @@ func TestFolderAPIBuilder_Mutate_Update(t *testing.T) {
},
{
name: "should return error if spec doesnt exist",
input: &v0alpha1.Folder{
input: &folders.Folder{
TypeMeta: metav1.TypeMeta{
Kind: "Folder",
},
@ -588,10 +588,10 @@ func TestFolderAPIBuilder_Mutate_Update(t *testing.T) {
err := b.Validate(context.Background(), admission.NewAttributesRecord(
tt.input,
existingObj,
v0alpha1.SchemeGroupVersion.WithKind("folder"),
folders.SchemeGroupVersion.WithKind("folder"),
"stacks-123",
tt.input.Name,
v0alpha1.SchemeGroupVersion.WithResource("folders"),
folders.SchemeGroupVersion.WithResource("folders"),
"",
"UPDATE",
nil,

@ -8,7 +8,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/guardian"
@ -22,7 +22,7 @@ var _ = rest.Connecter(&subAccessREST{})
var _ = rest.StorageMetadata(&subAccessREST{})
func (r *subAccessREST) New() runtime.Object {
return &v0alpha1.FolderAccessInfo{}
return &folders.FolderAccessInfo{}
}
func (r *subAccessREST) Destroy() {
@ -37,7 +37,7 @@ func (r *subAccessREST) ProducesMIMETypes(verb string) []string {
}
func (r *subAccessREST) ProducesObject(verb string) interface{} {
return &v0alpha1.FolderAccessInfo{}
return &folders.FolderAccessInfo{}
}
func (r *subAccessREST) NewConnectOptions() (runtime.Object, bool, string) {
@ -69,7 +69,7 @@ func (r *subAccessREST) Connect(ctx context.Context, name string, opts runtime.O
}
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
access := &v0alpha1.FolderAccessInfo{}
access := &folders.FolderAccessInfo{}
access.CanEdit, _ = guardian.CanEdit()
access.CanSave, _ = guardian.CanSave()
access.CanAdmin, _ = guardian.CanAdmin()

@ -7,7 +7,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/rest"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/storage/unified/resource"
)
@ -22,7 +22,7 @@ var (
)
func (r *subCountREST) New() runtime.Object {
return &v0alpha1.DescendantCounts{}
return &folders.DescendantCounts{}
}
func (r *subCountREST) Destroy() {
@ -37,7 +37,7 @@ func (r *subCountREST) ProducesMIMETypes(verb string) []string {
}
func (r *subCountREST) ProducesObject(verb string) interface{} {
return &v0alpha1.DescendantCounts{}
return &folders.DescendantCounts{}
}
func (r *subCountREST) NewConnectOptions() (runtime.Object, bool, string) {
@ -60,11 +60,11 @@ func (r *subCountREST) Connect(ctx context.Context, name string, opts runtime.Ob
responder.Error(err)
return
}
rsp := &v0alpha1.DescendantCounts{
Counts: make([]v0alpha1.ResourceStats, len(stats.Stats)),
rsp := &folders.DescendantCounts{
Counts: make([]folders.ResourceStats, len(stats.Stats)),
}
for i, v := range stats.Stats {
rsp.Counts[i] = v0alpha1.ResourceStats{
rsp.Counts[i] = folders.ResourceStats{
Group: v.Group,
Resource: v.Resource,
Count: v.Count,

@ -4,7 +4,7 @@ import (
"context"
"testing"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
@ -14,46 +14,46 @@ import (
func TestSubParent(t *testing.T) {
tests := []struct {
name string
input *v0alpha1.Folder
expected *v0alpha1.FolderInfoList
input *folders.Folder
expected *folders.FolderInfoList
setuFn func(*mock.Mock)
}{
{
name: "no parents",
input: &v0alpha1.Folder{
input: &folders.Folder{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Annotations: map[string]string{},
},
Spec: v0alpha1.Spec{
Spec: folders.Spec{
Title: "some tittle",
},
},
expected: &v0alpha1.FolderInfoList{Items: []v0alpha1.FolderInfo{{Name: "test", Title: "some tittle"}}},
expected: &folders.FolderInfoList{Items: []folders.FolderInfo{{Name: "test", Title: "some tittle"}}},
},
{
name: "has a parent",
input: &v0alpha1.Folder{
input: &folders.Folder{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Annotations: map[string]string{"grafana.app/folder": "parent-test"},
},
Spec: v0alpha1.Spec{
Spec: folders.Spec{
Title: "some tittle",
},
},
setuFn: func(m *mock.Mock) {
m.On("Get", context.TODO(), "parent-test", &metav1.GetOptions{}).Return(&v0alpha1.Folder{
m.On("Get", context.TODO(), "parent-test", &metav1.GetOptions{}).Return(&folders.Folder{
ObjectMeta: metav1.ObjectMeta{
Name: "parent-test",
Annotations: map[string]string{},
},
Spec: v0alpha1.Spec{
Spec: folders.Spec{
Title: "some other tittle",
},
}, nil).Once()
},
expected: &v0alpha1.FolderInfoList{Items: []v0alpha1.FolderInfo{
expected: &folders.FolderInfoList{Items: []folders.FolderInfo{
{Name: "test", Title: "some tittle", Parent: "parent-test"},
{Name: "parent-test", Title: "some other tittle"}},
}},

@ -10,7 +10,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/rest"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
)
type subParentsREST struct {
@ -21,7 +21,7 @@ var _ = rest.Connecter(&subParentsREST{})
var _ = rest.StorageMetadata(&subParentsREST{})
func (r *subParentsREST) New() runtime.Object {
return &v0alpha1.FolderInfoList{}
return &folders.FolderInfoList{}
}
func (r *subParentsREST) Destroy() {
@ -36,7 +36,7 @@ func (r *subParentsREST) ProducesMIMETypes(verb string) []string {
}
func (r *subParentsREST) ProducesObject(verb string) interface{} {
return &v0alpha1.FolderInfoList{}
return &folders.FolderInfoList{}
}
func (r *subParentsREST) NewConnectOptions() (runtime.Object, bool, string) {
@ -48,7 +48,7 @@ func (r *subParentsREST) Connect(ctx context.Context, name string, opts runtime.
if err != nil {
return nil, err
}
folder, ok := obj.(*v0alpha1.Folder)
folder, ok := obj.(*folders.Folder)
if !ok {
return nil, fmt.Errorf("expecting folder, found: %T", folder)
}
@ -61,13 +61,13 @@ func (r *subParentsREST) Connect(ctx context.Context, name string, opts runtime.
}), nil
}
func (r *subParentsREST) parents(ctx context.Context, folder *v0alpha1.Folder) *v0alpha1.FolderInfoList {
info := &v0alpha1.FolderInfoList{
Items: []v0alpha1.FolderInfo{},
func (r *subParentsREST) parents(ctx context.Context, folder *folders.Folder) *folders.FolderInfoList {
info := &folders.FolderInfoList{
Items: []folders.FolderInfo{},
}
for folder != nil {
parent := getParent(folder)
info.Items = append(info.Items, v0alpha1.FolderInfo{
info.Items = append(info.Items, folders.FolderInfo{
Name: folder.Name,
Title: folder.Spec.Title,
Description: folder.Spec.Description,
@ -79,7 +79,7 @@ func (r *subParentsREST) parents(ctx context.Context, folder *v0alpha1.Folder) *
obj, err := r.getter.Get(ctx, parent, &metav1.GetOptions{})
if err != nil {
info.Items = append(info.Items, v0alpha1.FolderInfo{
info.Items = append(info.Items, folders.FolderInfo{
Name: parent,
Detached: true,
Description: err.Error(),
@ -87,9 +87,9 @@ func (r *subParentsREST) parents(ctx context.Context, folder *v0alpha1.Folder) *
break
}
parentFolder, ok := obj.(*v0alpha1.Folder)
parentFolder, ok := obj.(*folders.Folder)
if !ok {
info.Items = append(info.Items, v0alpha1.FolderInfo{
info.Items = append(info.Items, folders.FolderInfo{
Name: parent,
Detached: true,
Description: fmt.Sprintf("expected folder, found: %T", obj),

@ -12,7 +12,7 @@ import (
"github.com/grafana/grafana-app-sdk/logging"
"github.com/grafana/grafana/pkg/apimachinery/utils"
folders "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/resources"

@ -29,7 +29,7 @@ import (
dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
apiutils "github.com/grafana/grafana/pkg/apimachinery/utils"
folders "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
"github.com/grafana/grafana/pkg/apiserver/readonly"
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"

@ -11,7 +11,7 @@ import (
"k8s.io/client-go/dynamic"
dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
iam "github.com/grafana/grafana/pkg/apis/iam/v0alpha1"
"github.com/grafana/grafana/pkg/services/apiserver"
"github.com/grafana/grafana/pkg/services/apiserver/client"

@ -12,7 +12,7 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/repository"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath"
)
@ -119,8 +119,8 @@ func (fm *FolderManager) EnsureFolderExists(ctx context.Context, folder Folder,
},
},
}
obj.SetAPIVersion(v0alpha1.APIVERSION)
obj.SetKind(v0alpha1.FolderResourceInfo.GroupVersionKind().Kind)
obj.SetAPIVersion(folders.APIVERSION)
obj.SetKind(folders.FolderResourceInfo.GroupVersionKind().Kind)
obj.SetNamespace(cfg.GetNamespace())
obj.SetName(folder.ID)

@ -8,7 +8,7 @@ import (
dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
folders "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy"
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"

@ -56,7 +56,7 @@ spec:
// Now try again without a name
_, err = parser.Parse(context.Background(), &repository.FileInfo{
Data: []byte(`apiVersion: dashboard.grafana.app/v1alpha1
Data: []byte(`apiVersion: ` + dashboardV1.APIVERSION + `
kind: Dashboard
spec:
title: Test dashboard

@ -8,7 +8,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/grafana/grafana/pkg/apimachinery/utils"
folders "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/safepath"
)

@ -4,11 +4,12 @@ import (
"fmt"
"testing"
dashboardv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/stretchr/testify/require"
)
func TestReadRuntimeCOnfig(t *testing.T) {
out, err := ReadRuntimeConfig("all/all=true,dashboard.grafana.app/v1alpha1=false")
out, err := ReadRuntimeConfig("all/all=true," + dashboardv1.APIVERSION + "=false")
require.NoError(t, err)
require.Equal(t, []RuntimeConfig{
{Group: "all", Version: "all", Enabled: true},

@ -307,7 +307,7 @@ func TestService_OrgID(t *testing.T) {
desc: "should set org id from default namespace",
req: &authn.Request{HTTPRequest: &http.Request{
Header: map[string][]string{},
URL: mustParseURL("http://localhost/apis/folder.grafana.app/v0alpha1/namespaces/default/folders"),
URL: mustParseURL("http://localhost/apis/folder.grafana.app/v1/namespaces/default/folders"),
}},
expectedOrgID: 1,
},
@ -315,7 +315,7 @@ func TestService_OrgID(t *testing.T) {
desc: "should set org id from namespace",
req: &authn.Request{HTTPRequest: &http.Request{
Header: map[string][]string{},
URL: mustParseURL("http://localhost/apis/folder.grafana.app/v0alpha1/namespaces/org-2/folders"),
URL: mustParseURL("http://localhost/apis/folder.grafana.app/v1/namespaces/org-2/folders"),
}},
expectedOrgID: 2,
},
@ -323,7 +323,7 @@ func TestService_OrgID(t *testing.T) {
desc: "should set set org 1 for stack namespace",
req: &authn.Request{HTTPRequest: &http.Request{
Header: map[string][]string{},
URL: mustParseURL("http://localhost/apis/folder.grafana.app/v0alpha1/namespaces/stacks-100/folders"),
URL: mustParseURL("http://localhost/apis/folder.grafana.app/v1/namespaces/stacks-100/folders"),
}},
stackID: 100,
expectedOrgID: 1,
@ -332,7 +332,7 @@ func TestService_OrgID(t *testing.T) {
desc: "should error for wrong stack namespace",
req: &authn.Request{HTTPRequest: &http.Request{
Header: map[string][]string{},
URL: mustParseURL("http://localhost/apis/folder.grafana.app/v0alpha1/namespaces/stacks-100/folders"),
URL: mustParseURL("http://localhost/apis/folder.grafana.app/v1/namespaces/stacks-100/folders"),
}},
stackID: 101,
expectedOrgID: 0,

@ -12,7 +12,7 @@ import (
"k8s.io/client-go/tools/pager"
"github.com/grafana/grafana/pkg/apimachinery/utils"
folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folderv1 "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/storage/legacysql"
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
@ -158,5 +158,5 @@ func (s *APIFolderStore) client(ctx context.Context, namespace string) (dynamic.
if err != nil {
return nil, err
}
return client.Resource(folderv0alpha1.FolderResourceInfo.GroupVersionResource()).Namespace(namespace), nil
return client.Resource(folderv1.FolderResourceInfo.GroupVersionResource()).Namespace(namespace), nil
}

@ -4,7 +4,7 @@ import (
authzv1 "github.com/grafana/authlib/authz/proto/v1"
"google.golang.org/protobuf/types/known/structpb"
folderalpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
iamalpha1 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1"
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
)
@ -16,8 +16,8 @@ type typeInfo struct {
var typedResources = map[string]typeInfo{
FormatGroupResource(
folderalpha1.FolderResourceInfo.GroupResource().Group,
folderalpha1.FolderResourceInfo.GroupResource().Resource,
folders.FolderResourceInfo.GroupResource().Group,
folders.FolderResourceInfo.GroupResource().Resource,
"",
): {Type: "folder", Relations: RelationsTyped},
FormatGroupResource(

@ -1,8 +1,8 @@
package zanzana
import (
dashboardalpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
folderalpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
dashboards "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
)
const (
@ -44,11 +44,11 @@ func newScopedMapping(relation, group, resource, subresource string) actionMappi
}
var (
folderGroup = folderalpha1.FolderResourceInfo.GroupResource().Group
folderResource = folderalpha1.FolderResourceInfo.GroupResource().Resource
folderGroup = folders.FolderResourceInfo.GroupResource().Group
folderResource = folders.FolderResourceInfo.GroupResource().Resource
dashboardGroup = dashboardalpha1.DashboardResourceInfo.GroupResource().Group
dashboardResource = dashboardalpha1.DashboardResourceInfo.GroupResource().Resource
dashboardGroup = dashboards.DashboardResourceInfo.GroupResource().Group
dashboardResource = dashboards.DashboardResourceInfo.GroupResource().Resource
)
var resourceTranslations = map[string]resourceTranslation{

@ -10,7 +10,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
dashboardv1alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashboardv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/apiserver"
@ -47,7 +47,7 @@ func NewK8sClientWithFallback(
) *K8sClientWithFallback {
newClientFunc := newK8sClientFactory(cfg, restConfigProvider, dashboardStore, userService, resourceClient, sorter, dual)
return &K8sClientWithFallback{
K8sHandler: newClientFunc(context.Background(), dashboardv1alpha1.VERSION),
K8sHandler: newClientFunc(context.Background(), dashboardv1.VERSION),
newClientFunc: newClientFunc,
metrics: newK8sClientMetrics(reg),
log: log.New("dashboards-k8s-client"),
@ -117,7 +117,7 @@ func newK8sClientFactory(
cacheMutex := &sync.RWMutex{}
return func(ctx context.Context, version string) client.K8sHandler {
_, span := tracing.Start(ctx, "k8sClientFactory.GetClient",
attribute.String("group", dashboardv1alpha1.GROUP),
attribute.String("group", dashboardv1.GROUP),
attribute.String("version", version),
attribute.String("resource", "dashboards"),
)
@ -143,7 +143,7 @@ func newK8sClientFactory(
}
gvr := schema.GroupVersionResource{
Group: dashboardv1alpha1.GROUP,
Group: dashboardv1.GROUP,
Version: version,
Resource: "dashboards",
}

@ -11,7 +11,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
dashboardv1alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashboardv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/apiserver/client"
)
@ -38,7 +38,7 @@ func setupTest(t *testing.T) *testSetup {
if version == "v2alpha1" {
return mockClientV2Alpha1
}
if version == dashboardv1alpha1.VERSION {
if version == dashboardv1.VERSION {
return mockClientV1Alpha1
}
t.Fatalf("Unexpected call to newClientFunc with version %s", version)

@ -26,11 +26,11 @@ import (
"github.com/grafana/grafana-app-sdk/logging"
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard"
dashboardv0alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
dashboardv1alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashboardv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
dashboardv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folderv1 "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/kvstore"
"github.com/grafana/grafana/pkg/infra/log"
@ -1553,7 +1553,7 @@ func (dr *DashboardServiceImpl) FindDashboards(ctx context.Context, query *dashb
result.SortMeta = hit.Field.GetNestedInt64(fieldName)
}
if hit.Resource == folderv0alpha1.RESOURCE {
if hit.Resource == folderv1.RESOURCE {
result.IsFolder = true
}
@ -1566,7 +1566,7 @@ func (dr *DashboardServiceImpl) FindDashboards(ctx context.Context, query *dashb
return dr.dashboardStore.FindDashboards(ctx, query)
}
func (dr *DashboardServiceImpl) fetchFolderNames(ctx context.Context, query *dashboards.FindPersistedDashboardsQuery, hits []dashboardv0alpha1.DashboardHit) (map[string]string, error) {
func (dr *DashboardServiceImpl) fetchFolderNames(ctx context.Context, query *dashboards.FindPersistedDashboardsQuery, hits []dashboardv0.DashboardHit) (map[string]string, error) {
// call this with elevated permissions so we can get folder names where user does not have access
// some dashboards are shared directly with user, but the folder is not accessible via the folder permissions
serviceCtx, serviceIdent := identity.WithServiceIdentity(ctx, query.OrgId)
@ -1923,7 +1923,7 @@ func (dr *DashboardServiceImpl) listDashboardsThroughK8s(ctx context.Context, or
return dashboards, nil
}
func (dr *DashboardServiceImpl) searchDashboardsThroughK8sRaw(ctx context.Context, query *dashboards.FindPersistedDashboardsQuery) (dashboardv0alpha1.SearchResults, error) {
func (dr *DashboardServiceImpl) searchDashboardsThroughK8sRaw(ctx context.Context, query *dashboards.FindPersistedDashboardsQuery) (dashboardv0.SearchResults, error) {
request := &resource.ResourceSearchRequest{
Options: &resource.ListOptions{
Fields: []*resource.Requirement{},
@ -2053,21 +2053,21 @@ func (dr *DashboardServiceImpl) searchDashboardsThroughK8sRaw(ctx context.Contex
switch query.Type {
case "":
// When no type specified, search for dashboards
request.Options.Key, err = resource.AsResourceKey(namespace, dashboardv1alpha1.DASHBOARD_RESOURCE)
request.Options.Key, err = resource.AsResourceKey(namespace, dashboardv1.DASHBOARD_RESOURCE)
// Currently a search query is across folders and dashboards
if err == nil {
federate, err = resource.AsResourceKey(namespace, folderv0alpha1.RESOURCE)
federate, err = resource.AsResourceKey(namespace, folderv1.RESOURCE)
}
case searchstore.TypeDashboard, searchstore.TypeAnnotation:
request.Options.Key, err = resource.AsResourceKey(namespace, dashboardv1alpha1.DASHBOARD_RESOURCE)
request.Options.Key, err = resource.AsResourceKey(namespace, dashboardv1.DASHBOARD_RESOURCE)
case searchstore.TypeFolder, searchstore.TypeAlertFolder:
request.Options.Key, err = resource.AsResourceKey(namespace, folderv0alpha1.RESOURCE)
request.Options.Key, err = resource.AsResourceKey(namespace, folderv1.RESOURCE)
default:
err = fmt.Errorf("bad type request")
}
if err != nil {
return dashboardv0alpha1.SearchResults{}, err
return dashboardv0.SearchResults{}, err
}
if federate != nil {
@ -2077,14 +2077,14 @@ func (dr *DashboardServiceImpl) searchDashboardsThroughK8sRaw(ctx context.Contex
if query.Sort.Name != "" {
sortName, isDesc, err := legacysearcher.ParseSortName(query.Sort.Name)
if err != nil {
return dashboardv0alpha1.SearchResults{}, err
return dashboardv0.SearchResults{}, err
}
request.SortBy = append(request.SortBy, &resource.ResourceSearchRequest_Sort{Field: sortName, Desc: isDesc})
}
res, err := dr.k8sclient.Search(ctx, query.OrgId, request)
if err != nil {
return dashboardv0alpha1.SearchResults{}, err
return dashboardv0.SearchResults{}, err
}
return dashboardsearch.ParseResults(res, 0)
@ -2114,7 +2114,7 @@ func (dr *DashboardServiceImpl) searchProvisionedDashboardsThroughK8s(ctx contex
var mu sync.Mutex
g, ctx := errgroup.WithContext(ctx)
for _, h := range searchResults.Hits {
func(hit dashboardv0alpha1.DashboardHit) {
func(hit dashboardv0.DashboardHit) {
g.Go(func() error {
out, err := dr.k8sclient.Get(ctx, hit.Name, query.OrgId, v1.GetOptions{})
if err != nil {
@ -2246,7 +2246,7 @@ func (dr *DashboardServiceImpl) unstructuredToLegacyDashboardWithUsers(item *uns
FolderUID: obj.GetFolder(),
Version: int(dashVersion),
Data: simplejson.NewFromAny(spec),
APIVersion: strings.TrimPrefix(item.GetAPIVersion(), dashboardv1alpha1.GROUP+"/"),
APIVersion: strings.TrimPrefix(item.GetAPIVersion(), dashboardv1.GROUP+"/"),
}
out.Created = obj.GetCreationTimestamp().Time
@ -2335,7 +2335,7 @@ func LegacySaveCommandToUnstructured(cmd *dashboards.SaveDashboardCommand, names
finalObj.Object["spec"] = obj
finalObj.SetName(uid)
finalObj.SetNamespace(namespace)
finalObj.SetGroupVersionKind(dashboardv1alpha1.DashboardResourceInfo.GroupVersionKind())
finalObj.SetGroupVersionKind(dashboardv1.DashboardResourceInfo.GroupVersionKind())
meta, err := utils.MetaAccessor(finalObj)
if err != nil {
@ -2353,7 +2353,7 @@ func LegacySaveCommandToUnstructured(cmd *dashboards.SaveDashboardCommand, names
return finalObj, nil
}
func getFolderUIDs(hits []dashboardv0alpha1.DashboardHit) []string {
func getFolderUIDs(hits []dashboardv0.DashboardHit) []string {
folderSet := map[string]bool{}
for _, hit := range hits {
if hit.Folder != "" && !folderSet[hit.Folder] {

@ -16,7 +16,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apiserver/pkg/endpoints/request"
dashboardv1alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashboardv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/components/simplejson"
@ -542,8 +542,8 @@ func TestGetProvisionedDashboardData(t *testing.T) {
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": dashboardv1alpha1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1alpha1.DashboardResourceInfo.GroupVersionKind().Kind,
"apiVersion": dashboardv1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1.DashboardResourceInfo.GroupVersionKind().Kind,
"metadata": map[string]interface{}{
"name": "uid",
"labels": map[string]interface{}{
@ -648,8 +648,8 @@ func TestGetProvisionedDashboardDataByDashboardID(t *testing.T) {
provisioningTimestamp := int64(1234567)
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": dashboardv1alpha1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1alpha1.DashboardResourceInfo.GroupVersionKind().Kind,
"apiVersion": dashboardv1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1.DashboardResourceInfo.GroupVersionKind().Kind,
"metadata": map[string]interface{}{
"name": "uid",
"labels": map[string]interface{}{
@ -742,8 +742,8 @@ func TestGetProvisionedDashboardDataByDashboardUID(t *testing.T) {
provisioningTimestamp := int64(1234567)
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": dashboardv1alpha1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1alpha1.DashboardResourceInfo.GroupVersionKind().Kind,
"apiVersion": dashboardv1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1.DashboardResourceInfo.GroupVersionKind().Kind,
"metadata": map[string]interface{}{
"name": "uid",
"labels": map[string]interface{}{
@ -975,8 +975,8 @@ func TestDeleteOrphanedProvisionedDashboards(t *testing.T) {
k8sCliMock.On("GetNamespace", mock.Anything, mock.Anything).Return("default")
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": dashboardv1alpha1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1alpha1.DashboardResourceInfo.GroupVersionKind().Kind,
"apiVersion": dashboardv1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1.DashboardResourceInfo.GroupVersionKind().Kind,
"metadata": map[string]interface{}{
"name": "uid",
"labels": map[string]interface{}{
@ -1120,7 +1120,7 @@ func TestUnprovisionDashboard(t *testing.T) {
}}
k8sCliMock.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(dash, nil)
dashWithoutAnnotations := &unstructured.Unstructured{Object: map[string]any{
"apiVersion": "dashboard.grafana.app/v1alpha1",
"apiVersion": dashboardv1.APIVERSION,
"kind": "Dashboard",
"metadata": map[string]any{
"name": "uid",
@ -2491,7 +2491,7 @@ func TestSetDefaultPermissionsAfterCreate(t *testing.T) {
// Create test object
key := &resource.ResourceKey{Group: "dashboard.grafana.app", Resource: "dashboards", Name: "test", Namespace: "default"}
obj := &dashboardv1alpha1.Dashboard{
obj := &dashboardv1.Dashboard{
TypeMeta: metav1.TypeMeta{
APIVersion: "dashboard.grafana.app/v0alpha1",
},
@ -2834,8 +2834,8 @@ func TestK8sDashboardCleanupJob(t *testing.T) {
func createTestUnstructuredDashboard(uid, title string, resourceVersion string) unstructured.Unstructured {
return unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": dashboardv1alpha1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1alpha1.DashboardResourceInfo.GroupVersionKind().Kind,
"apiVersion": dashboardv1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1.DashboardResourceInfo.GroupVersionKind().Kind,
"metadata": map[string]interface{}{
"name": uid,
"deletionTimestamp": "2023-01-01T00:00:00Z",

@ -11,7 +11,7 @@ import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/db"
@ -55,7 +55,7 @@ func ProvideService(cfg *setting.Cfg, db db.DB, dashboardService dashboards.Dash
k8sclient: client.NewK8sHandler(
dual,
request.GetNamespaceMapper(cfg),
v1alpha1.DashboardResourceInfo.GroupVersionResource(),
dashv1.DashboardResourceInfo.GroupVersionResource(),
restConfigProvider.GetRestConfig,
dashboardStore,
userService,

@ -18,7 +18,7 @@ func TestFolderConversions(t *testing.T) {
input := &unstructured.Unstructured{}
err := input.UnmarshalJSON([]byte(`{
"kind": "Folder",
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"metadata": {
"name": "be79sztagf20wd",
"namespace": "default",
@ -86,10 +86,10 @@ func TestFolderConversions(t *testing.T) {
func TestFolderListConversions(t *testing.T) {
input := &unstructured.UnstructuredList{}
err := input.UnmarshalJSON([]byte(`{
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"items": [
{
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"kind": "Folder",
"metadata": {
"annotations": {
@ -113,7 +113,7 @@ func TestFolderListConversions(t *testing.T) {
}
},
{
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"kind": "Folder",
"metadata": {
"annotations": {
@ -135,7 +135,7 @@ func TestFolderListConversions(t *testing.T) {
}
},
{
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"kind": "Folder",
"metadata": {
"annotations": {
@ -158,7 +158,7 @@ func TestFolderListConversions(t *testing.T) {
}
},
{
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"kind": "Folder",
"metadata": {
"annotations": {
@ -180,7 +180,7 @@ func TestFolderListConversions(t *testing.T) {
}
},
{
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"kind": "Folder",
"metadata": {
"annotations": {
@ -203,7 +203,7 @@ func TestFolderListConversions(t *testing.T) {
}
},
{
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"kind": "Folder",
"metadata": {
"annotations": {},

@ -18,9 +18,9 @@ import (
"github.com/grafana/dskit/concurrency"
dashboardalpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashboardv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folderv1 "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/events"
"github.com/grafana/grafana/pkg/infra/db"
@ -118,7 +118,7 @@ func ProvideService(
k8sHandler := client.NewK8sHandler(
dual,
request.GetNamespaceMapper(cfg),
v0alpha1.FolderResourceInfo.GroupVersionResource(),
folderv1.FolderResourceInfo.GroupVersionResource(),
restConfig.GetRestConfig,
dashboardStore,
userService,
@ -136,7 +136,7 @@ func ProvideService(
dashHandler := client.NewK8sHandler(
dual,
request.GetNamespaceMapper(cfg),
dashboardalpha1.DashboardResourceInfo.GroupVersionResource(),
dashboardv1.DashboardResourceInfo.GroupVersionResource(),
restConfig.GetRestConfig,
dashboardStore,
userService,

@ -16,7 +16,7 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folderv1 "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/events"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/infra/slugify"
@ -176,8 +176,8 @@ func (s *Service) searchFoldersFromApiServer(ctx context.Context, query folder.S
Options: &resource.ListOptions{
Key: &resource.ResourceKey{
Namespace: s.k8sclient.GetNamespace(query.OrgID),
Group: v0alpha1.FolderResourceInfo.GroupVersionResource().Group,
Resource: v0alpha1.FolderResourceInfo.GroupVersionResource().Resource,
Group: folderv1.FolderResourceInfo.GroupVersionResource().Group,
Resource: folderv1.FolderResourceInfo.GroupVersionResource().Resource,
},
Fields: []*resource.Requirement{},
Labels: []*resource.Requirement{},
@ -252,8 +252,8 @@ func (s *Service) getFolderByIDFromApiServer(ctx context.Context, id int64, orgI
folderkey := &resource.ResourceKey{
Namespace: s.k8sclient.GetNamespace(orgID),
Group: v0alpha1.FolderResourceInfo.GroupVersionResource().Group,
Resource: v0alpha1.FolderResourceInfo.GroupVersionResource().Resource,
Group: folderv1.FolderResourceInfo.GroupVersionResource().Group,
Resource: folderv1.FolderResourceInfo.GroupVersionResource().Resource,
}
request := &resource.ResourceSearchRequest{
@ -308,8 +308,8 @@ func (s *Service) getFolderByTitleFromApiServer(ctx context.Context, orgID int64
folderkey := &resource.ResourceKey{
Namespace: s.k8sclient.GetNamespace(orgID),
Group: v0alpha1.FolderResourceInfo.GroupVersionResource().Group,
Resource: v0alpha1.FolderResourceInfo.GroupVersionResource().Resource,
Group: folderv1.FolderResourceInfo.GroupVersionResource().Group,
Resource: folderv1.FolderResourceInfo.GroupVersionResource().Resource,
}
request := &resource.ResourceSearchRequest{

@ -17,7 +17,7 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
foldersv1 "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/log/logtest"
@ -59,9 +59,9 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
t.Skip("skipping integration test")
}
m := map[string]v0alpha1.Folder{}
m := map[string]foldersv1.Folder{}
unifiedStorageFolder := &v0alpha1.Folder{}
unifiedStorageFolder := &foldersv1.Folder{}
unifiedStorageFolder.Kind = "folder"
fooFolder := &folder.Folder{
@ -82,19 +82,19 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("DELETE /apis/folder.grafana.app/v0alpha1/namespaces/default/folders/deletefolder", func(w http.ResponseWriter, req *http.Request) {
mux.HandleFunc("DELETE /apis/folder.grafana.app/v1/namespaces/default/folders/deletefolder", func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
})
mux.HandleFunc("GET /apis/folder.grafana.app/v0alpha1/namespaces/default/folders", func(w http.ResponseWriter, req *http.Request) {
mux.HandleFunc("GET /apis/folder.grafana.app/v1/namespaces/default/folders", func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
l := &v0alpha1.FolderList{}
l := &foldersv1.FolderList{}
l.Kind = "Folder"
err := json.NewEncoder(w).Encode(l)
require.NoError(t, err)
})
mux.HandleFunc("GET /apis/folder.grafana.app/v0alpha1/namespaces/default/folders/foo", func(w http.ResponseWriter, req *http.Request) {
mux.HandleFunc("GET /apis/folder.grafana.app/v1/namespaces/default/folders/foo", func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
namespacer := func(_ int64) string { return "1" }
result, err := internalfolders.LegacyFolderToUnstructured(fooFolder, namespacer)
@ -104,7 +104,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
require.NoError(t, err)
})
mux.HandleFunc("GET /apis/folder.grafana.app/v0alpha1/namespaces/default/folders/updatefolder", func(w http.ResponseWriter, req *http.Request) {
mux.HandleFunc("GET /apis/folder.grafana.app/v1/namespaces/default/folders/updatefolder", func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
namespacer := func(_ int64) string { return "1" }
result, err := internalfolders.LegacyFolderToUnstructured(updateFolder, namespacer)
@ -114,12 +114,12 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
require.NoError(t, err)
})
mux.HandleFunc("PUT /apis/folder.grafana.app/v0alpha1/namespaces/default/folders/updatefolder", func(w http.ResponseWriter, req *http.Request) {
mux.HandleFunc("PUT /apis/folder.grafana.app/v1/namespaces/default/folders/updatefolder", func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
buf, err := io.ReadAll(req.Body)
require.NoError(t, err)
var foldr v0alpha1.Folder
var foldr foldersv1.Folder
err = json.Unmarshal(buf, &foldr)
require.NoError(t, err)
@ -133,22 +133,22 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
require.NoError(t, err)
})
mux.HandleFunc("GET /apis/folder.grafana.app/v0alpha1/namespaces/default/folders/ady4yobv315a8e", func(w http.ResponseWriter, req *http.Request) {
mux.HandleFunc("GET /apis/folder.grafana.app/v1/namespaces/default/folders/ady4yobv315a8e", func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(unifiedStorageFolder)
require.NoError(t, err)
})
mux.HandleFunc("PUT /apis/folder.grafana.app/v0alpha1/namespaces/default/folders/ady4yobv315a8e", func(w http.ResponseWriter, req *http.Request) {
mux.HandleFunc("PUT /apis/folder.grafana.app/v1/namespaces/default/folders/ady4yobv315a8e", func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
err := json.NewEncoder(w).Encode(unifiedStorageFolder)
require.NoError(t, err)
})
mux.HandleFunc("POST /apis/folder.grafana.app/v0alpha1/namespaces/default/folders", func(w http.ResponseWriter, req *http.Request) {
mux.HandleFunc("POST /apis/folder.grafana.app/v1/namespaces/default/folders", func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
buf, err := io.ReadAll(req.Body)
require.NoError(t, err)
var folder v0alpha1.Folder
var folder foldersv1.Folder
err = json.Unmarshal(buf, &folder)
require.NoError(t, err)
@ -184,7 +184,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
features := featuremgmt.WithFeatures(featuresArr...)
dashboardStore := dashboards.NewFakeDashboardStore(t)
k8sCli := client.NewK8sHandler(dualwrite.ProvideTestService(), request.GetNamespaceMapper(cfg), v0alpha1.FolderResourceInfo.GroupVersionResource(), restCfgProvider.GetRestConfig, dashboardStore, userService, nil, sort.ProvideService())
k8sCli := client.NewK8sHandler(dualwrite.ProvideTestService(), request.GetNamespaceMapper(cfg), foldersv1.FolderResourceInfo.GroupVersionResource(), restCfgProvider.GetRestConfig, dashboardStore, userService, nil, sort.ProvideService())
unifiedStore := ProvideUnifiedStore(k8sCli, userService)
ctx := context.Background()
@ -541,8 +541,8 @@ func TestSearchFoldersFromApiServer(t *testing.T) {
Options: &resource.ListOptions{
Key: &resource.ResourceKey{
Namespace: "default",
Group: v0alpha1.FolderResourceInfo.GroupVersionResource().Group,
Resource: v0alpha1.FolderResourceInfo.GroupVersionResource().Resource,
Group: foldersv1.FolderResourceInfo.GroupVersionResource().Group,
Resource: foldersv1.FolderResourceInfo.GroupVersionResource().Resource,
},
Fields: []*resource.Requirement{
{
@ -631,8 +631,8 @@ func TestSearchFoldersFromApiServer(t *testing.T) {
Options: &resource.ListOptions{
Key: &resource.ResourceKey{
Namespace: "default",
Group: v0alpha1.FolderResourceInfo.GroupVersionResource().Group,
Resource: v0alpha1.FolderResourceInfo.GroupVersionResource().Resource,
Group: foldersv1.FolderResourceInfo.GroupVersionResource().Group,
Resource: foldersv1.FolderResourceInfo.GroupVersionResource().Resource,
},
Fields: []*resource.Requirement{},
Labels: []*resource.Requirement{
@ -700,8 +700,8 @@ func TestSearchFoldersFromApiServer(t *testing.T) {
Options: &resource.ListOptions{
Key: &resource.ResourceKey{
Namespace: "default",
Group: v0alpha1.FolderResourceInfo.GroupVersionResource().Group,
Resource: v0alpha1.FolderResourceInfo.GroupVersionResource().Resource,
Group: foldersv1.FolderResourceInfo.GroupVersionResource().Group,
Resource: foldersv1.FolderResourceInfo.GroupVersionResource().Resource,
},
Fields: []*resource.Requirement{},
Labels: []*resource.Requirement{},
@ -797,8 +797,8 @@ func TestGetFoldersFromApiServer(t *testing.T) {
Options: &resource.ListOptions{
Key: &resource.ResourceKey{
Namespace: "default",
Group: v0alpha1.FolderResourceInfo.GroupVersionResource().Group,
Resource: v0alpha1.FolderResourceInfo.GroupVersionResource().Resource,
Group: foldersv1.FolderResourceInfo.GroupVersionResource().Group,
Resource: foldersv1.FolderResourceInfo.GroupVersionResource().Resource,
},
Fields: []*resource.Requirement{},
Labels: []*resource.Requirement{},

@ -16,7 +16,7 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folderv1 "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/infra/log"
internalfolders "github.com/grafana/grafana/pkg/registry/apis/folders"
"github.com/grafana/grafana/pkg/services/accesscontrol"
@ -452,7 +452,7 @@ func (ss *FolderUnifiedStoreImpl) CountInOrg(ctx context.Context, orgID int64) (
}
func toFolderLegacyCounts(u *unstructured.Unstructured) (*folder.DescendantCounts, error) {
ds, err := v0alpha1.UnstructuredToDescendantCounts(u)
ds, err := folderv1.UnstructuredToDescendantCounts(u)
if err != nil {
return nil, err
}

@ -5,7 +5,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
)
func IsReadingLegacyDashboardsAndFolders(ctx context.Context, svc Service) bool {

@ -9,7 +9,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
)
func TestIsReadingLegacyDashboardsAndFolders(t *testing.T) {

@ -191,7 +191,7 @@ No resources found in default namespace.
To create a folder, create a file `folder-generate.yaml`:
```yaml
apiVersion: folder.grafana.app/v0alpha1
apiVersion: folder.grafana.app/v1
kind: Folder
metadata:
generateName: x # anything is ok here... except yes or true -- they become boolean!

@ -3,7 +3,7 @@ package apistore
import (
"context"
dashboardv1alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashboardv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -16,7 +16,7 @@ type LargeObjectSupportFake struct {
}
func (s *LargeObjectSupportFake) GroupResource() schema.GroupResource {
return dashboardv1alpha1.DashboardResourceInfo.GroupResource()
return dashboardv1.DashboardResourceInfo.GroupResource()
}
func (s *LargeObjectSupportFake) Threshold() int {

@ -212,7 +212,6 @@ require (
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // indirect
github.com/grafana/grafana-plugin-sdk-go v0.274.1-0.20250318081012-21a7f15619b0 // indirect
github.com/grafana/grafana/pkg/aggregator v0.0.0-20250220163425-b4c4b9abbdc8 // indirect
github.com/grafana/grafana/pkg/apis/folder v0.0.0-20250402082028-6781612335d9 // indirect
github.com/grafana/grafana/pkg/promlib v0.0.8 // indirect
github.com/grafana/grafana/pkg/semconv v0.0.0-20250220164708-c8d4ff28a450 // indirect
github.com/grafana/otel-profiling-go v0.5.1 // indirect

@ -1276,7 +1276,6 @@ github.com/grafana/grafana/apps/dashboard v0.0.0-20250317130411-3f270d1de043/go.
github.com/grafana/grafana/pkg/aggregator v0.0.0-20250220163425-b4c4b9abbdc8 h1:9qOLpC21AmXZqZ6rUhrBWl2mVqS3CzV53pzw0BCuHt0=
github.com/grafana/grafana/pkg/aggregator v0.0.0-20250220163425-b4c4b9abbdc8/go.mod h1:deLQ/ywLvpVGbncRGUA4UDGt8a5Ei9sivOP+x6AQ2ko=
github.com/grafana/grafana/pkg/apis/folder v0.0.0-20250402082028-6781612335d9 h1:kJWonBYechx35NbOUVf1ulufKyjH1UlDJDXlk8bdGn0=
github.com/grafana/grafana/pkg/apis/folder v0.0.0-20250402082028-6781612335d9/go.mod h1:QdqsZpdCtg+3TEzXX3mUakjq79LFblZ8xliHmqZj3oA=
github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0=
github.com/grafana/grafana/pkg/promlib v0.0.8/go.mod h1:U1ezG/MGaEPoThqsr3lymMPN5yIPdVTJnDZ+wcXT+ao=
github.com/grafana/grafana/pkg/semconv v0.0.0-20250220164708-c8d4ff28a450 h1:wSqgLKFwI7fyeqf3djRXGClBLb/UPjZ4XPm/UsKFDB0=

@ -16,7 +16,7 @@ import (
"k8s.io/apiserver/pkg/storage"
authtypes "github.com/grafana/authlib/types"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apimachinery/utils"
)
@ -25,11 +25,11 @@ var scheme = runtime.NewScheme()
var codecs = serializer.NewCodecFactory(scheme)
func TestPrepareObjectForStorage(t *testing.T) {
_ = v1alpha1.AddToScheme(scheme)
_ = dashv1.AddToScheme(scheme)
node, err := snowflake.NewNode(rand.Int63n(1024))
require.NoError(t, err)
s := &Storage{
codec: apitesting.TestCodec(codecs, v1alpha1.DashboardResourceInfo.GroupVersion()),
codec: apitesting.TestCodec(codecs, dashv1.DashboardResourceInfo.GroupVersion()),
snowflake: node,
opts: StorageOptions{
EnableFolderSupport: true,
@ -48,14 +48,14 @@ func TestPrepareObjectForStorage(t *testing.T) {
})
t.Run("Error on missing name", func(t *testing.T) {
dashboard := v1alpha1.Dashboard{}
dashboard := dashv1.Dashboard{}
_, _, err := s.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
require.Error(t, err)
require.Contains(t, err.Error(), "missing name")
})
t.Run("Error on non-empty resource version", func(t *testing.T) {
dashboard := v1alpha1.Dashboard{}
dashboard := dashv1.Dashboard{}
dashboard.Name = "test-name"
dashboard.ResourceVersion = "123"
_, _, err := s.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
@ -64,13 +64,13 @@ func TestPrepareObjectForStorage(t *testing.T) {
})
t.Run("Generate UID and leave deprecated ID empty, if not required", func(t *testing.T) {
dashboard := v1alpha1.Dashboard{}
dashboard := dashv1.Dashboard{}
dashboard.Name = "test-name"
encodedData, _, err := s.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
require.NoError(t, err)
newObject, _, err := s.codec.Decode(encodedData, nil, &v1alpha1.Dashboard{})
newObject, _, err := s.codec.Decode(encodedData, nil, &dashv1.Dashboard{})
require.NoError(t, err)
obj, err := utils.MetaAccessor(newObject)
require.NoError(t, err)
@ -90,7 +90,7 @@ func TestPrepareObjectForStorage(t *testing.T) {
ctx, _, err := identity.WithProvisioningIdentity(ctx, "default")
require.NoError(t, err)
dashboard := v1alpha1.Dashboard{}
dashboard := dashv1.Dashboard{}
dashboard.Name = "test-name"
obj := dashboard.DeepCopyObject()
meta, err := utils.MetaAccessor(obj)
@ -109,7 +109,7 @@ func TestPrepareObjectForStorage(t *testing.T) {
encodedData, _, err := s.prepareObjectForStorage(ctx, obj)
require.NoError(t, err)
newObject, _, err := s.codec.Decode(encodedData, nil, &v1alpha1.Dashboard{})
newObject, _, err := s.codec.Decode(encodedData, nil, &dashv1.Dashboard{})
require.NoError(t, err)
meta, err = utils.MetaAccessor(newObject)
require.NoError(t, err)
@ -126,7 +126,7 @@ func TestPrepareObjectForStorage(t *testing.T) {
})
t.Run("Update should manage incrementing generation and metadata", func(t *testing.T) {
dashboard := v1alpha1.Dashboard{}
dashboard := dashv1.Dashboard{}
dashboard.Name = "test-name"
obj := dashboard.DeepCopyObject()
meta, err := utils.MetaAccessor(obj)
@ -136,7 +136,7 @@ func TestPrepareObjectForStorage(t *testing.T) {
encodedData, _, err := s.prepareObjectForStorage(ctx, obj)
require.NoError(t, err)
insertedObject, _, err := s.codec.Decode(encodedData, nil, &v1alpha1.Dashboard{})
insertedObject, _, err := s.codec.Decode(encodedData, nil, &dashv1.Dashboard{})
require.NoError(t, err)
meta, err = utils.MetaAccessor(insertedObject)
require.NoError(t, err)
@ -156,8 +156,8 @@ func TestPrepareObjectForStorage(t *testing.T) {
updatedObject := insertedObject.DeepCopyObject()
meta, err = utils.MetaAccessor(updatedObject)
require.NoError(t, err)
err = meta.SetStatus(v1alpha1.DashboardStatus{
Conversion: &v1alpha1.DashboardConversionStatus{
err = meta.SetStatus(dashv1.DashboardStatus{
Conversion: &dashv1.DashboardConversionStatus{
Failed: true,
Error: "test",
},
@ -172,7 +172,7 @@ func TestPrepareObjectForStorage(t *testing.T) {
require.Equal(t, int64(1), meta.GetGeneration())
// Change the folder -- the generation should increase and the updatedBy metadata
dashboard2 := &v1alpha1.Dashboard{ObjectMeta: v1.ObjectMeta{
dashboard2 := &dashv1.Dashboard{ObjectMeta: v1.ObjectMeta{
Name: dashboard.Name,
}} // TODO... deep copy, See: https://github.com/grafana/grafana/pull/102258
meta2, err := utils.MetaAccessor(dashboard2)
@ -186,12 +186,12 @@ func TestPrepareObjectForStorage(t *testing.T) {
s.opts.RequireDeprecatedInternalID = true
t.Run("Should generate internal id", func(t *testing.T) {
dashboard := v1alpha1.Dashboard{}
dashboard := dashv1.Dashboard{}
dashboard.Name = "test-name"
encodedData, _, err := s.prepareObjectForStorage(ctx, dashboard.DeepCopyObject())
require.NoError(t, err)
newObject, _, err := s.codec.Decode(encodedData, nil, &v1alpha1.Dashboard{})
newObject, _, err := s.codec.Decode(encodedData, nil, &dashv1.Dashboard{})
require.NoError(t, err)
obj, err := utils.MetaAccessor(newObject)
require.NoError(t, err)
@ -201,7 +201,7 @@ func TestPrepareObjectForStorage(t *testing.T) {
})
t.Run("Should use deprecated ID if given it", func(t *testing.T) {
dashboard := v1alpha1.Dashboard{}
dashboard := dashv1.Dashboard{}
dashboard.Name = "test-name"
obj := dashboard.DeepCopyObject()
meta, err := utils.MetaAccessor(obj)
@ -210,7 +210,7 @@ func TestPrepareObjectForStorage(t *testing.T) {
encodedData, _, err := s.prepareObjectForStorage(ctx, obj)
require.NoError(t, err)
newObject, _, err := s.codec.Decode(encodedData, nil, &v1alpha1.Dashboard{})
newObject, _, err := s.codec.Decode(encodedData, nil, &dashv1.Dashboard{})
require.NoError(t, err)
meta, err = utils.MetaAccessor(newObject)
require.NoError(t, err)
@ -218,7 +218,7 @@ func TestPrepareObjectForStorage(t *testing.T) {
})
t.Run("Should remove grant permissions annotation", func(t *testing.T) {
dashboard := v1alpha1.Dashboard{}
dashboard := dashv1.Dashboard{}
dashboard.Name = "test-name"
obj := dashboard.DeepCopyObject()
meta, err := utils.MetaAccessor(obj)
@ -227,7 +227,7 @@ func TestPrepareObjectForStorage(t *testing.T) {
encodedData, p, err := s.prepareObjectForStorage(ctx, obj)
require.NoError(t, err)
newObject, _, err := s.codec.Decode(encodedData, nil, &v1alpha1.Dashboard{})
newObject, _, err := s.codec.Decode(encodedData, nil, &dashv1.Dashboard{})
require.NoError(t, err)
meta, err = utils.MetaAccessor(newObject)
require.NoError(t, err)
@ -236,11 +236,11 @@ func TestPrepareObjectForStorage(t *testing.T) {
})
t.Run("calculate generation", func(t *testing.T) {
dash := &v1alpha1.Dashboard{
dash := &dashv1.Dashboard{
ObjectMeta: v1.ObjectMeta{
Name: "test",
},
Spec: v1alpha1.DashboardSpec{
Spec: dashv1.DashboardSpec{
Object: map[string]interface{}{
"hello": "world",
},
@ -286,8 +286,8 @@ func TestPrepareObjectForStorage(t *testing.T) {
b.Labels = map[string]string{
"a": "b",
}
b.Status = v1alpha1.DashboardStatus{
Conversion: &v1alpha1.DashboardConversionStatus{
b.Status = dashv1.DashboardStatus{
Conversion: &dashv1.DashboardConversionStatus{
Failed: true,
},
}
@ -320,13 +320,13 @@ func getPreparedObject(t *testing.T, ctx context.Context, s *Storage, obj runtim
}
func TestPrepareLargeObjectForStorage(t *testing.T) {
_ = v1alpha1.AddToScheme(scheme)
_ = dashv1.AddToScheme(scheme)
node, err := snowflake.NewNode(rand.Int63n(1024))
require.NoError(t, err)
ctx := authtypes.WithAuthInfo(context.Background(), &identity.StaticRequester{UserID: 1, UserUID: "user-uid", Type: authtypes.TypeUser})
dashboard := v1alpha1.Dashboard{}
dashboard := dashv1.Dashboard{}
dashboard.Name = "test-name"
t.Run("Should deconstruct object if size is over threshold", func(t *testing.T) {
los := LargeObjectSupportFake{
@ -334,7 +334,7 @@ func TestPrepareLargeObjectForStorage(t *testing.T) {
}
f := &Storage{
codec: apitesting.TestCodec(codecs, v1alpha1.DashboardResourceInfo.GroupVersion()),
codec: apitesting.TestCodec(codecs, dashv1.DashboardResourceInfo.GroupVersion()),
snowflake: node,
opts: StorageOptions{
LargeObjectSupport: &los,
@ -352,7 +352,7 @@ func TestPrepareLargeObjectForStorage(t *testing.T) {
}
f := &Storage{
codec: apitesting.TestCodec(codecs, v1alpha1.DashboardResourceInfo.GroupVersion()),
codec: apitesting.TestCodec(codecs, dashv1.DashboardResourceInfo.GroupVersion()),
snowflake: node,
opts: StorageOptions{
LargeObjectSupport: &los,

@ -17,7 +17,6 @@ require (
github.com/grafana/grafana-plugin-sdk-go v0.274.1-0.20250318081012-21a7f15619b0
github.com/grafana/grafana/apps/dashboard v0.0.0-20250317130411-3f270d1de043
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250401081501-6af5fbf3fff0
github.com/grafana/grafana/pkg/apis/folder v0.0.0-20250402082028-6781612335d9
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3
github.com/hashicorp/golang-lru/v2 v2.0.7

@ -273,8 +273,6 @@ github.com/grafana/grafana-plugin-sdk-go v0.274.1-0.20250318081012-21a7f15619b0
github.com/grafana/grafana-plugin-sdk-go v0.274.1-0.20250318081012-21a7f15619b0/go.mod h1:jV+CTjXqXYuaz8FgSG7ALOib3sgiDo/00dfsQFVTSpM=
github.com/grafana/grafana/apps/dashboard v0.0.0-20250317130411-3f270d1de043 h1:wdJy5x6M7auWDjUIubqhfZuZvphUMyjD7hxB3RqV4aE=
github.com/grafana/grafana/apps/dashboard v0.0.0-20250317130411-3f270d1de043/go.mod h1:jwYig4wlnLLq4HQKDpS95nDeZi4+DmcD17KYYS1gMJg=
github.com/grafana/grafana/pkg/apis/folder v0.0.0-20250402082028-6781612335d9 h1:kJWonBYechx35NbOUVf1ulufKyjH1UlDJDXlk8bdGn0=
github.com/grafana/grafana/pkg/apis/folder v0.0.0-20250402082028-6781612335d9/go.mod h1:QdqsZpdCtg+3TEzXX3mUakjq79LFblZ8xliHmqZj3oA=
github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8=
github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=

@ -16,8 +16,8 @@ import (
"golang.org/x/sync/errgroup"
"k8s.io/apimachinery/pkg/runtime/schema"
dashboardv1alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
dashboardv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/authlib/types"
)
@ -683,14 +683,14 @@ func AsResourceKey(ns string, t string) (*ResourceKey, error) {
case "folders", "folder":
return &ResourceKey{
Namespace: ns,
Group: folderv0alpha1.GROUP,
Resource: folderv0alpha1.RESOURCE,
Group: folders.GROUP,
Resource: folders.RESOURCE,
}, nil
case "dashboards", "dashboard":
return &ResourceKey{
Namespace: ns,
Group: dashboardv1alpha1.GROUP,
Resource: dashboardv1alpha1.DASHBOARD_RESOURCE,
Group: dashboardv1.GROUP,
Resource: dashboardv1.DASHBOARD_RESOURCE,
}, nil
// NOT really supported in the dashboard search UI, but useful for manual testing

@ -1,6 +1,6 @@
{
"kind": "Folder",
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"metadata": {
"name": "ae2ntrqxefvnke",
"namespace": "default",

@ -1,6 +1,6 @@
{
"kind": "Folder",
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"metadata": {
"name": "ae2ntrqxefvnke",
"namespace": "default",

@ -8,7 +8,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
dashV1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/services/store/kind/dashboard"
"github.com/grafana/grafana/pkg/storage/unified/resource"
@ -202,7 +202,7 @@ func DashboardBuilder(namespaced resource.NamespacedDocumentSupplier) (resource.
}
}
return resource.DocumentBuilderInfo{
GroupResource: v1alpha1.DashboardResourceInfo.GroupResource(),
GroupResource: dashV1.DashboardResourceInfo.GroupResource(),
Fields: fields,
Namespaced: namespaced,
}, err

@ -1,6 +1,6 @@
{
"kind": "Folder",
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"metadata": {
"name": "aaa",
"namespace": "default",

@ -1,6 +1,6 @@
{
"kind": "Folder",
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"metadata": {
"name": "bbb",
"namespace": "default",

@ -7,8 +7,8 @@ import (
"strings"
"testing"
dashboardv1alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
dashboardv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
"github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
@ -221,8 +221,8 @@ func runDashboardValidationTests(t *testing.T, ctx TestContext) {
t.Run("reject dashboard with invalid schema", func(t *testing.T) {
dashObj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": dashboardv1alpha1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1alpha1.DashboardResourceInfo.GroupVersionKind().Kind,
"apiVersion": dashboardv1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1.DashboardResourceInfo.GroupVersionKind().Kind,
"metadata": map[string]interface{}{
"generateName": "test-",
},
@ -684,18 +684,18 @@ func createTestContext(t *testing.T, helper *apis.K8sTestHelper, orgUsers apis.O
// getDashboardGVR returns the dashboard GroupVersionResource
func getDashboardGVR() schema.GroupVersionResource {
return schema.GroupVersionResource{
Group: dashboardv1alpha1.DashboardResourceInfo.GroupVersion().Group,
Version: dashboardv1alpha1.DashboardResourceInfo.GroupVersion().Version,
Resource: dashboardv1alpha1.DashboardResourceInfo.GetName(),
Group: dashboardv1.DashboardResourceInfo.GroupVersion().Group,
Version: dashboardv1.DashboardResourceInfo.GroupVersion().Version,
Resource: dashboardv1.DashboardResourceInfo.GetName(),
}
}
// getFolderGVR returns the folder GroupVersionResource
func getFolderGVR() schema.GroupVersionResource {
return schema.GroupVersionResource{
Group: folderv0alpha1.FolderResourceInfo.GroupVersion().Group,
Version: folderv0alpha1.FolderResourceInfo.GroupVersion().Version,
Resource: folderv0alpha1.FolderResourceInfo.GetName(),
Group: folders.FolderResourceInfo.GroupVersion().Group,
Version: folders.FolderResourceInfo.GroupVersion().Version,
Resource: folders.FolderResourceInfo.GetName(),
}
}
@ -728,8 +728,8 @@ func createFolderObject(t *testing.T, title string, namespace string, parentFold
folderObj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": folderv0alpha1.FolderResourceInfo.GroupVersion().String(),
"kind": folderv0alpha1.FolderResourceInfo.GroupVersionKind().Kind,
"apiVersion": folders.FolderResourceInfo.GroupVersion().String(),
"kind": folders.FolderResourceInfo.GroupVersionKind().Kind,
"metadata": map[string]interface{}{
"generateName": "test-folder-",
"namespace": namespace,
@ -784,8 +784,8 @@ func createDashboardObject(t *testing.T, title string, folderUID string, generat
dashObj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": dashboardv1alpha1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1alpha1.DashboardResourceInfo.GroupVersionKind().Kind,
"apiVersion": dashboardv1.DashboardResourceInfo.GroupVersion().String(),
"kind": dashboardv1.DashboardResourceInfo.GroupVersionKind().Kind,
"metadata": map[string]interface{}{
"generateName": "test-",
"annotations": map[string]interface{}{

@ -18,7 +18,7 @@ import (
"github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/api/dtos"
folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folders "github.com/grafana/grafana/pkg/apis/folder/v1"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
"github.com/grafana/grafana/pkg/services/dashboards"
@ -38,7 +38,7 @@ func TestMain(m *testing.M) {
var gvr = schema.GroupVersionResource{
Group: "folder.grafana.app",
Version: "v0alpha1",
Version: "v1",
Resource: "folders",
}
@ -55,7 +55,7 @@ func TestIntegrationFoldersApp(t *testing.T) {
t.Run("Check discovery client", func(t *testing.T) {
disco := helper.NewDiscoveryClient()
resources, err := disco.ServerResourcesForGroupVersion("folder.grafana.app/v0alpha1")
resources, err := disco.ServerResourcesForGroupVersion("folder.grafana.app/v1")
require.NoError(t, err)
v1Disco, err := json.MarshalIndent(resources, "", " ")
@ -64,7 +64,7 @@ func TestIntegrationFoldersApp(t *testing.T) {
require.JSONEq(t, `{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "folder.grafana.app/v0alpha1",
"groupVersion": "folder.grafana.app/v1",
"resources": [
{
"name": "folders",
@ -118,7 +118,7 @@ func TestIntegrationFoldersApp(t *testing.T) {
DisableAnonymous: true,
APIServerStorageType: "unified",
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
folderv0alpha1.RESOURCEGROUP: {
folders.RESOURCEGROUP: {
DualWriterMode: grafanarest.Mode0,
},
},
@ -134,7 +134,7 @@ func TestIntegrationFoldersApp(t *testing.T) {
DisableAnonymous: true,
APIServerStorageType: "unified",
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
folderv0alpha1.RESOURCEGROUP: {
folders.RESOURCEGROUP: {
DualWriterMode: grafanarest.Mode1,
},
},
@ -150,7 +150,7 @@ func TestIntegrationFoldersApp(t *testing.T) {
DisableAnonymous: true,
APIServerStorageType: "unified",
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
folderv0alpha1.RESOURCEGROUP: {
folders.RESOURCEGROUP: {
DualWriterMode: grafanarest.Mode1,
},
},
@ -167,7 +167,7 @@ func TestIntegrationFoldersApp(t *testing.T) {
DisableAnonymous: true,
APIServerStorageType: "unified",
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
folderv0alpha1.RESOURCEGROUP: {
folders.RESOURCEGROUP: {
DualWriterMode: grafanarest.Mode1,
},
},
@ -184,7 +184,7 @@ func TestIntegrationFoldersApp(t *testing.T) {
DisableAnonymous: true,
APIServerStorageType: "unified",
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
folderv0alpha1.RESOURCEGROUP: {
folders.RESOURCEGROUP: {
DualWriterMode: grafanarest.Mode1,
},
},
@ -201,7 +201,7 @@ func TestIntegrationFoldersApp(t *testing.T) {
DisableAnonymous: true,
APIServerStorageType: "unified",
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
folderv0alpha1.RESOURCEGROUP: {
folders.RESOURCEGROUP: {
DualWriterMode: grafanarest.Mode1,
},
},
@ -238,7 +238,7 @@ func doFolderTests(t *testing.T, helper *apis.K8sTestHelper) *apis.K8sTestHelper
require.NotEmpty(t, uid)
expectedResult := `{
"apiVersion": "folder.grafana.app/v0alpha1",
"apiVersion": "folder.grafana.app/v1",
"kind": "Folder",
"metadata": {
"creationTimestamp": "${creationTimestamp}",
@ -571,7 +571,7 @@ func TestIntegrationFolderCreatePermissions(t *testing.T) {
DisableAnonymous: true,
APIServerStorageType: "unified",
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
folderv0alpha1.RESOURCEGROUP: {
folders.RESOURCEGROUP: {
DualWriterMode: grafanarest.Mode1,
},
},
@ -673,7 +673,7 @@ func TestIntegrationFolderGetPermissions(t *testing.T) {
DisableAnonymous: true,
APIServerStorageType: "unified",
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
folderv0alpha1.RESOURCEGROUP: {
folders.RESOURCEGROUP: {
DualWriterMode: grafanarest.Mode1,
},
},
@ -850,7 +850,7 @@ func TestFoldersCreateAPIEndpointK8S(t *testing.T) {
DisableAnonymous: true,
APIServerStorageType: "unified",
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
folderv0alpha1.RESOURCEGROUP: {
folders.RESOURCEGROUP: {
DualWriterMode: grafanarest.Mode1,
},
},
@ -1020,7 +1020,7 @@ func TestFoldersGetAPIEndpointK8S(t *testing.T) {
DisableAnonymous: true,
APIServerStorageType: "unified",
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
folderv0alpha1.RESOURCEGROUP: {
folders.RESOURCEGROUP: {
DualWriterMode: modeDw,
},
},

@ -1,4 +1,4 @@
apiVersion: folder.grafana.app/v0alpha1
apiVersion: folder.grafana.app/v1
kind: Folder
metadata:
generateName: x # anything is ok here... except yes or true -- they become boolean!

@ -1,4 +1,4 @@
apiVersion: folder.grafana.app/v0alpha1
apiVersion: folder.grafana.app/v1
kind: Folder
metadata:
name: test

@ -1,4 +1,4 @@
apiVersion: folder.grafana.app/v0alpha1
apiVersion: folder.grafana.app/v1
kind: Folder
metadata:
name: test

File diff suppressed because it is too large Load Diff

@ -71,7 +71,7 @@ func TestIntegrationOpenAPIs(t *testing.T) {
Version: "v2alpha1",
}, {
Group: "folder.grafana.app",
Version: "v0alpha1",
Version: "v1",
}, {
Group: "provisioning.grafana.app",
Version: "v0alpha1",

@ -24,7 +24,7 @@ import (
"k8s.io/client-go/rest"
dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1alpha1"
folder "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
folder "github.com/grafana/grafana/pkg/apis/folder/v1"
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/jobs"

Loading…
Cancel
Save