diff --git a/pkg/registry/apis/folders/legacy_storage.go b/pkg/registry/apis/folders/legacy_storage.go index d27a9c5db68..db16aaaf89c 100644 --- a/pkg/registry/apis/folders/legacy_storage.go +++ b/pkg/registry/apis/folders/legacy_storage.go @@ -16,6 +16,7 @@ import ( "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/services/grafana-apiserver/endpoints/request" + "github.com/grafana/grafana/pkg/services/grafana-apiserver/storage/entity" "github.com/grafana/grafana/pkg/services/grafana-apiserver/utils" "github.com/grafana/grafana/pkg/util" ) @@ -65,6 +66,17 @@ func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListO return nil, err } + parentUID := "" + fieldRequirements, fieldSelector, err := entity.ReadFieldRequirements(options.FieldSelector) + if err != nil { + return nil, err + } + if fieldRequirements.Folder != nil { + parentUID = *fieldRequirements.Folder + } + // Update the field selector to remove the unneeded selectors + options.FieldSelector = fieldSelector + paging, err := readContinueToken(options) if err != nil { return nil, err @@ -77,6 +89,7 @@ func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListO // When nested folders are not enabled, all folders are root folders hits, err := s.service.GetChildren(ctx, &folder.GetChildrenQuery{ + UID: parentUID, // NOTE! we should do a different query when nested folders are enabled! SignedInUser: user, Limit: paging.page, OrgID: orgId, diff --git a/pkg/registry/apis/playlist/register.go b/pkg/registry/apis/playlist/register.go index 11a8aaf0a9c..b1dab9754f0 100644 --- a/pkg/registry/apis/playlist/register.go +++ b/pkg/registry/apis/playlist/register.go @@ -2,7 +2,6 @@ package playlist import ( "fmt" - "strings" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -68,28 +67,6 @@ func (b *PlaylistAPIBuilder) InstallSchema(scheme *runtime.Scheme) error { Version: runtime.APIVersionInternal, }) - gvk := playlist.PlaylistResourceInfo.GroupVersionKind() - - // Add playlist thing - _ = scheme.AddFieldLabelConversionFunc(gvk, - runtime.FieldLabelConversionFunc( - func(label, value string) (string, string, error) { - if strings.HasPrefix(label, "grafana.app/") { - return label, value, nil - } - - switch label { - case "metadata.name": - return label, value, nil - case "metadata.namespace": - return label, value, nil - default: - return "", "", fmt.Errorf("%q is not a known field selector: only %q, %q", label, "metadata.name", "metadata.namespace") - } - }, - ), - ) - // If multiple versions exist, then register conversions from zz_generated.conversion.go // if err := playlist.RegisterConversions(scheme); err != nil { // return err diff --git a/pkg/services/grafana-apiserver/service.go b/pkg/services/grafana-apiserver/service.go index 2056d8c3bf7..c23006132d5 100644 --- a/pkg/services/grafana-apiserver/service.go +++ b/pkg/services/grafana-apiserver/service.go @@ -324,6 +324,12 @@ func (s *service) start(ctx context.Context) error { return err } + // support folder selection + err = entitystorage.RegisterFieldSelectorSupport(Scheme) + if err != nil { + return err + } + // Create the server server, err := serverConfig.Complete().New("grafana-apiserver", genericapiserver.NewEmptyDelegate()) if err != nil { diff --git a/pkg/services/grafana-apiserver/storage/entity/fieldRequirements.go b/pkg/services/grafana-apiserver/storage/entity/fieldRequirements.go new file mode 100644 index 00000000000..72aab2595d8 --- /dev/null +++ b/pkg/services/grafana-apiserver/storage/entity/fieldRequirements.go @@ -0,0 +1,76 @@ +package entity + +import ( + "fmt" + "strings" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/selection" +) + +const folderAnnoKey = "grafana.app/folder" + +type FieldRequirements struct { + // Equals folder + Folder *string +} + +func ReadFieldRequirements(selector fields.Selector) (FieldRequirements, fields.Selector, error) { + requirements := FieldRequirements{} + + if selector == nil { + return requirements, selector, nil + } + + for _, r := range selector.Requirements() { + switch r.Field { + case folderAnnoKey: + if (r.Operator != selection.Equals) && (r.Operator != selection.DoubleEquals) { + return requirements, selector, apierrors.NewBadRequest("only equality is supported in the selectors") + } + folder := r.Value + requirements.Folder = &folder + } + } + + // use Transform function to remove grafana.app/folder field selector + selector, err := selector.Transform(func(field, value string) (string, string, error) { + switch field { + case folderAnnoKey: + return "", "", nil + } + return field, value, nil + }) + + return requirements, selector, err +} + +func RegisterFieldSelectorSupport(scheme *runtime.Scheme) error { + grafanaFieldSupport := runtime.FieldLabelConversionFunc( + func(field, value string) (string, string, error) { + if strings.HasPrefix(field, "grafana.app/") { + return field, value, nil + } + return "", "", getBadSelectorError(field) + }, + ) + + // Register all the internal types + for gvk := range scheme.AllKnownTypes() { + if strings.HasSuffix(gvk.Group, ".grafana.app") { + err := scheme.AddFieldLabelConversionFunc(gvk, grafanaFieldSupport) + if err != nil { + return err + } + } + } + return nil +} + +func getBadSelectorError(f string) error { + return apierrors.NewBadRequest( + fmt.Sprintf("%q is not a known field selector: only %q works", f, folderAnnoKey), + ) +} diff --git a/pkg/services/grafana-apiserver/storage/entity/storage.go b/pkg/services/grafana-apiserver/storage/entity/storage.go index 298f6364276..ce623fb7e7a 100644 --- a/pkg/services/grafana-apiserver/storage/entity/storage.go +++ b/pkg/services/grafana-apiserver/storage/entity/storage.go @@ -241,29 +241,16 @@ func (s *Storage) GetList(ctx context.Context, key string, opts storage.ListOpti } } - // translate grafana.app/folder field selector to folder condition - fields := opts.Predicate.Field.Requirements() - for _, f := range fields { - if f.Field == "grafana.app/folder" { - if f.Operator != selection.Equals { - return apierrors.NewBadRequest("grafana.app/folder field selector only supports equality") - } - - // select items in the spcified folder - req.Folder = f.Value - } - } - - // use Transform function to remove grafana.app/folder field selector - opts.Predicate.Field, err = opts.Predicate.Field.Transform(func(field, value string) (string, string, error) { - if field == "grafana.app/folder" { - return "", "", nil - } - return field, value, nil - }) + // translate grafana.app/folder field selector to the folder condition + fieldRequirements, fieldSelector, err := ReadFieldRequirements(opts.Predicate.Field) if err != nil { return err } + if fieldRequirements.Folder != nil { + req.Folder = *fieldRequirements.Folder + } + // Update the field selector to remove the unneeded selectors + opts.Predicate.Field = fieldSelector rsp, err := s.store.List(ctx, req) if err != nil {