package domain import ( "fmt" "net/url" ) type Version struct { code string name string host string timezone string } var versionModelName = "Version" // UnmarshalVersionFromDatabase unmarshals Version from the database. // // It should be used only for unmarshalling from the database! // You can't use UnmarshalVersionFromDatabase as constructor - It may put domain into the invalid state! func UnmarshalVersionFromDatabase( code string, name string, host string, timezone string, ) (Version, error) { if code == "" { return Version{}, ValidationError{ Model: versionModelName, Field: "code", Err: ErrRequired, } } if name == "" { return Version{}, ValidationError{ Model: versionModelName, Field: "name", Err: ErrRequired, } } if host == "" { return Version{}, ValidationError{ Model: versionModelName, Field: "host", Err: ErrRequired, } } if timezone == "" { return Version{}, ValidationError{ Model: versionModelName, Field: "timezone", Err: ErrRequired, } } return Version{ code: code, name: name, host: host, timezone: timezone, }, nil } func (v Version) Code() string { return v.code } func (v Version) Name() string { return v.name } func (v Version) Host() string { return v.host } func (v Version) Timezone() string { return v.timezone } func (v Version) URL() *url.URL { return &url.URL{ Scheme: "https", Host: v.host, } } func (v Version) ToCursor() (VersionCursor, error) { return NewVersionCursor(v.code) } func (v Version) IsZero() bool { return v == Version{} } type Versions []Version type VersionSort uint8 const ( VersionSortCodeASC VersionSort = iota + 1 VersionSortCodeDESC ) // IsInConflict returns true if two sorts can't be used together (e.g. VersionSortCodeASC and VersionSortCodeDESC). func (s VersionSort) IsInConflict(s2 VersionSort) bool { return isSortInConflict(s, s2) } func (s VersionSort) String() string { switch s { case VersionSortCodeASC: return "code:ASC" case VersionSortCodeDESC: return "code:DESC" default: return "unknown version sort" } } type VersionCursor struct { code string } const versionCursorModelName = "VersionCursor" func NewVersionCursor(code string) (VersionCursor, error) { if err := validateVersionCode(code); err != nil { return VersionCursor{}, ValidationError{ Model: versionCursorModelName, Field: "code", Err: err, } } return VersionCursor{ code: code, }, nil } func decodeVersionCursor(encoded string) (VersionCursor, error) { m, err := decodeCursor(encoded) if err != nil { return VersionCursor{}, err } code, err := m.string("code") if err != nil { return VersionCursor{}, ErrInvalidCursor } vc, err := NewVersionCursor(code) if err != nil { return VersionCursor{}, ErrInvalidCursor } return vc, nil } func (vc VersionCursor) Code() string { return vc.code } func (vc VersionCursor) IsZero() bool { return vc == VersionCursor{} } func (vc VersionCursor) Encode() string { if vc.IsZero() { return "" } return encodeCursor([]keyValuePair{ {"code", vc.code}, }) } type ListVersionsParams struct { codes []string sort []VersionSort cursor VersionCursor limit int } const ( VersionListMaxLimit = 500 listVersionsParamsModelName = "ListVersionsParams" ) func NewListVersionsParams() ListVersionsParams { return ListVersionsParams{ sort: []VersionSort{VersionSortCodeASC}, limit: VersionListMaxLimit, } } func (params *ListVersionsParams) Codes() []string { return params.codes } func (params *ListVersionsParams) SetCodes(codes []string) error { for i, c := range codes { if err := validateVersionCode(c); err != nil { return SliceElementValidationError{ Model: listVersionsParamsModelName, Field: "codes", Index: i, Err: err, } } } params.codes = codes return nil } const ( versionSortMinLength = 1 versionSortMaxLength = 1 ) func (params *ListVersionsParams) Sort() []VersionSort { return params.sort } func (params *ListVersionsParams) SetSort(sort []VersionSort) error { if err := validateSort(sort, versionSortMinLength, versionSortMaxLength); err != nil { return ValidationError{ Model: listVersionsParamsModelName, Field: "sort", Err: err, } } params.sort = sort return nil } func (params *ListVersionsParams) Cursor() VersionCursor { return params.cursor } func (params *ListVersionsParams) SetCursor(vc VersionCursor) error { params.cursor = vc return nil } func (params *ListVersionsParams) SetEncodedCursor(encoded string) error { decoded, err := decodeVersionCursor(encoded) if err != nil { return ValidationError{ Model: listVersionsParamsModelName, Field: "cursor", Err: err, } } params.cursor = decoded return nil } func (params *ListVersionsParams) Limit() int { return params.limit } func (params *ListVersionsParams) SetLimit(limit int) error { if err := validateIntInRange(limit, 1, VersionListMaxLimit); err != nil { return ValidationError{ Model: listVersionsParamsModelName, Field: "limit", Err: err, } } params.limit = limit return nil } type ListVersionsResult struct { versions Versions self VersionCursor next VersionCursor } const listVersionsResultModelName = "ListVersionsResult" func NewListVersionsResult(versions Versions, next Version) (ListVersionsResult, error) { var err error res := ListVersionsResult{ versions: versions, } if len(versions) > 0 { res.self, err = versions[0].ToCursor() if err != nil { return ListVersionsResult{}, ValidationError{ Model: listVersionsResultModelName, Field: "self", Err: err, } } } if !next.IsZero() { res.next, err = next.ToCursor() if err != nil { return ListVersionsResult{}, ValidationError{ Model: listVersionsResultModelName, Field: "next", Err: err, } } } return res, nil } func (res ListVersionsResult) Versions() Versions { return res.versions } func (res ListVersionsResult) Self() VersionCursor { return res.self } func (res ListVersionsResult) Next() VersionCursor { return res.next } type VersionNotFoundError struct { VersionCode string } func (e VersionNotFoundError) Error() string { return fmt.Sprintf("version with code %s not found", e.VersionCode) } func (e VersionNotFoundError) Type() ErrorType { return ErrorTypeNotFound } func (e VersionNotFoundError) Code() string { return "version-not-found" } func (e VersionNotFoundError) Params() map[string]any { return map[string]any{ "Code": e.VersionCode, } }