2023-12-20 07:03:03 +00:00
|
|
|
package domain
|
|
|
|
|
|
|
|
import (
|
2024-02-01 06:39:38 +00:00
|
|
|
"fmt"
|
2023-12-20 07:03:03 +00:00
|
|
|
"net/url"
|
2024-03-02 08:49:42 +00:00
|
|
|
"slices"
|
2023-12-20 07:03:03 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Version struct {
|
|
|
|
code string
|
|
|
|
name string
|
|
|
|
host string
|
|
|
|
timezone string
|
|
|
|
}
|
|
|
|
|
2023-12-28 10:56:59 +00:00
|
|
|
var versionModelName = "Version"
|
|
|
|
|
2023-12-20 07:03:03 +00:00
|
|
|
// 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 == "" {
|
2023-12-28 10:56:59 +00:00
|
|
|
return Version{}, ValidationError{
|
|
|
|
Model: versionModelName,
|
|
|
|
Field: "code",
|
|
|
|
Err: ErrRequired,
|
|
|
|
}
|
2023-12-20 07:03:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if name == "" {
|
2023-12-28 10:56:59 +00:00
|
|
|
return Version{}, ValidationError{
|
|
|
|
Model: versionModelName,
|
|
|
|
Field: "name",
|
|
|
|
Err: ErrRequired,
|
|
|
|
}
|
2023-12-20 07:03:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if host == "" {
|
2023-12-28 10:56:59 +00:00
|
|
|
return Version{}, ValidationError{
|
|
|
|
Model: versionModelName,
|
|
|
|
Field: "host",
|
|
|
|
Err: ErrRequired,
|
|
|
|
}
|
2023-12-20 07:03:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if timezone == "" {
|
2023-12-28 10:56:59 +00:00
|
|
|
return Version{}, ValidationError{
|
|
|
|
Model: versionModelName,
|
|
|
|
Field: "timezone",
|
|
|
|
Err: ErrRequired,
|
|
|
|
}
|
2023-12-20 07:03:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-06 06:35:26 +00:00
|
|
|
func (v Version) ToCursor() (VersionCursor, error) {
|
|
|
|
return NewVersionCursor(v.code)
|
|
|
|
}
|
|
|
|
|
2024-01-27 08:37:12 +00:00
|
|
|
func (v Version) IsZero() bool {
|
|
|
|
return v == Version{}
|
|
|
|
}
|
|
|
|
|
2023-12-20 07:03:03 +00:00
|
|
|
type Versions []Version
|
|
|
|
|
|
|
|
type VersionSort uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
VersionSortCodeASC VersionSort = iota + 1
|
|
|
|
VersionSortCodeDESC
|
|
|
|
)
|
|
|
|
|
2024-03-02 08:49:42 +00:00
|
|
|
// IsInConflict returns true if two sorts can't be used together (e.g. VersionSortCodeASC and VersionSortCodeDESC).
|
|
|
|
func (s VersionSort) IsInConflict(s2 VersionSort) bool {
|
|
|
|
ss := []VersionSort{s, s2}
|
|
|
|
slices.Sort(ss)
|
|
|
|
// ASC is always an odd number, DESC is always an even number
|
|
|
|
return (ss[0]%2 == 1 && ss[0] == ss[1]-1) || ss[0] == ss[1]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s VersionSort) String() string {
|
|
|
|
switch s {
|
|
|
|
case VersionSortCodeASC:
|
|
|
|
return "code:ASC"
|
|
|
|
case VersionSortCodeDESC:
|
|
|
|
return "code:DESC"
|
|
|
|
default:
|
|
|
|
return "unknown version sort"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-27 08:37:12 +00:00
|
|
|
type VersionCursor struct {
|
2024-02-06 06:49:01 +00:00
|
|
|
code string
|
2024-01-27 08:37:12 +00:00
|
|
|
}
|
|
|
|
|
2024-01-31 07:14:18 +00:00
|
|
|
const versionCursorModelName = "VersionCursor"
|
|
|
|
|
2024-02-06 06:49:01 +00:00
|
|
|
func NewVersionCursor(code string) (VersionCursor, error) {
|
|
|
|
if err := validateVersionCode(code); err != nil {
|
2024-01-31 07:14:18 +00:00
|
|
|
return VersionCursor{}, ValidationError{
|
|
|
|
Model: versionCursorModelName,
|
|
|
|
Field: "code",
|
|
|
|
Err: err,
|
|
|
|
}
|
2024-01-27 08:37:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return VersionCursor{
|
|
|
|
code: code,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeVersionCursor(encoded string) (VersionCursor, error) {
|
2024-02-06 07:11:05 +00:00
|
|
|
m, err := decodeCursor(encoded)
|
2024-01-27 08:37:12 +00:00
|
|
|
if err != nil {
|
2024-02-06 07:11:05 +00:00
|
|
|
return VersionCursor{}, err
|
2024-02-06 06:14:19 +00:00
|
|
|
}
|
2024-01-27 08:37:12 +00:00
|
|
|
|
2024-02-19 07:17:38 +00:00
|
|
|
code, err := m.string("code")
|
|
|
|
if err != nil {
|
|
|
|
return VersionCursor{}, ErrInvalidCursor
|
|
|
|
}
|
|
|
|
|
|
|
|
vc, err := NewVersionCursor(code)
|
2024-01-31 07:14:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return VersionCursor{}, ErrInvalidCursor
|
|
|
|
}
|
|
|
|
|
|
|
|
return vc, nil
|
2024-01-27 08:37:12 +00:00
|
|
|
}
|
|
|
|
|
2024-02-06 06:49:01 +00:00
|
|
|
func (vc VersionCursor) Code() string {
|
2024-01-27 08:37:12 +00:00
|
|
|
return vc.code
|
|
|
|
}
|
|
|
|
|
|
|
|
func (vc VersionCursor) IsZero() bool {
|
2024-02-06 06:49:01 +00:00
|
|
|
return vc == VersionCursor{}
|
2024-01-27 08:37:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (vc VersionCursor) Encode() string {
|
|
|
|
if vc.IsZero() {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2024-02-19 07:17:38 +00:00
|
|
|
return encodeCursor([]keyValuePair{
|
2024-02-07 07:17:47 +00:00
|
|
|
{"code", vc.code},
|
2024-02-06 07:11:05 +00:00
|
|
|
})
|
2024-01-27 08:37:12 +00:00
|
|
|
}
|
|
|
|
|
2023-12-20 07:03:03 +00:00
|
|
|
type ListVersionsParams struct {
|
2024-02-01 06:39:38 +00:00
|
|
|
codes []string
|
2024-01-27 08:37:12 +00:00
|
|
|
sort []VersionSort
|
|
|
|
cursor VersionCursor
|
|
|
|
limit int
|
2023-12-20 07:03:03 +00:00
|
|
|
}
|
|
|
|
|
2024-01-27 08:37:12 +00:00
|
|
|
const (
|
|
|
|
VersionListMaxLimit = 200
|
|
|
|
listVersionsParamsModelName = "ListVersionsParams"
|
|
|
|
)
|
2023-12-27 08:07:40 +00:00
|
|
|
|
2023-12-20 07:03:03 +00:00
|
|
|
func NewListVersionsParams() ListVersionsParams {
|
2024-01-27 08:37:12 +00:00
|
|
|
return ListVersionsParams{
|
|
|
|
sort: []VersionSort{VersionSortCodeASC},
|
|
|
|
limit: VersionListMaxLimit,
|
|
|
|
}
|
2023-12-20 07:03:03 +00:00
|
|
|
}
|
|
|
|
|
2024-02-01 06:39:38 +00:00
|
|
|
func (params *ListVersionsParams) Codes() []string {
|
|
|
|
return params.codes
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params *ListVersionsParams) SetCodes(codes []string) error {
|
2024-03-01 06:33:48 +00:00
|
|
|
for i, c := range codes {
|
|
|
|
if err := validateVersionCode(c); err != nil {
|
|
|
|
return SliceElementValidationError{
|
|
|
|
Model: listVersionsParamsModelName,
|
|
|
|
Field: "codes",
|
|
|
|
Index: i,
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-01 06:39:38 +00:00
|
|
|
params.codes = codes
|
2024-03-01 06:33:48 +00:00
|
|
|
|
2024-02-01 06:39:38 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-12-20 07:03:03 +00:00
|
|
|
const (
|
|
|
|
versionSortMinLength = 1
|
|
|
|
versionSortMaxLength = 1
|
|
|
|
)
|
|
|
|
|
2023-12-24 10:44:20 +00:00
|
|
|
func (params *ListVersionsParams) Sort() []VersionSort {
|
|
|
|
return params.sort
|
|
|
|
}
|
|
|
|
|
2023-12-20 07:03:03 +00:00
|
|
|
func (params *ListVersionsParams) SetSort(sort []VersionSort) error {
|
2024-03-02 08:49:42 +00:00
|
|
|
if err := validateSort(sort, versionSortMinLength, versionSortMaxLength); err != nil {
|
2023-12-20 07:03:03 +00:00
|
|
|
return ValidationError{
|
2023-12-27 08:07:40 +00:00
|
|
|
Model: listVersionsParamsModelName,
|
2023-12-20 07:03:03 +00:00
|
|
|
Field: "sort",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
params.sort = sort
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2024-01-27 08:37:12 +00:00
|
|
|
|
|
|
|
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 {
|
2024-03-06 06:35:26 +00:00
|
|
|
res.self, err = versions[0].ToCursor()
|
2024-01-27 08:37:12 +00:00
|
|
|
if err != nil {
|
|
|
|
return ListVersionsResult{}, ValidationError{
|
|
|
|
Model: listVersionsResultModelName,
|
|
|
|
Field: "self",
|
|
|
|
Err: err,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-06 06:49:01 +00:00
|
|
|
if !next.IsZero() {
|
2024-03-06 06:35:26 +00:00
|
|
|
res.next, err = next.ToCursor()
|
2024-02-06 06:49:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return ListVersionsResult{}, ValidationError{
|
|
|
|
Model: listVersionsResultModelName,
|
|
|
|
Field: "next",
|
|
|
|
Err: err,
|
|
|
|
}
|
2024-01-27 08:37:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2024-02-01 06:39:38 +00:00
|
|
|
|
|
|
|
type VersionNotFoundError struct {
|
|
|
|
VersionCode string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e VersionNotFoundError) Error() string {
|
|
|
|
return fmt.Sprintf("version with code %s not found", e.VersionCode)
|
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e VersionNotFoundError) Type() ErrorType {
|
|
|
|
return ErrorTypeNotFound
|
2024-02-01 06:39:38 +00:00
|
|
|
}
|
|
|
|
|
2024-02-01 06:48:03 +00:00
|
|
|
func (e VersionNotFoundError) Code() string {
|
2024-02-01 06:39:38 +00:00
|
|
|
return "version-not-found"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e VersionNotFoundError) Params() map[string]any {
|
|
|
|
return map[string]any{
|
|
|
|
"Code": e.VersionCode,
|
|
|
|
}
|
|
|
|
}
|