2023-12-24 10:44:20 +00:00
|
|
|
package domain
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ValidationError struct {
|
2023-12-27 08:07:40 +00:00
|
|
|
Model string
|
2023-12-24 10:44:20 +00:00
|
|
|
Field string
|
|
|
|
Err error
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ErrorWithParams = ValidationError{}
|
2024-03-01 06:46:42 +00:00
|
|
|
var _ ErrorWithPath = ValidationError{}
|
2023-12-24 10:44:20 +00:00
|
|
|
|
|
|
|
func (e ValidationError) Error() string {
|
2024-03-01 06:46:42 +00:00
|
|
|
prefix := e.Path().String()
|
2023-12-27 08:07:40 +00:00
|
|
|
|
|
|
|
if prefix != "" {
|
|
|
|
prefix += ": "
|
|
|
|
}
|
|
|
|
|
|
|
|
return prefix + e.Err.Error()
|
2023-12-24 10:44:20 +00:00
|
|
|
}
|
|
|
|
|
2024-03-01 06:46:42 +00:00
|
|
|
func (e ValidationError) Path() ErrorPathSegment {
|
|
|
|
return ErrorPathSegment{
|
|
|
|
Model: e.Model,
|
|
|
|
Field: e.Field,
|
|
|
|
Index: -1,
|
2024-01-31 07:14:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e ValidationError) Type() ErrorType {
|
2023-12-24 10:44:20 +00:00
|
|
|
var domainErr Error
|
|
|
|
if errors.As(e.Err, &domainErr) {
|
2024-02-01 06:48:03 +00:00
|
|
|
return domainErr.Type()
|
2023-12-24 10:44:20 +00:00
|
|
|
}
|
2024-02-01 06:48:03 +00:00
|
|
|
return ErrorTypeIncorrectInput
|
2023-12-24 10:44:20 +00:00
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e ValidationError) Code() string {
|
2023-12-24 10:44:20 +00:00
|
|
|
var domainErr Error
|
|
|
|
if errors.As(e.Err, &domainErr) {
|
2024-02-01 06:48:03 +00:00
|
|
|
return domainErr.Code()
|
2023-12-28 10:56:59 +00:00
|
|
|
}
|
|
|
|
return "validation-failed"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e ValidationError) Params() map[string]any {
|
|
|
|
var withParams ErrorWithParams
|
|
|
|
if ok := errors.As(e.Err, &withParams); !ok {
|
|
|
|
return nil
|
2023-12-24 10:44:20 +00:00
|
|
|
}
|
2023-12-28 10:56:59 +00:00
|
|
|
return withParams.Params()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e ValidationError) Unwrap() error {
|
|
|
|
return e.Err
|
|
|
|
}
|
|
|
|
|
|
|
|
type SliceElementValidationError struct {
|
|
|
|
Model string
|
|
|
|
Field string
|
|
|
|
Index int
|
|
|
|
Err error
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ErrorWithParams = SliceElementValidationError{}
|
2024-03-01 06:46:42 +00:00
|
|
|
var _ ErrorWithPath = SliceElementValidationError{}
|
2023-12-28 10:56:59 +00:00
|
|
|
|
|
|
|
func (e SliceElementValidationError) Error() string {
|
2024-03-01 06:46:42 +00:00
|
|
|
prefix := e.Path().String()
|
2023-12-27 08:07:40 +00:00
|
|
|
|
2023-12-28 10:56:59 +00:00
|
|
|
if prefix != "" {
|
|
|
|
prefix += ": "
|
2023-12-27 08:07:40 +00:00
|
|
|
}
|
|
|
|
|
2023-12-28 10:56:59 +00:00
|
|
|
return prefix + e.Err.Error()
|
2023-12-24 10:44:20 +00:00
|
|
|
}
|
|
|
|
|
2024-03-01 06:46:42 +00:00
|
|
|
func (e SliceElementValidationError) Path() ErrorPathSegment {
|
|
|
|
return ErrorPathSegment{
|
|
|
|
Model: e.Model,
|
|
|
|
Field: e.Field,
|
|
|
|
Index: e.Index,
|
2024-01-31 07:14:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e SliceElementValidationError) Type() ErrorType {
|
2023-12-28 10:56:59 +00:00
|
|
|
var domainErr Error
|
|
|
|
if errors.As(e.Err, &domainErr) {
|
2024-02-01 06:48:03 +00:00
|
|
|
return domainErr.Type()
|
2023-12-28 10:56:59 +00:00
|
|
|
}
|
2024-02-01 06:48:03 +00:00
|
|
|
return ErrorTypeIncorrectInput
|
2023-12-28 10:56:59 +00:00
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e SliceElementValidationError) Code() string {
|
2023-12-28 10:56:59 +00:00
|
|
|
var domainErr Error
|
|
|
|
if errors.As(e.Err, &domainErr) {
|
2024-02-01 06:48:03 +00:00
|
|
|
return domainErr.Code()
|
2023-12-28 10:56:59 +00:00
|
|
|
}
|
|
|
|
return "slice-element-validation-failed"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e SliceElementValidationError) Params() map[string]any {
|
2023-12-24 10:44:20 +00:00
|
|
|
var withParams ErrorWithParams
|
2023-12-27 08:07:40 +00:00
|
|
|
if ok := errors.As(e.Err, &withParams); !ok {
|
2023-12-24 10:44:20 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return withParams.Params()
|
|
|
|
}
|
|
|
|
|
2023-12-28 10:56:59 +00:00
|
|
|
func (e SliceElementValidationError) Unwrap() error {
|
2023-12-27 08:07:40 +00:00
|
|
|
return e.Err
|
|
|
|
}
|
|
|
|
|
2023-12-24 10:44:20 +00:00
|
|
|
type MinGreaterEqualError struct {
|
|
|
|
Min int
|
|
|
|
Current int
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ErrorWithParams = MinGreaterEqualError{}
|
|
|
|
|
|
|
|
func (e MinGreaterEqualError) Error() string {
|
|
|
|
return fmt.Sprintf("must be no less than %d (current: %d)", e.Min, e.Current)
|
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e MinGreaterEqualError) Type() ErrorType {
|
|
|
|
return ErrorTypeIncorrectInput
|
2023-12-24 10:44:20 +00:00
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e MinGreaterEqualError) Code() string {
|
2023-12-24 10:44:20 +00:00
|
|
|
return "min-greater-equal"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e MinGreaterEqualError) Params() map[string]any {
|
|
|
|
return map[string]any{
|
|
|
|
"Min": e.Min,
|
|
|
|
"Current": e.Current,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type MaxLessEqualError struct {
|
|
|
|
Max int
|
|
|
|
Current int
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ErrorWithParams = MaxLessEqualError{}
|
|
|
|
|
|
|
|
func (e MaxLessEqualError) Error() string {
|
|
|
|
return fmt.Sprintf("must be no greater than %d (current: %d)", e.Max, e.Current)
|
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e MaxLessEqualError) Type() ErrorType {
|
|
|
|
return ErrorTypeIncorrectInput
|
2023-12-24 10:44:20 +00:00
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e MaxLessEqualError) Code() string {
|
2023-12-24 10:44:20 +00:00
|
|
|
return "max-less-equal"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e MaxLessEqualError) Params() map[string]any {
|
|
|
|
return map[string]any{
|
|
|
|
"Max": e.Max,
|
|
|
|
"Current": e.Current,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type LenOutOfRangeError struct {
|
|
|
|
Min int
|
|
|
|
Max int
|
|
|
|
Current int
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ErrorWithParams = LenOutOfRangeError{}
|
|
|
|
|
|
|
|
func (e LenOutOfRangeError) Error() string {
|
|
|
|
return fmt.Sprintf("length must be between %d and %d (current length: %d)", e.Min, e.Max, e.Current)
|
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e LenOutOfRangeError) Type() ErrorType {
|
|
|
|
return ErrorTypeIncorrectInput
|
2023-12-24 10:44:20 +00:00
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e LenOutOfRangeError) Code() string {
|
2023-12-24 10:44:20 +00:00
|
|
|
return "length-out-of-range"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e LenOutOfRangeError) Params() map[string]any {
|
|
|
|
return map[string]any{
|
|
|
|
"Min": e.Min,
|
|
|
|
"Max": e.Max,
|
|
|
|
"Current": e.Current,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-01 10:20:30 +00:00
|
|
|
type InvalidURLError struct {
|
|
|
|
URL string
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ErrorWithParams = InvalidURLError{}
|
|
|
|
|
|
|
|
func (e InvalidURLError) Error() string {
|
|
|
|
return fmt.Sprintf("%s: invalid URL", e.URL)
|
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e InvalidURLError) Type() ErrorType {
|
|
|
|
return ErrorTypeIncorrectInput
|
2024-01-01 10:20:30 +00:00
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e InvalidURLError) Code() string {
|
2024-01-01 10:20:30 +00:00
|
|
|
return "invalid-url"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e InvalidURLError) Params() map[string]any {
|
|
|
|
return map[string]any{
|
|
|
|
"URL": e.URL,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-20 06:34:44 +00:00
|
|
|
type UnsupportedSortStringError struct {
|
|
|
|
Sort string
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ErrorWithParams = UnsupportedSortStringError{}
|
|
|
|
|
|
|
|
func (e UnsupportedSortStringError) Error() string {
|
|
|
|
return fmt.Sprintf("sort %s is unsupported", e.Sort)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e UnsupportedSortStringError) Type() ErrorType {
|
|
|
|
return ErrorTypeIncorrectInput
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e UnsupportedSortStringError) Code() string {
|
|
|
|
return "unsupported-sort-string"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e UnsupportedSortStringError) Params() map[string]any {
|
|
|
|
return map[string]any{
|
|
|
|
"Sort": e.Sort,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-02 08:49:42 +00:00
|
|
|
type SortConflictError struct {
|
|
|
|
Sort [2]string
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ ErrorWithParams = SortConflictError{}
|
|
|
|
|
|
|
|
func (e SortConflictError) Error() string {
|
|
|
|
return fmt.Sprintf("sort values %s are in conflict and can't be used together", e.Sort[0]+" and "+e.Sort[1])
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e SortConflictError) Type() ErrorType {
|
|
|
|
return ErrorTypeIncorrectInput
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e SortConflictError) Code() string {
|
|
|
|
return "sort-conflict"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e SortConflictError) Params() map[string]any {
|
|
|
|
return map[string]any{
|
|
|
|
"Sort": e.Sort,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-28 10:56:59 +00:00
|
|
|
var ErrRequired error = simpleError{
|
|
|
|
msg: "can't be blank",
|
2024-02-01 06:48:03 +00:00
|
|
|
typ: ErrorTypeIncorrectInput,
|
|
|
|
code: "required",
|
2023-12-28 10:56:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var ErrNil error = simpleError{
|
2023-12-27 08:07:40 +00:00
|
|
|
msg: "must not be nil",
|
2024-02-01 06:48:03 +00:00
|
|
|
typ: ErrorTypeIncorrectInput,
|
|
|
|
code: "nil",
|
2023-12-27 08:07:40 +00:00
|
|
|
}
|
|
|
|
|
2024-02-06 06:14:19 +00:00
|
|
|
// ErrInvalidCursor is an error that is returned when a cursor can't be decoded (e.g. is malformed).
|
2024-01-31 07:14:18 +00:00
|
|
|
var ErrInvalidCursor error = simpleError{
|
|
|
|
msg: "invalid cursor",
|
2024-02-01 06:48:03 +00:00
|
|
|
typ: ErrorTypeIncorrectInput,
|
|
|
|
code: "invalid-cursor",
|
2024-01-31 07:14:18 +00:00
|
|
|
}
|
|
|
|
|
2023-12-24 10:44:20 +00:00
|
|
|
func validateSliceLen[S ~[]E, E any](s S, min, max int) error {
|
|
|
|
if l := len(s); l > max || l < min {
|
|
|
|
return LenOutOfRangeError{
|
|
|
|
Min: min,
|
|
|
|
Max: max,
|
|
|
|
Current: l,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-12-28 10:56:59 +00:00
|
|
|
func validateStringLen(s string, min, max int) error {
|
|
|
|
if l := len(s); l > max || l < min {
|
|
|
|
return LenOutOfRangeError{
|
|
|
|
Min: min,
|
|
|
|
Max: max,
|
|
|
|
Current: l,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-03-01 06:33:48 +00:00
|
|
|
const (
|
|
|
|
versionCodeMinLength = 2
|
|
|
|
versionCodeMaxLength = 2
|
|
|
|
)
|
|
|
|
|
2023-12-24 10:44:20 +00:00
|
|
|
func validateVersionCode(code string) error {
|
2024-01-02 09:54:11 +00:00
|
|
|
return validateStringLen(code, versionCodeMinLength, versionCodeMaxLength)
|
|
|
|
}
|
2023-12-24 10:44:20 +00:00
|
|
|
|
2024-03-01 06:33:48 +00:00
|
|
|
const (
|
|
|
|
serverKeyMinLength = 1
|
|
|
|
serverKeyMaxLength = 10
|
|
|
|
)
|
|
|
|
|
2024-01-02 09:54:11 +00:00
|
|
|
func validateServerKey(key string) error {
|
|
|
|
return validateStringLen(key, serverKeyMinLength, serverKeyMaxLength)
|
2023-12-24 10:44:20 +00:00
|
|
|
}
|
2023-12-28 10:56:59 +00:00
|
|
|
|
|
|
|
func validateIntInRange(current, min, max int) error {
|
|
|
|
if current < min {
|
|
|
|
return MinGreaterEqualError{
|
|
|
|
Min: min,
|
|
|
|
Current: current,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if current > max {
|
|
|
|
return MaxLessEqualError{
|
|
|
|
Max: max,
|
|
|
|
Current: current,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2024-03-02 08:49:42 +00:00
|
|
|
|
|
|
|
func validateSort[S ~[]E, E interface {
|
|
|
|
IsInConflict(s E) bool
|
|
|
|
String() string
|
|
|
|
}](sort S, min, max int) error {
|
|
|
|
if err := validateSliceLen(sort, min, max); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, s1 := range sort {
|
|
|
|
for j := i + 1; j < len(sort); j++ {
|
|
|
|
s2 := sort[j]
|
|
|
|
if s1.IsInConflict(s2) {
|
|
|
|
return SortConflictError{
|
|
|
|
Sort: [2]string{s1.String(), s2.String()},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|