github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/services/v1/preconditions.go (about)

     1  package v1
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
     8  
     9  	"github.com/authzed/spicedb/pkg/datastore"
    10  	"github.com/authzed/spicedb/pkg/datastore/options"
    11  )
    12  
    13  var limitOne uint64 = 1
    14  
    15  // checkPreconditions checks whether the preconditions are met in the context of a datastore
    16  // read-write transaction, and returns an error if they are not met.
    17  func checkPreconditions(
    18  	ctx context.Context,
    19  	rwt datastore.ReadWriteTransaction,
    20  	preconditions []*v1.Precondition,
    21  ) error {
    22  	for _, precond := range preconditions {
    23  		dsFilter, err := datastore.RelationshipsFilterFromPublicFilter(precond.Filter)
    24  		if err != nil {
    25  			return fmt.Errorf("error converting filter: %w", err)
    26  		}
    27  
    28  		iter, err := rwt.QueryRelationships(ctx, dsFilter, options.WithLimit(&limitOne))
    29  		if err != nil {
    30  			return fmt.Errorf("error reading relationships: %w", err)
    31  		}
    32  		defer iter.Close()
    33  
    34  		first := iter.Next()
    35  		if first == nil && iter.Err() != nil {
    36  			return fmt.Errorf("error reading relationships from iterator: %w", err)
    37  		}
    38  		iter.Close()
    39  
    40  		switch precond.Operation {
    41  		case v1.Precondition_OPERATION_MUST_NOT_MATCH:
    42  			if first != nil {
    43  				return NewPreconditionFailedErr(precond)
    44  			}
    45  		case v1.Precondition_OPERATION_MUST_MATCH:
    46  			if first == nil {
    47  				return NewPreconditionFailedErr(precond)
    48  			}
    49  		default:
    50  			return fmt.Errorf("unspecified precondition operation: %s", precond.Operation)
    51  		}
    52  	}
    53  
    54  	return nil
    55  }