- add testing for simulationmodel endpoints

- add Signal serializer
- Use Signals DB table again
- remove / in some endpoint definitions
This commit is contained in:
Sonja Happ 2019-06-06 16:36:12 +02:00
parent 15114edbc6
commit 5ca6281a22
17 changed files with 576 additions and 167 deletions

View file

@ -91,6 +91,8 @@ test:backend:endpoints:
- ~/go/bin/swag init -p pascalcase -g "start.go" -o "./doc/api/" - ~/go/bin/swag init -p pascalcase -g "start.go" -o "./doc/api/"
- cd routes/simulation - cd routes/simulation
- go test -v -args -dbhost=/var/run/postgresql - go test -v -args -dbhost=/var/run/postgresql
- cd ../simulationmodel
- go test -v -args -dbhost=/var/run/postgresql
dependencies: dependencies:
- build:backend - build:backend

View file

@ -54,7 +54,7 @@ func VerifyConnection(db *gorm.DB) error {
// to the Dummy*() where it is called // to the Dummy*() where it is called
func DropTables(db *gorm.DB) { func DropTables(db *gorm.DB) {
db.DropTableIfExists(&Simulator{}) db.DropTableIfExists(&Simulator{})
//db.DropTableIfExists(&Signal{}) db.DropTableIfExists(&Signal{})
db.DropTableIfExists(&SimulationModel{}) db.DropTableIfExists(&SimulationModel{})
db.DropTableIfExists(&File{}) db.DropTableIfExists(&File{})
db.DropTableIfExists(&Simulation{}) db.DropTableIfExists(&Simulation{})
@ -66,7 +66,7 @@ func DropTables(db *gorm.DB) {
// AutoMigrate the models // AutoMigrate the models
func MigrateModels(db *gorm.DB) { func MigrateModels(db *gorm.DB) {
db.AutoMigrate(&Simulator{}) db.AutoMigrate(&Simulator{})
//db.AutoMigrate(&Signal{}) db.AutoMigrate(&Signal{})
db.AutoMigrate(&SimulationModel{}) db.AutoMigrate(&SimulationModel{})
db.AutoMigrate(&File{}) db.AutoMigrate(&File{})
db.AutoMigrate(&Simulation{}) 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_A).Error)
checkErr(test_db.Create(&simr_B).Error) checkErr(test_db.Create(&simr_B).Error)
//outSig_A := Signal{Name: "outSignal_A", Direction: "out"} outSig_A := Signal{Name: "outSignal_A", Direction: "out", Index: 0, Unit: "V"}
//outSig_B := Signal{Name: "outSignal_B", Direction: "out"} outSig_B := Signal{Name: "outSignal_B", Direction: "out", Index: 1, Unit: "V"}
//inSig_A := Signal{Name: "inSignal_A", Direction: "in"} inSig_A := Signal{Name: "inSignal_A", Direction: "in", Index: 0, Unit: "A"}
//inSig_B := Signal{Name: "inSignal_B", Direction: "in"} inSig_B := Signal{Name: "inSignal_B", Direction: "in", Index: 1, Unit: "A"}
//checkErr(test_db.Create(&outSig_A).Error) checkErr(test_db.Create(&outSig_A).Error)
//checkErr(test_db.Create(&outSig_B).Error) checkErr(test_db.Create(&outSig_B).Error)
//checkErr(test_db.Create(&inSig_A).Error) checkErr(test_db.Create(&inSig_A).Error)
//checkErr(test_db.Create(&inSig_B).Error) checkErr(test_db.Create(&inSig_B).Error)
mo_A := SimulationModel{Name: "SimulationModel_A"} mo_A := SimulationModel{Name: "SimulationModel_A"}
mo_B := SimulationModel{Name: "SimulationModel_B"} 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_A).Error)
checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_B).Error) checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_B).Error)
// SimulationModel HM Signal // 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_A).Error)
//checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSig_B).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_A).Error)
//checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_B).Error) checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_B).Error)
// SimulationModel HM Files // SimulationModel HM Files
checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_A).Error) checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_A).Error)
@ -191,6 +191,7 @@ func DummyPopulateDB(test_db *gorm.DB) {
// Simulator BT SimulationModel // Simulator BT SimulationModel
checkErr(test_db.Model(&mo_A).Association("Simulator").Append(&simr_A).Error) 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 // 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_A).Error)

View file

@ -37,7 +37,7 @@ func TestDummyDBAssociations(t *testing.T) {
var vis Visualization var vis Visualization
var widg Widget var widg Widget
//var sigs []Signal var sigs []Signal
var mos []SimulationModel var mos []SimulationModel
var files []File var files []File
var files_sm []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.NoError(db.Model(&mo).Association("Simulator").Find(&simr).Error)
a.EqualValues("Host_A", simr.Host, "Expected Host_A") a.EqualValues("Host_A", simr.Host, "Expected Host_A")
//a.NoError(db.Model(&mo).Where("Direction = ?", "out").Related(&sigs, "OutputMapping").Error) a.NoError(db.Model(&mo).Where("Direction = ?", "out").Related(&sigs, "OutputMapping").Error)
//if len(sigs) != 2 { if len(sigs) != 2 {
// a.Fail("Model Associations", a.Fail("SimulationModel Associations",
// "Expected to have %v Output AND Input Signals. Has %v.", 2, len(sigs)) "Expected to have %v Output Signals. Has %v.", 2, len(sigs))
//} }
a.NoError(db.Model(&mo).Related(&files_sm, "Files").Error) a.NoError(db.Model(&mo).Related(&files_sm, "Files").Error)
if len(files_sm) != 2 { if len(files_sm) != 2 {

View file

@ -57,14 +57,17 @@ type SimulationModel struct {
// ID of simulator associated with simulation model // ID of simulator associated with simulation model
SimulatorID uint SimulatorID uint
// Mapping of output signals of the simulation model, order of signals is important // 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 // 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 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 { type Signal struct {
// ID of simulation model
ID uint `gorm:"primary_key;auto_increment"`
// Name of Signal // Name of Signal
Name string Name string
// Unit of Signal // Unit of Signal
@ -73,6 +76,8 @@ type Signal struct {
Index uint Index uint
// Direction of the signal (in or out) // Direction of the signal (in or out)
Direction string Direction string
// ID of simulation model
SimulationModelID uint
} }
// Simulator data model // Simulator data model
@ -162,7 +167,7 @@ type File struct {
// Last modification time of file // Last modification time of file
Date time.Time Date time.Time
// ID of model to which file belongs // ID of model to which file belongs
ModelID uint `gorm:""` SimulationModelID uint `gorm:""`
// ID of widget to which file belongs // ID of widget to which file belongs
WidgetID uint `gorm:""` WidgetID uint `gorm:""`
} }

View file

@ -18,14 +18,13 @@ type SimulationResponse struct {
} }
type SimulationModelResponse struct { type SimulationModelResponse struct {
ID uint `json:"ID"`
Name string `json:"Name"` Name string `json:"Name"`
OutputLength int `json:"OutputLength"` OutputLength int `json:"OutputLength"`
InputLength int `json:"InputLength"` InputLength int `json:"InputLength"`
SimulationID uint `json:"SimulationID"` SimulationID uint `json:"SimulationID"`
SimulatorID uint `json:"SimulatorID"` SimulatorID uint `json:"SimulatorID"`
StartParams string `json:"StartParams"` StartParams string `json:"StartParams"`
InputMapping []Signal `json:"InputMapping"`
OutputMapping []Signal `json:"OutputMapping"`
} }
type SimulatorResponse struct { type SimulatorResponse struct {
@ -71,6 +70,14 @@ type FileResponse struct {
Date time.Time `json:"Date"` 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 // Response messages
type ResponseMsg struct { type ResponseMsg struct {
@ -92,3 +99,15 @@ type ResponseMsgSimulations struct {
type ResponseMsgSimulation struct { type ResponseMsgSimulation struct {
Simulation SimulationResponse `json:"simulation"` 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"`
}

View file

@ -104,14 +104,13 @@ type SimulationModelSerializer struct {
func (self *SimulationModelSerializer) Response() SimulationModelResponse { func (self *SimulationModelSerializer) Response() SimulationModelResponse {
response := SimulationModelResponse{ response := SimulationModelResponse{
ID: self.ID,
Name: self.Name, Name: self.Name,
OutputLength: self.OutputLength, OutputLength: self.OutputLength,
InputLength: self.InputLength, InputLength: self.InputLength,
SimulationID: self.SimulationID, SimulationID: self.SimulationID,
SimulatorID: self.SimulatorID, SimulatorID: self.SimulatorID,
StartParams: self.StartParameters, StartParams: self.StartParameters,
//InputMapping
//OutputMapping
} }
return response return response
} }
@ -255,3 +254,35 @@ func (self *FileSerializerNoAssoc) Response() FileResponse {
} }
return response 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
}

View file

@ -11,8 +11,8 @@ import (
) )
func RegisterFileEndpoints(r *gin.RouterGroup) { func RegisterFileEndpoints(r *gin.RouterGroup) {
r.GET("/", getFiles) r.GET("", getFiles)
r.POST("/", addFile) r.POST("", addFile)
r.GET("/:fileID", getFile) r.GET("/:fileID", getFile)
r.PUT("/:fileID", updateFile) r.PUT("/:fileID", updateFile)
r.DELETE("/:fileID", deleteFile) r.DELETE("/:fileID", deleteFile)

View file

@ -10,8 +10,8 @@ import (
) )
func RegisterSimulationEndpoints(r *gin.RouterGroup) { func RegisterSimulationEndpoints(r *gin.RouterGroup) {
r.GET("/", getSimulations) r.GET("", getSimulations)
r.POST("/", addSimulation) r.POST("", addSimulation)
r.PUT("/:simulationID", updateSimulation) r.PUT("/:simulationID", updateSimulation)
r.GET("/:simulationID", getSimulation) r.GET("/:simulationID", getSimulation)
r.DELETE("/:simulationID", deleteSimulation) r.DELETE("/:simulationID", deleteSimulation)

View file

@ -77,12 +77,7 @@ func (s *Simulation) deleteUser(username string) error {
func (s *Simulation) delete() error { func (s *Simulation) delete() error {
db := common.GetDB() 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() users, no_users, err := s.getUsers()
@ -105,13 +100,14 @@ func (s *Simulation) delete() error {
} }
} }
// Delete simulation // Simulation is not deleted from DB, only associations with users are removed
err = db.Delete(s).Error // Simulation remains "dangling" in DB
if err != nil {
return err
}
} // Delete simulation
//err = db.Delete(s).Error
//if err != nil {
// return err
//}
return nil return nil
} }

View file

@ -147,17 +147,17 @@ func TestSimulationEndpoints(t *testing.T) {
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200) token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
// test GET simulations/ // 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/ // 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 // test GET simulations/:SimulationID
common.TestEndpoint(t, router, token, "/api/simulations/3", "GET", nil, 200, string(msgSimulationjson)) common.TestEndpoint(t, router, token, "/api/simulations/3", "GET", nil, 200, string(msgSimulationjson))
// test DELETE simulations/:SimulationID // test DELETE simulations/:SimulationID
common.TestEndpoint(t, router, token, "/api/simulations/3", "DELETE", nil, 200, string(msgOKjson)) 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 // test GET simulations/:SimulationID/users
common.TestEndpoint(t, router, token, "/api/simulations/1/users", "GET", nil, 200, string(msgUsersjson)) common.TestEndpoint(t, router, token, "/api/simulations/1/users", "GET", nil, 200, string(msgUsersjson))

View file

@ -9,15 +9,15 @@ import (
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
) )
func RegisterModelEndpoints(r *gin.RouterGroup) { func RegisterSimulationModelEndpoints(r *gin.RouterGroup) {
r.GET("/", getSimulationModels) r.GET("", getSimulationModels)
r.POST("/", addSimulationModel) r.POST("", addSimulationModel)
//r.POST("/:modelID", cloneSimulationModel) //r.POST("/:modelID", cloneSimulationModel)
r.PUT("/:modelID", updateSimulationModel) r.PUT("/:modelID", updateSimulationModel)
r.GET("/:modelID", getSimulationModel) r.GET("/:modelID", getSimulationModel)
r.DELETE("/:modelID", deleteSimulationModel) r.DELETE("/:modelID", deleteSimulationModel)
r.GET("/:modelID/signals", getSignals) r.GET("/:modelID/signals", getSignals)
r.POST("/:modelID/signals", addSignal) r.PUT("/:modelID/signals", addSignal)
r.DELETE("/:modelID/signals", deleteSignals) r.DELETE("/:modelID/signals", deleteSignals)
} }
@ -83,7 +83,7 @@ func addSimulationModel(c *gin.Context) {
return return
} }
err = newModel.addToSimulation(newModel.SimulationID) err = newModel.addToSimulation()
if common.ProvideErrorResponse(c, err) == false { if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"message": "OK.", "message": "OK.",
@ -203,63 +203,19 @@ func getSimulationModel(c *gin.Context) {
// @Router /models/{modelID} [delete] // @Router /models/{modelID} [delete]
func deleteSimulationModel(c *gin.Context) { func deleteSimulationModel(c *gin.Context) {
//ok, m := checkPermissions(c, common.Delete) 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)
if !ok { if !ok {
return return
} }
direction := c.Request.URL.Query().Get("direction") err := m.delete()
if !(direction == "out") && !(direction == "in") { if common.ProvideErrorResponse(c, err) {
errormsg := "Bad request. Direction has to be in or out"
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return 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{ c.JSON(http.StatusOK, gin.H{
"message": "OK.", "message": "OK.",
}) })
}
} }
// getSignals godoc // getSignals godoc
@ -281,8 +237,13 @@ func getSignals(c *gin.Context) {
return return
} }
direction := c.Param("direction") var mapping string
if !(direction == "out") && !(direction == "in") { 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" errormsg := "Bad request. Direction has to be in or out"
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg, "error": errormsg,
@ -290,18 +251,58 @@ func getSignals(c *gin.Context) {
return return
} }
var signals []common.Signal db := common.GetDB()
if direction == "in" { var sigs []common.Signal
signals = m.InputMapping err := db.Order("ID asc").Model(m).Where("Direction = ?", direction).Related(&sigs, mapping).Error
} else { if common.ProvideErrorResponse(c, err) {
signals = m.OutputMapping return
} }
serializer := common.SignalsSerializer{c, sigs}
c.JSON(http.StatusOK, gin.H{ 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 // deleteSignals godoc
// @Summary Delete all signals of a direction // @Summary Delete all signals of a direction
// @ID deleteSignals // @ID deleteSignals
@ -322,7 +323,7 @@ func deleteSignals(c *gin.Context) {
return return
} }
direction := c.Param("direction") direction := c.Request.URL.Query().Get("direction")
if !(direction == "out") && !(direction == "in") { if !(direction == "out") && !(direction == "in") {
errormsg := "Bad request. Direction has to be in or out" errormsg := "Bad request. Direction has to be in or out"
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{

View file

@ -27,10 +27,10 @@ func (m *SimulationModel) ByID(id uint) error {
return nil return nil
} }
func (m *SimulationModel) addToSimulation(simID uint) error { func (m *SimulationModel) addToSimulation() error {
db := common.GetDB() db := common.GetDB()
var sim simulation.Simulation var sim simulation.Simulation
err := sim.ByID(simID) err := sim.ByID(m.SimulationID)
if err != nil { if err != nil {
return err return err
} }
@ -54,55 +54,70 @@ func (m *SimulationModel) addToSimulation(simID uint) error {
func (m *SimulationModel) update(modifiedSimulationModel SimulationModel) error { func (m *SimulationModel) update(modifiedSimulationModel SimulationModel) error {
db := common.GetDB() db := common.GetDB()
err := db.Model(m).Update(modifiedSimulationModel).Error
if err != nil {
return err
}
if m.SimulatorID != modifiedSimulationModel.SimulatorID { if m.SimulatorID != modifiedSimulationModel.SimulatorID {
// update simulator // update simulator
var s simulator.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 err = db.Model(m).Association("Simulator").Replace(s).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) 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 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() db := common.GetDB()
var err error var err error
if direction == "in" { if signal.Direction == "in" {
err = db.Model(m).Association("InputMapping").Append(signal).Error err = db.Model(m).Association("InputMapping").Append(signal).Error
if err != nil { if err != nil {
return err return err
} }
// adapt length of mapping // 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 { } else {
err = db.Model(m).Association("OutputMapping").Append(signal).Error err = db.Model(m).Association("OutputMapping").Append(signal).Error
if err != nil {
return err
}
// adapt length of mapping // 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 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 { if err != nil {
return err return err
} }
@ -147,6 +162,7 @@ func (m *SimulationModel) deleteSignals(direction string) error {
} else { } else {
m.OutputLength = 0 m.OutputLength = 0
} }
err = m.update(*m)
return err return err
} }

View 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
}

View file

@ -9,8 +9,8 @@ import (
) )
func RegisterSimulatorEndpoints(r *gin.RouterGroup) { func RegisterSimulatorEndpoints(r *gin.RouterGroup) {
r.GET("/", GetSimulators) r.GET("", GetSimulators)
r.POST("/", AddSimulator) r.POST("", AddSimulator)
r.PUT("/:simulatorID", UpdateSimulator) r.PUT("/:simulatorID", UpdateSimulator)
r.GET("/:simulatorID", GetSimulator) r.GET("/:simulatorID", GetSimulator)
r.DELETE("/:simulatorID", DeleteSimulator) r.DELETE("/:simulatorID", DeleteSimulator)

View file

@ -13,8 +13,8 @@ import (
func RegisterVisualizationEndpoints(r *gin.RouterGroup) { func RegisterVisualizationEndpoints(r *gin.RouterGroup) {
r.GET("/", getVisualizations) r.GET("", getVisualizations)
r.POST("/", addVisualization) r.POST("", addVisualization)
//r.POST("/:visualizationID", cloneVisualization) //r.POST("/:visualizationID", cloneVisualization)
r.PUT("/:visualizationID", updateVisualization) r.PUT("/:visualizationID", updateVisualization)
r.GET("/:visualizationID", getVisualization) r.GET("/:visualizationID", getVisualization)

View file

@ -10,8 +10,8 @@ import (
) )
func RegisterWidgetEndpoints(r *gin.RouterGroup) { func RegisterWidgetEndpoints(r *gin.RouterGroup) {
r.GET("/", getWidgets) r.GET("", getWidgets)
r.POST("/", addWidget) r.POST("", addWidget)
//r.POST("/:widgetID", cloneWidget) //r.POST("/:widgetID", cloneWidget)
r.PUT("/:widgetID", updateWidget) r.PUT("/:widgetID", updateWidget)
r.GET("/:widgetID", getWidget) r.GET("/:widgetID", getWidget)

View file

@ -51,7 +51,7 @@ func main() {
api.Use(user.Authentication(true)) api.Use(user.Authentication(true))
simulation.RegisterSimulationEndpoints(api.Group("/simulations")) simulation.RegisterSimulationEndpoints(api.Group("/simulations"))
simulationmodel.RegisterModelEndpoints(api.Group("/models")) simulationmodel.RegisterSimulationModelEndpoints(api.Group("/models"))
visualization.RegisterVisualizationEndpoints(api.Group("/visualizations")) visualization.RegisterVisualizationEndpoints(api.Group("/visualizations"))
widget.RegisterWidgetEndpoints(api.Group("/widgets")) widget.RegisterWidgetEndpoints(api.Group("/widgets"))
file.RegisterFileEndpoints(api.Group("/files")) file.RegisterFileEndpoints(api.Group("/files"))