summaryrefslogtreecommitdiff
path: root/vendor/github.com/authzed/spicedb/pkg/cursor
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-22 17:35:49 -0600
committermo khan <mo@mokhan.ca>2025-07-22 17:35:49 -0600
commit20ef0d92694465ac86b550df139e8366a0a2b4fa (patch)
tree3f14589e1ce6eb9306a3af31c3a1f9e1af5ed637 /vendor/github.com/authzed/spicedb/pkg/cursor
parent44e0d272c040cdc53a98b9f1dc58ae7da67752e6 (diff)
feat: connect to spicedb
Diffstat (limited to 'vendor/github.com/authzed/spicedb/pkg/cursor')
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/cursor/cursor.go133
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/cursor/doc.go2
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/cursor/errors.go46
3 files changed, 181 insertions, 0 deletions
diff --git a/vendor/github.com/authzed/spicedb/pkg/cursor/cursor.go b/vendor/github.com/authzed/spicedb/pkg/cursor/cursor.go
new file mode 100644
index 0000000..4233efa
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/cursor/cursor.go
@@ -0,0 +1,133 @@
+package cursor
+
+import (
+ "encoding/base64"
+ "errors"
+ "fmt"
+
+ v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
+
+ "github.com/authzed/spicedb/pkg/datastore"
+ dispatch "github.com/authzed/spicedb/pkg/proto/dispatch/v1"
+ impl "github.com/authzed/spicedb/pkg/proto/impl/v1"
+ "github.com/authzed/spicedb/pkg/spiceerrors"
+)
+
+// Encode converts a decoded cursor to its opaque version.
+func Encode(decoded *impl.DecodedCursor) (*v1.Cursor, error) {
+ marshalled, err := decoded.MarshalVT()
+ if err != nil {
+ return nil, NewInvalidCursorErr(fmt.Errorf(errEncodeError, err))
+ }
+
+ return &v1.Cursor{
+ Token: base64.StdEncoding.EncodeToString(marshalled),
+ }, nil
+}
+
+// Decode converts an encoded cursor to its decoded version.
+func Decode(encoded *v1.Cursor) (*impl.DecodedCursor, error) {
+ if encoded == nil {
+ return nil, NewInvalidCursorErr(errors.New("cursor pointer was nil"))
+ }
+
+ decodedBytes, err := base64.StdEncoding.DecodeString(encoded.Token)
+ if err != nil {
+ return nil, NewInvalidCursorErr(fmt.Errorf(errDecodeError, err))
+ }
+
+ decoded := &impl.DecodedCursor{}
+ if err := decoded.UnmarshalVT(decodedBytes); err != nil {
+ return nil, NewInvalidCursorErr(fmt.Errorf(errDecodeError, err))
+ }
+
+ return decoded, nil
+}
+
+// EncodeFromDispatchCursor encodes an internal dispatching cursor into a V1 cursor for external
+// consumption, including the provided call context to ensure the API cursor reflects the calling
+// API method. The call hash should contain all the parameters of the calling API function,
+// as well as its revision and name.
+func EncodeFromDispatchCursor(dispatchCursor *dispatch.Cursor, callAndParameterHash string, revision datastore.Revision, flags map[string]string) (*v1.Cursor, error) {
+ if dispatchCursor == nil {
+ return nil, spiceerrors.MustBugf("got nil dispatch cursor")
+ }
+
+ return Encode(&impl.DecodedCursor{
+ VersionOneof: &impl.DecodedCursor_V1{
+ V1: &impl.V1Cursor{
+ Revision: revision.String(),
+ DispatchVersion: dispatchCursor.DispatchVersion,
+ Sections: dispatchCursor.Sections,
+ CallAndParametersHash: callAndParameterHash,
+ Flags: flags,
+ },
+ },
+ })
+}
+
+// GetCursorFlag retrieves a flag from an encoded API cursor, if any.
+func GetCursorFlag(encoded *v1.Cursor, flagName string) (string, bool, error) {
+ decoded, err := Decode(encoded)
+ if err != nil {
+ return "", false, err
+ }
+
+ v1decoded := decoded.GetV1()
+ if v1decoded == nil {
+ return "", false, NewInvalidCursorErr(ErrNilCursor)
+ }
+
+ value, ok := v1decoded.Flags[flagName]
+ return value, ok, nil
+}
+
+// DecodeToDispatchCursor decodes an encoded API cursor into an internal dispatching cursor,
+// ensuring that the provided call context matches that encoded into the API cursor. The call
+// hash should contain all the parameters of the calling API function, as well as its revision
+// and name.
+func DecodeToDispatchCursor(encoded *v1.Cursor, callAndParameterHash string) (*dispatch.Cursor, map[string]string, error) {
+ decoded, err := Decode(encoded)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ v1decoded := decoded.GetV1()
+ if v1decoded == nil {
+ return nil, nil, NewInvalidCursorErr(ErrNilCursor)
+ }
+
+ if v1decoded.CallAndParametersHash != callAndParameterHash {
+ return nil, nil, NewInvalidCursorErr(ErrHashMismatch)
+ }
+
+ return &dispatch.Cursor{
+ DispatchVersion: v1decoded.DispatchVersion,
+ Sections: v1decoded.Sections,
+ }, v1decoded.Flags, nil
+}
+
+// DecodeToDispatchRevision decodes an encoded API cursor into an internal dispatch revision.
+// NOTE: this method does *not* verify the caller's method signature.
+func DecodeToDispatchRevision(encoded *v1.Cursor, ds revisionDecoder) (datastore.Revision, error) {
+ decoded, err := Decode(encoded)
+ if err != nil {
+ return nil, err
+ }
+
+ v1decoded := decoded.GetV1()
+ if v1decoded == nil {
+ return nil, ErrNilCursor
+ }
+
+ parsed, err := ds.RevisionFromString(v1decoded.Revision)
+ if err != nil {
+ return datastore.NoRevision, fmt.Errorf(errDecodeError, err)
+ }
+
+ return parsed, nil
+}
+
+type revisionDecoder interface {
+ RevisionFromString(string) (datastore.Revision, error)
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/cursor/doc.go b/vendor/github.com/authzed/spicedb/pkg/cursor/doc.go
new file mode 100644
index 0000000..bddf5cd
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/cursor/doc.go
@@ -0,0 +1,2 @@
+// Package cursor implements encoding and decoding of cursors used in various APIs.
+package cursor
diff --git a/vendor/github.com/authzed/spicedb/pkg/cursor/errors.go b/vendor/github.com/authzed/spicedb/pkg/cursor/errors.go
new file mode 100644
index 0000000..941df70
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/cursor/errors.go
@@ -0,0 +1,46 @@
+package cursor
+
+import (
+ "errors"
+
+ v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+
+ "github.com/authzed/spicedb/pkg/spiceerrors"
+)
+
+// Public facing errors
+const (
+ errEncodeError = "error encoding cursor: %w"
+ errDecodeError = "error decoding cursor: %w"
+)
+
+// ErrNilCursor is returned as the base error when nil is provided as the
+// cursor argument to Decode
+var ErrNilCursor = errors.New("cursor pointer was nil")
+
+// ErrHashMismatch is returned as the base error when a mismatching hash was given to the decoder.
+var ErrHashMismatch = errors.New("the cursor provided does not have the same arguments as the original API call; please ensure you are making the same API call, with the exact same parameters (besides the cursor)")
+
+// InvalidCursorError occurs when a cursor could not be decoded.
+type InvalidCursorError struct {
+ error
+}
+
+// GRPCStatus implements retrieving the gRPC status for the error.
+func (err InvalidCursorError) GRPCStatus() *status.Status {
+ return spiceerrors.WithCodeAndDetails(
+ err,
+ codes.InvalidArgument,
+ spiceerrors.ForReason(
+ v1.ErrorReason_ERROR_REASON_INVALID_CURSOR,
+ nil,
+ ),
+ )
+}
+
+// NewInvalidCursorErr creates and returns a new invalid cursor error.
+func NewInvalidCursorErr(err error) error {
+ return InvalidCursorError{err}
+}