github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/spiceerrors/withstatus.go (about)

     1  package spiceerrors
     2  
     3  import (
     4  	"errors"
     5  
     6  	log "github.com/authzed/spicedb/internal/logging"
     7  
     8  	"google.golang.org/genproto/googleapis/rpc/errdetails"
     9  	"google.golang.org/grpc/codes"
    10  	"google.golang.org/grpc/status"
    11  	"google.golang.org/protobuf/runtime/protoiface"
    12  
    13  	v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
    14  )
    15  
    16  // Domain is the domain used for all errors.
    17  const Domain = "authzed.com"
    18  
    19  // WithCodeAndDetails returns a gRPC status message containing the error's message, the given
    20  // status code and any supplied details.
    21  func WithCodeAndDetails(err error, code codes.Code, details ...protoiface.MessageV1) *status.Status {
    22  	created := status.New(code, err.Error())
    23  	created, derr := created.WithDetails(details...)
    24  	if derr != nil {
    25  		log.Err(derr).Str("provided-error", err.Error()).Msg("could not add details to provided error")
    26  	}
    27  	return created
    28  }
    29  
    30  // WithCodeAndDetailsAsError returns an error containing the error's message, the given
    31  // status code and any supplied details.
    32  func WithCodeAndDetailsAsError(err error, code codes.Code, details ...protoiface.MessageV1) error {
    33  	status := WithCodeAndDetails(err, code, details...)
    34  	return errWithStatus{err, status}
    35  }
    36  
    37  // ForReason returns an ErrorInfo block for a specific error reason as defined in the V1 API.
    38  func ForReason(reason v1.ErrorReason, metadata map[string]string) *errdetails.ErrorInfo {
    39  	return &errdetails.ErrorInfo{
    40  		Reason:   v1.ErrorReason_name[int32(reason)],
    41  		Domain:   Domain,
    42  		Metadata: metadata,
    43  	}
    44  }
    45  
    46  // WithCodeAndReason returns a new error which wraps the existing error with a gRPC code and
    47  // a reason block.
    48  func WithCodeAndReason(err error, code codes.Code, reason v1.ErrorReason) error {
    49  	metadata := map[string]string{}
    50  
    51  	var hasMetadata HasMetadata
    52  	if ok := errors.As(err, &hasMetadata); ok {
    53  		metadata = hasMetadata.DetailsMetadata()
    54  	}
    55  
    56  	status := WithCodeAndDetails(err, code, ForReason(reason, metadata))
    57  	return errWithStatus{err, status}
    58  }
    59  
    60  type errWithStatus struct {
    61  	error
    62  	status *status.Status
    63  }
    64  
    65  func (err errWithStatus) GRPCStatus() *status.Status {
    66  	return err.status
    67  }