github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/zedtoken/zedtoken.go (about) 1 // Package zedtoken converts decimal.Decimal to zedtoken and vice versa 2 package zedtoken 3 4 import ( 5 "encoding/base64" 6 "errors" 7 "fmt" 8 9 v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" 10 11 "github.com/authzed/spicedb/pkg/datastore" 12 zedtoken "github.com/authzed/spicedb/pkg/proto/impl/v1" 13 ) 14 15 // Public facing errors 16 const ( 17 errEncodeError = "error encoding zedtoken: %w" 18 errDecodeError = "error decoding zedtoken: %w" 19 ) 20 21 // ErrNilZedToken is returned as the base error when nil is provided as the 22 // zedtoken argument to Decode 23 var ErrNilZedToken = errors.New("zedtoken pointer was nil") 24 25 // MustNewFromRevision generates an encoded zedtoken from an integral revision. 26 func MustNewFromRevision(revision datastore.Revision) *v1.ZedToken { 27 encoded, err := NewFromRevision(revision) 28 if err != nil { 29 panic(err) 30 } 31 return encoded 32 } 33 34 // NewFromRevision generates an encoded zedtoken from an integral revision. 35 func NewFromRevision(revision datastore.Revision) (*v1.ZedToken, error) { 36 toEncode := &zedtoken.DecodedZedToken{ 37 VersionOneof: &zedtoken.DecodedZedToken_V1{ 38 V1: &zedtoken.DecodedZedToken_V1ZedToken{ 39 Revision: revision.String(), 40 }, 41 }, 42 } 43 encoded, err := Encode(toEncode) 44 if err != nil { 45 return nil, fmt.Errorf(errEncodeError, err) 46 } 47 48 return encoded, nil 49 } 50 51 // Encode converts a decoded zedtoken to its opaque version. 52 func Encode(decoded *zedtoken.DecodedZedToken) (*v1.ZedToken, error) { 53 marshalled, err := decoded.MarshalVT() 54 if err != nil { 55 return nil, fmt.Errorf(errEncodeError, err) 56 } 57 return &v1.ZedToken{ 58 Token: base64.StdEncoding.EncodeToString(marshalled), 59 }, nil 60 } 61 62 // Decode converts an encoded zedtoken to its decoded version. 63 func Decode(encoded *v1.ZedToken) (*zedtoken.DecodedZedToken, error) { 64 if encoded == nil { 65 return nil, fmt.Errorf(errDecodeError, ErrNilZedToken) 66 } 67 68 decodedBytes, err := base64.StdEncoding.DecodeString(encoded.Token) 69 if err != nil { 70 return nil, fmt.Errorf(errDecodeError, err) 71 } 72 decoded := &zedtoken.DecodedZedToken{} 73 if err := decoded.UnmarshalVT(decodedBytes); err != nil { 74 return nil, fmt.Errorf(errDecodeError, err) 75 } 76 return decoded, nil 77 } 78 79 // DecodeRevision converts and extracts the revision from a zedtoken or legacy zookie. 80 func DecodeRevision(encoded *v1.ZedToken, ds revisionDecoder) (datastore.Revision, error) { 81 decoded, err := Decode(encoded) 82 if err != nil { 83 return datastore.NoRevision, err 84 } 85 86 switch ver := decoded.VersionOneof.(type) { 87 case *zedtoken.DecodedZedToken_DeprecatedV1Zookie: 88 revString := fmt.Sprintf("%d", ver.DeprecatedV1Zookie.Revision) 89 parsed, err := ds.RevisionFromString(revString) 90 if err != nil { 91 return datastore.NoRevision, fmt.Errorf(errDecodeError, err) 92 } 93 return parsed, nil 94 95 case *zedtoken.DecodedZedToken_V1: 96 parsed, err := ds.RevisionFromString(ver.V1.Revision) 97 if err != nil { 98 return datastore.NoRevision, fmt.Errorf(errDecodeError, err) 99 } 100 return parsed, nil 101 default: 102 return datastore.NoRevision, fmt.Errorf(errDecodeError, fmt.Errorf("unknown zookie version: %T", decoded.VersionOneof)) 103 } 104 } 105 106 type revisionDecoder interface { 107 RevisionFromString(string) (datastore.Revision, error) 108 }