diff --git a/common/models.go b/common/models.go index cbc1977..372757d 100644 --- a/common/models.go +++ b/common/models.go @@ -100,6 +100,8 @@ type Simulator struct { Properties string // Raw properties of simulator as JSON string RawProperties string + // SimulationModels in which the simulator is used + SimulationModels []SimulationModel } // Visualization data model diff --git a/routes/signal/signalEndpoints.go b/routes/signal/signalEndpoints.go index 93fd5bb..29df23a 100644 --- a/routes/signal/signalEndpoints.go +++ b/routes/signal/signalEndpoints.go @@ -137,7 +137,6 @@ func updateSignal(c *gin.Context) { "message": "OK.", }) } - } // getSignal godoc diff --git a/routes/signal/signal_test.go b/routes/signal/signal_test.go index 725e0a1..c728819 100644 --- a/routes/signal/signal_test.go +++ b/routes/signal/signal_test.go @@ -109,7 +109,7 @@ var msgInSignalC = common.ResponseMsgSignal{ } // Test /models endpoints -func TestSimulationModelEndpoints(t *testing.T) { +func TestSignalEndpoints(t *testing.T) { db := common.DummyInitDB() defer db.Close() diff --git a/routes/simulationmodel/simulationmodelMethods.go b/routes/simulationmodel/simulationmodelMethods.go index a7ee89d..1a55bf3 100644 --- a/routes/simulationmodel/simulationmodelMethods.go +++ b/routes/simulationmodel/simulationmodelMethods.go @@ -45,6 +45,13 @@ func (m *SimulationModel) addToSimulation() error { var simltr simulator.Simulator err = simltr.ByID(m.SimulatorID) err = db.Model(m).Association("Simulator").Append(&simltr).Error + if err != nil { + return err + } + err = db.Model(&simltr).Association("SimulationModels").Append(m).Error + if err != nil { + return err + } // associate simulation model with simulation err = db.Model(&sim).Association("SimulationModels").Append(m).Error @@ -58,11 +65,26 @@ func (m *SimulationModel) Update(modifiedSimulationModel SimulationModel) error if m.SimulatorID != modifiedSimulationModel.SimulatorID { // update simulator var s simulator.Simulator + var s_old simulator.Simulator err := s.ByID(modifiedSimulationModel.SimulatorID) if err != nil { return err } + err = s_old.ByID(m.SimulatorID) + if err != nil { + return err + } err = db.Model(m).Association("Simulator").Replace(s).Error + if err != nil { + return err + } + // remove simulation model from old simulator + err = db.Model(&s_old).Association("SimulationModels").Delete(m).Error + if err != nil { + return err + } + // add simulation model to new simulator + err = db.Model(&s).Association("SimulationModels").Append(m).Error } diff --git a/routes/simulator/simulatorEndpoints.go b/routes/simulator/simulatorEndpoints.go index a43aa80..5f3346b 100644 --- a/routes/simulator/simulatorEndpoints.go +++ b/routes/simulator/simulatorEndpoints.go @@ -1,7 +1,9 @@ package simulator import ( + "fmt" "net/http" + "strconv" "github.com/gin-gonic/gin" @@ -9,17 +11,18 @@ import ( ) 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/action", SendActionToSimulator) + r.GET("", getSimulators) + r.POST("", addSimulator) + r.PUT("/:simulatorID", updateSimulator) + r.GET("/:simulatorID", getSimulator) + r.DELETE("/:simulatorID", deleteSimulator) + r.GET("/:simulatorID/models", getModelsOfSimulator) + r.POST("/:simulatorID/action", sendActionToSimulator) } -// GetSimulators godoc +// getSimulators godoc // @Summary Get all simulators -// @ID GetSimulators +// @ID getSimulators // @Tags simulators // @Produce json // @Success 200 {array} common.SimulatorResponse "Simulator parameters requested by user" @@ -28,10 +31,17 @@ func RegisterSimulatorEndpoints(r *gin.RouterGroup) { // @Failure 404 "Not found" // @Failure 500 "Internal server error" // @Router /simulators [get] -func GetSimulators(c *gin.Context) { +func getSimulators(c *gin.Context) { + + err := common.ValidateRole(c, common.ModelSimulator, common.Read) + if err != nil { + c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") + return + } + db := common.GetDB() var simulators []common.Simulator - err := db.Order("ID asc").Find(&simulators).Error + err = db.Order("ID asc").Find(&simulators).Error if common.ProvideErrorResponse(c, err) { return } @@ -41,9 +51,9 @@ func GetSimulators(c *gin.Context) { }) } -// AddSimulator godoc +// addSimulator godoc // @Summary Add a simulator -// @ID AddSimulator +// @ID addSimulator // @Accept json // @Produce json // @Tags simulators @@ -54,15 +64,35 @@ func GetSimulators(c *gin.Context) { // @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", - }) +func addSimulator(c *gin.Context) { + + err := common.ValidateRole(c, common.ModelSimulator, common.Create) + if err != nil { + c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") + return + } + + var newSimulator Simulator + err = c.BindJSON(&newSimulator) + if err != nil { + errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + err = newSimulator.save() + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } } -// UpdateSimulator godoc +// updateSimulator godoc // @Summary Update a simulator -// @ID UpdateSimulator +// @ID updateSimulator // @Tags simulators // @Accept json // @Produce json @@ -74,15 +104,49 @@ func AddSimulator(c *gin.Context) { // @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", - }) +func updateSimulator(c *gin.Context) { + err := common.ValidateRole(c, common.ModelSimulator, common.Update) + if err != nil { + c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") + return + } + + var modifiedSimulator Simulator + err = c.BindJSON(&modifiedSimulator) + if err != nil { + errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + simulatorID, err := strconv.Atoi(c.Param("simulatorID")) + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + var s Simulator + err = s.ByID(uint(simulatorID)) + if common.ProvideErrorResponse(c, err) { + return + } + + err = s.update(modifiedSimulator) + if common.ProvideErrorResponse(c, err) == false { + c.JSON(http.StatusOK, gin.H{ + "message": "OK.", + }) + } } -// GetSimulator godoc +// getSimulator godoc // @Summary Get simulator -// @ID GetSimulator +// @ID getSimulator // @Produce json // @Tags simulators // @Success 200 {object} common.SimulatorResponse "Simulator requested by user" @@ -92,15 +156,38 @@ func UpdateSimulator(c *gin.Context) { // @Failure 500 "Internal server error" // @Param simulatorID path int true "Simulator ID" // @Router /simulators/{simulatorID} [get] -func GetSimulator(c *gin.Context) { +func getSimulator(c *gin.Context) { + + err := common.ValidateRole(c, common.ModelSimulator, common.Read) + if err != nil { + c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") + return + } + + simulatorID, err := strconv.Atoi(c.Param("simulatorID")) + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + var s Simulator + err = s.ByID(uint(simulatorID)) + if common.ProvideErrorResponse(c, err) { + return + } + + serializer := common.SimulatorSerializer{c, s.Simulator} c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", + "simulator": serializer.Response(), }) } -// DeleteSimulator godoc +// deleteSimulator godoc // @Summary Delete a simulator -// @ID DeleteSimulator +// @ID deleteSimulator // @Tags simulators // @Produce json // @Success 200 "OK." @@ -110,15 +197,90 @@ func GetSimulator(c *gin.Context) { // @Failure 500 "Internal server error" // @Param simulatorID path int true "Simulator ID" // @Router /simulators/{simulatorID} [delete] -func DeleteSimulator(c *gin.Context) { +func deleteSimulator(c *gin.Context) { + + err := common.ValidateRole(c, common.ModelSimulator, common.Delete) + if err != nil { + c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") + return + } + + simulatorID, err := strconv.Atoi(c.Param("simulatorID")) + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + var s Simulator + err = s.ByID(uint(simulatorID)) + if common.ProvideErrorResponse(c, err) { + return + } + + err = s.delete() + if common.ProvideErrorResponse(c, err) { + return + } + c.JSON(http.StatusOK, gin.H{ - "message": "NOT implemented", + "message": "OK.", }) } -// SendActionToSimulator godoc +// getModelsOfSimulator godoc +// @Summary Get all simulation models in which the simulator is used +// @ID getModelsOfSimulator +// @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}/models [get] +func getModelsOfSimulator(c *gin.Context) { + + err := common.ValidateRole(c, common.ModelSimulator, common.Read) + if err != nil { + c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") + return + } + + simulatorID, err := strconv.Atoi(c.Param("simulatorID")) + if err != nil { + errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter") + c.JSON(http.StatusBadRequest, gin.H{ + "error": errormsg, + }) + return + } + + var s Simulator + err = s.ByID(uint(simulatorID)) + if common.ProvideErrorResponse(c, err) { + return + } + + // get all associated simulation models + allModels, _, err := s.getModels() + if common.ProvideErrorResponse(c, err) { + return + } + + serializer := common.SimulationModelsSerializer{c, allModels} + c.JSON(http.StatusOK, gin.H{ + "models": serializer.Response(), + }) + +} + +// sendActionToSimulator godoc // @Summary Send an action to simulator -// @ID SendActionToSimulator +// @ID sendActionToSimulator // @Tags simulators // @Produce json // @Param inputAction query string true "Action for simulator" @@ -129,7 +291,7 @@ func DeleteSimulator(c *gin.Context) { // @Failure 500 "Internal server error" // @Param simulatorID path int true "Simulator ID" // @Router /simulators/{simulatorID}/action [post] -func SendActionToSimulator(c *gin.Context) { +func sendActionToSimulator(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "NOT implemented", }) diff --git a/routes/simulator/simulatorMethods.go b/routes/simulator/simulatorMethods.go index 5df1401..41e4981 100644 --- a/routes/simulator/simulatorMethods.go +++ b/routes/simulator/simulatorMethods.go @@ -24,3 +24,43 @@ func (s *Simulator) ByID(id uint) error { } return nil } + +func (s *Simulator) update(modifiedSimulator Simulator) error { + + db := common.GetDB() + + err := db.Model(s).Updates(map[string]interface{}{ + "UUID": modifiedSimulator.UUID, + "Host": modifiedSimulator.Host, + "Modeltype": modifiedSimulator.Modeltype, + "Uptime": modifiedSimulator.Uptime, + "State": modifiedSimulator.State, + "StateUpdateAt": modifiedSimulator.StateUpdateAt, + "Properties": modifiedSimulator.Properties, + "RawProperties": modifiedSimulator.RawProperties, + }).Error + + return err + +} + +func (s *Simulator) delete() error { + db := common.GetDB() + + no_simulationmodels := db.Model(s).Association("SimulationModel").Count() + + if no_simulationmodels > 0 { + return fmt.Errorf("Simulator cannot be deleted as it is still used in SimulationModels (active or dangling)") + } + + // delete Simulator from DB (does NOT remain as dangling) + err := db.Delete(s).Error + return err +} + +func (s *Simulator) getModels() ([]common.SimulationModel, int, error) { + db := common.GetDB() + var models []common.SimulationModel + err := db.Order("ID asc").Model(s).Related(&models, "SimulationModels").Error + return models, len(models), err +} diff --git a/routes/visualization/visualization_test.go b/routes/visualization/visualization_test.go index 4ed65d9..68b5bbe 100644 --- a/routes/visualization/visualization_test.go +++ b/routes/visualization/visualization_test.go @@ -86,7 +86,7 @@ var msgVisupdated = common.ResponseMsgVisualization{ } // Test /models endpoints -func TestSimulationModelEndpoints(t *testing.T) { +func TestVisualizationEndpoints(t *testing.T) { db := common.DummyInitDB() defer db.Close() diff --git a/routes/widget/widget_test.go b/routes/widget/widget_test.go index 3746c86..1fb01ae 100644 --- a/routes/widget/widget_test.go +++ b/routes/widget/widget_test.go @@ -140,7 +140,7 @@ var msgWdgupdated = common.ResponseMsgWidget{ } // Test /models endpoints -func TestSimulationModelEndpoints(t *testing.T) { +func TestWidgetEndpoints(t *testing.T) { db := common.DummyInitDB() defer db.Close()