-
Notifications
You must be signed in to change notification settings - Fork 2
/
keys.go
136 lines (106 loc) · 3.04 KB
/
keys.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package db
import (
"bytes"
"context"
"github.com/oklog/ulid/v2"
)
// Keys Namespace maps object IDs to their fully qualified database keys.
const KeysNamespace = "object_keys"
// Key is composed of two concatenated IDs. The first 16 bytes are the ID of parent and
// the second 16 bytes are the ID of the object.
type Key [32]byte
var NullID = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
var _ Model = &Key{}
// CreateKey creates a new key from a parent ID and object ID so that callers can
// lookup the object ID from its namespace.
func CreateKey(parentID, objectID ulid.ULID) (key Key, err error) {
if err = parentID.MarshalBinaryTo(key[:16]); err != nil {
return Key{}, err
}
if err = objectID.MarshalBinaryTo(key[16:]); err != nil {
return Key{}, err
}
return key, nil
}
// Keys are stored by object ID. Since object IDs are locked monotonically increasing
// ulids they are guaranteed to be unique.
func (k Key) Key() ([]byte, error) {
if bytes.Equal(k[16:], NullID) {
return nil, ErrKeyNoID
}
return k[16:], nil
}
func (k Key) Namespace() string {
return KeysNamespace
}
func (k Key) MarshalValue() ([]byte, error) {
return k[:], nil
}
func (k *Key) UnmarshalValue(data []byte) error {
if len(data) != 32 {
return ErrKeyWrongSize
}
copy(k[:], data)
return nil
}
func (k Key) ParentID() (id ulid.ULID, err error) {
err = id.UnmarshalBinary(k[:16])
return id, err
}
func (k Key) ObjectID() (id ulid.ULID, err error) {
err = id.UnmarshalBinary(k[16:])
return id, err
}
// String returns a string representation of the key with the parent ID concatenated
// with the object ID.
func (k Key) String() (_ string, err error) {
if len(k) != 32 {
return "", ErrKeyWrongSize
}
var parentID, objectID ulid.ULID
if parentID, err = k.ParentID(); err != nil {
return "", err
}
if objectID, err = k.ObjectID(); err != nil {
return "", err
}
return parentID.String() + ":" + objectID.String(), nil
}
// ParseKey parses a string representation of a key into a Key struct.
func ParseKey(s string) (key Key, err error) {
if len(s) != 53 {
return Key{}, ErrKeyWrongSize
}
var parentID, objectID ulid.ULID
if parentID, err = ulid.Parse(s[:26]); err != nil {
return Key{}, err
}
if objectID, err = ulid.Parse(s[27:]); err != nil {
return Key{}, err
}
return CreateKey(parentID, objectID)
}
// Helper to retrieve an object's key from its ID from the database.
func GetObjectKey(ctx context.Context, objectID ulid.ULID) (key []byte, err error) {
return getRequest(ctx, KeysNamespace, objectID[:])
}
// Helper to store an object's key in the database.
func PutObjectKey(ctx context.Context, object Model) (err error) {
var keyData []byte
if keyData, err = object.Key(); err != nil {
return err
}
key := &Key{}
if err = key.UnmarshalValue(keyData); err != nil {
return err
}
return Put(ctx, key)
}
// Helper to delete an object's key from the database.
func DeleteObjectKey(ctx context.Context, key []byte) (err error) {
k := &Key{}
if err = k.UnmarshalValue(key); err != nil {
return err
}
return Delete(ctx, k)
}