fix swaggo documentation of user endpoints

This commit is contained in:
Sonja Happ 2019-09-03 12:03:47 +02:00
parent 9c7bb228d3
commit bddbfc028d
4 changed files with 170 additions and 73 deletions

View file

@ -23,12 +23,14 @@ func ProvideErrorResponse(c *gin.Context, err error) bool {
if err == gorm.ErrRecordNotFound { if err == gorm.ErrRecordNotFound {
errormsg := "Record not Found in DB: " + err.Error() errormsg := "Record not Found in DB: " + err.Error()
c.JSON(http.StatusNotFound, gin.H{ c.JSON(http.StatusNotFound, gin.H{
"error": errormsg, "success": false,
"message": errormsg,
}) })
} else { } else {
errormsg := "Error on DB Query or transaction: " + err.Error() errormsg := "Error on DB Query or transaction: " + err.Error()
c.JSON(http.StatusInternalServerError, gin.H{ c.JSON(http.StatusInternalServerError, gin.H{
"error": errormsg, "success": false,
"message": errormsg,
}) })
} }
return true // Error return true // Error

48
doc/api/responses.go Normal file
View file

@ -0,0 +1,48 @@
package docs
import "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
// This file defines the responses to any endpoint in the backend
// The defined structures are only used for documentation purposes with swaggo and are NOT used in the code
type ResponseError struct {
success bool
message string
}
type ResponseAuthenticate struct {
success bool
token string
message string
user common.User
}
type ResponseUsers struct {
success bool
users []common.User
}
type ResponseUser struct {
success bool
user common.User
}
type ResponseSimulators struct {
success bool
simulators []common.Simulator
}
type ResponseSimulator struct {
success bool
simulator common.Simulator
}
type ResponseScenarios struct {
success bool
scenarios []common.Scenario
}
type ResponseScenario struct {
success bool
scenario common.Scenario
}

View file

@ -25,7 +25,7 @@ func RegisterScenarioEndpoints(r *gin.RouterGroup) {
// @ID getScenarios // @ID getScenarios
// @Produce json // @Produce json
// @Tags scenarios // @Tags scenarios
// @Success 200 {array} common.ScenarioResponse "Array of scenarios to which user has access" // @Success 200 {array} docs.ResponseScenarios "Array of scenarios to which user has access"
// @Failure 401 "Unauthorized Access" // @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden." // @Failure 403 "Access forbidden."
// @Failure 404 "Not found" // @Failure 404 "Not found"
@ -135,7 +135,7 @@ func addScenario(c *gin.Context) {
// @Tags scenarios // @Tags scenarios
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param inputScenario body common.ResponseMsgScenario true "Scenario to be updated" // @Param inputScenario body common.Scenario true "Scenario to be updated"
// @Success 200 "OK." // @Success 200 "OK."
// @Failure 401 "Unauthorized Access" // @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden." // @Failure 403 "Access forbidden."
@ -226,7 +226,7 @@ func deleteScenario(c *gin.Context) {
// @ID getUsersOfScenario // @ID getUsersOfScenario
// @Produce json // @Produce json
// @Tags scenarios // @Tags scenarios
// @Success 200 {array} common.UserResponse "Array of users that have access to the scenario" // @Success 200 {object} docs.ResponseUsers "Array of users that have access to the scenario"
// @Failure 401 "Unauthorized Access" // @Failure 401 "Unauthorized Access"
// @Failure 403 "Access forbidden." // @Failure 403 "Access forbidden."
// @Failure 404 "Not found" // @Failure 404 "Not found"

View file

@ -3,12 +3,14 @@ package user
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strings"
"time" "time"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common" "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/common"
_ "git.rwth-aachen.de/acs/public/villas/villasweb-backend-go/doc/api"
) )
// TODO: the signing secret must be environmental variable // TODO: the signing secret must be environmental variable
@ -40,10 +42,11 @@ func RegisterUserEndpoints(r *gin.RouterGroup) {
// @Produce json // @Produce json
// @Tags users // @Tags users
// @Param inputUser body user.loginRequest true "loginRequest of user" // @Param inputUser body user.loginRequest true "loginRequest of user"
// @Success 200 {object} user.AuthResponse "JSON web token and message" // @Success 200 {object} docs.ResponseAuthenticate "JSON web token, success status, message and authenticated user object"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request"
// @Failure 404 "Not found" // @Failure 401 {object} docs.ResponseError "Unauthorized"
// @Failure 422 "Unprocessable entity." // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 422 {object} docs.ResponseError "Unprocessable entity."
// @Router /authenticate [post] // @Router /authenticate [post]
func authenticate(c *gin.Context) { func authenticate(c *gin.Context) {
@ -131,17 +134,19 @@ func authenticate(c *gin.Context) {
// @ID GetUsers // @ID GetUsers
// @Produce json // @Produce json
// @Tags users // @Tags users
// @Success 200 {array} common.UserResponse "Array of users" // @Success 200 {object} docs.ResponseUsers "Array of users"
// @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"
// @Router /users [get] // @Router /users [get]
func getUsers(c *gin.Context) { func getUsers(c *gin.Context) {
err := common.ValidateRole(c, common.ModelUsers, common.Read) err := common.ValidateRole(c, common.ModelUsers, common.Read)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, fmt.Sprintf("%v", err)) c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return return
} }
@ -152,7 +157,10 @@ func getUsers(c *gin.Context) {
return return
} }
c.JSON(http.StatusOK, gin.H{"users": users}) c.JSON(http.StatusOK, gin.H{
"success": true,
"users": users,
})
} }
// AddUser godoc // AddUser godoc
@ -161,25 +169,27 @@ func getUsers(c *gin.Context) {
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Tags users // @Tags users
// @Param inputUser body common.UserResponse true "User to be added" // @Param inputUser body user.validNewUser true "User to be added"
// @Success 200 "OK." // @Success 200 {object} docs.ResponseUser "Contains added user object"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request"
// @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"
// @Router /users [post] // @Router /users [post]
func addUser(c *gin.Context) { func addUser(c *gin.Context) {
err := common.ValidateRole(c, common.ModelUser, common.Create) err := common.ValidateRole(c, common.ModelUser, common.Create)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, fmt.Sprintf("%v", err)) c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return return
} }
// Bind the request // Bind the request
var req addUserRequest var req addUserRequest
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{ c.JSON(http.StatusBadRequest, gin.H{
"success": false, "success": false,
"message": fmt.Sprintf("%v", err), "message": fmt.Sprintf("%v", err),
}) })
@ -188,7 +198,7 @@ func addUser(c *gin.Context) {
// Validate the request // Validate the request
if err = req.validate(); err != nil { if err = req.validate(); err != nil {
c.JSON(http.StatusBadRequest, gin.H{ c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false, "success": false,
"message": fmt.Sprintf("%v", err), "message": fmt.Sprintf("%v", err),
}) })
@ -202,6 +212,7 @@ func addUser(c *gin.Context) {
err = newUser.ByUsername(newUser.Username) err = newUser.ByUsername(newUser.Username)
if err == nil { if err == nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{ c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": "Username is already taken", "message": "Username is already taken",
}) })
return return
@ -211,6 +222,7 @@ func addUser(c *gin.Context) {
err = newUser.setPassword(newUser.Password) err = newUser.setPassword(newUser.Password)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, gin.H{ c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": "Unable to encrypt the password", "message": "Unable to encrypt the password",
}) })
return return
@ -219,13 +231,14 @@ func addUser(c *gin.Context) {
// Save the user in the DB // Save the user in the DB
err = newUser.save() err = newUser.save()
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{ common.ProvideErrorResponse(c, err)
"message": "Unable to create new user",
})
return return
} }
c.JSON(http.StatusOK, gin.H{"user": newUser.User}) c.JSON(http.StatusOK, gin.H{
"success": true,
"user": newUser.User,
})
} }
// UpdateUser godoc // UpdateUser godoc
@ -234,19 +247,23 @@ func addUser(c *gin.Context) {
// @Tags users // @Tags users
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param inputUser body common.User true "User to be updated (anything except for ID can be changed, role can only be change by admin)" // @Param inputUser body user.validUpdatedRequest true "User to be updated (anything except for ID can be changed, role can only be change by admin)"
// @Success 200 "OK." // @Success 200 {object} docs.ResponseUser "Contains updated user"
// @Failure 401 "Unauthorized Access" // @Failure 400 {object} docs.ResponseError "Bad request."
// @Failure 403 "Access forbidden." // @Failure 403 {object} docs.ResponseError "Access forbidden."
// @Failure 404 "Not found" // @Failure 404 {object} docs.ResponseError "Not found"
// @Failure 500 "Internal server error" // @Failure 422 {object} docs.ResponseError "Unprocessable entity"
// @Failure 500 {object} docs.ResponseError "Internal server error"
// @Param userID path int true "User ID" // @Param userID path int true "User ID"
// @Router /users/{userID} [put] // @Router /users/{userID} [put]
func updateUser(c *gin.Context) { func updateUser(c *gin.Context) {
err := common.ValidateRole(c, common.ModelUser, common.Update) err := common.ValidateRole(c, common.ModelUser, common.Update)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, fmt.Sprintf("%v", err)) c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return return
} }
@ -254,15 +271,17 @@ func updateUser(c *gin.Context) {
var oldUser User var oldUser User
toBeUpdatedID, err := common.UintParamFromCtx(c, "userID") toBeUpdatedID, err := common.UintParamFromCtx(c, "userID")
if err != nil { if err != nil {
c.JSON(http.StatusNotFound, c.JSON(http.StatusNotFound, gin.H{
fmt.Sprintf("Could not get user's ID from context")) "success": false,
"message": fmt.Sprintf("Could not get user's ID from context"),
})
return return
} }
// Find the user // Find the user
err = oldUser.ByID(toBeUpdatedID) err = oldUser.ByID(toBeUpdatedID)
if err != nil { if err != nil {
c.JSON(http.StatusNotFound, fmt.Sprintf("%v", err)) common.ProvideErrorResponse(c, err)
return return
} }
@ -278,16 +297,20 @@ func updateUser(c *gin.Context) {
// Get caller's ID from context // Get caller's ID from context
callerID, exists := c.Get(common.UserIDCtx) callerID, exists := c.Get(common.UserIDCtx)
if !exists { if !exists {
c.JSON(http.StatusNotFound, c.JSON(http.StatusNotFound, gin.H{
fmt.Sprintf("Could not get caller's ID from context")) "success": false,
"message": fmt.Sprintf("Could not get caller's ID from context"),
})
return return
} }
// Get caller's Role from context // Get caller's Role from context
callerRole, exists := c.Get(common.UserRoleCtx) callerRole, exists := c.Get(common.UserRoleCtx)
if !exists { if !exists {
c.JSON(http.StatusNotFound, c.JSON(http.StatusNotFound, gin.H{
fmt.Sprintf("Could not get caller's Role from context")) "success": false,
"message": fmt.Sprintf("Could not get caller's Role from context"),
})
return return
} }
@ -322,23 +345,32 @@ func updateUser(c *gin.Context) {
// case that the request updates the role of the old user) // case that the request updates the role of the old user)
updatedUser, err := req.updatedUser(callerRole, oldUser) updatedUser, err := req.updatedUser(callerRole, oldUser)
if err != nil { if err != nil {
c.JSON(http.StatusForbidden, gin.H{ if strings.Contains(err.Error(), "Admin") {
"success": false, c.JSON(http.StatusForbidden, gin.H{
"message": fmt.Sprintf("%v", err), "success": false,
}) "message": fmt.Sprintf("%v", err),
})
} else if strings.Contains(err.Error(), "Username") || strings.Contains(err.Error(), "password") {
c.JSON(http.StatusBadRequest, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
}
return return
} }
// Finaly update the user // Finally update the user
err = oldUser.update(updatedUser) err = oldUser.update(updatedUser)
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{ common.ProvideErrorResponse(c, err)
"message": "Unable to update user",
})
return return
} }
c.JSON(http.StatusOK, gin.H{"user": updatedUser.User}) c.JSON(http.StatusOK, gin.H{
"success": true,
"user": updatedUser.User,
})
} }
// GetUser godoc // GetUser godoc
@ -346,25 +378,30 @@ func updateUser(c *gin.Context) {
// @ID GetUser // @ID GetUser
// @Produce json // @Produce json
// @Tags users // @Tags users
// @Success 200 {object} common.UserResponse "User requested by user" // @Success 200 {object} docs.ResponseUser "requested user"
// @Failure 401 "Unauthorized Access" // @Failure 403 {object} docs.ResponseError "Access forbidden."
// @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 userID path int true "User ID" // @Param userID path int true "User ID"
// @Router /users/{userID} [get] // @Router /users/{userID} [get]
func getUser(c *gin.Context) { func getUser(c *gin.Context) {
err := common.ValidateRole(c, common.ModelUser, common.Read) err := common.ValidateRole(c, common.ModelUser, common.Read)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, fmt.Sprintf("%v", err)) c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return return
} }
id, err := common.UintParamFromCtx(c, "userID") id, err := common.UintParamFromCtx(c, "userID")
if err != nil { if err != nil {
c.JSON(http.StatusNotFound, c.JSON(http.StatusNotFound, gin.H{
fmt.Sprintf("Could not get user's ID from context")) "success": false,
"message": fmt.Sprintf("Could not get user's ID from context"),
})
return return
} }
@ -382,11 +419,14 @@ func getUser(c *gin.Context) {
var user User var user User
err = user.ByID(id) err = user.ByID(id)
if err != nil { if err != nil {
c.JSON(http.StatusNotFound, fmt.Sprintf("%v", err)) common.ProvideErrorResponse(c, err)
return return
} }
c.JSON(http.StatusOK, gin.H{"user": user.User}) c.JSON(http.StatusOK, gin.H{
"success": true,
"user": user.User,
})
} }
// DeleteUser godoc // DeleteUser godoc
@ -394,42 +434,49 @@ func getUser(c *gin.Context) {
// @ID DeleteUser // @ID DeleteUser
// @Tags users // @Tags users
// @Produce json // @Produce json
// @Success 200 "OK." // @Success 200 {object} docs.ResponseUser "deleted user"
// @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 userID path int true "User ID" // @Param userID path int true "User ID"
// @Router /users/{userID} [delete] // @Router /users/{userID} [delete]
func deleteUser(c *gin.Context) { func deleteUser(c *gin.Context) {
err := common.ValidateRole(c, common.ModelUser, common.Delete) err := common.ValidateRole(c, common.ModelUser, common.Delete)
if err != nil { if err != nil {
c.JSON(http.StatusUnprocessableEntity, fmt.Sprintf("%v", err)) c.JSON(http.StatusUnprocessableEntity, gin.H{
"success": false,
"message": fmt.Sprintf("%v", err),
})
return return
} }
var user User var user User
id, err := common.UintParamFromCtx(c, "userID") id, err := common.UintParamFromCtx(c, "userID")
if err != nil { if err != nil {
c.JSON(http.StatusNotFound, c.JSON(http.StatusNotFound, gin.H{
fmt.Sprintf("Could not get user's ID from context")) "success": false,
"message": fmt.Sprintf("Could not get user's ID from context"),
})
return return
} }
// Check that the user exist // Check that the user exist
err = user.ByID(uint(id)) err = user.ByID(uint(id))
if err != nil { if err != nil {
c.JSON(http.StatusNotFound, fmt.Sprintf("%v", err)) common.ProvideErrorResponse(c, err)
return return
} }
// Try to remove user // Try to remove user
err = user.remove() err = user.remove()
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, fmt.Sprintf("%v", err)) common.ProvideErrorResponse(c, err)
return return
} }
c.JSON(http.StatusOK, gin.H{"user": user}) c.JSON(http.StatusOK, gin.H{
"success": true,
"user": user.User,
})
} }