mirror of
https://git.rwth-aachen.de/acs/public/villas/web-backend-go/
synced 2025-03-30 00:00:12 +01:00
- add testing for simulationmodel endpoints
- add Signal serializer - Use Signals DB table again - remove / in some endpoint definitions
This commit is contained in:
parent
15114edbc6
commit
5ca6281a22
17 changed files with 576 additions and 167 deletions
|
@ -91,6 +91,8 @@ test:backend:endpoints:
|
|||
- ~/go/bin/swag init -p pascalcase -g "start.go" -o "./doc/api/"
|
||||
- cd routes/simulation
|
||||
- go test -v -args -dbhost=/var/run/postgresql
|
||||
- cd ../simulationmodel
|
||||
- go test -v -args -dbhost=/var/run/postgresql
|
||||
dependencies:
|
||||
- build:backend
|
||||
|
||||
|
|
|
@ -54,7 +54,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(&SimulationModel{})
|
||||
db.DropTableIfExists(&File{})
|
||||
db.DropTableIfExists(&Simulation{})
|
||||
|
@ -66,7 +66,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(&SimulationModel{})
|
||||
db.AutoMigrate(&File{})
|
||||
db.AutoMigrate(&Simulation{})
|
||||
|
@ -101,14 +101,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", Index: 0, Unit: "V"}
|
||||
outSig_B := Signal{Name: "outSignal_B", Direction: "out", Index: 1, Unit: "V"}
|
||||
inSig_A := Signal{Name: "inSignal_A", Direction: "in", Index: 0, Unit: "A"}
|
||||
inSig_B := Signal{Name: "inSignal_B", Direction: "in", Index: 1, Unit: "A"}
|
||||
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 := SimulationModel{Name: "SimulationModel_A"}
|
||||
mo_B := SimulationModel{Name: "SimulationModel_B"}
|
||||
|
@ -179,11 +179,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)
|
||||
|
||||
// SimulationModel 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)
|
||||
// SimulationModel HM Signals
|
||||
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)
|
||||
|
||||
// SimulationModel HM Files
|
||||
checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_A).Error)
|
||||
|
@ -191,6 +191,7 @@ func DummyPopulateDB(test_db *gorm.DB) {
|
|||
|
||||
// Simulator BT SimulationModel
|
||||
checkErr(test_db.Model(&mo_A).Association("Simulator").Append(&simr_A).Error)
|
||||
checkErr(test_db.Model(&mo_B).Association("Simulator").Append(&simr_A).Error)
|
||||
|
||||
// Widget HM Files
|
||||
checkErr(test_db.Model(&widg_A).Association("Files").Append(&file_A).Error)
|
||||
|
|
|
@ -37,7 +37,7 @@ func TestDummyDBAssociations(t *testing.T) {
|
|||
var vis Visualization
|
||||
var widg Widget
|
||||
|
||||
//var sigs []Signal
|
||||
var sigs []Signal
|
||||
var mos []SimulationModel
|
||||
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("SimulationModel Associations",
|
||||
"Expected to have %v Output Signals. Has %v.", 2, len(sigs))
|
||||
}
|
||||
|
||||
a.NoError(db.Model(&mo).Related(&files_sm, "Files").Error)
|
||||
if len(files_sm) != 2 {
|
||||
|
|
|
@ -57,14 +57,17 @@ type SimulationModel struct {
|
|||
// ID of simulator associated with simulation model
|
||||
SimulatorID uint
|
||||
// Mapping of output signals of the simulation model, order of signals is important
|
||||
OutputMapping []Signal
|
||||
OutputMapping []Signal `gorm:"foreignkey:SimulationModelID"`
|
||||
// Mapping of input signals of the simulation model, order of signals is important
|
||||
InputMapping []Signal
|
||||
InputMapping []Signal `gorm:"foreignkey:SimulationModelID"`
|
||||
// Files of simulation model (can be CIM and other simulation model file formats)
|
||||
Files []File `gorm:"foreignkey:ModelID"`
|
||||
Files []File `gorm:"foreignkey:SimulationModelID"`
|
||||
}
|
||||
|
||||
// Signal data model
|
||||
type Signal struct {
|
||||
// ID of simulation model
|
||||
ID uint `gorm:"primary_key;auto_increment"`
|
||||
// Name of Signal
|
||||
Name string
|
||||
// Unit of Signal
|
||||
|
@ -73,6 +76,8 @@ type Signal struct {
|
|||
Index uint
|
||||
// Direction of the signal (in or out)
|
||||
Direction string
|
||||
// ID of simulation model
|
||||
SimulationModelID uint
|
||||
}
|
||||
|
||||
// Simulator data model
|
||||
|
@ -162,7 +167,7 @@ type File struct {
|
|||
// Last modification time of file
|
||||
Date time.Time
|
||||
// ID of model to which file belongs
|
||||
ModelID uint `gorm:""`
|
||||
SimulationModelID uint `gorm:""`
|
||||
// ID of widget to which file belongs
|
||||
WidgetID uint `gorm:""`
|
||||
}
|
||||
|
|
|
@ -18,14 +18,13 @@ type SimulationResponse struct {
|
|||
}
|
||||
|
||||
type SimulationModelResponse 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"`
|
||||
ID uint `json:"ID"`
|
||||
Name string `json:"Name"`
|
||||
OutputLength int `json:"OutputLength"`
|
||||
InputLength int `json:"InputLength"`
|
||||
SimulationID uint `json:"SimulationID"`
|
||||
SimulatorID uint `json:"SimulatorID"`
|
||||
StartParams string `json:"StartParams"`
|
||||
}
|
||||
|
||||
type SimulatorResponse struct {
|
||||
|
@ -71,6 +70,14 @@ type FileResponse struct {
|
|||
Date time.Time `json:"Date"`
|
||||
}
|
||||
|
||||
type SignalResponse struct {
|
||||
Name string `json:"Name"`
|
||||
Unit string `json:"Unit"`
|
||||
Index uint `json:"Index"`
|
||||
Direction string `json:"Direction"`
|
||||
SimulationModelID uint `json:"SimulationModelID"`
|
||||
}
|
||||
|
||||
// Response messages
|
||||
|
||||
type ResponseMsg struct {
|
||||
|
@ -92,3 +99,15 @@ type ResponseMsgSimulations struct {
|
|||
type ResponseMsgSimulation struct {
|
||||
Simulation SimulationResponse `json:"simulation"`
|
||||
}
|
||||
|
||||
type ResponseMsgSimulationModels struct {
|
||||
SimulationModels []SimulationModelResponse `json:"models"`
|
||||
}
|
||||
|
||||
type ResponseMsgSimulationModel struct {
|
||||
SimulationModel SimulationModelResponse `json:"model"`
|
||||
}
|
||||
|
||||
type ResponseMsgSignals struct {
|
||||
Signals []SignalResponse `json:"signals"`
|
||||
}
|
||||
|
|
|
@ -104,14 +104,13 @@ type SimulationModelSerializer struct {
|
|||
|
||||
func (self *SimulationModelSerializer) Response() SimulationModelResponse {
|
||||
response := SimulationModelResponse{
|
||||
ID: self.ID,
|
||||
Name: self.Name,
|
||||
OutputLength: self.OutputLength,
|
||||
InputLength: self.InputLength,
|
||||
SimulationID: self.SimulationID,
|
||||
SimulatorID: self.SimulatorID,
|
||||
StartParams: self.StartParameters,
|
||||
//InputMapping
|
||||
//OutputMapping
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
@ -255,3 +254,35 @@ func (self *FileSerializerNoAssoc) Response() FileResponse {
|
|||
}
|
||||
return response
|
||||
}
|
||||
|
||||
// Signal/s Serializers
|
||||
type SignalsSerializer struct {
|
||||
Ctx *gin.Context
|
||||
Signals []Signal
|
||||
}
|
||||
|
||||
func (self *SignalsSerializer) Response() []SignalResponse {
|
||||
response := []SignalResponse{}
|
||||
for _, s := range self.Signals {
|
||||
serializer := SignalSerializer{self.Ctx, s}
|
||||
response = append(response, serializer.Response())
|
||||
}
|
||||
return response
|
||||
|
||||
}
|
||||
|
||||
type SignalSerializer struct {
|
||||
Ctx *gin.Context
|
||||
Signal
|
||||
}
|
||||
|
||||
func (self *SignalSerializer) Response() SignalResponse {
|
||||
response := SignalResponse{
|
||||
Name: self.Name,
|
||||
Unit: self.Unit,
|
||||
Direction: self.Direction,
|
||||
SimulationModelID: self.SimulationModelID,
|
||||
Index: self.Index,
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
)
|
||||
|
||||
func RegisterFileEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("/", getFiles)
|
||||
r.POST("/", addFile)
|
||||
r.GET("", getFiles)
|
||||
r.POST("", addFile)
|
||||
r.GET("/:fileID", getFile)
|
||||
r.PUT("/:fileID", updateFile)
|
||||
r.DELETE("/:fileID", deleteFile)
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
)
|
||||
|
||||
func RegisterSimulationEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("/", getSimulations)
|
||||
r.POST("/", addSimulation)
|
||||
r.GET("", getSimulations)
|
||||
r.POST("", addSimulation)
|
||||
r.PUT("/:simulationID", updateSimulation)
|
||||
r.GET("/:simulationID", getSimulation)
|
||||
r.DELETE("/:simulationID", deleteSimulation)
|
||||
|
|
|
@ -77,42 +77,38 @@ func (s *Simulation) deleteUser(username string) error {
|
|||
|
||||
func (s *Simulation) delete() error {
|
||||
db := common.GetDB()
|
||||
no_models := db.Model(s).Association("SimulationModels").Count()
|
||||
no_visualizations := db.Model(s).Association("Visualizations").Count()
|
||||
|
||||
if no_models > 0 || no_visualizations > 0 {
|
||||
return fmt.Errorf("cannot delete simulation that contains models and/ or visualizations, doing nothing")
|
||||
} else {
|
||||
// delete simulation from all users and vice versa
|
||||
// delete simulation from all users and vice versa
|
||||
|
||||
users, no_users, err := s.getUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
users, no_users, err := s.getUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if no_users > 0 {
|
||||
for _, u := range users {
|
||||
// remove user from simulation
|
||||
err = db.Model(s).Association("Users").Delete(&u).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// remove simulation from user
|
||||
err = db.Model(&u).Association("Simulations").Delete(s).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if no_users > 0 {
|
||||
for _, u := range users {
|
||||
// remove user from simulation
|
||||
err = db.Model(s).Association("Users").Delete(&u).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// remove simulation from user
|
||||
err = db.Model(&u).Association("Simulations").Delete(s).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete simulation
|
||||
err = db.Delete(s).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Simulation is not deleted from DB, only associations with users are removed
|
||||
// Simulation remains "dangling" in DB
|
||||
|
||||
// Delete simulation
|
||||
//err = db.Delete(s).Error
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -147,17 +147,17 @@ func TestSimulationEndpoints(t *testing.T) {
|
|||
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
|
||||
|
||||
// test GET simulations/
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/", "GET", nil, 200, string(msgSimulationsjson))
|
||||
common.TestEndpoint(t, router, token, "/api/simulations", "GET", nil, 200, string(msgSimulationsjson))
|
||||
|
||||
// test POST simulations/
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/", "POST", simulationCjson, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/simulations", "POST", simulationCjson, 200, string(msgOKjson))
|
||||
|
||||
// test GET simulations/:SimulationID
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/3", "GET", nil, 200, string(msgSimulationjson))
|
||||
|
||||
// test DELETE simulations/:SimulationID
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/3", "DELETE", nil, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/", "GET", nil, 200, string(msgSimulationsjson))
|
||||
common.TestEndpoint(t, router, token, "/api/simulations", "GET", nil, 200, string(msgSimulationsjson))
|
||||
|
||||
// test GET simulations/:SimulationID/users
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/1/users", "GET", nil, 200, string(msgUsersjson))
|
||||
|
|
|
@ -9,15 +9,15 @@ import (
|
|||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
|
||||
)
|
||||
|
||||
func RegisterModelEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("/", getSimulationModels)
|
||||
r.POST("/", addSimulationModel)
|
||||
func RegisterSimulationModelEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("", getSimulationModels)
|
||||
r.POST("", addSimulationModel)
|
||||
//r.POST("/:modelID", cloneSimulationModel)
|
||||
r.PUT("/:modelID", updateSimulationModel)
|
||||
r.GET("/:modelID", getSimulationModel)
|
||||
r.DELETE("/:modelID", deleteSimulationModel)
|
||||
r.GET("/:modelID/signals", getSignals)
|
||||
r.POST("/:modelID/signals", addSignal)
|
||||
r.PUT("/:modelID/signals", addSignal)
|
||||
r.DELETE("/:modelID/signals", deleteSignals)
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ func addSimulationModel(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
err = newModel.addToSimulation(newModel.SimulationID)
|
||||
err = newModel.addToSimulation()
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
|
@ -203,63 +203,19 @@ func getSimulationModel(c *gin.Context) {
|
|||
// @Router /models/{modelID} [delete]
|
||||
func deleteSimulationModel(c *gin.Context) {
|
||||
|
||||
//ok, m := checkPermissions(c, common.Delete)
|
||||
//if !ok {
|
||||
// return
|
||||
//}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Not implemented.",
|
||||
})
|
||||
}
|
||||
|
||||
// AddSignal godoc
|
||||
// @Summary Add a signal to a signal mapping of a model
|
||||
// @ID AddSignal
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags models
|
||||
// @Param inputSignal body common.Signal true "A signal to be added to the model"
|
||||
// @Param direction query string true "Direction of signal (in or out)"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /models/{modelID}/signals [post]
|
||||
func addSignal(c *gin.Context) {
|
||||
|
||||
ok, m := checkPermissions(c, common.Update)
|
||||
ok, m := checkPermissions(c, common.Delete)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
direction := c.Request.URL.Query().Get("direction")
|
||||
if !(direction == "out") && !(direction == "in") {
|
||||
errormsg := "Bad request. Direction has to be in or out"
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
err := m.delete()
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
var sig common.Signal
|
||||
err := c.BindJSON(&sig)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Add signal to model
|
||||
err = m.addSignal(sig, direction)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
|
||||
// getSignals godoc
|
||||
|
@ -281,8 +237,13 @@ func getSignals(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
direction := c.Param("direction")
|
||||
if !(direction == "out") && !(direction == "in") {
|
||||
var mapping string
|
||||
direction := c.Request.URL.Query().Get("direction")
|
||||
if direction == "in" {
|
||||
mapping = "InputMapping"
|
||||
} else if direction == "out" {
|
||||
mapping = "OutputMapping"
|
||||
} else {
|
||||
errormsg := "Bad request. Direction has to be in or out"
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
|
@ -290,18 +251,58 @@ func getSignals(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
var signals []common.Signal
|
||||
if direction == "in" {
|
||||
signals = m.InputMapping
|
||||
} else {
|
||||
signals = m.OutputMapping
|
||||
db := common.GetDB()
|
||||
var sigs []common.Signal
|
||||
err := db.Order("ID asc").Model(m).Where("Direction = ?", direction).Related(&sigs, mapping).Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.SignalsSerializer{c, sigs}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"signals": signals,
|
||||
"signals": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// AddSignal godoc
|
||||
// @Summary Add a signal to a signal mapping of a model
|
||||
// @ID AddSignal
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags models
|
||||
// @Param inputSignal body common.Signal true "A signal to be added to the model incl. direction"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /models/{modelID}/signals [put]
|
||||
func addSignal(c *gin.Context) {
|
||||
|
||||
ok, m := checkPermissions(c, common.Update)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var sig common.Signal
|
||||
err := c.BindJSON(&sig)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Add signal to model
|
||||
err = m.addSignal(sig)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// deleteSignals godoc
|
||||
// @Summary Delete all signals of a direction
|
||||
// @ID deleteSignals
|
||||
|
@ -322,7 +323,7 @@ func deleteSignals(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
direction := c.Param("direction")
|
||||
direction := c.Request.URL.Query().Get("direction")
|
||||
if !(direction == "out") && !(direction == "in") {
|
||||
errormsg := "Bad request. Direction has to be in or out"
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
|
|
|
@ -27,10 +27,10 @@ func (m *SimulationModel) ByID(id uint) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *SimulationModel) addToSimulation(simID uint) error {
|
||||
func (m *SimulationModel) addToSimulation() error {
|
||||
db := common.GetDB()
|
||||
var sim simulation.Simulation
|
||||
err := sim.ByID(simID)
|
||||
err := sim.ByID(m.SimulationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -54,55 +54,70 @@ func (m *SimulationModel) addToSimulation(simID uint) error {
|
|||
|
||||
func (m *SimulationModel) update(modifiedSimulationModel SimulationModel) error {
|
||||
db := common.GetDB()
|
||||
err := db.Model(m).Update(modifiedSimulationModel).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if m.SimulatorID != modifiedSimulationModel.SimulatorID {
|
||||
// update simulator
|
||||
var s simulator.Simulator
|
||||
err = s.ByID(modifiedSimulationModel.SimulatorID)
|
||||
|
||||
err := s.ByID(modifiedSimulationModel.SimulatorID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = db.Model(m).Association("Simulator").Replace(s).Error
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *SimulationModel) 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
|
||||
err := db.Model(m).Updates(map[string]interface{}{"Name": modifiedSimulationModel.Name,
|
||||
"OutputLength": modifiedSimulationModel.OutputLength,
|
||||
"InputLength": modifiedSimulationModel.InputLength,
|
||||
"StartParameters": modifiedSimulationModel.StartParameters,
|
||||
"SimulatorID": modifiedSimulationModel.SimulatorID,
|
||||
}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *SimulationModel) addSignal(signal common.Signal, direction string) error {
|
||||
func (m *SimulationModel) delete() error {
|
||||
|
||||
db := common.GetDB()
|
||||
var sim simulation.Simulation
|
||||
err := sim.ByID(m.SimulationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove association between SimulationModel and Simulation
|
||||
// SimulationModel itself is not deleted from DB, it remains as "dangling"
|
||||
err = db.Model(&sim).Association("SimulationModels").Delete(m).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *SimulationModel) addSignal(signal common.Signal) error {
|
||||
|
||||
db := common.GetDB()
|
||||
var err error
|
||||
|
||||
if direction == "in" {
|
||||
if signal.Direction == "in" {
|
||||
err = db.Model(m).Association("InputMapping").Append(signal).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// adapt length of mapping
|
||||
m.InputLength = db.Model(m).Association("InputMapping").Count()
|
||||
m.InputLength = db.Model(m).Where("Direction = ?", "in").Association("InputMapping").Count()
|
||||
err = m.update(*m)
|
||||
|
||||
} else {
|
||||
err = db.Model(m).Association("OutputMapping").Append(signal).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// adapt length of mapping
|
||||
m.OutputLength = db.Model(m).Association("OutputMapping").Count()
|
||||
m.OutputLength = db.Model(m).Where("Direction = ?", "out").Association("OutputMapping").Count()
|
||||
err = m.update(*m)
|
||||
|
||||
}
|
||||
|
||||
|
@ -124,7 +139,7 @@ func (m *SimulationModel) deleteSignals(direction string) error {
|
|||
}
|
||||
|
||||
var signals []common.Signal
|
||||
err = db.Order("ID asc").Model(m).Related(&signals, columnName).Error
|
||||
err = db.Order("ID asc").Model(m).Where("Direction = ?", direction).Related(&signals, columnName).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -147,6 +162,7 @@ func (m *SimulationModel) deleteSignals(direction string) error {
|
|||
} else {
|
||||
m.OutputLength = 0
|
||||
}
|
||||
err = m.update(*m)
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
338
routes/simulationmodel/simulationmodel_test.go
Normal file
338
routes/simulationmodel/simulationmodel_test.go
Normal file
|
@ -0,0 +1,338 @@
|
|||
package simulationmodel
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"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/user"
|
||||
)
|
||||
|
||||
var token string
|
||||
|
||||
type credentials struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
var cred = credentials{
|
||||
Username: "User_A",
|
||||
Password: "abc123",
|
||||
}
|
||||
|
||||
var msgOK = common.ResponseMsg{
|
||||
Message: "OK.",
|
||||
}
|
||||
|
||||
var modelA = common.SimulationModelResponse{
|
||||
ID: 1,
|
||||
Name: "SimulationModel_A",
|
||||
OutputLength: 1,
|
||||
InputLength: 1,
|
||||
SimulationID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParams: "",
|
||||
}
|
||||
|
||||
var modelAUpdated = common.SimulationModelResponse{
|
||||
ID: 1,
|
||||
Name: "SimulationModel_A",
|
||||
OutputLength: 1,
|
||||
InputLength: 3,
|
||||
SimulationID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParams: "",
|
||||
}
|
||||
|
||||
var modelAUpdated2 = common.SimulationModelResponse{
|
||||
ID: 1,
|
||||
Name: "SimulationModel_A",
|
||||
OutputLength: 1,
|
||||
InputLength: 0,
|
||||
SimulationID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParams: "",
|
||||
}
|
||||
|
||||
var modelB = common.SimulationModelResponse{
|
||||
ID: 2,
|
||||
Name: "SimulationModel_B",
|
||||
OutputLength: 1,
|
||||
InputLength: 1,
|
||||
SimulationID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParams: "",
|
||||
}
|
||||
|
||||
var modelC = common.SimulationModel{
|
||||
ID: 3,
|
||||
Name: "SimulationModel_C",
|
||||
OutputLength: 1,
|
||||
InputLength: 1,
|
||||
SimulationID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParameters: "test",
|
||||
InputMapping: nil,
|
||||
OutputMapping: nil,
|
||||
}
|
||||
|
||||
var modelCupdated = common.SimulationModel{
|
||||
ID: modelC.ID,
|
||||
Name: "SimulationModel_CUpdated",
|
||||
OutputLength: modelC.OutputLength,
|
||||
InputLength: modelC.InputLength,
|
||||
SimulationID: modelC.SimulationID,
|
||||
SimulatorID: 2,
|
||||
StartParameters: modelC.StartParameters,
|
||||
InputMapping: modelC.InputMapping,
|
||||
OutputMapping: modelC.OutputMapping,
|
||||
}
|
||||
|
||||
var modelC_response = common.SimulationModelResponse{
|
||||
ID: modelC.ID,
|
||||
Name: modelC.Name,
|
||||
InputLength: modelC.InputLength,
|
||||
OutputLength: modelC.OutputLength,
|
||||
SimulationID: modelC.SimulationID,
|
||||
SimulatorID: modelC.SimulatorID,
|
||||
StartParams: modelC.StartParameters,
|
||||
}
|
||||
|
||||
var modelC_responseUpdated = common.SimulationModelResponse{
|
||||
ID: modelC.ID,
|
||||
Name: modelCupdated.Name,
|
||||
InputLength: modelC.InputLength,
|
||||
OutputLength: modelC.OutputLength,
|
||||
SimulationID: modelC.SimulationID,
|
||||
SimulatorID: modelCupdated.SimulatorID,
|
||||
StartParams: modelC.StartParameters,
|
||||
}
|
||||
|
||||
var myModels = []common.SimulationModelResponse{
|
||||
modelA,
|
||||
modelB,
|
||||
}
|
||||
|
||||
var msgModels = common.ResponseMsgSimulationModels{
|
||||
SimulationModels: myModels,
|
||||
}
|
||||
|
||||
var msgModel = common.ResponseMsgSimulationModel{
|
||||
SimulationModel: modelC_response,
|
||||
}
|
||||
|
||||
var msgModelAUpdated = common.ResponseMsgSimulationModel{
|
||||
SimulationModel: modelAUpdated,
|
||||
}
|
||||
|
||||
var msgModelAUpdated2 = common.ResponseMsgSimulationModel{
|
||||
SimulationModel: modelAUpdated2,
|
||||
}
|
||||
|
||||
var msgModelupdated = common.ResponseMsgSimulationModel{
|
||||
SimulationModel: modelC_responseUpdated,
|
||||
}
|
||||
|
||||
var inSignalA = common.SignalResponse{
|
||||
Name: "inSignal_A",
|
||||
Direction: "in",
|
||||
Index: 0,
|
||||
Unit: "A",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var inSignalB = common.SignalResponse{
|
||||
Name: "inSignal_B",
|
||||
Direction: "in",
|
||||
Index: 1,
|
||||
Unit: "A",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var inSignalC = common.SignalResponse{
|
||||
Name: "inSignal_C",
|
||||
Direction: "in",
|
||||
Index: 2,
|
||||
Unit: "A",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var outSignalA = common.SignalResponse{
|
||||
Name: "outSignal_A",
|
||||
Direction: "out",
|
||||
Index: 0,
|
||||
Unit: "V",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var outSignalB = common.SignalResponse{
|
||||
Name: "outSignal_B",
|
||||
Direction: "out",
|
||||
Index: 1,
|
||||
Unit: "V",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var myInSignals = []common.SignalResponse{
|
||||
inSignalA,
|
||||
inSignalB,
|
||||
}
|
||||
|
||||
var myInSignalsUpdated = []common.SignalResponse{
|
||||
inSignalA,
|
||||
inSignalB,
|
||||
inSignalC,
|
||||
}
|
||||
|
||||
var myOutSignals = []common.SignalResponse{
|
||||
outSignalA,
|
||||
outSignalB,
|
||||
}
|
||||
|
||||
var msgSignalsEmpty = common.ResponseMsgSignals{
|
||||
Signals: []common.SignalResponse{},
|
||||
}
|
||||
|
||||
var msgInSignals = common.ResponseMsgSignals{
|
||||
Signals: myInSignals,
|
||||
}
|
||||
|
||||
var msgInSignalsUpdated = common.ResponseMsgSignals{
|
||||
Signals: myInSignalsUpdated,
|
||||
}
|
||||
|
||||
var msgOutSignals = common.ResponseMsgSignals{
|
||||
Signals: myOutSignals,
|
||||
}
|
||||
|
||||
// Test /models endpoints
|
||||
func TestSimulationModelEndpoints(t *testing.T) {
|
||||
|
||||
db := common.DummyInitDB()
|
||||
defer db.Close()
|
||||
common.DummyPopulateDB(db)
|
||||
|
||||
router := gin.Default()
|
||||
api := router.Group("/api")
|
||||
|
||||
// All endpoints require authentication except when someone wants to
|
||||
// login (POST /authenticate)
|
||||
user.VisitorAuthenticate(api.Group("/authenticate"))
|
||||
|
||||
api.Use(user.Authentication(true))
|
||||
|
||||
RegisterSimulationModelEndpoints(api.Group("/models"))
|
||||
|
||||
credjson, err := json.Marshal(cred)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgOKjson, err := json.Marshal(msgOK)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgModelsjson, err := json.Marshal(msgModels)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgModeljson, err := json.Marshal(msgModel)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgModelupdatedjson, err := json.Marshal(msgModelupdated)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
modelCjson, err := json.Marshal(modelC)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
modelCupdatedjson, err := json.Marshal(modelCupdated)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgModelAUpdatedjson, err := json.Marshal(msgModelAUpdated)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgModelAUpdated2json, err := json.Marshal(msgModelAUpdated2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgSignalsEmptyjson, err := json.Marshal(msgSignalsEmpty)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgInSignalsjson, err := json.Marshal(msgInSignals)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgInSignalsUpdatedjson, err := json.Marshal(msgInSignalsUpdated)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgOutSignalsjson, err := json.Marshal(msgOutSignals)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
inSignalCjson, err := json.Marshal(inSignalC)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
|
||||
|
||||
// test GET models
|
||||
common.TestEndpoint(t, router, token, "/api/models?simulationID=1", "GET", nil, 200, string(msgModelsjson))
|
||||
|
||||
// test POST models
|
||||
common.TestEndpoint(t, router, token, "/api/models", "POST", modelCjson, 200, string(msgOKjson))
|
||||
|
||||
// test GET models/:ModelID to check if previous POST worked correctly
|
||||
common.TestEndpoint(t, router, token, "/api/models/3", "GET", nil, 200, string(msgModeljson))
|
||||
|
||||
// test PUT models/:ModelID
|
||||
common.TestEndpoint(t, router, token, "/api/models/3", "PUT", modelCupdatedjson, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models/3", "GET", nil, 200, string(msgModelupdatedjson))
|
||||
|
||||
// test DELETE models/:ModelID
|
||||
common.TestEndpoint(t, router, token, "/api/models/3", "DELETE", nil, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models?simulationID=1", "GET", nil, 200, string(msgModelsjson))
|
||||
|
||||
// test GET models/:ModelID/signals
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals?direction=in", "GET", nil, 200, string(msgInSignalsjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals?direction=out", "GET", nil, 200, string(msgOutSignalsjson))
|
||||
|
||||
// test PUT models/:ModelID/signals
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals", "PUT", inSignalCjson, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals?direction=in", "GET", nil, 200, string(msgInSignalsUpdatedjson))
|
||||
|
||||
// test GET models/:ModelID to check if PUT adapted InputLength correctly
|
||||
common.TestEndpoint(t, router, token, "/api/models/1", "GET", nil, 200, string(msgModelAUpdatedjson))
|
||||
|
||||
// test DELETE models/:ModelID/signals
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals?direction=in", "DELETE", nil, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals?direction=in", "GET", nil, 200, string(msgSignalsEmptyjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals?direction=out", "GET", nil, 200, string(msgOutSignalsjson))
|
||||
|
||||
// test GET models/:ModelID to check if DELETE adapted InputLength correctly
|
||||
common.TestEndpoint(t, router, token, "/api/models/1", "GET", nil, 200, string(msgModelAUpdated2json))
|
||||
|
||||
// TODO add testing for other return codes
|
||||
|
||||
}
|
|
@ -9,8 +9,8 @@ import (
|
|||
)
|
||||
|
||||
func RegisterSimulatorEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("/", GetSimulators)
|
||||
r.POST("/", AddSimulator)
|
||||
r.GET("", GetSimulators)
|
||||
r.POST("", AddSimulator)
|
||||
r.PUT("/:simulatorID", UpdateSimulator)
|
||||
r.GET("/:simulatorID", GetSimulator)
|
||||
r.DELETE("/:simulatorID", DeleteSimulator)
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
|
||||
func RegisterVisualizationEndpoints(r *gin.RouterGroup) {
|
||||
|
||||
r.GET("/", getVisualizations)
|
||||
r.POST("/", addVisualization)
|
||||
r.GET("", getVisualizations)
|
||||
r.POST("", addVisualization)
|
||||
//r.POST("/:visualizationID", cloneVisualization)
|
||||
r.PUT("/:visualizationID", updateVisualization)
|
||||
r.GET("/:visualizationID", getVisualization)
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
)
|
||||
|
||||
func RegisterWidgetEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("/", getWidgets)
|
||||
r.POST("/", addWidget)
|
||||
r.GET("", getWidgets)
|
||||
r.POST("", addWidget)
|
||||
//r.POST("/:widgetID", cloneWidget)
|
||||
r.PUT("/:widgetID", updateWidget)
|
||||
r.GET("/:widgetID", getWidget)
|
||||
|
|
2
start.go
2
start.go
|
@ -51,7 +51,7 @@ func main() {
|
|||
api.Use(user.Authentication(true))
|
||||
|
||||
simulation.RegisterSimulationEndpoints(api.Group("/simulations"))
|
||||
simulationmodel.RegisterModelEndpoints(api.Group("/models"))
|
||||
simulationmodel.RegisterSimulationModelEndpoints(api.Group("/models"))
|
||||
visualization.RegisterVisualizationEndpoints(api.Group("/visualizations"))
|
||||
widget.RegisterWidgetEndpoints(api.Group("/widgets"))
|
||||
file.RegisterFileEndpoints(api.Group("/files"))
|
||||
|
|
Loading…
Add table
Reference in a new issue