- revise testing of dashboard enpoints

- clean up testdata, serializers and responses
- add validators for dashboards
- revise documentation of dashboard endpoints for swaggo
- revise endpoint implementations
This commit is contained in:
Sonja Happ 2019-09-06 14:07:36 +02:00
parent e0ae839e96
commit dab027eef6
9 changed files with 434 additions and 160 deletions

View file

@ -2,13 +2,6 @@ package common
import "github.com/jinzhu/gorm/dialects/postgres" import "github.com/jinzhu/gorm/dialects/postgres"
type DashboardResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Grid int `json:"grid"`
ScenarioID uint `json:"scenarioID"`
}
type WidgetResponse struct { type WidgetResponse struct {
ID uint `json:"id"` ID uint `json:"id"`
Name string `json:"name"` Name string `json:"name"`
@ -59,14 +52,6 @@ type ResponseMsgSignal struct {
Signal SignalResponse `json:"signal"` Signal SignalResponse `json:"signal"`
} }
type ResponseMsgDashboards struct {
Dashboards []DashboardResponse `json:"dashboards"`
}
type ResponseMsgDashboard struct {
Dashboard DashboardResponse `json:"dashboard"`
}
type ResponseMsgWidgets struct { type ResponseMsgWidgets struct {
Widgets []WidgetResponse `json:"widgets"` Widgets []WidgetResponse `json:"widgets"`
} }

View file

@ -4,38 +4,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
// Dashboard/s Serializers
type DashboardsSerializer struct {
Ctx *gin.Context
Dashboards []Dashboard
}
func (self *DashboardsSerializer) Response() []DashboardResponse {
response := []DashboardResponse{}
for _, dashboard := range self.Dashboards {
serializer := DashboardSerializer{self.Ctx, dashboard}
response = append(response, serializer.Response())
}
return response
}
type DashboardSerializer struct {
Ctx *gin.Context
Dashboard
}
func (self *DashboardSerializer) Response() DashboardResponse {
response := DashboardResponse{
Name: self.Name,
Grid: self.Grid,
ScenarioID: self.ScenarioID,
ID: self.ID,
}
return response
}
// Widget/s Serializers // Widget/s Serializers
type WidgetsSerializer struct { type WidgetsSerializer struct {

View file

@ -196,14 +196,14 @@ var InSignalCUpdated_response = SignalResponse{
// Dashboards // Dashboards
var DashboardA = Dashboard{Name: "Dashboard_A", Grid: 15} var DashboardA = Dashboard{
var DashboardA_response = DashboardResponse{ID: 1, Name: DashboardA.Name, Grid: DashboardA.Grid, ScenarioID: DashboardA.ScenarioID} Name: "Dashboard_A",
var DashboardB = Dashboard{Name: "Dashboard_B", Grid: 10} Grid: 15,
var DashboardB_response = DashboardResponse{ID: 2, Name: DashboardB.Name, Grid: DashboardB.Grid, ScenarioID: DashboardB.ScenarioID} }
var DashboardC = Dashboard{Name: "Dashboard_C", Grid: 25} var DashboardB = Dashboard{
var DashboardC_response = DashboardResponse{ID: 3, Name: DashboardC.Name, Grid: DashboardC.Grid, ScenarioID: DashboardC.ScenarioID} Name: "Dashboard_B",
var DashboardCUpdated = Dashboard{Name: "Dashboard_Cupdated", Grid: 24} Grid: 10,
var DashboardCUpdated_response = DashboardResponse{ID: 3, Name: DashboardCUpdated.Name, Grid: DashboardCUpdated.Grid, ScenarioID: DashboardCUpdated.ScenarioID} }
// Files // Files

View file

@ -48,3 +48,11 @@ type ResponseSimulationModels struct {
type ResponseSimulationModel struct { type ResponseSimulationModel struct {
model common.SimulationModel model common.SimulationModel
} }
type ResponseDashboards struct {
dashboards []common.Dashboard
}
type ResponseDashboard struct {
dashboard common.Dashboard
}

View file

@ -1,6 +1,7 @@
package dashboard package dashboard
import ( import (
"fmt"
"net/http" "net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -23,11 +24,10 @@ func RegisterDashboardEndpoints(r *gin.RouterGroup) {
// @ID getDashboards // @ID getDashboards
// @Produce json // @Produce json
// @Tags dashboards // @Tags dashboards
// @Success 200 {array} common.DashboardResponse "Array of dashboards to which belong to scenario" // @Success 200 {object} docs.ResponseDashboards "Dashboards to which belong to scenario"
// @Failure 401 "Unauthorized Access" // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 403 "Access forbidden." // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 404 "Not found" // @Failure 500 {object} docs.ResponseError "Internal server error"
// @Failure 500 "Internal server error"
// @Param scenarioID query int true "Scenario ID" // @Param scenarioID query int true "Scenario ID"
// @Router /dashboards [get] // @Router /dashboards [get]
func getDashboards(c *gin.Context) { func getDashboards(c *gin.Context) {
@ -44,9 +44,8 @@ func getDashboards(c *gin.Context) {
return return
} }
serializer := common.DashboardsSerializer{c, dab}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"dashboards": serializer.Response(), "dashboards": dab,
}) })
} }
@ -56,44 +55,54 @@ func getDashboards(c *gin.Context) {
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Tags dashboards // @Tags dashboards
// @Param inputDab body common.ResponseMsgDashboard true "Dashboard to be added incl. ID of Scenario" // @Param inputDab body dashboard.validNewDashboard true "Dashboard to be added incl. ID of Scenario"
// @Success 200 "OK." // @Success 200 {object} docs.ResponseDashboard "Dashboards that was added"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 403 "Access forbidden." // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 404 "Not found" // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 "Internal server error" // @Failure 500 {object} docs.ResponseError "Internal server error"
// @Router /dashboards [post] // @Router /dashboards [post]
func addDashboard(c *gin.Context) { func addDashboard(c *gin.Context) {
var newDabData common.ResponseMsgDashboard // bind request to JSON
err := c.BindJSON(&newDabData) var req addDashboardRequest
if err != nil { if err := c.ShouldBindJSON(&req); err != nil {
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg, "success": false,
"message": fmt.Sprintf("%v", err),
}) })
return return
} }
var newDab Dashboard // Validate the request
newDab.ID = newDabData.Dashboard.ID if err := req.validate(); err != nil {
newDab.Grid = newDabData.Dashboard.Grid c.JSON(http.StatusUnprocessableEntity, gin.H{
newDab.ScenarioID = newDabData.Dashboard.ScenarioID "success": false,
newDab.Name = newDabData.Dashboard.Name "message": fmt.Sprintf("%v", err),
})
return
}
ok, _ := scenario.CheckPermissions(c, common.Create, "body", int(newDab.ScenarioID)) // Create the new dashboard from the request
newDashboard := req.createDashboard()
// Check if user is allowed to modify scenario specified in request
ok, _ := scenario.CheckPermissions(c, common.Create, "body", int(newDashboard.ScenarioID))
if !ok { if !ok {
return return
} }
// add dashboard to DB and add association to scenario // add dashboard to DB and add association to scenario
err = newDab.addToScenario() err := newDashboard.addToScenario()
if common.ProvideErrorResponse(c, err) == false { if err != nil {
c.JSON(http.StatusOK, gin.H{ common.ProvideErrorResponse(c, err)
"message": "OK.", return
})
} }
c.JSON(http.StatusOK, gin.H{
"dashboard": newDashboard.Dashboard,
})
} }
// updateDashboard godoc // updateDashboard godoc
@ -102,37 +111,59 @@ func addDashboard(c *gin.Context) {
// @Tags dashboards // @Tags dashboards
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param inputDab body common.ResponseMsgDashboard true "Dashboard to be updated" // @Param inputDab body dashboard.validUpdatedDashboard true "Dashboard to be updated"
// @Success 200 "OK." // @Success 200 {object} docs.ResponseDashboard "Dashboards that was updated"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 403 "Access forbidden." // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 404 "Not found" // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 "Internal server error" // @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param dashboardID path int true "Dashboard ID" // @Param dashboardID path int true "Dashboard ID"
// @Router /dashboards/{dashboardID} [put] // @Router /dashboards/{dashboardID} [put]
func updateDashboard(c *gin.Context) { func updateDashboard(c *gin.Context) {
ok, d := CheckPermissions(c, common.Update, "path", -1) ok, oldDashboard := CheckPermissions(c, common.Update, "path", -1)
if !ok { if !ok {
return return
} }
var modifiedDab common.ResponseMsgDashboard var req updateDashboardRequest
err := c.BindJSON(&modifiedDab) if err := c.ShouldBindJSON(&req); err != nil {
if err != nil {
errormsg := "Bad request. Error binding form data to JSON: " + err.Error()
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg, "success": false,
"message": fmt.Sprintf("%v", err),
}) })
return return
} }
err = d.update(modifiedDab.Dashboard) // Validate the request
if common.ProvideErrorResponse(c, err) == false { if err := req.validate(); err != nil {
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"message": "OK.", "success": false,
"message": fmt.Sprintf("%v", err),
}) })
return
} }
// Create the updatedScenario from oldScenario
updatedDashboard, err := req.updatedDashboard(oldDashboard)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return
}
// update the dashboard in the DB
err = oldDashboard.update(updatedDashboard)
if err != nil {
common.ProvideErrorResponse(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"dashboard": updatedDashboard.Dashboard,
})
} }
// getDashboard godoc // getDashboard godoc
@ -140,11 +171,11 @@ func updateDashboard(c *gin.Context) {
// @ID getDashboard // @ID getDashboard
// @Tags dashboards // @Tags dashboards
// @Produce json // @Produce json
// @Success 200 {object} common.DashboardResponse "Requested dashboard." // @Success 200 {object} docs.ResponseDashboard "Dashboards that was requested"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 403 "Access forbidden." // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 404 "Not found" // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 "Internal server error" // @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param dashboardID path int true "Dashboard ID" // @Param dashboardID path int true "Dashboard ID"
// @Router /dashboards/{dashboardID} [get] // @Router /dashboards/{dashboardID} [get]
func getDashboard(c *gin.Context) { func getDashboard(c *gin.Context) {
@ -154,9 +185,8 @@ func getDashboard(c *gin.Context) {
return return
} }
serializer := common.DashboardSerializer{c, dab.Dashboard}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"dashboard": serializer.Response(), "dashboard": dab.Dashboard,
}) })
} }
@ -165,11 +195,11 @@ func getDashboard(c *gin.Context) {
// @ID deleteDashboard // @ID deleteDashboard
// @Tags dashboards // @Tags dashboards
// @Produce json // @Produce json
// @Success 200 "OK." // @Success 200 {object} docs.ResponseDashboard "Dashboards that was deleted"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 403 "Access forbidden." // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 404 "Not found" // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 "Internal server error" // @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param dashboardID path int true "Dashboard ID" // @Param dashboardID path int true "Dashboard ID"
// @Router /dashboards/{dashboardID} [delete] // @Router /dashboards/{dashboardID} [delete]
func deleteDashboard(c *gin.Context) { func deleteDashboard(c *gin.Context) {
@ -184,6 +214,6 @@ func deleteDashboard(c *gin.Context) {
} }
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"message": "OK.", "dashboard": dab.Dashboard,
}) })
} }

View file

@ -1,8 +1,6 @@
package dashboard package dashboard
import ( import (
"fmt"
"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/scenario" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/scenario"
) )
@ -21,7 +19,7 @@ func (d *Dashboard) ByID(id uint) error {
db := common.GetDB() db := common.GetDB()
err := db.Find(d, id).Error err := db.Find(d, id).Error
if err != nil { if err != nil {
return fmt.Errorf("Dashboard with id=%v does not exist", id) return err
} }
return nil return nil
} }
@ -46,10 +44,11 @@ func (d *Dashboard) addToScenario() error {
return err return err
} }
func (d *Dashboard) update(modifiedDab common.DashboardResponse) error { func (d *Dashboard) update(modifiedDab Dashboard) error {
db := common.GetDB() db := common.GetDB()
// TODO do we allow to update scenarioID here as well?
err := db.Model(d).Updates(map[string]interface{}{ err := db.Model(d).Updates(map[string]interface{}{
"Name": modifiedDab.Name, "Name": modifiedDab.Name,
"Grid": modifiedDab.Grid, "Grid": modifiedDab.Grid,

View file

@ -17,7 +17,10 @@ func CheckPermissions(c *gin.Context, operation common.CRUD, dabIDSource string,
err := common.ValidateRole(c, common.ModelDashboard, operation) err := common.ValidateRole(c, common.ModelDashboard, operation)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, "Access denied (role validation failed).") c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("Access denied (role validation failed): %v", err),
})
return false, dab return false, dab
} }
@ -25,18 +28,18 @@ func CheckPermissions(c *gin.Context, operation common.CRUD, dabIDSource string,
if dabIDSource == "path" { if dabIDSource == "path" {
dabID, err = strconv.Atoi(c.Param("dashboardID")) dabID, err = strconv.Atoi(c.Param("dashboardID"))
if err != nil { if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of dashboardID path parameter")
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg, "success": false,
"message": fmt.Sprintf("Bad request. No or incorrect format of dashboardID path parameter"),
}) })
return false, dab return false, dab
} }
} else if dabIDSource == "query" { } else if dabIDSource == "query" {
dabID, err = strconv.Atoi(c.Request.URL.Query().Get("dashboardID")) dabID, err = strconv.Atoi(c.Request.URL.Query().Get("dashboardID"))
if err != nil { if err != nil {
errormsg := fmt.Sprintf("Bad request. No or incorrect format of dashboardID query parameter")
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"error": errormsg, "success": false,
"message": fmt.Sprintf("Bad request. No or incorrect format of dashboardID query parameter"),
}) })
return false, dab return false, dab
} }

View file

@ -1,65 +1,277 @@
package dashboard package dashboard
import ( import (
"encoding/json" "fmt"
"testing"
"github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/scenario"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/user"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"github.com/jinzhu/gorm/dialects/postgres"
"github.com/stretchr/testify/assert"
"os"
"testing"
) )
// Test /dashboards endpoints var router *gin.Engine
func TestEndpoints(t *testing.T) { var db *gorm.DB
var token string type DashboardRequest struct {
Name string `json:"name,omitempty"`
Grid int `json:"grid,omitempty"`
ScenarioID uint `json:"scenarioID,omitempty"`
}
var myDashboards = []common.DashboardResponse{common.DashboardA_response, common.DashboardB_response} type ScenarioRequest struct {
var msgDashboards = common.ResponseMsgDashboards{Dashboards: myDashboards} Name string `json:"name,omitempty"`
var msgDab = common.ResponseMsgDashboard{Dashboard: common.DashboardC_response} Running bool `json:"running,omitempty"`
var msgDabupdated = common.ResponseMsgDashboard{Dashboard: common.DashboardCUpdated_response} StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
}
db := common.DummyInitDB() func addScenario(token string) (scenarioID uint) {
// POST $newScenario
newScenario := ScenarioRequest{
Name: common.ScenarioA.Name,
Running: common.ScenarioA.Running,
StartParameters: common.ScenarioA.StartParameters,
}
_, resp, _ := common.NewTestEndpoint(router, token,
"/api/scenarios", "POST", common.KeyModels{"scenario": newScenario})
// Read newScenario's ID from the response
newScenarioID, _ := common.GetResponseID(resp)
return uint(newScenarioID)
}
func TestMain(m *testing.M) {
db = common.DummyInitDB()
defer db.Close() defer db.Close()
common.DummyPopulateDB(db)
router := gin.Default() router = gin.Default()
api := router.Group("/api") api := router.Group("/api")
// All endpoints require authentication except when someone wants to
// login (POST /authenticate)
user.RegisterAuthenticate(api.Group("/authenticate")) user.RegisterAuthenticate(api.Group("/authenticate"))
api.Use(user.Authentication(true)) api.Use(user.Authentication(true))
RegisterDashboardEndpoints(api.Group("/dashboards")) RegisterDashboardEndpoints(api.Group("/dashboards"))
// scenario endpoints required here to first add a scenario to the DB
// that can be associated with a new dashboard
scenario.RegisterScenarioEndpoints(api.Group("/scenarios"))
credjson, _ := json.Marshal(common.CredUser) os.Exit(m.Run())
msgOKjson, _ := json.Marshal(common.MsgOK) }
msgDashboardsjson, _ := json.Marshal(msgDashboards)
msgDabjson, _ := json.Marshal(msgDab)
msgDabupdatedjson, _ := json.Marshal(msgDabupdated)
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200) func TestAddDashboard(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// test GET dashboards // authenticate as normal user
common.TestEndpoint(t, router, token, "/api/dashboards?scenarioID=1", "GET", nil, 200, msgDashboardsjson) token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
// test POST dashboards scenarioID := addScenario(token)
common.TestEndpoint(t, router, token, "/api/dashboards", "POST", msgDabjson, 200, msgOKjson)
// test GET dashboards/:dashboardID to check if previous POST worked correctly // test POST dashboards/ $newDashboard
common.TestEndpoint(t, router, token, "/api/dashboards/3", "GET", nil, 200, msgDabjson) newDashboard := DashboardRequest{
Name: common.DashboardA.Name,
Grid: common.DashboardA.Grid,
ScenarioID: scenarioID,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/dashboards", "POST", common.KeyModels{"dashboard": newDashboard})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// test PUT dashboards/:dashboardID // Compare POST's response with the newDashboard
common.TestEndpoint(t, router, token, "/api/dashboards/3", "PUT", msgDabupdatedjson, 200, msgOKjson) err = common.CompareResponse(resp, common.KeyModels{"dashboard": newDashboard})
common.TestEndpoint(t, router, token, "/api/dashboards/3", "GET", nil, 200, msgDabupdatedjson) assert.NoError(t, err)
// test DELETE dashboards/:dashboardID // Read newDashboard's ID from the response
common.TestEndpoint(t, router, token, "/api/dashboards/3", "DELETE", nil, 200, msgOKjson) newDashbaordID, err := common.GetResponseID(resp)
common.TestEndpoint(t, router, token, "/api/dashboards?scenarioID=1", "GET", nil, 200, msgDashboardsjson) assert.NoError(t, err)
// TODO add testing for other return codes // Get the newDashboard
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/dashboards/%v", newDashbaordID), "GET", nil)
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Compare GET's response with the newDashboard
err = common.CompareResponse(resp, common.KeyModels{"dashboard": newDashboard})
assert.NoError(t, err)
// try to POST a malformed dashboard
// Required fields are missing
malformedNewDashboard := DashboardRequest{
Name: "ThisIsAMalformedDashboard",
}
// this should NOT work and return a unprocessable entity 442 status code
code, resp, err = common.NewTestEndpoint(router, token,
"/api/dashboards", "POST", common.KeyModels{"dashboard": malformedNewDashboard})
assert.NoError(t, err)
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
} }
func TestUpdateDashboard(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// authenticate as normal user
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
scenarioID := addScenario(token)
// test POST dashboards/ $newDashboard
newDashboard := DashboardRequest{
Name: common.DashboardA.Name,
Grid: common.DashboardA.Grid,
ScenarioID: scenarioID,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/dashboards", "POST", common.KeyModels{"dashboard": newDashboard})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Read newDashboard's ID from the response
newDashbaordID, err := common.GetResponseID(resp)
assert.NoError(t, err)
updatedDashboard := DashboardRequest{
Name: common.DashboardB.Name,
Grid: common.DashboardB.Grid,
}
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/dashboards/%v", newDashbaordID), "PUT", common.KeyModels{"dashboard": updatedDashboard})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Compare PUT's response with the updatedDashboard
err = common.CompareResponse(resp, common.KeyModels{"dashboard": updatedDashboard})
assert.NoError(t, err)
// Get the updatedDashboard
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/dashboards/%v", newDashbaordID), "GET", nil)
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Compare GET's response with the updatedDashboard
err = common.CompareResponse(resp, common.KeyModels{"dashboard": updatedDashboard})
assert.NoError(t, err)
// try to update a dashboard that does not exist (should return not found 404 status code)
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/dashboards/%v", newDashbaordID+1), "PUT", common.KeyModels{"dashboard": updatedDashboard})
assert.NoError(t, err)
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
}
func TestDeleteDashboard(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// authenticate as normal user
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
scenarioID := addScenario(token)
fmt.Println(scenarioID)
// test POST dashboards/ $newDashboard
newDashboard := DashboardRequest{
Name: common.DashboardA.Name,
Grid: common.DashboardA.Grid,
ScenarioID: scenarioID,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/dashboards", "POST", common.KeyModels{"dashboard": newDashboard})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Read newDashboard's ID from the response
newDashbaordID, err := common.GetResponseID(resp)
assert.NoError(t, err)
// Count the number of all the dashboards returned for scenario
initialNumber, err := common.LengthOfResponse(router, token,
fmt.Sprintf("/api/dashboards?scenarioID=%v", scenarioID), "GET", nil)
assert.NoError(t, err)
// Delete the added newDashboard
code, resp, err = common.NewTestEndpoint(router, token,
fmt.Sprintf("/api/dashboards/%v", newDashbaordID), "DELETE", nil)
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Compare DELETE's response with the newDashboard
err = common.CompareResponse(resp, common.KeyModels{"dashboard": newDashboard})
assert.NoError(t, err)
// Again count the number of all the dashboards returned for scenario
finalNumber, err := common.LengthOfResponse(router, token,
fmt.Sprintf("/api/dashboards?scenarioID=%v", scenarioID), "GET", nil)
assert.NoError(t, err)
assert.Equal(t, finalNumber, initialNumber-1)
}
func TestGetAllDashboardsOfScenario(t *testing.T) {
common.DropTables(db)
common.MigrateModels(db)
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
// authenticate as normal user
token, err := common.NewAuthenticateForTest(router,
"/api/authenticate", "POST", common.UserACredentials)
assert.NoError(t, err)
scenarioID := addScenario(token)
fmt.Println(scenarioID)
// Count the number of all the dashboards returned for scenario
initialNumber, err := common.LengthOfResponse(router, token,
fmt.Sprintf("/api/dashboards?scenarioID=%v", scenarioID), "GET", nil)
assert.NoError(t, err)
// test POST dashboards/ $newDashboard
newDashboardA := DashboardRequest{
Name: common.DashboardA.Name,
Grid: common.DashboardA.Grid,
ScenarioID: scenarioID,
}
code, resp, err := common.NewTestEndpoint(router, token,
"/api/dashboards", "POST", common.KeyModels{"dashboard": newDashboardA})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// POST a second dashboard for the same scenario
newDashboardB := DashboardRequest{
Name: common.DashboardB.Name,
Grid: common.DashboardB.Grid,
ScenarioID: scenarioID,
}
code, resp, err = common.NewTestEndpoint(router, token,
"/api/dashboards", "POST", common.KeyModels{"dashboard": newDashboardB})
assert.NoError(t, err)
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
// Count again the number of all the dashboards returned for scenario
finalNumber, err := common.LengthOfResponse(router, token,
fmt.Sprintf("/api/dashboards?scenarioID=%v", scenarioID), "GET", nil)
assert.NoError(t, err)
assert.Equal(t, initialNumber+2, finalNumber)
}

View file

@ -0,0 +1,69 @@
package dashboard
import (
"gopkg.in/go-playground/validator.v9"
)
var validate *validator.Validate
type validNewDashboard struct {
Name string `form:"Name" validate:"required"`
Grid int `form:"Grid" validate:"required"`
ScenarioID uint `form:"ScenarioID" validate:"required"`
}
type validUpdatedDashboard struct {
Name string `form:"Name" validate:"omitempty"`
Grid int `form:"Grid" validate:"omitempty"`
ScenarioID uint `form:"ScenarioID" validate:"omitempty"`
}
type addDashboardRequest struct {
validNewDashboard `json:"dashboard"`
}
type updateDashboardRequest struct {
validUpdatedDashboard `json:"dashboard"`
}
func (r *addDashboardRequest) validate() error {
validate = validator.New()
errs := validate.Struct(r)
return errs
}
func (r *validUpdatedDashboard) validate() error {
validate = validator.New()
errs := validate.Struct(r)
return errs
}
func (r *addDashboardRequest) createDashboard() Dashboard {
var s Dashboard
s.Name = r.Name
s.Grid = r.Grid
s.ScenarioID = r.ScenarioID
return s
}
func (r *updateDashboardRequest) updatedDashboard(oldDashboard Dashboard) (Dashboard, error) {
// Use the old Dashboard as a basis for the updated Dashboard `s`
s := oldDashboard
if r.Name != "" {
s.Name = r.Name
}
if r.Grid != 0 {
s.Grid = r.Grid
}
if r.ScenarioID != 0 {
// TODO do we allow this case?
//s.ScenarioID = r.ScenarioID
}
return s, nil
}