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 image: villaswebbackendgo:ubuntu
script: script:
- go mod tidy - 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 - go build
# Stage: test # Stage: test
############################################################################## ##############################################################################
test:backend: test:backend:database:
stage: test stage: test
tags: tags:
- docker - docker
@ -65,11 +67,28 @@ test:backend:
script: script:
- /etc/init.d/postgresql start - /etc/init.d/postgresql start
- go mod tidy - 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 - cd common
- go test -v -args -dbhost=/var/run/postgresql - go test -v -args -dbhost=/var/run/postgresql
dependencies: dependencies:
- build:backend - 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 # Stage: deploy
############################################################################## ##############################################################################

View file

@ -54,10 +54,9 @@ func VerifyConnection(db *gorm.DB) error {
// to the Dummy*() where it is called // to the Dummy*() where it is called
func DropTables(db *gorm.DB) { func DropTables(db *gorm.DB) {
db.DropTableIfExists(&Simulator{}) db.DropTableIfExists(&Simulator{})
db.DropTableIfExists(&Signal{}) //db.DropTableIfExists(&Signal{})
db.DropTableIfExists(&SimulationModel{}) db.DropTableIfExists(&Model{})
db.DropTableIfExists(&File{}) db.DropTableIfExists(&File{})
db.DropTableIfExists(&Project{})
db.DropTableIfExists(&Simulation{}) db.DropTableIfExists(&Simulation{})
db.DropTableIfExists(&User{}) db.DropTableIfExists(&User{})
db.DropTableIfExists(&Visualization{}) db.DropTableIfExists(&Visualization{})
@ -67,10 +66,9 @@ func DropTables(db *gorm.DB) {
// AutoMigrate the models // AutoMigrate the models
func MigrateModels(db *gorm.DB) { func MigrateModels(db *gorm.DB) {
db.AutoMigrate(&Simulator{}) db.AutoMigrate(&Simulator{})
db.AutoMigrate(&Signal{}) //db.AutoMigrate(&Signal{})
db.AutoMigrate(&SimulationModel{}) db.AutoMigrate(&Model{})
db.AutoMigrate(&File{}) db.AutoMigrate(&File{})
db.AutoMigrate(&Project{})
db.AutoMigrate(&Simulation{}) db.AutoMigrate(&Simulation{})
db.AutoMigrate(&User{}) db.AutoMigrate(&User{})
db.AutoMigrate(&Visualization{}) 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_A).Error)
checkErr(test_db.Create(&simr_B).Error) checkErr(test_db.Create(&simr_B).Error)
outSig_A := Signal{Name: "outSignal_A"} //outSig_A := Signal{Name: "outSignal_A", Direction: "out"}
outSig_B := Signal{Name: "outSignal_B"} //outSig_B := Signal{Name: "outSignal_B", Direction: "out"}
inSig_A := Signal{Name: "inSignal_A"} //inSig_A := Signal{Name: "inSignal_A", Direction: "in"}
inSig_B := Signal{Name: "inSignal_B"} //inSig_B := Signal{Name: "inSignal_B", Direction: "in"}
checkErr(test_db.Create(&outSig_A).Error) //checkErr(test_db.Create(&outSig_A).Error)
checkErr(test_db.Create(&outSig_B).Error) //checkErr(test_db.Create(&outSig_B).Error)
checkErr(test_db.Create(&inSig_A).Error) //checkErr(test_db.Create(&inSig_A).Error)
checkErr(test_db.Create(&inSig_B).Error) //checkErr(test_db.Create(&inSig_B).Error)
smo_A := SimulationModel{Name: "SimModel_A"} mo_A := Model{Name: "Model_A"}
smo_B := SimulationModel{Name: "SimModel_B"} mo_B := Model{Name: "Model_B"}
checkErr(test_db.Create(&smo_A).Error) checkErr(test_db.Create(&mo_A).Error)
checkErr(test_db.Create(&smo_B).Error) checkErr(test_db.Create(&mo_B).Error)
file_A := File{Name: "File_A"} file_A := File{Name: "File_A"}
file_B := File{Name: "File_B"} file_B := File{Name: "File_B"}
checkErr(test_db.Create(&file_A).Error) checkErr(test_db.Create(&file_A).Error)
checkErr(test_db.Create(&file_B).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_A := Simulation{Name: "Simulation_A"}
simn_B := Simulation{Name: "Simulation_B"} simn_B := Simulation{Name: "Simulation_B"}
checkErr(test_db.Create(&simn_A).Error) 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 `belongs to` use the model with id=1
// For `has many` use the models with id=1 and id=2 // For `has many` use the models with id=1 and id=2
// Project HM Visualization, Visualization BT Project // User HM Simulations, Simulation HM Users (Many-to-Many)
checkErr(test_db.Model(&vis_A).Association("Project").Append(&proj_A).Error) checkErr(test_db.Model(&simn_A).Association("Users").Append(&usr_A).Error)
checkErr(test_db.Model(&vis_B).Association("Project").Append(&proj_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 // Simulation HM Model
checkErr(test_db.Model(&proj_A).Association("User").Append(&usr_A).Error) checkErr(test_db.Model(&simn_A).Association("Models").Append(&mo_A).Error)
checkErr(test_db.Model(&proj_B).Association("User").Append(&usr_A).Error) checkErr(test_db.Model(&simn_A).Association("Models").Append(&mo_B).Error)
// Simulation HM Project, Project BT Simulation // Simulation HM Visualizations
checkErr(test_db.Model(&proj_A).Association("Simulation").Append(&simn_A).Error) checkErr(test_db.Model(&simn_A).Association("Visualizations").Append(&vis_A).Error)
checkErr(test_db.Model(&proj_B).Association("Simulation").Append(&simn_A).Error) checkErr(test_db.Model(&simn_A).Association("Visualizations").Append(&vis_B).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)
// Visualization HM Widget // 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_A).Error)
checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_B).Error) checkErr(test_db.Model(&vis_A).Association("Widgets").Append(&widg_B).Error)
// SimModel HM Signal // Model HM Signal
checkErr(test_db.Model(&smo_A).Association("InputMapping").Append(&inSig_A).Error) //checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSig_A).Error)
checkErr(test_db.Model(&smo_A).Association("InputMapping").Append(&inSig_B).Error) //checkErr(test_db.Model(&mo_A).Association("InputMapping").Append(&inSig_B).Error)
checkErr(test_db.Model(&smo_A).Association("OutputMapping").Append(&outSig_A).Error) //checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_A).Error)
checkErr(test_db.Model(&smo_A).Association("OutputMapping").Append(&outSig_B).Error) //checkErr(test_db.Model(&mo_A).Association("OutputMapping").Append(&outSig_B).Error)
//SimulationModel HM Files // Model HM Files
checkErr(test_db.Model(&smo_A).Association("Files").Append(&file_A).Error) checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_A).Error)
checkErr(test_db.Model(&smo_A).Association("Files").Append(&file_B).Error) checkErr(test_db.Model(&mo_A).Association("Files").Append(&file_B).Error)
// Visualization BT User // Simulator BT Model
checkErr(test_db.Model(&vis_A).Association("User").Append(&usr_A).Error) checkErr(test_db.Model(&mo_A).Association("Simulator").Append(&simr_A).Error)
// Simulator BT SimModel // Widget HM Files
checkErr(test_db.Model(&smo_A).Association("BelongsToSimulator").Append(&simr_A).Error) 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 ( import (
"fmt" "fmt"
"github.com/stretchr/testify/assert"
"testing" "testing"
"github.com/stretchr/testify/assert"
) )
// Verify that you can connect to the database // Verify that you can connect to the database
@ -28,45 +29,33 @@ func TestDummyDBAssociations(t *testing.T) {
// Variables for tests // Variables for tests
var simr Simulator var simr Simulator
var smo SimulationModel var mo Model
var file File var file File
var proj Project
var simn Simulation var simn Simulation
var usr User var usr User
var usrs []User
var vis Visualization var vis Visualization
var widg Widget
var sigs []Signal //var sigs []Signal
var smos []SimulationModel var mos []Model
var files []File var files []File
var files_sm []File var files_sm []File
var projs []Project
var simns []Simulation var simns []Simulation
var viss []Visualization var viss []Visualization
var widgs []Widget var widgs []Widget
// Simulation Model // User
a.NoError(db.Find(&smo, 1).Error, fM("SimulationModel")) a.NoError(db.Find(&usr, 1).Error, fM("User"))
a.EqualValues("SimModel_A", smo.Name) a.EqualValues("User_A", usr.Username)
// Simulation Model Associations // User Associations
a.NoError(db.Model(&smo).Association("BelongsToSimulation").Find(&simn).Error) a.NoError(db.Model(&usr).Related(&simns, "Simulations").Error)
a.EqualValues("Simulation_A", simn.Name, "Expected Simulation_A") if len(simns) != 2 {
a.Fail("User Associations",
a.NoError(db.Model(&smo).Association("BelongsToSimulator").Find(&simr).Error) "Expected to have %v Simulations. Has %v.", 2, len(simns))
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))
} }
// Simulation // Simulation
@ -76,64 +65,47 @@ func TestDummyDBAssociations(t *testing.T) {
// Simulation Associations // Simulation Associations
a.NoError(db.Model(&simn).Association("User").Find(&usr).Error) a.NoError(db.Model(&simn).Association("Users").Find(&usrs).Error)
a.EqualValues("User_A", usr.Username) if len(usrs) != 2 {
a.Fail("Simulations Associations",
a.NoError(db.Model(&simn).Related(&smos, "Models").Error) "Expected to have %v Users. Has %v.", 2, len(usrs))
if len(smos) != 2 {
a.Fail("Simulation Associations",
"Expected to have %v Simulation Models. Has %v.", 2, len(smos))
} }
a.NoError(db.Model(&simn).Related(&projs, "Projects").Error) a.NoError(db.Model(&simn).Related(&mos, "Models").Error)
if len(projs) != 2 { if len(mos) != 2 {
a.Fail("Simulation Associations", 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.Model(&simn).Related(&viss, "Visualizations").Error)
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)
if len(viss) != 2 { if len(viss) != 2 {
a.Fail("Project Associations", a.Fail("Simulation Associations",
"Expected to have %v Visualizations. Has %v.", 2, len(viss)) "Expected to have %v Visualizations. Has %v.", 2, len(viss))
} }
// User
a.NoError(db.Find(&usr, 1).Error, fM("User")) // Model
a.EqualValues("User_A", usr.Username)
// 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) // Model Associations
if len(projs) != 2 {
a.Fail("User Associations", a.NoError(db.Model(&mo).Association("Simulator").Find(&simr).Error)
"Expected to have %v Projects. Has %v.", 2, len(projs)) 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 // Visualization
@ -142,26 +114,29 @@ func TestDummyDBAssociations(t *testing.T) {
// Visualization Associations // 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) a.NoError(db.Model(&vis).Related(&widgs, "Widgets").Error)
if len(widgs) != 2 { if len(widgs) != 2 {
a.Fail("Widget Associations", a.Fail("Widget Associations",
"Expected to have %v Widget. Has %v.", 2, len(widgs)) "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 // File
a.NoError(db.Find(&file, 1).Error, fM("File")) a.NoError(db.Find(&file, 1).Error, fM("File"))
a.EqualValues("File_A", file.Name) 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 - name: simulators
description: Manage Simulators description: Manage Simulators
- name: projects - name: projects
description: Manage Projects description: (REMOVED) Manage Projects
- name: simulations - name: simulations
description: Manage Simulations description: Manage Simulations
- name: simulationmodels - name: models
description: Manage SimulationModels description: Manage Models
- name: visualizations - name: visualizations
description: Manage Visualizations description: Manage Visualizations
- name: uploads - name: uploads
@ -244,8 +244,8 @@ paths:
get: get:
tags: tags:
- files - files
summary: Get files of user summary: (REMOVED) Get files of user
operationId: getFilesOfUser operationId: (REMOVED) getFilesOfUser
responses: responses:
200: 200:
description: OK. description: OK.
@ -266,8 +266,8 @@ paths:
post: post:
tags: tags:
- files - files
summary: Add a new file to the database (NEW, was /uploads before)) summary: (REMOVED) Add a new file to the database
operationId: addFile operationId: (REMOVED) addFile
requestBody: requestBody:
description: "File object to be added" description: "File object to be added"
required: true required: true
@ -292,8 +292,8 @@ paths:
get: get:
tags: tags:
- files - files
summary: Get properties of file with ID FileID summary: (REMOVED) Get properties of file with ID FileID
operationId: getFileProperties operationId: (REMOVED) getFileProperties
parameters: parameters:
- in: path - in: path
name: FileID name: FileID
@ -329,8 +329,8 @@ paths:
put: put:
tags: tags:
- files - files
summary: Update properties of file with ID FileID (NEW) summary: (REMOVED) Update properties of file with ID FileID
operationId: updateFileProperties operationId: (REMOVED) updateFileProperties
requestBody: requestBody:
description: "File object to be updated" description: "File object to be updated"
required: true required: true
@ -361,8 +361,8 @@ paths:
delete: delete:
tags: tags:
- files - files
summary: Delete file with ID FileID summary: (REMOVED) Delete file with ID FileID
operationId: deleteFile operationId: (REMOVED) deleteFile
parameters: parameters:
- in: path - in: path
name: FileID name: FileID
@ -385,8 +385,8 @@ paths:
get: get:
tags: tags:
- projects - projects
summary: Get projects of user summary: (REMOVED) Get projects of user
operationId: getProjects operationId: (REMOVED) getProjects
responses: responses:
200: 200:
description: OK. description: OK.
@ -407,8 +407,8 @@ paths:
post: post:
tags: tags:
- projects - projects
summary: Add a new project to the database summary: (REMOVED) Add a new project to the database
operationId: addProject operationId: (REMOVED) addProject
requestBody: requestBody:
description: "Project object to add to DB" description: "Project object to add to DB"
required: true required: true
@ -431,8 +431,8 @@ paths:
put: put:
tags: tags:
- projects - projects
summary: Update properties of project with ID ProjectID summary: (REMOVED) Update properties of project with ID ProjectID
operationId: updateProjectProperties operationId: (REMOVED) updateProjectProperties
parameters: parameters:
- in: path - in: path
name: ProjectID name: ProjectID
@ -463,8 +463,8 @@ paths:
get: get:
tags: tags:
- projects - projects
summary: Get properties of project with ID ProjectID summary: (REMOVED) Get properties of project with ID ProjectID
operationId: getProjectProperties operationId: (REMOVED) getProjectProperties
parameters: parameters:
- in: path - in: path
name: ProjectID name: ProjectID
@ -490,8 +490,8 @@ paths:
delete: delete:
tags: tags:
- projects - projects
summary: Delete project with ID ProjectID summary: (REMOVED) Delete project with ID ProjectID
operationId: deleteProject operationId: (REMOVED) deleteProject
parameters: parameters:
- in: path - in: path
name: ProjectID name: ProjectID
@ -513,9 +513,9 @@ paths:
/models: /models:
get: get:
tags: tags:
- simulationmodels - models
summary: Get simulation models of user summary: (REMOVED) Get simulation models of user
operationId: getSimulationModels operationId: (REMOVED) getModels
responses: responses:
200: 200:
description: OK. description: OK.
@ -524,7 +524,7 @@ paths:
schema: schema:
type: array type: array
items: items:
$ref: '#/components/schemas/SimulationModel' $ref: '#/components/schemas/Model'
401: 401:
description: Unauthorized access. description: Unauthorized access.
403: 403:
@ -535,16 +535,16 @@ paths:
description: Internal server error. description: Internal server error.
post: post:
tags: tags:
- simulationmodels - models
summary: Add a new SimulationModel to the database summary: (REMOVED) Add a new Model to the database
operationId: addSimulationModel operationId: (REMOVED) addModel
requestBody: requestBody:
description: "SimulationModel object to add to DB" description: "Model object to add to DB"
required: true required: true
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/SimulationModel' $ref: '#/components/schemas/Model'
responses: responses:
200: 200:
description: OK. description: OK.
@ -556,26 +556,26 @@ paths:
description: Access forbidden. description: Access forbidden.
500: 500:
description: Internal server error. Unable to save simulation or simulation model. description: Internal server error. Unable to save simulation or simulation model.
/models/{SimulationModelID}: /models/{ModelID}:
put: put:
tags: tags:
- simulationmodels - models
summary: Update properties of SimulationModel with ID SimulationModelID summary: (REMOVED) Update properties of Model with ID ModelID
operationId: updateSimulationModelProperties operationId: (REMOVED) updateModelProperties
parameters: parameters:
- in: path - in: path
name: SimulationModelID name: ModelID
description: ID of a SimulationModel description: ID of a Model
required: true required: true
schema: schema:
type: integer type: integer
requestBody: requestBody:
description: "SimulationModel object with new properties" description: "Model object with new properties"
required: true required: true
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/SimulationModel' $ref: '#/components/schemas/Model'
responses: responses:
200: 200:
description: OK. description: OK.
@ -591,13 +591,13 @@ paths:
description: Internal server error. Unable to save simulation model. description: Internal server error. Unable to save simulation model.
get: get:
tags: tags:
- simulationmodels - models
summary: Get properties of SimulationModel with ID SimulationModelID summary: (REMOVED) Get properties of Model with ID ModelID
operationId: getSimulationModelProperties operationId: (REMOVED) getModelProperties
parameters: parameters:
- in: path - in: path
name: SimulationModelID name: ModelID
description: ID of a SimulationModel description: ID of a Model
required: true required: true
schema: schema:
type: integer type: integer
@ -607,7 +607,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/SimulationModel' $ref: '#/components/schemas/Model'
401: 401:
description: Unauthorized access. description: Unauthorized access.
403: 403:
@ -618,13 +618,13 @@ paths:
description: Internal server error. description: Internal server error.
delete: delete:
tags: tags:
- simulationmodels - models
summary: Delete SimulationModel with ID SimulationModelID summary: (REMOVED) Delete Model with ID ModelID
operationId: deleteSimulationModel operationId: (REMOVED) deleteModel
parameters: parameters:
- in: path - in: path
name: SimulationModelID name: ModelID
description: ID of a SimulationModel description: ID of a Model
required: true required: true
schema: schema:
type: integer type: integer
@ -639,16 +639,16 @@ paths:
description: Not found. Simulation or simulation model not found. description: Not found. Simulation or simulation model not found.
500: 500:
description: Internal server error. Unable to save changed simulation or to remove simulation model. description: Internal server error. Unable to save changed simulation or to remove simulation model.
/models/{SimulationModelID}/file: simulations/{SimulationID}/models/{ModelID}/file:
get: get:
tags: tags:
- simulationmodels - simulations
summary: Get file from SimulationModel with ID SimulationModelID (NEW) summary: (NEW) Get file from Model with ID ModelID
operationId: getFileFromSimulationModel operationId: getFileFromModel
parameters: parameters:
- in: path - in: path
name: SimulationModelID name: ModelID
description: ID of a SimulationModel description: ID of a Model
required: true required: true
schema: schema:
type: integer type: integer
@ -691,13 +691,13 @@ paths:
description: Internal server error. description: Internal server error.
put: put:
tags: tags:
- simulationmodels - simulations
summary: Update (Overwrite) file of SimulationModel with ID SimulationModelID, File object has to be in database (NEW) summary: (NEW) Update (Overwrite) file of Model with ID ModelID, File object has to be in database
operationId: updateFileForSimulationModel operationId: updateFileForModel
parameters: parameters:
- in: path - in: path
name: SimulationModelID name: ModelID
description: ID of a SimulationModel description: ID of a Model
required: true required: true
schema: schema:
type: integer type: integer
@ -1035,9 +1035,9 @@ paths:
get: get:
tags: tags:
- visualizations - visualizations
summary: Get all available visualizations summary: (REMOVED) Get all available visualizations
description: Return a JSON representation of all visualizations description: (REMOVED) Return a JSON representation of all visualizations
operationId: getVisualizations operationId: (REMOVED) getVisualizations
responses: responses:
200: 200:
description: OK. description: OK.
@ -1058,8 +1058,8 @@ paths:
post: post:
tags: tags:
- visualizations - visualizations
summary: Add a new visualization to the database summary: (REMOVED) Add a new visualization to the database
operationId: addVisualization operationId: (REMOVED) addVisualization
requestBody: requestBody:
description: "Visualization object to add to DB" description: "Visualization object to add to DB"
required: true required: true
@ -1084,8 +1084,8 @@ paths:
put: put:
tags: tags:
- visualizations - visualizations
summary: Update properties of Visualization with ID VisualizationID summary: (REMOVED) Update properties of Visualization with ID VisualizationID
operationId: updateVisualizationrProperties operationId: (REMOVED) updateVisualizationrProperties
parameters: parameters:
- in: path - in: path
name: VisualizationID name: VisualizationID
@ -1116,8 +1116,8 @@ paths:
get: get:
tags: tags:
- visualizations - visualizations
summary: Get properties of Visualization with ID VisualizationID summary: (REMOVED) Get properties of Visualization with ID VisualizationID
operationId: getVisualizationProperties operationId: (REMOVED) getVisualizationProperties
parameters: parameters:
- in: path - in: path
name: VisualizationID name: VisualizationID
@ -1143,8 +1143,8 @@ paths:
delete: delete:
tags: tags:
- visualizations - visualizations
summary: Delete Visualization with ID VisualizationID summary: (REMOVED) Delete Visualization with ID VisualizationID
operationId: deleteVisualization operationId: (REMOVED) deleteVisualization
parameters: parameters:
- in: path - in: path
name: VisualizationID name: VisualizationID
@ -1167,8 +1167,8 @@ paths:
post: post:
tags: tags:
- uploads - uploads
summary: Upload a new file to the database (REMOVED) summary: (REMOVED) Upload a new file to the database
operationId: uploadFile operationId: (REMOVED) uploadFile
requestBody: requestBody:
description: "File object to upload TODO CHANGE TO FORM" description: "File object to upload TODO CHANGE TO FORM"
required: true required: true
@ -1280,7 +1280,7 @@ components:
type: integer type: integer
UserID: UserID:
type: integer type: integer
SimulationModelID: ModelID:
type: integer type: integer
Date: Date:
type: string type: string
@ -1309,7 +1309,7 @@ components:
RawProperties: RawProperties:
type: object type: object
properties: {} properties: {}
SimulationModel: Model:
required: required:
- Name - Name
- OutputLength - OutputLength
@ -1343,14 +1343,14 @@ components:
required: required:
- Name - Name
- Unit - Unit
- SimulationModelID - ModelID
type: object type: object
properties: properties:
Name: Name:
type: string type: string
Unit: Unit:
type: string type: string
SimulationModelID: ModelID:
type: integer type: integer
Widget: Widget:
required: 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 module git.rwth-aachen.de/acs/public/villas/villasweb-backend-go
require ( 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/dgrijalva/jwt-go v3.2.0+incompatible
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect github.com/gin-gonic/gin v1.4.0
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 // indirect github.com/jinzhu/gorm v1.9.8
github.com/gin-gonic/gin v1.3.0 github.com/stretchr/testify v1.3.0
github.com/go-sql-driver/mysql v1.4.1 // indirect github.com/swaggo/gin-swagger v1.1.0
github.com/gofrs/uuid v3.2.0+incompatible // indirect github.com/swaggo/swag v1.5.0
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
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c 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.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.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.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
cloud.google.com/go v0.37.2/go.mod h1:H8IAquKe2L30IxoupDgqTaQvKSwF/c8prYHynGIWQbA= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
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=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 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/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/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/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/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/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/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/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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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-20190423183735-731ef375ac02 h1:PS3xfVPa8N84AzoWZHFCbA0+ikz4f4skktfjQoNMsgk=
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/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 h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 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= 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/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 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= 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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 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/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-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-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 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 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/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.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/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/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.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/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.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/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/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 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 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/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/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/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/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 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/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/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.8 h1:n5uvxqLepIP2R1XF7pudpt9Rv8I3m7G9trGxJVjLZ5k=
github.com/jinzhu/gorm v1.9.2 h1:lCvgEaqe/HVE+tjAR2mt4HbbHAZsQOv3XAZiEZV37iw= github.com/jinzhu/gorm v1.9.8/go.mod h1:bdqTT3q6dhSph2K3pWxrHP6nqxuAp2yQ3KFtc3U3F84=
github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k= 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/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.0 h1:6WV8LvwPpDhKjo5U9O6b4+xdG/jTXNPwlDme/MTo8Ns= github.com/jinzhu/now v1.0.0 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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 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.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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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.1.0 h1:/5u4a+KGJptBRqGzPvYQL9p0d/tPR4S31+Tnzj9lEO4=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 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 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= github.com/mattn/go-sqlite3 v1.10.0 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.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.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/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/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/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.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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.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_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-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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/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-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/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/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/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/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/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 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= github.com/ugorji/go/codec v0.0.0-20181209151446-772ced7fd4c2/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A= github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 h1:BasDe+IErOQKrMVXab7UayvSlIpiyGwRvuX3EKYY7UA=
go.opencensus.io v0.19.2/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M= github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780 h1:vG/gY/PxA3v3l04qxe3tDjXyu3bozii8ulSlIPOYKhI=
golang.org/x/build v0.0.0-20190314133821-5284462c4bec/go.mod h1:atTaCNAy0f16Ah5aV1gMSwgiKVHwu/JncqDpuRr7lS4= 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-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-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 h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-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/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-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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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-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-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-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-20181005035420-146acd28ed58/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-20181114220301-adae6a3d119a/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-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-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-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-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-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/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-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-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-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/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-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-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-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-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-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-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-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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.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/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-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-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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89 h1:iWXXYN3edZ3Nd/7I6Rt1sXrWVmhF9bgVtlEJ7BbH124=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU=
google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 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 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 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-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/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/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 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= 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= 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/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 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= 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/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.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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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-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-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/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 package file
import ( import (
"github.com/gin-gonic/gin" "fmt"
"net/http" "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) { func RegisterFileEndpoints(r *gin.RouterGroup){
r.GET("/", filesReadEp) r.GET("/", GetFiles)
r.POST("/", fileRegistrationEp) // NEW in API r.POST ("/", AddFile)
r.PUT("/:FileID", fileUpdateEp) // NEW in API r.GET("/:fileID", GetFile)
r.GET("/:FileID", fileReadEp) r.PUT("/:fileID", UpdateFile)
r.DELETE("/:FileID", fileDeleteEp) 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} // 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
}
// 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{ c.JSON(http.StatusOK, gin.H{
"files": serializer.Response(), "files": serializer.Response(),
}) })
}
} }
func fileRegistrationEp(c *gin.Context) {
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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "message": "NOT implemented",
}) })
} }
func fileReadEp(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{ func GetFileOfWidget(c *gin.Context) {
"message": "NOT implemented",
}) 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{ func UpdateFileOfWidget(c *gin.Context) {
"message": "NOT implemented",
}) //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 package file
import ( 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/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() db := common.GetDB()
var files []common.File 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 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() db := common.GetDB()
var files []common.File err := db.Where("Path = ?", path).Find(file).Error
err := db.Model(user).Related(&files, "Files").Error
return files, len(files), err 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 package simulation
import ( import (
"github.com/gin-gonic/gin" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
"net/http" "net/http"
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
) )
func SimulationsRegister(r *gin.RouterGroup) { func RegisterSimulationEndpoints(r *gin.RouterGroup){
r.GET("/", simulationsReadEp) r.GET("/", GetSimulations)
r.POST("/", simulationRegistrationEp) r.POST("/", AddSimulation)
r.PUT("/:SimulationID", simulationUpdateEp) //r.POST("/:simulationID", CloneSimulation)
r.GET("/:SimulationID", simulationReadEp) r.PUT("/:simulationID", UpdateSimulation)
r.DELETE("/:SimulationID", simulationDeleteEp) 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() allSimulations, _, _ := FindAllSimulations()
serializer := SimulationsSerializerNoAssoc{c, allSimulations} serializer := common.SimulationsSerializer{c, allSimulations}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"simulations": serializer.Response(), "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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "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{ 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" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
) )
type Simulation struct{
common.Simulation
}
func FindAllSimulations() ([]common.Simulation, int, error) { func FindAllSimulations() ([]common.Simulation, int, error) {
db := common.GetDB() db := common.GetDB()
var simulations []common.Simulation var simulations []common.Simulation
err := db.Find(&simulations).Error err := db.Order("ID asc").Find(&simulations).Error
return simulations, len(simulations), err return simulations, len(simulations), err
} }
func FindUserSimulations(user *common.User) ([]common.Simulation, int, error) { func FindUserSimulations(user *common.User) ([]common.Simulation, int, error) {
db := common.GetDB() db := common.GetDB()
var simulations []common.Simulation 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 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 package simulator
import ( import (
"github.com/gin-gonic/gin"
"net/http" "net/http"
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
) )
func SimulatorsRegister(r *gin.RouterGroup) { func RegisterSimulatorEndpoints(r *gin.RouterGroup){
r.GET("/", simulatorsReadEp) r.GET("/", GetSimulators)
r.POST("/", simulatorRegistrationEp) r.POST("/", AddSimulator)
r.PUT("/:SimulatorID", simulatorUpdateEp) r.PUT("/:simulatorID", UpdateSimulator)
r.GET("/:SimulatorID", simulatorReadEp) r.GET("/:simulatorID", GetSimulator)
r.DELETE("/:SimulatorID", simulatorDeleteEp) r.DELETE("/:simulatorID", DeleteSimulator)
r.POST("/:SimulatorID", simulatorSendActionEp) 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() allSimulators, _, _ := FindAllSimulators()
serializer := SimulatorsSerializer{c, allSimulators} serializer := common.SimulatorsSerializer{c, allSimulators}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"simulators": serializer.Response(), "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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "message": "NOT implemented",
}) })
} }

View file

@ -7,7 +7,14 @@ import (
func FindAllSimulators() ([]common.Simulator, int, error) { func FindAllSimulators() ([]common.Simulator, int, error) {
db := common.GetDB() db := common.GetDB()
var simulators []common.Simulator var simulators []common.Simulator
err := db.Find(&simulators).Error err := db.Order("ID asc").Find(&simulators).Error
return simulators, len(simulators), err 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" "net/http"
"strconv" "strconv"
"time" "time"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
) )
// TODO: the signing secret must be environmental variable // TODO: the signing secret must be environmental variable
@ -20,19 +22,37 @@ type tokenClaims struct {
jwt.StandardClaims jwt.StandardClaims
} }
type AuthResponse struct{
Success bool `json:"success"`
Message string `json:"message"`
Token string `json:"token"`
}
// `/authenticate` endpoint does not require Authentication // `/authenticate` endpoint does not require Authentication
func VisitorAuthenticate(r *gin.RouterGroup) { func VisitorAuthenticate(r *gin.RouterGroup) {
r.POST("", authenticationEp) r.POST("", authenticationEp)
} }
func UsersRegister(r *gin.RouterGroup) { func RegisterUserEndpoints(r *gin.RouterGroup) {
r.POST("", userRegistrationEp) r.POST("", addUser)
r.PUT("/:UserID", userUpdateEp) r.PUT("/:UserID", updateUser)
r.GET("", usersReadEp) r.GET("", getUsers)
r.GET("/:UserID", userReadEp) r.GET("/:UserID", getUser)
r.DELETE("/:UserID", userDeleteEp) 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) { func authenticationEp(c *gin.Context) {
// Bind the response (context) with the Credentials struct // 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 //// dummy TODO: check in the middleware if the user is authorized
//authorized := false //authorized := false
//// TODO: move this redirect in the authentication middleware //// TODO: move this redirect in the authentication middleware
@ -110,13 +141,26 @@ func usersReadEp(c *gin.Context) {
//return //return
//} //}
allUsers, _, _ := FindAllUsers() allUsers, _, _ := FindAllUsers()
serializer := UsersSerializer{c, allUsers} serializer := common.UsersSerializer{c, allUsers}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"users": serializer.Response(true), "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 // Bind the response (context) with the User struct
var newUser User 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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "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 var user User
id, _ := strconv.ParseInt(c.Param("UserID"), 10, 64) id, _ := strconv.ParseInt(c.Param("UserID"), 10, 64)
@ -180,13 +250,25 @@ func userReadEp(c *gin.Context) {
return return
} }
serializer := UserSerializer{c, user.User} serializer := common.UserSerializer{c, user.User}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"user": serializer.Response(false), "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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "message": "NOT implemented",
}) })

View file

@ -7,6 +7,46 @@ import (
func FindAllUsers() ([]common.User, int, error) { func FindAllUsers() ([]common.User, int, error) {
db := common.GetDB() db := common.GetDB()
var users []common.User var users []common.User
err := db.Find(&users).Error err := db.Order("ID asc").Find(&users).Error
return users, len(users), err 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 package visualization
import ( import (
"github.com/gin-gonic/gin"
"net/http" "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) { func RegisterVisualizationEndpoints(r *gin.RouterGroup){
r.GET("/", visualizationsReadEp)
r.POST("/", visualizationRegistrationEp) r.GET("/", GetVisualizations)
r.PUT("/:VisualizationID", visualizationUpdateEp) r.POST("/", AddVisualization)
r.GET("/:VisualizationID", visualizationReadEp) //r.POST("/:visualizationID", CloneVisualization)
r.DELETE("/:VisualizationID", visualizationDeleteEp) r.PUT("/:visualizationID", UpdateVisualization)
r.GET("/:visualizationID", GetVisualization)
r.DELETE("/:visualizationID", DeleteVisualization)
} }
func visualizationsReadEp(c *gin.Context) { // GetVisualizations godoc
allVisualizations, _, _ := FindAllVisualizations() // @Summary Get all visualizations of simulation
serializer := VisualizationsSerializer{c, allVisualizations} // @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{ c.JSON(http.StatusOK, gin.H{
"visualizations": serializer.Response(), "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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "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{ c.JSON(http.StatusOK, gin.H{
"message": "NOT implemented", "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" "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() db := common.GetDB()
var visualization []common.Visualization var visualizations []common.Visualization
err := db.Find(&visualization).Error err := db.Order("ID asc").Model(sim).Related(&visualizations, "Visualizations").Error
return visualization, len(visualization), err 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 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) { func FindVisualizationWidgets(visualization *common.Visualization) ([]common.Widget, int, error) {
db := common.GetDB() db := common.GetDB()
var widgets []common.Widget 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 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 package main
import ( 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/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/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/simulator"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/visualization"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/widget"
"github.com/gin-gonic/gin"
) )
// @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() { func main() {
// Testing // Testing
db := common.DummyInitDB() db := common.DummyInitDB()
@ -31,13 +50,15 @@ func main() {
api.Use(user.Authentication(true)) api.Use(user.Authentication(true))
user.UsersRegister(api.Group("/users")) simulation.RegisterSimulationEndpoints(api.Group("/simulations"))
file.FilesRegister(api.Group("/files")) model.RegisterModelEndpoints(api.Group("/models"))
project.ProjectsRegister(api.Group("/projects")) visualization.RegisterVisualizationEndpoints(api.Group("/visualizations"))
simulation.SimulationsRegister(api.Group("/simulations")) widget.RegisterWidgetEndpoints(api.Group("/widgets"))
simulationmodel.SimulationModelsRegister(api.Group("/models")) file.RegisterFileEndpoints(api.Group("/files"))
simulator.SimulatorsRegister(api.Group("/simulators")) user.RegisterUserEndpoints(api.Group("/users"))
visualization.VisualizationsRegister(api.Group("/visualizations")) simulator.RegisterSimulatorEndpoints(api.Group("/simulators"))
r.GET("swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// server at port 4000 to match frontend's redirect path // server at port 4000 to match frontend's redirect path
r.Run(":4000") r.Run(":4000")