Add signal package and move signal endpoints from simulationmodel package to this one

This commit is contained in:
Sonja Happ 2019-06-17 15:30:26 +02:00
parent 98fe2bdb94
commit 4793077a90
8 changed files with 385 additions and 216 deletions

View file

@ -20,6 +20,7 @@ const ModelSimulator = ModelName("simulator")
const ModelVisualization = ModelName("visualization")
const ModelWidget = ModelName("widget")
const ModelSimulationModel = ModelName("simulationmodel")
const ModelSignal = ModelName("signal")
type CRUD string
@ -52,6 +53,7 @@ var Roles = RoleActions{
ModelSimulator: crud,
ModelWidget: crud,
ModelVisualization: crud,
ModelSignal: crud,
},
"User": {
ModelUser: _ru_,
@ -60,6 +62,7 @@ var Roles = RoleActions{
ModelSimulator: _r__,
ModelWidget: crud,
ModelVisualization: crud,
ModelSignal: crud,
},
"Guest": {
ModelSimulation: _r__,
@ -68,6 +71,7 @@ var Roles = RoleActions{
ModelWidget: _r__,
ModelSimulator: _r__,
ModelUser: _ru_,
ModelSignal: _r__,
},
}

View file

@ -0,0 +1,193 @@
package signal
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/routes/simulationmodel"
)
func RegisterSignalEndpoints(r *gin.RouterGroup) {
r.GET("", getSignals)
r.POST("", addSignal)
r.PUT("/:signalID", updateSignal)
r.GET("/:signalID", getSignal)
r.DELETE("/:signalID", deleteSignal)
}
// getSignals godoc
// @Summary Get all signals of one direction
// @ID getSignals
// @Produce json
// @Tags signals
// @Param direction query string true "Direction of signal (in or out)"
// @Param direction modelID string true "Model ID of signals to be obtained"
// @Success 200 {array} common.Signal "Requested signals."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Router /signals [get]
func getSignals(c *gin.Context) {
ok, m := simulationmodel.CheckPermissions(c, common.Read, "query", -1)
if !ok {
return
}
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,
})
return
}
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": serializer.Response(),
})
}
// AddSignal godoc
// @Summary Add a signal to a signal mapping of a model
// @ID AddSignal
// @Accept json
// @Produce json
// @Tags signals
// @Param inputSignal body common.Signal true "A signal to be added to the model incl. direction and model ID to which signal shall be added"
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Router /signals [post]
func addSignal(c *gin.Context) {
var newSignal Signal
err := c.BindJSON(&newSignal)
if err != nil {
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return
}
ok, _ := simulationmodel.CheckPermissions(c, common.Update, "body", int(newSignal.SimulationModelID))
if !ok {
return
}
// Add signal to model
err = newSignal.addToSimulationModel()
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}
}
// updateSignal godoc
// @Summary Update a signal
// @ID updateSignal
// @Tags signals
// @Produce json
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param signalID path int true "ID of signal to be updated"
// @Router /signals/{signalID} [put]
func updateSignal(c *gin.Context) {
ok, sig := checkPermissions(c, common.Delete)
if !ok {
return
}
var modifiedSignal Signal
err := c.BindJSON(&modifiedSignal)
if err != nil {
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return
}
err = sig.update(modifiedSignal)
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}
}
// getSignal godoc
// @Summary Get a signal
// @ID getSignal
// @Tags signals
// @Produce json
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param signalID path int true "ID of signal to be obtained"
// @Router /signals/{signalID} [get]
func getSignal(c *gin.Context) {
ok, sig := checkPermissions(c, common.Delete)
if !ok {
return
}
serializer := common.SignalSerializer{c, sig.Signal}
c.JSON(http.StatusOK, gin.H{
"signal": serializer.Response(),
})
}
// deleteSignal godoc
// @Summary Delete a signal
// @ID deleteSignal
// @Tags signals
// @Produce json
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param signalID path int true "ID of signal to be deleted"
// @Router /signals/{signalID} [delete]
func deleteSignal(c *gin.Context) {
ok, sig := checkPermissions(c, common.Delete)
if !ok {
return
}
err := sig.delete()
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}
}

View file

@ -0,0 +1,112 @@
package signal
import (
"fmt"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel"
)
type Signal struct {
common.Signal
}
func (s *Signal) save() error {
db := common.GetDB()
err := db.Create(s).Error
return err
}
func (s *Signal) byID(id uint) error {
db := common.GetDB()
err := db.Find(s, id).Error
if err != nil {
return fmt.Errorf("Signal with id=%v does not exist", id)
}
return nil
}
func (s *Signal) addToSimulationModel() error {
db := common.GetDB()
var m simulationmodel.SimulationModel
err := m.ByID(s.SimulationModelID)
if err != nil {
return err
}
// save signal to DB
err = s.save()
if err != nil {
return err
}
// associate signal with simulation model in correct direction
if s.Direction == "in" {
err = db.Model(&m).Association("InputMapping").Append(s).Error
if err != nil {
return err
}
// adapt length of mapping
m.InputLength = db.Model(m).Where("Direction = ?", "in").Association("InputMapping").Count()
err = m.Update(m)
} else {
err = db.Model(&m).Association("OutputMapping").Append(s).Error
if err != nil {
return err
}
// adapt length of mapping
m.OutputLength = db.Model(m).Where("Direction = ?", "out").Association("OutputMapping").Count()
err = m.Update(m)
}
return err
}
func (s *Signal) update(modifiedSignal Signal) error {
db := common.GetDB()
err := db.Model(s).Updates(map[string]interface{}{
"Name": modifiedSignal.Name,
"Unit": modifiedSignal.Unit,
"Index": modifiedSignal.Index,
}).Error
return err
}
func (s *Signal) delete() error {
db := common.GetDB()
var m simulationmodel.SimulationModel
err := m.ByID(s.SimulationModelID)
if err != nil {
return err
}
// remove association between Signal and SimulationModel
// Signal itself is not deleted from DB, it remains as "dangling"
if s.Direction == "in" {
err = db.Model(&m).Association("InputMapping").Delete(s).Error
if err != nil {
return err
}
// Reduce length of mapping by 1
m.InputLength--
err = m.Update(m)
} else {
err = db.Model(&m).Association("OutputMapping").Delete(s).Error
if err != nil {
return err
}
// Reduce length of mapping by 1
m.OutputLength--
err = m.Update(m)
}
return err
}

View file

@ -0,0 +1,44 @@
package signal
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/routes/simulationmodel"
)
func checkPermissions(c *gin.Context, operation common.CRUD) (bool, Signal) {
var sig Signal
err := common.ValidateRole(c, common.ModelSignal, operation)
if err != nil {
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
return false, sig
}
signalID, err := strconv.Atoi(c.Param("signalID"))
if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of signalID path parameter")
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return false, sig
}
err = sig.byID(uint(signalID))
if common.ProvideErrorResponse(c, err) {
return false, sig
}
ok, _ := simulationmodel.CheckPermissions(c, operation, "body", int(sig.SimulationModelID))
if !ok {
return false, sig
}
return true, sig
}

View file

@ -15,9 +15,6 @@ func RegisterSimulationModelEndpoints(r *gin.RouterGroup) {
r.PUT("/:modelID", updateSimulationModel)
r.GET("/:modelID", getSimulationModel)
r.DELETE("/:modelID", deleteSimulationModel)
r.GET("/:modelID/signals", getSignals)
r.PUT("/:modelID/signals", addSignal)
r.DELETE("/:modelID/signals", deleteSignals)
}
// getSimulationModels godoc
@ -106,7 +103,7 @@ func addSimulationModel(c *gin.Context) {
// @Router /models/{modelID} [put]
func updateSimulationModel(c *gin.Context) {
ok, m := checkPermissions(c, common.Update)
ok, m := CheckPermissions(c, common.Update, "path", -1)
if !ok {
return
}
@ -121,7 +118,7 @@ func updateSimulationModel(c *gin.Context) {
return
}
err = m.update(modifiedModel)
err = m.Update(modifiedModel)
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
@ -143,7 +140,7 @@ func updateSimulationModel(c *gin.Context) {
// @Router /models/{modelID} [get]
func getSimulationModel(c *gin.Context) {
ok, m := checkPermissions(c, common.Read)
ok, m := CheckPermissions(c, common.Read, "path", -1)
if !ok {
return
}
@ -168,7 +165,7 @@ func getSimulationModel(c *gin.Context) {
// @Router /models/{modelID} [delete]
func deleteSimulationModel(c *gin.Context) {
ok, m := checkPermissions(c, common.Delete)
ok, m := CheckPermissions(c, common.Delete, "path", -1)
if !ok {
return
}
@ -182,126 +179,3 @@ func deleteSimulationModel(c *gin.Context) {
"message": "OK.",
})
}
// getSignals godoc
// @Summary Get all signals of one direction
// @ID getSignals
// @Produce json
// @Tags models
// @Param direction query string true "Direction of signal (in or out)"
// @Success 200 {array} common.Signal "Requested signals."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Router /models/{modelID}/signals [get]
func getSignals(c *gin.Context) {
ok, m := checkPermissions(c, common.Read)
if !ok {
return
}
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,
})
return
}
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": 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
// @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"
// @Param direction query string true "Direction of signals to delete (in or out)"
// @Router /models/{modelID}/signals [delete]
func deleteSignals(c *gin.Context) {
ok, m := checkPermissions(c, common.Update)
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,
})
return
}
err := m.deleteSignals(direction)
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}
}

View file

@ -52,7 +52,7 @@ func (m *SimulationModel) addToSimulation() error {
return err
}
func (m *SimulationModel) update(modifiedSimulationModel SimulationModel) error {
func (m *SimulationModel) Update(modifiedSimulationModel SimulationModel) error {
db := common.GetDB()
if m.SimulatorID != modifiedSimulationModel.SimulatorID {
@ -66,15 +66,13 @@ func (m *SimulationModel) update(modifiedSimulationModel SimulationModel) error
}
err := db.Model(m).Updates(map[string]interface{}{"Name": modifiedSimulationModel.Name,
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
}
@ -94,75 +92,3 @@ func (m *SimulationModel) delete() error {
return err
}
func (m *SimulationModel) addSignal(signal common.Signal) error {
db := common.GetDB()
var err error
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).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).Where("Direction = ?", "out").Association("OutputMapping").Count()
err = m.update(*m)
}
return err
}
func (m *SimulationModel) deleteSignals(direction string) error {
db := common.GetDB()
var err error
var columnName string
if direction == "in" {
columnName = "InputMapping"
} else {
columnName = "OutputMapping"
}
var signals []common.Signal
err = db.Order("ID asc").Model(m).Where("Direction = ?", direction).Related(&signals, columnName).Error
if err != nil {
return err
}
// remove association to each signal and delete each signal from db
for _, sig := range signals {
err = db.Model(m).Association(columnName).Delete(sig).Error
if err != nil {
return err
}
err = db.Delete(sig).Error
if err != nil {
return err
}
}
// set length of mapping to 0
if columnName == "InputMapping" {
m.InputLength = 0
} else {
m.OutputLength = 0
}
err = m.update(*m)
return err
}

View file

@ -11,7 +11,7 @@ import (
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
)
func checkPermissions(c *gin.Context, operation common.CRUD) (bool, SimulationModel) {
func CheckPermissions(c *gin.Context, operation common.CRUD, modelIDSource string, modelIDBody int) (bool, SimulationModel) {
var m SimulationModel
@ -21,14 +21,27 @@ func checkPermissions(c *gin.Context, operation common.CRUD) (bool, SimulationMo
return false, m
}
modelID, err := strconv.Atoi(c.Param("modelID"))
if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of model ID in path")
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return false, m
var modelID int
if modelIDSource == "path" {
modelID, err = strconv.Atoi(c.Param("modelID"))
if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of modelID path parameter")
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return false, m
}
} else if modelIDSource == "query" {
modelID, err = strconv.Atoi(c.Request.URL.Query().Get("modelID"))
if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of modelID query parameter")
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return false, m
}
} else if modelIDSource == "body" {
modelID = modelIDBody
}
err = m.ByID(uint(modelID))

View file

@ -2,6 +2,8 @@ package main
import (
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/signal"
"github.com/gin-gonic/gin"
"github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
@ -52,6 +54,7 @@ func main() {
simulation.RegisterSimulationEndpoints(api.Group("/simulations"))
simulationmodel.RegisterSimulationModelEndpoints(api.Group("/models"))
signal.RegisterSignalEndpoints(api.Group("/signals"))
visualization.RegisterVisualizationEndpoints(api.Group("/visualizations"))
widget.RegisterWidgetEndpoints(api.Group("/widgets"))
file.RegisterFileEndpoints(api.Group("/files"))