mirror of
https://git.rwth-aachen.de/acs/public/villas/web-backend-go/
synced 2025-03-30 00:00:12 +01:00
- revise testing of widget enpoints
- add validators for widgets - revise implementation of widget endpoints - clean up testdata, serializers and response - improve documentation for swaggo
This commit is contained in:
parent
7c7488ee00
commit
e3651e34f0
9 changed files with 594 additions and 254 deletions
|
@ -1,23 +1,5 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import "github.com/jinzhu/gorm/dialects/postgres"
|
|
||||||
|
|
||||||
type WidgetResponse struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
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"`
|
|
||||||
DashboardID uint `json:"dashboardID"`
|
|
||||||
IsLocked bool `json:"isLocked"`
|
|
||||||
CustomProperties postgres.Jsonb `json:"customProperties"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type FileResponse struct {
|
type FileResponse struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
|
@ -52,14 +34,6 @@ type ResponseMsgSignal struct {
|
||||||
Signal SignalResponse `json:"signal"`
|
Signal SignalResponse `json:"signal"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResponseMsgWidgets struct {
|
|
||||||
Widgets []WidgetResponse `json:"widgets"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResponseMsgWidget struct {
|
|
||||||
Widget WidgetResponse `json:"widget"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResponseMsgFiles struct {
|
type ResponseMsgFiles struct {
|
||||||
Files []FileResponse `json:"files"`
|
Files []FileResponse `json:"files"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,47 +4,6 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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{
|
|
||||||
ID: self.ID,
|
|
||||||
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,
|
|
||||||
DashboardID: self.DashboardID,
|
|
||||||
IsLocked: self.IsLocked,
|
|
||||||
CustomProperties: self.CustomProperties,
|
|
||||||
}
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
// File/s Serializers
|
// File/s Serializers
|
||||||
|
|
||||||
type FilesSerializerNoAssoc struct {
|
type FilesSerializerNoAssoc struct {
|
||||||
|
|
|
@ -265,7 +265,6 @@ var FileD = File{
|
||||||
// Widgets
|
// Widgets
|
||||||
var customPropertiesA = json.RawMessage(`{"property1" : "testValue1A", "property2" : "testValue2A", "property3" : 42}`)
|
var customPropertiesA = json.RawMessage(`{"property1" : "testValue1A", "property2" : "testValue2A", "property3" : 42}`)
|
||||||
var customPropertiesB = json.RawMessage(`{"property1" : "testValue1B", "property2" : "testValue2B", "property3" : 43}`)
|
var customPropertiesB = json.RawMessage(`{"property1" : "testValue1B", "property2" : "testValue2B", "property3" : 43}`)
|
||||||
var customPropertiesC = json.RawMessage(`{"property1" : "testValue1C", "property2" : "testValue2C", "property3" : 44}`)
|
|
||||||
|
|
||||||
var WidgetA = Widget{
|
var WidgetA = Widget{
|
||||||
Name: "Widget_A",
|
Name: "Widget_A",
|
||||||
|
@ -281,21 +280,6 @@ var WidgetA = Widget{
|
||||||
CustomProperties: postgres.Jsonb{customPropertiesA},
|
CustomProperties: postgres.Jsonb{customPropertiesA},
|
||||||
}
|
}
|
||||||
|
|
||||||
var WidgetA_response = WidgetResponse{
|
|
||||||
ID: 1,
|
|
||||||
Name: WidgetA.Name,
|
|
||||||
Type: WidgetA.Type,
|
|
||||||
Width: WidgetA.Width,
|
|
||||||
Height: WidgetA.Height,
|
|
||||||
MinWidth: WidgetA.MinWidth,
|
|
||||||
MinHeight: WidgetA.MinHeight,
|
|
||||||
X: WidgetA.X,
|
|
||||||
Y: WidgetA.Y,
|
|
||||||
Z: WidgetA.Z,
|
|
||||||
IsLocked: WidgetA.IsLocked,
|
|
||||||
CustomProperties: WidgetA.CustomProperties,
|
|
||||||
}
|
|
||||||
|
|
||||||
var WidgetB = Widget{
|
var WidgetB = Widget{
|
||||||
Name: "Widget_B",
|
Name: "Widget_B",
|
||||||
Type: "slider",
|
Type: "slider",
|
||||||
|
@ -305,66 +289,7 @@ var WidgetB = Widget{
|
||||||
MinWidth: 50,
|
MinWidth: 50,
|
||||||
X: 100,
|
X: 100,
|
||||||
Y: -40,
|
Y: -40,
|
||||||
Z: 0,
|
Z: -1,
|
||||||
IsLocked: false,
|
IsLocked: false,
|
||||||
CustomProperties: postgres.Jsonb{customPropertiesB},
|
CustomProperties: postgres.Jsonb{customPropertiesB},
|
||||||
}
|
}
|
||||||
|
|
||||||
var WidgetB_response = WidgetResponse{
|
|
||||||
ID: 2,
|
|
||||||
Name: WidgetB.Name,
|
|
||||||
Type: WidgetB.Type,
|
|
||||||
Width: WidgetB.Width,
|
|
||||||
Height: WidgetB.Height,
|
|
||||||
MinWidth: WidgetB.MinWidth,
|
|
||||||
MinHeight: WidgetB.MinHeight,
|
|
||||||
X: WidgetB.X,
|
|
||||||
Y: WidgetB.Y,
|
|
||||||
Z: WidgetB.Z,
|
|
||||||
IsLocked: WidgetB.IsLocked,
|
|
||||||
CustomProperties: WidgetB.CustomProperties,
|
|
||||||
}
|
|
||||||
|
|
||||||
var WidgetC = Widget{
|
|
||||||
Name: "Widget_C",
|
|
||||||
Type: "bargraph",
|
|
||||||
Height: 30,
|
|
||||||
Width: 100,
|
|
||||||
MinHeight: 20,
|
|
||||||
MinWidth: 50,
|
|
||||||
X: 11,
|
|
||||||
Y: 12,
|
|
||||||
Z: 13,
|
|
||||||
IsLocked: false,
|
|
||||||
CustomProperties: postgres.Jsonb{customPropertiesC},
|
|
||||||
}
|
|
||||||
|
|
||||||
var WidgetC_response = WidgetResponse{
|
|
||||||
ID: 3,
|
|
||||||
Name: WidgetC.Name,
|
|
||||||
Type: WidgetC.Type,
|
|
||||||
Width: WidgetC.Width,
|
|
||||||
Height: WidgetC.Height,
|
|
||||||
MinWidth: WidgetC.MinWidth,
|
|
||||||
MinHeight: WidgetC.MinHeight,
|
|
||||||
X: WidgetC.X,
|
|
||||||
Y: WidgetC.Y,
|
|
||||||
Z: WidgetC.Z,
|
|
||||||
IsLocked: WidgetC.IsLocked,
|
|
||||||
CustomProperties: WidgetC.CustomProperties,
|
|
||||||
}
|
|
||||||
|
|
||||||
var WidgetCUpdated_response = WidgetResponse{
|
|
||||||
ID: 3,
|
|
||||||
Name: "Widget_CUpdated",
|
|
||||||
Type: WidgetC.Type,
|
|
||||||
Height: 35,
|
|
||||||
Width: 110,
|
|
||||||
MinHeight: WidgetC.MinHeight,
|
|
||||||
MinWidth: WidgetC.MinWidth,
|
|
||||||
X: WidgetC.X,
|
|
||||||
Y: WidgetC.Y,
|
|
||||||
Z: WidgetC.Z,
|
|
||||||
IsLocked: WidgetC.IsLocked,
|
|
||||||
CustomProperties: WidgetC.CustomProperties,
|
|
||||||
}
|
|
||||||
|
|
|
@ -56,3 +56,11 @@ type ResponseDashboards struct {
|
||||||
type ResponseDashboard struct {
|
type ResponseDashboard struct {
|
||||||
dashboard common.Dashboard
|
dashboard common.Dashboard
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResponseWidgets struct {
|
||||||
|
widgets []common.Widget
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResponseWidget struct {
|
||||||
|
widget common.Widget
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package widget
|
package widget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
@ -22,11 +23,10 @@ func RegisterWidgetEndpoints(r *gin.RouterGroup) {
|
||||||
// @ID getWidgets
|
// @ID getWidgets
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Tags widgets
|
// @Tags widgets
|
||||||
// @Success 200 {array} common.WidgetResponse "Array of widgets to which belong to dashboard"
|
// @Success 200 {object} docs.ResponseWidgets "Widgets to which belong to dashboard"
|
||||||
// @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 dashboardID query int true "Dashboard ID"
|
// @Param dashboardID query int true "Dashboard ID"
|
||||||
// @Router /widgets [get]
|
// @Router /widgets [get]
|
||||||
func getWidgets(c *gin.Context) {
|
func getWidgets(c *gin.Context) {
|
||||||
|
@ -43,9 +43,8 @@ func getWidgets(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer := common.WidgetsSerializer{c, widgets}
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"widgets": serializer.Response(),
|
"widgets": widgets,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,51 +54,51 @@ func getWidgets(c *gin.Context) {
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Tags widgets
|
// @Tags widgets
|
||||||
// @Param inputWidget body common.ResponseMsgWidget true "Widget to be added incl. ID of dashboard"
|
// @Param inputWidget body widget.validNewWidget true "Widget to be added incl. ID of dashboard"
|
||||||
// @Success 200 "OK."
|
// @Success 200 {object} docs.ResponseWidget "Widget 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 /widgets [post]
|
// @Router /widgets [post]
|
||||||
func addWidget(c *gin.Context) {
|
func addWidget(c *gin.Context) {
|
||||||
|
|
||||||
var newWidgetData common.ResponseMsgWidget
|
var req addWidgetRequest
|
||||||
err := c.BindJSON(&newWidgetData)
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
var newWidget Widget
|
// Validate the request
|
||||||
newWidget.Name = newWidgetData.Widget.Name
|
if err := req.validate(); err != nil {
|
||||||
newWidget.Type = newWidgetData.Widget.Type
|
c.JSON(http.StatusUnprocessableEntity, gin.H{
|
||||||
newWidget.Height = newWidgetData.Widget.Height
|
"success": false,
|
||||||
newWidget.Width = newWidgetData.Widget.Width
|
"message": fmt.Sprintf("%v", err),
|
||||||
newWidget.MinHeight = newWidgetData.Widget.MinHeight
|
})
|
||||||
newWidget.MinWidth = newWidgetData.Widget.MinWidth
|
return
|
||||||
newWidget.X = newWidgetData.Widget.X
|
}
|
||||||
newWidget.Y = newWidgetData.Widget.Y
|
|
||||||
newWidget.Z = newWidgetData.Widget.Z
|
|
||||||
newWidget.CustomProperties = newWidgetData.Widget.CustomProperties
|
|
||||||
newWidget.IsLocked = newWidgetData.Widget.IsLocked
|
|
||||||
newWidget.DashboardID = newWidgetData.Widget.DashboardID
|
|
||||||
|
|
||||||
|
// Create the new widget from the request
|
||||||
|
newWidget := req.createWidget()
|
||||||
|
|
||||||
|
// Check if user is allowed to modify selected dashboard (scenario)
|
||||||
ok, _ := dashboard.CheckPermissions(c, common.Create, "body", int(newWidget.DashboardID))
|
ok, _ := dashboard.CheckPermissions(c, common.Create, "body", int(newWidget.DashboardID))
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = newWidget.addToDashboard()
|
err := newWidget.addToDashboard()
|
||||||
|
if err != nil {
|
||||||
if common.ProvideErrorResponse(c, err) == false {
|
common.ProvideErrorResponse(c, err)
|
||||||
c.JSON(http.StatusOK, gin.H{
|
return
|
||||||
"message": "OK.",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"widget": newWidget.Widget,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateWidget godoc
|
// updateWidget godoc
|
||||||
|
@ -108,37 +107,60 @@ func addWidget(c *gin.Context) {
|
||||||
// @Tags widgets
|
// @Tags widgets
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param inputWidget body common.ResponseMsgWidget true "Widget to be updated"
|
// @Param inputWidget body widget.validUpdatedWidget true "Widget to be updated"
|
||||||
// @Success 200 "OK."
|
// @Success 200 {object} docs.ResponseWidget "Widget 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 widgetID path int true "Widget ID"
|
// @Param widgetID path int true "Widget ID"
|
||||||
// @Router /widgets/{widgetID} [put]
|
// @Router /widgets/{widgetID} [put]
|
||||||
func updateWidget(c *gin.Context) {
|
func updateWidget(c *gin.Context) {
|
||||||
|
|
||||||
ok, w := CheckPermissions(c, common.Update, -1)
|
ok, oldWidget := CheckPermissions(c, common.Update, -1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var modifiedWidget common.ResponseMsgWidget
|
var req updateWidgetRequest
|
||||||
err := c.BindJSON(&modifiedWidget)
|
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 = w.update(modifiedWidget.Widget)
|
// 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
|
||||||
|
updatedWidget, err := req.updatedWidget(oldWidget)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"message": fmt.Sprintf("%v", err),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the widget in the DB
|
||||||
|
err = oldWidget.update(updatedWidget)
|
||||||
|
if err != nil {
|
||||||
|
common.ProvideErrorResponse(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"widget": updatedWidget.Widget,
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getWidget godoc
|
// getWidget godoc
|
||||||
|
@ -146,11 +168,11 @@ func updateWidget(c *gin.Context) {
|
||||||
// @ID getWidget
|
// @ID getWidget
|
||||||
// @Tags widgets
|
// @Tags widgets
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} common.WidgetResponse "Requested widget."
|
// @Success 200 {object} docs.ResponseWidget "Widget 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 widgetID path int true "Widget ID"
|
// @Param widgetID path int true "Widget ID"
|
||||||
// @Router /widgets/{widgetID} [get]
|
// @Router /widgets/{widgetID} [get]
|
||||||
func getWidget(c *gin.Context) {
|
func getWidget(c *gin.Context) {
|
||||||
|
@ -160,9 +182,8 @@ func getWidget(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer := common.WidgetSerializer{c, w.Widget}
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"widget": serializer.Response(),
|
"widget": w.Widget,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,11 +192,11 @@ func getWidget(c *gin.Context) {
|
||||||
// @ID deleteWidget
|
// @ID deleteWidget
|
||||||
// @Tags widgets
|
// @Tags widgets
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 "OK."
|
// @Success 200 {object} docs.ResponseWidget "Widget 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 widgetID path int true "Widget ID"
|
// @Param widgetID path int true "Widget ID"
|
||||||
// @Router /widgets/{widgetID} [delete]
|
// @Router /widgets/{widgetID} [delete]
|
||||||
func deleteWidget(c *gin.Context) {
|
func deleteWidget(c *gin.Context) {
|
||||||
|
@ -191,6 +212,6 @@ func deleteWidget(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"message": "OK.",
|
"widget": w.Widget,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package widget
|
package widget
|
||||||
|
|
||||||
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/dashboard"
|
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/routes/dashboard"
|
||||||
)
|
)
|
||||||
|
@ -21,7 +19,7 @@ func (w *Widget) ByID(id uint) error {
|
||||||
db := common.GetDB()
|
db := common.GetDB()
|
||||||
err := db.Find(w, id).Error
|
err := db.Find(w, id).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Widget with id=%v does not exist", id)
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -46,7 +44,7 @@ func (w *Widget) addToDashboard() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Widget) update(modifiedWidget common.WidgetResponse) error {
|
func (w *Widget) update(modifiedWidget Widget) error {
|
||||||
|
|
||||||
db := common.GetDB()
|
db := common.GetDB()
|
||||||
err := db.Model(w).Updates(map[string]interface{}{
|
err := db.Model(w).Updates(map[string]interface{}{
|
||||||
|
|
|
@ -17,7 +17,10 @@ func CheckPermissions(c *gin.Context, operation common.CRUD, widgetIDBody int) (
|
||||||
|
|
||||||
err := common.ValidateRole(c, common.ModelWidget, operation)
|
err := common.ValidateRole(c, common.ModelWidget, 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, w
|
return false, w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,9 +28,10 @@ func CheckPermissions(c *gin.Context, operation common.CRUD, widgetIDBody int) (
|
||||||
if widgetIDBody < 0 {
|
if widgetIDBody < 0 {
|
||||||
widgetID, err = strconv.Atoi(c.Param("widgetID"))
|
widgetID, err = strconv.Atoi(c.Param("widgetID"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errormsg := fmt.Sprintf("Bad request. No or incorrect format of widgetID 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 widgetID path parameter"),
|
||||||
})
|
})
|
||||||
return false, w
|
return false, w
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,65 +1,407 @@
|
||||||
package widget
|
package widget
|
||||||
|
|
||||||
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/dashboard"
|
||||||
|
"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 /widgets endpoints
|
var router *gin.Engine
|
||||||
func TestWidgetEndpoints(t *testing.T) {
|
var db *gorm.DB
|
||||||
|
|
||||||
var token string
|
type WidgetRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Width uint `json:"width,omitempty"`
|
||||||
|
Height uint `json:"height,omitempty"`
|
||||||
|
MinWidth uint `json:"minWidth,omitempty"`
|
||||||
|
MinHeight uint `json:"minHeight,omitempty"`
|
||||||
|
X int `json:"x,omitempty"`
|
||||||
|
Y int `json:"y,omitempty"`
|
||||||
|
Z int `json:"z,omitempty"`
|
||||||
|
DashboardID uint `json:"dashboardID,omitempty"`
|
||||||
|
IsLocked bool `json:"isLocked,omitempty"`
|
||||||
|
CustomProperties postgres.Jsonb `json:"customProperties,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
var myWidgets = []common.WidgetResponse{common.WidgetA_response, common.WidgetB_response}
|
type DashboardRequest struct {
|
||||||
var msgWidgets = common.ResponseMsgWidgets{Widgets: myWidgets}
|
Name string `json:"name,omitempty"`
|
||||||
var msgWdg = common.ResponseMsgWidget{Widget: common.WidgetC_response}
|
Grid int `json:"grid,omitempty"`
|
||||||
var msgWdgupdated = common.ResponseMsgWidget{Widget: common.WidgetCUpdated_response}
|
ScenarioID uint `json:"scenarioID,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
db := common.DummyInitDB()
|
type ScenarioRequest struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Running bool `json:"running,omitempty"`
|
||||||
|
StartParameters postgres.Jsonb `json:"startParameters,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func addScenarioAndDashboard(token string) (scenarioID uint, dashboardID 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)
|
||||||
|
|
||||||
|
// test POST dashboards/ $newDashboard
|
||||||
|
newDashboard := DashboardRequest{
|
||||||
|
Name: common.DashboardA.Name,
|
||||||
|
Grid: common.DashboardA.Grid,
|
||||||
|
ScenarioID: uint(newScenarioID),
|
||||||
|
}
|
||||||
|
_, resp, _ = common.NewTestEndpoint(router, token,
|
||||||
|
"/api/dashboards", "POST", common.KeyModels{"dashboard": newDashboard})
|
||||||
|
|
||||||
|
// Read newDashboard's ID from the response
|
||||||
|
newDashboardID, _ := common.GetResponseID(resp)
|
||||||
|
|
||||||
|
return uint(newScenarioID), uint(newDashboardID)
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
RegisterWidgetEndpoints(api.Group("/widgets"))
|
RegisterWidgetEndpoints(api.Group("/widgets"))
|
||||||
|
// 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"))
|
||||||
|
// dashboard endpoints required here to first add a dashboard to the DB
|
||||||
|
// that can be associated with a new widget
|
||||||
|
dashboard.RegisterDashboardEndpoints(api.Group("/dashboards"))
|
||||||
|
|
||||||
credjson, _ := json.Marshal(common.CredUser)
|
os.Exit(m.Run())
|
||||||
msgOKjson, _ := json.Marshal(common.MsgOK)
|
}
|
||||||
msgWidgetsjson, _ := json.Marshal(msgWidgets)
|
|
||||||
msgWdgjson, _ := json.Marshal(msgWdg)
|
|
||||||
msgWdgupdatedjson, _ := json.Marshal(msgWdgupdated)
|
|
||||||
|
|
||||||
token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
|
func TestAddWidget(t *testing.T) {
|
||||||
|
common.DropTables(db)
|
||||||
|
common.MigrateModels(db)
|
||||||
|
common.DummyAddOnlyUserTableWithAdminAndUsersDB(db)
|
||||||
|
|
||||||
// test GET widgets
|
// authenticate as normal user
|
||||||
common.TestEndpoint(t, router, token, "/api/widgets?dashboardID=1", "GET", nil, 200, msgWidgetsjson)
|
token, err := common.NewAuthenticateForTest(router,
|
||||||
|
"/api/authenticate", "POST", common.UserACredentials)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test POST widgets
|
_, dashboardID := addScenarioAndDashboard(token)
|
||||||
common.TestEndpoint(t, router, token, "/api/widgets", "POST", msgWdgjson, 200, msgOKjson)
|
|
||||||
|
|
||||||
// test GET widgets/:widgetID to check if previous POST worked correctly
|
// test POST widgets/ $newWidget
|
||||||
common.TestEndpoint(t, router, token, "/api/widgets/3", "GET", nil, 200, msgWdgjson)
|
newWidget := WidgetRequest{
|
||||||
|
Name: common.WidgetA.Name,
|
||||||
|
Type: common.WidgetA.Type,
|
||||||
|
Width: common.WidgetA.Width,
|
||||||
|
Height: common.WidgetA.Height,
|
||||||
|
MinWidth: common.WidgetA.MinWidth,
|
||||||
|
MinHeight: common.WidgetA.MinHeight,
|
||||||
|
X: common.WidgetA.X,
|
||||||
|
Y: common.WidgetA.Y,
|
||||||
|
Z: common.WidgetA.Z,
|
||||||
|
IsLocked: common.WidgetA.IsLocked,
|
||||||
|
CustomProperties: common.WidgetA.CustomProperties,
|
||||||
|
DashboardID: dashboardID,
|
||||||
|
}
|
||||||
|
code, resp, err := common.NewTestEndpoint(router, token,
|
||||||
|
"/api/widgets", "POST", common.KeyModels{"widget": newWidget})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
// test PUT widgets/:widgetID
|
// Compare POST's response with the newWidget
|
||||||
common.TestEndpoint(t, router, token, "/api/widgets/3", "PUT", msgWdgupdatedjson, 200, msgOKjson)
|
err = common.CompareResponse(resp, common.KeyModels{"widget": newWidget})
|
||||||
common.TestEndpoint(t, router, token, "/api/widgets/3", "GET", nil, 200, msgWdgupdatedjson)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// test DELETE widgets/:widgetID
|
// Read newWidget's ID from the response
|
||||||
common.TestEndpoint(t, router, token, "/api/widgets/3", "DELETE", nil, 200, msgOKjson)
|
newWidgetID, err := common.GetResponseID(resp)
|
||||||
common.TestEndpoint(t, router, token, "/api/widgets?dashboardID=1", "GET", nil, 200, msgWidgetsjson)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// TODO add testing for other return codes
|
// Get the newWidget
|
||||||
|
code, resp, err = common.NewTestEndpoint(router, token,
|
||||||
|
fmt.Sprintf("/api/widgets/%v", newWidgetID), "GET", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
|
// Compare GET's response with the newWidget
|
||||||
|
err = common.CompareResponse(resp, common.KeyModels{"widget": newWidget})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// try to POST a malformed widget
|
||||||
|
// Required fields are missing
|
||||||
|
malformedNewWidget := WidgetRequest{
|
||||||
|
Name: "ThisIsAMalformedDashboard",
|
||||||
|
}
|
||||||
|
// this should NOT work and return a unprocessable entity 442 status code
|
||||||
|
code, resp, err = common.NewTestEndpoint(router, token,
|
||||||
|
"/api/widgets", "POST", common.KeyModels{"widget": malformedNewWidget})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 422, code, "Response body: \n%v\n", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateWidget(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)
|
||||||
|
|
||||||
|
_, dashboardID := addScenarioAndDashboard(token)
|
||||||
|
|
||||||
|
// test POST widgets/ $newWidget
|
||||||
|
newWidget := WidgetRequest{
|
||||||
|
Name: common.WidgetA.Name,
|
||||||
|
Type: common.WidgetA.Type,
|
||||||
|
Width: common.WidgetA.Width,
|
||||||
|
Height: common.WidgetA.Height,
|
||||||
|
MinWidth: common.WidgetA.MinWidth,
|
||||||
|
MinHeight: common.WidgetA.MinHeight,
|
||||||
|
X: common.WidgetA.X,
|
||||||
|
Y: common.WidgetA.Y,
|
||||||
|
Z: common.WidgetA.Z,
|
||||||
|
IsLocked: common.WidgetA.IsLocked,
|
||||||
|
CustomProperties: common.WidgetA.CustomProperties,
|
||||||
|
DashboardID: dashboardID,
|
||||||
|
}
|
||||||
|
code, resp, err := common.NewTestEndpoint(router, token,
|
||||||
|
"/api/widgets", "POST", common.KeyModels{"widget": newWidget})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
|
// Read newWidget's ID from the response
|
||||||
|
newWidgetID, err := common.GetResponseID(resp)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
updatedWidget := WidgetRequest{
|
||||||
|
Name: common.WidgetB.Name,
|
||||||
|
Type: common.WidgetB.Type,
|
||||||
|
Width: common.WidgetB.Width,
|
||||||
|
Height: common.WidgetB.Height,
|
||||||
|
MinWidth: common.WidgetB.MinWidth,
|
||||||
|
MinHeight: common.WidgetB.MinHeight,
|
||||||
|
CustomProperties: common.WidgetA.CustomProperties,
|
||||||
|
}
|
||||||
|
|
||||||
|
code, resp, err = common.NewTestEndpoint(router, token,
|
||||||
|
fmt.Sprintf("/api/widgets/%v", newWidgetID), "PUT", common.KeyModels{"widget": updatedWidget})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
|
// Compare PUT's response with the updatedWidget
|
||||||
|
err = common.CompareResponse(resp, common.KeyModels{"widget": updatedWidget})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Get the updatedWidget
|
||||||
|
code, resp, err = common.NewTestEndpoint(router, token,
|
||||||
|
fmt.Sprintf("/api/widgets/%v", newWidgetID), "GET", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
|
// Compare GET's response with the updatedWidget
|
||||||
|
err = common.CompareResponse(resp, common.KeyModels{"widget": updatedWidget})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// try to update a widget that does not exist (should return not found 404 status code)
|
||||||
|
code, resp, err = common.NewTestEndpoint(router, token,
|
||||||
|
fmt.Sprintf("/api/widgets/%v", newWidgetID+1), "PUT", common.KeyModels{"widget": updatedWidget})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 404, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteWidget(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)
|
||||||
|
|
||||||
|
_, dashboardID := addScenarioAndDashboard(token)
|
||||||
|
|
||||||
|
// test POST widgets/ $newWidget
|
||||||
|
newWidget := WidgetRequest{
|
||||||
|
Name: common.WidgetA.Name,
|
||||||
|
Type: common.WidgetA.Type,
|
||||||
|
Width: common.WidgetA.Width,
|
||||||
|
Height: common.WidgetA.Height,
|
||||||
|
MinWidth: common.WidgetA.MinWidth,
|
||||||
|
MinHeight: common.WidgetA.MinHeight,
|
||||||
|
X: common.WidgetA.X,
|
||||||
|
Y: common.WidgetA.Y,
|
||||||
|
Z: common.WidgetA.Z,
|
||||||
|
IsLocked: common.WidgetA.IsLocked,
|
||||||
|
CustomProperties: common.WidgetA.CustomProperties,
|
||||||
|
DashboardID: dashboardID,
|
||||||
|
}
|
||||||
|
code, resp, err := common.NewTestEndpoint(router, token,
|
||||||
|
"/api/widgets", "POST", common.KeyModels{"widget": newWidget})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
|
// Read newWidget's ID from the response
|
||||||
|
newWidgetID, err := common.GetResponseID(resp)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Count the number of all the widgets returned for dashboard
|
||||||
|
initialNumber, err := common.LengthOfResponse(router, token,
|
||||||
|
fmt.Sprintf("/api/widgets?dashboardID=%v", dashboardID), "GET", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Delete the added newWidget
|
||||||
|
code, resp, err = common.NewTestEndpoint(router, token,
|
||||||
|
fmt.Sprintf("/api/widgets/%v", newWidgetID), "DELETE", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
|
// Compare DELETE's response with the newWidget
|
||||||
|
err = common.CompareResponse(resp, common.KeyModels{"widget": newWidget})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Again count the number of all the widgets returned for dashboard
|
||||||
|
finalNumber, err := common.LengthOfResponse(router, token,
|
||||||
|
fmt.Sprintf("/api/widgets?dashboardID=%v", dashboardID), "GET", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, initialNumber-1, finalNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAllWidgetsOfDashboard(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)
|
||||||
|
|
||||||
|
_, dashboardID := addScenarioAndDashboard(token)
|
||||||
|
|
||||||
|
// Count the number of all the widgets returned for dashboard
|
||||||
|
initialNumber, err := common.LengthOfResponse(router, token,
|
||||||
|
fmt.Sprintf("/api/widgets?dashboardID=%v", dashboardID), "GET", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// test POST widgets/ $newWidget
|
||||||
|
newWidgetA := WidgetRequest{
|
||||||
|
Name: common.WidgetA.Name,
|
||||||
|
Type: common.WidgetA.Type,
|
||||||
|
Width: common.WidgetA.Width,
|
||||||
|
Height: common.WidgetA.Height,
|
||||||
|
MinWidth: common.WidgetA.MinWidth,
|
||||||
|
MinHeight: common.WidgetA.MinHeight,
|
||||||
|
X: common.WidgetA.X,
|
||||||
|
Y: common.WidgetA.Y,
|
||||||
|
Z: common.WidgetA.Z,
|
||||||
|
IsLocked: common.WidgetA.IsLocked,
|
||||||
|
CustomProperties: common.WidgetA.CustomProperties,
|
||||||
|
DashboardID: dashboardID,
|
||||||
|
}
|
||||||
|
code, resp, err := common.NewTestEndpoint(router, token,
|
||||||
|
"/api/widgets", "POST", common.KeyModels{"widget": newWidgetA})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
|
newWidgetB := WidgetRequest{
|
||||||
|
Name: common.WidgetB.Name,
|
||||||
|
Type: common.WidgetB.Type,
|
||||||
|
Width: common.WidgetB.Width,
|
||||||
|
Height: common.WidgetB.Height,
|
||||||
|
MinWidth: common.WidgetB.MinWidth,
|
||||||
|
MinHeight: common.WidgetB.MinHeight,
|
||||||
|
X: common.WidgetB.X,
|
||||||
|
Y: common.WidgetB.Y,
|
||||||
|
Z: common.WidgetB.Z,
|
||||||
|
IsLocked: common.WidgetB.IsLocked,
|
||||||
|
CustomProperties: common.WidgetB.CustomProperties,
|
||||||
|
DashboardID: dashboardID,
|
||||||
|
}
|
||||||
|
code, resp, err = common.NewTestEndpoint(router, token,
|
||||||
|
"/api/widgets", "POST", common.KeyModels{"widget": newWidgetB})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equalf(t, 200, code, "Response body: \n%v\n", resp)
|
||||||
|
|
||||||
|
// Again count the number of all the widgets returned for dashboard
|
||||||
|
finalNumber, err := common.LengthOfResponse(router, token,
|
||||||
|
fmt.Sprintf("/api/widgets?dashboardID=%v", dashboardID), "GET", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, initialNumber+2, finalNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
//// Test /widgets endpoints
|
||||||
|
//func TestWidgetEndpoints(t *testing.T) {
|
||||||
|
//
|
||||||
|
// var token string
|
||||||
|
//
|
||||||
|
// var myWidgets = []common.WidgetResponse{common.WidgetA_response, common.WidgetB_response}
|
||||||
|
// var msgWidgets = common.ResponseMsgWidgets{Widgets: myWidgets}
|
||||||
|
// var msgWdg = common.ResponseMsgWidget{Widget: common.WidgetC_response}
|
||||||
|
// var msgWdgupdated = common.ResponseMsgWidget{Widget: common.WidgetCUpdated_response}
|
||||||
|
//
|
||||||
|
// db := common.DummyInitDB()
|
||||||
|
// defer db.Close()
|
||||||
|
// common.DummyPopulateDB(db)
|
||||||
|
//
|
||||||
|
// 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))
|
||||||
|
//
|
||||||
|
// RegisterWidgetEndpoints(api.Group("/widgets"))
|
||||||
|
//
|
||||||
|
// credjson, _ := json.Marshal(common.CredUser)
|
||||||
|
// msgOKjson, _ := json.Marshal(common.MsgOK)
|
||||||
|
// msgWidgetsjson, _ := json.Marshal(msgWidgets)
|
||||||
|
// msgWdgjson, _ := json.Marshal(msgWdg)
|
||||||
|
// msgWdgupdatedjson, _ := json.Marshal(msgWdgupdated)
|
||||||
|
//
|
||||||
|
// token = common.AuthenticateForTest(t, router, "/api/authenticate", "POST", credjson, 200)
|
||||||
|
//
|
||||||
|
// // test GET widgets
|
||||||
|
// common.TestEndpoint(t, router, token, "/api/widgets?dashboardID=1", "GET", nil, 200, msgWidgetsjson)
|
||||||
|
//
|
||||||
|
// // test POST widgets
|
||||||
|
// common.TestEndpoint(t, router, token, "/api/widgets", "POST", msgWdgjson, 200, msgOKjson)
|
||||||
|
//
|
||||||
|
// // test GET widgets/:widgetID to check if previous POST worked correctly
|
||||||
|
// common.TestEndpoint(t, router, token, "/api/widgets/3", "GET", nil, 200, msgWdgjson)
|
||||||
|
//
|
||||||
|
// // test PUT widgets/:widgetID
|
||||||
|
// common.TestEndpoint(t, router, token, "/api/widgets/3", "PUT", msgWdgupdatedjson, 200, msgOKjson)
|
||||||
|
// common.TestEndpoint(t, router, token, "/api/widgets/3", "GET", nil, 200, msgWdgupdatedjson)
|
||||||
|
//
|
||||||
|
// // test DELETE widgets/:widgetID
|
||||||
|
// common.TestEndpoint(t, router, token, "/api/widgets/3", "DELETE", nil, 200, msgOKjson)
|
||||||
|
// common.TestEndpoint(t, router, token, "/api/widgets?dashboardID=1", "GET", nil, 200, msgWidgetsjson)
|
||||||
|
//
|
||||||
|
// // TODO add testing for other return codes
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
109
routes/widget/widget_validators.go
Normal file
109
routes/widget/widget_validators.go
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
package widget
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/jinzhu/gorm/dialects/postgres"
|
||||||
|
"github.com/nsf/jsondiff"
|
||||||
|
"gopkg.in/go-playground/validator.v9"
|
||||||
|
)
|
||||||
|
|
||||||
|
var validate *validator.Validate
|
||||||
|
|
||||||
|
type validNewWidget struct {
|
||||||
|
Name string `form:"name" validate:"required"`
|
||||||
|
Type string `form:"type" validate:"required"`
|
||||||
|
Width uint `form:"width" validate:"required"`
|
||||||
|
Height uint `form:"height" validate:"required"`
|
||||||
|
MinWidth uint `form:"minWidth" validate:"omitempty"`
|
||||||
|
MinHeight uint `form:"minHeight" validate:"omitempty"`
|
||||||
|
X int `form:"x" validate:"omitempty"`
|
||||||
|
Y int `form:"y" validate:"omitempty"`
|
||||||
|
Z int `form:"z" validate:"omitempty"`
|
||||||
|
DashboardID uint `form:"dashboardID" validate:"required"`
|
||||||
|
IsLocked bool `form:"isLocked" validate:"omitempty"`
|
||||||
|
CustomProperties postgres.Jsonb `form:"customProperties" validate:"omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type validUpdatedWidget struct {
|
||||||
|
Name string `form:"name" validate:"omitempty"`
|
||||||
|
Type string `form:"type" validate:"omitempty"`
|
||||||
|
Width uint `form:"width" validate:"omitempty"`
|
||||||
|
Height uint `form:"height" validate:"omitempty"`
|
||||||
|
MinWidth uint `form:"minWidth" validate:"omitempty"`
|
||||||
|
MinHeight uint `form:"minHeight" validate:"omitempty"`
|
||||||
|
X int `form:"x" validate:"omitempty"`
|
||||||
|
Y int `form:"y" validate:"omitempty"`
|
||||||
|
Z int `form:"z" validate:"omitempty"`
|
||||||
|
IsLocked bool `form:"isLocked" validate:"omitempty"`
|
||||||
|
CustomProperties postgres.Jsonb `form:"customProperties" validate:"omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type addWidgetRequest struct {
|
||||||
|
validNewWidget `json:"widget"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type updateWidgetRequest struct {
|
||||||
|
validUpdatedWidget `json:"widget"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *addWidgetRequest) validate() error {
|
||||||
|
validate = validator.New()
|
||||||
|
errs := validate.Struct(r)
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *validUpdatedWidget) validate() error {
|
||||||
|
validate = validator.New()
|
||||||
|
errs := validate.Struct(r)
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *addWidgetRequest) createWidget() Widget {
|
||||||
|
var s Widget
|
||||||
|
|
||||||
|
s.Name = r.Name
|
||||||
|
s.Type = r.Type
|
||||||
|
s.Width = r.Width
|
||||||
|
s.Height = r.Height
|
||||||
|
s.MinWidth = r.MinWidth
|
||||||
|
s.MinHeight = r.MinHeight
|
||||||
|
s.X = r.X
|
||||||
|
s.Y = r.Y
|
||||||
|
s.Z = r.Z
|
||||||
|
s.IsLocked = r.IsLocked
|
||||||
|
s.CustomProperties = r.CustomProperties
|
||||||
|
s.DashboardID = r.DashboardID
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *updateWidgetRequest) updatedWidget(oldWidget Widget) (Widget, error) {
|
||||||
|
// Use the old Widget as a basis for the updated Widget `s`
|
||||||
|
s := oldWidget
|
||||||
|
|
||||||
|
if r.Name != "" {
|
||||||
|
s.Name = r.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Type = r.Type
|
||||||
|
s.Width = r.Width
|
||||||
|
s.Height = r.Height
|
||||||
|
s.MinWidth = r.MinWidth
|
||||||
|
s.MinHeight = r.MinHeight
|
||||||
|
s.X = r.X
|
||||||
|
s.Y = r.Y
|
||||||
|
s.Z = r.Z
|
||||||
|
s.IsLocked = r.IsLocked
|
||||||
|
|
||||||
|
// only update custom props if not empty
|
||||||
|
var emptyJson postgres.Jsonb
|
||||||
|
// Serialize empty json and params
|
||||||
|
emptyJson_ser, _ := json.Marshal(emptyJson)
|
||||||
|
customprops_ser, _ := json.Marshal(r.CustomProperties)
|
||||||
|
opts := jsondiff.DefaultConsoleOptions()
|
||||||
|
diff, _ := jsondiff.Compare(emptyJson_ser, customprops_ser, &opts)
|
||||||
|
if diff.String() != "FullMatch" {
|
||||||
|
s.CustomProperties = r.CustomProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue