NestedFolderPicker: Migrate to app platform API (#106926)
* Add /children endpoint * Update folder client * Add comment * Add feature toggle * Add new version of useFoldersQuery * Error handling * Format * Rename feature toggle * Remove options and move root folder constant * Fix feature toggle merge * Add feature toggle again * Rename useFoldersQuery files * Update API spec * Fix test * Add test * Better typings --------- Co-authored-by: Clarity-89 <homes89@ukr.net>
This commit is contained in:
co-authored by
Clarity-89
parent
185ce90a4b
commit
e76f470b44
@@ -192,6 +192,11 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API
|
||||
storage[resourceInfo.StoragePath("counts")] = &subCountREST{searcher: b.searcher}
|
||||
storage[resourceInfo.StoragePath("access")] = &subAccessREST{b.folderSvc, b.ac}
|
||||
|
||||
// Adds a path to return children of a given folder
|
||||
storage[resourceInfo.StoragePath("children")] = &subChildrenREST{
|
||||
lister: storage[resourceInfo.StoragePath()].(rest.Lister),
|
||||
}
|
||||
|
||||
apiGroupInfo.VersionedResourcesStorageMap[folders.VERSION] = storage
|
||||
b.storage = storage[resourceInfo.StoragePath()].(grafanarest.Storage)
|
||||
return nil
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package folders
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||
)
|
||||
|
||||
type subChildrenREST struct {
|
||||
lister rest.Lister
|
||||
}
|
||||
|
||||
var _ = rest.Connecter(&subChildrenREST{})
|
||||
var _ = rest.StorageMetadata(&subChildrenREST{})
|
||||
|
||||
// RootFolderName Hardcoded magic const to get root folders without parent.
|
||||
var RootFolderName = "general"
|
||||
|
||||
func (r *subChildrenREST) New() runtime.Object {
|
||||
return &folders.FolderList{}
|
||||
}
|
||||
|
||||
func (r *subChildrenREST) Destroy() {
|
||||
}
|
||||
|
||||
func (r *subChildrenREST) ProducesMIMETypes(verb string) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *subChildrenREST) ProducesObject(verb string) interface{} {
|
||||
return &folders.FolderList{}
|
||||
}
|
||||
|
||||
func (r *subChildrenREST) ConnectMethods() []string {
|
||||
return []string{"GET"}
|
||||
}
|
||||
|
||||
func (r *subChildrenREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||
return nil, false, "" // true means you can use the trailing path as a variable
|
||||
}
|
||||
|
||||
func (r *subChildrenREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
|
||||
obj, err := r.lister.List(ctx, &internalversion.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allFolders, ok := obj.(*folders.FolderList)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not list folders")
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
children := &folders.FolderList{}
|
||||
parentName := ""
|
||||
|
||||
if name != RootFolderName {
|
||||
parentName = name
|
||||
}
|
||||
for _, folder := range allFolders.Items {
|
||||
if parentName == getParent(&folder) {
|
||||
children.Items = append(children.Items, folder)
|
||||
}
|
||||
}
|
||||
|
||||
responder.Object(http.StatusOK, children)
|
||||
}), nil
|
||||
}
|
||||
@@ -1734,6 +1734,16 @@ var (
|
||||
FrontendOnly: true,
|
||||
Expression: "false", // extensions will be disabled by default
|
||||
},
|
||||
{
|
||||
Name: "foldersAppPlatformAPI",
|
||||
Description: "Enables use of app platform API for folders",
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaFrontendSearchNavOrganise,
|
||||
HideFromAdminPage: true,
|
||||
HideFromDocs: true,
|
||||
FrontendOnly: true,
|
||||
Expression: "false",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -226,3 +226,4 @@ preferLibraryPanelTitle,privatePreview,@grafana/dashboards-squad,false,false,fal
|
||||
tabularNumbers,GA,@grafana/grafana-frontend-platform,false,false,false
|
||||
newInfluxDSConfigPageDesign,privatePreview,@grafana/partner-datasources,false,false,false
|
||||
enableAppChromeExtensions,experimental,@grafana/plugins-platform-backend,false,false,true
|
||||
foldersAppPlatformAPI,experimental,@grafana/grafana-search-navigate-organise,false,false,true
|
||||
|
||||
|
@@ -914,4 +914,8 @@ const (
|
||||
// FlagEnableAppChromeExtensions
|
||||
// Set this to true to enable all app chrome extensions registered by plugins.
|
||||
FlagEnableAppChromeExtensions = "enableAppChromeExtensions"
|
||||
|
||||
// FlagFoldersAppPlatformAPI
|
||||
// Enables use of app platform API for folders
|
||||
FlagFoldersAppPlatformAPI = "foldersAppPlatformAPI"
|
||||
)
|
||||
|
||||
@@ -1257,6 +1257,22 @@
|
||||
"hideFromDocs": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "foldersAppPlatformAPI",
|
||||
"resourceVersion": "1751377081192",
|
||||
"creationTimestamp": "2025-07-01T13:38:01Z"
|
||||
},
|
||||
"spec": {
|
||||
"description": "Enables use of app platform API for folders",
|
||||
"stage": "experimental",
|
||||
"codeowner": "@grafana/grafana-search-navigate-organise",
|
||||
"frontend": true,
|
||||
"hideFromAdminPage": true,
|
||||
"hideFromDocs": true,
|
||||
"expression": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"name": "formatString",
|
||||
|
||||
@@ -88,6 +88,15 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
||||
"get"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "folders/children",
|
||||
"singularName": "",
|
||||
"namespaced": true,
|
||||
"kind": "FolderList",
|
||||
"verbs": [
|
||||
"get"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "folders/counts",
|
||||
"singularName": "",
|
||||
|
||||
@@ -915,6 +915,55 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"/apis/folder.grafana.app/v1beta1/namespaces/{namespace}/folders/{name}/children": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Folder"
|
||||
],
|
||||
"description": "connect GET requests to children of Folder",
|
||||
"operationId": "getFolderChildren",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"*/*": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.folder.pkg.apis.folder.v1beta1.FolderList"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-kubernetes-action": "connect",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "folder.grafana.app",
|
||||
"version": "v1beta1",
|
||||
"kind": "FolderList"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"description": "name of the FolderList",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "namespace",
|
||||
"in": "path",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"/apis/folder.grafana.app/v1beta1/namespaces/{namespace}/folders/{name}/counts": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
||||
Reference in New Issue
Block a user