/
zookie.go
93 lines (80 loc) · 2.52 KB
/
zookie.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
// Package zookie converts integers to zookies and vice versa
package zookie
import (
"encoding/base64"
"errors"
"fmt"
"github.com/shopspring/decimal"
"google.golang.org/protobuf/proto"
core "github.com/authzed/spicedb/pkg/proto/core/v1"
zookie "github.com/authzed/spicedb/pkg/proto/impl/v1"
)
// Public facing errors
const (
errEncodeError = "error encoding zookie: %w"
errDecodeError = "error decoding zookie: %w"
)
// ErrNilZookie is returned as the base error when nil is provided as the
// zookie argument to Decode
var ErrNilZookie = errors.New("zookie pointer was nil")
// NewFromRevision generates an encoded zookie from an integral revision.
func NewFromRevision(revision decimal.Decimal) *core.Zookie {
toEncode := &zookie.DecodedZookie{
Version: 2,
VersionOneof: &zookie.DecodedZookie_V2{
V2: &zookie.DecodedZookie_V2Zookie{
Revision: revision.String(),
},
},
}
encoded, err := Encode(toEncode)
if err != nil {
// All uint64s should be inherently encodeable
panic(fmt.Errorf(errEncodeError, err))
}
return encoded
}
// Encode converts a decoded zookie to its opaque version.
func Encode(decoded *zookie.DecodedZookie) (*core.Zookie, error) {
marshalled, err := proto.Marshal(decoded)
if err != nil {
return nil, fmt.Errorf(errEncodeError, err)
}
return &core.Zookie{
Token: base64.StdEncoding.EncodeToString(marshalled),
}, nil
}
// Decode converts an encoded zookie to its decoded version.
func Decode(encoded *core.Zookie) (*zookie.DecodedZookie, error) {
if encoded == nil {
return nil, fmt.Errorf(errDecodeError, ErrNilZookie)
}
decodedBytes, err := base64.StdEncoding.DecodeString(encoded.Token)
if err != nil {
return nil, fmt.Errorf(errDecodeError, err)
}
decoded := &zookie.DecodedZookie{}
if err := proto.Unmarshal(decodedBytes, decoded); err != nil {
return nil, fmt.Errorf(errDecodeError, err)
}
return decoded, nil
}
// DecodeRevision converts and extracts the revision from a zookie.
func DecodeRevision(encoded *core.Zookie) (decimal.Decimal, error) {
decoded, err := Decode(encoded)
if err != nil {
return decimal.Zero, err
}
switch ver := decoded.VersionOneof.(type) {
case *zookie.DecodedZookie_V1:
return decimal.NewFromInt(int64(ver.V1.Revision)), nil
case *zookie.DecodedZookie_V2:
parsed, err := decimal.NewFromString(ver.V2.Revision)
if err != nil {
return decimal.Zero, fmt.Errorf(errDecodeError, err)
}
return parsed, nil
default:
return decimal.Zero, fmt.Errorf(errDecodeError, fmt.Errorf("unknown zookie version: %T", decoded.VersionOneof))
}
}