Merge branch 'master' into authentication

# Conflicts:
#	go.mod
#	go.sum
#	routes/user/userEndpoints.go
#	routes/user/userSerializer.go
#	start.go
This commit is contained in:
Sonja Happ 2019-05-28 10:40:53 +02:00
commit f6ea39af12
43 changed files with 3261 additions and 1154 deletions

View file

@ -51,13 +51,15 @@ build:backend:
image: villaswebbackendgo:ubuntu
script:
- go mod tidy
- go get -u github.com/swaggo/swag/cmd/swag
- ~/go/bin/swag init -p pascalcase -g "start.go" -o "./doc/autoapi/"
- go build
# Stage: test
##############################################################################
test:backend:
test:backend:database:
stage: test
tags:
- docker
@ -65,11 +67,28 @@ test:backend:
script:
- /etc/init.d/postgresql start
- go mod tidy
- go get -u github.com/swaggo/swag/cmd/swag
- ~/go/bin/swag init -p pascalcase -g "start.go" -o "./doc/autoapi/"
- cd common
- go test -v -args -dbhost=/var/run/postgresql
dependencies:
- build:backend
test:backend:endpoints:
stage: test
tags:
- docker
image: villaswebbackendgo:ubuntu
script:
- /etc/init.d/postgresql start
- go mod tidy
- go get -u github.com/swaggo/swag/cmd/swag
- ~/go/bin/swag init -p pascalcase -g "start.go" -o "./doc/autoapi/"
- cd routes/simulation
- go test -v -args -dbhost=/var/run/postgresql
dependencies:
- build:backend
# Stage: deploy
##############################################################################

View file

@ -54,10 +54,9 @@ func VerifyConnection(db *gorm.DB) error {
// to the Dummy*() where it is called
func DropTables(db *gorm.DB) {
db.DropTableIfExists(&Simulator{})
db.DropTableIfExists(&Signal{})
db.DropTableIfExists(&SimulationModel{})
//db.DropTableIfExists(&Signal{})
db.DropTableIfExists(&Model{})
db.DropTableIfExists(&File{})
db.DropTableIfExists(&Project{})
db.DropTableIfExists(&Simulation{})
db.DropTableIfExists(&User{})
db.DropTableIfExists(&Visualization{})
@ -67,10 +66,9 @@ func DropTables(db *gorm.DB) {
// AutoMigrate the models
func MigrateModels(db *gorm.DB) {
db.AutoMigrate(&Simulator{})
db.AutoMigrate(&Signal{})
db.AutoMigrate(&SimulationModel{})
//db.AutoMigrate(&Signal{})
db.AutoMigrate(&Model{})
db.AutoMigrate(&File{})
db.AutoMigrate(&Project{})
db.AutoMigrate(&Simulation{})
db.AutoMigrate(&User{})
db.AutoMigrate(&Visualization{})
@ -103,30 +101,25 @@ func DummyPopulateDB(test_db *gorm.DB) {
checkErr(test_db.Create(&simr_A).Error)
checkErr(test_db.Create(&simr_B).Error)
outSig_A := Signal{Name: "outSignal_A"}
outSig_B := Signal{Name: "outSignal_B"}
inSig_A := Signal{Name: "inSignal_A"}
inSig_B := Signal{Name: "inSignal_B"}
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)
//outSig_A := Signal{Name: "outSignal_A", Direction: "out"}
//outSig_B := Signal{Name: "outSignal_B", Direction: "out"}
//inSig_A := Signal{Name: "inSignal_A", Direction: "in"}
//inSig_B := Signal{Name: "inSignal_B", Direction: "in"}
//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)
smo_A := SimulationModel{Name: "SimModel_A"}
smo_B := SimulationModel{Name: "SimModel_B"}
checkErr(test_db.Create(&smo_A).Error)
checkErr(test_db.Create(&smo_B).Error)
mo_A := Model{Name: "Model_A"}
mo_B := Model{Name: "Model_B"}
checkErr(test_db.Create(&mo_A).Error)
checkErr(test_db.Create(&mo_B).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)
proj_A := Project{Name: "Project_A"}
proj_B := Project{Name: "Project_B"}
checkErr(test_db.Create(&proj_A).Error)
checkErr(test_db.Create(&proj_B).Error)
simn_A := Simulation{Name: "Simulation_A"}
simn_B := Simulation{Name: "Simulation_B"}
checkErr(test_db.Create(&simn_A).Error)
@ -162,49 +155,40 @@ func DummyPopulateDB(test_db *gorm.DB) {
// For `belongs to` use the model with id=1
// For `has many` use the models with id=1 and id=2
// Project HM Visualization, Visualization BT Project
checkErr(test_db.Model(&vis_A).Association("Project").Append(&proj_A).Error)
checkErr(test_db.Model(&vis_B).Association("Project").Append(&proj_A).Error)
// 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 Project, Project BT User
checkErr(test_db.Model(&proj_A).Association("User").Append(&usr_A).Error)
checkErr(test_db.Model(&proj_B).Association("User").Append(&usr_A).Error)
// Simulation HM Model
checkErr(test_db.Model(&simn_A).Association("Models").Append(&mo_A).Error)
checkErr(test_db.Model(&simn_A).Association("Models").Append(&mo_B).Error)
// Simulation HM Project, Project BT Simulation
checkErr(test_db.Model(&proj_A).Association("Simulation").Append(&simn_A).Error)
checkErr(test_db.Model(&proj_B).Association("Simulation").Append(&simn_A).Error)
// User HM Files
checkErr(test_db.Model(&usr_A).Association("Files").Append(&file_A).Error)
checkErr(test_db.Model(&usr_A).Association("Files").Append(&file_B).Error)
// Simulation HM SimModel, SimModel BT Simulation
checkErr(test_db.Model(&smo_A).Association("BelongsToSimulation").Append(&simn_A).Error)
checkErr(test_db.Model(&smo_B).Association("BelongsToSimulation").Append(&simn_A).Error)
// User HM Simulation, Simulation BT User
checkErr(test_db.Model(&simn_A).Association("User").Append(&usr_A).Error)
checkErr(test_db.Model(&simn_B).Association("User").Append(&usr_A).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)
// 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)
// SimModel HM Signal
checkErr(test_db.Model(&smo_A).Association("InputMapping").Append(&inSig_A).Error)
checkErr(test_db.Model(&smo_A).Association("InputMapping").Append(&inSig_B).Error)
checkErr(test_db.Model(&smo_A).Association("OutputMapping").Append(&outSig_A).Error)
checkErr(test_db.Model(&smo_A).Association("OutputMapping").Append(&outSig_B).Error)
// Model HM Signal
//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)
//SimulationModel HM Files
checkErr(test_db.Model(&smo_A).Association("Files").Append(&file_A).Error)
checkErr(test_db.Model(&smo_A).Association("Files").Append(&file_B).Error)
// Model 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)
// Visualization BT User
checkErr(test_db.Model(&vis_A).Association("User").Append(&usr_A).Error)
// Simulator BT Model
checkErr(test_db.Model(&mo_A).Association("Simulator").Append(&simr_A).Error)
// Simulator BT SimModel
checkErr(test_db.Model(&smo_A).Association("BelongsToSimulator").Append(&simr_A).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)
}

View file

@ -2,8 +2,9 @@ package common
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
// Verify that you can connect to the database
@ -28,45 +29,33 @@ func TestDummyDBAssociations(t *testing.T) {
// Variables for tests
var simr Simulator
var smo SimulationModel
var mo Model
var file File
var proj Project
var simn Simulation
var usr User
var usrs []User
var vis Visualization
var widg Widget
var sigs []Signal
var smos []SimulationModel
//var sigs []Signal
var mos []Model
var files []File
var files_sm []File
var projs []Project
var simns []Simulation
var viss []Visualization
var widgs []Widget
// Simulation Model
// User
a.NoError(db.Find(&smo, 1).Error, fM("SimulationModel"))
a.EqualValues("SimModel_A", smo.Name)
a.NoError(db.Find(&usr, 1).Error, fM("User"))
a.EqualValues("User_A", usr.Username)
// Simulation Model Associations
// User Associations
a.NoError(db.Model(&smo).Association("BelongsToSimulation").Find(&simn).Error)
a.EqualValues("Simulation_A", simn.Name, "Expected Simulation_A")
a.NoError(db.Model(&smo).Association("BelongsToSimulator").Find(&simr).Error)
a.EqualValues("Host_A", simr.Host, "Expected Host_A")
a.NoError(db.Model(&smo).Related(&sigs, "OutputMapping").Error)
if len(sigs) != 4 {
a.Fail("Simulation Model Associations",
"Expected to have %v Output AND Input Signals. Has %v.", 4, len(sigs))
}
a.NoError(db.Model(&smo).Related(&files_sm, "Files").Error)
if len(files_sm) != 2 {
a.Fail("Simulation Model Associations",
"Expected to have %v Files. Has %v.", 2, len(files_sm))
a.NoError(db.Model(&usr).Related(&simns, "Simulations").Error)
if len(simns) != 2 {
a.Fail("User Associations",
"Expected to have %v Simulations. Has %v.", 2, len(simns))
}
// Simulation
@ -76,64 +65,47 @@ func TestDummyDBAssociations(t *testing.T) {
// Simulation Associations
a.NoError(db.Model(&simn).Association("User").Find(&usr).Error)
a.EqualValues("User_A", usr.Username)
a.NoError(db.Model(&simn).Related(&smos, "Models").Error)
if len(smos) != 2 {
a.Fail("Simulation Associations",
"Expected to have %v Simulation Models. Has %v.", 2, len(smos))
a.NoError(db.Model(&simn).Association("Users").Find(&usrs).Error)
if len(usrs) != 2 {
a.Fail("Simulations Associations",
"Expected to have %v Users. Has %v.", 2, len(usrs))
}
a.NoError(db.Model(&simn).Related(&projs, "Projects").Error)
if len(projs) != 2 {
a.NoError(db.Model(&simn).Related(&mos, "Models").Error)
if len(mos) != 2 {
a.Fail("Simulation Associations",
"Expected to have %v Projects. Has %v.", 2, len(projs))
"Expected to have %v Models. Has %v.", 2, len(mos))
}
// Project
a.NoError(db.Find(&proj, 1).Error, fM("Project"))
a.EqualValues("Project_A", proj.Name)
// Project Associations
a.NoError(db.Model(&proj).Association("Simulation").Find(&simn).Error)
a.EqualValues("Simulation_A", simn.Name)
a.NoError(db.Model(&proj).Association("User").Find(&usr).Error)
a.EqualValues("User_A", usr.Username)
a.NoError(db.Model(&proj).Related(&viss, "Visualizations").Error)
a.NoError(db.Model(&simn).Related(&viss, "Visualizations").Error)
if len(viss) != 2 {
a.Fail("Project Associations",
a.Fail("Simulation Associations",
"Expected to have %v Visualizations. Has %v.", 2, len(viss))
}
// User
a.NoError(db.Find(&usr, 1).Error, fM("User"))
a.EqualValues("User_A", usr.Username)
// Model
// User Associations
a.NoError(db.Find(&mo, 1).Error, fM("Model"))
a.EqualValues("Model_A", mo.Name)
a.NoError(db.Model(&usr).Related(&projs, "Projects").Error)
if len(projs) != 2 {
a.Fail("User Associations",
"Expected to have %v Projects. Has %v.", 2, len(projs))
// Model 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("Model Associations",
// "Expected to have %v Output AND Input Signals. Has %v.", 2, len(sigs))
//}
a.NoError(db.Model(&mo).Related(&files_sm, "Files").Error)
if len(files_sm) != 2 {
a.Fail("Model Associations",
"Expected to have %v Files. Has %v.", 2, len(files_sm))
}
a.NoError(db.Model(&usr).Related(&simns, "Simulations").Error)
if len(simns) != 2 {
a.Fail("User Associations",
"Expected to have %v Simulations. Has %v.", 2, len(simns))
}
a.NoError(db.Model(&usr).Related(&files, "Files").Error)
if len(files) != 2 {
a.Fail("User Associations",
"Expected to have %v Files. Has %v.", 2, len(files))
}
// Visualization
@ -142,26 +114,29 @@ func TestDummyDBAssociations(t *testing.T) {
// Visualization Associations
a.NoError(db.Model(&vis).Association("Project").Find(&proj).Error)
a.EqualValues("Project_A", proj.Name)
a.NoError(db.Model(&vis).Association("User").Find(&usr).Error)
a.EqualValues("User_A", usr.Username)
a.NoError(db.Model(&vis).Related(&widgs, "Widgets").Error)
if len(widgs) != 2 {
a.Fail("Widget Associations",
"Expected to have %v Widget. Has %v.", 2, len(widgs))
}
// Widget
a.NoError(db.Find(&widg, 1).Error, fM("Widget"))
a.EqualValues("Widget_A", widg.Name)
// Widget Association
a.NoError(db.Model(&widg).Related(&files, "Files").Error)
if len(files) != 2 {
a.Fail("Widget Associations",
"Expected to have %v Files. Has %v.", 2, len(files))
}
// File
a.NoError(db.Find(&file, 1).Error, fM("File"))
a.EqualValues("File_A", file.Name)
// File Associations
//a.NoError(db.Model(&file).Association("User").Find(&usr).Error)
//a.EqualValues("User_A", usr.Username)
}

View file

@ -1,142 +0,0 @@
package common
import (
//"github.com/jinzhu/gorm"
"github.com/jinzhu/gorm/dialects/postgres"
"time"
)
type Simulator struct {
//gorm.Model
ID uint `gorm:"primary_key;auto_increment"`
UUID string `gorm:"unique;not null"`
Host string `gorm:"default:''"`
Modeltype string `gorm:"default:''"`
Uptime int `gorm:"default:0"`
State string `gorm:"default:''"`
StateUpdateAt time.Time
Properties postgres.Jsonb // TODO: default value?
RawProperties postgres.Jsonb // TODO: default value?
}
type File struct {
//gorm.Model
ID uint `gorm:"primary_key;auto_increment"`
Name string `gorm:"not null"`
Path string `gorm:"not null"`
Type string `gorm:"not null"`
Size uint `gorm:"not null"`
ImageHeight uint // only required in case file is an image
ImageWidth uint // only required in case file is an image
Date time.Time
//remove belongs to User relation
//User User `gorm:"not null;association_autoupdate:false"`
UserID uint `gorm:"not null"`
SimulationModelID uint `gorm:""`
}
type Project struct {
//gorm.Model
ID uint `gorm:"primary_key;auto_increment"`
Name string `gorm:"not null"`
User User `gorm:"not null;association_autoupdate:false"`
UserID uint `gorm:"not null"`
Simulation Simulation `gorm:"not null;association_autoupdate:false"`
SimulationID uint `gorm:"not null"`
Visualizations []Visualization `gorm:"association_autoupdate:false"`
}
type Simulation struct {
//gorm.Model
ID uint `gorm:"primary_key;auto_increment"`
Name string `gorm:"not null"`
Running bool `gorm:"default:false"`
StartParameters postgres.Jsonb // TODO default value
User User `gorm:"not null;association_autoupdate:false"`
UserID uint `gorm:"not null"`
Models []SimulationModel `gorm:"foreignkey:BelongsToSimulationID;association_autoupdate:false"`
Projects []Project `gorm:"association_autoupdate:false"`
}
type SimulationModel struct {
//gorm.Model
ID uint `gorm:"primary_key;auto_increment"`
Name string `gorm:"not null"`
OutputLength int `gorm:"default:1"`
InputLength int `gorm:"default:1"`
StartParameters postgres.Jsonb // TODO: default value?
BelongsToSimulation Simulation `gorm:"not null;association_autoupdate:false"`
BelongsToSimulationID uint `gorm:"not null"`
BelongsToSimulator Simulator `gorm:"not null;association_autoupdate:false"`
BelongsToSimulatorID uint `gorm:"not null"`
// NOTE: order of signals is important
OutputMapping []Signal `gorm:""`
InputMapping []Signal `gorm:""`
//new in villasweb 2.0 (for CIM file of simulation model and other model file formats)
Files []File `gorm:""`
}
type User struct {
//gorm.Model
ID uint `gorm:"primary_key;auto_increment"`
Username string `gorm:"unique;not null"`
Password string `gorm:"not null"`
Mail string `gorm:"default:''"`
Role string `gorm:"default:'user'"`
Projects []Project `gorm:"association_autoupdate:false"`
Simulations []Simulation `gorm:"association_autoupdate:false"`
Files []File `gorm:""`
}
type Visualization struct {
//gorm.Model
ID uint `gorm:"primary_key;auto_increment"`
Name string `gorm:"not null"`
Grid int `gorm:"default:15"`
Project Project `gorm:"not null;association_autoupdate:false"`
ProjectID uint `gorm:"not null"`
User User `gorm:"not null;association_autoupdate:false"`
UserID uint `gorm:"not null"`
Widgets []Widget `gorm:""`
}
type Signal struct {
//gorm.Model
ID uint `gorm:"primary_key;auto_increment"`
Name string `gorm:"not null"`
Unit string `gorm:"not null"`
SimulationModelID uint
//IsRecorded bool `gorm:"default:false"`
}
type Widget struct {
//gorm.Model
ID uint `gorm:"primary_key;auto_increment"`
Name string `gorm:"not null"`
Type string `gorm:"not null"`
Width uint `gorm:"not null"`
Height uint `gorm:"not null"`
MinWidth uint `gorm:"not null"`
MinHeight uint `gorm:"not null"`
X int `gorm:"not null"`
Y int `gorm:"not null"`
Z int `gorm:"not null"`
IsLocked bool `gorm:"default:false"`
CustomProperties postgres.Jsonb // TODO: default value?
VisualizationID uint
}

172
common/models.go Normal file
View file

@ -0,0 +1,172 @@
package common
import (
"time"
)
// User data model
type User struct {
// ID of user
ID uint `gorm:"primary_key;auto_increment"`
// Username of user
Username string `gorm:"unique;not null"`
// Password of user
Password string `gorm:"not null"`
// Mail of user
Mail string `gorm:"default:''"`
// Role of user
Role string `gorm:"default:'user'"`
// Simulations to which user has access
Simulations []Simulation `gorm:"many2many:user_simulations"`
}
// Simulation data model
type Simulation struct {
// ID of simulation
ID uint `gorm:"primary_key;auto_increment"`
// Name of simulation
Name string `gorm:"not null"`
// Running state of simulation
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"`
// Models that belong to the simulation
Models []Model `gorm:"foreignkey:SimulationID"`
// Visualizations that belong to the simulation
Visualizations []Visualization `gorm:"foreignkey:SimulationID"`
}
// Model data model
type Model struct {
// ID of model
ID uint `gorm:"primary_key;auto_increment"`
// Name of 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 model as JSON string
StartParameters string
// ID of simulation to which model belongs
SimulationID uint
// Simulator associated with model
Simulator Simulator
// ID of simulator associated with model
SimulatorID uint
// Mapping of output signals of the model, order of signals is important
OutputMapping []Signal
// Mapping of input signals of the model, order of signals is important
InputMapping []Signal
// Files of model (can be CIM and other model file formats)
Files []File `gorm:"foreignkey:ModelID"`
}
type Signal struct {
// Name of Signal
Name string
// Unit of Signal
Unit string
// Index of the Signal in the mapping
Index uint
// Direction of the signal (in or out)
Direction string
}
// Simulator data model
type Simulator struct {
// ID of the simulator
ID uint `gorm:"primary_key;auto_increment"`
// UUID of the simulator
UUID string `gorm:"unique;not null"`
// Host if the simulator
Host string `gorm:"default:''"`
// Model type supported by the simulator
Modeltype string `gorm:"default:''"`
// Uptime of the simulator
Uptime int `gorm:"default:0"`
// State of the simulator
State string `gorm:"default:''"`
// Time of last state update
StateUpdateAt time.Time
// Properties of simulator as JSON string
Properties string
// Raw properties of simulator as JSON string
RawProperties string
}
// Visualization data model
type Visualization struct {
// ID of visualization
ID uint `gorm:"primary_key;auto_increment"`
// Name of visualization
Name string `gorm:"not null"`
// Grid of visualization
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"`
}
// Widget data model
type Widget struct {
// ID of widget
ID uint `gorm:"primary_key;auto_increment"`
// Name of widget
Name string `gorm:"not null"`
// Type of widget
Type string `gorm:"not null"`
// Width of widget
Width uint `gorm:"not null"`
// Height of widget
Height uint `gorm:"not null"`
// Minimal width of widget
MinWidth uint `gorm:"not null"`
// Minimal height of widget
MinHeight uint `gorm:"not null"`
// X position of widget
X int `gorm:"not null"`
// Y position of widget
Y int `gorm:"not null"`
// Z position of widget
Z int `gorm:"not null"`
// 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"`
// 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"`
// 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"`
// Size of file (in byte)
Size uint `gorm:"not null"`
// 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
// ID of model to which file belongs
ModelID uint `gorm:""`
// ID of widget to which file belongs
WidgetID uint `gorm:""`
}

95
common/responses.go Normal file
View file

@ -0,0 +1,95 @@
package common
import (
"time"
)
type UserResponse struct {
Username string `json:"Username"`
Role string `json:"Role"`
Mail string `json:"Mail"`
}
type SimulationResponse struct {
Name string `json:"Name"`
ID uint `json:"SimulationID"`
Running bool `json:"Running"`
StartParams string `json:"Starting Parameters"`
}
type ModelResponse struct {
Name string `json:"Name"`
OutputLength int `json:"OutputLength"`
InputLength int `json:"InputLength"`
SimulationID uint `json:"SimulationID"`
SimulatorID uint `json:"SimulatorID"`
StartParams string `json:"StartParams"`
InputMapping []Signal `json:"InputMapping"`
OutputMapping []Signal `json:"OutputMapping"`
}
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"`
}
type VisualizationResponse struct {
Name string `json:"Name"`
Grid int `json:"Grid"`
SimulationID uint `json:"SimulationID"`
}
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"`
}
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"`
}
// Response messages
type ResponseMsg struct{
Message string `json:"message"`
}
type ResponseMsgUsers struct {
Users []UserResponse `json:"users"`
}
type ResponseMsgUser struct {
User UserResponse `json:"user"`
}
type ResponseMsgSimulations struct {
Simulations []SimulationResponse `json:"simulations"`
}
type ResponseMsgSimulation struct {
Simulation SimulationResponse `json:"simulation"`
}

262
common/serializers.go Normal file
View file

@ -0,0 +1,262 @@
package common
import (
"github.com/gin-gonic/gin"
)
// User/s Serializers
type UsersSerializer struct {
Ctx *gin.Context
Users []User
}
func (self *UsersSerializer) Response(assoc bool) []UserResponse {
response := []UserResponse{}
for _, user := range self.Users {
serializer := UserSerializer{self.Ctx, user}
response = append(response, serializer.Response(assoc))
}
return response
}
type UserSerializer struct {
Ctx *gin.Context
User
}
func (self *UserSerializer) Response(assoc bool) UserResponse {
response := UserResponse{
Username: self.Username,
Role: self.Role,
Mail: self.Mail,
}
// Associated models MUST NOT called with assoc=true otherwise we
// will have an infinite loop due to the circular dependencies
if assoc {
// TODO: maybe all those should be made in one transaction
//simulations, _, _ := simulation.FindUserSimulations(&self.User)
//simulationsSerializer :=
// SimulationsSerializer{self.Ctx, simulations}
// Add the associated models to the response
//response.Simulations = simulationsSerializer.Response()
}
return response
}
// Simulation/s Serializers
type SimulationsSerializer struct {
Ctx *gin.Context
Simulations []Simulation
}
func (self *SimulationsSerializer) Response() []SimulationResponse {
response := []SimulationResponse{}
for _, simulation := range self.Simulations {
serializer := SimulationSerializer{self.Ctx, simulation}
response = append(response, serializer.Response())
}
return response
}
type SimulationSerializer struct {
Ctx *gin.Context
Simulation
}
func (self *SimulationSerializer) Response() SimulationResponse {
response := SimulationResponse{
Name: self.Name,
ID: self.ID,
Running: self.Running,
StartParams: self.StartParameters,
}
return response
}
// Model/s Serializers
type ModelsSerializer struct {
Ctx *gin.Context
Models []Model
}
func (self *ModelsSerializer) Response() []ModelResponse {
response := []ModelResponse{}
for _, model := range self.Models {
serializer := ModelSerializer{self.Ctx, model}
response = append(response, serializer.Response())
}
return response
}
type ModelSerializer struct {
Ctx *gin.Context
Model
}
func (self *ModelSerializer) Response() ModelResponse {
response := ModelResponse{
Name: self.Name,
OutputLength: self.OutputLength,
InputLength: self.InputLength,
SimulationID: self.SimulationID,
SimulatorID: self.SimulatorID,
StartParams: self.StartParameters,
//InputMapping
//OutputMapping
}
return response
}
// Simulator/s Serializers
type SimulatorsSerializer struct {
Ctx *gin.Context
Simulators []Simulator
}
func (self *SimulatorsSerializer) Response() []SimulatorResponse {
response := []SimulatorResponse{}
for _, simulator := range self.Simulators {
serializer := SimulatorSerializer{self.Ctx, simulator}
response = append(response, serializer.Response())
}
return response
}
type SimulatorSerializer struct {
Ctx *gin.Context
Simulator
}
func (self *SimulatorSerializer) Response() SimulatorResponse {
response := SimulatorResponse{
UUID: self.UUID,
Host: self.Host,
ModelType: self.Modeltype,
Uptime: self.Uptime,
State: self.State,
StateUpdateAt: self.StateUpdateAt,
}
return response
}
// Visualization/s Serializers
type VisualizationsSerializer struct {
Ctx *gin.Context
Visualizations []Visualization
}
func (self *VisualizationsSerializer) Response() []VisualizationResponse {
response := []VisualizationResponse{}
for _, visualization := range self.Visualizations {
serializer := VisualizationSerializer{self.Ctx, visualization}
response = append(response, serializer.Response())
}
return response
}
type VisualizationSerializer struct {
Ctx *gin.Context
Visualization
}
func (self *VisualizationSerializer) Response() VisualizationResponse {
response := VisualizationResponse{
Name: self.Name,
Grid: self.Grid,
SimulationID: self.SimulationID,
}
return response
}
// Widget/s Serializers
type WidgetsSerializer struct {
Ctx *gin.Context
Widgets []Widget
}
func (self *WidgetsSerializer) Response() []WidgetResponse {
response := []WidgetResponse{}
for _, widget := range self.Widgets {
serializer := WidgetSerializer{self.Ctx, widget}
response = append(response, serializer.Response())
}
return response
}
type WidgetSerializer struct {
Ctx *gin.Context
Widget
}
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
}
return response
}
// File/s Serializers
type FilesSerializerNoAssoc struct {
Ctx *gin.Context
Files []File
}
func (self *FilesSerializerNoAssoc) Response() []FileResponse {
response := []FileResponse{}
for _, files := range self.Files {
serializer := FileSerializerNoAssoc{self.Ctx, files}
response = append(response, serializer.Response())
}
return response
}
type FileSerializerNoAssoc struct {
Ctx *gin.Context
File
}
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
}
return response
}

93
common/utilities.go Normal file
View file

@ -0,0 +1,93 @@
package common
import (
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
func ProvideErrorResponse(c *gin.Context, err error) bool {
if err != nil {
if err == gorm.ErrRecordNotFound {
errormsg := "Record not Found in DB: " + err.Error()
c.JSON(http.StatusNotFound, gin.H{
"error": errormsg,
})
} else {
errormsg := "Error on DB Query or transaction: " + err.Error()
c.JSON(http.StatusInternalServerError, gin.H{
"error": errormsg,
})
}
return true // Error
}
return false // No error
}
func GetSimulationID(c *gin.Context) (int, error) {
simID, err := strconv.Atoi(c.Param("simulationID"))
if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulation ID")
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return -1, err
} else {
return simID, err
}
}
func GetModelID(c *gin.Context) (int, error) {
modelID, err := strconv.Atoi(c.Param("modelID"))
if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of model ID")
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return -1, err
} else {
return modelID, err
}
}
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
}
}

View file

@ -17,11 +17,11 @@ tags:
- name: simulators
description: Manage Simulators
- name: projects
description: Manage Projects
description: (REMOVED) Manage Projects
- name: simulations
description: Manage Simulations
- name: simulationmodels
description: Manage SimulationModels
- name: models
description: Manage Models
- name: visualizations
description: Manage Visualizations
- name: uploads
@ -244,8 +244,8 @@ paths:
get:
tags:
- files
summary: Get files of user
operationId: getFilesOfUser
summary: (REMOVED) Get files of user
operationId: (REMOVED) getFilesOfUser
responses:
200:
description: OK.
@ -266,8 +266,8 @@ paths:
post:
tags:
- files
summary: Add a new file to the database (NEW, was /uploads before))
operationId: addFile
summary: (REMOVED) Add a new file to the database
operationId: (REMOVED) addFile
requestBody:
description: "File object to be added"
required: true
@ -292,8 +292,8 @@ paths:
get:
tags:
- files
summary: Get properties of file with ID FileID
operationId: getFileProperties
summary: (REMOVED) Get properties of file with ID FileID
operationId: (REMOVED) getFileProperties
parameters:
- in: path
name: FileID
@ -329,8 +329,8 @@ paths:
put:
tags:
- files
summary: Update properties of file with ID FileID (NEW)
operationId: updateFileProperties
summary: (REMOVED) Update properties of file with ID FileID
operationId: (REMOVED) updateFileProperties
requestBody:
description: "File object to be updated"
required: true
@ -361,8 +361,8 @@ paths:
delete:
tags:
- files
summary: Delete file with ID FileID
operationId: deleteFile
summary: (REMOVED) Delete file with ID FileID
operationId: (REMOVED) deleteFile
parameters:
- in: path
name: FileID
@ -385,8 +385,8 @@ paths:
get:
tags:
- projects
summary: Get projects of user
operationId: getProjects
summary: (REMOVED) Get projects of user
operationId: (REMOVED) getProjects
responses:
200:
description: OK.
@ -407,8 +407,8 @@ paths:
post:
tags:
- projects
summary: Add a new project to the database
operationId: addProject
summary: (REMOVED) Add a new project to the database
operationId: (REMOVED) addProject
requestBody:
description: "Project object to add to DB"
required: true
@ -431,8 +431,8 @@ paths:
put:
tags:
- projects
summary: Update properties of project with ID ProjectID
operationId: updateProjectProperties
summary: (REMOVED) Update properties of project with ID ProjectID
operationId: (REMOVED) updateProjectProperties
parameters:
- in: path
name: ProjectID
@ -463,8 +463,8 @@ paths:
get:
tags:
- projects
summary: Get properties of project with ID ProjectID
operationId: getProjectProperties
summary: (REMOVED) Get properties of project with ID ProjectID
operationId: (REMOVED) getProjectProperties
parameters:
- in: path
name: ProjectID
@ -490,8 +490,8 @@ paths:
delete:
tags:
- projects
summary: Delete project with ID ProjectID
operationId: deleteProject
summary: (REMOVED) Delete project with ID ProjectID
operationId: (REMOVED) deleteProject
parameters:
- in: path
name: ProjectID
@ -513,9 +513,9 @@ paths:
/models:
get:
tags:
- simulationmodels
summary: Get simulation models of user
operationId: getSimulationModels
- models
summary: (REMOVED) Get simulation models of user
operationId: (REMOVED) getModels
responses:
200:
description: OK.
@ -524,7 +524,7 @@ paths:
schema:
type: array
items:
$ref: '#/components/schemas/SimulationModel'
$ref: '#/components/schemas/Model'
401:
description: Unauthorized access.
403:
@ -535,16 +535,16 @@ paths:
description: Internal server error.
post:
tags:
- simulationmodels
summary: Add a new SimulationModel to the database
operationId: addSimulationModel
- models
summary: (REMOVED) Add a new Model to the database
operationId: (REMOVED) addModel
requestBody:
description: "SimulationModel object to add to DB"
description: "Model object to add to DB"
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SimulationModel'
$ref: '#/components/schemas/Model'
responses:
200:
description: OK.
@ -556,26 +556,26 @@ paths:
description: Access forbidden.
500:
description: Internal server error. Unable to save simulation or simulation model.
/models/{SimulationModelID}:
/models/{ModelID}:
put:
tags:
- simulationmodels
summary: Update properties of SimulationModel with ID SimulationModelID
operationId: updateSimulationModelProperties
- models
summary: (REMOVED) Update properties of Model with ID ModelID
operationId: (REMOVED) updateModelProperties
parameters:
- in: path
name: SimulationModelID
description: ID of a SimulationModel
name: ModelID
description: ID of a Model
required: true
schema:
type: integer
requestBody:
description: "SimulationModel object with new properties"
description: "Model object with new properties"
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SimulationModel'
$ref: '#/components/schemas/Model'
responses:
200:
description: OK.
@ -591,13 +591,13 @@ paths:
description: Internal server error. Unable to save simulation model.
get:
tags:
- simulationmodels
summary: Get properties of SimulationModel with ID SimulationModelID
operationId: getSimulationModelProperties
- models
summary: (REMOVED) Get properties of Model with ID ModelID
operationId: (REMOVED) getModelProperties
parameters:
- in: path
name: SimulationModelID
description: ID of a SimulationModel
name: ModelID
description: ID of a Model
required: true
schema:
type: integer
@ -607,7 +607,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/SimulationModel'
$ref: '#/components/schemas/Model'
401:
description: Unauthorized access.
403:
@ -618,13 +618,13 @@ paths:
description: Internal server error.
delete:
tags:
- simulationmodels
summary: Delete SimulationModel with ID SimulationModelID
operationId: deleteSimulationModel
- models
summary: (REMOVED) Delete Model with ID ModelID
operationId: (REMOVED) deleteModel
parameters:
- in: path
name: SimulationModelID
description: ID of a SimulationModel
name: ModelID
description: ID of a Model
required: true
schema:
type: integer
@ -639,16 +639,16 @@ paths:
description: Not found. Simulation or simulation model not found.
500:
description: Internal server error. Unable to save changed simulation or to remove simulation model.
/models/{SimulationModelID}/file:
simulations/{SimulationID}/models/{ModelID}/file:
get:
tags:
- simulationmodels
summary: Get file from SimulationModel with ID SimulationModelID (NEW)
operationId: getFileFromSimulationModel
- simulations
summary: (NEW) Get file from Model with ID ModelID
operationId: getFileFromModel
parameters:
- in: path
name: SimulationModelID
description: ID of a SimulationModel
name: ModelID
description: ID of a Model
required: true
schema:
type: integer
@ -691,13 +691,13 @@ paths:
description: Internal server error.
put:
tags:
- simulationmodels
summary: Update (Overwrite) file of SimulationModel with ID SimulationModelID, File object has to be in database (NEW)
operationId: updateFileForSimulationModel
- simulations
summary: (NEW) Update (Overwrite) file of Model with ID ModelID, File object has to be in database
operationId: updateFileForModel
parameters:
- in: path
name: SimulationModelID
description: ID of a SimulationModel
name: ModelID
description: ID of a Model
required: true
schema:
type: integer
@ -1035,9 +1035,9 @@ paths:
get:
tags:
- visualizations
summary: Get all available visualizations
description: Return a JSON representation of all visualizations
operationId: getVisualizations
summary: (REMOVED) Get all available visualizations
description: (REMOVED) Return a JSON representation of all visualizations
operationId: (REMOVED) getVisualizations
responses:
200:
description: OK.
@ -1058,8 +1058,8 @@ paths:
post:
tags:
- visualizations
summary: Add a new visualization to the database
operationId: addVisualization
summary: (REMOVED) Add a new visualization to the database
operationId: (REMOVED) addVisualization
requestBody:
description: "Visualization object to add to DB"
required: true
@ -1084,8 +1084,8 @@ paths:
put:
tags:
- visualizations
summary: Update properties of Visualization with ID VisualizationID
operationId: updateVisualizationrProperties
summary: (REMOVED) Update properties of Visualization with ID VisualizationID
operationId: (REMOVED) updateVisualizationrProperties
parameters:
- in: path
name: VisualizationID
@ -1116,8 +1116,8 @@ paths:
get:
tags:
- visualizations
summary: Get properties of Visualization with ID VisualizationID
operationId: getVisualizationProperties
summary: (REMOVED) Get properties of Visualization with ID VisualizationID
operationId: (REMOVED) getVisualizationProperties
parameters:
- in: path
name: VisualizationID
@ -1143,8 +1143,8 @@ paths:
delete:
tags:
- visualizations
summary: Delete Visualization with ID VisualizationID
operationId: deleteVisualization
summary: (REMOVED) Delete Visualization with ID VisualizationID
operationId: (REMOVED) deleteVisualization
parameters:
- in: path
name: VisualizationID
@ -1167,8 +1167,8 @@ paths:
post:
tags:
- uploads
summary: Upload a new file to the database (REMOVED)
operationId: uploadFile
summary: (REMOVED) Upload a new file to the database
operationId: (REMOVED) uploadFile
requestBody:
description: "File object to upload TODO CHANGE TO FORM"
required: true
@ -1280,7 +1280,7 @@ components:
type: integer
UserID:
type: integer
SimulationModelID:
ModelID:
type: integer
Date:
type: string
@ -1309,7 +1309,7 @@ components:
RawProperties:
type: object
properties: {}
SimulationModel:
Model:
required:
- Name
- OutputLength
@ -1343,14 +1343,14 @@ components:
required:
- Name
- Unit
- SimulationModelID
- ModelID
type: object
properties:
Name:
type: string
Unit:
type: string
SimulationModelID:
ModelID:
type: integer
Widget:
required:

9
doc/autoapi/generateapidoc.sh Executable file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env bash
cd ../../
go mod tidy
swag init -p pascalcase -g "start.go" -o "./doc/autoapi/"
cd -
redoc-cli bundle --cdn --title "VILLASweb Backend API" --output index.html swagger.yaml

28
go.mod
View file

@ -1,26 +1,14 @@
module git.rwth-aachen.de/acs/public/villas/villasweb-backend-go
require (
github.com/denisenkom/go-mssqldb v0.0.0-20190401154936-ce35bd87d4b3 // indirect
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 // indirect
github.com/gin-gonic/gin v1.3.0
github.com/go-sql-driver/mysql v1.4.1 // indirect
github.com/gofrs/uuid v3.2.0+incompatible // indirect
github.com/jinzhu/gorm v1.9.2
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect
github.com/jinzhu/now v1.0.0 // indirect
github.com/json-iterator/go v1.1.6 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/lib/pq v1.0.0 // indirect
github.com/mattn/go-isatty v0.0.7 // indirect
github.com/mattn/go-sqlite3 v1.10.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/stretchr/testify v1.2.2
github.com/ugorji/go v1.1.4 // indirect
github.com/gin-gonic/gin v1.4.0
github.com/jinzhu/gorm v1.9.8
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
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
)
replace github.com/ugorji/go v1.1.4 => github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43

138
go.sum
View file

@ -1,25 +1,26 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.2 h1:4y4L7BdHenTfZL0HervofNTHh9Ad6mNX72cQvl+5eH0=
cloud.google.com/go v0.37.2/go.mod h1:H8IAquKe2L30IxoupDgqTaQvKSwF/c8prYHynGIWQbA=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
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/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=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
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-20190401154936-ce35bd87d4b3 h1:3mNLx0iFqaq/Ssxqkjte26072KMu96uz1VBlbiZhQU4=
github.com/denisenkom/go-mssqldb v0.0.0-20190401154936-ce35bd87d4b3/go.mod h1:EcO5fNtMZHCMjAvj8LE6T+5bphSdR6LQ75n+m1TtsFI=
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/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=
@ -27,49 +28,51 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
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/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
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-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs=
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
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/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/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/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-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
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/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jinzhu/gorm v1.9.2 h1:lCvgEaqe/HVE+tjAR2mt4HbbHAZsQOv3XAZiEZV37iw=
github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
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=
@ -84,11 +87,14 @@ 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.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
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/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
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/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/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-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
@ -102,104 +108,97 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
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=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
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/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/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
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/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A=
go.opencensus.io v0.19.2/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190314133821-5284462c4bec/go.mod h1:atTaCNAy0f16Ah5aV1gMSwgiKVHwu/JncqDpuRr7lS4=
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/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-20181030102418-4d3f4d9ffa16/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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/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-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 h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
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-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
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 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20181029174526-d69651ed3497/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-20181218192612-074acd46bca6/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 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
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/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/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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-20181219222714-6e267b5cc78e/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-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=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU=
google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw=
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=
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.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@ -211,13 +210,10 @@ gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXa
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View file

@ -1,46 +1,405 @@
package file
import (
"github.com/gin-gonic/gin"
"fmt"
"net/http"
"path/filepath"
"strconv"
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
func FilesRegister(r *gin.RouterGroup) {
r.GET("/", filesReadEp)
r.POST("/", fileRegistrationEp) // NEW in API
r.PUT("/:FileID", fileUpdateEp) // NEW in API
r.GET("/:FileID", fileReadEp)
r.DELETE("/:FileID", fileDeleteEp)
func RegisterFileEndpoints(r *gin.RouterGroup){
r.GET("/", GetFiles)
r.POST ("/", AddFile)
r.GET("/:fileID", GetFile)
r.PUT("/:fileID", UpdateFile)
r.DELETE("/:fileID", DeleteFile)
//r.GET("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/files", GetFilesOfWidget)
//r.POST ("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", AddFileToWidget)
//r.GET("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", GetFileOfWidget)
//r.PUT("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", UpdateFileOfWidget)
//r.DELETE("/:simulationID/visualizations/:visualizationID/widgets/:widgetID/file", DeleteFileOfWidget)
}
func filesReadEp(c *gin.Context) {
allFiles, _, _ := FindAllFiles()
serializer := FilesSerializerNoAssoc{c, allFiles}
c.JSON(http.StatusOK, gin.H{
"files": serializer.Response(),
})
// GetFiles godoc
// @Summary Get all files of a specific model or widget
// @ID GetFiles
// @Tags files
// @Produce json
// @Success 200 {array} common.FileResponse "File parameters requested by user"
// @Failure 401 "Unauthorized Access"
// @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"
// @Router /files [get]
func GetFiles(c *gin.Context) {
// TODO if originType == "model" --> GetFilesOfModel, if originType == "vis" --> GetFilesOfWidget
}
func fileRegistrationEp(c *gin.Context) {
// AddFile godoc
// @Summary Add a file to a specific model or widget
// @ID AddFile
// @Tags files
// @Produce json
// @Accept text/plain
// @Accept png
// @Accept jpeg
// @Accept gif
// @Accept model/x-cim
// @Accept model/x-cim.zip
// @Success 200 "OK"
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param inputFile formData file true "File to be uploaded"
// @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"
// @Router /files [post]
func AddFile(c *gin.Context){
// TODO if originType == "model" --> AddFileToModel, if originType == "vis" --> AddFileToWidget
}
// GetFile godoc
// @Summary Download a file
// @ID GetFile
// @Tags files
// @Produce text/plain
// @Produce png
// @Produce jpeg
// @Produce gif
// @Produce model/x-cim
// @Produce model/x-cim.zip
// @Success 200 "OK"
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param fileID path int true "ID of the file to download"
// @Router /files/{fileID} [get]
func GetFile(c *gin.Context){
// TODO
}
// UpdateFile godoc
// @Summary Update a file
// @ID UpdateFile
// @Tags files
// @Produce json
// @Accept text/plain
// @Accept png
// @Accept jpeg
// @Accept gif
// @Accept model/x-cim
// @Accept model/x-cim.zip
// @Success 200 "OK"
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param fileID path int true "ID of the file to update"
// @Router /files/{fileID} [put]
func UpdateFile(c *gin.Context){
//TODO parse this info based on fileID parameter
simulationID := 1
modelID := 1
widgetID := 1
// Extract file from PUT request form
err := c.Request.ParseForm()
if err != nil {
errormsg := fmt.Sprintf("Bad request. Get form error: %s", err.Error())
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return;
}
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;
}
filename := filepath.Base(file_header.Filename)
filetype := file_header.Header.Get("Content-Type") // TODO make sure this is properly set in file header
size := file_header.Size
foldername := getFolderName(simulationID, modelID, widgetID)
err = modifyFileOnDisc(file_header, filename, foldername, uint(size), false)
if err != nil {
errormsg := fmt.Sprintf("Internal Server Error. Error saving file: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": errormsg,
})
return
}
saveFileInDB(c, filename, foldername, filetype, uint(size), widgetID, modelID, false)
}
// DeleteFile godoc
// @Summary Delete a file
// @ID DeleteFile
// @Tags files
// @Produce json
// @Success 200 "OK"
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param fileID path int true "ID of the file to update"
// @Router /files/{fileID} [delete]
func DeleteFile(c *gin.Context){
// TODO
}
func GetFilesOfModel(c *gin.Context) {
simulationID, modelID, err := getRequestParams(c)
if err != nil{
return
}
// Find files' properties in DB and return in HTTP response, no change to DB
allFiles, _, err := FindFiles(c, -1, modelID, simulationID)
if common.ProvideErrorResponse(c, err) == false {
serializer := common.FilesSerializerNoAssoc{c, allFiles}
c.JSON(http.StatusOK, gin.H{
"files": serializer.Response(),
})
}
}
func AddFileToModel(c *gin.Context) {
simulationID, modelID, err := getRequestParams(c)
if err != nil{
return
}
// Save file locally and register file in DB, HTTP response is set by this method
RegisterFile(c,-1, modelID, simulationID)
}
func CloneFileOfModel(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func fileUpdateEp(c *gin.Context) {
func GetFileOfModel(c *gin.Context) {
simulationID, modelID, err := getRequestParams(c)
if err != nil{
return
}
// Read file from disk and return in HTTP response, no change to DB
ReadFile(c, -1, modelID, simulationID)
}
func UpdateFileOfModel(c *gin.Context) {
//simulationID, modelID, err := getRequestParams(c)
//if err != nil{
// return
//}
// Update file locally and update file entry in DB, HTTP response is set by this method
//UpdateFile(c,-1, modelID, simulationID)
}
func DeleteFileOfModel(c *gin.Context) {
//simulationID, modelID, err := getRequestParams(c)
//if err != nil{
// return
//}
// Delete file from disk and remove entry from DB, HTTP response is set by this method
//DeleteFile(c, -1, modelID, simulationID)
}
func GetFilesOfWidget(c *gin.Context) {
simulationID, widgetID, err := getRequestParams(c)
if err != nil{
return
}
// Find files' properties in DB and return in HTTP response, no change to DB
allFiles, _, err := FindFiles(c, widgetID, -1, simulationID)
if common.ProvideErrorResponse(c, err) == false {
serializer := common.FilesSerializerNoAssoc{c, allFiles}
c.JSON(http.StatusOK, gin.H{
"files": serializer.Response(),
})
}
}
func AddFileToWidget(c *gin.Context) {
simulationID, widgetID, err := getRequestParams(c)
if err != nil{
return
}
// Save file locally and register file in DB, HTTP response is set by this method
RegisterFile(c,widgetID, -1, simulationID)
}
func CloneFileOfWidget(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func fileReadEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
func GetFileOfWidget(c *gin.Context) {
simulationID, widgetID, err := getRequestParams(c)
if err != nil{
return
}
// Read file from disk and return in HTTP response, no change to DB
ReadFile(c, widgetID, -1, simulationID)
}
func fileDeleteEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
func UpdateFileOfWidget(c *gin.Context) {
//simulationID, widgetID, err := getRequestParams(c)
//if err != nil{
// return
//}
//
//// Update file locally and update file entry in DB, HTTP response is set by this method
//UpdateFile(c,widgetID, -1, simulationID)
}
func DeleteFileOfWidget(c *gin.Context) {
//simulationID, widgetID, err := getRequestParams(c)
//if err != nil{
// return
//}
//
//// Delete file from disk and remove entry from DB, HTTP response is set by this method
//DeleteFile(c, widgetID, -1, simulationID)
}
// local functions
//func filesReadEp(c *gin.Context) {
// // Database query
// allFiles, _, err := FindAllFiles()
//
// if common.ProvideErrorResponse(c, err) == false {
// serializer := FilesSerializerNoAssoc{c, allFiles}
// c.JSON(http.StatusOK, gin.H{
// "files": serializer.Response(),
// })
// }
//
//}
//
//
//
//func fileUpdateEp(c *gin.Context) {
// c.JSON(http.StatusOK, gin.H{
// "message": "NOT implemented",
// })
//}
//
//func fileReadEp(c *gin.Context) {
// var err error
// var file common.File
// fileID := c.Param("FileID")
// desc := c.GetHeader("X-Request-FileDesc")
// desc_b, _ := strconv.ParseBool(desc)
//
// userID := 1 // TODO obtain ID of user making the request
//
// //check if description of file or file itself shall be returned
// if desc_b {
// file, err = FindFile(userID, fileID)
// if common.ProvideErrorResponse(c, err) == false {
// serializer := FileSerializerNoAssoc{c, file}
// c.JSON(http.StatusOK, gin.H{
// "file": serializer.Response(),
// })
// }
//
//
// } else {
// //TODO: return file itself
// }
//}
//
//func fileDeleteEp(c *gin.Context) {
// c.JSON(http.StatusOK, gin.H{
// "message": "NOT implemented",
// })
//}
func getRequestParams(c *gin.Context) (int, int, error){
simulationID, err := strconv.Atoi(c.Param("SimulationID"))
if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of simulation ID")
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return -1, -1, err
}
var subID int
subID, err = common.GetModelID(c)
if err != nil{
subID, err = common.GetWidgetID(c)
if err != nil {
return -1, -1, err
}
}
return simulationID, subID, err
}

View file

@ -1,19 +1,352 @@
package file
import (
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"strconv"
"github.com/gin-gonic/gin"
_ "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/model"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget"
)
func FindAllFiles() ([]common.File, int, error) {
//func FindAllFiles() ([]common.File, int, error) {
// db := common.GetDB()
// var files []common.File
// err := db.Find(&files).Error
// if err != nil {
// // print error message to screen
// fmt.Println(fmt.Errorf("DB Error in FindAllFiles(): %q", err).Error())
// }
// return files, len(files), err
//}
//
//func FindUserFiles(user *common.User) ([]common.File, int, error) {
// db := common.GetDB()
// var files []common.File
// err := db.Model(user).Related(&files, "Files").Error
// return files, len(files), err
//}
//
//func FindFile(userID int, fileID string) ( common.File, error) {
// var file common.File
// db := common.GetDB()
// fileID_i, _ := strconv.Atoi(fileID)
//
// err := db.First(&file, fileID_i).Error
//
// return file, err
//
//}
func FindFiles(c *gin.Context, widgetID int, modelID int, simulationID int) ([]common.File, int, error){
db := common.GetDB()
var files []common.File
err := db.Find(&files).Error
var err error
if widgetID != -1 {
var w common.Widget
err = db.First(&w, widgetID).Error
if err != nil {
return files, 0, err
}
err = db.Model(&w).Related(&files, "Files").Error
if err != nil {
return files, 0, err
}
} else if modelID != -1 {
var m common.Model
err = db.First(&m, modelID).Error
if err != nil {
return files, 0, err
}
err = db.Model(&m).Related(&files, "Files").Error
if err != nil {
return files, 0, err
}
}
return files, len(files), err
}
func FindUserFiles(user *common.User) ([]common.File, int, error) {
func FindFileByPath(path string) (common.File, error) {
var file common.File
db := common.GetDB()
var files []common.File
err := db.Model(user).Related(&files, "Files").Error
return files, len(files), err
err := db.Where("Path = ?", path).Find(file).Error
return file, err
}
func RegisterFile(c *gin.Context, widgetID int, modelID int, simulationID int){
// 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;
}
// Obtain properties of file
filetype := file_header.Header.Get("Content-Type") // TODO make sure this is properly set in file header
filename := filepath.Base(file_header.Filename)
foldername := getFolderName(simulationID, modelID, widgetID)
size := file_header.Size
// Check if simulation and widget or model exist in DB
_, err = simulation.FindSimulation(simulationID)
if common.ProvideErrorResponse(c, err) {
return
}
if modelID != -1 {
_, err = model.FindModel(modelID)
if common.ProvideErrorResponse(c, err) {
return
}
} else if widgetID != -1 {
_, err = widget.FindWidget(widgetID)
if common.ProvideErrorResponse(c, err) {
return
}
}
// Save file to local disc (NOT DB!)
err = modifyFileOnDisc(file_header, filename, foldername, uint(size), true)
if err != nil {
errormsg := fmt.Sprintf("Internal Server Error. Error saving file: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": errormsg,
})
return
}
// Add File object with parameters to DB
saveFileInDB(c, filename, foldername, filetype, uint(size), widgetID, modelID, true)
}
func ReadFile(c *gin.Context, widgetID int, modelID int, simulationID int){
//contentType := c.GetHeader("Content-Type")
//TODO currently returns first file it finds in DB
db := common.GetDB()
var fileInDB common.File
if widgetID != -1 {
// get associated Widget
var wdgt common.Widget
err := db.First(&wdgt, modelID).Error
if common.ProvideErrorResponse(c, err) {
return
}
err = db.Model(&wdgt).Related(&fileInDB).Error
if common.ProvideErrorResponse(c, err) {
return
}
} else if modelID != -1 {
// get associated Simulation Model
var model common.Model
err := db.First(&model, modelID).Error
if common.ProvideErrorResponse(c, err) {
return
}
err = db.Model(&model).Related(&fileInDB).Error
if common.ProvideErrorResponse(c, err) {
return
}
}
//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="+fileInDB.Name )
//c.Header("Content-Type", contentType)
c.File(fileInDB.Path)
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}
func saveFileInDB(c *gin.Context, filename string, foldername string, filetype string, size uint, widgetID int, modelID int, createObj bool) {
filesavepath := filepath.Join(foldername, filename)
// get last modify time of target file
fileinfo, err := os.Stat(filesavepath)
if err != nil {
errormsg := fmt.Sprintf("Internal Server Error. Error stat on file: %s", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": errormsg,
})
return
}
modTime := fileinfo.ModTime()
// Create file object for Database
var fileObj common.File
fileObj.Size = uint(size)
fileObj.Type = filetype
fileObj.Path = filesavepath
fileObj.Date = modTime
fileObj.Name = filename
fileObj.ImageHeight = 0
fileObj.ImageWidth = 0
// Check if file shall be associated with Widget or Simulation Model
db := common.GetDB()
if widgetID != -1 {
if createObj {
// associate to Widget
var wdgt common.Widget
err := db.First(&wdgt, widgetID).Error
if common.ProvideErrorResponse(c, err) {
return
}
err = db.Model(&wdgt).Association("Files").Append(&fileObj).Error
} else {
// update file obj in DB
fileInDB, err := FindFileByPath(filesavepath)
if common.ProvideErrorResponse(c, err){
return
}
err = db.Model(&fileInDB).Where("Path = ?", filesavepath).Updates(map[string]interface{}{"Size": fileObj.Size, "Date": fileObj.Date, "ImageHeight": fileObj.ImageHeight, "ImageWidth": fileObj.ImageWidth}).Error
}
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
"fileID": fileObj.ID,
})
return
}
}
if modelID != -1 {
if createObj {
// associate to Simulation Model
db := common.GetDB()
var model common.Model
err := db.First(&model, modelID).Error
if common.ProvideErrorResponse(c, err) {
return
}
err = db.Model(&model).Association("Files").Append(&fileObj).Error
} else {
// update file obj in DB
fileInDB, err := FindFileByPath(filesavepath)
if common.ProvideErrorResponse(c, err){
return
}
err = db.Model(&fileInDB).Where("Path = ?", filesavepath).Updates(map[string]interface{}{"Size": fileObj.Size, "Date": fileObj.Date, "ImageHeight": fileObj.ImageHeight, "ImageWidth": fileObj.ImageWidth}).Error
}
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
"fileID": fileObj.ID,
})
return
}
}
}
func modifyFileOnDisc(file_header *multipart.FileHeader, filename string, foldername string, size uint, createFile bool) error {
filesavepath := filepath.Join(foldername, filename)
var err error
if createFile {
// Ensure folder with name foldername exists
err = os.MkdirAll(foldername, os.ModePerm)
} else {
// test if file exists
_, err = os.Stat(filesavepath)
}
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(filesavepath, open_options, 0666)
if err != nil {
return err
}
defer fileTarget.Close()
// Save file to target path
uploadedFile, err := file_header.Open()
if err != nil {
return err
}
defer uploadedFile.Close()
var uploadContent = make([]byte, 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 err != nil {
return err
}
}
return err
}
func getFolderName(simulationID int, modelID int, widgetID int) string {
base_foldername := "files/"
elementname := ""
elementid := 0
if modelID == -1{
elementname = "/widget_"
elementid = widgetID
} else {
elementname = "/model_"
elementid = modelID
}
foldername := base_foldername + "simulation_"+ strconv.Itoa(simulationID) + elementname + strconv.Itoa(elementid) + "/"
return foldername
}

View file

@ -1,53 +0,0 @@
package file
import (
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
// File/s Serializers
type FilesSerializerNoAssoc struct {
Ctx *gin.Context
Files []common.File
}
func (self *FilesSerializerNoAssoc) Response() []FileResponseNoAssoc {
response := []FileResponseNoAssoc{}
for _, files := range self.Files {
serializer := FileSerializerNoAssoc{self.Ctx, files}
response = append(response, serializer.Response())
}
return response
}
type FileSerializerNoAssoc struct {
Ctx *gin.Context
common.File
}
type FileResponseNoAssoc 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
}
func (self *FileSerializerNoAssoc) Response() FileResponseNoAssoc {
response := FileResponseNoAssoc{
Name: self.Name,
ID: self.ID,
Path: self.Path,
Type: self.Type,
Size: self.Size,
H: self.ImageHeight,
W: self.ImageWidth,
// Date
}
return response
}

View file

@ -0,0 +1,355 @@
package model
import (
"fmt"
"net/http"
"strconv"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulator"
"github.com/gin-gonic/gin"
)
func RegisterModelEndpoints(r *gin.RouterGroup){
r.GET("/", GetModels)
r.POST("/", AddModel)
//r.POST("/:modelID", CloneModel)
r.PUT("/:modelID", UpdateModel)
r.GET("/:modelID", GetModel)
r.DELETE("/:modelID", DeleteModel)
//r.PUT("/:modelID/simulator", UpdateSimulator)
//r.GET("/:modelID/simulator", GetSimulator)
//r.POST("/:modelID/signals/:direction", UpdateSignals)
//r.GET("/:modelID/signals/:direction", GetSignals)
}
// GetModels godoc
// @Summary Get all models of simulation
// @ID GetModels
// @Produce json
// @Tags models
// @Success 200 {array} common.ModelResponse "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 GetModels(c *gin.Context) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
allModels, _, err := FindAllModels(simID)
if common.ProvideErrorResponse(c, err) {
return
}
serializer := common.ModelsSerializer{c, allModels}
c.JSON(http.StatusOK, gin.H{
"models": serializer.Response(),
})
}
// AddModel godoc
// @Summary Add a model to a simulation
// @ID AddModel
// @Accept json
// @Produce json
// @Tags models
// @Param inputModel body common.ModelResponse true "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 AddModel(c *gin.Context) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
var m Model
err = c.BindJSON(&m)
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.addToSimulation(simID)
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}
}
func CloneModel(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.",
})
}
// UpdateModel godoc
// @Summary Update a model
// @ID UpdateModel
// @Tags models
// @Accept json
// @Produce json
// @Param inputModel body common.ModelResponse true "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 UpdateModel(c *gin.Context) {
modelID, err := common.GetModelID(c)
if err != nil {
return
}
var m Model
err = c.BindJSON(&m)
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.UpdateModel(modelID)
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}
}
// GetModel godoc
// @Summary Get a model
// @ID GetModel
// @Tags models
// @Produce json
// @Success 200 {object} common.ModelResponse "Requested 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 GetModel(c *gin.Context) {
modelID, err := common.GetModelID(c)
if err != nil {
return
}
m, err := FindModel(modelID)
if common.ProvideErrorResponse(c, err) {
return
}
serializer := common.ModelSerializer{c, m}
c.JSON(http.StatusOK, gin.H{
"model": serializer.Response(),
})
}
// DeleteModel godoc
// @Summary Delete a model
// @ID DeleteModel
// @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 DeleteModel(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "Not implemented.",
})
}
func GetSimulator(c *gin.Context) {
modelID, err := common.GetModelID(c)
if err != nil {
return
}
m, err := FindModel(modelID)
if common.ProvideErrorResponse(c, err) {
return
}
smtr, err := simulator.FindSimulator(int(m.SimulatorID))
if common.ProvideErrorResponse(c, err) {
return
}
serializer := common.SimulatorSerializer{c, smtr}
c.JSON(http.StatusOK, gin.H{
"simulator": serializer.Response(),
})
}
func UpdateSimulator(c *gin.Context) {
// simulator ID as parameter of Query, e.g. simulations/:SimulationID/models/:ModelID/simulator?simulatorID=42
simulatorID, err := strconv.Atoi(c.Query("simulatorID"))
if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect simulator ID")
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return
}
modelID, err := common.GetModelID(c)
if err != nil {
return
}
smtr, err := simulator.FindSimulator(simulatorID)
if common.ProvideErrorResponse(c, err) {
return
}
_m, err := FindModel(modelID)
if common.ProvideErrorResponse(c, err) {
return
}
var m = Model{_m}
err = m.UpdateSimulator(&smtr)
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK",
})
}
}
func UpdateSignals(c *gin.Context) {
modelID, err := common.GetModelID(c)
if err != nil {
return
}
_m, err := FindModel(modelID)
if common.ProvideErrorResponse(c, err) {
return
}
direction := c.Param("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
}
var sigs []common.Signal
err = c.BindJSON(&sigs)
if err != nil {
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return
}
// Add signals to model and remove all existing Signals of the requested direction (if any)
var m = Model{_m}
err = m.UpdateSignals(sigs, direction)
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}
}
func GetSignals(c *gin.Context) {
modelID, err := common.GetModelID(c)
if err != nil {
return
}
m, err := FindModel(modelID)
if common.ProvideErrorResponse(c, err) {
return
}
direction := c.Param("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
}
var signals []common.Signal
if direction == "in" {
signals = m.InputMapping
} else {
signals = m.OutputMapping
}
c.JSON(http.StatusOK, gin.H{
"signals": signals,
})
}

View file

@ -0,0 +1,79 @@
package model
import (
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
)
type Model struct{
common.Model
}
func FindAllModels(simID int) ([]common.Model, int, error) {
db := common.GetDB()
var models []common.Model
sim, err := simulation.FindSimulation(simID)
if err != nil {
return models, 0, err
}
err = db.Order("ID asc").Model(sim).Related(&models, "Models").Error
return models, len(models), err
}
func FindModel(modelID int) (common.Model, error){
db := common.GetDB()
var m common.Model
err := db.First(&m, modelID).Error
return m, err
}
func (m *Model) addToSimulation(simID int) error {
db := common.GetDB()
sim, err := simulation.FindSimulation(simID)
if err != nil {
return err
}
err = db.Create(m).Error
if err != nil {
return err
}
err = db.Model(&sim).Association("Models").Append(m).Error
return err
}
func (m *Model) UpdateModel(modelID int) error {
db := common.GetDB()
model_to_update, err := FindModel(modelID)
if err != nil {
return err
}
// only Name and Start Params can be updated directly by the user
err = db.Model(&model_to_update).Updates(map[string]interface{}{"Name": m.Name, "StartParameters": m.StartParameters}).Error
return err
}
func (m *Model) UpdateSimulator(simulator *common.Simulator) error {
db := common.GetDB()
err := db.Model(m).Association("Simulator").Replace(simulator).Error
return err
}
func (m *Model) UpdateSignals(signals []common.Signal, direction string) error {
db := common.GetDB()
var err error
if direction == "in" {
err = db.Model(m).Select("InputMapping").Update("InputMapping", signals).Error
} else {
err = db.Model(m).Select("OutputMapping").Update("OutputMapping", signals).Error
}
return err
}

View file

@ -1,46 +0,0 @@
package project
import (
"github.com/gin-gonic/gin"
"net/http"
)
func ProjectsRegister(r *gin.RouterGroup) {
r.GET("/", projectsReadEp)
r.POST("/", projectRegistrationEp)
r.PUT("/:ProjectID", projectUpdateEp)
r.GET("/:ProjectID", projectReadEp)
r.DELETE("/:ProjectID", projectDeleteEp)
}
func projectsReadEp(c *gin.Context) {
allProjects, _, _ := FindAllProjects()
serializer := ProjectsSerializerNoAssoc{c, allProjects}
c.JSON(http.StatusOK, gin.H{
"projects": serializer.Response(),
})
}
func projectRegistrationEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func projectUpdateEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func projectReadEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func projectDeleteEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}

View file

@ -1,28 +0,0 @@
package project
import (
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
func FindAllProjects() ([]common.Project, int, error) {
db := common.GetDB()
var projects []common.Project
err := db.Find(&projects).Error
return projects, len(projects), err
}
func FindUserProjects(user *common.User) ([]common.Project, int, error) {
db := common.GetDB()
var projects []common.Project
err := db.Model(user).Related(&projects, "Projects").Error
return projects, len(projects), err
}
func FindVisualizationProject(visualization *common.Visualization) (common.Project, int, error) {
db := common.GetDB()
var project common.Project
err := db.Model(visualization).Related(&project, "Projects").Error
return project, 1, err
}

View file

@ -1,41 +0,0 @@
package project
import (
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
type ProjectsSerializerNoAssoc struct {
Ctx *gin.Context
Projects []common.Project
}
func (self *ProjectsSerializerNoAssoc) Response() []ProjectResponseNoAssoc {
response := []ProjectResponseNoAssoc{}
for _, project := range self.Projects {
serializer := ProjectSerializerNoAssoc{self.Ctx, project}
response = append(response, serializer.Response())
}
return response
}
type ProjectSerializerNoAssoc struct {
Ctx *gin.Context
common.Project
}
type ProjectResponseNoAssoc struct {
Name string `json:"Name"`
ID uint `json:"ProjectID"`
}
func (self *ProjectSerializerNoAssoc) Response() ProjectResponseNoAssoc {
response := ProjectResponseNoAssoc{
Name: self.Name,
ID: self.ID,
}
return response
}

View file

@ -1,4 +0,0 @@
package signal
//TODO extend API with signal endpoints

View file

@ -1,3 +0,0 @@
package signal
//TODO extend API with signal endpoints

View file

@ -1,3 +0,0 @@
package signal
//TODO extend API with signal endpoints

View file

@ -1,47 +1,258 @@
package simulation
import (
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
"net/http"
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
func SimulationsRegister(r *gin.RouterGroup) {
r.GET("/", simulationsReadEp)
r.POST("/", simulationRegistrationEp)
r.PUT("/:SimulationID", simulationUpdateEp)
r.GET("/:SimulationID", simulationReadEp)
r.DELETE("/:SimulationID", simulationDeleteEp)
func RegisterSimulationEndpoints(r *gin.RouterGroup){
r.GET("/", GetSimulations)
r.POST("/", AddSimulation)
//r.POST("/:simulationID", CloneSimulation)
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)
}
func simulationsReadEp(c *gin.Context) {
// 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) {
//TODO Identify user who is issuing the request and return only those simulations that are known to the user
allSimulations, _, _ := FindAllSimulations()
serializer := SimulationsSerializerNoAssoc{c, allSimulations}
serializer := common.SimulationsSerializer{c, allSimulations}
c.JSON(http.StatusOK, gin.H{
"simulations": serializer.Response(),
})
}
func simulationRegistrationEp(c *gin.Context) {
// AddSimulation godoc
// @Summary Add a simulation
// @ID AddSimulation
// @Accept json
// @Produce json
// @Tags simulations
// @Param inputModel body common.ModelResponse 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) {
}
func CloneSimulation(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func simulationUpdateEp(c *gin.Context) {
// 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) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func simulationReadEp(c *gin.Context) {
// 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) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
sim, err := FindSimulation(simID)
if common.ProvideErrorResponse(c, err) {
return
}
serializer := common.SimulationSerializer{c, sim}
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) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func simulationDeleteEp(c *gin.Context) {
// 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) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
sim, err := FindSimulation(simID)
if common.ProvideErrorResponse(c, err) {
return
}
// Find all users of simulation
allUsers, _, err := user.FindAllUsersSim(&sim)
if common.ProvideErrorResponse(c, err) {
return
}
serializer := common.UsersSerializer{c, allUsers}
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
"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) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
sim, err := FindSimulation(simID)
if common.ProvideErrorResponse(c, err) {
return
}
querydata := c.Request.URL.Query()
username := querydata.Get("username")
u, err := user.FindUserByName(username)
if common.ProvideErrorResponse(c, err) {
return
}
err = user.AddUserToSim(&sim, &u)
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) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
sim, err := FindSimulation(simID)
if common.ProvideErrorResponse(c, err) {
return
}
querydata := c.Request.URL.Query()
username := querydata.Get("username")
err = user.RemoveUserFromSim(&sim, username)
if common.ProvideErrorResponse(c, err) {
return
}
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}

View file

@ -4,16 +4,27 @@ import (
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
type Simulation struct{
common.Simulation
}
func FindAllSimulations() ([]common.Simulation, int, error) {
db := common.GetDB()
var simulations []common.Simulation
err := db.Find(&simulations).Error
err := db.Order("ID asc").Find(&simulations).Error
return simulations, len(simulations), err
}
func FindUserSimulations(user *common.User) ([]common.Simulation, int, error) {
db := common.GetDB()
var simulations []common.Simulation
err := db.Model(user).Related(&simulations, "Simulations").Error
err := db.Order("ID asc").Model(user).Related(&simulations, "Simulations").Error
return simulations, len(simulations), err
}
func FindSimulation(simID int) (common.Simulation, error) {
db := common.GetDB()
var sim common.Simulation
err := db.First(&sim, simID).Error
return sim, err
}

View file

@ -1,43 +0,0 @@
package simulation
import (
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
type SimulationsSerializerNoAssoc struct {
Ctx *gin.Context
Simulations []common.Simulation
}
func (self *SimulationsSerializerNoAssoc) Response() []SimulationResponseNoAssoc {
response := []SimulationResponseNoAssoc{}
for _, simulation := range self.Simulations {
serializer := SimulationSerializerNoAssoc{self.Ctx, simulation}
response = append(response, serializer.Response())
}
return response
}
type SimulationSerializerNoAssoc struct {
Ctx *gin.Context
common.Simulation
}
type SimulationResponseNoAssoc struct {
Name string `json:"Name"`
ID uint `json:"SimulationID"`
Running bool `json:"Running"`
//StartParams postgres.Jsonb `json:"Starting Parameters"`
}
func (self *SimulationSerializerNoAssoc) Response() SimulationResponseNoAssoc {
response := SimulationResponseNoAssoc{
Name: self.Name,
ID: self.ID,
Running: self.Running,
//StartParams: self.StartParameters,
}
return response
}

View file

@ -0,0 +1,127 @@
package simulation
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
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 msgUsers = common.ResponseMsgUsers{
Users: myUsers,
}
var simulationA = common.SimulationResponse{
Name: "Simulation_A",
ID: 1,
Running: false,
}
var simulationB = common.SimulationResponse{
Name: "Simulation_B",
ID: 2,
Running: false,
}
var mySimulations = []common.SimulationResponse{
simulationA,
simulationB,
}
var msgSimulations = common.ResponseMsgSimulations{
Simulations: mySimulations,
}
var msgSimulation = common.ResponseMsgSimulation{
Simulation: simulationA,
}
// Test /simulation endpoints
func TestSimulationEndpoints(t *testing.T) {
db := common.DummyInitDB()
defer db.Close()
common.DummyPopulateDB(db)
router := gin.Default()
api := router.Group("/api")
RegisterSimulationEndpoints(api.Group("/simulations"))
msgOKjson, err := json.Marshal(msgOK)
if err !=nil {
panic(err)
}
msgUsersjson, err := json.Marshal(msgUsers)
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)
}
// test GET simulations/
testEndpoint(t, router, "/api/simulations/", "GET", "", 200, string(msgSimulationsjson))
// test GET simulations/:SimulationID
testEndpoint(t, router, "/api/simulations/1", "GET", "", 200, string(msgSimulationjson))
// test GET simulations/:SimulationID/users
testEndpoint(t, router, "/api/simulations/1/users", "GET", "", 200, string(msgUsersjson))
// test DELETE simulations/:SimulationID/user
testEndpoint(t, router, "/api/simulations/1/user?username=User_A", "DELETE", "", 200, string(msgOKjson))
// test PUT simulations/:SimulationID/user
testEndpoint(t, router, "/api/simulations/1/user?username=User_A", "PUT", "", 200, string(msgOKjson))
// TODO add more tests
}
func testEndpoint(t *testing.T, router *gin.Engine, url string, method string, body string, expected_code int, expected_response string ) {
w := httptest.NewRecorder()
req, _ := http.NewRequest(method, url, nil)
router.ServeHTTP(w, req)
assert.Equal(t, expected_code, w.Code)
fmt.Println(w.Body.String())
assert.Equal(t, expected_response, w.Body.String())
}

View file

@ -1,60 +0,0 @@
package simulationmodel
import (
"github.com/gin-gonic/gin"
"net/http"
)
func SimulationModelsRegister(r *gin.RouterGroup) {
r.GET("/", simulationmodelsReadEp)
r.POST("/", simulationmodelRegistrationEp)
r.PUT("/:SimulationModelID", simulationmodelUpdateEp)
r.GET("/:SimulationModelID", simulationmodelReadEp)
r.DELETE("/:SimulationModelID", simulationmodelDeleteEp)
r.GET("/:SimulationModelID/file", simulationmodelReadFileEp) // NEW in API
r.PUT("/:SimulationModelID/file", simulationmodelUpdateFileEp) // NEW in API
}
func simulationmodelsReadEp(c *gin.Context) {
allSimulationModels, _, _ := FindAllSimulationModels()
serializer := SimulationModelsSerializerNoAssoc{c, allSimulationModels}
c.JSON(http.StatusOK, gin.H{
"simulationmodels": serializer.Response(),
})
}
func simulationmodelRegistrationEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func simulationmodelUpdateEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func simulationmodelReadEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func simulationmodelDeleteEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func simulationmodelReadFileEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func simulationmodelUpdateFileEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}

View file

@ -1,12 +0,0 @@
package simulationmodel
import (
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
func FindAllSimulationModels() ([]common.SimulationModel, int, error) {
db := common.GetDB()
var simulationmodels []common.SimulationModel
err := db.Find(&simulationmodels).Error
return simulationmodels, len(simulationmodels), err
}

View file

@ -1,52 +0,0 @@
package simulationmodel
import (
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
type SimulationModelsSerializerNoAssoc struct {
Ctx *gin.Context
SimulationModels []common.SimulationModel
}
func (self *SimulationModelsSerializerNoAssoc) Response() []SimulationModelResponseNoAssoc {
response := []SimulationModelResponseNoAssoc{}
for _, simulationmodel := range self.SimulationModels {
serializer := SimulationModelSerializerNoAssoc{self.Ctx, simulationmodel}
response = append(response, serializer.Response())
}
return response
}
type SimulationModelSerializerNoAssoc struct {
Ctx *gin.Context
common.SimulationModel
}
type SimulationModelResponseNoAssoc struct {
Name string `json:"Name"`
OutputLength int `json:"OutputLength"`
InputLength int `json:"InputLength"`
BelongsToSimulationID uint `json:"BelongsToSimulationID"`
BelongsToSimulatorID uint `json:"BelongsToSimulatiorID"`
//StartParams postgres.Jsonb `json:"Starting Parameters"`
//Output Mapping
//Input Mapping
}
func (self *SimulationModelSerializerNoAssoc) Response() SimulationModelResponseNoAssoc {
response := SimulationModelResponseNoAssoc{
Name: self.Name,
OutputLength: self.OutputLength,
InputLength: self.InputLength,
BelongsToSimulationID: self.BelongsToSimulationID,
BelongsToSimulatorID: self.BelongsToSimulatorID,
//StartParams: self.StartParameters,
//InputMapping
//OutputMapping
}
return response
}

View file

@ -1,53 +1,134 @@
package simulator
import (
"github.com/gin-gonic/gin"
"net/http"
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
func SimulatorsRegister(r *gin.RouterGroup) {
r.GET("/", simulatorsReadEp)
r.POST("/", simulatorRegistrationEp)
r.PUT("/:SimulatorID", simulatorUpdateEp)
r.GET("/:SimulatorID", simulatorReadEp)
r.DELETE("/:SimulatorID", simulatorDeleteEp)
r.POST("/:SimulatorID", simulatorSendActionEp)
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)
}
func simulatorsReadEp(c *gin.Context) {
// 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) {
allSimulators, _, _ := FindAllSimulators()
serializer := SimulatorsSerializer{c, allSimulators}
serializer := common.SimulatorsSerializer{c, allSimulators}
c.JSON(http.StatusOK, gin.H{
"simulators": serializer.Response(),
})
}
func simulatorRegistrationEp(c *gin.Context) {
// 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",
})
}
func simulatorUpdateEp(c *gin.Context) {
// 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",
})
}
func simulatorReadEp(c *gin.Context) {
// 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",
})
}
func simulatorDeleteEp(c *gin.Context) {
// 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",
})
}
func simulatorSendActionEp(c *gin.Context) {
// 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",
})
}
}

View file

@ -7,7 +7,14 @@ import (
func FindAllSimulators() ([]common.Simulator, int, error) {
db := common.GetDB()
var simulators []common.Simulator
err := db.Find(&simulators).Error
err := db.Order("ID asc").Find(&simulators).Error
return simulators, len(simulators), err
}
func FindSimulator(simulatorID int) (common.Simulator, error) {
db := common.GetDB()
var simulator common.Simulator
err := db.First(&simulator, simulatorID).Error
return simulator, err
}

View file

@ -1,52 +0,0 @@
package simulator
import (
"time"
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
type SimulatorsSerializer struct {
Ctx *gin.Context
Simulators []common.Simulator
}
func (self *SimulatorsSerializer) Response() []SimulatorResponse {
response := []SimulatorResponse{}
for _, simulator := range self.Simulators {
serializer := SimulatorSerializer{self.Ctx, simulator}
response = append(response, serializer.Response())
}
return response
}
type SimulatorSerializer struct {
Ctx *gin.Context
common.Simulator
}
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
// Raw Properties
}
func (self *SimulatorSerializer) Response() SimulatorResponse {
response := SimulatorResponse{
UUID: self.UUID,
Host: self.Host,
ModelType: self.Modeltype,
Uptime: self.Uptime,
State: self.State,
StateUpdateAt: self.StateUpdateAt,
}
return response
}

View file

@ -8,6 +8,8 @@ import (
"net/http"
"strconv"
"time"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
// TODO: the signing secret must be environmental variable
@ -20,19 +22,37 @@ type tokenClaims struct {
jwt.StandardClaims
}
type AuthResponse struct{
Success bool `json:"success"`
Message string `json:"message"`
Token string `json:"token"`
}
// `/authenticate` endpoint does not require Authentication
func VisitorAuthenticate(r *gin.RouterGroup) {
r.POST("", authenticationEp)
}
func UsersRegister(r *gin.RouterGroup) {
r.POST("", userRegistrationEp)
r.PUT("/:UserID", userUpdateEp)
r.GET("", usersReadEp)
r.GET("/:UserID", userReadEp)
r.DELETE("/:UserID", userDeleteEp)
func RegisterUserEndpoints(r *gin.RouterGroup) {
r.POST("", addUser)
r.PUT("/:UserID", updateUser)
r.GET("", getUsers)
r.GET("/:UserID", getUser)
r.DELETE("/:UserID", deleteUser)
}
// authenticationEp godoc
// @Summary Authentication for user
// @ID authenticationEp
// @Accept json
// @Produce json
// @Tags users
// @Param inputUser body user.Credentials true "Credentials of user"
// @Success 200 {object} user.AuthResponse "JSON web token and message"
// @Failure 401 "Unauthorized Access"
// @Failure 404 "Not found"
// @Failure 422 "Unprocessable entity."
// @Router /authenticate [post]
func authenticationEp(c *gin.Context) {
// Bind the response (context) with the Credentials struct
@ -101,7 +121,18 @@ func authenticationEp(c *gin.Context) {
})
}
func usersReadEp(c *gin.Context) {
// GetUsers godoc
// @Summary Get all users
// @ID GetUsers
// @Produce json
// @Tags users
// @Success 200 {array} common.UserResponse "Array of users"
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Router /users [get]
func getUsers(c *gin.Context) {
//// dummy TODO: check in the middleware if the user is authorized
//authorized := false
//// TODO: move this redirect in the authentication middleware
@ -110,13 +141,26 @@ func usersReadEp(c *gin.Context) {
//return
//}
allUsers, _, _ := FindAllUsers()
serializer := UsersSerializer{c, allUsers}
serializer := common.UsersSerializer{c, allUsers}
c.JSON(http.StatusOK, gin.H{
"users": serializer.Response(true),
})
}
func userRegistrationEp(c *gin.Context) {
// AddUser godoc
// @Summary Add a user
// @ID AddUser
// @Accept json
// @Produce json
// @Tags users
// @Param inputUser body common.UserResponse true "User to be added"
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Router /users [post]
func addUser(c *gin.Context) {
// Bind the response (context) with the User struct
var newUser User
@ -163,13 +207,39 @@ func userRegistrationEp(c *gin.Context) {
})
}
func userUpdateEp(c *gin.Context) {
// UpdateUser godoc
// @Summary Update a user
// @ID UpdateUser
// @Tags users
// @Accept json
// @Produce json
// @Param inputUser body common.UserResponse true "User to be updated"
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param userID path int true "User ID"
// @Router /users/{userID} [put]
func updateUser(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func userReadEp(c *gin.Context) {
// GetUser godoc
// @Summary Get user
// @ID GetUser
// @Produce json
// @Tags users
// @Success 200 {object} common.UserResponse "User requested by user"
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param userID path int true "User ID"
// @Router /users/{userID} [get]
func getUser(c *gin.Context) {
var user User
id, _ := strconv.ParseInt(c.Param("UserID"), 10, 64)
@ -180,13 +250,25 @@ func userReadEp(c *gin.Context) {
return
}
serializer := UserSerializer{c, user.User}
serializer := common.UserSerializer{c, user.User}
c.JSON(http.StatusOK, gin.H{
"user": serializer.Response(false),
})
}
func userDeleteEp(c *gin.Context) {
// DeleteUser godoc
// @Summary Delete a user
// @ID DeleteUser
// @Tags users
// @Produce json
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param userID path int true "User ID"
// @Router /users/{userID} [delete]
func deleteUser(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})

View file

@ -7,6 +7,46 @@ import (
func FindAllUsers() ([]common.User, int, error) {
db := common.GetDB()
var users []common.User
err := db.Find(&users).Error
err := db.Order("ID asc").Find(&users).Error
return users, len(users), err
}
func FindAllUsersSim(sim *common.Simulation) ([]common.User, int, error) {
db := common.GetDB()
var users []common.User
err := db.Order("ID asc").Model(sim).Related(&users, "Users").Error
return users, len(users), err
}
func FindUserByName(username string) (common.User, error){
db := common.GetDB()
var user common.User
err := db.Where("Username = ?", username).Find(&user).Error
return user, err
}
func AddUserToSim(sim *common.Simulation, user *common.User) error {
db := common.GetDB()
err := db.Model(sim).Association("Users").Append(user).Error
return err
}
func RemoveUserFromSim(sim *common.Simulation, username string) error {
db := common.GetDB()
user, err := FindUserByName(username)
if err != nil {
return err
}
// remove user from simulation
err = db.Model(sim).Association("Users").Delete(&user).Error
if err != nil {
return err
}
// remove simulation from user
err = db.Model(&user).Association("Simulations").Delete(sim).Error
return err
}

View file

@ -1,73 +0,0 @@
package user
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/file"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/project"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
)
type UsersSerializer struct {
Ctx *gin.Context
Users []common.User
}
func (self *UsersSerializer) Response(assoc bool) []UserResponse {
response := []UserResponse{}
for _, user := range self.Users {
serializer := UserSerializer{self.Ctx, user}
response = append(response, serializer.Response(assoc))
}
return response
}
type UserSerializer struct {
Ctx *gin.Context
common.User
}
type UserResponse struct {
Username string `json:"Username"`
Password string `json:"Password"` // XXX: this is the hashed pw
Role string `json:"Role"`
Mail string `json:"Mail"`
Projects []project.ProjectResponseNoAssoc
Simulations []simulation.SimulationResponseNoAssoc
Files []file.FileResponseNoAssoc
}
func (self *UserSerializer) Response(assoc bool) UserResponse {
response := UserResponse{
Username: self.Username,
Password: self.Password,
Role: self.Role,
Mail: self.Mail,
}
// Associated models MUST NOT called with assoc=true otherwise we
// will have an infinite loop due to the circular dependencies
if assoc {
// TODO: maybe all those should be made in one transaction
projects, _, _ := project.FindUserProjects(&self.User)
projectsSerializer :=
project.ProjectsSerializerNoAssoc{self.Ctx, projects}
simulations, _, _ := simulation.FindUserSimulations(&self.User)
simulationsSerializer :=
simulation.SimulationsSerializerNoAssoc{self.Ctx, simulations}
files, _, _ := file.FindUserFiles(&self.User)
filesSerializer := file.FilesSerializerNoAssoc{self.Ctx, files}
// Add the associated models to the response
response.Projects = projectsSerializer.Response()
response.Simulations = simulationsSerializer.Response()
response.Files = filesSerializer.Response()
}
return response
}

View file

@ -1,46 +1,211 @@
package visualization
import (
"github.com/gin-gonic/gin"
"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 VisualizationsRegister(r *gin.RouterGroup) {
r.GET("/", visualizationsReadEp)
r.POST("/", visualizationRegistrationEp)
r.PUT("/:VisualizationID", visualizationUpdateEp)
r.GET("/:VisualizationID", visualizationReadEp)
r.DELETE("/:VisualizationID", visualizationDeleteEp)
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)
}
func visualizationsReadEp(c *gin.Context) {
allVisualizations, _, _ := FindAllVisualizations()
serializer := VisualizationsSerializer{c, allVisualizations}
// 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 := common.GetSimulationID(c)
if err != nil {
return
}
sim, err := simulation.FindSimulation(simID)
if common.ProvideErrorResponse(c, err) {
return
}
allVisualizations, _, _ := FindAllVisualizationsOfSim(&sim)
serializer := common.VisualizationsSerializer{c, allVisualizations}
c.JSON(http.StatusOK, gin.H{
"visualizations": serializer.Response(),
})
}
func visualizationRegistrationEp(c *gin.Context) {
// 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) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
sim, err := simulation.FindSimulation(simID)
if common.ProvideErrorResponse(c, err) {
return
}
var vis common.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 = AddVisualizationToSim(&sim, &vis)
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",
})
}
func visualizationUpdateEp(c *gin.Context) {
// 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) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
sim, err := simulation.FindSimulation(simID)
if common.ProvideErrorResponse(c, err) {
return
}
visID, err := common.GetVisualizationID(c)
if err != nil {
return
}
var vis common.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
}
err = UpdateVisualizationOfSim(&sim, vis, visID)
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) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
sim, err := simulation.FindSimulation(simID)
if common.ProvideErrorResponse(c, err) {
return
}
visID, err := common.GetVisualizationID(c)
if err != nil {
return
}
visualization, err := FindVisualizationOfSim(&sim, visID)
if common.ProvideErrorResponse(c, err) {
return
}
serializer := common.VisualizationSerializer{c, 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",
})
}
func visualizationReadEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}
func visualizationDeleteEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}

View file

@ -4,9 +4,46 @@ import (
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
func FindAllVisualizations() ([]common.Visualization, int, error) {
func FindAllVisualizationsOfSim(sim *common.Simulation) ([]common.Visualization, int, error) {
db := common.GetDB()
var visualization []common.Visualization
err := db.Find(&visualization).Error
return visualization, len(visualization), err
var visualizations []common.Visualization
err := db.Order("ID asc").Model(sim).Related(&visualizations, "Visualizations").Error
return visualizations, len(visualizations), err
}
func FindVisualizationOfSim(sim *common.Simulation, visID int) (common.Visualization, error) {
db := common.GetDB()
var vis common.Visualization
err := db.Model(sim).Where("ID = ?", visID).Related(&vis, "Visualizations").Error
return vis, err
}
func AddVisualizationToSim(sim * common.Simulation, vis * common.Visualization) error {
db := common.GetDB()
// Add visualization to DB
err := db.Create(vis).Error
if err != nil {
return err
}
// Add association with simulation
err = db.Model(sim).Association("Visualizations").Append(vis).Error
return err
}
func UpdateVisualizationOfSim(sim * common.Simulation, vis common.Visualization, visID int) error {
db := common.GetDB()
// Get visualization of simulation that matches with ID (= visualization to be updated)
var vis_old common.Visualization
err := db.Model(sim).Where("ID = ?", visID).Related(&vis_old, "Visualizations").Error
if err != nil {
return err
}
// Update visualization in DB (only name and grid can be updated)
err = db.Model(&vis_old).Updates(map[string]interface{}{"Name": vis.Name, "Grid": vis.Grid}).Error
return err
}

View file

@ -1,59 +0,0 @@
package visualization
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/project"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget"
)
type VisualizationsSerializer struct {
Ctx *gin.Context
Visualizations []common.Visualization
}
func (self *VisualizationsSerializer) Response() []VisualizationResponse {
response := []VisualizationResponse{}
for _, visualization := range self.Visualizations {
serializer := VisualizationSerializer{self.Ctx, visualization}
response = append(response, serializer.Response())
}
return response
}
type VisualizationSerializer struct {
Ctx *gin.Context
common.Visualization
}
type VisualizationResponse struct {
Name string `json:"Name"`
UserID uint `json:"UserID"`
Grid int `json:"Grid"`
ProjectID uint `json:"ProjectID"`
Project project.ProjectResponseNoAssoc
Widgets []widget.WidgetResponse
}
func (self *VisualizationSerializer) Response() VisualizationResponse {
// TODO: maybe all those should be made in one transaction
p, _, _ := project.FindVisualizationProject(&self.Visualization)
projectSerializer := project.ProjectSerializerNoAssoc{self.Ctx, p}
w, _, _:= widget.FindVisualizationWidgets(&self.Visualization)
widgetsSerializer := widget.WidgetsSerializer{self.Ctx, w}
response := VisualizationResponse{
Name: self.Name,
UserID: self.UserID,
Grid: self.Grid,
ProjectID: self.ProjectID,
Project: projectSerializer.Response(),
Widgets: widgetsSerializer.Response(),
}
return response
}

View file

@ -1,3 +1,287 @@
package widget
// TODO add new API endpoints for widgets
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"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization"
)
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
// @ID GetWidgets
// @Produce json
// @Tags widgets
// @Success 200 {array} common.WidgetResponse "Array of widgets to which belong to visualization"
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param visualizationID query int true "Visualization ID"
// @Router /widgets [get]
func GetWidgets(c *gin.Context) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
sim, err := simulation.FindSimulation(simID)
if common.ProvideErrorResponse(c, err) {
return
}
visID, err := common.GetVisualizationID(c)
if err != nil {
return
}
vis, err := visualization.FindVisualizationOfSim(&sim, visID)
if common.ProvideErrorResponse(c, err) {
return
}
widgets,_, err := FindWidgetsOfVisualization(&vis)
if common.ProvideErrorResponse(c, err) {
return
}
serializer := common.WidgetsSerializer{c, widgets}
c.JSON(http.StatusOK, gin.H{
"widgets": serializer.Response(),
})
}
// AddWidget godoc
// @Summary Add a widget to a visualization
// @ID AddWidget
// @Accept json
// @Produce json
// @Tags widgets
// @Param inputWidget body common.WidgetResponse true "Widget to be added incl. ID of visualization"
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Router /widgets [post]
func AddWidget(c *gin.Context) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
sim, err := simulation.FindSimulation(simID)
if common.ProvideErrorResponse(c, err) {
return
}
visID, err := common.GetVisualizationID(c)
if err != nil {
return
}
vis, err := visualization.FindVisualizationOfSim(&sim, visID)
if common.ProvideErrorResponse(c, err) {
return
}
var widget_input common.Widget
err = c.BindJSON(&widget_input)
if err != nil {
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return
}
err = AddWidgetToVisualization(&vis, &widget_input)
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK.",
})
}
}
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"
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param widgetID path int true "Widget ID"
// @Router /widgets/{widgetID} [put]
func UpdateWidget(c *gin.Context) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
sim, err := simulation.FindSimulation(simID)
if common.ProvideErrorResponse(c, err) {
return
}
visID, err := common.GetVisualizationID(c)
if err != nil {
return
}
vis, err := visualization.FindVisualizationOfSim(&sim, visID)
if common.ProvideErrorResponse(c, err) {
return
}
widgetID, err := common.GetWidgetID(c)
if err != nil {
return
}
var widget_input common.Widget
err = c.BindJSON(&widget_input)
if err != nil {
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg,
})
return
}
err = UpdateWidgetOfVisualization(&vis, widget_input, widgetID)
if common.ProvideErrorResponse(c, err) == false {
c.JSON(http.StatusOK, gin.H{
"message": "OK",
})
}
}
// GetWidget godoc
// @Summary Get a widget
// @ID GetWidget
// @Tags widgets
// @Produce json
// @Success 200 {object} common.WidgetResponse "Requested widget."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param widgetID path int true "Widget ID"
// @Router /widgets/{widgetID} [get]
func GetWidget(c *gin.Context) {
simID, err := common.GetSimulationID(c)
if err != nil {
return
}
sim, err := simulation.FindSimulation(simID)
if common.ProvideErrorResponse(c, err) {
return
}
visID, err := common.GetVisualizationID(c)
if err != nil {
return
}
vis, err := visualization.FindVisualizationOfSim(&sim, visID)
if common.ProvideErrorResponse(c, err) {
return
}
widgetID, err := common.GetWidgetID(c)
if err != nil {
return
}
widget, err := FindWidgetOfVisualization(&vis, widgetID)
serializer := common.WidgetSerializer{c, widget}
c.JSON(http.StatusOK, gin.H{
"widget": serializer.Response(),
})
}
// DeleteWidget godoc
// @Summary Delete a widget
// @ID DeleteWidget
// @Tags widgets
// @Produce json
// @Success 200 "OK."
// @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden."
// @Failure 404 "Not found"
// @Failure 500 "Internal server error"
// @Param widgetID path int true "Widget ID"
// @Router /widgets/{widgetID} [delete]
func DeleteWidget(c *gin.Context) {
// simID, err := GetSimulationID(c)
// if err != nil {
// return
// }
//
// sim, err := queries.FindSimulation(simID)
// if common.ProvideErrorResponse(c, err) {
// return
// }
//
// visID, err := GetVisualizationID(c)
// if err != nil {
// return
// }
//
// visualization, err := queries.FindVisualizationOfSim(&sim, visID)
// if common.ProvideErrorResponse(c, err) {
// return
// }
//
// widgetID, err := GetWidgetID(c)
// if err != nil {
// return
// }
//
// widget, err := queries.FindWidgetOfVisualization(&visualization, widgetID)
// if common.ProvideErrorResponse(c, err) {
// return
// }
// TODO delete files of widget in DB and on disk
// TODO Delete widget itself + association with visualization
c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented",
})
}

View file

@ -7,8 +7,61 @@ import (
func FindVisualizationWidgets(visualization *common.Visualization) ([]common.Widget, int, error) {
db := common.GetDB()
var widgets []common.Widget
err := db.Model(visualization).Related(&widgets, "Widgets").Error
err := db.Order("ID asc").Model(visualization).Related(&widgets, "Widgets").Error
return widgets, len(widgets), err
}
func FindWidget(widgetID int) (common.Widget, error){
db := common.GetDB()
var w common.Widget
err := db.First(&w, widgetID).Error
return w, err
}
func FindWidgetsOfVisualization(vis * common.Visualization) ([]common.Widget, int, error) {
db := common.GetDB()
var widgets []common.Widget
err := db.Order("ID asc").Model(vis).Related(&vis, "Widgets").Error
return widgets, len(widgets), err
}
func FindWidgetOfVisualization(visualization *common.Visualization, widgetID int) (common.Widget, error){
db := common.GetDB()
var widget common.Widget
err := db.Model(visualization).Where("ID = ?", widgetID).Related(&widget, "Widgets").Error
return widget, err
}
func AddWidgetToVisualization(vis *common.Visualization, widget_input * common.Widget) error {
db := common.GetDB()
// Add widget to DB
err := db.Create(widget_input).Error
if err != nil {
return err
}
// Add association with visualization
err = db.Model(vis).Association("Widgets").Append(widget_input).Error
return err
}
func UpdateWidgetOfVisualization(vis * common.Visualization, widget_input common.Widget, widgetID int) error {
db := common.GetDB()
// Get widget of visualization that matches with ID (= widget to be updated)
var widget_old common.Widget
err := db.Model(vis).Where("ID = ?", widgetID).Related(&widget_old, "Widgets").Error
if err != nil {
return err
}
// Update widget in DB
err = db.Model(&widget_old).Updates(map[string]interface{}{"Name": widget_input.Name, "Type": widget_input.Type, "MinHeight": widget_input.MinHeight, "MinWidth": widget_input.MinWidth, "Height": widget_input.Height, "Width": widget_input.Width, "X": widget_input.X, "Y": widget_input.Y, "Z": widget_input.Z, "CustomProperties": widget_input.CustomProperties}).Error
return err
}

View file

@ -1,60 +0,0 @@
package widget
import (
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
)
type WidgetsSerializer struct {
Ctx *gin.Context
Widgets []common.Widget
}
func (self *WidgetsSerializer) Response() []WidgetResponse {
response := []WidgetResponse{}
for _, widget := range self.Widgets {
serializer := WidgetSerializer{self.Ctx, widget}
response = append(response, serializer.Response())
}
return response
}
type WidgetSerializer struct {
Ctx *gin.Context
common.Widget
}
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
}
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
}
return response
}

View file

@ -1,18 +1,37 @@
package main
import (
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/file"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/project"
"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/autoapi" // apidocs folder is generated by Swag CLI, you have to import it
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/model"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/simulation"
"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"
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget"
)
// @title VILLASweb Backend API
// @version 2.0
// @description This is the API of the VILLASweb Backend
// @description WORK IN PROGRESS! PLEASE BE PATIENT!
// @description This documentation is auto-generated based on the API documentation in the code.
// @description The tool https://github.com/swaggo/swag is used to auto-generate API docs for gin.
// @contact.name Sonja Happ
// @contact.email sonja.happ@eonerc.rwth-aachen.de
// @license.name GNU GPL 3.0
// @license.url http://www.gnu.de/documents/gpl-3.0.en.html
// @host localhost:4000
// @BasePath /api/v1
func main() {
// Testing
db := common.DummyInitDB()
@ -31,13 +50,15 @@ func main() {
api.Use(user.Authentication(true))
user.UsersRegister(api.Group("/users"))
file.FilesRegister(api.Group("/files"))
project.ProjectsRegister(api.Group("/projects"))
simulation.SimulationsRegister(api.Group("/simulations"))
simulationmodel.SimulationModelsRegister(api.Group("/models"))
simulator.SimulatorsRegister(api.Group("/simulators"))
visualization.VisualizationsRegister(api.Group("/visualizations"))
simulation.RegisterSimulationEndpoints(api.Group("/simulations"))
model.RegisterModelEndpoints(api.Group("/models"))
visualization.RegisterVisualizationEndpoints(api.Group("/visualizations"))
widget.RegisterWidgetEndpoints(api.Group("/widgets"))
file.RegisterFileEndpoints(api.Group("/files"))
user.RegisterUserEndpoints(api.Group("/users"))
simulator.RegisterSimulatorEndpoints(api.Group("/simulators"))
r.GET("swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// server at port 4000 to match frontend's redirect path
r.Run(":4000")