From fc91ebc64cfbbddedf14dc198be7f8a20b249a60 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 3 Jul 2019 16:53:04 +0200 Subject: [PATCH] working on file endpoints; testing to be completed, other tests to be checked --- common/database.go | 8 +- common/models.go | 14 +- common/responses.go | 29 ++-- common/roles.go | 4 + common/serializers.go | 14 +- routes/file/fileEndpoints.go | 108 +++++++++----- routes/file/fileMethods.go | 232 ++++++++++++++++-------------- routes/file/fileMiddleware.go | 50 +++++++ routes/file/file_test.go | 217 ++++++++++++++++++++++++++++ routes/widget/widgetEndpoints.go | 6 +- routes/widget/widgetMiddleware.go | 21 +-- 11 files changed, 521 insertions(+), 182 deletions(-) create mode 100644 routes/file/fileMiddleware.go create mode 100644 routes/file/file_test.go diff --git a/common/database.go b/common/database.go index 4a07df0..f746885 100644 --- a/common/database.go +++ b/common/database.go @@ -117,8 +117,12 @@ func DummyPopulateDB(test_db *gorm.DB) { file_A := File{Name: "File_A"} file_B := File{Name: "File_B"} + file_C := File{Name: "File_C"} + file_D := File{Name: "File_D"} checkErr(test_db.Create(&file_A).Error) checkErr(test_db.Create(&file_B).Error) + checkErr(test_db.Create(&file_C).Error) + checkErr(test_db.Create(&file_D).Error) simn_A := Simulation{Name: "Simulation_A"} simn_B := Simulation{Name: "Simulation_B"} @@ -186,8 +190,8 @@ func DummyPopulateDB(test_db *gorm.DB) { checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_B).Error) // SimulationModel HM Files - checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_A).Error) - checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_B).Error) + checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_C).Error) + checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_D).Error) // Simulator HM SimulationModels checkErr(test_db.Model(&simr_A).Association("SimulationModels").Append(&mo_A).Error) diff --git a/common/models.go b/common/models.go index 5c682f7..45ab0d5 100644 --- a/common/models.go +++ b/common/models.go @@ -1,9 +1,5 @@ package common -import ( - "time" -) - // User data model type User struct { // ID of user @@ -155,7 +151,7 @@ type File struct { // Name of file Name string `gorm:"not null"` // Path at which file is saved at server side - Path string `gorm:"not null"` + //Path string `gorm:"not null"` // Type of file (MIME type) Type string `gorm:"not null"` // Size of file (in byte) @@ -165,9 +161,11 @@ type File struct { // Width of image (only needed in case of image) ImageWidth uint // Last modification time of file - Date time.Time + Date string // ID of model to which file belongs - SimulationModelID uint `gorm:""` + SimulationModelID uint // ID of widget to which file belongs - WidgetID uint `gorm:""` + WidgetID uint + // TODO Add file itself here?? + FileData []byte `gorm:"column:FileData"` } diff --git a/common/responses.go b/common/responses.go index d80ba8d..4960911 100644 --- a/common/responses.go +++ b/common/responses.go @@ -1,9 +1,5 @@ package common -import ( - "time" -) - type UserResponse struct { Username string `json:"Username"` Role string `json:"Role"` @@ -63,14 +59,15 @@ type WidgetResponse struct { } type FileResponse struct { - Name string `json:"Name"` - ID uint `json:"ID"` - Path string `json:"Path"` - Type string `json:"Type"` - Size uint `json:"Size"` - H uint `json:"ImageHeight"` - W uint `json:"ImageWidth"` - Date time.Time `json:"Date"` + Name string `json:"Name"` + ID uint `json:"ID"` + Type string `json:"Type"` + Size uint `json:"Size"` + H uint `json:"ImageHeight"` + W uint `json:"ImageWidth"` + Date string `json:"Date"` + WidgetID uint `json:"WidgetID"` + SimulationModelID uint `json:"SimulationModelID"` } type SignalResponse struct { @@ -142,3 +139,11 @@ type ResponseMsgSimulators struct { type ResponseMsgSimulator struct { Simulator SimulatorResponse `json:"simulator"` } + +type ResponseMsgFiles struct { + Files []FileResponse `json:"files"` +} + +type ResponseMsgFile struct { + File FileResponse `json:"file"` +} diff --git a/common/roles.go b/common/roles.go index 31c2e0a..907070f 100644 --- a/common/roles.go +++ b/common/roles.go @@ -21,6 +21,7 @@ const ModelVisualization = ModelName("visualization") const ModelWidget = ModelName("widget") const ModelSimulationModel = ModelName("simulationmodel") const ModelSignal = ModelName("signal") +const ModelFile = ModelName("file") type CRUD string @@ -54,6 +55,7 @@ var Roles = RoleActions{ ModelWidget: crud, ModelVisualization: crud, ModelSignal: crud, + ModelFile: crud, }, "User": { ModelUser: _ru_, @@ -63,6 +65,7 @@ var Roles = RoleActions{ ModelWidget: crud, ModelVisualization: crud, ModelSignal: crud, + ModelFile: crud, }, "Guest": { ModelSimulation: _r__, @@ -72,6 +75,7 @@ var Roles = RoleActions{ ModelSimulator: _r__, ModelUser: _ru_, ModelSignal: _r__, + ModelFile: _r__, }, } diff --git a/common/serializers.go b/common/serializers.go index 7b85389..59225d2 100644 --- a/common/serializers.go +++ b/common/serializers.go @@ -248,12 +248,14 @@ func (self *FileSerializerNoAssoc) Response() FileResponse { response := FileResponse{ Name: self.Name, ID: self.ID, - Path: self.Path, - Type: self.Type, - Size: self.Size, - H: self.ImageHeight, - W: self.ImageWidth, - // Date + //Path: self.Path, + Type: self.Type, + Size: self.Size, + H: self.ImageHeight, + W: self.ImageWidth, + Date: self.Date, + WidgetID: self.WidgetID, + SimulationModelID: self.SimulationModelID, } return response } diff --git a/routes/file/fileEndpoints.go b/routes/file/fileEndpoints.go index 4a3cc36..fe60277 100644 --- a/routes/file/fileEndpoints.go +++ b/routes/file/fileEndpoints.go @@ -8,6 +8,8 @@ import ( "github.com/gin-gonic/gin" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget" ) func RegisterFileEndpoints(r *gin.RouterGroup) { @@ -28,8 +30,8 @@ func RegisterFileEndpoints(r *gin.RouterGroup) { // @Failure 403 "Access forbidden." // @Failure 404 "Not found" // @Failure 500 "Internal server error" -// @Param originType query string true "Set to model for files of model, set to widget for files of widget" -// @Param originID query int true "ID of either model or widget of which files are requested" +// @Param objectType query string true "Set to model for files of model, set to widget for files of widget" +// @Param objectID query int true "ID of either model or widget of which files are requested" // @Router /files [get] func getFiles(c *gin.Context) { @@ -51,15 +53,33 @@ func getFiles(c *gin.Context) { return } + //Check access + var ok bool + var m simulationmodel.SimulationModel + var w widget.Widget + if objectType == "model" { + ok, m = simulationmodel.CheckPermissions(c, common.Read, "body", objectID) + if !ok { + return + } + } else { + ok, w = widget.CheckPermissions(c, common.Read, objectID) + if !ok { + return + } + } + + // get meta data of files db := common.GetDB() + var files []common.File if objectType == "model" { - err = db.Where("ModelID", objectID).Find(&files).Error + err = db.Order("ID asc").Model(&m).Related(&files, "Files").Error if common.ProvideErrorResponse(c, err) { return } } else { - err = db.Where("WidgetID", objectID).Find(&files).Error + err = db.Order("ID asc").Model(&w).Related(&files, "Files").Error if common.ProvideErrorResponse(c, err) { return } @@ -93,15 +113,6 @@ func getFiles(c *gin.Context) { // @Param objectID query int true "ID of either model or widget of which files are requested" // @Router /files [post] func addFile(c *gin.Context) { - // Extract file from PUT request form - file_header, err := c.FormFile("file") - if err != nil { - errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error()) - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return - } objectType := c.Request.URL.Query().Get("objectType") if objectType != "model" && objectType != "widget" { @@ -121,6 +132,30 @@ func addFile(c *gin.Context) { return } + // Check access + var ok bool + if objectType == "model" { + ok, _ = simulationmodel.CheckPermissions(c, common.Create, "body", objectID) + if !ok { + return + } + } else { + ok, _ = widget.CheckPermissions(c, common.Create, objectID) + if !ok { + return + } + } + + // Extract file from POST request form + file_header, err := c.FormFile("file") + if err != nil { + errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error()) + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + var newFile File err = newFile.register(file_header, objectType, uint(objectID)) if common.ProvideErrorResponse(c, err) == false { @@ -149,22 +184,16 @@ func addFile(c *gin.Context) { // @Router /files/{fileID} [get] func getFile(c *gin.Context) { - fileID, err := common.GetFileID(c) - if err != nil { + // check access + ok, f := checkPermissions(c, common.Read) + if !ok { return } - var f File - err = f.byID(uint(fileID)) + err := f.download(c) if common.ProvideErrorResponse(c, err) { return } - - f.download(c) - - c.JSON(http.StatusOK, gin.H{ - "message": "OK.", - }) } // updateFile godoc @@ -188,6 +217,12 @@ func getFile(c *gin.Context) { // @Router /files/{fileID} [put] func updateFile(c *gin.Context) { + // check access + ok, f := checkPermissions(c, common.Update) + if !ok { + return + } + // Extract file from PUT request form err := c.Request.ParseForm() if err != nil { @@ -207,17 +242,6 @@ func updateFile(c *gin.Context) { return } - fileID, err := common.GetFileID(c) - if err != nil { - return - } - - var f File - err = f.byID(uint(fileID)) - if common.ProvideErrorResponse(c, err) { - return - } - err = f.update(file_header) if common.ProvideErrorResponse(c, err) == false { c.JSON(http.StatusOK, gin.H{ @@ -239,7 +263,17 @@ func updateFile(c *gin.Context) { // @Param fileID path int true "ID of the file to update" // @Router /files/{fileID} [delete] func deleteFile(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "Not implemented.", - }) + + // check access + ok, f := checkPermissions(c, common.Delete) + if !ok { + return + } + + err := f.delete() + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } } diff --git a/routes/file/fileMethods.go b/routes/file/fileMethods.go index e8709f4..3cf3ffa 100644 --- a/routes/file/fileMethods.go +++ b/routes/file/fileMethods.go @@ -2,13 +2,12 @@ package file import ( "fmt" - "io" + "github.com/gin-gonic/gin" + "io/ioutil" "mime/multipart" "os" "path/filepath" - "strconv" - - "github.com/gin-gonic/gin" + "time" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel" @@ -38,29 +37,40 @@ func (f *File) byID(id uint) error { } func (f *File) save() error { - - // get last modify time of target file - fileinfo, err := os.Stat(f.Path) - if err != nil { - return fmt.Errorf("error stat on file: %s", err.Error()) - } - f.Date = fileinfo.ModTime() - f.ImageWidth = 0 - f.ImageHeight = 0 - db := common.GetDB() - err = db.Create(f).Error + err := db.Create(f).Error return err } +func (f *File) download(c *gin.Context) error { + + err := ioutil.WriteFile(f.Name, f.FileData, 0644) + if err != nil { + return fmt.Errorf("file could not be temporarily created on server disk: %s", err.Error()) + } + defer os.Remove(f.Name) + //Seems this headers needed for some browsers (for example without this headers Chrome will download files as txt) + c.Header("Content-Description", "File Transfer") + c.Header("Content-Transfer-Encoding", "binary") + c.Header("Content-Disposition", "attachment; filename="+f.Name) + //c.Header("Content-Type", contentType) + c.File(f.Name) + + return nil + +} + func (f *File) register(fileHeader *multipart.FileHeader, objectType string, objectID uint) error { // Obtain properties of file f.Type = fileHeader.Header.Get("Content-Type") f.Name = filepath.Base(fileHeader.Filename) - f.Path = filepath.Join(getFolderName(objectType, objectID), f.Name) + //f.Path = filepath.Join(getFolderName(objectType, objectID), f.Name) f.Size = uint(fileHeader.Size) + f.Date = time.Now().String() + f.ImageWidth = 0 // TODO: do we need this? + f.ImageHeight = 0 // TODO: do we need this? var m simulationmodel.SimulationModel var w widget.Widget @@ -68,12 +78,16 @@ func (f *File) register(fileHeader *multipart.FileHeader, objectType string, obj if objectType == "model" { // check if model exists err = m.ByID(objectID) + f.WidgetID = 0 + f.SimulationModelID = objectID if err != nil { return err } } else { // check if widget exists + f.WidgetID = objectID + f.SimulationModelID = 0 err = w.ByID(uint(objectID)) if err != nil { return err @@ -81,12 +95,21 @@ func (f *File) register(fileHeader *multipart.FileHeader, objectType string, obj } - // Save file to local disc (NOT DB!) - err = f.modifyFileOnDisc(fileHeader, true) + // set file data + fileContent, err := fileHeader.Open() if err != nil { - return fmt.Errorf("File could not be saved/ modified on disk: ", err.Error()) + return err } + f.FileData, err = ioutil.ReadAll(fileContent) + defer fileContent.Close() + + // Save file to local disc (NOT DB!) + //err = f.modifyFileOnDisc(fileHeader, true) + //if err != nil { + // return fmt.Errorf("File could not be saved/ modified on disk: ", err.Error()) + //} + // Add File object with parameters to DB err = f.save() if err != nil { @@ -95,12 +118,14 @@ func (f *File) register(fileHeader *multipart.FileHeader, objectType string, obj // Create association to model or widget if objectType == "model" { - err = f.addToModel(m) + db := common.GetDB() + err := db.Model(&m).Association("Files").Append(f).Error if err != nil { return err } } else { - err = f.addToWidget(w) + db := common.GetDB() + err := db.Model(&w).Association("Files").Append(f).Error if err != nil { return err } @@ -110,99 +135,94 @@ func (f *File) register(fileHeader *multipart.FileHeader, objectType string, obj } func (f *File) update(fileHeader *multipart.FileHeader) error { - err := f.modifyFileOnDisc(fileHeader, false) + + // set file data + fileContent, err := fileHeader.Open() if err != nil { return err } + fileData, err := ioutil.ReadAll(fileContent) + fmt.Println("File content: ", string(fileData)) + defer fileContent.Close() + + //err := f.modifyFileOnDisc(fileHeader, false) + //if err != nil { + // return err + //} + db := common.GetDB() - err = db.Model(f).Update("Size", fileHeader.Size).Error + err = db.Model(f).Updates(map[string]interface{}{"Size": fileHeader.Size, + "FileData": fileData, + "Date": time.Now().String()}).Error return err } -func (f *File) modifyFileOnDisc(fileHeader *multipart.FileHeader, createFile bool) error { - - //filesavepath := filepath.Join(foldername, filename) - var err error - - if createFile { - // Ensure folder with name foldername exists - err = os.MkdirAll(f.Path, os.ModePerm) - } else { - // test if file exists - _, err = os.Stat(f.Path) - } - if err != nil { - return err - } - - var open_options int - if createFile { - // create file it not exists, file MUST not exist - open_options = os.O_RDWR | os.O_CREATE | os.O_EXCL - } else { - open_options = os.O_RDWR - } - - fileTarget, err := os.OpenFile(f.Path, open_options, 0666) - if err != nil { - return err - } - defer fileTarget.Close() - - // Save file to target path - uploadedFile, err := fileHeader.Open() - if err != nil { - return err - } - defer uploadedFile.Close() - - var uploadContent = make([]byte, f.Size) - for { - - n, err := uploadedFile.Read(uploadContent) - if err != nil && err != io.EOF { - return err - } - - if n == 0 { - break - } - - _, err = fileTarget.Write(uploadContent[:n]) - if err != nil { - return err - } - - } - return err +func (f *File) delete() error { + return nil } -func (f *File) download(c *gin.Context) { +//func (f *File) modifyFileOnDisc(fileHeader *multipart.FileHeader, createFile bool) error { +// +// //filesavepath := filepath.Join(foldername, filename) +// var err error +// +// if createFile { +// // Ensure folder with name foldername exists +// err = os.MkdirAll(f.Path, os.ModePerm) +// } else { +// // test if file exists +// _, err = os.Stat(f.Path) +// } +// if err != nil { +// return err +// } +// +// var open_options int +// if createFile { +// // create file it not exists, file MUST not exist +// open_options = os.O_RDWR | os.O_CREATE | os.O_EXCL +// } else { +// open_options = os.O_RDWR +// } +// +// fileTarget, err := os.OpenFile(f.Path, open_options, 0666) +// if err != nil { +// return err +// } +// defer fileTarget.Close() +// +// // Save file to target path +// uploadedFile, err := fileHeader.Open() +// if err != nil { +// return err +// } +// defer uploadedFile.Close() +// +// var uploadContent = make([]byte, f.Size) +// for { +// +// n, err := uploadedFile.Read(uploadContent) +// if err != nil && err != io.EOF { +// return err +// } +// +// if n == 0 { +// break +// } +// +// _, err = fileTarget.Write(uploadContent[:n]) +// if err != nil { +// return err +// } +// +// } +// return err +//} - //Seems this headers needed for some browsers (for example without this headers Chrome will download files as txt) - c.Header("Content-Description", "File Transfer") - c.Header("Content-Transfer-Encoding", "binary") - c.Header("Content-Disposition", "attachment; filename="+f.Name) - //c.Header("Content-Type", contentType) - c.File(f.Path) -} - -func (f *File) addToModel(model simulationmodel.SimulationModel) error { - db := common.GetDB() - err := db.Model(&model).Association("Files").Append(f).Error - return err -} - -func (f *File) addToWidget(widget widget.Widget) error { - db := common.GetDB() - err := db.Model(&widget).Association("Files").Append(f).Error - return err -} - -func getFolderName(objectType string, objectID uint) string { - base_foldername := "files/" - - foldername := base_foldername + objectType + "_" + strconv.Itoa(int(objectID)) + "/" - return foldername -} +//func getFolderName(objectType string, objectID uint) string { +// base_foldername := "files/" +// +// foldername := base_foldername + objectType + "_" + strconv.Itoa(int(objectID)) + "/" +// return foldername +//} diff --git a/routes/file/fileMiddleware.go b/routes/file/fileMiddleware.go new file mode 100644 index 0000000..7d77992 --- /dev/null +++ b/routes/file/fileMiddleware.go @@ -0,0 +1,50 @@ +package file + +import ( + "fmt" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget" + "github.com/gin-gonic/gin" + "net/http" + "strconv" +) + +func checkPermissions(c *gin.Context, operation common.CRUD) (bool, File) { + + var f File + + err := common.ValidateRole(c, common.ModelFile, operation) + if err != nil { + c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") + return false, f + } + + fileID, err := strconv.Atoi(c.Param("fileID")) + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of fileID path parameter") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return false, f + } + + err = f.byID(uint(fileID)) + if common.ProvideErrorResponse(c, err) { + return false, f + } + + if f.SimulationModelID > 0 { + ok, _ := simulationmodel.CheckPermissions(c, operation, "body", int(f.SimulationModelID)) + if !ok { + return false, f + } + } else { + ok, _ := widget.CheckPermissions(c, operation, int(f.WidgetID)) + if !ok { + return false, f + } + } + + return true, f +} diff --git a/routes/file/file_test.go b/routes/file/file_test.go new file mode 100644 index 0000000..7448ab1 --- /dev/null +++ b/routes/file/file_test.go @@ -0,0 +1,217 @@ +package file + +import ( + "bytes" + "encoding/json" + "fmt" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/http/httptest" + "os" + "testing" +) + +var token string +var filecontent = "This is my testfile" +var filecontent_update = "This is my updated testfile with a dot at the end." +var filename = "testfile.txt" +var filename_update = "testfileupdate.txt" + +type credentials struct { + Username string `json:"username"` + Password string `json:"password"` +} + +var cred = credentials{ + Username: "User_A", + Password: "abc123", +} + +var msgOK = common.ResponseMsg{ + Message: "OK.", +} + +var FileA = common.FileResponse{ + Name: "File_A", + ID: 1, + WidgetID: 1, +} + +var FileB = common.FileResponse{ + Name: "File_B", + ID: 2, + WidgetID: 1, +} + +var myFiles = []common.FileResponse{ + FileA, + FileB, +} + +var msgFiles = common.ResponseMsgFiles{ + Files: myFiles, +} + +// Test /models endpoints +func TestSignalEndpoints(t *testing.T) { + + db := common.DummyInitDB() + defer db.Close() + common.DummyPopulateDB(db) + + // create a testfile in local folder + c1 := []byte(filecontent) + c2 := []byte(filecontent_update) + err := ioutil.WriteFile(filename, c1, 0644) + if err != nil { + panic(err) + } + err = ioutil.WriteFile(filename_update, c2, 0644) + if err != nil { + panic(err) + } + + router := gin.Default() + api := router.Group("/api") + + // All endpoints require authentication except when someone wants to + // login (POST /authenticate) + user.VisitorAuthenticate(api.Group("/authenticate")) + + api.Use(user.Authentication(true)) + + RegisterFileEndpoints(api.Group("/files")) + + credjson, err := json.Marshal(cred) + if err != nil { + panic(err) + } + + msgOKjson, err := json.Marshal(msgOK) + if err != nil { + panic(err) + } + + msgFilesjson, err := json.Marshal(msgFiles) + if err != nil { + panic(err) + } + + token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200) + + // test GET files + common.TestEndpoint(t, router, token, "/api/files?objectID=1&objectType=widget", "GET", nil, 200, string(msgFilesjson)) + + // test POST files + bodyBuf := &bytes.Buffer{} + bodyWriter := multipart.NewWriter(bodyBuf) + fileWriter, err := bodyWriter.CreateFormFile("file", "testuploadfile.txt") + if err != nil { + fmt.Println("error writing to buffer") + panic(err) + } + + // open file handle + fh, err := os.Open(filename) + if err != nil { + fmt.Println("error opening file") + panic(err) + } + defer fh.Close() + + // io copy + _, err = io.Copy(fileWriter, fh) + if err != nil { + fmt.Println("error on IO copy") + panic(err) + } + + contentType := bodyWriter.FormDataContentType() + bodyWriter.Close() + + w := httptest.NewRecorder() + req, err := http.NewRequest("POST", "/api/files?objectID=1&objectType=widget", bodyBuf) + req.Header.Add("Authorization", "Bearer "+token) + req.Header.Set("Content-Type", contentType) + if err != nil { + fmt.Println("error creating post request") + panic(err) + } + + router.ServeHTTP(w, req) + + assert.Equal(t, 200, w.Code) + fmt.Println(w.Body.String()) + assert.Equal(t, string(msgOKjson), w.Body.String()) + + // test GET files/:fileID + common.TestEndpoint(t, router, token, "/api/files/5", "GET", nil, 200, filecontent) + //common.TestEndpoint(t, router, token, "/api/files?objectID=1&objectType=widget", "GET", nil, 200, string(msgFilesjson)) + + // test PUT files/:fileID + bodyBuf_update := &bytes.Buffer{} + bodyWriter_update := multipart.NewWriter(bodyBuf_update) + fileWriter_update, err := bodyWriter_update.CreateFormFile("file", "testuploadfile.txt") + if err != nil { + fmt.Println("error writing to buffer") + panic(err) + } + + // open file handle + fh_update, err := os.Open(filename_update) + if err != nil { + fmt.Println("error opening file") + panic(err) + } + defer fh_update.Close() + + // io copy + _, err = io.Copy(fileWriter_update, fh_update) + if err != nil { + fmt.Println("error on IO copy") + panic(err) + } + + contentType_update := bodyWriter_update.FormDataContentType() + bodyWriter_update.Close() + w_update := httptest.NewRecorder() + req_update, err := http.NewRequest("PUT", "/api/files/5", bodyBuf_update) + req_update.Header.Add("Authorization", "Bearer "+token) + req_update.Header.Set("Content-Type", contentType_update) + if err != nil { + fmt.Println("error creating post request") + panic(err) + } + + router.ServeHTTP(w_update, req_update) + + assert.Equal(t, 200, w_update.Code) + fmt.Println(w_update.Body.String()) + assert.Equal(t, string(msgOKjson), w_update.Body.String()) + + common.TestEndpoint(t, router, token, "/api/files/5", "GET", nil, 200, filecontent_update) + + // test DELETE files/:fileID + //common.TestEndpoint(t, router, token, "/api/files/3", "DELETE", nil, 200, string(msgOKjson)) + //common.TestEndpoint(t, router, token, "/api/files?originID=1&originType=widget", "GET", nil, 200, string(msgFilesjson)) + + // TODO add testing for other return codes + + // clean up temporary file + err = os.Remove(filename) + if err != nil { + panic(err) + } + + err = os.Remove(filename_update) + if err != nil { + panic(err) + } + +} diff --git a/routes/widget/widgetEndpoints.go b/routes/widget/widgetEndpoints.go index a43e75f..3b16839 100644 --- a/routes/widget/widgetEndpoints.go +++ b/routes/widget/widgetEndpoints.go @@ -104,7 +104,7 @@ func addWidget(c *gin.Context) { // @Router /widgets/{widgetID} [put] func updateWidget(c *gin.Context) { - ok, w := CheckPermissions(c, common.Update) + ok, w := CheckPermissions(c, common.Update, -1) if !ok { return } @@ -141,7 +141,7 @@ func updateWidget(c *gin.Context) { // @Router /widgets/{widgetID} [get] func getWidget(c *gin.Context) { - ok, w := CheckPermissions(c, common.Read) + ok, w := CheckPermissions(c, common.Read, -1) if !ok { return } @@ -166,7 +166,7 @@ func getWidget(c *gin.Context) { // @Router /widgets/{widgetID} [delete] func deleteWidget(c *gin.Context) { - ok, w := CheckPermissions(c, common.Delete) + ok, w := CheckPermissions(c, common.Delete, -1) if !ok { return } diff --git a/routes/widget/widgetMiddleware.go b/routes/widget/widgetMiddleware.go index 85cb071..1bf85f5 100644 --- a/routes/widget/widgetMiddleware.go +++ b/routes/widget/widgetMiddleware.go @@ -11,7 +11,7 @@ import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization" ) -func CheckPermissions(c *gin.Context, operation common.CRUD) (bool, Widget) { +func CheckPermissions(c *gin.Context, operation common.CRUD, widgetIDBody int) (bool, Widget) { var w Widget @@ -21,13 +21,18 @@ func CheckPermissions(c *gin.Context, operation common.CRUD) (bool, Widget) { return false, w } - widgetID, err := strconv.Atoi(c.Param("widgetID")) - if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of widgetID path parameter") - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return false, w + var widgetID int + if widgetIDBody < 0 { + widgetID, err = strconv.Atoi(c.Param("widgetID")) + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of widgetID path parameter") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return false, w + } + } else { + widgetID = widgetIDBody } err = w.ByID(uint(widgetID))