Unified Storage: Add selectable fields to folders (#117270)

* makes folder title a selectable field and fixes cyclic import with selectable field utils

* make gofmt

* include metadata.name and metadata.namespace as default selectable fields in BuildGetAttrsFn() for selectable fields
This commit is contained in:
owensmallwood 2026-02-03 15:46:13 -06:00 committed by GitHub
parent 923239d48d
commit 8f8e076700
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 46 additions and 10 deletions

View file

@ -10,4 +10,8 @@ foldersV1beta1: {
description?: string
}
}
}
selectableFields: [
"spec.title",
]
}

View file

@ -5,13 +5,26 @@
package v1beta1
import (
"errors"
"github.com/grafana/grafana-app-sdk/resource"
)
// schema is unexported to prevent accidental overwrites
var (
schemaFolder = resource.NewSimpleSchema("folder.grafana.app", "v1beta1", NewFolder(), &FolderList{}, resource.WithKind("Folder"),
resource.WithPlural("folders"), resource.WithScope(resource.NamespacedScope))
resource.WithPlural("folders"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{{
FieldSelector: "spec.title",
FieldValueFunc: func(o resource.Object) (string, error) {
cast, ok := o.(*Folder)
if !ok {
return "", errors.New("provided object must be of type *Folder")
}
return cast.Spec.Title, nil
},
},
}))
kindFolder = resource.Kind{
Schema: schemaFolder,
Codecs: map[resource.KindEncoding]resource.Codec{

View file

@ -32,6 +32,9 @@ var appManifestData = app.ManifestData{
Plural: "Folders",
Scope: "Namespaced",
Conversion: false,
SelectableFields: []string{
"spec.title",
},
},
},
Routes: app.ManifestVersionRoutes{

View file

@ -28,6 +28,7 @@ import (
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
"github.com/grafana/grafana/pkg/registry/fieldselectors"
"github.com/grafana/grafana/pkg/services/accesscontrol"
grafanaauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
@ -134,6 +135,10 @@ func (b *FolderAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
// return err
// }
metav1.AddToGroupVersion(scheme, gv)
err := fieldselectors.AddSelectableFieldLabelConversions(scheme, gv, folders.FolderKind())
if err != nil {
return err
}
return scheme.SetVersionPriority(gv)
}
@ -148,7 +153,10 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API
Permissions: b.setDefaultFolderPermissions,
})
unified, err := grafanaregistry.NewRegistryStore(opts.Scheme, resourceInfo, opts.OptsGetter)
selectableFieldsOpts := grafanaregistry.SelectableFieldsOptions{
GetAttrs: fieldselectors.BuildGetAttrsFn(folders.FolderKind()),
}
unified, err := grafanaregistry.NewRegistryStoreWithSelectableFields(opts.Scheme, resourceInfo, opts.OptsGetter, selectableFieldsOpts)
if err != nil {
return err
}

View file

@ -1,4 +1,4 @@
package apiregistry
package fieldselectors
import (
"fmt"
@ -11,7 +11,8 @@ import (
sdkres "github.com/grafana/grafana-app-sdk/resource"
)
// These helper functions are to be used in InstallSchema() in apis/*/register.go files in order for already existing kinds to use field selectors.
// These helper functions are to be used in InstallSchema() in apis/*/register.go files
// in order for already existing kinds to use field selectors.
// AddSelectableFieldLabelConversions registers field selector conversions for kinds that
// expose selectable fields via the app SDK.
@ -46,6 +47,9 @@ func BuildGetAttrsFn(k sdkres.Kind) func(obj runtime.Object) (labels.Set, fields
return nil, nil, fmt.Errorf("not a resource.Object")
} else {
fieldsSet := fields.Set{}
// Always include metadata.name and metadata.namespace as they are the default selectable fields
fieldsSet["metadata.name"] = robj.GetName()
fieldsSet["metadata.namespace"] = robj.GetNamespace()
for _, f := range k.SelectableFields() {
v, err := f.FieldValueFunc(robj)

View file

@ -1,4 +1,4 @@
package apiregistry
package fieldselectors
import (
"testing"
@ -85,7 +85,9 @@ func TestSelectableFieldsBuildGetAttrsFn(t *testing.T) {
obj := &sdkres.TypedSpecObject[any]{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"label": "value"},
Labels: map[string]string{"label": "value"},
Namespace: "ns",
Name: "name",
},
}
@ -94,5 +96,7 @@ func TestSelectableFieldsBuildGetAttrsFn(t *testing.T) {
require.NoError(t, err)
require.Equal(t, labels.Set{"label": "value"}, lbls)
require.Equal(t, fields.Set{"spec.foo": "bar"}, flds)
require.Equal(t, fields.Set{"metadata.name": "name", "metadata.namespace": "ns", "spec.foo": "bar"}, flds)
require.Equal(t, "name", flds["metadata.name"])
require.Equal(t, "ns", flds["metadata.namespace"])
}

View file

@ -1,7 +1,7 @@
package runner
import (
apiregistry "github.com/grafana/grafana/pkg/registry/apis"
"github.com/grafana/grafana/pkg/registry/fieldselectors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/authorization/authorizer"
@ -89,7 +89,7 @@ func (b *appBuilder) InstallSchema(scheme *runtime.Scheme) error {
if len(kind.SelectableFields()) == 0 {
continue
}
err := apiregistry.AddSelectableFieldLabelConversions(scheme, gv, kind)
err := fieldselectors.AddSelectableFieldLabelConversions(scheme, gv, kind)
if err != nil {
return err
}