mirror of
https://git.rwth-aachen.de/acs/public/villas/web-backend-go/
synced 2025-03-30 00:00:12 +01:00
revision of simulator package
- move amqp endpoint implementation to amqp package - improve code coverage of simulator testing - remove some unnecessary code from package implementation
This commit is contained in:
parent
75c33c71a1
commit
c0b8a6be80
7 changed files with 126 additions and 95 deletions
61
amqp/amqp_endpoints.go
Normal file
61
amqp/amqp_endpoints.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package amqp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/database"
|
||||||
|
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/helper"
|
||||||
|
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterAMQPEndpoint(r *gin.RouterGroup) {
|
||||||
|
r.POST("/:simulatorID/action", sendActionToSimulator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendActionToSimulator godoc
|
||||||
|
// @Summary Send an action to simulator (only available if backend server is started with -amqp parameter)
|
||||||
|
// @ID sendActionToSimulator
|
||||||
|
// @Tags AMQP
|
||||||
|
// @Produce json
|
||||||
|
// @Param inputAction query string true "Action for simulator"
|
||||||
|
// @Success 200 {object} docs.ResponseError "Action sent successfully"
|
||||||
|
// @Failure 400 {object} docs.ResponseError "Bad request"
|
||||||
|
// @Failure 404 {object} docs.ResponseError "Not found"
|
||||||
|
// @Failure 422 {object} docs.ResponseError "Unprocessable entity"
|
||||||
|
// @Failure 500 {object} docs.ResponseError "Internal server error"
|
||||||
|
// @Param simulatorID path int true "Simulator ID"
|
||||||
|
// @Router /simulators/{simulatorID}/action [post]
|
||||||
|
func sendActionToSimulator(c *gin.Context) {
|
||||||
|
|
||||||
|
ok, s := simulator.CheckPermissions(c, database.ModelSimulatorAction, database.Update, true)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var actions []Action
|
||||||
|
err := c.BindJSON(&actions)
|
||||||
|
if err != nil {
|
||||||
|
helper.BadRequestError(c, "Error binding form data to JSON: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
for _, action := range actions {
|
||||||
|
if action.When == 0 {
|
||||||
|
action.When = float32(now.Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = SendActionAMQP(action, s.UUID)
|
||||||
|
if err != nil {
|
||||||
|
helper.InternalServerError(c, "Unable to send actions to simulator: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": true,
|
||||||
|
"message": "OK.",
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,12 +1,9 @@
|
||||||
package simulator
|
package simulator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/amqp"
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/helper"
|
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/helper"
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/database"
|
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/database"
|
||||||
)
|
)
|
||||||
|
@ -18,10 +15,6 @@ func RegisterSimulatorEndpoints(r *gin.RouterGroup) {
|
||||||
r.GET("/:simulatorID", getSimulator)
|
r.GET("/:simulatorID", getSimulator)
|
||||||
r.DELETE("/:simulatorID", deleteSimulator)
|
r.DELETE("/:simulatorID", deleteSimulator)
|
||||||
r.GET("/:simulatorID/models", getModelsOfSimulator)
|
r.GET("/:simulatorID/models", getModelsOfSimulator)
|
||||||
// register action endpoint only if AMQP client is used
|
|
||||||
if database.WITH_AMQP == true {
|
|
||||||
r.POST("/:simulatorID/action", sendActionToSimulator)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSimulators godoc
|
// getSimulators godoc
|
||||||
|
@ -36,19 +29,15 @@ func RegisterSimulatorEndpoints(r *gin.RouterGroup) {
|
||||||
// @Router /simulators [get]
|
// @Router /simulators [get]
|
||||||
func getSimulators(c *gin.Context) {
|
func getSimulators(c *gin.Context) {
|
||||||
|
|
||||||
ok, _ := checkPermissions(c, database.ModelSimulator, database.Read, false)
|
// Checking permission is not required here since READ access is independent of user's role
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var simulators []database.Simulator
|
var simulators []database.Simulator
|
||||||
err := db.Order("ID asc").Find(&simulators).Error
|
err := db.Order("ID asc").Find(&simulators).Error
|
||||||
if helper.DBError(c, err) {
|
if !helper.DBError(c, err) {
|
||||||
return
|
c.JSON(http.StatusOK, gin.H{"simulators": simulators})
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"simulators": simulators})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// addSimulator godoc
|
// addSimulator godoc
|
||||||
|
@ -66,7 +55,7 @@ func getSimulators(c *gin.Context) {
|
||||||
// @Router /simulators [post]
|
// @Router /simulators [post]
|
||||||
func addSimulator(c *gin.Context) {
|
func addSimulator(c *gin.Context) {
|
||||||
|
|
||||||
ok, _ := checkPermissions(c, database.ModelSimulator, database.Create, false)
|
ok, _ := CheckPermissions(c, database.ModelSimulator, database.Create, false)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -89,12 +78,10 @@ func addSimulator(c *gin.Context) {
|
||||||
|
|
||||||
// Save new simulator to DB
|
// Save new simulator to DB
|
||||||
err = newSimulator.save()
|
err = newSimulator.save()
|
||||||
if err != nil {
|
if !helper.DBError(c, err) {
|
||||||
helper.DBError(c, err)
|
c.JSON(http.StatusOK, gin.H{"simulator": newSimulator.Simulator})
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"simulator": newSimulator.Simulator})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateSimulator godoc
|
// updateSimulator godoc
|
||||||
|
@ -113,7 +100,7 @@ func addSimulator(c *gin.Context) {
|
||||||
// @Router /simulators/{simulatorID} [put]
|
// @Router /simulators/{simulatorID} [put]
|
||||||
func updateSimulator(c *gin.Context) {
|
func updateSimulator(c *gin.Context) {
|
||||||
|
|
||||||
ok, oldSimulator := checkPermissions(c, database.ModelSimulator, database.Update, true)
|
ok, oldSimulator := CheckPermissions(c, database.ModelSimulator, database.Update, true)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -132,21 +119,14 @@ func updateSimulator(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the updatedSimulator from oldSimulator
|
// Create the updatedSimulator from oldSimulator
|
||||||
updatedSimulator, err := req.updatedSimulator(oldSimulator)
|
updatedSimulator := req.updatedSimulator(oldSimulator)
|
||||||
if err != nil {
|
|
||||||
helper.BadRequestError(c, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally update the simulator in the DB
|
// Finally update the simulator in the DB
|
||||||
err = oldSimulator.update(updatedSimulator)
|
err = oldSimulator.update(updatedSimulator)
|
||||||
if err != nil {
|
if !helper.DBError(c, err) {
|
||||||
helper.DBError(c, err)
|
c.JSON(http.StatusOK, gin.H{"simulator": updatedSimulator.Simulator})
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"simulator": updatedSimulator.Simulator})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSimulator godoc
|
// getSimulator godoc
|
||||||
|
@ -163,7 +143,7 @@ func updateSimulator(c *gin.Context) {
|
||||||
// @Router /simulators/{simulatorID} [get]
|
// @Router /simulators/{simulatorID} [get]
|
||||||
func getSimulator(c *gin.Context) {
|
func getSimulator(c *gin.Context) {
|
||||||
|
|
||||||
ok, s := checkPermissions(c, database.ModelSimulator, database.Read, true)
|
ok, s := CheckPermissions(c, database.ModelSimulator, database.Read, true)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -185,18 +165,17 @@ func getSimulator(c *gin.Context) {
|
||||||
// @Router /simulators/{simulatorID} [delete]
|
// @Router /simulators/{simulatorID} [delete]
|
||||||
func deleteSimulator(c *gin.Context) {
|
func deleteSimulator(c *gin.Context) {
|
||||||
|
|
||||||
ok, s := checkPermissions(c, database.ModelSimulator, database.Delete, true)
|
ok, s := CheckPermissions(c, database.ModelSimulator, database.Delete, true)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the simulator
|
// Delete the simulator
|
||||||
err := s.delete()
|
err := s.delete()
|
||||||
if helper.DBError(c, err) {
|
if !helper.DBError(c, err) {
|
||||||
return
|
c.JSON(http.StatusOK, gin.H{"simulator": s.Simulator})
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"simulator": s.Simulator})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getModelsOfSimulator godoc
|
// getModelsOfSimulator godoc
|
||||||
|
@ -213,63 +192,15 @@ func deleteSimulator(c *gin.Context) {
|
||||||
// @Router /simulators/{simulatorID}/models [get]
|
// @Router /simulators/{simulatorID}/models [get]
|
||||||
func getModelsOfSimulator(c *gin.Context) {
|
func getModelsOfSimulator(c *gin.Context) {
|
||||||
|
|
||||||
ok, s := checkPermissions(c, database.ModelSimulator, database.Read, true)
|
ok, s := CheckPermissions(c, database.ModelSimulator, database.Read, true)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all associated simulation models
|
// get all associated simulation models
|
||||||
allModels, _, err := s.getModels()
|
allModels, _, err := s.getModels()
|
||||||
if helper.DBError(c, err) {
|
if !helper.DBError(c, err) {
|
||||||
return
|
c.JSON(http.StatusOK, gin.H{"models": allModels})
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"models": allModels})
|
|
||||||
}
|
|
||||||
|
|
||||||
// sendActionToSimulator godoc
|
|
||||||
// @Summary Send an action to simulator (only available if backend server is started with -amqp parameter)
|
|
||||||
// @ID sendActionToSimulator
|
|
||||||
// @Tags simulators
|
|
||||||
// @Produce json
|
|
||||||
// @Param inputAction query string true "Action for simulator"
|
|
||||||
// @Success 200 {object} docs.ResponseError "Action sent successfully"
|
|
||||||
// @Failure 400 {object} docs.ResponseError "Bad request"
|
|
||||||
// @Failure 404 {object} docs.ResponseError "Not found"
|
|
||||||
// @Failure 422 {object} docs.ResponseError "Unprocessable entity"
|
|
||||||
// @Failure 500 {object} docs.ResponseError "Internal server error"
|
|
||||||
// @Param simulatorID path int true "Simulator ID"
|
|
||||||
// @Router /simulators/{simulatorID}/action [post]
|
|
||||||
func sendActionToSimulator(c *gin.Context) {
|
|
||||||
|
|
||||||
ok, s := checkPermissions(c, database.ModelSimulatorAction, database.Update, true)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var actions []amqp.Action
|
|
||||||
err := c.BindJSON(&actions)
|
|
||||||
if err != nil {
|
|
||||||
helper.BadRequestError(c, "Error binding form data to JSON: "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
|
|
||||||
for _, action := range actions {
|
|
||||||
if action.When == 0 {
|
|
||||||
action.When = float32(now.Unix())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = amqp.SendActionAMQP(action, s.UUID)
|
|
||||||
if err != nil {
|
|
||||||
helper.InternalServerError(c, "Unable to send actions to simulator: "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
|
||||||
"success": true,
|
|
||||||
"message": "OK.",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,7 @@ func (s *Simulator) save() error {
|
||||||
func (s *Simulator) ByID(id uint) error {
|
func (s *Simulator) ByID(id uint) error {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
err := db.Find(s, id).Error
|
err := db.Find(s, id).Error
|
||||||
if err != nil {
|
return err
|
||||||
return fmt.Errorf("Simulator with id=%v does not exist", id)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Simulator) update(updatedSimulator Simulator) error {
|
func (s *Simulator) update(updatedSimulator Simulator) error {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkPermissions(c *gin.Context, modeltype database.ModelName, operation database.CRUD, hasID bool) (bool, Simulator) {
|
func CheckPermissions(c *gin.Context, modeltype database.ModelName, operation database.CRUD, hasID bool) (bool, Simulator) {
|
||||||
|
|
||||||
var s Simulator
|
var s Simulator
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,23 @@ func TestAddSimulatorAsAdmin(t *testing.T) {
|
||||||
"/api/authenticate", "POST", helper.AdminCredentials)
|
"/api/authenticate", "POST", helper.AdminCredentials)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// try to POST with non JSON body
|
||||||
|
// should result in bad request
|
||||||
|
code, resp, err := helper.TestEndpoint(router, token,
|
||||||
|
"/api/simulators", "POST", "This is no JSON")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
|
// try to POST malformed simulator (required fields missing, validation should fail)
|
||||||
|
// should result in an unprocessable entity
|
||||||
|
newMalformedSimulator := SimulatorRequest{
|
||||||
|
UUID: database.SimulatorB.UUID,
|
||||||
|
}
|
||||||
|
code, resp, err = helper.TestEndpoint(router, token,
|
||||||
|
"/api/simulators", "POST", helper.KeyModels{"simulator": newMalformedSimulator})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// test POST simulators/ $newSimulator
|
// test POST simulators/ $newSimulator
|
||||||
newSimulator := SimulatorRequest{
|
newSimulator := SimulatorRequest{
|
||||||
UUID: database.SimulatorA.UUID,
|
UUID: database.SimulatorA.UUID,
|
||||||
|
@ -59,7 +76,7 @@ func TestAddSimulatorAsAdmin(t *testing.T) {
|
||||||
State: database.SimulatorA.State,
|
State: database.SimulatorA.State,
|
||||||
Properties: database.SimulatorA.Properties,
|
Properties: database.SimulatorA.Properties,
|
||||||
}
|
}
|
||||||
code, resp, err := helper.TestEndpoint(router, token,
|
code, resp, err = helper.TestEndpoint(router, token,
|
||||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulator})
|
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulator})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
@ -82,6 +99,13 @@ func TestAddSimulatorAsAdmin(t *testing.T) {
|
||||||
// Compare GET's response with the newSimulator
|
// Compare GET's response with the newSimulator
|
||||||
err = helper.CompareResponse(resp, helper.KeyModels{"simulator": newSimulator})
|
err = helper.CompareResponse(resp, helper.KeyModels{"simulator": newSimulator})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Try to GET a simulator that does not exist
|
||||||
|
// should result in not found
|
||||||
|
code, resp, err = helper.TestEndpoint(router, token,
|
||||||
|
fmt.Sprintf("/api/simulators/%v", newSimulatorID+1), "GET", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddSimulatorAsUser(t *testing.T) {
|
func TestAddSimulatorAsUser(t *testing.T) {
|
||||||
|
@ -142,6 +166,13 @@ func TestUpdateSimulatorAsAdmin(t *testing.T) {
|
||||||
newSimulatorID, err := helper.GetResponseID(resp)
|
newSimulatorID, err := helper.GetResponseID(resp)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// try to PUT with non JSON body
|
||||||
|
// should result in bad request
|
||||||
|
code, resp, err = helper.TestEndpoint(router, token,
|
||||||
|
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "PUT", "This is no JSON")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// Test PUT simulators
|
// Test PUT simulators
|
||||||
newSimulator.Host = "ThisIsMyNewHost"
|
newSimulator.Host = "ThisIsMyNewHost"
|
||||||
code, resp, err = helper.TestEndpoint(router, token,
|
code, resp, err = helper.TestEndpoint(router, token,
|
||||||
|
@ -409,4 +440,11 @@ func TestGetSimulationModelsOfSimulator(t *testing.T) {
|
||||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
assert.Equal(t, 0, numberOfModels)
|
assert.Equal(t, 0, numberOfModels)
|
||||||
|
|
||||||
|
// Try to get models of simulator that does not exist
|
||||||
|
// should result in not found
|
||||||
|
code, resp, err = helper.TestEndpoint(router, token,
|
||||||
|
fmt.Sprintf("/api/simulators/%v/models", newSimulatorID+1), "GET", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ func (r *addSimulatorRequest) createSimulator() Simulator {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *updateSimulatorRequest) updatedSimulator(oldSimulator Simulator) (Simulator, error) {
|
func (r *updateSimulatorRequest) updatedSimulator(oldSimulator Simulator) Simulator {
|
||||||
// Use the old Simulator as a basis for the updated Simulator `s`
|
// Use the old Simulator as a basis for the updated Simulator `s`
|
||||||
s := oldSimulator
|
s := oldSimulator
|
||||||
|
|
||||||
|
@ -89,5 +89,5 @@ func (r *updateSimulatorRequest) updatedSimulator(oldSimulator Simulator) (Simul
|
||||||
s.Properties = r.Properties
|
s.Properties = r.Properties
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s
|
||||||
}
|
}
|
||||||
|
|
4
start.go
4
start.go
|
@ -64,6 +64,10 @@ func main() {
|
||||||
file.RegisterFileEndpoints(api.Group("/files"))
|
file.RegisterFileEndpoints(api.Group("/files"))
|
||||||
user.RegisterUserEndpoints(api.Group("/users"))
|
user.RegisterUserEndpoints(api.Group("/users"))
|
||||||
simulator.RegisterSimulatorEndpoints(api.Group("/simulators"))
|
simulator.RegisterSimulatorEndpoints(api.Group("/simulators"))
|
||||||
|
// register simulator action endpoint only if AMQP client is used
|
||||||
|
if database.WITH_AMQP == true {
|
||||||
|
amqp.RegisterAMQPEndpoint(api.Group("/simulators"))
|
||||||
|
}
|
||||||
|
|
||||||
r.GET("swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
r.GET("swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue