159 lines
4.6 KiB
Go
159 lines
4.6 KiB
Go
// Package validator provides validation utilities for API parameters
|
|
package validator
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"time"
|
|
)
|
|
|
|
// Pagination constants
|
|
const (
|
|
DefaultPageSize = 20
|
|
MaxPageSize = 100
|
|
MinPageSize = 1
|
|
)
|
|
|
|
// Amount constants
|
|
const (
|
|
MinAmount = 0.01
|
|
MaxAmount = 999999999999.99
|
|
)
|
|
|
|
// Date constants
|
|
const (
|
|
MaxDateRangeDays = 366
|
|
MaxFutureDays = 365
|
|
)
|
|
|
|
// String length constants
|
|
const (
|
|
MaxNameLength = 100
|
|
MaxNoteLength = 500
|
|
)
|
|
|
|
// Validation errors
|
|
var (
|
|
ErrPaginationLimitExceeded = errors.New("pagination limit exceeded maximum allowed value")
|
|
ErrInvalidOffset = errors.New("pagination offset cannot be negative")
|
|
ErrAmountTooSmall = errors.New("amount is below minimum allowed value")
|
|
ErrAmountTooLarge = errors.New("amount exceeds maximum allowed value")
|
|
ErrDateRangeExceeded = errors.New("date range exceeds maximum allowed days")
|
|
ErrFutureDateExceeded = errors.New("date exceeds maximum allowed future date")
|
|
ErrEndDateBeforeStartDate = errors.New("end date must be after start date")
|
|
ErrNameTooLong = errors.New("name exceeds maximum allowed length")
|
|
ErrNoteTooLong = errors.New("note exceeds maximum allowed length")
|
|
)
|
|
|
|
// ValidatePagination validates and normalizes pagination parameters
|
|
// Returns normalized offset and limit values
|
|
// Feature: api-interface-optimization
|
|
// Validates: Requirements 7.1, 7.2
|
|
func ValidatePagination(offset, limit int) (int, int) {
|
|
// Normalize offset
|
|
if offset < 0 {
|
|
offset = 0
|
|
}
|
|
|
|
// Normalize limit
|
|
if limit <= 0 {
|
|
limit = DefaultPageSize
|
|
}
|
|
if limit > MaxPageSize {
|
|
limit = MaxPageSize
|
|
}
|
|
|
|
return offset, limit
|
|
}
|
|
|
|
// ValidatePaginationStrict validates pagination parameters and returns error if invalid
|
|
// Feature: api-interface-optimization
|
|
// Validates: Requirements 7.1, 7.2, 7.3
|
|
func ValidatePaginationStrict(offset, limit int) error {
|
|
if offset < 0 {
|
|
return fmt.Errorf("%w: offset=%d", ErrInvalidOffset, offset)
|
|
}
|
|
if limit > MaxPageSize {
|
|
return fmt.Errorf("%w: limit=%d, max=%d", ErrPaginationLimitExceeded, limit, MaxPageSize)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateAmount validates that an amount is within acceptable bounds
|
|
// Feature: api-interface-optimization
|
|
// Validates: Requirements 8.1, 8.2, 8.3
|
|
func ValidateAmount(amount float64) error {
|
|
if amount < MinAmount {
|
|
return fmt.Errorf("%w: amount=%.2f, min=%.2f", ErrAmountTooSmall, amount, MinAmount)
|
|
}
|
|
if amount > MaxAmount {
|
|
return fmt.Errorf("%w: amount=%.2f, max=%.2f", ErrAmountTooLarge, amount, MaxAmount)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RoundAmount rounds an amount to 2 decimal places
|
|
// Feature: api-interface-optimization
|
|
// Validates: Requirements 8.2
|
|
func RoundAmount(amount float64) float64 {
|
|
return math.Round(amount*100) / 100
|
|
}
|
|
|
|
// ValidateDateRange validates that a date range is within acceptable bounds
|
|
// Feature: api-interface-optimization
|
|
// Validates: Requirements 9.1
|
|
func ValidateDateRange(startDate, endDate time.Time) error {
|
|
if endDate.Before(startDate) {
|
|
return ErrEndDateBeforeStartDate
|
|
}
|
|
|
|
days := int(endDate.Sub(startDate).Hours() / 24)
|
|
if days > MaxDateRangeDays {
|
|
return fmt.Errorf("%w: days=%d, max=%d", ErrDateRangeExceeded, days, MaxDateRangeDays)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ValidateFutureDate validates that a date is not too far in the future
|
|
// Feature: api-interface-optimization
|
|
// Validates: Requirements 9.2
|
|
func ValidateFutureDate(date time.Time) error {
|
|
maxFutureDate := time.Now().AddDate(0, 0, MaxFutureDays)
|
|
if date.After(maxFutureDate) {
|
|
return fmt.Errorf("%w: date=%s, max=%s", ErrFutureDateExceeded, date.Format("2006-01-02"), maxFutureDate.Format("2006-01-02"))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateStringLength validates that a string does not exceed the maximum length
|
|
// Feature: api-interface-optimization
|
|
// Validates: Requirements 10.1, 10.2
|
|
func ValidateStringLength(s string, maxLen int, fieldName string) error {
|
|
if len(s) > maxLen {
|
|
if fieldName == "name" {
|
|
return fmt.Errorf("%w: length=%d, max=%d", ErrNameTooLong, len(s), maxLen)
|
|
}
|
|
if fieldName == "note" {
|
|
return fmt.Errorf("%w: length=%d, max=%d", ErrNoteTooLong, len(s), maxLen)
|
|
}
|
|
return fmt.Errorf("%s exceeds maximum length: length=%d, max=%d", fieldName, len(s), maxLen)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateName validates a name field
|
|
// Feature: api-interface-optimization
|
|
// Validates: Requirements 10.1
|
|
func ValidateName(name string) error {
|
|
return ValidateStringLength(name, MaxNameLength, "name")
|
|
}
|
|
|
|
// ValidateNote validates a note field
|
|
// Feature: api-interface-optimization
|
|
// Validates: Requirements 10.2
|
|
func ValidateNote(note string) error {
|
|
return ValidateStringLength(note, MaxNoteLength, "note")
|
|
}
|