From 5cd2d5411f08cfda511a5924d388523b86d6f9ee Mon Sep 17 00:00:00 2001 From: "Helmut K. C. Tessarek" Date: Sun, 28 May 2023 09:59:49 -0400 Subject: [PATCH] Add delete application image endpoint --- api/application.go | 64 ++++++++++++++++++++++++++++++++++++ api/application_test.go | 41 +++++++++++++++++++++++ docs/spec.json | 72 +++++++++++++++++++++++++++++++++++++++++ router/router.go | 2 ++ 4 files changed, 179 insertions(+) diff --git a/api/application.go b/api/application.go index 142809c..6fa0f95 100644 --- a/api/application.go +++ b/api/application.go @@ -358,6 +358,70 @@ func (a *ApplicationAPI) UploadApplicationImage(ctx *gin.Context) { }) } +// RemoveApplicationImage deletes an image of an application. +// swagger:operation DELETE /application/{id}/image application removeAppImage +// +// Deletes an image of an application. +// +// --- +// consumes: [application/json] +// produces: [application/json] +// parameters: +// - name: id +// in: path +// description: the application id +// required: true +// type: integer +// format: int64 +// security: [clientTokenAuthorizationHeader: [], clientTokenHeader: [], clientTokenQuery: [], basicAuth: []] +// responses: +// 200: +// description: Ok +// 400: +// description: Bad Request +// schema: +// $ref: "#/definitions/Error" +// 401: +// description: Unauthorized +// schema: +// $ref: "#/definitions/Error" +// 403: +// description: Forbidden +// schema: +// $ref: "#/definitions/Error" +// 404: +// description: Not Found +// schema: +// $ref: "#/definitions/Error" +// 500: +// description: Server Error +// schema: +// $ref: "#/definitions/Error" +func (a *ApplicationAPI) RemoveApplicationImage(ctx *gin.Context) { + withID(ctx, "id", func(id uint) { + app, err := a.DB.GetApplicationByID(id) + if success := successOrAbort(ctx, 500, err); !success { + return + } + if app != nil && app.UserID == auth.GetUserID(ctx) { + if app.Image == "" { + ctx.AbortWithError(400, fmt.Errorf("app with id %d does not have a customized image", id)) + return + } + + image := app.Image + app.Image = "" + if success := successOrAbort(ctx, 500, a.DB.UpdateApplication(app)); !success { + return + } + os.Remove(a.ImageDir + image) + ctx.JSON(200, withResolvedImage(app)) + } else { + ctx.AbortWithError(404, fmt.Errorf("app with id %d doesn't exists", id)) + } + }) +} + func withResolvedImage(app *model.Application) *model.Application { if app.Image == "" { app.Image = "static/defaultapp.png" diff --git a/api/application_test.go b/api/application_test.go index 22777eb..6312a0a 100644 --- a/api/application_test.go +++ b/api/application_test.go @@ -426,6 +426,47 @@ func (s *ApplicationSuite) Test_UploadAppImage_expectNotFound() { assert.Equal(s.T(), 404, s.recorder.Code) } +func (s *ApplicationSuite) Test_RemoveAppImage_expectNotFound() { + s.db.User(5) + + test.WithUser(s.ctx, 5) + s.ctx.Request = httptest.NewRequest("DELETE", "/irrelevant", nil) + s.ctx.Params = gin.Params{{Key: "id", Value: "4"}} + + s.a.RemoveApplicationImage(s.ctx) + + assert.Equal(s.T(), 404, s.recorder.Code) +} + +func (s *ApplicationSuite) Test_RemoveAppImage_noCustomizedImage() { + s.db.User(5).App(1) + + test.WithUser(s.ctx, 5) + s.ctx.Request = httptest.NewRequest("DELETE", "/irrelevant", nil) + s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} + s.a.RemoveApplicationImage(s.ctx) + + assert.Equal(s.T(), 400, s.recorder.Code) +} + +func (s *ApplicationSuite) Test_RemoveAppImage_expectSuccess() { + s.db.User(5) + + imageFile := "existing.png" + s.db.CreateApplication(&model.Application{UserID: 5, ID: 1, Image: imageFile}) + fakeImage(s.T(), imageFile) + + test.WithUser(s.ctx, 5) + s.ctx.Request = httptest.NewRequest("DELETE", "/irrelevant", nil) + s.ctx.Params = gin.Params{{Key: "id", Value: "1"}} + s.a.RemoveApplicationImage(s.ctx) + + _, err := os.Stat(imageFile) + assert.True(s.T(), os.IsNotExist(err)) + + assert.Equal(s.T(), 200, s.recorder.Code) +} + func (s *ApplicationSuite) Test_UploadAppImage_WithSaveError_expectServerError() { s.db.User(5).App(1) diff --git a/docs/spec.json b/docs/spec.json index c011db1..6bb5aaf 100644 --- a/docs/spec.json +++ b/docs/spec.json @@ -365,6 +365,78 @@ } } } + }, + "delete": { + "security": [ + { + "clientTokenAuthorizationHeader": [] + }, + { + "clientTokenHeader": [] + }, + { + "clientTokenQuery": [] + }, + { + "basicAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "application" + ], + "summary": "Deletes an image of an application.", + "operationId": "removeAppImage", + "parameters": [ + { + "type": "integer", + "format": "int64", + "description": "the application id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Ok" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "500": { + "description": "Server Error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } } }, "/application/{id}/message": { diff --git a/router/router.go b/router/router.go index 56b6bf4..d2a77b2 100644 --- a/router/router.go +++ b/router/router.go @@ -119,6 +119,8 @@ func Create(db *database.GormDatabase, vInfo *model.VersionInfo, conf *config.Co app.POST("/:id/image", applicationHandler.UploadApplicationImage) + app.DELETE("/:id/image", applicationHandler.RemoveApplicationImage) + app.PUT("/:id", applicationHandler.UpdateApplication) app.DELETE("/:id", applicationHandler.DeleteApplication)