diff --git a/api/v1/controller/generic.go b/api/v1/controller/generic.go new file mode 100644 index 0000000..9b0add6 --- /dev/null +++ b/api/v1/controller/generic.go @@ -0,0 +1,124 @@ +package controller + +import ( + "context" + "net/http" + "reflect" + + "github.com/gin-gonic/gin" + "github.com/zetsux/gin-gorm-api-starter/support/base" +) + +func HandleCreate[T any, R any]( + ctx *gin.Context, + dto T, + createFunc func(context.Context, T) (R, error), + successMsg, failMsg string, +) { + if err := ctx.ShouldBind(&dto); err != nil { + msg := base.GetValidationErrorMessage(err, dto, failMsg) + _ = ctx.Error(base.NewAppError(http.StatusBadRequest, msg, err)) + return + } + + result, err := createFunc(ctx, dto) + if err != nil { + _ = ctx.Error(base.NewAppError(http.StatusBadRequest, failMsg, err)) + return + } + + ctx.JSON(http.StatusCreated, base.CreateSuccessResponse( + successMsg, http.StatusCreated, result)) +} + +func HandleGetAll[T any, R any]( + ctx *gin.Context, + dto T, + getAllFunc func(context.Context, T) (R, base.PaginationResponse, error), + successMsg, failMsg string, +) { + if err := ctx.ShouldBind(&dto); err != nil { + msg := base.GetValidationErrorMessage(err, dto, failMsg) + _ = ctx.Error(base.NewAppError(http.StatusBadRequest, msg, err)) + return + } + + results, pageMeta, err := getAllFunc(ctx, dto) + if err != nil { + _ = ctx.Error(base.NewAppError(http.StatusBadRequest, failMsg, err)) + return + } + + if reflect.DeepEqual(pageMeta, base.PaginationResponse{}) { + ctx.JSON(http.StatusOK, base.CreateSuccessResponse( + successMsg, http.StatusOK, results, + )) + } else { + ctx.JSON(http.StatusOK, base.CreatePaginatedResponse( + successMsg, http.StatusOK, results, pageMeta, + )) + } +} + +func HandleGetByID[R any]( + ctx *gin.Context, + id string, + getByIDFunc func(context.Context, string) (R, error), + successMsg, failMsg string, +) { + result, err := getByIDFunc(ctx, id) + if err != nil { + _ = ctx.Error(base.NewAppError(http.StatusBadRequest, failMsg, err)) + return + } + + ctx.JSON(http.StatusOK, base.CreateSuccessResponse( + successMsg, http.StatusOK, result, + )) +} + +func HandleUpdate[T any, R any]( + ctx *gin.Context, + id string, + dto T, + updateFunc func(context.Context, T) (R, error), + successMsg, failMsg string, +) { + if err := ctx.ShouldBind(&dto); err != nil { + msg := base.GetValidationErrorMessage(err, dto, failMsg) + _ = ctx.Error(base.NewAppError(http.StatusBadRequest, msg, err)) + return + } + + v := reflect.ValueOf(&dto).Elem() + if field := v.FieldByName("ID"); field.IsValid() && field.CanSet() && field.Kind() == reflect.String { + field.SetString(id) + } + + result, err := updateFunc(ctx, dto) + if err != nil { + _ = ctx.Error(base.NewAppError(http.StatusBadRequest, failMsg, err)) + return + } + + ctx.JSON(http.StatusOK, base.CreateSuccessResponse( + successMsg, http.StatusOK, result, + )) +} + +func HandleDelete( + ctx *gin.Context, + id string, + deleteFunc func(context.Context, string) error, + successMsg, failMsg string, +) { + err := deleteFunc(ctx, id) + if err != nil { + _ = ctx.Error(base.NewAppError(http.StatusBadRequest, failMsg, err)) + return + } + + ctx.JSON(http.StatusOK, base.CreateSuccessResponse( + successMsg, http.StatusOK, nil, + )) +} diff --git a/api/v1/controller/tests/user_test.go b/api/v1/controller/tests/user_test.go index fcbe721..a66b66a 100644 --- a/api/v1/controller/tests/user_test.go +++ b/api/v1/controller/tests/user_test.go @@ -21,6 +21,7 @@ import ( "github.com/zetsux/gin-gorm-api-starter/api/v1/router" "github.com/zetsux/gin-gorm-api-starter/core/helper/dto" errs "github.com/zetsux/gin-gorm-api-starter/core/helper/errors" + "github.com/zetsux/gin-gorm-api-starter/core/helper/messages" "github.com/zetsux/gin-gorm-api-starter/core/service" "github.com/zetsux/gin-gorm-api-starter/support/base" "github.com/zetsux/gin-gorm-api-starter/support/constant" @@ -59,8 +60,8 @@ func (m *userServiceMock) DeleteUserByID(ctx context.Context, id string) error { args := m.Called(ctx, id) return args.Error(0) } -func (m *userServiceMock) ChangePicture(ctx context.Context, req dto.UserChangePictureRequest, userID string) (dto.UserResponse, error) { - args := m.Called(ctx, req, userID) +func (m *userServiceMock) ChangePicture(ctx context.Context, req dto.UserChangePictureRequest) (dto.UserResponse, error) { + args := m.Called(ctx, req) return args.Get(0).(dto.UserResponse), args.Error(1) } func (m *userServiceMock) DeletePicture(ctx context.Context, userID string) error { @@ -117,6 +118,11 @@ func TestUserController_Register(t *testing.T) { r.ServeHTTP(w, req) require.Equal(t, http.StatusCreated, w.Code) + + var resp map[string]interface{} + err := json.Unmarshal(w.Body.Bytes(), &resp) + require.NoError(t, err) + require.Equal(t, messages.MsgUserRegisterSuccess, resp["message"]) } func TestUserController_Login(t *testing.T) { @@ -135,6 +141,11 @@ func TestUserController_Login(t *testing.T) { r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) + + var resp map[string]interface{} + err := json.Unmarshal(w.Body.Bytes(), &resp) + require.NoError(t, err) + require.Equal(t, messages.MsgUserLoginSuccess, resp["message"]) } func TestUserController_Login_Invalid(t *testing.T) { @@ -170,6 +181,11 @@ func TestUserController_GetMe(t *testing.T) { r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) + + var resp map[string]interface{} + err := json.Unmarshal(w.Body.Bytes(), &resp) + require.NoError(t, err) + require.Equal(t, messages.MsgUserFetchSuccess, resp["message"]) } func TestUserController_GetMe_Unauthenticated(t *testing.T) { @@ -199,6 +215,11 @@ func TestUserController_GetAllUsers(t *testing.T) { r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) + + var resp map[string]interface{} + err := json.Unmarshal(w.Body.Bytes(), &resp) + require.NoError(t, err) + require.Equal(t, messages.MsgUsersFetchSuccess, resp["message"]) } func TestUserController_UpdateSelfName(t *testing.T) { @@ -220,6 +241,11 @@ func TestUserController_UpdateSelfName(t *testing.T) { r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) + + var resp map[string]interface{} + err := json.Unmarshal(w.Body.Bytes(), &resp) + require.NoError(t, err) + require.Equal(t, messages.MsgUserUpdateSuccess, resp["message"]) } func TestUserController_UpdateUserByID(t *testing.T) { @@ -241,6 +267,11 @@ func TestUserController_UpdateUserByID(t *testing.T) { r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) + + var resp map[string]interface{} + err := json.Unmarshal(w.Body.Bytes(), &resp) + require.NoError(t, err) + require.Equal(t, messages.MsgUserUpdateSuccess, resp["message"]) } func TestUserController_Delete(t *testing.T) { @@ -256,6 +287,11 @@ func TestUserController_Delete(t *testing.T) { r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) + + var resp map[string]interface{} + err := json.Unmarshal(w.Body.Bytes(), &resp) + require.NoError(t, err) + require.Equal(t, messages.MsgUserDeleteSuccess, resp["message"]) } func TestUserController_ChangePicture(t *testing.T) { @@ -283,16 +319,21 @@ func TestUserController_ChangePicture(t *testing.T) { require.NoError(t, err) fileHeader := req.MultipartForm.File["picture"][0] - changePicReq := dto.UserChangePictureRequest{Picture: fileHeader} - usm.On("ChangePicture", mock.Anything, changePicReq, uuidStr).Return( + changePicReq := dto.UserChangePictureRequest{ID: uuidStr, Picture: fileHeader} + usm.On("ChangePicture", mock.Anything, changePicReq).Return( dto.UserResponse{ID: uuidStr}, nil, ) - usm.On("ChangePicture", mock.Anything, changePicReq, uuidStr). + usm.On("ChangePicture", mock.Anything, changePicReq). Return(dto.UserResponse{ID: uuidStr, Name: "Updated"}, nil) r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) + + var resp map[string]interface{} + err = json.Unmarshal(w.Body.Bytes(), &resp) + require.NoError(t, err) + require.Equal(t, messages.MsgUserPictureUpdateSuccess, resp["message"]) } func TestUserController_DeletePicture(t *testing.T) { @@ -309,4 +350,9 @@ func TestUserController_DeletePicture(t *testing.T) { r.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) + + var resp map[string]interface{} + err := json.Unmarshal(w.Body.Bytes(), &resp) + require.NoError(t, err) + require.Equal(t, messages.MsgUserPictureDeleteSuccess, resp["message"]) } diff --git a/api/v1/controller/user.go b/api/v1/controller/user.go index a05815e..54544e2 100644 --- a/api/v1/controller/user.go +++ b/api/v1/controller/user.go @@ -2,7 +2,6 @@ package controller import ( "net/http" - "reflect" "github.com/zetsux/gin-gorm-api-starter/core/helper/dto" "github.com/zetsux/gin-gorm-api-starter/core/helper/messages" @@ -38,50 +37,34 @@ func NewUserController(userS service.UserService, jwtS service.JWTService) UserC } } -func (ct *userController) Register(ctx *gin.Context) { - var req dto.UserRegisterRequest - if err := ctx.ShouldBind(&req); err != nil { - msg := base.GetValidationErrorMessage(err, req, messages.MsgUserRegisterFailed) - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, msg, err)) - return - } - - newUser, err := ct.userService.CreateNewUser(ctx, req) - if err != nil { - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, - messages.MsgUserRegisterFailed, err)) - return - } - - ctx.JSON(http.StatusCreated, base.CreateSuccessResponse( - messages.MsgUserRegisterSuccess, - http.StatusCreated, newUser, - )) +func (uc *userController) Register(ctx *gin.Context) { + HandleCreate(ctx, dto.UserRegisterRequest{}, uc.userService.CreateNewUser, + messages.MsgUserRegisterSuccess, messages.MsgUserRegisterFailed) } -func (ct *userController) Login(ctx *gin.Context) { - var req dto.UserLoginRequest - if err := ctx.ShouldBind(&req); err != nil { - msg := base.GetValidationErrorMessage(err, req, messages.MsgUserLoginFailed) +func (uc *userController) Login(ctx *gin.Context) { + var userDTO dto.UserLoginRequest + if err := ctx.ShouldBind(&userDTO); err != nil { + msg := base.GetValidationErrorMessage(err, userDTO, messages.MsgUserLoginFailed) _ = ctx.Error(base.NewAppError(http.StatusBadRequest, msg, err)) return } - res := ct.userService.VerifyLogin(ctx, req.Email, req.Password) + res := uc.userService.VerifyLogin(ctx, userDTO.Email, userDTO.Password) if !res { _ = ctx.Error(base.NewAppError(http.StatusUnauthorized, messages.MsgUserWrongCredential, nil)) return } - user, err := ct.userService.GetUserByPrimaryKey(ctx, constant.DBAttrEmail, req.Email) + user, err := uc.userService.GetUserByPrimaryKey(ctx, constant.DBAttrEmail, userDTO.Email) if err != nil { _ = ctx.Error(base.NewAppError(http.StatusBadRequest, messages.MsgUserLoginFailed, err)) return } - token := ct.jwtService.GenerateToken(user.ID, user.Role) + token := uc.jwtService.GenerateToken(user.ID, user.Role) authResp := base.CreateAuthResponse(token, user.Role) ctx.JSON(http.StatusOK, base.CreateSuccessResponse( messages.MsgUserLoginSuccess, @@ -89,37 +72,14 @@ func (ct *userController) Login(ctx *gin.Context) { )) } -func (ct *userController) GetAllUsers(ctx *gin.Context) { - var req dto.UserGetsRequest - if err := ctx.ShouldBind(&req); err != nil { - msg := base.GetValidationErrorMessage(err, req, messages.MsgUsersFetchFailed) - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, msg, err)) - return - } - - users, pageMeta, err := ct.userService.GetAllUsers(ctx, req) - if err != nil { - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, - messages.MsgUsersFetchFailed, err)) - return - } - - if reflect.DeepEqual(pageMeta, base.PaginationResponse{}) { - ctx.JSON(http.StatusOK, base.CreateSuccessResponse( - messages.MsgUsersFetchSuccess, - http.StatusOK, users, - )) - } else { - ctx.JSON(http.StatusOK, base.CreatePaginatedResponse( - messages.MsgUsersFetchSuccess, - http.StatusOK, users, pageMeta, - )) - } +func (uc *userController) GetAllUsers(ctx *gin.Context) { + HandleGetAll(ctx, dto.UserGetsRequest{}, uc.userService.GetAllUsers, + messages.MsgUsersFetchSuccess, messages.MsgUsersFetchFailed) } -func (ct *userController) GetMe(ctx *gin.Context) { +func (uc *userController) GetMe(ctx *gin.Context) { id := ctx.MustGet("ID").(string) - user, err := ct.userService.GetUserByPrimaryKey(ctx, constant.DBAttrID, id) + user, err := uc.userService.GetUserByPrimaryKey(ctx, constant.DBAttrID, id) if err != nil { _ = ctx.Error(base.NewAppError(http.StatusBadRequest, messages.MsgUserFetchFailed, err)) @@ -132,118 +92,38 @@ func (ct *userController) GetMe(ctx *gin.Context) { )) } -func (ct *userController) UpdateSelfName(ctx *gin.Context) { +func (uc *userController) UpdateSelfName(ctx *gin.Context) { id := ctx.MustGet("ID").(string) - - var req dto.UserNameUpdateRequest - if err := ctx.ShouldBind(&req); err != nil { - msg := base.GetValidationErrorMessage(err, req, messages.MsgUserUpdateFailed) - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, msg, err)) - return - } - - req.ID = id - user, err := ct.userService.UpdateSelfName(ctx, req) - if err != nil { - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, - messages.MsgUserUpdateFailed, err)) - return - } - - ctx.JSON(http.StatusOK, base.CreateSuccessResponse( - messages.MsgUserUpdateSuccess, - http.StatusOK, user, - )) + HandleUpdate(ctx, id, dto.UserNameUpdateRequest{}, uc.userService.UpdateSelfName, + messages.MsgUserUpdateSuccess, messages.MsgUserUpdateFailed) } -func (ct *userController) UpdateUserByID(ctx *gin.Context) { +func (uc *userController) UpdateUserByID(ctx *gin.Context) { id := ctx.Param("user_id") - - var req dto.UserUpdateRequest - if err := ctx.ShouldBind(&req); err != nil { - msg := base.GetValidationErrorMessage(err, req, messages.MsgUserUpdateFailed) - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, msg, err)) - return - } - - req.ID = id - user, err := ct.userService.UpdateUserByID(ctx, req) - if err != nil { - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, - messages.MsgUserUpdateFailed, err)) - return - } - - ctx.JSON(http.StatusOK, base.CreateSuccessResponse( - messages.MsgUserUpdateSuccess, - http.StatusOK, user, - )) + HandleUpdate(ctx, id, dto.UserUpdateRequest{}, uc.userService.UpdateUserByID, + messages.MsgUserUpdateSuccess, messages.MsgUserUpdateFailed) } -func (ct *userController) DeleteSelfUser(ctx *gin.Context) { +func (uc *userController) DeleteSelfUser(ctx *gin.Context) { id := ctx.MustGet("ID").(string) - err := ct.userService.DeleteUserByID(ctx, id) - if err != nil { - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, - messages.MsgUserDeleteFailed, err)) - return - } - - ctx.JSON(http.StatusOK, base.CreateSuccessResponse( - messages.MsgUserDeleteSuccess, - http.StatusOK, nil, - )) + HandleDelete(ctx, id, uc.userService.DeleteUserByID, + messages.MsgUserDeleteSuccess, messages.MsgUserDeleteFailed) } -func (ct *userController) DeleteUserByID(ctx *gin.Context) { +func (uc *userController) DeleteUserByID(ctx *gin.Context) { id := ctx.Param("user_id") - err := ct.userService.DeleteUserByID(ctx, id) - if err != nil { - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, - messages.MsgUserDeleteFailed, err)) - return - } - - ctx.JSON(http.StatusOK, base.CreateSuccessResponse( - messages.MsgUserDeleteSuccess, - http.StatusOK, nil, - )) + HandleDelete(ctx, id, uc.userService.DeleteUserByID, + messages.MsgUserDeleteSuccess, messages.MsgUserDeleteFailed) } -func (ct *userController) ChangePicture(ctx *gin.Context) { +func (uc *userController) ChangePicture(ctx *gin.Context) { id := ctx.MustGet("ID").(string) - - var req dto.UserChangePictureRequest - if err := ctx.ShouldBind(&req); err != nil { - msg := base.GetValidationErrorMessage(err, req, messages.MsgUserPictureUpdateFailed) - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, msg, err)) - return - } - - res, err := ct.userService.ChangePicture(ctx, req, id) - if err != nil { - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, - messages.MsgUserPictureUpdateFailed, err)) - return - } - - ctx.JSON(http.StatusOK, base.CreateSuccessResponse( - messages.MsgUserPictureUpdateSuccess, - http.StatusOK, res, - )) + HandleUpdate(ctx, id, dto.UserChangePictureRequest{}, uc.userService.ChangePicture, + messages.MsgUserPictureUpdateSuccess, messages.MsgUserPictureUpdateFailed) } -func (ct *userController) DeletePicture(ctx *gin.Context) { +func (uc *userController) DeletePicture(ctx *gin.Context) { id := ctx.Param("user_id") - err := ct.userService.DeletePicture(ctx, id) - if err != nil { - _ = ctx.Error(base.NewAppError(http.StatusBadRequest, - messages.MsgUserPictureDeleteFailed, err)) - return - } - - ctx.JSON(http.StatusOK, base.CreateSuccessResponse( - messages.MsgUserPictureDeleteSuccess, - http.StatusOK, nil, - )) + HandleDelete(ctx, id, uc.userService.DeletePicture, + messages.MsgUserPictureDeleteSuccess, messages.MsgUserPictureDeleteFailed) } diff --git a/core/helper/dto/user.go b/core/helper/dto/user.go index 47736b3..0c55f7d 100644 --- a/core/helper/dto/user.go +++ b/core/helper/dto/user.go @@ -39,6 +39,7 @@ type ( } UserChangePictureRequest struct { + ID string `json:"id"` Picture *multipart.FileHeader `json:"picture" form:"picture"` } diff --git a/core/service/tests/picture_test.go b/core/service/tests/picture_test.go index e94d8fe..9298d37 100644 --- a/core/service/tests/picture_test.go +++ b/core/service/tests/picture_test.go @@ -67,7 +67,7 @@ func TestUserService_ChangePicture(t *testing.T) { repo.On("GetUserByPrimaryKey", ctx, (*gorm.DB)(nil), "id", createdUser.ID.String()).Return(createdUser, nil).Once() repo.On("UpdateUser", ctx, (*gorm.DB)(nil), mock.AnythingOfType("entity.User")).Return(nil) - updated, err := us.ChangePicture(ctx, dto.UserChangePictureRequest{Picture: fh}, created.ID) + updated, err := us.ChangePicture(ctx, dto.UserChangePictureRequest{ID: created.ID, Picture: fh}) require.NoError(t, err) require.NotEmpty(t, updated.Picture) expectedFilePath := filepath.Join(tmpDir, "files", updated.Picture) diff --git a/core/service/user.go b/core/service/user.go index 79ae29c..3e9e6bd 100644 --- a/core/service/user.go +++ b/core/service/user.go @@ -29,7 +29,7 @@ type UserService interface { UpdateSelfName(ctx context.Context, req dto.UserNameUpdateRequest) (dto.UserResponse, error) UpdateUserByID(ctx context.Context, req dto.UserUpdateRequest) (dto.UserResponse, error) DeleteUserByID(ctx context.Context, id string) error - ChangePicture(ctx context.Context, req dto.UserChangePictureRequest, userID string) (dto.UserResponse, error) + ChangePicture(ctx context.Context, req dto.UserChangePictureRequest) (dto.UserResponse, error) DeletePicture(ctx context.Context, userID string) error } @@ -208,8 +208,8 @@ func (sv *userService) DeleteUserByID(ctx context.Context, id string) error { } func (sv *userService) ChangePicture(ctx context.Context, - req dto.UserChangePictureRequest, userID string) (dto.UserResponse, error) { - user, err := sv.userRepository.GetUserByPrimaryKey(ctx, nil, constant.DBAttrID, userID) + req dto.UserChangePictureRequest) (dto.UserResponse, error) { + user, err := sv.userRepository.GetUserByPrimaryKey(ctx, nil, constant.DBAttrID, req.ID) if err != nil { return dto.UserResponse{}, err } diff --git a/go.mod b/go.mod index 1bc5f15..f7d7d72 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.11.2 // indirect + github.com/go-playground/validator/v10 v10.11.2 github.com/goccy/go-json v0.10.0 // indirect github.com/golang-jwt/jwt/v5 v5.0.0-rc.1 github.com/jackc/pgpassfile v1.0.0 // indirect diff --git a/support/base/query.go b/infrastructure/query/generic.go similarity index 84% rename from support/base/query.go rename to infrastructure/query/generic.go index 370db5d..d2a304d 100644 --- a/support/base/query.go +++ b/infrastructure/query/generic.go @@ -1,10 +1,11 @@ -package base +package query import ( "math" "strings" errs "github.com/zetsux/gin-gorm-api-starter/core/helper/errors" + "github.com/zetsux/gin-gorm-api-starter/support/base" "golang.org/x/exp/slices" "gorm.io/gorm" ) @@ -27,8 +28,8 @@ func applySorting(stmt *gorm.DB, allowedSorts []string, sort string) *gorm.DB { return stmt } -func GetWithPagination[T any](stmt *gorm.DB, req PaginationRequest, allowedSorts []string, -) (data []T, paginationResp PaginationResponse, err error) { +func GetWithPagination[T any](stmt *gorm.DB, req base.PaginationRequest, allowedSorts []string, +) (data []T, paginationResp base.PaginationResponse, err error) { stmt = applySorting(stmt, allowedSorts, req.Sort) if req.PerPage == 0 { diff --git a/infrastructure/query/user.go b/infrastructure/query/user.go index 6ca9904..1b75255 100644 --- a/infrastructure/query/user.go +++ b/infrastructure/query/user.go @@ -36,7 +36,7 @@ func (qr *userQuery) GetAllUsers(ctx context.Context, req dto.UserGetsRequest, stmt = stmt.Where("name ILIKE ? OR email ILIKE ?", search, search) } - users, pageResp, err := base.GetWithPagination[entity.User](stmt, req.PaginationRequest, allowedSorts) + users, pageResp, err := GetWithPagination[entity.User](stmt, req.PaginationRequest, allowedSorts) if err != nil { return nil, pageResp, err }