This repository has been archived by the owner on Jun 5, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
/
object.go
121 lines (107 loc) · 3.81 KB
/
object.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package model
import (
"bytes"
"context"
"errors"
"fmt"
"google.golang.org/protobuf/proto"
)
type Type string
type Resource interface {
proto.Message
}
type Object interface {
ID() string
Type() Type
Resource() Resource
Validate(ctx context.Context) error
Indexes() []Index
ProcessDefaults(ctx context.Context) error
// SetResource replaces the object's underlining resource with the provided resource.
SetResource(Resource) error
}
// ObjectWithResourceDTO defines a model object that may not store the exact JSON-encoded representation of the
// underlining Protobuf resource in the persistence store. All callers will still work with the underlining Protobuf
// resource returned by Object.Resource(), however, when it comes time to store/fetch the resource from the persistence
// store, it will be translated to/from automatically.
//
// A practical use-case of this interface is to ensure commonality across JSON keys, in the event such key needs to be
// indexed. For example, there could be `$.name` key that is an indexed value in the datastore, however on one resource,
// instead of calling it `name`, it's called `description`. These (un)marhsal methods can then be used to re-write that
// key to `name` only within the datastore, in order to prevent the need of creating another index.
//
// Given the above example, another use-case is such key may be called the same, however, it may be within a nested
// object, e.g.: `$.metadata.name`. In that case, these (un)marhsal methods can then be used to rewrite that nested
// `name` field to be stored at the root of the JSON object, in order to take advantage of an already existing index.
type ObjectWithResourceDTO interface {
// MarshalResourceJSON is just like json.Marshaler, but specifically
// for marshalling the resource for use in the persistence store.
MarshalResourceJSON() ([]byte, error)
// UnmarshalResourceJSON is just like json.Unmarshaler, but specifically for unmarshalling
// a resource's JSON representation outputted by MarshalResourceJSON().
UnmarshalResourceJSON([]byte) error
}
type TypeIndex string
const (
IndexUnique TypeIndex = "unique"
IndexForeign TypeIndex = "foreign"
)
type Index struct {
// Name of the Index.
Name string
// FieldName is the name of the field this constraint applies on.
// This is used for annotating errors.
// Use JSON path notation (foo.bar.baz) for nested fields.
FieldName string
// Type is the type of the Index.
Type TypeIndex
// ForeignType denotes the type of the foreign object.
// This must be populated for IndexForeign and otherwise must be empty.
ForeignType Type
// Value is the value of the field for this Index.
Value string
}
type ObjectList interface {
Type() Type
Add(Object)
GetAll() []Object
// GetTotalCount returns the count of objects in the underlying store across all pages.
GetTotalCount() int
SetTotalCount(count int)
SetNextPage(pageNum int)
GetNextPage() int
}
func MultiValueIndex(values ...string) string {
switch len(values) {
case 0:
return ""
case 1:
return values[0]
case 2: //nolint:gomnd
return fmt.Sprintf("%s:%s", values[0], values[1])
default:
var buf bytes.Buffer
l := len(values)
for i, value := range values {
buf.WriteString(value)
if i+1 != l {
buf.WriteRune(':')
}
}
return buf.String()
}
}
// SetResource replaces the object's underlining resource with the provided resource.
func SetResource(o Object, r Resource) error {
expected, actual := o.Resource().ProtoReflect().Descriptor(), r.ProtoReflect().Descriptor()
if expected != actual {
return fmt.Errorf("unable to set resource: expected %q but got %q", expected.FullName(), actual.FullName())
}
dst := o.Resource()
if !dst.ProtoReflect().IsValid() {
return errors.New("unable to set resource: got invalid destination resource")
}
proto.Reset(dst)
proto.Merge(dst, r)
return nil
}