mirror of
https://git.rwth-aachen.de/acs/public/villas/web-backend-go/
synced 2025-03-30 00:00:12 +01:00
Merge remote-tracking branch 'origin/master' into user-validators
This commit is contained in:
commit
e6c8fff397
54 changed files with 4039 additions and 3865 deletions
|
@ -2,6 +2,7 @@ variables:
|
|||
DEPLOY_USER: deploy
|
||||
DEPLOY_HOST: acs-os-fein-website
|
||||
DEPLOY_PATH: /var/www/villas/api/web/
|
||||
TEST_FOLDER: routes/scenario
|
||||
|
||||
stages:
|
||||
- prepare
|
||||
|
@ -64,7 +65,7 @@ test:api:bundle:
|
|||
- doc/api/index.html
|
||||
- doc/api/swagger.json
|
||||
|
||||
test:backend:database:
|
||||
test:database:
|
||||
stage: test
|
||||
tags:
|
||||
- docker
|
||||
|
@ -79,7 +80,7 @@ test:backend:database:
|
|||
dependencies:
|
||||
- build:backend
|
||||
|
||||
test:backend:endpoints:
|
||||
test:scenario:
|
||||
stage: test
|
||||
tags:
|
||||
- docker
|
||||
|
@ -89,13 +90,40 @@ test:backend:endpoints:
|
|||
- go mod tidy
|
||||
- go get -u github.com/swaggo/swag/cmd/swag
|
||||
- ~/go/bin/swag init -p pascalcase -g "start.go" -o "./doc/api/"
|
||||
- cd routes/simulation
|
||||
- go test -v -args -dbhost=/var/run/postgresql
|
||||
- cd ../simulationmodel
|
||||
- cd ${TEST_FOLDER}
|
||||
- go test -v -args -dbhost=/var/run/postgresql
|
||||
dependencies:
|
||||
- build:backend
|
||||
|
||||
test:simulationmodel:
|
||||
extends: test:scenario
|
||||
variables:
|
||||
TEST_FOLDER: routes/simulationmodel
|
||||
|
||||
test:signal:
|
||||
extends: test:scenario
|
||||
variables:
|
||||
TEST_FOLDER: routes/signal
|
||||
|
||||
test:dashboard:
|
||||
extends: test:scenario
|
||||
variables:
|
||||
TEST_FOLDER: routes/dashboard
|
||||
|
||||
test:widget:
|
||||
extends: test:scenario
|
||||
variables:
|
||||
TEST_FOLDER: routes/widget
|
||||
|
||||
test:simulator:
|
||||
extends: test:scenario
|
||||
variables:
|
||||
TEST_FOLDER: routes/simulator
|
||||
|
||||
test:file:
|
||||
extends: test:scenario
|
||||
variables:
|
||||
TEST_FOLDER: routes/file
|
||||
|
||||
|
||||
# Stage: deploy
|
||||
|
|
164
common/amqpclient.go
Normal file
164
common/amqpclient.go
Normal file
|
@ -0,0 +1,164 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/streadway/amqp"
|
||||
"github.com/tidwall/gjson"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const VILLAS_EXCHANGE = "villas"
|
||||
|
||||
type AMQPclient struct {
|
||||
connection *amqp.Connection
|
||||
channel *amqp.Channel
|
||||
replies <-chan amqp.Delivery
|
||||
}
|
||||
|
||||
type Action struct {
|
||||
Act string `json:"action"`
|
||||
When float32 `json:"when"`
|
||||
Parameters struct{} `json:"parameters"`
|
||||
Model struct{} `json:"model"`
|
||||
Results struct{} `json:"results"`
|
||||
}
|
||||
|
||||
var client AMQPclient
|
||||
|
||||
func ConnectAMQP(uri string) error {
|
||||
|
||||
var err error
|
||||
|
||||
// connect to broker
|
||||
client.connection, err = amqp.Dial(uri)
|
||||
if err != nil {
|
||||
return fmt.Errorf("AMQP: failed to connect to RabbitMQ broker")
|
||||
}
|
||||
|
||||
// create channel
|
||||
client.channel, err = client.connection.Channel()
|
||||
if err != nil {
|
||||
return fmt.Errorf("AMQP: failed to open a channel")
|
||||
}
|
||||
// declare exchange
|
||||
err = client.channel.ExchangeDeclare(VILLAS_EXCHANGE,
|
||||
"headers",
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("AMQP: failed to declare the exchange")
|
||||
}
|
||||
|
||||
// add a queue for the simulators
|
||||
simulatorQueue, err := client.channel.QueueDeclare("simulators",
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("AMQP: failed to declare the queue")
|
||||
}
|
||||
|
||||
err = client.channel.QueueBind(simulatorQueue.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,
|
||||
"",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("AMQP: failed to consume deliveries")
|
||||
}
|
||||
|
||||
// consuming queue
|
||||
go func() {
|
||||
for message := range client.replies {
|
||||
//err = message.Ack(false)
|
||||
//if err != nil {
|
||||
// fmt.Println("AMQP: Unable to ack message:", err)
|
||||
//}
|
||||
|
||||
content := string(message.Body)
|
||||
|
||||
// any action message sent by the VILLAScontroller should be ignored by the web backend
|
||||
if strings.Contains(content, "action") {
|
||||
continue
|
||||
}
|
||||
|
||||
var sToBeUpdated Simulator
|
||||
db := GetDB()
|
||||
err = db.Where("UUID = ?", gjson.Get(content, "properties.uuid")).Find(sToBeUpdated).Error
|
||||
if err != nil {
|
||||
fmt.Println("AMQP: Unable to find simulator with UUID: ", gjson.Get(content, "properties.uuid"), " DB error message: ", err)
|
||||
}
|
||||
|
||||
err = db.Model(&sToBeUpdated).Updates(map[string]interface{}{
|
||||
"Host": gjson.Get(content, "host"),
|
||||
"Modeltype": gjson.Get(content, "model"),
|
||||
"Uptime": gjson.Get(content, "uptime"),
|
||||
"State": gjson.Get(content, "state"),
|
||||
"StateUpdateAt": time.Now().String(),
|
||||
"RawProperties": gjson.Get(content, "properties"),
|
||||
}).Error
|
||||
if err != nil {
|
||||
fmt.Println("AMQP: Unable to update simulator in DB: ", err)
|
||||
}
|
||||
|
||||
fmt.Println("AMQP: Updated simulator with UUID ", gjson.Get(content, "properties.uuid"))
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendActionAMQP(action Action, uuid string) error {
|
||||
|
||||
payload, err := json.Marshal(action)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := amqp.Publishing{
|
||||
DeliveryMode: 2,
|
||||
Timestamp: time.Now(),
|
||||
ContentType: "application/json",
|
||||
ContentEncoding: "utf-8",
|
||||
Priority: 0,
|
||||
Body: payload,
|
||||
}
|
||||
|
||||
if uuid != "" {
|
||||
msg.Headers["uuid"] = uuid
|
||||
msg.Headers["action"] = "ping"
|
||||
}
|
||||
|
||||
err = client.channel.Publish(VILLAS_EXCHANGE,
|
||||
"",
|
||||
false,
|
||||
false,
|
||||
msg)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
func PingAMQP() error {
|
||||
fmt.Println("AMQP: sending ping command to all simulators")
|
||||
|
||||
var a Action
|
||||
a.Act = "ping"
|
||||
|
||||
err := SendActionAMQP(a, "")
|
||||
return err
|
||||
}
|
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"log"
|
||||
)
|
||||
|
||||
|
@ -13,6 +12,7 @@ var DB_HOST string
|
|||
var DB_NAME string
|
||||
var DB_DUMMY string
|
||||
var DB_SSLMODE string
|
||||
var WITH_AMQP bool
|
||||
|
||||
var DBpool *gorm.DB
|
||||
|
||||
|
@ -22,11 +22,13 @@ func init() {
|
|||
flag.StringVar(&DB_NAME, "dbname", "villasdb", "Name of the database to use (default is villasdb)")
|
||||
flag.StringVar(&DB_DUMMY, "dbdummy", "testvillasdb", "Name of the test database to use (default is testvillasdb)")
|
||||
flag.StringVar(&DB_SSLMODE, "dbsslmode", "disable", "SSL mode of DB (default is disable)") // TODO: change default for production
|
||||
flag.BoolVar(&WITH_AMQP, "amqp", false, "If AMQP client for simulators shall be enabled, set this option to true (default is false)")
|
||||
flag.Parse()
|
||||
fmt.Println("DB_HOST has value ", DB_HOST)
|
||||
fmt.Println("DB_NAME has value ", DB_NAME)
|
||||
fmt.Println("DB_DUMMY has value ", DB_DUMMY)
|
||||
fmt.Println("DB_SSLMODE has value ", DB_SSLMODE)
|
||||
fmt.Println("WITH_AMQP has value ", WITH_AMQP)
|
||||
}
|
||||
|
||||
// Initialize connection to the database
|
||||
|
@ -57,9 +59,9 @@ func DropTables(db *gorm.DB) {
|
|||
db.DropTableIfExists(&Signal{})
|
||||
db.DropTableIfExists(&SimulationModel{})
|
||||
db.DropTableIfExists(&File{})
|
||||
db.DropTableIfExists(&Simulation{})
|
||||
db.DropTableIfExists(&Scenario{})
|
||||
db.DropTableIfExists(&User{})
|
||||
db.DropTableIfExists(&Visualization{})
|
||||
db.DropTableIfExists(&Dashboard{})
|
||||
db.DropTableIfExists(&Widget{})
|
||||
}
|
||||
|
||||
|
@ -69,9 +71,9 @@ func MigrateModels(db *gorm.DB) {
|
|||
db.AutoMigrate(&Signal{})
|
||||
db.AutoMigrate(&SimulationModel{})
|
||||
db.AutoMigrate(&File{})
|
||||
db.AutoMigrate(&Simulation{})
|
||||
db.AutoMigrate(&Scenario{})
|
||||
db.AutoMigrate(&User{})
|
||||
db.AutoMigrate(&Visualization{})
|
||||
db.AutoMigrate(&Dashboard{})
|
||||
db.AutoMigrate(&Widget{})
|
||||
}
|
||||
|
||||
|
@ -94,109 +96,84 @@ func DummyPopulateDB(test_db *gorm.DB) {
|
|||
|
||||
MigrateModels(test_db)
|
||||
|
||||
// Create two entries of each model
|
||||
// Create entries of each model (data defined in testdata.go)
|
||||
|
||||
simr_A := Simulator{UUID: "1", Host: "Host_A"}
|
||||
simr_B := Simulator{UUID: "2", Host: "Host_B"}
|
||||
checkErr(test_db.Create(&simr_A).Error)
|
||||
checkErr(test_db.Create(&simr_B).Error)
|
||||
// Users
|
||||
checkErr(test_db.Create(&User0).Error) // Admin
|
||||
checkErr(test_db.Create(&UserA).Error) // Normal User
|
||||
checkErr(test_db.Create(&UserB).Error) // Normal User
|
||||
|
||||
outSig_A := Signal{Name: "outSignal_A", Direction: "out", Index: 0, Unit: "V"}
|
||||
outSig_B := Signal{Name: "outSignal_B", Direction: "out", Index: 1, Unit: "V"}
|
||||
inSig_A := Signal{Name: "inSignal_A", Direction: "in", Index: 0, Unit: "A"}
|
||||
inSig_B := Signal{Name: "inSignal_B", Direction: "in", Index: 1, Unit: "A"}
|
||||
checkErr(test_db.Create(&outSig_A).Error)
|
||||
checkErr(test_db.Create(&outSig_B).Error)
|
||||
checkErr(test_db.Create(&inSig_A).Error)
|
||||
checkErr(test_db.Create(&inSig_B).Error)
|
||||
// Simulators
|
||||
checkErr(test_db.Create(&SimulatorA).Error)
|
||||
checkErr(test_db.Create(&SimulatorB).Error)
|
||||
|
||||
mo_A := SimulationModel{Name: "SimulationModel_A"}
|
||||
mo_B := SimulationModel{Name: "SimulationModel_B"}
|
||||
checkErr(test_db.Create(&mo_A).Error)
|
||||
checkErr(test_db.Create(&mo_B).Error)
|
||||
// Scenarios
|
||||
checkErr(test_db.Create(&ScenarioA).Error)
|
||||
checkErr(test_db.Create(&ScenarioB).Error)
|
||||
|
||||
file_A := File{Name: "File_A"}
|
||||
file_B := File{Name: "File_B"}
|
||||
checkErr(test_db.Create(&file_A).Error)
|
||||
checkErr(test_db.Create(&file_B).Error)
|
||||
// Signals
|
||||
checkErr(test_db.Create(&OutSignalA).Error)
|
||||
checkErr(test_db.Create(&OutSignalB).Error)
|
||||
checkErr(test_db.Create(&InSignalA).Error)
|
||||
checkErr(test_db.Create(&InSignalB).Error)
|
||||
|
||||
simn_A := Simulation{Name: "Simulation_A"}
|
||||
simn_B := Simulation{Name: "Simulation_B"}
|
||||
checkErr(test_db.Create(&simn_A).Error)
|
||||
checkErr(test_db.Create(&simn_B).Error)
|
||||
// Simulation Models
|
||||
checkErr(test_db.Create(&SimulationModelA).Error)
|
||||
checkErr(test_db.Create(&SimulationModelB).Error)
|
||||
|
||||
// Hash passwords with bcrypt algorithm
|
||||
var bcryptCost = 10
|
||||
// Dashboards
|
||||
checkErr(test_db.Create(&DashboardA).Error)
|
||||
checkErr(test_db.Create(&DashboardB).Error)
|
||||
|
||||
pw_0, err :=
|
||||
bcrypt.GenerateFromPassword([]byte("xyz789"), bcryptCost)
|
||||
checkErr(err)
|
||||
// Files
|
||||
checkErr(test_db.Create(&FileA).Error)
|
||||
checkErr(test_db.Create(&FileB).Error)
|
||||
checkErr(test_db.Create(&FileC).Error)
|
||||
checkErr(test_db.Create(&FileD).Error)
|
||||
|
||||
pw_A, err :=
|
||||
bcrypt.GenerateFromPassword([]byte("abc123"), bcryptCost)
|
||||
checkErr(err)
|
||||
|
||||
pw_B, err :=
|
||||
bcrypt.GenerateFromPassword([]byte("bcd234"), bcryptCost)
|
||||
checkErr(err)
|
||||
|
||||
usr_0 := User{Username: "User_0", Password: string(pw_0), Role: "Admin"}
|
||||
usr_A := User{Username: "User_A", Password: string(pw_A), Role: "User"}
|
||||
usr_B := User{Username: "User_B", Password: string(pw_B), Role: "User"}
|
||||
checkErr(test_db.Create(&usr_0).Error)
|
||||
checkErr(test_db.Create(&usr_A).Error)
|
||||
checkErr(test_db.Create(&usr_B).Error)
|
||||
|
||||
vis_A := Visualization{Name: "Visualization_A"}
|
||||
vis_B := Visualization{Name: "Visualization_B"}
|
||||
checkErr(test_db.Create(&vis_A).Error)
|
||||
checkErr(test_db.Create(&vis_B).Error)
|
||||
|
||||
widg_A := Widget{Name: "Widget_A"}
|
||||
widg_B := Widget{Name: "Widget_B"}
|
||||
checkErr(test_db.Create(&widg_A).Error)
|
||||
checkErr(test_db.Create(&widg_B).Error)
|
||||
// Widgets
|
||||
checkErr(test_db.Create(&WidgetA).Error)
|
||||
checkErr(test_db.Create(&WidgetB).Error)
|
||||
|
||||
// Associations between models
|
||||
// For `belongs to` use the model with id=1
|
||||
// For `has many` use the models with id=1 and id=2
|
||||
|
||||
// User HM Simulations, Simulation HM Users (Many-to-Many)
|
||||
checkErr(test_db.Model(&simn_A).Association("Users").Append(&usr_A).Error)
|
||||
checkErr(test_db.Model(&simn_A).Association("Users").Append(&usr_B).Error)
|
||||
checkErr(test_db.Model(&simn_B).Association("Users").Append(&usr_A).Error)
|
||||
checkErr(test_db.Model(&simn_B).Association("Users").Append(&usr_B).Error)
|
||||
// User HM Scenarios, Scenario HM Users (Many-to-Many)
|
||||
checkErr(test_db.Model(&ScenarioA).Association("Users").Append(&UserA).Error)
|
||||
checkErr(test_db.Model(&ScenarioA).Association("Users").Append(&UserB).Error)
|
||||
checkErr(test_db.Model(&ScenarioB).Association("Users").Append(&UserA).Error)
|
||||
checkErr(test_db.Model(&ScenarioB).Association("Users").Append(&UserB).Error)
|
||||
|
||||
// Simulation HM SimulationModels
|
||||
checkErr(test_db.Model(&simn_A).Association("SimulationModels").Append(&mo_A).Error)
|
||||
checkErr(test_db.Model(&simn_A).Association("SimulationModels").Append(&mo_B).Error)
|
||||
// Scenario HM SimulationModels
|
||||
checkErr(test_db.Model(&ScenarioA).Association("SimulationModels").Append(&SimulationModelA).Error)
|
||||
checkErr(test_db.Model(&ScenarioA).Association("SimulationModels").Append(&SimulationModelB).Error)
|
||||
|
||||
// Simulation HM Visualizations
|
||||
checkErr(test_db.Model(&simn_A).Association("Visualizations").Append(&vis_A).Error)
|
||||
checkErr(test_db.Model(&simn_A).Association("Visualizations").Append(&vis_B).Error)
|
||||
// Scenario HM Dashboards
|
||||
checkErr(test_db.Model(&ScenarioA).Association("Dashboards").Append(&DashboardA).Error)
|
||||
checkErr(test_db.Model(&ScenarioA).Association("Dashboards").Append(&DashboardB).Error)
|
||||
|
||||
// Visualization HM Widget
|
||||
checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_A).Error)
|
||||
checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_B).Error)
|
||||
// Dashboard HM Widget
|
||||
checkErr(test_db.Model(&DashboardA).Association("Widgets").Append(&WidgetA).Error)
|
||||
checkErr(test_db.Model(&DashboardA).Association("Widgets").Append(&WidgetB).Error)
|
||||
|
||||
// SimulationModel HM Signals
|
||||
checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSig_A).Error)
|
||||
checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSig_B).Error)
|
||||
checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_A).Error)
|
||||
checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_B).Error)
|
||||
checkErr(test_db.Model(&SimulationModelA).Association("InputMapping").Append(&InSignalA).Error)
|
||||
checkErr(test_db.Model(&SimulationModelA).Association("InputMapping").Append(&InSignalB).Error)
|
||||
checkErr(test_db.Model(&SimulationModelA).Association("OutputMapping").Append(&OutSignalA).Error)
|
||||
checkErr(test_db.Model(&SimulationModelA).Association("OutputMapping").Append(&OutSignalB).Error)
|
||||
|
||||
// SimulationModel HM Files
|
||||
checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_A).Error)
|
||||
checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_B).Error)
|
||||
checkErr(test_db.Model(&SimulationModelA).Association("Files").Append(&FileC).Error)
|
||||
checkErr(test_db.Model(&SimulationModelA).Association("Files").Append(&FileD).Error)
|
||||
|
||||
// Simulator BT SimulationModel
|
||||
checkErr(test_db.Model(&mo_A).Association("Simulator").Append(&simr_A).Error)
|
||||
checkErr(test_db.Model(&mo_B).Association("Simulator").Append(&simr_A).Error)
|
||||
// Simulator HM SimulationModels
|
||||
checkErr(test_db.Model(&SimulatorA).Association("SimulationModels").Append(&SimulationModelA).Error)
|
||||
checkErr(test_db.Model(&SimulatorA).Association("SimulationModels").Append(&SimulationModelB).Error)
|
||||
|
||||
// Widget HM Files
|
||||
checkErr(test_db.Model(&widg_A).Association("Files").Append(&file_A).Error)
|
||||
checkErr(test_db.Model(&widg_A).Association("Files").Append(&file_B).Error)
|
||||
|
||||
checkErr(test_db.Model(&WidgetA).Association("Files").Append(&FileA).Error)
|
||||
checkErr(test_db.Model(&WidgetA).Association("Files").Append(&FileB).Error)
|
||||
}
|
||||
|
||||
// Erase tables and glose the testdb
|
||||
|
|
|
@ -31,18 +31,18 @@ func TestDummyDBAssociations(t *testing.T) {
|
|||
var simr Simulator
|
||||
var mo SimulationModel
|
||||
var file File
|
||||
var simn Simulation
|
||||
var so Scenario
|
||||
var usr User
|
||||
var usrs []User
|
||||
var vis Visualization
|
||||
var dab Dashboard
|
||||
var widg Widget
|
||||
|
||||
var sigs []Signal
|
||||
var mos []SimulationModel
|
||||
var files []File
|
||||
var files_sm []File
|
||||
var simns []Simulation
|
||||
var viss []Visualization
|
||||
var sos []Scenario
|
||||
var dabs []Dashboard
|
||||
var widgs []Widget
|
||||
|
||||
// User
|
||||
|
@ -52,35 +52,46 @@ func TestDummyDBAssociations(t *testing.T) {
|
|||
|
||||
// User Associations
|
||||
|
||||
a.NoError(db.Model(&usr).Related(&simns, "Simulations").Error)
|
||||
if len(simns) != 2 {
|
||||
a.NoError(db.Model(&usr).Related(&sos, "Scenarios").Error)
|
||||
if len(sos) != 2 {
|
||||
a.Fail("User Associations",
|
||||
"Expected to have %v Simulations. Has %v.", 2, len(simns))
|
||||
"Expected to have %v Scenarios. Has %v.", 2, len(sos))
|
||||
}
|
||||
|
||||
// Simulation
|
||||
// Scenario
|
||||
|
||||
a.NoError(db.Find(&simn, 1).Error, fM("Simulation"))
|
||||
a.EqualValues("Simulation_A", simn.Name)
|
||||
a.NoError(db.Find(&so, 1).Error, fM("Scenario"))
|
||||
a.EqualValues("Scenario_A", so.Name)
|
||||
|
||||
// Simulation Associations
|
||||
// Scenario Associations
|
||||
|
||||
a.NoError(db.Model(&simn).Association("Users").Find(&usrs).Error)
|
||||
a.NoError(db.Model(&so).Association("Users").Find(&usrs).Error)
|
||||
if len(usrs) != 2 {
|
||||
a.Fail("Simulations Associations",
|
||||
a.Fail("Scenario Associations",
|
||||
"Expected to have %v Users. Has %v.", 2, len(usrs))
|
||||
}
|
||||
|
||||
a.NoError(db.Model(&simn).Related(&mos, "SimulationModels").Error)
|
||||
a.NoError(db.Model(&so).Related(&mos, "SimulationModels").Error)
|
||||
if len(mos) != 2 {
|
||||
a.Fail("Simulation Associations",
|
||||
a.Fail("Scenario Associations",
|
||||
"Expected to have %v simulation models. Has %v.", 2, len(mos))
|
||||
}
|
||||
|
||||
a.NoError(db.Model(&simn).Related(&viss, "Visualizations").Error)
|
||||
if len(viss) != 2 {
|
||||
a.Fail("Simulation Associations",
|
||||
"Expected to have %v Visualizations. Has %v.", 2, len(viss))
|
||||
a.NoError(db.Model(&so).Related(&dabs, "Dashboards").Error)
|
||||
if len(dabs) != 2 {
|
||||
a.Fail("Scenario Associations",
|
||||
"Expected to have %v Dashboards. Has %v.", 2, len(dabs))
|
||||
}
|
||||
|
||||
// Simulator
|
||||
a.NoError(db.Find(&simr, 1).Error, fM("Simulator"))
|
||||
a.EqualValues("Host_A", simr.Host)
|
||||
|
||||
// Simulator Associations
|
||||
a.NoError(db.Model(&simr).Association("SimulationModels").Find(&mos).Error)
|
||||
if len(mos) != 2 {
|
||||
a.Fail("Simulator Associations",
|
||||
"Expected to have %v SimulationModels. Has %v.", 2, len(mos))
|
||||
}
|
||||
|
||||
// SimulationModel
|
||||
|
@ -90,9 +101,6 @@ func TestDummyDBAssociations(t *testing.T) {
|
|||
|
||||
// SimulationModel Associations
|
||||
|
||||
a.NoError(db.Model(&mo).Association("Simulator").Find(&simr).Error)
|
||||
a.EqualValues("Host_A", simr.Host, "Expected Host_A")
|
||||
|
||||
a.NoError(db.Model(&mo).Where("Direction = ?", "out").Related(&sigs, "OutputMapping").Error)
|
||||
if len(sigs) != 2 {
|
||||
a.Fail("SimulationModel Associations",
|
||||
|
@ -105,14 +113,16 @@ func TestDummyDBAssociations(t *testing.T) {
|
|||
"Expected to have %v Files. Has %v.", 2, len(files_sm))
|
||||
}
|
||||
|
||||
// Visualization
|
||||
fmt.Println("SimulatorID: ", mo.SimulatorID)
|
||||
|
||||
a.NoError(db.Find(&vis, 1).Error, fM("Visualization"))
|
||||
a.EqualValues("Visualization_A", vis.Name)
|
||||
// Dashboard
|
||||
|
||||
// Visualization Associations
|
||||
a.NoError(db.Find(&dab, 1).Error, fM("Dashboard"))
|
||||
a.EqualValues("Dashboard_A", dab.Name)
|
||||
|
||||
a.NoError(db.Model(&vis).Related(&widgs, "Widgets").Error)
|
||||
// Dashboard Associations
|
||||
|
||||
a.NoError(db.Model(&dab).Related(&widgs, "Widgets").Error)
|
||||
if len(widgs) != 2 {
|
||||
a.Fail("Widget Associations",
|
||||
"Expected to have %v Widget. Has %v.", 2, len(widgs))
|
||||
|
|
125
common/models.go
125
common/models.go
|
@ -1,13 +1,13 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/jinzhu/gorm/dialects/postgres"
|
||||
)
|
||||
|
||||
// User data model
|
||||
type User struct {
|
||||
// ID of user
|
||||
ID uint `gorm:"primary_key;auto_increment"`
|
||||
gorm.Model
|
||||
// Username of user
|
||||
Username string `gorm:"unique;not null"`
|
||||
// Password of user
|
||||
|
@ -16,44 +16,40 @@ type User struct {
|
|||
Mail string `gorm:"default:''"`
|
||||
// Role of user
|
||||
Role string `gorm:"default:'user'"`
|
||||
// Simulations to which user has access
|
||||
Simulations []*Simulation `gorm:"many2many:user_simulations"`
|
||||
// Scenarios to which user has access
|
||||
Scenarios []*Scenario `gorm:"many2many:user_scenarios"`
|
||||
}
|
||||
|
||||
// Simulation data model
|
||||
type Simulation struct {
|
||||
// ID of simulation
|
||||
ID uint `gorm:"primary_key;auto_increment"`
|
||||
// Name of simulation
|
||||
// Scenario data model
|
||||
type Scenario struct {
|
||||
gorm.Model
|
||||
// Name of scenario
|
||||
Name string `gorm:"not null"`
|
||||
// Running state of simulation
|
||||
// Running state of scenario
|
||||
Running bool `gorm:"default:false"`
|
||||
// Start parameters of simulation as JSON string
|
||||
StartParameters string
|
||||
// Users that have access to the simulation
|
||||
Users []*User `gorm:"not null;many2many:user_simulations"`
|
||||
// SimulationModels that belong to the simulation
|
||||
SimulationModels []SimulationModel `gorm:"foreignkey:SimulationID"`
|
||||
// Visualizations that belong to the simulation
|
||||
Visualizations []Visualization `gorm:"foreignkey:SimulationID"`
|
||||
// Start parameters of scenario as JSON
|
||||
StartParameters postgres.Jsonb
|
||||
// Users that have access to the scenario
|
||||
Users []*User `gorm:"not null;many2many:user_scenarios"`
|
||||
// SimulationModels that belong to the scenario
|
||||
SimulationModels []SimulationModel `gorm:"foreignkey:ScenarioID"`
|
||||
// Dashboards that belong to the Scenario
|
||||
Dashboards []Dashboard `gorm:"foreignkey:ScenarioID"`
|
||||
}
|
||||
|
||||
// SimulationModel data model
|
||||
type SimulationModel struct {
|
||||
// ID of simulation model
|
||||
ID uint `gorm:"primary_key;auto_increment"`
|
||||
gorm.Model
|
||||
// Name of simulation model
|
||||
Name string `gorm:"not null"`
|
||||
// Number of output signals
|
||||
OutputLength int `gorm:"default:1"`
|
||||
// Number of input signals
|
||||
InputLength int `gorm:"default:1"`
|
||||
// Start parameters of simulation model as JSON string
|
||||
StartParameters string
|
||||
// ID of simulation to which simulation model belongs
|
||||
SimulationID uint
|
||||
// Simulator associated with simulation model
|
||||
Simulator Simulator
|
||||
// Start parameters of simulation model as JSON
|
||||
StartParameters postgres.Jsonb
|
||||
// ID of Scenario to which simulation model belongs
|
||||
ScenarioID uint
|
||||
// ID of simulator associated with simulation model
|
||||
SimulatorID uint
|
||||
// Mapping of output signals of the simulation model, order of signals is important
|
||||
|
@ -66,8 +62,7 @@ type SimulationModel struct {
|
|||
|
||||
// Signal data model
|
||||
type Signal struct {
|
||||
// ID of simulation model
|
||||
ID uint `gorm:"primary_key;auto_increment"`
|
||||
gorm.Model
|
||||
// Name of Signal
|
||||
Name string
|
||||
// Unit of Signal
|
||||
|
@ -82,44 +77,43 @@ type Signal struct {
|
|||
|
||||
// Simulator data model
|
||||
type Simulator struct {
|
||||
// ID of the simulator
|
||||
ID uint `gorm:"primary_key;auto_increment"`
|
||||
gorm.Model
|
||||
// UUID of the simulator
|
||||
UUID string `gorm:"unique;not null"`
|
||||
UUID string `gorm:"not null",json:"uuid"`
|
||||
// Host if the simulator
|
||||
Host string `gorm:"default:''"`
|
||||
Host string `gorm:"default:''",json:"host"`
|
||||
// Model type supported by the simulator
|
||||
Modeltype string `gorm:"default:''"`
|
||||
Modeltype string `gorm:"default:''",json:"modelType"`
|
||||
// Uptime of the simulator
|
||||
Uptime int `gorm:"default:0"`
|
||||
Uptime int `gorm:"default:0",json:"uptime"`
|
||||
// State of the simulator
|
||||
State string `gorm:"default:''"`
|
||||
State string `gorm:"default:''",json:"state"`
|
||||
// Time of last state update
|
||||
StateUpdateAt time.Time
|
||||
StateUpdateAt string `gorm:"default:''",json:"stateUpdateAt"`
|
||||
// Properties of simulator as JSON string
|
||||
Properties string
|
||||
Properties postgres.Jsonb `json:"properties"`
|
||||
// Raw properties of simulator as JSON string
|
||||
RawProperties string
|
||||
RawProperties postgres.Jsonb `json:"rawProperties"`
|
||||
// SimulationModels in which the simulator is used
|
||||
SimulationModels []SimulationModel `gorm:"foreignkey:SimulatorID"`
|
||||
}
|
||||
|
||||
// Visualization data model
|
||||
type Visualization struct {
|
||||
// ID of visualization
|
||||
ID uint `gorm:"primary_key;auto_increment"`
|
||||
// Name of visualization
|
||||
// Dashboard data model
|
||||
type Dashboard struct {
|
||||
gorm.Model
|
||||
// Name of dashboard
|
||||
Name string `gorm:"not null"`
|
||||
// Grid of visualization
|
||||
// Grid of dashboard
|
||||
Grid int `gorm:"default:15"`
|
||||
// ID of simulation to which visualization belongs
|
||||
SimulationID uint `gorm:"not null"`
|
||||
// Widgets that belong to visualization
|
||||
Widgets []Widget `gorm:"foreignkey:VisualizationID"`
|
||||
// ID of scenario to which dashboard belongs
|
||||
ScenarioID uint
|
||||
// Widgets that belong to dashboard
|
||||
Widgets []Widget `gorm:"foreignkey:DashboardID"`
|
||||
}
|
||||
|
||||
// Widget data model
|
||||
type Widget struct {
|
||||
// ID of widget
|
||||
ID uint `gorm:"primary_key;auto_increment"`
|
||||
gorm.Model
|
||||
// Name of widget
|
||||
Name string `gorm:"not null"`
|
||||
// Type of widget
|
||||
|
@ -141,33 +135,38 @@ type Widget struct {
|
|||
// Locked state of widget
|
||||
IsLocked bool `gorm:"default:false"`
|
||||
// Custom properties of widget as JSON string
|
||||
CustomProperties string
|
||||
// ID of visualization to which widget belongs
|
||||
VisualizationID uint `gorm:"not null"`
|
||||
CustomProperties postgres.Jsonb
|
||||
// ID of dashboard to which widget belongs
|
||||
DashboardID uint
|
||||
// Files that belong to widget (for example images)
|
||||
Files []File `gorm:"foreignkey:WidgetID"`
|
||||
}
|
||||
|
||||
// File data model
|
||||
type File struct {
|
||||
// ID of file
|
||||
ID uint `gorm:"primary_key;auto_increment"`
|
||||
gorm.Model
|
||||
// Name of file
|
||||
Name string `gorm:"not null"`
|
||||
// Path at which file is saved at server side
|
||||
Path string `gorm:"not null"`
|
||||
// Type of file (MIME type)
|
||||
Type string `gorm:"not null"`
|
||||
Type string
|
||||
// Size of file (in byte)
|
||||
Size uint `gorm:"not null"`
|
||||
Size uint
|
||||
// Height of image (only needed in case of image)
|
||||
ImageHeight uint
|
||||
// Width of image (only needed in case of image)
|
||||
ImageWidth uint
|
||||
// Last modification time of file
|
||||
Date time.Time
|
||||
Date string
|
||||
// ID of model to which file belongs
|
||||
SimulationModelID uint `gorm:""`
|
||||
SimulationModelID uint
|
||||
// ID of widget to which file belongs
|
||||
WidgetID uint `gorm:""`
|
||||
WidgetID uint
|
||||
// File itself
|
||||
FileData []byte `gorm:"column:FileData"`
|
||||
}
|
||||
|
||||
// Credentials type (not for DB)
|
||||
type credentials struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
|
|
@ -1,81 +1,84 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
import "github.com/jinzhu/gorm/dialects/postgres"
|
||||
|
||||
type UserResponse struct {
|
||||
Username string `json:"Username"`
|
||||
Role string `json:"Role"`
|
||||
Mail string `json:"Mail"`
|
||||
Username string `json:"username"`
|
||||
Role string `json:"role"`
|
||||
Mail string `json:"mail"`
|
||||
ID uint `json:"id"`
|
||||
}
|
||||
|
||||
type SimulationResponse struct {
|
||||
Name string `json:"Name"`
|
||||
ID uint `json:"SimulationID"`
|
||||
Running bool `json:"Running"`
|
||||
StartParams string `json:"Starting Parameters"`
|
||||
type ScenarioResponse struct {
|
||||
Name string `json:"name"`
|
||||
ID uint `json:"id"`
|
||||
Running bool `json:"running"`
|
||||
StartParameters postgres.Jsonb `json:"startParameters"`
|
||||
}
|
||||
|
||||
type SimulationModelResponse struct {
|
||||
ID uint `json:"ID"`
|
||||
Name string `json:"Name"`
|
||||
OutputLength int `json:"OutputLength"`
|
||||
InputLength int `json:"InputLength"`
|
||||
SimulationID uint `json:"SimulationID"`
|
||||
SimulatorID uint `json:"SimulatorID"`
|
||||
StartParams string `json:"StartParams"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
OutputLength int `json:"outputLength"`
|
||||
InputLength int `json:"inputLength"`
|
||||
ScenarioID uint `json:"scenarioID"`
|
||||
SimulatorID uint `json:"simulatorID"`
|
||||
StartParameters postgres.Jsonb `json:"startParameters"`
|
||||
}
|
||||
|
||||
type SimulatorResponse struct {
|
||||
UUID string `json:"UUID"`
|
||||
Host string `json:"Host"`
|
||||
ModelType string `json:"ModelType"`
|
||||
Uptime int `json:"Uptime"`
|
||||
State string `json:"State"`
|
||||
StateUpdateAt time.Time `json:"StateUpdateAt"`
|
||||
Properties string `json:"Properties"`
|
||||
RawProperties string `json:"RawProperties"`
|
||||
ID uint `json:"id"`
|
||||
UUID string `json:"uuid"`
|
||||
Host string `json:"host"`
|
||||
Modeltype string `json:"modelType"`
|
||||
Uptime int `json:"uptime"`
|
||||
State string `json:"state"`
|
||||
StateUpdateAt string `json:"stateUpdateAt"`
|
||||
Properties postgres.Jsonb `json:"properties"`
|
||||
RawProperties postgres.Jsonb `json:"rawProperties"`
|
||||
}
|
||||
|
||||
type VisualizationResponse struct {
|
||||
Name string `json:"Name"`
|
||||
Grid int `json:"Grid"`
|
||||
SimulationID uint `json:"SimulationID"`
|
||||
type DashboardResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Grid int `json:"grid"`
|
||||
ScenarioID uint `json:"scenarioID"`
|
||||
}
|
||||
|
||||
type WidgetResponse struct {
|
||||
Name string `json:"Name"`
|
||||
Type string `json:"Type"`
|
||||
Width uint `json:"Width"`
|
||||
Height uint `json:"Height"`
|
||||
MinWidth uint `json:"MinWidth"`
|
||||
MinHeight uint `json:"MinHeight"`
|
||||
X int `json:"X"`
|
||||
Y int `json:"Y"`
|
||||
Z int `json:"Z"`
|
||||
VisualizationID uint `json:"VisualizationID"`
|
||||
IsLocked bool `json:"IsLocked"`
|
||||
CustomProperties string `json:"CustomProperties"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Width uint `json:"width"`
|
||||
Height uint `json:"height"`
|
||||
MinWidth uint `json:"minWidth"`
|
||||
MinHeight uint `json:"minHeight"`
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
Z int `json:"z"`
|
||||
DashboardID uint `json:"dashboardID"`
|
||||
IsLocked bool `json:"isLocked"`
|
||||
CustomProperties postgres.Jsonb `json:"customProperties"`
|
||||
}
|
||||
|
||||
type FileResponse struct {
|
||||
Name string `json:"Name"`
|
||||
ID uint `json:"FileID"`
|
||||
Path string `json:"Path"`
|
||||
Type string `json:"Type"`
|
||||
Size uint `json:"Size"`
|
||||
H uint `json:"ImageHeight"`
|
||||
W uint `json:"ImageWidth"`
|
||||
Date time.Time `json:"Date"`
|
||||
Name string `json:"name"`
|
||||
ID uint `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Size uint `json:"size"`
|
||||
ImageWidth uint `json:"imageHeight"`
|
||||
ImageHeight uint `json:"imageWidth"`
|
||||
Date string `json:"date"`
|
||||
WidgetID uint `json:"widgetID"`
|
||||
SimulationModelID uint `json:"simulationModelID"`
|
||||
}
|
||||
|
||||
type SignalResponse struct {
|
||||
Name string `json:"Name"`
|
||||
Unit string `json:"Unit"`
|
||||
Index uint `json:"Index"`
|
||||
Direction string `json:"Direction"`
|
||||
SimulationModelID uint `json:"SimulationModelID"`
|
||||
Name string `json:"name"`
|
||||
Unit string `json:"unit"`
|
||||
Index uint `json:"index"`
|
||||
Direction string `json:"direction"`
|
||||
SimulationModelID uint `json:"simulationModelID"`
|
||||
}
|
||||
|
||||
// Response messages
|
||||
|
@ -92,12 +95,12 @@ type ResponseMsgUser struct {
|
|||
User UserResponse `json:"user"`
|
||||
}
|
||||
|
||||
type ResponseMsgSimulations struct {
|
||||
Simulations []SimulationResponse `json:"simulations"`
|
||||
type ResponseMsgScenarios struct {
|
||||
Scenarios []ScenarioResponse `json:"scenarios"`
|
||||
}
|
||||
|
||||
type ResponseMsgSimulation struct {
|
||||
Simulation SimulationResponse `json:"simulation"`
|
||||
type ResponseMsgScenario struct {
|
||||
Scenario ScenarioResponse `json:"scenario"`
|
||||
}
|
||||
|
||||
type ResponseMsgSimulationModels struct {
|
||||
|
@ -111,3 +114,39 @@ type ResponseMsgSimulationModel struct {
|
|||
type ResponseMsgSignals struct {
|
||||
Signals []SignalResponse `json:"signals"`
|
||||
}
|
||||
|
||||
type ResponseMsgSignal struct {
|
||||
Signal SignalResponse `json:"signal"`
|
||||
}
|
||||
|
||||
type ResponseMsgDashboards struct {
|
||||
Dashboards []DashboardResponse `json:"dashboards"`
|
||||
}
|
||||
|
||||
type ResponseMsgDashboard struct {
|
||||
Dashboard DashboardResponse `json:"dashboard"`
|
||||
}
|
||||
|
||||
type ResponseMsgWidgets struct {
|
||||
Widgets []WidgetResponse `json:"widgets"`
|
||||
}
|
||||
|
||||
type ResponseMsgWidget struct {
|
||||
Widget WidgetResponse `json:"widget"`
|
||||
}
|
||||
|
||||
type ResponseMsgSimulators struct {
|
||||
Simulators []SimulatorResponse `json:"simulators"`
|
||||
}
|
||||
|
||||
type ResponseMsgSimulator struct {
|
||||
Simulator SimulatorResponse `json:"simulator"`
|
||||
}
|
||||
|
||||
type ResponseMsgFiles struct {
|
||||
Files []FileResponse `json:"files"`
|
||||
}
|
||||
|
||||
type ResponseMsgFile struct {
|
||||
File FileResponse `json:"file"`
|
||||
}
|
||||
|
|
|
@ -15,10 +15,14 @@ import (
|
|||
type ModelName string
|
||||
|
||||
const ModelUser = ModelName("user")
|
||||
const ModelSimulation = ModelName("simulation")
|
||||
const ModelScenario = ModelName("scenario")
|
||||
const ModelSimulator = ModelName("simulator")
|
||||
const ModelVisualization = ModelName("visualization")
|
||||
const ModelSimulatorAction = ModelName("simulatoraction")
|
||||
const ModelDashboard = ModelName("dashboard")
|
||||
const ModelWidget = ModelName("widget")
|
||||
const ModelSimulationModel = ModelName("simulationmodel")
|
||||
const ModelSignal = ModelName("signal")
|
||||
const ModelFile = ModelName("file")
|
||||
|
||||
type CRUD string
|
||||
|
||||
|
@ -47,18 +51,36 @@ var _r__ = Permission{Create: false, Read: true, Update: false, Delete: false}
|
|||
var Roles = RoleActions{
|
||||
"Admin": {
|
||||
ModelUser: crud,
|
||||
ModelSimulation: crud,
|
||||
ModelScenario: crud,
|
||||
ModelSimulationModel: crud,
|
||||
ModelSimulator: crud,
|
||||
ModelSimulatorAction: crud,
|
||||
ModelWidget: crud,
|
||||
ModelDashboard: crud,
|
||||
ModelSignal: crud,
|
||||
ModelFile: crud,
|
||||
},
|
||||
"User": {
|
||||
ModelUser: __u_,
|
||||
ModelSimulation: crud,
|
||||
ModelScenario: crud,
|
||||
ModelSimulationModel: crud,
|
||||
ModelSimulator: _r__,
|
||||
ModelSimulatorAction: _ru_,
|
||||
ModelWidget: crud,
|
||||
ModelDashboard: crud,
|
||||
ModelSignal: crud,
|
||||
ModelFile: crud,
|
||||
},
|
||||
"Guest": {
|
||||
ModelVisualization: _r__,
|
||||
ModelScenario: _r__,
|
||||
ModelSimulationModel: _r__,
|
||||
ModelDashboard: _r__,
|
||||
ModelWidget: _r__,
|
||||
ModelSimulator: _r__,
|
||||
ModelSimulatorAction: _r__,
|
||||
ModelUser: _ru_,
|
||||
ModelSignal: _r__,
|
||||
ModelFile: _r__,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ func (self *UserSerializer) Response(assoc bool) UserResponse {
|
|||
Username: self.Username,
|
||||
Role: self.Role,
|
||||
Mail: self.Mail,
|
||||
ID: self.ID,
|
||||
}
|
||||
|
||||
// Associated models MUST NOT called with assoc=true otherwise we
|
||||
|
@ -39,44 +40,44 @@ func (self *UserSerializer) Response(assoc bool) UserResponse {
|
|||
|
||||
// TODO: maybe all those should be made in one transaction
|
||||
|
||||
//simulations, _, _ := simulation.FindUserSimulations(&self.User)
|
||||
//simulationsSerializer :=
|
||||
// SimulationsSerializer{self.Ctx, simulations}
|
||||
//scenarios, _, _ := scenario.FindUserScenarios(&self.User)
|
||||
//scenariosSerializer :=
|
||||
// ScenariosSerializer{self.Ctx, scenarios}
|
||||
|
||||
// Add the associated models to the response
|
||||
//response.Simulations = simulationsSerializer.Response()
|
||||
//response.Scenarios = scenariosSerializer.Response()
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// Simulation/s Serializers
|
||||
// Scenario/s Serializers
|
||||
|
||||
type SimulationsSerializer struct {
|
||||
Ctx *gin.Context
|
||||
Simulations []Simulation
|
||||
type ScenariosSerializer struct {
|
||||
Ctx *gin.Context
|
||||
Scenarios []Scenario
|
||||
}
|
||||
|
||||
func (self *SimulationsSerializer) Response() []SimulationResponse {
|
||||
response := []SimulationResponse{}
|
||||
for _, simulation := range self.Simulations {
|
||||
serializer := SimulationSerializer{self.Ctx, simulation}
|
||||
func (self *ScenariosSerializer) Response() []ScenarioResponse {
|
||||
response := []ScenarioResponse{}
|
||||
for _, so := range self.Scenarios {
|
||||
serializer := ScenarioSerializer{self.Ctx, so}
|
||||
response = append(response, serializer.Response())
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
type SimulationSerializer struct {
|
||||
type ScenarioSerializer struct {
|
||||
Ctx *gin.Context
|
||||
Simulation
|
||||
Scenario
|
||||
}
|
||||
|
||||
func (self *SimulationSerializer) Response() SimulationResponse {
|
||||
response := SimulationResponse{
|
||||
Name: self.Name,
|
||||
ID: self.ID,
|
||||
Running: self.Running,
|
||||
StartParams: self.StartParameters,
|
||||
func (self *ScenarioSerializer) Response() ScenarioResponse {
|
||||
response := ScenarioResponse{
|
||||
Name: self.Name,
|
||||
ID: self.ID,
|
||||
Running: self.Running,
|
||||
StartParameters: self.StartParameters,
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
@ -104,13 +105,13 @@ type SimulationModelSerializer struct {
|
|||
|
||||
func (self *SimulationModelSerializer) Response() SimulationModelResponse {
|
||||
response := SimulationModelResponse{
|
||||
ID: self.ID,
|
||||
Name: self.Name,
|
||||
OutputLength: self.OutputLength,
|
||||
InputLength: self.InputLength,
|
||||
SimulationID: self.SimulationID,
|
||||
SimulatorID: self.SimulatorID,
|
||||
StartParams: self.StartParameters,
|
||||
ID: self.ID,
|
||||
Name: self.Name,
|
||||
OutputLength: self.OutputLength,
|
||||
InputLength: self.InputLength,
|
||||
ScenarioID: self.ScenarioID,
|
||||
SimulatorID: self.SimulatorID,
|
||||
StartParameters: self.StartParameters,
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
@ -139,43 +140,47 @@ type SimulatorSerializer struct {
|
|||
func (self *SimulatorSerializer) Response() SimulatorResponse {
|
||||
|
||||
response := SimulatorResponse{
|
||||
ID: self.ID,
|
||||
UUID: self.UUID,
|
||||
Host: self.Host,
|
||||
ModelType: self.Modeltype,
|
||||
Modeltype: self.Modeltype,
|
||||
Uptime: self.Uptime,
|
||||
State: self.State,
|
||||
StateUpdateAt: self.StateUpdateAt,
|
||||
Properties: self.Properties,
|
||||
RawProperties: self.RawProperties,
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
// Visualization/s Serializers
|
||||
// Dashboard/s Serializers
|
||||
|
||||
type VisualizationsSerializer struct {
|
||||
Ctx *gin.Context
|
||||
Visualizations []Visualization
|
||||
type DashboardsSerializer struct {
|
||||
Ctx *gin.Context
|
||||
Dashboards []Dashboard
|
||||
}
|
||||
|
||||
func (self *VisualizationsSerializer) Response() []VisualizationResponse {
|
||||
response := []VisualizationResponse{}
|
||||
for _, visualization := range self.Visualizations {
|
||||
serializer := VisualizationSerializer{self.Ctx, visualization}
|
||||
func (self *DashboardsSerializer) Response() []DashboardResponse {
|
||||
response := []DashboardResponse{}
|
||||
for _, dashboard := range self.Dashboards {
|
||||
serializer := DashboardSerializer{self.Ctx, dashboard}
|
||||
response = append(response, serializer.Response())
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
type VisualizationSerializer struct {
|
||||
type DashboardSerializer struct {
|
||||
Ctx *gin.Context
|
||||
Visualization
|
||||
Dashboard
|
||||
}
|
||||
|
||||
func (self *VisualizationSerializer) Response() VisualizationResponse {
|
||||
func (self *DashboardSerializer) Response() DashboardResponse {
|
||||
|
||||
response := VisualizationResponse{
|
||||
Name: self.Name,
|
||||
Grid: self.Grid,
|
||||
SimulationID: self.SimulationID,
|
||||
response := DashboardResponse{
|
||||
Name: self.Name,
|
||||
Grid: self.Grid,
|
||||
ScenarioID: self.ScenarioID,
|
||||
ID: self.ID,
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
@ -204,18 +209,19 @@ type WidgetSerializer struct {
|
|||
func (self *WidgetSerializer) Response() WidgetResponse {
|
||||
|
||||
response := WidgetResponse{
|
||||
Name: self.Name,
|
||||
Type: self.Type,
|
||||
Width: self.Width,
|
||||
Height: self.Height,
|
||||
MinWidth: self.MinWidth,
|
||||
MinHeight: self.MinHeight,
|
||||
X: self.X,
|
||||
Y: self.Y,
|
||||
Z: self.Z,
|
||||
VisualizationID: self.VisualizationID,
|
||||
IsLocked: self.IsLocked,
|
||||
//CustomProperties
|
||||
ID: self.ID,
|
||||
Name: self.Name,
|
||||
Type: self.Type,
|
||||
Width: self.Width,
|
||||
Height: self.Height,
|
||||
MinWidth: self.MinWidth,
|
||||
MinHeight: self.MinHeight,
|
||||
X: self.X,
|
||||
Y: self.Y,
|
||||
Z: self.Z,
|
||||
DashboardID: self.DashboardID,
|
||||
IsLocked: self.IsLocked,
|
||||
CustomProperties: self.CustomProperties,
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
@ -245,12 +251,14 @@ func (self *FileSerializerNoAssoc) Response() FileResponse {
|
|||
response := FileResponse{
|
||||
Name: self.Name,
|
||||
ID: self.ID,
|
||||
Path: self.Path,
|
||||
Type: self.Type,
|
||||
Size: self.Size,
|
||||
H: self.ImageHeight,
|
||||
W: self.ImageWidth,
|
||||
// Date
|
||||
//Path: self.Path,
|
||||
Type: self.Type,
|
||||
Size: self.Size,
|
||||
ImageHeight: self.ImageHeight,
|
||||
ImageWidth: self.ImageWidth,
|
||||
Date: self.Date,
|
||||
WidgetID: self.WidgetID,
|
||||
SimulationModelID: self.SimulationModelID,
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
|
526
common/testdata.go
Normal file
526
common/testdata.go
Normal file
|
@ -0,0 +1,526 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/jinzhu/gorm/dialects/postgres"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Generic
|
||||
|
||||
var MsgOK = ResponseMsg{
|
||||
Message: "OK.",
|
||||
}
|
||||
|
||||
// Users
|
||||
|
||||
// Hash passwords with bcrypt algorithm
|
||||
var bcryptCost = 10
|
||||
var pw0, _ = bcrypt.GenerateFromPassword([]byte("xyz789"), bcryptCost)
|
||||
var pwA, _ = bcrypt.GenerateFromPassword([]byte("abc123"), bcryptCost)
|
||||
var pwB, _ = bcrypt.GenerateFromPassword([]byte("bcd234"), bcryptCost)
|
||||
var User0 = User{Username: "User_0", Password: string(pw0), Role: "Admin", Mail: "User_0@example.com"}
|
||||
var User0_response = UserResponse{Username: User0.Username, Role: User0.Role, ID: 1, Mail: User0.Mail}
|
||||
var UserA = User{Username: "User_A", Password: string(pwA), Role: "User", Mail: "User_A@example.com"}
|
||||
var UserA_response = UserResponse{Username: UserA.Username, Role: UserA.Role, ID: 2, Mail: UserA.Mail}
|
||||
var UserB = User{Username: "User_B", Password: string(pwB), Role: "User", Mail: "User_B@example.com"}
|
||||
var UserB_response = UserResponse{Username: UserB.Username, Role: UserB.Role, ID: 3, Mail: UserB.Mail}
|
||||
|
||||
// Credentials
|
||||
|
||||
var CredAdmin = credentials{
|
||||
Username: User0.Username,
|
||||
Password: "xyz789",
|
||||
}
|
||||
|
||||
var CredUser = credentials{
|
||||
Username: UserA.Username,
|
||||
Password: "abc123",
|
||||
}
|
||||
|
||||
// Simulators
|
||||
|
||||
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 propertiesC = json.RawMessage(`{"name" : "TestNameC", "category" : "CategoryC", "location" : "my desk", "type": "blubb"}`)
|
||||
var propertiesCupdated = json.RawMessage(`{"name" : "TestNameCUpdate", "category" : "CategoryC", "location" : "my desk", "type": "blubb"}`)
|
||||
|
||||
var SimulatorA = Simulator{
|
||||
UUID: "4854af30-325f-44a5-ad59-b67b2597de68",
|
||||
Host: "Host_A",
|
||||
Modeltype: "ModelTypeA",
|
||||
Uptime: 0,
|
||||
State: "running",
|
||||
StateUpdateAt: time.Now().String(),
|
||||
Properties: postgres.Jsonb{propertiesA},
|
||||
RawProperties: postgres.Jsonb{propertiesA},
|
||||
}
|
||||
|
||||
var SimulatorA_response = SimulatorResponse{
|
||||
ID: 1,
|
||||
UUID: SimulatorA.UUID,
|
||||
Host: SimulatorA.Host,
|
||||
Modeltype: SimulatorA.Modeltype,
|
||||
Uptime: SimulatorA.Uptime,
|
||||
State: SimulatorA.State,
|
||||
StateUpdateAt: SimulatorA.StateUpdateAt,
|
||||
Properties: SimulatorA.Properties,
|
||||
RawProperties: SimulatorA.RawProperties,
|
||||
}
|
||||
|
||||
var SimulatorB = Simulator{
|
||||
UUID: "7be0322d-354e-431e-84bd-ae4c9633138b",
|
||||
Host: "Host_B",
|
||||
Modeltype: "ModelTypeB",
|
||||
Uptime: 0,
|
||||
State: "idle",
|
||||
StateUpdateAt: time.Now().String(),
|
||||
Properties: postgres.Jsonb{propertiesB},
|
||||
RawProperties: postgres.Jsonb{propertiesB},
|
||||
}
|
||||
|
||||
var SimulatorB_response = SimulatorResponse{
|
||||
ID: 2,
|
||||
UUID: SimulatorB.UUID,
|
||||
Host: SimulatorB.Host,
|
||||
Modeltype: SimulatorB.Modeltype,
|
||||
Uptime: SimulatorB.Uptime,
|
||||
State: SimulatorB.State,
|
||||
StateUpdateAt: SimulatorB.StateUpdateAt,
|
||||
Properties: SimulatorB.Properties,
|
||||
RawProperties: SimulatorB.RawProperties,
|
||||
}
|
||||
|
||||
var SimulatorC = Simulator{
|
||||
UUID: "6d9776bf-b693-45e8-97b6-4c13d151043f",
|
||||
Host: "Host_C",
|
||||
Modeltype: "ModelTypeC",
|
||||
Uptime: 0,
|
||||
State: "idle",
|
||||
StateUpdateAt: time.Now().String(),
|
||||
Properties: postgres.Jsonb{propertiesC},
|
||||
RawProperties: postgres.Jsonb{propertiesC},
|
||||
}
|
||||
|
||||
var SimulatorC_response = SimulatorResponse{
|
||||
ID: 3,
|
||||
UUID: SimulatorC.UUID,
|
||||
Host: SimulatorC.Host,
|
||||
Modeltype: SimulatorC.Modeltype,
|
||||
Uptime: SimulatorC.Uptime,
|
||||
State: SimulatorC.State,
|
||||
StateUpdateAt: SimulatorC.StateUpdateAt,
|
||||
Properties: SimulatorC.Properties,
|
||||
RawProperties: SimulatorC.RawProperties,
|
||||
}
|
||||
|
||||
var SimulatorCUpdated = Simulator{
|
||||
UUID: SimulatorC.UUID,
|
||||
Host: "Host_Cupdated",
|
||||
Modeltype: "ModelTypeCUpdated",
|
||||
Uptime: SimulatorC.Uptime,
|
||||
State: "running",
|
||||
StateUpdateAt: time.Now().String(),
|
||||
Properties: postgres.Jsonb{propertiesCupdated},
|
||||
RawProperties: postgres.Jsonb{propertiesCupdated},
|
||||
}
|
||||
|
||||
var SimulatorCUpdated_response = SimulatorResponse{
|
||||
ID: 3,
|
||||
UUID: SimulatorCUpdated.UUID,
|
||||
Host: SimulatorCUpdated.Host,
|
||||
Modeltype: SimulatorCUpdated.Modeltype,
|
||||
Uptime: SimulatorCUpdated.Uptime,
|
||||
State: SimulatorCUpdated.State,
|
||||
StateUpdateAt: SimulatorCUpdated.StateUpdateAt,
|
||||
Properties: SimulatorCUpdated.Properties,
|
||||
RawProperties: SimulatorCUpdated.RawProperties,
|
||||
}
|
||||
|
||||
// Scenarios
|
||||
|
||||
var startParametersA = json.RawMessage(`{"parameter1" : "testValue1A", "parameter2" : "testValue2A", "parameter3" : 42}`)
|
||||
var startParametersB = json.RawMessage(`{"parameter1" : "testValue1B", "parameter2" : "testValue2B", "parameter3" : 43}`)
|
||||
var startParametersC = json.RawMessage(`{"parameter1" : "testValue1C", "parameter2" : "testValue2C", "parameter3" : 44}`)
|
||||
|
||||
var ScenarioA = Scenario{Name: "Scenario_A", Running: true, StartParameters: postgres.Jsonb{startParametersA}}
|
||||
var ScenarioA_response = ScenarioResponse{ID: 1, Name: ScenarioA.Name, Running: ScenarioA.Running, StartParameters: ScenarioA.StartParameters}
|
||||
var ScenarioB = Scenario{Name: "Scenario_B", Running: false, StartParameters: postgres.Jsonb{startParametersB}}
|
||||
var ScenarioB_response = ScenarioResponse{ID: 2, Name: ScenarioB.Name, Running: ScenarioB.Running, StartParameters: ScenarioB.StartParameters}
|
||||
var ScenarioC = Scenario{Name: "Scenario_C", Running: false, StartParameters: postgres.Jsonb{startParametersC}}
|
||||
var ScenarioC_response = ScenarioResponse{ID: 3, Name: ScenarioC.Name, Running: ScenarioC.Running, StartParameters: ScenarioC.StartParameters}
|
||||
var ScenarioCUpdated = Scenario{Name: "Scenario_Cupdated", Running: true, StartParameters: postgres.Jsonb{startParametersC}}
|
||||
var ScenarioCUpdated_response = ScenarioResponse{ID: 3, Name: ScenarioCUpdated.Name, Running: ScenarioCUpdated.Running, StartParameters: ScenarioCUpdated.StartParameters}
|
||||
|
||||
// Simulation Models
|
||||
|
||||
var SimulationModelA = SimulationModel{
|
||||
Name: "SimulationModel_A",
|
||||
OutputLength: 1,
|
||||
InputLength: 1,
|
||||
ScenarioID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParameters: postgres.Jsonb{startParametersA},
|
||||
}
|
||||
|
||||
var SimulationModelA_response = SimulationModelResponse{
|
||||
ID: 1,
|
||||
Name: SimulationModelA.Name,
|
||||
InputLength: SimulationModelA.InputLength,
|
||||
OutputLength: SimulationModelA.OutputLength,
|
||||
ScenarioID: SimulationModelA.ScenarioID,
|
||||
SimulatorID: SimulationModelA.SimulatorID,
|
||||
StartParameters: SimulationModelA.StartParameters,
|
||||
}
|
||||
|
||||
var SimulationModelB = SimulationModel{
|
||||
Name: "SimulationModel_B",
|
||||
OutputLength: 1,
|
||||
InputLength: 1,
|
||||
ScenarioID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParameters: postgres.Jsonb{startParametersB},
|
||||
}
|
||||
|
||||
var SimulationModelB_response = SimulationModelResponse{
|
||||
ID: 2,
|
||||
Name: SimulationModelB.Name,
|
||||
InputLength: SimulationModelB.InputLength,
|
||||
OutputLength: SimulationModelB.OutputLength,
|
||||
ScenarioID: SimulationModelB.ScenarioID,
|
||||
SimulatorID: SimulationModelB.SimulatorID,
|
||||
StartParameters: SimulationModelB.StartParameters,
|
||||
}
|
||||
|
||||
var SimulationModelC = SimulationModel{
|
||||
Name: "SimulationModel_C",
|
||||
OutputLength: 1,
|
||||
InputLength: 1,
|
||||
ScenarioID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParameters: postgres.Jsonb{startParametersC},
|
||||
}
|
||||
|
||||
var SimulationModelC_response = SimulationModelResponse{
|
||||
ID: 3,
|
||||
Name: SimulationModelC.Name,
|
||||
InputLength: SimulationModelC.InputLength,
|
||||
OutputLength: SimulationModelC.OutputLength,
|
||||
ScenarioID: SimulationModelC.ScenarioID,
|
||||
SimulatorID: SimulationModelC.SimulatorID,
|
||||
StartParameters: SimulationModelC.StartParameters,
|
||||
}
|
||||
|
||||
var SimulationModelCUpdated = SimulationModel{
|
||||
Name: "SimulationModel_CUpdated",
|
||||
OutputLength: SimulationModelC.OutputLength,
|
||||
InputLength: SimulationModelC.InputLength,
|
||||
ScenarioID: SimulationModelC.ScenarioID,
|
||||
SimulatorID: 2,
|
||||
StartParameters: SimulationModelC.StartParameters,
|
||||
InputMapping: SimulationModelC.InputMapping,
|
||||
OutputMapping: SimulationModelC.OutputMapping,
|
||||
}
|
||||
|
||||
var SimulationModelCUpdated_response = SimulationModelResponse{
|
||||
ID: 3,
|
||||
Name: SimulationModelCUpdated.Name,
|
||||
InputLength: SimulationModelCUpdated.InputLength,
|
||||
OutputLength: SimulationModelCUpdated.OutputLength,
|
||||
ScenarioID: SimulationModelCUpdated.ScenarioID,
|
||||
SimulatorID: SimulationModelCUpdated.SimulatorID,
|
||||
StartParameters: SimulationModelCUpdated.StartParameters,
|
||||
}
|
||||
|
||||
// Signals
|
||||
|
||||
var OutSignalA = Signal{
|
||||
Name: "outSignal_A",
|
||||
Direction: "out",
|
||||
Index: 0,
|
||||
Unit: "V",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var OutSignalA_response = SignalResponse{
|
||||
Name: OutSignalA.Name,
|
||||
Direction: OutSignalA.Direction,
|
||||
Index: OutSignalA.Index,
|
||||
Unit: OutSignalA.Unit,
|
||||
SimulationModelID: OutSignalA.SimulationModelID,
|
||||
}
|
||||
|
||||
var OutSignalB = Signal{
|
||||
Name: "outSignal_B",
|
||||
Direction: "out",
|
||||
Index: 1,
|
||||
Unit: "V",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var OutSignalB_response = SignalResponse{
|
||||
Name: OutSignalB.Name,
|
||||
Direction: OutSignalB.Direction,
|
||||
Index: OutSignalB.Index,
|
||||
Unit: OutSignalB.Unit,
|
||||
SimulationModelID: OutSignalB.SimulationModelID,
|
||||
}
|
||||
|
||||
var InSignalA = Signal{
|
||||
Name: "inSignal_A",
|
||||
Direction: "in",
|
||||
Index: 0,
|
||||
Unit: "A",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var InSignalA_response = SignalResponse{
|
||||
Name: InSignalA.Name,
|
||||
Direction: InSignalA.Direction,
|
||||
Index: InSignalA.Index,
|
||||
Unit: InSignalA.Unit,
|
||||
SimulationModelID: InSignalA.SimulationModelID,
|
||||
}
|
||||
|
||||
var InSignalB = Signal{
|
||||
Name: "inSignal_B",
|
||||
Direction: "in",
|
||||
Index: 1,
|
||||
Unit: "A",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var InSignalB_response = SignalResponse{
|
||||
Name: InSignalB.Name,
|
||||
Direction: InSignalB.Direction,
|
||||
Index: InSignalB.Index,
|
||||
Unit: InSignalB.Unit,
|
||||
SimulationModelID: InSignalB.SimulationModelID,
|
||||
}
|
||||
|
||||
var InSignalC = Signal{
|
||||
Name: "inSignal_C",
|
||||
Direction: "in",
|
||||
Index: 2,
|
||||
Unit: "A",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var InSignalC_response = SignalResponse{
|
||||
Name: InSignalC.Name,
|
||||
Direction: InSignalC.Direction,
|
||||
Index: InSignalC.Index,
|
||||
Unit: InSignalC.Unit,
|
||||
SimulationModelID: InSignalC.SimulationModelID,
|
||||
}
|
||||
|
||||
var InSignalCUpdated = Signal{
|
||||
Name: "inSignalupdated_C",
|
||||
Direction: InSignalC.Direction,
|
||||
Index: InSignalC.Index,
|
||||
Unit: "Ohm",
|
||||
SimulationModelID: InSignalC.SimulationModelID,
|
||||
}
|
||||
|
||||
var InSignalCUpdated_response = SignalResponse{
|
||||
Name: InSignalCUpdated.Name,
|
||||
Direction: InSignalCUpdated.Direction,
|
||||
Index: InSignalCUpdated.Index,
|
||||
Unit: InSignalCUpdated.Unit,
|
||||
SimulationModelID: InSignalCUpdated.SimulationModelID,
|
||||
}
|
||||
|
||||
// Dashboards
|
||||
|
||||
var DashboardA = Dashboard{Name: "Dashboard_A", Grid: 15, ScenarioID: 1}
|
||||
var DashboardA_response = DashboardResponse{ID: 1, Name: DashboardA.Name, Grid: DashboardA.Grid, ScenarioID: DashboardA.ScenarioID}
|
||||
var DashboardB = Dashboard{Name: "Dashboard_B", Grid: 10, ScenarioID: 1}
|
||||
var DashboardB_response = DashboardResponse{ID: 2, Name: DashboardB.Name, Grid: DashboardB.Grid, ScenarioID: DashboardB.ScenarioID}
|
||||
var DashboardC = Dashboard{Name: "Dashboard_C", Grid: 25, ScenarioID: 1}
|
||||
var DashboardC_response = DashboardResponse{ID: 3, Name: DashboardC.Name, Grid: DashboardC.Grid, ScenarioID: DashboardC.ScenarioID}
|
||||
var DashboardCUpdated = Dashboard{Name: "Dashboard_Cupdated", Grid: 24, ScenarioID: DashboardC.ScenarioID}
|
||||
var DashboardCUpdated_response = DashboardResponse{ID: 3, Name: DashboardCUpdated.Name, Grid: DashboardCUpdated.Grid, ScenarioID: DashboardCUpdated.ScenarioID}
|
||||
|
||||
// Files
|
||||
|
||||
var FileA = File{
|
||||
Name: "File_A",
|
||||
Type: "text/plain",
|
||||
Size: 42,
|
||||
ImageHeight: 333,
|
||||
ImageWidth: 111,
|
||||
Date: time.Now().String(),
|
||||
WidgetID: 1,
|
||||
SimulationModelID: 0,
|
||||
}
|
||||
|
||||
var FileA_response = FileResponse{
|
||||
ID: 1,
|
||||
Name: FileA.Name,
|
||||
Type: FileA.Type,
|
||||
Size: FileA.Size,
|
||||
ImageWidth: FileA.ImageWidth,
|
||||
ImageHeight: FileA.ImageHeight,
|
||||
Date: FileA.Date,
|
||||
WidgetID: FileA.WidgetID,
|
||||
SimulationModelID: FileA.SimulationModelID,
|
||||
}
|
||||
|
||||
var FileB = File{
|
||||
Name: "File_B",
|
||||
Type: "text/plain",
|
||||
Size: 1234,
|
||||
ImageHeight: 55,
|
||||
ImageWidth: 22,
|
||||
Date: time.Now().String(),
|
||||
WidgetID: 1,
|
||||
SimulationModelID: 0,
|
||||
}
|
||||
|
||||
var FileB_response = FileResponse{
|
||||
ID: 2,
|
||||
Name: FileB.Name,
|
||||
Type: FileB.Type,
|
||||
Size: FileB.Size,
|
||||
ImageWidth: FileB.ImageWidth,
|
||||
ImageHeight: FileB.ImageHeight,
|
||||
Date: FileB.Date,
|
||||
WidgetID: FileB.WidgetID,
|
||||
SimulationModelID: FileB.SimulationModelID,
|
||||
}
|
||||
|
||||
var FileC = File{
|
||||
Name: "File_C",
|
||||
Type: "text/plain",
|
||||
Size: 32,
|
||||
ImageHeight: 10,
|
||||
ImageWidth: 10,
|
||||
Date: time.Now().String(),
|
||||
WidgetID: 0,
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
var FileD = File{
|
||||
Name: "File_D",
|
||||
Type: "text/plain",
|
||||
Size: 5000,
|
||||
ImageHeight: 400,
|
||||
ImageWidth: 800,
|
||||
Date: time.Now().String(),
|
||||
WidgetID: 0,
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
// Widgets
|
||||
var customPropertiesA = json.RawMessage(`{"property1" : "testValue1A", "property2" : "testValue2A", "property3" : 42}`)
|
||||
var customPropertiesB = json.RawMessage(`{"property1" : "testValue1B", "property2" : "testValue2B", "property3" : 43}`)
|
||||
var customPropertiesC = json.RawMessage(`{"property1" : "testValue1C", "property2" : "testValue2C", "property3" : 44}`)
|
||||
|
||||
var WidgetA = Widget{
|
||||
Name: "Widget_A",
|
||||
Type: "graph",
|
||||
Width: 100,
|
||||
Height: 50,
|
||||
MinHeight: 40,
|
||||
MinWidth: 80,
|
||||
X: 10,
|
||||
Y: 10,
|
||||
Z: 10,
|
||||
IsLocked: false,
|
||||
CustomProperties: postgres.Jsonb{customPropertiesA},
|
||||
DashboardID: 1,
|
||||
}
|
||||
|
||||
var WidgetA_response = WidgetResponse{
|
||||
ID: 1,
|
||||
Name: WidgetA.Name,
|
||||
Type: WidgetA.Type,
|
||||
Width: WidgetA.Width,
|
||||
Height: WidgetA.Height,
|
||||
MinWidth: WidgetA.MinWidth,
|
||||
MinHeight: WidgetA.MinHeight,
|
||||
X: WidgetA.X,
|
||||
Y: WidgetA.Y,
|
||||
Z: WidgetA.Z,
|
||||
IsLocked: WidgetA.IsLocked,
|
||||
CustomProperties: WidgetA.CustomProperties,
|
||||
DashboardID: WidgetA.DashboardID,
|
||||
}
|
||||
|
||||
var WidgetB = Widget{
|
||||
Name: "Widget_B",
|
||||
Type: "slider",
|
||||
Width: 200,
|
||||
Height: 20,
|
||||
MinHeight: 10,
|
||||
MinWidth: 50,
|
||||
X: 100,
|
||||
Y: -40,
|
||||
Z: 0,
|
||||
IsLocked: false,
|
||||
CustomProperties: postgres.Jsonb{customPropertiesB},
|
||||
DashboardID: 1,
|
||||
}
|
||||
|
||||
var WidgetB_response = WidgetResponse{
|
||||
ID: 2,
|
||||
Name: WidgetB.Name,
|
||||
Type: WidgetB.Type,
|
||||
Width: WidgetB.Width,
|
||||
Height: WidgetB.Height,
|
||||
MinWidth: WidgetB.MinWidth,
|
||||
MinHeight: WidgetB.MinHeight,
|
||||
X: WidgetB.X,
|
||||
Y: WidgetB.Y,
|
||||
Z: WidgetB.Z,
|
||||
IsLocked: WidgetB.IsLocked,
|
||||
CustomProperties: WidgetB.CustomProperties,
|
||||
DashboardID: WidgetB.DashboardID,
|
||||
}
|
||||
|
||||
var WidgetC = Widget{
|
||||
Name: "Widget_C",
|
||||
Type: "bargraph",
|
||||
Height: 30,
|
||||
Width: 100,
|
||||
MinHeight: 20,
|
||||
MinWidth: 50,
|
||||
X: 11,
|
||||
Y: 12,
|
||||
Z: 13,
|
||||
IsLocked: false,
|
||||
CustomProperties: postgres.Jsonb{customPropertiesC},
|
||||
DashboardID: 1,
|
||||
}
|
||||
|
||||
var WidgetC_response = WidgetResponse{
|
||||
ID: 3,
|
||||
Name: WidgetC.Name,
|
||||
Type: WidgetC.Type,
|
||||
Width: WidgetC.Width,
|
||||
Height: WidgetC.Height,
|
||||
MinWidth: WidgetC.MinWidth,
|
||||
MinHeight: WidgetC.MinHeight,
|
||||
X: WidgetC.X,
|
||||
Y: WidgetC.Y,
|
||||
Z: WidgetC.Z,
|
||||
IsLocked: WidgetC.IsLocked,
|
||||
CustomProperties: WidgetC.CustomProperties,
|
||||
DashboardID: WidgetC.DashboardID,
|
||||
}
|
||||
|
||||
var WidgetCUpdated_response = WidgetResponse{
|
||||
ID: 3,
|
||||
Name: "Widget_CUpdated",
|
||||
Type: WidgetC.Type,
|
||||
Height: 35,
|
||||
Width: 110,
|
||||
MinHeight: WidgetC.MinHeight,
|
||||
MinWidth: WidgetC.MinWidth,
|
||||
X: WidgetC.X,
|
||||
Y: WidgetC.Y,
|
||||
Z: WidgetC.Z,
|
||||
IsLocked: WidgetC.IsLocked,
|
||||
CustomProperties: WidgetC.CustomProperties,
|
||||
DashboardID: WidgetC.DashboardID,
|
||||
}
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/nsf/jsondiff"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -35,53 +36,7 @@ func ProvideErrorResponse(c *gin.Context, err error) bool {
|
|||
return false // No error
|
||||
}
|
||||
|
||||
func GetVisualizationID(c *gin.Context) (int, error) {
|
||||
|
||||
simID, err := strconv.Atoi(c.Param("visualizationID"))
|
||||
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of visualization ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return -1, err
|
||||
} else {
|
||||
return simID, err
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func GetWidgetID(c *gin.Context) (int, error) {
|
||||
|
||||
widgetID, err := strconv.Atoi(c.Param("widgetID"))
|
||||
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of widget ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return -1, err
|
||||
} else {
|
||||
return widgetID, err
|
||||
}
|
||||
}
|
||||
|
||||
func GetFileID(c *gin.Context) (int, error) {
|
||||
|
||||
fileID, err := strconv.Atoi(c.Param("fileID"))
|
||||
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of file ID")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return -1, err
|
||||
} else {
|
||||
return fileID, err
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpoint(t *testing.T, router *gin.Engine, token string, url string, method string, body []byte, expected_code int, expected_response string) {
|
||||
func TestEndpoint(t *testing.T, router *gin.Engine, token string, url string, method string, body []byte, expected_code int, expected_response []byte) {
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
if body != nil {
|
||||
|
@ -96,8 +51,12 @@ func TestEndpoint(t *testing.T, router *gin.Engine, token string, url string, me
|
|||
}
|
||||
|
||||
assert.Equal(t, expected_code, w.Code)
|
||||
fmt.Println(w.Body.String())
|
||||
assert.Equal(t, expected_response, w.Body.String())
|
||||
//fmt.Println("Actual:", w.Body.String())
|
||||
//fmt.Println("Expected: ", string(expected_response))
|
||||
opts := jsondiff.DefaultConsoleOptions()
|
||||
diff, _ := jsondiff.Compare(w.Body.Bytes(), expected_response, &opts)
|
||||
assert.Equal(t, "FullMatch", diff.String())
|
||||
|
||||
}
|
||||
|
||||
func AuthenticateForTest(t *testing.T, router *gin.Engine, url string, method string, body []byte, expected_code int) string {
|
||||
|
@ -118,6 +77,7 @@ func AuthenticateForTest(t *testing.T, router *gin.Engine, url string, method st
|
|||
|
||||
success := body_data["success"].(bool)
|
||||
if !success {
|
||||
fmt.Println("Authentication not successful: ", body_data["message"])
|
||||
panic(-1)
|
||||
}
|
||||
|
||||
|
|
1397
doc/api/api.yaml
1397
doc/api/api.yaml
File diff suppressed because it is too large
Load diff
19
go.mod
19
go.mod
|
@ -4,15 +4,22 @@ require (
|
|||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/gin-gonic/gin v1.4.0
|
||||
github.com/go-openapi/spec v0.19.2 // indirect
|
||||
github.com/go-openapi/swag v0.19.3 // indirect
|
||||
github.com/go-playground/locales v0.12.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.16.0 // indirect
|
||||
github.com/jinzhu/gorm v1.9.8
|
||||
github.com/jinzhu/gorm v1.9.10
|
||||
github.com/leodido/go-urn v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e // indirect
|
||||
github.com/nsf/jsondiff v0.0.0-20190712045011-8443391ee9b6
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/swaggo/gin-swagger v1.1.0
|
||||
github.com/swaggo/swag v1.5.0
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
|
||||
github.com/swaggo/gin-swagger v1.2.0
|
||||
github.com/swaggo/swag v1.6.0
|
||||
github.com/tidwall/gjson v1.3.0
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
|
||||
golang.org/x/tools v0.0.0-20190703212419-2214986f1668 // indirect
|
||||
gopkg.in/go-playground/validator.v9 v9.29.0
|
||||
)
|
||||
|
||||
replace github.com/ugorji/go v1.1.4 => github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43
|
||||
|
|
136
go.sum
136
go.sum
|
@ -3,6 +3,9 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
|||
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
|
||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
|
@ -19,8 +22,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02 h1:PS3xfVPa8N84AzoWZHFCbA0+ikz4f4skktfjQoNMsgk=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190423183735-731ef375ac02/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
|
@ -29,26 +32,37 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
|
|||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc=
|
||||
github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=
|
||||
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
|
||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
||||
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
|
||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.18.0 h1:KVRzjXpMzgdM4GEMDmDTnGcY5yBwGWreJwmmk4k35yU=
|
||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.18.0 h1:oP2OUNdG1l2r5kYhrfVMXO54gWmzcfAwP/GFuHpNTkE=
|
||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/spec v0.18.0 h1:aIjeyG5mo5/FrvDkpKKEGZPmF9MPHahS72mzfVqeQXQ=
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk=
|
||||
github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=
|
||||
github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.18.0 h1:1DU8Km1MRGv9Pj7BNLmkA+umwTStwDHttXvx3NhJA70=
|
||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.3 h1:FKTmP0GPWwRqRP2WIYltgctgYTN+gr8iZ7zSKdZtbz0=
|
||||
github.com/go-openapi/swag v0.19.3/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
|
||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
|
||||
|
@ -75,12 +89,13 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
|
|||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jinzhu/gorm v1.9.8 h1:n5uvxqLepIP2R1XF7pudpt9Rv8I3m7G9trGxJVjLZ5k=
|
||||
github.com/jinzhu/gorm v1.9.8/go.mod h1:bdqTT3q6dhSph2K3pWxrHP6nqxuAp2yQ3KFtc3U3F84=
|
||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
|
||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.0 h1:6WV8LvwPpDhKjo5U9O6b4+xdG/jTXNPwlDme/MTo8Ns=
|
||||
github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc=
|
||||
github.com/jinzhu/gorm v1.9.10 h1:HvrsqdhCW78xpJF67g1hMxS6eCToo9PZH4LDB8WKPac=
|
||||
github.com/jinzhu/gorm v1.9.10/go.mod h1:Kh6hTsSGffh4ui079FHrR5Gg+5D0hgihqDcsDN2BBJY=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
|
@ -91,18 +106,22 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
|
|||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/lib/pq v1.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
|
@ -111,6 +130,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
|||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nsf/jsondiff v0.0.0-20190712045011-8443391ee9b6 h1:qsqscDgSJy+HqgMTR+3NwjYJBbp1+honwDsszLoS+pA=
|
||||
github.com/nsf/jsondiff v0.0.0-20190712045011-8443391ee9b6/go.mod h1:uFMI8w+ref4v2r9jz+c9i1IfIttS/OkmLfrk1jne5hs=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
|
@ -129,33 +150,41 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
|||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94 h1:0ngsPmuP6XIjiFRNFYlvKwSr5zff2v+uPHaffZ6/M4k=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/swaggo/gin-swagger v1.1.0 h1:ZI6/82S07DkkrMfGKbJhKj1R+QNTICkeAJP06pU36pU=
|
||||
github.com/swaggo/gin-swagger v1.1.0/go.mod h1:FQlm07YuT1glfN3hQiO11UQ2m39vOCZ/aa3WWr5E+XU=
|
||||
github.com/swaggo/swag v1.4.0/go.mod h1:hog2WgeMOrQ/LvQ+o1YGTeT+vWVrbi0SiIslBtxKTyM=
|
||||
github.com/swaggo/swag v1.5.0 h1:haK8VG3hj+v/c8hQ4f3U+oYpkdI/26m9LAUTXHOv+2U=
|
||||
github.com/swaggo/swag v1.5.0/go.mod h1:+xZrnu5Ut3GcUkKAJm9spnOooIS1WB1cUOkLNPrvrE0=
|
||||
github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||
github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
|
||||
github.com/swaggo/gin-swagger v1.2.0 h1:YskZXEiv51fjOMTsXrOetAjrMDfFaXD79PEoQBOe2W0=
|
||||
github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=
|
||||
github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
|
||||
github.com/swaggo/swag v1.6.0 h1:d6Z3kNGQmM2Z6DZtOyK138eFTkpi2JwSwNVOV4wnlLk=
|
||||
github.com/swaggo/swag v1.6.0/go.mod h1:YyZstMc22WYm6GEDx/CYWxq+faBbjQ5EqwQcrjREDBo=
|
||||
github.com/tidwall/gjson v1.3.0 h1:kfpsw1W3trbg4Xm6doUtqSl9+LhLB6qJ9PkltVAQZYs=
|
||||
github.com/tidwall/gjson v1.3.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
|
||||
github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
|
||||
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
|
||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 h1:BasDe+IErOQKrMVXab7UayvSlIpiyGwRvuX3EKYY7UA=
|
||||
github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA=
|
||||
github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 h1:vG/gY/PxA3v3l04qxe3tDjXyu3bozii8ulSlIPOYKhI=
|
||||
github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA=
|
||||
github.com/ugorji/go v1.1.5-pre h1:jyJKFOSEbdOc2HODrf2qcCkYOdq7zzXqA9bhW5oV4fM=
|
||||
github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=
|
||||
github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v1.1.5-pre h1:5YV9PsFAN+ndcCtTM7s60no7nY7eTG3LPtxhSwuxzCs=
|
||||
github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=
|
||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
|
@ -165,39 +194,56 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
|
|||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190322120337-addf6b3196f6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a h1:+KkCgOMgnKSgenxTBoiwkMqTiouMIy/3o8RLdmSbGoY=
|
||||
golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc h1:4gbWbmmPFp4ySWICouJl6emP0MyS31yy9SrTlAGFT+g=
|
||||
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae h1:xiXzMMEQdQcric9hXtr1QU98MHunKK7OTtsoU6bYWs4=
|
||||
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190110015856-aa033095749b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89 h1:iWXXYN3edZ3Nd/7I6Rt1sXrWVmhF9bgVtlEJ7BbH124=
|
||||
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b h1:/mJ+GKieZA6hFDQGdWZrjj4AXPl5ylY+5HusG80roy0=
|
||||
golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190703212419-2214986f1668 h1:3LJOYcj2ObWSZJXX21oGIIPv5SaOoi5JkzQTWnCXRhg=
|
||||
golang.org/x/tools v0.0.0-20190703212419-2214986f1668/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
|
|
189
routes/dashboard/dashboard_endpoints.go
Normal file
189
routes/dashboard/dashboard_endpoints.go
Normal file
|
@ -0,0 +1,189 @@
|
|||
package dashboard
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/scenario"
|
||||
)
|
||||
|
||||
func RegisterDashboardEndpoints(r *gin.RouterGroup) {
|
||||
|
||||
r.GET("", getDashboards)
|
||||
r.POST("", addDashboard)
|
||||
r.PUT("/:dashboardID", updateDashboard)
|
||||
r.GET("/:dashboardID", getDashboard)
|
||||
r.DELETE("/:dashboardID", deleteDashboard)
|
||||
}
|
||||
|
||||
// getDashboards godoc
|
||||
// @Summary Get all dashboards of scenario
|
||||
// @ID getDashboards
|
||||
// @Produce json
|
||||
// @Tags dashboards
|
||||
// @Success 200 {array} common.DashboardResponse "Array of dashboards to which belong to scenario"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param scenarioID query int true "Scenario ID"
|
||||
// @Router /dashboards [get]
|
||||
func getDashboards(c *gin.Context) {
|
||||
|
||||
ok, sim := scenario.CheckPermissions(c, common.Read, "query", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
var dab []common.Dashboard
|
||||
err := db.Order("ID asc").Model(sim).Related(&dab, "Dashboards").Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.DashboardsSerializer{c, dab}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"dashboards": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// addDashboard godoc
|
||||
// @Summary Add a dashboard to a scenario
|
||||
// @ID addDashboard
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags dashboards
|
||||
// @Param inputDab body common.ResponseMsgDashboard true "Dashboard to be added incl. ID of Scenario"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /dashboards [post]
|
||||
func addDashboard(c *gin.Context) {
|
||||
|
||||
var newDabData common.ResponseMsgDashboard
|
||||
err := c.BindJSON(&newDabData)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var newDab Dashboard
|
||||
newDab.ID = newDabData.Dashboard.ID
|
||||
newDab.Grid = newDabData.Dashboard.Grid
|
||||
newDab.ScenarioID = newDabData.Dashboard.ScenarioID
|
||||
newDab.Name = newDabData.Dashboard.Name
|
||||
|
||||
ok, _ := scenario.CheckPermissions(c, common.Create, "body", int(newDab.ScenarioID))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// add dashboard to DB and add association to scenario
|
||||
err = newDab.addToScenario()
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// updateDashboard godoc
|
||||
// @Summary Update a dashboard
|
||||
// @ID updateDashboard
|
||||
// @Tags dashboards
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param inputDab body common.ResponseMsgDashboard true "Dashboard to be updated"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param dashboardID path int true "Dashboard ID"
|
||||
// @Router /dashboards/{dashboardID} [put]
|
||||
func updateDashboard(c *gin.Context) {
|
||||
|
||||
ok, d := CheckPermissions(c, common.Update, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var modifiedDab common.ResponseMsgDashboard
|
||||
err := c.BindJSON(&modifiedDab)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = d.update(modifiedDab.Dashboard)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getDashboard godoc
|
||||
// @Summary Get a dashboard
|
||||
// @ID getDashboard
|
||||
// @Tags dashboards
|
||||
// @Produce json
|
||||
// @Success 200 {object} common.DashboardResponse "Requested dashboard."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param dashboardID path int true "Dashboard ID"
|
||||
// @Router /dashboards/{dashboardID} [get]
|
||||
func getDashboard(c *gin.Context) {
|
||||
|
||||
ok, dab := CheckPermissions(c, common.Read, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.DashboardSerializer{c, dab.Dashboard}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"dashboard": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// deleteDashboard godoc
|
||||
// @Summary Delete a dashboard
|
||||
// @ID deleteDashboard
|
||||
// @Tags dashboards
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param dashboardID path int true "Dashboard ID"
|
||||
// @Router /dashboards/{dashboardID} [delete]
|
||||
func deleteDashboard(c *gin.Context) {
|
||||
ok, dab := CheckPermissions(c, common.Delete, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
err := dab.delete()
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
75
routes/dashboard/dashboard_methods.go
Normal file
75
routes/dashboard/dashboard_methods.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package dashboard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/scenario"
|
||||
)
|
||||
|
||||
type Dashboard struct {
|
||||
common.Dashboard
|
||||
}
|
||||
|
||||
func (v *Dashboard) save() error {
|
||||
db := common.GetDB()
|
||||
err := db.Create(v).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Dashboard) ByID(id uint) error {
|
||||
db := common.GetDB()
|
||||
err := db.Find(d, id).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("Dashboard with id=%v does not exist", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dashboard) addToScenario() error {
|
||||
db := common.GetDB()
|
||||
var sim scenario.Scenario
|
||||
err := sim.ByID(d.ScenarioID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save dashboard to DB
|
||||
err = d.save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// associate dashboard with scenario
|
||||
err = db.Model(&sim).Association("Dashboards").Append(d).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Dashboard) update(modifiedDab common.DashboardResponse) error {
|
||||
|
||||
db := common.GetDB()
|
||||
|
||||
err := db.Model(d).Updates(map[string]interface{}{
|
||||
"Name": modifiedDab.Name,
|
||||
"Grid": modifiedDab.Grid,
|
||||
}).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Dashboard) delete() error {
|
||||
|
||||
db := common.GetDB()
|
||||
var sim scenario.Scenario
|
||||
err := sim.ByID(d.ScenarioID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove association between Dashboard and Scenario
|
||||
// Dashboard itself is not deleted from DB, it remains as "dangling"
|
||||
err = db.Model(&sim).Association("Dashboards").Delete(d).Error
|
||||
|
||||
return err
|
||||
}
|
58
routes/dashboard/dashboard_middleware.go
Normal file
58
routes/dashboard/dashboard_middleware.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package dashboard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/scenario"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
)
|
||||
|
||||
func CheckPermissions(c *gin.Context, operation common.CRUD, dabIDSource string, dabIDBody int) (bool, Dashboard) {
|
||||
|
||||
var dab Dashboard
|
||||
|
||||
err := common.ValidateRole(c, common.ModelDashboard, operation)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return false, dab
|
||||
}
|
||||
|
||||
var dabID int
|
||||
if dabIDSource == "path" {
|
||||
dabID, err = strconv.Atoi(c.Param("dashboardID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of dashboardID path parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return false, dab
|
||||
}
|
||||
} else if dabIDSource == "query" {
|
||||
dabID, err = strconv.Atoi(c.Request.URL.Query().Get("dashboardID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of dashboardID query parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return false, dab
|
||||
}
|
||||
} else if dabIDSource == "body" {
|
||||
dabID = dabIDBody
|
||||
}
|
||||
|
||||
err = dab.ByID(uint(dabID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return false, dab
|
||||
}
|
||||
|
||||
ok, _ := scenario.CheckPermissions(c, operation, "body", int(dab.ScenarioID))
|
||||
if !ok {
|
||||
return false, dab
|
||||
}
|
||||
|
||||
return true, dab
|
||||
}
|
65
routes/dashboard/dashboard_test.go
Normal file
65
routes/dashboard/dashboard_test.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package dashboard
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
|
||||
)
|
||||
|
||||
// Test /dashboards endpoints
|
||||
func TestEndpoints(t *testing.T) {
|
||||
|
||||
var token string
|
||||
|
||||
var myDashboards = []common.DashboardResponse{common.DashboardA_response, common.DashboardB_response}
|
||||
var msgDashboards = common.ResponseMsgDashboards{Dashboards: myDashboards}
|
||||
var msgDab = common.ResponseMsgDashboard{Dashboard: common.DashboardC_response}
|
||||
var msgDabupdated = common.ResponseMsgDashboard{Dashboard: common.DashboardCUpdated_response}
|
||||
|
||||
db := common.DummyInitDB()
|
||||
defer db.Close()
|
||||
common.DummyPopulateDB(db)
|
||||
|
||||
router := gin.Default()
|
||||
api := router.Group("/api")
|
||||
|
||||
// All endpoints require authentication except when someone wants to
|
||||
// login (POST /authenticate)
|
||||
user.VisitorAuthenticate(api.Group("/authenticate"))
|
||||
|
||||
api.Use(user.Authentication(true))
|
||||
|
||||
RegisterDashboardEndpoints(api.Group("/dashboards"))
|
||||
|
||||
credjson, _ := json.Marshal(common.CredUser)
|
||||
msgOKjson, _ := json.Marshal(common.MsgOK)
|
||||
msgDashboardsjson, _ := json.Marshal(msgDashboards)
|
||||
msgDabjson, _ := json.Marshal(msgDab)
|
||||
msgDabupdatedjson, _ := json.Marshal(msgDabupdated)
|
||||
|
||||
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
|
||||
|
||||
// test GET dashboards
|
||||
common.TestEndpoint(t, router, token, "/api/dashboards?scenarioID=1", "GET", nil, 200, msgDashboardsjson)
|
||||
|
||||
// test POST dashboards
|
||||
common.TestEndpoint(t, router, token, "/api/dashboards", "POST", msgDabjson, 200, msgOKjson)
|
||||
|
||||
// test GET dashboards/:dashboardID to check if previous POST worked correctly
|
||||
common.TestEndpoint(t, router, token, "/api/dashboards/3", "GET", nil, 200, msgDabjson)
|
||||
|
||||
// test PUT dashboards/:dashboardID
|
||||
common.TestEndpoint(t, router, token, "/api/dashboards/3", "PUT", msgDabupdatedjson, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/dashboards/3", "GET", nil, 200, msgDabupdatedjson)
|
||||
|
||||
// test DELETE dashboards/:dashboardID
|
||||
common.TestEndpoint(t, router, token, "/api/dashboards/3", "DELETE", nil, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/dashboards?scenarioID=1", "GET", nil, 200, msgDashboardsjson)
|
||||
|
||||
// TODO add testing for other return codes
|
||||
|
||||
}
|
|
@ -8,6 +8,8 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget"
|
||||
)
|
||||
|
||||
func RegisterFileEndpoints(r *gin.RouterGroup) {
|
||||
|
@ -28,8 +30,8 @@ func RegisterFileEndpoints(r *gin.RouterGroup) {
|
|||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param originType query string true "Set to model for files of model, set to widget for files of widget"
|
||||
// @Param originID query int true "ID of either model or widget of which files are requested"
|
||||
// @Param objectType query string true "Set to model for files of model, set to widget for files of widget"
|
||||
// @Param objectID query int true "ID of either model or widget of which files are requested"
|
||||
// @Router /files [get]
|
||||
func getFiles(c *gin.Context) {
|
||||
|
||||
|
@ -51,15 +53,33 @@ func getFiles(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
//Check access
|
||||
var ok bool
|
||||
var m simulationmodel.SimulationModel
|
||||
var w widget.Widget
|
||||
if objectType == "model" {
|
||||
ok, m = simulationmodel.CheckPermissions(c, common.Read, "body", objectID)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ok, w = widget.CheckPermissions(c, common.Read, objectID)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// get meta data of files
|
||||
db := common.GetDB()
|
||||
|
||||
var files []common.File
|
||||
if objectType == "model" {
|
||||
err = db.Where("ModelID", objectID).Find(&files).Error
|
||||
err = db.Order("ID asc").Model(&m).Related(&files, "Files").Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = db.Where("WidgetID", objectID).Find(&files).Error
|
||||
err = db.Order("ID asc").Model(&w).Related(&files, "Files").Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
@ -93,15 +113,6 @@ func getFiles(c *gin.Context) {
|
|||
// @Param objectID query int true "ID of either model or widget of which files are requested"
|
||||
// @Router /files [post]
|
||||
func addFile(c *gin.Context) {
|
||||
// Extract file from PUT request form
|
||||
file_header, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error())
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
objectType := c.Request.URL.Query().Get("objectType")
|
||||
if objectType != "model" && objectType != "widget" {
|
||||
|
@ -121,6 +132,30 @@ func addFile(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
// Check access
|
||||
var ok bool
|
||||
if objectType == "model" {
|
||||
ok, _ = simulationmodel.CheckPermissions(c, common.Create, "body", objectID)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ok, _ = widget.CheckPermissions(c, common.Create, objectID)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Extract file from POST request form
|
||||
file_header, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error())
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var newFile File
|
||||
err = newFile.register(file_header, objectType, uint(objectID))
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
|
@ -149,22 +184,16 @@ func addFile(c *gin.Context) {
|
|||
// @Router /files/{fileID} [get]
|
||||
func getFile(c *gin.Context) {
|
||||
|
||||
fileID, err := common.GetFileID(c)
|
||||
if err != nil {
|
||||
// check access
|
||||
ok, f := checkPermissions(c, common.Read)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var f File
|
||||
err = f.byID(uint(fileID))
|
||||
err := f.download(c)
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
f.download(c)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
|
||||
// updateFile godoc
|
||||
|
@ -188,6 +217,12 @@ func getFile(c *gin.Context) {
|
|||
// @Router /files/{fileID} [put]
|
||||
func updateFile(c *gin.Context) {
|
||||
|
||||
// check access
|
||||
ok, f := checkPermissions(c, common.Update)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Extract file from PUT request form
|
||||
err := c.Request.ParseForm()
|
||||
if err != nil {
|
||||
|
@ -207,17 +242,6 @@ func updateFile(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
fileID, err := common.GetFileID(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var f File
|
||||
err = f.byID(uint(fileID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = f.update(file_header)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
|
@ -239,7 +263,17 @@ func updateFile(c *gin.Context) {
|
|||
// @Param fileID path int true "ID of the file to update"
|
||||
// @Router /files/{fileID} [delete]
|
||||
func deleteFile(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Not implemented.",
|
||||
})
|
||||
|
||||
// check access
|
||||
ok, f := checkPermissions(c, common.Delete)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
err := f.delete()
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
|
@ -2,13 +2,12 @@ package file
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"github.com/gin-gonic/gin"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"time"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel"
|
||||
|
@ -38,29 +37,40 @@ func (f *File) byID(id uint) error {
|
|||
}
|
||||
|
||||
func (f *File) save() error {
|
||||
|
||||
// get last modify time of target file
|
||||
fileinfo, err := os.Stat(f.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error stat on file: %s", err.Error())
|
||||
}
|
||||
f.Date = fileinfo.ModTime()
|
||||
f.ImageWidth = 0
|
||||
f.ImageHeight = 0
|
||||
|
||||
db := common.GetDB()
|
||||
err = db.Create(f).Error
|
||||
err := db.Create(f).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *File) download(c *gin.Context) error {
|
||||
|
||||
err := ioutil.WriteFile(f.Name, f.FileData, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("file could not be temporarily created on server disk: %s", err.Error())
|
||||
}
|
||||
defer os.Remove(f.Name)
|
||||
//Seems this headers needed for some browsers (for example without this headers Chrome will download files as txt)
|
||||
c.Header("Content-Description", "File Transfer")
|
||||
c.Header("Content-Transfer-Encoding", "binary")
|
||||
c.Header("Content-Disposition", "attachment; filename="+f.Name)
|
||||
//c.Header("Content-Type", contentType)
|
||||
c.File(f.Name)
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (f *File) register(fileHeader *multipart.FileHeader, objectType string, objectID uint) error {
|
||||
|
||||
// Obtain properties of file
|
||||
f.Type = fileHeader.Header.Get("Content-Type")
|
||||
f.Name = filepath.Base(fileHeader.Filename)
|
||||
f.Path = filepath.Join(getFolderName(objectType, objectID), f.Name)
|
||||
//f.Path = filepath.Join(getFolderName(objectType, objectID), f.Name)
|
||||
f.Size = uint(fileHeader.Size)
|
||||
f.Date = time.Now().String()
|
||||
f.ImageWidth = 0 // TODO: do we need this?
|
||||
f.ImageHeight = 0 // TODO: do we need this?
|
||||
|
||||
var m simulationmodel.SimulationModel
|
||||
var w widget.Widget
|
||||
|
@ -68,12 +78,16 @@ func (f *File) register(fileHeader *multipart.FileHeader, objectType string, obj
|
|||
if objectType == "model" {
|
||||
// check if model exists
|
||||
err = m.ByID(objectID)
|
||||
f.WidgetID = 0
|
||||
f.SimulationModelID = objectID
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
// check if widget exists
|
||||
f.WidgetID = objectID
|
||||
f.SimulationModelID = 0
|
||||
err = w.ByID(uint(objectID))
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -81,12 +95,15 @@ func (f *File) register(fileHeader *multipart.FileHeader, objectType string, obj
|
|||
|
||||
}
|
||||
|
||||
// Save file to local disc (NOT DB!)
|
||||
err = f.modifyFileOnDisc(fileHeader, true)
|
||||
// set file data
|
||||
fileContent, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("File could not be saved/ modified on disk: ", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
f.FileData, err = ioutil.ReadAll(fileContent)
|
||||
defer fileContent.Close()
|
||||
|
||||
// Add File object with parameters to DB
|
||||
err = f.save()
|
||||
if err != nil {
|
||||
|
@ -95,12 +112,14 @@ func (f *File) register(fileHeader *multipart.FileHeader, objectType string, obj
|
|||
|
||||
// Create association to model or widget
|
||||
if objectType == "model" {
|
||||
err = f.addToModel(m)
|
||||
db := common.GetDB()
|
||||
err := db.Model(&m).Association("Files").Append(f).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = f.addToWidget(w)
|
||||
db := common.GetDB()
|
||||
err := db.Model(&w).Association("Files").Append(f).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -110,99 +129,54 @@ func (f *File) register(fileHeader *multipart.FileHeader, objectType string, obj
|
|||
}
|
||||
|
||||
func (f *File) update(fileHeader *multipart.FileHeader) error {
|
||||
err := f.modifyFileOnDisc(fileHeader, false)
|
||||
|
||||
// set file data
|
||||
fileContent, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileData, err := ioutil.ReadAll(fileContent)
|
||||
fmt.Println("File content: ", string(fileData))
|
||||
defer fileContent.Close()
|
||||
|
||||
db := common.GetDB()
|
||||
err = db.Model(f).Update("Size", fileHeader.Size).Error
|
||||
err = db.Model(f).Updates(map[string]interface{}{"Size": fileHeader.Size,
|
||||
"FileData": fileData,
|
||||
"Date": time.Now().String()}).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *File) modifyFileOnDisc(fileHeader *multipart.FileHeader, createFile bool) error {
|
||||
func (f *File) delete() error {
|
||||
|
||||
//filesavepath := filepath.Join(foldername, filename)
|
||||
var err error
|
||||
db := common.GetDB()
|
||||
|
||||
if createFile {
|
||||
// Ensure folder with name foldername exists
|
||||
err = os.MkdirAll(f.Path, os.ModePerm)
|
||||
} else {
|
||||
// test if file exists
|
||||
_, err = os.Stat(f.Path)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var open_options int
|
||||
if createFile {
|
||||
// create file it not exists, file MUST not exist
|
||||
open_options = os.O_RDWR | os.O_CREATE | os.O_EXCL
|
||||
} else {
|
||||
open_options = os.O_RDWR
|
||||
}
|
||||
|
||||
fileTarget, err := os.OpenFile(f.Path, open_options, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fileTarget.Close()
|
||||
|
||||
// Save file to target path
|
||||
uploadedFile, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer uploadedFile.Close()
|
||||
|
||||
var uploadContent = make([]byte, f.Size)
|
||||
for {
|
||||
|
||||
n, err := uploadedFile.Read(uploadContent)
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
_, err = fileTarget.Write(uploadContent[:n])
|
||||
if f.WidgetID > 0 {
|
||||
// remove association between file and widget
|
||||
var w widget.Widget
|
||||
err := w.ByID(f.WidgetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = db.Model(&w).Association("Files").Delete(f).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// remove association between file and simulation model
|
||||
var m simulationmodel.SimulationModel
|
||||
err := m.ByID(f.SimulationModelID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = db.Model(&m).Association("Files").Delete(f).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// delete file from DB
|
||||
err := db.Delete(f).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *File) download(c *gin.Context) {
|
||||
|
||||
//Seems this headers needed for some browsers (for example without this headers Chrome will download files as txt)
|
||||
c.Header("Content-Description", "File Transfer")
|
||||
c.Header("Content-Transfer-Encoding", "binary")
|
||||
c.Header("Content-Disposition", "attachment; filename="+f.Name)
|
||||
//c.Header("Content-Type", contentType)
|
||||
c.File(f.Path)
|
||||
}
|
||||
|
||||
func (f *File) addToModel(model simulationmodel.SimulationModel) error {
|
||||
db := common.GetDB()
|
||||
err := db.Model(&model).Association("Files").Append(f).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *File) addToWidget(widget widget.Widget) error {
|
||||
db := common.GetDB()
|
||||
err := db.Model(&widget).Association("Files").Append(f).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func getFolderName(objectType string, objectID uint) string {
|
||||
base_foldername := "files/"
|
||||
|
||||
foldername := base_foldername + objectType + "_" + strconv.Itoa(int(objectID)) + "/"
|
||||
return foldername
|
||||
}
|
50
routes/file/file_middleware.go
Normal file
50
routes/file/file_middleware.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func checkPermissions(c *gin.Context, operation common.CRUD) (bool, File) {
|
||||
|
||||
var f File
|
||||
|
||||
err := common.ValidateRole(c, common.ModelFile, operation)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return false, f
|
||||
}
|
||||
|
||||
fileID, err := strconv.Atoi(c.Param("fileID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of fileID path parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return false, f
|
||||
}
|
||||
|
||||
err = f.byID(uint(fileID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return false, f
|
||||
}
|
||||
|
||||
if f.SimulationModelID > 0 {
|
||||
ok, _ := simulationmodel.CheckPermissions(c, operation, "body", int(f.SimulationModelID))
|
||||
if !ok {
|
||||
return false, f
|
||||
}
|
||||
} else {
|
||||
ok, _ := widget.CheckPermissions(c, operation, int(f.WidgetID))
|
||||
if !ok {
|
||||
return false, f
|
||||
}
|
||||
}
|
||||
|
||||
return true, f
|
||||
}
|
201
routes/file/file_test.go
Normal file
201
routes/file/file_test.go
Normal file
|
@ -0,0 +1,201 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test /files endpoints
|
||||
func TestSignalEndpoints(t *testing.T) {
|
||||
|
||||
var token string
|
||||
var filecontent = "This is my testfile"
|
||||
var filecontent_update = "This is my updated testfile with a dot at the end."
|
||||
var filename = "testfile.txt"
|
||||
var filename_update = "testfileupdate.txt"
|
||||
|
||||
var myFiles = []common.FileResponse{common.FileA_response, common.FileB_response}
|
||||
var msgFiles = common.ResponseMsgFiles{Files: myFiles}
|
||||
|
||||
db := common.DummyInitDB()
|
||||
defer db.Close()
|
||||
common.DummyPopulateDB(db)
|
||||
|
||||
// create a testfile in local folder
|
||||
c1 := []byte(filecontent)
|
||||
c2 := []byte(filecontent_update)
|
||||
err := ioutil.WriteFile(filename, c1, 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = ioutil.WriteFile(filename_update, c2, 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
router := gin.Default()
|
||||
api := router.Group("/api")
|
||||
|
||||
// All endpoints require authentication except when someone wants to
|
||||
// login (POST /authenticate)
|
||||
user.VisitorAuthenticate(api.Group("/authenticate"))
|
||||
|
||||
api.Use(user.Authentication(true))
|
||||
|
||||
RegisterFileEndpoints(api.Group("/files"))
|
||||
|
||||
credjson, err := json.Marshal(common.CredUser)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgOKjson, err := json.Marshal(common.MsgOK)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgFilesjson, err := json.Marshal(msgFiles)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
|
||||
|
||||
// test GET files
|
||||
common.TestEndpoint(t, router, token, "/api/files?objectID=1&objectType=widget", "GET", nil, 200, msgFilesjson)
|
||||
|
||||
// test POST files
|
||||
bodyBuf := &bytes.Buffer{}
|
||||
bodyWriter := multipart.NewWriter(bodyBuf)
|
||||
fileWriter, err := bodyWriter.CreateFormFile("file", "testuploadfile.txt")
|
||||
if err != nil {
|
||||
fmt.Println("error writing to buffer")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// open file handle
|
||||
fh, err := os.Open(filename)
|
||||
if err != nil {
|
||||
fmt.Println("error opening file")
|
||||
panic(err)
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
// io copy
|
||||
_, err = io.Copy(fileWriter, fh)
|
||||
if err != nil {
|
||||
fmt.Println("error on IO copy")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
contentType := bodyWriter.FormDataContentType()
|
||||
bodyWriter.Close()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, err := http.NewRequest("POST", "/api/files?objectID=1&objectType=widget", bodyBuf)
|
||||
req.Header.Add("Authorization", "Bearer "+token)
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
if err != nil {
|
||||
fmt.Println("error creating post request")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, 200, w.Code)
|
||||
fmt.Println(w.Body.String())
|
||||
assert.Equal(t, string(msgOKjson), w.Body.String())
|
||||
|
||||
// test GET files/:fileID
|
||||
w2 := httptest.NewRecorder()
|
||||
req2, _ := http.NewRequest("GET", "/api/files/5", nil)
|
||||
req2.Header.Add("Authorization", "Bearer "+token)
|
||||
router.ServeHTTP(w2, req2)
|
||||
|
||||
assert.Equal(t, 200, w2.Code)
|
||||
fmt.Println(w2.Body.String())
|
||||
assert.Equal(t, filecontent, w2.Body.String())
|
||||
|
||||
//common.TestEndpoint(t, router, token, "/api/files?objectID=1&objectType=widget", "GET", nil, 200, string(msgFilesjson))
|
||||
|
||||
// test PUT files/:fileID
|
||||
bodyBuf_update := &bytes.Buffer{}
|
||||
bodyWriter_update := multipart.NewWriter(bodyBuf_update)
|
||||
fileWriter_update, err := bodyWriter_update.CreateFormFile("file", "testuploadfile.txt")
|
||||
if err != nil {
|
||||
fmt.Println("error writing to buffer")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// open file handle
|
||||
fh_update, err := os.Open(filename_update)
|
||||
if err != nil {
|
||||
fmt.Println("error opening file")
|
||||
panic(err)
|
||||
}
|
||||
defer fh_update.Close()
|
||||
|
||||
// io copy
|
||||
_, err = io.Copy(fileWriter_update, fh_update)
|
||||
if err != nil {
|
||||
fmt.Println("error on IO copy")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
contentType_update := bodyWriter_update.FormDataContentType()
|
||||
bodyWriter_update.Close()
|
||||
w_update := httptest.NewRecorder()
|
||||
req_update, err := http.NewRequest("PUT", "/api/files/5", bodyBuf_update)
|
||||
req_update.Header.Add("Authorization", "Bearer "+token)
|
||||
req_update.Header.Set("Content-Type", contentType_update)
|
||||
if err != nil {
|
||||
fmt.Println("error creating post request")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
router.ServeHTTP(w_update, req_update)
|
||||
|
||||
assert.Equal(t, 200, w_update.Code)
|
||||
fmt.Println(w_update.Body.String())
|
||||
assert.Equal(t, string(msgOKjson), w_update.Body.String())
|
||||
|
||||
// Test GET on updated file content
|
||||
w3 := httptest.NewRecorder()
|
||||
req3, _ := http.NewRequest("GET", "/api/files/5", nil)
|
||||
req3.Header.Add("Authorization", "Bearer "+token)
|
||||
router.ServeHTTP(w3, req3)
|
||||
|
||||
assert.Equal(t, 200, w3.Code)
|
||||
fmt.Println(w3.Body.String())
|
||||
assert.Equal(t, filecontent_update, w3.Body.String())
|
||||
|
||||
// test DELETE files/:fileID
|
||||
common.TestEndpoint(t, router, token, "/api/files/5", "DELETE", nil, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/files?objectID=1&objectType=widget", "GET", nil, 200, msgFilesjson)
|
||||
|
||||
// TODO add testing for other return codes
|
||||
|
||||
// clean up temporary file
|
||||
err = os.Remove(filename)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = os.Remove(filename_update)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
}
|
322
routes/scenario/scenario_endpoints.go
Normal file
322
routes/scenario/scenario_endpoints.go
Normal file
|
@ -0,0 +1,322 @@
|
|||
package scenario
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
|
||||
)
|
||||
|
||||
func RegisterScenarioEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("", getScenarios)
|
||||
r.POST("", addScenario)
|
||||
r.PUT("/:scenarioID", updateScenario)
|
||||
r.GET("/:scenarioID", getScenario)
|
||||
r.DELETE("/:scenarioID", deleteScenario)
|
||||
r.GET("/:scenarioID/users", getUsersOfScenario)
|
||||
r.PUT("/:scenarioID/user", addUserToScenario)
|
||||
r.DELETE("/:scenarioID/user", deleteUserFromScenario)
|
||||
}
|
||||
|
||||
// getScenarios godoc
|
||||
// @Summary Get all scenarios
|
||||
// @ID getScenarios
|
||||
// @Produce json
|
||||
// @Tags scenarios
|
||||
// @Success 200 {array} common.ScenarioResponse "Array of scenarios to which user has access"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /scenarios [get]
|
||||
func getScenarios(c *gin.Context) {
|
||||
|
||||
ok, _ := CheckPermissions(c, common.Read, "none", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// ATTENTION: do not use c.GetInt (common.UserIDCtx) since user_id is of type uint and not int
|
||||
userID, _ := c.Get(common.UserIDCtx)
|
||||
userRole, _ := c.Get(common.UserRoleCtx)
|
||||
|
||||
var u user.User
|
||||
err := u.ByID(userID.(uint))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// get all scenarios for the user who issues the request
|
||||
db := common.GetDB()
|
||||
var scenarios []common.Scenario
|
||||
if userRole == "Admin" { // Admin can see all scenarios
|
||||
err = db.Order("ID asc").Find(&scenarios).Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
} else { // User or Guest roles see only their scenarios
|
||||
err = db.Order("ID asc").Model(&u).Related(&scenarios, "Scenarios").Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
serializer := common.ScenariosSerializer{c, scenarios}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"scenarios": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// addScenario godoc
|
||||
// @Summary Add a scenario
|
||||
// @ID addScenario
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags scenarios
|
||||
// @Param inputScenario body common.ResponseMsgScenario true "Scenario to be added"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /scenarios [post]
|
||||
func addScenario(c *gin.Context) {
|
||||
|
||||
ok, _ := CheckPermissions(c, common.Create, "none", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := c.Get(common.UserIDCtx)
|
||||
|
||||
var u user.User
|
||||
err := u.ByID(userID.(uint))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
var newScenarioData common.ResponseMsgScenario
|
||||
err = c.BindJSON(&newScenarioData)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var newScenario Scenario
|
||||
newScenario.ID = newScenarioData.Scenario.ID
|
||||
newScenario.StartParameters = newScenarioData.Scenario.StartParameters
|
||||
newScenario.Running = newScenarioData.Scenario.Running
|
||||
newScenario.Name = newScenarioData.Scenario.Name
|
||||
|
||||
// save new scenario to DB
|
||||
err = newScenario.save()
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// add user to new scenario
|
||||
err = newScenario.addUser(&(u.User))
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// updateScenario godoc
|
||||
// @Summary Update a scenario
|
||||
// @ID updateScenario
|
||||
// @Tags scenarios
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param inputScenario body common.ResponseMsgScenario true "Scenario to be updated"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param scenarioID path int true "Scenario ID"
|
||||
// @Router /scenarios/{scenarioID} [put]
|
||||
func updateScenario(c *gin.Context) {
|
||||
|
||||
ok, so := CheckPermissions(c, common.Update, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var modifiedScenarioData common.ResponseMsgScenario
|
||||
err := c.BindJSON(&modifiedScenarioData)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = so.update(modifiedScenarioData.Scenario)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getScenario godoc
|
||||
// @Summary Get scenario
|
||||
// @ID getScenario
|
||||
// @Produce json
|
||||
// @Tags scenarios
|
||||
// @Success 200 {object} common.ScenarioResponse "Scenario requested by user"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param scenarioID path int true "Scenario ID"
|
||||
// @Router /scenarios/{scenarioID} [get]
|
||||
func getScenario(c *gin.Context) {
|
||||
|
||||
ok, so := CheckPermissions(c, common.Read, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.ScenarioSerializer{c, so.Scenario}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"scenario": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// deleteScenario godoc
|
||||
// @Summary Delete a scenario
|
||||
// @ID deleteScenario
|
||||
// @Tags scenarios
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param scenarioID path int true "Scenario ID"
|
||||
// @Router /scenarios/{scenarioID} [delete]
|
||||
func deleteScenario(c *gin.Context) {
|
||||
|
||||
ok, so := CheckPermissions(c, common.Delete, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
err := so.delete()
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getUsersOfScenario godoc
|
||||
// @Summary Get users of a scenario
|
||||
// @ID getUsersOfScenario
|
||||
// @Produce json
|
||||
// @Tags scenarios
|
||||
// @Success 200 {array} common.UserResponse "Array of users that have access to the scenario"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param scenarioID path int true "Scenario ID"
|
||||
// @Router /scenarios/{scenarioID}/users/ [get]
|
||||
func getUsersOfScenario(c *gin.Context) {
|
||||
|
||||
ok, so := CheckPermissions(c, common.Read, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Find all users of scenario
|
||||
allUsers, _, err := so.getUsers()
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.UsersSerializer{c, allUsers}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"users": serializer.Response(false),
|
||||
})
|
||||
}
|
||||
|
||||
// addUserToScenario godoc
|
||||
// @Summary Add a user to a a scenario
|
||||
// @ID addUserToScenario
|
||||
// @Tags scenarios
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param scenarioID path int true "Scenario ID"
|
||||
// @Param username query string true "User name"
|
||||
// @Router /scenarios/{scenarioID}/user [put]
|
||||
func addUserToScenario(c *gin.Context) {
|
||||
|
||||
ok, so := CheckPermissions(c, common.Update, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
username := c.Request.URL.Query().Get("username")
|
||||
|
||||
var u user.User
|
||||
err := u.ByUsername(username)
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = so.addUser(&(u.User))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
|
||||
// deleteUserFromScenario godoc
|
||||
// @Summary Delete a user from a scenario
|
||||
// @ID deleteUserFromScenario
|
||||
// @Tags scenarios
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param scenarioID path int true "Scenario ID"
|
||||
// @Param username query string true "User name"
|
||||
// @Router /scenarios/{scenarioID}/user [delete]
|
||||
func deleteUserFromScenario(c *gin.Context) {
|
||||
|
||||
ok, so := CheckPermissions(c, common.Update, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
username := c.Request.URL.Query().Get("username")
|
||||
|
||||
err := so.deleteUser(username)
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package simulation
|
||||
package scenario
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -7,46 +7,46 @@ import (
|
|||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
|
||||
)
|
||||
|
||||
type Simulation struct {
|
||||
common.Simulation
|
||||
type Scenario struct {
|
||||
common.Scenario
|
||||
}
|
||||
|
||||
func (s *Simulation) ByID(id uint) error {
|
||||
func (s *Scenario) ByID(id uint) error {
|
||||
db := common.GetDB()
|
||||
err := db.Find(s, id).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("simulation with id=%v does not exist", id)
|
||||
return fmt.Errorf("scenario with id=%v does not exist", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Simulation) getUsers() ([]common.User, int, error) {
|
||||
func (s *Scenario) getUsers() ([]common.User, int, error) {
|
||||
db := common.GetDB()
|
||||
var users []common.User
|
||||
err := db.Order("ID asc").Model(s).Related(&users, "Users").Error
|
||||
return users, len(users), err
|
||||
}
|
||||
|
||||
func (s *Simulation) save() error {
|
||||
func (s *Scenario) save() error {
|
||||
db := common.GetDB()
|
||||
err := db.Create(s).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Simulation) update(modifiedSimulation Simulation) error {
|
||||
func (s *Scenario) update(modifiedScenario common.ScenarioResponse) error {
|
||||
db := common.GetDB()
|
||||
err := db.Model(s).Update(modifiedSimulation).Error
|
||||
err := db.Model(s).Update(modifiedScenario).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Simulation) addUser(u *common.User) error {
|
||||
func (s *Scenario) addUser(u *common.User) error {
|
||||
|
||||
db := common.GetDB()
|
||||
err := db.Model(s).Association("Users").Append(u).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Simulation) deleteUser(username string) error {
|
||||
func (s *Scenario) deleteUser(username string) error {
|
||||
db := common.GetDB()
|
||||
|
||||
var deletedUser user.User
|
||||
|
@ -58,27 +58,27 @@ func (s *Simulation) deleteUser(username string) error {
|
|||
no_users := db.Model(s).Association("Users").Count()
|
||||
|
||||
if no_users > 1 {
|
||||
// remove user from simulation
|
||||
// remove user from scenario
|
||||
err = db.Model(s).Association("Users").Delete(&deletedUser.User).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// remove simulation from user
|
||||
err = db.Model(&deletedUser.User).Association("Simulations").Delete(s).Error
|
||||
// remove scenario from user
|
||||
err = db.Model(&deletedUser.User).Association("Scenarios").Delete(s).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("cannot delete last user from simulation without deleting simulation itself, doing nothing")
|
||||
return fmt.Errorf("cannot delete last user from scenario without deleting scenario itself, doing nothing")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Simulation) delete() error {
|
||||
func (s *Scenario) delete() error {
|
||||
db := common.GetDB()
|
||||
|
||||
// delete simulation from all users and vice versa
|
||||
// delete scenario from all users and vice versa
|
||||
|
||||
users, no_users, err := s.getUsers()
|
||||
if err != nil {
|
||||
|
@ -87,23 +87,23 @@ func (s *Simulation) delete() error {
|
|||
|
||||
if no_users > 0 {
|
||||
for _, u := range users {
|
||||
// remove user from simulation
|
||||
// remove user from scenario
|
||||
err = db.Model(s).Association("Users").Delete(&u).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// remove simulation from user
|
||||
err = db.Model(&u).Association("Simulations").Delete(s).Error
|
||||
// remove scenario from user
|
||||
err = db.Model(&u).Association("Scenarios").Delete(s).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simulation is not deleted from DB, only associations with users are removed
|
||||
// Simulation remains "dangling" in DB
|
||||
// Scenario is not deleted from DB, only associations with users are removed
|
||||
// Scenario remains "dangling" in DB
|
||||
|
||||
// Delete simulation
|
||||
// Delete scenario
|
||||
//err = db.Delete(s).Error
|
||||
//if err != nil {
|
||||
// return err
|
||||
|
@ -112,7 +112,7 @@ func (s *Simulation) delete() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Simulation) checkAccess(userID uint, userRole string) bool {
|
||||
func (s *Scenario) checkAccess(userID uint, userRole string) bool {
|
||||
|
||||
if userRole == "Admin" {
|
||||
return true
|
|
@ -1,4 +1,4 @@
|
|||
package simulation
|
||||
package scenario
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -10,62 +10,62 @@ import (
|
|||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
)
|
||||
|
||||
func CheckPermissions(c *gin.Context, modelname common.ModelName, operation common.CRUD, simIDSource string, simIDBody int) (bool, Simulation) {
|
||||
func CheckPermissions(c *gin.Context, operation common.CRUD, simIDSource string, simIDBody int) (bool, Scenario) {
|
||||
|
||||
var sim Simulation
|
||||
var so Scenario
|
||||
|
||||
err := common.ValidateRole(c, modelname, operation)
|
||||
err := common.ValidateRole(c, common.ModelScenario, operation)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return false, sim
|
||||
return false, so
|
||||
}
|
||||
|
||||
if operation == common.Create || (operation == common.Read && simIDSource == "none") {
|
||||
return true, sim
|
||||
return true, so
|
||||
}
|
||||
|
||||
var simID int
|
||||
if simIDSource == "path" {
|
||||
simID, err = strconv.Atoi(c.Param("simulationID"))
|
||||
simID, err = strconv.Atoi(c.Param("scenarioID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulationID path parameter")
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of scenarioID path parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return false, sim
|
||||
return false, so
|
||||
}
|
||||
} else if simIDSource == "query" {
|
||||
simID, err = strconv.Atoi(c.Request.URL.Query().Get("simulationID"))
|
||||
simID, err = strconv.Atoi(c.Request.URL.Query().Get("scenarioID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulationID query parameter")
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of scenarioID query parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return false, sim
|
||||
return false, so
|
||||
}
|
||||
} else if simIDSource == "body" {
|
||||
simID = simIDBody
|
||||
|
||||
} else {
|
||||
errormsg := fmt.Sprintf("Bad request. The following source of your simulation ID is not valid: %s", simIDSource)
|
||||
errormsg := fmt.Sprintf("Bad request. The following source of your scenario ID is not valid: %s", simIDSource)
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return false, sim
|
||||
return false, so
|
||||
}
|
||||
|
||||
userID, _ := c.Get(common.UserIDCtx)
|
||||
userRole, _ := c.Get(common.UserRoleCtx)
|
||||
|
||||
err = sim.ByID(uint(simID))
|
||||
err = so.ByID(uint(simID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return false, sim
|
||||
return false, so
|
||||
}
|
||||
|
||||
if sim.checkAccess(userID.(uint), userRole.(string)) == false {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (for simulation ID).")
|
||||
return false, sim
|
||||
if so.checkAccess(userID.(uint), userRole.(string)) == false {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (for scenario ID).")
|
||||
return false, so
|
||||
}
|
||||
|
||||
return true, sim
|
||||
return true, so
|
||||
}
|
98
routes/scenario/scenario_test.go
Normal file
98
routes/scenario/scenario_test.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package scenario
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
|
||||
)
|
||||
|
||||
// Test /scenarios endpoints
|
||||
func TestScenarioEndpoints(t *testing.T) {
|
||||
|
||||
var token string
|
||||
|
||||
var myUsers = []common.UserResponse{common.UserA_response, common.UserB_response}
|
||||
var myUserA = []common.UserResponse{common.UserA_response}
|
||||
var msgUsers = common.ResponseMsgUsers{Users: myUsers}
|
||||
var msgUserA = common.ResponseMsgUsers{Users: myUserA}
|
||||
var myScenarios = []common.ScenarioResponse{common.ScenarioA_response, common.ScenarioB_response}
|
||||
var msgScenarios = common.ResponseMsgScenarios{Scenarios: myScenarios}
|
||||
var msgScenario = common.ResponseMsgScenario{Scenario: common.ScenarioC_response}
|
||||
var msgScenarioUpdated = common.ResponseMsgScenario{Scenario: common.ScenarioCUpdated_response}
|
||||
|
||||
db := common.DummyInitDB()
|
||||
defer db.Close()
|
||||
common.DummyPopulateDB(db)
|
||||
|
||||
router := gin.Default()
|
||||
api := router.Group("/api")
|
||||
|
||||
// All endpoints require authentication except when someone wants to
|
||||
// login (POST /authenticate)
|
||||
user.VisitorAuthenticate(api.Group("/authenticate"))
|
||||
|
||||
api.Use(user.Authentication(true))
|
||||
|
||||
RegisterScenarioEndpoints(api.Group("/scenarios"))
|
||||
|
||||
credjson, _ := json.Marshal(common.CredUser)
|
||||
msgOKjson, _ := json.Marshal(common.MsgOK)
|
||||
msgScenariosjson, _ := json.Marshal(msgScenarios)
|
||||
msgScenariojson, _ := json.Marshal(msgScenario)
|
||||
msgScenarioUpdatedjson, _ := json.Marshal(msgScenarioUpdated)
|
||||
|
||||
msgUsersjson, _ := json.Marshal(msgUsers)
|
||||
msgUserAjson, _ := json.Marshal(msgUserA)
|
||||
|
||||
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
|
||||
|
||||
// test GET scenarios/
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios", "GET", nil, 200, msgScenariosjson)
|
||||
|
||||
// test POST scenarios/
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios", "POST", msgScenariojson, 200, msgOKjson)
|
||||
|
||||
// test GET scenarios/:ScenarioID
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios/3", "GET", nil, 200, msgScenariojson)
|
||||
|
||||
// test PUT scenarios/:ScenarioID
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios/3", "PUT", msgScenarioUpdatedjson, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios/3", "GET", nil, 200, msgScenarioUpdatedjson)
|
||||
|
||||
// test DELETE scenarios/:ScenarioID
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios/3", "DELETE", nil, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios", "GET", nil, 200, msgScenariosjson)
|
||||
|
||||
// test GET scenarios/:ScenarioID/users
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios/1/users", "GET", nil, 200, msgUsersjson)
|
||||
|
||||
// test DELETE scenarios/:ScenarioID/user
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios/1/user?username=User_B", "DELETE", nil, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios/1/users", "GET", nil, 200, msgUserAjson)
|
||||
|
||||
// test PUT scenarios/:ScenarioID/user
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios/1/user?username=User_B", "PUT", nil, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios/1/users", "GET", nil, 200, msgUsersjson)
|
||||
|
||||
// test DELETE scenarios/:ScenarioID/user for logged in user User_A
|
||||
common.TestEndpoint(t, router, token, "/api/scenarios/1/user?username=User_A", "DELETE", nil, 200, msgOKjson)
|
||||
|
||||
// test if deletion of user from scenario has worked
|
||||
w2 := httptest.NewRecorder()
|
||||
req2, _ := http.NewRequest("GET", "/api/scenarios/1/users", nil)
|
||||
req2.Header.Add("Authorization", "Bearer "+token)
|
||||
router.ServeHTTP(w2, req2)
|
||||
|
||||
assert.Equal(t, 422, w2.Code)
|
||||
fmt.Println(w2.Body.String())
|
||||
assert.Equal(t, "\"Access denied (for scenario ID).\"", w2.Body.String())
|
||||
|
||||
// TODO add tests for other return codes
|
||||
}
|
199
routes/signal/signal_endpoints.go
Normal file
199
routes/signal/signal_endpoints.go
Normal file
|
@ -0,0 +1,199 @@
|
|||
package signal
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel"
|
||||
)
|
||||
|
||||
func RegisterSignalEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("", getSignals)
|
||||
r.POST("", addSignal)
|
||||
r.PUT("/:signalID", updateSignal)
|
||||
r.GET("/:signalID", getSignal)
|
||||
r.DELETE("/:signalID", deleteSignal)
|
||||
}
|
||||
|
||||
// getSignals godoc
|
||||
// @Summary Get all signals of one direction
|
||||
// @ID getSignals
|
||||
// @Produce json
|
||||
// @Tags signals
|
||||
// @Param direction query string true "Direction of signal (in or out)"
|
||||
// @Param modelID query string true "Model ID of signals to be obtained"
|
||||
// @Success 200 {array} common.Signal "Requested signals."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /signals [get]
|
||||
func getSignals(c *gin.Context) {
|
||||
|
||||
ok, m := simulationmodel.CheckPermissions(c, common.Read, "query", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var mapping string
|
||||
direction := c.Request.URL.Query().Get("direction")
|
||||
if direction == "in" {
|
||||
mapping = "InputMapping"
|
||||
} else if direction == "out" {
|
||||
mapping = "OutputMapping"
|
||||
} else {
|
||||
errormsg := "Bad request. Direction has to be in or out"
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
var sigs []common.Signal
|
||||
err := db.Order("ID asc").Model(m).Where("Direction = ?", direction).Related(&sigs, mapping).Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.SignalsSerializer{c, sigs}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"signals": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// AddSignal godoc
|
||||
// @Summary Add a signal to a signal mapping of a model
|
||||
// @ID AddSignal
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags signals
|
||||
// @Param inputSignal body common.ResponseMsgSignal true "A signal to be added to the model incl. direction and model ID to which signal shall be added"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /signals [post]
|
||||
func addSignal(c *gin.Context) {
|
||||
|
||||
var newSignalData common.ResponseMsgSignal
|
||||
err := c.BindJSON(&newSignalData)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var newSignal Signal
|
||||
newSignal.Index = newSignalData.Signal.Index
|
||||
newSignal.SimulationModelID = newSignalData.Signal.SimulationModelID
|
||||
newSignal.Direction = newSignalData.Signal.Direction
|
||||
newSignal.Unit = newSignalData.Signal.Unit
|
||||
newSignal.Name = newSignalData.Signal.Name
|
||||
|
||||
ok, _ := simulationmodel.CheckPermissions(c, common.Update, "body", int(newSignal.SimulationModelID))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Add signal to model
|
||||
err = newSignal.addToSimulationModel()
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// updateSignal godoc
|
||||
// @Summary Update a signal
|
||||
// @ID updateSignal
|
||||
// @Tags signals
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param signalID path int true "ID of signal to be updated"
|
||||
// @Router /signals/{signalID} [put]
|
||||
func updateSignal(c *gin.Context) {
|
||||
ok, sig := checkPermissions(c, common.Delete)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var modifiedSignal common.ResponseMsgSignal
|
||||
err := c.BindJSON(&modifiedSignal)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = sig.update(modifiedSignal.Signal)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getSignal godoc
|
||||
// @Summary Get a signal
|
||||
// @ID getSignal
|
||||
// @Tags signals
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param signalID path int true "ID of signal to be obtained"
|
||||
// @Router /signals/{signalID} [get]
|
||||
func getSignal(c *gin.Context) {
|
||||
ok, sig := checkPermissions(c, common.Delete)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.SignalSerializer{c, sig.Signal}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"signal": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// deleteSignal godoc
|
||||
// @Summary Delete a signal
|
||||
// @ID deleteSignal
|
||||
// @Tags signals
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param signalID path int true "ID of signal to be deleted"
|
||||
// @Router /signals/{signalID} [delete]
|
||||
func deleteSignal(c *gin.Context) {
|
||||
|
||||
ok, sig := checkPermissions(c, common.Delete)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
err := sig.delete()
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
|
||||
}
|
113
routes/signal/signal_methods.go
Normal file
113
routes/signal/signal_methods.go
Normal file
|
@ -0,0 +1,113 @@
|
|||
package signal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel"
|
||||
)
|
||||
|
||||
type Signal struct {
|
||||
common.Signal
|
||||
}
|
||||
|
||||
func (s *Signal) save() error {
|
||||
db := common.GetDB()
|
||||
err := db.Create(s).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Signal) byID(id uint) error {
|
||||
db := common.GetDB()
|
||||
err := db.Find(s, id).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("Signal with id=%v does not exist", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Signal) addToSimulationModel() error {
|
||||
db := common.GetDB()
|
||||
var m simulationmodel.SimulationModel
|
||||
err := m.ByID(s.SimulationModelID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save signal to DB
|
||||
err = s.save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// associate signal with simulation model in correct direction
|
||||
if s.Direction == "in" {
|
||||
err = db.Model(&m).Association("InputMapping").Append(s).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// adapt length of mapping
|
||||
var newInputLength = db.Model(m).Where("Direction = ?", "in").Association("InputMapping").Count()
|
||||
err = db.Model(m).Update("InputLength", newInputLength).Error
|
||||
|
||||
} else {
|
||||
err = db.Model(&m).Association("OutputMapping").Append(s).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// adapt length of mapping
|
||||
var newOutputLength = db.Model(m).Where("Direction = ?", "out").Association("OutputMapping").Count()
|
||||
err = db.Model(m).Update("OutputLength", newOutputLength).Error
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Signal) update(modifiedSignal common.SignalResponse) error {
|
||||
db := common.GetDB()
|
||||
|
||||
err := db.Model(s).Updates(map[string]interface{}{
|
||||
"Name": modifiedSignal.Name,
|
||||
"Unit": modifiedSignal.Unit,
|
||||
"Index": modifiedSignal.Index,
|
||||
}).Error
|
||||
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
func (s *Signal) delete() error {
|
||||
|
||||
db := common.GetDB()
|
||||
var m simulationmodel.SimulationModel
|
||||
err := m.ByID(s.SimulationModelID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove association between Signal and SimulationModel
|
||||
// Signal itself is not deleted from DB, it remains as "dangling"
|
||||
if s.Direction == "in" {
|
||||
err = db.Model(&m).Association("InputMapping").Delete(s).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Reduce length of mapping by 1
|
||||
var newInputLength = m.InputLength - 1
|
||||
err = db.Model(m).Update("InputLength", newInputLength).Error
|
||||
|
||||
} else {
|
||||
err = db.Model(&m).Association("OutputMapping").Delete(s).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Reduce length of mapping by 1
|
||||
var newOutputLength = m.OutputLength - 1
|
||||
err = db.Model(m).Update("OutputLength", newOutputLength).Error
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
44
routes/signal/signal_middleware.go
Normal file
44
routes/signal/signal_middleware.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package signal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel"
|
||||
)
|
||||
|
||||
func checkPermissions(c *gin.Context, operation common.CRUD) (bool, Signal) {
|
||||
|
||||
var sig Signal
|
||||
|
||||
err := common.ValidateRole(c, common.ModelSignal, operation)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return false, sig
|
||||
}
|
||||
|
||||
signalID, err := strconv.Atoi(c.Param("signalID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of signalID path parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return false, sig
|
||||
}
|
||||
|
||||
err = sig.byID(uint(signalID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return false, sig
|
||||
}
|
||||
|
||||
ok, _ := simulationmodel.CheckPermissions(c, operation, "body", int(sig.SimulationModelID))
|
||||
if !ok {
|
||||
return false, sig
|
||||
}
|
||||
|
||||
return true, sig
|
||||
}
|
73
routes/signal/signal_test.go
Normal file
73
routes/signal/signal_test.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
package signal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
|
||||
)
|
||||
|
||||
// Test /models endpoints
|
||||
func TestSignalEndpoints(t *testing.T) {
|
||||
|
||||
var token string
|
||||
|
||||
var myInSignals = []common.SignalResponse{common.InSignalA_response, common.InSignalB_response}
|
||||
var myOutSignals = []common.SignalResponse{common.OutSignalA_response, common.OutSignalB_response}
|
||||
var msgInSignals = common.ResponseMsgSignals{Signals: myInSignals}
|
||||
var msgInSignalC = common.ResponseMsgSignal{Signal: common.InSignalC_response}
|
||||
var msgInSignalCupdated = common.ResponseMsgSignal{Signal: common.InSignalCUpdated_response}
|
||||
var msgOutSignals = common.ResponseMsgSignals{Signals: myOutSignals}
|
||||
|
||||
db := common.DummyInitDB()
|
||||
defer db.Close()
|
||||
common.DummyPopulateDB(db)
|
||||
|
||||
router := gin.Default()
|
||||
api := router.Group("/api")
|
||||
|
||||
// All endpoints require authentication except when someone wants to
|
||||
// login (POST /authenticate)
|
||||
user.VisitorAuthenticate(api.Group("/authenticate"))
|
||||
|
||||
api.Use(user.Authentication(true))
|
||||
|
||||
RegisterSignalEndpoints(api.Group("/signals"))
|
||||
|
||||
credjson, _ := json.Marshal(common.CredUser)
|
||||
msgOKjson, _ := json.Marshal(common.MsgOK)
|
||||
msgInSignalsjson, _ := json.Marshal(msgInSignals)
|
||||
msgOutSignalsjson, _ := json.Marshal(msgOutSignals)
|
||||
inSignalCjson, _ := json.Marshal(msgInSignalC)
|
||||
inSignalCupdatedjson, _ := json.Marshal(msgInSignalCupdated)
|
||||
|
||||
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
|
||||
|
||||
// test GET signals
|
||||
common.TestEndpoint(t, router, token, "/api/signals?modelID=1&direction=in", "GET", nil, 200, msgInSignalsjson)
|
||||
common.TestEndpoint(t, router, token, "/api/signals?modelID=1&direction=out", "GET", nil, 200, msgOutSignalsjson)
|
||||
|
||||
// test POST signals
|
||||
common.TestEndpoint(t, router, token, "/api/signals", "POST", inSignalCjson, 200, msgOKjson)
|
||||
|
||||
// test GET signals/:signalID
|
||||
common.TestEndpoint(t, router, token, "/api/signals/5", "GET", nil, 200, inSignalCjson)
|
||||
|
||||
// test PUT signals/:signalID
|
||||
common.TestEndpoint(t, router, token, "/api/signals/5", "PUT", inSignalCupdatedjson, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/signals/5", "GET", nil, 200, inSignalCupdatedjson)
|
||||
|
||||
// test DELETE signals/:signalID
|
||||
common.TestEndpoint(t, router, token, "/api/signals/5", "DELETE", nil, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/signals?modelID=1&direction=in", "GET", nil, 200, msgInSignalsjson)
|
||||
common.TestEndpoint(t, router, token, "/api/signals?modelID=1&direction=out", "GET", nil, 200, msgOutSignalsjson)
|
||||
|
||||
// TODO test GET models/:ModelID to check if POST and DELETE adapt InputLength correctly??
|
||||
//common.TestEndpoint(t, router, token, "/api/models/1", "GET", nil, 200, string(msgModelAUpdated2json))
|
||||
|
||||
// TODO add testing for other return codes
|
||||
|
||||
}
|
|
@ -1,316 +0,0 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
|
||||
)
|
||||
|
||||
func RegisterSimulationEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("", getSimulations)
|
||||
r.POST("", addSimulation)
|
||||
r.PUT("/:simulationID", updateSimulation)
|
||||
r.GET("/:simulationID", getSimulation)
|
||||
r.DELETE("/:simulationID", deleteSimulation)
|
||||
r.GET("/:simulationID/users", getUsersOfSimulation)
|
||||
r.PUT("/:simulationID/user", addUserToSimulation)
|
||||
r.DELETE("/:simulationID/user", deleteUserFromSimulation)
|
||||
}
|
||||
|
||||
// getSimulations godoc
|
||||
// @Summary Get all simulations
|
||||
// @ID getSimulations
|
||||
// @Produce json
|
||||
// @Tags simulations
|
||||
// @Success 200 {array} common.SimulationResponse "Array of simulations to which user has access"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /simulations [get]
|
||||
func getSimulations(c *gin.Context) {
|
||||
|
||||
ok, _ := CheckPermissions(c, common.ModelSimulation, common.Read, "none", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// ATTENTION: do not use c.GetInt (common.UserIDCtx) since user_id is of type uint and not int
|
||||
userID, _ := c.Get(common.UserIDCtx)
|
||||
userRole, _ := c.Get(common.UserRoleCtx)
|
||||
|
||||
var u user.User
|
||||
err := u.ByID(userID.(uint))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// get all simulations for the user who issues the request
|
||||
db := common.GetDB()
|
||||
var simulations []common.Simulation
|
||||
if userRole == "Admin" { // Admin can see all simulations
|
||||
err = db.Order("ID asc").Find(&simulations).Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
} else { // User or Guest roles see only their simulations
|
||||
err = db.Order("ID asc").Model(&u).Related(&simulations, "Simulations").Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
serializer := common.SimulationsSerializer{c, simulations}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"simulations": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// addSimulation godoc
|
||||
// @Summary Add a simulation
|
||||
// @ID addSimulation
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags simulations
|
||||
// @Param inputSimulation body common.SimulationResponse true "Simulation to be added"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /simulations [post]
|
||||
func addSimulation(c *gin.Context) {
|
||||
|
||||
ok, _ := CheckPermissions(c, common.ModelSimulation, common.Create, "none", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := c.Get(common.UserIDCtx)
|
||||
|
||||
var u user.User
|
||||
err := u.ByID(userID.(uint))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
var sim Simulation
|
||||
err = c.BindJSON(&sim)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// save new simulation to DB
|
||||
err = sim.save()
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// add user to new simulation
|
||||
err = sim.addUser(&(u.User))
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// updateSimulation godoc
|
||||
// @Summary Update a simulation
|
||||
// @ID updateSimulation
|
||||
// @Tags simulations
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param inputSimulation body common.SimulationResponse true "Simulation to be updated"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulationID path int true "Simulation ID"
|
||||
// @Router /simulations/{simulationID} [put]
|
||||
func updateSimulation(c *gin.Context) {
|
||||
|
||||
ok, sim := CheckPermissions(c, common.ModelSimulation, common.Update, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var modifiedSim Simulation
|
||||
err := c.BindJSON(&modifiedSim)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = sim.update(modifiedSim)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getSimulation godoc
|
||||
// @Summary Get simulation
|
||||
// @ID getSimulation
|
||||
// @Produce json
|
||||
// @Tags simulations
|
||||
// @Success 200 {object} common.SimulationResponse "Simulation requested by user"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulationID path int true "Simulation ID"
|
||||
// @Router /simulations/{simulationID} [get]
|
||||
func getSimulation(c *gin.Context) {
|
||||
|
||||
ok, sim := CheckPermissions(c, common.ModelSimulation, common.Read, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.SimulationSerializer{c, sim.Simulation}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"simulation": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// deleteSimulation godoc
|
||||
// @Summary Delete a simulation
|
||||
// @ID deleteSimulation
|
||||
// @Tags simulations
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulationID path int true "Simulation ID"
|
||||
// @Router /simulations/{simulationID} [delete]
|
||||
func deleteSimulation(c *gin.Context) {
|
||||
|
||||
ok, sim := CheckPermissions(c, common.ModelSimulation, common.Delete, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
err := sim.delete()
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getUsersOfSimulation godoc
|
||||
// @Summary Get users of simulation
|
||||
// @ID getUsersOfSimulation
|
||||
// @Produce json
|
||||
// @Tags simulations
|
||||
// @Success 200 {array} common.UserResponse "Array of users that have access to the simulation"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulationID path int true "Simulation ID"
|
||||
// @Router /simulations/{simulationID}/users/ [get]
|
||||
func getUsersOfSimulation(c *gin.Context) {
|
||||
|
||||
ok, sim := CheckPermissions(c, common.ModelSimulation, common.Read, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Find all users of simulation
|
||||
allUsers, _, err := sim.getUsers()
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.UsersSerializer{c, allUsers}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"users": serializer.Response(false),
|
||||
})
|
||||
}
|
||||
|
||||
// addUserToSimulation godoc
|
||||
// @Summary Add a user to a a simulation
|
||||
// @ID addUserToSimulation
|
||||
// @Tags simulations
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulationID path int true "Simulation ID"
|
||||
// @Param username query string true "User name"
|
||||
// @Router /simulations/{simulationID}/user [put]
|
||||
func addUserToSimulation(c *gin.Context) {
|
||||
|
||||
ok, sim := CheckPermissions(c, common.ModelSimulation, common.Update, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
username := c.Request.URL.Query().Get("username")
|
||||
|
||||
var u user.User
|
||||
err := u.ByUsername(username)
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = sim.addUser(&(u.User))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
|
||||
// deleteUserFromSimulation godoc
|
||||
// @Summary Delete a user from a simulation
|
||||
// @ID deleteUserFromSimulation
|
||||
// @Tags simulations
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulationID path int true "Simulation ID"
|
||||
// @Param username query string true "User name"
|
||||
// @Router /simulations/{simulationID}/user [delete]
|
||||
func deleteUserFromSimulation(c *gin.Context) {
|
||||
|
||||
ok, sim := CheckPermissions(c, common.ModelSimulation, common.Update, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
username := c.Request.URL.Query().Get("username")
|
||||
|
||||
err := sim.deleteUser(username)
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
|
@ -1,178 +0,0 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
)
|
||||
|
||||
var token string
|
||||
|
||||
type credentials struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
var cred = credentials{
|
||||
Username: "User_A",
|
||||
Password: "abc123",
|
||||
}
|
||||
|
||||
var msgOK = common.ResponseMsg{
|
||||
Message: "OK.",
|
||||
}
|
||||
|
||||
var user_A = common.UserResponse{
|
||||
Username: "User_A",
|
||||
Role: "User",
|
||||
Mail: "",
|
||||
}
|
||||
|
||||
var user_B = common.UserResponse{
|
||||
Username: "User_B",
|
||||
Role: "User",
|
||||
Mail: "",
|
||||
}
|
||||
|
||||
var myUsers = []common.UserResponse{
|
||||
user_A,
|
||||
user_B,
|
||||
}
|
||||
|
||||
var myUserA = []common.UserResponse{
|
||||
user_A,
|
||||
}
|
||||
|
||||
var msgUsers = common.ResponseMsgUsers{
|
||||
Users: myUsers,
|
||||
}
|
||||
|
||||
var msgUserA = common.ResponseMsgUsers{
|
||||
Users: myUserA,
|
||||
}
|
||||
|
||||
var simulationA = common.SimulationResponse{
|
||||
Name: "Simulation_A",
|
||||
ID: 1,
|
||||
Running: false,
|
||||
}
|
||||
|
||||
var simulationB = common.SimulationResponse{
|
||||
Name: "Simulation_B",
|
||||
ID: 2,
|
||||
Running: false,
|
||||
}
|
||||
|
||||
var simulationC = common.Simulation{
|
||||
Name: "Simulation_C",
|
||||
Running: false,
|
||||
StartParameters: "test",
|
||||
}
|
||||
|
||||
var simulationC_response = common.SimulationResponse{
|
||||
ID: 3,
|
||||
Name: simulationC.Name,
|
||||
Running: simulationC.Running,
|
||||
StartParams: simulationC.StartParameters,
|
||||
}
|
||||
|
||||
var mySimulations = []common.SimulationResponse{
|
||||
simulationA,
|
||||
simulationB,
|
||||
}
|
||||
|
||||
var msgSimulations = common.ResponseMsgSimulations{
|
||||
Simulations: mySimulations,
|
||||
}
|
||||
|
||||
var msgSimulation = common.ResponseMsgSimulation{
|
||||
Simulation: simulationC_response,
|
||||
}
|
||||
|
||||
// Test /simulation endpoints
|
||||
func TestSimulationEndpoints(t *testing.T) {
|
||||
|
||||
db := common.DummyInitDB()
|
||||
defer db.Close()
|
||||
common.DummyPopulateDB(db)
|
||||
|
||||
router := gin.Default()
|
||||
api := router.Group("/api")
|
||||
|
||||
// All endpoints require authentication except when someone wants to
|
||||
// login (POST /authenticate)
|
||||
user.VisitorAuthenticate(api.Group("/authenticate"))
|
||||
|
||||
api.Use(user.Authentication(true))
|
||||
|
||||
RegisterSimulationEndpoints(api.Group("/simulations"))
|
||||
|
||||
credjson, err := json.Marshal(cred)
|
||||
|
||||
msgOKjson, err := json.Marshal(msgOK)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgUsersjson, err := json.Marshal(msgUsers)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgUserAjson, err := json.Marshal(msgUserA)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgSimulationsjson, err := json.Marshal(msgSimulations)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgSimulationjson, err := json.Marshal(msgSimulation)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
simulationCjson, err := json.Marshal(simulationC)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
|
||||
|
||||
// test GET simulations/
|
||||
common.TestEndpoint(t, router, token, "/api/simulations", "GET", nil, 200, string(msgSimulationsjson))
|
||||
|
||||
// test POST simulations/
|
||||
common.TestEndpoint(t, router, token, "/api/simulations", "POST", simulationCjson, 200, string(msgOKjson))
|
||||
|
||||
// test GET simulations/:SimulationID
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/3", "GET", nil, 200, string(msgSimulationjson))
|
||||
|
||||
// test DELETE simulations/:SimulationID
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/3", "DELETE", nil, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/simulations", "GET", nil, 200, string(msgSimulationsjson))
|
||||
|
||||
// test GET simulations/:SimulationID/users
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/1/users", "GET", nil, 200, string(msgUsersjson))
|
||||
|
||||
// test DELETE simulations/:SimulationID/user
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/1/user?username=User_B", "DELETE", nil, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/1/users", "GET", nil, 200, string(msgUserAjson))
|
||||
|
||||
// test PUT simulations/:SimulationID/user
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/1/user?username=User_B", "PUT", nil, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/1/users", "GET", nil, 200, string(msgUsersjson))
|
||||
|
||||
// test DELETE simulations/:SimulationID/user for logged in user User_A
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/1/user?username=User_A", "DELETE", nil, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/simulations/1/users", "GET", nil, 422, "\"Access denied (for simulation ID).\"")
|
||||
|
||||
// TODO add tests for other return codes
|
||||
}
|
|
@ -1,342 +0,0 @@
|
|||
package simulationmodel
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
|
||||
)
|
||||
|
||||
func RegisterSimulationModelEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("", getSimulationModels)
|
||||
r.POST("", addSimulationModel)
|
||||
//r.POST("/:modelID", cloneSimulationModel)
|
||||
r.PUT("/:modelID", updateSimulationModel)
|
||||
r.GET("/:modelID", getSimulationModel)
|
||||
r.DELETE("/:modelID", deleteSimulationModel)
|
||||
r.GET("/:modelID/signals", getSignals)
|
||||
r.PUT("/:modelID/signals", addSignal)
|
||||
r.DELETE("/:modelID/signals", deleteSignals)
|
||||
}
|
||||
|
||||
// getSimulationModels godoc
|
||||
// @Summary Get all simulation models of simulation
|
||||
// @ID getSimulationModels
|
||||
// @Produce json
|
||||
// @Tags models
|
||||
// @Success 200 {array} common.SimulationModelResponse "Array of models to which belong to simulation"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulationID query int true "Simulation ID"
|
||||
// @Router /models [get]
|
||||
func getSimulationModels(c *gin.Context) {
|
||||
|
||||
ok, sim := simulation.CheckPermissions(c, common.ModelSimulationModel, common.Read, "query", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
var models []common.SimulationModel
|
||||
err := db.Order("ID asc").Model(sim).Related(&models, "Models").Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.SimulationModelsSerializer{c, models}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"models": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// addSimulationModel godoc
|
||||
// @Summary Add a simulation model to a simulation
|
||||
// @ID addSimulationModel
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags models
|
||||
// @Param inputSimulationModel body common.SimulationModelResponse true "Simulation model to be added incl. IDs of simulation and simulator"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /models [post]
|
||||
func addSimulationModel(c *gin.Context) {
|
||||
|
||||
var newModel SimulationModel
|
||||
err := c.BindJSON(&newModel)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ok, _ := simulation.CheckPermissions(c, common.ModelSimulationModel, common.Create, "body", int(newModel.SimulationID))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
err = newModel.addToSimulation()
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func cloneSimulationModel(c *gin.Context) {
|
||||
|
||||
// modelID, err := routes.GetModelID(c)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// targetSimID, err := strconv.Atoi(c.PostForm("TargetSim"))
|
||||
// if err != nil {
|
||||
// errormsg := fmt.Sprintf("Bad request. No or incorrect format of target sim ID")
|
||||
// c.JSON(http.StatusBadRequest, gin.H{
|
||||
// "error": errormsg,
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
// TODO TO BE IMPLEMENTED
|
||||
// Check if target sim exists
|
||||
// Check if model exists
|
||||
|
||||
// Get all Signals of Model
|
||||
// Get Simulator of Model
|
||||
// Get Files of model
|
||||
|
||||
// Add new model object to DB and associate with target sim
|
||||
// Add new signal objects to DB and associate with new model object (careful with directions)
|
||||
// Associate Simulator with new Model object
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Not implemented.",
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// updateSimulationModel godoc
|
||||
// @Summary Update a simulation model
|
||||
// @ID updateSimulationModel
|
||||
// @Tags models
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param inputSimulationModel body common.SimulationModelResponse true "Simulation model to be updated"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param modelID path int true "Model ID"
|
||||
// @Router /models/{modelID} [put]
|
||||
func updateSimulationModel(c *gin.Context) {
|
||||
|
||||
ok, m := checkPermissions(c, common.Update)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var modifiedModel SimulationModel
|
||||
err := c.BindJSON(&modifiedModel)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = m.update(modifiedModel)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getSimulationModel godoc
|
||||
// @Summary Get a simulation model
|
||||
// @ID getSimulationModel
|
||||
// @Tags models
|
||||
// @Produce json
|
||||
// @Success 200 {object} common.SimulationModelResponse "Requested simulation model."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param modelID path int true "Model ID"
|
||||
// @Router /models/{modelID} [get]
|
||||
func getSimulationModel(c *gin.Context) {
|
||||
|
||||
ok, m := checkPermissions(c, common.Read)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.SimulationModelSerializer{c, m.SimulationModel}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"model": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// deleteSimulationModel godoc
|
||||
// @Summary Delete a simulation model
|
||||
// @ID deleteSimulationModel
|
||||
// @Tags models
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param modelID path int true "Model ID"
|
||||
// @Router /models/{modelID} [delete]
|
||||
func deleteSimulationModel(c *gin.Context) {
|
||||
|
||||
ok, m := checkPermissions(c, common.Delete)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
err := m.delete()
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
|
||||
// getSignals godoc
|
||||
// @Summary Get all signals of one direction
|
||||
// @ID getSignals
|
||||
// @Produce json
|
||||
// @Tags models
|
||||
// @Param direction query string true "Direction of signal (in or out)"
|
||||
// @Success 200 {array} common.Signal "Requested signals."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /models/{modelID}/signals [get]
|
||||
func getSignals(c *gin.Context) {
|
||||
|
||||
ok, m := checkPermissions(c, common.Read)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var mapping string
|
||||
direction := c.Request.URL.Query().Get("direction")
|
||||
if direction == "in" {
|
||||
mapping = "InputMapping"
|
||||
} else if direction == "out" {
|
||||
mapping = "OutputMapping"
|
||||
} else {
|
||||
errormsg := "Bad request. Direction has to be in or out"
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
var sigs []common.Signal
|
||||
err := db.Order("ID asc").Model(m).Where("Direction = ?", direction).Related(&sigs, mapping).Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.SignalsSerializer{c, sigs}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"signals": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// AddSignal godoc
|
||||
// @Summary Add a signal to a signal mapping of a model
|
||||
// @ID AddSignal
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags models
|
||||
// @Param inputSignal body common.Signal true "A signal to be added to the model incl. direction"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /models/{modelID}/signals [put]
|
||||
func addSignal(c *gin.Context) {
|
||||
|
||||
ok, m := checkPermissions(c, common.Update)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var sig common.Signal
|
||||
err := c.BindJSON(&sig)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Add signal to model
|
||||
err = m.addSignal(sig)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// deleteSignals godoc
|
||||
// @Summary Delete all signals of a direction
|
||||
// @ID deleteSignals
|
||||
// @Tags models
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param modelID path int true "Model ID"
|
||||
// @Param direction query string true "Direction of signals to delete (in or out)"
|
||||
// @Router /models/{modelID}/signals [delete]
|
||||
func deleteSignals(c *gin.Context) {
|
||||
|
||||
ok, m := checkPermissions(c, common.Update)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
direction := c.Request.URL.Query().Get("direction")
|
||||
if !(direction == "out") && !(direction == "in") {
|
||||
errormsg := "Bad request. Direction has to be in or out"
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err := m.deleteSignals(direction)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
package simulationmodel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator"
|
||||
)
|
||||
|
||||
type SimulationModel struct {
|
||||
common.SimulationModel
|
||||
}
|
||||
|
||||
func (m *SimulationModel) save() error {
|
||||
db := common.GetDB()
|
||||
err := db.Create(m).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *SimulationModel) ByID(id uint) error {
|
||||
db := common.GetDB()
|
||||
err := db.Find(m, id).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("Simulation Model with id=%v does not exist", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SimulationModel) addToSimulation() error {
|
||||
db := common.GetDB()
|
||||
var sim simulation.Simulation
|
||||
err := sim.ByID(m.SimulationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save simulation model to DB
|
||||
err = m.save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// associate simulator with simulation model
|
||||
var simltr simulator.Simulator
|
||||
err = simltr.ByID(m.SimulatorID)
|
||||
err = db.Model(m).Association("Simulator").Append(&simltr).Error
|
||||
|
||||
// associate simulation model with simulation
|
||||
err = db.Model(&sim).Association("SimulationModels").Append(m).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *SimulationModel) update(modifiedSimulationModel SimulationModel) error {
|
||||
db := common.GetDB()
|
||||
|
||||
if m.SimulatorID != modifiedSimulationModel.SimulatorID {
|
||||
// update simulator
|
||||
var s simulator.Simulator
|
||||
err := s.ByID(modifiedSimulationModel.SimulatorID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = db.Model(m).Association("Simulator").Replace(s).Error
|
||||
|
||||
}
|
||||
|
||||
err := db.Model(m).Updates(map[string]interface{}{"Name": modifiedSimulationModel.Name,
|
||||
"OutputLength": modifiedSimulationModel.OutputLength,
|
||||
"InputLength": modifiedSimulationModel.InputLength,
|
||||
"StartParameters": modifiedSimulationModel.StartParameters,
|
||||
"SimulatorID": modifiedSimulationModel.SimulatorID,
|
||||
}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *SimulationModel) delete() error {
|
||||
|
||||
db := common.GetDB()
|
||||
var sim simulation.Simulation
|
||||
err := sim.ByID(m.SimulationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove association between SimulationModel and Simulation
|
||||
// SimulationModel itself is not deleted from DB, it remains as "dangling"
|
||||
err = db.Model(&sim).Association("SimulationModels").Delete(m).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *SimulationModel) addSignal(signal common.Signal) error {
|
||||
|
||||
db := common.GetDB()
|
||||
var err error
|
||||
|
||||
if signal.Direction == "in" {
|
||||
err = db.Model(m).Association("InputMapping").Append(signal).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// adapt length of mapping
|
||||
m.InputLength = db.Model(m).Where("Direction = ?", "in").Association("InputMapping").Count()
|
||||
err = m.update(*m)
|
||||
|
||||
} else {
|
||||
err = db.Model(m).Association("OutputMapping").Append(signal).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// adapt length of mapping
|
||||
m.OutputLength = db.Model(m).Where("Direction = ?", "out").Association("OutputMapping").Count()
|
||||
err = m.update(*m)
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *SimulationModel) deleteSignals(direction string) error {
|
||||
|
||||
db := common.GetDB()
|
||||
var err error
|
||||
|
||||
var columnName string
|
||||
|
||||
if direction == "in" {
|
||||
columnName = "InputMapping"
|
||||
|
||||
} else {
|
||||
columnName = "OutputMapping"
|
||||
}
|
||||
|
||||
var signals []common.Signal
|
||||
err = db.Order("ID asc").Model(m).Where("Direction = ?", direction).Related(&signals, columnName).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove association to each signal and delete each signal from db
|
||||
for _, sig := range signals {
|
||||
err = db.Model(m).Association(columnName).Delete(sig).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = db.Delete(sig).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// set length of mapping to 0
|
||||
if columnName == "InputMapping" {
|
||||
m.InputLength = 0
|
||||
} else {
|
||||
m.OutputLength = 0
|
||||
}
|
||||
err = m.update(*m)
|
||||
|
||||
return err
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package simulationmodel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
|
||||
)
|
||||
|
||||
func checkPermissions(c *gin.Context, operation common.CRUD) (bool, SimulationModel) {
|
||||
|
||||
var m SimulationModel
|
||||
|
||||
modelID, err := strconv.Atoi(c.Param("modelID"))
|
||||
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of model ID in path")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return false, m
|
||||
}
|
||||
|
||||
err = m.ByID(uint(modelID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return false, m
|
||||
}
|
||||
|
||||
ok, _ := simulation.CheckPermissions(c, common.ModelSimulationModel, operation, "body", int(m.SimulationID))
|
||||
if !ok {
|
||||
return false, m
|
||||
}
|
||||
|
||||
return true, m
|
||||
}
|
190
routes/simulationmodel/simulationmodel_endpoints.go
Normal file
190
routes/simulationmodel/simulationmodel_endpoints.go
Normal file
|
@ -0,0 +1,190 @@
|
|||
package simulationmodel
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/scenario"
|
||||
)
|
||||
|
||||
func RegisterSimulationModelEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("", getSimulationModels)
|
||||
r.POST("", addSimulationModel)
|
||||
r.PUT("/:modelID", updateSimulationModel)
|
||||
r.GET("/:modelID", getSimulationModel)
|
||||
r.DELETE("/:modelID", deleteSimulationModel)
|
||||
}
|
||||
|
||||
// getSimulationModels godoc
|
||||
// @Summary Get all simulation models of scenario
|
||||
// @ID getSimulationModels
|
||||
// @Produce json
|
||||
// @Tags models
|
||||
// @Success 200 {array} common.SimulationModelResponse "Array of models to which belong to scenario"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param scenarioID query int true "Scenario ID"
|
||||
// @Router /models [get]
|
||||
func getSimulationModels(c *gin.Context) {
|
||||
|
||||
ok, so := scenario.CheckPermissions(c, common.Read, "query", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
var models []common.SimulationModel
|
||||
err := db.Order("ID asc").Model(so).Related(&models, "Models").Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.SimulationModelsSerializer{c, models}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"models": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// addSimulationModel godoc
|
||||
// @Summary Add a simulation model to a scenario
|
||||
// @ID addSimulationModel
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags models
|
||||
// @Param inputSimulationModel body common.ResponseMsgSimulationModel true "Simulation model to be added incl. IDs of scenario and simulator"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /models [post]
|
||||
func addSimulationModel(c *gin.Context) {
|
||||
|
||||
var newModelData common.ResponseMsgSimulationModel
|
||||
err := c.BindJSON(&newModelData)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var newModel SimulationModel
|
||||
newModel.ID = newModelData.SimulationModel.ID
|
||||
newModel.Name = newModelData.SimulationModel.Name
|
||||
newModel.SimulatorID = newModelData.SimulationModel.SimulatorID
|
||||
newModel.ScenarioID = newModelData.SimulationModel.ScenarioID
|
||||
newModel.StartParameters = newModelData.SimulationModel.StartParameters
|
||||
newModel.OutputLength = 0
|
||||
newModel.InputLength = 0
|
||||
|
||||
ok, _ := scenario.CheckPermissions(c, common.Create, "body", int(newModel.ScenarioID))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
err = newModel.addToScenario()
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// updateSimulationModel godoc
|
||||
// @Summary Update a simulation model
|
||||
// @ID updateSimulationModel
|
||||
// @Tags models
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param inputSimulationModel body common.ResponseMsgSimulationModel true "Simulation model to be updated"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param modelID path int true "Model ID"
|
||||
// @Router /models/{modelID} [put]
|
||||
func updateSimulationModel(c *gin.Context) {
|
||||
|
||||
ok, m := CheckPermissions(c, common.Update, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var modifiedModel common.ResponseMsgSimulationModel
|
||||
err := c.BindJSON(&modifiedModel)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = m.Update(modifiedModel.SimulationModel)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getSimulationModel godoc
|
||||
// @Summary Get a simulation model
|
||||
// @ID getSimulationModel
|
||||
// @Tags models
|
||||
// @Produce json
|
||||
// @Success 200 {object} common.SimulationModelResponse "Requested simulation model."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param modelID path int true "Model ID"
|
||||
// @Router /models/{modelID} [get]
|
||||
func getSimulationModel(c *gin.Context) {
|
||||
|
||||
ok, m := CheckPermissions(c, common.Read, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.SimulationModelSerializer{c, m.SimulationModel}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"model": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// deleteSimulationModel godoc
|
||||
// @Summary Delete a simulation model
|
||||
// @ID deleteSimulationModel
|
||||
// @Tags models
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param modelID path int true "Model ID"
|
||||
// @Router /models/{modelID} [delete]
|
||||
func deleteSimulationModel(c *gin.Context) {
|
||||
|
||||
ok, m := CheckPermissions(c, common.Delete, "path", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
err := m.delete()
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
110
routes/simulationmodel/simulationmodel_methods.go
Normal file
110
routes/simulationmodel/simulationmodel_methods.go
Normal file
|
@ -0,0 +1,110 @@
|
|||
package simulationmodel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/scenario"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator"
|
||||
)
|
||||
|
||||
type SimulationModel struct {
|
||||
common.SimulationModel
|
||||
}
|
||||
|
||||
func (m *SimulationModel) save() error {
|
||||
db := common.GetDB()
|
||||
err := db.Create(m).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *SimulationModel) ByID(id uint) error {
|
||||
db := common.GetDB()
|
||||
err := db.Find(m, id).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("Simulation Model with id=%v does not exist", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SimulationModel) addToScenario() error {
|
||||
db := common.GetDB()
|
||||
var so scenario.Scenario
|
||||
err := so.ByID(m.ScenarioID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save simulation model to DB
|
||||
err = m.save()
|
||||
if err != nil {
|
||||
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
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// associate simulation model with scenario
|
||||
err = db.Model(&so).Association("SimulationModels").Append(m).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *SimulationModel) Update(modifiedSimulationModel common.SimulationModelResponse) error {
|
||||
db := common.GetDB()
|
||||
|
||||
if m.SimulatorID != modifiedSimulationModel.SimulatorID {
|
||||
// update simulator
|
||||
var s simulator.Simulator
|
||||
var s_old simulator.Simulator
|
||||
err := s.ByID(modifiedSimulationModel.SimulatorID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s_old.ByID(m.SimulatorID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// remove simulation model from old simulator
|
||||
err = db.Model(&s_old).Association("SimulationModels").Delete(m).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// add simulation model to new simulator
|
||||
err = db.Model(&s).Association("SimulationModels").Append(m).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := db.Model(m).Updates(map[string]interface{}{
|
||||
"Name": modifiedSimulationModel.Name,
|
||||
"OutputLength": modifiedSimulationModel.OutputLength,
|
||||
"InputLength": modifiedSimulationModel.InputLength,
|
||||
"StartParameters": modifiedSimulationModel.StartParameters,
|
||||
"SimulatorID": modifiedSimulationModel.SimulatorID,
|
||||
}).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *SimulationModel) delete() error {
|
||||
|
||||
db := common.GetDB()
|
||||
var so scenario.Scenario
|
||||
err := so.ByID(m.ScenarioID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove association between SimulationModel and Scenario
|
||||
// SimulationModel itself is not deleted from DB, it remains as "dangling"
|
||||
err = db.Model(&so).Association("SimulationModels").Delete(m).Error
|
||||
|
||||
return err
|
||||
}
|
58
routes/simulationmodel/simulationmodel_middleware.go
Normal file
58
routes/simulationmodel/simulationmodel_middleware.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package simulationmodel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/scenario"
|
||||
)
|
||||
|
||||
func CheckPermissions(c *gin.Context, operation common.CRUD, modelIDSource string, modelIDBody int) (bool, SimulationModel) {
|
||||
|
||||
var m SimulationModel
|
||||
|
||||
err := common.ValidateRole(c, common.ModelSimulationModel, operation)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return false, m
|
||||
}
|
||||
|
||||
var modelID int
|
||||
if modelIDSource == "path" {
|
||||
modelID, err = strconv.Atoi(c.Param("modelID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of modelID path parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return false, m
|
||||
}
|
||||
} else if modelIDSource == "query" {
|
||||
modelID, err = strconv.Atoi(c.Request.URL.Query().Get("modelID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of modelID query parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return false, m
|
||||
}
|
||||
} else if modelIDSource == "body" {
|
||||
modelID = modelIDBody
|
||||
}
|
||||
|
||||
err = m.ByID(uint(modelID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return false, m
|
||||
}
|
||||
|
||||
ok, _ := scenario.CheckPermissions(c, operation, "body", int(m.ScenarioID))
|
||||
if !ok {
|
||||
return false, m
|
||||
}
|
||||
|
||||
return true, m
|
||||
}
|
|
@ -10,206 +10,16 @@ import (
|
|||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
|
||||
)
|
||||
|
||||
var token string
|
||||
|
||||
type credentials struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
var cred = credentials{
|
||||
Username: "User_A",
|
||||
Password: "abc123",
|
||||
}
|
||||
|
||||
var msgOK = common.ResponseMsg{
|
||||
Message: "OK.",
|
||||
}
|
||||
|
||||
var modelA = common.SimulationModelResponse{
|
||||
ID: 1,
|
||||
Name: "SimulationModel_A",
|
||||
OutputLength: 1,
|
||||
InputLength: 1,
|
||||
SimulationID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParams: "",
|
||||
}
|
||||
|
||||
var modelAUpdated = common.SimulationModelResponse{
|
||||
ID: 1,
|
||||
Name: "SimulationModel_A",
|
||||
OutputLength: 1,
|
||||
InputLength: 3,
|
||||
SimulationID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParams: "",
|
||||
}
|
||||
|
||||
var modelAUpdated2 = common.SimulationModelResponse{
|
||||
ID: 1,
|
||||
Name: "SimulationModel_A",
|
||||
OutputLength: 1,
|
||||
InputLength: 0,
|
||||
SimulationID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParams: "",
|
||||
}
|
||||
|
||||
var modelB = common.SimulationModelResponse{
|
||||
ID: 2,
|
||||
Name: "SimulationModel_B",
|
||||
OutputLength: 1,
|
||||
InputLength: 1,
|
||||
SimulationID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParams: "",
|
||||
}
|
||||
|
||||
var modelC = common.SimulationModel{
|
||||
ID: 3,
|
||||
Name: "SimulationModel_C",
|
||||
OutputLength: 1,
|
||||
InputLength: 1,
|
||||
SimulationID: 1,
|
||||
SimulatorID: 1,
|
||||
StartParameters: "test",
|
||||
InputMapping: nil,
|
||||
OutputMapping: nil,
|
||||
}
|
||||
|
||||
var modelCupdated = common.SimulationModel{
|
||||
ID: modelC.ID,
|
||||
Name: "SimulationModel_CUpdated",
|
||||
OutputLength: modelC.OutputLength,
|
||||
InputLength: modelC.InputLength,
|
||||
SimulationID: modelC.SimulationID,
|
||||
SimulatorID: 2,
|
||||
StartParameters: modelC.StartParameters,
|
||||
InputMapping: modelC.InputMapping,
|
||||
OutputMapping: modelC.OutputMapping,
|
||||
}
|
||||
|
||||
var modelC_response = common.SimulationModelResponse{
|
||||
ID: modelC.ID,
|
||||
Name: modelC.Name,
|
||||
InputLength: modelC.InputLength,
|
||||
OutputLength: modelC.OutputLength,
|
||||
SimulationID: modelC.SimulationID,
|
||||
SimulatorID: modelC.SimulatorID,
|
||||
StartParams: modelC.StartParameters,
|
||||
}
|
||||
|
||||
var modelC_responseUpdated = common.SimulationModelResponse{
|
||||
ID: modelC.ID,
|
||||
Name: modelCupdated.Name,
|
||||
InputLength: modelC.InputLength,
|
||||
OutputLength: modelC.OutputLength,
|
||||
SimulationID: modelC.SimulationID,
|
||||
SimulatorID: modelCupdated.SimulatorID,
|
||||
StartParams: modelC.StartParameters,
|
||||
}
|
||||
|
||||
var myModels = []common.SimulationModelResponse{
|
||||
modelA,
|
||||
modelB,
|
||||
}
|
||||
|
||||
var msgModels = common.ResponseMsgSimulationModels{
|
||||
SimulationModels: myModels,
|
||||
}
|
||||
|
||||
var msgModel = common.ResponseMsgSimulationModel{
|
||||
SimulationModel: modelC_response,
|
||||
}
|
||||
|
||||
var msgModelAUpdated = common.ResponseMsgSimulationModel{
|
||||
SimulationModel: modelAUpdated,
|
||||
}
|
||||
|
||||
var msgModelAUpdated2 = common.ResponseMsgSimulationModel{
|
||||
SimulationModel: modelAUpdated2,
|
||||
}
|
||||
|
||||
var msgModelupdated = common.ResponseMsgSimulationModel{
|
||||
SimulationModel: modelC_responseUpdated,
|
||||
}
|
||||
|
||||
var inSignalA = common.SignalResponse{
|
||||
Name: "inSignal_A",
|
||||
Direction: "in",
|
||||
Index: 0,
|
||||
Unit: "A",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var inSignalB = common.SignalResponse{
|
||||
Name: "inSignal_B",
|
||||
Direction: "in",
|
||||
Index: 1,
|
||||
Unit: "A",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var inSignalC = common.SignalResponse{
|
||||
Name: "inSignal_C",
|
||||
Direction: "in",
|
||||
Index: 2,
|
||||
Unit: "A",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var outSignalA = common.SignalResponse{
|
||||
Name: "outSignal_A",
|
||||
Direction: "out",
|
||||
Index: 0,
|
||||
Unit: "V",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var outSignalB = common.SignalResponse{
|
||||
Name: "outSignal_B",
|
||||
Direction: "out",
|
||||
Index: 1,
|
||||
Unit: "V",
|
||||
SimulationModelID: 1,
|
||||
}
|
||||
|
||||
var myInSignals = []common.SignalResponse{
|
||||
inSignalA,
|
||||
inSignalB,
|
||||
}
|
||||
|
||||
var myInSignalsUpdated = []common.SignalResponse{
|
||||
inSignalA,
|
||||
inSignalB,
|
||||
inSignalC,
|
||||
}
|
||||
|
||||
var myOutSignals = []common.SignalResponse{
|
||||
outSignalA,
|
||||
outSignalB,
|
||||
}
|
||||
|
||||
var msgSignalsEmpty = common.ResponseMsgSignals{
|
||||
Signals: []common.SignalResponse{},
|
||||
}
|
||||
|
||||
var msgInSignals = common.ResponseMsgSignals{
|
||||
Signals: myInSignals,
|
||||
}
|
||||
|
||||
var msgInSignalsUpdated = common.ResponseMsgSignals{
|
||||
Signals: myInSignalsUpdated,
|
||||
}
|
||||
|
||||
var msgOutSignals = common.ResponseMsgSignals{
|
||||
Signals: myOutSignals,
|
||||
}
|
||||
|
||||
// Test /models endpoints
|
||||
func TestSimulationModelEndpoints(t *testing.T) {
|
||||
|
||||
var token string
|
||||
|
||||
var myModels = []common.SimulationModelResponse{common.SimulationModelA_response, common.SimulationModelB_response}
|
||||
var msgModels = common.ResponseMsgSimulationModels{SimulationModels: myModels}
|
||||
var msgModel = common.ResponseMsgSimulationModel{SimulationModel: common.SimulationModelC_response}
|
||||
var msgModelupdated = common.ResponseMsgSimulationModel{SimulationModel: common.SimulationModelCUpdated_response}
|
||||
|
||||
db := common.DummyInitDB()
|
||||
defer db.Close()
|
||||
common.DummyPopulateDB(db)
|
||||
|
@ -225,113 +35,30 @@ func TestSimulationModelEndpoints(t *testing.T) {
|
|||
|
||||
RegisterSimulationModelEndpoints(api.Group("/models"))
|
||||
|
||||
credjson, err := json.Marshal(cred)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgOKjson, err := json.Marshal(msgOK)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgModelsjson, err := json.Marshal(msgModels)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgModeljson, err := json.Marshal(msgModel)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgModelupdatedjson, err := json.Marshal(msgModelupdated)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
modelCjson, err := json.Marshal(modelC)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
modelCupdatedjson, err := json.Marshal(modelCupdated)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgModelAUpdatedjson, err := json.Marshal(msgModelAUpdated)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgModelAUpdated2json, err := json.Marshal(msgModelAUpdated2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgSignalsEmptyjson, err := json.Marshal(msgSignalsEmpty)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgInSignalsjson, err := json.Marshal(msgInSignals)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgInSignalsUpdatedjson, err := json.Marshal(msgInSignalsUpdated)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
msgOutSignalsjson, err := json.Marshal(msgOutSignals)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
inSignalCjson, err := json.Marshal(inSignalC)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
credjson, _ := json.Marshal(common.CredUser)
|
||||
msgOKjson, _ := json.Marshal(common.MsgOK)
|
||||
msgModelsjson, _ := json.Marshal(msgModels)
|
||||
msgModeljson, _ := json.Marshal(msgModel)
|
||||
msgModelupdatedjson, _ := json.Marshal(msgModelupdated)
|
||||
|
||||
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
|
||||
|
||||
// test GET models
|
||||
common.TestEndpoint(t, router, token, "/api/models?simulationID=1", "GET", nil, 200, string(msgModelsjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models?scenarioID=1", "GET", nil, 200, msgModelsjson)
|
||||
|
||||
// test POST models
|
||||
common.TestEndpoint(t, router, token, "/api/models", "POST", modelCjson, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models", "POST", msgModeljson, 200, msgOKjson)
|
||||
|
||||
// test GET models/:ModelID to check if previous POST worked correctly
|
||||
common.TestEndpoint(t, router, token, "/api/models/3", "GET", nil, 200, string(msgModeljson))
|
||||
common.TestEndpoint(t, router, token, "/api/models/3", "GET", nil, 200, msgModeljson)
|
||||
|
||||
// test PUT models/:ModelID
|
||||
common.TestEndpoint(t, router, token, "/api/models/3", "PUT", modelCupdatedjson, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models/3", "GET", nil, 200, string(msgModelupdatedjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models/3", "PUT", msgModelupdatedjson, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/models/3", "GET", nil, 200, msgModelupdatedjson)
|
||||
|
||||
// test DELETE models/:ModelID
|
||||
common.TestEndpoint(t, router, token, "/api/models/3", "DELETE", nil, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models?simulationID=1", "GET", nil, 200, string(msgModelsjson))
|
||||
|
||||
// test GET models/:ModelID/signals
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals?direction=in", "GET", nil, 200, string(msgInSignalsjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals?direction=out", "GET", nil, 200, string(msgOutSignalsjson))
|
||||
|
||||
// test PUT models/:ModelID/signals
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals", "PUT", inSignalCjson, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals?direction=in", "GET", nil, 200, string(msgInSignalsUpdatedjson))
|
||||
|
||||
// test GET models/:ModelID to check if PUT adapted InputLength correctly
|
||||
common.TestEndpoint(t, router, token, "/api/models/1", "GET", nil, 200, string(msgModelAUpdatedjson))
|
||||
|
||||
// test DELETE models/:ModelID/signals
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals?direction=in", "DELETE", nil, 200, string(msgOKjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals?direction=in", "GET", nil, 200, string(msgSignalsEmptyjson))
|
||||
common.TestEndpoint(t, router, token, "/api/models/1/signals?direction=out", "GET", nil, 200, string(msgOutSignalsjson))
|
||||
|
||||
// test GET models/:ModelID to check if DELETE adapted InputLength correctly
|
||||
common.TestEndpoint(t, router, token, "/api/models/1", "GET", nil, 200, string(msgModelAUpdated2json))
|
||||
common.TestEndpoint(t, router, token, "/api/models/3", "DELETE", nil, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/models?scenarioID=1", "GET", nil, 200, msgModelsjson)
|
||||
|
||||
// TODO add testing for other return codes
|
||||
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
package simulator
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
)
|
||||
|
||||
func RegisterSimulatorEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("", GetSimulators)
|
||||
r.POST("", AddSimulator)
|
||||
r.PUT("/:simulatorID", UpdateSimulator)
|
||||
r.GET("/:simulatorID", GetSimulator)
|
||||
r.DELETE("/:simulatorID", DeleteSimulator)
|
||||
r.POST("/:simulatorID/action", SendActionToSimulator)
|
||||
}
|
||||
|
||||
// GetSimulators godoc
|
||||
// @Summary Get all simulators
|
||||
// @ID GetSimulators
|
||||
// @Tags simulators
|
||||
// @Produce json
|
||||
// @Success 200 {array} common.SimulatorResponse "Simulator parameters requested by user"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /simulators [get]
|
||||
func GetSimulators(c *gin.Context) {
|
||||
db := common.GetDB()
|
||||
var simulators []common.Simulator
|
||||
err := db.Order("ID asc").Find(&simulators).Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
serializer := common.SimulatorsSerializer{c, simulators}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"simulators": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// AddSimulator godoc
|
||||
// @Summary Add a simulator
|
||||
// @ID AddSimulator
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags simulators
|
||||
// @Param inputSimulator body common.SimulatorResponse true "Simulator to be added"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /simulators [post]
|
||||
func AddSimulator(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "NOT implemented",
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateSimulator godoc
|
||||
// @Summary Update a simulator
|
||||
// @ID UpdateSimulator
|
||||
// @Tags simulators
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param inputSimulator body common.SimulatorResponse true "Simulator to be updated"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID} [put]
|
||||
func UpdateSimulator(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "NOT implemented",
|
||||
})
|
||||
}
|
||||
|
||||
// GetSimulator godoc
|
||||
// @Summary Get simulator
|
||||
// @ID GetSimulator
|
||||
// @Produce json
|
||||
// @Tags simulators
|
||||
// @Success 200 {object} common.SimulatorResponse "Simulator requested by user"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID} [get]
|
||||
func GetSimulator(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "NOT implemented",
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteSimulator godoc
|
||||
// @Summary Delete a simulator
|
||||
// @ID DeleteSimulator
|
||||
// @Tags simulators
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID} [delete]
|
||||
func DeleteSimulator(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "NOT implemented",
|
||||
})
|
||||
}
|
||||
|
||||
// SendActionToSimulator godoc
|
||||
// @Summary Send an action to simulator
|
||||
// @ID SendActionToSimulator
|
||||
// @Tags simulators
|
||||
// @Produce json
|
||||
// @Param inputAction query string true "Action for simulator"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID}/action [post]
|
||||
func SendActionToSimulator(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "NOT implemented",
|
||||
})
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package simulator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
)
|
||||
|
||||
type Simulator struct {
|
||||
common.Simulator
|
||||
}
|
||||
|
||||
func (s *Simulator) save() error {
|
||||
db := common.GetDB()
|
||||
err := db.Create(s).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Simulator) ByID(id uint) error {
|
||||
db := common.GetDB()
|
||||
err := db.Find(s, id).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("Simulator with id=%v does not exist", id)
|
||||
}
|
||||
return nil
|
||||
}
|
363
routes/simulator/simulator_endpoints.go
Normal file
363
routes/simulator/simulator_endpoints.go
Normal file
|
@ -0,0 +1,363 @@
|
|||
package simulator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
)
|
||||
|
||||
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)
|
||||
// register action endpoint only if AMQP client is used
|
||||
if common.WITH_AMQP == true {
|
||||
r.POST("/:simulatorID/action", sendActionToSimulator)
|
||||
}
|
||||
}
|
||||
|
||||
// getSimulators godoc
|
||||
// @Summary Get all simulators
|
||||
// @ID getSimulators
|
||||
// @Tags simulators
|
||||
// @Produce json
|
||||
// @Success 200 {array} common.ResponseMsgSimulators "Simulator parameters requested by user"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /simulators [get]
|
||||
func getSimulators(c *gin.Context) {
|
||||
|
||||
err := common.ValidateRole(c, common.ModelSimulator, common.Read)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
var simulators []common.Simulator
|
||||
err = db.Order("ID asc").Find(&simulators).Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
serializer := common.SimulatorsSerializer{c, simulators}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"simulators": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// addSimulator godoc
|
||||
// @Summary Add a simulator
|
||||
// @ID addSimulator
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags simulators
|
||||
// @Param inputSimulator body common.ResponseMsgSimulator true "Simulator to be added"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /simulators [post]
|
||||
func addSimulator(c *gin.Context) {
|
||||
|
||||
err := common.ValidateRole(c, common.ModelSimulator, common.Create)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return
|
||||
}
|
||||
|
||||
var newSimulatorData common.ResponseMsgSimulator
|
||||
err = c.BindJSON(&newSimulatorData)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var newSimulator Simulator
|
||||
newSimulator.ID = newSimulatorData.Simulator.ID
|
||||
newSimulator.State = newSimulatorData.Simulator.State
|
||||
newSimulator.StateUpdateAt = newSimulatorData.Simulator.StateUpdateAt
|
||||
newSimulator.Modeltype = newSimulatorData.Simulator.Modeltype
|
||||
newSimulator.UUID = newSimulatorData.Simulator.UUID
|
||||
newSimulator.Uptime = newSimulatorData.Simulator.Uptime
|
||||
newSimulator.Host = newSimulatorData.Simulator.Host
|
||||
newSimulator.RawProperties = newSimulatorData.Simulator.RawProperties
|
||||
newSimulator.Properties = newSimulatorData.Simulator.Properties
|
||||
|
||||
err = newSimulator.save()
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// updateSimulator godoc
|
||||
// @Summary Update a simulator
|
||||
// @ID updateSimulator
|
||||
// @Tags simulators
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param inputSimulator body common.ResponseMsgSimulator true "Simulator to be updated"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID} [put]
|
||||
func updateSimulator(c *gin.Context) {
|
||||
err := common.ValidateRole(c, common.ModelSimulator, common.Update)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return
|
||||
}
|
||||
|
||||
var modifiedSimulator common.ResponseMsgSimulator
|
||||
|
||||
err = c.BindJSON(&modifiedSimulator)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error unmarshalling data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
simulatorID, err := strconv.Atoi(c.Param("simulatorID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var s Simulator
|
||||
err = s.ByID(uint(simulatorID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = s.update(modifiedSimulator.Simulator)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getSimulator godoc
|
||||
// @Summary Get simulator
|
||||
// @ID getSimulator
|
||||
// @Produce json
|
||||
// @Tags simulators
|
||||
// @Success 200 {object} common.ResponseMsgSimulator "Simulator requested by user"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID} [get]
|
||||
func getSimulator(c *gin.Context) {
|
||||
|
||||
err := common.ValidateRole(c, common.ModelSimulator, common.Read)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return
|
||||
}
|
||||
|
||||
simulatorID, err := strconv.Atoi(c.Param("simulatorID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var s Simulator
|
||||
err = s.ByID(uint(simulatorID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.SimulatorSerializer{c, s.Simulator}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"simulator": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// deleteSimulator godoc
|
||||
// @Summary Delete a simulator
|
||||
// @ID deleteSimulator
|
||||
// @Tags simulators
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID} [delete]
|
||||
func deleteSimulator(c *gin.Context) {
|
||||
|
||||
err := common.ValidateRole(c, common.ModelSimulator, common.Delete)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return
|
||||
}
|
||||
|
||||
simulatorID, err := strconv.Atoi(c.Param("simulatorID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var s Simulator
|
||||
err = s.ByID(uint(simulatorID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = s.delete()
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
|
||||
// getModelsOfSimulator godoc
|
||||
// @Summary Get all simulation models in which the simulator is used
|
||||
// @ID getModelsOfSimulator
|
||||
// @Tags simulators
|
||||
// @Produce json
|
||||
// @Success 200 {object} common.ResponseMsgSimulationModels "Simulation models requested by user"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID}/models [get]
|
||||
func getModelsOfSimulator(c *gin.Context) {
|
||||
|
||||
err := common.ValidateRole(c, common.ModelSimulator, common.Read)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return
|
||||
}
|
||||
|
||||
simulatorID, err := strconv.Atoi(c.Param("simulatorID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var s Simulator
|
||||
err = s.ByID(uint(simulatorID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// get all associated simulation models
|
||||
allModels, _, err := s.getModels()
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.SimulationModelsSerializer{c, allModels}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"models": serializer.Response(),
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// sendActionToSimulator godoc
|
||||
// @Summary Send an action to simulator (only available if backend server is started with -amqp parameter)
|
||||
// @ID sendActionToSimulator
|
||||
// @Tags simulators
|
||||
// @Produce json
|
||||
// @Param inputAction query string true "Action for simulator"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulatorID path int true "Simulator ID"
|
||||
// @Router /simulators/{simulatorID}/action [post]
|
||||
func sendActionToSimulator(c *gin.Context) {
|
||||
|
||||
err := common.ValidateRole(c, common.ModelSimulatorAction, common.Update)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return
|
||||
}
|
||||
|
||||
simulatorID, err := strconv.Atoi(c.Param("simulatorID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulatorID path parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var actions []common.Action
|
||||
err = c.BindJSON(&actions)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var s Simulator
|
||||
err = s.ByID(uint(simulatorID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
for _, action := range actions {
|
||||
if action.When == 0 {
|
||||
action.When = float32(now.Unix())
|
||||
}
|
||||
|
||||
err = common.SendActionAMQP(action, s.UUID)
|
||||
if err != nil {
|
||||
errormsg := "Internal Server Error. Unable to send actions to simulator: " + err.Error()
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
56
routes/simulator/simulator_methods.go
Normal file
56
routes/simulator/simulator_methods.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package simulator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
)
|
||||
|
||||
type Simulator struct {
|
||||
common.Simulator
|
||||
}
|
||||
|
||||
func (s *Simulator) save() error {
|
||||
db := common.GetDB()
|
||||
err := db.Create(s).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Simulator) ByID(id uint) error {
|
||||
db := common.GetDB()
|
||||
err := db.Find(s, id).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("Simulator with id=%v does not exist", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Simulator) update(modifiedSimulator common.SimulatorResponse) error {
|
||||
|
||||
db := common.GetDB()
|
||||
err := db.Model(s).Updates(modifiedSimulator).Error
|
||||
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
func (s *Simulator) delete() error {
|
||||
db := common.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)")
|
||||
}
|
||||
|
||||
// delete Simulator from DB (does NOT remain as dangling)
|
||||
err := db.Delete(s).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Simulator) getModels() ([]common.SimulationModel, int, error) {
|
||||
db := common.GetDB()
|
||||
var models []common.SimulationModel
|
||||
err := db.Order("ID asc").Model(s).Related(&models, "SimulationModels").Error
|
||||
return models, len(models), err
|
||||
}
|
71
routes/simulator/simulator_test.go
Normal file
71
routes/simulator/simulator_test.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package simulator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
|
||||
)
|
||||
|
||||
// Test /simulator endpoints
|
||||
func TestSimulatorEndpoints(t *testing.T) {
|
||||
|
||||
var token string
|
||||
|
||||
var myModels = []common.SimulationModelResponse{common.SimulationModelA_response, common.SimulationModelB_response}
|
||||
var msgModels = common.ResponseMsgSimulationModels{SimulationModels: myModels}
|
||||
var simulatorC_msg = common.ResponseMsgSimulator{Simulator: common.SimulatorC_response}
|
||||
var simulatorCupdated_msg = common.ResponseMsgSimulator{Simulator: common.SimulatorCUpdated_response}
|
||||
var mySimulators = []common.SimulatorResponse{common.SimulatorA_response, common.SimulatorB_response}
|
||||
var msgSimulators = common.ResponseMsgSimulators{Simulators: mySimulators}
|
||||
|
||||
db := common.DummyInitDB()
|
||||
defer db.Close()
|
||||
common.DummyPopulateDB(db)
|
||||
|
||||
router := gin.Default()
|
||||
api := router.Group("/api")
|
||||
|
||||
// All endpoints require authentication except when someone wants to
|
||||
// login (POST /authenticate)
|
||||
user.VisitorAuthenticate(api.Group("/authenticate"))
|
||||
|
||||
api.Use(user.Authentication(true))
|
||||
|
||||
RegisterSimulatorEndpoints(api.Group("/simulators"))
|
||||
|
||||
credjson, _ := json.Marshal(common.CredAdmin)
|
||||
msgOKjson, _ := json.Marshal(common.MsgOK)
|
||||
msgModelsjson, _ := json.Marshal(msgModels)
|
||||
msgSimulatorsjson, _ := json.Marshal(msgSimulators)
|
||||
msgSimulatorjson, _ := json.Marshal(simulatorC_msg)
|
||||
simulatorCjson, _ := json.Marshal(simulatorC_msg)
|
||||
simulatorCupdatedjson, _ := json.Marshal(simulatorCupdated_msg)
|
||||
|
||||
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
|
||||
|
||||
// test GET simulators/
|
||||
common.TestEndpoint(t, router, token, "/api/simulators", "GET", nil, 200, msgSimulatorsjson)
|
||||
|
||||
// test POST simulators/
|
||||
common.TestEndpoint(t, router, token, "/api/simulators", "POST", simulatorCjson, 200, msgOKjson)
|
||||
|
||||
// test GET simulators/:SimulatorID
|
||||
common.TestEndpoint(t, router, token, "/api/simulators/3", "GET", nil, 200, msgSimulatorjson)
|
||||
|
||||
// test PUT simulators/:SimulatorID
|
||||
common.TestEndpoint(t, router, token, "/api/simulators/3", "PUT", simulatorCupdatedjson, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/simulators/3", "GET", nil, 200, simulatorCupdatedjson)
|
||||
|
||||
// test DELETE simulators/:SimulatorID
|
||||
common.TestEndpoint(t, router, token, "/api/simulators/3", "DELETE", nil, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/simulators", "GET", nil, 200, msgSimulatorsjson)
|
||||
|
||||
// test GET simulators/:SimulatorID/models
|
||||
common.TestEndpoint(t, router, token, "/api/simulators/1/models", "GET", nil, 200, msgModelsjson)
|
||||
|
||||
// TODO add tests for other return codes
|
||||
}
|
|
@ -21,9 +21,10 @@ type tokenClaims struct {
|
|||
}
|
||||
|
||||
type AuthResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Token string `json:"token"`
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Token string `json:"token"`
|
||||
User common.UserResponse `json:"user"`
|
||||
}
|
||||
|
||||
func VisitorAuthenticate(r *gin.RouterGroup) {
|
||||
|
@ -32,10 +33,10 @@ func VisitorAuthenticate(r *gin.RouterGroup) {
|
|||
|
||||
func RegisterUserEndpoints(r *gin.RouterGroup) {
|
||||
r.POST("", addUser)
|
||||
r.PUT("/:UserID", updateUser)
|
||||
r.PUT("/:userID", updateUser)
|
||||
r.GET("", getUsers)
|
||||
r.GET("/:UserID", getUser)
|
||||
r.DELETE("/:UserID", deleteUser)
|
||||
r.GET("/:userID", getUser)
|
||||
r.DELETE("/:userID", deleteUser)
|
||||
}
|
||||
|
||||
// authenticate godoc
|
||||
|
@ -123,10 +124,13 @@ func authenticate(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
serializer := common.UserSerializer{c, user.User}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Authenticated",
|
||||
"token": tokenString,
|
||||
"user": serializer.Response(false),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -242,7 +246,7 @@ func addUser(c *gin.Context) {
|
|||
// @Tags users
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param inputUser body common.UserResponse true "User to be updated"
|
||||
// @Param inputUser body common.User true "User to be updated (anything except for ID can be changed, role can only be change by admin)"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
|
@ -260,7 +264,7 @@ func updateUser(c *gin.Context) {
|
|||
|
||||
// Find the user
|
||||
var user User
|
||||
toBeUpdatedID, _ := common.UintParamFromCtx(c, "UserID")
|
||||
toBeUpdatedID, _ := common.UintParamFromCtx(c, "userID")
|
||||
err = user.ByID(toBeUpdatedID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, fmt.Sprintf("%v", err))
|
||||
|
@ -324,6 +328,15 @@ func updateUser(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
// To change the role of a user admin role is required
|
||||
if (updatedUser.Role != user.Role) && (userRole != "Admin") {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid authorization. User role can only be changed by Admin",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Finaly update the user
|
||||
err = user.update(updatedUser)
|
||||
if err != nil {
|
||||
|
@ -359,7 +372,7 @@ func getUser(c *gin.Context) {
|
|||
}
|
||||
|
||||
var user User
|
||||
id, _ := common.UintParamFromCtx(c, "UserID")
|
||||
id, _ := common.UintParamFromCtx(c, "userID")
|
||||
|
||||
err = user.ByID(id)
|
||||
if err != nil {
|
||||
|
@ -394,7 +407,7 @@ func deleteUser(c *gin.Context) {
|
|||
}
|
||||
|
||||
var user User
|
||||
id, _ := common.UintParamFromCtx(c, "UserID")
|
||||
id, _ := common.UintParamFromCtx(c, "userID")
|
||||
|
||||
// Check that the user exist
|
||||
err = user.ByID(uint(id))
|
|
@ -1,196 +0,0 @@
|
|||
package visualization
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
|
||||
)
|
||||
|
||||
func RegisterVisualizationEndpoints(r *gin.RouterGroup) {
|
||||
|
||||
r.GET("", getVisualizations)
|
||||
r.POST("", addVisualization)
|
||||
//r.POST("/:visualizationID", cloneVisualization)
|
||||
r.PUT("/:visualizationID", updateVisualization)
|
||||
r.GET("/:visualizationID", getVisualization)
|
||||
r.DELETE("/:visualizationID", deleteVisualization)
|
||||
|
||||
}
|
||||
|
||||
// getVisualizations godoc
|
||||
// @Summary Get all visualizations of simulation
|
||||
// @ID getVisualizations
|
||||
// @Produce json
|
||||
// @Tags visualizations
|
||||
// @Success 200 {array} common.VisualizationResponse "Array of visualizations to which belong to simulation"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param simulationID query int true "Simulation ID"
|
||||
// @Router /visualizations [get]
|
||||
func getVisualizations(c *gin.Context) {
|
||||
|
||||
simID, err := strconv.Atoi(c.Request.URL.Query().Get("simulationID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulationID query parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var sim simulation.Simulation
|
||||
err = sim.ByID(uint(simID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
var visualizations []common.Visualization
|
||||
err = db.Order("ID asc").Model(sim).Related(&visualizations, "Visualizations").Error
|
||||
|
||||
serializer := common.VisualizationsSerializer{c, visualizations}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"visualizations": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// addVisualization godoc
|
||||
// @Summary Add a visualization to a simulation
|
||||
// @ID addVisualization
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags visualizations
|
||||
// @Param inputVis body common.VisualizationResponse true "Visualization to be added incl. ID of simulation"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Router /visualizations [post]
|
||||
func addVisualization(c *gin.Context) {
|
||||
|
||||
var vis Visualization
|
||||
err := c.BindJSON(&vis)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// add visualization to DB and add association to simulation
|
||||
err = vis.addToSimulation(vis.SimulationID)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func cloneVisualization(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "NOT implemented",
|
||||
})
|
||||
}
|
||||
|
||||
// updateVisualization godoc
|
||||
// @Summary Update a visualization
|
||||
// @ID updateVisualization
|
||||
// @Tags visualizations
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param inputVis body common.VisualizationResponse true "Visualization to be updated"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param visualizationID path int true "Visualization ID"
|
||||
// @Router /visualizations/{visualizationID} [put]
|
||||
func updateVisualization(c *gin.Context) {
|
||||
|
||||
visID, err := common.GetVisualizationID(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var modifiedVis Visualization
|
||||
err = c.BindJSON(&modifiedVis)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var vis Visualization
|
||||
err = vis.ByID(uint(visID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = vis.update(modifiedVis)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getVisualization godoc
|
||||
// @Summary Get a visualization
|
||||
// @ID getVisualization
|
||||
// @Tags visualizations
|
||||
// @Produce json
|
||||
// @Success 200 {object} common.VisualizationResponse "Requested visualization."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param visualizationID path int true "Visualization ID"
|
||||
// @Router /visualizations/{visualizationID} [get]
|
||||
func getVisualization(c *gin.Context) {
|
||||
|
||||
visID, err := common.GetVisualizationID(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var vis Visualization
|
||||
err = vis.ByID(uint(visID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
serializer := common.VisualizationSerializer{c, vis.Visualization}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"visualization": serializer.Response(),
|
||||
})
|
||||
}
|
||||
|
||||
// deleteVisualization godoc
|
||||
// @Summary Delete a visualization
|
||||
// @ID deleteVisualization
|
||||
// @Tags visualizations
|
||||
// @Produce json
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param visualizationID path int true "Visualization ID"
|
||||
// @Router /visualizations/{visualizationID} [delete]
|
||||
func deleteVisualization(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "NOT implemented",
|
||||
})
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
package visualization
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
|
||||
)
|
||||
|
||||
type Visualization struct {
|
||||
common.Visualization
|
||||
}
|
||||
|
||||
func (v *Visualization) save() error {
|
||||
db := common.GetDB()
|
||||
err := db.Create(v).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (v *Visualization) ByID(id uint) error {
|
||||
db := common.GetDB()
|
||||
err := db.Find(v, id).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("Visualization with id=%v does not exist", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *Visualization) addToSimulation(simID uint) error {
|
||||
db := common.GetDB()
|
||||
var sim simulation.Simulation
|
||||
err := sim.ByID(simID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save visualization to DB
|
||||
err = v.save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// associate visualization with simulation
|
||||
err = db.Model(&sim).Association("Visualizations").Append(v).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (v *Visualization) update(modifiedVis Visualization) error {
|
||||
db := common.GetDB()
|
||||
err := db.Model(v).Update(modifiedVis).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
package widget
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization"
|
||||
)
|
||||
|
||||
type Widget struct {
|
||||
common.Widget
|
||||
}
|
||||
|
||||
func (w *Widget) save() error {
|
||||
db := common.GetDB()
|
||||
err := db.Create(w).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *Widget) ByID(id uint) error {
|
||||
db := common.GetDB()
|
||||
err := db.Find(w, id).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("Widget with id=%v does not exist", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Widget) addToVisualization(visID uint) error {
|
||||
db := common.GetDB()
|
||||
var vis visualization.Visualization
|
||||
err := vis.ByID(uint(visID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save visualization to DB
|
||||
err = w.save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// associate visualization with simulation
|
||||
err = db.Model(&vis).Association("Widgets").Append(w).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *Widget) update(modifiedWidget Widget) error {
|
||||
db := common.GetDB()
|
||||
err := db.Model(w).Update(modifiedWidget).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
|
@ -6,46 +6,39 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/dashboard"
|
||||
)
|
||||
|
||||
func RegisterWidgetEndpoints(r *gin.RouterGroup) {
|
||||
r.GET("", getWidgets)
|
||||
r.POST("", addWidget)
|
||||
//r.POST("/:widgetID", cloneWidget)
|
||||
r.PUT("/:widgetID", updateWidget)
|
||||
r.GET("/:widgetID", getWidget)
|
||||
r.DELETE("/:widgetID", deleteWidget)
|
||||
}
|
||||
|
||||
// getWidgets godoc
|
||||
// @Summary Get all widgets of visualization
|
||||
// @Summary Get all widgets of dashboard
|
||||
// @ID getWidgets
|
||||
// @Produce json
|
||||
// @Tags widgets
|
||||
// @Success 200 {array} common.WidgetResponse "Array of widgets to which belong to visualization"
|
||||
// @Success 200 {array} common.WidgetResponse "Array of widgets to which belong to dashboard"
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
// @Failure 404 "Not found"
|
||||
// @Failure 500 "Internal server error"
|
||||
// @Param visualizationID query int true "Visualization ID"
|
||||
// @Param dashboardID query int true "Dashboard ID"
|
||||
// @Router /widgets [get]
|
||||
func getWidgets(c *gin.Context) {
|
||||
|
||||
visID, err := common.GetVisualizationID(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var vis visualization.Visualization
|
||||
err = vis.ByID(uint(visID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
ok, dab := dashboard.CheckPermissions(c, common.Read, "query", -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
var widgets []common.Widget
|
||||
err = db.Order("ID asc").Model(vis).Related(&widgets, "Widgets").Error
|
||||
err := db.Order("ID asc").Model(dab).Related(&widgets, "Widgets").Error
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
@ -57,12 +50,12 @@ func getWidgets(c *gin.Context) {
|
|||
}
|
||||
|
||||
// addWidget godoc
|
||||
// @Summary Add a widget to a visualization
|
||||
// @Summary Add a widget to a dashboard
|
||||
// @ID addWidget
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags widgets
|
||||
// @Param inputWidget body common.WidgetResponse true "Widget to be added incl. ID of visualization"
|
||||
// @Param inputWidget body common.ResponseMsgWidget true "Widget to be added incl. ID of dashboard"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
|
@ -71,8 +64,8 @@ func getWidgets(c *gin.Context) {
|
|||
// @Router /widgets [post]
|
||||
func addWidget(c *gin.Context) {
|
||||
|
||||
var newWidget Widget
|
||||
err := c.BindJSON(&newWidget)
|
||||
var newWidgetData common.ResponseMsgWidget
|
||||
err := c.BindJSON(&newWidgetData)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
|
@ -81,7 +74,26 @@ func addWidget(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
err = newWidget.addToVisualization(newWidget.VisualizationID)
|
||||
var newWidget Widget
|
||||
newWidget.Name = newWidgetData.Widget.Name
|
||||
newWidget.Type = newWidgetData.Widget.Type
|
||||
newWidget.Height = newWidgetData.Widget.Height
|
||||
newWidget.Width = newWidgetData.Widget.Width
|
||||
newWidget.MinHeight = newWidgetData.Widget.MinHeight
|
||||
newWidget.MinWidth = newWidgetData.Widget.MinWidth
|
||||
newWidget.X = newWidgetData.Widget.X
|
||||
newWidget.Y = newWidgetData.Widget.Y
|
||||
newWidget.Z = newWidgetData.Widget.Z
|
||||
newWidget.CustomProperties = newWidgetData.Widget.CustomProperties
|
||||
newWidget.IsLocked = newWidgetData.Widget.IsLocked
|
||||
newWidget.DashboardID = newWidgetData.Widget.DashboardID
|
||||
|
||||
ok, _ := dashboard.CheckPermissions(c, common.Create, "body", int(newWidget.DashboardID))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
err = newWidget.addToDashboard()
|
||||
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
|
@ -90,19 +102,13 @@ func addWidget(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
func cloneWidget(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "NOT implemented",
|
||||
})
|
||||
}
|
||||
|
||||
// updateWidget godoc
|
||||
// @Summary Update a widget
|
||||
// @ID updateWidget
|
||||
// @Tags widgets
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param inputWidget body common.WidgetResponse true "Widget to be updated"
|
||||
// @Param inputWidget body common.ResponseMsgWidget true "Widget to be updated"
|
||||
// @Success 200 "OK."
|
||||
// @Failure 401 "Unauthorized Access"
|
||||
// @Failure 403 "Access forbidden."
|
||||
|
@ -112,13 +118,13 @@ func cloneWidget(c *gin.Context) {
|
|||
// @Router /widgets/{widgetID} [put]
|
||||
func updateWidget(c *gin.Context) {
|
||||
|
||||
widgetID, err := common.GetWidgetID(c)
|
||||
if err != nil {
|
||||
ok, w := CheckPermissions(c, common.Update, -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var modifiedWidget Widget
|
||||
err = c.BindJSON(&modifiedWidget)
|
||||
var modifiedWidget common.ResponseMsgWidget
|
||||
err := c.BindJSON(&modifiedWidget)
|
||||
if err != nil {
|
||||
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
|
@ -127,16 +133,10 @@ func updateWidget(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
var w Widget
|
||||
err = w.ByID(uint(widgetID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = w.update(modifiedWidget)
|
||||
err = w.update(modifiedWidget.Widget)
|
||||
if common.ProvideErrorResponse(c, err) == false {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "OK",
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -155,14 +155,8 @@ func updateWidget(c *gin.Context) {
|
|||
// @Router /widgets/{widgetID} [get]
|
||||
func getWidget(c *gin.Context) {
|
||||
|
||||
widgetID, err := common.GetWidgetID(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var w Widget
|
||||
err = w.ByID(uint(widgetID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
ok, w := CheckPermissions(c, common.Read, -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -186,21 +180,17 @@ func getWidget(c *gin.Context) {
|
|||
// @Router /widgets/{widgetID} [delete]
|
||||
func deleteWidget(c *gin.Context) {
|
||||
|
||||
// widgetID, err := GetWidgetID(c)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// widget, err := queries.FindWidgetOfVisualization(&visualization, widgetID)
|
||||
// if common.ProvideErrorResponse(c, err) {
|
||||
// return
|
||||
// }
|
||||
ok, w := CheckPermissions(c, common.Delete, -1)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO delete files of widget in DB and on disk
|
||||
|
||||
// TODO Delete widget itself + association with visualization
|
||||
err := w.delete()
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "NOT implemented",
|
||||
"message": "OK.",
|
||||
})
|
||||
}
|
85
routes/widget/widget_methods.go
Normal file
85
routes/widget/widget_methods.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
package widget
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/dashboard"
|
||||
)
|
||||
|
||||
type Widget struct {
|
||||
common.Widget
|
||||
}
|
||||
|
||||
func (w *Widget) save() error {
|
||||
db := common.GetDB()
|
||||
err := db.Create(w).Error
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *Widget) ByID(id uint) error {
|
||||
db := common.GetDB()
|
||||
err := db.Find(w, id).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("Widget with id=%v does not exist", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Widget) addToDashboard() error {
|
||||
db := common.GetDB()
|
||||
var dab dashboard.Dashboard
|
||||
err := dab.ByID(uint(w.DashboardID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// save widget to DB
|
||||
err = w.save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// associate dashboard with simulation
|
||||
err = db.Model(&dab).Association("Widgets").Append(w).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *Widget) update(modifiedWidget common.WidgetResponse) error {
|
||||
|
||||
db := common.GetDB()
|
||||
err := db.Model(w).Updates(map[string]interface{}{
|
||||
"Name": modifiedWidget.Name,
|
||||
"Type": modifiedWidget.Type,
|
||||
"Width": modifiedWidget.Width,
|
||||
"Height": modifiedWidget.Height,
|
||||
"MinWidth": modifiedWidget.MinWidth,
|
||||
"MinHeight": modifiedWidget.MinHeight,
|
||||
"X": modifiedWidget.X,
|
||||
"Y": modifiedWidget.Y,
|
||||
"Z": modifiedWidget.Z,
|
||||
"IsLocked": modifiedWidget.IsLocked,
|
||||
"CustomProperties": modifiedWidget.CustomProperties,
|
||||
}).Error
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *Widget) delete() error {
|
||||
|
||||
db := common.GetDB()
|
||||
var dab dashboard.Dashboard
|
||||
err := dab.ByID(w.DashboardID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove association between Dashboard and Widget
|
||||
// Widget itself is not deleted from DB, it remains as "dangling"
|
||||
err = db.Model(&dab).Association("Widgets").Delete(w).Error
|
||||
|
||||
// TODO: What about files that belong to a widget? Keep them or remove them here?
|
||||
|
||||
return err
|
||||
}
|
49
routes/widget/widget_middleware.go
Normal file
49
routes/widget/widget_middleware.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package widget
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/dashboard"
|
||||
)
|
||||
|
||||
func CheckPermissions(c *gin.Context, operation common.CRUD, widgetIDBody int) (bool, Widget) {
|
||||
|
||||
var w Widget
|
||||
|
||||
err := common.ValidateRole(c, common.ModelWidget, operation)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).")
|
||||
return false, w
|
||||
}
|
||||
|
||||
var widgetID int
|
||||
if widgetIDBody < 0 {
|
||||
widgetID, err = strconv.Atoi(c.Param("widgetID"))
|
||||
if err != nil {
|
||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of widgetID path parameter")
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errormsg,
|
||||
})
|
||||
return false, w
|
||||
}
|
||||
} else {
|
||||
widgetID = widgetIDBody
|
||||
}
|
||||
|
||||
err = w.ByID(uint(widgetID))
|
||||
if common.ProvideErrorResponse(c, err) {
|
||||
return false, w
|
||||
}
|
||||
|
||||
ok, _ := dashboard.CheckPermissions(c, operation, "body", int(w.DashboardID))
|
||||
if !ok {
|
||||
return false, w
|
||||
}
|
||||
|
||||
return true, w
|
||||
}
|
65
routes/widget/widget_test.go
Normal file
65
routes/widget/widget_test.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package widget
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
|
||||
)
|
||||
|
||||
// Test /widgets endpoints
|
||||
func TestWidgetEndpoints(t *testing.T) {
|
||||
|
||||
var token string
|
||||
|
||||
var myWidgets = []common.WidgetResponse{common.WidgetA_response, common.WidgetB_response}
|
||||
var msgWidgets = common.ResponseMsgWidgets{Widgets: myWidgets}
|
||||
var msgWdg = common.ResponseMsgWidget{Widget: common.WidgetC_response}
|
||||
var msgWdgupdated = common.ResponseMsgWidget{Widget: common.WidgetCUpdated_response}
|
||||
|
||||
db := common.DummyInitDB()
|
||||
defer db.Close()
|
||||
common.DummyPopulateDB(db)
|
||||
|
||||
router := gin.Default()
|
||||
api := router.Group("/api")
|
||||
|
||||
// All endpoints require authentication except when someone wants to
|
||||
// login (POST /authenticate)
|
||||
user.VisitorAuthenticate(api.Group("/authenticate"))
|
||||
|
||||
api.Use(user.Authentication(true))
|
||||
|
||||
RegisterWidgetEndpoints(api.Group("/widgets"))
|
||||
|
||||
credjson, _ := json.Marshal(common.CredUser)
|
||||
msgOKjson, _ := json.Marshal(common.MsgOK)
|
||||
msgWidgetsjson, _ := json.Marshal(msgWidgets)
|
||||
msgWdgjson, _ := json.Marshal(msgWdg)
|
||||
msgWdgupdatedjson, _ := json.Marshal(msgWdgupdated)
|
||||
|
||||
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
|
||||
|
||||
// test GET widgets
|
||||
common.TestEndpoint(t, router, token, "/api/widgets?dashboardID=1", "GET", nil, 200, msgWidgetsjson)
|
||||
|
||||
// test POST widgets
|
||||
common.TestEndpoint(t, router, token, "/api/widgets", "POST", msgWdgjson, 200, msgOKjson)
|
||||
|
||||
// test GET widgets/:widgetID to check if previous POST worked correctly
|
||||
common.TestEndpoint(t, router, token, "/api/widgets/3", "GET", nil, 200, msgWdgjson)
|
||||
|
||||
// test PUT widgets/:widgetID
|
||||
common.TestEndpoint(t, router, token, "/api/widgets/3", "PUT", msgWdgupdatedjson, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/widgets/3", "GET", nil, 200, msgWdgupdatedjson)
|
||||
|
||||
// test DELETE widgets/:widgetID
|
||||
common.TestEndpoint(t, router, token, "/api/widgets/3", "DELETE", nil, 200, msgOKjson)
|
||||
common.TestEndpoint(t, router, token, "/api/widgets?dashboardID=1", "GET", nil, 200, msgWidgetsjson)
|
||||
|
||||
// TODO add testing for other return codes
|
||||
|
||||
}
|
38
start.go
38
start.go
|
@ -1,18 +1,22 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/swaggo/gin-swagger"
|
||||
"github.com/swaggo/gin-swagger/swaggerFiles"
|
||||
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
|
||||
_ "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/doc/api" // doc/api folder is used by Swag CLI, you have to import it
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/dashboard"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/scenario"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/signal"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulationmodel"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization"
|
||||
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget"
|
||||
)
|
||||
|
||||
|
@ -50,9 +54,10 @@ func main() {
|
|||
|
||||
api.Use(user.Authentication(true))
|
||||
|
||||
simulation.RegisterSimulationEndpoints(api.Group("/simulations"))
|
||||
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
|
||||
simulationmodel.RegisterSimulationModelEndpoints(api.Group("/models"))
|
||||
visualization.RegisterVisualizationEndpoints(api.Group("/visualizations"))
|
||||
signal.RegisterSignalEndpoints(api.Group("/signals"))
|
||||
dashboard.RegisterDashboardEndpoints(api.Group("/dashboards"))
|
||||
widget.RegisterWidgetEndpoints(api.Group("/widgets"))
|
||||
file.RegisterFileEndpoints(api.Group("/files"))
|
||||
user.RegisterUserEndpoints(api.Group("/users"))
|
||||
|
@ -60,6 +65,29 @@ func main() {
|
|||
|
||||
r.GET("swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
|
||||
if common.WITH_AMQP == true {
|
||||
fmt.Println("Starting AMQP client")
|
||||
err := common.ConnectAMQP("amqp://localhost")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Periodically call the Ping function to check which simulators are still there
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
go func() {
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
err = common.PingAMQP()
|
||||
if err != nil {
|
||||
fmt.Println("AMQP Error: ", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
// server at port 4000 to match frontend's redirect path
|
||||
r.Run(":4000")
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue