Skip to content
6 changes: 0 additions & 6 deletions backend/internal/handler/guests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/generate/selfserve/internal/errs"
"github.com/generate/selfserve/internal/models"
storage "github.com/generate/selfserve/internal/service/storage/postgres"
"github.com/generate/selfserve/internal/validation"
"github.com/gofiber/fiber/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -39,11 +38,6 @@ func (m *mockGuestsRepository) UpdateGuest(ctx context.Context, id string, updat
// Makes the compiler verify the mock
var _ storage.GuestsRepository = (*mockGuestsRepository)(nil)

func TestMain(m *testing.M) {
validation.Init()
m.Run()
}

func TestGuestsHandler_CreateGuest(t *testing.T) {
t.Parallel()

Expand Down
10 changes: 3 additions & 7 deletions backend/internal/handler/hotels.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"log/slog"

"github.com/generate/selfserve/internal/errs"
"github.com/generate/selfserve/internal/httpx"
"github.com/generate/selfserve/internal/models"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
Expand Down Expand Up @@ -85,13 +86,8 @@ func (h *HotelsHandler) CreateHotel(c *fiber.Ctx) error {
return errs.InvalidJSON()
}

// Validate required fields
if hotelRequest.Name == "" {
return errs.BadRequest("hotel name is required")
}

if hotelRequest.Floors <= 0 {
return errs.BadRequest("hotel floors must be greater than 0")
if err := httpx.BindAndValidate(c, &hotelRequest); err != nil {
return err
}

createdHotel, err := h.repo.InsertHotel(c.Context(), &hotelRequest)
Expand Down
13 changes: 13 additions & 0 deletions backend/internal/handler/test_setup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package handler

import (
"os"
"testing"

"github.com/generate/selfserve/internal/validation"
)

func TestMain(m *testing.M) {
validation.Init()
os.Exit(m.Run())
}
31 changes: 2 additions & 29 deletions backend/internal/handler/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import (
"context"
"errors"
"log/slog"
"strings"
"time"

"github.com/generate/selfserve/internal/errs"
"github.com/generate/selfserve/internal/httpx"
"github.com/generate/selfserve/internal/models"
"github.com/gofiber/fiber/v2"
)
Expand Down Expand Up @@ -69,7 +68,7 @@ func (h *UsersHandler) CreateUser(c *fiber.Ctx) error {
return errs.InvalidJSON()
}

if err := validateCreateUser(&CreateUserRequest); err != nil {
if err := httpx.BindAndValidate(c, &CreateUserRequest); err != nil {
return err
}

Expand All @@ -80,29 +79,3 @@ func (h *UsersHandler) CreateUser(c *fiber.Ctx) error {

return c.JSON(res)
}

func validateCreateUser(user *models.CreateUser) error {
errors := make(map[string]string)

if strings.TrimSpace(user.FirstName) == "" {
errors["first_name"] = "must not be an empty string"
}

if strings.TrimSpace(user.LastName) == "" {
errors["last_name"] = "must not be an empty string"
}

if user.Timezone != nil {
_, err := time.LoadLocation(*user.Timezone)
if err != nil || !strings.Contains(*user.Timezone, "/") {
errors["timezone"] = "invalid IANA timezone"
}
}

if strings.TrimSpace(user.ClerkID) == "" {
errors["clerk_id"] = "must not be an empty string"
}

// Aggregates errors deterministically
return AggregateErrors(errors)
}
4 changes: 2 additions & 2 deletions backend/internal/models/hotels.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package models
import "time"

type CreateHotelRequest struct {
Name string `json:"name" example:"Hotel California"`
Floors int `json:"floors" example:"10"`
Name string `json:"name" validate:"notblank" example:"Hotel California"`
Floors int `json:"floors" validate:"gt=1" example:"10"`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it should be gte here, in the rare case that the hotel only has one floor

Suggested change
Floors int `json:"floors" validate:"gt=1" example:"10"`
Floors int `json:"floors" validate:"gte=1" example:"10"`

} //@name CreateHotelRequest

type Hotel struct {
Expand Down
16 changes: 8 additions & 8 deletions backend/internal/models/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package models
import "time"

type CreateUser struct {
FirstName string `json:"first_name" example:"John"`
LastName string `json:"last_name" example:"Doe"`
EmployeeID *string `json:"employee_id" example:"EMP-1234"`
ProfilePicture *string `json:"profile_picture" example:"https://example.com/john.jpg"`
Role *string `json:"role" example:"Receptionist"`
Department *string `json:"department" example:"Housekeeping"`
Timezone *string `json:"timezone" example:"America/New_York"`
ClerkID string `json:"clerk_id" example:"user_123"`
FirstName string `json:"first_name" validate:"notblank" example:"John"`
LastName string `json:"last_name" validate:"notblank" example:"Doe"`
EmployeeID *string `json:"employee_id,omitempty" validate:"omitempty" example:"EMP-1234"`
ProfilePicture *string `json:"profile_picture,omitempty" validate:"omitempty,url" example:"https://example.com/john.jpg"`
Role *string `json:"role,omitempty" validate:"omitempty" example:"Receptionist"`
Department *string `json:"department,omitempty" validate:"omitempty" example:"Housekeeping"`
Timezone *string `json:"timezone,omitempty" validate:"omitempty,timezone" example:"America/New_York"`
ClerkID string `json:"clerk_id" validate:"notblank" example:"user_123"`
} //@name CreateUser

type CreateUserWebhook struct {
Expand Down