github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datastore/common/errors.go (about)

     1  package common
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"regexp"
     8  	"strings"
     9  
    10  	"google.golang.org/grpc/codes"
    11  	"google.golang.org/grpc/status"
    12  
    13  	v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
    14  
    15  	log "github.com/authzed/spicedb/internal/logging"
    16  	core "github.com/authzed/spicedb/pkg/proto/core/v1"
    17  	"github.com/authzed/spicedb/pkg/spiceerrors"
    18  	"github.com/authzed/spicedb/pkg/tuple"
    19  )
    20  
    21  // SerializationError is returned when there's been a serialization
    22  // error while performing a datastore operation
    23  type SerializationError struct {
    24  	error
    25  }
    26  
    27  func (err SerializationError) GRPCStatus() *status.Status {
    28  	return spiceerrors.WithCodeAndDetails(
    29  		err,
    30  		codes.Aborted,
    31  		spiceerrors.ForReason(
    32  			v1.ErrorReason_ERROR_REASON_SERIALIZATION_FAILURE,
    33  			map[string]string{},
    34  		),
    35  	)
    36  }
    37  
    38  func (err SerializationError) Unwrap() error {
    39  	return err.error
    40  }
    41  
    42  // NewSerializationError creates a new SerializationError
    43  func NewSerializationError(err error) error {
    44  	return SerializationError{err}
    45  }
    46  
    47  // CreateRelationshipExistsError is an error returned when attempting to CREATE an already-existing
    48  // relationship.
    49  type CreateRelationshipExistsError struct {
    50  	error
    51  
    52  	// Relationship is the relationship that caused the error. May be nil, depending on the datastore.
    53  	Relationship *core.RelationTuple
    54  }
    55  
    56  // GRPCStatus implements retrieving the gRPC status for the error.
    57  func (err CreateRelationshipExistsError) GRPCStatus() *status.Status {
    58  	if err.Relationship == nil {
    59  		return spiceerrors.WithCodeAndDetails(
    60  			err,
    61  			codes.AlreadyExists,
    62  			spiceerrors.ForReason(
    63  				v1.ErrorReason_ERROR_REASON_ATTEMPT_TO_RECREATE_RELATIONSHIP,
    64  				map[string]string{},
    65  			),
    66  		)
    67  	}
    68  
    69  	relationship := tuple.ToRelationship(err.Relationship)
    70  	return spiceerrors.WithCodeAndDetails(
    71  		err,
    72  		codes.AlreadyExists,
    73  		spiceerrors.ForReason(
    74  			v1.ErrorReason_ERROR_REASON_ATTEMPT_TO_RECREATE_RELATIONSHIP,
    75  			map[string]string{
    76  				"relationship":       tuple.StringRelationshipWithoutCaveat(relationship),
    77  				"resource_type":      relationship.Resource.ObjectType,
    78  				"resource_object_id": relationship.Resource.ObjectId,
    79  				"resource_relation":  relationship.Relation,
    80  				"subject_type":       relationship.Subject.Object.ObjectType,
    81  				"subject_object_id":  relationship.Subject.Object.ObjectId,
    82  				"subject_relation":   relationship.Subject.OptionalRelation,
    83  			},
    84  		),
    85  	)
    86  }
    87  
    88  // NewCreateRelationshipExistsError creates a new CreateRelationshipExistsError.
    89  func NewCreateRelationshipExistsError(relationship *core.RelationTuple) error {
    90  	msg := "could not CREATE one or more relationships, as they already existed. If this is persistent, please switch to TOUCH operations or specify a precondition"
    91  	if relationship != nil {
    92  		msg = fmt.Sprintf("could not CREATE relationship `%s`, as it already existed. If this is persistent, please switch to TOUCH operations or specify a precondition", tuple.StringWithoutCaveat(relationship))
    93  	}
    94  
    95  	return CreateRelationshipExistsError{
    96  		fmt.Errorf(msg),
    97  		relationship,
    98  	}
    99  }
   100  
   101  var (
   102  	portMatchRegex  = regexp.MustCompile("invalid port \\\"(.+)\\\" after host")
   103  	parseMatchRegex = regexp.MustCompile("parse \\\"(.+)\\\":")
   104  )
   105  
   106  // RedactAndLogSensitiveConnString elides the given error, logging it only at trace
   107  // level (after being redacted).
   108  func RedactAndLogSensitiveConnString(ctx context.Context, baseErr string, err error, pgURL string) error {
   109  	if err == nil {
   110  		return errors.New(baseErr)
   111  	}
   112  
   113  	// See: https://github.com/jackc/pgx/issues/1271
   114  	filtered := err.Error()
   115  	filtered = strings.ReplaceAll(filtered, pgURL, "(redacted)")
   116  	filtered = portMatchRegex.ReplaceAllString(filtered, "(redacted)")
   117  	filtered = parseMatchRegex.ReplaceAllString(filtered, "(redacted)")
   118  	log.Ctx(ctx).Trace().Msg(baseErr + ": " + filtered)
   119  	return fmt.Errorf("%s. To view details of this error (that may contain sensitive information), please run with --log-level=trace", baseErr)
   120  }