refactor: standardize API error handling (#1635)

This commit is contained in:
Elias Schneider
2026-08-02 23:36:06 +02:00
committed by GitHub
parent 281bea54d3
commit 7a4d0dd275
108 changed files with 3163 additions and 1629 deletions
@@ -7,6 +7,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/pocket-id/pocket-id/backend/internal/appconfig"
"github.com/pocket-id/pocket-id/backend/internal/dto"
"github.com/pocket-id/pocket-id/backend/internal/httpserver"
"github.com/pocket-id/pocket-id/backend/internal/middleware"
"github.com/pocket-id/pocket-id/backend/internal/service"
"github.com/pocket-id/pocket-id/backend/internal/utils"
@@ -25,13 +26,13 @@ func NewUserGroupController(group *gin.RouterGroup, authMiddleware *middleware.A
userGroupsGroup := group.Group("/user-groups")
userGroupsGroup.Use(authMiddleware.Add())
{
userGroupsGroup.GET("", ugc.list)
userGroupsGroup.GET("/:id", ugc.get)
userGroupsGroup.POST("", ugc.create)
userGroupsGroup.PUT("/:id", ugc.update)
userGroupsGroup.DELETE("/:id", ugc.delete)
userGroupsGroup.PUT("/:id/users", ugc.updateUsers)
userGroupsGroup.PUT("/:id/allowed-oidc-clients", ugc.updateAllowedOidcClients)
userGroupsGroup.GET("", httpserver.Handle(ugc.list))
userGroupsGroup.GET("/:id", httpserver.Handle(ugc.get))
userGroupsGroup.POST("", httpserver.Handle(ugc.create))
userGroupsGroup.PUT("/:id", httpserver.Handle(ugc.update))
userGroupsGroup.DELETE("/:id", httpserver.Handle(ugc.delete))
userGroupsGroup.PUT("/:id/users", httpserver.Handle(ugc.updateUsers))
userGroupsGroup.PUT("/:id/allowed-oidc-clients", httpserver.Handle(ugc.updateAllowedOidcClients))
}
}
@@ -51,14 +52,13 @@ type UserGroupController struct {
// @Param sort[direction] query string false "Sort direction (asc or desc)" default("asc")
// @Success 200 {object} dto.Paginated[dto.UserGroupMinimalDto]
// @Router /api/user-groups [get]
func (ugc *UserGroupController) list(c *gin.Context) {
func (ugc *UserGroupController) list(c *gin.Context) error {
searchTerm := c.Query("search")
listRequestOptions := utils.ParseListRequestOptions(c)
groups, pagination, err := ugc.UserGroupService.List(c, searchTerm, listRequestOptions)
if err != nil {
_ = c.Error(err)
return
return err
}
// Map the user groups to DTOs
@@ -66,13 +66,11 @@ func (ugc *UserGroupController) list(c *gin.Context) {
for i, group := range groups {
var groupDto dto.UserGroupMinimalDto
if err := dto.MapStruct(group, &groupDto); err != nil {
_ = c.Error(err)
return
return err
}
groupDto.UserCount, err = ugc.UserGroupService.GetUserCountOfGroup(c.Request.Context(), group.ID)
if err != nil {
_ = c.Error(err)
return
return err
}
groupsDto[i] = groupDto
}
@@ -81,6 +79,7 @@ func (ugc *UserGroupController) list(c *gin.Context) {
Data: groupsDto,
Pagination: pagination,
})
return nil
}
// get godoc
@@ -92,20 +91,19 @@ func (ugc *UserGroupController) list(c *gin.Context) {
// @Param id path string true "User Group ID"
// @Success 200 {object} dto.UserGroupDto
// @Router /api/user-groups/{id} [get]
func (ugc *UserGroupController) get(c *gin.Context) {
func (ugc *UserGroupController) get(c *gin.Context) error {
group, err := ugc.UserGroupService.Get(c.Request.Context(), c.Param("id"))
if err != nil {
_ = c.Error(err)
return
return err
}
var groupDto dto.UserGroupDto
if err := dto.MapStruct(group, &groupDto); err != nil {
_ = c.Error(err)
return
return err
}
c.JSON(http.StatusOK, groupDto)
return nil
}
// create godoc
@@ -117,26 +115,24 @@ func (ugc *UserGroupController) get(c *gin.Context) {
// @Param userGroup body dto.UserGroupCreateDto true "User group information"
// @Success 201 {object} dto.UserGroupDto "Created user group"
// @Router /api/user-groups [post]
func (ugc *UserGroupController) create(c *gin.Context) {
func (ugc *UserGroupController) create(c *gin.Context) error {
var input dto.UserGroupCreateDto
if err := dto.ShouldBindWithNormalizedJSON(c, &input); err != nil {
_ = c.Error(err)
return
if err := httpserver.BindJSON(c, &input); err != nil {
return err
}
group, err := ugc.UserGroupService.Create(c.Request.Context(), input)
if err != nil {
_ = c.Error(err)
return
return err
}
var groupDto dto.UserGroupDto
if err := dto.MapStruct(group, &groupDto); err != nil {
_ = c.Error(err)
return
return err
}
c.JSON(http.StatusCreated, groupDto)
return nil
}
// update godoc
@@ -149,32 +145,29 @@ func (ugc *UserGroupController) create(c *gin.Context) {
// @Param userGroup body dto.UserGroupCreateDto true "User group information"
// @Success 200 {object} dto.UserGroupDto "Updated user group"
// @Router /api/user-groups/{id} [put]
func (ugc *UserGroupController) update(c *gin.Context) {
func (ugc *UserGroupController) update(c *gin.Context) error {
dbConfig, err := ugc.appConfigService.GetConfig(c.Request.Context())
if err != nil {
_ = c.Error(fmt.Errorf("error loading app configuration: %w", err))
return
return fmt.Errorf("error loading app configuration: %w", err)
}
var input dto.UserGroupCreateDto
if err := dto.ShouldBindWithNormalizedJSON(c, &input); err != nil {
_ = c.Error(err)
return
if err := httpserver.BindJSON(c, &input); err != nil {
return err
}
group, err := ugc.UserGroupService.Update(c.Request.Context(), dbConfig, c.Param("id"), input)
if err != nil {
_ = c.Error(err)
return
return err
}
var groupDto dto.UserGroupDto
if err := dto.MapStruct(group, &groupDto); err != nil {
_ = c.Error(err)
return
return err
}
c.JSON(http.StatusOK, groupDto)
return nil
}
// delete godoc
@@ -186,19 +179,18 @@ func (ugc *UserGroupController) update(c *gin.Context) {
// @Param id path string true "User Group ID"
// @Success 204 "No Content"
// @Router /api/user-groups/{id} [delete]
func (ugc *UserGroupController) delete(c *gin.Context) {
func (ugc *UserGroupController) delete(c *gin.Context) error {
dbConfig, err := ugc.appConfigService.GetConfig(c.Request.Context())
if err != nil {
_ = c.Error(fmt.Errorf("error loading app configuration: %w", err))
return
return fmt.Errorf("error loading app configuration: %w", err)
}
if err := ugc.UserGroupService.Delete(c.Request.Context(), dbConfig, c.Param("id")); err != nil {
_ = c.Error(err)
return
return err
}
c.Status(http.StatusNoContent)
return nil
}
// updateUsers godoc
@@ -211,26 +203,24 @@ func (ugc *UserGroupController) delete(c *gin.Context) {
// @Param users body dto.UserGroupUpdateUsersDto true "List of user IDs to assign to this group"
// @Success 200 {object} dto.UserGroupDto
// @Router /api/user-groups/{id}/users [put]
func (ugc *UserGroupController) updateUsers(c *gin.Context) {
func (ugc *UserGroupController) updateUsers(c *gin.Context) error {
var input dto.UserGroupUpdateUsersDto
if err := c.ShouldBindJSON(&input); err != nil {
_ = c.Error(err)
return
if err := httpserver.BindJSON(c, &input); err != nil {
return err
}
group, err := ugc.UserGroupService.UpdateUsers(c.Request.Context(), c.Param("id"), input.UserIDs)
if err != nil {
_ = c.Error(err)
return
return err
}
var groupDto dto.UserGroupDto
if err := dto.MapStruct(group, &groupDto); err != nil {
_ = c.Error(err)
return
return err
}
c.JSON(http.StatusOK, groupDto)
return nil
}
// updateAllowedOidcClients godoc
@@ -243,24 +233,22 @@ func (ugc *UserGroupController) updateUsers(c *gin.Context) {
// @Param groups body dto.UserGroupUpdateAllowedOidcClientsDto true "OIDC client IDs to allow"
// @Success 200 {object} dto.UserGroupDto "Updated user group"
// @Router /api/user-groups/{id}/allowed-oidc-clients [put]
func (ugc *UserGroupController) updateAllowedOidcClients(c *gin.Context) {
func (ugc *UserGroupController) updateAllowedOidcClients(c *gin.Context) error {
var input dto.UserGroupUpdateAllowedOidcClientsDto
if err := c.ShouldBindJSON(&input); err != nil {
_ = c.Error(err)
return
if err := httpserver.BindJSON(c, &input); err != nil {
return err
}
userGroup, err := ugc.UserGroupService.UpdateAllowedOidcClient(c.Request.Context(), c.Param("id"), input)
if err != nil {
_ = c.Error(err)
return
return err
}
var userGroupDto dto.UserGroupDto
if err := dto.MapStruct(userGroup, &userGroupDto); err != nil {
_ = c.Error(err)
return
return err
}
c.JSON(http.StatusOK, userGroupDto)
return nil
}