diff --git a/common/responses.go b/common/responses.go index 4de4cc6..64f7e77 100644 --- a/common/responses.go +++ b/common/responses.go @@ -2,13 +2,6 @@ package common 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 { ID uint `json:"id"` Name string `json:"name"` @@ -59,14 +52,6 @@ type ResponseMsgSignal struct { Signal SignalResponse `json:"signal"` } -type ResponseMsgDashboards struct { - Dashboards []DashboardResponse `json:"dashboards"` -} - -type ResponseMsgDashboard struct { - Dashboard DashboardResponse `json:"dashboard"` -} - type ResponseMsgWidgets struct { Widgets []WidgetResponse `json:"widgets"` } diff --git a/common/serializers.go b/common/serializers.go index 5d3be7e..051a064 100644 --- a/common/serializers.go +++ b/common/serializers.go @@ -4,38 +4,6 @@ import ( "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 type WidgetsSerializer struct { diff --git a/common/testdata.go b/common/testdata.go index 9f8a539..37aedc4 100644 --- a/common/testdata.go +++ b/common/testdata.go @@ -196,14 +196,14 @@ var InSignalCUpdated_response = SignalResponse{ // Dashboards -var DashboardA = Dashboard{Name: "Dashboard_A", Grid: 15} -var DashboardA_response = DashboardResponse{ID: 1, Name: DashboardA.Name, Grid: DashboardA.Grid, ScenarioID: DashboardA.ScenarioID} -var DashboardB = Dashboard{Name: "Dashboard_B", Grid: 10} -var DashboardB_response = DashboardResponse{ID: 2, Name: DashboardB.Name, Grid: DashboardB.Grid, ScenarioID: DashboardB.ScenarioID} -var DashboardC = Dashboard{Name: "Dashboard_C", Grid: 25} -var DashboardC_response = DashboardResponse{ID: 3, Name: DashboardC.Name, Grid: DashboardC.Grid, ScenarioID: DashboardC.ScenarioID} -var DashboardCUpdated = Dashboard{Name: "Dashboard_Cupdated", Grid: 24} -var DashboardCUpdated_response = DashboardResponse{ID: 3, Name: DashboardCUpdated.Name, Grid: DashboardCUpdated.Grid, ScenarioID: DashboardCUpdated.ScenarioID} +var DashboardA = Dashboard{ + Name: "Dashboard_A", + Grid: 15, +} +var DashboardB = Dashboard{ + Name: "Dashboard_B", + Grid: 10, +} // Files diff --git a/doc/api/responses.go b/doc/api/responses.go index 20e4075..b64e4fa 100644 --- a/doc/api/responses.go +++ b/doc/api/responses.go @@ -48,3 +48,11 @@ type ResponseSimulationModels struct { type ResponseSimulationModel struct { model common.SimulationModel } + +type ResponseDashboards struct { + dashboards []common.Dashboard +} + +type ResponseDashboard struct { + dashboard common.Dashboard +} diff --git a/routes/dashboard/dashboard_endpoints.go b/routes/dashboard/dashboard_endpoints.go index be2f1ee..7efa3ef 100644 --- a/routes/dashboard/dashboard_endpoints.go +++ b/routes/dashboard/dashboard_endpoints.go @@ -1,6 +1,7 @@ package dashboard import ( + "fmt" "net/http" "github.com/gin-gonic/gin" @@ -23,11 +24,10 @@ func RegisterDashboardEndpoints(r *gin.RouterGroup) { // @ID getDashboards // @Produce json // @Tags dashboards -// @Success 200 {array} common.DashboardResponse "Array of dashboards to which belong to scenario" -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" +// @Success 200 {object} docs.ResponseDashboards "Dashboards to which belong to scenario" +// @Failure 404 {object} docs.ResponseError "Not found" +// @Failure 422 {object} docs.ResponseError "Unprocessable entity" +// @Failure 500 {object} docs.ResponseError "Internal server error" // @Param scenarioID query int true "Scenario ID" // @Router /dashboards [get] func getDashboards(c *gin.Context) { @@ -44,9 +44,8 @@ func getDashboards(c *gin.Context) { return } - serializer := common.DashboardsSerializer{c, dab} c.JSON(http.StatusOK, gin.H{ - "dashboards": serializer.Response(), + "dashboards": dab, }) } @@ -56,44 +55,54 @@ func getDashboards(c *gin.Context) { // @Accept json // @Produce json // @Tags dashboards -// @Param inputDab body common.ResponseMsgDashboard true "Dashboard to be added incl. ID of Scenario" -// @Success 200 "OK." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" +// @Param inputDab body dashboard.validNewDashboard true "Dashboard to be added incl. ID of Scenario" +// @Success 200 {object} docs.ResponseDashboard "Dashboards that was added" +// @Failure 400 {object} docs.ResponseError "Bad request" +// @Failure 404 {object} docs.ResponseError "Not found" +// @Failure 422 {object} docs.ResponseError "Unprocessable entity" +// @Failure 500 {object} docs.ResponseError "Internal server error" // @Router /dashboards [post] func addDashboard(c *gin.Context) { - var newDabData common.ResponseMsgDashboard - err := c.BindJSON(&newDabData) - if err != nil { - errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + // bind request to JSON + var req addDashboardRequest + if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, + "success": false, + "message": fmt.Sprintf("%v", err), }) return } - var newDab Dashboard - newDab.ID = newDabData.Dashboard.ID - newDab.Grid = newDabData.Dashboard.Grid - newDab.ScenarioID = newDabData.Dashboard.ScenarioID - newDab.Name = newDabData.Dashboard.Name + // Validate the request + if err := req.validate(); err != nil { + c.JSON(http.StatusUnprocessableEntity, gin.H{ + "success": false, + "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 { return } // add dashboard to DB and add association to scenario - err = newDab.addToScenario() - if common.ProvideErrorResponse(c, err) == false { - c.JSON(http.StatusOK, gin.H{ - "message": "OK.", - }) + err := newDashboard.addToScenario() + if err != nil { + common.ProvideErrorResponse(c, err) + return } + c.JSON(http.StatusOK, gin.H{ + "dashboard": newDashboard.Dashboard, + }) + } // updateDashboard godoc @@ -102,37 +111,59 @@ func addDashboard(c *gin.Context) { // @Tags dashboards // @Accept json // @Produce json -// @Param inputDab body common.ResponseMsgDashboard true "Dashboard to be updated" -// @Success 200 "OK." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" +// @Param inputDab body dashboard.validUpdatedDashboard true "Dashboard to be updated" +// @Success 200 {object} docs.ResponseDashboard "Dashboards that was updated" +// @Failure 400 {object} docs.ResponseError "Bad request" +// @Failure 404 {object} docs.ResponseError "Not found" +// @Failure 422 {object} docs.ResponseError "Unprocessable entity" +// @Failure 500 {object} docs.ResponseError "Internal server error" // @Param dashboardID path int true "Dashboard ID" // @Router /dashboards/{dashboardID} [put] func updateDashboard(c *gin.Context) { - ok, d := CheckPermissions(c, common.Update, "path", -1) + ok, oldDashboard := CheckPermissions(c, common.Update, "path", -1) if !ok { return } - var modifiedDab common.ResponseMsgDashboard - err := c.BindJSON(&modifiedDab) - if err != nil { - errormsg := "Bad request. Error binding form data to JSON: " + err.Error() + var req updateDashboardRequest + if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, + "success": false, + "message": fmt.Sprintf("%v", err), }) return } - err = d.update(modifiedDab.Dashboard) - if common.ProvideErrorResponse(c, err) == false { - c.JSON(http.StatusOK, gin.H{ - "message": "OK.", + // Validate the request + if err := req.validate(); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "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 @@ -140,11 +171,11 @@ func updateDashboard(c *gin.Context) { // @ID getDashboard // @Tags dashboards // @Produce json -// @Success 200 {object} common.DashboardResponse "Requested dashboard." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" +// @Success 200 {object} docs.ResponseDashboard "Dashboards that was requested" +// @Failure 400 {object} docs.ResponseError "Bad request" +// @Failure 404 {object} docs.ResponseError "Not found" +// @Failure 422 {object} docs.ResponseError "Unprocessable entity" +// @Failure 500 {object} docs.ResponseError "Internal server error" // @Param dashboardID path int true "Dashboard ID" // @Router /dashboards/{dashboardID} [get] func getDashboard(c *gin.Context) { @@ -154,9 +185,8 @@ func getDashboard(c *gin.Context) { return } - serializer := common.DashboardSerializer{c, dab.Dashboard} c.JSON(http.StatusOK, gin.H{ - "dashboard": serializer.Response(), + "dashboard": dab.Dashboard, }) } @@ -165,11 +195,11 @@ func getDashboard(c *gin.Context) { // @ID deleteDashboard // @Tags dashboards // @Produce json -// @Success 200 "OK." -// @Failure 401 "Unauthorized Access" -// @Failure 403 "Access forbidden." -// @Failure 404 "Not found" -// @Failure 500 "Internal server error" +// @Success 200 {object} docs.ResponseDashboard "Dashboards that was deleted" +// @Failure 400 {object} docs.ResponseError "Bad request" +// @Failure 404 {object} docs.ResponseError "Not found" +// @Failure 422 {object} docs.ResponseError "Unprocessable entity" +// @Failure 500 {object} docs.ResponseError "Internal server error" // @Param dashboardID path int true "Dashboard ID" // @Router /dashboards/{dashboardID} [delete] func deleteDashboard(c *gin.Context) { @@ -184,6 +214,6 @@ func deleteDashboard(c *gin.Context) { } c.JSON(http.StatusOK, gin.H{ - "message": "OK.", + "dashboard": dab.Dashboard, }) } diff --git a/routes/dashboard/dashboard_methods.go b/routes/dashboard/dashboard_methods.go index a534b67..2747a8e 100644 --- a/routes/dashboard/dashboard_methods.go +++ b/routes/dashboard/dashboard_methods.go @@ -1,8 +1,6 @@ package dashboard import ( - "fmt" - "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/scenario" ) @@ -21,7 +19,7 @@ func (d *Dashboard) ByID(id uint) error { db := common.GetDB() err := db.Find(d, id).Error if err != nil { - return fmt.Errorf("Dashboard with id=%v does not exist", id) + return err } return nil } @@ -46,10 +44,11 @@ func (d *Dashboard) addToScenario() error { return err } -func (d *Dashboard) update(modifiedDab common.DashboardResponse) error { +func (d *Dashboard) update(modifiedDab Dashboard) error { db := common.GetDB() + // TODO do we allow to update scenarioID here as well? err := db.Model(d).Updates(map[string]interface{}{ "Name": modifiedDab.Name, "Grid": modifiedDab.Grid, diff --git a/routes/dashboard/dashboard_middleware.go b/routes/dashboard/dashboard_middleware.go index 956d9ef..dfddaa1 100644 --- a/routes/dashboard/dashboard_middleware.go +++ b/routes/dashboard/dashboard_middleware.go @@ -17,7 +17,10 @@ func CheckPermissions(c *gin.Context, operation common.CRUD, dabIDSource string, err := common.ValidateRole(c, common.ModelDashboard, operation) 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 } @@ -25,18 +28,18 @@ func CheckPermissions(c *gin.Context, operation common.CRUD, dabIDSource string, if dabIDSource == "path" { dabID, err = strconv.Atoi(c.Param("dashboardID")) if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of dashboardID path parameter") c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, + "success": false, + "message": fmt.Sprintf("Bad request. No or incorrect format of dashboardID path parameter"), }) return false, dab } } else if dabIDSource == "query" { dabID, err = strconv.Atoi(c.Request.URL.Query().Get("dashboardID")) if err != nil { - errormsg := fmt.Sprintf("Bad request. No or incorrect format of dashboardID query parameter") c.JSON(http.StatusBadRequest, gin.H{ - "error": errormsg, + "success": false, + "message": fmt.Sprintf("Bad request. No or incorrect format of dashboardID query parameter"), }) return false, dab } diff --git a/routes/dashboard/dashboard_test.go b/routes/dashboard/dashboard_test.go index db77bcf..6b0790a 100644 --- a/routes/dashboard/dashboard_test.go +++ b/routes/dashboard/dashboard_test.go @@ -1,65 +1,277 @@ package dashboard import ( - "encoding/json" - "testing" - - "github.com/gin-gonic/gin" - + "fmt" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" + "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/scenario" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/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 -func TestEndpoints(t *testing.T) { +var router *gin.Engine +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} - var msgDashboards = common.ResponseMsgDashboards{Dashboards: myDashboards} - var msgDab = common.ResponseMsgDashboard{Dashboard: common.DashboardC_response} - var msgDabupdated = common.ResponseMsgDashboard{Dashboard: common.DashboardCUpdated_response} +type ScenarioRequest struct { + Name string `json:"name,omitempty"` + Running bool `json:"running,omitempty"` + 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() - common.DummyPopulateDB(db) - router := gin.Default() + router = gin.Default() api := router.Group("/api") - // All endpoints require authentication except when someone wants to - // login (POST /authenticate) user.RegisterAuthenticate(api.Group("/authenticate")) - api.Use(user.Authentication(true)) - 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) - msgOKjson, _ := json.Marshal(common.MsgOK) - msgDashboardsjson, _ := json.Marshal(msgDashboards) - msgDabjson, _ := json.Marshal(msgDab) - msgDabupdatedjson, _ := json.Marshal(msgDabupdated) + os.Exit(m.Run()) +} - 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 - common.TestEndpoint(t, router, token, "/api/dashboards?scenarioID=1", "GET", nil, 200, msgDashboardsjson) + // authenticate as normal user + token, err := common.NewAuthenticateForTest(router, + "/api/authenticate", "POST", common.UserACredentials) + assert.NoError(t, err) - // test POST dashboards - common.TestEndpoint(t, router, token, "/api/dashboards", "POST", msgDabjson, 200, msgOKjson) + scenarioID := addScenario(token) - // test GET dashboards/:dashboardID to check if previous POST worked correctly - common.TestEndpoint(t, router, token, "/api/dashboards/3", "GET", nil, 200, msgDabjson) + // 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) - // test PUT dashboards/:dashboardID - common.TestEndpoint(t, router, token, "/api/dashboards/3", "PUT", msgDabupdatedjson, 200, msgOKjson) - common.TestEndpoint(t, router, token, "/api/dashboards/3", "GET", nil, 200, msgDabupdatedjson) + // Compare POST's response with the newDashboard + err = common.CompareResponse(resp, common.KeyModels{"dashboard": newDashboard}) + assert.NoError(t, err) - // test DELETE dashboards/:dashboardID - common.TestEndpoint(t, router, token, "/api/dashboards/3", "DELETE", nil, 200, msgOKjson) - common.TestEndpoint(t, router, token, "/api/dashboards?scenarioID=1", "GET", nil, 200, msgDashboardsjson) + // Read newDashboard's ID from the response + newDashbaordID, err := common.GetResponseID(resp) + 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) +} diff --git a/routes/dashboard/dashboard_validators.go b/routes/dashboard/dashboard_validators.go new file mode 100644 index 0000000..e013cf9 --- /dev/null +++ b/routes/dashboard/dashboard_validators.go @@ -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 +}