mirror of
https://git.rwth-aachen.de/acs/public/villas/web-backend-go/
synced 2025-03-30 00:00:12 +01:00
rename simulator to infrastructure component
This commit is contained in:
parent
9539c42e0b
commit
11a0c53b85
26 changed files with 2166 additions and 2096 deletions
|
@ -115,10 +115,10 @@ test:widget:
|
|||
variables:
|
||||
TEST_FOLDER: routes/widget
|
||||
|
||||
test:simulator:
|
||||
test:infrastructure-component:
|
||||
extends: test:database
|
||||
variables:
|
||||
TEST_FOLDER: routes/simulator
|
||||
TEST_FOLDER: routes/infrastructure-component
|
||||
|
||||
test:file:
|
||||
extends: test:database
|
||||
|
|
|
@ -24,32 +24,32 @@ package amqp
|
|||
import (
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/simulator"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/infrastructure-component"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func RegisterAMQPEndpoint(r *gin.RouterGroup) {
|
||||
r.POST("/:simulatorID/action", sendActionToSimulator)
|
||||
r.POST("/ICID/action", sendActionToIC)
|
||||
}
|
||||
|
||||
// sendActionToSimulator godoc
|
||||
// @Summary Send an action to simulator (only available if backend server is started with -amqp parameter)
|
||||
// @ID sendActionToSimulator
|
||||
// sendActionToIC godoc
|
||||
// @Summary Send an action to IC (only available if backend server is started with -amqp parameter)
|
||||
// @ID sendActionToIC
|
||||
// @Tags AMQP
|
||||
// @Produce json
|
||||
// @Param inputAction query string true "Action for simulator"
|
||||
// @Param inputAction query string true "Action for IC"
|
||||
// @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) {
|
||||
// @Param ICID path int true "InfrastructureComponent ID"
|
||||
// @Router /ic/{ICID}/action [post]
|
||||
func sendActionToIC(c *gin.Context) {
|
||||
|
||||
ok, s := simulator.CheckPermissions(c, database.ModelSimulatorAction, database.Update, true)
|
||||
ok, s := infrastructure_component.CheckPermissions(c, database.ModelInfrastructureComponentAction, database.Update, true)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ func sendActionToSimulator(c *gin.Context) {
|
|||
|
||||
err = SendActionAMQP(action, s.UUID)
|
||||
if err != nil {
|
||||
helper.InternalServerError(c, "Unable to send actions to simulator: "+err.Error())
|
||||
helper.InternalServerError(c, "Unable to send actions to IC: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,8 +77,8 @@ func ConnectAMQP(uri string) error {
|
|||
return fmt.Errorf("AMQP: failed to declare the exchange")
|
||||
}
|
||||
|
||||
// add a queue for the simulators
|
||||
simulatorQueue, err := client.channel.QueueDeclare("simulators",
|
||||
// add a queue for the ICs
|
||||
ICQueue, err := client.channel.QueueDeclare("infrastructure_components",
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
|
@ -88,13 +88,13 @@ func ConnectAMQP(uri string) error {
|
|||
return fmt.Errorf("AMQP: failed to declare the queue")
|
||||
}
|
||||
|
||||
err = client.channel.QueueBind(simulatorQueue.Name, "", VILLAS_EXCHANGE, false, nil)
|
||||
err = client.channel.QueueBind(ICQueue.Name, "", VILLAS_EXCHANGE, false, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("AMQP: failed to bind the queue")
|
||||
}
|
||||
|
||||
// consume deliveries
|
||||
client.replies, err = client.channel.Consume(simulatorQueue.Name,
|
||||
client.replies, err = client.channel.Consume(ICQueue.Name,
|
||||
"",
|
||||
false,
|
||||
false,
|
||||
|
@ -120,15 +120,15 @@ func ConnectAMQP(uri string) error {
|
|||
continue
|
||||
}
|
||||
|
||||
var sToBeUpdated database.Simulator
|
||||
var sToBeUpdated database.InfrastructureComponent
|
||||
db := database.GetDB()
|
||||
simulatorUUID := gjson.Get(content, "properties.uuid").String()
|
||||
if simulatorUUID == "" {
|
||||
log.Println("AMQP: Could not extract UUID of simulator from content of received message, SIMULATOR NOT UPDATED")
|
||||
ICUUID := gjson.Get(content, "properties.uuid").String()
|
||||
if ICUUID == "" {
|
||||
log.Println("AMQP: Could not extract UUID of IC from content of received message, COMPONENT NOT UPDATED")
|
||||
} else {
|
||||
err = db.Where("UUID = ?", simulatorUUID).Find(sToBeUpdated).Error
|
||||
err = db.Where("UUID = ?", ICUUID).Find(sToBeUpdated).Error
|
||||
if err != nil {
|
||||
log.Println("AMQP: Unable to find simulator with UUID: ", gjson.Get(content, "properties.uuid"), " DB error message: ", err)
|
||||
log.Println("AMQP: Unable to find IC with UUID: ", gjson.Get(content, "properties.uuid"), " DB error message: ", err)
|
||||
}
|
||||
|
||||
err = db.Model(&sToBeUpdated).Updates(map[string]interface{}{
|
||||
|
@ -140,10 +140,10 @@ func ConnectAMQP(uri string) error {
|
|||
"RawProperties": gjson.Get(content, "properties"),
|
||||
}).Error
|
||||
if err != nil {
|
||||
log.Println("AMQP: Unable to update simulator in DB: ", err)
|
||||
log.Println("AMQP: Unable to update IC in DB: ", err)
|
||||
}
|
||||
|
||||
log.Println("AMQP: Updated simulator with UUID ", gjson.Get(content, "properties.uuid"))
|
||||
log.Println("AMQP: Updated IC with UUID ", gjson.Get(content, "properties.uuid"))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -182,7 +182,7 @@ func SendActionAMQP(action Action, uuid string) error {
|
|||
}
|
||||
|
||||
func PingAMQP() error {
|
||||
log.Println("AMQP: sending ping command to all simulators")
|
||||
log.Println("AMQP: sending ping command to all ICs")
|
||||
|
||||
var a Action
|
||||
a.Act = "ping"
|
||||
|
|
|
@ -55,9 +55,9 @@ read_users () {
|
|||
-X GET | jq '.' && printf '\n'
|
||||
}
|
||||
|
||||
read_simulators () {
|
||||
printf "> GET "$apiBase"/simulators\n"
|
||||
curl "$apiBase"/simulators -s \
|
||||
read_infrastructure_components () {
|
||||
printf "> GET "$apiBase"/ic\n"
|
||||
curl "$apiBase"/ic -s \
|
||||
-H "Contet-Type: application/json" \
|
||||
-H "Authorization: Bearer $(< auth.jwt)" \
|
||||
-X GET | jq '.' && printf '\n'
|
||||
|
@ -114,7 +114,7 @@ login "$admin"
|
|||
#create_user "$userC"
|
||||
#read_users
|
||||
#read_user 1
|
||||
#read_simulators
|
||||
#read_infrastructure_components
|
||||
create_user "$newUserW"
|
||||
#read_users
|
||||
read_user 4
|
||||
|
|
|
@ -102,7 +102,7 @@ func GetDB() *gorm.DB {
|
|||
// TODO: Remove that function from the codebase and substitute the body
|
||||
// to the Dummy*() where it is called
|
||||
func DropTables(db *gorm.DB) {
|
||||
db.DropTableIfExists(&Simulator{})
|
||||
db.DropTableIfExists(&InfrastructureComponent{})
|
||||
db.DropTableIfExists(&Signal{})
|
||||
db.DropTableIfExists(&SimulationModel{})
|
||||
db.DropTableIfExists(&File{})
|
||||
|
@ -116,7 +116,7 @@ func DropTables(db *gorm.DB) {
|
|||
|
||||
// AutoMigrate the models
|
||||
func MigrateModels(db *gorm.DB) {
|
||||
db.AutoMigrate(&Simulator{})
|
||||
db.AutoMigrate(&InfrastructureComponent{})
|
||||
db.AutoMigrate(&Signal{})
|
||||
db.AutoMigrate(&SimulationModel{})
|
||||
db.AutoMigrate(&File{})
|
||||
|
|
|
@ -173,38 +173,38 @@ func TestScenarioAssociations(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSimulatorAssociations(t *testing.T) {
|
||||
func TestICAssociations(t *testing.T) {
|
||||
|
||||
DropTables(db)
|
||||
MigrateModels(db)
|
||||
|
||||
// create copies of global test data
|
||||
simulatorA := SimulatorA
|
||||
simulatorB := SimulatorB
|
||||
icA := ICA
|
||||
icB := ICB
|
||||
modelA := SimulationModelA
|
||||
modelB := SimulationModelB
|
||||
|
||||
// add simulators to DB
|
||||
assert.NoError(t, db.Create(&simulatorA).Error)
|
||||
assert.NoError(t, db.Create(&simulatorB).Error)
|
||||
// add ICs to DB
|
||||
assert.NoError(t, db.Create(&icA).Error)
|
||||
assert.NoError(t, db.Create(&icB).Error)
|
||||
|
||||
// add simulation models to DB
|
||||
assert.NoError(t, db.Create(&modelA).Error)
|
||||
assert.NoError(t, db.Create(&modelB).Error)
|
||||
|
||||
// add simulator has many simulation models association to DB
|
||||
assert.NoError(t, db.Model(&simulatorA).Association("SimulationModels").Append(&modelA).Error)
|
||||
assert.NoError(t, db.Model(&simulatorA).Association("SimulationModels").Append(&modelB).Error)
|
||||
// add IC has many simulation models association to DB
|
||||
assert.NoError(t, db.Model(&icA).Association("SimulationModels").Append(&modelA).Error)
|
||||
assert.NoError(t, db.Model(&icA).Association("SimulationModels").Append(&modelB).Error)
|
||||
|
||||
var simulator1 Simulator
|
||||
assert.NoError(t, db.Find(&simulator1, 1).Error, fmt.Sprintf("Find Simulator with ID=1"))
|
||||
assert.EqualValues(t, "Host_A", simulator1.Host)
|
||||
var ic1 InfrastructureComponent
|
||||
assert.NoError(t, db.Find(&ic1, 1).Error, fmt.Sprintf("Find InfrastructureComponent with ID=1"))
|
||||
assert.EqualValues(t, "Host_A", ic1.Host)
|
||||
|
||||
// Get simulation models of simulator1
|
||||
// Get simulation models of ic1
|
||||
var models []SimulationModel
|
||||
assert.NoError(t, db.Model(&simulator1).Association("SimulationModels").Find(&models).Error)
|
||||
assert.NoError(t, db.Model(&ic1).Association("SimulationModels").Find(&models).Error)
|
||||
if len(models) != 2 {
|
||||
assert.Fail(t, "Simulator Associations",
|
||||
assert.Fail(t, "InfrastructureComponent Associations",
|
||||
"Expected to have %v SimulationModels. Has %v.", 2, len(models))
|
||||
}
|
||||
}
|
||||
|
@ -225,8 +225,8 @@ func TestSimulationModelAssociations(t *testing.T) {
|
|||
fileB := FileB
|
||||
fileC := FileC
|
||||
fileD := FileD
|
||||
simulatorA := SimulatorA
|
||||
simulatorB := SimulatorB
|
||||
icA := ICA
|
||||
icB := ICB
|
||||
|
||||
// add simulation models to DB
|
||||
assert.NoError(t, db.Create(&modelA).Error)
|
||||
|
@ -244,9 +244,9 @@ func TestSimulationModelAssociations(t *testing.T) {
|
|||
assert.NoError(t, db.Create(&fileC).Error)
|
||||
assert.NoError(t, db.Create(&fileD).Error)
|
||||
|
||||
// add simulators to DB
|
||||
assert.NoError(t, db.Create(&simulatorA).Error)
|
||||
assert.NoError(t, db.Create(&simulatorB).Error)
|
||||
// add ICs to DB
|
||||
assert.NoError(t, db.Create(&icA).Error)
|
||||
assert.NoError(t, db.Create(&icB).Error)
|
||||
|
||||
// add simulation model has many signals associations
|
||||
assert.NoError(t, db.Model(&modelA).Association("InputMapping").Append(&inSignalA).Error)
|
||||
|
@ -258,17 +258,17 @@ func TestSimulationModelAssociations(t *testing.T) {
|
|||
assert.NoError(t, db.Model(&modelA).Association("Files").Append(&fileC).Error)
|
||||
assert.NoError(t, db.Model(&modelA).Association("Files").Append(&fileD).Error)
|
||||
|
||||
// associate simulation models with simulators
|
||||
assert.NoError(t, db.Model(&simulatorA).Association("SimulationModels").Append(&modelA).Error)
|
||||
assert.NoError(t, db.Model(&simulatorA).Association("SimulationModels").Append(&modelB).Error)
|
||||
// associate simulation models with IC
|
||||
assert.NoError(t, db.Model(&icA).Association("SimulationModels").Append(&modelA).Error)
|
||||
assert.NoError(t, db.Model(&icA).Association("SimulationModels").Append(&modelB).Error)
|
||||
|
||||
var model1 SimulationModel
|
||||
assert.NoError(t, db.Find(&model1, 1).Error, fmt.Sprintf("Find SimulationModel with ID=1"))
|
||||
assert.EqualValues(t, "SimulationModel_A", model1.Name)
|
||||
|
||||
// Check simulator ID
|
||||
if model1.SimulatorID != 1 {
|
||||
assert.Fail(t, "Simulation Model expected to have Simulator ID 1, but is %v", model1.SimulatorID)
|
||||
// Check IC ID
|
||||
if model1.ICID != 1 {
|
||||
assert.Fail(t, "Simulation Model expected to have InfrastructureComponent ID 1, but is %v", model1.ICID)
|
||||
}
|
||||
|
||||
// Get OutputMapping signals of model1
|
||||
|
|
|
@ -84,8 +84,8 @@ type SimulationModel struct {
|
|||
StartParameters postgres.Jsonb `json:"startParameters"`
|
||||
// ID of Scenario to which simulation model belongs
|
||||
ScenarioID uint `json:"scenarioID"`
|
||||
// ID of simulator associated with simulation model
|
||||
SimulatorID uint `json:"simulatorID"`
|
||||
// ID of IC associated with simulation model
|
||||
ICID uint `json:"icID"`
|
||||
// Mapping of output signals of the simulation model, order of signals is important
|
||||
OutputMapping []Signal `json:"-" gorm:"foreignkey:SimulationModelID"`
|
||||
// Mapping of input signals of the simulation model, order of signals is important
|
||||
|
@ -111,27 +111,27 @@ type Signal struct {
|
|||
SimulationModelID uint `json:"simulationModelID"`
|
||||
}
|
||||
|
||||
// Simulator data model
|
||||
type Simulator struct {
|
||||
// InfrastructureComponent data model
|
||||
type InfrastructureComponent struct {
|
||||
Model
|
||||
// UUID of the simulator
|
||||
// UUID of the IC
|
||||
UUID string `json:"uuid" gorm:"not null"`
|
||||
// Host if the simulator
|
||||
// Host if the IC
|
||||
Host string `json:"host" gorm:"default:''"`
|
||||
// Model type supported by the simulator
|
||||
// Model type supported by the IC
|
||||
Modeltype string `json:"modelType" gorm:"default:''"`
|
||||
// Uptime of the simulator
|
||||
// Uptime of the IC
|
||||
Uptime int `json:"uptime" gorm:"default:0"`
|
||||
// State of the simulator
|
||||
// State of the IC
|
||||
State string `json:"state" gorm:"default:''"`
|
||||
// Time of last state update
|
||||
StateUpdateAt string `json:"stateUpdateAt" gorm:"default:''"`
|
||||
// Properties of simulator as JSON string
|
||||
// Properties of IC as JSON string
|
||||
Properties postgres.Jsonb `json:"properties"`
|
||||
// Raw properties of simulator as JSON string
|
||||
// Raw properties of IC as JSON string
|
||||
RawProperties postgres.Jsonb `json:"rawProperties"`
|
||||
// SimulationModels in which the simulator is used
|
||||
SimulationModels []SimulationModel `json:"-" gorm:"foreignkey:SimulatorID"`
|
||||
// SimulationModels in which the IC is used
|
||||
SimulationModels []SimulationModel `json:"-" gorm:"foreignkey:ICID"`
|
||||
}
|
||||
|
||||
// Dashboard data model
|
||||
|
|
|
@ -38,8 +38,8 @@ type ModelName string
|
|||
const ModelUser = ModelName("user")
|
||||
const ModelUsers = ModelName("users")
|
||||
const ModelScenario = ModelName("scenario")
|
||||
const ModelSimulator = ModelName("simulator")
|
||||
const ModelSimulatorAction = ModelName("simulatoraction")
|
||||
const ModelInfrastructureComponent = ModelName("ic")
|
||||
const ModelInfrastructureComponentAction = ModelName("icaction")
|
||||
const ModelDashboard = ModelName("dashboard")
|
||||
const ModelWidget = ModelName("widget")
|
||||
const ModelSimulationModel = ModelName("simulationmodel")
|
||||
|
@ -73,40 +73,40 @@ var none = Permission{Create: false, Read: false, Update: false, Delete: false}
|
|||
// allowed to do a certain action on a given model based on his role
|
||||
var Roles = RoleActions{
|
||||
"Admin": {
|
||||
ModelUser: crud,
|
||||
ModelUsers: crud,
|
||||
ModelScenario: crud,
|
||||
ModelSimulationModel: crud,
|
||||
ModelSimulator: crud,
|
||||
ModelSimulatorAction: crud,
|
||||
ModelWidget: crud,
|
||||
ModelDashboard: crud,
|
||||
ModelSignal: crud,
|
||||
ModelFile: crud,
|
||||
ModelUser: crud,
|
||||
ModelUsers: crud,
|
||||
ModelScenario: crud,
|
||||
ModelSimulationModel: crud,
|
||||
ModelInfrastructureComponent: crud,
|
||||
ModelInfrastructureComponentAction: crud,
|
||||
ModelWidget: crud,
|
||||
ModelDashboard: crud,
|
||||
ModelSignal: crud,
|
||||
ModelFile: crud,
|
||||
},
|
||||
"User": {
|
||||
ModelUser: _ru_,
|
||||
ModelUsers: none,
|
||||
ModelScenario: crud,
|
||||
ModelSimulationModel: crud,
|
||||
ModelSimulator: _r__,
|
||||
ModelSimulatorAction: _ru_,
|
||||
ModelWidget: crud,
|
||||
ModelDashboard: crud,
|
||||
ModelSignal: crud,
|
||||
ModelFile: crud,
|
||||
ModelUser: _ru_,
|
||||
ModelUsers: none,
|
||||
ModelScenario: crud,
|
||||
ModelSimulationModel: crud,
|
||||
ModelInfrastructureComponent: _r__,
|
||||
ModelInfrastructureComponentAction: _ru_,
|
||||
ModelWidget: crud,
|
||||
ModelDashboard: crud,
|
||||
ModelSignal: crud,
|
||||
ModelFile: crud,
|
||||
},
|
||||
"Guest": {
|
||||
ModelScenario: _r__,
|
||||
ModelSimulationModel: _r__,
|
||||
ModelDashboard: _r__,
|
||||
ModelWidget: _r__,
|
||||
ModelSimulator: _r__,
|
||||
ModelSimulatorAction: _r__,
|
||||
ModelUser: _ru_,
|
||||
ModelUsers: none,
|
||||
ModelSignal: _r__,
|
||||
ModelFile: _r__,
|
||||
ModelScenario: _r__,
|
||||
ModelSimulationModel: _r__,
|
||||
ModelDashboard: _r__,
|
||||
ModelWidget: _r__,
|
||||
ModelInfrastructureComponent: _r__,
|
||||
ModelInfrastructureComponentAction: _r__,
|
||||
ModelUser: _ru_,
|
||||
ModelUsers: none,
|
||||
ModelSignal: _r__,
|
||||
ModelFile: _r__,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -55,12 +55,12 @@ var UserB = User{Username: "User_B", Password: string(pwB),
|
|||
var UserC = User{Username: "User_C", Password: string(pwC),
|
||||
Role: "Guest", Mail: "User_C@example.com", Active: true}
|
||||
|
||||
// Simulators
|
||||
// Infrastructure components
|
||||
|
||||
var propertiesA = json.RawMessage(`{"name" : "TestNameA", "category" : "CategoryA", "location" : "anywhere on earth", "type": "dummy"}`)
|
||||
var propertiesB = json.RawMessage(`{"name" : "TestNameB", "category" : "CategoryB", "location" : "where ever you want", "type": "generic"}`)
|
||||
var propertiesA = json.RawMessage(`{"name" : "DPsim simulator", "category" : "Simulator", "location" : "ACSlab", "type": "DPsim"}`)
|
||||
var propertiesB = json.RawMessage(`{"name" : "VILLASnode gateway", "category" : "Gateway", "location" : "ACSlab", "type": "VILLASnode"}`)
|
||||
|
||||
var SimulatorA = Simulator{
|
||||
var ICA = InfrastructureComponent{
|
||||
UUID: "4854af30-325f-44a5-ad59-b67b2597de68",
|
||||
Host: "Host_A",
|
||||
Modeltype: "ModelTypeA",
|
||||
|
@ -71,7 +71,7 @@ var SimulatorA = Simulator{
|
|||
RawProperties: postgres.Jsonb{propertiesA},
|
||||
}
|
||||
|
||||
var SimulatorB = Simulator{
|
||||
var ICB = InfrastructureComponent{
|
||||
UUID: "7be0322d-354e-431e-84bd-ae4c9633138b",
|
||||
Host: "Host_B",
|
||||
Modeltype: "ModelTypeB",
|
||||
|
@ -316,8 +316,8 @@ func DBAddTestData(db *gorm.DB) error {
|
|||
userB := UserB
|
||||
userC := UserC
|
||||
|
||||
simulatorA := SimulatorA
|
||||
simulatorB := SimulatorB
|
||||
ICA := ICA
|
||||
ICB := ICB
|
||||
|
||||
scenarioA := ScenarioA
|
||||
scenarioB := ScenarioB
|
||||
|
@ -354,9 +354,9 @@ func DBAddTestData(db *gorm.DB) error {
|
|||
// add Guest user to DB
|
||||
err = db.Create(&userC).Error
|
||||
|
||||
// Simulators
|
||||
err = db.Create(&simulatorA).Error
|
||||
err = db.Create(&simulatorB).Error
|
||||
// ICs
|
||||
err = db.Create(&ICA).Error
|
||||
err = db.Create(&ICB).Error
|
||||
|
||||
// Scenarios
|
||||
err = db.Create(&scenarioA).Error
|
||||
|
@ -425,9 +425,9 @@ func DBAddTestData(db *gorm.DB) error {
|
|||
err = db.Model(&modelA).Association("Files").Append(&fileC).Error
|
||||
err = db.Model(&modelA).Association("Files").Append(&fileD).Error
|
||||
|
||||
// Simulator HM SimulationModels
|
||||
err = db.Model(&simulatorA).Association("SimulationModels").Append(&modelA).Error
|
||||
err = db.Model(&simulatorA).Association("SimulationModels").Append(&modelB).Error
|
||||
// InfrastructureComponent HM SimulationModels
|
||||
err = db.Model(&ICA).Association("SimulationModels").Append(&modelA).Error
|
||||
err = db.Model(&ICA).Association("SimulationModels").Append(&modelB).Error
|
||||
|
||||
// Widget HM Files
|
||||
err = db.Model(&widgetA).Association("Files").Append(&fileA).Error
|
||||
|
|
1196
doc/api/docs.go
1196
doc/api/docs.go
File diff suppressed because it is too large
Load diff
|
@ -46,12 +46,12 @@ type ResponseUser struct {
|
|||
user database.User
|
||||
}
|
||||
|
||||
type ResponseSimulators struct {
|
||||
simulators []database.Simulator
|
||||
type ResponseICs struct {
|
||||
ics []database.InfrastructureComponent
|
||||
}
|
||||
|
||||
type ResponseSimulator struct {
|
||||
simulator database.Simulator
|
||||
type ResponseIC struct {
|
||||
ic database.InfrastructureComponent
|
||||
}
|
||||
|
||||
type ResponseScenarios struct {
|
||||
|
|
1194
doc/api/swagger.json
1194
doc/api/swagger.json
File diff suppressed because it is too large
Load diff
|
@ -75,6 +75,35 @@ definitions:
|
|||
description: ID of widget to which file belongs
|
||||
type: integer
|
||||
type: object
|
||||
database.InfrastructureComponent:
|
||||
properties:
|
||||
host:
|
||||
description: Host if the IC
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
modelType:
|
||||
description: Model type supported by the IC
|
||||
type: string
|
||||
properties:
|
||||
description: Properties of IC as JSON string
|
||||
type: string
|
||||
rawProperties:
|
||||
description: Raw properties of IC as JSON string
|
||||
type: string
|
||||
state:
|
||||
description: State of the IC
|
||||
type: string
|
||||
stateUpdateAt:
|
||||
description: Time of last state update
|
||||
type: string
|
||||
uptime:
|
||||
description: Uptime of the IC
|
||||
type: integer
|
||||
uuid:
|
||||
description: UUID of the IC
|
||||
type: string
|
||||
type: object
|
||||
database.Scenario:
|
||||
properties:
|
||||
id:
|
||||
|
@ -111,6 +140,9 @@ definitions:
|
|||
type: object
|
||||
database.SimulationModel:
|
||||
properties:
|
||||
icID:
|
||||
description: ID of IC associated with simulation model
|
||||
type: integer
|
||||
id:
|
||||
type: integer
|
||||
inputLength:
|
||||
|
@ -125,42 +157,13 @@ definitions:
|
|||
scenarioID:
|
||||
description: ID of Scenario to which simulation model belongs
|
||||
type: integer
|
||||
simulatorID:
|
||||
description: ID of simulator associated with simulation model
|
||||
selectedModelFileID:
|
||||
description: Currently selected simulation model FileID
|
||||
type: integer
|
||||
startParameters:
|
||||
description: Start parameters of simulation model as JSON
|
||||
type: string
|
||||
type: object
|
||||
database.Simulator:
|
||||
properties:
|
||||
host:
|
||||
description: Host if the simulator
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
modelType:
|
||||
description: Model type supported by the simulator
|
||||
type: string
|
||||
properties:
|
||||
description: Properties of simulator as JSON string
|
||||
type: string
|
||||
rawProperties:
|
||||
description: Raw properties of simulator as JSON string
|
||||
type: string
|
||||
state:
|
||||
description: State of the simulator
|
||||
type: string
|
||||
stateUpdateAt:
|
||||
description: Time of last state update
|
||||
type: string
|
||||
uptime:
|
||||
description: Uptime of the simulator
|
||||
type: integer
|
||||
uuid:
|
||||
description: UUID of the simulator
|
||||
type: string
|
||||
type: object
|
||||
database.User:
|
||||
properties:
|
||||
active:
|
||||
|
@ -204,6 +207,9 @@ definitions:
|
|||
name:
|
||||
description: Name of widget
|
||||
type: string
|
||||
signalIDs:
|
||||
description: IDs of signals that widget uses
|
||||
type: string
|
||||
type:
|
||||
description: Type of widget
|
||||
type: string
|
||||
|
@ -265,6 +271,19 @@ definitions:
|
|||
$ref: '#/definitions/database.File'
|
||||
type: array
|
||||
type: object
|
||||
docs.ResponseIC:
|
||||
properties:
|
||||
ic:
|
||||
$ref: '#/definitions/database.InfrastructureComponent'
|
||||
type: object
|
||||
type: object
|
||||
docs.ResponseICs:
|
||||
properties:
|
||||
ics:
|
||||
items:
|
||||
$ref: '#/definitions/database.InfrastructureComponent'
|
||||
type: array
|
||||
type: object
|
||||
docs.ResponseScenario:
|
||||
properties:
|
||||
scenario:
|
||||
|
@ -304,19 +323,6 @@ definitions:
|
|||
$ref: '#/definitions/database.SimulationModel'
|
||||
type: array
|
||||
type: object
|
||||
docs.ResponseSimulator:
|
||||
properties:
|
||||
simulator:
|
||||
$ref: '#/definitions/database.Simulator'
|
||||
type: object
|
||||
type: object
|
||||
docs.ResponseSimulators:
|
||||
properties:
|
||||
simulators:
|
||||
items:
|
||||
$ref: '#/definitions/database.Simulator'
|
||||
type: array
|
||||
type: object
|
||||
docs.ResponseUser:
|
||||
properties:
|
||||
user:
|
||||
|
@ -343,6 +349,49 @@ definitions:
|
|||
$ref: '#/definitions/database.Widget'
|
||||
type: array
|
||||
type: object
|
||||
infrastructure_component.addICRequest:
|
||||
properties:
|
||||
ic:
|
||||
$ref: '#/definitions/infrastructure_component.validNewIC'
|
||||
type: object
|
||||
type: object
|
||||
infrastructure_component.updateICRequest:
|
||||
properties:
|
||||
ic:
|
||||
$ref: '#/definitions/infrastructure_component.validUpdatedIC'
|
||||
type: object
|
||||
type: object
|
||||
infrastructure_component.validNewIC:
|
||||
properties:
|
||||
Host:
|
||||
type: string
|
||||
Modeltype:
|
||||
type: string
|
||||
Properties:
|
||||
type: string
|
||||
State:
|
||||
type: string
|
||||
UUID:
|
||||
type: string
|
||||
required:
|
||||
- Host
|
||||
- Modeltype
|
||||
- Properties
|
||||
- UUID
|
||||
type: object
|
||||
infrastructure_component.validUpdatedIC:
|
||||
properties:
|
||||
Host:
|
||||
type: string
|
||||
Modeltype:
|
||||
type: string
|
||||
Properties:
|
||||
type: string
|
||||
State:
|
||||
type: string
|
||||
UUID:
|
||||
type: string
|
||||
type: object
|
||||
scenario.addScenarioRequest:
|
||||
properties:
|
||||
scenario:
|
||||
|
@ -429,72 +478,33 @@ definitions:
|
|||
type: object
|
||||
simulationmodel.validNewSimulationModel:
|
||||
properties:
|
||||
ICID:
|
||||
type: integer
|
||||
Name:
|
||||
type: string
|
||||
ScenarioID:
|
||||
type: integer
|
||||
SimulatorID:
|
||||
SelectedModelFileID:
|
||||
type: integer
|
||||
StartParameters:
|
||||
type: string
|
||||
required:
|
||||
- ICID
|
||||
- Name
|
||||
- ScenarioID
|
||||
- SimulatorID
|
||||
- StartParameters
|
||||
type: object
|
||||
simulationmodel.validUpdatedSimulationModel:
|
||||
properties:
|
||||
ICID:
|
||||
type: integer
|
||||
Name:
|
||||
type: string
|
||||
SimulatorID:
|
||||
SelectedModelFileID:
|
||||
type: integer
|
||||
StartParameters:
|
||||
type: string
|
||||
type: object
|
||||
simulator.addSimulatorRequest:
|
||||
properties:
|
||||
simulator:
|
||||
$ref: '#/definitions/simulator.validNewSimulator'
|
||||
type: object
|
||||
type: object
|
||||
simulator.updateSimulatorRequest:
|
||||
properties:
|
||||
simulator:
|
||||
$ref: '#/definitions/simulator.validUpdatedSimulator'
|
||||
type: object
|
||||
type: object
|
||||
simulator.validNewSimulator:
|
||||
properties:
|
||||
Host:
|
||||
type: string
|
||||
Modeltype:
|
||||
type: string
|
||||
Properties:
|
||||
type: string
|
||||
State:
|
||||
type: string
|
||||
UUID:
|
||||
type: string
|
||||
required:
|
||||
- Host
|
||||
- Modeltype
|
||||
- Properties
|
||||
- UUID
|
||||
type: object
|
||||
simulator.validUpdatedSimulator:
|
||||
properties:
|
||||
Host:
|
||||
type: string
|
||||
Modeltype:
|
||||
type: string
|
||||
Properties:
|
||||
type: string
|
||||
State:
|
||||
type: string
|
||||
UUID:
|
||||
type: string
|
||||
type: object
|
||||
user.addUserRequest:
|
||||
properties:
|
||||
user:
|
||||
|
@ -576,6 +586,10 @@ definitions:
|
|||
type: integer
|
||||
Name:
|
||||
type: string
|
||||
SignalIDs:
|
||||
items:
|
||||
type: integer
|
||||
type: array
|
||||
Type:
|
||||
type: string
|
||||
Width:
|
||||
|
@ -607,6 +621,10 @@ definitions:
|
|||
type: integer
|
||||
Name:
|
||||
type: string
|
||||
SignalIDs:
|
||||
items:
|
||||
type: integer
|
||||
type: array
|
||||
Type:
|
||||
type: string
|
||||
Width:
|
||||
|
@ -1171,6 +1189,322 @@ paths:
|
|||
summary: Get health status of backend
|
||||
tags:
|
||||
- healthz
|
||||
/ic:
|
||||
get:
|
||||
operationId: getICs
|
||||
parameters:
|
||||
- description: Authorization token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: ICs requested
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseICs'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Get all infrastructure components
|
||||
tags:
|
||||
- infrastructure-components
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
operationId: addIC
|
||||
parameters:
|
||||
- description: Authorization token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
- description: Infrastructure Component to be added
|
||||
in: body
|
||||
name: inputIC
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/infrastructure_component.addICRequest'
|
||||
type: object
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Infrastructure Component that was added
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseIC'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Add an infrastructure component
|
||||
tags:
|
||||
- infrastructure-components
|
||||
/ic/{ICID}:
|
||||
delete:
|
||||
operationId: deleteIC
|
||||
parameters:
|
||||
- description: Authorization token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
- description: Infrastructure Component ID
|
||||
in: path
|
||||
name: ICID
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Infrastructure Component that was deleted
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseIC'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Delete an infrastructure component
|
||||
tags:
|
||||
- infrastructure-components
|
||||
get:
|
||||
operationId: getIC
|
||||
parameters:
|
||||
- description: Authorization token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
- description: Infrastructure Component ID
|
||||
in: path
|
||||
name: ICID
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Infrastructure Component that was requested
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseIC'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Get infrastructure component
|
||||
tags:
|
||||
- infrastructure-components
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
operationId: updateIC
|
||||
parameters:
|
||||
- description: Authorization token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
- description: InfrastructureComponent to be updated
|
||||
in: body
|
||||
name: inputIC
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/infrastructure_component.updateICRequest'
|
||||
type: object
|
||||
- description: InfrastructureComponent ID
|
||||
in: path
|
||||
name: ICID
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Infrastructure Component that was updated
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseIC'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Update an infrastructure component
|
||||
tags:
|
||||
- infrastructure-components
|
||||
/ic/{ICID}/action:
|
||||
post:
|
||||
operationId: sendActionToIC
|
||||
parameters:
|
||||
- description: Action for IC
|
||||
in: query
|
||||
name: inputAction
|
||||
required: true
|
||||
type: string
|
||||
- description: InfrastructureComponent ID
|
||||
in: path
|
||||
name: ICID
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Action sent successfully
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Send an action to IC (only available if backend server is started with
|
||||
-amqp parameter)
|
||||
tags:
|
||||
- AMQP
|
||||
/ic/{ICID}/models:
|
||||
get:
|
||||
operationId: getConfigsOfIC
|
||||
parameters:
|
||||
- description: Authorization token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
- description: Infrastructure Component ID
|
||||
in: path
|
||||
name: ICID
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Configs requested by user
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseSimulationModels'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Get all configurations of the infrastructure component
|
||||
tags:
|
||||
- infrastructure-components
|
||||
/metrics:
|
||||
get:
|
||||
operationId: getMetrics
|
||||
|
@ -1232,7 +1566,7 @@ paths:
|
|||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
- description: Simulation model to be added incl. IDs of scenario and simulator
|
||||
- description: Simulation model to be added incl. IDs of scenario and IC
|
||||
in: body
|
||||
name: inputSimulationModel
|
||||
required: true
|
||||
|
@ -1990,322 +2324,6 @@ paths:
|
|||
summary: Update a signal
|
||||
tags:
|
||||
- signals
|
||||
/simulators:
|
||||
get:
|
||||
operationId: getSimulators
|
||||
parameters:
|
||||
- description: Authorization token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Simulators requested
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseSimulators'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Get all simulators
|
||||
tags:
|
||||
- simulators
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
operationId: addSimulator
|
||||
parameters:
|
||||
- description: Authorization token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
- description: Simulator to be added
|
||||
in: body
|
||||
name: inputSimulator
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/simulator.addSimulatorRequest'
|
||||
type: object
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Simulator that was added
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseSimulator'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Add a simulator
|
||||
tags:
|
||||
- simulators
|
||||
/simulators/{simulatorID}:
|
||||
delete:
|
||||
operationId: deleteSimulator
|
||||
parameters:
|
||||
- description: Authorization token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
- description: Simulator ID
|
||||
in: path
|
||||
name: simulatorID
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Simulator that was deleted
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseSimulator'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Delete a simulator
|
||||
tags:
|
||||
- simulators
|
||||
get:
|
||||
operationId: getSimulator
|
||||
parameters:
|
||||
- description: Authorization token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
- description: Simulator ID
|
||||
in: path
|
||||
name: simulatorID
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Simulator that was requested
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseSimulator'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Get simulator
|
||||
tags:
|
||||
- simulators
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
operationId: updateSimulator
|
||||
parameters:
|
||||
- description: Authorization token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
- description: Simulator to be updated
|
||||
in: body
|
||||
name: inputSimulator
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/simulator.updateSimulatorRequest'
|
||||
type: object
|
||||
- description: Simulator ID
|
||||
in: path
|
||||
name: simulatorID
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Simulator that was updated
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseSimulator'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Update a simulator
|
||||
tags:
|
||||
- simulators
|
||||
/simulators/{simulatorID}/action:
|
||||
post:
|
||||
operationId: sendActionToSimulator
|
||||
parameters:
|
||||
- description: Action for simulator
|
||||
in: query
|
||||
name: inputAction
|
||||
required: true
|
||||
type: string
|
||||
- description: Simulator ID
|
||||
in: path
|
||||
name: simulatorID
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Action sent successfully
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Send an action to simulator (only available if backend server is started
|
||||
with -amqp parameter)
|
||||
tags:
|
||||
- AMQP
|
||||
/simulators/{simulatorID}/models:
|
||||
get:
|
||||
operationId: getModelsOfSimulator
|
||||
parameters:
|
||||
- description: Authorization token
|
||||
in: header
|
||||
name: Authorization
|
||||
required: true
|
||||
type: string
|
||||
- description: Simulator ID
|
||||
in: path
|
||||
name: simulatorID
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Simulation models requested by user
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseSimulationModels'
|
||||
type: object
|
||||
"400":
|
||||
description: Bad request
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"404":
|
||||
description: Not found
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"422":
|
||||
description: Unprocessable entity
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
$ref: '#/definitions/docs.ResponseError'
|
||||
type: object
|
||||
summary: Get all simulation models in which the simulator is used
|
||||
tags:
|
||||
- simulators
|
||||
/users:
|
||||
get:
|
||||
operationId: GetUsers
|
||||
|
|
|
@ -28,9 +28,9 @@ import (
|
|||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/dashboard"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/infrastructure-component"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/simulationmodel"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/simulator"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/widget"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -52,11 +52,11 @@ var db *gorm.DB
|
|||
type SimulationModelRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
ScenarioID uint `json:"scenarioID,omitempty"`
|
||||
SimulatorID uint `json:"simulatorID,omitempty"`
|
||||
ICID uint `json:"icID,omitempty"`
|
||||
StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
|
||||
}
|
||||
|
||||
type SimulatorRequest struct {
|
||||
type ICRequest struct {
|
||||
UUID string `json:"uuid,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Modeltype string `json:"modelType,omitempty"`
|
||||
|
@ -91,25 +91,25 @@ type WidgetRequest struct {
|
|||
CustomProperties postgres.Jsonb `json:"customProperties,omitempty"`
|
||||
}
|
||||
|
||||
func addScenarioAndSimulatorAndSimulationModelAndDashboardAndWidget() (scenarioID uint, simulatorID uint, simulationModelID uint, dashboardID uint, widgetID uint) {
|
||||
func addScenarioAndICAndSimulationModelAndDashboardAndWidget() (scenarioID uint, ICID uint, simulationModelID uint, dashboardID uint, widgetID uint) {
|
||||
|
||||
// authenticate as admin
|
||||
token, _ := helper.AuthenticateForTest(router,
|
||||
"/api/authenticate", "POST", helper.AdminCredentials)
|
||||
|
||||
// POST $newSimulatorA
|
||||
newSimulatorA := SimulatorRequest{
|
||||
UUID: database.SimulatorA.UUID,
|
||||
Host: database.SimulatorA.Host,
|
||||
Modeltype: database.SimulatorA.Modeltype,
|
||||
State: database.SimulatorA.State,
|
||||
Properties: database.SimulatorA.Properties,
|
||||
// POST $newICA
|
||||
newICA := ICRequest{
|
||||
UUID: database.ICA.UUID,
|
||||
Host: database.ICA.Host,
|
||||
Modeltype: database.ICA.Modeltype,
|
||||
State: database.ICA.State,
|
||||
Properties: database.ICA.Properties,
|
||||
}
|
||||
_, resp, _ := helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulatorA})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newICA})
|
||||
|
||||
// Read newSimulator's ID from the response
|
||||
newSimulatorID, _ := helper.GetResponseID(resp)
|
||||
// Read newIC's ID from the response
|
||||
newICID, _ := helper.GetResponseID(resp)
|
||||
|
||||
// authenticate as normal user
|
||||
token, _ = helper.AuthenticateForTest(router,
|
||||
|
@ -131,7 +131,7 @@ func addScenarioAndSimulatorAndSimulationModelAndDashboardAndWidget() (scenarioI
|
|||
newSimulationModel := SimulationModelRequest{
|
||||
Name: database.SimulationModelA.Name,
|
||||
ScenarioID: uint(newScenarioID),
|
||||
SimulatorID: uint(newSimulatorID),
|
||||
ICID: uint(newICID),
|
||||
StartParameters: database.SimulationModelA.StartParameters,
|
||||
}
|
||||
_, resp, _ = helper.TestEndpoint(router, token,
|
||||
|
@ -177,7 +177,7 @@ func addScenarioAndSimulatorAndSimulationModelAndDashboardAndWidget() (scenarioI
|
|||
_, resp, _ = helper.TestEndpoint(router, token,
|
||||
fmt.Sprintf("/api/scenarios/%v/user?username=User_C", newScenarioID), "PUT", nil)
|
||||
|
||||
return uint(newScenarioID), uint(newSimulatorID), uint(newSimulationModelID), uint(newDashboardID), uint(newWidgetID)
|
||||
return uint(newScenarioID), uint(newICID), uint(newSimulationModelID), uint(newDashboardID), uint(newWidgetID)
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
@ -202,9 +202,9 @@ func TestMain(m *testing.M) {
|
|||
// scenario endpoints required here to first add a scenario to the DB
|
||||
// that can be associated with a new simulation model
|
||||
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
|
||||
// simulator endpoints required here to first add a simulator to the DB
|
||||
// IC endpoints required here to first add a IC to the DB
|
||||
// that can be associated with a new simulation model
|
||||
simulator.RegisterSimulatorEndpoints(api.Group("/simulators"))
|
||||
infrastructure_component.RegisterICEndpoints(api.Group("/ic"))
|
||||
// dashboard endpoints required here to first add a dashboard to the DB
|
||||
// that can be associated with a new widget
|
||||
dashboard.RegisterDashboardEndpoints(api.Group("/dashboards"))
|
||||
|
@ -224,7 +224,7 @@ func TestAddFile(t *testing.T) {
|
|||
|
||||
// prepare the content of the DB for testing
|
||||
// using the respective endpoints of the API
|
||||
_, _, simulationModelID, _, widgetID := addScenarioAndSimulatorAndSimulationModelAndDashboardAndWidget()
|
||||
_, _, simulationModelID, _, widgetID := addScenarioAndICAndSimulationModelAndDashboardAndWidget()
|
||||
|
||||
// authenticate as userB who has no access to the elements in the DB
|
||||
token, err := helper.AuthenticateForTest(router,
|
||||
|
@ -340,7 +340,7 @@ func TestUpdateFile(t *testing.T) {
|
|||
|
||||
// prepare the content of the DB for testing
|
||||
// using the respective endpoints of the API
|
||||
_, _, simulationModelID, _, _ := addScenarioAndSimulatorAndSimulationModelAndDashboardAndWidget()
|
||||
_, _, simulationModelID, _, _ := addScenarioAndICAndSimulationModelAndDashboardAndWidget()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router,
|
||||
|
@ -474,7 +474,7 @@ func TestDeleteFile(t *testing.T) {
|
|||
|
||||
// prepare the content of the DB for testing
|
||||
// using the respective endpoints of the API
|
||||
_, _, simulationModelID, _, widgetID := addScenarioAndSimulatorAndSimulationModelAndDashboardAndWidget()
|
||||
_, _, simulationModelID, _, widgetID := addScenarioAndICAndSimulationModelAndDashboardAndWidget()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router,
|
||||
|
@ -625,7 +625,7 @@ func TestGetAllFilesOfSimulationModel(t *testing.T) {
|
|||
|
||||
// prepare the content of the DB for testing
|
||||
// using the respective endpoints of the API
|
||||
_, _, simulationModelID, _, widgetID := addScenarioAndSimulatorAndSimulationModelAndDashboardAndWidget()
|
||||
_, _, simulationModelID, _, widgetID := addScenarioAndICAndSimulationModelAndDashboardAndWidget()
|
||||
|
||||
// authenticate as userB who has no access to the elements in the DB
|
||||
token, err := helper.AuthenticateForTest(router,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/** Simulator package, endpoints.
|
||||
/** InfrastructureComponent package, endpoints.
|
||||
*
|
||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
||||
|
@ -19,7 +19,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
package simulator
|
||||
package infrastructure_component
|
||||
|
||||
import (
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||
|
@ -29,61 +29,61 @@ import (
|
|||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||
)
|
||||
|
||||
func RegisterSimulatorEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("", getSimulators)
|
||||
r.POST("", addSimulator)
|
||||
r.PUT("/:simulatorID", updateSimulator)
|
||||
r.GET("/:simulatorID", getSimulator)
|
||||
r.DELETE("/:simulatorID", deleteSimulator)
|
||||
r.GET("/:simulatorID/models", getModelsOfSimulator)
|
||||
func RegisterICEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("", getICs)
|
||||
r.POST("", addIC)
|
||||
r.PUT("/:ICID", updateIC)
|
||||
r.GET("/:ICID", getIC)
|
||||
r.DELETE("/:ICID", deleteIC)
|
||||
r.GET("/:ICID/models", getConfigsOfIC)
|
||||
}
|
||||
|
||||
// getSimulators godoc
|
||||
// @Summary Get all simulators
|
||||
// @ID getSimulators
|
||||
// @Tags simulators
|
||||
// getICs godoc
|
||||
// @Summary Get all infrastructure components
|
||||
// @ID getICs
|
||||
// @Tags infrastructure-components
|
||||
// @Produce json
|
||||
// @Success 200 {object} docs.ResponseSimulators "Simulators requested"
|
||||
// @Success 200 {object} docs.ResponseICs "ICs requested"
|
||||
// @Failure 404 {object} docs.ResponseError "Not found"
|
||||
// @Failure 422 {object} docs.ResponseError "Unprocessable entity"
|
||||
// @Failure 500 {object} docs.ResponseError "Internal server error"
|
||||
// @Param Authorization header string true "Authorization token"
|
||||
// @Router /simulators [get]
|
||||
func getSimulators(c *gin.Context) {
|
||||
// @Router /ic [get]
|
||||
func getICs(c *gin.Context) {
|
||||
|
||||
// Checking permission is not required here since READ access is independent of user's role
|
||||
|
||||
db := database.GetDB()
|
||||
var simulators []database.Simulator
|
||||
err := db.Order("ID asc").Find(&simulators).Error
|
||||
var ics []database.InfrastructureComponent
|
||||
err := db.Order("ID asc").Find(&ics).Error
|
||||
if !helper.DBError(c, err) {
|
||||
c.JSON(http.StatusOK, gin.H{"simulators": simulators})
|
||||
c.JSON(http.StatusOK, gin.H{"ics": ics})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// addSimulator godoc
|
||||
// @Summary Add a simulator
|
||||
// @ID addSimulator
|
||||
// addIC godoc
|
||||
// @Summary Add an infrastructure component
|
||||
// @ID addIC
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags simulators
|
||||
// @Success 200 {object} docs.ResponseSimulator "Simulator that was added"
|
||||
// @Tags infrastructure-components
|
||||
// @Success 200 {object} docs.ResponseIC "Infrastructure Component that was added"
|
||||
// @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 Authorization header string true "Authorization token"
|
||||
// @Param inputSimulator body simulator.addSimulatorRequest true "Simulator to be added"
|
||||
// @Router /simulators [post]
|
||||
func addSimulator(c *gin.Context) {
|
||||
// @Param inputIC body infrastructure_component.addICRequest true "Infrastructure Component to be added"
|
||||
// @Router /ic [post]
|
||||
func addIC(c *gin.Context) {
|
||||
|
||||
ok, _ := CheckPermissions(c, database.ModelSimulator, database.Create, false)
|
||||
ok, _ := CheckPermissions(c, database.ModelInfrastructureComponent, database.Create, false)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req addSimulatorRequest
|
||||
var req addICRequest
|
||||
err := c.BindJSON(&req)
|
||||
if err != nil {
|
||||
helper.BadRequestError(c, "Error binding form data to JSON: "+err.Error())
|
||||
|
@ -96,40 +96,40 @@ func addSimulator(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
// Create the new simulator from the request
|
||||
newSimulator := req.createSimulator()
|
||||
// Create the new IC from the request
|
||||
newIC := req.createIC()
|
||||
|
||||
// Save new simulator to DB
|
||||
err = newSimulator.save()
|
||||
// Save new IC to DB
|
||||
err = newIC.save()
|
||||
if !helper.DBError(c, err) {
|
||||
c.JSON(http.StatusOK, gin.H{"simulator": newSimulator.Simulator})
|
||||
c.JSON(http.StatusOK, gin.H{"ic": newIC.InfrastructureComponent})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// updateSimulator godoc
|
||||
// @Summary Update a simulator
|
||||
// @ID updateSimulator
|
||||
// @Tags simulators
|
||||
// updateIC godoc
|
||||
// @Summary Update an infrastructure component
|
||||
// @ID updateIC
|
||||
// @Tags infrastructure-components
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} docs.ResponseSimulator "Simulator that was updated"
|
||||
// @Success 200 {object} docs.ResponseIC "Infrastructure Component that was updated"
|
||||
// @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 Authorization header string true "Authorization token"
|
||||
// @Param inputSimulator body simulator.updateSimulatorRequest true "Simulator to be updated"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID} [put]
|
||||
func updateSimulator(c *gin.Context) {
|
||||
// @Param inputIC body infrastructure_component.updateICRequest true "InfrastructureComponent to be updated"
|
||||
// @Param ICID path int true "InfrastructureComponent ID"
|
||||
// @Router /ic/{ICID} [put]
|
||||
func updateIC(c *gin.Context) {
|
||||
|
||||
ok, oldSimulator := CheckPermissions(c, database.ModelSimulator, database.Update, true)
|
||||
ok, oldIC := CheckPermissions(c, database.ModelInfrastructureComponent, database.Update, true)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req updateSimulatorRequest
|
||||
var req updateICRequest
|
||||
err := c.BindJSON(&req)
|
||||
if err != nil {
|
||||
helper.BadRequestError(c, "Error binding form data to JSON: "+err.Error())
|
||||
|
@ -137,89 +137,89 @@ func updateSimulator(c *gin.Context) {
|
|||
}
|
||||
|
||||
// Validate the request
|
||||
if err = req.Simulator.validate(); err != nil {
|
||||
if err = req.InfrastructureComponent.validate(); err != nil {
|
||||
helper.UnprocessableEntityError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Create the updatedSimulator from oldSimulator
|
||||
updatedSimulator := req.updatedSimulator(oldSimulator)
|
||||
// Create the updatedIC from oldIC
|
||||
updatedIC := req.updatedIC(oldIC)
|
||||
|
||||
// Finally update the simulator in the DB
|
||||
err = oldSimulator.update(updatedSimulator)
|
||||
// Finally update the IC in the DB
|
||||
err = oldIC.update(updatedIC)
|
||||
if !helper.DBError(c, err) {
|
||||
c.JSON(http.StatusOK, gin.H{"simulator": updatedSimulator.Simulator})
|
||||
c.JSON(http.StatusOK, gin.H{"ic": updatedIC.InfrastructureComponent})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// getSimulator godoc
|
||||
// @Summary Get simulator
|
||||
// @ID getSimulator
|
||||
// getIC godoc
|
||||
// @Summary Get infrastructure component
|
||||
// @ID getIC
|
||||
// @Produce json
|
||||
// @Tags simulators
|
||||
// @Success 200 {object} docs.ResponseSimulator "Simulator that was requested"
|
||||
// @Tags infrastructure-components
|
||||
// @Success 200 {object} docs.ResponseIC "Infrastructure Component that was requested"
|
||||
// @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 Authorization header string true "Authorization token"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID} [get]
|
||||
func getSimulator(c *gin.Context) {
|
||||
// @Param ICID path int true "Infrastructure Component ID"
|
||||
// @Router /ic/{ICID} [get]
|
||||
func getIC(c *gin.Context) {
|
||||
|
||||
ok, s := CheckPermissions(c, database.ModelSimulator, database.Read, true)
|
||||
ok, s := CheckPermissions(c, database.ModelInfrastructureComponent, database.Read, true)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"simulator": s.Simulator})
|
||||
c.JSON(http.StatusOK, gin.H{"ic": s.InfrastructureComponent})
|
||||
}
|
||||
|
||||
// deleteSimulator godoc
|
||||
// @Summary Delete a simulator
|
||||
// @ID deleteSimulator
|
||||
// @Tags simulators
|
||||
// deleteIC godoc
|
||||
// @Summary Delete an infrastructure component
|
||||
// @ID deleteIC
|
||||
// @Tags infrastructure-components
|
||||
// @Produce json
|
||||
// @Success 200 {object} docs.ResponseSimulator "Simulator that was deleted"
|
||||
// @Success 200 {object} docs.ResponseIC "Infrastructure Component that was deleted"
|
||||
// @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 Authorization header string true "Authorization token"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID} [delete]
|
||||
func deleteSimulator(c *gin.Context) {
|
||||
// @Param ICID path int true "Infrastructure Component ID"
|
||||
// @Router /ic/{ICID} [delete]
|
||||
func deleteIC(c *gin.Context) {
|
||||
|
||||
ok, s := CheckPermissions(c, database.ModelSimulator, database.Delete, true)
|
||||
ok, s := CheckPermissions(c, database.ModelInfrastructureComponent, database.Delete, true)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete the simulator
|
||||
// Delete the IC
|
||||
err := s.delete()
|
||||
if !helper.DBError(c, err) {
|
||||
c.JSON(http.StatusOK, gin.H{"simulator": s.Simulator})
|
||||
c.JSON(http.StatusOK, gin.H{"ic": s.InfrastructureComponent})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// getModelsOfSimulator godoc
|
||||
// @Summary Get all simulation models in which the simulator is used
|
||||
// @ID getModelsOfSimulator
|
||||
// @Tags simulators
|
||||
// getConfigsOfIC godoc
|
||||
// @Summary Get all configurations of the infrastructure component
|
||||
// @ID getConfigsOfIC
|
||||
// @Tags infrastructure-components
|
||||
// @Produce json
|
||||
// @Success 200 {object} docs.ResponseSimulationModels "Simulation models requested by user"
|
||||
// @Success 200 {object} docs.ResponseSimulationModels "Configs requested by user"
|
||||
// @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 Authorization header string true "Authorization token"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID}/models [get]
|
||||
func getModelsOfSimulator(c *gin.Context) {
|
||||
// @Param ICID path int true "Infrastructure Component ID"
|
||||
// @Router /ic/{ICID}/models [get]
|
||||
func getConfigsOfIC(c *gin.Context) {
|
||||
|
||||
ok, s := CheckPermissions(c, database.ModelSimulator, database.Read, true)
|
||||
ok, s := CheckPermissions(c, database.ModelInfrastructureComponent, database.Read, true)
|
||||
if !ok {
|
||||
return
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/** Simulator package, methods.
|
||||
/** InfrastructureComponent package, methods.
|
||||
*
|
||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
||||
|
@ -19,7 +19,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
package simulator
|
||||
package infrastructure_component
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -27,45 +27,45 @@ import (
|
|||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||
)
|
||||
|
||||
type Simulator struct {
|
||||
database.Simulator
|
||||
type InfrastructureComponent struct {
|
||||
database.InfrastructureComponent
|
||||
}
|
||||
|
||||
func (s *Simulator) save() error {
|
||||
func (s *InfrastructureComponent) save() error {
|
||||
db := database.GetDB()
|
||||
err := db.Create(s).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Simulator) ByID(id uint) error {
|
||||
func (s *InfrastructureComponent) ByID(id uint) error {
|
||||
db := database.GetDB()
|
||||
err := db.Find(s, id).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Simulator) update(updatedSimulator Simulator) error {
|
||||
func (s *InfrastructureComponent) update(updatedIC InfrastructureComponent) error {
|
||||
|
||||
db := database.GetDB()
|
||||
err := db.Model(s).Updates(updatedSimulator).Error
|
||||
err := db.Model(s).Updates(updatedIC).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Simulator) delete() error {
|
||||
func (s *InfrastructureComponent) delete() error {
|
||||
db := database.GetDB()
|
||||
|
||||
no_simulationmodels := db.Model(s).Association("SimulationModels").Count()
|
||||
|
||||
if no_simulationmodels > 0 {
|
||||
return fmt.Errorf("Simulator cannot be deleted as it is still used in SimulationModels (active or dangling)")
|
||||
return fmt.Errorf("InfrastructureComponent cannot be deleted as it is still used in SimulationModels (active or dangling)")
|
||||
}
|
||||
|
||||
// delete Simulator from DB (does NOT remain as dangling)
|
||||
// delete InfrastructureComponent from DB (does NOT remain as dangling)
|
||||
err := db.Delete(s).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Simulator) getModels() ([]database.SimulationModel, int, error) {
|
||||
func (s *InfrastructureComponent) getModels() ([]database.SimulationModel, int, error) {
|
||||
db := database.GetDB()
|
||||
var models []database.SimulationModel
|
||||
err := db.Order("ID asc").Model(s).Related(&models, "SimulationModels").Error
|
|
@ -1,4 +1,4 @@
|
|||
/** Simulator package, middleware.
|
||||
/** InfrastructureComponent package, middleware.
|
||||
*
|
||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
||||
|
@ -19,7 +19,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
package simulator
|
||||
package infrastructure_component
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -28,24 +28,24 @@ import (
|
|||
"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, InfrastructureComponent) {
|
||||
|
||||
var s Simulator
|
||||
var s InfrastructureComponent
|
||||
|
||||
err := database.ValidateRole(c, modeltype, operation)
|
||||
if err != nil {
|
||||
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of simulator failed): %v", err.Error()))
|
||||
helper.UnprocessableEntityError(c, fmt.Sprintf("Access denied (role validation of infrastructure component failed): %v", err.Error()))
|
||||
return false, s
|
||||
}
|
||||
|
||||
if hasID {
|
||||
// Get the ID of the simulator from the context
|
||||
simulatorID, err := helper.GetIDOfElement(c, "simulatorID", "path", -1)
|
||||
// Get the ID of the infrastructure component from the context
|
||||
ICID, err := helper.GetIDOfElement(c, "ICID", "path", -1)
|
||||
if err != nil {
|
||||
return false, s
|
||||
}
|
||||
|
||||
err = s.ByID(uint(simulatorID))
|
||||
err = s.ByID(uint(ICID))
|
||||
if helper.DBError(c, err) {
|
||||
return false, s
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/** Simulator package, testing.
|
||||
/** InfrastructureComponent package, testing.
|
||||
*
|
||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
||||
|
@ -19,7 +19,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
package simulator
|
||||
package infrastructure_component
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -40,7 +40,7 @@ import (
|
|||
var router *gin.Engine
|
||||
var db *gorm.DB
|
||||
|
||||
type SimulatorRequest struct {
|
||||
type ICRequest struct {
|
||||
UUID string `json:"uuid,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Modeltype string `json:"modelType,omitempty"`
|
||||
|
@ -65,12 +65,12 @@ func TestMain(m *testing.M) {
|
|||
|
||||
user.RegisterAuthenticate(api.Group("/authenticate"))
|
||||
api.Use(user.Authentication(true))
|
||||
RegisterSimulatorEndpoints(api.Group("/simulators"))
|
||||
RegisterICEndpoints(api.Group("/ic"))
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestAddSimulatorAsAdmin(t *testing.T) {
|
||||
func TestAddICAsAdmin(t *testing.T) {
|
||||
database.DropTables(db)
|
||||
database.MigrateModels(db)
|
||||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
@ -83,61 +83,61 @@ func TestAddSimulatorAsAdmin(t *testing.T) {
|
|||
// 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")
|
||||
"/api/ic", "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)
|
||||
// try to POST malformed IC (required fields missing, validation should fail)
|
||||
// should result in an unprocessable entity
|
||||
newMalformedSimulator := SimulatorRequest{
|
||||
UUID: database.SimulatorB.UUID,
|
||||
newMalformedIC := ICRequest{
|
||||
UUID: database.ICB.UUID,
|
||||
}
|
||||
code, resp, err = helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newMalformedSimulator})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newMalformedIC})
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// test POST simulators/ $newSimulator
|
||||
newSimulator := SimulatorRequest{
|
||||
UUID: database.SimulatorA.UUID,
|
||||
Host: database.SimulatorA.Host,
|
||||
Modeltype: database.SimulatorA.Modeltype,
|
||||
State: database.SimulatorA.State,
|
||||
Properties: database.SimulatorA.Properties,
|
||||
// test POST ic/ $newIC
|
||||
newIC := ICRequest{
|
||||
UUID: database.ICA.UUID,
|
||||
Host: database.ICA.Host,
|
||||
Modeltype: database.ICA.Modeltype,
|
||||
State: database.ICA.State,
|
||||
Properties: database.ICA.Properties,
|
||||
}
|
||||
code, resp, err = helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulator})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// Compare POST's response with the newSimulator
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"simulator": newSimulator})
|
||||
// Compare POST's response with the newIC
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Read newSimulator's ID from the response
|
||||
newSimulatorID, err := helper.GetResponseID(resp)
|
||||
// Read newIC's ID from the response
|
||||
newICID, err := helper.GetResponseID(resp)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get the newSimulator
|
||||
// Get the newIC
|
||||
code, resp, err = helper.TestEndpoint(router, token,
|
||||
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "GET", nil)
|
||||
fmt.Sprintf("/api/ic/%v", newICID), "GET", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// Compare GET's response with the newSimulator
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"simulator": newSimulator})
|
||||
// Compare GET's response with the newIC
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Try to GET a simulator that does not exist
|
||||
// Try to GET a IC 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)
|
||||
fmt.Sprintf("/api/ic/%v", newICID+1), "GET", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
||||
}
|
||||
|
||||
func TestAddSimulatorAsUser(t *testing.T) {
|
||||
func TestAddICAsUser(t *testing.T) {
|
||||
database.DropTables(db)
|
||||
database.MigrateModels(db)
|
||||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
@ -147,24 +147,24 @@ func TestAddSimulatorAsUser(t *testing.T) {
|
|||
"/api/authenticate", "POST", helper.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST simulators/ $newSimulator
|
||||
newSimulator := SimulatorRequest{
|
||||
UUID: database.SimulatorA.UUID,
|
||||
Host: database.SimulatorA.Host,
|
||||
Modeltype: database.SimulatorA.Modeltype,
|
||||
State: database.SimulatorA.State,
|
||||
Properties: database.SimulatorA.Properties,
|
||||
// test POST ic/ $newIC
|
||||
newIC := ICRequest{
|
||||
UUID: database.ICA.UUID,
|
||||
Host: database.ICA.Host,
|
||||
Modeltype: database.ICA.Modeltype,
|
||||
State: database.ICA.State,
|
||||
Properties: database.ICA.Properties,
|
||||
}
|
||||
|
||||
// This should fail with unprocessable entity 422 error code
|
||||
// Normal users are not allowed to add simulators
|
||||
// Normal users are not allowed to add ICs
|
||||
code, resp, err := helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulator})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||
}
|
||||
|
||||
func TestUpdateSimulatorAsAdmin(t *testing.T) {
|
||||
func TestUpdateICAsAdmin(t *testing.T) {
|
||||
database.DropTables(db)
|
||||
database.MigrateModels(db)
|
||||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
@ -174,59 +174,59 @@ func TestUpdateSimulatorAsAdmin(t *testing.T) {
|
|||
"/api/authenticate", "POST", helper.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST simulators/ $newSimulator
|
||||
newSimulator := SimulatorRequest{
|
||||
UUID: database.SimulatorA.UUID,
|
||||
Host: database.SimulatorA.Host,
|
||||
Modeltype: database.SimulatorA.Modeltype,
|
||||
State: database.SimulatorA.State,
|
||||
Properties: database.SimulatorA.Properties,
|
||||
// test POST ic/ $newIC
|
||||
newIC := ICRequest{
|
||||
UUID: database.ICA.UUID,
|
||||
Host: database.ICA.Host,
|
||||
Modeltype: database.ICA.Modeltype,
|
||||
State: database.ICA.State,
|
||||
Properties: database.ICA.Properties,
|
||||
}
|
||||
code, resp, err := helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulator})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// Compare POST's response with the newSimulator
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"simulator": newSimulator})
|
||||
// Compare POST's response with the newIC
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Read newSimulator's ID from the response
|
||||
newSimulatorID, err := helper.GetResponseID(resp)
|
||||
// Read newIC's ID from the response
|
||||
newICID, err := helper.GetResponseID(resp)
|
||||
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")
|
||||
fmt.Sprintf("/api/ic/%v", newICID), "PUT", "This is no JSON")
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 400, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// Test PUT simulators
|
||||
newSimulator.Host = "ThisIsMyNewHost"
|
||||
// Test PUT IC
|
||||
newIC.Host = "ThisIsMyNewHost"
|
||||
code, resp, err = helper.TestEndpoint(router, token,
|
||||
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "PUT", helper.KeyModels{"simulator": newSimulator})
|
||||
fmt.Sprintf("/api/ic/%v", newICID), "PUT", helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// Compare PUT's response with the updated newSimulator
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"simulator": newSimulator})
|
||||
// Compare PUT's response with the updated newIC
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get the updated newSimulator
|
||||
// Get the updated newIC
|
||||
code, resp, err = helper.TestEndpoint(router, token,
|
||||
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "GET", nil)
|
||||
fmt.Sprintf("/api/ic/%v", newICID), "GET", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// Compare GET's response with the updated newSimulator
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"simulator": newSimulator})
|
||||
// Compare GET's response with the updated newIC
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestUpdateSimulatorAsUser(t *testing.T) {
|
||||
func TestUpdateICAsUser(t *testing.T) {
|
||||
database.DropTables(db)
|
||||
database.MigrateModels(db)
|
||||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
@ -236,21 +236,21 @@ func TestUpdateSimulatorAsUser(t *testing.T) {
|
|||
"/api/authenticate", "POST", helper.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST simulators/ $newSimulator
|
||||
newSimulator := SimulatorRequest{
|
||||
UUID: database.SimulatorA.UUID,
|
||||
Host: database.SimulatorA.Host,
|
||||
Modeltype: database.SimulatorA.Modeltype,
|
||||
State: database.SimulatorA.State,
|
||||
Properties: database.SimulatorA.Properties,
|
||||
// test POST ic/ $newIC
|
||||
newIC := ICRequest{
|
||||
UUID: database.ICA.UUID,
|
||||
Host: database.ICA.Host,
|
||||
Modeltype: database.ICA.Modeltype,
|
||||
State: database.ICA.State,
|
||||
Properties: database.ICA.Properties,
|
||||
}
|
||||
code, resp, err := helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulator})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// Read newSimulator's ID from the response
|
||||
newSimulatorID, err := helper.GetResponseID(resp)
|
||||
// Read newIC's ID from the response
|
||||
newICID, err := helper.GetResponseID(resp)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// authenticate as user
|
||||
|
@ -258,17 +258,17 @@ func TestUpdateSimulatorAsUser(t *testing.T) {
|
|||
"/api/authenticate", "POST", helper.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test PUT simulators
|
||||
// Test PUT IC
|
||||
// This should fail with unprocessable entity status code 422
|
||||
newSimulator.Host = "ThisIsMyNewHost"
|
||||
newIC.Host = "ThisIsMyNewHost"
|
||||
code, resp, err = helper.TestEndpoint(router, token,
|
||||
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "PUT", helper.KeyModels{"simulator": newSimulator})
|
||||
fmt.Sprintf("/api/ic/%v", newICID), "PUT", helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||
|
||||
}
|
||||
|
||||
func TestDeleteSimulatorAsAdmin(t *testing.T) {
|
||||
func TestDeleteICAsAdmin(t *testing.T) {
|
||||
database.DropTables(db)
|
||||
database.MigrateModels(db)
|
||||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
@ -278,47 +278,47 @@ func TestDeleteSimulatorAsAdmin(t *testing.T) {
|
|||
"/api/authenticate", "POST", helper.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST simulators/ $newSimulator
|
||||
newSimulator := SimulatorRequest{
|
||||
UUID: database.SimulatorA.UUID,
|
||||
Host: database.SimulatorA.Host,
|
||||
Modeltype: database.SimulatorA.Modeltype,
|
||||
State: database.SimulatorA.State,
|
||||
Properties: database.SimulatorA.Properties,
|
||||
// test POST ic/ $newIC
|
||||
newIC := ICRequest{
|
||||
UUID: database.ICA.UUID,
|
||||
Host: database.ICA.Host,
|
||||
Modeltype: database.ICA.Modeltype,
|
||||
State: database.ICA.State,
|
||||
Properties: database.ICA.Properties,
|
||||
}
|
||||
code, resp, err := helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulator})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// Read newSimulator's ID from the response
|
||||
newSimulatorID, err := helper.GetResponseID(resp)
|
||||
// Read newIC's ID from the response
|
||||
newICID, err := helper.GetResponseID(resp)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Count the number of all the simulators returned for admin
|
||||
// Count the number of all the ICs returned for admin
|
||||
initialNumber, err := helper.LengthOfResponse(router, token,
|
||||
"/api/simulators", "GET", nil)
|
||||
"/api/ic", "GET", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Delete the added newSimulator
|
||||
// Delete the added newIC
|
||||
code, resp, err = helper.TestEndpoint(router, token,
|
||||
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "DELETE", nil)
|
||||
fmt.Sprintf("/api/ic/%v", newICID), "DELETE", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// Compare DELETE's response with the newSimulator
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"simulator": newSimulator})
|
||||
// Compare DELETE's response with the newIC
|
||||
err = helper.CompareResponse(resp, helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Again count the number of all the simulators returned
|
||||
// Again count the number of all the ICs returned
|
||||
finalNumber, err := helper.LengthOfResponse(router, token,
|
||||
"/api/simulators", "GET", nil)
|
||||
"/api/ic", "GET", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, finalNumber, initialNumber-1)
|
||||
}
|
||||
|
||||
func TestDeleteSimulatorAsUser(t *testing.T) {
|
||||
func TestDeleteICAsUser(t *testing.T) {
|
||||
database.DropTables(db)
|
||||
database.MigrateModels(db)
|
||||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
@ -328,21 +328,21 @@ func TestDeleteSimulatorAsUser(t *testing.T) {
|
|||
"/api/authenticate", "POST", helper.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST simulators/ $newSimulator
|
||||
newSimulator := SimulatorRequest{
|
||||
UUID: database.SimulatorA.UUID,
|
||||
Host: database.SimulatorA.Host,
|
||||
Modeltype: database.SimulatorA.Modeltype,
|
||||
State: database.SimulatorA.State,
|
||||
Properties: database.SimulatorA.Properties,
|
||||
// test POST ic/ $newIC
|
||||
newIC := ICRequest{
|
||||
UUID: database.ICA.UUID,
|
||||
Host: database.ICA.Host,
|
||||
Modeltype: database.ICA.Modeltype,
|
||||
State: database.ICA.State,
|
||||
Properties: database.ICA.Properties,
|
||||
}
|
||||
code, resp, err := helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulator})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newIC})
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// Read newSimulator's ID from the response
|
||||
newSimulatorID, err := helper.GetResponseID(resp)
|
||||
// Read newIC's ID from the response
|
||||
newICID, err := helper.GetResponseID(resp)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// authenticate as user
|
||||
|
@ -350,16 +350,16 @@ func TestDeleteSimulatorAsUser(t *testing.T) {
|
|||
"/api/authenticate", "POST", helper.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test DELETE simulators
|
||||
// Test DELETE ICs
|
||||
// This should fail with unprocessable entity status code 422
|
||||
newSimulator.Host = "ThisIsMyNewHost"
|
||||
newIC.Host = "ThisIsMyNewHost"
|
||||
code, resp, err = helper.TestEndpoint(router, token,
|
||||
fmt.Sprintf("/api/simulators/%v", newSimulatorID), "DELETE", nil)
|
||||
fmt.Sprintf("/api/ic/%v", newICID), "DELETE", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||
}
|
||||
|
||||
func TestGetAllSimulators(t *testing.T) {
|
||||
func TestGetAllICs(t *testing.T) {
|
||||
database.DropTables(db)
|
||||
database.MigrateModels(db)
|
||||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
@ -369,40 +369,40 @@ func TestGetAllSimulators(t *testing.T) {
|
|||
"/api/authenticate", "POST", helper.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// get the length of the GET all simulators response for user
|
||||
// get the length of the GET all ICs response for user
|
||||
initialNumber, err := helper.LengthOfResponse(router, token,
|
||||
"/api/simulators", "GET", nil)
|
||||
"/api/ic", "GET", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST simulators/ $newSimulatorA
|
||||
newSimulatorA := SimulatorRequest{
|
||||
UUID: database.SimulatorA.UUID,
|
||||
Host: database.SimulatorA.Host,
|
||||
Modeltype: database.SimulatorA.Modeltype,
|
||||
State: database.SimulatorA.State,
|
||||
Properties: database.SimulatorA.Properties,
|
||||
// test POST ic/ $newICA
|
||||
newICA := ICRequest{
|
||||
UUID: database.ICA.UUID,
|
||||
Host: database.ICA.Host,
|
||||
Modeltype: database.ICA.Modeltype,
|
||||
State: database.ICA.State,
|
||||
Properties: database.ICA.Properties,
|
||||
}
|
||||
code, resp, err := helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulatorA})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newICA})
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// test POST simulators/ $newSimulatorB
|
||||
newSimulatorB := SimulatorRequest{
|
||||
UUID: database.SimulatorB.UUID,
|
||||
Host: database.SimulatorB.Host,
|
||||
Modeltype: database.SimulatorB.Modeltype,
|
||||
State: database.SimulatorB.State,
|
||||
Properties: database.SimulatorB.Properties,
|
||||
// test POST ic/ $newICB
|
||||
newICB := ICRequest{
|
||||
UUID: database.ICB.UUID,
|
||||
Host: database.ICB.Host,
|
||||
Modeltype: database.ICB.Modeltype,
|
||||
State: database.ICB.State,
|
||||
Properties: database.ICB.Properties,
|
||||
}
|
||||
code, resp, err = helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulatorB})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newICB})
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// get the length of the GET all simulators response again
|
||||
// get the length of the GET all ICs response again
|
||||
finalNumber, err := helper.LengthOfResponse(router, token,
|
||||
"/api/simulators", "GET", nil)
|
||||
"/api/ic", "GET", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, finalNumber, initialNumber+2)
|
||||
|
@ -412,15 +412,15 @@ func TestGetAllSimulators(t *testing.T) {
|
|||
"/api/authenticate", "POST", helper.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// get the length of the GET all simulators response again
|
||||
// get the length of the GET all ICs response again
|
||||
finalNumber2, err := helper.LengthOfResponse(router, token,
|
||||
"/api/simulators", "GET", nil)
|
||||
"/api/ic", "GET", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, finalNumber2, initialNumber+2)
|
||||
}
|
||||
|
||||
func TestGetSimulationModelsOfSimulator(t *testing.T) {
|
||||
func TestGetConfigsOfIC(t *testing.T) {
|
||||
database.DropTables(db)
|
||||
database.MigrateModels(db)
|
||||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
@ -430,27 +430,27 @@ func TestGetSimulationModelsOfSimulator(t *testing.T) {
|
|||
"/api/authenticate", "POST", helper.AdminCredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test POST simulators/ $newSimulatorA
|
||||
newSimulatorA := SimulatorRequest{
|
||||
UUID: database.SimulatorA.UUID,
|
||||
Host: database.SimulatorA.Host,
|
||||
Modeltype: database.SimulatorA.Modeltype,
|
||||
State: database.SimulatorA.State,
|
||||
Properties: database.SimulatorA.Properties,
|
||||
// test POST ic/ $newICA
|
||||
newICA := ICRequest{
|
||||
UUID: database.ICA.UUID,
|
||||
Host: database.ICA.Host,
|
||||
Modeltype: database.ICA.Modeltype,
|
||||
State: database.ICA.State,
|
||||
Properties: database.ICA.Properties,
|
||||
}
|
||||
code, resp, err := helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulatorA})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newICA})
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
// Read newSimulator's ID from the response
|
||||
newSimulatorID, err := helper.GetResponseID(resp)
|
||||
// Read newIC's ID from the response
|
||||
newICID, err := helper.GetResponseID(resp)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test GET simulators/ID/models
|
||||
// test GET ic/ID/models
|
||||
// TODO how to properly test this without using simulation model endpoints?
|
||||
numberOfModels, err := helper.LengthOfResponse(router, token,
|
||||
fmt.Sprintf("/api/simulators/%v/models", newSimulatorID), "GET", nil)
|
||||
fmt.Sprintf("/api/ic/%v/models", newICID), "GET", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
|
@ -461,19 +461,19 @@ func TestGetSimulationModelsOfSimulator(t *testing.T) {
|
|||
"/api/authenticate", "POST", helper.UserACredentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// test GET simulators/ID/models
|
||||
// test GET ic/ID/models
|
||||
// TODO how to properly test this without using simulation model endpoints?
|
||||
numberOfModels, err = helper.LengthOfResponse(router, token,
|
||||
fmt.Sprintf("/api/simulators/%v/models", newSimulatorID), "GET", nil)
|
||||
fmt.Sprintf("/api/ic/%v/models", newICID), "GET", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||
|
||||
assert.Equal(t, 0, numberOfModels)
|
||||
|
||||
// Try to get models of simulator that does not exist
|
||||
// Try to get models of IC 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)
|
||||
fmt.Sprintf("/api/ic/%v/models", newICID+1), "GET", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
/** Simulator package, validators.
|
||||
/** InfrastructureComponent package, validators.
|
||||
*
|
||||
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
||||
|
@ -19,7 +19,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
package simulator
|
||||
package infrastructure_component
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -30,7 +30,7 @@ import (
|
|||
|
||||
var validate *validator.Validate
|
||||
|
||||
type validNewSimulator struct {
|
||||
type validNewIC struct {
|
||||
UUID string `form:"UUID" validate:"required"`
|
||||
Host string `form:"Host" validate:"required"`
|
||||
Modeltype string `form:"Modeltype" validate:"required"`
|
||||
|
@ -38,7 +38,7 @@ type validNewSimulator struct {
|
|||
State string `form:"State"`
|
||||
}
|
||||
|
||||
type validUpdatedSimulator struct {
|
||||
type validUpdatedIC struct {
|
||||
UUID string `form:"UUID" validate:"omitempty"`
|
||||
Host string `form:"Host" validate:"omitempty"`
|
||||
Modeltype string `form:"Modeltype" validate:"omitempty"`
|
||||
|
@ -46,68 +46,68 @@ type validUpdatedSimulator struct {
|
|||
State string `form:"State" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type addSimulatorRequest struct {
|
||||
Simulator validNewSimulator `json:"simulator"`
|
||||
type addICRequest struct {
|
||||
InfrastructureComponent validNewIC `json:"ic"`
|
||||
}
|
||||
|
||||
type updateSimulatorRequest struct {
|
||||
Simulator validUpdatedSimulator `json:"simulator"`
|
||||
type updateICRequest struct {
|
||||
InfrastructureComponent validUpdatedIC `json:"ic"`
|
||||
}
|
||||
|
||||
func (r *addSimulatorRequest) validate() error {
|
||||
func (r *addICRequest) validate() error {
|
||||
validate = validator.New()
|
||||
errs := validate.Struct(r)
|
||||
return errs
|
||||
}
|
||||
|
||||
func (r *validUpdatedSimulator) validate() error {
|
||||
func (r *validUpdatedIC) validate() error {
|
||||
validate = validator.New()
|
||||
errs := validate.Struct(r)
|
||||
return errs
|
||||
}
|
||||
|
||||
func (r *addSimulatorRequest) createSimulator() Simulator {
|
||||
var s Simulator
|
||||
func (r *addICRequest) createIC() InfrastructureComponent {
|
||||
var s InfrastructureComponent
|
||||
|
||||
s.UUID = r.Simulator.UUID
|
||||
s.Host = r.Simulator.Host
|
||||
s.Modeltype = r.Simulator.Modeltype
|
||||
s.Properties = r.Simulator.Properties
|
||||
if r.Simulator.State != "" {
|
||||
s.State = r.Simulator.State
|
||||
s.UUID = r.InfrastructureComponent.UUID
|
||||
s.Host = r.InfrastructureComponent.Host
|
||||
s.Modeltype = r.InfrastructureComponent.Modeltype
|
||||
s.Properties = r.InfrastructureComponent.Properties
|
||||
if r.InfrastructureComponent.State != "" {
|
||||
s.State = r.InfrastructureComponent.State
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *updateSimulatorRequest) updatedSimulator(oldSimulator Simulator) Simulator {
|
||||
// Use the old Simulator as a basis for the updated Simulator `s`
|
||||
s := oldSimulator
|
||||
func (r *updateICRequest) updatedIC(oldIC InfrastructureComponent) InfrastructureComponent {
|
||||
// Use the old InfrastructureComponent as a basis for the updated InfrastructureComponent `s`
|
||||
s := oldIC
|
||||
|
||||
if r.Simulator.UUID != "" {
|
||||
s.UUID = r.Simulator.UUID
|
||||
if r.InfrastructureComponent.UUID != "" {
|
||||
s.UUID = r.InfrastructureComponent.UUID
|
||||
}
|
||||
|
||||
if r.Simulator.Host != "" {
|
||||
s.Host = r.Simulator.Host
|
||||
if r.InfrastructureComponent.Host != "" {
|
||||
s.Host = r.InfrastructureComponent.Host
|
||||
}
|
||||
|
||||
if r.Simulator.Modeltype != "" {
|
||||
s.Modeltype = r.Simulator.Modeltype
|
||||
if r.InfrastructureComponent.Modeltype != "" {
|
||||
s.Modeltype = r.InfrastructureComponent.Modeltype
|
||||
}
|
||||
|
||||
if r.Simulator.State != "" {
|
||||
s.State = r.Simulator.State
|
||||
if r.InfrastructureComponent.State != "" {
|
||||
s.State = r.InfrastructureComponent.State
|
||||
}
|
||||
|
||||
// only update props if not empty
|
||||
var emptyJson postgres.Jsonb
|
||||
// Serialize empty json and params
|
||||
emptyJson_ser, _ := json.Marshal(emptyJson)
|
||||
startParams_ser, _ := json.Marshal(r.Simulator.Properties)
|
||||
startParams_ser, _ := json.Marshal(r.InfrastructureComponent.Properties)
|
||||
opts := jsondiff.DefaultConsoleOptions()
|
||||
diff, _ := jsondiff.Compare(emptyJson_ser, startParams_ser, &opts)
|
||||
if diff.String() != "FullMatch" {
|
||||
s.Properties = r.Simulator.Properties
|
||||
s.Properties = r.InfrastructureComponent.Properties
|
||||
}
|
||||
|
||||
return s
|
|
@ -30,10 +30,10 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
SimulatorCounter = prometheus.NewCounter(
|
||||
ICCounter = prometheus.NewCounter(
|
||||
prometheus.CounterOpts{
|
||||
Name: "simulators",
|
||||
Help: "A counter for the total number of simulators",
|
||||
Name: "infrastructure_components",
|
||||
Help: "A counter for the total number of infrastructure_components",
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -102,7 +102,7 @@ func RegisterMetricsEndpoint(rg *gin.RouterGroup) {
|
|||
|
||||
// Register metrics
|
||||
prometheus.MustRegister(
|
||||
SimulatorCounter,
|
||||
ICCounter,
|
||||
SimulationModelCounter,
|
||||
FileCounter,
|
||||
ScenarioCounter,
|
||||
|
@ -112,16 +112,16 @@ func RegisterMetricsEndpoint(rg *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
func InitCounters(db *gorm.DB) {
|
||||
var simulators, simulation_models, files, scenarios, users, dashboards float64
|
||||
var infrastructure_components, simulation_models, files, scenarios, users, dashboards float64
|
||||
|
||||
db.Table("simulators").Count(&simulators)
|
||||
db.Table("infrastructure_components").Count(&infrastructure_components)
|
||||
db.Table("simulation_models").Count(&simulation_models)
|
||||
db.Table("files").Count(&files)
|
||||
db.Table("scenarios").Count(&scenarios)
|
||||
db.Table("users").Count(&users)
|
||||
db.Table("dashboards").Count(&dashboards)
|
||||
|
||||
SimulatorCounter.Add(simulators)
|
||||
ICCounter.Add(infrastructure_components)
|
||||
SimulationModelCounter.Add(simulation_models)
|
||||
FileCounter.Add(files)
|
||||
ScenarioCounter.Add(scenarios)
|
||||
|
|
|
@ -26,9 +26,9 @@ import (
|
|||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/infrastructure-component"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/simulationmodel"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/simulator"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/gorm"
|
||||
|
@ -52,11 +52,11 @@ type SignalRequest struct {
|
|||
type SimulationModelRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
ScenarioID uint `json:"scenarioID,omitempty"`
|
||||
SimulatorID uint `json:"simulatorID,omitempty"`
|
||||
ICID uint `json:"icID,omitempty"`
|
||||
StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
|
||||
}
|
||||
|
||||
type SimulatorRequest struct {
|
||||
type ICRequest struct {
|
||||
UUID string `json:"uuid,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Modeltype string `json:"modelType,omitempty"`
|
||||
|
@ -70,25 +70,25 @@ type ScenarioRequest struct {
|
|||
StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
|
||||
}
|
||||
|
||||
func addScenarioAndSimulatorAndSimulationModel() (scenarioID uint, simulatorID uint, simulationModelID uint) {
|
||||
func addScenarioAndICAndSimulationModel() (scenarioID uint, ICID uint, simulationModelID uint) {
|
||||
|
||||
// authenticate as admin
|
||||
token, _ := helper.AuthenticateForTest(router,
|
||||
"/api/authenticate", "POST", helper.AdminCredentials)
|
||||
|
||||
// POST $newSimulatorA
|
||||
newSimulatorA := SimulatorRequest{
|
||||
UUID: database.SimulatorA.UUID,
|
||||
Host: database.SimulatorA.Host,
|
||||
Modeltype: database.SimulatorA.Modeltype,
|
||||
State: database.SimulatorA.State,
|
||||
Properties: database.SimulatorA.Properties,
|
||||
// POST $newICA
|
||||
newICA := ICRequest{
|
||||
UUID: database.ICA.UUID,
|
||||
Host: database.ICA.Host,
|
||||
Modeltype: database.ICA.Modeltype,
|
||||
State: database.ICA.State,
|
||||
Properties: database.ICA.Properties,
|
||||
}
|
||||
_, resp, _ := helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulatorA})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newICA})
|
||||
|
||||
// Read newSimulator's ID from the response
|
||||
newSimulatorID, _ := helper.GetResponseID(resp)
|
||||
// Read newIC's ID from the response
|
||||
newICID, _ := helper.GetResponseID(resp)
|
||||
|
||||
// authenticate as normal user
|
||||
token, _ = helper.AuthenticateForTest(router,
|
||||
|
@ -110,7 +110,7 @@ func addScenarioAndSimulatorAndSimulationModel() (scenarioID uint, simulatorID u
|
|||
newSimulationModel := SimulationModelRequest{
|
||||
Name: database.SimulationModelA.Name,
|
||||
ScenarioID: uint(newScenarioID),
|
||||
SimulatorID: uint(newSimulatorID),
|
||||
ICID: uint(newICID),
|
||||
StartParameters: database.SimulationModelA.StartParameters,
|
||||
}
|
||||
_, resp, _ = helper.TestEndpoint(router, token,
|
||||
|
@ -123,7 +123,7 @@ func addScenarioAndSimulatorAndSimulationModel() (scenarioID uint, simulatorID u
|
|||
_, resp, _ = helper.TestEndpoint(router, token,
|
||||
fmt.Sprintf("/api/scenarios/%v/user?username=User_C", newScenarioID), "PUT", nil)
|
||||
|
||||
return uint(newScenarioID), uint(newSimulatorID), uint(newSimulationModelID)
|
||||
return uint(newScenarioID), uint(newICID), uint(newSimulationModelID)
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
@ -149,9 +149,9 @@ func TestMain(m *testing.M) {
|
|||
// scenario endpoints required here to first add a scenario to the DB
|
||||
// that can be associated with a new simulation model
|
||||
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
|
||||
// simulator endpoints required here to first add a simulator to the DB
|
||||
// IC endpoints required here to first add a IC to the DB
|
||||
// that can be associated with a new simulation model
|
||||
simulator.RegisterSimulatorEndpoints(api.Group("/simulators"))
|
||||
infrastructure_component.RegisterICEndpoints(api.Group("/ic"))
|
||||
RegisterSignalEndpoints(api.Group("/signals"))
|
||||
|
||||
os.Exit(m.Run())
|
||||
|
@ -163,9 +163,9 @@ func TestAddSignal(t *testing.T) {
|
|||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a simulator to the DB
|
||||
// by adding a scenario and a IC to the DB
|
||||
// using the respective endpoints of the API
|
||||
_, _, simulationModelID := addScenarioAndSimulatorAndSimulationModel()
|
||||
_, _, simulationModelID := addScenarioAndICAndSimulationModel()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router,
|
||||
|
@ -258,9 +258,9 @@ func TestUpdateSignal(t *testing.T) {
|
|||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a simulator to the DB
|
||||
// by adding a scenario and a IC to the DB
|
||||
// using the respective endpoints of the API
|
||||
_, _, simulationModelID := addScenarioAndSimulatorAndSimulationModel()
|
||||
_, _, simulationModelID := addScenarioAndICAndSimulationModel()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router,
|
||||
|
@ -360,9 +360,9 @@ func TestDeleteSignal(t *testing.T) {
|
|||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a simulator to the DB
|
||||
// by adding a scenario and a IC to the DB
|
||||
// using the respective endpoints of the API
|
||||
_, _, simulationModelID := addScenarioAndSimulatorAndSimulationModel()
|
||||
_, _, simulationModelID := addScenarioAndICAndSimulationModel()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router,
|
||||
|
@ -455,9 +455,9 @@ func TestGetAllInputSignalsOfSimulationModel(t *testing.T) {
|
|||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a simulator to the DB
|
||||
// by adding a scenario and a IC to the DB
|
||||
// using the respective endpoints of the API
|
||||
_, _, simulationModelID := addScenarioAndSimulatorAndSimulationModel()
|
||||
_, _, simulationModelID := addScenarioAndICAndSimulationModel()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router,
|
||||
|
|
|
@ -79,7 +79,7 @@ func getSimulationModels(c *gin.Context) {
|
|||
// @Failure 422 {object} docs.ResponseError "Unprocessable entity"
|
||||
// @Failure 500 {object} docs.ResponseError "Internal server error"
|
||||
// @Param Authorization header string true "Authorization token"
|
||||
// @Param inputSimulationModel body simulationmodel.addSimulationModelRequest true "Simulation model to be added incl. IDs of scenario and simulator"
|
||||
// @Param inputSimulationModel body simulationmodel.addSimulationModelRequest true "Simulation model to be added incl. IDs of scenario and IC"
|
||||
// @Router /models [post]
|
||||
func addSimulationModel(c *gin.Context) {
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ package simulationmodel
|
|||
|
||||
import (
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/infrastructure-component"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/simulator"
|
||||
)
|
||||
|
||||
type SimulationModel struct {
|
||||
|
@ -60,10 +60,10 @@ func (m *SimulationModel) addToScenario() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// associate simulator with simulation model
|
||||
var simltr simulator.Simulator
|
||||
err = simltr.ByID(m.SimulatorID)
|
||||
err = db.Model(&simltr).Association("SimulationModels").Append(m).Error
|
||||
// associate IC with simulation model
|
||||
var ic infrastructure_component.InfrastructureComponent
|
||||
err = ic.ByID(m.ICID)
|
||||
err = db.Model(&ic).Association("SimulationModels").Append(m).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -77,25 +77,25 @@ func (m *SimulationModel) addToScenario() error {
|
|||
func (m *SimulationModel) Update(modifiedSimulationModel SimulationModel) error {
|
||||
db := database.GetDB()
|
||||
|
||||
// check if simulator has been updated
|
||||
if m.SimulatorID != modifiedSimulationModel.SimulatorID {
|
||||
// update simulator
|
||||
var s simulator.Simulator
|
||||
var s_old simulator.Simulator
|
||||
err := s.ByID(modifiedSimulationModel.SimulatorID)
|
||||
// check if IC has been updated
|
||||
if m.ICID != modifiedSimulationModel.ICID {
|
||||
// update IC
|
||||
var s infrastructure_component.InfrastructureComponent
|
||||
var s_old infrastructure_component.InfrastructureComponent
|
||||
err := s.ByID(modifiedSimulationModel.ICID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s_old.ByID(m.SimulatorID)
|
||||
err = s_old.ByID(m.ICID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// remove simulation model from old simulator
|
||||
// remove simulation model from old IC
|
||||
err = db.Model(&s_old).Association("SimulationModels").Delete(m).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// add simulation model to new simulator
|
||||
// add simulation model to new IC
|
||||
err = db.Model(&s).Association("SimulationModels").Append(m).Error
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -105,7 +105,7 @@ func (m *SimulationModel) Update(modifiedSimulationModel SimulationModel) error
|
|||
err := db.Model(m).Updates(map[string]interface{}{
|
||||
"Name": modifiedSimulationModel.Name,
|
||||
"StartParameters": modifiedSimulationModel.StartParameters,
|
||||
"SimulatorID": modifiedSimulationModel.SimulatorID,
|
||||
"ICID": modifiedSimulationModel.ICID,
|
||||
"SelectedModelFileID": modifiedSimulationModel.SelectedModelFileID,
|
||||
}).Error
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@ import (
|
|||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/configuration"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/infrastructure-component"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/simulator"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/gorm"
|
||||
|
@ -43,12 +43,12 @@ var db *gorm.DB
|
|||
type SimulationModelRequest struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
ScenarioID uint `json:"scenarioID,omitempty"`
|
||||
SimulatorID uint `json:"simulatorID,omitempty"`
|
||||
ICID uint `json:"icID,omitempty"`
|
||||
StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
|
||||
SelectedModelFileID uint `json:"selectedModelFileID,omitempty"`
|
||||
}
|
||||
|
||||
type SimulatorRequest struct {
|
||||
type ICRequest struct {
|
||||
UUID string `json:"uuid,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Modeltype string `json:"modelType,omitempty"`
|
||||
|
@ -62,36 +62,36 @@ type ScenarioRequest struct {
|
|||
StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
|
||||
}
|
||||
|
||||
func addScenarioAndSimulator() (scenarioID uint, simulatorID uint) {
|
||||
func addScenarioAndIC() (scenarioID uint, ICID uint) {
|
||||
|
||||
// authenticate as admin
|
||||
token, _ := helper.AuthenticateForTest(router,
|
||||
"/api/authenticate", "POST", helper.AdminCredentials)
|
||||
|
||||
// POST $newSimulatorA
|
||||
newSimulatorA := SimulatorRequest{
|
||||
UUID: database.SimulatorA.UUID,
|
||||
Host: database.SimulatorA.Host,
|
||||
Modeltype: database.SimulatorA.Modeltype,
|
||||
State: database.SimulatorA.State,
|
||||
Properties: database.SimulatorA.Properties,
|
||||
// POST $newICA
|
||||
newICA := ICRequest{
|
||||
UUID: database.ICA.UUID,
|
||||
Host: database.ICA.Host,
|
||||
Modeltype: database.ICA.Modeltype,
|
||||
State: database.ICA.State,
|
||||
Properties: database.ICA.Properties,
|
||||
}
|
||||
_, resp, _ := helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulatorA})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newICA})
|
||||
|
||||
// Read newSimulator's ID from the response
|
||||
newSimulatorID, _ := helper.GetResponseID(resp)
|
||||
// Read newIC's ID from the response
|
||||
newICID, _ := helper.GetResponseID(resp)
|
||||
|
||||
// POST a second simulator to change to that simulator during testing
|
||||
newSimulatorB := SimulatorRequest{
|
||||
UUID: database.SimulatorB.UUID,
|
||||
Host: database.SimulatorB.Host,
|
||||
Modeltype: database.SimulatorB.Modeltype,
|
||||
State: database.SimulatorB.State,
|
||||
Properties: database.SimulatorB.Properties,
|
||||
// POST a second IC to change to that IC during testing
|
||||
newICB := ICRequest{
|
||||
UUID: database.ICB.UUID,
|
||||
Host: database.ICB.Host,
|
||||
Modeltype: database.ICB.Modeltype,
|
||||
State: database.ICB.State,
|
||||
Properties: database.ICB.Properties,
|
||||
}
|
||||
_, resp, _ = helper.TestEndpoint(router, token,
|
||||
"/api/simulators", "POST", helper.KeyModels{"simulator": newSimulatorB})
|
||||
"/api/ic", "POST", helper.KeyModels{"ic": newICB})
|
||||
|
||||
// authenticate as normal user
|
||||
token, _ = helper.AuthenticateForTest(router,
|
||||
|
@ -113,7 +113,7 @@ func addScenarioAndSimulator() (scenarioID uint, simulatorID uint) {
|
|||
_, resp, _ = helper.TestEndpoint(router, token,
|
||||
fmt.Sprintf("/api/scenarios/%v/user?username=User_C", newScenarioID), "PUT", nil)
|
||||
|
||||
return uint(newScenarioID), uint(newSimulatorID)
|
||||
return uint(newScenarioID), uint(newICID)
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
@ -137,9 +137,9 @@ func TestMain(m *testing.M) {
|
|||
// scenario endpoints required here to first add a scenario to the DB
|
||||
// that can be associated with a new simulation model
|
||||
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
|
||||
// simulator endpoints required here to first add a simulator to the DB
|
||||
// IC endpoints required here to first add a IC to the DB
|
||||
// that can be associated with a new simulation model
|
||||
simulator.RegisterSimulatorEndpoints(api.Group("/simulators"))
|
||||
infrastructure_component.RegisterICEndpoints(api.Group("/ic"))
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
@ -150,14 +150,14 @@ func TestAddSimulationModel(t *testing.T) {
|
|||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a simulator to the DB
|
||||
// by adding a scenario and a IC to the DB
|
||||
// using the respective endpoints of the API
|
||||
scenarioID, simulatorID := addScenarioAndSimulator()
|
||||
scenarioID, ICID := addScenarioAndIC()
|
||||
|
||||
newSimulationModel := SimulationModelRequest{
|
||||
Name: database.SimulationModelA.Name,
|
||||
ScenarioID: scenarioID,
|
||||
SimulatorID: simulatorID,
|
||||
ICID: ICID,
|
||||
StartParameters: database.SimulationModelA.StartParameters,
|
||||
SelectedModelFileID: database.SimulationModelA.SelectedModelFileID,
|
||||
}
|
||||
|
@ -246,9 +246,9 @@ func TestUpdateSimulationModel(t *testing.T) {
|
|||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a simulator to the DB
|
||||
// by adding a scenario and a IC to the DB
|
||||
// using the respective endpoints of the API
|
||||
scenarioID, simulatorID := addScenarioAndSimulator()
|
||||
scenarioID, ICID := addScenarioAndIC()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router,
|
||||
|
@ -259,7 +259,7 @@ func TestUpdateSimulationModel(t *testing.T) {
|
|||
newSimulationModel := SimulationModelRequest{
|
||||
Name: database.SimulationModelA.Name,
|
||||
ScenarioID: scenarioID,
|
||||
SimulatorID: simulatorID,
|
||||
ICID: ICID,
|
||||
StartParameters: database.SimulationModelA.StartParameters,
|
||||
SelectedModelFileID: database.SimulationModelA.SelectedModelFileID,
|
||||
}
|
||||
|
@ -323,8 +323,8 @@ func TestUpdateSimulationModel(t *testing.T) {
|
|||
err = helper.CompareResponse(resp, helper.KeyModels{"simulationModel": updatedSimulationModel})
|
||||
assert.NoError(t, err)
|
||||
|
||||
//Change simulator ID to use second simulator available in DB
|
||||
updatedSimulationModel.SimulatorID = simulatorID + 1
|
||||
//Change IC ID to use second IC available in DB
|
||||
updatedSimulationModel.ICID = ICID + 1
|
||||
// test PUT again
|
||||
code, resp, err = helper.TestEndpoint(router, token,
|
||||
fmt.Sprintf("/api/models/%v", newSimulationModelID), "PUT", helper.KeyModels{"simulationModel": updatedSimulationModel})
|
||||
|
@ -358,14 +358,14 @@ func TestDeleteSimulationModel(t *testing.T) {
|
|||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a simulator to the DB
|
||||
// by adding a scenario and a IC to the DB
|
||||
// using the respective endpoints of the API
|
||||
scenarioID, simulatorID := addScenarioAndSimulator()
|
||||
scenarioID, ICID := addScenarioAndIC()
|
||||
|
||||
newSimulationModel := SimulationModelRequest{
|
||||
Name: database.SimulationModelA.Name,
|
||||
ScenarioID: scenarioID,
|
||||
SimulatorID: simulatorID,
|
||||
ICID: ICID,
|
||||
StartParameters: database.SimulationModelA.StartParameters,
|
||||
SelectedModelFileID: database.SimulationModelA.SelectedModelFileID,
|
||||
}
|
||||
|
@ -431,9 +431,9 @@ func TestGetAllSimulationModelsOfScenario(t *testing.T) {
|
|||
assert.NoError(t, database.DBAddAdminAndUserAndGuest(db))
|
||||
|
||||
// prepare the content of the DB for testing
|
||||
// by adding a scenario and a simulator to the DB
|
||||
// by adding a scenario and a IC to the DB
|
||||
// using the respective endpoints of the API
|
||||
scenarioID, simulatorID := addScenarioAndSimulator()
|
||||
scenarioID, ICID := addScenarioAndIC()
|
||||
|
||||
// authenticate as normal user
|
||||
token, err := helper.AuthenticateForTest(router,
|
||||
|
@ -444,7 +444,7 @@ func TestGetAllSimulationModelsOfScenario(t *testing.T) {
|
|||
newSimulationModel := SimulationModelRequest{
|
||||
Name: database.SimulationModelA.Name,
|
||||
ScenarioID: scenarioID,
|
||||
SimulatorID: simulatorID,
|
||||
ICID: ICID,
|
||||
StartParameters: database.SimulationModelA.StartParameters,
|
||||
SelectedModelFileID: database.SimulationModelA.SelectedModelFileID,
|
||||
}
|
||||
|
|
|
@ -33,14 +33,14 @@ var validate *validator.Validate
|
|||
type validNewSimulationModel struct {
|
||||
Name string `form:"Name" validate:"required"`
|
||||
ScenarioID uint `form:"ScenarioID" validate:"required"`
|
||||
SimulatorID uint `form:"SimulatorID" validate:"required"`
|
||||
ICID uint `form:"ICID" validate:"required"`
|
||||
StartParameters postgres.Jsonb `form:"StartParameters" validate:"required"`
|
||||
SelectedModelFileID uint `form:"SelectedModelFilID" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type validUpdatedSimulationModel struct {
|
||||
Name string `form:"Name" validate:"omitempty"`
|
||||
SimulatorID uint `form:"SimulatorID" validate:"omitempty"`
|
||||
ICID uint `form:"ICID" validate:"omitempty"`
|
||||
StartParameters postgres.Jsonb `form:"StartParameters" validate:"omitempty"`
|
||||
SelectedModelFileID uint `form:"SelectedModelFileID" validate:"omitempty"`
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ func (r *addSimulationModelRequest) createSimulationModel() SimulationModel {
|
|||
|
||||
s.Name = r.SimulationModel.Name
|
||||
s.ScenarioID = r.SimulationModel.ScenarioID
|
||||
s.SimulatorID = r.SimulationModel.SimulatorID
|
||||
s.ICID = r.SimulationModel.ICID
|
||||
s.StartParameters = r.SimulationModel.StartParameters
|
||||
s.SelectedModelFileID = r.SimulationModel.SelectedModelFileID
|
||||
|
||||
|
@ -85,8 +85,8 @@ func (r *updateSimulationModelRequest) updatedSimulationModel(oldSimulationModel
|
|||
s.Name = r.SimulationModel.Name
|
||||
}
|
||||
|
||||
if r.SimulationModel.SimulatorID != 0 {
|
||||
s.SimulatorID = r.SimulationModel.SimulatorID
|
||||
if r.SimulationModel.ICID != 0 {
|
||||
s.ICID = r.SimulationModel.ICID
|
||||
}
|
||||
|
||||
if r.SimulationModel.SelectedModelFileID != 0 {
|
||||
|
|
10
start.go
10
start.go
|
@ -36,11 +36,11 @@ import (
|
|||
apidocs "git.rwth-aachen.de/acs/public/villas/web-backend-go/doc/api" // doc/api folder is used by Swag CLI, you have to import it
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/dashboard"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/file"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/infrastructure-component"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/metrics"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/signal"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/simulationmodel"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/simulator"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
|
||||
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/widget"
|
||||
)
|
||||
|
@ -120,7 +120,7 @@ func main() {
|
|||
widget.RegisterWidgetEndpoints(api.Group("/widgets"))
|
||||
file.RegisterFileEndpoints(api.Group("/files"))
|
||||
user.RegisterUserEndpoints(api.Group("/users"))
|
||||
simulator.RegisterSimulatorEndpoints(api.Group("/simulators"))
|
||||
infrastructure_component.RegisterICEndpoints(api.Group("/ic"))
|
||||
|
||||
r.GET("swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
|
||||
|
@ -133,10 +133,10 @@ func main() {
|
|||
log.Panic(err)
|
||||
}
|
||||
|
||||
// register simulator action endpoint only if AMQP client is used
|
||||
amqp.RegisterAMQPEndpoint(api.Group("/simulators"))
|
||||
// register IC action endpoint only if AMQP client is used
|
||||
amqp.RegisterAMQPEndpoint(api.Group("/ic"))
|
||||
|
||||
// Periodically call the Ping function to check which simulators are still there
|
||||
// Periodically call the Ping function to check which ICs are still there
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
go func() {
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue