From 93efaa95f86a9a82210ca45aa5dabd6889aed2d1 Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 4 Jul 2018 19:21:04 +0200 Subject: [PATCH] Implemented method to delete a namespace --- Featurecreep.md | 1 + models/error.go | 14 ++++++ models/lists.go | 4 +- models/namespace_delete.go | 43 +++++++++++++++++ models/namespaces.go | 8 ++-- public/swagger/swagger.v1.json | 39 +++++++++++++++ routes/api/v1/namespace_add_update.go | 9 ++-- routes/api/v1/namespace_delete.go | 68 +++++++++++++++++++++++++++ routes/api/v1/namespace_show.go | 5 +- routes/routes.go | 2 +- 10 files changed, 178 insertions(+), 15 deletions(-) create mode 100644 models/namespace_delete.go create mode 100644 routes/api/v1/namespace_delete.go diff --git a/Featurecreep.md b/Featurecreep.md index 26ecd11b..98c8166a 100644 --- a/Featurecreep.md +++ b/Featurecreep.md @@ -156,6 +156,7 @@ Teams sind global, d.h. Ein Team kann mehrere Namespaces verwalten. * [ ] Namen finden * [ ] Alle Packages umziehen * [x] Swagger UI aufsetzen +* [ ] Bessere Fehlermeldungen wenn das Model was ankommt falsch ist und nicht geparst werden kann * [ ] Globale Limits für anlegbare Listen + Namespaces * [ ] Mgl., dass die Instanz geschlossen ist, also sich keiner registrieren kann, und man sich einloggen muss diff --git a/models/error.go b/models/error.go index fe26d46b..37dda242 100644 --- a/models/error.go +++ b/models/error.go @@ -258,4 +258,18 @@ func (err ErrUserDoesNotHaveAccessToNamespace) Error() string { return fmt.Sprintf("You need to be namespace owner to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) } +// ErrUserNeedsToBeNamespaceAdmin represents an error, where the user is not the owner of that namespace (used i.e. when deleting a namespace) +type ErrUserNeedsToBeNamespaceAdmin struct { + NamespaceID int64 + UserID int64 +} +// IsErrUserNeedsToBeNamespaceAdmin checks if an error is a ErrNamespaceDoesNotExist. +func IsErrUserNeedsToBeNamespaceAdmin(err error) bool { + _, ok := err.(ErrUserNeedsToBeNamespaceAdmin) + return ok +} + +func (err ErrUserNeedsToBeNamespaceAdmin) Error() string { + return fmt.Sprintf("You need to be namespace owner to do that [NamespaceID: %d, UserID: %d]", err.NamespaceID, err.UserID) +} diff --git a/models/lists.go b/models/lists.go index 6c29b000..cff19b2e 100644 --- a/models/lists.go +++ b/models/lists.go @@ -11,8 +11,8 @@ type List struct { Owner User `xorm:"-" json:"owner"` Items []*ListItem `xorm:"-" json:"items"` - Created int64 `xorm:"created" json:"created"` - Updated int64 `xorm:"updated" json:"updated"` + Created int64 `xorm:"created" json:"created"` + Updated int64 `xorm:"updated" json:"updated"` } // GetListByID returns a list by its ID diff --git a/models/namespace_delete.go b/models/namespace_delete.go new file mode 100644 index 00000000..ed644210 --- /dev/null +++ b/models/namespace_delete.go @@ -0,0 +1,43 @@ +package models + +func DeleteNamespaceByID(namespaceID int64, doer *User) (err error) { + + // Check if the namespace exists + namespace, err := GetNamespaceByID(namespaceID) + if err != nil { + return + } + + // Check if the user is namespace admin + err = doer.IsNamespaceAdmin(&namespace) + if err != nil { + return + } + + // Delete the namespace + _, err = x.ID(namespaceID).Delete(&Namespace{}) + if err != nil { + return + } + + // Delete all lists with their items + lists, err := GetListsByNamespaceID(namespaceID) + var listIDs []int64 + for _, list := range lists { + listIDs = append(listIDs, list.ID) + } + + // Delete items + _, err = x.In("list_id", listIDs).Delete(&ListItem{}) + if err != nil { + return + } + + // Delete the lists + _, err = x.In("id", listIDs).Delete(&List{}) + if err != nil { + return + } + + return +} diff --git a/models/namespaces.go b/models/namespaces.go index 4ae6dbc8..542b86d8 100644 --- a/models/namespaces.go +++ b/models/namespaces.go @@ -36,15 +36,15 @@ const ( NamespaceRightAdmin ) -func (user *User) IsNamespaceAdmin(namespace *Namespace) (ok bool, err error) { +func (user *User) IsNamespaceAdmin(namespace *Namespace) (err error) { // Owners always have admin rights if user.ID == namespace.Owner.ID { - return true, nil + return nil } // Check if that user is in a team which has admin rights to that namespace - return + return ErrUserNeedsToBeNamespaceAdmin{UserID: user.ID, NamespaceID: namespace.ID} } func (user *User) HasNamespaceAccess(namespace *Namespace) (err error) { @@ -55,7 +55,7 @@ func (user *User) HasNamespaceAccess(namespace *Namespace) (err error) { // Check if the user is in a team which has access to the namespace - return ErrUserDoesNotHaveAccessToNamespace{UserID:user.ID, NamespaceID:namespace.ID} + return ErrUserDoesNotHaveAccessToNamespace{UserID: user.ID, NamespaceID: namespace.ID} } func GetNamespaceByID(id int64) (namespace Namespace, err error) { diff --git a/public/swagger/swagger.v1.json b/public/swagger/swagger.v1.json index e02b95f8..9499eb77 100644 --- a/public/swagger/swagger.v1.json +++ b/public/swagger/swagger.v1.json @@ -487,6 +487,45 @@ "$ref": "#/responses/Message" } } + }, + "delete": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "namespaces" + ], + "summary": "Deletes a namespace with all lists", + "operationId": "deleteNamespace", + "parameters": [ + { + "type": "string", + "description": "ID of the namespace to delete", + "name": "namespaceID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/Message" + }, + "400": { + "$ref": "#/responses/Message" + }, + "403": { + "$ref": "#/responses/Message" + }, + "404": { + "$ref": "#/responses/Message" + }, + "500": { + "$ref": "#/responses/Message" + } + } } }, "/namespaces/{namespaceID}/lists": { diff --git a/routes/api/v1/namespace_add_update.go b/routes/api/v1/namespace_add_update.go index 7d7771d0..8fc9c0ac 100644 --- a/routes/api/v1/namespace_add_update.go +++ b/routes/api/v1/namespace_add_update.go @@ -117,13 +117,14 @@ func addOrUpdateNamespace(c echo.Context) error { if err != nil { return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."}) } - has, err := user.IsNamespaceAdmin(&oldNamespace) + err = user.IsNamespaceAdmin(&oldNamespace) if err != nil { + if models.IsErrUserNeedsToBeNamespaceAdmin(err) { + return c.JSON(http.StatusForbidden, models.Message{"You need to be namespace admin to edit a namespace."}) + } + return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."}) } - if !has { - return c.JSON(http.StatusForbidden, models.Message{"You need to be namespace admin to edit a namespace."}) - } err = models.CreateOrUpdateNamespace(namespace) if err != nil { diff --git a/routes/api/v1/namespace_delete.go b/routes/api/v1/namespace_delete.go new file mode 100644 index 00000000..27669a5a --- /dev/null +++ b/routes/api/v1/namespace_delete.go @@ -0,0 +1,68 @@ +package v1 + +import ( + "git.kolaente.de/konrad/list/models" + "github.com/labstack/echo" + "net/http" + "strconv" +) + +func DeleteNamespaceByID(c echo.Context) error { + // swagger:operation DELETE /namespaces/{namespaceID} namespaces deleteNamespace + // --- + // summary: Deletes a namespace with all lists + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: namespaceID + // in: path + // description: ID of the namespace to delete + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/Message" + // "400": + // "$ref": "#/responses/Message" + // "403": + // "$ref": "#/responses/Message" + // "404": + // "$ref": "#/responses/Message" + // "500": + // "$ref": "#/responses/Message" + + // Check if we have our ID + id := c.Param("id") + // Make int + itemID, err := strconv.ParseInt(id, 10, 64) + if err != nil { + return c.JSON(http.StatusBadRequest, models.Message{"Invalid ID."}) + } + + // Check if the user has the right to delete that namespace + user, err := models.GetCurrentUser(c) + if err != nil { + return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."}) + } + + err = models.DeleteNamespaceByID(itemID, &user) + if err != nil { + if models.IsErrNeedToBeNamespaceOwner(err) { + return c.JSON(http.StatusForbidden, models.Message{"You need to be the namespace owner to delete a namespace."}) + } + + if models.IsErrNamespaceDoesNotExist(err) { + return c.JSON(http.StatusNotFound, models.Message{"This namespace does not exist."}) + } + + if models.IsErrUserNeedsToBeNamespaceAdmin(err) { + return c.JSON(http.StatusForbidden, models.Message{"You need to be namespace admin to delete a namespace."}) + } + + return c.JSON(http.StatusInternalServerError, models.Message{"An error occured."}) + } + + return c.JSON(http.StatusOK, models.Message{"The namespace was deleted with success."}) +} diff --git a/routes/api/v1/namespace_show.go b/routes/api/v1/namespace_show.go index 12760698..26bc1940 100644 --- a/routes/api/v1/namespace_show.go +++ b/routes/api/v1/namespace_show.go @@ -55,9 +55,6 @@ func getNamespace(c echo.Context) (namespace models.Namespace, err error) { // Get the namespace namespace, err = models.GetNamespaceByID(namespaceID) if err != nil { - if models.IsErrNamespaceDoesNotExist(err) { - return - } return } @@ -72,4 +69,4 @@ func getNamespace(c echo.Context) (namespace models.Namespace, err error) { } return -} \ No newline at end of file +} diff --git a/routes/routes.go b/routes/routes.go index 9bd43092..04ecf6b0 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -97,7 +97,7 @@ func RegisterRoutes(e *echo.Echo) { a.PUT("/namespaces", apiv1.AddNamespace) a.GET("/namespaces/:id", apiv1.ShowNamespace) a.POST("/namespaces/:id", apiv1.UpdateNamespace) - // a.DELETE("/namespaces/:id") // Deletes a namespace with all lists + a.DELETE("/namespaces/:id", apiv1.DeleteNamespaceByID) a.GET("/namespaces/:id/lists", apiv1.GetListsByNamespaceID) //a.PUT("/namespaces/:id/lists") // Creates a new list in that namespace }