github.com/openfga/openfga@v1.5.4-rc1/internal/condition/eval/eval.go (about)

     1  package eval
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strconv"
     7  	"time"
     8  
     9  	openfgav1 "github.com/openfga/api/proto/openfga/v1"
    10  	"go.opentelemetry.io/otel"
    11  	"go.opentelemetry.io/otel/attribute"
    12  	"go.opentelemetry.io/otel/trace"
    13  	"google.golang.org/protobuf/types/known/structpb"
    14  
    15  	"github.com/openfga/openfga/internal/condition"
    16  	"github.com/openfga/openfga/internal/condition/metrics"
    17  	"github.com/openfga/openfga/pkg/telemetry"
    18  	"github.com/openfga/openfga/pkg/typesystem"
    19  )
    20  
    21  var tracer = otel.Tracer("openfga/internal/condition/eval")
    22  
    23  // EvaluateTupleCondition looks at the given tuple's condition and returns an evaluation result for the given context.
    24  // If the tuple doesn't have a condition, it exits early and doesn't create a span.
    25  // If the tuple's condition isn't found in the model it returns an EvaluationError.
    26  func EvaluateTupleCondition(
    27  	ctx context.Context,
    28  	tupleKey *openfgav1.TupleKey,
    29  	typesys *typesystem.TypeSystem,
    30  	context *structpb.Struct,
    31  ) (*condition.EvaluationResult, error) {
    32  	tupleCondition := tupleKey.GetCondition()
    33  	conditionName := tupleCondition.GetName()
    34  	if conditionName == "" {
    35  		return &condition.EvaluationResult{
    36  			ConditionMet: true,
    37  		}, nil
    38  	}
    39  
    40  	ctx, span := tracer.Start(ctx, "EvaluateTupleCondition", trace.WithAttributes(
    41  		attribute.String("tuple_key", tupleKey.String()),
    42  		attribute.String("condition_name", conditionName)))
    43  	defer span.End()
    44  
    45  	start := time.Now()
    46  
    47  	evaluableCondition, ok := typesys.GetCondition(conditionName)
    48  	if !ok {
    49  		err := condition.NewEvaluationError(conditionName, fmt.Errorf("condition was not found"))
    50  		telemetry.TraceError(span, err)
    51  		return nil, err
    52  	}
    53  
    54  	span.SetAttributes(attribute.String("condition_expression", evaluableCondition.GetExpression()))
    55  
    56  	// merge both contexts
    57  	contextFields := []map[string]*structpb.Value{
    58  		{},
    59  	}
    60  	if context != nil {
    61  		contextFields = []map[string]*structpb.Value{context.GetFields()}
    62  	}
    63  
    64  	tupleContext := tupleCondition.GetContext()
    65  	if tupleContext != nil {
    66  		contextFields = append(contextFields, tupleContext.GetFields())
    67  	}
    68  
    69  	conditionResult, err := evaluableCondition.Evaluate(ctx, contextFields...)
    70  	if err != nil {
    71  		telemetry.TraceError(span, err)
    72  		return nil, err
    73  	}
    74  
    75  	metrics.Metrics.ObserveEvaluationDuration(time.Since(start))
    76  	metrics.Metrics.ObserveEvaluationCost(conditionResult.Cost)
    77  
    78  	span.SetAttributes(attribute.Bool("condition_met", conditionResult.ConditionMet),
    79  		attribute.String("condition_cost", strconv.FormatUint(conditionResult.Cost, 10)),
    80  		attribute.StringSlice("condition_missing_params", conditionResult.MissingParameters),
    81  	)
    82  	return &conditionResult, nil
    83  }