github.com/weaviate/weaviate@v1.24.6/usecases/objects/validation/model_validation.go (about)

     1  //                           _       _
     2  // __      _____  __ ___   ___  __ _| |_ ___
     3  // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
     4  //  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
     5  //   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
     6  //
     7  //  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
     8  //
     9  //  CONTACT: hello@weaviate.io
    10  //
    11  
    12  package validation
    13  
    14  import (
    15  	"context"
    16  	"fmt"
    17  	"strings"
    18  
    19  	"github.com/go-openapi/strfmt"
    20  	"github.com/weaviate/weaviate/entities/additional"
    21  	"github.com/weaviate/weaviate/entities/models"
    22  	"github.com/weaviate/weaviate/entities/schema/crossref"
    23  	"github.com/weaviate/weaviate/usecases/config"
    24  )
    25  
    26  type exists func(_ context.Context, class string, _ strfmt.UUID, _ *additional.ReplicationProperties, _ string) (bool, error)
    27  
    28  const (
    29  	// ErrorMissingActionObjects message
    30  	ErrorMissingActionObjects string = "no objects, object and subject, are added. Add 'objects' by using the 'objects' key in the root of the JSON"
    31  	// ErrorMissingActionObjectsObject message
    32  	ErrorMissingActionObjectsObject string = "no object-thing is added. Add the 'object' inside the 'objects' part of the JSON"
    33  	// ErrorMissingActionObjectsSubject message
    34  	ErrorMissingActionObjectsSubject string = "no subject-thing is added. Add the 'subject' inside the 'objects' part of the JSON"
    35  	// ErrorMissingActionObjectsObjectLocation message
    36  	ErrorMissingActionObjectsObjectLocation string = "no 'locationURL' is found in the object-thing. Add the 'locationURL' inside the 'object-thing' part of the JSON"
    37  	// ErrorMissingActionObjectsObjectType message
    38  	ErrorMissingActionObjectsObjectType string = "no 'type' is found in the object-thing. Add the 'type' inside the 'object-thing' part of the JSON"
    39  	// ErrorInvalidActionObjectsObjectType message
    40  	ErrorInvalidActionObjectsObjectType string = "object-thing requires one of the following values in 'type': '%s', '%s' or '%s'"
    41  	// ErrorMissingActionObjectsSubjectLocation message
    42  	ErrorMissingActionObjectsSubjectLocation string = "no 'locationURL' is found in the subject-thing. Add the 'locationURL' inside the 'subject-thing' part of the JSON"
    43  	// ErrorMissingActionObjectsSubjectType message
    44  	ErrorMissingActionObjectsSubjectType string = "no 'type' is found in the subject-thing. Add the 'type' inside the 'subject-thing' part of the JSON"
    45  	// ErrorInvalidActionObjectsSubjectType message
    46  	ErrorInvalidActionObjectsSubjectType string = "subject-thing requires one of the following values in 'type': '%s', '%s' or '%s'"
    47  	// ErrorMisingObject message
    48  	ErrorMissingObject = "the given object is empty"
    49  	// ErrorMissingClass message
    50  	ErrorMissingClass string = "the given class is empty"
    51  	// ErrorMissingContext message
    52  	ErrorMissingContext string = "the given context is empty"
    53  	// ErrorNoExternalCredentials message
    54  	ErrorNoExternalCredentials string = "no credentials available for the Weaviate instance for %s given in the %s"
    55  	// ErrorExternalNotFound message
    56  	ErrorExternalNotFound string = "given statuscode of '%s' is '%d', but 200 was expected for LocationURL given in the %s"
    57  	// ErrorInvalidCRefType message
    58  	ErrorInvalidCRefType string = "'cref' type '%s' does not exists"
    59  	// ErrorNotFoundInDatabase message
    60  	ErrorNotFoundInDatabase string = "%s: no object with id %s found"
    61  	// ErrorInvalidProperties message
    62  	ErrorInvalidProperties string = "properties of object %v must be of type map[string]interface"
    63  )
    64  
    65  type Validator struct {
    66  	exists           exists
    67  	config           *config.WeaviateConfig
    68  	replicationProps *additional.ReplicationProperties
    69  }
    70  
    71  func New(exists exists, config *config.WeaviateConfig,
    72  	repl *additional.ReplicationProperties,
    73  ) *Validator {
    74  	return &Validator{
    75  		exists:           exists,
    76  		config:           config,
    77  		replicationProps: repl,
    78  	}
    79  }
    80  
    81  func (v *Validator) Object(ctx context.Context, class *models.Class,
    82  	incoming *models.Object, existing *models.Object,
    83  ) error {
    84  	if err := validateClass(incoming.Class); err != nil {
    85  		return err
    86  	}
    87  
    88  	return v.properties(ctx, class, incoming, existing)
    89  }
    90  
    91  func validateClass(class string) error {
    92  	// If the given class is empty, return an error
    93  	if class == "" {
    94  		return fmt.Errorf(ErrorMissingClass)
    95  	}
    96  
    97  	// No error
    98  	return nil
    99  }
   100  
   101  // ValidateSingleRef validates a single ref based on location URL and existence of the object in the database
   102  func (v *Validator) ValidateSingleRef(cref *models.SingleRef) (*crossref.Ref, error) {
   103  	ref, err := crossref.ParseSingleRef(cref)
   104  	if err != nil {
   105  		return nil, fmt.Errorf("invalid reference: %w", err)
   106  	}
   107  
   108  	// target id must be lowercase
   109  	ref.TargetID = strfmt.UUID(strings.ToLower(ref.TargetID.String()))
   110  
   111  	if !ref.Local {
   112  		return nil, fmt.Errorf("unrecognized cross-ref ref format")
   113  	}
   114  
   115  	return ref, nil
   116  }
   117  
   118  func (v *Validator) ValidateExistence(ctx context.Context, ref *crossref.Ref, errorVal string, tenant string) error {
   119  	// locally check for object existence
   120  	ok, err := v.exists(ctx, ref.Class, ref.TargetID, v.replicationProps, tenant)
   121  	if err != nil {
   122  		if tenant == "" {
   123  			return err
   124  		}
   125  		// since refs can be created to non-MT classes, check again if non-MT object exists
   126  		// (use empty tenant, if previously was given)
   127  		ok2, err2 := v.exists(ctx, ref.Class, ref.TargetID, v.replicationProps, "")
   128  		if err2 != nil {
   129  			// return orig error
   130  			return err
   131  		}
   132  		ok = ok2
   133  	}
   134  	if !ok {
   135  		return fmt.Errorf(ErrorNotFoundInDatabase, errorVal, ref.TargetID)
   136  	}
   137  
   138  	return nil
   139  }
   140  
   141  func (v *Validator) ValidateMultipleRef(ctx context.Context, refs models.MultipleRef,
   142  	errorVal string, tenant string,
   143  ) ([]*crossref.Ref, error) {
   144  	parsedRefs := make([]*crossref.Ref, len(refs))
   145  
   146  	if refs == nil {
   147  		return parsedRefs, nil
   148  	}
   149  
   150  	for i, ref := range refs {
   151  		parsedRef, err := v.ValidateSingleRef(ref)
   152  		if err != nil {
   153  			return nil, err
   154  		}
   155  		parsedRefs[i] = parsedRef
   156  
   157  	}
   158  	return parsedRefs, nil
   159  }