From d1d578785caaf7a916017404a59f199fd04f3834 Mon Sep 17 00:00:00 2001 From: maicon Date: Thu, 12 Sep 2024 14:38:27 -0300 Subject: [PATCH] Add remaining k8s endpoints for Folders (#93146) * Add remaining Folders k8s endpoints Signed-off-by: Maicon Costa * Remove duplicated import Signed-off-by: Maicon Costa --------- Signed-off-by: Maicon Costa --- pkg/api/api.go | 3 ++ pkg/api/folder.go | 66 +++++++++++++++++++++++- pkg/registry/apis/folders/conversions.go | 13 ++++- 3 files changed, 78 insertions(+), 4 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 9e42b65cb7a..e79d338a912 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -448,9 +448,12 @@ func (hs *HTTPServer) registerRoutes() { if hs.Features.IsEnabledGlobally(featuremgmt.FlagKubernetesFolders) { // Use k8s client to implement legacy API handler := newFolderK8sHandler(hs) + folderRoute.Get("/", handler.searchFolders) folderRoute.Post("/", handler.createFolder) folderRoute.Group("/:uid", func(folderUidRoute routing.RouteRegister) { folderUidRoute.Get("/", handler.getFolder) + folderUidRoute.Delete("/", handler.deleteFolder) + folderUidRoute.Put("/:uid", handler.updateFolder) }) } else { idScope := dashboards.ScopeFoldersProvider.GetResourceScope(ac.Parameter(":id")) diff --git a/pkg/api/folder.go b/pkg/api/folder.go index 833de98ed14..1433e12422b 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -5,6 +5,7 @@ import ( "errors" "net/http" "strconv" + "strings" k8sErrors "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -648,17 +649,43 @@ func newFolderK8sHandler(hs *HTTPServer) *folderK8sHandler { } } +func (fk8s *folderK8sHandler) searchFolders(c *contextmodel.ReqContext) { + client, ok := fk8s.getClient(c) + if !ok { + return // error is already sent + } + out, err := client.List(c.Req.Context(), v1.ListOptions{}) + if err != nil { + fk8s.writeError(c, err) + return + } + + query := strings.ToUpper(c.Query("query")) + folders := []folder.Folder{} + for _, item := range out.Items { + p := internalfolders.UnstructuredToLegacyFolder(item) + if p == nil { + continue + } + if query != "" && !strings.Contains(strings.ToUpper(p.Title), query) { + continue // query filter + } + folders = append(folders, *p) + } + c.JSON(http.StatusOK, folders) +} + func (fk8s *folderK8sHandler) createFolder(c *contextmodel.ReqContext) { client, ok := fk8s.getClient(c) if !ok { return // error is already sent } - cmd := folder.CreateFolderCommand{} + cmd := folder.UpdateFolderCommand{} if err := web.Bind(c.Req, &cmd); err != nil { c.JsonApiErr(http.StatusBadRequest, "bad request data", err) return } - obj := internalfolders.LegacyCreateCommandToUnstructured(cmd) + obj := internalfolders.LegacyUpdateCommandToUnstructured(cmd) out, err := client.Create(c.Req.Context(), &obj, v1.CreateOptions{}) if err != nil { fk8s.writeError(c, err) @@ -681,6 +708,41 @@ func (fk8s *folderK8sHandler) getFolder(c *contextmodel.ReqContext) { c.JSON(http.StatusOK, internalfolders.UnstructuredToLegacyFolderDTO(*out)) } +func (fk8s *folderK8sHandler) deleteFolder(c *contextmodel.ReqContext) { + client, ok := fk8s.getClient(c) + if !ok { + return // error is already sent + } + uid := web.Params(c.Req)[":uid"] + err := client.Delete(c.Req.Context(), uid, v1.DeleteOptions{}) + if err != nil { + fk8s.writeError(c, err) + return + } + c.JSON(http.StatusOK, "") +} + +func (fk8s *folderK8sHandler) updateFolder(c *contextmodel.ReqContext) { + client, ok := fk8s.getClient(c) + if !ok { + return // error is already sent + } + uid := web.Params(c.Req)[":uid"] + cmd := folder.UpdateFolderCommand{} + if err := web.Bind(c.Req, &cmd); err != nil { + c.JsonApiErr(http.StatusBadRequest, "bad request data", err) + return + } + obj := internalfolders.LegacyUpdateCommandToUnstructured(cmd) + obj.SetName(uid) + out, err := client.Update(c.Req.Context(), &obj, v1.UpdateOptions{}) + if err != nil { + fk8s.writeError(c, err) + return + } + c.JSON(http.StatusOK, internalfolders.UnstructuredToLegacyFolderDTO(*out)) +} + //----------------------------------------------------------------------------------------- // Utility functions //----------------------------------------------------------------------------------------- diff --git a/pkg/registry/apis/folders/conversions.go b/pkg/registry/apis/folders/conversions.go index 2e1fb92a3f0..75c13777af1 100644 --- a/pkg/registry/apis/folders/conversions.go +++ b/pkg/registry/apis/folders/conversions.go @@ -14,12 +14,12 @@ import ( "github.com/grafana/grafana/pkg/services/folder" ) -func LegacyCreateCommandToUnstructured(cmd folder.CreateFolderCommand) unstructured.Unstructured { +func LegacyUpdateCommandToUnstructured(cmd folder.UpdateFolderCommand) unstructured.Unstructured { // #TODO add other fields obj := unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{ - "title": cmd.Title, + "title": cmd.NewTitle, }, }, } @@ -27,6 +27,15 @@ func LegacyCreateCommandToUnstructured(cmd folder.CreateFolderCommand) unstructu return obj } +func UnstructuredToLegacyFolder(item unstructured.Unstructured) *folder.Folder { + spec := item.Object["spec"].(map[string]any) + return &folder.Folder{ + UID: item.GetName(), + Title: spec["title"].(string), + // #TODO add other fields + } +} + func UnstructuredToLegacyFolderDTO(item unstructured.Unstructured) *dtos.Folder { spec := item.Object["spec"].(map[string]any) dto := &dtos.Folder{