From 9ef4ae9b5ecf428535f20ca24fb0674635b28f72 Mon Sep 17 00:00:00 2001 From: Ingo Oppermann Date: Wed, 28 Sep 2022 21:49:25 +0200 Subject: [PATCH] Fix cluster api responses --- cluster/cluster.go | 5 ++++- docs/docs.go | 30 +++++++++++++++++++++--------- docs/swagger.json | 30 +++++++++++++++++++++--------- docs/swagger.yaml | 24 ++++++++++++++++-------- http/api/cluster.go | 2 +- http/handler/api/cluster.go | 31 ++++++++++++++++++++++++++----- 6 files changed, 89 insertions(+), 33 deletions(-) diff --git a/cluster/cluster.go b/cluster/cluster.go index 17eab23d..f46221fe 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -2,6 +2,7 @@ package cluster import ( "context" + "errors" "fmt" "io" "sync" @@ -11,6 +12,8 @@ import ( "github.com/datarhei/core/v16/net" ) +var ErrNodeNotFound = errors.New("node not found") + type ClusterReader interface { GetURL(path string) (string, error) GetFile(path string) (io.ReadCloser, error) @@ -182,7 +185,7 @@ func (c *cluster) RemoveNode(id string) error { node, ok := c.nodes[id] if !ok { - return nil + return ErrNodeNotFound } node.stop() diff --git a/docs/docs.go b/docs/docs.go index 2c9331ca..79872642 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -62,7 +62,7 @@ const docTemplate = `{ "operationId": "graph-playground", "responses": { "200": { - "description": "" + "description": "OK" } } } @@ -327,14 +327,14 @@ const docTemplate = `{ "ApiKeyAuth": [] } ], - "description": "Replace an existing Node", + "description": "Replaces an existing node and returns the new node ID", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "Replace an existing Node", + "summary": "Replaces an existing node", "operationId": "cluster-3-update-node", "parameters": [ { @@ -403,8 +403,14 @@ const docTemplate = `{ "type": "string" } }, - "400": { - "description": "Bad Request", + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "500": { + "description": "Internal Server Error", "schema": { "$ref": "#/definitions/api.Error" } @@ -438,10 +444,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/definitions/api.ClusterNodeFiles" } }, "404": { @@ -2581,6 +2584,15 @@ const docTemplate = `{ } } }, + "api.ClusterNodeFiles": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, "api.Command": { "type": "object", "required": [ diff --git a/docs/swagger.json b/docs/swagger.json index d3246920..4fc96258 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -54,7 +54,7 @@ "operationId": "graph-playground", "responses": { "200": { - "description": "" + "description": "OK" } } } @@ -319,14 +319,14 @@ "ApiKeyAuth": [] } ], - "description": "Replace an existing Node", + "description": "Replaces an existing node and returns the new node ID", "consumes": [ "application/json" ], "produces": [ "application/json" ], - "summary": "Replace an existing Node", + "summary": "Replaces an existing node", "operationId": "cluster-3-update-node", "parameters": [ { @@ -395,8 +395,14 @@ "type": "string" } }, - "400": { - "description": "Bad Request", + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "500": { + "description": "Internal Server Error", "schema": { "$ref": "#/definitions/api.Error" } @@ -430,10 +436,7 @@ "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/definitions/api.ClusterNodeFiles" } }, "404": { @@ -2573,6 +2576,15 @@ } } }, + "api.ClusterNodeFiles": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, "api.Command": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 20af5f31..9f73365f 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -76,6 +76,12 @@ definitions: username: type: string type: object + api.ClusterNodeFiles: + additionalProperties: + items: + type: string + type: array + type: object api.Command: properties: command: @@ -1774,7 +1780,7 @@ paths: - text/html responses: "200": - description: "" + description: OK security: - ApiKeyAuth: [] summary: Load GraphQL playground @@ -1933,8 +1939,12 @@ paths: description: OK schema: type: string - "400": - description: Bad Request + "404": + description: Not Found + schema: + $ref: '#/definitions/api.Error' + "500": + description: Internal Server Error schema: $ref: '#/definitions/api.Error' security: @@ -1966,7 +1976,7 @@ paths: put: consumes: - application/json - description: Replace an existing Node + description: Replaces an existing node and returns the new node ID operationId: cluster-3-update-node parameters: - description: Node ID @@ -1997,7 +2007,7 @@ paths: $ref: '#/definitions/api.Error' security: - ApiKeyAuth: [] - summary: Replace an existing Node + summary: Replaces an existing node /api/v3/cluster/node/{id}/proxy: get: description: List the files of a node by its ID @@ -2014,9 +2024,7 @@ paths: "200": description: OK schema: - items: - type: string - type: array + $ref: '#/definitions/api.ClusterNodeFiles' "404": description: Not Found schema: diff --git a/http/api/cluster.go b/http/api/cluster.go index 71cba657..770ef763 100644 --- a/http/api/cluster.go +++ b/http/api/cluster.go @@ -13,4 +13,4 @@ type ClusterNode struct { State string `json:"state"` } -type ClusterNodeFiles []string +type ClusterNodeFiles map[string][]string diff --git a/http/handler/api/cluster.go b/http/handler/api/cluster.go index 0a1354d8..656f9c2f 100644 --- a/http/handler/api/cluster.go +++ b/http/handler/api/cluster.go @@ -2,7 +2,9 @@ package api import ( "net/http" + "regexp" "sort" + "strings" "github.com/datarhei/core/v16/cluster" "github.com/datarhei/core/v16/http/api" @@ -14,12 +16,14 @@ import ( // The ClusterHandler type provides handler functions for manipulating the cluster config. type ClusterHandler struct { cluster cluster.Cluster + prefix *regexp.Regexp } // NewCluster return a new ClusterHandler type. You have to provide a cluster. func NewCluster(cluster cluster.Cluster) *ClusterHandler { return &ClusterHandler{ cluster: cluster, + prefix: regexp.MustCompile(`^[a-z]+:`), } } @@ -85,14 +89,19 @@ func (h *ClusterHandler) AddNode(c echo.Context) error { // @Produce json // @Param id path string true "Node ID" // @Success 200 {string} string -// @Failure 400 {object} api.Error +// @Failure 404 {object} api.Error +// @Failure 500 {object} api.Error // @Security ApiKeyAuth // @Router /api/v3/cluster/node/{id} [delete] func (h *ClusterHandler) DeleteNode(c echo.Context) error { id := util.PathParam(c, "id") if err := h.cluster.RemoveNode(id); err != nil { - return api.Err(http.StatusBadRequest, "Failed to remove node", "%s", err) + if err == cluster.ErrNodeNotFound { + return api.Err(http.StatusNotFound, err.Error(), "%s", id) + } + + return api.Err(http.StatusInternalServerError, "Failed to remove node", "%s", err) } return c.JSON(http.StatusOK, "OK") @@ -146,16 +155,25 @@ func (h *ClusterHandler) GetNodeProxy(c echo.Context) error { return api.Err(http.StatusNotFound, "Node not found", "%s", err) } + files := api.ClusterNodeFiles{} + state := peer.State() sort.Strings(state.Files) - return c.JSON(http.StatusOK, state.Files) + for _, path := range state.Files { + prefix := strings.TrimSuffix(h.prefix.FindString(path), ":") + path = h.prefix.ReplaceAllString(path, "") + + files[prefix] = append(files[prefix], path) + } + + return c.JSON(http.StatusOK, files) } // UpdateNode replaces an existing node -// @Summary Replace an existing Node -// @Description Replace an existing Node +// @Summary Replaces an existing node +// @Description Replaces an existing node and returns the new node ID // @ID cluster-3-update-node // @Accept json // @Produce json @@ -176,6 +194,9 @@ func (h *ClusterHandler) UpdateNode(c echo.Context) error { } if err := h.cluster.RemoveNode(id); err != nil { + if err == cluster.ErrNodeNotFound { + return api.Err(http.StatusNotFound, err.Error(), "%s", id) + } return api.Err(http.StatusBadRequest, "Failed to remove node", "%s", err) }