From 473abac8f4e30deb441223654dc2bc70eeba6310 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Thu, 16 May 2019 16:58:53 +0200 Subject: [PATCH 01/30] work on file endpoints (WIP, not working) --- common/dberrorhandling.go | 24 ++++++++++++ routes/file/fileEndpoints.go | 73 +++++++++++++++++++++++++++++------- routes/file/fileQueries.go | 34 +++++++++++++++++ 3 files changed, 118 insertions(+), 13 deletions(-) create mode 100644 common/dberrorhandling.go diff --git a/common/dberrorhandling.go b/common/dberrorhandling.go new file mode 100644 index 0000000..31c2f5c --- /dev/null +++ b/common/dberrorhandling.go @@ -0,0 +1,24 @@ +package common + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/jinzhu/gorm" +) + +func ProvideErrorResponse(c *gin.Context, err error) bool { + if err != nil { + if err == gorm.ErrRecordNotFound { + c.JSON(http.StatusNotFound, gin.H{ + "error": "No files found in DB", + }) + } else { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "Error on DB Query or transaction", + }) + } + return true // Error + } + return false // No error +} diff --git a/routes/file/fileEndpoints.go b/routes/file/fileEndpoints.go index afdaa41..65a1938 100644 --- a/routes/file/fileEndpoints.go +++ b/routes/file/fileEndpoints.go @@ -1,8 +1,14 @@ package file import ( - "github.com/gin-gonic/gin" + "strconv" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/json" + + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) func FilesRegister(r *gin.RouterGroup) { @@ -13,18 +19,40 @@ func FilesRegister(r *gin.RouterGroup) { r.DELETE("/:FileID", fileDeleteEp) } -func filesReadEp(c *gin.Context) { - allFiles, _, _ := FindAllFiles() - serializer := FilesSerializerNoAssoc{c, allFiles} - c.JSON(http.StatusOK, gin.H{ - "files": serializer.Response(), - }) +func filesReadEp(c *gin.Context) { + // Database query + allFiles, _, err := FindAllFiles() + + if common.ProvideErrorResponse(c, err) == false { + serializer := FilesSerializerNoAssoc{c, allFiles} + c.JSON(http.StatusOK, gin.H{ + "files": serializer.Response(), + }) + } + } func fileRegistrationEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) + var m map[string]interface{} + + decoder := json.NewDecoder(c.Request.Body) + defer c.Request.Body.Close() + + if err := decoder.Decode(&m); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "Bad request. Invalid body.", + }) + return; + } + + // Database query + err := AddFile(m) + + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } } func fileUpdateEp(c *gin.Context) { @@ -34,9 +62,28 @@ func fileUpdateEp(c *gin.Context) { } func fileReadEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) + var err error + var file common.File + fileID := c.Param("FileID") + desc := c.GetHeader("X-Request-FileDesc") + desc_b, _ := strconv.ParseBool(desc) + + userID := 1 // TODO obtain ID of user making the request + + //check if description of file or file itself shall be returned + if desc_b { + file, err = FindFile(userID, fileID) + if common.ProvideErrorResponse(c, err) == false { + serializer := FileSerializerNoAssoc{c, file} + c.JSON(http.StatusOK, gin.H{ + "file": serializer.Response(), + }) + } + + + } else { + //TODO: return file itself + } } func fileDeleteEp(c *gin.Context) { diff --git a/routes/file/fileQueries.go b/routes/file/fileQueries.go index 6c977ed..8efaab5 100644 --- a/routes/file/fileQueries.go +++ b/routes/file/fileQueries.go @@ -1,6 +1,11 @@ package file import ( + "fmt" + "strconv" + + _ "github.com/gin-gonic/gin" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) @@ -8,6 +13,10 @@ func FindAllFiles() ([]common.File, int, error) { db := common.GetDB() var files []common.File err := db.Find(&files).Error + if err != nil { + // print error message to screen + fmt.Println(fmt.Errorf("DB Error in FindAllFiles(): %q", err).Error()) + } return files, len(files), err } @@ -16,4 +25,29 @@ func FindUserFiles(user *common.User) ([]common.File, int, error) { var files []common.File err := db.Model(user).Related(&files, "Files").Error return files, len(files), err +} + +func FindFile(userID int, fileID string) ( common.File, error) { + //TODO Check here if user owns the file + var file common.File + db := common.GetDB() + fileID_i, _ := strconv.Atoi(fileID) + + err := db.First(&file, fileID_i).Error + + return file, err + +} + +func AddFile(m map[string]interface{}) error { + + // TODO deserialize m (JSON file object) to data struct File + + // TODO we need the user here as well to be able to create the association in the DB + + // TODO add deserialized File to DB + + var err error + return err + } \ No newline at end of file From 8d3f7c4fa0468edcc1bdd5ab8e965765c6ff6bc4 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Fri, 17 May 2019 15:32:58 +0200 Subject: [PATCH 02/30] remove Files from User, add relation to to Widget --- common/database.go | 8 ++++---- common/database_test.go | 20 +++++++++++++++----- common/datastructs.go | 12 +++++++++--- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/common/database.go b/common/database.go index 75e5937..a67e7a5 100644 --- a/common/database.go +++ b/common/database.go @@ -162,10 +162,6 @@ func DummyPopulateDB(test_db *gorm.DB) { checkErr(test_db.Model(&proj_A).Association("Simulation").Append(&simn_A).Error) checkErr(test_db.Model(&proj_B).Association("Simulation").Append(&simn_A).Error) - // User HM Files - checkErr(test_db.Model(&usr_A).Association("Files").Append(&file_A).Error) - checkErr(test_db.Model(&usr_A).Association("Files").Append(&file_B).Error) - // Simulation HM SimModel, SimModel BT Simulation checkErr(test_db.Model(&smo_A).Association("BelongsToSimulation").Append(&simn_A).Error) checkErr(test_db.Model(&smo_B).Association("BelongsToSimulation").Append(&simn_A).Error) @@ -194,6 +190,10 @@ func DummyPopulateDB(test_db *gorm.DB) { // Simulator BT SimModel checkErr(test_db.Model(&smo_A).Association("BelongsToSimulator").Append(&simr_A).Error) + // Widget HM Files + checkErr(test_db.Model(&widg_A).Association("Files").Append(&file_A).Error) + checkErr(test_db.Model(&widg_A).Association("Files").Append(&file_B).Error) + } // Erase tables and glose the testdb diff --git a/common/database_test.go b/common/database_test.go index 92d0296..2382df1 100644 --- a/common/database_test.go +++ b/common/database_test.go @@ -34,6 +34,7 @@ func TestDummyDBAssociations(t *testing.T) { var simn Simulation var usr User var vis Visualization + var widg Widget var sigs []Signal var smos []SimulationModel @@ -129,11 +130,7 @@ func TestDummyDBAssociations(t *testing.T) { "Expected to have %v Simulations. Has %v.", 2, len(simns)) } - a.NoError(db.Model(&usr).Related(&files, "Files").Error) - if len(files) != 2 { - a.Fail("User Associations", - "Expected to have %v Files. Has %v.", 2, len(files)) - } + // Visualization @@ -154,6 +151,19 @@ func TestDummyDBAssociations(t *testing.T) { "Expected to have %v Widget. Has %v.", 2, len(widgs)) } + + // Widget + a.NoError(db.Find(&widg, 1).Error, fM("Widget")) + a.EqualValues("Widget_A", vis.Name) + + + // Widget Association + a.NoError(db.Model(&widg).Related(&files, "Files").Error) + if len(files) != 2 { + a.Fail("Widget Associations", + "Expected to have %v Files. Has %v.", 2, len(files)) + } + // File a.NoError(db.Find(&file, 1).Error, fM("File")) diff --git a/common/datastructs.go b/common/datastructs.go index 24e004f..cc029ab 100644 --- a/common/datastructs.go +++ b/common/datastructs.go @@ -32,8 +32,11 @@ type File struct { //remove belongs to User relation //User User `gorm:"not null;association_autoupdate:false"` - UserID uint `gorm:"not null"` + //UserID uint `gorm:"not null"` + + //new in villasweb 2.0 SimulationModelID uint `gorm:""` + WidgetID uint `gorm:""` } type Project struct { @@ -84,7 +87,6 @@ type SimulationModel struct { //new in villasweb 2.0 (for CIM file of simulation model and other model file formats) Files []File `gorm:""` - } type User struct { @@ -97,7 +99,9 @@ type User struct { Projects []Project `gorm:"association_autoupdate:false"` Simulations []Simulation `gorm:"association_autoupdate:false"` - Files []File `gorm:""` + + //remove has many files relation + //Files []File `gorm:""` } type Visualization struct { @@ -139,4 +143,6 @@ type Widget struct { IsLocked bool `gorm:"default:false"` CustomProperties postgres.Jsonb // TODO: default value? VisualizationID uint + //new in villasweb 2.0 + Files []File `gorm:""` } From 4dfa4fd5f4f343897b5060ad620a56f53a867f7a Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Fri, 17 May 2019 15:40:58 +0200 Subject: [PATCH 03/30] fix error in db test --- common/database_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/database_test.go b/common/database_test.go index 2382df1..4a1c284 100644 --- a/common/database_test.go +++ b/common/database_test.go @@ -154,7 +154,7 @@ func TestDummyDBAssociations(t *testing.T) { // Widget a.NoError(db.Find(&widg, 1).Error, fM("Widget")) - a.EqualValues("Widget_A", vis.Name) + a.EqualValues("Widget_A", widg.Name) // Widget Association From 75f8ef280d0f89a554b21240fc51a1413ac98c0a Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Fri, 17 May 2019 16:27:59 +0200 Subject: [PATCH 04/30] Add file upload mechanism --- common/dberrorhandling.go | 6 +- routes/file/fileEndpoints.go | 25 ------ routes/file/fileQueries.go | 13 --- routes/simulation/simulationEndpoints.go | 69 +++++++++++++++ routes/simulation/simulationQueries.go | 104 +++++++++++++++++++++++ 5 files changed, 177 insertions(+), 40 deletions(-) diff --git a/common/dberrorhandling.go b/common/dberrorhandling.go index 31c2f5c..12112aa 100644 --- a/common/dberrorhandling.go +++ b/common/dberrorhandling.go @@ -10,12 +10,14 @@ import ( func ProvideErrorResponse(c *gin.Context, err error) bool { if err != nil { if err == gorm.ErrRecordNotFound { + errormsg := "Record not Found in DB: " + err.Error() c.JSON(http.StatusNotFound, gin.H{ - "error": "No files found in DB", + "error": errormsg, }) } else { + errormsg := "Error on DB Query or transaction: " + err.Error() c.JSON(http.StatusInternalServerError, gin.H{ - "error": "Error on DB Query or transaction", + "error": errormsg, }) } return true // Error diff --git a/routes/file/fileEndpoints.go b/routes/file/fileEndpoints.go index 65a1938..fbd020e 100644 --- a/routes/file/fileEndpoints.go +++ b/routes/file/fileEndpoints.go @@ -4,16 +4,12 @@ import ( "strconv" "net/http" - "github.com/gin-gonic/gin" - "github.com/gin-gonic/gin/json" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) func FilesRegister(r *gin.RouterGroup) { r.GET("/", filesReadEp) - r.POST("/", fileRegistrationEp) // NEW in API r.PUT("/:FileID", fileUpdateEp) // NEW in API r.GET("/:FileID", fileReadEp) r.DELETE("/:FileID", fileDeleteEp) @@ -32,28 +28,7 @@ func filesReadEp(c *gin.Context) { } -func fileRegistrationEp(c *gin.Context) { - var m map[string]interface{} - decoder := json.NewDecoder(c.Request.Body) - defer c.Request.Body.Close() - - if err := decoder.Decode(&m); err != nil { - c.JSON(http.StatusBadRequest, gin.H{ - "error": "Bad request. Invalid body.", - }) - return; - } - - // Database query - err := AddFile(m) - - if common.ProvideErrorResponse(c, err) == false { - c.JSON(http.StatusOK, gin.H{ - "message": "OK.", - }) - } -} func fileUpdateEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ diff --git a/routes/file/fileQueries.go b/routes/file/fileQueries.go index 8efaab5..96e8760 100644 --- a/routes/file/fileQueries.go +++ b/routes/file/fileQueries.go @@ -37,17 +37,4 @@ func FindFile(userID int, fileID string) ( common.File, error) { return file, err -} - -func AddFile(m map[string]interface{}) error { - - // TODO deserialize m (JSON file object) to data struct File - - // TODO we need the user here as well to be able to create the association in the DB - - // TODO add deserialized File to DB - - var err error - return err - } \ No newline at end of file diff --git a/routes/simulation/simulationEndpoints.go b/routes/simulation/simulationEndpoints.go index 00a6c73..e49aae8 100644 --- a/routes/simulation/simulationEndpoints.go +++ b/routes/simulation/simulationEndpoints.go @@ -2,7 +2,12 @@ package simulation import ( "github.com/gin-gonic/gin" + "fmt" "net/http" + "path/filepath" + "strconv" + + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) func SimulationsRegister(r *gin.RouterGroup) { @@ -11,6 +16,7 @@ func SimulationsRegister(r *gin.RouterGroup) { r.PUT("/:SimulationID", simulationUpdateEp) r.GET("/:SimulationID", simulationReadEp) r.DELETE("/:SimulationID", simulationDeleteEp) + r.POST ("/:SimulationID/models/:SimulationModelID/file", fileRegistrationEp) // NEW } func simulationsReadEp(c *gin.Context) { @@ -45,3 +51,66 @@ func simulationDeleteEp(c *gin.Context) { }) } +func fileRegistrationEp(c *gin.Context) { + + // TODO Check if file upload is ok for this user or simulation (user or simulation exists) + var widgetID_s = c.Param("WidgetID") + var widgetID_i int + var simulationmodelID_s = c.Param("SimulationModelID") + var simulationmodelID_i int + + if widgetID_s != "" { + widgetID_i, _ = strconv.Atoi(widgetID_s) + } else { + widgetID_i = -1 + } + + if simulationmodelID_s != "" { + simulationmodelID_i, _ = strconv.Atoi(simulationmodelID_s) + } else { + simulationmodelID_i = -1 + } + + if simulationmodelID_i == -1 && widgetID_i == -1 { + errormsg := fmt.Sprintf("Bad request. Did not provide simulation model ID or widget ID for file") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return; + } + + // Extract file from POST request form + file, 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; + } + + // Obtain properties of file + filetype := file.Header.Get("Content-Type") // TODO make sure this is properly set in file header + filename := filepath.Base(file.Filename) + foldername := "files/testfolder" //TODO replace this placeholder with systematic foldername (e.g. simulation ID) + size := file.Size + + // Save file to local disc (NOT DB!) + err = SaveFile(file, filename, foldername, uint(size)) + if err != nil { + errormsg := fmt.Sprintf("Internal Server Error. Error saving file: %s", err.Error()) + c.JSON(http.StatusInternalServerError, gin.H{ + "error": errormsg, + }) + return + } + + // Add File object with parameters to DB + err = AddFile(filename, foldername, filetype, uint(size), widgetID_i, simulationmodelID_i ) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } + +} diff --git a/routes/simulation/simulationQueries.go b/routes/simulation/simulationQueries.go index 58b88ce..2b7ecf7 100644 --- a/routes/simulation/simulationQueries.go +++ b/routes/simulation/simulationQueries.go @@ -1,6 +1,11 @@ package simulation import ( + "io" + "mime/multipart" + "os" + "path/filepath" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) @@ -17,3 +22,102 @@ func FindUserSimulations(user *common.User) ([]common.Simulation, int, error) { err := db.Model(user).Related(&simulations, "Simulations").Error return simulations, len(simulations), err } + + +func AddFile(filename string, foldername string, filetype string, size uint, widgetID int, simulationmodelID int ) error { + + filesavepath := filepath.Join(foldername, filename) + + // get last modify time of target file + fileinfo, err := os.Stat(filesavepath) + if err != nil { + return err + } + modTime := fileinfo.ModTime() + + // Create file object for Database + var fileObj common.File + fileObj.Size = uint(size) + fileObj.Type = filetype + fileObj.Path = filesavepath + fileObj.Date = modTime + fileObj.Name = filename + fileObj.ImageHeight = 0 + fileObj.ImageWidth = 0 + + // Check if file shall be associated with Widget or Simulation Model + db := common.GetDB() + if widgetID != -1 { + // associate to Widget + var widget common.Widget + err := db.First(&widget, widgetID).Error + + if err != nil { + return err + } + err = db.Model(&widget).Association("Files").Append(&fileObj).Error + } + if simulationmodelID != -1 { + // associate to Simulation Model + var model common.SimulationModel + err := db.First(&model, simulationmodelID).Error + if err != nil { + return err + } + err = db.Model(&model).Association("Files").Append(&fileObj).Error + } + + return err +} + +func SaveFile(file *multipart.FileHeader, filename string, foldername string, size uint, ) error { + + filesavepath := filepath.Join(foldername, filename) + + // Ensure folder with name foldername exists + err := os.MkdirAll(foldername, os.ModePerm) + if err != nil { + // TODO error handling + return err + } + + fileTarget , errcreate := os.OpenFile(filesavepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) + if errcreate != nil { + // TODO error handling: File could not be created + return errcreate + } + defer fileTarget.Close() + + + // Save file to target path + uploadedFile, erropen := file.Open() + if erropen != nil { + // TODO error handling + return erropen + } + defer uploadedFile.Close() + + var uploadContent = make([]byte, size) + for { + + n, err := uploadedFile.Read(uploadContent) + if err != nil && err != io.EOF { + // TODO error handling + return err + } + + if n == 0 { + break + } + + _, err = fileTarget.Write(uploadContent[:n]) + if err != nil { + // TODO error handling + return err + } + + } + + return err + +} \ No newline at end of file From 17d88f4c6789a4e3def54bbb02c912e57178c690 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 20 May 2019 16:17:54 +0200 Subject: [PATCH 05/30] Work on endpoints involving file management, To be tested, WIP --- routes/file/fileQueries.go | 290 +++++++++++++++++- routes/simulation/simulationEndpoints.go | 71 +---- routes/simulation/simulationQueries.go | 104 ------- .../simulationmodelEnpoints.go | 108 ++++++- 4 files changed, 394 insertions(+), 179 deletions(-) diff --git a/routes/file/fileQueries.go b/routes/file/fileQueries.go index 96e8760..92219b0 100644 --- a/routes/file/fileQueries.go +++ b/routes/file/fileQueries.go @@ -2,8 +2,14 @@ package file import ( "fmt" + "io" + "mime/multipart" + "net/http" + "os" + "path/filepath" "strconv" + "github.com/gin-gonic/gin" _ "github.com/gin-gonic/gin" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" @@ -28,7 +34,6 @@ func FindUserFiles(user *common.User) ([]common.File, int, error) { } func FindFile(userID int, fileID string) ( common.File, error) { - //TODO Check here if user owns the file var file common.File db := common.GetDB() fileID_i, _ := strconv.Atoi(fileID) @@ -37,4 +42,287 @@ func FindFile(userID int, fileID string) ( common.File, error) { return file, err +} + +func FindFileByPath(path string) (common.File, error) { + var file common.File + db := common.GetDB() + err := db.Where("Path = ?", path).Find(file).Error + + return file, err +} + +func RegisterFile(c *gin.Context, widgetID int, simulationmodelID int, simulationID int){ + + // 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; + } + + // Obtain properties of file + filetype := file_header.Header.Get("Content-Type") // TODO make sure this is properly set in file header + filename := filepath.Base(file_header.Filename) + foldername := getFolderName(simulationID, simulationmodelID, widgetID) + size := file_header.Size + + // Save file to local disc (NOT DB!) + err = modifyFileOnDisc(file_header, filename, foldername, uint(size), true) + if err != nil { + errormsg := fmt.Sprintf("Internal Server Error. Error saving file: %s", err.Error()) + c.JSON(http.StatusInternalServerError, gin.H{ + "error": errormsg, + }) + return + } + + // Add File object with parameters to DB + saveFileInDB(c, filename, foldername, filetype, uint(size), widgetID, simulationmodelID, true) + +} + +func UpdateFile(c *gin.Context, widgetID int, simulationmodelID int, simulationID int){ + + // 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; + } + + filename := filepath.Base(file_header.Filename) + filetype := file_header.Header.Get("Content-Type") // TODO make sure this is properly set in file header + size := file_header.Size + foldername := getFolderName(simulationID, simulationmodelID, widgetID) + + err = modifyFileOnDisc(file_header, filename, foldername, uint(size), false) + if err != nil { + errormsg := fmt.Sprintf("Internal Server Error. Error saving file: %s", err.Error()) + c.JSON(http.StatusInternalServerError, gin.H{ + "error": errormsg, + }) + return + } + + saveFileInDB(c, filename, foldername, filetype, uint(size), widgetID, simulationmodelID, false) +} + +func ReadFile(c *gin.Context, widgetID int, simulationmodelID int, simulationID int){ + + contentType := c.GetHeader("Content-Type") + + db := common.GetDB() + var fileInDB common.File + if widgetID != -1 { + // get associated Widget + var wdgt common.Widget + err := db.First(&wdgt, simulationmodelID).Error + if common.ProvideErrorResponse(c, err) { + return + } + err = db.Model(&wdgt).Related(&fileInDB).Where("Type = ?", contentType).Error + if common.ProvideErrorResponse(c, err) { + return + } + + } else if simulationmodelID != -1 { + + // get associated Simulation Model + var model common.SimulationModel + err := db.First(&model, simulationmodelID).Error + if common.ProvideErrorResponse(c, err) { + return + } + err = db.Model(&model).Related(&fileInDB).Where("Type = ?", contentType).Error + if common.ProvideErrorResponse(c, err) { + return + } + } + + //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="+fileInDB.Name ) + c.Header("Content-Type", contentType) + c.File(fileInDB.Path) + + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) +} + +func DeleteFile(c *gin.Context, widgetID int, simulationmodelID int, simulationID int){ + // TODO +} + + +func saveFileInDB(c *gin.Context, filename string, foldername string, filetype string, size uint, widgetID int, simulationmodelID int, createObj bool) { + + filesavepath := filepath.Join(foldername, filename) + + // get last modify time of target file + fileinfo, err := os.Stat(filesavepath) + if err != nil { + errormsg := fmt.Sprintf("Internal Server Error. Error stat on file: %s", err.Error()) + c.JSON(http.StatusInternalServerError, gin.H{ + "error": errormsg, + }) + return + } + modTime := fileinfo.ModTime() + + // Create file object for Database + var fileObj common.File + fileObj.Size = uint(size) + fileObj.Type = filetype + fileObj.Path = filesavepath + fileObj.Date = modTime + fileObj.Name = filename + fileObj.ImageHeight = 0 + fileObj.ImageWidth = 0 + + // Check if file shall be associated with Widget or Simulation Model + db := common.GetDB() + if widgetID != -1 { + + if createObj { + // associate to Widget + var wdgt common.Widget + err := db.First(&wdgt, widgetID).Error + if common.ProvideErrorResponse(c, err) { + return + } + err = db.Model(&wdgt).Association("Files").Append(&fileObj).Error + } else { + // update file obj in DB + fileInDB, err := FindFileByPath(filesavepath) + if common.ProvideErrorResponse(c, err){ + return + } + + err = db.Model(&fileInDB).Where("Path = ?", filesavepath).Updates(map[string]interface{}{"Size": fileObj.Size, "Date": fileObj.Date, "ImageHeight": fileObj.ImageHeight, "ImageWidth": fileObj.ImageWidth}).Error + + } + + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + "fileID": fileObj.ID, + }) + return + } + + } + if simulationmodelID != -1 { + + if createObj { + // associate to Simulation Model + db := common.GetDB() + var model common.SimulationModel + err := db.First(&model, simulationmodelID).Error + if common.ProvideErrorResponse(c, err) { + return + } + err = db.Model(&model).Association("Files").Append(&fileObj).Error + } else { + // update file obj in DB + fileInDB, err := FindFileByPath(filesavepath) + if common.ProvideErrorResponse(c, err){ + return + } + + err = db.Model(&fileInDB).Where("Path = ?", filesavepath).Updates(map[string]interface{}{"Size": fileObj.Size, "Date": fileObj.Date, "ImageHeight": fileObj.ImageHeight, "ImageWidth": fileObj.ImageWidth}).Error + } + + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + "fileID": fileObj.ID, + }) + return + } + } +} + +func modifyFileOnDisc(file_header *multipart.FileHeader, filename string, foldername string, size uint, createFile bool) error { + + filesavepath := filepath.Join(foldername, filename) + var err error + + if createFile { + // Ensure folder with name foldername exists + err = os.MkdirAll(foldername, os.ModePerm) + } else { + // test if file exists + _, err = os.Stat(filesavepath) + } + 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(filesavepath, open_options, 0666) + if err != nil { + return err + } + defer fileTarget.Close() + + // Save file to target path + uploadedFile, err := file_header.Open() + if err != nil { + return err + } + defer uploadedFile.Close() + + var uploadContent = make([]byte, 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 getFolderName(simulationID int, simulationmodelID int, widgetID int) string { + base_foldername := "files/" + elementname := "" + elementid := 0 + if simulationmodelID == -1{ + elementname = "/widget_" + elementid = widgetID + } else { + elementname = "/simulationmodel_" + elementid = simulationmodelID + } + + + foldername := base_foldername + "simulation_"+ string(simulationID) + elementname + string(elementid) + "/" + return foldername } \ No newline at end of file diff --git a/routes/simulation/simulationEndpoints.go b/routes/simulation/simulationEndpoints.go index e49aae8..fc05749 100644 --- a/routes/simulation/simulationEndpoints.go +++ b/routes/simulation/simulationEndpoints.go @@ -1,22 +1,21 @@ package simulation import ( - "github.com/gin-gonic/gin" "fmt" "net/http" - "path/filepath" "strconv" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "github.com/gin-gonic/gin" ) func SimulationsRegister(r *gin.RouterGroup) { + + //simulations r.GET("/", simulationsReadEp) r.POST("/", simulationRegistrationEp) r.PUT("/:SimulationID", simulationUpdateEp) r.GET("/:SimulationID", simulationReadEp) r.DELETE("/:SimulationID", simulationDeleteEp) - r.POST ("/:SimulationID/models/:SimulationModelID/file", fileRegistrationEp) // NEW } func simulationsReadEp(c *gin.Context) { @@ -51,66 +50,18 @@ func simulationDeleteEp(c *gin.Context) { }) } -func fileRegistrationEp(c *gin.Context) { +func GetSimulationID(c *gin.Context) (int, error) { - // TODO Check if file upload is ok for this user or simulation (user or simulation exists) - var widgetID_s = c.Param("WidgetID") - var widgetID_i int - var simulationmodelID_s = c.Param("SimulationModelID") - var simulationmodelID_i int + simulationID, err := strconv.Atoi(c.Param("SimulationID")) - if widgetID_s != "" { - widgetID_i, _ = strconv.Atoi(widgetID_s) - } else { - widgetID_i = -1 - } - - if simulationmodelID_s != "" { - simulationmodelID_i, _ = strconv.Atoi(simulationmodelID_s) - } else { - simulationmodelID_i = -1 - } - - if simulationmodelID_i == -1 && widgetID_i == -1 { - errormsg := fmt.Sprintf("Bad request. Did not provide simulation model ID or widget ID for file") + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulation ID") c.JSON(http.StatusBadRequest, gin.H{ "error": errormsg, }) - return; + return -1, err + } else { + return simulationID, err + } - - // Extract file from POST request form - file, 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; - } - - // Obtain properties of file - filetype := file.Header.Get("Content-Type") // TODO make sure this is properly set in file header - filename := filepath.Base(file.Filename) - foldername := "files/testfolder" //TODO replace this placeholder with systematic foldername (e.g. simulation ID) - size := file.Size - - // Save file to local disc (NOT DB!) - err = SaveFile(file, filename, foldername, uint(size)) - if err != nil { - errormsg := fmt.Sprintf("Internal Server Error. Error saving file: %s", err.Error()) - c.JSON(http.StatusInternalServerError, gin.H{ - "error": errormsg, - }) - return - } - - // Add File object with parameters to DB - err = AddFile(filename, foldername, filetype, uint(size), widgetID_i, simulationmodelID_i ) - if common.ProvideErrorResponse(c, err) == false { - c.JSON(http.StatusOK, gin.H{ - "message": "OK.", - }) - } - } diff --git a/routes/simulation/simulationQueries.go b/routes/simulation/simulationQueries.go index 2b7ecf7..58b88ce 100644 --- a/routes/simulation/simulationQueries.go +++ b/routes/simulation/simulationQueries.go @@ -1,11 +1,6 @@ package simulation import ( - "io" - "mime/multipart" - "os" - "path/filepath" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) @@ -22,102 +17,3 @@ func FindUserSimulations(user *common.User) ([]common.Simulation, int, error) { err := db.Model(user).Related(&simulations, "Simulations").Error return simulations, len(simulations), err } - - -func AddFile(filename string, foldername string, filetype string, size uint, widgetID int, simulationmodelID int ) error { - - filesavepath := filepath.Join(foldername, filename) - - // get last modify time of target file - fileinfo, err := os.Stat(filesavepath) - if err != nil { - return err - } - modTime := fileinfo.ModTime() - - // Create file object for Database - var fileObj common.File - fileObj.Size = uint(size) - fileObj.Type = filetype - fileObj.Path = filesavepath - fileObj.Date = modTime - fileObj.Name = filename - fileObj.ImageHeight = 0 - fileObj.ImageWidth = 0 - - // Check if file shall be associated with Widget or Simulation Model - db := common.GetDB() - if widgetID != -1 { - // associate to Widget - var widget common.Widget - err := db.First(&widget, widgetID).Error - - if err != nil { - return err - } - err = db.Model(&widget).Association("Files").Append(&fileObj).Error - } - if simulationmodelID != -1 { - // associate to Simulation Model - var model common.SimulationModel - err := db.First(&model, simulationmodelID).Error - if err != nil { - return err - } - err = db.Model(&model).Association("Files").Append(&fileObj).Error - } - - return err -} - -func SaveFile(file *multipart.FileHeader, filename string, foldername string, size uint, ) error { - - filesavepath := filepath.Join(foldername, filename) - - // Ensure folder with name foldername exists - err := os.MkdirAll(foldername, os.ModePerm) - if err != nil { - // TODO error handling - return err - } - - fileTarget , errcreate := os.OpenFile(filesavepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) - if errcreate != nil { - // TODO error handling: File could not be created - return errcreate - } - defer fileTarget.Close() - - - // Save file to target path - uploadedFile, erropen := file.Open() - if erropen != nil { - // TODO error handling - return erropen - } - defer uploadedFile.Close() - - var uploadContent = make([]byte, size) - for { - - n, err := uploadedFile.Read(uploadContent) - if err != nil && err != io.EOF { - // TODO error handling - return err - } - - if n == 0 { - break - } - - _, err = fileTarget.Write(uploadContent[:n]) - if err != nil { - // TODO error handling - return err - } - - } - - return err - -} \ No newline at end of file diff --git a/routes/simulationmodel/simulationmodelEnpoints.go b/routes/simulationmodel/simulationmodelEnpoints.go index 7f41593..868de1e 100644 --- a/routes/simulationmodel/simulationmodelEnpoints.go +++ b/routes/simulationmodel/simulationmodelEnpoints.go @@ -1,18 +1,31 @@ package simulationmodel import ( - "github.com/gin-gonic/gin" + "fmt" "net/http" + "strconv" + + "github.com/gin-gonic/gin" + + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" ) func SimulationModelsRegister(r *gin.RouterGroup) { - r.GET("/", simulationmodelsReadEp) - r.POST("/", simulationmodelRegistrationEp) - r.PUT("/:SimulationModelID", simulationmodelUpdateEp) - r.GET("/:SimulationModelID", simulationmodelReadEp) - r.DELETE("/:SimulationModelID", simulationmodelDeleteEp) - r.GET("/:SimulationModelID/file", simulationmodelReadFileEp) // NEW in API - r.PUT("/:SimulationModelID/file", simulationmodelUpdateFileEp) // NEW in API + r.GET("/:SimulationID/models/", simulationmodelsReadEp) + r.POST("/:SimulationID/models/", simulationmodelRegistrationEp) + + r.PUT("/:SimulationID/models/:SimulationModelID", simulationmodelUpdateEp) + r.GET("/:SimulationID/models/:SimulationModelID", simulationmodelReadEp) + r.DELETE("/:SimulationID/models/:SimulationModelID", simulationmodelDeleteEp) + + // Files + r.POST ("/:SimulationID/models/:SimulationModelID/file", simulationmodelRegisterFileEp) // NEW in API + r.GET("/:SimulationID/models/:SimulationModelID/file", simulationmodelReadFileEp) // NEW in API + r.PUT("/:SimulationID/models/:SimulationModelID/file", simulationmodelUpdateFileEp) // NEW in API + r.DELETE("/:SimulationID/models/:SimulationModelID/file", simulationmodelDeleteFileEp) // NEW in API + + } func simulationmodelsReadEp(c *gin.Context) { @@ -47,14 +60,81 @@ func simulationmodelDeleteEp(c *gin.Context) { }) } + +func simulationmodelRegisterFileEp(c *gin.Context) { + + simulationID, simulationmodelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Save file locally and register file in DB, HTTP response is set by this method + file.RegisterFile(c,-1, simulationmodelID, simulationID) + +} + func simulationmodelReadFileEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) + + simulationID, simulationmodelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Read file from disk and return in HTTP response, no change to DB + file.ReadFile(c, -1, simulationmodelID, simulationID) } func simulationmodelUpdateFileEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) + + simulationID, simulationmodelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Update file locally and update file entry in DB, HTTP response is set by this method + file.UpdateFile(c,-1, simulationmodelID, simulationID) +} + +func simulationmodelDeleteFileEp(c *gin.Context) { + + simulationID, simulationmodelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Delete file from disk and remove entry from DB, HTTP response is set by this method + file.DeleteFile(c, -1, simulationmodelID, simulationID) + + +} + + +func GetSimulationmodelID(c *gin.Context) (int, error) { + + simulationmodelID, err := strconv.Atoi(c.Param("SimulationModelID")) + + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulation model ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return -1, err + } else { + return simulationmodelID, err + + } +} + +func getRequestParams(c *gin.Context) (int, int, error){ + simulationID, err := simulation.GetSimulationID(c) + if err != nil{ + return -1, -1, err + } + + simulationmodelID, err := GetSimulationmodelID(c) + if err != nil{ + return -1, -1, err + } + + return simulationID, simulationmodelID, err } \ No newline at end of file From a78bf892f5e8755ca2930c9dec663141efee19f6 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 21 May 2019 09:14:44 +0200 Subject: [PATCH 06/30] Renaming: - SimulationModel to Model - Signal to Sample --- common/database.go | 54 +++---- common/database_test.go | 36 ++--- common/datastructs.go | 23 +-- doc/api/api.yaml | 94 +++++------ routes/file/fileQueries.go | 40 ++--- routes/model/modelEnpoints.go | 148 ++++++++++++++++++ routes/model/modelQueries.go | 12 ++ .../modelSerializer.go} | 24 +-- routes/sample/sampleEndpoints.go | 4 + routes/sample/sampleQueries.go | 3 + routes/sample/sampleSerializer.go | 3 + routes/signal/signalEndpoints.go | 4 - routes/signal/signalQueries.go | 3 - routes/signal/signalSerializer.go | 3 - .../simulationmodelEnpoints.go | 140 ----------------- .../simulationmodel/simulationmodelQueries.go | 12 -- start.go | 16 +- 17 files changed, 313 insertions(+), 306 deletions(-) create mode 100644 routes/model/modelEnpoints.go create mode 100644 routes/model/modelQueries.go rename routes/{simulationmodel/simulationmodelSerializer.go => model/modelSerializer.go} (57%) create mode 100644 routes/sample/sampleEndpoints.go create mode 100644 routes/sample/sampleQueries.go create mode 100644 routes/sample/sampleSerializer.go delete mode 100644 routes/signal/signalEndpoints.go delete mode 100644 routes/signal/signalQueries.go delete mode 100644 routes/signal/signalSerializer.go delete mode 100644 routes/simulationmodel/simulationmodelEnpoints.go delete mode 100644 routes/simulationmodel/simulationmodelQueries.go diff --git a/common/database.go b/common/database.go index a67e7a5..1883d62 100644 --- a/common/database.go +++ b/common/database.go @@ -53,8 +53,8 @@ func VerifyConnection(db *gorm.DB) error { // to the Dummy*() where it is called func DropTables(db *gorm.DB) { db.DropTableIfExists(&Simulator{}) - db.DropTableIfExists(&Signal{}) - db.DropTableIfExists(&SimulationModel{}) + db.DropTableIfExists(&Sample{}) + db.DropTableIfExists(&Model{}) db.DropTableIfExists(&File{}) db.DropTableIfExists(&Project{}) db.DropTableIfExists(&Simulation{}) @@ -66,8 +66,8 @@ func DropTables(db *gorm.DB) { // AutoMigrate the models func MigrateModels(db *gorm.DB) { db.AutoMigrate(&Simulator{}) - db.AutoMigrate(&Signal{}) - db.AutoMigrate(&SimulationModel{}) + db.AutoMigrate(&Sample{}) + db.AutoMigrate(&Model{}) db.AutoMigrate(&File{}) db.AutoMigrate(&Project{}) db.AutoMigrate(&Simulation{}) @@ -102,19 +102,19 @@ func DummyPopulateDB(test_db *gorm.DB) { checkErr(test_db.Create(&simr_A).Error) checkErr(test_db.Create(&simr_B).Error) - outSig_A := Signal{Name: "outSignal_A"} - outSig_B := Signal{Name: "outSignal_B"} - inSig_A := Signal{Name: "inSignal_A"} - inSig_B := Signal{Name: "inSignal_B"} - checkErr(test_db.Create(&outSig_A).Error) - checkErr(test_db.Create(&outSig_B).Error) - checkErr(test_db.Create(&inSig_A).Error) - checkErr(test_db.Create(&inSig_B).Error) + outSmp_A := Sample{Name: "outSample_A"} + outSmp_B := Sample{Name: "outSample_B"} + inSmp_A := Sample{Name: "inSample_A"} + inSmp_B := Sample{Name: "inSample_B"} + checkErr(test_db.Create(&outSmp_A).Error) + checkErr(test_db.Create(&outSmp_B).Error) + checkErr(test_db.Create(&inSmp_A).Error) + checkErr(test_db.Create(&inSmp_B).Error) - smo_A := SimulationModel{Name: "SimModel_A"} - smo_B := SimulationModel{Name: "SimModel_B"} - checkErr(test_db.Create(&smo_A).Error) - checkErr(test_db.Create(&smo_B).Error) + mo_A := Model{Name: "Model_A"} + mo_B := Model{Name: "Model_B"} + checkErr(test_db.Create(&mo_A).Error) + checkErr(test_db.Create(&mo_B).Error) file_A := File{Name: "File_A"} file_B := File{Name: "File_B"} @@ -163,8 +163,8 @@ func DummyPopulateDB(test_db *gorm.DB) { checkErr(test_db.Model(&proj_B).Association("Simulation").Append(&simn_A).Error) // Simulation HM SimModel, SimModel BT Simulation - checkErr(test_db.Model(&smo_A).Association("BelongsToSimulation").Append(&simn_A).Error) - checkErr(test_db.Model(&smo_B).Association("BelongsToSimulation").Append(&simn_A).Error) + checkErr(test_db.Model(&mo_A).Association("BelongsToSimulation").Append(&simn_A).Error) + checkErr(test_db.Model(&mo_B).Association("BelongsToSimulation").Append(&simn_A).Error) // User HM Simulation, Simulation BT User checkErr(test_db.Model(&simn_A).Association("User").Append(&usr_A).Error) @@ -174,21 +174,21 @@ func DummyPopulateDB(test_db *gorm.DB) { checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_A).Error) checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_B).Error) - // SimModel HM Signal - checkErr(test_db.Model(&smo_A).Association("InputMapping").Append(&inSig_A).Error) - checkErr(test_db.Model(&smo_A).Association("InputMapping").Append(&inSig_B).Error) - checkErr(test_db.Model(&smo_A).Association("OutputMapping").Append(&outSig_A).Error) - checkErr(test_db.Model(&smo_A).Association("OutputMapping").Append(&outSig_B).Error) + // SimModel HM Samples + checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSmp_A).Error) + checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSmp_B).Error) + checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSmp_A).Error) + checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSmp_B).Error) - //SimulationModel HM Files - checkErr(test_db.Model(&smo_A).Association("Files").Append(&file_A).Error) - checkErr(test_db.Model(&smo_A).Association("Files").Append(&file_B).Error) + // Model 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) // Visualization BT User checkErr(test_db.Model(&vis_A).Association("User").Append(&usr_A).Error) // Simulator BT SimModel - checkErr(test_db.Model(&smo_A).Association("BelongsToSimulator").Append(&simr_A).Error) + checkErr(test_db.Model(&mo_A).Association("BelongsToSimulator").Append(&simr_A).Error) // Widget HM Files checkErr(test_db.Model(&widg_A).Association("Files").Append(&file_A).Error) diff --git a/common/database_test.go b/common/database_test.go index 4a1c284..949086e 100644 --- a/common/database_test.go +++ b/common/database_test.go @@ -28,7 +28,7 @@ func TestDummyDBAssociations(t *testing.T) { // Variables for tests var simr Simulator - var smo SimulationModel + var mo Model var file File var proj Project var simn Simulation @@ -36,8 +36,8 @@ func TestDummyDBAssociations(t *testing.T) { var vis Visualization var widg Widget - var sigs []Signal - var smos []SimulationModel + var smps []Sample + var mos []Model var files []File var files_sm []File var projs []Project @@ -45,28 +45,28 @@ func TestDummyDBAssociations(t *testing.T) { var viss []Visualization var widgs []Widget - // Simulation Model + // Model - a.NoError(db.Find(&smo, 1).Error, fM("SimulationModel")) - a.EqualValues("SimModel_A", smo.Name) + a.NoError(db.Find(&mo, 1).Error, fM("Model")) + a.EqualValues("SimModel_A", mo.Name) - // Simulation Model Associations + // Model Associations - a.NoError(db.Model(&smo).Association("BelongsToSimulation").Find(&simn).Error) + a.NoError(db.Model(&mo).Association("BelongsToSimulation").Find(&simn).Error) a.EqualValues("Simulation_A", simn.Name, "Expected Simulation_A") - a.NoError(db.Model(&smo).Association("BelongsToSimulator").Find(&simr).Error) + a.NoError(db.Model(&mo).Association("BelongsToSimulator").Find(&simr).Error) a.EqualValues("Host_A", simr.Host, "Expected Host_A") - a.NoError(db.Model(&smo).Related(&sigs, "OutputMapping").Error) - if len(sigs) != 4 { - a.Fail("Simulation Model Associations", - "Expected to have %v Output AND Input Signals. Has %v.", 4, len(sigs)) + a.NoError(db.Model(&mo).Related(&smps, "OutputMapping").Error) + if len(smps) != 4 { + a.Fail("Model Associations", + "Expected to have %v Output AND Input Samples. Has %v.", 4, len(smps)) } - a.NoError(db.Model(&smo).Related(&files_sm, "Files").Error) + a.NoError(db.Model(&mo).Related(&files_sm, "Files").Error) if len(files_sm) != 2 { - a.Fail("Simulation Model Associations", + a.Fail("Model Associations", "Expected to have %v Files. Has %v.", 2, len(files_sm)) } @@ -80,10 +80,10 @@ func TestDummyDBAssociations(t *testing.T) { a.NoError(db.Model(&simn).Association("User").Find(&usr).Error) a.EqualValues("User_A", usr.Username) - a.NoError(db.Model(&simn).Related(&smos, "Models").Error) - if len(smos) != 2 { + a.NoError(db.Model(&simn).Related(&mos, "Models").Error) + if len(mos) != 2 { a.Fail("Simulation Associations", - "Expected to have %v Simulation Models. Has %v.", 2, len(smos)) + "Expected to have %v Models. Has %v.", 2, len(mos)) } a.NoError(db.Model(&simn).Related(&projs, "Projects").Error) diff --git a/common/datastructs.go b/common/datastructs.go index cc029ab..ac4d8c6 100644 --- a/common/datastructs.go +++ b/common/datastructs.go @@ -1,9 +1,10 @@ package common import ( - //"github.com/jinzhu/gorm" - "github.com/jinzhu/gorm/dialects/postgres" "time" + + // "github.com/jinzhu/gorm" + "github.com/jinzhu/gorm/dialects/postgres" ) type Simulator struct { @@ -35,7 +36,7 @@ type File struct { //UserID uint `gorm:"not null"` //new in villasweb 2.0 - SimulationModelID uint `gorm:""` + ModelID uint `gorm:""` WidgetID uint `gorm:""` } @@ -63,11 +64,11 @@ type Simulation struct { User User `gorm:"not null;association_autoupdate:false"` UserID uint `gorm:"not null"` - Models []SimulationModel `gorm:"foreignkey:BelongsToSimulationID;association_autoupdate:false"` + Models []Model `gorm:"foreignkey:BelongsToSimulationID;association_autoupdate:false"` Projects []Project `gorm:"association_autoupdate:false"` } -type SimulationModel struct { +type Model struct { //gorm.Model ID uint `gorm:"primary_key;auto_increment"` Name string `gorm:"not null"` @@ -80,9 +81,9 @@ type SimulationModel struct { BelongsToSimulator Simulator `gorm:"not null;association_autoupdate:false"` BelongsToSimulatorID uint `gorm:"not null"` - // NOTE: order of signals is important - OutputMapping []Signal `gorm:""` - InputMapping []Signal `gorm:""` + // NOTE: order of samples is important + OutputMapping []Sample `gorm:""` + InputMapping []Sample `gorm:""` //new in villasweb 2.0 (for CIM file of simulation model and other model file formats) Files []File `gorm:""` @@ -119,12 +120,14 @@ type Visualization struct { Widgets []Widget `gorm:""` } -type Signal struct { +type Sample struct { //gorm.Model ID uint `gorm:"primary_key;auto_increment"` Name string `gorm:"not null"` Unit string `gorm:"not null"` - SimulationModelID uint + Index uint `gorm:"not null"` + Direction string `gorm:"not null"` + ModelID uint //IsRecorded bool `gorm:"default:false"` } diff --git a/doc/api/api.yaml b/doc/api/api.yaml index e0cd988..4cac0e6 100644 --- a/doc/api/api.yaml +++ b/doc/api/api.yaml @@ -20,8 +20,8 @@ tags: description: Manage Projects - name: simulations description: Manage Simulations -- name: simulationmodels - description: Manage SimulationModels +- name: models + description: Manage Models - name: visualizations description: Manage Visualizations - name: uploads @@ -502,9 +502,9 @@ paths: /models: get: tags: - - simulationmodels + - models summary: Get simulation models of user - operationId: getSimulationModels + operationId: getModels responses: 200: description: OK. @@ -513,7 +513,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/SimulationModel' + $ref: '#/components/schemas/Model' 401: description: Unauthorized access. 403: @@ -524,16 +524,16 @@ paths: description: Internal server error. post: tags: - - simulationmodels - summary: Add a new SimulationModel to the database - operationId: addSimulationModel + - models + summary: Add a new Model to the database + operationId: addModel requestBody: - description: "SimulationModel object to add to DB" + description: "Model object to add to DB" required: true content: application/json: schema: - $ref: '#/components/schemas/SimulationModel' + $ref: '#/components/schemas/Model' responses: 200: description: OK. @@ -545,26 +545,26 @@ paths: description: Access forbidden. 500: description: Internal server error. Unable to save simulation or simulation model. - /models/{SimulationModelID}: + /models/{ModelID}: put: tags: - - simulationmodels - summary: Update properties of SimulationModel with ID SimulationModelID - operationId: updateSimulationModelProperties + - models + summary: Update properties of Model with ID ModelID + operationId: updateModelProperties parameters: - in: path - name: SimulationModelID - description: ID of a SimulationModel + name: ModelID + description: ID of a Model required: true schema: type: integer requestBody: - description: "SimulationModel object with new properties" + description: "Model object with new properties" required: true content: application/json: schema: - $ref: '#/components/schemas/SimulationModel' + $ref: '#/components/schemas/Model' responses: 200: description: OK. @@ -580,13 +580,13 @@ paths: description: Internal server error. Unable to save simulation model. get: tags: - - simulationmodels - summary: Get properties of SimulationModel with ID SimulationModelID - operationId: getSimulationModelProperties + - models + summary: Get properties of Model with ID ModelID + operationId: getModelProperties parameters: - in: path - name: SimulationModelID - description: ID of a SimulationModel + name: ModelID + description: ID of a Model required: true schema: type: integer @@ -596,7 +596,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/SimulationModel' + $ref: '#/components/schemas/Model' 401: description: Unauthorized access. 403: @@ -607,13 +607,13 @@ paths: description: Internal server error. delete: tags: - - simulationmodels - summary: Delete SimulationModel with ID SimulationModelID - operationId: deleteSimulationModel + - models + summary: Delete Model with ID ModelID + operationId: deleteModel parameters: - in: path - name: SimulationModelID - description: ID of a SimulationModel + name: ModelID + description: ID of a Model required: true schema: type: integer @@ -628,16 +628,16 @@ paths: description: Not found. Simulation or simulation model not found. 500: description: Internal server error. Unable to save changed simulation or to remove simulation model. - /models/{SimulationModelID}/file: + /models/{ModelID}/file: get: tags: - - simulationmodels - summary: Get file from SimulationModel with ID SimulationModelID (NEW) - operationId: getFileFromSimulationModel + - models + summary: Get file from Model with ID ModelID (NEW) + operationId: getFileFromModel parameters: - in: path - name: SimulationModelID - description: ID of a SimulationModel + name: ModelID + description: ID of a Model required: true schema: type: integer @@ -680,13 +680,13 @@ paths: description: Internal server error. put: tags: - - simulationmodels - summary: Update (Overwrite) file of SimulationModel with ID SimulationModelID, File object has to be in database (NEW) - operationId: updateFileForSimulationModel + - models + summary: Update (Overwrite) file of Model with ID ModelID, File object has to be in database (NEW) + operationId: updateFileForModel parameters: - in: path - name: SimulationModelID - description: ID of a SimulationModel + name: ModelID + description: ID of a Model required: true schema: type: integer @@ -1269,7 +1269,7 @@ components: type: integer UserID: type: integer - SimulationModelID: + ModelID: type: integer Date: type: string @@ -1298,7 +1298,7 @@ components: RawProperties: type: object properties: {} - SimulationModel: + Model: required: - Name - OutputLength @@ -1323,23 +1323,23 @@ components: OutputMapping: type: array items: - $ref: '#/components/schemas/Signal' + $ref: '#/components/schemas/Sample' InputMapping: type: array items: - $ref: '#/components/schemas/Signal' - Signal: + $ref: '#/components/schemas/Sample' + Sample: required: - Name - Unit - - SimulationModelID + - ModelID type: object properties: Name: type: string Unit: type: string - SimulationModelID: + ModelID: type: integer Widget: required: diff --git a/routes/file/fileQueries.go b/routes/file/fileQueries.go index 92219b0..72fe117 100644 --- a/routes/file/fileQueries.go +++ b/routes/file/fileQueries.go @@ -52,7 +52,7 @@ func FindFileByPath(path string) (common.File, error) { return file, err } -func RegisterFile(c *gin.Context, widgetID int, simulationmodelID int, simulationID int){ +func RegisterFile(c *gin.Context, widgetID int, modelID int, simulationID int){ // Extract file from PUT request form file_header, err := c.FormFile("file") @@ -67,7 +67,7 @@ func RegisterFile(c *gin.Context, widgetID int, simulationmodelID int, simulatio // Obtain properties of file filetype := file_header.Header.Get("Content-Type") // TODO make sure this is properly set in file header filename := filepath.Base(file_header.Filename) - foldername := getFolderName(simulationID, simulationmodelID, widgetID) + foldername := getFolderName(simulationID, modelID, widgetID) size := file_header.Size // Save file to local disc (NOT DB!) @@ -81,11 +81,11 @@ func RegisterFile(c *gin.Context, widgetID int, simulationmodelID int, simulatio } // Add File object with parameters to DB - saveFileInDB(c, filename, foldername, filetype, uint(size), widgetID, simulationmodelID, true) + saveFileInDB(c, filename, foldername, filetype, uint(size), widgetID, modelID, true) } -func UpdateFile(c *gin.Context, widgetID int, simulationmodelID int, simulationID int){ +func UpdateFile(c *gin.Context, widgetID int, modelID int, simulationID int){ // Extract file from PUT request form file_header, err := c.FormFile("file") @@ -100,7 +100,7 @@ func UpdateFile(c *gin.Context, widgetID int, simulationmodelID int, simulationI filename := filepath.Base(file_header.Filename) filetype := file_header.Header.Get("Content-Type") // TODO make sure this is properly set in file header size := file_header.Size - foldername := getFolderName(simulationID, simulationmodelID, widgetID) + foldername := getFolderName(simulationID, modelID, widgetID) err = modifyFileOnDisc(file_header, filename, foldername, uint(size), false) if err != nil { @@ -111,10 +111,10 @@ func UpdateFile(c *gin.Context, widgetID int, simulationmodelID int, simulationI return } - saveFileInDB(c, filename, foldername, filetype, uint(size), widgetID, simulationmodelID, false) + saveFileInDB(c, filename, foldername, filetype, uint(size), widgetID, modelID, false) } -func ReadFile(c *gin.Context, widgetID int, simulationmodelID int, simulationID int){ +func ReadFile(c *gin.Context, widgetID int, modelID int, simulationID int){ contentType := c.GetHeader("Content-Type") @@ -123,7 +123,7 @@ func ReadFile(c *gin.Context, widgetID int, simulationmodelID int, simulationID if widgetID != -1 { // get associated Widget var wdgt common.Widget - err := db.First(&wdgt, simulationmodelID).Error + err := db.First(&wdgt, modelID).Error if common.ProvideErrorResponse(c, err) { return } @@ -132,11 +132,11 @@ func ReadFile(c *gin.Context, widgetID int, simulationmodelID int, simulationID return } - } else if simulationmodelID != -1 { + } else if modelID != -1 { // get associated Simulation Model - var model common.SimulationModel - err := db.First(&model, simulationmodelID).Error + var model common.Model + err := db.First(&model, modelID).Error if common.ProvideErrorResponse(c, err) { return } @@ -158,12 +158,12 @@ func ReadFile(c *gin.Context, widgetID int, simulationmodelID int, simulationID }) } -func DeleteFile(c *gin.Context, widgetID int, simulationmodelID int, simulationID int){ +func DeleteFile(c *gin.Context, widgetID int, nmodelID int, simulationID int){ // TODO } -func saveFileInDB(c *gin.Context, filename string, foldername string, filetype string, size uint, widgetID int, simulationmodelID int, createObj bool) { +func saveFileInDB(c *gin.Context, filename string, foldername string, filetype string, size uint, widgetID int, modelID int, createObj bool) { filesavepath := filepath.Join(foldername, filename) @@ -220,13 +220,13 @@ func saveFileInDB(c *gin.Context, filename string, foldername string, filetype s } } - if simulationmodelID != -1 { + if modelID != -1 { if createObj { // associate to Simulation Model db := common.GetDB() - var model common.SimulationModel - err := db.First(&model, simulationmodelID).Error + var model common.Model + err := db.First(&model, modelID).Error if common.ProvideErrorResponse(c, err) { return } @@ -310,16 +310,16 @@ func modifyFileOnDisc(file_header *multipart.FileHeader, filename string, folder } -func getFolderName(simulationID int, simulationmodelID int, widgetID int) string { +func getFolderName(simulationID int, modelID int, widgetID int) string { base_foldername := "files/" elementname := "" elementid := 0 - if simulationmodelID == -1{ + if modelID == -1{ elementname = "/widget_" elementid = widgetID } else { - elementname = "/simulationmodel_" - elementid = simulationmodelID + elementname = "/model_" + elementid = modelID } diff --git a/routes/model/modelEnpoints.go b/routes/model/modelEnpoints.go new file mode 100644 index 0000000..fee4298 --- /dev/null +++ b/routes/model/modelEnpoints.go @@ -0,0 +1,148 @@ +package model + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" +) + +func ModelsRegister(r *gin.RouterGroup) { + r.GET("/:SimulationID/models/", modelsReadEp) + r.POST("/:SimulationID/models/", modelRegistrationEp) + + r.PUT("/:SimulationID/models/:ModelID", modelUpdateEp) + r.GET("/:SimulationID/models/:ModelID", modelReadEp) + r.DELETE("/:SimulationID/models/:ModelID", modelDeleteEp) + + // Files + r.POST ("/:SimulationID/models/:ModelID/file", modelRegisterFileEp) // NEW in API + r.GET("/:SimulationID/models/:ModelID/file", modelReadFileEp) // NEW in API + r.PUT("/:SimulationID/models/:ModelID/file", modelUpdateFileEp) // NEW in API + r.DELETE("/:SimulationID/models/:ModelID/file", modelDeleteFileEp) // NEW in API + + // Simulator + r.PUT("/:SimulationID/models/:ModelID/simulator", modelUpdateSimulatorEp) // NEW in API + r.GET("/:SimulationID/models/:ModelID/simulator", modelReadSimulatorEp) // NEW in API + + // Input and Output Samples + r.POST("/:SimulationID/models/:ModelID/Samples/:Direction", modelRegisterSamplesEp) // NEW in API + r.GET("/:SimulationID/models/:ModelID/Samples/:Direction", modelReadSamplesEp) // NEW in API + r.PUT("/:SimulationID/models/:ModelID/Samples/:Direction", modelUpdateSamplesEp) // NEW in API + r.DELETE("/:SimulationID/models/:ModelID/Samples/:Direction", modelDeleteSamplesEp) // NEW in API +} + +func modelsReadEp(c *gin.Context) { + allModels, _, _ := FindAllModels() + serializer := ModelsSerializerNoAssoc{c, allModels} + c.JSON(http.StatusOK, gin.H{ + "models": serializer.Response(), + }) +} + +func modelRegistrationEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func modelUpdateEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func modelReadEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func modelDeleteEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + + +func modelRegisterFileEp(c *gin.Context) { + + simulationID, modelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Save file locally and register file in DB, HTTP response is set by this method + file.RegisterFile(c,-1, modelID, simulationID) + +} + +func modelReadFileEp(c *gin.Context) { + + simulationID, modelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Read file from disk and return in HTTP response, no change to DB + file.ReadFile(c, -1, modelID, simulationID) +} + +func modelUpdateFileEp(c *gin.Context) { + + simulationID, modelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Update file locally and update file entry in DB, HTTP response is set by this method + file.UpdateFile(c,-1, modelID, simulationID) +} + +func modelDeleteFileEp(c *gin.Context) { + + simulationID, modelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Delete file from disk and remove entry from DB, HTTP response is set by this method + file.DeleteFile(c, -1, modelID, simulationID) + + +} + + +func GetModelID(c *gin.Context) (int, error) { + + modelID, err := strconv.Atoi(c.Param("ModelID")) + + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulation model ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return -1, err + } else { + return modelID, err + + } +} + +func getRequestParams(c *gin.Context) (int, int, error){ + simulationID, err := simulation.GetSimulationID(c) + if err != nil{ + return -1, -1, err + } + + modelID, err := GetModelID(c) + if err != nil{ + return -1, -1, err + } + + return simulationID, modelID, err +} \ No newline at end of file diff --git a/routes/model/modelQueries.go b/routes/model/modelQueries.go new file mode 100644 index 0000000..6e3a10c --- /dev/null +++ b/routes/model/modelQueries.go @@ -0,0 +1,12 @@ +package model + +import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" +) + +func FindAllModels() ([]common.Model, int, error) { + db := common.GetDB() + var models []common.Model + err := db.Find(&models).Error + return models, len(models), err +} diff --git a/routes/simulationmodel/simulationmodelSerializer.go b/routes/model/modelSerializer.go similarity index 57% rename from routes/simulationmodel/simulationmodelSerializer.go rename to routes/model/modelSerializer.go index fdee9f7..6096a87 100644 --- a/routes/simulationmodel/simulationmodelSerializer.go +++ b/routes/model/modelSerializer.go @@ -1,4 +1,4 @@ -package simulationmodel +package model import ( "github.com/gin-gonic/gin" @@ -6,26 +6,26 @@ import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) -type SimulationModelsSerializerNoAssoc struct { +type ModelsSerializerNoAssoc struct { Ctx *gin.Context - SimulationModels []common.SimulationModel + Models []common.Model } -func (self *SimulationModelsSerializerNoAssoc) Response() []SimulationModelResponseNoAssoc { - response := []SimulationModelResponseNoAssoc{} - for _, simulationmodel := range self.SimulationModels { - serializer := SimulationModelSerializerNoAssoc{self.Ctx, simulationmodel} +func (self *ModelsSerializerNoAssoc) Response() []ModelResponseNoAssoc { + response := []ModelResponseNoAssoc{} + for _, model := range self.Models { + serializer := ModelSerializerNoAssoc{self.Ctx, model} response = append(response, serializer.Response()) } return response } -type SimulationModelSerializerNoAssoc struct { +type ModelSerializerNoAssoc struct { Ctx *gin.Context - common.SimulationModel + common.Model } -type SimulationModelResponseNoAssoc struct { +type ModelResponseNoAssoc struct { Name string `json:"Name"` OutputLength int `json:"OutputLength"` InputLength int `json:"InputLength"` @@ -36,8 +36,8 @@ type SimulationModelResponseNoAssoc struct { //Input Mapping } -func (self *SimulationModelSerializerNoAssoc) Response() SimulationModelResponseNoAssoc { - response := SimulationModelResponseNoAssoc{ +func (self *ModelSerializerNoAssoc) Response() ModelResponseNoAssoc { + response := ModelResponseNoAssoc{ Name: self.Name, OutputLength: self.OutputLength, InputLength: self.InputLength, diff --git a/routes/sample/sampleEndpoints.go b/routes/sample/sampleEndpoints.go new file mode 100644 index 0000000..ca6c759 --- /dev/null +++ b/routes/sample/sampleEndpoints.go @@ -0,0 +1,4 @@ +package sample + + +//TODO extend API with sample endpoints \ No newline at end of file diff --git a/routes/sample/sampleQueries.go b/routes/sample/sampleQueries.go new file mode 100644 index 0000000..3f86c11 --- /dev/null +++ b/routes/sample/sampleQueries.go @@ -0,0 +1,3 @@ +package sample + +//TODO extend API with sample endpoints \ No newline at end of file diff --git a/routes/sample/sampleSerializer.go b/routes/sample/sampleSerializer.go new file mode 100644 index 0000000..3f86c11 --- /dev/null +++ b/routes/sample/sampleSerializer.go @@ -0,0 +1,3 @@ +package sample + +//TODO extend API with sample endpoints \ No newline at end of file diff --git a/routes/signal/signalEndpoints.go b/routes/signal/signalEndpoints.go deleted file mode 100644 index 3fde498..0000000 --- a/routes/signal/signalEndpoints.go +++ /dev/null @@ -1,4 +0,0 @@ -package signal - - -//TODO extend API with signal endpoints \ No newline at end of file diff --git a/routes/signal/signalQueries.go b/routes/signal/signalQueries.go deleted file mode 100644 index 65499f1..0000000 --- a/routes/signal/signalQueries.go +++ /dev/null @@ -1,3 +0,0 @@ -package signal - -//TODO extend API with signal endpoints \ No newline at end of file diff --git a/routes/signal/signalSerializer.go b/routes/signal/signalSerializer.go deleted file mode 100644 index 65499f1..0000000 --- a/routes/signal/signalSerializer.go +++ /dev/null @@ -1,3 +0,0 @@ -package signal - -//TODO extend API with signal endpoints \ No newline at end of file diff --git a/routes/simulationmodel/simulationmodelEnpoints.go b/routes/simulationmodel/simulationmodelEnpoints.go deleted file mode 100644 index 868de1e..0000000 --- a/routes/simulationmodel/simulationmodelEnpoints.go +++ /dev/null @@ -1,140 +0,0 @@ -package simulationmodel - -import ( - "fmt" - "net/http" - "strconv" - - "github.com/gin-gonic/gin" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" -) - -func SimulationModelsRegister(r *gin.RouterGroup) { - r.GET("/:SimulationID/models/", simulationmodelsReadEp) - r.POST("/:SimulationID/models/", simulationmodelRegistrationEp) - - r.PUT("/:SimulationID/models/:SimulationModelID", simulationmodelUpdateEp) - r.GET("/:SimulationID/models/:SimulationModelID", simulationmodelReadEp) - r.DELETE("/:SimulationID/models/:SimulationModelID", simulationmodelDeleteEp) - - // Files - r.POST ("/:SimulationID/models/:SimulationModelID/file", simulationmodelRegisterFileEp) // NEW in API - r.GET("/:SimulationID/models/:SimulationModelID/file", simulationmodelReadFileEp) // NEW in API - r.PUT("/:SimulationID/models/:SimulationModelID/file", simulationmodelUpdateFileEp) // NEW in API - r.DELETE("/:SimulationID/models/:SimulationModelID/file", simulationmodelDeleteFileEp) // NEW in API - - -} - -func simulationmodelsReadEp(c *gin.Context) { - allSimulationModels, _, _ := FindAllSimulationModels() - serializer := SimulationModelsSerializerNoAssoc{c, allSimulationModels} - c.JSON(http.StatusOK, gin.H{ - "simulationmodels": serializer.Response(), - }) -} - -func simulationmodelRegistrationEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func simulationmodelUpdateEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func simulationmodelReadEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func simulationmodelDeleteEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - - -func simulationmodelRegisterFileEp(c *gin.Context) { - - simulationID, simulationmodelID, err := getRequestParams(c) - if err != nil{ - return - } - - // Save file locally and register file in DB, HTTP response is set by this method - file.RegisterFile(c,-1, simulationmodelID, simulationID) - -} - -func simulationmodelReadFileEp(c *gin.Context) { - - simulationID, simulationmodelID, err := getRequestParams(c) - if err != nil{ - return - } - - // Read file from disk and return in HTTP response, no change to DB - file.ReadFile(c, -1, simulationmodelID, simulationID) -} - -func simulationmodelUpdateFileEp(c *gin.Context) { - - simulationID, simulationmodelID, err := getRequestParams(c) - if err != nil{ - return - } - - // Update file locally and update file entry in DB, HTTP response is set by this method - file.UpdateFile(c,-1, simulationmodelID, simulationID) -} - -func simulationmodelDeleteFileEp(c *gin.Context) { - - simulationID, simulationmodelID, err := getRequestParams(c) - if err != nil{ - return - } - - // Delete file from disk and remove entry from DB, HTTP response is set by this method - file.DeleteFile(c, -1, simulationmodelID, simulationID) - - -} - - -func GetSimulationmodelID(c *gin.Context) (int, error) { - - simulationmodelID, err := strconv.Atoi(c.Param("SimulationModelID")) - - if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulation model ID") - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return -1, err - } else { - return simulationmodelID, err - - } -} - -func getRequestParams(c *gin.Context) (int, int, error){ - simulationID, err := simulation.GetSimulationID(c) - if err != nil{ - return -1, -1, err - } - - simulationmodelID, err := GetSimulationmodelID(c) - if err != nil{ - return -1, -1, err - } - - return simulationID, simulationmodelID, err -} \ No newline at end of file diff --git a/routes/simulationmodel/simulationmodelQueries.go b/routes/simulationmodel/simulationmodelQueries.go deleted file mode 100644 index 0eaf308..0000000 --- a/routes/simulationmodel/simulationmodelQueries.go +++ /dev/null @@ -1,12 +0,0 @@ -package simulationmodel - -import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -func FindAllSimulationModels() ([]common.SimulationModel, int, error) { - db := common.GetDB() - var simulationmodels []common.SimulationModel - err := db.Find(&simulationmodels).Error - return simulationmodels, len(simulationmodels), err -} diff --git a/start.go b/start.go index b1e14d8..33eb69a 100644 --- a/start.go +++ b/start.go @@ -1,16 +1,12 @@ package main 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/file" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/project" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization" - - "github.com/gin-gonic/gin" ) func main() { @@ -23,12 +19,12 @@ func main() { api := r.Group("/api") user.UsersRegister(api.Group("/users")) - file.FilesRegister(api.Group("/files")) - project.ProjectsRegister(api.Group("/projects")) + //file.FilesRegister(api.Group("/files")) + //project.ProjectsRegister(api.Group("/projects")) simulation.SimulationsRegister(api.Group("/simulations")) - simulationmodel.SimulationModelsRegister(api.Group("/models")) + //model.ModelsRegister(api.Group("/models")) simulator.SimulatorsRegister(api.Group("/simulators")) - visualization.VisualizationsRegister(api.Group("/visualizations")) + //visualization.VisualizationsRegister(api.Group("/visualizations")) From 44e15773fd5884386cf234823b84789e57a9bf99 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 21 May 2019 12:49:39 +0200 Subject: [PATCH 07/30] Major changes: - revise data model to new version (without project) - adapt database test to new data model - use separate folder for all endpoint defs and functions to avoid circular deps - add all API endpoints and skeletons for their implementation --- common/database.go | 52 ++-- common/database_test.go | 100 +++----- common/datastructs.go | 179 ++++++-------- endpoints/fileEndpoints.go | 224 ++++++++++++++++++ endpoints/modelEnpoints.go | 59 +++++ endpoints/registration.go | 85 +++++++ endpoints/sampleEndpoints.go | 30 +++ endpoints/simulationEndpoints.go | 40 ++++ .../simulatorEndpoints.go | 34 +-- {routes/user => endpoints}/userEndpoints.go | 36 ++- .../visualizationEndpoints.go | 17 +- endpoints/widgetEndpoints.go | 56 +++++ routes/file/fileEndpoints.go | 68 ------ routes/file/fileQueries.go | 87 ++++--- routes/model/modelEnpoints.go | 148 ------------ routes/model/modelSerializer.go | 8 +- routes/project/projectEndpoints.go | 46 ---- routes/project/projectQueries.go | 28 --- routes/project/projectSerializer.go | 41 ---- routes/sample/sampleEndpoints.go | 4 - routes/sample/sampleQueries.go | 2 - routes/sample/sampleSerializer.go | 2 - routes/simulation/simulationEndpoints.go | 67 ------ routes/user/userSerializer.go | 10 - .../visualization/visualizationSerializer.go | 14 +- routes/widget/widgetEndpoints.go | 3 - start.go | 12 +- 27 files changed, 740 insertions(+), 712 deletions(-) create mode 100644 endpoints/fileEndpoints.go create mode 100644 endpoints/modelEnpoints.go create mode 100644 endpoints/registration.go create mode 100644 endpoints/sampleEndpoints.go create mode 100644 endpoints/simulationEndpoints.go rename {routes/simulator => endpoints}/simulatorEndpoints.go (58%) rename {routes/user => endpoints}/userEndpoints.go (50%) rename {routes/visualization => endpoints}/visualizationEndpoints.go (56%) create mode 100644 endpoints/widgetEndpoints.go delete mode 100644 routes/file/fileEndpoints.go delete mode 100644 routes/model/modelEnpoints.go delete mode 100644 routes/project/projectEndpoints.go delete mode 100644 routes/project/projectQueries.go delete mode 100644 routes/project/projectSerializer.go delete mode 100644 routes/sample/sampleEndpoints.go delete mode 100644 routes/simulation/simulationEndpoints.go delete mode 100644 routes/widget/widgetEndpoints.go diff --git a/common/database.go b/common/database.go index 1883d62..91b2fa6 100644 --- a/common/database.go +++ b/common/database.go @@ -56,7 +56,6 @@ func DropTables(db *gorm.DB) { db.DropTableIfExists(&Sample{}) db.DropTableIfExists(&Model{}) db.DropTableIfExists(&File{}) - db.DropTableIfExists(&Project{}) db.DropTableIfExists(&Simulation{}) db.DropTableIfExists(&User{}) db.DropTableIfExists(&Visualization{}) @@ -69,7 +68,6 @@ func MigrateModels(db *gorm.DB) { db.AutoMigrate(&Sample{}) db.AutoMigrate(&Model{}) db.AutoMigrate(&File{}) - db.AutoMigrate(&Project{}) db.AutoMigrate(&Simulation{}) db.AutoMigrate(&User{}) db.AutoMigrate(&Visualization{}) @@ -102,10 +100,10 @@ func DummyPopulateDB(test_db *gorm.DB) { checkErr(test_db.Create(&simr_A).Error) checkErr(test_db.Create(&simr_B).Error) - outSmp_A := Sample{Name: "outSample_A"} - outSmp_B := Sample{Name: "outSample_B"} - inSmp_A := Sample{Name: "inSample_A"} - inSmp_B := Sample{Name: "inSample_B"} + outSmp_A := Sample{Name: "outSample_A", Direction: "out"} + outSmp_B := Sample{Name: "outSample_B", Direction: "out"} + inSmp_A := Sample{Name: "inSample_A", Direction: "in"} + inSmp_B := Sample{Name: "inSample_B", Direction: "in"} checkErr(test_db.Create(&outSmp_A).Error) checkErr(test_db.Create(&outSmp_B).Error) checkErr(test_db.Create(&inSmp_A).Error) @@ -121,11 +119,6 @@ func DummyPopulateDB(test_db *gorm.DB) { checkErr(test_db.Create(&file_A).Error) checkErr(test_db.Create(&file_B).Error) - proj_A := Project{Name: "Project_A"} - proj_B := Project{Name: "Project_B"} - checkErr(test_db.Create(&proj_A).Error) - checkErr(test_db.Create(&proj_B).Error) - simn_A := Simulation{Name: "Simulation_A"} simn_B := Simulation{Name: "Simulation_B"} checkErr(test_db.Create(&simn_A).Error) @@ -150,31 +143,25 @@ func DummyPopulateDB(test_db *gorm.DB) { // For `belongs to` use the model with id=1 // For `has many` use the models with id=1 and id=2 - // Project HM Visualization, Visualization BT Project - checkErr(test_db.Model(&vis_A).Association("Project").Append(&proj_A).Error) - checkErr(test_db.Model(&vis_B).Association("Project").Append(&proj_A).Error) + // User HM Simulations, Simulation HM Users (Many-to-Many) + checkErr(test_db.Model(&simn_A).Association("Users").Append(&usr_A).Error) + checkErr(test_db.Model(&simn_A).Association("Users").Append(&usr_B).Error) + checkErr(test_db.Model(&simn_B).Association("Users").Append(&usr_A).Error) + checkErr(test_db.Model(&simn_B).Association("Users").Append(&usr_B).Error) - // User HM Project, Project BT User - checkErr(test_db.Model(&proj_A).Association("User").Append(&usr_A).Error) - checkErr(test_db.Model(&proj_B).Association("User").Append(&usr_A).Error) + // Simulation HM Model + checkErr(test_db.Model(&simn_A).Association("Models").Append(&mo_A).Error) + checkErr(test_db.Model(&simn_A).Association("Models").Append(&mo_B).Error) - // Simulation HM Project, Project BT Simulation - checkErr(test_db.Model(&proj_A).Association("Simulation").Append(&simn_A).Error) - checkErr(test_db.Model(&proj_B).Association("Simulation").Append(&simn_A).Error) - - // Simulation HM SimModel, SimModel BT Simulation - checkErr(test_db.Model(&mo_A).Association("BelongsToSimulation").Append(&simn_A).Error) - checkErr(test_db.Model(&mo_B).Association("BelongsToSimulation").Append(&simn_A).Error) - - // User HM Simulation, Simulation BT User - checkErr(test_db.Model(&simn_A).Association("User").Append(&usr_A).Error) - checkErr(test_db.Model(&simn_B).Association("User").Append(&usr_A).Error) + // Simulation HM Visualizations + checkErr(test_db.Model(&simn_A).Association("Visualizations").Append(&vis_A).Error) + checkErr(test_db.Model(&simn_A).Association("Visualizations").Append(&vis_B).Error) // Visualization HM Widget checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_A).Error) checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_B).Error) - // SimModel HM Samples + // Model HM Samples checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSmp_A).Error) checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSmp_B).Error) checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSmp_A).Error) @@ -184,11 +171,8 @@ func DummyPopulateDB(test_db *gorm.DB) { checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_A).Error) checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_B).Error) - // Visualization BT User - checkErr(test_db.Model(&vis_A).Association("User").Append(&usr_A).Error) - - // Simulator BT SimModel - checkErr(test_db.Model(&mo_A).Association("BelongsToSimulator").Append(&simr_A).Error) + // Simulator BT Model + checkErr(test_db.Model(&mo_A).Association("Simulator").Append(&simr_A).Error) // Widget HM Files checkErr(test_db.Model(&widg_A).Association("Files").Append(&file_A).Error) diff --git a/common/database_test.go b/common/database_test.go index 949086e..d4c3553 100644 --- a/common/database_test.go +++ b/common/database_test.go @@ -30,9 +30,9 @@ func TestDummyDBAssociations(t *testing.T) { var simr Simulator var mo Model var file File - var proj Project var simn Simulation var usr User + var usrs []User var vis Visualization var widg Widget @@ -40,34 +40,21 @@ func TestDummyDBAssociations(t *testing.T) { var mos []Model var files []File var files_sm []File - var projs []Project var simns []Simulation var viss []Visualization var widgs []Widget - // Model + // User - a.NoError(db.Find(&mo, 1).Error, fM("Model")) - a.EqualValues("SimModel_A", mo.Name) + a.NoError(db.Find(&usr, 1).Error, fM("User")) + a.EqualValues("User_A", usr.Username) - // Model Associations + // User Associations - a.NoError(db.Model(&mo).Association("BelongsToSimulation").Find(&simn).Error) - a.EqualValues("Simulation_A", simn.Name, "Expected Simulation_A") - - a.NoError(db.Model(&mo).Association("BelongsToSimulator").Find(&simr).Error) - a.EqualValues("Host_A", simr.Host, "Expected Host_A") - - a.NoError(db.Model(&mo).Related(&smps, "OutputMapping").Error) - if len(smps) != 4 { - a.Fail("Model Associations", - "Expected to have %v Output AND Input Samples. Has %v.", 4, len(smps)) - } - - a.NoError(db.Model(&mo).Related(&files_sm, "Files").Error) - if len(files_sm) != 2 { - a.Fail("Model Associations", - "Expected to have %v Files. Has %v.", 2, len(files_sm)) + a.NoError(db.Model(&usr).Related(&simns, "Simulations").Error) + if len(simns) != 2 { + a.Fail("User Associations", + "Expected to have %v Simulations. Has %v.", 2, len(simns)) } // Simulation @@ -77,8 +64,11 @@ func TestDummyDBAssociations(t *testing.T) { // Simulation Associations - a.NoError(db.Model(&simn).Association("User").Find(&usr).Error) - a.EqualValues("User_A", usr.Username) + a.NoError(db.Model(&simn).Association("Users").Find(&usrs).Error) + if len(usrs) != 2 { + a.Fail("Simulations Associations", + "Expected to have %v Users. Has %v.", 2, len(usrs)) + } a.NoError(db.Model(&simn).Related(&mos, "Models").Error) if len(mos) != 2 { @@ -86,52 +76,36 @@ func TestDummyDBAssociations(t *testing.T) { "Expected to have %v Models. Has %v.", 2, len(mos)) } - a.NoError(db.Model(&simn).Related(&projs, "Projects").Error) - if len(projs) != 2 { - a.Fail("Simulation Associations", - "Expected to have %v Projects. Has %v.", 2, len(projs)) - } - - // Project - - a.NoError(db.Find(&proj, 1).Error, fM("Project")) - a.EqualValues("Project_A", proj.Name) - - // Project Associations - - a.NoError(db.Model(&proj).Association("Simulation").Find(&simn).Error) - a.EqualValues("Simulation_A", simn.Name) - - a.NoError(db.Model(&proj).Association("User").Find(&usr).Error) - a.EqualValues("User_A", usr.Username) - - a.NoError(db.Model(&proj).Related(&viss, "Visualizations").Error) + a.NoError(db.Model(&simn).Related(&viss, "Visualizations").Error) if len(viss) != 2 { - a.Fail("Project Associations", + a.Fail("Simulation Associations", "Expected to have %v Visualizations. Has %v.", 2, len(viss)) } - // User - a.NoError(db.Find(&usr, 1).Error, fM("User")) - a.EqualValues("User_A", usr.Username) + // Model - // User Associations + a.NoError(db.Find(&mo, 1).Error, fM("Model")) + a.EqualValues("Model_A", mo.Name) - a.NoError(db.Model(&usr).Related(&projs, "Projects").Error) - if len(projs) != 2 { - a.Fail("User Associations", - "Expected to have %v Projects. Has %v.", 2, len(projs)) + // Model Associations + + a.NoError(db.Model(&mo).Association("Simulator").Find(&simr).Error) + a.EqualValues("Host_A", simr.Host, "Expected Host_A") + + a.NoError(db.Model(&mo).Where("Direction = ?", "out").Related(&smps, "OutputMapping").Error) + if len(smps) != 2 { + a.Fail("Model Associations", + "Expected to have %v Output AND Input Samples. Has %v.", 2, len(smps)) } - a.NoError(db.Model(&usr).Related(&simns, "Simulations").Error) - if len(simns) != 2 { - a.Fail("User Associations", - "Expected to have %v Simulations. Has %v.", 2, len(simns)) + a.NoError(db.Model(&mo).Related(&files_sm, "Files").Error) + if len(files_sm) != 2 { + a.Fail("Model Associations", + "Expected to have %v Files. Has %v.", 2, len(files_sm)) } - // Visualization a.NoError(db.Find(&vis, 1).Error, fM("Visualization")) @@ -139,12 +113,6 @@ func TestDummyDBAssociations(t *testing.T) { // Visualization Associations - a.NoError(db.Model(&vis).Association("Project").Find(&proj).Error) - a.EqualValues("Project_A", proj.Name) - - a.NoError(db.Model(&vis).Association("User").Find(&usr).Error) - a.EqualValues("User_A", usr.Username) - a.NoError(db.Model(&vis).Related(&widgs, "Widgets").Error) if len(widgs) != 2 { a.Fail("Widget Associations", @@ -169,9 +137,5 @@ func TestDummyDBAssociations(t *testing.T) { a.NoError(db.Find(&file, 1).Error, fM("File")) a.EqualValues("File_A", file.Name) - // File Associations - - //a.NoError(db.Model(&file).Association("User").Find(&usr).Error) - //a.EqualValues("User_A", usr.Username) } diff --git a/common/datastructs.go b/common/datastructs.go index ac4d8c6..190c41b 100644 --- a/common/datastructs.go +++ b/common/datastructs.go @@ -7,6 +7,61 @@ import ( "github.com/jinzhu/gorm/dialects/postgres" ) +type User struct { + //gorm.Model + ID uint `gorm:"primary_key;auto_increment"` + Username string `gorm:"unique;not null"` + Password string `gorm:"not null"` + Mail string `gorm:"default:''"` + Role string `gorm:"default:'user'"` + + Simulations []Simulation `gorm:"many2many:user_simulations"` +} + +type Simulation struct { + //gorm.Model + ID uint `gorm:"primary_key;auto_increment"` + Name string `gorm:"not null"` + Running bool `gorm:"default:false"` + StartParameters postgres.Jsonb // TODO default value + + Users []User `gorm:"not null;many2many:user_simulations"` + Models []Model `gorm:"foreignkey:SimulationID"` + Visualizations []Visualization `gorm:"foreignkey:SimulationID"` +} + +type Model struct { + //gorm.Model + ID uint `gorm:"primary_key;auto_increment"` + Name string `gorm:"not null"` + OutputLength int `gorm:"default:1"` + InputLength int `gorm:"default:1"` + StartParameters postgres.Jsonb // TODO: default value? + + SimulationID uint `gorm:"not null"` + Simulator Simulator `gorm:"not null"` + SimulatorID uint `gorm:"not null"` + + // NOTE: order of samples is important + OutputMapping []Sample `gorm:"foreignkey:ModelID"` + InputMapping []Sample `gorm:"foreignkey:ModelID"` + + //new in villasweb 2.0 (for CIM file of simulation model and other model file formats) + Files []File `gorm:"foreignkey:ModelID"` + +} + +type Sample struct { + //gorm.Model + ID uint `gorm:"primary_key;auto_increment"` + Name string `gorm:"not null"` + Unit string `gorm:"not null"` + Index uint `gorm:"not null"` + Direction string `gorm:"not null"` + ModelID uint + //IsRecorded bool `gorm:"default:false"` +} + type Simulator struct { //gorm.Model ID uint `gorm:"primary_key;auto_increment"` @@ -20,115 +75,15 @@ type Simulator struct { RawProperties postgres.Jsonb // TODO: default value? } -type File struct { - //gorm.Model - ID uint `gorm:"primary_key;auto_increment"` - Name string `gorm:"not null"` - Path string `gorm:"not null"` - Type string `gorm:"not null"` - Size uint `gorm:"not null"` - ImageHeight uint // only required in case file is an image - ImageWidth uint // only required in case file is an image - Date time.Time - - //remove belongs to User relation - //User User `gorm:"not null;association_autoupdate:false"` - //UserID uint `gorm:"not null"` - - //new in villasweb 2.0 - ModelID uint `gorm:""` - WidgetID uint `gorm:""` -} - -type Project struct { - //gorm.Model - ID uint `gorm:"primary_key;auto_increment"` - Name string `gorm:"not null"` - - User User `gorm:"not null;association_autoupdate:false"` - UserID uint `gorm:"not null"` - - Simulation Simulation `gorm:"not null;association_autoupdate:false"` - SimulationID uint `gorm:"not null"` - - Visualizations []Visualization `gorm:"association_autoupdate:false"` -} - -type Simulation struct { - //gorm.Model - ID uint `gorm:"primary_key;auto_increment"` - Name string `gorm:"not null"` - Running bool `gorm:"default:false"` - StartParameters postgres.Jsonb // TODO default value - - User User `gorm:"not null;association_autoupdate:false"` - UserID uint `gorm:"not null"` - - Models []Model `gorm:"foreignkey:BelongsToSimulationID;association_autoupdate:false"` - Projects []Project `gorm:"association_autoupdate:false"` -} - -type Model struct { - //gorm.Model - ID uint `gorm:"primary_key;auto_increment"` - Name string `gorm:"not null"` - OutputLength int `gorm:"default:1"` - InputLength int `gorm:"default:1"` - StartParameters postgres.Jsonb // TODO: default value? - - BelongsToSimulation Simulation `gorm:"not null;association_autoupdate:false"` - BelongsToSimulationID uint `gorm:"not null"` - - BelongsToSimulator Simulator `gorm:"not null;association_autoupdate:false"` - BelongsToSimulatorID uint `gorm:"not null"` - // NOTE: order of samples is important - OutputMapping []Sample `gorm:""` - InputMapping []Sample `gorm:""` - - //new in villasweb 2.0 (for CIM file of simulation model and other model file formats) - Files []File `gorm:""` - -} - -type User struct { - //gorm.Model - ID uint `gorm:"primary_key;auto_increment"` - Username string `gorm:"unique;not null"` - Password string `gorm:"not null"` - Mail string `gorm:"default:''"` - Role string `gorm:"default:'user'"` - - Projects []Project `gorm:"association_autoupdate:false"` - Simulations []Simulation `gorm:"association_autoupdate:false"` - - //remove has many files relation - //Files []File `gorm:""` -} - type Visualization struct { //gorm.Model ID uint `gorm:"primary_key;auto_increment"` Name string `gorm:"not null"` Grid int `gorm:"default:15"` - Project Project `gorm:"not null;association_autoupdate:false"` - ProjectID uint `gorm:"not null"` + SimulationID uint `gorm:"not null"` - User User `gorm:"not null;association_autoupdate:false"` - UserID uint `gorm:"not null"` - - Widgets []Widget `gorm:""` -} - -type Sample struct { - //gorm.Model - ID uint `gorm:"primary_key;auto_increment"` - Name string `gorm:"not null"` - Unit string `gorm:"not null"` - Index uint `gorm:"not null"` - Direction string `gorm:"not null"` - ModelID uint - //IsRecorded bool `gorm:"default:false"` + Widgets []Widget `gorm:"foreignkey:VisualizationID"` } type Widget struct { @@ -145,7 +100,23 @@ type Widget struct { Z int `gorm:"not null"` IsLocked bool `gorm:"default:false"` CustomProperties postgres.Jsonb // TODO: default value? - VisualizationID uint + VisualizationID uint `gorm:"not null"` //new in villasweb 2.0 - Files []File `gorm:""` + Files []File `gorm:"foreignkey:WidgetID"` +} + +type File struct { + //gorm.Model + ID uint `gorm:"primary_key;auto_increment"` + Name string `gorm:"not null"` + Path string `gorm:"not null"` + Type string `gorm:"not null"` + Size uint `gorm:"not null"` + ImageHeight uint // only required in case file is an image + ImageWidth uint // only required in case file is an image + Date time.Time + + //new in villasweb 2.0 + ModelID uint `gorm:""` + WidgetID uint `gorm:""` } diff --git a/endpoints/fileEndpoints.go b/endpoints/fileEndpoints.go new file mode 100644 index 0000000..3c859a3 --- /dev/null +++ b/endpoints/fileEndpoints.go @@ -0,0 +1,224 @@ +package endpoints + +import ( + "fmt" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" + "strconv" + + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "github.com/gin-gonic/gin" + "net/http" +) + +// Endpoint functions + +func fileMReadAllEp(c *gin.Context) { + + simulationID, modelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Find files' properties in DB and return in HTTP response, no change to DB + allFiles, _, err := file.FindFiles(c, -1, modelID, simulationID) + + if common.ProvideErrorResponse(c, err) == false { + serializer := file.FilesSerializerNoAssoc{c, allFiles} + c.JSON(http.StatusOK, gin.H{ + "files": serializer.Response(), + }) + } + +} + +func fileMRegistrationEp(c *gin.Context) { + + simulationID, modelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Save file locally and register file in DB, HTTP response is set by this method + file.RegisterFile(c,-1, modelID, simulationID) + +} + +func fileMReadEp(c *gin.Context) { + + simulationID, modelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Read file from disk and return in HTTP response, no change to DB + file.ReadFile(c, -1, modelID, simulationID) +} + +func fileMUpdateEp(c *gin.Context) { + + simulationID, modelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Update file locally and update file entry in DB, HTTP response is set by this method + file.UpdateFile(c,-1, modelID, simulationID) +} + +func fileMDeleteEp(c *gin.Context) { + + simulationID, modelID, err := getRequestParams(c) + if err != nil{ + return + } + + // Delete file from disk and remove entry from DB, HTTP response is set by this method + file.DeleteFile(c, -1, modelID, simulationID) + + +} + +func fileWReadAllEp(c *gin.Context) { + + simulationID, widgetID, err := getRequestParams(c) + if err != nil{ + return + } + + // Find files' properties in DB and return in HTTP response, no change to DB + allFiles, _, err := file.FindFiles(c, widgetID, -1, simulationID) + + if common.ProvideErrorResponse(c, err) == false { + serializer := file.FilesSerializerNoAssoc{c, allFiles} + c.JSON(http.StatusOK, gin.H{ + "files": serializer.Response(), + }) + } + +} + +func fileWRegistrationEp(c *gin.Context) { + + simulationID, widgetID, err := getRequestParams(c) + if err != nil{ + return + } + + // Save file locally and register file in DB, HTTP response is set by this method + file.RegisterFile(c,widgetID, -1, simulationID) + +} + +func fileWReadEp(c *gin.Context) { + + simulationID, widgetID, err := getRequestParams(c) + if err != nil{ + return + } + + // Read file from disk and return in HTTP response, no change to DB + file.ReadFile(c, widgetID, -1, simulationID) +} + +func fileWUpdateEp(c *gin.Context) { + + simulationID, widgetID, err := getRequestParams(c) + if err != nil{ + return + } + + // Update file locally and update file entry in DB, HTTP response is set by this method + file.UpdateFile(c,widgetID, -1, simulationID) +} + +func fileWDeleteEp(c *gin.Context) { + + simulationID, widgetID, err := getRequestParams(c) + if err != nil{ + return + } + + // Delete file from disk and remove entry from DB, HTTP response is set by this method + file.DeleteFile(c, widgetID, -1, simulationID) + + +} + + +// local functions + +//func filesReadEp(c *gin.Context) { +// // Database query +// allFiles, _, err := FindAllFiles() +// +// if common.ProvideErrorResponse(c, err) == false { +// serializer := FilesSerializerNoAssoc{c, allFiles} +// c.JSON(http.StatusOK, gin.H{ +// "files": serializer.Response(), +// }) +// } +// +//} +// +// +// +//func fileUpdateEp(c *gin.Context) { +// c.JSON(http.StatusOK, gin.H{ +// "message": "NOT implemented", +// }) +//} +// +//func fileReadEp(c *gin.Context) { +// var err error +// var file common.File +// fileID := c.Param("FileID") +// desc := c.GetHeader("X-Request-FileDesc") +// desc_b, _ := strconv.ParseBool(desc) +// +// userID := 1 // TODO obtain ID of user making the request +// +// //check if description of file or file itself shall be returned +// if desc_b { +// file, err = FindFile(userID, fileID) +// if common.ProvideErrorResponse(c, err) == false { +// serializer := FileSerializerNoAssoc{c, file} +// c.JSON(http.StatusOK, gin.H{ +// "file": serializer.Response(), +// }) +// } +// +// +// } else { +// //TODO: return file itself +// } +//} +// +//func fileDeleteEp(c *gin.Context) { +// c.JSON(http.StatusOK, gin.H{ +// "message": "NOT implemented", +// }) +//} + + +func getRequestParams(c *gin.Context) (int, int, error){ + simulationID, err := strconv.Atoi(c.Param("SimulationID")) + + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulation ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return -1, -1, err + } + + var subID int + subID, err = GetModelID(c) + if err != nil{ + subID, err = GetWidgetID(c) + if err != nil { + return -1, -1, err + } + } + + return simulationID, subID, err +} \ No newline at end of file diff --git a/endpoints/modelEnpoints.go b/endpoints/modelEnpoints.go new file mode 100644 index 0000000..b858eff --- /dev/null +++ b/endpoints/modelEnpoints.go @@ -0,0 +1,59 @@ +package endpoints + +import ( + "fmt" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/model" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" +) + +func modelReadAllEp(c *gin.Context) { + allModels, _, _ := model.FindAllModels() + serializer := model.ModelsSerializerNoAssoc{c, allModels} + c.JSON(http.StatusOK, gin.H{ + "models": serializer.Response(), + }) +} + +func modelRegistrationEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func modelUpdateEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func modelReadEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func modelDeleteEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func GetModelID(c *gin.Context) (int, error) { + + modelID, err := strconv.Atoi(c.Param("ModelID")) + + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of model ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return -1, err + } else { + return modelID, err + + } +} + diff --git a/endpoints/registration.go b/endpoints/registration.go new file mode 100644 index 0000000..4a7f729 --- /dev/null +++ b/endpoints/registration.go @@ -0,0 +1,85 @@ +package endpoints + +import ( + "github.com/gin-gonic/gin" +) + +func SimulationsRegister(r *gin.RouterGroup) { + + r.GET("/", simulationReadAllEp) + r.POST("/", simulationRegistrationEp) + r.PUT("/:SimulationID", simulationUpdateEp) + r.GET("/:SimulationID", simulationReadEp) + r.DELETE("/:SimulationID", simulationDeleteEp) + + // Users + r.GET("/:SimulationID/users", userReadAllSimEp) + r.PUT("/:SimulationID/user/:username", userUpdateSimEp) + r.DELETE("/:SimulationID/user/:username", userDeleteSimEp) + + // Models + r.GET("/:SimulationID/models/", modelReadAllEp) + r.POST("/:SimulationID/models/", modelRegistrationEp) + r.PUT("/:SimulationID/models/:ModelID", modelUpdateEp) + r.GET("/:SimulationID/models/:ModelID", modelReadEp) + r.DELETE("/:SimulationID/models/:ModelID", modelDeleteEp) + + // Simulators + r.PUT("/:SimulationID/models/:ModelID/simulator", simulatorUpdateModelEp) // NEW in API + r.GET("/:SimulationID/models/:ModelID/simulator", simulatorReadModelEp) // NEW in API + + // Input and Output Samples + r.POST("/:SimulationID/models/:ModelID/Samples/:Direction", sampleRegistrationEp) // NEW in API + r.GET("/:SimulationID/models/:ModelID/Samples/:Direction", sampleReadAllEp) // NEW in API + r.PUT("/:SimulationID/models/:ModelID/Samples/:Direction", sampleUpdateEp) // NEW in API + r.DELETE("/:SimulationID/models/:ModelID/Samples/:Direction", sampleDeleteEp) // NEW in API + + // Visualizations + r.GET("/:SimulationID/visualizations", visualizationReadAllEp) + r.POST("/:SimulationID/visualization", visualizationRegistrationEp) + r.PUT("/:SimulationID/visualization/:visualizationID", visualizationUpdateEp) + r.GET("/:SimulationID/visualization/:visualizationID", visualizationReadEp) + r.DELETE("/:SimulationID/visualization/:visualizationID", visualizationDeleteEp) + + // Widgets + r.GET("/:SimulationID/visualization/:visualizationID/widgets", widgetReadAllEp) + r.POST("/:SimulationID/visualization/:visualizationID/widget", widgetRegistrationEp) + r.PUT("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetUpdateEp) + r.GET("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetReadEp) + r.DELETE("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetDeleteEp) + + // Files + // Files of Models + r.GET("/:SimulationID/models/:ModelID/files", fileMReadAllEp) // NEW in API + r.POST ("/:SimulationID/models/:ModelID/file", fileMRegistrationEp) // NEW in API + r.GET("/:SimulationID/models/:ModelID/file", fileMReadEp) // NEW in API + r.PUT("/:SimulationID/models/:ModelID/file", fileMUpdateEp) // NEW in API + r.DELETE("/:SimulationID/models/:ModelID/file", fileMDeleteEp) // NEW in API + + // Files of Widgets + r.GET("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/files", fileWReadAllEp) // NEW in API + r.POST ("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWRegistrationEp) // NEW in API + r.GET("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWReadEp) // NEW in API + r.PUT("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWUpdateEp) // NEW in API + r.DELETE("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWDeleteEp) // NEW in API + +} + +func UsersRegister(r *gin.RouterGroup) { + r.GET("/", userReadAllEp) + r.POST("/", userRegistrationEp) + r.PUT("/:UserID", userUpdateEp) + r.GET("/:UserID", userReadEp) + r.DELETE("/:UserID", userDeleteEp) + r.GET("/me", userSelfEp) +} + + +func SimulatorsRegister(r *gin.RouterGroup) { + r.GET("/", simulatorReadAllEp) + r.POST("/", simulatorRegistrationEp) + r.PUT("/:SimulatorID", simulatorUpdateEp) + r.GET("/:SimulatorID", simulatorReadEp) + r.DELETE("/:SimulatorID", simulatorDeleteEp) + r.POST("/:SimulatorID", simulatorSendActionEp) +} \ No newline at end of file diff --git a/endpoints/sampleEndpoints.go b/endpoints/sampleEndpoints.go new file mode 100644 index 0000000..38c17d9 --- /dev/null +++ b/endpoints/sampleEndpoints.go @@ -0,0 +1,30 @@ +package endpoints + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +func sampleRegistrationEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func sampleReadAllEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func sampleUpdateEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func sampleDeleteEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} diff --git a/endpoints/simulationEndpoints.go b/endpoints/simulationEndpoints.go new file mode 100644 index 0000000..e46490e --- /dev/null +++ b/endpoints/simulationEndpoints.go @@ -0,0 +1,40 @@ +package endpoints + +import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" + "github.com/gin-gonic/gin" + "net/http" +) + + +func simulationReadAllEp(c *gin.Context) { + allSimulations, _, _ := simulation.FindAllSimulations() + serializer := simulation.SimulationsSerializerNoAssoc{c, allSimulations} + c.JSON(http.StatusOK, gin.H{ + "simulations": serializer.Response(), + }) +} + +func simulationRegistrationEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func simulationUpdateEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func simulationReadEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func simulationDeleteEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} diff --git a/routes/simulator/simulatorEndpoints.go b/endpoints/simulatorEndpoints.go similarity index 58% rename from routes/simulator/simulatorEndpoints.go rename to endpoints/simulatorEndpoints.go index d8ce7ca..49fe3fb 100644 --- a/routes/simulator/simulatorEndpoints.go +++ b/endpoints/simulatorEndpoints.go @@ -1,22 +1,14 @@ -package simulator +package endpoints import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator" "github.com/gin-gonic/gin" "net/http" ) -func SimulatorsRegister(r *gin.RouterGroup) { - r.GET("/", simulatorsReadEp) - r.POST("/", simulatorRegistrationEp) - r.PUT("/:SimulatorID", simulatorUpdateEp) - r.GET("/:SimulatorID", simulatorReadEp) - r.DELETE("/:SimulatorID", simulatorDeleteEp) - r.POST("/:SimulatorID", simulatorSendActionEp) -} - -func simulatorsReadEp(c *gin.Context) { - allSimulators, _, _ := FindAllSimulators() - serializer := SimulatorsSerializer{c, allSimulators} +func simulatorReadAllEp(c *gin.Context) { + allSimulators, _, _ := simulator.FindAllSimulators() + serializer := simulator.SimulatorsSerializer{c, allSimulators} c.JSON(http.StatusOK, gin.H{ "simulators": serializer.Response(), }) @@ -34,12 +26,24 @@ func simulatorUpdateEp(c *gin.Context) { }) } +func simulatorUpdateModelEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + func simulatorReadEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } +func simulatorReadModelEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + func simulatorDeleteEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", @@ -50,4 +54,6 @@ func simulatorSendActionEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) -} \ No newline at end of file +} + + diff --git a/routes/user/userEndpoints.go b/endpoints/userEndpoints.go similarity index 50% rename from routes/user/userEndpoints.go rename to endpoints/userEndpoints.go index ab0a7b6..d7b6103 100644 --- a/routes/user/userEndpoints.go +++ b/endpoints/userEndpoints.go @@ -1,22 +1,24 @@ -package user +package endpoints import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user" "github.com/gin-gonic/gin" "net/http" ) -func UsersRegister(r *gin.RouterGroup) { - r.GET("/", usersReadEp) - r.POST("/", userRegistrationEp) - r.PUT("/:UserID", userUpdateEp) - r.GET("/:UserID", userReadEp) - r.DELETE("/:UserID", userDeleteEp) - //r.GET("/me", userSelfEp) // TODO: this conflicts with GET /:userID + + +func userReadAllEp(c *gin.Context) { + allUsers, _, _ := user.FindAllUsers() + serializer := user.UsersSerializer{c, allUsers} + c.JSON(http.StatusOK, gin.H{ + "users": serializer.Response(), + }) } -func usersReadEp(c *gin.Context) { - allUsers, _, _ := FindAllUsers() - serializer := UsersSerializer{c, allUsers} +func userReadAllSimEp(c *gin.Context) { + allUsers, _, _ := user.FindAllUsers() + serializer := user.UsersSerializer{c, allUsers} c.JSON(http.StatusOK, gin.H{ "users": serializer.Response(), }) @@ -34,6 +36,12 @@ func userUpdateEp(c *gin.Context) { }) } +func userUpdateSimEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + func userReadEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", @@ -46,6 +54,12 @@ func userDeleteEp(c *gin.Context) { }) } +func userDeleteSimEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + func userSelfEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", diff --git a/routes/visualization/visualizationEndpoints.go b/endpoints/visualizationEndpoints.go similarity index 56% rename from routes/visualization/visualizationEndpoints.go rename to endpoints/visualizationEndpoints.go index bfd5a52..b1a9165 100644 --- a/routes/visualization/visualizationEndpoints.go +++ b/endpoints/visualizationEndpoints.go @@ -1,21 +1,14 @@ -package visualization +package endpoints import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization" "github.com/gin-gonic/gin" "net/http" ) -func VisualizationsRegister(r *gin.RouterGroup) { - r.GET("/", visualizationsReadEp) - r.POST("/", visualizationRegistrationEp) - r.PUT("/:VisualizationID", visualizationUpdateEp) - r.GET("/:VisualizationID", visualizationReadEp) - r.DELETE("/:VisualizationID", visualizationDeleteEp) -} - -func visualizationsReadEp(c *gin.Context) { - allVisualizations, _, _ := FindAllVisualizations() - serializer := VisualizationsSerializer{c, allVisualizations} +func visualizationReadAllEp(c *gin.Context) { + allVisualizations, _, _ := visualization.FindAllVisualizations() + serializer := visualization.VisualizationsSerializer{c, allVisualizations} c.JSON(http.StatusOK, gin.H{ "visualizations": serializer.Response(), }) diff --git a/endpoints/widgetEndpoints.go b/endpoints/widgetEndpoints.go new file mode 100644 index 0000000..3be404f --- /dev/null +++ b/endpoints/widgetEndpoints.go @@ -0,0 +1,56 @@ +package endpoints + +import ( + "fmt" + "github.com/gin-gonic/gin" + "net/http" + "strconv" +) + + +func widgetReadAllEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func widgetRegistrationEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func widgetUpdateEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func widgetReadEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func widgetDeleteEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + + +func GetWidgetID(c *gin.Context) (int, error) { + + widgetID, err := strconv.Atoi(c.Param("WidgetID")) + + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of widget ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return -1, err + } else { + return widgetID, err + + } +} \ No newline at end of file diff --git a/routes/file/fileEndpoints.go b/routes/file/fileEndpoints.go deleted file mode 100644 index fbd020e..0000000 --- a/routes/file/fileEndpoints.go +++ /dev/null @@ -1,68 +0,0 @@ -package file - -import ( - "strconv" - - "net/http" - "github.com/gin-gonic/gin" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -func FilesRegister(r *gin.RouterGroup) { - r.GET("/", filesReadEp) - r.PUT("/:FileID", fileUpdateEp) // NEW in API - r.GET("/:FileID", fileReadEp) - r.DELETE("/:FileID", fileDeleteEp) -} - -func filesReadEp(c *gin.Context) { - // Database query - allFiles, _, err := FindAllFiles() - - if common.ProvideErrorResponse(c, err) == false { - serializer := FilesSerializerNoAssoc{c, allFiles} - c.JSON(http.StatusOK, gin.H{ - "files": serializer.Response(), - }) - } - -} - - - -func fileUpdateEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func fileReadEp(c *gin.Context) { - var err error - var file common.File - fileID := c.Param("FileID") - desc := c.GetHeader("X-Request-FileDesc") - desc_b, _ := strconv.ParseBool(desc) - - userID := 1 // TODO obtain ID of user making the request - - //check if description of file or file itself shall be returned - if desc_b { - file, err = FindFile(userID, fileID) - if common.ProvideErrorResponse(c, err) == false { - serializer := FileSerializerNoAssoc{c, file} - c.JSON(http.StatusOK, gin.H{ - "file": serializer.Response(), - }) - } - - - } else { - //TODO: return file itself - } -} - -func fileDeleteEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} diff --git a/routes/file/fileQueries.go b/routes/file/fileQueries.go index 72fe117..f4a1bcb 100644 --- a/routes/file/fileQueries.go +++ b/routes/file/fileQueries.go @@ -2,48 +2,78 @@ package file import ( "fmt" + "github.com/gin-gonic/gin" + _ "github.com/gin-gonic/gin" "io" "mime/multipart" "net/http" "os" "path/filepath" - "strconv" - - "github.com/gin-gonic/gin" - _ "github.com/gin-gonic/gin" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) -func FindAllFiles() ([]common.File, int, error) { +//func FindAllFiles() ([]common.File, int, error) { +// db := common.GetDB() +// var files []common.File +// err := db.Find(&files).Error +// if err != nil { +// // print error message to screen +// fmt.Println(fmt.Errorf("DB Error in FindAllFiles(): %q", err).Error()) +// } +// return files, len(files), err +//} +// +//func FindUserFiles(user *common.User) ([]common.File, int, error) { +// db := common.GetDB() +// var files []common.File +// err := db.Model(user).Related(&files, "Files").Error +// return files, len(files), err +//} +// +//func FindFile(userID int, fileID string) ( common.File, error) { +// var file common.File +// db := common.GetDB() +// fileID_i, _ := strconv.Atoi(fileID) +// +// err := db.First(&file, fileID_i).Error +// +// return file, err +// +//} + +func FindFiles(c *gin.Context, widgetID int, modelID int, simulationID int) ([]common.File, int, error){ db := common.GetDB() var files []common.File - err := db.Find(&files).Error - if err != nil { - // print error message to screen - fmt.Println(fmt.Errorf("DB Error in FindAllFiles(): %q", err).Error()) + var err error + + if widgetID != -1 { + var w common.Widget + err = db.First(&w, widgetID).Error + if err != nil { + return files, 0, err + } + err = db.Model(&w).Related(&files, "Files").Error + if err != nil { + return files, 0, err + } + + } else if modelID != -1 { + var m common.Model + err = db.First(&m, modelID).Error + if err != nil { + return files, 0, err + } + err = db.Model(&m).Related(&files, "Files").Error + if err != nil { + return files, 0, err + } + } + return files, len(files), err } -func FindUserFiles(user *common.User) ([]common.File, int, error) { - db := common.GetDB() - var files []common.File - err := db.Model(user).Related(&files, "Files").Error - return files, len(files), err -} - -func FindFile(userID int, fileID string) ( common.File, error) { - var file common.File - db := common.GetDB() - fileID_i, _ := strconv.Atoi(fileID) - - err := db.First(&file, fileID_i).Error - - return file, err - -} - func FindFileByPath(path string) (common.File, error) { var file common.File db := common.GetDB() @@ -158,6 +188,9 @@ func ReadFile(c *gin.Context, widgetID int, modelID int, simulationID int){ }) } + + + func DeleteFile(c *gin.Context, widgetID int, nmodelID int, simulationID int){ // TODO } diff --git a/routes/model/modelEnpoints.go b/routes/model/modelEnpoints.go deleted file mode 100644 index fee4298..0000000 --- a/routes/model/modelEnpoints.go +++ /dev/null @@ -1,148 +0,0 @@ -package model - -import ( - "fmt" - "net/http" - "strconv" - - "github.com/gin-gonic/gin" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" -) - -func ModelsRegister(r *gin.RouterGroup) { - r.GET("/:SimulationID/models/", modelsReadEp) - r.POST("/:SimulationID/models/", modelRegistrationEp) - - r.PUT("/:SimulationID/models/:ModelID", modelUpdateEp) - r.GET("/:SimulationID/models/:ModelID", modelReadEp) - r.DELETE("/:SimulationID/models/:ModelID", modelDeleteEp) - - // Files - r.POST ("/:SimulationID/models/:ModelID/file", modelRegisterFileEp) // NEW in API - r.GET("/:SimulationID/models/:ModelID/file", modelReadFileEp) // NEW in API - r.PUT("/:SimulationID/models/:ModelID/file", modelUpdateFileEp) // NEW in API - r.DELETE("/:SimulationID/models/:ModelID/file", modelDeleteFileEp) // NEW in API - - // Simulator - r.PUT("/:SimulationID/models/:ModelID/simulator", modelUpdateSimulatorEp) // NEW in API - r.GET("/:SimulationID/models/:ModelID/simulator", modelReadSimulatorEp) // NEW in API - - // Input and Output Samples - r.POST("/:SimulationID/models/:ModelID/Samples/:Direction", modelRegisterSamplesEp) // NEW in API - r.GET("/:SimulationID/models/:ModelID/Samples/:Direction", modelReadSamplesEp) // NEW in API - r.PUT("/:SimulationID/models/:ModelID/Samples/:Direction", modelUpdateSamplesEp) // NEW in API - r.DELETE("/:SimulationID/models/:ModelID/Samples/:Direction", modelDeleteSamplesEp) // NEW in API -} - -func modelsReadEp(c *gin.Context) { - allModels, _, _ := FindAllModels() - serializer := ModelsSerializerNoAssoc{c, allModels} - c.JSON(http.StatusOK, gin.H{ - "models": serializer.Response(), - }) -} - -func modelRegistrationEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func modelUpdateEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func modelReadEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func modelDeleteEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - - -func modelRegisterFileEp(c *gin.Context) { - - simulationID, modelID, err := getRequestParams(c) - if err != nil{ - return - } - - // Save file locally and register file in DB, HTTP response is set by this method - file.RegisterFile(c,-1, modelID, simulationID) - -} - -func modelReadFileEp(c *gin.Context) { - - simulationID, modelID, err := getRequestParams(c) - if err != nil{ - return - } - - // Read file from disk and return in HTTP response, no change to DB - file.ReadFile(c, -1, modelID, simulationID) -} - -func modelUpdateFileEp(c *gin.Context) { - - simulationID, modelID, err := getRequestParams(c) - if err != nil{ - return - } - - // Update file locally and update file entry in DB, HTTP response is set by this method - file.UpdateFile(c,-1, modelID, simulationID) -} - -func modelDeleteFileEp(c *gin.Context) { - - simulationID, modelID, err := getRequestParams(c) - if err != nil{ - return - } - - // Delete file from disk and remove entry from DB, HTTP response is set by this method - file.DeleteFile(c, -1, modelID, simulationID) - - -} - - -func GetModelID(c *gin.Context) (int, error) { - - modelID, err := strconv.Atoi(c.Param("ModelID")) - - if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulation model ID") - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return -1, err - } else { - return modelID, err - - } -} - -func getRequestParams(c *gin.Context) (int, int, error){ - simulationID, err := simulation.GetSimulationID(c) - if err != nil{ - return -1, -1, err - } - - modelID, err := GetModelID(c) - if err != nil{ - return -1, -1, err - } - - return simulationID, modelID, err -} \ No newline at end of file diff --git a/routes/model/modelSerializer.go b/routes/model/modelSerializer.go index 6096a87..2c8032d 100644 --- a/routes/model/modelSerializer.go +++ b/routes/model/modelSerializer.go @@ -29,8 +29,8 @@ type ModelResponseNoAssoc struct { Name string `json:"Name"` OutputLength int `json:"OutputLength"` InputLength int `json:"InputLength"` - BelongsToSimulationID uint `json:"BelongsToSimulationID"` - BelongsToSimulatorID uint `json:"BelongsToSimulatiorID"` + SimulationID uint `json:"SimulationID"` + SimulatorID uint `json:"SimulatiorID"` //StartParams postgres.Jsonb `json:"Starting Parameters"` //Output Mapping //Input Mapping @@ -41,8 +41,8 @@ func (self *ModelSerializerNoAssoc) Response() ModelResponseNoAssoc { Name: self.Name, OutputLength: self.OutputLength, InputLength: self.InputLength, - BelongsToSimulationID: self.BelongsToSimulationID, - BelongsToSimulatorID: self.BelongsToSimulatorID, + SimulationID: self.SimulationID, + SimulatorID: self.SimulatorID, //StartParams: self.StartParameters, //InputMapping //OutputMapping diff --git a/routes/project/projectEndpoints.go b/routes/project/projectEndpoints.go deleted file mode 100644 index a5fac47..0000000 --- a/routes/project/projectEndpoints.go +++ /dev/null @@ -1,46 +0,0 @@ -package project - -import ( - "github.com/gin-gonic/gin" - "net/http" -) - -func ProjectsRegister(r *gin.RouterGroup) { - r.GET("/", projectsReadEp) - r.POST("/", projectRegistrationEp) - r.PUT("/:ProjectID", projectUpdateEp) - r.GET("/:ProjectID", projectReadEp) - r.DELETE("/:ProjectID", projectDeleteEp) -} - -func projectsReadEp(c *gin.Context) { - allProjects, _, _ := FindAllProjects() - serializer := ProjectsSerializerNoAssoc{c, allProjects} - c.JSON(http.StatusOK, gin.H{ - "projects": serializer.Response(), - }) -} - -func projectRegistrationEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func projectUpdateEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func projectReadEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func projectDeleteEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} diff --git a/routes/project/projectQueries.go b/routes/project/projectQueries.go deleted file mode 100644 index c42cb5b..0000000 --- a/routes/project/projectQueries.go +++ /dev/null @@ -1,28 +0,0 @@ -package project - -import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -func FindAllProjects() ([]common.Project, int, error) { - db := common.GetDB() - var projects []common.Project - err := db.Find(&projects).Error - return projects, len(projects), err -} - -func FindUserProjects(user *common.User) ([]common.Project, int, error) { - db := common.GetDB() - var projects []common.Project - err := db.Model(user).Related(&projects, "Projects").Error - return projects, len(projects), err -} - -func FindVisualizationProject(visualization *common.Visualization) (common.Project, int, error) { - db := common.GetDB() - var project common.Project - err := db.Model(visualization).Related(&project, "Projects").Error - return project, 1, err -} - - diff --git a/routes/project/projectSerializer.go b/routes/project/projectSerializer.go deleted file mode 100644 index 753a79a..0000000 --- a/routes/project/projectSerializer.go +++ /dev/null @@ -1,41 +0,0 @@ -package project - -import ( - "github.com/gin-gonic/gin" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -type ProjectsSerializerNoAssoc struct { - Ctx *gin.Context - Projects []common.Project -} - -func (self *ProjectsSerializerNoAssoc) Response() []ProjectResponseNoAssoc { - response := []ProjectResponseNoAssoc{} - for _, project := range self.Projects { - serializer := ProjectSerializerNoAssoc{self.Ctx, project} - response = append(response, serializer.Response()) - } - return response -} - -type ProjectSerializerNoAssoc struct { - Ctx *gin.Context - common.Project -} - -type ProjectResponseNoAssoc struct { - Name string `json:"Name"` - ID uint `json:"ProjectID"` -} - -func (self *ProjectSerializerNoAssoc) Response() ProjectResponseNoAssoc { - response := ProjectResponseNoAssoc{ - Name: self.Name, - ID: self.ID, - } - return response -} - - diff --git a/routes/sample/sampleEndpoints.go b/routes/sample/sampleEndpoints.go deleted file mode 100644 index ca6c759..0000000 --- a/routes/sample/sampleEndpoints.go +++ /dev/null @@ -1,4 +0,0 @@ -package sample - - -//TODO extend API with sample endpoints \ No newline at end of file diff --git a/routes/sample/sampleQueries.go b/routes/sample/sampleQueries.go index 3f86c11..4079b78 100644 --- a/routes/sample/sampleQueries.go +++ b/routes/sample/sampleQueries.go @@ -1,3 +1 @@ package sample - -//TODO extend API with sample endpoints \ No newline at end of file diff --git a/routes/sample/sampleSerializer.go b/routes/sample/sampleSerializer.go index 3f86c11..4079b78 100644 --- a/routes/sample/sampleSerializer.go +++ b/routes/sample/sampleSerializer.go @@ -1,3 +1 @@ package sample - -//TODO extend API with sample endpoints \ No newline at end of file diff --git a/routes/simulation/simulationEndpoints.go b/routes/simulation/simulationEndpoints.go deleted file mode 100644 index fc05749..0000000 --- a/routes/simulation/simulationEndpoints.go +++ /dev/null @@ -1,67 +0,0 @@ -package simulation - -import ( - "fmt" - "net/http" - "strconv" - - "github.com/gin-gonic/gin" -) - -func SimulationsRegister(r *gin.RouterGroup) { - - //simulations - r.GET("/", simulationsReadEp) - r.POST("/", simulationRegistrationEp) - r.PUT("/:SimulationID", simulationUpdateEp) - r.GET("/:SimulationID", simulationReadEp) - r.DELETE("/:SimulationID", simulationDeleteEp) -} - -func simulationsReadEp(c *gin.Context) { - allSimulations, _, _ := FindAllSimulations() - serializer := SimulationsSerializerNoAssoc{c, allSimulations} - c.JSON(http.StatusOK, gin.H{ - "simulations": serializer.Response(), - }) -} - -func simulationRegistrationEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func simulationUpdateEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func simulationReadEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func simulationDeleteEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func GetSimulationID(c *gin.Context) (int, error) { - - simulationID, err := strconv.Atoi(c.Param("SimulationID")) - - if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulation ID") - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return -1, err - } else { - return simulationID, err - - } -} diff --git a/routes/user/userSerializer.go b/routes/user/userSerializer.go index 31fac71..b5ecb7b 100644 --- a/routes/user/userSerializer.go +++ b/routes/user/userSerializer.go @@ -4,9 +4,7 @@ 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/project" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" ) type UsersSerializer struct { @@ -33,30 +31,22 @@ type UserResponse struct { Password string `json:"Password"` // XXX: ??? Role string `json:"Role"` Mail string `json:"Mail"` - Projects []project.ProjectResponseNoAssoc Simulations []simulation.SimulationResponseNoAssoc - Files []file.FileResponseNoAssoc } func (self *UserSerializer) Response() UserResponse { // TODO: maybe all those should be made in one transaction - projects, _, _ := project.FindUserProjects(&self.User) - projectsSerializer := project.ProjectsSerializerNoAssoc{self.Ctx, projects} simulations, _, _ := simulation.FindUserSimulations(&self.User) simulationsSerializer := simulation.SimulationsSerializerNoAssoc{self.Ctx, simulations} - files, _, _ := file.FindUserFiles(&self.User) - filesSerializer := file.FilesSerializerNoAssoc{self.Ctx, files} response := UserResponse{ Username: self.Username, Password: self.Password, Role: self.Role, Mail: self.Mail, - Projects: projectsSerializer.Response(), Simulations: simulationsSerializer.Response(), - Files: filesSerializer.Response(), } return response } diff --git a/routes/visualization/visualizationSerializer.go b/routes/visualization/visualizationSerializer.go index 968b21e..f9dcdd8 100644 --- a/routes/visualization/visualizationSerializer.go +++ b/routes/visualization/visualizationSerializer.go @@ -4,7 +4,6 @@ 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/project" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget" ) @@ -29,29 +28,20 @@ type VisualizationSerializer struct { type VisualizationResponse struct { Name string `json:"Name"` - UserID uint `json:"UserID"` Grid int `json:"Grid"` - ProjectID uint `json:"ProjectID"` - Project project.ProjectResponseNoAssoc + SimulationID uint `json:"SimulationID"` Widgets []widget.WidgetResponse } func (self *VisualizationSerializer) Response() VisualizationResponse { - // TODO: maybe all those should be made in one transaction - p, _, _ := project.FindVisualizationProject(&self.Visualization) - projectSerializer := project.ProjectSerializerNoAssoc{self.Ctx, p} - w, _, _:= widget.FindVisualizationWidgets(&self.Visualization) widgetsSerializer := widget.WidgetsSerializer{self.Ctx, w} - response := VisualizationResponse{ Name: self.Name, - UserID: self.UserID, Grid: self.Grid, - ProjectID: self.ProjectID, - Project: projectSerializer.Response(), + SimulationID: self.SimulationID, Widgets: widgetsSerializer.Response(), } return response diff --git a/routes/widget/widgetEndpoints.go b/routes/widget/widgetEndpoints.go deleted file mode 100644 index 950ec4d..0000000 --- a/routes/widget/widgetEndpoints.go +++ /dev/null @@ -1,3 +0,0 @@ -package widget - -// TODO add new API endpoints for widgets diff --git a/start.go b/start.go index 33eb69a..673530c 100644 --- a/start.go +++ b/start.go @@ -1,12 +1,10 @@ package main import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/endpoints" "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/simulation" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user" ) func main() { @@ -18,12 +16,12 @@ func main() { r := gin.Default() api := r.Group("/api") - user.UsersRegister(api.Group("/users")) + endpoints.UsersRegister(api.Group("/users")) //file.FilesRegister(api.Group("/files")) //project.ProjectsRegister(api.Group("/projects")) - simulation.SimulationsRegister(api.Group("/simulations")) - //model.ModelsRegister(api.Group("/models")) - simulator.SimulatorsRegister(api.Group("/simulators")) + endpoints.SimulationsRegister(api.Group("/simulations")) + //model.ModelsRegister(api.Group("/simulations")) + endpoints.SimulatorsRegister(api.Group("/simulators")) //visualization.VisualizationsRegister(api.Group("/visualizations")) From 5409c6193974db8c5efd7e078df0a61d99935996 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 21 May 2019 14:37:45 +0200 Subject: [PATCH 08/30] Major changes: - continue revision of folder structure - work on model endpoints - add skeletons for clone endpoints --- common/datastructs.go | 16 +- endpoints/fileEndpoints.go | 43 +++-- endpoints/modelEndpoints.go | 166 ++++++++++++++++++ endpoints/modelEnpoints.go | 59 ------- endpoints/registration.go | 6 + endpoints/simulationEndpoints.go | 32 +++- endpoints/simulatorEndpoints.go | 7 +- endpoints/userEndpoints.go | 11 +- endpoints/visualizationEndpoints.go | 13 +- endpoints/widgetEndpoints.go | 6 + {routes/file => queries}/fileQueries.go | 2 +- queries/modelQueries.go | 93 ++++++++++ queries/sampleQueries.go | 1 + .../simulationQueries.go | 9 +- .../simulator => queries}/simulatorQueries.go | 2 +- {routes/user => queries}/userQueries.go | 2 +- .../visualizationQueries.go | 2 +- {routes/widget => queries}/widgetQueries.go | 2 +- routes/model/modelQueries.go | 12 -- routes/sample/sampleQueries.go | 1 - routes/sample/sampleSerializer.go | 1 - .../file => serializers}/fileSerializer.go | 2 +- .../model => serializers}/modelSerializer.go | 24 +-- serializers/sampleSerializer.go | 1 + .../simulationSerializer.go | 2 +- .../simulatorSerializer.go | 2 +- .../user => serializers}/userSerializer.go | 10 +- .../visualizationSerializer.go | 10 +- .../widgetSerializer.go | 2 +- 29 files changed, 400 insertions(+), 139 deletions(-) create mode 100644 endpoints/modelEndpoints.go delete mode 100644 endpoints/modelEnpoints.go rename {routes/file => queries}/fileQueries.go (99%) create mode 100644 queries/modelQueries.go create mode 100644 queries/sampleQueries.go rename {routes/simulation => queries}/simulationQueries.go (74%) rename {routes/simulator => queries}/simulatorQueries.go (93%) rename {routes/user => queries}/userQueries.go (93%) rename {routes/visualization => queries}/visualizationQueries.go (93%) rename {routes/widget => queries}/widgetQueries.go (95%) delete mode 100644 routes/model/modelQueries.go delete mode 100644 routes/sample/sampleQueries.go delete mode 100644 routes/sample/sampleSerializer.go rename {routes/file => serializers}/fileSerializer.go (98%) rename {routes/model => serializers}/modelSerializer.go (61%) create mode 100644 serializers/sampleSerializer.go rename {routes/simulation => serializers}/simulationSerializer.go (98%) rename {routes/simulator => serializers}/simulatorSerializer.go (98%) rename {routes/user => serializers}/userSerializer.go (77%) rename {routes/visualization => serializers}/visualizationSerializer.go (79%) rename {routes/widget => serializers}/widgetSerializer.go (98%) diff --git a/common/datastructs.go b/common/datastructs.go index 190c41b..85049d2 100644 --- a/common/datastructs.go +++ b/common/datastructs.go @@ -23,7 +23,7 @@ type Simulation struct { ID uint `gorm:"primary_key;auto_increment"` Name string `gorm:"not null"` Running bool `gorm:"default:false"` - StartParameters postgres.Jsonb // TODO default value + StartParameters postgres.Jsonb `gorm:"default:{}"` Users []User `gorm:"not null;many2many:user_simulations"` Models []Model `gorm:"foreignkey:SimulationID"` @@ -36,11 +36,11 @@ type Model struct { Name string `gorm:"not null"` OutputLength int `gorm:"default:1"` InputLength int `gorm:"default:1"` - StartParameters postgres.Jsonb // TODO: default value? + StartParameters postgres.Jsonb `gorm:"default:{}"` - SimulationID uint `gorm:"not null"` - Simulator Simulator `gorm:"not null"` - SimulatorID uint `gorm:"not null"` + SimulationID uint + Simulator Simulator + SimulatorID uint // NOTE: order of samples is important OutputMapping []Sample `gorm:"foreignkey:ModelID"` @@ -71,8 +71,8 @@ type Simulator struct { Uptime int `gorm:"default:0"` State string `gorm:"default:''"` StateUpdateAt time.Time - Properties postgres.Jsonb // TODO: default value? - RawProperties postgres.Jsonb // TODO: default value? + Properties postgres.Jsonb `gorm:"default:{}"` + RawProperties postgres.Jsonb `gorm:"default:{}"` } type Visualization struct { @@ -99,7 +99,7 @@ type Widget struct { Y int `gorm:"not null"` Z int `gorm:"not null"` IsLocked bool `gorm:"default:false"` - CustomProperties postgres.Jsonb // TODO: default value? + CustomProperties postgres.Jsonb `gorm:"default:{}"` VisualizationID uint `gorm:"not null"` //new in villasweb 2.0 Files []File `gorm:"foreignkey:WidgetID"` diff --git a/endpoints/fileEndpoints.go b/endpoints/fileEndpoints.go index 3c859a3..858e4da 100644 --- a/endpoints/fileEndpoints.go +++ b/endpoints/fileEndpoints.go @@ -2,7 +2,8 @@ package endpoints import ( "fmt" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" "strconv" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" @@ -20,10 +21,10 @@ func fileMReadAllEp(c *gin.Context) { } // Find files' properties in DB and return in HTTP response, no change to DB - allFiles, _, err := file.FindFiles(c, -1, modelID, simulationID) + allFiles, _, err := queries.FindFiles(c, -1, modelID, simulationID) if common.ProvideErrorResponse(c, err) == false { - serializer := file.FilesSerializerNoAssoc{c, allFiles} + serializer := serializers.FilesSerializerNoAssoc{c, allFiles} c.JSON(http.StatusOK, gin.H{ "files": serializer.Response(), }) @@ -39,7 +40,15 @@ func fileMRegistrationEp(c *gin.Context) { } // Save file locally and register file in DB, HTTP response is set by this method - file.RegisterFile(c,-1, modelID, simulationID) + queries.RegisterFile(c,-1, modelID, simulationID) + +} + +func fileMCloneEp(c *gin.Context) { + + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) } @@ -51,7 +60,7 @@ func fileMReadEp(c *gin.Context) { } // Read file from disk and return in HTTP response, no change to DB - file.ReadFile(c, -1, modelID, simulationID) + queries.ReadFile(c, -1, modelID, simulationID) } func fileMUpdateEp(c *gin.Context) { @@ -62,7 +71,7 @@ func fileMUpdateEp(c *gin.Context) { } // Update file locally and update file entry in DB, HTTP response is set by this method - file.UpdateFile(c,-1, modelID, simulationID) + queries.UpdateFile(c,-1, modelID, simulationID) } func fileMDeleteEp(c *gin.Context) { @@ -73,7 +82,7 @@ func fileMDeleteEp(c *gin.Context) { } // Delete file from disk and remove entry from DB, HTTP response is set by this method - file.DeleteFile(c, -1, modelID, simulationID) + queries.DeleteFile(c, -1, modelID, simulationID) } @@ -86,10 +95,10 @@ func fileWReadAllEp(c *gin.Context) { } // Find files' properties in DB and return in HTTP response, no change to DB - allFiles, _, err := file.FindFiles(c, widgetID, -1, simulationID) + allFiles, _, err := queries.FindFiles(c, widgetID, -1, simulationID) if common.ProvideErrorResponse(c, err) == false { - serializer := file.FilesSerializerNoAssoc{c, allFiles} + serializer := serializers.FilesSerializerNoAssoc{c, allFiles} c.JSON(http.StatusOK, gin.H{ "files": serializer.Response(), }) @@ -105,7 +114,15 @@ func fileWRegistrationEp(c *gin.Context) { } // Save file locally and register file in DB, HTTP response is set by this method - file.RegisterFile(c,widgetID, -1, simulationID) + queries.RegisterFile(c,widgetID, -1, simulationID) + +} + +func fileWCloneEp(c *gin.Context) { + + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) } @@ -117,7 +134,7 @@ func fileWReadEp(c *gin.Context) { } // Read file from disk and return in HTTP response, no change to DB - file.ReadFile(c, widgetID, -1, simulationID) + queries.ReadFile(c, widgetID, -1, simulationID) } func fileWUpdateEp(c *gin.Context) { @@ -128,7 +145,7 @@ func fileWUpdateEp(c *gin.Context) { } // Update file locally and update file entry in DB, HTTP response is set by this method - file.UpdateFile(c,widgetID, -1, simulationID) + queries.UpdateFile(c,widgetID, -1, simulationID) } func fileWDeleteEp(c *gin.Context) { @@ -139,7 +156,7 @@ func fileWDeleteEp(c *gin.Context) { } // Delete file from disk and remove entry from DB, HTTP response is set by this method - file.DeleteFile(c, widgetID, -1, simulationID) + queries.DeleteFile(c, widgetID, -1, simulationID) } diff --git a/endpoints/modelEndpoints.go b/endpoints/modelEndpoints.go new file mode 100644 index 0000000..0f2ee47 --- /dev/null +++ b/endpoints/modelEndpoints.go @@ -0,0 +1,166 @@ +package endpoints + +import ( + "fmt" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" +) + +func modelReadAllEp(c *gin.Context) { + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + allModels, _, err := queries.FindAllModels(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + serializer := serializers.ModelsSerializer{c, allModels} + c.JSON(http.StatusOK, gin.H{ + "models": serializer.Response(), + }) +} + +func modelRegistrationEp(c *gin.Context) { + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + var m common.Model + err = c.BindJSON(&m) + if err != nil { + errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + err = queries.AddModel(simID, &m) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } +} + +func modelCloneEp(c *gin.Context) { + + modelID, err := GetModelID(c) + if err != nil { + return + } + + targetSimID, err := strconv.Atoi(c.PostForm("TargetSim")) + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of target sim ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + err = queries.CloneModel(targetSimID, modelID) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "Not implemented.", + }) + } + +} + +func modelUpdateEp(c *gin.Context) { + + //simID, err := GetSimulationID(c) + //if err != nil { + // return + //} + + modelID, err := GetModelID(c) + if err != nil { + return + } + + var m common.Model + err = c.BindJSON(&m) + if err != nil { + errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + err = queries.UpdateModel(modelID, &m) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } + +} + +func modelReadEp(c *gin.Context) { + + modelID, err := GetModelID(c) + if err != nil { + return + } + + m, err := queries.FindModel(modelID) + if common.ProvideErrorResponse(c, err) { + return + } + + serializer := serializers.ModelSerializer{c, m} + c.JSON(http.StatusOK, gin.H{ + "model": serializer.Response(), + }) +} + +func modelDeleteEp(c *gin.Context) { + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + modelID, err := GetModelID(c) + if err != nil { + return + } + + err = queries.DeleteModel(simID, modelID) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } +} + +func GetModelID(c *gin.Context) (int, error) { + + modelID, err := strconv.Atoi(c.Param("ModelID")) + + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of model ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return -1, err + } else { + return modelID, err + + } +} + diff --git a/endpoints/modelEnpoints.go b/endpoints/modelEnpoints.go deleted file mode 100644 index b858eff..0000000 --- a/endpoints/modelEnpoints.go +++ /dev/null @@ -1,59 +0,0 @@ -package endpoints - -import ( - "fmt" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/model" - "net/http" - "strconv" - - "github.com/gin-gonic/gin" -) - -func modelReadAllEp(c *gin.Context) { - allModels, _, _ := model.FindAllModels() - serializer := model.ModelsSerializerNoAssoc{c, allModels} - c.JSON(http.StatusOK, gin.H{ - "models": serializer.Response(), - }) -} - -func modelRegistrationEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func modelUpdateEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func modelReadEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func modelDeleteEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func GetModelID(c *gin.Context) (int, error) { - - modelID, err := strconv.Atoi(c.Param("ModelID")) - - if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of model ID") - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return -1, err - } else { - return modelID, err - - } -} - diff --git a/endpoints/registration.go b/endpoints/registration.go index 4a7f729..9bb92a2 100644 --- a/endpoints/registration.go +++ b/endpoints/registration.go @@ -8,6 +8,7 @@ func SimulationsRegister(r *gin.RouterGroup) { r.GET("/", simulationReadAllEp) r.POST("/", simulationRegistrationEp) + r.POST("/:SimulationID", simulationCloneEp) r.PUT("/:SimulationID", simulationUpdateEp) r.GET("/:SimulationID", simulationReadEp) r.DELETE("/:SimulationID", simulationDeleteEp) @@ -20,6 +21,7 @@ func SimulationsRegister(r *gin.RouterGroup) { // Models r.GET("/:SimulationID/models/", modelReadAllEp) r.POST("/:SimulationID/models/", modelRegistrationEp) + r.POST("/:SimulationID/models/:ModelID", modelCloneEp) r.PUT("/:SimulationID/models/:ModelID", modelUpdateEp) r.GET("/:SimulationID/models/:ModelID", modelReadEp) r.DELETE("/:SimulationID/models/:ModelID", modelDeleteEp) @@ -37,6 +39,7 @@ func SimulationsRegister(r *gin.RouterGroup) { // Visualizations r.GET("/:SimulationID/visualizations", visualizationReadAllEp) r.POST("/:SimulationID/visualization", visualizationRegistrationEp) + r.POST("/:SimulationID/visualization/:visualizationID", visualizationCloneEp) r.PUT("/:SimulationID/visualization/:visualizationID", visualizationUpdateEp) r.GET("/:SimulationID/visualization/:visualizationID", visualizationReadEp) r.DELETE("/:SimulationID/visualization/:visualizationID", visualizationDeleteEp) @@ -44,6 +47,7 @@ func SimulationsRegister(r *gin.RouterGroup) { // Widgets r.GET("/:SimulationID/visualization/:visualizationID/widgets", widgetReadAllEp) r.POST("/:SimulationID/visualization/:visualizationID/widget", widgetRegistrationEp) + r.POST("/:SimulationID/visualization/:visualizationID/widget:widgetID", widgetCloneEp) r.PUT("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetUpdateEp) r.GET("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetReadEp) r.DELETE("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetDeleteEp) @@ -52,6 +56,7 @@ func SimulationsRegister(r *gin.RouterGroup) { // Files of Models r.GET("/:SimulationID/models/:ModelID/files", fileMReadAllEp) // NEW in API r.POST ("/:SimulationID/models/:ModelID/file", fileMRegistrationEp) // NEW in API + r.POST ("/:SimulationID/models/:ModelID/file", fileMCloneEp) // NEW in API r.GET("/:SimulationID/models/:ModelID/file", fileMReadEp) // NEW in API r.PUT("/:SimulationID/models/:ModelID/file", fileMUpdateEp) // NEW in API r.DELETE("/:SimulationID/models/:ModelID/file", fileMDeleteEp) // NEW in API @@ -59,6 +64,7 @@ func SimulationsRegister(r *gin.RouterGroup) { // Files of Widgets r.GET("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/files", fileWReadAllEp) // NEW in API r.POST ("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWRegistrationEp) // NEW in API + r.POST ("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWCloneEp) // NEW in API r.GET("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWReadEp) // NEW in API r.PUT("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWUpdateEp) // NEW in API r.DELETE("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWDeleteEp) // NEW in API diff --git a/endpoints/simulationEndpoints.go b/endpoints/simulationEndpoints.go index e46490e..3a0e8b6 100644 --- a/endpoints/simulationEndpoints.go +++ b/endpoints/simulationEndpoints.go @@ -1,15 +1,18 @@ package endpoints import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" + "fmt" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" "github.com/gin-gonic/gin" "net/http" + "strconv" ) func simulationReadAllEp(c *gin.Context) { - allSimulations, _, _ := simulation.FindAllSimulations() - serializer := simulation.SimulationsSerializerNoAssoc{c, allSimulations} + allSimulations, _, _ := queries.FindAllSimulations() + serializer := serializers.SimulationsSerializerNoAssoc{c, allSimulations} c.JSON(http.StatusOK, gin.H{ "simulations": serializer.Response(), }) @@ -21,6 +24,12 @@ func simulationRegistrationEp(c *gin.Context) { }) } +func simulationCloneEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + func simulationUpdateEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", @@ -38,3 +47,20 @@ func simulationDeleteEp(c *gin.Context) { "message": "NOT implemented", }) } + + +func GetSimulationID(c *gin.Context) (int, error) { + + simID, err := strconv.Atoi(c.Param("SimulationID")) + + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulation ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return -1, err + } else { + return simID, err + + } +} \ No newline at end of file diff --git a/endpoints/simulatorEndpoints.go b/endpoints/simulatorEndpoints.go index 49fe3fb..2f40330 100644 --- a/endpoints/simulatorEndpoints.go +++ b/endpoints/simulatorEndpoints.go @@ -1,14 +1,15 @@ package endpoints import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" "github.com/gin-gonic/gin" "net/http" ) func simulatorReadAllEp(c *gin.Context) { - allSimulators, _, _ := simulator.FindAllSimulators() - serializer := simulator.SimulatorsSerializer{c, allSimulators} + allSimulators, _, _ := queries.FindAllSimulators() + serializer := serializers.SimulatorsSerializer{c, allSimulators} c.JSON(http.StatusOK, gin.H{ "simulators": serializer.Response(), }) diff --git a/endpoints/userEndpoints.go b/endpoints/userEndpoints.go index d7b6103..c63412e 100644 --- a/endpoints/userEndpoints.go +++ b/endpoints/userEndpoints.go @@ -1,7 +1,8 @@ package endpoints import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" "github.com/gin-gonic/gin" "net/http" ) @@ -9,16 +10,16 @@ import ( func userReadAllEp(c *gin.Context) { - allUsers, _, _ := user.FindAllUsers() - serializer := user.UsersSerializer{c, allUsers} + allUsers, _, _ := queries.FindAllUsers() + serializer := serializers.UsersSerializer{c, allUsers} c.JSON(http.StatusOK, gin.H{ "users": serializer.Response(), }) } func userReadAllSimEp(c *gin.Context) { - allUsers, _, _ := user.FindAllUsers() - serializer := user.UsersSerializer{c, allUsers} + allUsers, _, _ := queries.FindAllUsers() + serializer := serializers.UsersSerializer{c, allUsers} c.JSON(http.StatusOK, gin.H{ "users": serializer.Response(), }) diff --git a/endpoints/visualizationEndpoints.go b/endpoints/visualizationEndpoints.go index b1a9165..7271ef2 100644 --- a/endpoints/visualizationEndpoints.go +++ b/endpoints/visualizationEndpoints.go @@ -1,14 +1,15 @@ package endpoints import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" "github.com/gin-gonic/gin" "net/http" ) func visualizationReadAllEp(c *gin.Context) { - allVisualizations, _, _ := visualization.FindAllVisualizations() - serializer := visualization.VisualizationsSerializer{c, allVisualizations} + allVisualizations, _, _ := queries.FindAllVisualizations() + serializer := serializers.VisualizationsSerializer{c, allVisualizations} c.JSON(http.StatusOK, gin.H{ "visualizations": serializer.Response(), }) @@ -20,6 +21,12 @@ func visualizationRegistrationEp(c *gin.Context) { }) } +func visualizationCloneEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + func visualizationUpdateEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", diff --git a/endpoints/widgetEndpoints.go b/endpoints/widgetEndpoints.go index 3be404f..33d9029 100644 --- a/endpoints/widgetEndpoints.go +++ b/endpoints/widgetEndpoints.go @@ -20,6 +20,12 @@ func widgetRegistrationEp(c *gin.Context) { }) } +func widgetCloneEp(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + func widgetUpdateEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", diff --git a/routes/file/fileQueries.go b/queries/fileQueries.go similarity index 99% rename from routes/file/fileQueries.go rename to queries/fileQueries.go index f4a1bcb..340c78f 100644 --- a/routes/file/fileQueries.go +++ b/queries/fileQueries.go @@ -1,4 +1,4 @@ -package file +package queries import ( "fmt" diff --git a/queries/modelQueries.go b/queries/modelQueries.go new file mode 100644 index 0000000..97d9b8c --- /dev/null +++ b/queries/modelQueries.go @@ -0,0 +1,93 @@ +package queries + +import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" +) + +func FindAllModels(simID int) ([]common.Model, int, error) { + db := common.GetDB() + var models []common.Model + sim, err := FindSimulation(simID) + if err != nil { + return models, 0, err + } + + err = db.Model(sim).Related(&models, "Models").Error + + return models, len(models), err +} + +func FindModel(modelID int) (common.Model, error){ + db := common.GetDB() + var m common.Model + err := db.First(&m, modelID).Error + return m, err +} + +func AddModel(simID int, model *common.Model) error { + db := common.GetDB() + sim, err := FindSimulation(simID) + if err != nil { + return err + } + + err = db.Model(&sim).Association("Models").Append(model).Error + return err +} + +func CloneModel(targetSim int, modelID int) error { + + // TODO TO BE IMPLEMENTED + // Check if target sim exists + // Check if model exists + + // Get all Samples of Model + // Get Simulator of Model + // Get Files of model + + // Add new model object to DB and associate with target sim + // Add new sample objects to DB and associate with new model object (careful with directions) + // Associate Simulator with new Model object + var err error + return err + +} + +func UpdateModel(modelID int , modelUpdate *common.Model) error { + db := common.GetDB() + m, err := FindModel(modelID) + if err != nil { + return err + } + // only Name and Start Params can be updated directly by the user + err = db.Model(&m).Updates(map[string]interface{}{"Name": modelUpdate.Name, "StartParameters": modelUpdate.StartParameters}).Error + return err +} + +func DeleteModel(simID int , modelID int ) error { + db := common.GetDB() + sim, err := FindSimulation(simID) + if err != nil { + return err + } + + m, err := FindModel(modelID) + if err != nil { + return err + } + + //remove relationship between simulation and model + err = db.Model(&sim).Association("Models").Delete(m).Error + if err != nil { + return err + } + + // TODO remove File Associations and files on disk + // TODO remove Sample Associations and Samples in DB + // TODO how to treat simulator association? + + //remove model itself from DB + //TODO: do we want this?? + err = db.Delete(&m).Error + return err +} \ No newline at end of file diff --git a/queries/sampleQueries.go b/queries/sampleQueries.go new file mode 100644 index 0000000..58eadae --- /dev/null +++ b/queries/sampleQueries.go @@ -0,0 +1 @@ +package queries diff --git a/routes/simulation/simulationQueries.go b/queries/simulationQueries.go similarity index 74% rename from routes/simulation/simulationQueries.go rename to queries/simulationQueries.go index 58b88ce..94dcc63 100644 --- a/routes/simulation/simulationQueries.go +++ b/queries/simulationQueries.go @@ -1,4 +1,4 @@ -package simulation +package queries import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" @@ -17,3 +17,10 @@ func FindUserSimulations(user *common.User) ([]common.Simulation, int, error) { err := db.Model(user).Related(&simulations, "Simulations").Error return simulations, len(simulations), err } + +func FindSimulation(simID int) (common.Simulation, error) { + db := common.GetDB() + var sim common.Simulation + err := db.First(&sim, simID).Error + return sim, err +} diff --git a/routes/simulator/simulatorQueries.go b/queries/simulatorQueries.go similarity index 93% rename from routes/simulator/simulatorQueries.go rename to queries/simulatorQueries.go index 92220d9..bcedf27 100644 --- a/routes/simulator/simulatorQueries.go +++ b/queries/simulatorQueries.go @@ -1,4 +1,4 @@ -package simulator +package queries import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" diff --git a/routes/user/userQueries.go b/queries/userQueries.go similarity index 93% rename from routes/user/userQueries.go rename to queries/userQueries.go index 4c45c94..fec1af3 100644 --- a/routes/user/userQueries.go +++ b/queries/userQueries.go @@ -1,4 +1,4 @@ -package user +package queries import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" diff --git a/routes/visualization/visualizationQueries.go b/queries/visualizationQueries.go similarity index 93% rename from routes/visualization/visualizationQueries.go rename to queries/visualizationQueries.go index a1f97db..10a40a8 100644 --- a/routes/visualization/visualizationQueries.go +++ b/queries/visualizationQueries.go @@ -1,4 +1,4 @@ -package visualization +package queries import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" diff --git a/routes/widget/widgetQueries.go b/queries/widgetQueries.go similarity index 95% rename from routes/widget/widgetQueries.go rename to queries/widgetQueries.go index 3d64294..a3391b4 100644 --- a/routes/widget/widgetQueries.go +++ b/queries/widgetQueries.go @@ -1,4 +1,4 @@ -package widget +package queries import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" diff --git a/routes/model/modelQueries.go b/routes/model/modelQueries.go deleted file mode 100644 index 6e3a10c..0000000 --- a/routes/model/modelQueries.go +++ /dev/null @@ -1,12 +0,0 @@ -package model - -import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -func FindAllModels() ([]common.Model, int, error) { - db := common.GetDB() - var models []common.Model - err := db.Find(&models).Error - return models, len(models), err -} diff --git a/routes/sample/sampleQueries.go b/routes/sample/sampleQueries.go deleted file mode 100644 index 4079b78..0000000 --- a/routes/sample/sampleQueries.go +++ /dev/null @@ -1 +0,0 @@ -package sample diff --git a/routes/sample/sampleSerializer.go b/routes/sample/sampleSerializer.go deleted file mode 100644 index 4079b78..0000000 --- a/routes/sample/sampleSerializer.go +++ /dev/null @@ -1 +0,0 @@ -package sample diff --git a/routes/file/fileSerializer.go b/serializers/fileSerializer.go similarity index 98% rename from routes/file/fileSerializer.go rename to serializers/fileSerializer.go index 16d85ae..2b30206 100644 --- a/routes/file/fileSerializer.go +++ b/serializers/fileSerializer.go @@ -1,4 +1,4 @@ -package file +package serializers import ( "github.com/gin-gonic/gin" diff --git a/routes/model/modelSerializer.go b/serializers/modelSerializer.go similarity index 61% rename from routes/model/modelSerializer.go rename to serializers/modelSerializer.go index 2c8032d..0f91387 100644 --- a/routes/model/modelSerializer.go +++ b/serializers/modelSerializer.go @@ -1,49 +1,51 @@ -package model +package serializers import ( "github.com/gin-gonic/gin" + "github.com/jinzhu/gorm/dialects/postgres" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) -type ModelsSerializerNoAssoc struct { +type ModelsSerializer struct { Ctx *gin.Context Models []common.Model } -func (self *ModelsSerializerNoAssoc) Response() []ModelResponseNoAssoc { - response := []ModelResponseNoAssoc{} +func (self *ModelsSerializer) Response() []ModelResponse { + response := []ModelResponse{} for _, model := range self.Models { - serializer := ModelSerializerNoAssoc{self.Ctx, model} + serializer := ModelSerializer{self.Ctx, model} response = append(response, serializer.Response()) } return response } -type ModelSerializerNoAssoc struct { +type ModelSerializer struct { Ctx *gin.Context common.Model } -type ModelResponseNoAssoc struct { +type ModelResponse struct { Name string `json:"Name"` OutputLength int `json:"OutputLength"` InputLength int `json:"InputLength"` SimulationID uint `json:"SimulationID"` - SimulatorID uint `json:"SimulatiorID"` + SimulatorID uint `json:"SimulatorID"` + StartParams postgres.Jsonb `json:"StartParams"` //StartParams postgres.Jsonb `json:"Starting Parameters"` //Output Mapping //Input Mapping } -func (self *ModelSerializerNoAssoc) Response() ModelResponseNoAssoc { - response := ModelResponseNoAssoc{ +func (self *ModelSerializer) Response() ModelResponse { + response := ModelResponse{ Name: self.Name, OutputLength: self.OutputLength, InputLength: self.InputLength, SimulationID: self.SimulationID, SimulatorID: self.SimulatorID, - //StartParams: self.StartParameters, + StartParams: self.StartParameters, //InputMapping //OutputMapping } diff --git a/serializers/sampleSerializer.go b/serializers/sampleSerializer.go new file mode 100644 index 0000000..2978a89 --- /dev/null +++ b/serializers/sampleSerializer.go @@ -0,0 +1 @@ +package serializers diff --git a/routes/simulation/simulationSerializer.go b/serializers/simulationSerializer.go similarity index 98% rename from routes/simulation/simulationSerializer.go rename to serializers/simulationSerializer.go index b967b12..cbd8f5e 100644 --- a/routes/simulation/simulationSerializer.go +++ b/serializers/simulationSerializer.go @@ -1,4 +1,4 @@ -package simulation +package serializers import ( "github.com/gin-gonic/gin" diff --git a/routes/simulator/simulatorSerializer.go b/serializers/simulatorSerializer.go similarity index 98% rename from routes/simulator/simulatorSerializer.go rename to serializers/simulatorSerializer.go index 7307238..27b5e4b 100644 --- a/routes/simulator/simulatorSerializer.go +++ b/serializers/simulatorSerializer.go @@ -1,4 +1,4 @@ -package simulator +package serializers import ( "time" diff --git a/routes/user/userSerializer.go b/serializers/userSerializer.go similarity index 77% rename from routes/user/userSerializer.go rename to serializers/userSerializer.go index b5ecb7b..8bd338d 100644 --- a/routes/user/userSerializer.go +++ b/serializers/userSerializer.go @@ -1,10 +1,10 @@ -package user +package serializers import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" "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/simulation" ) type UsersSerializer struct { @@ -31,14 +31,14 @@ type UserResponse struct { Password string `json:"Password"` // XXX: ??? Role string `json:"Role"` Mail string `json:"Mail"` - Simulations []simulation.SimulationResponseNoAssoc + Simulations []SimulationResponseNoAssoc } func (self *UserSerializer) Response() UserResponse { // TODO: maybe all those should be made in one transaction - simulations, _, _ := simulation.FindUserSimulations(&self.User) - simulationsSerializer := simulation.SimulationsSerializerNoAssoc{self.Ctx, simulations} + simulations, _, _ := queries.FindUserSimulations(&self.User) + simulationsSerializer := SimulationsSerializerNoAssoc{self.Ctx, simulations} response := UserResponse{ diff --git a/routes/visualization/visualizationSerializer.go b/serializers/visualizationSerializer.go similarity index 79% rename from routes/visualization/visualizationSerializer.go rename to serializers/visualizationSerializer.go index f9dcdd8..0fea770 100644 --- a/routes/visualization/visualizationSerializer.go +++ b/serializers/visualizationSerializer.go @@ -1,10 +1,10 @@ -package visualization +package serializers import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" "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/widget" ) type VisualizationsSerializer struct { @@ -30,13 +30,13 @@ type VisualizationResponse struct { Name string `json:"Name"` Grid int `json:"Grid"` SimulationID uint `json:"SimulationID"` - Widgets []widget.WidgetResponse + Widgets []WidgetResponse } func (self *VisualizationSerializer) Response() VisualizationResponse { - w, _, _:= widget.FindVisualizationWidgets(&self.Visualization) - widgetsSerializer := widget.WidgetsSerializer{self.Ctx, w} + w, _, _:= queries.FindVisualizationWidgets(&self.Visualization) + widgetsSerializer := WidgetsSerializer{self.Ctx, w} response := VisualizationResponse{ Name: self.Name, diff --git a/routes/widget/widgetSerializer.go b/serializers/widgetSerializer.go similarity index 98% rename from routes/widget/widgetSerializer.go rename to serializers/widgetSerializer.go index cb9a629..1e0077f 100644 --- a/routes/widget/widgetSerializer.go +++ b/serializers/widgetSerializer.go @@ -1,4 +1,4 @@ -package widget +package serializers import ( "github.com/gin-gonic/gin" From d7d07e1db4072da089398c61aa55b6fff262039b Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 21 May 2019 15:46:40 +0200 Subject: [PATCH 09/30] fix bug with json default values --- common/datastructs.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/datastructs.go b/common/datastructs.go index 85049d2..629d50c 100644 --- a/common/datastructs.go +++ b/common/datastructs.go @@ -23,7 +23,7 @@ type Simulation struct { ID uint `gorm:"primary_key;auto_increment"` Name string `gorm:"not null"` Running bool `gorm:"default:false"` - StartParameters postgres.Jsonb `gorm:"default:{}"` + StartParameters postgres.Jsonb Users []User `gorm:"not null;many2many:user_simulations"` Models []Model `gorm:"foreignkey:SimulationID"` @@ -36,7 +36,7 @@ type Model struct { Name string `gorm:"not null"` OutputLength int `gorm:"default:1"` InputLength int `gorm:"default:1"` - StartParameters postgres.Jsonb `gorm:"default:{}"` + StartParameters postgres.Jsonb SimulationID uint Simulator Simulator @@ -71,8 +71,8 @@ type Simulator struct { Uptime int `gorm:"default:0"` State string `gorm:"default:''"` StateUpdateAt time.Time - Properties postgres.Jsonb `gorm:"default:{}"` - RawProperties postgres.Jsonb `gorm:"default:{}"` + Properties postgres.Jsonb + RawProperties postgres.Jsonb } type Visualization struct { @@ -99,7 +99,7 @@ type Widget struct { Y int `gorm:"not null"` Z int `gorm:"not null"` IsLocked bool `gorm:"default:false"` - CustomProperties postgres.Jsonb `gorm:"default:{}"` + CustomProperties postgres.Jsonb VisualizationID uint `gorm:"not null"` //new in villasweb 2.0 Files []File `gorm:"foreignkey:WidgetID"` From 2adcb1cdb647e75b90b23ae4d651c258f4c6def3 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 21 May 2019 16:07:33 +0200 Subject: [PATCH 10/30] fix some errors in file endpoints --- endpoints/registration.go | 6 +++--- queries/fileQueries.go | 40 ++++++++++++++++++++++++++++++++++----- queries/widgetQueries.go | 8 ++++++++ 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/endpoints/registration.go b/endpoints/registration.go index 9bb92a2..1a04a24 100644 --- a/endpoints/registration.go +++ b/endpoints/registration.go @@ -56,7 +56,7 @@ func SimulationsRegister(r *gin.RouterGroup) { // Files of Models r.GET("/:SimulationID/models/:ModelID/files", fileMReadAllEp) // NEW in API r.POST ("/:SimulationID/models/:ModelID/file", fileMRegistrationEp) // NEW in API - r.POST ("/:SimulationID/models/:ModelID/file", fileMCloneEp) // NEW in API + //r.POST ("/:SimulationID/models/:ModelID/file", fileMCloneEp) // NEW in API r.GET("/:SimulationID/models/:ModelID/file", fileMReadEp) // NEW in API r.PUT("/:SimulationID/models/:ModelID/file", fileMUpdateEp) // NEW in API r.DELETE("/:SimulationID/models/:ModelID/file", fileMDeleteEp) // NEW in API @@ -64,7 +64,7 @@ func SimulationsRegister(r *gin.RouterGroup) { // Files of Widgets r.GET("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/files", fileWReadAllEp) // NEW in API r.POST ("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWRegistrationEp) // NEW in API - r.POST ("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWCloneEp) // NEW in API + //r.POST ("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWCloneEp) // NEW in API r.GET("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWReadEp) // NEW in API r.PUT("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWUpdateEp) // NEW in API r.DELETE("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWDeleteEp) // NEW in API @@ -77,7 +77,7 @@ func UsersRegister(r *gin.RouterGroup) { r.PUT("/:UserID", userUpdateEp) r.GET("/:UserID", userReadEp) r.DELETE("/:UserID", userDeleteEp) - r.GET("/me", userSelfEp) + //r.GET("/me", userSelfEp) // TODO redirect to users/:UserID } diff --git a/queries/fileQueries.go b/queries/fileQueries.go index 340c78f..7187d0b 100644 --- a/queries/fileQueries.go +++ b/queries/fileQueries.go @@ -9,6 +9,7 @@ import ( "net/http" "os" "path/filepath" + "strconv" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) @@ -100,6 +101,24 @@ func RegisterFile(c *gin.Context, widgetID int, modelID int, simulationID int){ foldername := getFolderName(simulationID, modelID, widgetID) size := file_header.Size + // Check if simulation and widget or model exist in DB + _, err = FindSimulation(simulationID) + if common.ProvideErrorResponse(c, err) { + return + } + + if modelID != -1 { + _, err = FindModel(modelID) + if common.ProvideErrorResponse(c, err) { + return + } + } else if widgetID != -1 { + _, err = FindWidget(widgetID) + if common.ProvideErrorResponse(c, err) { + return + } + } + // Save file to local disc (NOT DB!) err = modifyFileOnDisc(file_header, filename, foldername, uint(size), true) if err != nil { @@ -118,6 +137,15 @@ func RegisterFile(c *gin.Context, widgetID int, modelID int, simulationID int){ func UpdateFile(c *gin.Context, widgetID int, modelID int, simulationID int){ // Extract file from PUT request form + err := c.Request.ParseForm() + if err != nil { + errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error()) + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return; + } + file_header, err := c.FormFile("file") if err != nil { errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error()) @@ -146,7 +174,9 @@ func UpdateFile(c *gin.Context, widgetID int, modelID int, simulationID int){ func ReadFile(c *gin.Context, widgetID int, modelID int, simulationID int){ - contentType := c.GetHeader("Content-Type") + //contentType := c.GetHeader("Content-Type") + + //TODO currently returns first file it finds in DB db := common.GetDB() var fileInDB common.File @@ -157,7 +187,7 @@ func ReadFile(c *gin.Context, widgetID int, modelID int, simulationID int){ if common.ProvideErrorResponse(c, err) { return } - err = db.Model(&wdgt).Related(&fileInDB).Where("Type = ?", contentType).Error + err = db.Model(&wdgt).Related(&fileInDB).Error if common.ProvideErrorResponse(c, err) { return } @@ -170,7 +200,7 @@ func ReadFile(c *gin.Context, widgetID int, modelID int, simulationID int){ if common.ProvideErrorResponse(c, err) { return } - err = db.Model(&model).Related(&fileInDB).Where("Type = ?", contentType).Error + err = db.Model(&model).Related(&fileInDB).Error if common.ProvideErrorResponse(c, err) { return } @@ -180,7 +210,7 @@ func ReadFile(c *gin.Context, widgetID int, modelID int, simulationID int){ c.Header("Content-Description", "File Transfer") c.Header("Content-Transfer-Encoding", "binary") c.Header("Content-Disposition", "attachment; filename="+fileInDB.Name ) - c.Header("Content-Type", contentType) + //c.Header("Content-Type", contentType) c.File(fileInDB.Path) c.JSON(http.StatusOK, gin.H{ @@ -356,6 +386,6 @@ func getFolderName(simulationID int, modelID int, widgetID int) string { } - foldername := base_foldername + "simulation_"+ string(simulationID) + elementname + string(elementid) + "/" + foldername := base_foldername + "simulation_"+ strconv.Itoa(simulationID) + elementname + strconv.Itoa(elementid) + "/" return foldername } \ No newline at end of file diff --git a/queries/widgetQueries.go b/queries/widgetQueries.go index a3391b4..6cce6c5 100644 --- a/queries/widgetQueries.go +++ b/queries/widgetQueries.go @@ -12,3 +12,11 @@ func FindVisualizationWidgets(visualization *common.Visualization) ([]common.Wid } +func FindWidget(widgetID int) (common.Widget, error){ + db := common.GetDB() + var w common.Widget + err := db.First(&w, widgetID).Error + return w, err +} + + From 8f2e039ff0e9e381224376fdeafa99232c7c0253 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 21 May 2019 16:51:34 +0200 Subject: [PATCH 11/30] removed signal/sample naming chaos --- common/database.go | 30 +++++++++---------- common/database_test.go | 8 ++--- common/datastructs.go | 8 ++--- doc/api/Makefile | 2 +- doc/api/api.yaml | 6 ++-- endpoints/registration.go | 10 +++---- ...{sampleEndpoints.go => signalEndpoints.go} | 8 ++--- endpoints/simulationEndpoints.go | 20 +++++++++++-- queries/modelQueries.go | 6 ++-- .../{sampleQueries.go => signalQueries.go} | 0 ...ampleSerializer.go => signalSerializer.go} | 0 serializers/simulationSerializer.go | 21 ++++++------- serializers/userSerializer.go | 11 ------- 13 files changed, 68 insertions(+), 62 deletions(-) rename endpoints/{sampleEndpoints.go => signalEndpoints.go} (68%) rename queries/{sampleQueries.go => signalQueries.go} (100%) rename serializers/{sampleSerializer.go => signalSerializer.go} (100%) diff --git a/common/database.go b/common/database.go index 91b2fa6..74a438e 100644 --- a/common/database.go +++ b/common/database.go @@ -53,7 +53,7 @@ func VerifyConnection(db *gorm.DB) error { // to the Dummy*() where it is called func DropTables(db *gorm.DB) { db.DropTableIfExists(&Simulator{}) - db.DropTableIfExists(&Sample{}) + db.DropTableIfExists(&Signal{}) db.DropTableIfExists(&Model{}) db.DropTableIfExists(&File{}) db.DropTableIfExists(&Simulation{}) @@ -65,7 +65,7 @@ func DropTables(db *gorm.DB) { // AutoMigrate the models func MigrateModels(db *gorm.DB) { db.AutoMigrate(&Simulator{}) - db.AutoMigrate(&Sample{}) + db.AutoMigrate(&Signal{}) db.AutoMigrate(&Model{}) db.AutoMigrate(&File{}) db.AutoMigrate(&Simulation{}) @@ -100,14 +100,14 @@ func DummyPopulateDB(test_db *gorm.DB) { checkErr(test_db.Create(&simr_A).Error) checkErr(test_db.Create(&simr_B).Error) - outSmp_A := Sample{Name: "outSample_A", Direction: "out"} - outSmp_B := Sample{Name: "outSample_B", Direction: "out"} - inSmp_A := Sample{Name: "inSample_A", Direction: "in"} - inSmp_B := Sample{Name: "inSample_B", Direction: "in"} - checkErr(test_db.Create(&outSmp_A).Error) - checkErr(test_db.Create(&outSmp_B).Error) - checkErr(test_db.Create(&inSmp_A).Error) - checkErr(test_db.Create(&inSmp_B).Error) + outSig_A := Signal{Name: "outSignal_A", Direction: "out"} + outSig_B := Signal{Name: "outSignal_B", Direction: "out"} + inSig_A := Signal{Name: "inSignal_A", Direction: "in"} + inSig_B := Signal{Name: "inSignal_B", Direction: "in"} + checkErr(test_db.Create(&outSig_A).Error) + checkErr(test_db.Create(&outSig_B).Error) + checkErr(test_db.Create(&inSig_A).Error) + checkErr(test_db.Create(&inSig_B).Error) mo_A := Model{Name: "Model_A"} mo_B := Model{Name: "Model_B"} @@ -161,11 +161,11 @@ func DummyPopulateDB(test_db *gorm.DB) { checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_A).Error) checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_B).Error) - // Model HM Samples - checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSmp_A).Error) - checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSmp_B).Error) - checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSmp_A).Error) - checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSmp_B).Error) + // Model HM Signal + checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSig_A).Error) + checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSig_B).Error) + checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_A).Error) + checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_B).Error) // Model HM Files checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_A).Error) diff --git a/common/database_test.go b/common/database_test.go index d4c3553..2375e9c 100644 --- a/common/database_test.go +++ b/common/database_test.go @@ -36,7 +36,7 @@ func TestDummyDBAssociations(t *testing.T) { var vis Visualization var widg Widget - var smps []Sample + var sigs []Signal var mos []Model var files []File var files_sm []File @@ -93,10 +93,10 @@ func TestDummyDBAssociations(t *testing.T) { a.NoError(db.Model(&mo).Association("Simulator").Find(&simr).Error) a.EqualValues("Host_A", simr.Host, "Expected Host_A") - a.NoError(db.Model(&mo).Where("Direction = ?", "out").Related(&smps, "OutputMapping").Error) - if len(smps) != 2 { + a.NoError(db.Model(&mo).Where("Direction = ?", "out").Related(&sigs, "OutputMapping").Error) + if len(sigs) != 2 { a.Fail("Model Associations", - "Expected to have %v Output AND Input Samples. Has %v.", 2, len(smps)) + "Expected to have %v Output AND Input Signals. Has %v.", 2, len(sigs)) } a.NoError(db.Model(&mo).Related(&files_sm, "Files").Error) diff --git a/common/datastructs.go b/common/datastructs.go index 629d50c..250a9f0 100644 --- a/common/datastructs.go +++ b/common/datastructs.go @@ -42,16 +42,16 @@ type Model struct { Simulator Simulator SimulatorID uint - // NOTE: order of samples is important - OutputMapping []Sample `gorm:"foreignkey:ModelID"` - InputMapping []Sample `gorm:"foreignkey:ModelID"` + // NOTE: order of signals is important + OutputMapping []Signal `gorm:"foreignkey:ModelID"` + InputMapping []Signal `gorm:"foreignkey:ModelID"` //new in villasweb 2.0 (for CIM file of simulation model and other model file formats) Files []File `gorm:"foreignkey:ModelID"` } -type Sample struct { +type Signal struct { //gorm.Model ID uint `gorm:"primary_key;auto_increment"` Name string `gorm:"not null"` diff --git a/doc/api/Makefile b/doc/api/Makefile index 97118b8..8372465 100644 --- a/doc/api/Makefile +++ b/doc/api/Makefile @@ -9,7 +9,7 @@ RSYNC_OPTS ?= --recursive --ignore-missing-args --copy-links --chown $(DEPLOY_US all: index.html index.html: api.yaml - redoc-cli bundle --cdn --title "$(TITLE)" --output $@ api.yaml + redoc-cli bundle --cdn --title $(TITLE) --output $@ api.yaml deploy: index.html rsync $(RSYNC_OPTS) index.html api.yaml $(DEPLOY_USER)@$(DEPLOY_HOST):$(DEPLOY_PATH) diff --git a/doc/api/api.yaml b/doc/api/api.yaml index 4cac0e6..e715d5f 100644 --- a/doc/api/api.yaml +++ b/doc/api/api.yaml @@ -1323,12 +1323,12 @@ components: OutputMapping: type: array items: - $ref: '#/components/schemas/Sample' + $ref: '#/components/schemas/Signal' InputMapping: type: array items: - $ref: '#/components/schemas/Sample' - Sample: + $ref: '#/components/schemas/Signal' + Signal: required: - Name - Unit diff --git a/endpoints/registration.go b/endpoints/registration.go index 1a04a24..fecc96d 100644 --- a/endpoints/registration.go +++ b/endpoints/registration.go @@ -30,11 +30,11 @@ func SimulationsRegister(r *gin.RouterGroup) { r.PUT("/:SimulationID/models/:ModelID/simulator", simulatorUpdateModelEp) // NEW in API r.GET("/:SimulationID/models/:ModelID/simulator", simulatorReadModelEp) // NEW in API - // Input and Output Samples - r.POST("/:SimulationID/models/:ModelID/Samples/:Direction", sampleRegistrationEp) // NEW in API - r.GET("/:SimulationID/models/:ModelID/Samples/:Direction", sampleReadAllEp) // NEW in API - r.PUT("/:SimulationID/models/:ModelID/Samples/:Direction", sampleUpdateEp) // NEW in API - r.DELETE("/:SimulationID/models/:ModelID/Samples/:Direction", sampleDeleteEp) // NEW in API + // Input and Output Signals + r.POST("/:SimulationID/models/:ModelID/signals/:Direction", signalRegistrationEp) // NEW in API + r.GET("/:SimulationID/models/:ModelID/signals/:Direction", signalReadAllEp) // NEW in API + r.PUT("/:SimulationID/models/:ModelID/signals/:Direction", signalUpdateEp) // NEW in API + r.DELETE("/:SimulationID/models/:ModelID/signals/:Direction", signalDeleteEp) // NEW in API // Visualizations r.GET("/:SimulationID/visualizations", visualizationReadAllEp) diff --git a/endpoints/sampleEndpoints.go b/endpoints/signalEndpoints.go similarity index 68% rename from endpoints/sampleEndpoints.go rename to endpoints/signalEndpoints.go index 38c17d9..da5c6f1 100644 --- a/endpoints/sampleEndpoints.go +++ b/endpoints/signalEndpoints.go @@ -5,25 +5,25 @@ import ( "net/http" ) -func sampleRegistrationEp(c *gin.Context) { +func signalRegistrationEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func sampleReadAllEp(c *gin.Context) { +func signalReadAllEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func sampleUpdateEp(c *gin.Context) { +func signalUpdateEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func sampleDeleteEp(c *gin.Context) { +func signalDeleteEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) diff --git a/endpoints/simulationEndpoints.go b/endpoints/simulationEndpoints.go index 3a0e8b6..48e3781 100644 --- a/endpoints/simulationEndpoints.go +++ b/endpoints/simulationEndpoints.go @@ -2,6 +2,7 @@ package endpoints import ( "fmt" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" "github.com/gin-gonic/gin" @@ -11,8 +12,11 @@ import ( func simulationReadAllEp(c *gin.Context) { + + //TODO Identify user who is issuing the request and return only those simulations that are known to the user + allSimulations, _, _ := queries.FindAllSimulations() - serializer := serializers.SimulationsSerializerNoAssoc{c, allSimulations} + serializer := serializers.SimulationsSerializer{c, allSimulations} c.JSON(http.StatusOK, gin.H{ "simulations": serializer.Response(), }) @@ -37,8 +41,20 @@ func simulationUpdateEp(c *gin.Context) { } func simulationReadEp(c *gin.Context) { + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + sim, err := queries.FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + serializer := serializers.SimulationSerializer{c, sim} c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", + "simulation": serializer.Response(), }) } diff --git a/queries/modelQueries.go b/queries/modelQueries.go index 97d9b8c..0d66028 100644 --- a/queries/modelQueries.go +++ b/queries/modelQueries.go @@ -41,12 +41,12 @@ func CloneModel(targetSim int, modelID int) error { // Check if target sim exists // Check if model exists - // Get all Samples of Model + // Get all Signals of Model // Get Simulator of Model // Get Files of model // Add new model object to DB and associate with target sim - // Add new sample objects to DB and associate with new model object (careful with directions) + // Add new signal objects to DB and associate with new model object (careful with directions) // Associate Simulator with new Model object var err error return err @@ -83,7 +83,7 @@ func DeleteModel(simID int , modelID int ) error { } // TODO remove File Associations and files on disk - // TODO remove Sample Associations and Samples in DB + // TODO remove Signal Associations and Signals in DB // TODO how to treat simulator association? //remove model itself from DB diff --git a/queries/sampleQueries.go b/queries/signalQueries.go similarity index 100% rename from queries/sampleQueries.go rename to queries/signalQueries.go diff --git a/serializers/sampleSerializer.go b/serializers/signalSerializer.go similarity index 100% rename from serializers/sampleSerializer.go rename to serializers/signalSerializer.go diff --git a/serializers/simulationSerializer.go b/serializers/simulationSerializer.go index cbd8f5e..865cdd7 100644 --- a/serializers/simulationSerializer.go +++ b/serializers/simulationSerializer.go @@ -2,42 +2,43 @@ package serializers import ( "github.com/gin-gonic/gin" + "github.com/jinzhu/gorm/dialects/postgres" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) -type SimulationsSerializerNoAssoc struct { +type SimulationsSerializer struct { Ctx *gin.Context Simulations []common.Simulation } -func (self *SimulationsSerializerNoAssoc) Response() []SimulationResponseNoAssoc { - response := []SimulationResponseNoAssoc{} +func (self *SimulationsSerializer) Response() []SimulationResponse { + response := []SimulationResponse{} for _, simulation := range self.Simulations { - serializer := SimulationSerializerNoAssoc{self.Ctx, simulation} + serializer := SimulationSerializer{self.Ctx, simulation} response = append(response, serializer.Response()) } return response } -type SimulationSerializerNoAssoc struct { +type SimulationSerializer struct { Ctx *gin.Context common.Simulation } -type SimulationResponseNoAssoc struct { +type SimulationResponse struct { Name string `json:"Name"` ID uint `json:"SimulationID"` Running bool `json:"Running"` - //StartParams postgres.Jsonb `json:"Starting Parameters"` + StartParams postgres.Jsonb `json:"Starting Parameters"` } -func (self *SimulationSerializerNoAssoc) Response() SimulationResponseNoAssoc { - response := SimulationResponseNoAssoc{ +func (self *SimulationSerializer) Response() SimulationResponse { + response := SimulationResponse{ Name: self.Name, ID: self.ID, Running: self.Running, - //StartParams: self.StartParameters, + StartParams: self.StartParameters, } return response } diff --git a/serializers/userSerializer.go b/serializers/userSerializer.go index 8bd338d..fc2d84f 100644 --- a/serializers/userSerializer.go +++ b/serializers/userSerializer.go @@ -1,7 +1,6 @@ package serializers import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" "github.com/gin-gonic/gin" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" @@ -28,25 +27,15 @@ type UserSerializer struct { type UserResponse struct { Username string `json:"Username"` - Password string `json:"Password"` // XXX: ??? Role string `json:"Role"` Mail string `json:"Mail"` - Simulations []SimulationResponseNoAssoc } func (self *UserSerializer) Response() UserResponse { - // TODO: maybe all those should be made in one transaction - - simulations, _, _ := queries.FindUserSimulations(&self.User) - simulationsSerializer := SimulationsSerializerNoAssoc{self.Ctx, simulations} - - response := UserResponse{ Username: self.Username, - Password: self.Password, Role: self.Role, Mail: self.Mail, - Simulations: simulationsSerializer.Response(), } return response } From 0b1d8657b94f8778bba0471ca73ad2d7d4c14eb5 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 21 May 2019 17:04:55 +0200 Subject: [PATCH 12/30] add (REMOVED) keyword to all endpoints that are replaced or removed in the new backend --- doc/api/api.yaml | 98 ++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/doc/api/api.yaml b/doc/api/api.yaml index e715d5f..c0de9f4 100644 --- a/doc/api/api.yaml +++ b/doc/api/api.yaml @@ -17,7 +17,7 @@ tags: - name: simulators description: Manage Simulators - name: projects - description: Manage Projects + description: (REMOVED) Manage Projects - name: simulations description: Manage Simulations - name: models @@ -233,8 +233,8 @@ paths: get: tags: - files - summary: Get files of user - operationId: getFilesOfUser + summary: (REMOVED) Get files of user + operationId: (REMOVED) getFilesOfUser responses: 200: description: OK. @@ -255,8 +255,8 @@ paths: post: tags: - files - summary: Add a new file to the database (NEW, was /uploads before)) - operationId: addFile + summary: (REMOVED) Add a new file to the database + operationId: (REMOVED) addFile requestBody: description: "File object to be added" required: true @@ -281,8 +281,8 @@ paths: get: tags: - files - summary: Get properties of file with ID FileID - operationId: getFileProperties + summary: (REMOVED) Get properties of file with ID FileID + operationId: (REMOVED) getFileProperties parameters: - in: path name: FileID @@ -318,8 +318,8 @@ paths: put: tags: - files - summary: Update properties of file with ID FileID (NEW) - operationId: updateFileProperties + summary: (REMOVED) Update properties of file with ID FileID + operationId: (REMOVED) updateFileProperties requestBody: description: "File object to be updated" required: true @@ -350,8 +350,8 @@ paths: delete: tags: - files - summary: Delete file with ID FileID - operationId: deleteFile + summary: (REMOVED) Delete file with ID FileID + operationId: (REMOVED) deleteFile parameters: - in: path name: FileID @@ -374,8 +374,8 @@ paths: get: tags: - projects - summary: Get projects of user - operationId: getProjects + summary: (REMOVED) Get projects of user + operationId: (REMOVED) getProjects responses: 200: description: OK. @@ -396,8 +396,8 @@ paths: post: tags: - projects - summary: Add a new project to the database - operationId: addProject + summary: (REMOVED) Add a new project to the database + operationId: (REMOVED) addProject requestBody: description: "Project object to add to DB" required: true @@ -420,8 +420,8 @@ paths: put: tags: - projects - summary: Update properties of project with ID ProjectID - operationId: updateProjectProperties + summary: (REMOVED) Update properties of project with ID ProjectID + operationId: (REMOVED) updateProjectProperties parameters: - in: path name: ProjectID @@ -452,8 +452,8 @@ paths: get: tags: - projects - summary: Get properties of project with ID ProjectID - operationId: getProjectProperties + summary: (REMOVED) Get properties of project with ID ProjectID + operationId: (REMOVED) getProjectProperties parameters: - in: path name: ProjectID @@ -479,8 +479,8 @@ paths: delete: tags: - projects - summary: Delete project with ID ProjectID - operationId: deleteProject + summary: (REMOVED) Delete project with ID ProjectID + operationId: (REMOVED) deleteProject parameters: - in: path name: ProjectID @@ -503,8 +503,8 @@ paths: get: tags: - models - summary: Get simulation models of user - operationId: getModels + summary: (REMOVED) Get simulation models of user + operationId: (REMOVED) getModels responses: 200: description: OK. @@ -525,8 +525,8 @@ paths: post: tags: - models - summary: Add a new Model to the database - operationId: addModel + summary: (REMOVED) Add a new Model to the database + operationId: (REMOVED) addModel requestBody: description: "Model object to add to DB" required: true @@ -549,8 +549,8 @@ paths: put: tags: - models - summary: Update properties of Model with ID ModelID - operationId: updateModelProperties + summary: (REMOVED) Update properties of Model with ID ModelID + operationId: (REMOVED) updateModelProperties parameters: - in: path name: ModelID @@ -581,8 +581,8 @@ paths: get: tags: - models - summary: Get properties of Model with ID ModelID - operationId: getModelProperties + summary: (REMOVED) Get properties of Model with ID ModelID + operationId: (REMOVED) getModelProperties parameters: - in: path name: ModelID @@ -608,8 +608,8 @@ paths: delete: tags: - models - summary: Delete Model with ID ModelID - operationId: deleteModel + summary: (REMOVED) Delete Model with ID ModelID + operationId: (REMOVED) deleteModel parameters: - in: path name: ModelID @@ -628,11 +628,11 @@ paths: description: Not found. Simulation or simulation model not found. 500: description: Internal server error. Unable to save changed simulation or to remove simulation model. - /models/{ModelID}/file: + simulations/{SimulationID}/models/{ModelID}/file: get: tags: - - models - summary: Get file from Model with ID ModelID (NEW) + - simulations + summary: (NEW) Get file from Model with ID ModelID operationId: getFileFromModel parameters: - in: path @@ -680,8 +680,8 @@ paths: description: Internal server error. put: tags: - - models - summary: Update (Overwrite) file of Model with ID ModelID, File object has to be in database (NEW) + - simulations + summary: (NEW) Update (Overwrite) file of Model with ID ModelID, File object has to be in database operationId: updateFileForModel parameters: - in: path @@ -1024,9 +1024,9 @@ paths: get: tags: - visualizations - summary: Get all available visualizations - description: Return a JSON representation of all visualizations - operationId: getVisualizations + summary: (REMOVED) Get all available visualizations + description: (REMOVED) Return a JSON representation of all visualizations + operationId: (REMOVED) getVisualizations responses: 200: description: OK. @@ -1047,8 +1047,8 @@ paths: post: tags: - visualizations - summary: Add a new visualization to the database - operationId: addVisualization + summary: (REMOVED) Add a new visualization to the database + operationId: (REMOVED) addVisualization requestBody: description: "Visualization object to add to DB" required: true @@ -1073,8 +1073,8 @@ paths: put: tags: - visualizations - summary: Update properties of Visualization with ID VisualizationID - operationId: updateVisualizationrProperties + summary: (REMOVED) Update properties of Visualization with ID VisualizationID + operationId: (REMOVED) updateVisualizationrProperties parameters: - in: path name: VisualizationID @@ -1105,8 +1105,8 @@ paths: get: tags: - visualizations - summary: Get properties of Visualization with ID VisualizationID - operationId: getVisualizationProperties + summary: (REMOVED) Get properties of Visualization with ID VisualizationID + operationId: (REMOVED) getVisualizationProperties parameters: - in: path name: VisualizationID @@ -1132,8 +1132,8 @@ paths: delete: tags: - visualizations - summary: Delete Visualization with ID VisualizationID - operationId: deleteVisualization + summary: (REMOVED) Delete Visualization with ID VisualizationID + operationId: (REMOVED) deleteVisualization parameters: - in: path name: VisualizationID @@ -1156,8 +1156,8 @@ paths: post: tags: - uploads - summary: Upload a new file to the database (REMOVED) - operationId: uploadFile + summary: (REMOVED) Upload a new file to the database + operationId: (REMOVED) uploadFile requestBody: description: "File object to upload TODO CHANGE TO FORM" required: true From 403f350b25649921ac50455872db5668b47e9631 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 22 May 2019 10:06:31 +0200 Subject: [PATCH 13/30] Signal is no longer a DB table --- common/database.go | 28 ++++++++++++++-------------- common/database_test.go | 12 ++++++------ common/datastructs.go | 18 +++++++----------- 3 files changed, 27 insertions(+), 31 deletions(-) diff --git a/common/database.go b/common/database.go index 74a438e..11d8eb5 100644 --- a/common/database.go +++ b/common/database.go @@ -53,7 +53,7 @@ func VerifyConnection(db *gorm.DB) error { // to the Dummy*() where it is called func DropTables(db *gorm.DB) { db.DropTableIfExists(&Simulator{}) - db.DropTableIfExists(&Signal{}) + //db.DropTableIfExists(&Signal{}) db.DropTableIfExists(&Model{}) db.DropTableIfExists(&File{}) db.DropTableIfExists(&Simulation{}) @@ -65,7 +65,7 @@ func DropTables(db *gorm.DB) { // AutoMigrate the models func MigrateModels(db *gorm.DB) { db.AutoMigrate(&Simulator{}) - db.AutoMigrate(&Signal{}) + //db.AutoMigrate(&Signal{}) db.AutoMigrate(&Model{}) db.AutoMigrate(&File{}) db.AutoMigrate(&Simulation{}) @@ -100,14 +100,14 @@ func DummyPopulateDB(test_db *gorm.DB) { checkErr(test_db.Create(&simr_A).Error) checkErr(test_db.Create(&simr_B).Error) - outSig_A := Signal{Name: "outSignal_A", Direction: "out"} - outSig_B := Signal{Name: "outSignal_B", Direction: "out"} - inSig_A := Signal{Name: "inSignal_A", Direction: "in"} - inSig_B := Signal{Name: "inSignal_B", Direction: "in"} - checkErr(test_db.Create(&outSig_A).Error) - checkErr(test_db.Create(&outSig_B).Error) - checkErr(test_db.Create(&inSig_A).Error) - checkErr(test_db.Create(&inSig_B).Error) + //outSig_A := Signal{Name: "outSignal_A", Direction: "out"} + //outSig_B := Signal{Name: "outSignal_B", Direction: "out"} + //inSig_A := Signal{Name: "inSignal_A", Direction: "in"} + //inSig_B := Signal{Name: "inSignal_B", Direction: "in"} + //checkErr(test_db.Create(&outSig_A).Error) + //checkErr(test_db.Create(&outSig_B).Error) + //checkErr(test_db.Create(&inSig_A).Error) + //checkErr(test_db.Create(&inSig_B).Error) mo_A := Model{Name: "Model_A"} mo_B := Model{Name: "Model_B"} @@ -162,10 +162,10 @@ func DummyPopulateDB(test_db *gorm.DB) { checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_B).Error) // Model HM Signal - checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSig_A).Error) - checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSig_B).Error) - checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_A).Error) - checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_B).Error) + //checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSig_A).Error) + //checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSig_B).Error) + //checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_A).Error) + //checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_B).Error) // Model HM Files checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_A).Error) diff --git a/common/database_test.go b/common/database_test.go index 2375e9c..5df04bb 100644 --- a/common/database_test.go +++ b/common/database_test.go @@ -36,7 +36,7 @@ func TestDummyDBAssociations(t *testing.T) { var vis Visualization var widg Widget - var sigs []Signal + //var sigs []Signal var mos []Model var files []File var files_sm []File @@ -93,11 +93,11 @@ func TestDummyDBAssociations(t *testing.T) { a.NoError(db.Model(&mo).Association("Simulator").Find(&simr).Error) a.EqualValues("Host_A", simr.Host, "Expected Host_A") - a.NoError(db.Model(&mo).Where("Direction = ?", "out").Related(&sigs, "OutputMapping").Error) - if len(sigs) != 2 { - a.Fail("Model Associations", - "Expected to have %v Output AND Input Signals. Has %v.", 2, len(sigs)) - } + //a.NoError(db.Model(&mo).Where("Direction = ?", "out").Related(&sigs, "OutputMapping").Error) + //if len(sigs) != 2 { + // a.Fail("Model Associations", + // "Expected to have %v Output AND Input Signals. Has %v.", 2, len(sigs)) + //} a.NoError(db.Model(&mo).Related(&files_sm, "Files").Error) if len(files_sm) != 2 { diff --git a/common/datastructs.go b/common/datastructs.go index 250a9f0..c7687f0 100644 --- a/common/datastructs.go +++ b/common/datastructs.go @@ -43,23 +43,19 @@ type Model struct { SimulatorID uint // NOTE: order of signals is important - OutputMapping []Signal `gorm:"foreignkey:ModelID"` - InputMapping []Signal `gorm:"foreignkey:ModelID"` + OutputMapping []Signal + InputMapping []Signal //new in villasweb 2.0 (for CIM file of simulation model and other model file formats) Files []File `gorm:"foreignkey:ModelID"` } -type Signal struct { - //gorm.Model - ID uint `gorm:"primary_key;auto_increment"` - Name string `gorm:"not null"` - Unit string `gorm:"not null"` - Index uint `gorm:"not null"` - Direction string `gorm:"not null"` - ModelID uint - //IsRecorded bool `gorm:"default:false"` +type Signal struct { // NOT A DB TABLE + Name string + Unit string + Index uint + Direction string } type Simulator struct { From 7e135add5b1d1f4e1d79035e01e426012eebf652 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 22 May 2019 10:06:46 +0200 Subject: [PATCH 14/30] Implement more endpoints --- endpoints/modelEndpoints.go | 5 -- endpoints/registration.go | 24 +++++----- endpoints/signalEndpoints.go | 83 +++++++++++++++++++++++++++------ endpoints/simulatorEndpoints.go | 58 +++++++++++++++++++++-- endpoints/userEndpoints.go | 71 ++++++++++++++++++++++++++-- queries/modelQueries.go | 6 +++ queries/signalQueries.go | 19 ++++++++ queries/simulatorQueries.go | 7 +++ queries/userQueries.go | 40 ++++++++++++++++ 9 files changed, 274 insertions(+), 39 deletions(-) diff --git a/endpoints/modelEndpoints.go b/endpoints/modelEndpoints.go index 0f2ee47..a32aaa9 100644 --- a/endpoints/modelEndpoints.go +++ b/endpoints/modelEndpoints.go @@ -81,11 +81,6 @@ func modelCloneEp(c *gin.Context) { func modelUpdateEp(c *gin.Context) { - //simID, err := GetSimulationID(c) - //if err != nil { - // return - //} - modelID, err := GetModelID(c) if err != nil { return diff --git a/endpoints/registration.go b/endpoints/registration.go index fecc96d..947ed3f 100644 --- a/endpoints/registration.go +++ b/endpoints/registration.go @@ -7,11 +7,11 @@ import ( func SimulationsRegister(r *gin.RouterGroup) { r.GET("/", simulationReadAllEp) - r.POST("/", simulationRegistrationEp) - r.POST("/:SimulationID", simulationCloneEp) - r.PUT("/:SimulationID", simulationUpdateEp) + //r.POST("/", simulationRegistrationEp) + //r.POST("/:SimulationID", simulationCloneEp) + //r.PUT("/:SimulationID", simulationUpdateEp) r.GET("/:SimulationID", simulationReadEp) - r.DELETE("/:SimulationID", simulationDeleteEp) + //r.DELETE("/:SimulationID", simulationDeleteEp) // Users r.GET("/:SimulationID/users", userReadAllSimEp) @@ -21,21 +21,20 @@ func SimulationsRegister(r *gin.RouterGroup) { // Models r.GET("/:SimulationID/models/", modelReadAllEp) r.POST("/:SimulationID/models/", modelRegistrationEp) - r.POST("/:SimulationID/models/:ModelID", modelCloneEp) + //r.POST("/:SimulationID/models/:ModelID", modelCloneEp) r.PUT("/:SimulationID/models/:ModelID", modelUpdateEp) r.GET("/:SimulationID/models/:ModelID", modelReadEp) - r.DELETE("/:SimulationID/models/:ModelID", modelDeleteEp) + //r.DELETE("/:SimulationID/models/:ModelID", modelDeleteEp) // Simulators - r.PUT("/:SimulationID/models/:ModelID/simulator", simulatorUpdateModelEp) // NEW in API - r.GET("/:SimulationID/models/:ModelID/simulator", simulatorReadModelEp) // NEW in API + r.PUT("/:SimulationID/models/:ModelID/simulator", simulatorUpdateModelEp) + r.GET("/:SimulationID/models/:ModelID/simulator", simulatorReadModelEp) // Input and Output Signals - r.POST("/:SimulationID/models/:ModelID/signals/:Direction", signalRegistrationEp) // NEW in API - r.GET("/:SimulationID/models/:ModelID/signals/:Direction", signalReadAllEp) // NEW in API - r.PUT("/:SimulationID/models/:ModelID/signals/:Direction", signalUpdateEp) // NEW in API - r.DELETE("/:SimulationID/models/:ModelID/signals/:Direction", signalDeleteEp) // NEW in API + r.POST("/:SimulationID/models/:ModelID/signals/:direction", signalRegistrationEp) + r.GET("/:SimulationID/models/:ModelID/signals/:direction", signalReadAllEp) + //TODO I was here // Visualizations r.GET("/:SimulationID/visualizations", visualizationReadAllEp) r.POST("/:SimulationID/visualization", visualizationRegistrationEp) @@ -76,6 +75,7 @@ func UsersRegister(r *gin.RouterGroup) { r.POST("/", userRegistrationEp) r.PUT("/:UserID", userUpdateEp) r.GET("/:UserID", userReadEp) + r.GET("/:UserID/simulations", userReadSimEp) r.DELETE("/:UserID", userDeleteEp) //r.GET("/me", userSelfEp) // TODO redirect to users/:UserID } diff --git a/endpoints/signalEndpoints.go b/endpoints/signalEndpoints.go index da5c6f1..e42dd92 100644 --- a/endpoints/signalEndpoints.go +++ b/endpoints/signalEndpoints.go @@ -3,28 +3,81 @@ package endpoints import ( "github.com/gin-gonic/gin" "net/http" + + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" ) func signalRegistrationEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) + + modelID, err := GetModelID(c) + if err != nil { + return + } + + model, err := queries.FindModel(modelID) + if common.ProvideErrorResponse(c, err) { + return + } + + direction := c.Param("direction") + if !(direction == "out") && !(direction == "in") { + errormsg := "Bad request. Direction has to be in or out" + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + var sigs []common.Signal + err = c.BindJSON(&sigs) + if err != nil { + errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + // Add signals to model and remove all existing Signals of the requested direction (if any) + err = queries.ReplaceSignals(&model, sigs, direction) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } } func signalReadAllEp(c *gin.Context) { + + modelID, err := GetModelID(c) + if err != nil { + return + } + + model, err := queries.FindModel(modelID) + if common.ProvideErrorResponse(c, err) { + return + } + + direction := c.Param("direction") + if !(direction == "out") && !(direction == "in") { + errormsg := "Bad request. Direction has to be in or out" + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + var signals []common.Signal + if direction == "in" { + signals = model.InputMapping + } else { + signals = model.OutputMapping + } + c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", + "signals": signals, }) } -func signalUpdateEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func signalDeleteEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} diff --git a/endpoints/simulatorEndpoints.go b/endpoints/simulatorEndpoints.go index 2f40330..d9b95f2 100644 --- a/endpoints/simulatorEndpoints.go +++ b/endpoints/simulatorEndpoints.go @@ -1,10 +1,13 @@ package endpoints import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" "github.com/gin-gonic/gin" + "fmt" "net/http" + "strconv" ) func simulatorReadAllEp(c *gin.Context) { @@ -28,9 +31,39 @@ func simulatorUpdateEp(c *gin.Context) { } func simulatorUpdateModelEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) + + // simulator ID as parameter of Query, e.g. simulations/:SimulationID/models/:ModelID/simulator?simulatorID=42 + simulatorID, err := strconv.Atoi(c.Query("simulatorID")) + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect simulator ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + modelID, err := GetModelID(c) + if err != nil { + return + } + + simulator, err := queries.FindSimulator(simulatorID) + if common.ProvideErrorResponse(c, err) { + return + } + + model, err := queries.FindModel(modelID) + if common.ProvideErrorResponse(c, err) { + return + } + + err = queries.UpdateSimulatorOfModel(&model, &simulator) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK", + }) + } + } func simulatorReadEp(c *gin.Context) { @@ -40,8 +73,25 @@ func simulatorReadEp(c *gin.Context) { } func simulatorReadModelEp(c *gin.Context) { + + modelID, err := GetModelID(c) + if err != nil { + return + } + + model, err := queries.FindModel(modelID) + if common.ProvideErrorResponse(c, err) { + return + } + + simulator, err := queries.FindSimulator(int(model.SimulatorID)) + if common.ProvideErrorResponse(c, err) { + return + } + + serializer := serializers.SimulatorSerializer{c, simulator} c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", + "simulator": serializer.Response(), }) } diff --git a/endpoints/userEndpoints.go b/endpoints/userEndpoints.go index c63412e..5611dcf 100644 --- a/endpoints/userEndpoints.go +++ b/endpoints/userEndpoints.go @@ -1,6 +1,7 @@ package endpoints import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" "github.com/gin-gonic/gin" @@ -18,7 +19,23 @@ func userReadAllEp(c *gin.Context) { } func userReadAllSimEp(c *gin.Context) { - allUsers, _, _ := queries.FindAllUsers() + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + sim, err := queries.FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + // Find all users of simulation + allUsers, _, err := queries.FindAllUsersSim(&sim) + if common.ProvideErrorResponse(c, err) { + return + } + serializer := serializers.UsersSerializer{c, allUsers} c.JSON(http.StatusOK, gin.H{ "users": serializer.Response(), @@ -38,12 +55,42 @@ func userUpdateEp(c *gin.Context) { } func userUpdateSimEp(c *gin.Context) { + + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + sim, err := queries.FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + username := c.Param("username") + + user, err := queries.FindUserByName(username) + if common.ProvideErrorResponse(c, err) { + return + } + + err = queries.AddUserToSim(&sim, &user) + if common.ProvideErrorResponse(c, err){ + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) +} + +func userReadEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func userReadEp(c *gin.Context) { +func userReadSimEp(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) @@ -56,8 +103,26 @@ func userDeleteEp(c *gin.Context) { } func userDeleteSimEp(c *gin.Context) { + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + sim, err := queries.FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + username := c.Param("username") + + err = queries.RemoveUserFromSim(&sim, username) + if common.ProvideErrorResponse(c, err) { + return + } + c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", + "message": "OK.", }) } diff --git a/queries/modelQueries.go b/queries/modelQueries.go index 0d66028..30b248e 100644 --- a/queries/modelQueries.go +++ b/queries/modelQueries.go @@ -90,4 +90,10 @@ func DeleteModel(simID int , modelID int ) error { //TODO: do we want this?? err = db.Delete(&m).Error return err +} + +func UpdateSimulatorOfModel(model *common.Model, simulator *common.Simulator) error { + db := common.GetDB() + err := db.Model(model).Association("Simulator").Replace(simulator).Error + return err } \ No newline at end of file diff --git a/queries/signalQueries.go b/queries/signalQueries.go index 58eadae..f0a04a3 100644 --- a/queries/signalQueries.go +++ b/queries/signalQueries.go @@ -1 +1,20 @@ package queries + +import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" +) + +func ReplaceSignals(model *common.Model, signals []common.Signal, direction string) error { + + db := common.GetDB() + var err error + + if direction == "in" { + err = db.Model(model).Select("InputMapping").Update("InputMapping", signals).Error + } else { + err = db.Model(model).Select("OutputMapping").Update("OutputMapping", signals).Error + } + + return err + +} \ No newline at end of file diff --git a/queries/simulatorQueries.go b/queries/simulatorQueries.go index bcedf27..6ccf6ec 100644 --- a/queries/simulatorQueries.go +++ b/queries/simulatorQueries.go @@ -11,3 +11,10 @@ func FindAllSimulators() ([]common.Simulator, int, error) { return simulators, len(simulators), err } +func FindSimulator(simulatorID int) (common.Simulator, error) { + db := common.GetDB() + var simulator common.Simulator + err := db.First(&simulator, simulatorID).Error + return simulator, err +} + diff --git a/queries/userQueries.go b/queries/userQueries.go index fec1af3..17536a5 100644 --- a/queries/userQueries.go +++ b/queries/userQueries.go @@ -10,3 +10,43 @@ func FindAllUsers() ([]common.User, int, error) { err := db.Find(&users).Error return users, len(users), err } + +func FindAllUsersSim(sim *common.Simulation) ([]common.User, int, error) { + db := common.GetDB() + var users []common.User + err := db.Model(sim).Related(&users, "Users").Error + return users, len(users), err +} + +func FindUserByName(username string) (common.User, error){ + db := common.GetDB() + var user common.User + err := db.Where("Username = ?", username).Find(user).Error + return user, err +} + +func AddUserToSim(sim *common.Simulation, user *common.User) error { + db := common.GetDB() + err := db.Model(sim).Association("Users").Append(user).Error + return err +} + +func RemoveUserFromSim(sim *common.Simulation, username string) error { + db := common.GetDB() + + user, err := FindUserByName(username) + if err != nil { + return err + } + + // remove user from simulation + err = db.Model(sim).Association("Users").Delete(&user).Error + if err != nil { + return err + } + + // remove simulation from user + err = db.Model(&user).Association("Simulations").Delete(sim).Error + + return err +} \ No newline at end of file From 1e0103495f78c666b7307bf330f0f12fc341f084 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 22 May 2019 16:01:01 +0200 Subject: [PATCH 15/30] work on visualization and widget endpoints --- endpoints/registration.go | 11 +- endpoints/visualizationEndpoints.go | 123 ++++++++++++++++-- endpoints/widgetEndpoints.go | 185 ++++++++++++++++++++++++++-- queries/visualizationQueries.go | 45 ++++++- queries/widgetQueries.go | 45 +++++++ 5 files changed, 384 insertions(+), 25 deletions(-) diff --git a/endpoints/registration.go b/endpoints/registration.go index 947ed3f..a539679 100644 --- a/endpoints/registration.go +++ b/endpoints/registration.go @@ -34,23 +34,24 @@ func SimulationsRegister(r *gin.RouterGroup) { r.POST("/:SimulationID/models/:ModelID/signals/:direction", signalRegistrationEp) r.GET("/:SimulationID/models/:ModelID/signals/:direction", signalReadAllEp) - //TODO I was here // Visualizations r.GET("/:SimulationID/visualizations", visualizationReadAllEp) r.POST("/:SimulationID/visualization", visualizationRegistrationEp) - r.POST("/:SimulationID/visualization/:visualizationID", visualizationCloneEp) + //r.POST("/:SimulationID/visualization/:visualizationID", visualizationCloneEp) r.PUT("/:SimulationID/visualization/:visualizationID", visualizationUpdateEp) r.GET("/:SimulationID/visualization/:visualizationID", visualizationReadEp) - r.DELETE("/:SimulationID/visualization/:visualizationID", visualizationDeleteEp) + //r.DELETE("/:SimulationID/visualization/:visualizationID", visualizationDeleteEp) // Widgets r.GET("/:SimulationID/visualization/:visualizationID/widgets", widgetReadAllEp) r.POST("/:SimulationID/visualization/:visualizationID/widget", widgetRegistrationEp) - r.POST("/:SimulationID/visualization/:visualizationID/widget:widgetID", widgetCloneEp) + //r.POST("/:SimulationID/visualization/:visualizationID/widget:widgetID", widgetCloneEp) r.PUT("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetUpdateEp) r.GET("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetReadEp) - r.DELETE("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetDeleteEp) + //r.DELETE("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetDeleteEp) + + // TODO I was here // Files // Files of Models r.GET("/:SimulationID/models/:ModelID/files", fileMReadAllEp) // NEW in API diff --git a/endpoints/visualizationEndpoints.go b/endpoints/visualizationEndpoints.go index 7271ef2..6553cbc 100644 --- a/endpoints/visualizationEndpoints.go +++ b/endpoints/visualizationEndpoints.go @@ -1,14 +1,28 @@ package endpoints import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" "github.com/gin-gonic/gin" + "fmt" "net/http" + "strconv" ) func visualizationReadAllEp(c *gin.Context) { - allVisualizations, _, _ := queries.FindAllVisualizations() + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + sim, err := queries.FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + allVisualizations, _, _ := queries.FindAllVisualizationsOfSim(&sim) serializer := serializers.VisualizationsSerializer{c, allVisualizations} c.JSON(http.StatusOK, gin.H{ "visualizations": serializer.Response(), @@ -16,9 +30,34 @@ func visualizationReadAllEp(c *gin.Context) { } func visualizationRegistrationEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + sim, err := queries.FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + var vis common.Visualization + err = c.BindJSON(&vis) + if err != nil { + errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + // add visualization to DB and add association to simulation + err = queries.AddVisualizationToSim(&sim, &vis) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK", + }) + } } func visualizationCloneEp(c *gin.Context) { @@ -28,14 +67,65 @@ func visualizationCloneEp(c *gin.Context) { } func visualizationUpdateEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + sim, err := queries.FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + visID, err := GetVisualizationID(c) + if err != nil { + return + } + + var vis common.Visualization + err = c.BindJSON(&vis) + if err != nil { + errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + err = queries.UpdateVisualizationOfSim(&sim, vis, visID) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK", + }) + } } func visualizationReadEp(c *gin.Context) { + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + sim, err := queries.FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + visID, err := GetVisualizationID(c) + if err != nil { + return + } + + visualization, err := queries.FindVisualizationOfSim(&sim, visID) + if common.ProvideErrorResponse(c, err) { + return + } + + serializer := serializers.VisualizationSerializer{c, visualization} c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", + "visualization": serializer.Response(), }) } @@ -44,3 +134,20 @@ func visualizationDeleteEp(c *gin.Context) { "message": "NOT implemented", }) } + + +func GetVisualizationID(c *gin.Context) (int, error) { + + simID, err := strconv.Atoi(c.Param("visualizationID")) + + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of visualization ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return -1, err + } else { + return simID, err + + } +} \ No newline at end of file diff --git a/endpoints/widgetEndpoints.go b/endpoints/widgetEndpoints.go index 33d9029..5ca9907 100644 --- a/endpoints/widgetEndpoints.go +++ b/endpoints/widgetEndpoints.go @@ -5,19 +5,86 @@ import ( "github.com/gin-gonic/gin" "net/http" "strconv" + + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" ) func widgetReadAllEp(c *gin.Context) { + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + sim, err := queries.FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + visID, err := GetVisualizationID(c) + if err != nil { + return + } + + vis, err := queries.FindVisualizationOfSim(&sim, visID) + if common.ProvideErrorResponse(c, err) { + return + } + + widgets,_, err := queries.FindWidgetsOfVisualization(&vis) + if common.ProvideErrorResponse(c, err) { + return + } + + serializer := serializers.WidgetsSerializer{c, widgets} c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", + "widgets": serializer.Response(), }) } func widgetRegistrationEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + sim, err := queries.FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + visID, err := GetVisualizationID(c) + if err != nil { + return + } + + vis, err := queries.FindVisualizationOfSim(&sim, visID) + if common.ProvideErrorResponse(c, err) { + return + } + + var widget_input common.Widget + err = c.BindJSON(&widget_input) + if err != nil { + errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + err = queries.AddWidgetToVisualization(&vis, &widget_input) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } + + } func widgetCloneEp(c *gin.Context) { @@ -27,18 +94,120 @@ func widgetCloneEp(c *gin.Context) { } func widgetUpdateEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) + simID, err := GetSimulationID(c) + if err != nil { + return + } + + sim, err := queries.FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + visID, err := GetVisualizationID(c) + if err != nil { + return + } + + vis, err := queries.FindVisualizationOfSim(&sim, visID) + if common.ProvideErrorResponse(c, err) { + return + } + + widgetID, err := GetWidgetID(c) + if err != nil { + return + } + + var widget_input common.Widget + err = c.BindJSON(&widget_input) + if err != nil { + errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + err = queries.UpdateWidgetOfVisualization(&vis, widget_input, widgetID) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK", + }) + } } func widgetReadEp(c *gin.Context) { + + simID, err := GetSimulationID(c) + if err != nil { + return + } + + sim, err := queries.FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + visID, err := GetVisualizationID(c) + if err != nil { + return + } + + visualization, err := queries.FindVisualizationOfSim(&sim, visID) + if common.ProvideErrorResponse(c, err) { + return + } + + widgetID, err := GetWidgetID(c) + if err != nil { + return + } + + widget, err := queries.FindWidgetOfVisualization(&visualization, widgetID) + serializer := serializers.WidgetSerializer{c, widget} c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", + "widget": serializer.Response(), }) } func widgetDeleteEp(c *gin.Context) { + + // simID, err := GetSimulationID(c) + // if err != nil { + // return + // } + // + // sim, err := queries.FindSimulation(simID) + // if common.ProvideErrorResponse(c, err) { + // return + // } + // + // visID, err := GetVisualizationID(c) + // if err != nil { + // return + // } + // + // visualization, err := queries.FindVisualizationOfSim(&sim, visID) + // if common.ProvideErrorResponse(c, err) { + // return + // } + // + // widgetID, err := GetWidgetID(c) + // if err != nil { + // return + // } + // + // widget, err := queries.FindWidgetOfVisualization(&visualization, widgetID) + // if common.ProvideErrorResponse(c, err) { + // return + // } + + // TODO delete files of widget in DB and on disk + + + // TODO Delete widget itself + association with visualization + c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) diff --git a/queries/visualizationQueries.go b/queries/visualizationQueries.go index 10a40a8..6626da3 100644 --- a/queries/visualizationQueries.go +++ b/queries/visualizationQueries.go @@ -4,9 +4,46 @@ import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) -func FindAllVisualizations() ([]common.Visualization, int, error) { +func FindAllVisualizationsOfSim(sim *common.Simulation) ([]common.Visualization, int, error) { db := common.GetDB() - var visualization []common.Visualization - err := db.Find(&visualization).Error - return visualization, len(visualization), err + var visualizations []common.Visualization + err := db.Model(sim).Related(&visualizations, "Visualizations").Error + return visualizations, len(visualizations), err } + +func FindVisualizationOfSim(sim *common.Simulation, visID int) (common.Visualization, error) { + db := common.GetDB() + var vis common.Visualization + err := db.Model(sim).Where("ID = ?", visID).Related(&vis, "Visualizations").Error + return vis, err +} + +func AddVisualizationToSim(sim * common.Simulation, vis * common.Visualization) error { + db := common.GetDB() + + // Add visualization to DB + err := db.Create(vis).Error + if err != nil { + return err + } + + // Add association with simulation + err = db.Model(sim).Association("Visualizations").Append(vis).Error + return err + +} + +func UpdateVisualizationOfSim(sim * common.Simulation, vis common.Visualization, visID int) error { + db := common.GetDB() + + // Get visualization of simulation that matches with ID (= visualization to be updated) + var vis_old common.Visualization + err := db.Model(sim).Where("ID = ?", visID).Related(&vis_old, "Visualizations").Error + if err != nil { + return err + } + + // Update visualization in DB (only name and grid can be updated) + err = db.Model(&vis_old).Updates(map[string]interface{}{"Name": vis.Name, "Grid": vis.Grid}).Error + return err +} \ No newline at end of file diff --git a/queries/widgetQueries.go b/queries/widgetQueries.go index 6cce6c5..2cd6d89 100644 --- a/queries/widgetQueries.go +++ b/queries/widgetQueries.go @@ -20,3 +20,48 @@ func FindWidget(widgetID int) (common.Widget, error){ } +func FindWidgetsOfVisualization(vis * common.Visualization) ([]common.Widget, int, error) { + db := common.GetDB() + var widgets []common.Widget + err := db.Model(vis).Related(&vis, "Widgets").Error + return widgets, len(widgets), err +} + +func FindWidgetOfVisualization(visualization *common.Visualization, widgetID int) (common.Widget, error){ + db := common.GetDB() + var widget common.Widget + err := db.Model(visualization).Where("ID = ?", widgetID).Related(&widget, "Widgets").Error + return widget, err +} + + +func AddWidgetToVisualization(vis *common.Visualization, widget_input * common.Widget) error { + + db := common.GetDB() + + // Add widget to DB + err := db.Create(widget_input).Error + if err != nil { + return err + } + + // Add association with visualization + err = db.Model(vis).Association("Widgets").Append(widget_input).Error + return err +} + +func UpdateWidgetOfVisualization(vis * common.Visualization, widget_input common.Widget, widgetID int) error { + db := common.GetDB() + + // Get widget of visualization that matches with ID (= widget to be updated) + var widget_old common.Widget + err := db.Model(vis).Where("ID = ?", widgetID).Related(&widget_old, "Widgets").Error + if err != nil { + return err + } + + // Update widget in DB + err = db.Model(&widget_old).Updates(map[string]interface{}{"Name": widget_input.Name, "Type": widget_input.Type, "MinHeight": widget_input.MinHeight, "MinWidth": widget_input.MinWidth, "Height": widget_input.Height, "Width": widget_input.Width, "X": widget_input.X, "Y": widget_input.Y, "Z": widget_input.Z, "CustomProperties": widget_input.CustomProperties}).Error + return err + +} \ No newline at end of file From 937194cecd90a388e89c870db7e39b86221d1883 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 22 May 2019 16:37:51 +0200 Subject: [PATCH 16/30] add endpoint testing with Go (WIP) --- .gitlab-ci.yml | 16 ++++++++++- endpoints/endpoints_test.go | 53 +++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 endpoints/endpoints_test.go diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7b7862d..d5087fa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -57,7 +57,7 @@ build:backend: # Stage: test ############################################################################## -test:backend: +test:backend:database: stage: test tags: - docker @@ -70,6 +70,20 @@ test:backend: dependencies: - build:backend +test:backend:endpoints: + stage: test + tags: + - docker + image: villaswebbackendgo:ubuntu + script: + - /etc/init.d/postgresql start + - go mod tidy + - cd endpoints + - go test -v -args -dbhost=/var/run/postgresql + dependencies: + - build:backend + - test:backend:database + # Stage: deploy ############################################################################## diff --git a/endpoints/endpoints_test.go b/endpoints/endpoints_test.go new file mode 100644 index 0000000..e23da32 --- /dev/null +++ b/endpoints/endpoints_test.go @@ -0,0 +1,53 @@ +package endpoints + +import ( + "net/http" + "testing" + "net/http/httptest" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" +) + +// Test /simulation endpoints +func TestSimulationEndpoints(t *testing.T) { + + db := common.DummyInitDB() + defer db.Close() + common.DummyPopulateDB(db) + + + router := gin.Default() + api := router.Group("/api") + SimulationsRegister(api.Group("/simulations")) + + w := httptest.NewRecorder() + + // test GET simulations/ + req, _ := http.NewRequest("GET", "/api/simulations/", nil) + router.ServeHTTP(w, req) + assert.Equal(t, 200, w.Code) + var simulations_response = "{\"simulations\":[{\"Name\":\"Simulation_A\",\"SimulationID\":1,\"Running\":false,\"Starting Parameters\":null},{\"Name\":\"Simulation_B\",\"SimulationID\":2,\"Running\":false,\"Starting Parameters\":null}]}" + assert.Equal(t, simulations_response, w.Body.String()) + + // test get simulations/:SimulationID + w = httptest.NewRecorder() + req, _ = http.NewRequest("GET", "/api/simulations/1", nil) + router.ServeHTTP(w, req) + assert.Equal(t, 200, w.Code) + simulations_response = "{\"simulation\":{\"Name\":\"Simulation_A\",\"SimulationID\":1,\"Running\":false,\"Starting Parameters\":null}}" + assert.Equal(t, simulations_response, w.Body.String()) + + // test get simulations/:SimulationID/users + w = httptest.NewRecorder() + req, _ = http.NewRequest("GET", "/api/simulations/1/users", nil) + router.ServeHTTP(w, req) + assert.Equal(t, 200, w.Code) + simulations_response = "{\"users\":[{\"Username\":\"User_A\",\"Role\":\"user\",\"Mail\":\"\"},{\"Username\":\"User_B\",\"Role\":\"user\",\"Mail\":\"\"}]}" + assert.Equal(t, simulations_response, w.Body.String()) + + // TODO add more tests + +} From 5a046103b5a3521f684239b02c79bed635210c41 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Wed, 22 May 2019 16:38:44 +0200 Subject: [PATCH 17/30] fix typo --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d5087fa..8059e9a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -82,7 +82,6 @@ test:backend:endpoints: - go test -v -args -dbhost=/var/run/postgresql dependencies: - build:backend - - test:backend:database # Stage: deploy ############################################################################## From b89a68296faa9280950bb68515884dfefb3ce7b6 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Thu, 23 May 2019 14:36:42 +0200 Subject: [PATCH 18/30] Experimenting with testing and auto generating API specs based on code --- common/database_test.go | 3 +- common/datastructs.go | 123 ++++++++++++++++------ doc/autoapi/generateapidoc.sh | 9 ++ endpoints/endpoints_test.go | 134 ++++++++++++++++++++---- endpoints/fileEndpoints.go | 37 +++++-- endpoints/modelEndpoints.go | 27 ++++- endpoints/registration.go | 10 +- endpoints/signalEndpoints.go | 3 +- endpoints/simulationEndpoints.go | 36 +++++-- endpoints/simulatorEndpoints.go | 10 +- endpoints/userEndpoints.go | 42 +++++++- endpoints/visualizationEndpoints.go | 10 +- endpoints/widgetEndpoints.go | 3 +- go.mod | 28 ++--- go.sum | 137 ++++++++++++------------- queries/fileQueries.go | 5 +- queries/userQueries.go | 2 +- serializers/visualizationSerializer.go | 2 +- start.go | 29 +++++- 19 files changed, 465 insertions(+), 185 deletions(-) create mode 100755 doc/autoapi/generateapidoc.sh diff --git a/common/database_test.go b/common/database_test.go index 5df04bb..319f4e8 100644 --- a/common/database_test.go +++ b/common/database_test.go @@ -2,8 +2,9 @@ package common import ( "fmt" - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) // Verify that you can connect to the database diff --git a/common/datastructs.go b/common/datastructs.go index c7687f0..9ee868d 100644 --- a/common/datastructs.go +++ b/common/datastructs.go @@ -7,112 +7,169 @@ import ( "github.com/jinzhu/gorm/dialects/postgres" ) +// User data model type User struct { - //gorm.Model + // ID of user ID uint `gorm:"primary_key;auto_increment"` + // Username of user Username string `gorm:"unique;not null"` + // Password of user Password string `gorm:"not null"` + // Mail of user Mail string `gorm:"default:''"` + // Role of user Role string `gorm:"default:'user'"` - + // Simulations to which user has access Simulations []Simulation `gorm:"many2many:user_simulations"` } -type Simulation struct { - //gorm.Model - ID uint `gorm:"primary_key;auto_increment"` - Name string `gorm:"not null"` - Running bool `gorm:"default:false"` - StartParameters postgres.Jsonb +// Simulation data model +type Simulation struct { + // ID of simulation + ID uint `gorm:"primary_key;auto_increment"` + // Name of simulation + Name string `gorm:"not null"` + // Running state of simulation + Running bool `gorm:"default:false"` + // Start parameters of simulation + StartParameters postgres.Jsonb + // Users that have access to the simulation Users []User `gorm:"not null;many2many:user_simulations"` + // Models that belong to the simulation Models []Model `gorm:"foreignkey:SimulationID"` + // Visualizations that belong to the simulation Visualizations []Visualization `gorm:"foreignkey:SimulationID"` } +// Model data model type Model struct { - //gorm.Model + // ID of model ID uint `gorm:"primary_key;auto_increment"` + // Name of model Name string `gorm:"not null"` + // Number of output signals OutputLength int `gorm:"default:1"` + // Number of input signals InputLength int `gorm:"default:1"` + // Start parameters of model StartParameters postgres.Jsonb - + // ID of simulation to which model belongs SimulationID uint + // Simulator associated with model Simulator Simulator + // ID of simulator associated with model SimulatorID uint - - // NOTE: order of signals is important + // Mapping of output signals of the model, order of signals is important OutputMapping []Signal + // Mapping of input signals of the model, order of signals is important InputMapping []Signal - - //new in villasweb 2.0 (for CIM file of simulation model and other model file formats) + // Files of model (can be CIM and other model file formats) Files []File `gorm:"foreignkey:ModelID"` - } -type Signal struct { // NOT A DB TABLE +type Signal struct { + // Name of Signal Name string + // Unit of Signal Unit string + // Index of the Signal in the mapping Index uint + // Direction of the signal (in or out) Direction string } + +// Simulator data model type Simulator struct { - //gorm.Model + // ID of the simulator ID uint `gorm:"primary_key;auto_increment"` + // UUID of the simulator UUID string `gorm:"unique;not null"` + // Host if the simulator Host string `gorm:"default:''"` + // Model type supported by the simulator Modeltype string `gorm:"default:''"` + // Uptime of the simulator Uptime int `gorm:"default:0"` + // State of the simulator State string `gorm:"default:''"` + // Time of last state update StateUpdateAt time.Time + // Properties of simulator Properties postgres.Jsonb + // Raw properties of simulator RawProperties postgres.Jsonb } +// Visualization data model type Visualization struct { - //gorm.Model + // ID of visualization ID uint `gorm:"primary_key;auto_increment"` + // Name of visualization Name string `gorm:"not null"` + // Grid of visualization Grid int `gorm:"default:15"` - + // ID of simulation to which visualization belongs SimulationID uint `gorm:"not null"` - + // Widgets that belong to visualization Widgets []Widget `gorm:"foreignkey:VisualizationID"` } + +// Widget data model type Widget struct { - //gorm.Model + // ID of widget ID uint `gorm:"primary_key;auto_increment"` + // Name of widget Name string `gorm:"not null"` + // Type of widget Type string `gorm:"not null"` + // Width of widget Width uint `gorm:"not null"` + // Height of widget Height uint `gorm:"not null"` + // Minimal width of widget MinWidth uint `gorm:"not null"` + // Minimal height of widget MinHeight uint `gorm:"not null"` + // X position of widget X int `gorm:"not null"` + // Y position of widget Y int `gorm:"not null"` + // Z position of widget Z int `gorm:"not null"` + // Locked state of widget IsLocked bool `gorm:"default:false"` + // Custom properties of widget CustomProperties postgres.Jsonb + // ID of visualization to which widget belongs VisualizationID uint `gorm:"not null"` - //new in villasweb 2.0 + // Files that belong to widget (for example images) Files []File `gorm:"foreignkey:WidgetID"` } -type File struct { - //gorm.Model - ID uint `gorm:"primary_key;auto_increment"` - Name string `gorm:"not null"` - Path string `gorm:"not null"` - Type string `gorm:"not null"` - Size uint `gorm:"not null"` - ImageHeight uint // only required in case file is an image - ImageWidth uint // only required in case file is an image - Date time.Time - //new in villasweb 2.0 +// File data model +type File struct { + // ID of file + ID uint `gorm:"primary_key;auto_increment"` + // Name of file + Name string `gorm:"not null"` + // Path at which file is saved at server side + Path string `gorm:"not null"` + // Type of file (MIME type) + Type string `gorm:"not null"` + // Size of file (in byte) + Size uint `gorm:"not null"` + // Height of image (only needed in case of image) + ImageHeight uint + // Width of image (only needed in case of image) + ImageWidth uint + // Last modification time of file + Date time.Time + // ID of model to which file belongs ModelID uint `gorm:""` + // ID of widget to which file belongs WidgetID uint `gorm:""` } diff --git a/doc/autoapi/generateapidoc.sh b/doc/autoapi/generateapidoc.sh new file mode 100755 index 0000000..37d26d3 --- /dev/null +++ b/doc/autoapi/generateapidoc.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + + +cd ../../ +go mod tidy +swag init -p pascalcase -g "start.go" -o "./doc/autoapi/" +cd - + +redoc-cli bundle --cdn --title "VILLASweb Backend API" --output index.html swagger.yaml \ No newline at end of file diff --git a/endpoints/endpoints_test.go b/endpoints/endpoints_test.go index e23da32..7cee614 100644 --- a/endpoints/endpoints_test.go +++ b/endpoints/endpoints_test.go @@ -1,9 +1,11 @@ package endpoints import ( + "encoding/json" + "fmt" "net/http" - "testing" "net/http/httptest" + "testing" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" @@ -11,6 +13,77 @@ import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) + +type responseMsg struct{ + Message string `json:"message"` +} + +var msgOK = responseMsg{ + Message: "OK.", +} + +type user struct{ + Username string `json:"Username"` + Role string `json:"Role"` + Mail string `json:"Mail"` +} + +type responseUsers struct { + Users []user `json:"users"` +} + +var users = []user{ + { + Username: "User_A", + Role: "user", + Mail: "", + }, + { + Username: "User_B", + Role: "user", + Mail: "", + }, +} + +var msgUsers = responseUsers{ + Users: users, +} + +type simulation struct{ + Name string `json:"Name"` + SimulationID uint `json:"SimulationID"` + Running bool `json:"Running"` +} + +type responseSimulations struct { + Simulations []simulation `json:"simulations"` +} + +type responseSimulation struct { + Simulation simulation `json:"simulation"` +} + +var simulationA = simulation{ + Name: "Simulation_A", + SimulationID: 1, + Running: false, +} + +var simulationB = simulation{ + Name: "Simulation_B", + SimulationID: 2, + Running: false, +} + +var simulations = []simulation{ + simulationA, + simulationB, +} + +var msgSimulations = responseSimulations{ + Simulations: simulations, +} + // Test /simulation endpoints func TestSimulationEndpoints(t *testing.T) { @@ -23,31 +96,50 @@ func TestSimulationEndpoints(t *testing.T) { api := router.Group("/api") SimulationsRegister(api.Group("/simulations")) - w := httptest.NewRecorder() + msgOKjson, err := json.Marshal(msgOK) + if err !=nil { + panic(err) + } + + msgUsersjson, err := json.Marshal(msgUsers) + if err !=nil { + panic(err) + } + + // msgSimulationsjson, err := json.Marshal(msgSimulations) + // if err !=nil { + // panic(err) + // } // test GET simulations/ - req, _ := http.NewRequest("GET", "/api/simulations/", nil) - router.ServeHTTP(w, req) - assert.Equal(t, 200, w.Code) - var simulations_response = "{\"simulations\":[{\"Name\":\"Simulation_A\",\"SimulationID\":1,\"Running\":false,\"Starting Parameters\":null},{\"Name\":\"Simulation_B\",\"SimulationID\":2,\"Running\":false,\"Starting Parameters\":null}]}" - assert.Equal(t, simulations_response, w.Body.String()) + var expected_response = "{\"simulations\":[{\"Name\":\"Simulation_A\",\"SimulationID\":1,\"Running\":false,\"Starting Parameters\":null},{\"Name\":\"Simulation_B\",\"SimulationID\":2,\"Running\":false,\"Starting Parameters\":null}]}" + testEndpoint(t, router, "/api/simulations/", "GET", "", 200, expected_response) + + // test GET simulations/:SimulationID + expected_response = "{\"simulation\":{\"Name\":\"Simulation_A\",\"SimulationID\":1,\"Running\":false,\"Starting Parameters\":null}}" + testEndpoint(t, router, "/api/simulations/1", "GET", "", 200, expected_response) + + // test GET simulations/:SimulationID/users + testEndpoint(t, router, "/api/simulations/1/users", "GET", "", 200, string(msgUsersjson)) + + // test DELETE simulations/:SimulationID/user/:username + testEndpoint(t, router, "/api/simulations/1/user/User_A", "DELETE", "", 200, string(msgOKjson)) + + // test PUT simulations/:SimulationID/user/:username + testEndpoint(t, router, "/api/simulations/1/user/User_A", "PUT", "", 200, string(msgOKjson)) - // test get simulations/:SimulationID - w = httptest.NewRecorder() - req, _ = http.NewRequest("GET", "/api/simulations/1", nil) - router.ServeHTTP(w, req) - assert.Equal(t, 200, w.Code) - simulations_response = "{\"simulation\":{\"Name\":\"Simulation_A\",\"SimulationID\":1,\"Running\":false,\"Starting Parameters\":null}}" - assert.Equal(t, simulations_response, w.Body.String()) - // test get simulations/:SimulationID/users - w = httptest.NewRecorder() - req, _ = http.NewRequest("GET", "/api/simulations/1/users", nil) - router.ServeHTTP(w, req) - assert.Equal(t, 200, w.Code) - simulations_response = "{\"users\":[{\"Username\":\"User_A\",\"Role\":\"user\",\"Mail\":\"\"},{\"Username\":\"User_B\",\"Role\":\"user\",\"Mail\":\"\"}]}" - assert.Equal(t, simulations_response, w.Body.String()) // TODO add more tests } + + +func testEndpoint(t *testing.T, router *gin.Engine, url string, method string, body string, expected_code int, expected_response string ) { + w := httptest.NewRecorder() + req, _ := http.NewRequest(method, url, nil) + router.ServeHTTP(w, req) + assert.Equal(t, expected_code, w.Code) + fmt.Println(w.Body.String()) + assert.Equal(t, expected_response, w.Body.String()) +} \ No newline at end of file diff --git a/endpoints/fileEndpoints.go b/endpoints/fileEndpoints.go index 858e4da..7cb6cb7 100644 --- a/endpoints/fileEndpoints.go +++ b/endpoints/fileEndpoints.go @@ -2,17 +2,30 @@ package endpoints import ( "fmt" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" "strconv" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" - "github.com/gin-gonic/gin" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" + "net/http" + + "github.com/gin-gonic/gin" + + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) -// Endpoint functions - +// fileMReadAllEp godoc +// @Summary Get all parameters of files of model +// @ID GetAllModelFileParams +// @Tags file +// @Success 200 {array} common.File "File parameters requested by user" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param modelID path int true "Model ID" +// @Router simulations/{simulationID}/models/{modelID}/files [get] func fileMReadAllEp(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) @@ -32,6 +45,18 @@ func fileMReadAllEp(c *gin.Context) { } +// fileMRegistrationEp godoc +// @Summary Get all parameters of files of model +// @ID PostFileToModel +// @Tags file +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param modelID path int true "Model ID" +// @Router simulations/{simulationID}/models/{modelID}/file [post] func fileMRegistrationEp(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) diff --git a/endpoints/modelEndpoints.go b/endpoints/modelEndpoints.go index a32aaa9..62e67a4 100644 --- a/endpoints/modelEndpoints.go +++ b/endpoints/modelEndpoints.go @@ -2,15 +2,27 @@ package endpoints import ( "fmt" + "net/http" + "strconv" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" - "net/http" - "strconv" "github.com/gin-gonic/gin" ) +// modelReadAllEp godoc +// @Summary Get all models of simulation +// @ID GetAllModelsOfSimulation +// @Produce json +// @Tags model +// @Success 200 {array} common.Model "Array of models to which belong to simulation" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Router /simulations/{simulationID}/models [get] func modelReadAllEp(c *gin.Context) { simID, err := GetSimulationID(c) @@ -29,6 +41,17 @@ func modelReadAllEp(c *gin.Context) { }) } +// modelRegistrationEp godoc +// @Summary Add a model to a simulation +// @ID AddModelToSimulation +// @Tags model +// @Param inputModel body common.Model true "Model to be added" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Router /simulations/{simulationID}/models [post] func modelRegistrationEp(c *gin.Context) { simID, err := GetSimulationID(c) diff --git a/endpoints/registration.go b/endpoints/registration.go index a539679..67d5264 100644 --- a/endpoints/registration.go +++ b/endpoints/registration.go @@ -6,11 +6,11 @@ import ( func SimulationsRegister(r *gin.RouterGroup) { - r.GET("/", simulationReadAllEp) + r.GET("/", getSimulationsEp) //r.POST("/", simulationRegistrationEp) //r.POST("/:SimulationID", simulationCloneEp) //r.PUT("/:SimulationID", simulationUpdateEp) - r.GET("/:SimulationID", simulationReadEp) + r.GET("/:SimulationID", getSimulationEp) //r.DELETE("/:SimulationID", simulationDeleteEp) // Users @@ -50,11 +50,7 @@ func SimulationsRegister(r *gin.RouterGroup) { r.GET("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetReadEp) //r.DELETE("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetDeleteEp) - - // TODO I was here - // Files - // Files of Models - r.GET("/:SimulationID/models/:ModelID/files", fileMReadAllEp) // NEW in API + r.GET("/:SimulationID/models/:ModelID/files", fileMReadAllEp) r.POST ("/:SimulationID/models/:ModelID/file", fileMRegistrationEp) // NEW in API //r.POST ("/:SimulationID/models/:ModelID/file", fileMCloneEp) // NEW in API r.GET("/:SimulationID/models/:ModelID/file", fileMReadEp) // NEW in API diff --git a/endpoints/signalEndpoints.go b/endpoints/signalEndpoints.go index e42dd92..6297d7e 100644 --- a/endpoints/signalEndpoints.go +++ b/endpoints/signalEndpoints.go @@ -1,9 +1,10 @@ package endpoints import ( - "github.com/gin-gonic/gin" "net/http" + "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/queries" ) diff --git a/endpoints/simulationEndpoints.go b/endpoints/simulationEndpoints.go index 48e3781..7af97e3 100644 --- a/endpoints/simulationEndpoints.go +++ b/endpoints/simulationEndpoints.go @@ -2,16 +2,28 @@ package endpoints import ( "fmt" + "net/http" + "strconv" + + "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/queries" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" - "github.com/gin-gonic/gin" - "net/http" - "strconv" ) - -func simulationReadAllEp(c *gin.Context) { +// getSimulationsEp godoc +// @Summary Get all simulations +// @ID GetAllSimulations +// @Produce json +// @Tags simulation +// @Success 200 {array} common.Simulation "Array of simulations to which user has access" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Router /simulations [get] +func getSimulationsEp(c *gin.Context) { //TODO Identify user who is issuing the request and return only those simulations that are known to the user @@ -40,7 +52,19 @@ func simulationUpdateEp(c *gin.Context) { }) } -func simulationReadEp(c *gin.Context) { +// getSimulationEp godoc +// @Summary Get simulation +// @ID GetSimulation +// @Produce json +// @Tags simulation +// @Success 200 {object} common.Simulation "Simulation requested by user" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Router /simulations/{simulationID} [get] +func getSimulationEp(c *gin.Context) { simID, err := GetSimulationID(c) if err != nil { diff --git a/endpoints/simulatorEndpoints.go b/endpoints/simulatorEndpoints.go index d9b95f2..49eb0f2 100644 --- a/endpoints/simulatorEndpoints.go +++ b/endpoints/simulatorEndpoints.go @@ -1,13 +1,15 @@ package endpoints import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" - "github.com/gin-gonic/gin" "fmt" "net/http" "strconv" + + "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/queries" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" ) func simulatorReadAllEp(c *gin.Context) { diff --git a/endpoints/userEndpoints.go b/endpoints/userEndpoints.go index 5611dcf..8fb79de 100644 --- a/endpoints/userEndpoints.go +++ b/endpoints/userEndpoints.go @@ -1,11 +1,13 @@ package endpoints import ( + "net/http" + + "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/queries" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" - "github.com/gin-gonic/gin" - "net/http" ) @@ -18,6 +20,18 @@ func userReadAllEp(c *gin.Context) { }) } +// userReadAllSimEp godoc +// @Summary Get users of simulation +// @ID GetAllUsersOfSimulation +// @Produce json +// @Tags user +// @Success 200 {array} common.User "Array of users that have access to the simulation" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Router /simulations/{simulationID}/users [get] func userReadAllSimEp(c *gin.Context) { simID, err := GetSimulationID(c) @@ -54,6 +68,18 @@ func userUpdateEp(c *gin.Context) { }) } +// userUpdateSimEp godoc +// @Summary Add user to simulation +// @ID AddUserToSimulation +// @Tags user +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param username path int true "Username of user to be added" +// @Router /simulations/{simulationID}/users/{username} [put] func userUpdateSimEp(c *gin.Context) { @@ -102,6 +128,18 @@ func userDeleteEp(c *gin.Context) { }) } +// userDeleteSimEp godoc +// @Summary Delete user from simulation +// @ID DeleteUserFromSimulation +// @Tags user +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param username path int true "Username of user" +// @Router /simulations/{simulationID}/users/{username} [delete] func userDeleteSimEp(c *gin.Context) { simID, err := GetSimulationID(c) diff --git a/endpoints/visualizationEndpoints.go b/endpoints/visualizationEndpoints.go index 6553cbc..73ed287 100644 --- a/endpoints/visualizationEndpoints.go +++ b/endpoints/visualizationEndpoints.go @@ -1,13 +1,15 @@ package endpoints import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" - "github.com/gin-gonic/gin" "fmt" "net/http" "strconv" + + "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/queries" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" ) func visualizationReadAllEp(c *gin.Context) { diff --git a/endpoints/widgetEndpoints.go b/endpoints/widgetEndpoints.go index 5ca9907..15a7867 100644 --- a/endpoints/widgetEndpoints.go +++ b/endpoints/widgetEndpoints.go @@ -2,10 +2,11 @@ package endpoints import ( "fmt" - "github.com/gin-gonic/gin" "net/http" "strconv" + "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/queries" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" diff --git a/go.mod b/go.mod index 2887ab2..c1aa15d 100644 --- a/go.mod +++ b/go.mod @@ -1,24 +1,12 @@ module git.rwth-aachen.de/acs/public/villas/villasweb-backend-go require ( - github.com/denisenkom/go-mssqldb v0.0.0-20190401154936-ce35bd87d4b3 // indirect - github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect - github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 // indirect - github.com/gin-gonic/gin v1.3.0 - github.com/go-sql-driver/mysql v1.4.1 // indirect - github.com/gofrs/uuid v3.2.0+incompatible // indirect - github.com/jinzhu/gorm v1.9.2 - github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect - github.com/jinzhu/now v1.0.0 // indirect - github.com/json-iterator/go v1.1.6 // indirect - github.com/kr/pretty v0.1.0 // indirect - github.com/lib/pq v1.0.0 // indirect - github.com/mattn/go-isatty v0.0.7 // indirect - github.com/mattn/go-sqlite3 v1.10.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/stretchr/testify v1.2.2 - github.com/ugorji/go v1.1.4 // indirect - gopkg.in/go-playground/assert.v1 v1.2.1 // indirect - gopkg.in/go-playground/validator.v8 v8.18.2 // indirect + github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc + github.com/gin-gonic/gin v1.4.0 + github.com/jinzhu/gorm v1.9.8 + github.com/stretchr/testify v1.3.0 + github.com/swaggo/gin-swagger v1.1.0 + github.com/swaggo/swag v1.5.0 ) + +replace github.com/ugorji/go v1.1.4 => github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 diff --git a/go.sum b/go.sum index 51d48d0..9d8301b 100644 --- a/go.sum +++ b/go.sum @@ -1,73 +1,76 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.2 h1:4y4L7BdHenTfZL0HervofNTHh9Ad6mNX72cQvl+5eH0= -cloud.google.com/go v0.37.2/go.mod h1:H8IAquKe2L30IxoupDgqTaQvKSwF/c8prYHynGIWQbA= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= +cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20190401154936-ce35bd87d4b3 h1:3mNLx0iFqaq/Ssxqkjte26072KMu96uz1VBlbiZhQU4= -github.com/denisenkom/go-mssqldb v0.0.0-20190401154936-ce35bd87d4b3/go.mod h1:EcO5fNtMZHCMjAvj8LE6T+5bphSdR6LQ75n+m1TtsFI= +github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02 h1:PS3xfVPa8N84AzoWZHFCbA0+ikz4f4skktfjQoNMsgk= +github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs= github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0 h1:KVRzjXpMzgdM4GEMDmDTnGcY5yBwGWreJwmmk4k35yU= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0 h1:oP2OUNdG1l2r5kYhrfVMXO54gWmzcfAwP/GFuHpNTkE= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/spec v0.18.0 h1:aIjeyG5mo5/FrvDkpKKEGZPmF9MPHahS72mzfVqeQXQ= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0 h1:1DU8Km1MRGv9Pj7BNLmkA+umwTStwDHttXvx3NhJA70= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/jinzhu/gorm v1.9.2 h1:lCvgEaqe/HVE+tjAR2mt4HbbHAZsQOv3XAZiEZV37iw= -github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= +github.com/jinzhu/gorm v1.9.8 h1:n5uvxqLepIP2R1XF7pudpt9Rv8I3m7G9trGxJVjLZ5k= +github.com/jinzhu/gorm v1.9.8/go.mod h1:bdqTT3q6dhSph2K3pWxrHP6nqxuAp2yQ3KFtc3U3F84= github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k= github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.0 h1:6WV8LvwPpDhKjo5U9O6b4+xdG/jTXNPwlDme/MTo8Ns= @@ -82,11 +85,14 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= @@ -100,104 +106,96 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/swaggo/gin-swagger v1.1.0 h1:ZI6/82S07DkkrMfGKbJhKj1R+QNTICkeAJP06pU36pU= +github.com/swaggo/gin-swagger v1.1.0/go.mod h1:FQlm07YuT1glfN3hQiO11UQ2m39vOCZ/aa3WWr5E+XU= +github.com/swaggo/swag v1.4.0/go.mod h1:hog2WgeMOrQ/LvQ+o1YGTeT+vWVrbi0SiIslBtxKTyM= +github.com/swaggo/swag v1.5.0 h1:haK8VG3hj+v/c8hQ4f3U+oYpkdI/26m9LAUTXHOv+2U= +github.com/swaggo/swag v1.5.0/go.mod h1:+xZrnu5Ut3GcUkKAJm9spnOooIS1WB1cUOkLNPrvrE0= +github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A= -go.opencensus.io v0.19.2/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190314133821-5284462c4bec/go.mod h1:atTaCNAy0f16Ah5aV1gMSwgiKVHwu/JncqDpuRr7lS4= +github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= +github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 h1:vG/gY/PxA3v3l04qxe3tDjXyu3bozii8ulSlIPOYKhI= +github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190322120337-addf6b3196f6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc h1:4gbWbmmPFp4ySWICouJl6emP0MyS31yy9SrTlAGFT+g= +golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190110015856-aa033095749b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU= -google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw= +golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89 h1:iWXXYN3edZ3Nd/7I6Rt1sXrWVmhF9bgVtlEJ7BbH124= +golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -209,13 +207,10 @@ gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXa gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/queries/fileQueries.go b/queries/fileQueries.go index 7187d0b..a571d77 100644 --- a/queries/fileQueries.go +++ b/queries/fileQueries.go @@ -2,8 +2,6 @@ package queries import ( "fmt" - "github.com/gin-gonic/gin" - _ "github.com/gin-gonic/gin" "io" "mime/multipart" "net/http" @@ -11,6 +9,9 @@ import ( "path/filepath" "strconv" + "github.com/gin-gonic/gin" + _ "github.com/gin-gonic/gin" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) diff --git a/queries/userQueries.go b/queries/userQueries.go index 17536a5..de48191 100644 --- a/queries/userQueries.go +++ b/queries/userQueries.go @@ -21,7 +21,7 @@ func FindAllUsersSim(sim *common.Simulation) ([]common.User, int, error) { func FindUserByName(username string) (common.User, error){ db := common.GetDB() var user common.User - err := db.Where("Username = ?", username).Find(user).Error + err := db.Where("Username = ?", username).Find(&user).Error return user, err } diff --git a/serializers/visualizationSerializer.go b/serializers/visualizationSerializer.go index 0fea770..e6ff9c0 100644 --- a/serializers/visualizationSerializer.go +++ b/serializers/visualizationSerializer.go @@ -1,9 +1,9 @@ package serializers import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" "github.com/gin-gonic/gin" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) diff --git a/start.go b/start.go index 673530c..8294c30 100644 --- a/start.go +++ b/start.go @@ -1,12 +1,33 @@ package main import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/endpoints" "github.com/gin-gonic/gin" + "github.com/swaggo/gin-swagger" + "github.com/swaggo/gin-swagger/swaggerFiles" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/endpoints" + + _ "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/apidocs" // apidocs folder is generated by Swag CLI, you have to import it ) + +// @title VILLASweb Backend API +// @version 2.0 +// @description This is the API of the VILLASweb Backend +// @description WORK IN PROGRESS! PLEASE BE PATIENT! + +// @description This documentation is auto-generated based on the API documentation in the code. +// @description The tool https://github.com/swaggo/swag is used to auto-generate API docs for gin. + +// @contact.name Sonja Happ +// @contact.email sonja.happ@eonerc.rwth-aachen.de + +// @license.name GNU GPL 3.0 +// @license.url http://www.gnu.de/documents/gpl-3.0.en.html + +// @host aaa.bbb.ccc.ddd:pppp +// @BasePath /api/v2 func main() { // Testing db := common.InitDB() @@ -15,7 +36,11 @@ func main() { r := gin.Default() + + + api := r.Group("/api") + // use ginSwagger middleware to endpoints.UsersRegister(api.Group("/users")) //file.FilesRegister(api.Group("/files")) //project.ProjectsRegister(api.Group("/projects")) @@ -24,7 +49,7 @@ func main() { endpoints.SimulatorsRegister(api.Group("/simulators")) //visualization.VisualizationsRegister(api.Group("/visualizations")) - + r.GET("swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) r.Run() From 144c97ce49a2a23853cb4a404ef4ff455f9baf78 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Thu, 23 May 2019 16:50:05 +0200 Subject: [PATCH 19/30] Refactoring code base to new structure, removing circular dependencies, creating response types --- common/dberrorhandling.go | 26 -- common/{datastructs.go => models.go} | 23 +- common/responses.go | 95 ++++++ common/serializers.go | 245 ++++++++++++++ common/utilities.go | 93 ++++++ endpoints/endpoints_test.go | 145 -------- endpoints/modelEndpoints.go | 184 ---------- endpoints/registration.go | 88 ----- endpoints/signalEndpoints.go | 84 ----- endpoints/simulatorEndpoints.go | 112 ------- go.sum | 1 + queries/modelQueries.go | 99 ------ queries/signalQueries.go | 20 -- routes/endpoints_test.go | 144 ++++++++ {endpoints => routes/file}/fileEndpoints.go | 84 +++-- {queries => routes/file}/fileQueries.go | 11 +- routes/model/modelEndpoints.go | 314 ++++++++++++++++++ routes/model/modelQueries.go | 74 +++++ .../simulation}/simulationEndpoints.go | 62 ++-- .../simulation}/simulationQueries.go | 2 +- routes/simulator/simulatorEndpoints.go | 58 ++++ .../simulator}/simulatorQueries.go | 2 +- {endpoints => routes/user}/userEndpoints.go | 85 ++--- {queries => routes/user}/userQueries.go | 2 +- .../visualization}/visualizationEndpoints.go | 77 ++--- .../visualization}/visualizationQueries.go | 2 +- .../widget}/widgetEndpoints.go | 91 +++-- {queries => routes/widget}/widgetQueries.go | 2 +- serializers/fileSerializer.go | 53 --- serializers/modelSerializer.go | 54 --- serializers/signalSerializer.go | 1 - serializers/simulationSerializer.go | 44 --- serializers/simulatorSerializer.go | 52 --- serializers/userSerializer.go | 45 --- serializers/visualizationSerializer.go | 49 --- serializers/widgetSerializer.go | 60 ---- start.go | 31 +- 37 files changed, 1261 insertions(+), 1353 deletions(-) delete mode 100644 common/dberrorhandling.go rename common/{datastructs.go => models.go} (92%) create mode 100644 common/responses.go create mode 100644 common/serializers.go create mode 100644 common/utilities.go delete mode 100644 endpoints/endpoints_test.go delete mode 100644 endpoints/modelEndpoints.go delete mode 100644 endpoints/registration.go delete mode 100644 endpoints/signalEndpoints.go delete mode 100644 endpoints/simulatorEndpoints.go delete mode 100644 queries/modelQueries.go delete mode 100644 queries/signalQueries.go create mode 100644 routes/endpoints_test.go rename {endpoints => routes/file}/fileEndpoints.go (67%) rename {queries => routes/file}/fileQueries.go (96%) create mode 100644 routes/model/modelEndpoints.go create mode 100644 routes/model/modelQueries.go rename {endpoints => routes/simulation}/simulationEndpoints.go (51%) rename {queries => routes/simulation}/simulationQueries.go (97%) create mode 100644 routes/simulator/simulatorEndpoints.go rename {queries => routes/simulator}/simulatorQueries.go (96%) rename {endpoints => routes/user}/userEndpoints.go (56%) rename {queries => routes/user}/userQueries.go (98%) rename {endpoints => routes/visualization}/visualizationEndpoints.go (50%) rename {queries => routes/visualization}/visualizationQueries.go (98%) rename {endpoints => routes/widget}/widgetEndpoints.go (55%) rename {queries => routes/widget}/widgetQueries.go (99%) delete mode 100644 serializers/fileSerializer.go delete mode 100644 serializers/modelSerializer.go delete mode 100644 serializers/signalSerializer.go delete mode 100644 serializers/simulationSerializer.go delete mode 100644 serializers/simulatorSerializer.go delete mode 100644 serializers/userSerializer.go delete mode 100644 serializers/visualizationSerializer.go delete mode 100644 serializers/widgetSerializer.go diff --git a/common/dberrorhandling.go b/common/dberrorhandling.go deleted file mode 100644 index 12112aa..0000000 --- a/common/dberrorhandling.go +++ /dev/null @@ -1,26 +0,0 @@ -package common - -import ( - "net/http" - - "github.com/gin-gonic/gin" - "github.com/jinzhu/gorm" -) - -func ProvideErrorResponse(c *gin.Context, err error) bool { - if err != nil { - if err == gorm.ErrRecordNotFound { - errormsg := "Record not Found in DB: " + err.Error() - c.JSON(http.StatusNotFound, gin.H{ - "error": errormsg, - }) - } else { - errormsg := "Error on DB Query or transaction: " + err.Error() - c.JSON(http.StatusInternalServerError, gin.H{ - "error": errormsg, - }) - } - return true // Error - } - return false // No error -} diff --git a/common/datastructs.go b/common/models.go similarity index 92% rename from common/datastructs.go rename to common/models.go index 9ee868d..bb19053 100644 --- a/common/datastructs.go +++ b/common/models.go @@ -2,9 +2,6 @@ package common import ( "time" - - // "github.com/jinzhu/gorm" - "github.com/jinzhu/gorm/dialects/postgres" ) // User data model @@ -32,8 +29,8 @@ type Simulation struct { Name string `gorm:"not null"` // Running state of simulation Running bool `gorm:"default:false"` - // Start parameters of simulation - StartParameters postgres.Jsonb + // Start parameters of simulation as JSON string + StartParameters string // Users that have access to the simulation Users []User `gorm:"not null;many2many:user_simulations"` // Models that belong to the simulation @@ -52,8 +49,8 @@ type Model struct { OutputLength int `gorm:"default:1"` // Number of input signals InputLength int `gorm:"default:1"` - // Start parameters of model - StartParameters postgres.Jsonb + // Start parameters of model as JSON string + StartParameters string // ID of simulation to which model belongs SimulationID uint // Simulator associated with model @@ -96,10 +93,10 @@ type Simulator struct { State string `gorm:"default:''"` // Time of last state update StateUpdateAt time.Time - // Properties of simulator - Properties postgres.Jsonb - // Raw properties of simulator - RawProperties postgres.Jsonb + // Properties of simulator as JSON string + Properties string + // Raw properties of simulator as JSON string + RawProperties string } // Visualization data model @@ -141,8 +138,8 @@ type Widget struct { Z int `gorm:"not null"` // Locked state of widget IsLocked bool `gorm:"default:false"` - // Custom properties of widget - CustomProperties postgres.Jsonb + // Custom properties of widget as JSON string + CustomProperties string // ID of visualization to which widget belongs VisualizationID uint `gorm:"not null"` // Files that belong to widget (for example images) diff --git a/common/responses.go b/common/responses.go new file mode 100644 index 0000000..5415866 --- /dev/null +++ b/common/responses.go @@ -0,0 +1,95 @@ +package common + +import ( + "time" +) + +type UserResponse struct { + Username string `json:"Username"` + Role string `json:"Role"` + Mail string `json:"Mail"` +} + +type SimulationResponse struct { + Name string `json:"Name"` + ID uint `json:"SimulationID"` + Running bool `json:"Running"` + StartParams string `json:"Starting Parameters"` +} + +type ModelResponse struct { + Name string `json:"Name"` + OutputLength int `json:"OutputLength"` + InputLength int `json:"InputLength"` + SimulationID uint `json:"SimulationID"` + SimulatorID uint `json:"SimulatorID"` + StartParams string `json:"StartParams"` + InputMapping []Signal `json:"InputMapping"` + OutputMapping []Signal `json:"OutputMapping"` +} + +type SimulatorResponse struct { + UUID string `json:"UUID"` + Host string `json:"Host"` + ModelType string `json:"ModelType"` + Uptime int `json:"Uptime"` + State string `json:"State"` + StateUpdateAt time.Time `json:"StateUpdateAt"` + Properties string `json:"Properties"` + RawProperties string `json:"RawProperties"` +} + +type VisualizationResponse struct { + Name string `json:"Name"` + Grid int `json:"Grid"` + SimulationID uint `json:"SimulationID"` +} + +type WidgetResponse struct { + Name string `json:"Name"` + Type string `json:"Type"` + Width uint `json:"Width"` + Height uint `json:"Height"` + MinWidth uint `json:"MinWidth"` + MinHeight uint `json:"MinHeight"` + X int `json:"X"` + Y int `json:"Y"` + Z int `json:"Z"` + VisualizationID uint `json:"VisualizationID"` + IsLocked bool `json:"IsLocked"` + CustomProperties string `json:"CustomProperties"` +} + +type FileResponse struct { + Name string `json:"Name"` + ID uint `json:"FileID"` + 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"` +} + +// Response messages + +type ResponseMsg struct{ + Message string `json:"message"` +} + +type ResponseMsgUsers struct { + Users []UserResponse `json:"users"` +} + +type ResponseMsgUser struct { + User UserResponse `json:"user"` +} + +type ResponseMsgSimulations struct { + Simulations []SimulationResponse `json:"simulations"` +} + +type ResponseMsgSimulation struct { + Simulation SimulationResponse `json:"simulation"` +} + diff --git a/common/serializers.go b/common/serializers.go new file mode 100644 index 0000000..7fcd1c3 --- /dev/null +++ b/common/serializers.go @@ -0,0 +1,245 @@ +package common + +import ( + "github.com/gin-gonic/gin" +) + +// User/s Serializers + +type UsersSerializer struct { + Ctx *gin.Context + Users []User +} + +func (self *UsersSerializer) Response() []UserResponse { + response := []UserResponse{} + for _, user := range self.Users { + serializer := UserSerializer{self.Ctx, user} + response = append(response, serializer.Response()) + } + return response +} + +type UserSerializer struct { + Ctx *gin.Context + User +} + +func (self *UserSerializer) Response() UserResponse { + response := UserResponse{ + Username: self.Username, + Role: self.Role, + Mail: self.Mail, + } + return response +} + +// Simulation/s Serializers + +type SimulationsSerializer struct { + Ctx *gin.Context + Simulations []Simulation +} + +func (self *SimulationsSerializer) Response() []SimulationResponse { + response := []SimulationResponse{} + for _, simulation := range self.Simulations { + serializer := SimulationSerializer{self.Ctx, simulation} + response = append(response, serializer.Response()) + } + return response +} + +type SimulationSerializer struct { + Ctx *gin.Context + Simulation +} + + +func (self *SimulationSerializer) Response() SimulationResponse { + response := SimulationResponse{ + Name: self.Name, + ID: self.ID, + Running: self.Running, + StartParams: self.StartParameters, + } + return response +} + + +// Model/s Serializers + +type ModelsSerializer struct { + Ctx *gin.Context + Models []Model +} + +func (self *ModelsSerializer) Response() []ModelResponse { + response := []ModelResponse{} + for _, model := range self.Models { + serializer := ModelSerializer{self.Ctx, model} + response = append(response, serializer.Response()) + } + return response +} + +type ModelSerializer struct { + Ctx *gin.Context + Model +} + +func (self *ModelSerializer) Response() ModelResponse { + response := ModelResponse{ + Name: self.Name, + OutputLength: self.OutputLength, + InputLength: self.InputLength, + SimulationID: self.SimulationID, + SimulatorID: self.SimulatorID, + StartParams: self.StartParameters, + //InputMapping + //OutputMapping + } + return response +} + +// Simulator/s Serializers + +type SimulatorsSerializer struct { + Ctx *gin.Context + Simulators []Simulator +} + +func (self *SimulatorsSerializer) Response() []SimulatorResponse { + response := []SimulatorResponse{} + for _, simulator := range self.Simulators { + serializer := SimulatorSerializer{self.Ctx, simulator} + response = append(response, serializer.Response()) + } + return response +} + +type SimulatorSerializer struct { + Ctx *gin.Context + Simulator +} + +func (self *SimulatorSerializer) Response() SimulatorResponse { + + response := SimulatorResponse{ + UUID: self.UUID, + Host: self.Host, + ModelType: self.Modeltype, + Uptime: self.Uptime, + State: self.State, + StateUpdateAt: self.StateUpdateAt, + } + return response +} + +// Visualization/s Serializers + +type VisualizationsSerializer struct { + Ctx *gin.Context + Visualizations []Visualization +} + +func (self *VisualizationsSerializer) Response() []VisualizationResponse { + response := []VisualizationResponse{} + for _, visualization := range self.Visualizations { + serializer := VisualizationSerializer{self.Ctx, visualization} + response = append(response, serializer.Response()) + } + return response +} + +type VisualizationSerializer struct { + Ctx *gin.Context + Visualization +} + + +func (self *VisualizationSerializer) Response() VisualizationResponse { + + response := VisualizationResponse{ + Name: self.Name, + Grid: self.Grid, + SimulationID: self.SimulationID, + } + return response +} + +// Widget/s Serializers + +type WidgetsSerializer struct { + Ctx *gin.Context + Widgets []Widget +} + +func (self *WidgetsSerializer) Response() []WidgetResponse { + response := []WidgetResponse{} + for _, widget := range self.Widgets { + serializer := WidgetSerializer{self.Ctx, widget} + response = append(response, serializer.Response()) + } + return response +} + +type WidgetSerializer struct { + Ctx *gin.Context + Widget +} + +func (self *WidgetSerializer) Response() WidgetResponse { + + response := WidgetResponse{ + Name: self.Name, + Type: self.Type, + Width: self.Width, + Height: self.Height, + MinWidth: self.MinWidth, + MinHeight: self.MinHeight, + X: self.X, + Y: self.Y, + Z: self.Z, + VisualizationID: self.VisualizationID, + IsLocked: self.IsLocked, + //CustomProperties + } + return response +} + +// File/s Serializers + +type FilesSerializerNoAssoc struct { + Ctx *gin.Context + Files []File +} + +func (self *FilesSerializerNoAssoc) Response() []FileResponse { + response := []FileResponse{} + for _, files := range self.Files { + serializer := FileSerializerNoAssoc{self.Ctx, files} + response = append(response, serializer.Response()) + } + return response +} + +type FileSerializerNoAssoc struct { + Ctx *gin.Context + File +} + + +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 + } + return response +} diff --git a/common/utilities.go b/common/utilities.go new file mode 100644 index 0000000..18c790d --- /dev/null +++ b/common/utilities.go @@ -0,0 +1,93 @@ +package common + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + "github.com/jinzhu/gorm" +) + +func ProvideErrorResponse(c *gin.Context, err error) bool { + if err != nil { + if err == gorm.ErrRecordNotFound { + errormsg := "Record not Found in DB: " + err.Error() + c.JSON(http.StatusNotFound, gin.H{ + "error": errormsg, + }) + } else { + errormsg := "Error on DB Query or transaction: " + err.Error() + c.JSON(http.StatusInternalServerError, gin.H{ + "error": errormsg, + }) + } + return true // Error + } + return false // No error +} + + +func GetSimulationID(c *gin.Context) (int, error) { + + simID, err := strconv.Atoi(c.Param("simulationID")) + + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulation ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return -1, err + } else { + return simID, err + + } +} + +func GetModelID(c *gin.Context) (int, error) { + + modelID, err := strconv.Atoi(c.Param("modelID")) + + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of model ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return -1, err + } else { + return modelID, err + + } +} + +func GetVisualizationID(c *gin.Context) (int, error) { + + simID, err := strconv.Atoi(c.Param("visualizationID")) + + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of visualization ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return -1, err + } else { + return simID, err + + } +} + +func GetWidgetID(c *gin.Context) (int, error) { + + widgetID, err := strconv.Atoi(c.Param("widgetID")) + + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of widget ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return -1, err + } else { + return widgetID, err + + } +} \ No newline at end of file diff --git a/endpoints/endpoints_test.go b/endpoints/endpoints_test.go deleted file mode 100644 index 7cee614..0000000 --- a/endpoints/endpoints_test.go +++ /dev/null @@ -1,145 +0,0 @@ -package endpoints - -import ( - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "testing" - - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - - -type responseMsg struct{ - Message string `json:"message"` -} - -var msgOK = responseMsg{ - Message: "OK.", -} - -type user struct{ - Username string `json:"Username"` - Role string `json:"Role"` - Mail string `json:"Mail"` -} - -type responseUsers struct { - Users []user `json:"users"` -} - -var users = []user{ - { - Username: "User_A", - Role: "user", - Mail: "", - }, - { - Username: "User_B", - Role: "user", - Mail: "", - }, -} - -var msgUsers = responseUsers{ - Users: users, -} - -type simulation struct{ - Name string `json:"Name"` - SimulationID uint `json:"SimulationID"` - Running bool `json:"Running"` -} - -type responseSimulations struct { - Simulations []simulation `json:"simulations"` -} - -type responseSimulation struct { - Simulation simulation `json:"simulation"` -} - -var simulationA = simulation{ - Name: "Simulation_A", - SimulationID: 1, - Running: false, -} - -var simulationB = simulation{ - Name: "Simulation_B", - SimulationID: 2, - Running: false, -} - -var simulations = []simulation{ - simulationA, - simulationB, -} - -var msgSimulations = responseSimulations{ - Simulations: simulations, -} - -// Test /simulation endpoints -func TestSimulationEndpoints(t *testing.T) { - - db := common.DummyInitDB() - defer db.Close() - common.DummyPopulateDB(db) - - - router := gin.Default() - api := router.Group("/api") - SimulationsRegister(api.Group("/simulations")) - - msgOKjson, err := json.Marshal(msgOK) - if err !=nil { - panic(err) - } - - msgUsersjson, err := json.Marshal(msgUsers) - if err !=nil { - panic(err) - } - - // msgSimulationsjson, err := json.Marshal(msgSimulations) - // if err !=nil { - // panic(err) - // } - - // test GET simulations/ - var expected_response = "{\"simulations\":[{\"Name\":\"Simulation_A\",\"SimulationID\":1,\"Running\":false,\"Starting Parameters\":null},{\"Name\":\"Simulation_B\",\"SimulationID\":2,\"Running\":false,\"Starting Parameters\":null}]}" - testEndpoint(t, router, "/api/simulations/", "GET", "", 200, expected_response) - - // test GET simulations/:SimulationID - expected_response = "{\"simulation\":{\"Name\":\"Simulation_A\",\"SimulationID\":1,\"Running\":false,\"Starting Parameters\":null}}" - testEndpoint(t, router, "/api/simulations/1", "GET", "", 200, expected_response) - - // test GET simulations/:SimulationID/users - testEndpoint(t, router, "/api/simulations/1/users", "GET", "", 200, string(msgUsersjson)) - - // test DELETE simulations/:SimulationID/user/:username - testEndpoint(t, router, "/api/simulations/1/user/User_A", "DELETE", "", 200, string(msgOKjson)) - - // test PUT simulations/:SimulationID/user/:username - testEndpoint(t, router, "/api/simulations/1/user/User_A", "PUT", "", 200, string(msgOKjson)) - - - - // TODO add more tests - -} - - -func testEndpoint(t *testing.T, router *gin.Engine, url string, method string, body string, expected_code int, expected_response string ) { - w := httptest.NewRecorder() - req, _ := http.NewRequest(method, url, nil) - router.ServeHTTP(w, req) - assert.Equal(t, expected_code, w.Code) - fmt.Println(w.Body.String()) - assert.Equal(t, expected_response, w.Body.String()) -} \ No newline at end of file diff --git a/endpoints/modelEndpoints.go b/endpoints/modelEndpoints.go deleted file mode 100644 index 62e67a4..0000000 --- a/endpoints/modelEndpoints.go +++ /dev/null @@ -1,184 +0,0 @@ -package endpoints - -import ( - "fmt" - "net/http" - "strconv" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" - - "github.com/gin-gonic/gin" -) - -// modelReadAllEp godoc -// @Summary Get all models of simulation -// @ID GetAllModelsOfSimulation -// @Produce json -// @Tags model -// @Success 200 {array} common.Model "Array of models to which belong to simulation" -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" -// @Router /simulations/{simulationID}/models [get] -func modelReadAllEp(c *gin.Context) { - - simID, err := GetSimulationID(c) - if err != nil { - return - } - - allModels, _, err := queries.FindAllModels(simID) - if common.ProvideErrorResponse(c, err) { - return - } - - serializer := serializers.ModelsSerializer{c, allModels} - c.JSON(http.StatusOK, gin.H{ - "models": serializer.Response(), - }) -} - -// modelRegistrationEp godoc -// @Summary Add a model to a simulation -// @ID AddModelToSimulation -// @Tags model -// @Param inputModel body common.Model true "Model to be added" -// @Success 200 "OK." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" -// @Router /simulations/{simulationID}/models [post] -func modelRegistrationEp(c *gin.Context) { - - simID, err := GetSimulationID(c) - if err != nil { - return - } - - var m common.Model - err = c.BindJSON(&m) - if err != nil { - errormsg := "Bad request. Error binding form data to JSON: " + err.Error() - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return - } - - err = queries.AddModel(simID, &m) - if common.ProvideErrorResponse(c, err) == false { - c.JSON(http.StatusOK, gin.H{ - "message": "OK.", - }) - } -} - -func modelCloneEp(c *gin.Context) { - - modelID, err := GetModelID(c) - if err != nil { - return - } - - targetSimID, err := strconv.Atoi(c.PostForm("TargetSim")) - if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of target sim ID") - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return - } - - err = queries.CloneModel(targetSimID, modelID) - if common.ProvideErrorResponse(c, err) == false { - c.JSON(http.StatusOK, gin.H{ - "message": "Not implemented.", - }) - } - -} - -func modelUpdateEp(c *gin.Context) { - - modelID, err := GetModelID(c) - if err != nil { - return - } - - var m common.Model - err = c.BindJSON(&m) - if err != nil { - errormsg := "Bad request. Error binding form data to JSON: " + err.Error() - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return - } - - err = queries.UpdateModel(modelID, &m) - if common.ProvideErrorResponse(c, err) == false { - c.JSON(http.StatusOK, gin.H{ - "message": "OK.", - }) - } - -} - -func modelReadEp(c *gin.Context) { - - modelID, err := GetModelID(c) - if err != nil { - return - } - - m, err := queries.FindModel(modelID) - if common.ProvideErrorResponse(c, err) { - return - } - - serializer := serializers.ModelSerializer{c, m} - c.JSON(http.StatusOK, gin.H{ - "model": serializer.Response(), - }) -} - -func modelDeleteEp(c *gin.Context) { - - simID, err := GetSimulationID(c) - if err != nil { - return - } - - modelID, err := GetModelID(c) - if err != nil { - return - } - - err = queries.DeleteModel(simID, modelID) - if common.ProvideErrorResponse(c, err) == false { - c.JSON(http.StatusOK, gin.H{ - "message": "OK.", - }) - } -} - -func GetModelID(c *gin.Context) (int, error) { - - modelID, err := strconv.Atoi(c.Param("ModelID")) - - if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of model ID") - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return -1, err - } else { - return modelID, err - - } -} - diff --git a/endpoints/registration.go b/endpoints/registration.go deleted file mode 100644 index 67d5264..0000000 --- a/endpoints/registration.go +++ /dev/null @@ -1,88 +0,0 @@ -package endpoints - -import ( - "github.com/gin-gonic/gin" -) - -func SimulationsRegister(r *gin.RouterGroup) { - - r.GET("/", getSimulationsEp) - //r.POST("/", simulationRegistrationEp) - //r.POST("/:SimulationID", simulationCloneEp) - //r.PUT("/:SimulationID", simulationUpdateEp) - r.GET("/:SimulationID", getSimulationEp) - //r.DELETE("/:SimulationID", simulationDeleteEp) - - // Users - r.GET("/:SimulationID/users", userReadAllSimEp) - r.PUT("/:SimulationID/user/:username", userUpdateSimEp) - r.DELETE("/:SimulationID/user/:username", userDeleteSimEp) - - // Models - r.GET("/:SimulationID/models/", modelReadAllEp) - r.POST("/:SimulationID/models/", modelRegistrationEp) - //r.POST("/:SimulationID/models/:ModelID", modelCloneEp) - r.PUT("/:SimulationID/models/:ModelID", modelUpdateEp) - r.GET("/:SimulationID/models/:ModelID", modelReadEp) - //r.DELETE("/:SimulationID/models/:ModelID", modelDeleteEp) - - // Simulators - r.PUT("/:SimulationID/models/:ModelID/simulator", simulatorUpdateModelEp) - r.GET("/:SimulationID/models/:ModelID/simulator", simulatorReadModelEp) - - // Input and Output Signals - r.POST("/:SimulationID/models/:ModelID/signals/:direction", signalRegistrationEp) - r.GET("/:SimulationID/models/:ModelID/signals/:direction", signalReadAllEp) - - // Visualizations - r.GET("/:SimulationID/visualizations", visualizationReadAllEp) - r.POST("/:SimulationID/visualization", visualizationRegistrationEp) - //r.POST("/:SimulationID/visualization/:visualizationID", visualizationCloneEp) - r.PUT("/:SimulationID/visualization/:visualizationID", visualizationUpdateEp) - r.GET("/:SimulationID/visualization/:visualizationID", visualizationReadEp) - //r.DELETE("/:SimulationID/visualization/:visualizationID", visualizationDeleteEp) - - // Widgets - r.GET("/:SimulationID/visualization/:visualizationID/widgets", widgetReadAllEp) - r.POST("/:SimulationID/visualization/:visualizationID/widget", widgetRegistrationEp) - //r.POST("/:SimulationID/visualization/:visualizationID/widget:widgetID", widgetCloneEp) - r.PUT("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetUpdateEp) - r.GET("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetReadEp) - //r.DELETE("/:SimulationID/visualization/:visualizationID/widget/:widgetID", widgetDeleteEp) - - r.GET("/:SimulationID/models/:ModelID/files", fileMReadAllEp) - r.POST ("/:SimulationID/models/:ModelID/file", fileMRegistrationEp) // NEW in API - //r.POST ("/:SimulationID/models/:ModelID/file", fileMCloneEp) // NEW in API - r.GET("/:SimulationID/models/:ModelID/file", fileMReadEp) // NEW in API - r.PUT("/:SimulationID/models/:ModelID/file", fileMUpdateEp) // NEW in API - r.DELETE("/:SimulationID/models/:ModelID/file", fileMDeleteEp) // NEW in API - - // Files of Widgets - r.GET("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/files", fileWReadAllEp) // NEW in API - r.POST ("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWRegistrationEp) // NEW in API - //r.POST ("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWCloneEp) // NEW in API - r.GET("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWReadEp) // NEW in API - r.PUT("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWUpdateEp) // NEW in API - r.DELETE("/:SimulationID/visualizations/:VisID/widgets/:WidgetID/file", fileWDeleteEp) // NEW in API - -} - -func UsersRegister(r *gin.RouterGroup) { - r.GET("/", userReadAllEp) - r.POST("/", userRegistrationEp) - r.PUT("/:UserID", userUpdateEp) - r.GET("/:UserID", userReadEp) - r.GET("/:UserID/simulations", userReadSimEp) - r.DELETE("/:UserID", userDeleteEp) - //r.GET("/me", userSelfEp) // TODO redirect to users/:UserID -} - - -func SimulatorsRegister(r *gin.RouterGroup) { - r.GET("/", simulatorReadAllEp) - r.POST("/", simulatorRegistrationEp) - r.PUT("/:SimulatorID", simulatorUpdateEp) - r.GET("/:SimulatorID", simulatorReadEp) - r.DELETE("/:SimulatorID", simulatorDeleteEp) - r.POST("/:SimulatorID", simulatorSendActionEp) -} \ No newline at end of file diff --git a/endpoints/signalEndpoints.go b/endpoints/signalEndpoints.go deleted file mode 100644 index 6297d7e..0000000 --- a/endpoints/signalEndpoints.go +++ /dev/null @@ -1,84 +0,0 @@ -package endpoints - -import ( - "net/http" - - "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/queries" -) - -func signalRegistrationEp(c *gin.Context) { - - modelID, err := GetModelID(c) - if err != nil { - return - } - - model, err := queries.FindModel(modelID) - if common.ProvideErrorResponse(c, err) { - return - } - - direction := c.Param("direction") - if !(direction == "out") && !(direction == "in") { - errormsg := "Bad request. Direction has to be in or out" - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return - } - - var sigs []common.Signal - err = c.BindJSON(&sigs) - if err != nil { - errormsg := "Bad request. Error binding form data to JSON: " + err.Error() - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return - } - - // Add signals to model and remove all existing Signals of the requested direction (if any) - err = queries.ReplaceSignals(&model, sigs, direction) - if common.ProvideErrorResponse(c, err) == false { - c.JSON(http.StatusOK, gin.H{ - "message": "OK.", - }) - } -} - -func signalReadAllEp(c *gin.Context) { - - modelID, err := GetModelID(c) - if err != nil { - return - } - - model, err := queries.FindModel(modelID) - if common.ProvideErrorResponse(c, err) { - return - } - - direction := c.Param("direction") - if !(direction == "out") && !(direction == "in") { - errormsg := "Bad request. Direction has to be in or out" - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return - } - - var signals []common.Signal - if direction == "in" { - signals = model.InputMapping - } else { - signals = model.OutputMapping - } - - c.JSON(http.StatusOK, gin.H{ - "signals": signals, - }) -} - diff --git a/endpoints/simulatorEndpoints.go b/endpoints/simulatorEndpoints.go deleted file mode 100644 index 49eb0f2..0000000 --- a/endpoints/simulatorEndpoints.go +++ /dev/null @@ -1,112 +0,0 @@ -package endpoints - -import ( - "fmt" - "net/http" - "strconv" - - "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/queries" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" -) - -func simulatorReadAllEp(c *gin.Context) { - allSimulators, _, _ := queries.FindAllSimulators() - serializer := serializers.SimulatorsSerializer{c, allSimulators} - c.JSON(http.StatusOK, gin.H{ - "simulators": serializer.Response(), - }) -} - -func simulatorRegistrationEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func simulatorUpdateEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func simulatorUpdateModelEp(c *gin.Context) { - - // simulator ID as parameter of Query, e.g. simulations/:SimulationID/models/:ModelID/simulator?simulatorID=42 - simulatorID, err := strconv.Atoi(c.Query("simulatorID")) - if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect simulator ID") - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return - } - - modelID, err := GetModelID(c) - if err != nil { - return - } - - simulator, err := queries.FindSimulator(simulatorID) - if common.ProvideErrorResponse(c, err) { - return - } - - model, err := queries.FindModel(modelID) - if common.ProvideErrorResponse(c, err) { - return - } - - err = queries.UpdateSimulatorOfModel(&model, &simulator) - if common.ProvideErrorResponse(c, err) == false { - c.JSON(http.StatusOK, gin.H{ - "message": "OK", - }) - } - -} - -func simulatorReadEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func simulatorReadModelEp(c *gin.Context) { - - modelID, err := GetModelID(c) - if err != nil { - return - } - - model, err := queries.FindModel(modelID) - if common.ProvideErrorResponse(c, err) { - return - } - - simulator, err := queries.FindSimulator(int(model.SimulatorID)) - if common.ProvideErrorResponse(c, err) { - return - } - - serializer := serializers.SimulatorSerializer{c, simulator} - c.JSON(http.StatusOK, gin.H{ - "simulator": serializer.Response(), - }) -} - -func simulatorDeleteEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func simulatorSendActionEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - - diff --git a/go.sum b/go.sum index 9d8301b..2930dee 100644 --- a/go.sum +++ b/go.sum @@ -138,6 +138,7 @@ github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJ github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 h1:BasDe+IErOQKrMVXab7UayvSlIpiyGwRvuX3EKYY7UA= github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 h1:vG/gY/PxA3v3l04qxe3tDjXyu3bozii8ulSlIPOYKhI= github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= diff --git a/queries/modelQueries.go b/queries/modelQueries.go deleted file mode 100644 index 30b248e..0000000 --- a/queries/modelQueries.go +++ /dev/null @@ -1,99 +0,0 @@ -package queries - -import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -func FindAllModels(simID int) ([]common.Model, int, error) { - db := common.GetDB() - var models []common.Model - sim, err := FindSimulation(simID) - if err != nil { - return models, 0, err - } - - err = db.Model(sim).Related(&models, "Models").Error - - return models, len(models), err -} - -func FindModel(modelID int) (common.Model, error){ - db := common.GetDB() - var m common.Model - err := db.First(&m, modelID).Error - return m, err -} - -func AddModel(simID int, model *common.Model) error { - db := common.GetDB() - sim, err := FindSimulation(simID) - if err != nil { - return err - } - - err = db.Model(&sim).Association("Models").Append(model).Error - return err -} - -func CloneModel(targetSim int, modelID int) error { - - // TODO TO BE IMPLEMENTED - // Check if target sim exists - // Check if model exists - - // Get all Signals of Model - // Get Simulator of Model - // Get Files of model - - // Add new model object to DB and associate with target sim - // Add new signal objects to DB and associate with new model object (careful with directions) - // Associate Simulator with new Model object - var err error - return err - -} - -func UpdateModel(modelID int , modelUpdate *common.Model) error { - db := common.GetDB() - m, err := FindModel(modelID) - if err != nil { - return err - } - // only Name and Start Params can be updated directly by the user - err = db.Model(&m).Updates(map[string]interface{}{"Name": modelUpdate.Name, "StartParameters": modelUpdate.StartParameters}).Error - return err -} - -func DeleteModel(simID int , modelID int ) error { - db := common.GetDB() - sim, err := FindSimulation(simID) - if err != nil { - return err - } - - m, err := FindModel(modelID) - if err != nil { - return err - } - - //remove relationship between simulation and model - err = db.Model(&sim).Association("Models").Delete(m).Error - if err != nil { - return err - } - - // TODO remove File Associations and files on disk - // TODO remove Signal Associations and Signals in DB - // TODO how to treat simulator association? - - //remove model itself from DB - //TODO: do we want this?? - err = db.Delete(&m).Error - return err -} - -func UpdateSimulatorOfModel(model *common.Model, simulator *common.Simulator) error { - db := common.GetDB() - err := db.Model(model).Association("Simulator").Replace(simulator).Error - return err -} \ No newline at end of file diff --git a/queries/signalQueries.go b/queries/signalQueries.go deleted file mode 100644 index f0a04a3..0000000 --- a/queries/signalQueries.go +++ /dev/null @@ -1,20 +0,0 @@ -package queries - -import ( - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -func ReplaceSignals(model *common.Model, signals []common.Signal, direction string) error { - - db := common.GetDB() - var err error - - if direction == "in" { - err = db.Model(model).Select("InputMapping").Update("InputMapping", signals).Error - } else { - err = db.Model(model).Select("OutputMapping").Update("OutputMapping", signals).Error - } - - return err - -} \ No newline at end of file diff --git a/routes/endpoints_test.go b/routes/endpoints_test.go new file mode 100644 index 0000000..7110e15 --- /dev/null +++ b/routes/endpoints_test.go @@ -0,0 +1,144 @@ +package routes + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/model" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget" +) + + +var msgOK = common.ResponseMsg{ + Message: "OK.", +} + + +var user_A = common.UserResponse{ + Username: "User_A", + Role: "user", + Mail: "", +} + +var user_B = common.UserResponse{ + Username: "User_B", + Role: "user", + Mail: "", +} + +var myUsers = []common.UserResponse{ + user_B, + user_A, +} + +var msgUsers = common.ResponseMsgUsers{ + Users: myUsers, +} + + +var simulationA = common.SimulationResponse{ + Name: "Simulation_A", + ID: 1, + Running: false, +} + +var simulationB = common.SimulationResponse{ + Name: "Simulation_B", + ID: 2, + Running: false, +} + +var mySimulations = []common.SimulationResponse{ + simulationA, + simulationB, +} + +var msgSimulations = common.ResponseMsgSimulations{ + Simulations: mySimulations, +} + +var msgSimulation = common.ResponseMsgSimulation{ + Simulation: simulationA, +} + +// Test /simulation endpoints +func TestSimulationEndpoints(t *testing.T) { + + db := common.DummyInitDB() + defer db.Close() + common.DummyPopulateDB(db) + + + router := gin.Default() + api := router.Group("/api") + simulation.RegisterSimulationEndpoints(api.Group("/simulations")) + file.RegisterFileEndpoints(api.Group("/simulations")) + model.RegisterModelEndpoints(api.Group("/simulations")) + visualization.RegisterVisualizationEndpoints(api.Group("/simulations")) + widget.RegisterWidgetEndpoints(api.Group("/simulations")) + user.RegisterUserEndpointsForSimulation(api.Group("/simulations")) // TODO ugly structure + + user.RegisterUserEndpoints(api.Group("/users")) + simulator.RegisterSimulatorEndpoints(api.Group("/simulators")) + + msgOKjson, err := json.Marshal(msgOK) + if err !=nil { + panic(err) + } + + msgUsersjson, err := json.Marshal(msgUsers) + if err !=nil { + panic(err) + } + + msgSimulationsjson, err := json.Marshal(msgSimulations) + if err !=nil { + panic(err) + } + + msgSimulationjson, err := json.Marshal(msgSimulation) + if err !=nil { + panic(err) + } + + // test GET simulations/ + testEndpoint(t, router, "/api/simulations/", "GET", "", 200, string(msgSimulationsjson)) + + // test GET simulations/:SimulationID + testEndpoint(t, router, "/api/simulations/1", "GET", "", 200, string(msgSimulationjson)) + + // test GET simulations/:SimulationID/users + testEndpoint(t, router, "/api/simulations/1/users", "GET", "", 200, string(msgUsersjson)) + + // test DELETE simulations/:SimulationID/user/:username + testEndpoint(t, router, "/api/simulations/1/user/User_A", "DELETE", "", 200, string(msgOKjson)) + + // test PUT simulations/:SimulationID/user/:username + testEndpoint(t, router, "/api/simulations/1/user/User_A", "PUT", "", 200, string(msgOKjson)) + + + // TODO add more tests + +} + + +func testEndpoint(t *testing.T, router *gin.Engine, url string, method string, body string, expected_code int, expected_response string ) { + w := httptest.NewRecorder() + req, _ := http.NewRequest(method, url, nil) + router.ServeHTTP(w, req) + assert.Equal(t, expected_code, w.Code) + fmt.Println(w.Body.String()) + assert.Equal(t, expected_response, w.Body.String()) +} \ No newline at end of file diff --git a/endpoints/fileEndpoints.go b/routes/file/fileEndpoints.go similarity index 67% rename from endpoints/fileEndpoints.go rename to routes/file/fileEndpoints.go index 7cb6cb7..a3df5e3 100644 --- a/endpoints/fileEndpoints.go +++ b/routes/file/fileEndpoints.go @@ -1,22 +1,34 @@ -package endpoints +package file import ( "fmt" - "strconv" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" - "net/http" + "strconv" "github.com/gin-gonic/gin" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) -// fileMReadAllEp godoc +func RegisterFileEndpoints(r *gin.RouterGroup){ + r.GET("/:simulationID/models/:modelID/files", GetFilesOfModel) + r.POST ("/:simulationID/models/:modelID/file", AddFileToModel) + //r.POST ("/:simulationID/models/:modelID/file", CloneFileOfModel) + r.GET("/:simulationID/models/:modelID/file", GetFileOfModel) + r.PUT("/:simulationID/models/:modelID/file", UpdateFileOfModel) + r.DELETE("/:simulationID/models/:modelID/file", DeleteFileOfModel) + + r.GET("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/files", GetFilesOfWidget) + r.POST ("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", AddFileToWidget) + //r.POST ("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", CloneFileOfWidget) + r.GET("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", GetFileOfWidget) + r.PUT("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", UpdateFileOfWidget) + r.DELETE("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", DeleteFileOfWidget) +} + +// GetFilesOfModel godoc // @Summary Get all parameters of files of model -// @ID GetAllModelFileParams +// @ID GetFilesOfModel // @Tags file // @Success 200 {array} common.File "File parameters requested by user" // @Failure 401 "Unauthorized Access" @@ -26,7 +38,7 @@ import ( // @Param simulationID path int true "Simulation ID" // @Param modelID path int true "Model ID" // @Router simulations/{simulationID}/models/{modelID}/files [get] -func fileMReadAllEp(c *gin.Context) { +func GetFilesOfModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) if err != nil{ @@ -34,10 +46,10 @@ func fileMReadAllEp(c *gin.Context) { } // Find files' properties in DB and return in HTTP response, no change to DB - allFiles, _, err := queries.FindFiles(c, -1, modelID, simulationID) + allFiles, _, err := FindFiles(c, -1, modelID, simulationID) if common.ProvideErrorResponse(c, err) == false { - serializer := serializers.FilesSerializerNoAssoc{c, allFiles} + serializer := common.FilesSerializerNoAssoc{c, allFiles} c.JSON(http.StatusOK, gin.H{ "files": serializer.Response(), }) @@ -45,9 +57,9 @@ func fileMReadAllEp(c *gin.Context) { } -// fileMRegistrationEp godoc +// AddFileToModel godoc // @Summary Get all parameters of files of model -// @ID PostFileToModel +// @ID AddFileToModel // @Tags file // @Success 200 "OK." // @Failure 401 "Unauthorized Access" @@ -57,7 +69,7 @@ func fileMReadAllEp(c *gin.Context) { // @Param simulationID path int true "Simulation ID" // @Param modelID path int true "Model ID" // @Router simulations/{simulationID}/models/{modelID}/file [post] -func fileMRegistrationEp(c *gin.Context) { +func AddFileToModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) if err != nil{ @@ -65,11 +77,11 @@ func fileMRegistrationEp(c *gin.Context) { } // Save file locally and register file in DB, HTTP response is set by this method - queries.RegisterFile(c,-1, modelID, simulationID) + RegisterFile(c,-1, modelID, simulationID) } -func fileMCloneEp(c *gin.Context) { +func CloneFileOfModel(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", @@ -77,7 +89,7 @@ func fileMCloneEp(c *gin.Context) { } -func fileMReadEp(c *gin.Context) { +func GetFileOfModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) if err != nil{ @@ -85,10 +97,10 @@ func fileMReadEp(c *gin.Context) { } // Read file from disk and return in HTTP response, no change to DB - queries.ReadFile(c, -1, modelID, simulationID) + ReadFile(c, -1, modelID, simulationID) } -func fileMUpdateEp(c *gin.Context) { +func UpdateFileOfModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) if err != nil{ @@ -96,10 +108,10 @@ func fileMUpdateEp(c *gin.Context) { } // Update file locally and update file entry in DB, HTTP response is set by this method - queries.UpdateFile(c,-1, modelID, simulationID) + UpdateFile(c,-1, modelID, simulationID) } -func fileMDeleteEp(c *gin.Context) { +func DeleteFileOfModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) if err != nil{ @@ -107,12 +119,12 @@ func fileMDeleteEp(c *gin.Context) { } // Delete file from disk and remove entry from DB, HTTP response is set by this method - queries.DeleteFile(c, -1, modelID, simulationID) + DeleteFile(c, -1, modelID, simulationID) } -func fileWReadAllEp(c *gin.Context) { +func GetFilesOfWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) if err != nil{ @@ -120,10 +132,10 @@ func fileWReadAllEp(c *gin.Context) { } // Find files' properties in DB and return in HTTP response, no change to DB - allFiles, _, err := queries.FindFiles(c, widgetID, -1, simulationID) + allFiles, _, err := FindFiles(c, widgetID, -1, simulationID) if common.ProvideErrorResponse(c, err) == false { - serializer := serializers.FilesSerializerNoAssoc{c, allFiles} + serializer := common.FilesSerializerNoAssoc{c, allFiles} c.JSON(http.StatusOK, gin.H{ "files": serializer.Response(), }) @@ -131,7 +143,7 @@ func fileWReadAllEp(c *gin.Context) { } -func fileWRegistrationEp(c *gin.Context) { +func AddFileToWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) if err != nil{ @@ -139,11 +151,11 @@ func fileWRegistrationEp(c *gin.Context) { } // Save file locally and register file in DB, HTTP response is set by this method - queries.RegisterFile(c,widgetID, -1, simulationID) + RegisterFile(c,widgetID, -1, simulationID) } -func fileWCloneEp(c *gin.Context) { +func CloneFileOfWidget(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", @@ -151,7 +163,7 @@ func fileWCloneEp(c *gin.Context) { } -func fileWReadEp(c *gin.Context) { +func GetFileOfWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) if err != nil{ @@ -159,10 +171,10 @@ func fileWReadEp(c *gin.Context) { } // Read file from disk and return in HTTP response, no change to DB - queries.ReadFile(c, widgetID, -1, simulationID) + ReadFile(c, widgetID, -1, simulationID) } -func fileWUpdateEp(c *gin.Context) { +func UpdateFileOfWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) if err != nil{ @@ -170,10 +182,10 @@ func fileWUpdateEp(c *gin.Context) { } // Update file locally and update file entry in DB, HTTP response is set by this method - queries.UpdateFile(c,widgetID, -1, simulationID) + UpdateFile(c,widgetID, -1, simulationID) } -func fileWDeleteEp(c *gin.Context) { +func DeleteFileOfWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) if err != nil{ @@ -181,7 +193,7 @@ func fileWDeleteEp(c *gin.Context) { } // Delete file from disk and remove entry from DB, HTTP response is set by this method - queries.DeleteFile(c, widgetID, -1, simulationID) + DeleteFile(c, widgetID, -1, simulationID) } @@ -254,9 +266,9 @@ func getRequestParams(c *gin.Context) (int, int, error){ } var subID int - subID, err = GetModelID(c) + subID, err = common.GetModelID(c) if err != nil{ - subID, err = GetWidgetID(c) + subID, err = common.GetWidgetID(c) if err != nil { return -1, -1, err } diff --git a/queries/fileQueries.go b/routes/file/fileQueries.go similarity index 96% rename from queries/fileQueries.go rename to routes/file/fileQueries.go index a571d77..6bcad30 100644 --- a/queries/fileQueries.go +++ b/routes/file/fileQueries.go @@ -1,4 +1,4 @@ -package queries +package file import ( "fmt" @@ -13,6 +13,9 @@ 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/model" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget" ) //func FindAllFiles() ([]common.File, int, error) { @@ -103,18 +106,18 @@ func RegisterFile(c *gin.Context, widgetID int, modelID int, simulationID int){ size := file_header.Size // Check if simulation and widget or model exist in DB - _, err = FindSimulation(simulationID) + _, err = simulation.FindSimulation(simulationID) if common.ProvideErrorResponse(c, err) { return } if modelID != -1 { - _, err = FindModel(modelID) + _, err = model.FindModel(modelID) if common.ProvideErrorResponse(c, err) { return } } else if widgetID != -1 { - _, err = FindWidget(widgetID) + _, err = widget.FindWidget(widgetID) if common.ProvideErrorResponse(c, err) { return } diff --git a/routes/model/modelEndpoints.go b/routes/model/modelEndpoints.go new file mode 100644 index 0000000..f5214e6 --- /dev/null +++ b/routes/model/modelEndpoints.go @@ -0,0 +1,314 @@ +package model + +import ( + "fmt" + "net/http" + "strconv" + + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator" + + "github.com/gin-gonic/gin" +) + +func RegisterModelEndpoints(r *gin.RouterGroup){ + r.GET("/:simulationID/models/", GetModels) + r.POST("/:simulationID/models/", AddModel) + r.POST("/:simulationID/models/:modelID", CloneModel) + r.PUT("/:simulationID/models/:modelID", UpdateModel) + r.GET("/:simulationID/models/:modelID", GetModel) + r.DELETE("/:simulationID/models/:modelID", DeleteModel) + r.PUT("/:simulationID/models/:modelID/simulator", UpdateSimulator) + r.GET("/:simulationID/models/:modelID/simulator", GetSimulator) + r.POST("/:simulationID/models/:modelID/signals/:direction", UpdateSignals) + r.GET("/:simulationID/models/:modelID/signals/:direction", GetSignals) +} + +// GetModels godoc +// @Summary Get all models of simulation +// @ID GetModels +// @Produce json +// @Tags model +// @Success 200 {array} common.ModelResponse "Array of models to which belong to simulation" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Router /simulations/{simulationID}/models [get] +func GetModels(c *gin.Context) { + + simID, err := common.GetSimulationID(c) + if err != nil { + return + } + + allModels, _, err := FindAllModels(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + serializer := common.ModelsSerializer{c, allModels} + c.JSON(http.StatusOK, gin.H{ + "models": serializer.Response(), + }) +} + +// AddModel godoc +// @Summary Add a model to a simulation +// @ID AddModel +// @Tags model +// @Param inputModel body common.ModelResponse true "Model to be added" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Router /simulations/{simulationID}/models [post] +func AddModel(c *gin.Context) { + + simID, err := common.GetSimulationID(c) + if err != nil { + return + } + + var m Model + err = c.BindJSON(&m) + if err != nil { + errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + err = m.addToSimulation(simID) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } +} + +func CloneModel(c *gin.Context) { + + // modelID, err := routes.GetModelID(c) + // if err != nil { + // return + // } + // + // targetSimID, err := strconv.Atoi(c.PostForm("TargetSim")) + // if err != nil { + // errormsg := fmt.Sprintf("Bad request. No or incorrect format of target sim ID") + // c.JSON(http.StatusBadRequest, gin.H{ + // "error": errormsg, + // }) + // return + // } + + // TODO TO BE IMPLEMENTED + // Check if target sim exists + // Check if model exists + + // Get all Signals of Model + // Get Simulator of Model + // Get Files of model + + // Add new model object to DB and associate with target sim + // Add new signal objects to DB and associate with new model object (careful with directions) + // Associate Simulator with new Model object + + + c.JSON(http.StatusOK, gin.H{ + "message": "Not implemented.", + }) + + +} + +func UpdateModel(c *gin.Context) { + + modelID, err := common.GetModelID(c) + if err != nil { + return + } + + var m Model + err = c.BindJSON(&m) + if err != nil { + errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + err = m.UpdateModel(modelID) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } + +} + +func GetModel(c *gin.Context) { + + modelID, err := common.GetModelID(c) + if err != nil { + return + } + + m, err := FindModel(modelID) + if common.ProvideErrorResponse(c, err) { + return + } + + serializer := common.ModelSerializer{c, m} + c.JSON(http.StatusOK, gin.H{ + "model": serializer.Response(), + }) +} + +func DeleteModel(c *gin.Context) { + + c.JSON(http.StatusOK, gin.H{ + "message": "Not implemented.", + }) +} + + +func GetSimulator(c *gin.Context) { + + modelID, err := common.GetModelID(c) + if err != nil { + return + } + + m, err := FindModel(modelID) + if common.ProvideErrorResponse(c, err) { + return + } + + smtr, err := simulator.FindSimulator(int(m.SimulatorID)) + if common.ProvideErrorResponse(c, err) { + return + } + + serializer := common.SimulatorSerializer{c, smtr} + c.JSON(http.StatusOK, gin.H{ + "simulator": serializer.Response(), + }) +} + + +func UpdateSimulator(c *gin.Context) { + + // simulator ID as parameter of Query, e.g. simulations/:SimulationID/models/:ModelID/simulator?simulatorID=42 + simulatorID, err := strconv.Atoi(c.Query("simulatorID")) + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect simulator ID") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + modelID, err := common.GetModelID(c) + if err != nil { + return + } + + smtr, err := simulator.FindSimulator(simulatorID) + if common.ProvideErrorResponse(c, err) { + return + } + + _m, err := FindModel(modelID) + if common.ProvideErrorResponse(c, err) { + return + } + + var m = Model{_m} + err = m.UpdateSimulator(&smtr) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK", + }) + } + +} + + +func UpdateSignals(c *gin.Context) { + + modelID, err := common.GetModelID(c) + if err != nil { + return + } + + _m, err := FindModel(modelID) + if common.ProvideErrorResponse(c, err) { + return + } + + direction := c.Param("direction") + if !(direction == "out") && !(direction == "in") { + errormsg := "Bad request. Direction has to be in or out" + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + var sigs []common.Signal + err = c.BindJSON(&sigs) + if err != nil { + errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + // Add signals to model and remove all existing Signals of the requested direction (if any) + var m = Model{_m} + err = m.UpdateSignals(sigs, direction) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } +} + +func GetSignals(c *gin.Context) { + + modelID, err := common.GetModelID(c) + if err != nil { + return + } + + m, err := FindModel(modelID) + if common.ProvideErrorResponse(c, err) { + return + } + + direction := c.Param("direction") + if !(direction == "out") && !(direction == "in") { + errormsg := "Bad request. Direction has to be in or out" + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + var signals []common.Signal + if direction == "in" { + signals = m.InputMapping + } else { + signals = m.OutputMapping + } + + c.JSON(http.StatusOK, gin.H{ + "signals": signals, + }) +} \ No newline at end of file diff --git a/routes/model/modelQueries.go b/routes/model/modelQueries.go new file mode 100644 index 0000000..1b46dc3 --- /dev/null +++ b/routes/model/modelQueries.go @@ -0,0 +1,74 @@ +package model + +import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" +) + +type Model struct{ + common.Model +} + +func FindAllModels(simID int) ([]common.Model, int, error) { + db := common.GetDB() + var models []common.Model + sim, err := simulation.FindSimulation(simID) + if err != nil { + return models, 0, err + } + + err = db.Model(sim).Related(&models, "Models").Error + + return models, len(models), err +} + +func FindModel(modelID int) (common.Model, error){ + db := common.GetDB() + var m common.Model + err := db.First(&m, modelID).Error + return m, err +} + +func (m *Model) addToSimulation(simID int) error { + db := common.GetDB() + sim, err := simulation.FindSimulation(simID) + if err != nil { + return err + } + + err = db.Model(&sim).Association("Models").Append(m).Error + return err +} + +func (m *Model) UpdateModel(modelID int) error { + db := common.GetDB() + model_to_update, err := FindModel(modelID) + if err != nil { + return err + } + // only Name and Start Params can be updated directly by the user + err = db.Model(&model_to_update).Updates(map[string]interface{}{"Name": m.Name, "StartParameters": m.StartParameters}).Error + return err +} + + +func (m *Model) UpdateSimulator(simulator *common.Simulator) error { + db := common.GetDB() + err := db.Model(m).Association("Simulator").Replace(simulator).Error + return err +} + +func (m *Model) UpdateSignals(signals []common.Signal, direction string) error { + + db := common.GetDB() + var err error + + if direction == "in" { + err = db.Model(m).Select("InputMapping").Update("InputMapping", signals).Error + } else { + err = db.Model(m).Select("OutputMapping").Update("OutputMapping", signals).Error + } + + return err + +} \ No newline at end of file diff --git a/endpoints/simulationEndpoints.go b/routes/simulation/simulationEndpoints.go similarity index 51% rename from endpoints/simulationEndpoints.go rename to routes/simulation/simulationEndpoints.go index 7af97e3..bd4b5b9 100644 --- a/endpoints/simulationEndpoints.go +++ b/routes/simulation/simulationEndpoints.go @@ -1,106 +1,96 @@ -package endpoints +package simulation import ( - "fmt" "net/http" - "strconv" "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/queries" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" ) -// getSimulationsEp godoc +func RegisterSimulationEndpoints(r *gin.RouterGroup){ + r.GET("/", GetSimulations) + r.POST("/", AddSimulation) + r.POST("/:simulationID", CloneSimulation) + r.PUT("/:simulationID", UpdateSimulation) + r.GET("/:simulationID", GetSimulation) + r.DELETE("/:simulationID", DeleteSimulation) +} + +// GetSimulations godoc // @Summary Get all simulations -// @ID GetAllSimulations +// @ID GetSimulations // @Produce json // @Tags simulation -// @Success 200 {array} common.Simulation "Array of simulations to which user has access" +// @Success 200 {array} common.SimulationResponse "Array of simulations to which user has access" // @Failure 401 "Unauthorized Access" // @Failure 403 "Access forbidden." // @Failure 404 "Not found" // @Failure 500 "Internal server error" // @Router /simulations [get] -func getSimulationsEp(c *gin.Context) { +func GetSimulations(c *gin.Context) { //TODO Identify user who is issuing the request and return only those simulations that are known to the user - allSimulations, _, _ := queries.FindAllSimulations() - serializer := serializers.SimulationsSerializer{c, allSimulations} + allSimulations, _, _ := FindAllSimulations() + serializer := common.SimulationsSerializer{c, allSimulations} c.JSON(http.StatusOK, gin.H{ "simulations": serializer.Response(), }) } -func simulationRegistrationEp(c *gin.Context) { +func AddSimulation(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func simulationCloneEp(c *gin.Context) { +func CloneSimulation(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func simulationUpdateEp(c *gin.Context) { +func UpdateSimulation(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -// getSimulationEp godoc +// GetSimulation godoc // @Summary Get simulation // @ID GetSimulation // @Produce json // @Tags simulation -// @Success 200 {object} common.Simulation "Simulation requested by user" +// @Success 200 {object} common.SimulationResponse "Simulation requested by user" // @Failure 401 "Unauthorized Access" // @Failure 403 "Access forbidden." // @Failure 404 "Not found" // @Failure 500 "Internal server error" // @Param simulationID path int true "Simulation ID" // @Router /simulations/{simulationID} [get] -func getSimulationEp(c *gin.Context) { +func GetSimulation(c *gin.Context) { - simID, err := GetSimulationID(c) + simID, err := common.GetSimulationID(c) if err != nil { return } - sim, err := queries.FindSimulation(simID) + sim, err := FindSimulation(simID) if common.ProvideErrorResponse(c, err) { return } - serializer := serializers.SimulationSerializer{c, sim} + serializer := common.SimulationSerializer{c, sim} c.JSON(http.StatusOK, gin.H{ "simulation": serializer.Response(), }) } -func simulationDeleteEp(c *gin.Context) { +func DeleteSimulation(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func GetSimulationID(c *gin.Context) (int, error) { - - simID, err := strconv.Atoi(c.Param("SimulationID")) - - if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulation ID") - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return -1, err - } else { - return simID, err - - } -} \ No newline at end of file diff --git a/queries/simulationQueries.go b/routes/simulation/simulationQueries.go similarity index 97% rename from queries/simulationQueries.go rename to routes/simulation/simulationQueries.go index 94dcc63..957746a 100644 --- a/queries/simulationQueries.go +++ b/routes/simulation/simulationQueries.go @@ -1,4 +1,4 @@ -package queries +package simulation import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" diff --git a/routes/simulator/simulatorEndpoints.go b/routes/simulator/simulatorEndpoints.go new file mode 100644 index 0000000..813ef4b --- /dev/null +++ b/routes/simulator/simulatorEndpoints.go @@ -0,0 +1,58 @@ +package simulator + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" +) + +func RegisterSimulatorEndpoints(r *gin.RouterGroup){ + r.GET("/", GetSimulators) + r.POST("/", AddSimulator) + r.PUT("/:simulatorID", UpdateSimulator) + r.GET("/:simulatorID", GetSimulator) + r.DELETE("/:simulatorID", DeleteSimulator) + r.POST("/:simulatorID", SendActionToSimulator) +} + +func GetSimulators(c *gin.Context) { + allSimulators, _, _ := FindAllSimulators() + serializer := common.SimulatorsSerializer{c, allSimulators} + c.JSON(http.StatusOK, gin.H{ + "simulators": serializer.Response(), + }) +} + +func AddSimulator(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func UpdateSimulator(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func GetSimulator(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func DeleteSimulator(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + +func SendActionToSimulator(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) +} + + diff --git a/queries/simulatorQueries.go b/routes/simulator/simulatorQueries.go similarity index 96% rename from queries/simulatorQueries.go rename to routes/simulator/simulatorQueries.go index 6ccf6ec..44046e5 100644 --- a/queries/simulatorQueries.go +++ b/routes/simulator/simulatorQueries.go @@ -1,4 +1,4 @@ -package queries +package simulator import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" diff --git a/endpoints/userEndpoints.go b/routes/user/userEndpoints.go similarity index 56% rename from endpoints/userEndpoints.go rename to routes/user/userEndpoints.go index 8fb79de..eb764db 100644 --- a/endpoints/userEndpoints.go +++ b/routes/user/userEndpoints.go @@ -1,4 +1,4 @@ -package endpoints +package user import ( "net/http" @@ -6,71 +6,86 @@ 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/queries" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" ) +func RegisterUserEndpoints(r *gin.RouterGroup){ + r.GET("/", GetUsers) + r.POST("/", AddUser) + r.PUT("/:userID", UpdateUser) + r.GET("/:userID", GetUser) + r.GET("/:userID/simulations", GetSimulationsOfUser) + r.DELETE("/:userID", DeleteUser) + //r.GET("/me", userSelfEp) // TODO redirect to users/:userID +} -func userReadAllEp(c *gin.Context) { - allUsers, _, _ := queries.FindAllUsers() - serializer := serializers.UsersSerializer{c, allUsers} +func RegisterUserEndpointsForSimulation(r *gin.RouterGroup){ + r.GET("/:simulationID/users", GetUsersOfSimulation) + r.PUT("/:simulationID/user/:username", UpdateUserOfSimulation) + r.DELETE("/:simulationID/user/:username", DeleteUserOfSimulation) + +} + +func GetUsers(c *gin.Context) { + allUsers, _, _ := FindAllUsers() + serializer := common.UsersSerializer{c, allUsers} c.JSON(http.StatusOK, gin.H{ "users": serializer.Response(), }) } -// userReadAllSimEp godoc +// GetUsersOfSimulation godoc // @Summary Get users of simulation -// @ID GetAllUsersOfSimulation +// @ID GetUsersOfSimulation // @Produce json // @Tags user -// @Success 200 {array} common.User "Array of users that have access to the simulation" +// @Success 200 {array} common.UserResponse "Array of users that have access to the simulation" // @Failure 401 "Unauthorized Access" // @Failure 403 "Access forbidden." // @Failure 404 "Not found" // @Failure 500 "Internal server error" // @Param simulationID path int true "Simulation ID" // @Router /simulations/{simulationID}/users [get] -func userReadAllSimEp(c *gin.Context) { +func GetUsersOfSimulation(c *gin.Context) { - simID, err := GetSimulationID(c) + simID, err := common.GetSimulationID(c) if err != nil { return } - sim, err := queries.FindSimulation(simID) + sim, err := simulation.FindSimulation(simID) if common.ProvideErrorResponse(c, err) { return } // Find all users of simulation - allUsers, _, err := queries.FindAllUsersSim(&sim) + allUsers, _, err := FindAllUsersSim(&sim) if common.ProvideErrorResponse(c, err) { return } - serializer := serializers.UsersSerializer{c, allUsers} + serializer := common.UsersSerializer{c, allUsers} c.JSON(http.StatusOK, gin.H{ "users": serializer.Response(), }) } -func userRegistrationEp(c *gin.Context) { +func AddUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func userUpdateEp(c *gin.Context) { +func UpdateUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -// userUpdateSimEp godoc +// UpdateUserOfSimulation godoc // @Summary Add user to simulation -// @ID AddUserToSimulation +// @ID UpdateUserOfSimulation // @Tags user // @Success 200 "OK." // @Failure 401 "Unauthorized Access" @@ -80,27 +95,27 @@ func userUpdateEp(c *gin.Context) { // @Param simulationID path int true "Simulation ID" // @Param username path int true "Username of user to be added" // @Router /simulations/{simulationID}/users/{username} [put] -func userUpdateSimEp(c *gin.Context) { +func UpdateUserOfSimulation(c *gin.Context) { - simID, err := GetSimulationID(c) + simID, err := common.GetSimulationID(c) if err != nil { return } - sim, err := queries.FindSimulation(simID) + sim, err := simulation.FindSimulation(simID) if common.ProvideErrorResponse(c, err) { return } username := c.Param("username") - user, err := queries.FindUserByName(username) + user, err := FindUserByName(username) if common.ProvideErrorResponse(c, err) { return } - err = queries.AddUserToSim(&sim, &user) + err = AddUserToSim(&sim, &user) if common.ProvideErrorResponse(c, err){ return } @@ -110,27 +125,27 @@ func userUpdateSimEp(c *gin.Context) { }) } -func userReadEp(c *gin.Context) { +func GetUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func userReadSimEp(c *gin.Context) { +func GetSimulationsOfUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func userDeleteEp(c *gin.Context) { +func DeleteUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -// userDeleteSimEp godoc +// DeleteUserOfSimulation godoc // @Summary Delete user from simulation -// @ID DeleteUserFromSimulation +// @ID DeleteUserOfSimulation // @Tags user // @Success 200 "OK." // @Failure 401 "Unauthorized Access" @@ -140,21 +155,21 @@ func userDeleteEp(c *gin.Context) { // @Param simulationID path int true "Simulation ID" // @Param username path int true "Username of user" // @Router /simulations/{simulationID}/users/{username} [delete] -func userDeleteSimEp(c *gin.Context) { +func DeleteUserOfSimulation(c *gin.Context) { - simID, err := GetSimulationID(c) + simID, err := common.GetSimulationID(c) if err != nil { return } - sim, err := queries.FindSimulation(simID) + sim, err := simulation.FindSimulation(simID) if common.ProvideErrorResponse(c, err) { return } username := c.Param("username") - err = queries.RemoveUserFromSim(&sim, username) + err = RemoveUserFromSim(&sim, username) if common.ProvideErrorResponse(c, err) { return } @@ -163,9 +178,3 @@ func userDeleteSimEp(c *gin.Context) { "message": "OK.", }) } - -func userSelfEp(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} \ No newline at end of file diff --git a/queries/userQueries.go b/routes/user/userQueries.go similarity index 98% rename from queries/userQueries.go rename to routes/user/userQueries.go index de48191..6bdc812 100644 --- a/queries/userQueries.go +++ b/routes/user/userQueries.go @@ -1,4 +1,4 @@ -package queries +package user import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" diff --git a/endpoints/visualizationEndpoints.go b/routes/visualization/visualizationEndpoints.go similarity index 50% rename from endpoints/visualizationEndpoints.go rename to routes/visualization/visualizationEndpoints.go index 73ed287..89a9700 100644 --- a/endpoints/visualizationEndpoints.go +++ b/routes/visualization/visualizationEndpoints.go @@ -1,44 +1,52 @@ -package endpoints +package visualization import ( - "fmt" "net/http" - "strconv" "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/queries" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" ) -func visualizationReadAllEp(c *gin.Context) { +func RegisterVisualizationEndpoints(r *gin.RouterGroup){ - simID, err := GetSimulationID(c) + r.GET("/:simulationID/visualizations", GetVisualizations) + r.POST("/:simulationID/visualization", AddVisualization) + r.POST("/:simulationID/visualization/:visualizationID", CloneVisualization) + r.PUT("/:simulationID/visualization/:visualizationID", UpdateVisualization) + r.GET("/:simulationID/visualization/:visualizationID", GetVisualization) + r.DELETE("/:simulationID/visualization/:visualizationID", DeleteVisualization) + +} + +func GetVisualizations(c *gin.Context) { + + simID, err := common.GetSimulationID(c) if err != nil { return } - sim, err := queries.FindSimulation(simID) + sim, err := simulation.FindSimulation(simID) if common.ProvideErrorResponse(c, err) { return } - allVisualizations, _, _ := queries.FindAllVisualizationsOfSim(&sim) - serializer := serializers.VisualizationsSerializer{c, allVisualizations} + allVisualizations, _, _ := FindAllVisualizationsOfSim(&sim) + serializer := common.VisualizationsSerializer{c, allVisualizations} c.JSON(http.StatusOK, gin.H{ "visualizations": serializer.Response(), }) } -func visualizationRegistrationEp(c *gin.Context) { +func AddVisualization(c *gin.Context) { - simID, err := GetSimulationID(c) + simID, err := common.GetSimulationID(c) if err != nil { return } - sim, err := queries.FindSimulation(simID) + sim, err := simulation.FindSimulation(simID) if common.ProvideErrorResponse(c, err) { return } @@ -54,7 +62,7 @@ func visualizationRegistrationEp(c *gin.Context) { } // add visualization to DB and add association to simulation - err = queries.AddVisualizationToSim(&sim, &vis) + err = AddVisualizationToSim(&sim, &vis) if common.ProvideErrorResponse(c, err) == false { c.JSON(http.StatusOK, gin.H{ "message": "OK", @@ -62,25 +70,25 @@ func visualizationRegistrationEp(c *gin.Context) { } } -func visualizationCloneEp(c *gin.Context) { +func CloneVisualization(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func visualizationUpdateEp(c *gin.Context) { +func UpdateVisualization(c *gin.Context) { - simID, err := GetSimulationID(c) + simID, err := common.GetSimulationID(c) if err != nil { return } - sim, err := queries.FindSimulation(simID) + sim, err := simulation.FindSimulation(simID) if common.ProvideErrorResponse(c, err) { return } - visID, err := GetVisualizationID(c) + visID, err := common.GetVisualizationID(c) if err != nil { return } @@ -95,7 +103,7 @@ func visualizationUpdateEp(c *gin.Context) { return } - err = queries.UpdateVisualizationOfSim(&sim, vis, visID) + err = UpdateVisualizationOfSim(&sim, vis, visID) if common.ProvideErrorResponse(c, err) == false { c.JSON(http.StatusOK, gin.H{ "message": "OK", @@ -103,53 +111,38 @@ func visualizationUpdateEp(c *gin.Context) { } } -func visualizationReadEp(c *gin.Context) { +func GetVisualization(c *gin.Context) { - simID, err := GetSimulationID(c) + simID, err := common.GetSimulationID(c) if err != nil { return } - sim, err := queries.FindSimulation(simID) + sim, err := simulation.FindSimulation(simID) if common.ProvideErrorResponse(c, err) { return } - visID, err := GetVisualizationID(c) + visID, err := common.GetVisualizationID(c) if err != nil { return } - visualization, err := queries.FindVisualizationOfSim(&sim, visID) + visualization, err := FindVisualizationOfSim(&sim, visID) if common.ProvideErrorResponse(c, err) { return } - serializer := serializers.VisualizationSerializer{c, visualization} + serializer := common.VisualizationSerializer{c, visualization} c.JSON(http.StatusOK, gin.H{ "visualization": serializer.Response(), }) } -func visualizationDeleteEp(c *gin.Context) { +func DeleteVisualization(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func GetVisualizationID(c *gin.Context) (int, error) { - - simID, err := strconv.Atoi(c.Param("visualizationID")) - - if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of visualization ID") - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return -1, err - } else { - return simID, err - - } -} \ No newline at end of file diff --git a/queries/visualizationQueries.go b/routes/visualization/visualizationQueries.go similarity index 98% rename from queries/visualizationQueries.go rename to routes/visualization/visualizationQueries.go index 6626da3..da5f70b 100644 --- a/queries/visualizationQueries.go +++ b/routes/visualization/visualizationQueries.go @@ -1,4 +1,4 @@ -package queries +package visualization import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" diff --git a/endpoints/widgetEndpoints.go b/routes/widget/widgetEndpoints.go similarity index 55% rename from endpoints/widgetEndpoints.go rename to routes/widget/widgetEndpoints.go index 15a7867..59c4f00 100644 --- a/endpoints/widgetEndpoints.go +++ b/routes/widget/widgetEndpoints.go @@ -1,69 +1,75 @@ -package endpoints +package widget import ( - "fmt" "net/http" - "strconv" "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/queries" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/serializers" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization" ) +func RegisterWidgetEndpoints(r *gin.RouterGroup){ + r.GET("/:simulationID/visualization/:visualizationID/widgets", GetWidgets) + r.POST("/:simulationID/visualization/:visualizationID/widget", AddWidget) + r.POST("/:simulationID/visualization/:visualizationID/widget:widgetID", CloneWidget) + r.PUT("/:simulationID/visualization/:visualizationID/widget/:widgetID", UpdateWidget) + r.GET("/:simulationID/visualization/:visualizationID/widget/:widgetID", GetWidget) + r.DELETE("/:simulationID/visualization/:visualizationID/widget/:widgetID", DeleteWidget) +} -func widgetReadAllEp(c *gin.Context) { +func GetWidgets(c *gin.Context) { - simID, err := GetSimulationID(c) + simID, err := common.GetSimulationID(c) if err != nil { return } - sim, err := queries.FindSimulation(simID) + sim, err := simulation.FindSimulation(simID) if common.ProvideErrorResponse(c, err) { return } - visID, err := GetVisualizationID(c) + visID, err := common.GetVisualizationID(c) if err != nil { return } - vis, err := queries.FindVisualizationOfSim(&sim, visID) + vis, err := visualization.FindVisualizationOfSim(&sim, visID) if common.ProvideErrorResponse(c, err) { return } - widgets,_, err := queries.FindWidgetsOfVisualization(&vis) + widgets,_, err := FindWidgetsOfVisualization(&vis) if common.ProvideErrorResponse(c, err) { return } - serializer := serializers.WidgetsSerializer{c, widgets} + serializer := common.WidgetsSerializer{c, widgets} c.JSON(http.StatusOK, gin.H{ "widgets": serializer.Response(), }) } -func widgetRegistrationEp(c *gin.Context) { +func AddWidget(c *gin.Context) { - simID, err := GetSimulationID(c) + simID, err := common.GetSimulationID(c) if err != nil { return } - sim, err := queries.FindSimulation(simID) + sim, err := simulation.FindSimulation(simID) if common.ProvideErrorResponse(c, err) { return } - visID, err := GetVisualizationID(c) + visID, err := common.GetVisualizationID(c) if err != nil { return } - vis, err := queries.FindVisualizationOfSim(&sim, visID) + vis, err := visualization.FindVisualizationOfSim(&sim, visID) if common.ProvideErrorResponse(c, err) { return } @@ -78,7 +84,7 @@ func widgetRegistrationEp(c *gin.Context) { return } - err = queries.AddWidgetToVisualization(&vis, &widget_input) + err = AddWidgetToVisualization(&vis, &widget_input) if common.ProvideErrorResponse(c, err) == false { c.JSON(http.StatusOK, gin.H{ "message": "OK.", @@ -88,34 +94,34 @@ func widgetRegistrationEp(c *gin.Context) { } -func widgetCloneEp(c *gin.Context) { +func CloneWidget(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func widgetUpdateEp(c *gin.Context) { - simID, err := GetSimulationID(c) +func UpdateWidget(c *gin.Context) { + simID, err := common.GetSimulationID(c) if err != nil { return } - sim, err := queries.FindSimulation(simID) + sim, err := simulation.FindSimulation(simID) if common.ProvideErrorResponse(c, err) { return } - visID, err := GetVisualizationID(c) + visID, err := common.GetVisualizationID(c) if err != nil { return } - vis, err := queries.FindVisualizationOfSim(&sim, visID) + vis, err := visualization.FindVisualizationOfSim(&sim, visID) if common.ProvideErrorResponse(c, err) { return } - widgetID, err := GetWidgetID(c) + widgetID, err := common.GetWidgetID(c) if err != nil { return } @@ -130,7 +136,7 @@ func widgetUpdateEp(c *gin.Context) { return } - err = queries.UpdateWidgetOfVisualization(&vis, widget_input, widgetID) + err = UpdateWidgetOfVisualization(&vis, widget_input, widgetID) if common.ProvideErrorResponse(c, err) == false { c.JSON(http.StatusOK, gin.H{ "message": "OK", @@ -138,41 +144,41 @@ func widgetUpdateEp(c *gin.Context) { } } -func widgetReadEp(c *gin.Context) { +func GetWidget(c *gin.Context) { - simID, err := GetSimulationID(c) + simID, err := common.GetSimulationID(c) if err != nil { return } - sim, err := queries.FindSimulation(simID) + sim, err := simulation.FindSimulation(simID) if common.ProvideErrorResponse(c, err) { return } - visID, err := GetVisualizationID(c) + visID, err := common.GetVisualizationID(c) if err != nil { return } - visualization, err := queries.FindVisualizationOfSim(&sim, visID) + vis, err := visualization.FindVisualizationOfSim(&sim, visID) if common.ProvideErrorResponse(c, err) { return } - widgetID, err := GetWidgetID(c) + widgetID, err := common.GetWidgetID(c) if err != nil { return } - widget, err := queries.FindWidgetOfVisualization(&visualization, widgetID) - serializer := serializers.WidgetSerializer{c, widget} + widget, err := FindWidgetOfVisualization(&vis, widgetID) + serializer := common.WidgetSerializer{c, widget} c.JSON(http.StatusOK, gin.H{ "widget": serializer.Response(), }) } -func widgetDeleteEp(c *gin.Context) { +func DeleteWidget(c *gin.Context) { // simID, err := GetSimulationID(c) // if err != nil { @@ -215,18 +221,3 @@ func widgetDeleteEp(c *gin.Context) { } -func GetWidgetID(c *gin.Context) (int, error) { - - widgetID, err := strconv.Atoi(c.Param("WidgetID")) - - if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of widget ID") - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return -1, err - } else { - return widgetID, err - - } -} \ No newline at end of file diff --git a/queries/widgetQueries.go b/routes/widget/widgetQueries.go similarity index 99% rename from queries/widgetQueries.go rename to routes/widget/widgetQueries.go index 2cd6d89..f67b091 100644 --- a/queries/widgetQueries.go +++ b/routes/widget/widgetQueries.go @@ -1,4 +1,4 @@ -package queries +package widget import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" diff --git a/serializers/fileSerializer.go b/serializers/fileSerializer.go deleted file mode 100644 index 2b30206..0000000 --- a/serializers/fileSerializer.go +++ /dev/null @@ -1,53 +0,0 @@ -package serializers - -import ( - "github.com/gin-gonic/gin" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -// File/s Serializers - -type FilesSerializerNoAssoc struct { - Ctx *gin.Context - Files []common.File -} - -func (self *FilesSerializerNoAssoc) Response() []FileResponseNoAssoc { - response := []FileResponseNoAssoc{} - for _, files := range self.Files { - serializer := FileSerializerNoAssoc{self.Ctx, files} - response = append(response, serializer.Response()) - } - return response -} - -type FileSerializerNoAssoc struct { - Ctx *gin.Context - common.File -} - -type FileResponseNoAssoc struct { - Name string `json:"Name"` - ID uint `json:"FileID"` - Path string `json:"Path"` - Type string `json:"Type"` - Size uint `json:"Size"` - H uint `json:"ImageHeight"` - W uint `json:"ImageWidth"` - // Date -} - -func (self *FileSerializerNoAssoc) Response() FileResponseNoAssoc { - response := FileResponseNoAssoc{ - Name: self.Name, - ID: self.ID, - Path: self.Path, - Type: self.Type, - Size: self.Size, - H: self.ImageHeight, - W: self.ImageWidth, - // Date - } - return response -} \ No newline at end of file diff --git a/serializers/modelSerializer.go b/serializers/modelSerializer.go deleted file mode 100644 index 0f91387..0000000 --- a/serializers/modelSerializer.go +++ /dev/null @@ -1,54 +0,0 @@ -package serializers - -import ( - "github.com/gin-gonic/gin" - "github.com/jinzhu/gorm/dialects/postgres" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -type ModelsSerializer struct { - Ctx *gin.Context - Models []common.Model -} - -func (self *ModelsSerializer) Response() []ModelResponse { - response := []ModelResponse{} - for _, model := range self.Models { - serializer := ModelSerializer{self.Ctx, model} - response = append(response, serializer.Response()) - } - return response -} - -type ModelSerializer struct { - Ctx *gin.Context - common.Model -} - -type ModelResponse struct { - Name string `json:"Name"` - OutputLength int `json:"OutputLength"` - InputLength int `json:"InputLength"` - SimulationID uint `json:"SimulationID"` - SimulatorID uint `json:"SimulatorID"` - StartParams postgres.Jsonb `json:"StartParams"` - //StartParams postgres.Jsonb `json:"Starting Parameters"` - //Output Mapping - //Input Mapping -} - -func (self *ModelSerializer) Response() ModelResponse { - response := ModelResponse{ - Name: self.Name, - OutputLength: self.OutputLength, - InputLength: self.InputLength, - SimulationID: self.SimulationID, - SimulatorID: self.SimulatorID, - StartParams: self.StartParameters, - //InputMapping - //OutputMapping - } - return response -} - diff --git a/serializers/signalSerializer.go b/serializers/signalSerializer.go deleted file mode 100644 index 2978a89..0000000 --- a/serializers/signalSerializer.go +++ /dev/null @@ -1 +0,0 @@ -package serializers diff --git a/serializers/simulationSerializer.go b/serializers/simulationSerializer.go deleted file mode 100644 index 865cdd7..0000000 --- a/serializers/simulationSerializer.go +++ /dev/null @@ -1,44 +0,0 @@ -package serializers - -import ( - "github.com/gin-gonic/gin" - "github.com/jinzhu/gorm/dialects/postgres" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -type SimulationsSerializer struct { - Ctx *gin.Context - Simulations []common.Simulation -} - -func (self *SimulationsSerializer) Response() []SimulationResponse { - response := []SimulationResponse{} - for _, simulation := range self.Simulations { - serializer := SimulationSerializer{self.Ctx, simulation} - response = append(response, serializer.Response()) - } - return response -} - -type SimulationSerializer struct { - Ctx *gin.Context - common.Simulation -} - -type SimulationResponse struct { - Name string `json:"Name"` - ID uint `json:"SimulationID"` - Running bool `json:"Running"` - StartParams postgres.Jsonb `json:"Starting Parameters"` -} - -func (self *SimulationSerializer) Response() SimulationResponse { - response := SimulationResponse{ - Name: self.Name, - ID: self.ID, - Running: self.Running, - StartParams: self.StartParameters, - } - return response -} diff --git a/serializers/simulatorSerializer.go b/serializers/simulatorSerializer.go deleted file mode 100644 index 27b5e4b..0000000 --- a/serializers/simulatorSerializer.go +++ /dev/null @@ -1,52 +0,0 @@ -package serializers - -import ( - "time" - - "github.com/gin-gonic/gin" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -type SimulatorsSerializer struct { - Ctx *gin.Context - Simulators []common.Simulator -} - -func (self *SimulatorsSerializer) Response() []SimulatorResponse { - response := []SimulatorResponse{} - for _, simulator := range self.Simulators { - serializer := SimulatorSerializer{self.Ctx, simulator} - response = append(response, serializer.Response()) - } - return response -} - -type SimulatorSerializer struct { - Ctx *gin.Context - common.Simulator -} - -type SimulatorResponse struct { - UUID string `json:"UUID"` - Host string `json:"Host"` - ModelType string `json:"ModelType"` - Uptime int `json:"Uptime"` - State string `json:"State"` - StateUpdateAt time.Time `json:"StateUpdateAt"` - // Properties - // Raw Properties -} - -func (self *SimulatorSerializer) Response() SimulatorResponse { - - response := SimulatorResponse{ - UUID: self.UUID, - Host: self.Host, - ModelType: self.Modeltype, - Uptime: self.Uptime, - State: self.State, - StateUpdateAt: self.StateUpdateAt, - } - return response -} diff --git a/serializers/userSerializer.go b/serializers/userSerializer.go deleted file mode 100644 index fc2d84f..0000000 --- a/serializers/userSerializer.go +++ /dev/null @@ -1,45 +0,0 @@ -package serializers - -import ( - "github.com/gin-gonic/gin" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -type UsersSerializer struct { - Ctx *gin.Context - Users []common.User -} - -func (self *UsersSerializer) Response() []UserResponse { - response := []UserResponse{} - for _, user := range self.Users { - serializer := UserSerializer{self.Ctx, user} - response = append(response, serializer.Response()) - } - return response -} - -type UserSerializer struct { - Ctx *gin.Context - common.User -} - -type UserResponse struct { - Username string `json:"Username"` - Role string `json:"Role"` - Mail string `json:"Mail"` -} - -func (self *UserSerializer) Response() UserResponse { - response := UserResponse{ - Username: self.Username, - Role: self.Role, - Mail: self.Mail, - } - return response -} - - - - diff --git a/serializers/visualizationSerializer.go b/serializers/visualizationSerializer.go deleted file mode 100644 index e6ff9c0..0000000 --- a/serializers/visualizationSerializer.go +++ /dev/null @@ -1,49 +0,0 @@ -package serializers - -import ( - "github.com/gin-gonic/gin" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/queries" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -type VisualizationsSerializer struct { - Ctx *gin.Context - Visualizations []common.Visualization -} - -func (self *VisualizationsSerializer) Response() []VisualizationResponse { - response := []VisualizationResponse{} - for _, visualization := range self.Visualizations { - serializer := VisualizationSerializer{self.Ctx, visualization} - response = append(response, serializer.Response()) - } - return response -} - -type VisualizationSerializer struct { - Ctx *gin.Context - common.Visualization -} - -type VisualizationResponse struct { - Name string `json:"Name"` - Grid int `json:"Grid"` - SimulationID uint `json:"SimulationID"` - Widgets []WidgetResponse -} - -func (self *VisualizationSerializer) Response() VisualizationResponse { - - w, _, _:= queries.FindVisualizationWidgets(&self.Visualization) - widgetsSerializer := WidgetsSerializer{self.Ctx, w} - - response := VisualizationResponse{ - Name: self.Name, - Grid: self.Grid, - SimulationID: self.SimulationID, - Widgets: widgetsSerializer.Response(), - } - return response -} - diff --git a/serializers/widgetSerializer.go b/serializers/widgetSerializer.go deleted file mode 100644 index 1e0077f..0000000 --- a/serializers/widgetSerializer.go +++ /dev/null @@ -1,60 +0,0 @@ -package serializers - -import ( - "github.com/gin-gonic/gin" - - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" -) - -type WidgetsSerializer struct { - Ctx *gin.Context - Widgets []common.Widget -} - -func (self *WidgetsSerializer) Response() []WidgetResponse { - response := []WidgetResponse{} - for _, widget := range self.Widgets { - serializer := WidgetSerializer{self.Ctx, widget} - response = append(response, serializer.Response()) - } - return response -} - -type WidgetSerializer struct { - Ctx *gin.Context - common.Widget -} - -type WidgetResponse struct { - Name string `json:"Name"` - Type string `json:"Type"` - Width uint `json:"Width"` - Height uint `json:"Height"` - MinWidth uint `json:"MinWidth"` - MinHeight uint `json:"MinHeight"` - X int `json:"X"` - Y int `json:"Y"` - Z int `json:"Z"` - VisualizationID uint `json:"VisualizationID"` - IsLocked bool `json:"IsLocked"` - //CustomProperties -} - -func (self *WidgetSerializer) Response() WidgetResponse { - - response := WidgetResponse{ - Name: self.Name, - Type: self.Type, - Width: self.Width, - Height: self.Height, - MinWidth: self.MinWidth, - MinHeight: self.MinHeight, - X: self.X, - Y: self.Y, - Z: self.Z, - VisualizationID: self.VisualizationID, - IsLocked: self.IsLocked, - //CustomProperties - } - return response -} diff --git a/start.go b/start.go index 8294c30..1765b1b 100644 --- a/start.go +++ b/start.go @@ -5,10 +5,15 @@ import ( "github.com/swaggo/gin-swagger" "github.com/swaggo/gin-swagger/swaggerFiles" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/endpoints" - _ "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/apidocs" // apidocs folder is generated by Swag CLI, you have to import it + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/model" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget" ) @@ -36,18 +41,18 @@ func main() { r := gin.Default() - - - api := r.Group("/api") + // use ginSwagger middleware to - endpoints.UsersRegister(api.Group("/users")) - //file.FilesRegister(api.Group("/files")) - //project.ProjectsRegister(api.Group("/projects")) - endpoints.SimulationsRegister(api.Group("/simulations")) - //model.ModelsRegister(api.Group("/simulations")) - endpoints.SimulatorsRegister(api.Group("/simulators")) - //visualization.VisualizationsRegister(api.Group("/visualizations")) + simulation.RegisterSimulationEndpoints(api.Group("/simulations")) + file.RegisterFileEndpoints(api.Group("/simulations")) + model.RegisterModelEndpoints(api.Group("/simulations")) + visualization.RegisterVisualizationEndpoints(api.Group("/simulations")) + widget.RegisterWidgetEndpoints(api.Group("/simulations")) + user.RegisterUserEndpointsForSimulation(api.Group("/simulations")) + + user.RegisterUserEndpoints(api.Group("/users")) + simulator.RegisterSimulatorEndpoints(api.Group("/simulators")) r.GET("swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) From 3f4ed4f4048230aa728cacfae86c9374de70cc54 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Thu, 23 May 2019 16:54:46 +0200 Subject: [PATCH 20/30] CI: fix due to auto generated API docs --- .gitlab-ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8059e9a..d9fdedb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -51,6 +51,8 @@ build:backend: image: villaswebbackendgo:ubuntu script: - go mod tidy + - go get -u github.com/swaggo/swag/cmd/swag + - swag init -p pascalcase -g "start.go" -o "./doc/autoapi/" - go build @@ -65,6 +67,8 @@ test:backend:database: script: - /etc/init.d/postgresql start - go mod tidy + - go get -u github.com/swaggo/swag/cmd/swag + - swag init -p pascalcase -g "start.go" -o "./doc/autoapi/" - cd common - go test -v -args -dbhost=/var/run/postgresql dependencies: @@ -78,6 +82,8 @@ test:backend:endpoints: script: - /etc/init.d/postgresql start - go mod tidy + - go get -u github.com/swaggo/swag/cmd/swag + - swag init -p pascalcase -g "start.go" -o "./doc/autoapi/" - cd endpoints - go test -v -args -dbhost=/var/run/postgresql dependencies: From 13e80dcdca180dbc1b6df8131a7e42441435b8bf Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Thu, 23 May 2019 16:58:35 +0200 Subject: [PATCH 21/30] CI: another fix --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d9fdedb..6704008 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -52,7 +52,7 @@ build:backend: script: - go mod tidy - go get -u github.com/swaggo/swag/cmd/swag - - swag init -p pascalcase -g "start.go" -o "./doc/autoapi/" + - ~/go/bin/swag init -p pascalcase -g "start.go" -o "./doc/autoapi/" - go build @@ -68,7 +68,7 @@ test:backend:database: - /etc/init.d/postgresql start - go mod tidy - go get -u github.com/swaggo/swag/cmd/swag - - swag init -p pascalcase -g "start.go" -o "./doc/autoapi/" + - ~/go/bin/swag init -p pascalcase -g "start.go" -o "./doc/autoapi/" - cd common - go test -v -args -dbhost=/var/run/postgresql dependencies: @@ -83,7 +83,7 @@ test:backend:endpoints: - /etc/init.d/postgresql start - go mod tidy - go get -u github.com/swaggo/swag/cmd/swag - - swag init -p pascalcase -g "start.go" -o "./doc/autoapi/" + - ~/go/bin/swag init -p pascalcase -g "start.go" -o "./doc/autoapi/" - cd endpoints - go test -v -args -dbhost=/var/run/postgresql dependencies: From 9030363d17a0206a39a10dc8e1f642d8acfa42e4 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Thu, 23 May 2019 17:01:38 +0200 Subject: [PATCH 22/30] Typo fix --- start.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/start.go b/start.go index 1765b1b..216af69 100644 --- a/start.go +++ b/start.go @@ -5,7 +5,7 @@ import ( "github.com/swaggo/gin-swagger" "github.com/swaggo/gin-swagger/swaggerFiles" - _ "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/apidocs" // apidocs folder is generated by Swag CLI, you have to import it + _ "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/doc/autoapi" // apidocs folder is generated by Swag CLI, you have to import it "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/model" From 196beafcf962e3397236ff1d8929f561e750efda Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Thu, 23 May 2019 17:05:46 +0200 Subject: [PATCH 23/30] CI: fix a path --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6704008..e42323d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -84,7 +84,7 @@ test:backend:endpoints: - go mod tidy - go get -u github.com/swaggo/swag/cmd/swag - ~/go/bin/swag init -p pascalcase -g "start.go" -o "./doc/autoapi/" - - cd endpoints + - cd routes - go test -v -args -dbhost=/var/run/postgresql dependencies: - build:backend From e7790f82a45c355ed2c1c610a49629605e7d2cfa Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Fri, 24 May 2019 16:31:28 +0200 Subject: [PATCH 24/30] deterministic order of elements based on ID if multiple elements are extracted from DB --- routes/endpoints_test.go | 2 +- routes/model/modelQueries.go | 2 +- routes/simulation/simulationQueries.go | 8 ++++++-- routes/simulator/simulatorQueries.go | 2 +- routes/user/userQueries.go | 4 ++-- routes/visualization/visualizationQueries.go | 2 +- routes/widget/widgetQueries.go | 4 ++-- 7 files changed, 14 insertions(+), 10 deletions(-) diff --git a/routes/endpoints_test.go b/routes/endpoints_test.go index 7110e15..06903b2 100644 --- a/routes/endpoints_test.go +++ b/routes/endpoints_test.go @@ -39,8 +39,8 @@ var user_B = common.UserResponse{ } var myUsers = []common.UserResponse{ - user_B, user_A, + user_B, } var msgUsers = common.ResponseMsgUsers{ diff --git a/routes/model/modelQueries.go b/routes/model/modelQueries.go index 1b46dc3..784eb59 100644 --- a/routes/model/modelQueries.go +++ b/routes/model/modelQueries.go @@ -17,7 +17,7 @@ func FindAllModels(simID int) ([]common.Model, int, error) { return models, 0, err } - err = db.Model(sim).Related(&models, "Models").Error + err = db.Order("ID asc").Model(sim).Related(&models, "Models").Error return models, len(models), err } diff --git a/routes/simulation/simulationQueries.go b/routes/simulation/simulationQueries.go index 957746a..382128e 100644 --- a/routes/simulation/simulationQueries.go +++ b/routes/simulation/simulationQueries.go @@ -4,17 +4,21 @@ import ( "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" ) +type Simulation struct{ + common.Simulation +} + func FindAllSimulations() ([]common.Simulation, int, error) { db := common.GetDB() var simulations []common.Simulation - err := db.Find(&simulations).Error + err := db.Order("ID asc").Find(&simulations).Error return simulations, len(simulations), err } func FindUserSimulations(user *common.User) ([]common.Simulation, int, error) { db := common.GetDB() var simulations []common.Simulation - err := db.Model(user).Related(&simulations, "Simulations").Error + err := db.Order("ID asc").Model(user).Related(&simulations, "Simulations").Error return simulations, len(simulations), err } diff --git a/routes/simulator/simulatorQueries.go b/routes/simulator/simulatorQueries.go index 44046e5..b52285c 100644 --- a/routes/simulator/simulatorQueries.go +++ b/routes/simulator/simulatorQueries.go @@ -7,7 +7,7 @@ import ( func FindAllSimulators() ([]common.Simulator, int, error) { db := common.GetDB() var simulators []common.Simulator - err := db.Find(&simulators).Error + err := db.Order("ID asc").Find(&simulators).Error return simulators, len(simulators), err } diff --git a/routes/user/userQueries.go b/routes/user/userQueries.go index 6bdc812..82d3613 100644 --- a/routes/user/userQueries.go +++ b/routes/user/userQueries.go @@ -7,14 +7,14 @@ import ( func FindAllUsers() ([]common.User, int, error) { db := common.GetDB() var users []common.User - err := db.Find(&users).Error + err := db.Order("ID asc").Find(&users).Error return users, len(users), err } func FindAllUsersSim(sim *common.Simulation) ([]common.User, int, error) { db := common.GetDB() var users []common.User - err := db.Model(sim).Related(&users, "Users").Error + err := db.Order("ID asc").Model(sim).Related(&users, "Users").Error return users, len(users), err } diff --git a/routes/visualization/visualizationQueries.go b/routes/visualization/visualizationQueries.go index da5f70b..0f20f7d 100644 --- a/routes/visualization/visualizationQueries.go +++ b/routes/visualization/visualizationQueries.go @@ -7,7 +7,7 @@ import ( func FindAllVisualizationsOfSim(sim *common.Simulation) ([]common.Visualization, int, error) { db := common.GetDB() var visualizations []common.Visualization - err := db.Model(sim).Related(&visualizations, "Visualizations").Error + err := db.Order("ID asc").Model(sim).Related(&visualizations, "Visualizations").Error return visualizations, len(visualizations), err } diff --git a/routes/widget/widgetQueries.go b/routes/widget/widgetQueries.go index f67b091..2278622 100644 --- a/routes/widget/widgetQueries.go +++ b/routes/widget/widgetQueries.go @@ -7,7 +7,7 @@ import ( func FindVisualizationWidgets(visualization *common.Visualization) ([]common.Widget, int, error) { db := common.GetDB() var widgets []common.Widget - err := db.Model(visualization).Related(&widgets, "Widgets").Error + err := db.Order("ID asc").Model(visualization).Related(&widgets, "Widgets").Error return widgets, len(widgets), err } @@ -23,7 +23,7 @@ func FindWidget(widgetID int) (common.Widget, error){ func FindWidgetsOfVisualization(vis * common.Visualization) ([]common.Widget, int, error) { db := common.GetDB() var widgets []common.Widget - err := db.Model(vis).Related(&vis, "Widgets").Error + err := db.Order("ID asc").Model(vis).Related(&vis, "Widgets").Error return widgets, len(widgets), err } From 0c4c60c1ce27e78bab4ad60324e7880e711b4526 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Fri, 24 May 2019 17:15:15 +0200 Subject: [PATCH 25/30] Add more API doc in code, modify deployment endpoint of auto generated API doc --- routes/file/fileEndpoints.go | 128 +++++++++++++++++++++++-- routes/simulator/simulatorEndpoints.go | 13 +++ start.go | 2 +- 3 files changed, 134 insertions(+), 9 deletions(-) diff --git a/routes/file/fileEndpoints.go b/routes/file/fileEndpoints.go index a3df5e3..07a3d46 100644 --- a/routes/file/fileEndpoints.go +++ b/routes/file/fileEndpoints.go @@ -13,14 +13,11 @@ import ( func RegisterFileEndpoints(r *gin.RouterGroup){ r.GET("/:simulationID/models/:modelID/files", GetFilesOfModel) r.POST ("/:simulationID/models/:modelID/file", AddFileToModel) - //r.POST ("/:simulationID/models/:modelID/file", CloneFileOfModel) r.GET("/:simulationID/models/:modelID/file", GetFileOfModel) r.PUT("/:simulationID/models/:modelID/file", UpdateFileOfModel) r.DELETE("/:simulationID/models/:modelID/file", DeleteFileOfModel) - r.GET("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/files", GetFilesOfWidget) r.POST ("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", AddFileToWidget) - //r.POST ("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", CloneFileOfWidget) r.GET("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", GetFileOfWidget) r.PUT("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", UpdateFileOfWidget) r.DELETE("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", DeleteFileOfWidget) @@ -29,15 +26,16 @@ func RegisterFileEndpoints(r *gin.RouterGroup){ // GetFilesOfModel godoc // @Summary Get all parameters of files of model // @ID GetFilesOfModel -// @Tags file -// @Success 200 {array} common.File "File parameters requested by user" +// @Tags files +// @Produce json +// @Success 200 {array} common.FileResponse "File parameters requested by user" // @Failure 401 "Unauthorized Access" // @Failure 403 "Access forbidden." // @Failure 404 "Not found" // @Failure 500 "Internal server error" // @Param simulationID path int true "Simulation ID" // @Param modelID path int true "Model ID" -// @Router simulations/{simulationID}/models/{modelID}/files [get] +// @Router /simulations/{simulationID}/models/{modelID}/files [get] func GetFilesOfModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) @@ -60,7 +58,9 @@ func GetFilesOfModel(c *gin.Context) { // AddFileToModel godoc // @Summary Get all parameters of files of model // @ID AddFileToModel -// @Tags file +// @Tags files +// @Accept text/plain +// @Produce json // @Success 200 "OK." // @Failure 401 "Unauthorized Access" // @Failure 403 "Access forbidden." @@ -68,7 +68,7 @@ func GetFilesOfModel(c *gin.Context) { // @Failure 500 "Internal server error" // @Param simulationID path int true "Simulation ID" // @Param modelID path int true "Model ID" -// @Router simulations/{simulationID}/models/{modelID}/file [post] +// @Router /simulations/{simulationID}/models/{modelID}/file [post] func AddFileToModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) @@ -89,6 +89,19 @@ func CloneFileOfModel(c *gin.Context) { } +// GetFileOfModel godoc +// @Summary Download a file that belongs to a model +// @ID GetFileOfModel +// @Tags files +// @Produce text/plain +// @Success 200 "OK, File included in response." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param modelID path int true "Model ID" +// @Router /simulations/{simulationID}/models/{modelID}/file [get] func GetFileOfModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) @@ -100,6 +113,20 @@ func GetFileOfModel(c *gin.Context) { ReadFile(c, -1, modelID, simulationID) } +// UpdateFileOfModel godoc +// @Summary Update (overwrite) a file that belongs to a model +// @ID UpdateFileOfModel +// @Tags files +// @Accept text/plain +// @Produce json +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param modelID path int true "Model ID" +// @Router /simulations/{simulationID}/models/{modelID}/file [put] func UpdateFileOfModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) @@ -111,6 +138,19 @@ func UpdateFileOfModel(c *gin.Context) { UpdateFile(c,-1, modelID, simulationID) } +// DeleteFileOfModel godoc +// @Summary Delete a file that belongs to a model +// @ID DeleteFileOfModel +// @Tags files +// @Produce json +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param modelID path int true "Model ID" +// @Router /simulations/{simulationID}/models/{modelID}/file [delete] func DeleteFileOfModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) @@ -124,6 +164,20 @@ func DeleteFileOfModel(c *gin.Context) { } +// GetFilesOfWidget godoc +// @Summary Get all parameters of files of widget +// @ID GetFilesOfWidget +// @Tags files +// @Produce json +// @Success 200 {array} common.WidgetResponse "File parameters requested by user" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param visualizationID path int true "Visualization ID" +// @Param widgetID path int true "Widget ID" +// @Router /simulations/{simulationID}/visualizations/{visualizationID}/widgets/{widgetID}/files [get] func GetFilesOfWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) @@ -143,6 +197,21 @@ func GetFilesOfWidget(c *gin.Context) { } +// AddFileToWidget godoc +// @Summary Get all parameters of files of widget +// @ID AddFileToWidget +// @Tags files +// @Accept text/plain +// @Produce json +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param visualizationID path int true "Visualization ID" +// @Param widgetID path int true "Widget ID" +// @Router /simulations/{simulationID}/visualizations/{visualizationID}/widgets/{widgetID}/file [post] func AddFileToWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) @@ -163,6 +232,20 @@ func CloneFileOfWidget(c *gin.Context) { } +// GetFileOfWidget godoc +// @Summary Download a file that belongs to a widget +// @ID GetFileOfWidget +// @Tags files +// @Produce text/plain +// @Success 200 "OK, File included in response." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param visualizationID path int true "Visualization ID" +// @Param widgetID path int true "Widget ID" +// @Router /simulations/{simulationID}/visualizations/{visualizationID}/widgets/{widgetID}/file [get] func GetFileOfWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) @@ -174,6 +257,21 @@ func GetFileOfWidget(c *gin.Context) { ReadFile(c, widgetID, -1, simulationID) } +// UpdateFileOfWidget godoc +// @Summary Update (overwrite) a file that belongs to a widget +// @ID UpdateFileOfWidget +// @Tags files +// @Accept text/plain +// @Produce json +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param visualizationID path int true "Visualization ID" +// @Param widgetID path int true "Widget ID" +// @Router /simulations/{simulationID}/visualizations/{visualizationID}/widgets/{widgetID}/file [put] func UpdateFileOfWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) @@ -185,6 +283,20 @@ func UpdateFileOfWidget(c *gin.Context) { UpdateFile(c,widgetID, -1, simulationID) } +// DeleteFileOfWidget godoc +// @Summary Delete a file that belongs to a widget +// @ID DeleteFileOfWidget +// @Tags files +// @Produce json +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param visualizationID path int true "Visualization ID" +// @Param widgetID path int true "Widget ID" +// @Router /simulations/{simulationID}/visualizations/{visualizationID}/widgets/{widgetID}/file [delete] func DeleteFileOfWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) diff --git a/routes/simulator/simulatorEndpoints.go b/routes/simulator/simulatorEndpoints.go index 813ef4b..63d5b94 100644 --- a/routes/simulator/simulatorEndpoints.go +++ b/routes/simulator/simulatorEndpoints.go @@ -17,6 +17,19 @@ func RegisterSimulatorEndpoints(r *gin.RouterGroup){ r.POST("/:simulatorID", SendActionToSimulator) } +// GetSimulators godoc +// @Summary Get all simulators +// @ID GetSimulators +// @Tags simulators +// @Produce json +// @Success 200 {array} common.SimulatorResponse "Simulator parameters requested by user" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param modelID path int true "Model ID" +// @Router /simulators [get] func GetSimulators(c *gin.Context) { allSimulators, _, _ := FindAllSimulators() serializer := common.SimulatorsSerializer{c, allSimulators} diff --git a/start.go b/start.go index 216af69..e46d5f4 100644 --- a/start.go +++ b/start.go @@ -31,7 +31,7 @@ import ( // @license.name GNU GPL 3.0 // @license.url http://www.gnu.de/documents/gpl-3.0.en.html -// @host aaa.bbb.ccc.ddd:pppp +// @host localhost:8080 // @BasePath /api/v2 func main() { // Testing From 34eebbab8889f32b9620200bc6a57703f4137527 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 27 May 2019 14:43:12 +0200 Subject: [PATCH 26/30] Shorten API URLs, add more documentation for swaggo --- routes/file/fileEndpoints.go | 347 +++++++++--------- routes/file/fileQueries.go | 43 --- routes/model/modelEndpoints.go | 71 +++- routes/model/modelQueries.go | 5 + routes/simulation/simulationEndpoints.go | 174 ++++++++- routes/simulator/simulatorEndpoints.go | 69 +++- routes/user/userEndpoints.go | 170 +++------ .../visualization/visualizationEndpoints.go | 75 +++- routes/widget/widgetEndpoints.go | 76 +++- start.go | 15 +- 10 files changed, 673 insertions(+), 372 deletions(-) diff --git a/routes/file/fileEndpoints.go b/routes/file/fileEndpoints.go index 07a3d46..3980279 100644 --- a/routes/file/fileEndpoints.go +++ b/routes/file/fileEndpoints.go @@ -3,6 +3,7 @@ package file import ( "fmt" "net/http" + "path/filepath" "strconv" "github.com/gin-gonic/gin" @@ -11,21 +12,23 @@ import ( ) func RegisterFileEndpoints(r *gin.RouterGroup){ - r.GET("/:simulationID/models/:modelID/files", GetFilesOfModel) - r.POST ("/:simulationID/models/:modelID/file", AddFileToModel) - r.GET("/:simulationID/models/:modelID/file", GetFileOfModel) - r.PUT("/:simulationID/models/:modelID/file", UpdateFileOfModel) - r.DELETE("/:simulationID/models/:modelID/file", DeleteFileOfModel) - r.GET("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/files", GetFilesOfWidget) - r.POST ("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", AddFileToWidget) - r.GET("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", GetFileOfWidget) - r.PUT("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", UpdateFileOfWidget) - r.DELETE("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", DeleteFileOfWidget) + r.GET("/", GetFiles) + r.POST ("/", AddFile) + r.GET("/:fileID", GetFile) + r.PUT("/:fileID", UpdateFile) + r.DELETE("/:fileID", DeleteFile) + //r.GET("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/files", GetFilesOfWidget) + //r.POST ("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", AddFileToWidget) + //r.GET("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", GetFileOfWidget) + //r.PUT("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", UpdateFileOfWidget) + //r.DELETE("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", DeleteFileOfWidget) } -// GetFilesOfModel godoc -// @Summary Get all parameters of files of model -// @ID GetFilesOfModel + + +// GetFiles godoc +// @Summary Get all files of a specific model or widget +// @ID GetFiles // @Tags files // @Produce json // @Success 200 {array} common.FileResponse "File parameters requested by user" @@ -33,9 +36,140 @@ func RegisterFileEndpoints(r *gin.RouterGroup){ // @Failure 403 "Access forbidden." // @Failure 404 "Not found" // @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param modelID path int true "Model ID" -// @Router /simulations/{simulationID}/models/{modelID}/files [get] +// @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" +// @Router /files [get] +func GetFiles(c *gin.Context) { + + // TODO if originType == "model" --> GetFilesOfModel, if originType == "vis" --> GetFilesOfWidget + +} + +// AddFile godoc +// @Summary Add a file to a specific model or widget +// @ID AddFile +// @Tags files +// @Produce json +// @Accept text/plain +// @Accept png +// @Accept jpeg +// @Accept gif +// @Accept model/x-cim +// @Accept model/x-cim.zip +// @Success 200 "OK" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param inputFile formData file true "File to be uploaded" +// @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" +// @Router /files [post] +func AddFile(c *gin.Context){ + // TODO if originType == "model" --> AddFileToModel, if originType == "vis" --> AddFileToWidget +} + +// GetFile godoc +// @Summary Download a file +// @ID GetFile +// @Tags files +// @Produce text/plain +// @Produce png +// @Produce jpeg +// @Produce gif +// @Produce model/x-cim +// @Produce model/x-cim.zip +// @Success 200 "OK" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param fileID path int true "ID of the file to download" +// @Router /files/{fileID} [get] +func GetFile(c *gin.Context){ + // TODO +} + +// UpdateFile godoc +// @Summary Update a file +// @ID UpdateFile +// @Tags files +// @Produce json +// @Accept text/plain +// @Accept png +// @Accept jpeg +// @Accept gif +// @Accept model/x-cim +// @Accept model/x-cim.zip +// @Success 200 "OK" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param fileID path int true "ID of the file to update" +// @Router /files/{fileID} [put] +func UpdateFile(c *gin.Context){ + + //TODO parse this info based on fileID parameter + simulationID := 1 + modelID := 1 + widgetID := 1 + + + // Extract file from PUT request form + err := c.Request.ParseForm() + if err != nil { + errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error()) + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return; + } + + 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; + } + + filename := filepath.Base(file_header.Filename) + filetype := file_header.Header.Get("Content-Type") // TODO make sure this is properly set in file header + size := file_header.Size + foldername := getFolderName(simulationID, modelID, widgetID) + + err = modifyFileOnDisc(file_header, filename, foldername, uint(size), false) + if err != nil { + errormsg := fmt.Sprintf("Internal Server Error. Error saving file: %s", err.Error()) + c.JSON(http.StatusInternalServerError, gin.H{ + "error": errormsg, + }) + return + } + + saveFileInDB(c, filename, foldername, filetype, uint(size), widgetID, modelID, false) +} + + +// DeleteFile godoc +// @Summary Delete a file +// @ID DeleteFile +// @Tags files +// @Produce json +// @Success 200 "OK" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param fileID path int true "ID of the file to update" +// @Router /files/{fileID} [delete] +func DeleteFile(c *gin.Context){ + // TODO +} + + func GetFilesOfModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) @@ -55,20 +189,7 @@ func GetFilesOfModel(c *gin.Context) { } -// AddFileToModel godoc -// @Summary Get all parameters of files of model -// @ID AddFileToModel -// @Tags files -// @Accept text/plain -// @Produce json -// @Success 200 "OK." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param modelID path int true "Model ID" -// @Router /simulations/{simulationID}/models/{modelID}/file [post] + func AddFileToModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) @@ -89,19 +210,7 @@ func CloneFileOfModel(c *gin.Context) { } -// GetFileOfModel godoc -// @Summary Download a file that belongs to a model -// @ID GetFileOfModel -// @Tags files -// @Produce text/plain -// @Success 200 "OK, File included in response." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param modelID path int true "Model ID" -// @Router /simulations/{simulationID}/models/{modelID}/file [get] + func GetFileOfModel(c *gin.Context) { simulationID, modelID, err := getRequestParams(c) @@ -113,71 +222,31 @@ func GetFileOfModel(c *gin.Context) { ReadFile(c, -1, modelID, simulationID) } -// UpdateFileOfModel godoc -// @Summary Update (overwrite) a file that belongs to a model -// @ID UpdateFileOfModel -// @Tags files -// @Accept text/plain -// @Produce json -// @Success 200 "OK." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param modelID path int true "Model ID" -// @Router /simulations/{simulationID}/models/{modelID}/file [put] + func UpdateFileOfModel(c *gin.Context) { - simulationID, modelID, err := getRequestParams(c) - if err != nil{ - return - } + //simulationID, modelID, err := getRequestParams(c) + //if err != nil{ + // return + //} // Update file locally and update file entry in DB, HTTP response is set by this method - UpdateFile(c,-1, modelID, simulationID) + //UpdateFile(c,-1, modelID, simulationID) } -// DeleteFileOfModel godoc -// @Summary Delete a file that belongs to a model -// @ID DeleteFileOfModel -// @Tags files -// @Produce json -// @Success 200 "OK." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param modelID path int true "Model ID" -// @Router /simulations/{simulationID}/models/{modelID}/file [delete] func DeleteFileOfModel(c *gin.Context) { - simulationID, modelID, err := getRequestParams(c) - if err != nil{ - return - } + //simulationID, modelID, err := getRequestParams(c) + //if err != nil{ + // return + //} // Delete file from disk and remove entry from DB, HTTP response is set by this method - DeleteFile(c, -1, modelID, simulationID) + //DeleteFile(c, -1, modelID, simulationID) } -// GetFilesOfWidget godoc -// @Summary Get all parameters of files of widget -// @ID GetFilesOfWidget -// @Tags files -// @Produce json -// @Success 200 {array} common.WidgetResponse "File parameters requested by user" -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param visualizationID path int true "Visualization ID" -// @Param widgetID path int true "Widget ID" -// @Router /simulations/{simulationID}/visualizations/{visualizationID}/widgets/{widgetID}/files [get] func GetFilesOfWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) @@ -197,21 +266,7 @@ func GetFilesOfWidget(c *gin.Context) { } -// AddFileToWidget godoc -// @Summary Get all parameters of files of widget -// @ID AddFileToWidget -// @Tags files -// @Accept text/plain -// @Produce json -// @Success 200 "OK." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param visualizationID path int true "Visualization ID" -// @Param widgetID path int true "Widget ID" -// @Router /simulations/{simulationID}/visualizations/{visualizationID}/widgets/{widgetID}/file [post] + func AddFileToWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) @@ -232,20 +287,7 @@ func CloneFileOfWidget(c *gin.Context) { } -// GetFileOfWidget godoc -// @Summary Download a file that belongs to a widget -// @ID GetFileOfWidget -// @Tags files -// @Produce text/plain -// @Success 200 "OK, File included in response." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param visualizationID path int true "Visualization ID" -// @Param widgetID path int true "Widget ID" -// @Router /simulations/{simulationID}/visualizations/{visualizationID}/widgets/{widgetID}/file [get] + func GetFileOfWidget(c *gin.Context) { simulationID, widgetID, err := getRequestParams(c) @@ -257,55 +299,28 @@ func GetFileOfWidget(c *gin.Context) { ReadFile(c, widgetID, -1, simulationID) } -// UpdateFileOfWidget godoc -// @Summary Update (overwrite) a file that belongs to a widget -// @ID UpdateFileOfWidget -// @Tags files -// @Accept text/plain -// @Produce json -// @Success 200 "OK." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param visualizationID path int true "Visualization ID" -// @Param widgetID path int true "Widget ID" -// @Router /simulations/{simulationID}/visualizations/{visualizationID}/widgets/{widgetID}/file [put] + func UpdateFileOfWidget(c *gin.Context) { - simulationID, widgetID, err := getRequestParams(c) - if err != nil{ - return - } - - // Update file locally and update file entry in DB, HTTP response is set by this method - UpdateFile(c,widgetID, -1, simulationID) + //simulationID, widgetID, err := getRequestParams(c) + //if err != nil{ + // return + //} + // + //// Update file locally and update file entry in DB, HTTP response is set by this method + //UpdateFile(c,widgetID, -1, simulationID) } -// DeleteFileOfWidget godoc -// @Summary Delete a file that belongs to a widget -// @ID DeleteFileOfWidget -// @Tags files -// @Produce json -// @Success 200 "OK." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param visualizationID path int true "Visualization ID" -// @Param widgetID path int true "Widget ID" -// @Router /simulations/{simulationID}/visualizations/{visualizationID}/widgets/{widgetID}/file [delete] + func DeleteFileOfWidget(c *gin.Context) { - simulationID, widgetID, err := getRequestParams(c) - if err != nil{ - return - } - - // Delete file from disk and remove entry from DB, HTTP response is set by this method - DeleteFile(c, widgetID, -1, simulationID) + //simulationID, widgetID, err := getRequestParams(c) + //if err != nil{ + // return + //} + // + //// Delete file from disk and remove entry from DB, HTTP response is set by this method + //DeleteFile(c, widgetID, -1, simulationID) } diff --git a/routes/file/fileQueries.go b/routes/file/fileQueries.go index 6bcad30..766296c 100644 --- a/routes/file/fileQueries.go +++ b/routes/file/fileQueries.go @@ -138,43 +138,7 @@ func RegisterFile(c *gin.Context, widgetID int, modelID int, simulationID int){ } -func UpdateFile(c *gin.Context, widgetID int, modelID int, simulationID int){ - // Extract file from PUT request form - err := c.Request.ParseForm() - if err != nil { - errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error()) - c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, - }) - return; - } - - 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; - } - - filename := filepath.Base(file_header.Filename) - filetype := file_header.Header.Get("Content-Type") // TODO make sure this is properly set in file header - size := file_header.Size - foldername := getFolderName(simulationID, modelID, widgetID) - - err = modifyFileOnDisc(file_header, filename, foldername, uint(size), false) - if err != nil { - errormsg := fmt.Sprintf("Internal Server Error. Error saving file: %s", err.Error()) - c.JSON(http.StatusInternalServerError, gin.H{ - "error": errormsg, - }) - return - } - - saveFileInDB(c, filename, foldername, filetype, uint(size), widgetID, modelID, false) -} func ReadFile(c *gin.Context, widgetID int, modelID int, simulationID int){ @@ -223,13 +187,6 @@ func ReadFile(c *gin.Context, widgetID int, modelID int, simulationID int){ } - - -func DeleteFile(c *gin.Context, widgetID int, nmodelID int, simulationID int){ - // TODO -} - - func saveFileInDB(c *gin.Context, filename string, foldername string, filetype string, size uint, widgetID int, modelID int, createObj bool) { filesavepath := filepath.Join(foldername, filename) diff --git a/routes/model/modelEndpoints.go b/routes/model/modelEndpoints.go index f5214e6..84a58ab 100644 --- a/routes/model/modelEndpoints.go +++ b/routes/model/modelEndpoints.go @@ -12,29 +12,30 @@ import ( ) func RegisterModelEndpoints(r *gin.RouterGroup){ - r.GET("/:simulationID/models/", GetModels) - r.POST("/:simulationID/models/", AddModel) - r.POST("/:simulationID/models/:modelID", CloneModel) - r.PUT("/:simulationID/models/:modelID", UpdateModel) - r.GET("/:simulationID/models/:modelID", GetModel) - r.DELETE("/:simulationID/models/:modelID", DeleteModel) - r.PUT("/:simulationID/models/:modelID/simulator", UpdateSimulator) - r.GET("/:simulationID/models/:modelID/simulator", GetSimulator) - r.POST("/:simulationID/models/:modelID/signals/:direction", UpdateSignals) - r.GET("/:simulationID/models/:modelID/signals/:direction", GetSignals) + r.GET("/", GetModels) + r.POST("/", AddModel) + //r.POST("/:modelID", CloneModel) + r.PUT("/:modelID", UpdateModel) + r.GET("/:modelID", GetModel) + r.DELETE("/:modelID", DeleteModel) + //r.PUT("/:modelID/simulator", UpdateSimulator) + //r.GET("/:modelID/simulator", GetSimulator) + //r.POST("/:modelID/signals/:direction", UpdateSignals) + //r.GET("/:modelID/signals/:direction", GetSignals) } // GetModels godoc // @Summary Get all models of simulation // @ID GetModels // @Produce json -// @Tags model +// @Tags models // @Success 200 {array} common.ModelResponse "Array of models to which belong to simulation" // @Failure 401 "Unauthorized Access" // @Failure 403 "Access forbidden." // @Failure 404 "Not found" // @Failure 500 "Internal server error" -// @Router /simulations/{simulationID}/models [get] +// @Param simulationID query int true "Simulation ID" +// @Router /models [get] func GetModels(c *gin.Context) { simID, err := common.GetSimulationID(c) @@ -56,14 +57,16 @@ func GetModels(c *gin.Context) { // AddModel godoc // @Summary Add a model to a simulation // @ID AddModel -// @Tags model -// @Param inputModel body common.ModelResponse true "Model to be added" +// @Accept json +// @Produce json +// @Tags models +// @Param inputModel body common.ModelResponse true "Model to be added incl. IDs of simulation and simulator" // @Success 200 "OK." // @Failure 401 "Unauthorized Access" // @Failure 403 "Access forbidden." // @Failure 404 "Not found" // @Failure 500 "Internal server error" -// @Router /simulations/{simulationID}/models [post] +// @Router /models [post] func AddModel(c *gin.Context) { simID, err := common.GetSimulationID(c) @@ -125,6 +128,20 @@ func CloneModel(c *gin.Context) { } +// UpdateModel godoc +// @Summary Update a model +// @ID UpdateModel +// @Tags models +// @Accept json +// @Produce json +// @Param inputModel body common.ModelResponse true "Model to be updated" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param modelID path int true "Model ID" +// @Router /models/{modelID} [put] func UpdateModel(c *gin.Context) { modelID, err := common.GetModelID(c) @@ -151,6 +168,18 @@ func UpdateModel(c *gin.Context) { } +// GetModel godoc +// @Summary Get a model +// @ID GetModel +// @Tags models +// @Produce json +// @Success 200 {object} common.ModelResponse "Requested model." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param modelID path int true "Model ID" +// @Router /models/{modelID} [get] func GetModel(c *gin.Context) { modelID, err := common.GetModelID(c) @@ -169,6 +198,18 @@ func GetModel(c *gin.Context) { }) } +// DeleteModel godoc +// @Summary Delete a model +// @ID DeleteModel +// @Tags models +// @Produce json +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param modelID path int true "Model ID" +// @Router /models/{modelID} [delete] func DeleteModel(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ diff --git a/routes/model/modelQueries.go b/routes/model/modelQueries.go index 784eb59..d48ddfe 100644 --- a/routes/model/modelQueries.go +++ b/routes/model/modelQueries.go @@ -36,6 +36,11 @@ func (m *Model) addToSimulation(simID int) error { return err } + err = db.Create(m).Error + if err != nil { + return err + } + err = db.Model(&sim).Association("Models").Append(m).Error return err } diff --git a/routes/simulation/simulationEndpoints.go b/routes/simulation/simulationEndpoints.go index bd4b5b9..48be6f7 100644 --- a/routes/simulation/simulationEndpoints.go +++ b/routes/simulation/simulationEndpoints.go @@ -1,6 +1,7 @@ package simulation import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user" "net/http" "github.com/gin-gonic/gin" @@ -11,17 +12,21 @@ import ( func RegisterSimulationEndpoints(r *gin.RouterGroup){ r.GET("/", GetSimulations) r.POST("/", AddSimulation) - r.POST("/:simulationID", CloneSimulation) + //r.POST("/:simulationID", CloneSimulation) r.PUT("/:simulationID", UpdateSimulation) r.GET("/:simulationID", GetSimulation) r.DELETE("/:simulationID", DeleteSimulation) + + r.GET("/:simulationID/users", GetUsersOfSimulation) + r.PUT("/:simulationID/users/:userID", AddUserToSimulation) + r.DELETE("/:simulationID/users/:userID", DeleteUserFromSimulation) } // GetSimulations godoc // @Summary Get all simulations // @ID GetSimulations // @Produce json -// @Tags simulation +// @Tags simulations // @Success 200 {array} common.SimulationResponse "Array of simulations to which user has access" // @Failure 401 "Unauthorized Access" // @Failure 403 "Access forbidden." @@ -39,10 +44,22 @@ func GetSimulations(c *gin.Context) { }) } +// AddSimulation godoc +// @Summary Add a simulation +// @ID AddSimulation +// @Accept json +// @Produce json +// @Tags simulations +// @Param inputModel body common.ModelResponse true "Simulation to be added" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Router /simulations [post] func AddSimulation(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) + + } func CloneSimulation(c *gin.Context) { @@ -51,6 +68,20 @@ func CloneSimulation(c *gin.Context) { }) } +// UpdateSimulation godoc +// @Summary Update a simulation +// @ID UpdateSimulation +// @Tags simulations +// @Accept json +// @Produce json +// @Param inputSimulation body common.SimulationResponse true "Simulation to be updated" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Router /simulations/{simulationID} [put] func UpdateSimulation(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", @@ -61,7 +92,7 @@ func UpdateSimulation(c *gin.Context) { // @Summary Get simulation // @ID GetSimulation // @Produce json -// @Tags simulation +// @Tags simulations // @Success 200 {object} common.SimulationResponse "Simulation requested by user" // @Failure 401 "Unauthorized Access" // @Failure 403 "Access forbidden." @@ -87,6 +118,18 @@ func GetSimulation(c *gin.Context) { }) } +// DeleteSimulation godoc +// @Summary Delete a simulation +// @ID DeleteSimulation +// @Tags simulations +// @Produce json +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Router /simulations/{simulationID} [delete] func DeleteSimulation(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", @@ -94,3 +137,122 @@ func DeleteSimulation(c *gin.Context) { } +// GetUsersOfSimulation godoc +// @Summary Get users of simulation +// @ID GetUsersOfSimulation +// @Produce json +// @Tags simulations +// @Success 200 {array} common.UserResponse "Array of users that have access to the simulation" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Router /simulations/{simulationID}/users/ [get] +func GetUsersOfSimulation(c *gin.Context) { + + simID, err := common.GetSimulationID(c) + if err != nil { + return + } + + sim, err := FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + // Find all users of simulation + allUsers, _, err := user.FindAllUsersSim(&sim) + if common.ProvideErrorResponse(c, err) { + return + } + + serializer := common.UsersSerializer{c, allUsers} + c.JSON(http.StatusOK, gin.H{ + "users": serializer.Response(), + }) +} + + +// AddUserToSimulation godoc +// @Summary Add a user to a asimulation +// @ID AddUserToSimulation +// @Tags simulations +// @Produce json +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param userID path int true "User ID" +// @Router /simulations/{simulationID}/users/{userID} [put] +func AddUserToSimulation(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "NOT implemented", + }) + + simID, err := common.GetSimulationID(c) + if err != nil { + return + } + + sim, err := FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + username := c.Param("username") + + u, err := user.FindUserByName(username) + if common.ProvideErrorResponse(c, err) { + return + } + + err = user.AddUserToSim(&sim, &u) + if common.ProvideErrorResponse(c, err){ + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) +} + +// DeleteUserFromSimulation godoc +// @Summary Delete a user from asimulation +// @ID DeleteUserFromSimulation +// @Tags simulations +// @Produce json +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID path int true "Simulation ID" +// @Param userID path int true "User ID" +// @Router /simulations/{simulationID}/users/{userID} [delete] +func DeleteUserFromSimulation(c *gin.Context) { + simID, err := common.GetSimulationID(c) + if err != nil { + return + } + + sim, err := FindSimulation(simID) + if common.ProvideErrorResponse(c, err) { + return + } + + username := c.Param("username") + + err = user.RemoveUserFromSim(&sim, username) + if common.ProvideErrorResponse(c, err) { + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) +} + + diff --git a/routes/simulator/simulatorEndpoints.go b/routes/simulator/simulatorEndpoints.go index 63d5b94..ccb0127 100644 --- a/routes/simulator/simulatorEndpoints.go +++ b/routes/simulator/simulatorEndpoints.go @@ -14,7 +14,7 @@ func RegisterSimulatorEndpoints(r *gin.RouterGroup){ r.PUT("/:simulatorID", UpdateSimulator) r.GET("/:simulatorID", GetSimulator) r.DELETE("/:simulatorID", DeleteSimulator) - r.POST("/:simulatorID", SendActionToSimulator) + r.POST("/:simulatorID/action", SendActionToSimulator) } // GetSimulators godoc @@ -27,8 +27,6 @@ func RegisterSimulatorEndpoints(r *gin.RouterGroup){ // @Failure 403 "Access forbidden." // @Failure 404 "Not found" // @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param modelID path int true "Model ID" // @Router /simulators [get] func GetSimulators(c *gin.Context) { allSimulators, _, _ := FindAllSimulators() @@ -38,30 +36,95 @@ func GetSimulators(c *gin.Context) { }) } +// AddSimulator godoc +// @Summary Add a simulator +// @ID AddSimulator +// @Accept json +// @Produce json +// @Tags simulators +// @Param inputSimulator body common.SimulatorResponse true "Simulator to be added" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Router /simulators [post] func AddSimulator(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } + +// UpdateSimulator godoc +// @Summary Update a simulator +// @ID UpdateSimulator +// @Tags simulators +// @Accept json +// @Produce json +// @Param inputSimulator body common.SimulatorResponse true "Simulator to be updated" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulatorID path int true "Simulator ID" +// @Router /simulators/{simulatorID} [put] func UpdateSimulator(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } +// GetSimulator godoc +// @Summary Get simulator +// @ID GetSimulator +// @Produce json +// @Tags simulators +// @Success 200 {object} common.SimulatorResponse "Simulator requested by user" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulatorID path int true "Simulator ID" +// @Router /simulators/{simulatorID} [get] func GetSimulator(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } +// DeleteSimulator godoc +// @Summary Delete a simulator +// @ID DeleteSimulator +// @Tags simulators +// @Produce json +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulatorID path int true "Simulator ID" +// @Router /simulators/{simulatorID} [delete] func DeleteSimulator(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } +// SendActionToSimulator godoc +// @Summary Send an action to simulator +// @ID SendActionToSimulator +// @Tags simulators +// @Produce json +// @Param inputAction query string true "Action for simulator" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulatorID path int true "Simulator ID" +// @Router /simulators/{simulatorID}/action [post] func SendActionToSimulator(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", diff --git a/routes/user/userEndpoints.go b/routes/user/userEndpoints.go index eb764db..0dc4b41 100644 --- a/routes/user/userEndpoints.go +++ b/routes/user/userEndpoints.go @@ -6,27 +6,28 @@ 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/simulation" ) - func RegisterUserEndpoints(r *gin.RouterGroup){ r.GET("/", GetUsers) r.POST("/", AddUser) r.PUT("/:userID", UpdateUser) r.GET("/:userID", GetUser) - r.GET("/:userID/simulations", GetSimulationsOfUser) r.DELETE("/:userID", DeleteUser) //r.GET("/me", userSelfEp) // TODO redirect to users/:userID } -func RegisterUserEndpointsForSimulation(r *gin.RouterGroup){ - r.GET("/:simulationID/users", GetUsersOfSimulation) - r.PUT("/:simulationID/user/:username", UpdateUserOfSimulation) - r.DELETE("/:simulationID/user/:username", DeleteUserOfSimulation) - -} - +// GetUsers godoc +// @Summary Get all users +// @ID GetUsers +// @Produce json +// @Tags users +// @Success 200 {array} common.UserResponse "Array of users" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Router /users [get] func GetUsers(c *gin.Context) { allUsers, _, _ := FindAllUsers() serializer := common.UsersSerializer{c, allUsers} @@ -35,146 +36,77 @@ func GetUsers(c *gin.Context) { }) } -// GetUsersOfSimulation godoc -// @Summary Get users of simulation -// @ID GetUsersOfSimulation -// @Produce json -// @Tags user -// @Success 200 {array} common.UserResponse "Array of users that have access to the simulation" +// AddUser godoc +// @Summary Add a user +// @ID AddUser +// @Accept json +// @Produce json +// @Tags users +// @Param inputUser body common.UserResponse true "User to be added" +// @Success 200 "OK." // @Failure 401 "Unauthorized Access" // @Failure 403 "Access forbidden." // @Failure 404 "Not found" // @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Router /simulations/{simulationID}/users [get] -func GetUsersOfSimulation(c *gin.Context) { - - simID, err := common.GetSimulationID(c) - if err != nil { - return - } - - sim, err := simulation.FindSimulation(simID) - if common.ProvideErrorResponse(c, err) { - return - } - - // Find all users of simulation - allUsers, _, err := FindAllUsersSim(&sim) - if common.ProvideErrorResponse(c, err) { - return - } - - serializer := common.UsersSerializer{c, allUsers} - c.JSON(http.StatusOK, gin.H{ - "users": serializer.Response(), - }) -} - +// @Router /users [post] func AddUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } +// UpdateUser godoc +// @Summary Update a user +// @ID UpdateUser +// @Tags users +// @Accept json +// @Produce json +// @Param inputUser body common.UserResponse true "User to be updated" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param userID path int true "User ID" +// @Router /users/{userID} [put] func UpdateUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -// UpdateUserOfSimulation godoc -// @Summary Add user to simulation -// @ID UpdateUserOfSimulation -// @Tags user -// @Success 200 "OK." +// GetUser godoc +// @Summary Get user +// @ID GetUser +// @Produce json +// @Tags users +// @Success 200 {object} common.UserResponse "User requested by user" // @Failure 401 "Unauthorized Access" // @Failure 403 "Access forbidden." // @Failure 404 "Not found" // @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param username path int true "Username of user to be added" -// @Router /simulations/{simulationID}/users/{username} [put] -func UpdateUserOfSimulation(c *gin.Context) { - - - simID, err := common.GetSimulationID(c) - if err != nil { - return - } - - sim, err := simulation.FindSimulation(simID) - if common.ProvideErrorResponse(c, err) { - return - } - - username := c.Param("username") - - user, err := FindUserByName(username) - if common.ProvideErrorResponse(c, err) { - return - } - - err = AddUserToSim(&sim, &user) - if common.ProvideErrorResponse(c, err){ - return - } - - c.JSON(http.StatusOK, gin.H{ - "message": "OK.", - }) -} - +// @Param userID path int true "User ID" +// @Router /users/{userID} [get] func GetUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) } -func GetSimulationsOfUser(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -func DeleteUser(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) -} - -// DeleteUserOfSimulation godoc -// @Summary Delete user from simulation -// @ID DeleteUserOfSimulation -// @Tags user +// DeleteUser godoc +// @Summary Delete a user +// @ID DeleteUser +// @Tags users +// @Produce json // @Success 200 "OK." // @Failure 401 "Unauthorized Access" // @Failure 403 "Access forbidden." // @Failure 404 "Not found" // @Failure 500 "Internal server error" -// @Param simulationID path int true "Simulation ID" -// @Param username path int true "Username of user" -// @Router /simulations/{simulationID}/users/{username} [delete] -func DeleteUserOfSimulation(c *gin.Context) { - - simID, err := common.GetSimulationID(c) - if err != nil { - return - } - - sim, err := simulation.FindSimulation(simID) - if common.ProvideErrorResponse(c, err) { - return - } - - username := c.Param("username") - - err = RemoveUserFromSim(&sim, username) - if common.ProvideErrorResponse(c, err) { - return - } - +// @Param userID path int true "User ID" +// @Router /users/{userID} [delete] +func DeleteUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ - "message": "OK.", + "message": "NOT implemented", }) } diff --git a/routes/visualization/visualizationEndpoints.go b/routes/visualization/visualizationEndpoints.go index 89a9700..0a19230 100644 --- a/routes/visualization/visualizationEndpoints.go +++ b/routes/visualization/visualizationEndpoints.go @@ -11,15 +11,27 @@ import ( func RegisterVisualizationEndpoints(r *gin.RouterGroup){ - r.GET("/:simulationID/visualizations", GetVisualizations) - r.POST("/:simulationID/visualization", AddVisualization) - r.POST("/:simulationID/visualization/:visualizationID", CloneVisualization) - r.PUT("/:simulationID/visualization/:visualizationID", UpdateVisualization) - r.GET("/:simulationID/visualization/:visualizationID", GetVisualization) - r.DELETE("/:simulationID/visualization/:visualizationID", DeleteVisualization) + r.GET("/", GetVisualizations) + r.POST("/", AddVisualization) + //r.POST("/:visualizationID", CloneVisualization) + r.PUT("/:visualizationID", UpdateVisualization) + r.GET("/:visualizationID", GetVisualization) + r.DELETE("/:visualizationID", DeleteVisualization) } +// GetVisualizations godoc +// @Summary Get all visualizations of simulation +// @ID GetVisualizations +// @Produce json +// @Tags visualizations +// @Success 200 {array} common.VisualizationResponse "Array of visualizations to which belong to simulation" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param simulationID query int true "Simulation ID" +// @Router /visualizations [get] func GetVisualizations(c *gin.Context) { simID, err := common.GetSimulationID(c) @@ -39,6 +51,19 @@ func GetVisualizations(c *gin.Context) { }) } +// AddVisualization godoc +// @Summary Add a visualization to a simulation +// @ID AddVisualization +// @Accept json +// @Produce json +// @Tags visualizations +// @Param inputVis body common.VisualizationResponse true "Visualization to be added incl. ID of simulation" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Router /visualizations [post] func AddVisualization(c *gin.Context) { simID, err := common.GetSimulationID(c) @@ -76,6 +101,20 @@ func CloneVisualization(c *gin.Context) { }) } +// UpdateVisualization godoc +// @Summary Update a visualization +// @ID UpdateVisualization +// @Tags visualizations +// @Accept json +// @Produce json +// @Param inputVis body common.VisualizationResponse true "Visualization to be updated" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param visualizationID path int true "Visualization ID" +// @Router /visualizations/{visualizationID} [put] func UpdateVisualization(c *gin.Context) { simID, err := common.GetSimulationID(c) @@ -111,6 +150,18 @@ func UpdateVisualization(c *gin.Context) { } } +// GetVisualization godoc +// @Summary Get a visualization +// @ID GetVisualization +// @Tags visualizations +// @Produce json +// @Success 200 {object} common.VisualizationResponse "Requested visualization." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param visualizationID path int true "Visualization ID" +// @Router /visualizations/{visualizationID} [get] func GetVisualization(c *gin.Context) { simID, err := common.GetSimulationID(c) @@ -139,6 +190,18 @@ func GetVisualization(c *gin.Context) { }) } +// DeleteVisualization godoc +// @Summary Delete a visualization +// @ID DeleteVisualization +// @Tags visualizations +// @Produce json +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param visualizationID path int true "Visualization ID" +// @Router /visualizations/{visualizationID} [delete] func DeleteVisualization(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", diff --git a/routes/widget/widgetEndpoints.go b/routes/widget/widgetEndpoints.go index 59c4f00..be08605 100644 --- a/routes/widget/widgetEndpoints.go +++ b/routes/widget/widgetEndpoints.go @@ -11,14 +11,26 @@ import ( ) func RegisterWidgetEndpoints(r *gin.RouterGroup){ - r.GET("/:simulationID/visualization/:visualizationID/widgets", GetWidgets) - r.POST("/:simulationID/visualization/:visualizationID/widget", AddWidget) - r.POST("/:simulationID/visualization/:visualizationID/widget:widgetID", CloneWidget) - r.PUT("/:simulationID/visualization/:visualizationID/widget/:widgetID", UpdateWidget) - r.GET("/:simulationID/visualization/:visualizationID/widget/:widgetID", GetWidget) - r.DELETE("/:simulationID/visualization/:visualizationID/widget/:widgetID", DeleteWidget) + r.GET("/", GetWidgets) + r.POST("/", AddWidget) + //r.POST("/:widgetID", CloneWidget) + r.PUT("/:widgetID", UpdateWidget) + r.GET("/:widgetID", GetWidget) + r.DELETE("/:widgetID", DeleteWidget) } +// GetWidgets godoc +// @Summary Get all widgets of visualization +// @ID GetWidgets +// @Produce json +// @Tags widgets +// @Success 200 {array} common.WidgetResponse "Array of widgets to which belong to visualization" +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param visualizationID query int true "Visualization ID" +// @Router /widgets [get] func GetWidgets(c *gin.Context) { simID, err := common.GetSimulationID(c) @@ -52,6 +64,19 @@ func GetWidgets(c *gin.Context) { }) } +// AddWidget godoc +// @Summary Add a widget to a visualization +// @ID AddWidget +// @Accept json +// @Produce json +// @Tags widgets +// @Param inputWidget body common.WidgetResponse true "Widget to be added incl. ID of visualization" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Router /widgets [post] func AddWidget(c *gin.Context) { simID, err := common.GetSimulationID(c) @@ -100,6 +125,20 @@ func CloneWidget(c *gin.Context) { }) } +// UpdateWidget godoc +// @Summary Update a widget +// @ID UpdateWidget +// @Tags widgets +// @Accept json +// @Produce json +// @Param inputWidget body common.WidgetResponse true "Widget to be updated" +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param widgetID path int true "Widget ID" +// @Router /widgets/{widgetID} [put] func UpdateWidget(c *gin.Context) { simID, err := common.GetSimulationID(c) if err != nil { @@ -144,6 +183,18 @@ func UpdateWidget(c *gin.Context) { } } +// GetWidget godoc +// @Summary Get a widget +// @ID GetWidget +// @Tags widgets +// @Produce json +// @Success 200 {object} common.WidgetResponse "Requested widget." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param widgetID path int true "Widget ID" +// @Router /widgets/{widgetID} [get] func GetWidget(c *gin.Context) { simID, err := common.GetSimulationID(c) @@ -178,6 +229,19 @@ func GetWidget(c *gin.Context) { }) } + +// DeleteWidget godoc +// @Summary Delete a widget +// @ID DeleteWidget +// @Tags widgets +// @Produce json +// @Success 200 "OK." +// @Failure 401 "Unauthorized Access" +// @Failure 403 "Access forbidden." +// @Failure 404 "Not found" +// @Failure 500 "Internal server error" +// @Param widgetID path int true "Widget ID" +// @Router /widgets/{widgetID} [delete] func DeleteWidget(c *gin.Context) { // simID, err := GetSimulationID(c) diff --git a/start.go b/start.go index e46d5f4..17ea234 100644 --- a/start.go +++ b/start.go @@ -1,13 +1,13 @@ package main import ( + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" "github.com/gin-gonic/gin" "github.com/swaggo/gin-swagger" "github.com/swaggo/gin-swagger/swaggerFiles" - _ "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/doc/autoapi" // apidocs folder is generated by Swag CLI, you have to import it "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" + _ "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/doc/autoapi" // apidocs folder is generated by Swag CLI, you have to import it "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/model" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator" @@ -32,7 +32,7 @@ import ( // @license.url http://www.gnu.de/documents/gpl-3.0.en.html // @host localhost:8080 -// @BasePath /api/v2 +// @BasePath /api func main() { // Testing db := common.InitDB() @@ -45,11 +45,10 @@ func main() { // use ginSwagger middleware to simulation.RegisterSimulationEndpoints(api.Group("/simulations")) - file.RegisterFileEndpoints(api.Group("/simulations")) - model.RegisterModelEndpoints(api.Group("/simulations")) - visualization.RegisterVisualizationEndpoints(api.Group("/simulations")) - widget.RegisterWidgetEndpoints(api.Group("/simulations")) - user.RegisterUserEndpointsForSimulation(api.Group("/simulations")) + model.RegisterModelEndpoints(api.Group("/models")) + visualization.RegisterVisualizationEndpoints(api.Group("/visualizations")) + widget.RegisterWidgetEndpoints(api.Group("/widgets")) + file.RegisterFileEndpoints(api.Group("/files")) user.RegisterUserEndpoints(api.Group("/users")) simulator.RegisterSimulatorEndpoints(api.Group("/simulators")) From 28751c41612bc2a75dd9fc1515c59e65507bc003 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Mon, 27 May 2019 14:55:12 +0200 Subject: [PATCH 27/30] clean up endpoint registration in start and test --- routes/endpoints_test.go | 10 ++++------ start.go | 2 -- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/routes/endpoints_test.go b/routes/endpoints_test.go index 06903b2..4dd9461 100644 --- a/routes/endpoints_test.go +++ b/routes/endpoints_test.go @@ -84,12 +84,10 @@ func TestSimulationEndpoints(t *testing.T) { router := gin.Default() api := router.Group("/api") simulation.RegisterSimulationEndpoints(api.Group("/simulations")) - file.RegisterFileEndpoints(api.Group("/simulations")) - model.RegisterModelEndpoints(api.Group("/simulations")) - visualization.RegisterVisualizationEndpoints(api.Group("/simulations")) - widget.RegisterWidgetEndpoints(api.Group("/simulations")) - user.RegisterUserEndpointsForSimulation(api.Group("/simulations")) // TODO ugly structure - + model.RegisterModelEndpoints(api.Group("/models")) + visualization.RegisterVisualizationEndpoints(api.Group("/visualizations")) + widget.RegisterWidgetEndpoints(api.Group("/widgets")) + file.RegisterFileEndpoints(api.Group("/files")) user.RegisterUserEndpoints(api.Group("/users")) simulator.RegisterSimulatorEndpoints(api.Group("/simulators")) diff --git a/start.go b/start.go index 17ea234..6dab422 100644 --- a/start.go +++ b/start.go @@ -43,13 +43,11 @@ func main() { api := r.Group("/api") - // use ginSwagger middleware to simulation.RegisterSimulationEndpoints(api.Group("/simulations")) model.RegisterModelEndpoints(api.Group("/models")) visualization.RegisterVisualizationEndpoints(api.Group("/visualizations")) widget.RegisterWidgetEndpoints(api.Group("/widgets")) file.RegisterFileEndpoints(api.Group("/files")) - user.RegisterUserEndpoints(api.Group("/users")) simulator.RegisterSimulatorEndpoints(api.Group("/simulators")) From cf1c6e9c18608479f693d33e85ee25bc5268e4a5 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 28 May 2019 09:31:36 +0200 Subject: [PATCH 28/30] fix for simulation endpoints regarding adding and deleting user --- routes/simulation/simulationEndpoints.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/routes/simulation/simulationEndpoints.go b/routes/simulation/simulationEndpoints.go index 48be6f7..60df33e 100644 --- a/routes/simulation/simulationEndpoints.go +++ b/routes/simulation/simulationEndpoints.go @@ -18,8 +18,8 @@ func RegisterSimulationEndpoints(r *gin.RouterGroup){ r.DELETE("/:simulationID", DeleteSimulation) r.GET("/:simulationID/users", GetUsersOfSimulation) - r.PUT("/:simulationID/users/:userID", AddUserToSimulation) - r.DELETE("/:simulationID/users/:userID", DeleteUserFromSimulation) + r.PUT("/:simulationID/users/:username", AddUserToSimulation) + r.DELETE("/:simulationID/users/:username", DeleteUserFromSimulation) } // GetSimulations godoc @@ -175,7 +175,7 @@ func GetUsersOfSimulation(c *gin.Context) { // AddUserToSimulation godoc -// @Summary Add a user to a asimulation +// @Summary Add a user to a a simulation // @ID AddUserToSimulation // @Tags simulations // @Produce json @@ -185,8 +185,8 @@ func GetUsersOfSimulation(c *gin.Context) { // @Failure 404 "Not found" // @Failure 500 "Internal server error" // @Param simulationID path int true "Simulation ID" -// @Param userID path int true "User ID" -// @Router /simulations/{simulationID}/users/{userID} [put] +// @Param username path int true "User name" +// @Router /simulations/{simulationID}/users/{username} [put] func AddUserToSimulation(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", @@ -230,8 +230,8 @@ func AddUserToSimulation(c *gin.Context) { // @Failure 404 "Not found" // @Failure 500 "Internal server error" // @Param simulationID path int true "Simulation ID" -// @Param userID path int true "User ID" -// @Router /simulations/{simulationID}/users/{userID} [delete] +// @Param username path int true "User ID" +// @Router /simulations/{simulationID}/users/{username} [delete] func DeleteUserFromSimulation(c *gin.Context) { simID, err := common.GetSimulationID(c) if err != nil { From 0e6ca2d87761e5e3abc6c0610b3c5574ad9d0c28 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 28 May 2019 09:39:30 +0200 Subject: [PATCH 29/30] make username a query parameter --- go.sum | 1 - routes/endpoints_test.go | 4 ++-- routes/simulation/simulationEndpoints.go | 22 +++++++++++++--------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/go.sum b/go.sum index 2930dee..9d8301b 100644 --- a/go.sum +++ b/go.sum @@ -138,7 +138,6 @@ github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJ github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 h1:BasDe+IErOQKrMVXab7UayvSlIpiyGwRvuX3EKYY7UA= github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 h1:vG/gY/PxA3v3l04qxe3tDjXyu3bozii8ulSlIPOYKhI= github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= diff --git a/routes/endpoints_test.go b/routes/endpoints_test.go index 4dd9461..bcc3d2e 100644 --- a/routes/endpoints_test.go +++ b/routes/endpoints_test.go @@ -121,10 +121,10 @@ func TestSimulationEndpoints(t *testing.T) { testEndpoint(t, router, "/api/simulations/1/users", "GET", "", 200, string(msgUsersjson)) // test DELETE simulations/:SimulationID/user/:username - testEndpoint(t, router, "/api/simulations/1/user/User_A", "DELETE", "", 200, string(msgOKjson)) + testEndpoint(t, router, "/api/simulations/1/user/?username=User_A", "DELETE", "", 200, string(msgOKjson)) // test PUT simulations/:SimulationID/user/:username - testEndpoint(t, router, "/api/simulations/1/user/User_A", "PUT", "", 200, string(msgOKjson)) + testEndpoint(t, router, "/api/simulations/1/user/?username=User_A", "PUT", "", 200, string(msgOKjson)) // TODO add more tests diff --git a/routes/simulation/simulationEndpoints.go b/routes/simulation/simulationEndpoints.go index 60df33e..b5390ce 100644 --- a/routes/simulation/simulationEndpoints.go +++ b/routes/simulation/simulationEndpoints.go @@ -18,8 +18,8 @@ func RegisterSimulationEndpoints(r *gin.RouterGroup){ r.DELETE("/:simulationID", DeleteSimulation) r.GET("/:simulationID/users", GetUsersOfSimulation) - r.PUT("/:simulationID/users/:username", AddUserToSimulation) - r.DELETE("/:simulationID/users/:username", DeleteUserFromSimulation) + r.PUT("/:simulationID/user/", AddUserToSimulation) + r.DELETE("/:simulationID/user/", DeleteUserFromSimulation) } // GetSimulations godoc @@ -185,8 +185,8 @@ func GetUsersOfSimulation(c *gin.Context) { // @Failure 404 "Not found" // @Failure 500 "Internal server error" // @Param simulationID path int true "Simulation ID" -// @Param username path int true "User name" -// @Router /simulations/{simulationID}/users/{username} [put] +// @Param username query string true "User name" +// @Router /simulations/{simulationID}/user [put] func AddUserToSimulation(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", @@ -202,7 +202,9 @@ func AddUserToSimulation(c *gin.Context) { return } - username := c.Param("username") + querydata := c.Request.URL.Query() + + username := querydata.Get("username") u, err := user.FindUserByName(username) if common.ProvideErrorResponse(c, err) { @@ -220,7 +222,7 @@ func AddUserToSimulation(c *gin.Context) { } // DeleteUserFromSimulation godoc -// @Summary Delete a user from asimulation +// @Summary Delete a user from a simulation // @ID DeleteUserFromSimulation // @Tags simulations // @Produce json @@ -230,8 +232,8 @@ func AddUserToSimulation(c *gin.Context) { // @Failure 404 "Not found" // @Failure 500 "Internal server error" // @Param simulationID path int true "Simulation ID" -// @Param username path int true "User ID" -// @Router /simulations/{simulationID}/users/{username} [delete] +// @Param username query string true "User name" +// @Router /simulations/{simulationID}/user [delete] func DeleteUserFromSimulation(c *gin.Context) { simID, err := common.GetSimulationID(c) if err != nil { @@ -243,7 +245,9 @@ func DeleteUserFromSimulation(c *gin.Context) { return } - username := c.Param("username") + querydata := c.Request.URL.Query() + + username := querydata.Get("username") err = user.RemoveUserFromSim(&sim, username) if common.ProvideErrorResponse(c, err) { From 97701b6faa9f4ddb92c411e0c158d817212e34a7 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 28 May 2019 09:53:10 +0200 Subject: [PATCH 30/30] fix simulation endpoint testing --- .gitlab-ci.yml | 2 +- go.sum | 1 + routes/simulation/simulationEndpoints.go | 8 ++---- .../simulation_test.go} | 27 +++++-------------- 4 files changed, 10 insertions(+), 28 deletions(-) rename routes/{endpoints_test.go => simulation/simulation_test.go} (64%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e42323d..67de5d3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -84,7 +84,7 @@ test:backend:endpoints: - go mod tidy - go get -u github.com/swaggo/swag/cmd/swag - ~/go/bin/swag init -p pascalcase -g "start.go" -o "./doc/autoapi/" - - cd routes + - cd routes/simulation - go test -v -args -dbhost=/var/run/postgresql dependencies: - build:backend diff --git a/go.sum b/go.sum index 9d8301b..2930dee 100644 --- a/go.sum +++ b/go.sum @@ -138,6 +138,7 @@ github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJ github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 h1:BasDe+IErOQKrMVXab7UayvSlIpiyGwRvuX3EKYY7UA= github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 h1:vG/gY/PxA3v3l04qxe3tDjXyu3bozii8ulSlIPOYKhI= github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA= diff --git a/routes/simulation/simulationEndpoints.go b/routes/simulation/simulationEndpoints.go index b5390ce..a2e52ba 100644 --- a/routes/simulation/simulationEndpoints.go +++ b/routes/simulation/simulationEndpoints.go @@ -18,8 +18,8 @@ func RegisterSimulationEndpoints(r *gin.RouterGroup){ r.DELETE("/:simulationID", DeleteSimulation) r.GET("/:simulationID/users", GetUsersOfSimulation) - r.PUT("/:simulationID/user/", AddUserToSimulation) - r.DELETE("/:simulationID/user/", DeleteUserFromSimulation) + r.PUT("/:simulationID/user", AddUserToSimulation) + r.DELETE("/:simulationID/user", DeleteUserFromSimulation) } // GetSimulations godoc @@ -188,10 +188,6 @@ func GetUsersOfSimulation(c *gin.Context) { // @Param username query string true "User name" // @Router /simulations/{simulationID}/user [put] func AddUserToSimulation(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", - }) - simID, err := common.GetSimulationID(c) if err != nil { return diff --git a/routes/endpoints_test.go b/routes/simulation/simulation_test.go similarity index 64% rename from routes/endpoints_test.go rename to routes/simulation/simulation_test.go index bcc3d2e..fb9c9b3 100644 --- a/routes/endpoints_test.go +++ b/routes/simulation/simulation_test.go @@ -1,4 +1,4 @@ -package routes +package simulation import ( "encoding/json" @@ -11,13 +11,6 @@ import ( "github.com/stretchr/testify/assert" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/model" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget" ) @@ -83,13 +76,7 @@ func TestSimulationEndpoints(t *testing.T) { router := gin.Default() api := router.Group("/api") - simulation.RegisterSimulationEndpoints(api.Group("/simulations")) - model.RegisterModelEndpoints(api.Group("/models")) - visualization.RegisterVisualizationEndpoints(api.Group("/visualizations")) - widget.RegisterWidgetEndpoints(api.Group("/widgets")) - file.RegisterFileEndpoints(api.Group("/files")) - user.RegisterUserEndpoints(api.Group("/users")) - simulator.RegisterSimulatorEndpoints(api.Group("/simulators")) + RegisterSimulationEndpoints(api.Group("/simulations")) msgOKjson, err := json.Marshal(msgOK) if err !=nil { @@ -120,15 +107,13 @@ func TestSimulationEndpoints(t *testing.T) { // test GET simulations/:SimulationID/users testEndpoint(t, router, "/api/simulations/1/users", "GET", "", 200, string(msgUsersjson)) - // test DELETE simulations/:SimulationID/user/:username - testEndpoint(t, router, "/api/simulations/1/user/?username=User_A", "DELETE", "", 200, string(msgOKjson)) - - // test PUT simulations/:SimulationID/user/:username - testEndpoint(t, router, "/api/simulations/1/user/?username=User_A", "PUT", "", 200, string(msgOKjson)) + // test DELETE simulations/:SimulationID/user + testEndpoint(t, router, "/api/simulations/1/user?username=User_A", "DELETE", "", 200, string(msgOKjson)) + // test PUT simulations/:SimulationID/user + testEndpoint(t, router, "/api/simulations/1/user?username=User_A", "PUT", "", 200, string(msgOKjson)) // TODO add more tests - }