Skip to content

Commit

Permalink
Modify converter to allow setting values for object and array (#687)
Browse files Browse the repository at this point in the history
PR#678 triggers the regression error, so I made corrections and
re-submitted. I've added handling for cases where values are not set
when converting to PbJSONElementSimple. In the bytesToObject function,
It was previously creating a new object with initialTicket when the
bytes were nil. However, this logic became inappropriate after
separating it from bytesToSnapshot, so I removed it.
  • Loading branch information
chacha912 authored Nov 23, 2023
1 parent 17d0039 commit 10245f8
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 20 deletions.
31 changes: 24 additions & 7 deletions api/converter/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,8 @@ import (

func TestConverter(t *testing.T) {
t.Run("snapshot simple test", func(t *testing.T) {
obj, err := converter.BytesToObject(nil)
assert.NoError(t, err)
assert.Equal(t, "{}", obj.Marshal())

doc := document.New("d1")

err = doc.Update(func(root *json.Object, p *presence.Presence) error {
err := doc.Update(func(root *json.Object, p *presence.Presence) error {
root.SetNewText("k1").Edit(0, 0, "A")
return nil
})
Expand All @@ -59,7 +54,7 @@ func TestConverter(t *testing.T) {
bytes, err := converter.ObjectToBytes(doc.RootObject())
assert.NoError(t, err)

obj, err = converter.BytesToObject(bytes)
obj, err := converter.BytesToObject(bytes)
assert.NoError(t, err)
assert.Equal(t, `{"k1":[{"val":"B"}]}`, obj.Marshal())
})
Expand Down Expand Up @@ -243,6 +238,28 @@ func TestConverter(t *testing.T) {
assert.Equal(t, tree.ToXML(), clone.ToXML())
})

t.Run("array converting to bytes test", func(t *testing.T) {
root := helper.TestRoot()
ctx := helper.TextChangeContext(root)

treeList := crdt.NewRGATreeList()
arr := crdt.NewArray(treeList, ctx.IssueTimeTicket())
primitive, _ := crdt.NewPrimitive("1", ctx.IssueTimeTicket())
_ = arr.Add(primitive)
primitive, _ = crdt.NewPrimitive("2", ctx.IssueTimeTicket())
_ = arr.Add(primitive)
primitive, _ = crdt.NewPrimitive("3", ctx.IssueTimeTicket())
_ = arr.Add(primitive)

bytes, err := converter.ArrayToBytes(arr)
assert.NoError(t, err)
clone, err := converter.BytesToArray(bytes)
assert.NoError(t, err)

assert.Equal(t, `["1","2","3"]`, arr.Marshal())
assert.Equal(t, `["1","2","3"]`, clone.Marshal())
})

t.Run("empty presence converting test", func(t *testing.T) {
change, err := innerpresence.NewChangeFromJSON(`{"ChangeType":"put","Presence":{}}`)
assert.NoError(t, err)
Expand Down
21 changes: 20 additions & 1 deletion api/converter/from_bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func BytesToSnapshot(snapshot []byte) (*crdt.Object, *innerpresence.Map, error)
// BytesToObject creates an Object from the given byte array.
func BytesToObject(snapshot []byte) (*crdt.Object, error) {
if snapshot == nil {
return crdt.NewObject(crdt.NewElementRHT(), time.InitialTicket), nil
return nil, errors.New("snapshot should not be nil")
}

pbElem := &api.JSONElement{}
Expand All @@ -67,6 +67,25 @@ func BytesToObject(snapshot []byte) (*crdt.Object, error) {
return obj, nil
}

// BytesToArray creates a Array from the given byte array.
func BytesToArray(snapshot []byte) (*crdt.Array, error) {
if snapshot == nil {
return nil, errors.New("snapshot should not be nil")
}

pbArray := &api.JSONElement{}
if err := proto.Unmarshal(snapshot, pbArray); err != nil {
return nil, fmt.Errorf("unmarshal array: %w", err)
}

array, err := fromJSONArray(pbArray.GetJsonArray())
if err != nil {
return nil, err
}

return array, nil
}

// BytesToTree creates a Tree from the given byte array.
func BytesToTree(snapshot []byte) (*crdt.Tree, error) {
if snapshot == nil {
Expand Down
30 changes: 18 additions & 12 deletions api/converter/from_pb.go
Original file line number Diff line number Diff line change
Expand Up @@ -728,21 +728,27 @@ func fromTimeTicket(pbTicket *api.TimeTicket) (*time.Ticket, error) {
func fromElement(pbElement *api.JSONElementSimple) (crdt.Element, error) {
switch pbType := pbElement.Type; pbType {
case api.ValueType_VALUE_TYPE_JSON_OBJECT:
createdAt, err := fromTimeTicket(pbElement.CreatedAt)
if err != nil {
return nil, err
if pbElement.Value == nil {
createdAt, err := fromTimeTicket(pbElement.CreatedAt)
if err != nil {
return nil, err
}
return crdt.NewObject(
crdt.NewElementRHT(),
createdAt,
), nil
}
return crdt.NewObject(
crdt.NewElementRHT(),
createdAt,
), nil
return BytesToObject(pbElement.Value)
case api.ValueType_VALUE_TYPE_JSON_ARRAY:
createdAt, err := fromTimeTicket(pbElement.CreatedAt)
if err != nil {
return nil, err
if pbElement.Value == nil {
createdAt, err := fromTimeTicket(pbElement.CreatedAt)
if err != nil {
return nil, err
}
elements := crdt.NewRGATreeList()
return crdt.NewArray(elements, createdAt), nil
}
elements := crdt.NewRGATreeList()
return crdt.NewArray(elements, createdAt), nil
return BytesToArray(pbElement.Value)
case api.ValueType_VALUE_TYPE_NULL:
fallthrough
case api.ValueType_VALUE_TYPE_BOOLEAN:
Expand Down
13 changes: 13 additions & 0 deletions api/converter/to_bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ func ObjectToBytes(obj *crdt.Object) ([]byte, error) {
return bytes, nil
}

// ArrayToBytes converts the given array to byte array.
func ArrayToBytes(array *crdt.Array) ([]byte, error) {
pbArray, err := toJSONArray(array)
if err != nil {
return nil, err
}
bytes, err := proto.Marshal(pbArray)
if err != nil {
return nil, fmt.Errorf("marshal Array to bytes: %w", err)
}
return bytes, nil
}

// TreeToBytes converts the given tree to byte array.
func TreeToBytes(tree *crdt.Tree) ([]byte, error) {
pbTree := toTree(tree)
Expand Down
10 changes: 10 additions & 0 deletions api/converter/to_pb.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,14 +409,24 @@ func toTreeStyle(style *operations.TreeStyle) (*api.Operation_TreeStyle_, error)
func toJSONElementSimple(elem crdt.Element) (*api.JSONElementSimple, error) {
switch elem := elem.(type) {
case *crdt.Object:
bytes, err := ObjectToBytes(elem)
if err != nil {
return nil, err
}
return &api.JSONElementSimple{
Type: api.ValueType_VALUE_TYPE_JSON_OBJECT,
CreatedAt: ToTimeTicket(elem.CreatedAt()),
Value: bytes,
}, nil
case *crdt.Array:
bytes, err := ArrayToBytes(elem)
if err != nil {
return nil, err
}
return &api.JSONElementSimple{
Type: api.ValueType_VALUE_TYPE_JSON_ARRAY,
CreatedAt: ToTimeTicket(elem.CreatedAt()),
Value: bytes,
}, nil
case *crdt.Primitive:
pbValueType, err := toValueType(elem.ValueType())
Expand Down

0 comments on commit 10245f8

Please sign in to comment.