Skip to content

Commit

Permalink
apply feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
fredcarle committed Jan 3, 2024
1 parent c708f28 commit 23c1ffa
Show file tree
Hide file tree
Showing 16 changed files with 165 additions and 135 deletions.
2 changes: 1 addition & 1 deletion cli/collection_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Example: update by keys
if err != nil {
return err
}
if err := doc.SetWithJSON([]byte(args[0]), col.Schema()); err != nil {
if err := doc.SetWithJSON([]byte(args[0])); err != nil {
return err
}
return col.Update(cmd.Context(), doc)
Expand Down
167 changes: 76 additions & 91 deletions client/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

"github.com/fxamacker/cbor/v2"
"github.com/ipfs/go-cid"
"github.com/sourcenetwork/immutable"
"github.com/valyala/fastjson"

"github.com/sourcenetwork/defradb/client/request"
Expand Down Expand Up @@ -55,43 +56,36 @@ import (
// @body: A document interface can be implemented by both a TypedDocument and a
// UnTypedDocument, which use a schema and schemaless approach respectively.
type Document struct {
key DocKey
// SchemaVersionID holds the id of the schema version that this document is
// currently at.
//
// Migrating the document will update this value to the output version of the
// migration.
SchemaVersionID string
fields map[string]Field
values map[Field]Value
head cid.Cid
mu sync.RWMutex
key DocKey
fields map[string]Field
values map[Field]Value
head cid.Cid
mu sync.RWMutex
// marks if document has unsaved changes
isDirty bool
}

// NewDocWithKey creates a new Document with a specified key.
func NewDocWithKey(key DocKey) *Document {
doc := newEmptyDoc()
doc.key = key
return doc
}

func NewEmptyDoc() *Document {
return newEmptyDoc()
schemaDescription SchemaDescription
}

func newEmptyDoc() *Document {
func newEmptyDoc(sd SchemaDescription) *Document {
return &Document{
fields: make(map[string]Field),
values: make(map[Field]Value),
fields: make(map[string]Field),
values: make(map[Field]Value),
schemaDescription: sd,
}
}

// NewDocWithKey creates a new Document with a specified key.
func NewDocWithKey(key DocKey, sd SchemaDescription) *Document {
doc := newEmptyDoc(sd)
doc.key = key
return doc
}

// NewDocFromMap creates a new Document from a data map.
func NewDocFromMap(data map[string]any, sd SchemaDescription) (*Document, error) {
var err error
doc := newEmptyDoc()
doc := newEmptyDoc(sd)

// check if document contains special _key field
k, hasKey := data["_key"]
Expand All @@ -106,7 +100,7 @@ func NewDocFromMap(data map[string]any, sd SchemaDescription) (*Document, error)
}
}

err = doc.setAndParseObjectType(data, sd)
err = doc.setAndParseObjectType(data)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -134,8 +128,8 @@ func IsJSONArray(obj []byte) bool {

// NewFromJSON creates a new instance of a Document from a raw JSON object byte array.
func NewDocFromJSON(obj []byte, sd SchemaDescription) (*Document, error) {
doc := newEmptyDoc()
err := doc.SetWithJSON(obj, sd)
doc := newEmptyDoc(sd)
err := doc.SetWithJSON(obj)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -163,8 +157,8 @@ func NewDocsFromJSON(obj []byte, sd SchemaDescription) ([]*Document, error) {
if err != nil {
return nil, err
}
doc := newEmptyDoc()
err = doc.setWithFastJSONObject(o, sd)
doc := newEmptyDoc(sd)
err = doc.setWithFastJSONObject(o)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -256,35 +250,29 @@ func getFloat64(v any) (float64, error) {
switch val := v.(type) {
case *fastjson.Value:
return val.Float64()
case int:
return float64(val), nil
case int64:
return float64(val), nil

Check warning on line 256 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L253-L256

Added lines #L253 - L256 were not covered by tests
case float64:
return val, nil
default:
switch v := val.(type) {
case int:
return float64(v), nil
case int64:
return float64(v), nil
case float64:
return v, nil
default:
return 0, NewErrUnexpectedType[float64]("field", v)
}
return 0, NewErrUnexpectedType[float64]("field", v)

Check warning on line 260 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L259-L260

Added lines #L259 - L260 were not covered by tests
}
}

func getInt64(v any) (int64, error) {
switch val := v.(type) {
case *fastjson.Value:
return val.Int64()
case int:
return int64(val), nil
case int64:
return val, nil
case float64:
return int64(val), nil
default:
switch v := val.(type) {
case int:
return int64(v), nil
case int64:
return v, nil
case float64:
return int64(v), nil
default:
return 0, NewErrUnexpectedType[int64]("field", v)
}
return 0, NewErrUnexpectedType[int64]("field", v)

Check warning on line 275 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L274-L275

Added lines #L274 - L275 were not covered by tests
}
}

Expand All @@ -297,6 +285,8 @@ func getDateTime(v any) (time.Time, error) {
return time.Time{}, err
}

Check warning on line 286 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L285-L286

Added lines #L285 - L286 were not covered by tests
s = string(b)
case time.Time:
return val, nil
default:
s = val.(string)

Check warning on line 291 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L290-L291

Added lines #L290 - L291 were not covered by tests
}
Expand Down Expand Up @@ -338,7 +328,7 @@ func getArray[T any](
func getNillableArray[T any](
v any,
typeGetter func(any) (T, error),
) ([]*T, error) {
) ([]immutable.Option[T], error) {
switch val := v.(type) {
case *fastjson.Value:
if val.Type() == fastjson.TypeNull {
Expand All @@ -350,21 +340,22 @@ func getNillableArray[T any](
return nil, err
}

Check warning on line 341 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L340-L341

Added lines #L340 - L341 were not covered by tests

arr := make([]*T, len(valArray))
arr := make([]immutable.Option[T], len(valArray))
for i, arrItem := range valArray {
if arrItem.Type() == fastjson.TypeNull {
arr[i] = immutable.None[T]()
continue
}
v, err := typeGetter(arrItem)
if err != nil {
return nil, err
}

Check warning on line 352 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L351-L352

Added lines #L351 - L352 were not covered by tests
arr[i] = &v
arr[i] = immutable.Some(v)
}

return arr, nil
default:
return val.([]*T), nil
return []immutable.Option[T]{}, nil
}
}

Expand Down Expand Up @@ -439,7 +430,7 @@ func (doc *Document) GetValueWithField(f Field) (Value, error) {
// JSON Merge Patch object. Note: fields indicated as nil in the Merge
// Patch are to be deleted
// @todo: Handle sub documents for SetWithJSON
func (doc *Document) SetWithJSON(obj []byte, sd SchemaDescription) error {
func (doc *Document) SetWithJSON(obj []byte) error {
v, err := fastjson.ParseBytes(obj)
if err != nil {
return err
Expand All @@ -449,32 +440,14 @@ func (doc *Document) SetWithJSON(obj []byte, sd SchemaDescription) error {
return err
}

Check warning on line 441 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L440-L441

Added lines #L440 - L441 were not covered by tests

return doc.setWithFastJSONObject(o, sd)
return doc.setWithFastJSONObject(o)
}

func (doc *Document) setWithFastJSONObject(obj *fastjson.Object, sd SchemaDescription) error {
func (doc *Document) setWithFastJSONObject(obj *fastjson.Object) error {
var visitErr error
obj.Visit(func(k []byte, v *fastjson.Value) {
fieldName := string(k)
fd, exists := sd.GetField(fieldName)
if !exists {
visitErr = NewErrFieldNotExist(fieldName)
return
}
if fd.IsPrimaryRelation() {
fd, exists = sd.GetField(fieldName + request.RelatedObjectID)
if !exists {
visitErr = NewErrFieldNotExist(fieldName)
return
}
}
val, err := validateFieldSchema(v, fd)
if err != nil {
visitErr = err
return
}

err = doc.SetAs(fieldName, val, fd.Typ)
err := doc.Set(fieldName, v)
if err != nil {
visitErr = err
return
Expand All @@ -483,9 +456,30 @@ func (doc *Document) setWithFastJSONObject(obj *fastjson.Object, sd SchemaDescri
return visitErr
}

// SetAs is the same as set, but you can manually set the CRDT type.
func (doc *Document) SetAs(field string, value any, t CType) error {
return doc.setCBOR(t, field, value)
// Set the value of a field.
func (doc *Document) Set(field string, value any) error {
fd, exists := doc.schemaDescription.GetField(field)
if !exists {
return NewErrFieldNotExist(field)
}
if fd.IsPrimaryRelation() {
if strings.HasSuffix(field, request.RelatedObjectID) {
fd, exists = doc.schemaDescription.GetField(field)
if !exists {
return NewErrFieldNotExist(field)
}

Check warning on line 470 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L469-L470

Added lines #L469 - L470 were not covered by tests
} else {
fd, exists = doc.schemaDescription.GetField(field + request.RelatedObjectID)
if !exists {
return NewErrFieldNotExist(field)
}

Check warning on line 475 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L474-L475

Added lines #L474 - L475 were not covered by tests
}
}
val, err := validateFieldSchema(value, fd)
if err != nil {
return err
}
return doc.setCBOR(fd.Typ, field, val)
}

// Delete removes a field, and marks it to be deleted on the following db.Update() call.
Expand Down Expand Up @@ -522,21 +516,12 @@ func (doc *Document) setCBOR(t CType, field string, val any) error {
return doc.set(t, field, value)
}

func (doc *Document) setAndParseObjectType(value map[string]any, sd SchemaDescription) error {
func (doc *Document) setAndParseObjectType(value map[string]any) error {
for k, v := range value {
if v == nil {
continue
}
fd, exists := sd.GetField(k)
if !exists {
return NewErrFieldNotExist(k)
}
val, err := validateFieldSchema(v, fd)
if err != nil {
return err
}

err = doc.SetAs(k, val, fd.Typ)
err := doc.Set(k, v)
if err != nil {
return err
}
Expand Down Expand Up @@ -623,7 +608,7 @@ func (doc *Document) Clean() {
val, _ := doc.GetValueWithField(v)
if val.IsDirty() {
if val.IsDelete() {
doc.SetAs(v.Name(), nil, v.Type()) //nolint:errcheck
doc.Set(v.Name(), nil) //nolint:errcheck

Check warning on line 611 in client/document.go

View check run for this annotation

Codecov / codecov/patch

client/document.go#L611

Added line #L611 was not covered by tests
}
val.Clean()
}
Expand Down
2 changes: 1 addition & 1 deletion client/document_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func TestSetWithJSON(t *testing.T) {
"Name": "Alice",
"Age": 27
}`)
err = doc.SetWithJSON(updatePatch, schemaDescriptions[0])
err = doc.SetWithJSON(updatePatch)
if err != nil {
t.Error(err)
}
Expand Down
30 changes: 29 additions & 1 deletion client/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package client

import (
"github.com/fxamacker/cbor/v2"
"github.com/sourcenetwork/immutable"
)

// Value is an interface that points to a concrete Value implementation.
Expand Down Expand Up @@ -111,5 +112,32 @@ func (v cborValue) Bytes() ([]byte, error) {
if err != nil {
return nil, err
}

Check warning on line 114 in client/value.go

View check run for this annotation

Codecov / codecov/patch

client/value.go#L113-L114

Added lines #L113 - L114 were not covered by tests
return em.Marshal(v.value)

var val any
switch tempVal := v.value.(type) {
case []immutable.Option[string]:
val = convertImmutable(tempVal)
case []immutable.Option[int64]:
val = convertImmutable(tempVal)
case []immutable.Option[float64]:
val = convertImmutable(tempVal)
case []immutable.Option[bool]:
val = convertImmutable(tempVal)
default:
val = v.value
}

return em.Marshal(val)
}

func convertImmutable[T any](vals []immutable.Option[T]) []any {
var out []any
for _, val := range vals {
if !val.HasValue() {
out = append(out, nil)
continue
}
out = append(out, val.Value())
}
return out
}
Loading

0 comments on commit 23c1ffa

Please sign in to comment.