Spaces:
Running
Running
// _ _ | |
// __ _____ __ ___ ___ __ _| |_ ___ | |
// \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ | |
// \ V V / __/ (_| |\ V /| | (_| | || __/ | |
// \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| | |
// | |
// Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. | |
// | |
// CONTACT: [email protected] | |
// | |
package crossref | |
import ( | |
"fmt" | |
"net/url" | |
"strings" | |
"unicode" | |
"github.com/go-openapi/strfmt" | |
"github.com/weaviate/weaviate/entities/schema" | |
) | |
// RefSource is an abstraction of the source of a cross-ref. The opposite would | |
// be Ref which represents the target instead. A RefSource is specified in a URI | |
// format in the API. When this type is used it is safe to assume that a Ref is | |
// semantically valid. This guarantee would not be possible on the URI format, | |
// as the URI can be well-formed, but not contain the data we expect in it. Do | |
// not use directly, such as crossref.RefSource{}, as you won't have any | |
// guarantees in this case. Always use one of the parsing options or New() | |
type RefSource struct { | |
Local bool `json:"local"` | |
PeerName string `json:"peerName"` | |
Property schema.PropertyName `json:"property"` | |
Class schema.ClassName `json:"class"` | |
TargetID strfmt.UUID `json:"targetID"` | |
} | |
func NewSource(className schema.ClassName, | |
property schema.PropertyName, id strfmt.UUID, | |
) *RefSource { | |
return &RefSource{ | |
Local: true, | |
PeerName: "localhost", | |
Class: className, | |
TargetID: id, | |
Property: property, | |
} | |
} | |
// ParseSource is a safe way to generate a RefSource, as it will error if any | |
// of the input parameters are not as expected. | |
func ParseSource(uriString string) (*RefSource, error) { | |
uri, err := url.Parse(uriString) | |
if err != nil { | |
return nil, fmt.Errorf("invalid cref URI: %s", err) | |
} | |
pathSegments := strings.Split(uri.Path, "/") | |
if len(pathSegments) != 4 { | |
return nil, fmt.Errorf( | |
"invalid cref URI: must use long-form: path must be of format '/<className>/<uuid>/<propertyName>', but got '%s'", | |
uri.Path) | |
} | |
if ok := strfmt.IsUUID(pathSegments[2]); !ok { | |
return nil, fmt.Errorf("invalid cref URI: 2nd path segment must be uuid, but got '%s'", | |
pathSegments[3]) | |
} | |
class := pathSegments[1] | |
if class == "" { | |
return nil, fmt.Errorf("className cannot be empty") | |
} | |
if unicode.IsLower(rune(class[0])) { | |
return nil, fmt.Errorf("className must start with an uppercase letter, but got %s", class) | |
} | |
property := pathSegments[3] | |
if property == "" { | |
return nil, fmt.Errorf("property cannot be empty") | |
} | |
return &RefSource{ | |
Local: (uri.Host == "localhost"), | |
PeerName: uri.Host, | |
TargetID: strfmt.UUID(pathSegments[2]), | |
Class: schema.ClassName(class), | |
Property: schema.PropertyName(property), | |
}, nil | |
} | |
func (r *RefSource) String() string { | |
uri := url.URL{ | |
Host: r.PeerName, | |
Scheme: "weaviate", | |
Path: fmt.Sprintf("/%s/%s/%s", r.Class, r.TargetID, r.Property), | |
} | |
return uri.String() | |
} | |