mirror of
https://git.rwth-aachen.de/acs/public/villas/web-backend-go/
synced 2025-03-30 00:00:12 +01:00
working on file endpoints; testing to be completed, other tests to be checked
This commit is contained in:
parent
cde0ee01c2
commit
fc91ebc64c
11 changed files with 521 additions and 182 deletions
|
@ -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)
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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__,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
//}
|
||||
|
|
50
routes/file/fileMiddleware.go
Normal file
50
routes/file/fileMiddleware.go
Normal file
|
@ -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
|
||||
}
|
217
routes/file/file_test.go
Normal file
217
routes/file/file_test.go
Normal file
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Add table
Reference in a new issue