Spaces:
Running
Running
// _ _ | |
// __ _____ __ ___ ___ __ _| |_ ___ | |
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ | |
// \ V V / __/ (_| |\ V /| | (_| | || __/ | |
// \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| | |
// | |
// Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. | |
// | |
// CONTACT: [email protected] | |
// | |
package validation | |
import ( | |
"context" | |
"fmt" | |
"strings" | |
"github.com/go-openapi/strfmt" | |
"github.com/weaviate/weaviate/entities/additional" | |
"github.com/weaviate/weaviate/entities/models" | |
"github.com/weaviate/weaviate/entities/schema/crossref" | |
"github.com/weaviate/weaviate/usecases/config" | |
) | |
type exists func(_ context.Context, class string, _ strfmt.UUID, _ *additional.ReplicationProperties, _ string) (bool, error) | |
const ( | |
// ErrorMissingActionObjects message | |
ErrorMissingActionObjects string = "no objects, object and subject, are added. Add 'objects' by using the 'objects' key in the root of the JSON" | |
// ErrorMissingActionObjectsObject message | |
ErrorMissingActionObjectsObject string = "no object-thing is added. Add the 'object' inside the 'objects' part of the JSON" | |
// ErrorMissingActionObjectsSubject message | |
ErrorMissingActionObjectsSubject string = "no subject-thing is added. Add the 'subject' inside the 'objects' part of the JSON" | |
// ErrorMissingActionObjectsObjectLocation message | |
ErrorMissingActionObjectsObjectLocation string = "no 'locationURL' is found in the object-thing. Add the 'locationURL' inside the 'object-thing' part of the JSON" | |
// ErrorMissingActionObjectsObjectType message | |
ErrorMissingActionObjectsObjectType string = "no 'type' is found in the object-thing. Add the 'type' inside the 'object-thing' part of the JSON" | |
// ErrorInvalidActionObjectsObjectType message | |
ErrorInvalidActionObjectsObjectType string = "object-thing requires one of the following values in 'type': '%s', '%s' or '%s'" | |
// ErrorMissingActionObjectsSubjectLocation message | |
ErrorMissingActionObjectsSubjectLocation string = "no 'locationURL' is found in the subject-thing. Add the 'locationURL' inside the 'subject-thing' part of the JSON" | |
// ErrorMissingActionObjectsSubjectType message | |
ErrorMissingActionObjectsSubjectType string = "no 'type' is found in the subject-thing. Add the 'type' inside the 'subject-thing' part of the JSON" | |
// ErrorInvalidActionObjectsSubjectType message | |
ErrorInvalidActionObjectsSubjectType string = "subject-thing requires one of the following values in 'type': '%s', '%s' or '%s'" | |
// ErrorMisingObject message | |
ErrorMissingObject = "the given object is empty" | |
// ErrorMissingClass message | |
ErrorMissingClass string = "the given class is empty" | |
// ErrorMissingContext message | |
ErrorMissingContext string = "the given context is empty" | |
// ErrorNoExternalCredentials message | |
ErrorNoExternalCredentials string = "no credentials available for the Weaviate instance for %s given in the %s" | |
// ErrorExternalNotFound message | |
ErrorExternalNotFound string = "given statuscode of '%s' is '%d', but 200 was expected for LocationURL given in the %s" | |
// ErrorInvalidCRefType message | |
ErrorInvalidCRefType string = "'cref' type '%s' does not exists" | |
// ErrorNotFoundInDatabase message | |
ErrorNotFoundInDatabase string = "%s: no object with id %s found" | |
// ErrorInvalidProperties message | |
ErrorInvalidProperties string = "properties of object %v must be of type map[string]interface" | |
) | |
type Validator struct { | |
exists exists | |
config *config.WeaviateConfig | |
replicationProps *additional.ReplicationProperties | |
} | |
func New(exists exists, config *config.WeaviateConfig, | |
repl *additional.ReplicationProperties, | |
) *Validator { | |
return &Validator{ | |
exists: exists, | |
config: config, | |
replicationProps: repl, | |
} | |
} | |
func (v *Validator) Object(ctx context.Context, class *models.Class, | |
incoming *models.Object, existing *models.Object, | |
) error { | |
if err := validateClass(incoming.Class); err != nil { | |
return err | |
} | |
return v.properties(ctx, class, incoming, existing) | |
} | |
func validateClass(class string) error { | |
// If the given class is empty, return an error | |
if class == "" { | |
return fmt.Errorf(ErrorMissingClass) | |
} | |
// No error | |
return nil | |
} | |
// ValidateSingleRef validates a single ref based on location URL and existence of the object in the database | |
func (v *Validator) ValidateSingleRef(cref *models.SingleRef) (*crossref.Ref, error) { | |
ref, err := crossref.ParseSingleRef(cref) | |
if err != nil { | |
return nil, fmt.Errorf("invalid reference: %w", err) | |
} | |
// target id must be lowercase | |
ref.TargetID = strfmt.UUID(strings.ToLower(ref.TargetID.String())) | |
if !ref.Local { | |
return nil, fmt.Errorf("unrecognized cross-ref ref format") | |
} | |
return ref, nil | |
} | |
func (v *Validator) ValidateExistence(ctx context.Context, ref *crossref.Ref, errorVal string, tenant string) error { | |
// locally check for object existence | |
ok, err := v.exists(ctx, ref.Class, ref.TargetID, v.replicationProps, tenant) | |
if err != nil { | |
if tenant == "" { | |
return err | |
} | |
// since refs can be created to non-MT classes, check again if non-MT object exists | |
// (use empty tenant, if previously was given) | |
ok2, err2 := v.exists(ctx, ref.Class, ref.TargetID, v.replicationProps, "") | |
if err2 != nil { | |
// return orig error | |
return err | |
} | |
ok = ok2 | |
} | |
if !ok { | |
return fmt.Errorf(ErrorNotFoundInDatabase, errorVal, ref.TargetID) | |
} | |
return nil | |
} | |
func (v *Validator) ValidateMultipleRef(ctx context.Context, refs models.MultipleRef, | |
errorVal string, tenant string, | |
) ([]*crossref.Ref, error) { | |
parsedRefs := make([]*crossref.Ref, len(refs)) | |
if refs == nil { | |
return parsedRefs, nil | |
} | |
for i, ref := range refs { | |
parsedRef, err := v.ValidateSingleRef(ref) | |
if err != nil { | |
return nil, err | |
} | |
parsedRefs[i] = parsedRef | |
} | |
return parsedRefs, nil | |
} | |