summaryrefslogtreecommitdiff
path: root/vendor/github.com/authzed/spicedb/pkg/spiceerrors
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/authzed/spicedb/pkg/spiceerrors')
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/spiceerrors/assert_off.go21
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/spiceerrors/assert_on.go31
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/spiceerrors/bug.go35
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/spiceerrors/common.go88
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/spiceerrors/details_metadata.go112
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/spiceerrors/termination.go66
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/spiceerrors/testutil.go28
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/spiceerrors/util.go18
-rw-r--r--vendor/github.com/authzed/spicedb/pkg/spiceerrors/withstatus.go82
9 files changed, 481 insertions, 0 deletions
diff --git a/vendor/github.com/authzed/spicedb/pkg/spiceerrors/assert_off.go b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/assert_off.go
new file mode 100644
index 0000000..20ac7ef
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/assert_off.go
@@ -0,0 +1,21 @@
+//go:build !ci
+// +build !ci
+
+package spiceerrors
+
+const DebugAssertionsEnabled = false
+
+// DebugAssert is a no-op in non-CI builds
+func DebugAssert(condition func() bool, format string, args ...any) {
+ // Do nothing on purpose
+}
+
+// DebugAssertNotNil is a no-op in non-CI builds
+func DebugAssertNotNil(obj any, format string, args ...any) {
+ // Do nothing on purpose
+}
+
+// SetFinalizerForDebugging is a no-op in non-CI builds
+func SetFinalizerForDebugging[T any](obj interface{}, finalizer func(obj T)) {
+ // Do nothing on purpose
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/spiceerrors/assert_on.go b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/assert_on.go
new file mode 100644
index 0000000..a214147
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/assert_on.go
@@ -0,0 +1,31 @@
+//go:build ci
+// +build ci
+
+package spiceerrors
+
+import (
+ "fmt"
+ "runtime"
+)
+
+const DebugAssertionsEnabled = true
+
+// DebugAssert panics if the condition is false in CI builds.
+func DebugAssert(condition func() bool, format string, args ...any) {
+ if !condition() {
+ panic(fmt.Sprintf(format, args...))
+ }
+}
+
+// DebugAssertNotNil panics if the object is nil in CI builds.
+func DebugAssertNotNil(obj any, format string, args ...any) {
+ if obj == nil {
+ panic(fmt.Sprintf(format, args...))
+ }
+}
+
+// SetFinalizerForDebugging sets a finalizer on the object for debugging purposes
+// in CI builds.
+func SetFinalizerForDebugging[T any](obj interface{}, finalizer func(obj T)) {
+ runtime.SetFinalizer(obj, finalizer)
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/spiceerrors/bug.go b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/bug.go
new file mode 100644
index 0000000..7680c33
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/bug.go
@@ -0,0 +1,35 @@
+package spiceerrors
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/go-errors/errors"
+)
+
+// IsInTests returns true if go test is running
+// Based on: https://stackoverflow.com/a/58945030
+func IsInTests() bool {
+ for _, arg := range os.Args {
+ if strings.HasPrefix(arg, "-test.") {
+ return true
+ }
+ }
+ return false
+}
+
+// MustPanic is a special function for panicing when necessary to violate the linter.
+func MustPanic(format string, args ...any) {
+ panic(fmt.Sprintf(format, args...))
+}
+
+// MustBugf returns an error representing a bug in the system. Will panic if run under testing.
+func MustBugf(format string, args ...any) error {
+ if IsInTests() {
+ panic(fmt.Sprintf(format, args...))
+ }
+
+ e := errors.Errorf(format, args...)
+ return fmt.Errorf("BUG: %s", e.ErrorStack())
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/spiceerrors/common.go b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/common.go
new file mode 100644
index 0000000..625faef
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/common.go
@@ -0,0 +1,88 @@
+package spiceerrors
+
+import (
+ "errors"
+ "maps"
+)
+
+// SourcePosition is a position in the input source.
+type SourcePosition struct {
+ // LineNumber is the 1-indexed line number in the input source.
+ LineNumber int
+
+ // ColumnPosition is the 1-indexed column position in the input source.
+ ColumnPosition int
+}
+
+// WithSourceError is an error that includes the source text and position
+// information.
+type WithSourceError struct {
+ error
+
+ // SourceCodeString is the input source code string for the error.
+ SourceCodeString string
+
+ // LineNumber is the (1-indexed) line number of the error, or 0 if unknown.
+ LineNumber uint64
+
+ // ColumnPosition is the (1-indexed) column position of the error, or 0 if
+ // unknown.
+ ColumnPosition uint64
+}
+
+// HasMetadata indicates that the error has metadata defined.
+type HasMetadata interface {
+ // DetailsMetadata returns the metadata for details for this error.
+ DetailsMetadata() map[string]string
+}
+
+// Unwrap returns the inner, wrapped error.
+func (err *WithSourceError) Unwrap() error {
+ return err.error
+}
+
+// NewWithSourceError creates and returns a new WithSourceError.
+func NewWithSourceError(err error, sourceCodeString string, oneIndexedLineNumber uint64, oneIndexedColumnPosition uint64) *WithSourceError {
+ return &WithSourceError{err, sourceCodeString, oneIndexedLineNumber, oneIndexedColumnPosition}
+}
+
+// AsWithSourceError returns the error as an WithSourceError, if applicable.
+func AsWithSourceError(err error) (*WithSourceError, bool) {
+ var serr *WithSourceError
+ if errors.As(err, &serr) {
+ return serr, true
+ }
+ return nil, false
+}
+
+// WithAdditionalDetailsError is an error that includes additional details.
+type WithAdditionalDetailsError struct {
+ error
+
+ // AdditionalDetails is a map of additional details for the error.
+ AdditionalDetails map[string]string
+}
+
+func NewWithAdditionalDetailsError(err error) *WithAdditionalDetailsError {
+ return &WithAdditionalDetailsError{err, nil}
+}
+
+// Unwrap returns the inner, wrapped error.
+func (err *WithAdditionalDetailsError) Unwrap() error {
+ return err.error
+}
+
+func (err *WithAdditionalDetailsError) WithAdditionalDetails(key string, value string) {
+ if err.AdditionalDetails == nil {
+ err.AdditionalDetails = make(map[string]string)
+ }
+ err.AdditionalDetails[key] = value
+}
+
+func (err *WithAdditionalDetailsError) AddToDetails(details map[string]string) map[string]string {
+ if err.AdditionalDetails != nil {
+ maps.Copy(details, err.AdditionalDetails)
+ }
+
+ return details
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/spiceerrors/details_metadata.go b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/details_metadata.go
new file mode 100644
index 0000000..20d7795
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/details_metadata.go
@@ -0,0 +1,112 @@
+package spiceerrors
+
+import (
+ "fmt"
+
+ "google.golang.org/genproto/googleapis/rpc/errdetails"
+ "google.golang.org/grpc/status"
+ "google.golang.org/protobuf/runtime/protoiface"
+)
+
+// MetadataKey is the type used to represent the keys of the metadata map in
+// the error details.
+type MetadataKey string
+
+// DebugTraceErrorDetailsKey is the key used to store the debug trace in the error details.
+// The value is expected to be a string containing the proto text of a DebugInformation message.
+const DebugTraceErrorDetailsKey MetadataKey = "debug_trace_proto_text"
+
+// AppendDetailsMetadata appends the key-value pair to the error details metadata.
+// If the error is nil or is not a status error, it is returned as is.
+func AppendDetailsMetadata(err error, key MetadataKey, value string) error {
+ if err == nil {
+ return nil
+ }
+
+ s, ok := status.FromError(err)
+ if !ok {
+ return err
+ }
+
+ var foundErrDetails *errdetails.ErrorInfo
+ var otherDetails []protoiface.MessageV1
+ for _, details := range s.Details() {
+ if errInfo, ok := details.(*errdetails.ErrorInfo); ok {
+ foundErrDetails = errInfo
+ } else if cast, ok := details.(protoiface.MessageV1); ok {
+ otherDetails = append(otherDetails, cast)
+ }
+ }
+
+ if foundErrDetails != nil {
+ if foundErrDetails.Metadata == nil {
+ foundErrDetails.Metadata = make(map[string]string, 1)
+ }
+
+ foundErrDetails.Metadata[string(key)] = value
+ }
+
+ return WithCodeAndDetailsAsError(
+ fmt.Errorf("%s", s.Message()),
+ s.Code(),
+ append(otherDetails, foundErrDetails)...,
+ )
+}
+
+// WithReplacedDetails replaces the details of the error with the provided details.
+// If the error is nil or is not a status error, it is returned as is.
+// If the error does not have the details to replace, the provided details are appended.
+func WithReplacedDetails[T protoiface.MessageV1](err error, toReplace T) error {
+ if err == nil {
+ return nil
+ }
+
+ s, ok := status.FromError(err)
+ if !ok {
+ return err
+ }
+
+ details := make([]protoiface.MessageV1, 0, len(s.Details()))
+ wasReplaced := false
+ for _, current := range s.Details() {
+ if _, ok := current.(T); ok {
+ details = append(details, toReplace)
+ wasReplaced = true
+ continue
+ }
+
+ if cast, ok := current.(protoiface.MessageV1); ok {
+ details = append(details, cast)
+ }
+ }
+
+ if !wasReplaced {
+ details = append(details, toReplace)
+ }
+
+ return WithCodeAndDetailsAsError(
+ fmt.Errorf("%s", s.Message()),
+ s.Code(),
+ details...,
+ )
+}
+
+// GetDetails returns the details of the error if they are of the provided type, if any.
+func GetDetails[T protoiface.MessageV1](err error) (T, bool) {
+ if err == nil {
+ return *new(T), false
+ }
+
+ s, ok := status.FromError(err)
+ if !ok {
+ return *new(T), false
+ }
+
+ for _, details := range s.Details() {
+ if cast, ok := details.(T); ok {
+ return cast, true
+ }
+ }
+
+ return *new(T), false
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/spiceerrors/termination.go b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/termination.go
new file mode 100644
index 0000000..58360eb
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/termination.go
@@ -0,0 +1,66 @@
+package spiceerrors
+
+import "time"
+
+// TerminationError represents an error that captures contextual information to make
+// available on process termination. The error will be marshalled as JSON and
+// serialized into a file-path specified via CLI arguments
+type TerminationError struct {
+ error
+ Component string `json:"component"`
+ Timestamp time.Time `json:"timestamp"`
+ ErrorString string `json:"error"`
+ Metadata map[string]string `json:"metadata"`
+ exitCode int
+}
+
+// ExitCode returns the exit code to be returned on process termination
+func (e TerminationError) ExitCode() int {
+ return e.exitCode
+}
+
+// ErrorBuilder is a fluent-style builder for TerminationError
+type ErrorBuilder struct {
+ terminationErr TerminationError
+}
+
+// TerminationError returns the built termination TerminationError
+func (eb *ErrorBuilder) Error() TerminationError {
+ return eb.terminationErr
+}
+
+// Component specifies the component in SpiceDB that
+func (eb *ErrorBuilder) Component(component string) *ErrorBuilder {
+ eb.terminationErr.Component = component
+ return eb
+}
+
+// Metadata adds a new key-value pair of metadata to the termination TerminationError being built
+func (eb *ErrorBuilder) Metadata(key, value string) *ErrorBuilder {
+ eb.terminationErr.Metadata[key] = value
+ return eb
+}
+
+// ExitCode defines the ExitCode to be used upon process termination. Defaults to 1 if not specified.
+func (eb *ErrorBuilder) ExitCode(exitCode int) *ErrorBuilder {
+ eb.terminationErr.exitCode = exitCode
+ return eb
+}
+
+// Timestamp defines the time of the error. Defaults to time.Now().UTC() if not specified.
+func (eb *ErrorBuilder) Timestamp(timestamp time.Time) *ErrorBuilder {
+ eb.terminationErr.Timestamp = timestamp
+ return eb
+}
+
+// NewTerminationErrorBuilder returns a new ErrorBuilder for a termination.TerminationError.
+func NewTerminationErrorBuilder(err error) *ErrorBuilder {
+ return &ErrorBuilder{terminationErr: TerminationError{
+ error: err,
+ Component: "unspecified",
+ Timestamp: time.Now().UTC(),
+ ErrorString: err.Error(),
+ Metadata: make(map[string]string, 0),
+ exitCode: 1,
+ }}
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/spiceerrors/testutil.go b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/testutil.go
new file mode 100644
index 0000000..db71540
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/testutil.go
@@ -0,0 +1,28 @@
+package spiceerrors
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "google.golang.org/genproto/googleapis/rpc/errdetails"
+ "google.golang.org/grpc/status"
+
+ v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
+)
+
+// RequireReason asserts that an error is a gRPC error and returns the expected
+// reason in the ErrorInfo.
+// TODO(jschorr): Move into grpcutil.
+func RequireReason(t testing.TB, reason v1.ErrorReason, err error, expectedMetadataKeys ...string) {
+ require.Error(t, err)
+ withStatus, ok := status.FromError(err)
+ require.True(t, ok)
+ require.True(t, len(withStatus.Details()) > 0)
+
+ info := withStatus.Details()[0].(*errdetails.ErrorInfo)
+ require.Equal(t, v1.ErrorReason_name[int32(reason)], info.GetReason())
+
+ for _, expectedKey := range expectedMetadataKeys {
+ require.Contains(t, info.Metadata, expectedKey)
+ }
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/spiceerrors/util.go b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/util.go
new file mode 100644
index 0000000..744e41b
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/util.go
@@ -0,0 +1,18 @@
+package spiceerrors
+
+import (
+ "maps"
+)
+
+type WithMetadata interface {
+ DetailsMetadata() map[string]string
+}
+
+// CombineMetadata combines the metadata found on an existing error with that given.
+func CombineMetadata(withMetadata WithMetadata, metadata map[string]string) map[string]string {
+ clone := maps.Clone(withMetadata.DetailsMetadata())
+ for key, value := range metadata {
+ clone[key] = value
+ }
+ return clone
+}
diff --git a/vendor/github.com/authzed/spicedb/pkg/spiceerrors/withstatus.go b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/withstatus.go
new file mode 100644
index 0000000..1ee6e0a
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/pkg/spiceerrors/withstatus.go
@@ -0,0 +1,82 @@
+package spiceerrors
+
+import (
+ "errors"
+
+ log "github.com/authzed/spicedb/internal/logging"
+
+ "google.golang.org/genproto/googleapis/rpc/errdetails"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+ "google.golang.org/protobuf/runtime/protoiface"
+
+ v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
+)
+
+// Domain is the domain used for all errors.
+const Domain = "authzed.com"
+
+// WithCodeAndDetails returns a gRPC status message containing the error's message, the given
+// status code and any supplied details.
+func WithCodeAndDetails(err error, code codes.Code, details ...protoiface.MessageV1) *status.Status {
+ created := status.New(code, err.Error())
+ created, derr := created.WithDetails(details...)
+ if derr != nil {
+ log.Err(derr).Str("provided-error", err.Error()).Msg("could not add details to provided error")
+ }
+ return created
+}
+
+// WithCodeAndDetailsAsError returns an error containing the error's message, the given
+// status code and any supplied details.
+func WithCodeAndDetailsAsError(err error, code codes.Code, details ...protoiface.MessageV1) error {
+ status := WithCodeAndDetails(err, code, details...)
+ return withStatusError{err, status}
+}
+
+// ForReason returns an ErrorInfo block for a specific error reason as defined in the V1 API.
+func ForReason(reason v1.ErrorReason, metadata map[string]string) *errdetails.ErrorInfo {
+ return &errdetails.ErrorInfo{
+ Reason: v1.ErrorReason_name[int32(reason)],
+ Domain: Domain,
+ Metadata: metadata,
+ }
+}
+
+// WithCodeAndReason returns a new error which wraps the existing error with a gRPC code and
+// a reason block.
+func WithCodeAndReason(err error, code codes.Code, reason v1.ErrorReason) error {
+ metadata := map[string]string{}
+
+ var hasMetadata HasMetadata
+ if ok := errors.As(err, &hasMetadata); ok {
+ metadata = hasMetadata.DetailsMetadata()
+ }
+
+ status := WithCodeAndDetails(err, code, ForReason(reason, metadata))
+ return withStatusError{err, status}
+}
+
+type SupportsAdditionalMetadata interface {
+ WithAdditionalDetails(key MetadataKey, value string)
+}
+
+// WithAdditionalDetails adds an additional details field to the error if it is possible.
+func WithAdditionalDetails(err error, key MetadataKey, value string) bool {
+ var supportsAdditionalDetails SupportsAdditionalMetadata
+ if ok := errors.As(err, &supportsAdditionalDetails); ok {
+ supportsAdditionalDetails.WithAdditionalDetails(key, value)
+ return true
+ }
+
+ return false
+}
+
+type withStatusError struct {
+ error
+ status *status.Status
+}
+
+func (err withStatusError) GRPCStatus() *status.Status {
+ return err.status
+}