mirror of
https://git.rwth-aachen.de/acs/public/villas/web-backend-go/
synced 2025-03-30 00:00:12 +01:00
395 lines
12 KiB
Go
395 lines
12 KiB
Go
/** Routes package, registration function
|
|
*
|
|
* @author Sonja Happ <sonja.happ@eonerc.rwth-aachen.de>
|
|
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
|
* @license GNU General Public License (version 3)
|
|
*
|
|
* VILLASweb-backend-go
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*********************************************************************************/
|
|
|
|
package routes
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
|
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/database"
|
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/helper"
|
|
component_configuration "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/component-configuration"
|
|
config_route "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/config"
|
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/dashboard"
|
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/file"
|
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/healthz"
|
|
infrastructure_component "git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/infrastructure-component"
|
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/metrics"
|
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/openapi"
|
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/result"
|
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/scenario"
|
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/signal"
|
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/user"
|
|
"git.rwth-aachen.de/acs/public/villas/web-backend-go/routes/widget"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/zpatrick/go-config"
|
|
)
|
|
|
|
// #######################################################################
|
|
// #################### Structures for test data #########################
|
|
// #######################################################################
|
|
|
|
type jsonUser struct {
|
|
Username string
|
|
Password string
|
|
Mail string
|
|
Role string
|
|
}
|
|
|
|
var GlobalTestData struct {
|
|
Users []jsonUser
|
|
ICs []database.InfrastructureComponent
|
|
Scenarios []database.Scenario
|
|
Results []database.Result
|
|
Configs []database.ComponentConfiguration
|
|
Dashboards []database.Dashboard
|
|
Widgets []database.Widget
|
|
Signals []database.Signal
|
|
}
|
|
|
|
// register all backend endpoints; to be called after DB is initialized
|
|
func RegisterEndpoints(router *gin.Engine, api *gin.RouterGroup) {
|
|
|
|
healthz.RegisterHealthzEndpoint(api.Group("/healthz"))
|
|
metrics.RegisterMetricsEndpoint(api.Group("/metrics"))
|
|
openapi.RegisterOpenAPIEndpoint(api.Group("/openapi"))
|
|
config_route.RegisterConfigEndpoint(api.Group("/config"))
|
|
user.RegisterAuthenticate(api.Group("/authenticate"))
|
|
|
|
// The following endpoints require authentication
|
|
|
|
api.Use(user.Authentication())
|
|
|
|
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
|
|
component_configuration.RegisterComponentConfigurationEndpoints(api.Group("/configs"))
|
|
signal.RegisterSignalEndpoints(api.Group("/signals"))
|
|
dashboard.RegisterDashboardEndpoints(api.Group("/dashboards"))
|
|
widget.RegisterWidgetEndpoints(api.Group("/widgets"))
|
|
file.RegisterFileEndpoints(api.Group("/files"))
|
|
user.RegisterUserEndpoints(api.Group("/users"))
|
|
infrastructure_component.RegisterICEndpoints(api.Group("/ic"))
|
|
result.RegisterResultEndpoints(api.Group("/results"))
|
|
|
|
metrics.InitCounters()
|
|
|
|
}
|
|
|
|
// ReadTestDataFromJson Reads test data from JSON file (path set by ENV variable or command line param)
|
|
func ReadTestDataFromJson(path string) error {
|
|
|
|
_, err := os.Stat(path)
|
|
|
|
if err == nil {
|
|
|
|
jsonFile, err := os.Open(path)
|
|
if err != nil {
|
|
return fmt.Errorf("error opening json file: %v", err)
|
|
}
|
|
log.Println("Successfully opened json data file", path)
|
|
|
|
defer jsonFile.Close()
|
|
|
|
byteValue, _ := ioutil.ReadAll(jsonFile)
|
|
|
|
err = json.Unmarshal(byteValue, &GlobalTestData)
|
|
if err != nil {
|
|
return fmt.Errorf("error unmarshalling json: %v", err)
|
|
}
|
|
} else if os.IsNotExist(err) {
|
|
log.Println("Test data file does not exist, no test data added to DB:", path)
|
|
return nil
|
|
} else {
|
|
log.Println("Something is wrong with this file path:", path)
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AddTestData Uses API endpoints to add test data to the backend; All endpoints have to be registered before invoking this function.
|
|
func AddTestData(cfg *config.Config, router *gin.Engine) (*bytes.Buffer, error) {
|
|
|
|
adminPW, errPW := cfg.String("admin.pass")
|
|
adminName, errName := cfg.String("admin.user")
|
|
if errPW != nil || errName != nil {
|
|
if errName != nil {
|
|
log.Println("WARNING:", errName)
|
|
}
|
|
if errPW != nil {
|
|
log.Println("WARNING:", errPW)
|
|
}
|
|
|
|
log.Println("WARNING: cannot add test data because of missing admin config, continue without it")
|
|
return nil, nil
|
|
}
|
|
|
|
var Admin = database.Credentials{
|
|
Username: adminName,
|
|
Password: adminPW,
|
|
}
|
|
|
|
// authenticate as admin
|
|
token, err := helper.AuthenticateForTest(router, Admin)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
basePath := "/api/v2"
|
|
|
|
db := database.GetDB()
|
|
|
|
// add users
|
|
for _, u := range GlobalTestData.Users {
|
|
|
|
var x []user.User
|
|
err = db.Find(&x, "Username = ?", u.Username).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(x) == 0 {
|
|
code, resp, err := helper.TestEndpoint(router, token, basePath+"/users", "POST", helper.KeyModels{"user": u})
|
|
if code != http.StatusOK {
|
|
return resp, fmt.Errorf("error adding user %v: %v", u.Username, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// add infrastructure components
|
|
amqphost, _ := cfg.String("amqp.host")
|
|
counterICs := 0
|
|
for _, i := range GlobalTestData.ICs {
|
|
|
|
if (i.ManagedExternally && amqphost != "") || !i.ManagedExternally {
|
|
|
|
var x []infrastructure_component.InfrastructureComponent
|
|
err = db.Find(&x, "Name = ?", i.Name).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(x) == 0 {
|
|
code, resp, err := helper.TestEndpoint(router, token, basePath+"/ic", "POST", helper.KeyModels{"ic": i})
|
|
if code != http.StatusOK {
|
|
return resp, fmt.Errorf("error adding IC %v: %v", i.Name, err)
|
|
}
|
|
counterICs++
|
|
}
|
|
}
|
|
}
|
|
|
|
// add scenarios
|
|
for _, s := range GlobalTestData.Scenarios {
|
|
|
|
var x []scenario.Scenario
|
|
err = db.Find(&x, "Name = ?", s.Name).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(x) == 0 {
|
|
code, resp, err := helper.TestEndpoint(router, token, basePath+"/scenarios", "POST", helper.KeyModels{"scenario": s})
|
|
if code != http.StatusOK {
|
|
return resp, fmt.Errorf("error adding Scenario %v: %v", s.Name, err)
|
|
}
|
|
|
|
// add all users to the scenario
|
|
for _, u := range GlobalTestData.Users {
|
|
code, resp, err := helper.TestEndpoint(router, token, fmt.Sprintf("%v/scenarios/1/user?username="+u.Username, basePath), "PUT", nil)
|
|
if code != http.StatusOK {
|
|
return resp, fmt.Errorf("error adding user %v to scenario %v: %v", u.Username, s.Name, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there is at least one scenario and one IC in the test data, add component configs
|
|
configCounter := 0
|
|
if len(GlobalTestData.Scenarios) > 0 && counterICs > 0 {
|
|
|
|
for _, c := range GlobalTestData.Configs {
|
|
|
|
var x []component_configuration.ComponentConfiguration
|
|
err = db.Find(&x, "Name = ?", c.Name).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(x) == 0 {
|
|
c.ScenarioID = 1
|
|
c.ICID = 1
|
|
code, resp, err := helper.TestEndpoint(router, token, basePath+"/configs", "POST", helper.KeyModels{"config": c})
|
|
if code != http.StatusOK {
|
|
return resp, fmt.Errorf("error adding Config %v: %v", c.Name, err)
|
|
}
|
|
}
|
|
configCounter++
|
|
}
|
|
}
|
|
|
|
// If there is at least one scenario, add dashboards, results, and 2 test files
|
|
dashboardCounter := 0
|
|
if len(GlobalTestData.Scenarios) > 0 {
|
|
for _, d := range GlobalTestData.Dashboards {
|
|
|
|
var x []dashboard.Dashboard
|
|
err = db.Find(&x, "Name = ?", d.Name).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(x) == 0 {
|
|
d.ScenarioID = 1
|
|
code, resp, err := helper.TestEndpoint(router, token, basePath+"/dashboards", "POST", helper.KeyModels{"dashboard": d})
|
|
if code != http.StatusOK {
|
|
return resp, fmt.Errorf("error adding Dashboard %v: %v", d.Name, err)
|
|
}
|
|
}
|
|
dashboardCounter++
|
|
}
|
|
|
|
for _, r := range GlobalTestData.Results {
|
|
|
|
var x []result.Result
|
|
err = db.Find(&x, "Description = ?", r.Description).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(x) == 0 {
|
|
r.ScenarioID = 1
|
|
r.ResultFileIDs = []int64{}
|
|
code, resp, err := helper.TestEndpoint(router, token, basePath+"/results", "POST", helper.KeyModels{"result": r})
|
|
if code != http.StatusOK {
|
|
return resp, fmt.Errorf("error adding Result %v: %v", r.Description, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// upload files
|
|
|
|
var x []file.File
|
|
err = db.Find(&x, "Name = ?", "Readme.md").Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(x) == 0 {
|
|
// upload readme file
|
|
bodyBuf := &bytes.Buffer{}
|
|
bodyWriter := multipart.NewWriter(bodyBuf)
|
|
fileWriter, _ := bodyWriter.CreateFormFile("file", "Readme.md")
|
|
fh, _ := os.Open("README.md")
|
|
defer fh.Close()
|
|
|
|
// io copy
|
|
_, err = io.Copy(fileWriter, fh)
|
|
contentType := bodyWriter.FormDataContentType()
|
|
bodyWriter.Close()
|
|
|
|
// Create the request and add file to scenario
|
|
w1 := httptest.NewRecorder()
|
|
req1, _ := http.NewRequest("POST", basePath+"/files?scenarioID=1", bodyBuf)
|
|
req1.Header.Set("Content-Type", contentType)
|
|
req1.Header.Add("Authorization", "Bearer "+token)
|
|
router.ServeHTTP(w1, req1)
|
|
}
|
|
|
|
var y []file.File
|
|
err = db.Find(&y, "Name = ?", "logo.png").Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(y) == 0 {
|
|
// upload image file
|
|
bodyBuf := &bytes.Buffer{}
|
|
bodyWriter := multipart.NewWriter(bodyBuf)
|
|
fileWriter, _ := bodyWriter.CreateFormFile("file", "logo.png")
|
|
fh, _ := os.Open("doc/pictures/villas_web.png")
|
|
defer fh.Close()
|
|
|
|
// io copy
|
|
_, err = io.Copy(fileWriter, fh)
|
|
contentType := bodyWriter.FormDataContentType()
|
|
bodyWriter.Close()
|
|
|
|
// Create the request and add a second file to scenario
|
|
w2 := httptest.NewRecorder()
|
|
req2, _ := http.NewRequest("POST", basePath+"/files?scenarioID=1", bodyBuf)
|
|
req2.Header.Set("Content-Type", contentType)
|
|
req2.Header.Add("Authorization", "Bearer "+token)
|
|
router.ServeHTTP(w2, req2)
|
|
}
|
|
}
|
|
|
|
// If there is at least one dashboard, add widgets
|
|
if dashboardCounter > 0 {
|
|
for _, w := range GlobalTestData.Widgets {
|
|
|
|
var x []widget.Widget
|
|
err = db.Find(&x, "Name = ?", w.Name).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(x) == 0 {
|
|
w.DashboardID = 1
|
|
code, resp, err := helper.TestEndpoint(router, token, basePath+"/widgets", "POST", helper.KeyModels{"widget": w})
|
|
if code != http.StatusOK {
|
|
return resp, fmt.Errorf("error adding Widget %v: %v", w.Name, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there is at least one config, add signals
|
|
if configCounter > 0 {
|
|
for _, s := range GlobalTestData.Signals {
|
|
|
|
var x []signal.Signal
|
|
err = db.Find(&x, "Name = ?", s.Name).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(x) == 0 {
|
|
s.ConfigID = 1
|
|
code, resp, err := helper.TestEndpoint(router, token, basePath+"/signals", "POST", helper.KeyModels{"signal": s})
|
|
if code != http.StatusOK {
|
|
return resp, fmt.Errorf("error adding Signal %v: %v", s.Name, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, nil
|
|
}
|