From 865904dbb22842035c3ba8925a6dba2f122ddc3e Mon Sep 17 00:00:00 2001 From: Youngteac Hong Date: Fri, 7 Jul 2023 14:03:08 +0900 Subject: [PATCH 1/5] Add presence to the callback of Document.Update --- api/converter/converter_test.go | 9 +- api/converter/from_pb.go | 12 +- api/converter/to_pb.go | 16 +- api/yorkie/v1/resources.pb.go | 405 ++++++++++-------- api/yorkie/v1/resources.proto | 4 +- client/client.go | 3 +- pkg/document/change/change.go | 20 +- pkg/document/change/context.go | 15 +- pkg/document/document.go | 24 +- pkg/document/document_test.go | 61 +-- pkg/document/internal_document.go | 47 +- pkg/document/presence/presence.go | 64 +++ pkg/document/presenceproxy/proxy.go | 46 ++ server/backend/database/change_info.go | 25 +- server/backend/database/memory/database.go | 5 + server/backend/database/mongo/client.go | 5 + .../backend/database/testcases/testcases.go | 7 +- server/packs/serverpacks.go | 7 + test/bench/document_bench_test.go | 60 +-- test/bench/grpc_bench_test.go | 12 +- test/bench/text_editing_bench_test.go | 5 +- test/helper/helper.go | 5 +- test/integration/admin_test.go | 3 +- test/integration/array_test.go | 35 +- test/integration/auth_webhook_test.go | 5 +- test/integration/client_test.go | 15 +- test/integration/counter_test.go | 11 +- test/integration/document_test.go | 39 +- test/integration/gc_test.go | 25 +- test/integration/history_test.go | 7 +- test/integration/object_test.go | 25 +- test/integration/peer_awareness_test.go | 32 ++ test/integration/primitive_test.go | 3 +- test/integration/retention_test.go | 12 +- test/integration/snapshot_test.go | 14 +- test/integration/text_test.go | 37 +- test/integration/tree_test.go | 39 +- 37 files changed, 730 insertions(+), 429 deletions(-) create mode 100644 pkg/document/presence/presence.go create mode 100644 pkg/document/presenceproxy/proxy.go diff --git a/api/converter/converter_test.go b/api/converter/converter_test.go index c247913a0..31da9e119 100644 --- a/api/converter/converter_test.go +++ b/api/converter/converter_test.go @@ -17,6 +17,7 @@ package converter_test import ( + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "math" "testing" gotime "time" @@ -41,14 +42,14 @@ func TestConverter(t *testing.T) { doc := document.New("d1") - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1").Edit(0, 0, "A") return nil }) assert.NoError(t, err) assert.Equal(t, `{"k1":[{"val":"A"}]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1").Edit(0, 0, "B") return nil }) @@ -66,7 +67,7 @@ func TestConverter(t *testing.T) { t.Run("snapshot test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { // an object and primitive types root.SetNewObject("k1"). SetNull("k1.0"). @@ -138,7 +139,7 @@ func TestConverter(t *testing.T) { t.Run("change pack test", func(t *testing.T) { d1 := document.New("d1") - err := d1.Update(func(root *json.Object) error { + err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { // an object and primitive types root.SetNewObject("k1"). SetBool("k1.1", true). diff --git a/api/converter/from_pb.go b/api/converter/from_pb.go index 0e12ca109..6cd896835 100644 --- a/api/converter/from_pb.go +++ b/api/converter/from_pb.go @@ -18,6 +18,7 @@ package converter import ( "fmt" + "github.com/yorkie-team/yorkie/pkg/document/presence" protoTypes "github.com/gogo/protobuf/types" @@ -135,7 +136,6 @@ func FromClient(pbClient *api.Client) (*types.Client, error) { // FromPresenceInfo converts the given Protobuf formats to model format. func FromPresenceInfo(pbPresence *api.Presence) types.PresenceInfo { return types.PresenceInfo{ - Clock: pbPresence.Clock, Presence: pbPresence.Data, } } @@ -192,6 +192,7 @@ func FromChanges(pbChanges []*api.Change) ([]*change.Change, error) { changeID, pbChange.Message, ops, + FromPresence(pbChange.Presence), )) } @@ -323,6 +324,15 @@ func FromOperations(pbOps []*api.Operation) ([]operations.Operation, error) { return ops, nil } +func FromPresence(pbPresence *api.Presence) *presence.InternalPresence { + if pbPresence == nil { + return nil + } + + p := presence.InternalPresence(pbPresence.GetData()) + return &p +} + func fromSet(pbSet *api.Operation_Set) (*operations.Set, error) { parentCreatedAt, err := fromTimeTicket(pbSet.ParentCreatedAt) if err != nil { diff --git a/api/converter/to_pb.go b/api/converter/to_pb.go index 350f3ed85..10cbdef0f 100644 --- a/api/converter/to_pb.go +++ b/api/converter/to_pb.go @@ -18,6 +18,7 @@ package converter import ( "fmt" + "github.com/yorkie-team/yorkie/pkg/document/presence" "reflect" protoTypes "github.com/gogo/protobuf/types" @@ -129,11 +130,21 @@ func ToClient(client types.Client) *api.Client { } } +// ToPresence converts the given model to Protobuf format. +func ToPresence(p *presence.InternalPresence) *api.Presence { + if p == nil { + return nil + } + + return &api.Presence{ + Data: *p, + } +} + // ToPresenceInfo converts the given model to Protobuf format. func ToPresenceInfo(info types.PresenceInfo) *api.Presence { return &api.Presence{ - Clock: info.Clock, - Data: info.Presence, + Data: info.Presence, } } @@ -278,6 +289,7 @@ func ToChanges(changes []*change.Change) ([]*api.Change, error) { Id: ToChangeID(c.ID()), Message: c.Message(), Operations: pbOperations, + Presence: ToPresence(c.Presence()), }) } diff --git a/api/yorkie/v1/resources.pb.go b/api/yorkie/v1/resources.pb.go index 177adde83..5842b72a2 100644 --- a/api/yorkie/v1/resources.pb.go +++ b/api/yorkie/v1/resources.pb.go @@ -208,6 +208,7 @@ type Change struct { Id *ChangeID `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` Operations []*Operation `protobuf:"bytes,3,rep,name=operations,proto3" json:"operations,omitempty"` + Presence *Presence `protobuf:"bytes,4,opt,name=presence,proto3" json:"presence,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -267,6 +268,13 @@ func (m *Change) GetOperations() []*Operation { return nil } +func (m *Change) GetPresence() *Presence { + if m != nil { + return m.Presence + } + return nil +} + type ChangeID struct { ClientSeq uint32 `protobuf:"varint,1,opt,name=client_seq,json=clientSeq,proto3" json:"client_seq,omitempty"` ServerSeq int64 `protobuf:"varint,2,opt,name=server_seq,json=serverSeq,proto3" json:"server_seq,omitempty"` @@ -2768,8 +2776,7 @@ func (m *DocumentSummary) GetUpdatedAt() *types.Timestamp { } type Presence struct { - Clock int32 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` - Data map[string]string `protobuf:"bytes,2,rep,name=data,proto3" json:"data,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Data map[string]string `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2808,13 +2815,6 @@ func (m *Presence) XXX_DiscardUnknown() { var xxx_messageInfo_Presence proto.InternalMessageInfo -func (m *Presence) GetClock() int32 { - if m != nil { - return m.Clock - } - return 0 -} - func (m *Presence) GetData() map[string]string { if m != nil { return m.Data @@ -3176,159 +3176,159 @@ func init() { func init() { proto.RegisterFile("yorkie/v1/resources.proto", fileDescriptor_36361b2f5d0f0896) } var fileDescriptor_36361b2f5d0f0896 = []byte{ - // 2432 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xcd, 0x8f, 0x1b, 0x49, - 0x15, 0x9f, 0xb6, 0xdb, 0x1f, 0xfd, 0x3c, 0xc9, 0x38, 0x35, 0x49, 0xd6, 0xf1, 0x6e, 0x26, 0x13, - 0x67, 0x09, 0x93, 0x04, 0x3c, 0xc9, 0x90, 0xf0, 0xb1, 0x61, 0x01, 0x8f, 0xdd, 0x9b, 0x99, 0xec, - 0xc4, 0x33, 0x6a, 0x7b, 0x12, 0xb2, 0x02, 0xb5, 0x7a, 0xba, 0x2b, 0x71, 0xef, 0xd8, 0xdd, 0xbd, - 0xdd, 0x65, 0x6f, 0x7c, 0x40, 0x42, 0x08, 0x24, 0x90, 0xb8, 0x70, 0xe3, 0x3f, 0x40, 0xfc, 0x09, - 0x7b, 0xe5, 0x80, 0xe0, 0x80, 0x40, 0x62, 0x25, 0xae, 0x10, 0x0e, 0x88, 0x23, 0x42, 0x70, 0x43, - 0x42, 0x55, 0xd5, 0xdd, 0x6e, 0xb7, 0xdb, 0x8e, 0x63, 0x46, 0xab, 0x44, 0xdc, 0x5c, 0x55, 0xbf, - 0x57, 0xf5, 0xbe, 0xeb, 0xbd, 0x2e, 0xc3, 0x85, 0xa1, 0xed, 0x1e, 0x9b, 0x78, 0x73, 0x70, 0x6b, - 0xd3, 0xc5, 0x9e, 0xdd, 0x77, 0x75, 0xec, 0x55, 0x1d, 0xd7, 0x26, 0x36, 0x92, 0xf8, 0x52, 0x75, - 0x70, 0xab, 0x7c, 0xe9, 0xa9, 0x6d, 0x3f, 0xed, 0xe2, 0x4d, 0xb6, 0x70, 0xd4, 0x7f, 0xb2, 0x49, - 0xcc, 0x1e, 0xf6, 0x88, 0xd6, 0x73, 0x38, 0xb6, 0xbc, 0x16, 0x07, 0x7c, 0xec, 0x6a, 0x8e, 0x83, - 0x5d, 0x7f, 0xaf, 0xca, 0xcf, 0x52, 0x00, 0xf5, 0x8e, 0x66, 0x3d, 0xc5, 0x07, 0x9a, 0x7e, 0x8c, - 0x2e, 0xc3, 0xb2, 0x61, 0xeb, 0xfd, 0x1e, 0xb6, 0x88, 0x7a, 0x8c, 0x87, 0x25, 0x61, 0x5d, 0xd8, - 0x90, 0x94, 0x42, 0x30, 0xf7, 0x3e, 0x1e, 0xa2, 0x3b, 0x00, 0x7a, 0x07, 0xeb, 0xc7, 0x8e, 0x6d, - 0x5a, 0xa4, 0x94, 0x5a, 0x17, 0x36, 0x0a, 0x5b, 0xe7, 0xaa, 0x21, 0x4b, 0xd5, 0x7a, 0xb8, 0xa8, - 0x44, 0x80, 0xa8, 0x0c, 0x79, 0xcf, 0xd2, 0x1c, 0xaf, 0x63, 0x93, 0x52, 0x7a, 0x5d, 0xd8, 0x58, - 0x56, 0xc2, 0x31, 0xba, 0x01, 0x39, 0x9d, 0xf1, 0xe0, 0x95, 0xc4, 0xf5, 0xf4, 0x46, 0x61, 0xeb, - 0xcc, 0xd8, 0x7e, 0x74, 0x45, 0x09, 0x10, 0xa8, 0x06, 0x67, 0x7a, 0xa6, 0xa5, 0x7a, 0x43, 0x4b, - 0xc7, 0x86, 0x4a, 0x4c, 0xfd, 0x18, 0x93, 0x52, 0x66, 0x82, 0x8d, 0xb6, 0xd9, 0xc3, 0x6d, 0xb6, - 0xa8, 0xac, 0xf4, 0x4c, 0xab, 0xc5, 0xe0, 0x7c, 0x02, 0x5d, 0x04, 0x30, 0x3d, 0xd5, 0xc5, 0x3d, - 0x7b, 0x80, 0x8d, 0x52, 0x76, 0x5d, 0xd8, 0xc8, 0x2b, 0x92, 0xe9, 0x29, 0x7c, 0xa2, 0xf2, 0x3d, - 0xc8, 0xf2, 0x43, 0xd1, 0x15, 0x48, 0x99, 0x06, 0x53, 0x42, 0x61, 0x6b, 0x75, 0x82, 0xa7, 0xdd, - 0x86, 0x92, 0x32, 0x0d, 0x54, 0x82, 0x5c, 0x0f, 0x7b, 0x9e, 0xf6, 0x14, 0x33, 0x6d, 0x48, 0x4a, - 0x30, 0x44, 0xb7, 0x01, 0x6c, 0x07, 0xbb, 0x1a, 0x31, 0x6d, 0xcb, 0x2b, 0xa5, 0x99, 0x68, 0x67, - 0x23, 0xdb, 0xec, 0x07, 0x8b, 0x4a, 0x04, 0x57, 0xf9, 0x91, 0x00, 0xf9, 0xe0, 0x00, 0xca, 0xaa, - 0xde, 0x35, 0xa9, 0x39, 0x3c, 0xfc, 0x11, 0xe3, 0xe4, 0x94, 0x22, 0xf1, 0x99, 0x16, 0xfe, 0x08, - 0x5d, 0x06, 0xf0, 0xb0, 0x3b, 0xc0, 0x2e, 0x5b, 0xa6, 0xc7, 0xa7, 0xb7, 0x53, 0x37, 0x05, 0x45, - 0xe2, 0xb3, 0x14, 0xf2, 0x16, 0xe4, 0xba, 0x5a, 0xcf, 0xb1, 0x5d, 0xae, 0x77, 0xbe, 0x1e, 0x4c, - 0xa1, 0x0b, 0x90, 0xd7, 0x74, 0x62, 0xbb, 0xaa, 0x69, 0x94, 0x44, 0x66, 0x96, 0x1c, 0x1b, 0xef, - 0x1a, 0x95, 0xdf, 0x96, 0x41, 0x0a, 0x39, 0x44, 0x5f, 0x80, 0xb4, 0x87, 0x89, 0xaf, 0x8b, 0x52, - 0x92, 0x10, 0xd5, 0x16, 0x26, 0x3b, 0x4b, 0x0a, 0x85, 0x51, 0xb4, 0x66, 0x18, 0xbe, 0x77, 0x24, - 0xa3, 0x6b, 0x86, 0x41, 0xd1, 0x9a, 0x61, 0xa0, 0x4d, 0x10, 0xa9, 0xe6, 0x19, 0x7f, 0x85, 0xad, - 0x0b, 0x89, 0xf0, 0x07, 0xf6, 0x00, 0xef, 0x2c, 0x29, 0x0c, 0x88, 0xee, 0x40, 0x96, 0x5b, 0x8f, - 0xf1, 0x5c, 0xd8, 0x7a, 0x33, 0x91, 0x84, 0xdb, 0x73, 0x67, 0x49, 0xf1, 0xc1, 0xf4, 0x1c, 0x6c, - 0x98, 0x81, 0xb7, 0x24, 0x9f, 0x23, 0x1b, 0x26, 0x95, 0x82, 0x01, 0xe9, 0x39, 0x1e, 0xee, 0x62, - 0x9d, 0x30, 0x27, 0x99, 0x76, 0x4e, 0x8b, 0x41, 0xe8, 0x39, 0x1c, 0x8c, 0xb6, 0x20, 0xe3, 0x91, - 0x61, 0x17, 0x97, 0x72, 0x8c, 0xaa, 0x9c, 0x4c, 0x45, 0x11, 0x3b, 0x4b, 0x0a, 0x87, 0xa2, 0xbb, - 0x90, 0x37, 0x2d, 0xdd, 0xc5, 0x9a, 0x87, 0x4b, 0x79, 0x46, 0x76, 0x31, 0x91, 0x6c, 0xd7, 0x07, - 0xed, 0x2c, 0x29, 0x21, 0x01, 0xfa, 0x3a, 0x48, 0xc4, 0xc5, 0x58, 0x65, 0xd2, 0x49, 0x33, 0xa8, - 0xdb, 0x2e, 0xc6, 0xbe, 0x84, 0x79, 0xe2, 0xff, 0x46, 0xdf, 0x04, 0x60, 0xd4, 0x9c, 0x67, 0x60, - 0xe4, 0x6b, 0x53, 0xc9, 0x03, 0xbe, 0xd9, 0x89, 0x6c, 0x50, 0xfe, 0xb5, 0x00, 0xe9, 0x16, 0x26, - 0x34, 0x34, 0x1d, 0xcd, 0xa5, 0xce, 0x4a, 0xf9, 0x22, 0xd8, 0x50, 0xb5, 0xc0, 0x63, 0xa6, 0x85, - 0x26, 0xc7, 0xd7, 0x39, 0xbc, 0x46, 0x50, 0x11, 0xd2, 0x34, 0xef, 0xf0, 0x40, 0xa2, 0x3f, 0xa9, - 0x32, 0x07, 0x5a, 0xb7, 0x1f, 0x78, 0xc7, 0x5b, 0x91, 0x8d, 0xee, 0xb7, 0xf6, 0x9b, 0x72, 0x17, - 0xd3, 0xcc, 0xd4, 0x32, 0x7b, 0x4e, 0x17, 0x2b, 0x1c, 0x8a, 0xbe, 0x0c, 0x05, 0xfc, 0x0c, 0xeb, - 0x7d, 0x9f, 0x05, 0x71, 0x16, 0x0b, 0x10, 0x20, 0x6b, 0xa4, 0xfc, 0x4f, 0x01, 0xd2, 0x35, 0xc3, - 0x38, 0x09, 0x41, 0xde, 0x85, 0x15, 0xc7, 0xc5, 0x83, 0xe8, 0x06, 0xa9, 0x59, 0x1b, 0x9c, 0xa2, - 0xe8, 0x11, 0xf9, 0x67, 0x29, 0xf5, 0xbf, 0x05, 0x10, 0x69, 0x78, 0xbd, 0x02, 0x62, 0xdf, 0x06, - 0x88, 0x50, 0xa6, 0x67, 0x51, 0x4a, 0x7a, 0x48, 0xb5, 0xa8, 0xe0, 0x9f, 0x08, 0x90, 0xe5, 0x49, - 0xe2, 0x24, 0x44, 0x1f, 0xe7, 0x3d, 0xb5, 0x18, 0xef, 0xe9, 0x79, 0x79, 0xff, 0x95, 0x08, 0x22, - 0x8b, 0xde, 0x13, 0xe0, 0xfc, 0x3a, 0x88, 0x4f, 0x5c, 0xbb, 0xe7, 0xf3, 0x7c, 0x3e, 0x4a, 0x85, - 0x9f, 0x91, 0xa6, 0x6d, 0xe0, 0x03, 0xdb, 0x53, 0x18, 0x06, 0x5d, 0x85, 0x14, 0xb1, 0x7d, 0x36, - 0xa7, 0x21, 0x53, 0xc4, 0x46, 0x1d, 0x78, 0x63, 0xc4, 0x8f, 0xda, 0xd3, 0x1c, 0xf5, 0x68, 0xa8, - 0xb2, 0xab, 0xc5, 0xbf, 0xe3, 0xb7, 0xa6, 0xa6, 0xdf, 0x6a, 0xc8, 0xd9, 0x03, 0xcd, 0xd9, 0x1e, - 0xd6, 0x28, 0x91, 0x6c, 0x11, 0x77, 0xa8, 0xac, 0xea, 0x93, 0x2b, 0xf4, 0xfe, 0xd5, 0x6d, 0x8b, - 0x60, 0x8b, 0x27, 0x76, 0x49, 0x09, 0x86, 0x71, 0xdd, 0x66, 0xe7, 0xd4, 0x2d, 0xda, 0x05, 0xd0, - 0x08, 0x71, 0xcd, 0xa3, 0x3e, 0xc1, 0x5e, 0x29, 0xc7, 0xd8, 0xbd, 0x36, 0x9d, 0xdd, 0x5a, 0x88, - 0xe5, 0x5c, 0x46, 0x88, 0xcb, 0xdf, 0x85, 0xd2, 0x34, 0x69, 0x82, 0x5c, 0x27, 0x8c, 0x72, 0xdd, - 0x8d, 0x20, 0xea, 0x67, 0x7a, 0x0f, 0xc7, 0xbc, 0x93, 0xfa, 0xaa, 0x50, 0x7e, 0x17, 0x56, 0x62, - 0xa7, 0x27, 0xec, 0x7a, 0x36, 0xba, 0xab, 0x14, 0x25, 0xff, 0x93, 0x00, 0x59, 0x7e, 0x7b, 0xbd, - 0xaa, 0x6e, 0xb4, 0x68, 0x68, 0xff, 0x25, 0x05, 0x19, 0x76, 0x39, 0xbd, 0xaa, 0x82, 0xdd, 0x1f, - 0xf3, 0x31, 0x1e, 0x12, 0xd7, 0xa7, 0x17, 0x0a, 0xb3, 0x9c, 0x2c, 0xae, 0xa4, 0xcc, 0xbc, 0x4a, - 0xfa, 0x1f, 0xbd, 0xe7, 0x13, 0x01, 0xf2, 0x41, 0x39, 0x72, 0x12, 0x6a, 0xde, 0x1a, 0xf7, 0xfe, - 0x45, 0xee, 0xbc, 0xb9, 0xd3, 0xe7, 0xf7, 0x53, 0x90, 0x0f, 0x8a, 0xa1, 0x93, 0xe0, 0xfd, 0xea, - 0x98, 0x8b, 0xa0, 0x28, 0x95, 0x8b, 0x23, 0xee, 0x51, 0x89, 0xb8, 0x47, 0x12, 0x8a, 0xba, 0xc6, - 0x17, 0x47, 0x09, 0x8d, 0xfb, 0xc5, 0x6a, 0x0c, 0x48, 0xfd, 0x68, 0x6a, 0x96, 0xcb, 0xbc, 0x44, - 0x88, 0x48, 0x61, 0x41, 0xf7, 0xaa, 0xe9, 0xa0, 0x99, 0x10, 0x1e, 0xd5, 0xd9, 0x35, 0xe9, 0x2b, - 0x18, 0x22, 0xdb, 0x59, 0x10, 0x8f, 0x6c, 0x63, 0x58, 0xf9, 0x87, 0x00, 0x67, 0x26, 0x7c, 0x38, - 0x56, 0x31, 0x08, 0x73, 0x56, 0x0c, 0x37, 0x21, 0xcf, 0xfa, 0xd4, 0x17, 0x56, 0x19, 0x39, 0x06, - 0xe3, 0x95, 0x89, 0xdf, 0xec, 0xbe, 0xb8, 0xaa, 0xf2, 0x81, 0x35, 0x82, 0x36, 0x40, 0x24, 0x43, - 0x87, 0xb7, 0x58, 0xa7, 0xc7, 0xfa, 0xd6, 0x87, 0x54, 0xbe, 0xf6, 0xd0, 0xc1, 0x0a, 0x43, 0x8c, - 0xe4, 0xcf, 0xb0, 0x0e, 0x92, 0x0f, 0x2a, 0xbf, 0x3c, 0x05, 0x85, 0x88, 0xcc, 0xa8, 0x01, 0x85, - 0x0f, 0x3d, 0xdb, 0x52, 0xed, 0xa3, 0x0f, 0x69, 0x47, 0xc5, 0xc5, 0xbd, 0x9c, 0x1c, 0xe4, 0xec, - 0xf7, 0x3e, 0x03, 0xee, 0x2c, 0x29, 0x40, 0xe9, 0xf8, 0x08, 0xd5, 0x80, 0x8d, 0x54, 0xcd, 0x75, - 0xb5, 0xa1, 0x2f, 0xff, 0xfa, 0x8c, 0x4d, 0x6a, 0x14, 0x47, 0xdb, 0x15, 0x4a, 0xc5, 0x06, 0xe8, - 0x5b, 0x20, 0x39, 0xae, 0xd9, 0x33, 0x89, 0x19, 0xf6, 0x9c, 0xd3, 0x76, 0x38, 0x08, 0x70, 0x74, - 0x87, 0x90, 0x08, 0xdd, 0x02, 0x91, 0xe0, 0x67, 0x81, 0x1b, 0xbd, 0x39, 0x85, 0x98, 0xa6, 0x7c, - 0xda, 0x4a, 0x52, 0x28, 0x7a, 0x87, 0x06, 0x75, 0xdf, 0x22, 0xd8, 0xf5, 0xeb, 0x90, 0xb5, 0x29, - 0x54, 0x75, 0x8e, 0xda, 0x59, 0x52, 0x02, 0x02, 0x76, 0x9c, 0x8b, 0x83, 0x76, 0x72, 0xea, 0x71, - 0x2e, 0x66, 0x1d, 0x32, 0x85, 0x96, 0x3f, 0x15, 0x00, 0x46, 0x3a, 0x44, 0x1b, 0x90, 0xb1, 0x6c, - 0x03, 0x7b, 0x25, 0x81, 0x45, 0x52, 0x34, 0xea, 0x94, 0x9d, 0x36, 0xcb, 0x27, 0x1c, 0xb0, 0x60, - 0x15, 0x1b, 0xf5, 0xc9, 0xf4, 0x02, 0x3e, 0x29, 0xce, 0xe7, 0x93, 0xe5, 0x3f, 0x0a, 0x20, 0x85, - 0x56, 0x9d, 0x29, 0xd5, 0xbd, 0xda, 0xeb, 0x23, 0xd5, 0xdf, 0x05, 0x90, 0x42, 0x4f, 0x0b, 0xe3, - 0x4e, 0x98, 0x3f, 0xee, 0x52, 0x91, 0xb8, 0x5b, 0xb0, 0x87, 0x8a, 0xca, 0x2a, 0x2e, 0x20, 0x6b, - 0x66, 0x4e, 0x59, 0x7f, 0x2f, 0x80, 0x48, 0x03, 0x03, 0x5d, 0x1b, 0x37, 0xde, 0x6a, 0x42, 0xad, - 0xf4, 0x7a, 0x58, 0xef, 0x6f, 0x02, 0xe4, 0xfc, 0xa0, 0xfd, 0x7f, 0xb0, 0x9d, 0x8b, 0xf1, 0x4c, - 0xdb, 0x05, 0xf5, 0xc9, 0xeb, 0x60, 0xbb, 0xf0, 0x7e, 0x7e, 0x00, 0x39, 0x3f, 0x0f, 0x26, 0x5c, - 0xef, 0x37, 0x21, 0x87, 0x79, 0x8e, 0x4d, 0xe8, 0x00, 0x22, 0x19, 0x58, 0x09, 0x60, 0x15, 0x1d, - 0x72, 0x7e, 0x02, 0xa2, 0x45, 0x91, 0x45, 0xaf, 0x0a, 0x61, 0xa2, 0xdc, 0x09, 0x52, 0x14, 0x5b, - 0x5f, 0xe0, 0x90, 0x87, 0x90, 0xa7, 0xf4, 0xb4, 0x3c, 0x19, 0x79, 0x93, 0x10, 0xa9, 0x40, 0xa8, - 0x4e, 0xfa, 0x8e, 0x31, 0x9f, 0xee, 0x7d, 0x60, 0x8d, 0x54, 0x7e, 0x47, 0x4b, 0x63, 0x3f, 0x02, - 0xd1, 0xe7, 0x22, 0x5f, 0xc0, 0xcf, 0x25, 0x84, 0xa8, 0xff, 0x0d, 0x3c, 0xb1, 0x02, 0x5a, 0xb0, - 0xee, 0xb8, 0x03, 0x05, 0xd3, 0xf2, 0x54, 0xf6, 0x19, 0xc9, 0xff, 0x2a, 0x3d, 0xf5, 0x6c, 0xc9, - 0xb4, 0xbc, 0x03, 0x17, 0x0f, 0x76, 0x0d, 0x54, 0x1f, 0xab, 0x18, 0x33, 0xcc, 0x31, 0xaf, 0x24, - 0x50, 0xcd, 0x6c, 0xd7, 0x95, 0x79, 0xca, 0xbd, 0x6b, 0xe3, 0x7d, 0x4a, 0xd4, 0xfb, 0x03, 0x83, - 0x44, 0x6a, 0xc0, 0xca, 0x07, 0x00, 0x23, 0x8e, 0x17, 0xac, 0xf9, 0xce, 0x43, 0xd6, 0x7e, 0xf2, - 0xc4, 0xc3, 0xdc, 0x8a, 0x19, 0xc5, 0x1f, 0x55, 0xfe, 0xe5, 0xb7, 0x31, 0xcc, 0x56, 0x6f, 0x43, - 0xda, 0xb1, 0xbd, 0x04, 0x4f, 0x0b, 0x0a, 0x6b, 0xba, 0x8c, 0x90, 0x9f, 0xa2, 0xb8, 0xa5, 0x62, - 0xc9, 0x28, 0x3d, 0xdd, 0x7c, 0x73, 0x86, 0x14, 0xba, 0x0d, 0xcb, 0xa1, 0xf9, 0x28, 0x3b, 0x99, - 0xa9, 0xec, 0x80, 0x6f, 0xbc, 0x03, 0xdb, 0xa3, 0x1c, 0x18, 0xd8, 0x21, 0x1d, 0x56, 0x1c, 0x65, - 0x14, 0x3e, 0x88, 0xd9, 0x34, 0x37, 0x69, 0x53, 0x5f, 0xf4, 0xcf, 0xdc, 0xa6, 0x8f, 0x20, 0xe7, - 0x4b, 0x71, 0xc2, 0x06, 0xed, 0x81, 0x78, 0xe8, 0x61, 0x17, 0x9d, 0x0e, 0xe3, 0x4e, 0x62, 0x01, - 0x56, 0x86, 0x7c, 0xdf, 0xc3, 0xae, 0xa5, 0xf5, 0x02, 0xcb, 0x85, 0x63, 0xf4, 0xb5, 0x84, 0x4b, - 0xa3, 0x5c, 0xe5, 0x0f, 0x7f, 0xd5, 0xe0, 0xe1, 0x8f, 0xf1, 0xc1, 0x5e, 0x06, 0x23, 0x6c, 0x54, - 0xfe, 0x93, 0x82, 0xdc, 0x81, 0x6b, 0xb3, 0x1a, 0x31, 0x7e, 0x24, 0x02, 0x31, 0x72, 0x1c, 0xfb, - 0x8d, 0x2e, 0x02, 0x38, 0xfd, 0xa3, 0xae, 0xa9, 0xb3, 0xd7, 0x41, 0xee, 0x2d, 0x12, 0x9f, 0x79, - 0x1f, 0x0f, 0xe9, 0xb2, 0x87, 0x75, 0x17, 0xf3, 0xc7, 0x43, 0x91, 0x2f, 0xf3, 0x19, 0xba, 0xbc, - 0x01, 0x45, 0xad, 0x4f, 0x3a, 0xea, 0xc7, 0xf8, 0xa8, 0x63, 0xdb, 0xc7, 0x6a, 0xdf, 0xed, 0xfa, - 0x9f, 0xec, 0x4e, 0xd3, 0xf9, 0x47, 0x7c, 0xfa, 0xd0, 0xed, 0xa2, 0x9b, 0x70, 0x76, 0x0c, 0xd9, - 0xc3, 0xa4, 0x63, 0x1b, 0x5e, 0x29, 0xbb, 0x9e, 0xde, 0x90, 0x14, 0x14, 0x41, 0x3f, 0xe0, 0x2b, - 0xe8, 0x1b, 0xf0, 0xa6, 0xff, 0x50, 0x66, 0x60, 0x4d, 0x27, 0xe6, 0x40, 0x23, 0x58, 0x25, 0x1d, - 0x17, 0x7b, 0x1d, 0xbb, 0x6b, 0xb0, 0xd2, 0x59, 0x52, 0x2e, 0x70, 0x48, 0x23, 0x44, 0xb4, 0x03, - 0x40, 0x4c, 0x89, 0xf9, 0x97, 0x50, 0x22, 0x25, 0x8d, 0xa4, 0x59, 0xe9, 0xc5, 0xa4, 0xa3, 0x5c, - 0xfb, 0xe3, 0x34, 0x9c, 0x3f, 0xa4, 0x23, 0xed, 0xa8, 0x8b, 0x7d, 0x43, 0xbc, 0x67, 0xe2, 0xae, - 0xe1, 0xa1, 0x9b, 0xbe, 0xfa, 0x05, 0xff, 0x63, 0x48, 0x7c, 0xbf, 0x16, 0x71, 0x4d, 0xeb, 0x29, - 0x2b, 0x2b, 0x7c, 0xe3, 0xbc, 0x97, 0xa0, 0xde, 0xd4, 0x1c, 0xd4, 0x71, 0xe5, 0x3f, 0x99, 0xa2, - 0x7c, 0xee, 0x59, 0xb7, 0x23, 0xbe, 0x9d, 0xcc, 0x7a, 0xb5, 0x36, 0x61, 0x9e, 0x44, 0x93, 0x7d, - 0x67, 0xb6, 0xc9, 0xc4, 0x39, 0x58, 0x9f, 0x6e, 0xd0, 0x72, 0x15, 0xd0, 0x24, 0x1f, 0xfc, 0xb1, - 0x96, 0x8b, 0x23, 0x30, 0x5f, 0x0a, 0x86, 0x95, 0x1f, 0xa4, 0x60, 0xa5, 0xe1, 0xbf, 0x73, 0xb7, - 0xfa, 0xbd, 0x9e, 0xe6, 0x0e, 0x27, 0x42, 0x62, 0xf2, 0x75, 0x2a, 0xfe, 0xac, 0x2d, 0x45, 0x9e, - 0xb5, 0xc7, 0x5d, 0x4a, 0x7c, 0x19, 0x97, 0xba, 0x0b, 0x05, 0x4d, 0xd7, 0xb1, 0xe7, 0x45, 0x0b, - 0xb4, 0x59, 0xb4, 0x10, 0xc0, 0x27, 0xfc, 0x31, 0xfb, 0x32, 0xfe, 0xf8, 0x53, 0x01, 0xf2, 0x07, - 0x2e, 0xf6, 0xb0, 0xa5, 0xb3, 0x5b, 0x41, 0xef, 0xda, 0xfa, 0x31, 0x53, 0x40, 0x46, 0xe1, 0x03, - 0xda, 0x8c, 0x52, 0xa3, 0x97, 0x52, 0x2c, 0x1b, 0x47, 0x9f, 0x19, 0x03, 0xc2, 0x6a, 0x43, 0x23, - 0x1a, 0xcf, 0xc3, 0x0c, 0x5a, 0xfe, 0x0a, 0x48, 0xe1, 0xd4, 0xcb, 0x7c, 0x3e, 0xa9, 0xec, 0x42, - 0xb6, 0xce, 0x0c, 0x1c, 0xb1, 0xc4, 0x32, 0xb3, 0xc4, 0x26, 0xe4, 0x1d, 0xff, 0xb8, 0x84, 0x94, - 0x1d, 0x70, 0xa2, 0x84, 0xa0, 0x4a, 0x13, 0x60, 0xf4, 0xcf, 0x84, 0xd8, 0xbb, 0xb9, 0x90, 0xf4, - 0x6e, 0x3e, 0xfe, 0xf2, 0x9e, 0x8a, 0xbd, 0xbc, 0x57, 0x7e, 0x28, 0x40, 0x21, 0xf2, 0x4d, 0xf7, - 0x64, 0xaf, 0x01, 0xf4, 0x79, 0x58, 0x71, 0x71, 0x57, 0xa3, 0xfd, 0xa0, 0xea, 0x03, 0xd2, 0x0c, - 0x70, 0x3a, 0x98, 0xde, 0xe7, 0xf7, 0x85, 0x0e, 0x30, 0xda, 0x39, 0xfa, 0xd6, 0x2f, 0x4c, 0xbe, - 0xf5, 0xbf, 0x05, 0x92, 0x81, 0xbb, 0xb4, 0xcd, 0xc4, 0x6e, 0x20, 0x50, 0x38, 0x31, 0xf6, 0x4f, - 0x80, 0xf4, 0xf8, 0x3f, 0x01, 0x7e, 0x22, 0x40, 0xbe, 0x61, 0xeb, 0xf2, 0x80, 0x5a, 0xe2, 0xc6, - 0x58, 0x8b, 0xf3, 0x46, 0x44, 0xc4, 0x00, 0x12, 0xe9, 0x72, 0x36, 0x81, 0xdf, 0x0e, 0x5e, 0xc7, - 0x3f, 0x32, 0xf6, 0xdf, 0x0e, 0xa6, 0x4e, 0x65, 0x84, 0x41, 0x97, 0x20, 0xfc, 0xb3, 0x49, 0xc0, - 0x88, 0xa4, 0x40, 0x30, 0xb5, 0x6b, 0x5c, 0xff, 0x34, 0x05, 0x52, 0xd8, 0x4b, 0xa1, 0x55, 0x58, - 0x79, 0x58, 0xdb, 0x3b, 0x94, 0xd5, 0xf6, 0xe3, 0x03, 0x59, 0x6d, 0x1e, 0xee, 0xed, 0x15, 0x97, - 0xd0, 0x79, 0x40, 0x91, 0xc9, 0xed, 0xfd, 0xfd, 0x3d, 0xb9, 0xd6, 0x2c, 0x0a, 0xb1, 0xf9, 0xdd, - 0x66, 0x5b, 0xbe, 0x27, 0x2b, 0xc5, 0x54, 0x6c, 0x93, 0xbd, 0xfd, 0xe6, 0xbd, 0x62, 0x1a, 0x9d, - 0x83, 0x33, 0x91, 0xc9, 0xc6, 0xfe, 0xe1, 0xf6, 0x9e, 0x5c, 0x14, 0x63, 0xd3, 0xad, 0xb6, 0xb2, - 0xdb, 0xbc, 0x57, 0xcc, 0xa0, 0xb3, 0x50, 0x8c, 0x1e, 0xf9, 0xb8, 0x2d, 0xb7, 0x8a, 0xd9, 0xd8, - 0xc6, 0x8d, 0x5a, 0x5b, 0x2e, 0xe6, 0x50, 0x19, 0xce, 0x47, 0x26, 0x69, 0x65, 0xaf, 0xee, 0x6f, - 0xdf, 0x97, 0xeb, 0xed, 0x62, 0x1e, 0x5d, 0x80, 0x73, 0xf1, 0xb5, 0x9a, 0xa2, 0xd4, 0x1e, 0x17, - 0xa5, 0xd8, 0x5e, 0x6d, 0xf9, 0xdb, 0xed, 0x22, 0xc4, 0xf6, 0xf2, 0x25, 0x52, 0xeb, 0xcd, 0x76, - 0xb1, 0x80, 0xde, 0x80, 0xd5, 0x98, 0x54, 0x6c, 0x61, 0x39, 0xbe, 0x93, 0x22, 0xcb, 0xc5, 0x53, - 0xd7, 0x7f, 0x21, 0xc0, 0x72, 0xd4, 0x7e, 0xe8, 0x6d, 0x58, 0x6f, 0xec, 0xd7, 0x55, 0xf9, 0xa1, - 0xdc, 0x6c, 0x07, 0x3a, 0xa8, 0x1f, 0x3e, 0x90, 0x9b, 0xed, 0x96, 0x5a, 0xdf, 0xa9, 0x35, 0xef, - 0xc9, 0x8d, 0xe2, 0xd2, 0x4c, 0xd4, 0xa3, 0x5a, 0xbb, 0xbe, 0x23, 0x37, 0x8a, 0x02, 0xba, 0x0a, - 0x95, 0xa9, 0xa8, 0xc3, 0x66, 0x80, 0x4b, 0xa1, 0x2b, 0x70, 0x29, 0x86, 0x3b, 0x50, 0xe4, 0x96, - 0xdc, 0xac, 0xcb, 0xe1, 0x91, 0xe9, 0xed, 0x1b, 0xbf, 0x79, 0xbe, 0x26, 0xfc, 0xe1, 0xf9, 0x9a, - 0xf0, 0xe7, 0xe7, 0x6b, 0xc2, 0xcf, 0xff, 0xba, 0xb6, 0x04, 0x67, 0x0c, 0x3c, 0x08, 0x9c, 0x4a, - 0x73, 0xcc, 0xea, 0xe0, 0xd6, 0x81, 0xf0, 0x81, 0x58, 0xbd, 0x3b, 0xb8, 0x75, 0x94, 0x65, 0xe9, - 0xee, 0x4b, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x01, 0xec, 0x60, 0x4e, 0x25, 0x00, 0x00, + // 2424 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xcf, 0x8f, 0x1b, 0x49, + 0xf5, 0x9f, 0xb6, 0xdb, 0x3f, 0xfa, 0x79, 0x92, 0x71, 0x6a, 0x92, 0xac, 0xe3, 0x4d, 0x26, 0x13, + 0x67, 0xbf, 0xf9, 0x4e, 0x12, 0xf0, 0x24, 0x43, 0xc2, 0x8f, 0x0d, 0x0b, 0x78, 0xec, 0xde, 0xcc, + 0x64, 0x27, 0x9e, 0x51, 0xdb, 0x93, 0x90, 0x15, 0xa8, 0xd5, 0xd3, 0x5d, 0x89, 0x7b, 0xc7, 0xee, + 0xee, 0xed, 0x2e, 0x7b, 0xe3, 0x1b, 0x42, 0x20, 0xc1, 0x91, 0x1b, 0xff, 0x01, 0xe2, 0xc2, 0x7d, + 0xaf, 0x1c, 0x10, 0x1c, 0x10, 0x48, 0xac, 0xc4, 0x15, 0xc2, 0x01, 0x71, 0x44, 0x08, 0x6e, 0x48, + 0xa8, 0xaa, 0xba, 0xdb, 0xed, 0x76, 0xdb, 0x71, 0xcc, 0x68, 0x95, 0x88, 0x9b, 0xab, 0xea, 0xf3, + 0xaa, 0xde, 0xef, 0x7a, 0xd5, 0xcf, 0x70, 0x61, 0x68, 0xbb, 0xc7, 0x26, 0xde, 0x1c, 0xdc, 0xde, + 0x74, 0xb1, 0x67, 0xf7, 0x5d, 0x1d, 0x7b, 0x55, 0xc7, 0xb5, 0x89, 0x8d, 0x24, 0xbe, 0x54, 0x1d, + 0xdc, 0x2e, 0x5f, 0x7e, 0x66, 0xdb, 0xcf, 0xba, 0x78, 0x93, 0x2d, 0x1c, 0xf5, 0x9f, 0x6e, 0x12, + 0xb3, 0x87, 0x3d, 0xa2, 0xf5, 0x1c, 0x8e, 0x2d, 0xaf, 0xc5, 0x01, 0x9f, 0xb8, 0x9a, 0xe3, 0x60, + 0xd7, 0xdf, 0xab, 0xf2, 0x93, 0x14, 0x40, 0xbd, 0xa3, 0x59, 0xcf, 0xf0, 0x81, 0xa6, 0x1f, 0xa3, + 0x2b, 0xb0, 0x6c, 0xd8, 0x7a, 0xbf, 0x87, 0x2d, 0xa2, 0x1e, 0xe3, 0x61, 0x49, 0x58, 0x17, 0x36, + 0x24, 0xa5, 0x10, 0xcc, 0x7d, 0x80, 0x87, 0xe8, 0x2e, 0x80, 0xde, 0xc1, 0xfa, 0xb1, 0x63, 0x9b, + 0x16, 0x29, 0xa5, 0xd6, 0x85, 0x8d, 0xc2, 0xd6, 0xb9, 0x6a, 0xc8, 0x52, 0xb5, 0x1e, 0x2e, 0x2a, + 0x11, 0x20, 0x2a, 0x43, 0xde, 0xb3, 0x34, 0xc7, 0xeb, 0xd8, 0xa4, 0x94, 0x5e, 0x17, 0x36, 0x96, + 0x95, 0x70, 0x8c, 0x6e, 0x42, 0x4e, 0x67, 0x3c, 0x78, 0x25, 0x71, 0x3d, 0xbd, 0x51, 0xd8, 0x3a, + 0x33, 0xb6, 0x1f, 0x5d, 0x51, 0x02, 0x04, 0xaa, 0xc1, 0x99, 0x9e, 0x69, 0xa9, 0xde, 0xd0, 0xd2, + 0xb1, 0xa1, 0x12, 0x53, 0x3f, 0xc6, 0xa4, 0x94, 0x99, 0x60, 0xa3, 0x6d, 0xf6, 0x70, 0x9b, 0x2d, + 0x2a, 0x2b, 0x3d, 0xd3, 0x6a, 0x31, 0x38, 0x9f, 0x40, 0x97, 0x00, 0x4c, 0x4f, 0x75, 0x71, 0xcf, + 0x1e, 0x60, 0xa3, 0x94, 0x5d, 0x17, 0x36, 0xf2, 0x8a, 0x64, 0x7a, 0x0a, 0x9f, 0xa8, 0xfc, 0x42, + 0x80, 0x2c, 0x3f, 0x15, 0x5d, 0x85, 0x94, 0x69, 0x30, 0x2d, 0x14, 0xb6, 0x56, 0x27, 0x98, 0xda, + 0x6d, 0x28, 0x29, 0xd3, 0x40, 0x25, 0xc8, 0xf5, 0xb0, 0xe7, 0x69, 0xcf, 0x30, 0x53, 0x87, 0xa4, + 0x04, 0x43, 0x74, 0x07, 0xc0, 0x76, 0xb0, 0xab, 0x11, 0xd3, 0xb6, 0xbc, 0x52, 0x9a, 0xc9, 0x76, + 0x36, 0xb2, 0xcd, 0x7e, 0xb0, 0xa8, 0x44, 0x70, 0x68, 0x13, 0xf2, 0x8e, 0x8b, 0x3d, 0x6c, 0xe9, + 0xb8, 0x24, 0x4e, 0x1c, 0x7d, 0xe0, 0x2f, 0x29, 0x21, 0xa8, 0xf2, 0x43, 0x01, 0xf2, 0x01, 0x47, + 0x54, 0x38, 0xbd, 0x6b, 0x52, 0x03, 0x7a, 0xf8, 0x63, 0xc6, 0xfa, 0x29, 0x45, 0xe2, 0x33, 0x2d, + 0xfc, 0x31, 0xba, 0x02, 0xe0, 0x61, 0x77, 0x80, 0x5d, 0xb6, 0x4c, 0xf9, 0x4d, 0x6f, 0xa7, 0x6e, + 0x09, 0x8a, 0xc4, 0x67, 0x29, 0xe4, 0x22, 0xe4, 0xba, 0x5a, 0xcf, 0xb1, 0x5d, 0x6e, 0x29, 0xbe, + 0x1e, 0x4c, 0xa1, 0x0b, 0x90, 0xd7, 0x74, 0x62, 0xbb, 0xaa, 0x69, 0x30, 0xee, 0x96, 0x95, 0x1c, + 0x1b, 0xef, 0x1a, 0x95, 0xdf, 0x94, 0x41, 0x0a, 0x45, 0x42, 0x5f, 0x80, 0xb4, 0x87, 0x89, 0xaf, + 0xbc, 0x52, 0x92, 0xd4, 0xd5, 0x16, 0x26, 0x3b, 0x4b, 0x0a, 0x85, 0x51, 0xb4, 0x66, 0x18, 0xbe, + 0x3f, 0x25, 0xa3, 0x6b, 0x86, 0x41, 0xd1, 0x9a, 0x61, 0xa0, 0x4d, 0x10, 0xa9, 0xad, 0x18, 0x7f, + 0x85, 0xad, 0x0b, 0x89, 0xf0, 0x87, 0xf6, 0x00, 0xef, 0x2c, 0x29, 0x0c, 0x88, 0xee, 0x42, 0x96, + 0xdb, 0xdb, 0xd7, 0xe8, 0xdb, 0x89, 0x24, 0xdc, 0x03, 0x76, 0x96, 0x14, 0x1f, 0x4c, 0xcf, 0xc1, + 0x86, 0x19, 0xf8, 0x57, 0xf2, 0x39, 0xb2, 0x61, 0x52, 0x29, 0x18, 0x90, 0x9e, 0xe3, 0xe1, 0x2e, + 0xd6, 0x09, 0x73, 0xab, 0x69, 0xe7, 0xb4, 0x18, 0x84, 0x9e, 0xc3, 0xc1, 0x68, 0x0b, 0x32, 0x1e, + 0x19, 0x76, 0x71, 0x29, 0xc7, 0xa8, 0xca, 0xc9, 0x54, 0x14, 0xb1, 0xb3, 0xa4, 0x70, 0x28, 0xba, + 0x07, 0x79, 0xd3, 0xd2, 0x5d, 0xac, 0x79, 0xb8, 0x94, 0x67, 0x64, 0x97, 0x12, 0xc9, 0x76, 0x7d, + 0xd0, 0xce, 0x92, 0x12, 0x12, 0xa0, 0xaf, 0x83, 0x44, 0x5c, 0x8c, 0x55, 0x26, 0x9d, 0x34, 0x83, + 0xba, 0xed, 0x62, 0xec, 0x4b, 0x98, 0x27, 0xfe, 0x6f, 0xf4, 0x4d, 0x00, 0x46, 0xcd, 0x79, 0x06, + 0x46, 0xbe, 0x36, 0x95, 0x3c, 0xe0, 0x9b, 0x9d, 0xc8, 0x06, 0xe5, 0x5f, 0x09, 0x90, 0x6e, 0x61, + 0x42, 0x83, 0xd9, 0xd1, 0x5c, 0xea, 0xac, 0x94, 0x2f, 0x82, 0x0d, 0x55, 0x0b, 0x3c, 0x66, 0x5a, + 0x30, 0x73, 0x7c, 0x9d, 0xc3, 0x6b, 0x04, 0x15, 0x21, 0x4d, 0x33, 0x15, 0x8f, 0x3c, 0xfa, 0x93, + 0x2a, 0x73, 0xa0, 0x75, 0xfb, 0x81, 0x77, 0x5c, 0x8c, 0x6c, 0xf4, 0xa0, 0xb5, 0xdf, 0x94, 0xbb, + 0x98, 0xe6, 0xb2, 0x96, 0xd9, 0x73, 0xba, 0x58, 0xe1, 0x50, 0xf4, 0x65, 0x28, 0xe0, 0xe7, 0x58, + 0xef, 0xfb, 0x2c, 0x88, 0xb3, 0x58, 0x80, 0x00, 0x59, 0x23, 0xe5, 0x7f, 0x08, 0x90, 0xae, 0x19, + 0xc6, 0x49, 0x08, 0xf2, 0x1e, 0xac, 0x38, 0x2e, 0x1e, 0x44, 0x37, 0x48, 0xcd, 0xda, 0xe0, 0x14, + 0x45, 0x8f, 0xc8, 0x3f, 0x4f, 0xa9, 0xff, 0x25, 0x80, 0x48, 0xc3, 0xeb, 0x35, 0x10, 0xfb, 0x0e, + 0x40, 0x84, 0x32, 0x3d, 0x8b, 0x52, 0xd2, 0x43, 0xaa, 0x45, 0x05, 0xff, 0x54, 0x80, 0x2c, 0x4f, + 0x12, 0x27, 0x21, 0xfa, 0x38, 0xef, 0xa9, 0xc5, 0x78, 0x4f, 0xcf, 0xcb, 0xfb, 0x2f, 0x45, 0x10, + 0x59, 0xf4, 0x9e, 0x00, 0xe7, 0x37, 0x40, 0x7c, 0xea, 0xda, 0x3d, 0x9f, 0xe7, 0xf3, 0x51, 0x2a, + 0xfc, 0x9c, 0x34, 0x6d, 0x03, 0x1f, 0xd8, 0x9e, 0xc2, 0x30, 0xe8, 0x1a, 0xa4, 0x88, 0xed, 0xb3, + 0x39, 0x0d, 0x99, 0x22, 0x36, 0xea, 0xc0, 0x5b, 0x23, 0x7e, 0xd4, 0x9e, 0xe6, 0xa8, 0x47, 0x43, + 0x95, 0x5d, 0x2d, 0x7e, 0x55, 0xb0, 0x35, 0x35, 0xfd, 0x56, 0x43, 0xce, 0x1e, 0x6a, 0xce, 0xf6, + 0xb0, 0x46, 0x89, 0x64, 0x8b, 0xb8, 0x43, 0x65, 0x55, 0x9f, 0x5c, 0xa1, 0x17, 0xb6, 0x6e, 0x5b, + 0x04, 0x5b, 0x3c, 0xb1, 0x4b, 0x4a, 0x30, 0x8c, 0xeb, 0x36, 0x3b, 0xa7, 0x6e, 0xd1, 0x2e, 0x80, + 0x46, 0x88, 0x6b, 0x1e, 0xf5, 0x09, 0xf6, 0x4a, 0x39, 0xc6, 0xee, 0xf5, 0xe9, 0xec, 0xd6, 0x42, + 0x2c, 0xe7, 0x32, 0x42, 0x5c, 0xfe, 0x2e, 0x94, 0xa6, 0x49, 0x13, 0xe4, 0x3a, 0x61, 0x94, 0xeb, + 0x6e, 0x06, 0x51, 0x3f, 0xd3, 0x7b, 0x38, 0xe6, 0xdd, 0xd4, 0x57, 0x85, 0xf2, 0x7b, 0xb0, 0x12, + 0x3b, 0x3d, 0x61, 0xd7, 0xb3, 0xd1, 0x5d, 0xa5, 0x28, 0xf9, 0x1f, 0x05, 0xc8, 0xf2, 0xdb, 0xeb, + 0x75, 0x75, 0xa3, 0x45, 0x43, 0xfb, 0xcf, 0x29, 0xc8, 0xb0, 0xcb, 0xe9, 0x75, 0x15, 0xec, 0xc1, + 0x98, 0x8f, 0xf1, 0x90, 0xb8, 0x31, 0xbd, 0x50, 0x98, 0xe5, 0x64, 0x71, 0x25, 0x65, 0xe6, 0x55, + 0xd2, 0x7f, 0xe9, 0x3d, 0x9f, 0x0a, 0x90, 0x0f, 0xca, 0x91, 0x93, 0x50, 0xf3, 0xd6, 0xb8, 0xf7, + 0x2f, 0x72, 0xe7, 0xcd, 0x9d, 0x3e, 0xbf, 0x97, 0x82, 0x7c, 0x50, 0x0c, 0x9d, 0x04, 0xef, 0xd7, + 0xc6, 0x5c, 0x04, 0x45, 0xa9, 0x5c, 0x1c, 0x71, 0x8f, 0x4a, 0xc4, 0x3d, 0x92, 0x50, 0xd4, 0x35, + 0xbe, 0x38, 0x4a, 0x68, 0xdc, 0x2f, 0x56, 0x63, 0x40, 0xea, 0x47, 0x53, 0xb3, 0x5c, 0xe6, 0x15, + 0x42, 0x44, 0x0a, 0x0b, 0xba, 0xd7, 0x4d, 0x07, 0xcd, 0x84, 0xf0, 0xa8, 0xce, 0xae, 0x49, 0x5f, + 0xc3, 0x10, 0xd9, 0xce, 0x82, 0x78, 0x64, 0x1b, 0xc3, 0xca, 0xdf, 0x05, 0x38, 0x33, 0xe1, 0xc3, + 0xb1, 0x8a, 0x41, 0x98, 0xb3, 0x62, 0xb8, 0x05, 0x79, 0xf6, 0xb2, 0x7d, 0x69, 0x95, 0x91, 0x63, + 0x30, 0x5e, 0x99, 0xf8, 0xcf, 0xe3, 0x97, 0x57, 0x55, 0x3e, 0xb0, 0x46, 0xd0, 0x06, 0x88, 0x64, + 0xe8, 0xf0, 0x27, 0xd6, 0xe9, 0xb1, 0x87, 0xee, 0x23, 0x2a, 0x5f, 0x7b, 0xe8, 0x60, 0x85, 0x21, + 0x46, 0xf2, 0x67, 0xd8, 0x0b, 0x92, 0x0f, 0x2a, 0x3f, 0x3f, 0x05, 0x85, 0x88, 0xcc, 0xa8, 0x01, + 0x85, 0x8f, 0x3c, 0xdb, 0x52, 0xed, 0xa3, 0x8f, 0xe8, 0x8b, 0x8a, 0x8b, 0x7b, 0x25, 0x39, 0xc8, + 0xd9, 0xef, 0x7d, 0x06, 0xdc, 0x59, 0x52, 0x80, 0xd2, 0xf1, 0x11, 0xaa, 0x01, 0x1b, 0xa9, 0x9a, + 0xeb, 0x6a, 0x43, 0x5f, 0xfe, 0xf5, 0x19, 0x9b, 0xd4, 0x28, 0x8e, 0x3e, 0x57, 0x28, 0x15, 0x1b, + 0xa0, 0x6f, 0x81, 0xe4, 0xb8, 0x66, 0xcf, 0x24, 0x66, 0xf8, 0xe6, 0x9c, 0xb6, 0xc3, 0x41, 0x80, + 0xa3, 0x3b, 0x84, 0x44, 0xe8, 0x36, 0x88, 0x04, 0x3f, 0x0f, 0xdc, 0xe8, 0xed, 0x29, 0xc4, 0x34, + 0xe5, 0xd3, 0xa7, 0x24, 0x85, 0xa2, 0x77, 0x69, 0x50, 0xf7, 0x2d, 0x82, 0x5d, 0xbf, 0x0e, 0x59, + 0x9b, 0x42, 0x55, 0xe7, 0xa8, 0x9d, 0x25, 0x25, 0x20, 0x60, 0xc7, 0xb9, 0x38, 0x78, 0x4e, 0x4e, + 0x3d, 0xce, 0xc5, 0xec, 0x85, 0x4c, 0xa1, 0xe5, 0xcf, 0x04, 0x80, 0x91, 0x0e, 0xd1, 0x06, 0x64, + 0x2c, 0xdb, 0xc0, 0x5e, 0x49, 0x60, 0x91, 0x14, 0x8d, 0x3a, 0x65, 0xa7, 0xcd, 0xf2, 0x09, 0x07, + 0x2c, 0x58, 0xc5, 0x46, 0x7d, 0x32, 0xbd, 0x80, 0x4f, 0x8a, 0xf3, 0xf9, 0x64, 0xf9, 0x0f, 0x02, + 0x48, 0xa1, 0x55, 0x67, 0x4a, 0x75, 0xbf, 0xf6, 0xe6, 0x48, 0xf5, 0x37, 0x01, 0xa4, 0xd0, 0xd3, + 0xc2, 0xb8, 0x13, 0xe6, 0x8f, 0xbb, 0x54, 0x24, 0xee, 0x16, 0x7c, 0x43, 0x45, 0x65, 0x15, 0x17, + 0x90, 0x35, 0x33, 0xa7, 0xac, 0xbf, 0x13, 0x40, 0xa4, 0x81, 0x81, 0xae, 0x8f, 0x1b, 0x6f, 0x35, + 0xa1, 0x56, 0x7a, 0x33, 0xac, 0xf7, 0x57, 0x01, 0x72, 0x7e, 0xd0, 0xfe, 0x2f, 0xd8, 0xce, 0xc5, + 0x78, 0xa6, 0xed, 0x82, 0xfa, 0xe4, 0x4d, 0xb0, 0x5d, 0x78, 0x3f, 0x3f, 0x84, 0x9c, 0x9f, 0x07, + 0x13, 0xae, 0xf7, 0x5b, 0x90, 0xc3, 0x3c, 0xc7, 0x26, 0xbc, 0x00, 0x22, 0x19, 0x58, 0x09, 0x60, + 0x15, 0x1d, 0x72, 0x7e, 0x02, 0xa2, 0x45, 0x91, 0x45, 0xaf, 0x0a, 0x61, 0xa2, 0xdc, 0x09, 0x52, + 0x14, 0x5b, 0x5f, 0xe0, 0x90, 0x47, 0x90, 0xa7, 0xf4, 0xb4, 0x3c, 0x19, 0x79, 0x93, 0x10, 0xa9, + 0x40, 0xa8, 0x4e, 0xfa, 0x8e, 0x31, 0x9f, 0xee, 0x7d, 0x60, 0x8d, 0x54, 0x7e, 0x4b, 0x4b, 0x63, + 0x3f, 0x02, 0xd1, 0xff, 0x45, 0x3e, 0x99, 0x9f, 0x4b, 0x08, 0x51, 0xff, 0xa3, 0x79, 0x62, 0x05, + 0xb4, 0x60, 0xdd, 0x71, 0x17, 0x0a, 0xa6, 0xe5, 0xa9, 0xec, 0x33, 0x92, 0xff, 0x55, 0x7a, 0xea, + 0xd9, 0x92, 0x69, 0x79, 0x07, 0x2e, 0x1e, 0xec, 0x1a, 0xa8, 0x3e, 0x56, 0x31, 0x66, 0x98, 0x63, + 0x5e, 0x4d, 0xa0, 0x9a, 0xf9, 0x5c, 0x57, 0xe6, 0x29, 0xf7, 0xae, 0x8f, 0xbf, 0x53, 0xa2, 0xde, + 0x1f, 0x18, 0x24, 0x52, 0x03, 0x56, 0x3e, 0x04, 0x18, 0x71, 0xbc, 0x60, 0xcd, 0x77, 0x1e, 0xb2, + 0xf6, 0xd3, 0xa7, 0x1e, 0xe6, 0x56, 0xcc, 0x28, 0xfe, 0xa8, 0xf2, 0x4f, 0xff, 0x19, 0xc3, 0x6c, + 0xf5, 0x0e, 0xa4, 0x1d, 0xdb, 0x4b, 0xf0, 0xb4, 0xa0, 0xb0, 0xa6, 0xcb, 0x08, 0xf9, 0x29, 0x8a, + 0x5b, 0x2a, 0x96, 0x8c, 0xd2, 0xd3, 0xcd, 0x37, 0x67, 0x48, 0xa1, 0x3b, 0xb0, 0x1c, 0x9a, 0x8f, + 0xb2, 0x93, 0x99, 0xca, 0x0e, 0xf8, 0xc6, 0x3b, 0xb0, 0x3d, 0xca, 0x81, 0x81, 0x1d, 0xd2, 0x61, + 0xc5, 0x51, 0x46, 0xe1, 0x83, 0x98, 0x4d, 0x73, 0x93, 0x36, 0xf5, 0x45, 0xff, 0xdc, 0x6d, 0xfa, + 0x18, 0x72, 0xbe, 0x14, 0x27, 0x6c, 0xd0, 0x1e, 0x88, 0x87, 0x1e, 0x76, 0xd1, 0xe9, 0x30, 0xee, + 0x24, 0x16, 0x60, 0x65, 0xc8, 0xf7, 0x3d, 0xec, 0x5a, 0x5a, 0x2f, 0xb0, 0x5c, 0x38, 0x46, 0x5f, + 0x4b, 0xb8, 0x34, 0xca, 0x55, 0xde, 0x2a, 0xac, 0x06, 0xad, 0x42, 0xc6, 0x07, 0xeb, 0x25, 0x46, + 0xd8, 0xa8, 0xfc, 0x3b, 0x05, 0xb9, 0x03, 0xd7, 0x66, 0x35, 0x62, 0xfc, 0x48, 0x04, 0x62, 0xe4, + 0x38, 0xf6, 0x1b, 0x5d, 0x02, 0x70, 0xfa, 0x47, 0x5d, 0x53, 0x67, 0xfd, 0x44, 0xee, 0x2d, 0x12, + 0x9f, 0xf9, 0x00, 0x0f, 0xe9, 0xb2, 0x87, 0x75, 0x17, 0xf3, 0x76, 0xa3, 0xc8, 0x97, 0xf9, 0x0c, + 0x5d, 0xde, 0x80, 0xa2, 0xd6, 0x27, 0x1d, 0xf5, 0x13, 0x7c, 0xd4, 0xb1, 0xed, 0x63, 0xb5, 0xef, + 0x76, 0xfd, 0x4f, 0x76, 0xa7, 0xe9, 0xfc, 0x63, 0x3e, 0x7d, 0xe8, 0x76, 0xd1, 0x2d, 0x38, 0x3b, + 0x86, 0xec, 0x61, 0xd2, 0xb1, 0x0d, 0xaf, 0x94, 0x5d, 0x4f, 0x6f, 0x48, 0x0a, 0x8a, 0xa0, 0x1f, + 0xf2, 0x15, 0xf4, 0x0d, 0x78, 0xdb, 0x6f, 0x94, 0x19, 0x58, 0xd3, 0x89, 0x39, 0xd0, 0x08, 0x56, + 0x49, 0xc7, 0xc5, 0x5e, 0xc7, 0xee, 0x1a, 0xac, 0x74, 0x96, 0x94, 0x0b, 0x1c, 0xd2, 0x08, 0x11, + 0xed, 0x00, 0x10, 0x53, 0x62, 0xfe, 0x15, 0x94, 0x48, 0x49, 0x23, 0x69, 0x56, 0x7a, 0x39, 0xe9, + 0x28, 0xd7, 0xfe, 0x28, 0x0d, 0xe7, 0x0f, 0xe9, 0x48, 0x3b, 0xea, 0x62, 0xdf, 0x10, 0xef, 0x9b, + 0xb8, 0x6b, 0x78, 0xe8, 0x96, 0xaf, 0x7e, 0xc1, 0xff, 0x18, 0x12, 0xdf, 0xaf, 0x45, 0x5c, 0xd3, + 0x7a, 0xc6, 0xca, 0x0a, 0xdf, 0x38, 0xef, 0x27, 0xa8, 0x37, 0x35, 0x07, 0x75, 0x5c, 0xf9, 0x4f, + 0xa7, 0x28, 0x9f, 0x7b, 0xd6, 0x9d, 0x88, 0x6f, 0x27, 0xb3, 0x5e, 0xad, 0x4d, 0x98, 0x27, 0xd1, + 0x64, 0xdf, 0x99, 0x6d, 0x32, 0x71, 0x0e, 0xd6, 0xa7, 0x1b, 0xb4, 0x5c, 0x05, 0x34, 0xc9, 0x07, + 0xef, 0xee, 0x72, 0x71, 0x04, 0xe6, 0x4b, 0xc1, 0xb0, 0xf2, 0xfd, 0x14, 0xac, 0x34, 0xfc, 0xce, + 0x78, 0xab, 0xdf, 0xeb, 0x69, 0xee, 0x70, 0x22, 0x24, 0x26, 0xbb, 0x53, 0xf1, 0x46, 0xb8, 0x14, + 0x69, 0x84, 0x8f, 0xbb, 0x94, 0xf8, 0x2a, 0x2e, 0x75, 0x0f, 0x0a, 0x9a, 0xae, 0x63, 0xcf, 0x8b, + 0x16, 0x68, 0xb3, 0x68, 0x21, 0x80, 0x4f, 0xf8, 0x63, 0xf6, 0x55, 0xfc, 0x71, 0x00, 0xf9, 0xa0, + 0x23, 0x4d, 0x5f, 0x9d, 0xd4, 0xba, 0x7e, 0x8d, 0x77, 0x29, 0xa1, 0x69, 0x5d, 0x6d, 0x68, 0x44, + 0xe3, 0x09, 0x97, 0x41, 0xcb, 0x5f, 0x01, 0x29, 0x9c, 0x7a, 0x95, 0xef, 0x24, 0x95, 0x5d, 0xc8, + 0xd6, 0x99, 0x25, 0x23, 0x2a, 0x5f, 0x66, 0x2a, 0x8f, 0xb6, 0xcf, 0x53, 0xf3, 0xb4, 0xcf, 0x9b, + 0x00, 0xa3, 0x3f, 0x2d, 0xc4, 0x1a, 0xe4, 0x42, 0x52, 0x83, 0x7c, 0xbc, 0xc5, 0x9e, 0x8a, 0xb5, + 0xd8, 0x2b, 0x3f, 0x10, 0xa0, 0x10, 0xf9, 0x78, 0x7b, 0xb2, 0xf9, 0x1e, 0xfd, 0x3f, 0xac, 0xb8, + 0xb8, 0xab, 0xd1, 0x87, 0x9f, 0xea, 0x03, 0xd2, 0x0c, 0x70, 0x3a, 0x98, 0xde, 0xe7, 0x17, 0x83, + 0x0e, 0x30, 0xda, 0x39, 0xda, 0xd4, 0x17, 0x26, 0x9b, 0xfa, 0x17, 0x41, 0x32, 0x70, 0x97, 0xbe, + 0x27, 0xb1, 0x1b, 0x08, 0x14, 0x4e, 0x8c, 0xb5, 0xfc, 0xd3, 0xe3, 0x2d, 0xff, 0x1f, 0x0b, 0x90, + 0x6f, 0xd8, 0xba, 0x3c, 0xa0, 0x96, 0xb8, 0x39, 0xf6, 0x96, 0x79, 0x2b, 0x22, 0x62, 0x00, 0x89, + 0x3c, 0x67, 0x36, 0x81, 0x5f, 0x03, 0x5e, 0xc7, 0x3f, 0x32, 0xf6, 0xb7, 0x0f, 0xa6, 0x4e, 0x65, + 0x84, 0x41, 0x97, 0x21, 0xfc, 0x1f, 0x4a, 0xc0, 0x88, 0xa4, 0x40, 0x30, 0xb5, 0x6b, 0xdc, 0xf8, + 0x2c, 0x05, 0x52, 0xf8, 0x68, 0x42, 0xab, 0xb0, 0xf2, 0xa8, 0xb6, 0x77, 0x28, 0xab, 0xed, 0x27, + 0x07, 0xb2, 0xda, 0x3c, 0xdc, 0xdb, 0x2b, 0x2e, 0xa1, 0xf3, 0x80, 0x22, 0x93, 0xdb, 0xfb, 0xfb, + 0x7b, 0x72, 0xad, 0x59, 0x14, 0x62, 0xf3, 0xbb, 0xcd, 0xb6, 0x7c, 0x5f, 0x56, 0x8a, 0xa9, 0xd8, + 0x26, 0x7b, 0xfb, 0xcd, 0xfb, 0xc5, 0x34, 0x3a, 0x07, 0x67, 0x22, 0x93, 0x8d, 0xfd, 0xc3, 0xed, + 0x3d, 0xb9, 0x28, 0xc6, 0xa6, 0x5b, 0x6d, 0x65, 0xb7, 0x79, 0xbf, 0x98, 0x41, 0x67, 0xa1, 0x18, + 0x3d, 0xf2, 0x49, 0x5b, 0x6e, 0x15, 0xb3, 0xb1, 0x8d, 0x1b, 0xb5, 0xb6, 0x5c, 0xcc, 0xa1, 0x32, + 0x9c, 0x8f, 0x4c, 0xd2, 0x12, 0x5e, 0xdd, 0xdf, 0x7e, 0x20, 0xd7, 0xdb, 0xc5, 0x3c, 0xba, 0x00, + 0xe7, 0xe2, 0x6b, 0x35, 0x45, 0xa9, 0x3d, 0x29, 0x4a, 0xb1, 0xbd, 0xda, 0xf2, 0xb7, 0xdb, 0x45, + 0x88, 0xed, 0xe5, 0x4b, 0xa4, 0xd6, 0x9b, 0xed, 0x62, 0x01, 0xbd, 0x05, 0xab, 0x31, 0xa9, 0xd8, + 0xc2, 0x72, 0x7c, 0x27, 0x45, 0x96, 0x8b, 0xa7, 0x6e, 0xfc, 0x4c, 0x80, 0xe5, 0xa8, 0xfd, 0xd0, + 0x3b, 0xb0, 0xde, 0xd8, 0xaf, 0xab, 0xf2, 0x23, 0xb9, 0xd9, 0x0e, 0x74, 0x50, 0x3f, 0x7c, 0x28, + 0x37, 0xdb, 0x2d, 0xb5, 0xbe, 0x53, 0x6b, 0xde, 0x97, 0x1b, 0xc5, 0xa5, 0x99, 0xa8, 0xc7, 0xb5, + 0x76, 0x7d, 0x47, 0x6e, 0x14, 0x05, 0x74, 0x0d, 0x2a, 0x53, 0x51, 0x87, 0xcd, 0x00, 0x97, 0x42, + 0x57, 0xe1, 0x72, 0x0c, 0x77, 0xa0, 0xc8, 0x2d, 0xb9, 0x59, 0x97, 0xc3, 0x23, 0xd3, 0xdb, 0x37, + 0x7f, 0xfd, 0x62, 0x4d, 0xf8, 0xfd, 0x8b, 0x35, 0xe1, 0x4f, 0x2f, 0xd6, 0x84, 0x9f, 0xfe, 0x65, + 0x6d, 0x09, 0xce, 0x18, 0x78, 0x10, 0x38, 0x95, 0xe6, 0x98, 0xd5, 0xc1, 0xed, 0x03, 0xe1, 0x43, + 0xb1, 0x7a, 0x6f, 0x70, 0xfb, 0x28, 0xcb, 0xf2, 0xda, 0x97, 0xfe, 0x13, 0x00, 0x00, 0xff, 0xff, + 0x71, 0x73, 0x1f, 0x35, 0x69, 0x25, 0x00, 0x00, } func (m *ChangePack) Marshal() (dAtA []byte, err error) { @@ -3444,6 +3444,18 @@ func (m *Change) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Presence != nil { + { + size, err := m.Presence.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintResources(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } if len(m.Operations) > 0 { for iNdEx := len(m.Operations) - 1; iNdEx >= 0; iNdEx-- { { @@ -6120,14 +6132,9 @@ func (m *Presence) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0xa i = encodeVarintResources(dAtA, i, uint64(baseI-i)) i-- - dAtA[i] = 0x12 + dAtA[i] = 0xa } } - if m.Clock != 0 { - i = encodeVarintResources(dAtA, i, uint64(m.Clock)) - i-- - dAtA[i] = 0x8 - } return len(dAtA) - i, nil } @@ -6426,6 +6433,10 @@ func (m *Change) Size() (n int) { n += 1 + l + sovResources(uint64(l)) } } + if m.Presence != nil { + l = m.Presence.Size() + n += 1 + l + sovResources(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -7560,9 +7571,6 @@ func (m *Presence) Size() (n int) { } var l int _ = l - if m.Clock != 0 { - n += 1 + sovResources(uint64(m.Clock)) - } if len(m.Data) > 0 { for k, v := range m.Data { _ = k @@ -8062,6 +8070,42 @@ func (m *Change) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Presence", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Presence == nil { + m.Presence = &Presence{} + } + if err := m.Presence.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipResources(dAtA[iNdEx:]) @@ -15071,25 +15115,6 @@ func (m *Presence) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Clock", wireType) - } - m.Clock = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowResources - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Clock |= int32(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) } diff --git a/api/yorkie/v1/resources.proto b/api/yorkie/v1/resources.proto index 0955ea0de..06da1fa4c 100644 --- a/api/yorkie/v1/resources.proto +++ b/api/yorkie/v1/resources.proto @@ -44,6 +44,7 @@ message Change { ChangeID id = 1; string message = 2; repeated Operation operations = 3; + Presence presence = 4; } message ChangeID { @@ -281,8 +282,7 @@ message DocumentSummary { } message Presence { - int32 clock = 1; - map data = 2; + map data = 1; } message Client { diff --git a/client/client.go b/client/client.go index d8d952342..8f81d5e61 100644 --- a/client/client.go +++ b/client/client.go @@ -24,13 +24,12 @@ import ( "fmt" "strings" - "google.golang.org/grpc/metadata" - "github.com/rs/xid" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/metadata" "github.com/yorkie-team/yorkie/api/converter" "github.com/yorkie-team/yorkie/api/types" diff --git a/pkg/document/change/change.go b/pkg/document/change/change.go index ed392207f..b23825295 100644 --- a/pkg/document/change/change.go +++ b/pkg/document/change/change.go @@ -21,6 +21,7 @@ package change import ( "github.com/yorkie-team/yorkie/pkg/document/crdt" "github.com/yorkie-team/yorkie/pkg/document/operations" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" ) @@ -34,24 +35,34 @@ type Change struct { // operations represent a series of user edits. operations []operations.Operation + + // presence represents the presence of the user who made the change. + // TODO(hackerwins): Consider using changes instead of entire presence. + presence *presence.InternalPresence } // New creates a new instance of Change. -func New(id ID, message string, operations []operations.Operation) *Change { +func New(id ID, message string, operations []operations.Operation, p *presence.InternalPresence) *Change { return &Change{ id: id, message: message, operations: operations, + presence: p, } } // Execute applies this change to the given JSON root. -func (c *Change) Execute(root *crdt.Root) error { +func (c *Change) Execute(root *crdt.Root, presenceMap *presence.Map) error { for _, op := range c.operations { if err := op.Execute(root); err != nil { return err } } + + if c.presence != nil { + presenceMap.Store(c.id.actorID.String(), c.presence) + } + return nil } @@ -92,3 +103,8 @@ func (c *Change) SetActor(actor *time.ActorID) { op.SetActor(actor) } } + +// Presence returns the presence of this change. +func (c *Change) Presence() *presence.InternalPresence { + return c.presence +} diff --git a/pkg/document/change/context.go b/pkg/document/change/context.go index 931d7859e..209d1e74f 100644 --- a/pkg/document/change/context.go +++ b/pkg/document/change/context.go @@ -19,6 +19,7 @@ package change import ( "github.com/yorkie-team/yorkie/pkg/document/crdt" "github.com/yorkie-team/yorkie/pkg/document/operations" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" ) @@ -31,6 +32,7 @@ type Context struct { operations []operations.Operation delimiter uint32 root *crdt.Root + presence *presence.InternalPresence } // NewContext creates a new instance of Context. @@ -49,12 +51,12 @@ func (c *Context) ID() ID { // ToChange creates a new change of this context. func (c *Context) ToChange() *Change { - return New(c.id, c.message, c.operations) + return New(c.id, c.message, c.operations, c.presence) } -// HasOperations returns whether this change has operations or not. -func (c *Context) HasOperations() bool { - return len(c.operations) > 0 +// HasChange returns whether this context has changes. +func (c *Context) HasChange() bool { + return len(c.operations) > 0 || c.presence != nil } // IssueTimeTicket creates a time ticket to be used to create a new operation. @@ -87,3 +89,8 @@ func (c *Context) RegisterElementHasRemovedNodes(element crdt.GCElement) { func (c *Context) LastTimeTicket() *time.Ticket { return c.id.NewTimeTicket(c.delimiter) } + +// SetPresence sets the presence of the user who made the change. +func (c *Context) SetPresence(presence presence.InternalPresence) { + c.presence = &presence +} diff --git a/pkg/document/document.go b/pkg/document/document.go index 5c98d5b07..0add3ad9b 100644 --- a/pkg/document/document.go +++ b/pkg/document/document.go @@ -19,6 +19,8 @@ package document import ( "fmt" + "github.com/yorkie-team/yorkie/pkg/document/presence" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/crdt" @@ -52,7 +54,7 @@ func New(key key.Key) *Document { // Update executes the given updater to update this document. func (d *Document) Update( - updater func(root *json.Object) error, + updater func(root *json.Object, p *presenceproxy.Presence) error, msgAndArgs ...interface{}, ) error { if d.doc.status == StatusRemoved { @@ -69,15 +71,20 @@ func (d *Document) Update( d.clone, ) - if err := updater(json.NewObject(ctx, d.clone.Object())); err != nil { + // TODO(hackerwins): We should use the presence of the client that cloned + // from the document. + if err := updater( + json.NewObject(ctx, d.clone.Object()), + presenceproxy.New(ctx, d.doc.Presence()), + ); err != nil { // drop clone because it is contaminated. d.clone = nil return err } - if ctx.HasOperations() { + if ctx.HasChange() { c := ctx.ToChange() - if err := c.Execute(d.doc.root); err != nil { + if err := c.Execute(d.doc.root, d.doc.presenceMap); err != nil { return err } @@ -102,7 +109,7 @@ func (d *Document) ApplyChangePack(pack *change.Pack) error { } for _, c := range pack.Changes { - if err := c.Execute(d.clone); err != nil { + if err := c.Execute(d.clone, d.doc.presenceMap); err != nil { return err } } @@ -186,7 +193,7 @@ func (d *Document) Status() StatusType { return d.doc.status } -// IsAttached returns the whether this document is attached or not. +// IsAttached returns whether this document is attached or not. func (d *Document) IsAttached() bool { return d.doc.IsAttached() } @@ -238,6 +245,11 @@ func (d *Document) ensureClone() error { return nil } +// PresenceMap returns the presence map of this document. +func (d *Document) PresenceMap() map[string]presence.InternalPresence { + return d.doc.PresenceMap() +} + func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { if len(msgAndArgs) == 0 { return "" diff --git a/pkg/document/document_test.go b/pkg/document/document_test.go index b4a221a31..75d6be74f 100644 --- a/pkg/document/document_test.go +++ b/pkg/document/document_test.go @@ -19,6 +19,7 @@ package document_test import ( "errors" "fmt" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" @@ -54,7 +55,7 @@ func TestDocument(t *testing.T) { doc2 := document.New("d2") doc3 := document.New("d3") - err := doc1.Update(func(root *json.Object) error { + err := doc1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") return nil }, "updates k1") @@ -71,7 +72,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, "{}", doc.Marshal()) assert.False(t, doc.HasLocalChanges()) - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") root.SetNewObject("k2").SetString("k4", "v4") root.SetNewArray("k3").AddString("v5", "v6") @@ -89,7 +90,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, "{}", doc.Marshal()) assert.False(t, doc.HasLocalChanges()) expected := `{"k1":"v1","k2":{"k4":"v4"},"k3":["v5","v6"]}` - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") root.SetNewObject("k2").SetString("k4", "v4") root.SetNewArray("k3").AddString("v5", "v6") @@ -100,7 +101,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, expected, doc.Marshal()) expected = `{"k1":"v1","k3":["v5","v6"]}` - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.Delete("k2") assert.Equal(t, expected, root.Marshal()) return nil @@ -111,7 +112,7 @@ func TestDocument(t *testing.T) { t.Run("object test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") assert.Equal(t, `{"k1":"v1"}`, root.Marshal()) root.SetString("k1", "v2") @@ -125,7 +126,7 @@ func TestDocument(t *testing.T) { t.Run("array test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k1").AddInteger(1).AddInteger(2).AddInteger(3) assert.Equal(t, 3, root.GetArray("k1").Len()) assert.Equal(t, `{"k1":[1,2,3]}`, root.Marshal()) @@ -162,7 +163,7 @@ func TestDocument(t *testing.T) { t.Run("delete elements of array test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("data").AddInteger(0).AddInteger(1).AddInteger(2) return nil }) @@ -170,7 +171,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, `{"data":[0,1,2]}`, doc.Marshal()) assert.Equal(t, 3, doc.Root().GetArray("data").Len()) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("data").Delete(0) return nil }) @@ -178,7 +179,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, `{"data":[1,2]}`, doc.Marshal()) assert.Equal(t, 2, doc.Root().GetArray("data").Len()) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("data").Delete(1) return nil }) @@ -186,7 +187,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, `{"data":[1]}`, doc.Marshal()) assert.Equal(t, 1, doc.Root().GetArray("data").Len()) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("data").Delete(0) return nil }) @@ -201,7 +202,7 @@ func TestDocument(t *testing.T) { // ---------- ins links -------- // | | | // [init] - [A] - [12] - [BC deleted] - [D] - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1"). Edit(0, 0, "ABCD"). Edit(1, 3, "12") @@ -211,7 +212,7 @@ func TestDocument(t *testing.T) { assert.NoError(t, err) assert.Equal(t, `{"k1":[{"val":"A"},{"val":"12"},{"val":"D"}]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") assert.Equal(t, `[0:0:00:0 {} ""][1:2:00:0 {} "A"][1:3:00:0 {} "12"]{1:2:00:1 {} "BC"}[1:2:00:3 {} "D"]`, @@ -240,7 +241,7 @@ func TestDocument(t *testing.T) { t.Run("text composition test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1"). Edit(0, 0, "ㅎ"). Edit(0, 1, "하"). @@ -258,7 +259,7 @@ func TestDocument(t *testing.T) { t.Run("rich text test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.SetNewText("k1") text.Edit(0, 0, "Hello world", nil) assert.Equal( @@ -271,7 +272,7 @@ func TestDocument(t *testing.T) { assert.NoError(t, err) assert.Equal(t, `{"k1":[{"val":"Hello world"}]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") text.Style(0, 5, map[string]string{"b": "1"}) assert.Equal(t, @@ -287,7 +288,7 @@ func TestDocument(t *testing.T) { doc.Marshal(), ) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") text.Style(0, 5, map[string]string{"b": "1"}) assert.Equal( @@ -311,7 +312,7 @@ func TestDocument(t *testing.T) { doc.Marshal(), ) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") text.Edit(5, 11, " Yorkie", nil) assert.Equal( @@ -329,7 +330,7 @@ func TestDocument(t *testing.T) { doc.Marshal(), ) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") text.Edit(5, 5, "\n", map[string]string{"list": "true"}) assert.Equal( @@ -358,7 +359,7 @@ func TestDocument(t *testing.T) { var double = 5.66 // integer type test - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewCounter("age", crdt.IntegerCnt, 5) age := root.GetCounter("age") @@ -375,7 +376,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, "128", doc.Root().GetCounter("age").Counter.Marshal()) // long type test - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewCounter("price", crdt.LongCnt, 9000000000000000000) price := root.GetCounter("price") println(price.ValueType()) @@ -391,7 +392,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, `{"age":128,"price":9000000000000000123}`, doc.Marshal()) // negative operator test - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { age := root.GetCounter("age") age.Increase(-5) age.Increase(-3.14) @@ -407,7 +408,7 @@ func TestDocument(t *testing.T) { // TODO: it should be modified to error check // when 'Remove panic from server code (#50)' is completed. - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { defer func() { r := recover() assert.NotNil(t, r) @@ -427,21 +428,21 @@ func TestDocument(t *testing.T) { t.Run("rollback test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k1").AddInteger(1, 2, 3) return nil }) assert.NoError(t, err) assert.Equal(t, `{"k1":[1,2,3]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("k1").AddInteger(4, 5) return errDummy }) assert.Equal(t, err, errDummy, "should returns the dummy error") assert.Equal(t, `{"k1":[1,2,3]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("k1").AddInteger(4, 5) return nil }) @@ -452,7 +453,7 @@ func TestDocument(t *testing.T) { t.Run("rollback test, primitive deepcopy", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewObject("k1"). SetInteger("k1.1", 1). SetInteger("k1.2", 2) @@ -461,7 +462,7 @@ func TestDocument(t *testing.T) { assert.NoError(t, err) assert.Equal(t, `{"k1":{"k1.1":1,"k1.2":2}}`, doc.Marshal()) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetObject("k1").Delete("k1.1") return errDummy }) @@ -472,7 +473,7 @@ func TestDocument(t *testing.T) { t.Run("text garbage collection test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("text") root.GetText("text").Edit(0, 0, "ABCD") root.GetText("text").Edit(0, 2, "12") @@ -494,7 +495,7 @@ func TestDocument(t *testing.T) { doc.Root().GetText("text").StructureAsString(), ) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("text").Edit(2, 4, "") return nil }) @@ -509,7 +510,7 @@ func TestDocument(t *testing.T) { t.Run("previously inserted elements in heap when running GC test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetInteger("a", 1) root.SetInteger("a", 2) root.Delete("a") diff --git a/pkg/document/internal_document.go b/pkg/document/internal_document.go index 9163fc1f9..ab8caf019 100644 --- a/pkg/document/internal_document.go +++ b/pkg/document/internal_document.go @@ -18,11 +18,11 @@ package document import ( "errors" - "github.com/yorkie-team/yorkie/api/converter" "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/crdt" "github.com/yorkie-team/yorkie/pkg/document/key" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" ) @@ -56,18 +56,21 @@ type InternalDocument struct { checkpoint change.Checkpoint changeID change.ID localChanges []*change.Change + presenceMap *presence.Map } // NewInternalDocument creates a new instance of InternalDocument. func NewInternalDocument(k key.Key) *InternalDocument { root := crdt.NewObject(crdt.NewElementRHT(), time.InitialTicket) + // TODO(hackerwins): We need to initialize the presence of the actor who edited the document. return &InternalDocument{ - key: k, - status: StatusDetached, - root: crdt.NewRoot(root), - checkpoint: change.InitialCheckpoint, - changeID: change.InitialID, + key: k, + status: StatusDetached, + root: crdt.NewRoot(root), + checkpoint: change.InitialCheckpoint, + changeID: change.InitialID, + presenceMap: presence.NewMap(), } } @@ -84,11 +87,12 @@ func NewInternalDocumentFromSnapshot( } return &InternalDocument{ - key: k, - status: StatusDetached, - root: crdt.NewRoot(obj), - checkpoint: change.InitialCheckpoint.NextServerSeq(serverSeq), - changeID: change.InitialID.SyncLamport(lamport), + key: k, + status: StatusDetached, + root: crdt.NewRoot(obj), + presenceMap: presence.NewMap(), + checkpoint: change.InitialCheckpoint.NextServerSeq(serverSeq), + changeID: change.InitialID.SyncLamport(lamport), }, nil } @@ -188,7 +192,7 @@ func (d *InternalDocument) SetStatus(status StatusType) { d.status = status } -// IsAttached returns the whether this document is attached or not. +// IsAttached returns whether this document is attached or not. func (d *InternalDocument) IsAttached() bool { return d.status == StatusAttached } @@ -218,7 +222,7 @@ func (d *InternalDocument) applySnapshot(snapshot []byte, serverSeq int64) error // ApplyChanges applies remote changes to the document. func (d *InternalDocument) ApplyChanges(changes ...*change.Change) error { for _, c := range changes { - if err := c.Execute(d.root); err != nil { + if err := c.Execute(d.root, d.presenceMap); err != nil { return err } d.changeID = d.changeID.SyncLamport(c.ID().Lamport()) @@ -226,3 +230,20 @@ func (d *InternalDocument) ApplyChanges(changes ...*change.Change) error { return nil } + +// Presence returns the presence of the actor currently editing the document. +func (d *InternalDocument) Presence() *presence.InternalPresence { + value, _ := d.presenceMap.LoadOrStore(d.changeID.ActorID().String(), presence.NewInternalPresence()) + return value.(*presence.InternalPresence) +} + +// PresenceMap returns the map of presences of the actors currently editing the document. +func (d *InternalDocument) PresenceMap() map[string]presence.InternalPresence { + // TODO(hackerwins): We need to use client key instead of actor ID for exposing presence. + presenceMap := make(map[string]presence.InternalPresence) + d.presenceMap.Range(func(key, value interface{}) bool { + presenceMap[key.(string)] = *value.(*presence.InternalPresence) + return true + }) + return presenceMap +} diff --git a/pkg/document/presence/presence.go b/pkg/document/presence/presence.go new file mode 100644 index 000000000..c15d54ef2 --- /dev/null +++ b/pkg/document/presence/presence.go @@ -0,0 +1,64 @@ +/* + * Copyright 2023 The Yorkie Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package presence provides the implementation of InternalPresence. +// If the client is watching a document, the presence is shared with +// all other clients watching the same document. +package presence + +import ( + "encoding/json" + "fmt" + "sync" +) + +// Map is a map of presence by clientID. +type Map = sync.Map // map[string]*presence.InternalPresence + +// NewMap creates a new instance of Map. +func NewMap() *Map { + return &sync.Map{} +} + +// InternalPresence represents custom presence that can be defined by the client. +type InternalPresence map[string]string + +// NewFromJSON creates a new instance of InternalPresence from JSON. +func NewFromJSON(encodedJSON string) (*InternalPresence, error) { + if encodedJSON == "" { + return nil, nil + } + + p := InternalPresence{} + if err := json.Unmarshal([]byte(encodedJSON), &p); err != nil { + return nil, fmt.Errorf("unmarshal presence: %w", err) + } + + return &p, nil +} + +// NewInternalPresence creates a new instance of InternalPresence. +func NewInternalPresence() *InternalPresence { + data := make(map[string]string) + p := InternalPresence(data) + return &p +} + +// Set sets the value of the given key. +func (p *InternalPresence) Set(key string, value string) { + presence := *p + presence[key] = value +} diff --git a/pkg/document/presenceproxy/proxy.go b/pkg/document/presenceproxy/proxy.go new file mode 100644 index 000000000..8ef51f1eb --- /dev/null +++ b/pkg/document/presenceproxy/proxy.go @@ -0,0 +1,46 @@ +/* + * Copyright 2023 The Yorkie Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package presenceproxy provides the proxy for the InternalPresence to be manipulated from the outside. +// TODO(hackerwins): Consider to remove this package. It is used to solve the problem of cyclic dependency +// between pkg/document/presence and pkg/document/change. +package presenceproxy + +import ( + "github.com/yorkie-team/yorkie/pkg/document/change" + presence2 "github.com/yorkie-team/yorkie/pkg/document/presence" +) + +// Presence represents a proxy for the InternalPresence to be manipulated from the outside. +type Presence struct { + presence *presence2.InternalPresence + context *change.Context +} + +// New creates a new instance of Presence. +func New(ctx *change.Context, presence *presence2.InternalPresence) *Presence { + return &Presence{ + presence: presence, + context: ctx, + } +} + +// Set sets the value of the given key. +func (p *Presence) Set(key string, value string) { + internalPresence := *p.presence + internalPresence.Set(key, value) + p.context.SetPresence(internalPresence) +} diff --git a/server/backend/database/change_info.go b/server/backend/database/change_info.go index e0cbce058..625d1fb72 100644 --- a/server/backend/database/change_info.go +++ b/server/backend/database/change_info.go @@ -17,7 +17,10 @@ package database import ( + "encoding/json" "errors" + "fmt" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/api/converter" "github.com/yorkie-team/yorkie/api/types" @@ -40,6 +43,7 @@ type ChangeInfo struct { ActorID types.ID `bson:"actor_id"` Message string `bson:"message"` Operations [][]byte `bson:"operations"` + Presence string `bson:"presence"` } // EncodeOperations encodes the given operations into bytes array. @@ -62,6 +66,20 @@ func EncodeOperations(operations []operations.Operation) ([][]byte, error) { return encodedOps, nil } +// EncodePresence encodes the given presence into string. +func EncodePresence(p *presence.InternalPresence) (string, error) { + if p == nil { + return "", nil + } + + bytes, err := json.Marshal(p) + if err != nil { + return "", fmt.Errorf("marshal presence to bytes: %w", err) + } + + return string(bytes), nil +} + // ToChange creates Change model from this ChangeInfo. func (i *ChangeInfo) ToChange() (*change.Change, error) { actorID, err := time.ActorIDFromHex(i.ActorID.String()) @@ -85,7 +103,12 @@ func (i *ChangeInfo) ToChange() (*change.Change, error) { return nil, err } - c := change.New(changeID, i.Message, ops) + p, err := presence.NewFromJSON(i.Presence) + if err != nil { + return nil, err + } + + c := change.New(changeID, i.Message, ops, p) c.SetServerSeq(i.ServerSeq) return c, nil diff --git a/server/backend/database/memory/database.go b/server/backend/database/memory/database.go index f77bdf7d6..380609134 100644 --- a/server/backend/database/memory/database.go +++ b/server/backend/database/memory/database.go @@ -782,6 +782,10 @@ func (d *DB) CreateChangeInfos( if err != nil { return err } + encodedPresence, err := database.EncodePresence(cn.Presence()) + if err != nil { + return err + } if err := txn.Insert(tblChanges, &database.ChangeInfo{ ID: newID(), @@ -792,6 +796,7 @@ func (d *DB) CreateChangeInfos( Lamport: cn.ID().Lamport(), Message: cn.Message(), Operations: encodedOperations, + Presence: encodedPresence, }); err != nil { return fmt.Errorf("create change: %w", err) } diff --git a/server/backend/database/mongo/client.go b/server/backend/database/mongo/client.go index 25feb5e3e..bedaa23ee 100644 --- a/server/backend/database/mongo/client.go +++ b/server/backend/database/mongo/client.go @@ -864,6 +864,10 @@ func (c *Client) CreateChangeInfos( if err != nil { return err } + encodedPresence, err := database.EncodePresence(cn.Presence()) + if err != nil { + return err + } models = append(models, mongo.NewUpdateOneModel().SetFilter(bson.M{ "doc_id": encodedDocID, @@ -874,6 +878,7 @@ func (c *Client) CreateChangeInfos( "lamport": cn.ID().Lamport(), "message": cn.Message(), "operations": encodedOperations, + "presence": encodedPresence, }}).SetUpsert(true)) } diff --git a/server/backend/database/testcases/testcases.go b/server/backend/database/testcases/testcases.go index c07140809..6621e8168 100644 --- a/server/backend/database/testcases/testcases.go +++ b/server/backend/database/testcases/testcases.go @@ -21,6 +21,7 @@ package testcases import ( "context" "fmt" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "strconv" "testing" gotime "time" @@ -175,12 +176,12 @@ func RunFindChangesBetweenServerSeqsTest( actorID, _ := time.ActorIDFromBytes(bytesID) doc := document.New(key.Key(t.Name())) doc.SetActor(actorID) - assert.NoError(t, doc.Update(func(root *json.Object) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("array") return nil })) for idx := 0; idx < 10; idx++ { - assert.NoError(t, doc.Update(func(root *json.Object) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("array").AddInteger(idx) return nil })) @@ -220,7 +221,7 @@ func RunFindClosestSnapshotInfoTest(t *testing.T, db database.Database, projectI doc := document.New(key.Key(t.Name())) doc.SetActor(actorID) - assert.NoError(t, doc.Update(func(root *json.Object) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("array") return nil })) diff --git a/server/packs/serverpacks.go b/server/packs/serverpacks.go index cf51db0e5..95c531f97 100644 --- a/server/packs/serverpacks.go +++ b/server/packs/serverpacks.go @@ -21,6 +21,7 @@ import ( api "github.com/yorkie-team/yorkie/api/yorkie/v1" "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/key" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" "github.com/yorkie-team/yorkie/server/backend/database" ) @@ -101,10 +102,16 @@ func (p *ServerPack) ToPBChangePack() (*api.ChangePack, error) { pbOps = append(pbOps, &pbOp) } + p, err := presence.NewFromJSON(info.Presence) + if err != nil { + return nil, err + } + pbChanges = append(pbChanges, &api.Change{ Id: converter.ToChangeID(changeID), Message: info.Message, Operations: pbOps, + Presence: converter.ToPresence(p), }) } diff --git a/test/bench/document_bench_test.go b/test/bench/document_bench_test.go index d71f28895..7248f657d 100644 --- a/test/bench/document_bench_test.go +++ b/test/bench/document_bench_test.go @@ -57,7 +57,7 @@ func BenchmarkDocument(b *testing.B) { doc2 := document.New("d2") doc3 := document.New("d3") - err := doc1.Update(func(root *json.Object) error { + err := doc1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") return nil }, "updates k1") @@ -76,7 +76,7 @@ func BenchmarkDocument(b *testing.B) { assert.Equal(b, "{}", doc.Marshal()) assert.False(b, doc.HasLocalChanges()) - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") root.SetNewObject("k2").SetString("k4", "v4") root.SetNewArray("k3").AddString("v5", "v6") @@ -97,7 +97,7 @@ func BenchmarkDocument(b *testing.B) { assert.False(b, doc.HasLocalChanges()) expected := `{"k1":"v1","k2":{"k4":"v4"},"k3":["v5","v6"]}` - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") root.SetNewObject("k2").SetString("k4", "v4") root.SetNewArray("k3").AddString("v5", "v6") @@ -108,7 +108,7 @@ func BenchmarkDocument(b *testing.B) { assert.Equal(b, expected, doc.Marshal()) expected = `{"k1":"v1","k3":["v5","v6"]}` - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.Delete("k2") assert.Equal(b, expected, root.Marshal()) return nil @@ -121,7 +121,7 @@ func BenchmarkDocument(b *testing.B) { b.Run("object test", func(b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") assert.Equal(b, `{"k1":"v1"}`, root.Marshal()) root.SetString("k1", "v2") @@ -137,7 +137,7 @@ func BenchmarkDocument(b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k1").AddInteger(1).AddInteger(2).AddInteger(3) assert.Equal(b, 3, root.GetArray("k1").Len()) assert.Equal(b, `{"k1":[1,2,3]}`, root.Marshal()) @@ -180,7 +180,7 @@ func BenchmarkDocument(b *testing.B) { // ---------- ins links -------- // | | | // [init] - [A] - [12] - [BC deleted] - [D] - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1"). Edit(0, 0, "ABCD"). Edit(1, 3, "12") @@ -190,7 +190,7 @@ func BenchmarkDocument(b *testing.B) { assert.NoError(b, err) assert.Equal(b, `{"k1":[{"val":"A"},{"val":"12"},{"val":"D"}]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") assert.Equal(b, `[0:0:00:0 {} ""][1:2:00:0 {} "A"][1:3:00:0 {} "12"]{1:2:00:1 {} "BC"}[1:2:00:3 {} "D"]`, @@ -221,7 +221,7 @@ func BenchmarkDocument(b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1"). Edit(0, 0, "ㅎ"). Edit(0, 1, "하"). @@ -241,7 +241,7 @@ func BenchmarkDocument(b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.SetNewText("k1") text.Edit(0, 0, "Hello world", nil) assert.Equal( @@ -254,7 +254,7 @@ func BenchmarkDocument(b *testing.B) { assert.NoError(b, err) assert.Equal(b, `{"k1":[{"val":"Hello world"}]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") text.Style(0, 5, map[string]string{"b": "1"}) assert.Equal(b, @@ -270,7 +270,7 @@ func BenchmarkDocument(b *testing.B) { doc.Marshal(), ) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") text.Style(0, 5, map[string]string{"b": "1"}) assert.Equal( @@ -294,7 +294,7 @@ func BenchmarkDocument(b *testing.B) { doc.Marshal(), ) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") text.Edit(5, 11, " Yorkie", nil) assert.Equal( @@ -312,7 +312,7 @@ func BenchmarkDocument(b *testing.B) { doc.Marshal(), ) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") text.Edit(5, 5, "\n", map[string]string{"list": "true"}) assert.Equal( @@ -341,7 +341,7 @@ func BenchmarkDocument(b *testing.B) { var double = 5.66 // integer type test - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewCounter("age", crdt.IntegerCnt, 5) age := root.GetCounter("age") @@ -357,7 +357,7 @@ func BenchmarkDocument(b *testing.B) { assert.Equal(b, `{"age":128}`, doc.Marshal()) // long type test - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewCounter("price", crdt.LongCnt, 9000000000000000000) price := root.GetCounter("price") price.Increase(long) @@ -372,7 +372,7 @@ func BenchmarkDocument(b *testing.B) { assert.Equal(b, `{"age":128,"price":9000000000000000123}`, doc.Marshal()) // negative operator test - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { age := root.GetCounter("age") age.Increase(-5) age.Increase(-3.14) @@ -388,7 +388,7 @@ func BenchmarkDocument(b *testing.B) { // TODO: it should be modified to error check // when 'Remove panic from server code (#50)' is completed. - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { defer func() { r := recover() assert.NotNil(b, r) @@ -475,7 +475,7 @@ func benchmarkText(cnt int, b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.SetNewText("k1") for c := 0; c < cnt; c++ { text.Edit(c, c, "a") @@ -491,7 +491,7 @@ func benchmarkTextDeleteAll(cnt int, b *testing.B) { b.StopTimer() doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.SetNewText("k1") for c := 0; c < cnt; c++ { text.Edit(c, c, "a") @@ -501,7 +501,7 @@ func benchmarkTextDeleteAll(cnt int, b *testing.B) { assert.NoError(b, err) b.StartTimer() - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") text.Edit(0, cnt, "") return nil @@ -518,7 +518,7 @@ func benchmarkTextEditGC(cnt int, b *testing.B) { assert.Equal(b, "{}", doc.Marshal()) assert.False(b, doc.HasLocalChanges()) - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.SetNewText("k1") for i := 0; i < cnt; i++ { text.Edit(i, i, "a") @@ -527,7 +527,7 @@ func benchmarkTextEditGC(cnt int, b *testing.B) { }, "creates a text then appends a") assert.NoError(b, err) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") for i := 0; i < cnt; i++ { text.Edit(i, i+1, "b") @@ -549,14 +549,14 @@ func benchmarkTextSplitGC(cnt int, b *testing.B) { for i := 0; i < cnt; i++ { builder.WriteString("a") } - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.SetNewText("k2") text.Edit(0, 0, builder.String()) return nil }, "initial") assert.NoError(b, err) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k2") for i := 0; i < cnt; i++ { if i != cnt { @@ -576,7 +576,7 @@ func benchmarkArray(cnt int, b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { array := root.SetNewArray("k1") for c := 0; c < cnt; c++ { array.AddInteger(c) @@ -590,7 +590,7 @@ func benchmarkArray(cnt int, b *testing.B) { func benchmarkArrayGC(cnt int, b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("1") for i := 0; i < cnt; i++ { root.GetArray("1").AddInteger(i) @@ -600,7 +600,7 @@ func benchmarkArrayGC(cnt int, b *testing.B) { }, "creates an array then adds integers") assert.NoError(b, err) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.Delete("1") return nil }, "deletes the array") @@ -615,7 +615,7 @@ func benchmarkCounter(cnt int, b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { counter := root.SetNewCounter("k1", crdt.IntegerCnt, 0) for c := 0; c < cnt; c++ { counter.Increase(c) @@ -630,7 +630,7 @@ func benchmarkObject(cnt int, b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { for c := 0; c < cnt; c++ { root.SetInteger("k1", c) } diff --git a/test/bench/grpc_bench_test.go b/test/bench/grpc_bench_test.go index 9279bfd60..eae516269 100644 --- a/test/bench/grpc_bench_test.go +++ b/test/bench/grpc_bench_test.go @@ -89,7 +89,7 @@ func benchmarkUpdateAndSync( key string, ) { for i := 0; i < cnt; i++ { - err := d.Update(func(root *json.Object) error { + err := d.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText(key) text.Edit(0, 0, "c") return nil @@ -184,7 +184,7 @@ func BenchmarkRPC(b *testing.B) { for i := 0; i < b.N; i++ { testKey := "testKey" - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText(testKey) return nil }) @@ -205,7 +205,7 @@ func BenchmarkRPC(b *testing.B) { err := c1.Attach(ctx, d1) assert.NoError(b, err) testKey1 := "testKey1" - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText(testKey1) return nil }) @@ -215,7 +215,7 @@ func BenchmarkRPC(b *testing.B) { err = c2.Attach(ctx, d2) assert.NoError(b, err) testKey2 := "testKey2" - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText(testKey2) return nil }) @@ -270,13 +270,13 @@ func BenchmarkRPC(b *testing.B) { doc1 := document.New(key.Key(helper.TestDocKey(b))) doc2 := document.New(key.Key(helper.TestDocKey(b))) - err := doc1.Update(func(root *json.Object) error { + err := doc1.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.SetNewText("k1") text.Edit(0, 0, builder.String()) return nil }) assert.NoError(b, err) - err = doc2.Update(func(root *json.Object) error { + err = doc2.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.SetNewText("k1") text.Edit(0, 0, builder.String()) return nil diff --git a/test/bench/text_editing_bench_test.go b/test/bench/text_editing_bench_test.go index 35dc74f23..facbc6b6e 100644 --- a/test/bench/text_editing_bench_test.go +++ b/test/bench/text_editing_bench_test.go @@ -20,6 +20,7 @@ package bench import ( gojson "encoding/json" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "io" "os" "testing" @@ -41,7 +42,7 @@ func BenchmarkTextEditing(b *testing.B) { b.StartTimer() doc := document.New("d1") - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("text") return nil }) @@ -50,7 +51,7 @@ func BenchmarkTextEditing(b *testing.B) { cursor := int(edit[0].(float64)) mode := int(edit[1].(float64)) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("text") if mode == 0 { value := edit[2].(string) diff --git a/test/helper/helper.go b/test/helper/helper.go index b324d5f27..27456f873 100644 --- a/test/helper/helper.go +++ b/test/helper/helper.go @@ -20,6 +20,7 @@ package helper import ( "context" "fmt" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "log" "strings" "testing" @@ -177,7 +178,7 @@ func ToDiagnostic(node *crdt.TreeNode) string { // BuildIndexTree builds an index tree from the given block node. func BuildIndexTree(node *json.TreeNode) *index.Tree[*crdt.TreeNode] { doc := document.New("test") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("test", node) return nil @@ -192,7 +193,7 @@ func BuildIndexTree(node *json.TreeNode) *index.Tree[*crdt.TreeNode] { // BuildTreeNode builds a crdt.TreeNode from the given tree node. func BuildTreeNode(node *json.TreeNode) *crdt.TreeNode { doc := document.New("test") - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("test", node) return nil diff --git a/test/integration/admin_test.go b/test/integration/admin_test.go index 695e783ee..714b2a467 100644 --- a/test/integration/admin_test.go +++ b/test/integration/admin_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "io" "sync" "testing" @@ -73,7 +74,7 @@ func TestAdmin(t *testing.T) { assert.Equal(t, document.StatusAttached, d1.Status()) // 03. client updates the document and sync to the server. - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") return nil })) diff --git a/test/integration/array_test.go b/test/integration/array_test.go index 5d8568ae0..2e729217c 100644 --- a/test/integration/array_test.go +++ b/test/integration/array_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" @@ -40,7 +41,7 @@ func TestArray(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k1"). AddString("v1"). AddNewArray().AddString("1", "2", "3") @@ -61,7 +62,7 @@ func TestArray(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k1").AddString("v1", "v2") return nil }, "add v1, v2 by c1") @@ -74,13 +75,13 @@ func TestArray(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("k1").Delete(1) return nil }, "delete v2 by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("k1").AddString("v3") return nil }, "add v3 by c2") @@ -95,7 +96,7 @@ func TestArray(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k1").AddString("v1") return nil }, "new array and add v1") @@ -107,14 +108,14 @@ func TestArray(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("k1").AddString("v2", "v3") root.GetArray("k1").Delete(1) return nil }, "add v2, v3 and delete v2 by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("k1").AddString("v4", "v5") return nil }, "add v4, v5 by c2") @@ -129,7 +130,7 @@ func TestArray(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k1").AddString("v1", "v2", "v3") return nil }, "new array and add v1 v2 v3") @@ -141,20 +142,20 @@ func TestArray(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("k1").Delete(1) return nil }, "delete v2") assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("k1").Delete(1) return nil }, "delete v2") assert.NoError(t, err) syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { assert.Equal(t, 2, root.GetArray("k1").Len()) return nil }, "check array length") @@ -168,7 +169,7 @@ func TestArray(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k1").AddInteger(0, 1, 2) assert.Equal(t, `{"k1":[0,1,2]}`, root.Marshal()) return nil @@ -181,7 +182,7 @@ func TestArray(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { prev := root.GetArray("k1").Get(0) elem := root.GetArray("k1").Get(2) root.GetArray("k1").MoveBefore(prev.CreatedAt(), elem.CreatedAt()) @@ -190,7 +191,7 @@ func TestArray(t *testing.T) { }, "move 2 before 0") assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { prev := root.GetArray("k1").Get(1) elem := root.GetArray("k1").Get(2) root.GetArray("k1").MoveBefore(prev.CreatedAt(), elem.CreatedAt()) @@ -206,7 +207,7 @@ func TestArray(t *testing.T) { ctx := context.Background() d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, c1.Attach(ctx, d1)) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k1").AddInteger(0, 1, 2) assert.Equal(t, `{"k1":[0,1,2]}`, root.Marshal()) return nil @@ -215,7 +216,7 @@ func TestArray(t *testing.T) { d2 := document.New(helper.TestDocKey(t)) assert.NoError(t, c2.Attach(ctx, d2)) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { next := root.GetArray("k1").Get(0) elem := root.GetArray("k1").Get(2) root.GetArray("k1").MoveBefore(next.CreatedAt(), elem.CreatedAt()) @@ -223,7 +224,7 @@ func TestArray(t *testing.T) { return nil })) - assert.NoError(t, d2.Update(func(root *json.Object) error { + assert.NoError(t, d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { next := root.GetArray("k1").Get(0) elem := root.GetArray("k1").Get(1) root.GetArray("k1").MoveBefore(next.CreatedAt(), elem.CreatedAt()) diff --git a/test/integration/auth_webhook_test.go b/test/integration/auth_webhook_test.go index 0c5651788..0ffcd01da 100644 --- a/test/integration/auth_webhook_test.go +++ b/test/integration/auth_webhook_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "net/http" "net/http/httptest" "testing" @@ -322,7 +323,7 @@ func TestAuthWebhook(t *testing.T) { // 01. multiple requests to update the document. for i := 0; i < 3; i++ { - assert.NoError(t, doc.Update(func(root *json.Object) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewObject("k1") return nil })) @@ -332,7 +333,7 @@ func TestAuthWebhook(t *testing.T) { // 02. multiple requests to update the document after eviction by ttl. time.Sleep(authorizedTTL) for i := 0; i < 3; i++ { - assert.NoError(t, doc.Update(func(root *json.Object) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewObject("k1") return nil })) diff --git a/test/integration/client_test.go b/test/integration/client_test.go index 3c6a96da3..b55a3d27f 100644 --- a/test/integration/client_test.go +++ b/test/integration/client_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" @@ -88,11 +89,11 @@ func TestClient(t *testing.T) { assert.NoError(t, c3.Attach(ctx, d3)) // 02. c1, c2 sync with push-pull mode. - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetInteger("c1", 0) return nil })) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetInteger("c2", 0) return nil })) @@ -104,11 +105,11 @@ func TestClient(t *testing.T) { // 03. c1 and c2 sync with push-only mode. So, the changes of c1 and c2 // are not reflected to each other. // But, c3 can get the changes of c1 and c2, because c3 sync with pull-pull mode. - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetInteger("c1", 1) return nil })) - assert.NoError(t, d2.Update(func(root *json.Object) error { + assert.NoError(t, d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetInteger("c2", 1) return nil })) @@ -139,7 +140,7 @@ func TestClient(t *testing.T) { // 02. cli update the document with creating a counter // and sync with push-pull mode: CP(0, 0) -> CP(1, 1) - assert.NoError(t, doc.Update(func(root *json.Object) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewCounter("counter", crdt.IntegerCnt, 0) return nil })) @@ -149,7 +150,7 @@ func TestClient(t *testing.T) { // 03. cli update the document with increasing the counter(0 -> 1) // and sync with push-only mode: CP(1, 1) -> CP(2, 1) - assert.NoError(t, doc.Update(func(root *json.Object) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetCounter("counter").Increase(1) return nil })) @@ -159,7 +160,7 @@ func TestClient(t *testing.T) { // 04. cli update the document with increasing the counter(1 -> 2) // and sync with push-pull mode. CP(2, 1) -> CP(3, 3) - assert.NoError(t, doc.Update(func(root *json.Object) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetCounter("counter").Increase(1) return nil })) diff --git a/test/integration/counter_test.go b/test/integration/counter_test.go index 06c601581..492d476dd 100644 --- a/test/integration/counter_test.go +++ b/test/integration/counter_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "math" "testing" @@ -42,7 +43,7 @@ func TestCounter(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewCounter("age", crdt.LongCnt, 1). Increase(2). Increase(2.5). @@ -66,7 +67,7 @@ func TestCounter(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewCounter("age", crdt.IntegerCnt, 0) root.SetNewCounter("width", crdt.LongCnt, 0) root.SetNewCounter("height", crdt.LongCnt, 0) @@ -80,7 +81,7 @@ func TestCounter(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetCounter("age"). Increase(1). Increase(2). @@ -90,13 +91,13 @@ func TestCounter(t *testing.T) { }) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetCounter("width").Increase(math.MaxInt32 + 100).Increase(10) return nil }) assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetCounter("age").Increase(20) root.GetCounter("width").Increase(100).Increase(200) root.GetCounter("height").Increase(50) diff --git a/test/integration/document_test.go b/test/integration/document_test.go index dc7a87303..82c959c7a 100644 --- a/test/integration/document_test.go +++ b/test/integration/document_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "io" "sync" "testing" @@ -42,7 +43,7 @@ func TestDocument(t *testing.T) { t.Run("attach/detach test", func(t *testing.T) { ctx := context.Background() doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") return nil }, "update k1 with v1") @@ -57,7 +58,7 @@ func TestDocument(t *testing.T) { assert.False(t, doc.IsAttached()) doc2 := document.New(helper.TestDocKey(t)) - err = doc2.Update(func(root *json.Object) error { + err = doc2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v2") return nil }, "update k1 with v2") @@ -76,7 +77,7 @@ func TestDocument(t *testing.T) { // 01. create a document and attach it to c1 ctx := context.Background() doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") return nil }, "update k1 with v1") @@ -114,26 +115,26 @@ func TestDocument(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewObject("k1").SetNewArray("k1.1").AddString("1", "2") return nil }) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k2").AddString("1", "2", "3") return nil }) assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k1").AddString("4", "5") root.SetNewArray("k2").AddString("6", "7") return nil }) assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.Delete("k2") return nil }) @@ -182,7 +183,7 @@ func TestDocument(t *testing.T) { }() // 02. cli2 updates doc2. - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("key", "value") return nil }) @@ -200,7 +201,7 @@ func TestDocument(t *testing.T) { ctx := context.Background() d1 := document.New(helper.TestDocKey(t)) - err := d1.Update(func(root *json.Object) error { + err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k1").AddInteger(1, 2) return nil }) @@ -215,7 +216,7 @@ func TestDocument(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("k1").AddInteger(3) return nil }) @@ -224,7 +225,7 @@ func TestDocument(t *testing.T) { prevArray := d1.Root().Get("k1") assert.Nil(t, prevArray.RemovedAt()) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("k1") return nil }) @@ -257,7 +258,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, document.StatusRemoved, d1.Status()) // 04. try to update a removed document. - assert.ErrorIs(t, d1.Update(func(root *json.Object) error { + assert.ErrorIs(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") return nil }), document.ErrDocumentRemoved) @@ -271,7 +272,7 @@ func TestDocument(t *testing.T) { // 01. cli1 creates d1 and removes it. d1 := document.New(helper.TestDocKey(t)) - err := d1.Update(func(root *json.Object) error { + err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") return nil }) @@ -294,7 +295,7 @@ func TestDocument(t *testing.T) { // 01. cli1 creates d1 and cli2 syncs. d1 := document.New(helper.TestDocKey(t)) - err := d1.Update(func(root *json.Object) error { + err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") return nil }) @@ -306,7 +307,7 @@ func TestDocument(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) // 02. cli1 updates d1 and removes it. - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v2") return nil }) @@ -325,7 +326,7 @@ func TestDocument(t *testing.T) { // 01. cli1 creates d1 and cli2 syncs. d1 := document.New(helper.TestDocKey(t)) - err := d1.Update(func(root *json.Object) error { + err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") return nil }) @@ -347,7 +348,7 @@ func TestDocument(t *testing.T) { // 01. cli1 creates d1 and cli2 syncs. d1 := document.New(helper.TestDocKey(t)) - err := d1.Update(func(root *json.Object) error { + err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") return nil }) @@ -516,7 +517,7 @@ func TestDocumentWithProjects(t *testing.T) { expected = append(expected, watchResponsePair{ Type: client.DocumentsChanged, }) - assert.NoError(t, d2.Update(func(root *json.Object) error { + assert.NoError(t, d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("key", "value") return nil })) @@ -528,7 +529,7 @@ func TestDocumentWithProjects(t *testing.T) { defer cancel3() _, err = c3.Watch(watch3Ctx, d3) assert.NoError(t, err) - assert.NoError(t, d3.Update(func(root *json.Object) error { + assert.NoError(t, d3.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("key3", "value3") return nil })) diff --git a/test/integration/gc_test.go b/test/integration/gc_test.go index d425f2b23..aa026201c 100644 --- a/test/integration/gc_test.go +++ b/test/integration/gc_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" @@ -45,7 +46,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetInteger("1", 1) root.SetNewArray("2").AddInteger(1, 2, 3) root.SetInteger("3", 3) @@ -63,7 +64,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Sync(ctx) assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.Delete("2") return nil }, "removes 2") @@ -112,7 +113,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("text"). Edit(0, 0, "Hello world") root.SetNewText("richText"). @@ -131,7 +132,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Sync(ctx) assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("text"). Edit(0, 1, "a"). Edit(1, 2, "b") @@ -177,7 +178,7 @@ func TestGarbageCollection(t *testing.T) { t.Run("garbage collection for tree type test", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -200,7 +201,7 @@ func TestGarbageCollection(t *testing.T) { }) assert.NoError(t, err) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetTree("t").EditByPath([]int{0, 0, 0}, []int{0, 0, 2}, &json.TreeNode{Type: "text", Value: "gh"}) assert.Equal(t, `

ghcd

`, root.GetTree("t").ToXML()) return nil @@ -212,7 +213,7 @@ func TestGarbageCollection(t *testing.T) { assert.Equal(t, doc.GarbageCollect(time.MaxTicket), 2) assert.Equal(t, doc.GarbageLen(), 0) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetTree("t").EditByPath([]int{0, 0, 0}, []int{0, 0, 2}, &json.TreeNode{Type: "text", Value: "cv"}) assert.Equal(t, `

cvcd

`, root.GetTree("t").ToXML()) return nil @@ -224,7 +225,7 @@ func TestGarbageCollection(t *testing.T) { assert.Equal(t, doc.GarbageCollect(time.MaxTicket), 1) assert.Equal(t, doc.GarbageLen(), 0) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetTree("t").EditByPath([]int{0}, []int{1}, &json.TreeNode{ Type: "p", Children: []json.TreeNode{{ Type: "tn", Children: []json.TreeNode{{ @@ -252,7 +253,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -281,7 +282,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Sync(ctx) assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetTree("t").EditByPath([]int{0, 0, 0}, []int{0, 0, 2}, &json.TreeNode{Type: "text", Value: "gh"}) return nil }) @@ -330,7 +331,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetInteger("1", 1) root.SetNewArray("2").AddInteger(1, 2, 3) root.SetInteger("3", 3) @@ -350,7 +351,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Sync(ctx) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.Delete("2") root.GetText("4").Edit(0, 1, "h") root.GetText("5").Edit(0, 1, "h", map[string]string{"b": "1"}) diff --git a/test/integration/history_test.go b/test/integration/history_test.go index b03446da2..a6059a06a 100644 --- a/test/integration/history_test.go +++ b/test/integration/history_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" @@ -43,19 +44,19 @@ func TestHistory(t *testing.T) { assert.NoError(t, cli.Attach(ctx, d1)) defer func() { assert.NoError(t, cli.Detach(ctx, d1, false)) }() - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("todos") return nil }, "create todos")) assert.Equal(t, `{"todos":[]}`, d1.Marshal()) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("todos").AddString("buy coffee") return nil }, "buy coffee")) assert.Equal(t, `{"todos":["buy coffee"]}`, d1.Marshal()) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("todos").AddString("buy bread") return nil }, "buy bread")) diff --git a/test/integration/object_test.go b/test/integration/object_test.go index 33a158c3c..c05bfb933 100644 --- a/test/integration/object_test.go +++ b/test/integration/object_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" @@ -45,7 +46,7 @@ func TestObject(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewObject("k1"). SetString("k1.1", "v1"). SetString("k1.2", "v2"). @@ -59,7 +60,7 @@ func TestObject(t *testing.T) { assert.NoError(t, err) syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.Delete("k1") root.GetObject("k2").Delete("k2.2") return nil @@ -74,7 +75,7 @@ func TestObject(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewObject("k1") return nil }, "set v1 by c1") @@ -87,7 +88,7 @@ func TestObject(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.Delete("k1") root.SetString("k1", "v1") return nil @@ -95,7 +96,7 @@ func TestObject(t *testing.T) { assert.NoError(t, err) assert.Equal(t, `{"k1":"v1"}`, d1.Marshal()) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.Delete("k1") root.SetString("k1", "v2") return nil @@ -116,12 +117,12 @@ func TestObject(t *testing.T) { assert.NoError(t, err) // 01. concurrent set on same key - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v1") return nil }, "set k1 by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k1", "v2") return nil }, "set k1 by c2") @@ -129,19 +130,19 @@ func TestObject(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) // 02. concurrent set between ancestor descendant - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewObject("k2") return nil }, "set k2 by c1") assert.NoError(t, err) syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k2", "v2") return nil }, "set k2 by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetObject("k2").SetNewObject("k2.1").SetString("k2.1.1", "v2") return nil }, "set k2.1.1 by c2") @@ -149,12 +150,12 @@ func TestObject(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) // 03. concurrent set between independent keys - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k3", "v3") return nil }, "set k3 by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("k4", "v4") return nil }, "set k4 by c2") diff --git a/test/integration/peer_awareness_test.go b/test/integration/peer_awareness_test.go index 316d52e22..7409d4221 100644 --- a/test/integration/peer_awareness_test.go +++ b/test/integration/peer_awareness_test.go @@ -20,6 +20,10 @@ package integration import ( "context" + gojson "encoding/json" + "fmt" + "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "sync" "testing" "time" @@ -207,4 +211,32 @@ func TestPeerAwareness(t *testing.T) { assert.Equal(t, expected, responsePairs) }) + + t.Run("allows updatePresence to be called within update", func(t *testing.T) { + // 01. Create a document and attach it to the clients + ctx := context.Background() + d1 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c1.Attach(ctx, d1)) + defer func() { assert.NoError(t, c1.Detach(ctx, d1, false)) }() + d2 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c2.Attach(ctx, d2)) + defer func() { assert.NoError(t, c2.Detach(ctx, d2, false)) }() + + // 02. Update the root of the document and presence + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + root.SetString("key", "value") + p.Set("updated", "true") + return nil + })) + encoded, err := gojson.Marshal(d1.PresenceMap()) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"true"}}`, c1.ID()), string(encoded)) + + // 03 Sync documents and check that the presence is updated on the other client + assert.NoError(t, c1.Sync(ctx)) + assert.NoError(t, c2.Sync(ctx)) + encoded, err = gojson.Marshal(d2.PresenceMap()) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"true"}}`, c1.ID()), string(encoded)) + }) } diff --git a/test/integration/primitive_test.go b/test/integration/primitive_test.go index 24f2e8303..50ec623e6 100644 --- a/test/integration/primitive_test.go +++ b/test/integration/primitive_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "time" @@ -41,7 +42,7 @@ func TestPrimitive(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewObject("k1"). SetBool("k1.1", true). SetInteger("k1.2", 2147483647). diff --git a/test/integration/retention_test.go b/test/integration/retention_test.go index 2f33cd3fa..a7e335ca3 100644 --- a/test/integration/retention_test.go +++ b/test/integration/retention_test.go @@ -87,19 +87,19 @@ func TestRetention(t *testing.T) { assert.NoError(t, cli.Attach(ctx, doc)) defer func() { assert.NoError(t, cli.Detach(ctx, doc, false)) }() - assert.NoError(t, doc.Update(func(root *json.Object) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewArray("todos") return nil }, "create todos")) assert.Equal(t, `{"todos":[]}`, doc.Marshal()) - assert.NoError(t, doc.Update(func(root *json.Object) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("todos").AddString("buy coffee") return nil }, "buy coffee")) assert.Equal(t, `{"todos":["buy coffee"]}`, doc.Marshal()) - assert.NoError(t, doc.Update(func(root *json.Object) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetArray("todos").AddString("buy bread") return nil }, "buy bread")) @@ -149,7 +149,7 @@ func TestRetention(t *testing.T) { assert.NoError(t, cli1.Attach(ctx, d1)) defer func() { assert.NoError(t, cli1.Detach(ctx, d1, false)) }() - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1") return nil }) @@ -167,7 +167,7 @@ func TestRetention(t *testing.T) { // Create 6 changes for _, edit := range edits { - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(edit.from, edit.to, edit.content) return nil }) @@ -226,7 +226,7 @@ func TestRetention(t *testing.T) { // Create 6 changes for _, edit := range edits { - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(edit.from, edit.to, edit.content) return nil }) diff --git a/test/integration/snapshot_test.go b/test/integration/snapshot_test.go index a4f03ca2b..710bb6349 100644 --- a/test/integration/snapshot_test.go +++ b/test/integration/snapshot_test.go @@ -65,7 +65,7 @@ func TestSnapshot(t *testing.T) { // 01. Update changes over snapshot threshold. for i := 0; i <= int(helper.SnapshotThreshold); i++ { - err := d1.Update(func(root *json.Object) error { + err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetInteger(fmt.Sprintf("%d", i), i) return nil }) @@ -75,7 +75,7 @@ func TestSnapshot(t *testing.T) { assert.NoError(t, err) // 02. Makes local changes then pull a snapshot from the server. - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetString("key", "value") return nil }) @@ -95,7 +95,7 @@ func TestSnapshot(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1") return nil }) @@ -116,7 +116,7 @@ func TestSnapshot(t *testing.T) { } for _, edit := range edits { - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(edit.from, edit.to, edit.content) return nil }) @@ -140,7 +140,7 @@ func TestSnapshot(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1") return nil }) @@ -153,14 +153,14 @@ func TestSnapshot(t *testing.T) { assert.NoError(t, err) for i := 0; i <= int(helper.SnapshotThreshold); i++ { - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(i, i, "x") return nil }) assert.NoError(t, err) } - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(0, 0, "o") return nil }) diff --git a/test/integration/text_test.go b/test/integration/text_test.go index c7e39dc0a..a60e95658 100644 --- a/test/integration/text_test.go +++ b/test/integration/text_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" @@ -40,7 +41,7 @@ func TestText(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1") return nil }, "set a new text by c1") @@ -52,13 +53,13 @@ func TestText(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(0, 0, "ABCD") return nil }, "edit 0,0 ABCD by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(0, 0, "1234") return nil }, "edit 0,0 1234 by c2") @@ -66,13 +67,13 @@ func TestText(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(2, 3, "XX") return nil }, "edit 2,3 XX by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(2, 3, "YY") return nil }, "edit 2,3 YY by c2") @@ -80,13 +81,13 @@ func TestText(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(4, 5, "ZZ") return nil }, "edit 4,5 ZZ by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(2, 3, "TT") return nil }, "edit 2,3 TT by c2") @@ -101,7 +102,7 @@ func TestText(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1").Edit(0, 0, "Hello world", nil) return nil }, `set a new text with "Hello world" by c1`) @@ -113,14 +114,14 @@ func TestText(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") text.Style(0, 1, map[string]string{"b": "1"}) return nil }, `set style b to "H" by c1`) assert.NoError(t, err) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { text := root.GetText("k1") text.Style(0, 5, map[string]string{"i": "1"}) return nil @@ -136,7 +137,7 @@ func TestText(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1") return nil }, "set a new text by c1") @@ -148,7 +149,7 @@ func TestText(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(0, 0, "123") root.GetText("k1").Edit(3, 3, "456") root.GetText("k1").Edit(6, 6, "789") @@ -159,14 +160,14 @@ func TestText(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) assert.Equal(t, `{"k1":[{"val":"123"},{"val":"456"},{"val":"789"}]}`, d2.Marshal()) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(1, 7, "") return nil }, "delete block by c1") assert.NoError(t, err) assert.Equal(t, `{"k1":[{"val":"1"},{"val":"89"}]}`, d1.Marshal()) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(2, 5, "") return nil }, "delete block by c2") @@ -182,7 +183,7 @@ func TestText(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewText("k1") return nil }, "set a new text by c1") @@ -194,7 +195,7 @@ func TestText(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(0, 0, "0") root.GetText("k1").Edit(1, 1, "0") root.GetText("k1").Edit(2, 2, "0") @@ -205,7 +206,7 @@ func TestText(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) assert.Equal(t, `{"k1":[{"val":"0"},{"val":"0"},{"val":"0"}]}`, d2.Marshal()) - err = d1.Update(func(root *json.Object) error { + err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(1, 2, "1") root.GetText("k1").Edit(1, 2, "1") root.GetText("k1").Edit(1, 2, "") @@ -214,7 +215,7 @@ func TestText(t *testing.T) { assert.NoError(t, err) assert.Equal(t, `{"k1":[{"val":"0"},{"val":"0"}]}`, d1.Marshal()) - err = d2.Update(func(root *json.Object) error { + err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetText("k1").Edit(0, 3, "") return nil }, "delete the range includes above new nodes") diff --git a/test/integration/tree_test.go b/test/integration/tree_test.go index 9d803e3d8..790f75578 100644 --- a/test/integration/tree_test.go +++ b/test/integration/tree_test.go @@ -20,6 +20,7 @@ package integration import ( "context" + "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" @@ -36,7 +37,7 @@ func TestTree(t *testing.T) { t.Run("tree", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { // 01. Create a tree and insert a paragraph. root.SetNewTree("t").Edit(0, 0, &json.TreeNode{ Type: "p", @@ -92,7 +93,7 @@ func TestTree(t *testing.T) { t.Run("created from JSON test", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -119,7 +120,7 @@ func TestTree(t *testing.T) { t.Run("created from JSON with attributes test", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -139,7 +140,7 @@ func TestTree(t *testing.T) { t.Run("edit its content test", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -172,7 +173,7 @@ func TestTree(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "

ab

", doc.Root().GetTree("t").ToXML()) - err = doc.Update(func(root *json.Object) error { + err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -201,7 +202,7 @@ func TestTree(t *testing.T) { t.Run("edit its content with path", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -241,7 +242,7 @@ func TestTree(t *testing.T) { t.Run("edit its content with attributes test", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object) error { + err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{Type: "doc"}) assert.Equal(t, "", root.GetTree("t").ToXML()) @@ -273,7 +274,7 @@ func TestTree(t *testing.T) { d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, c1.Attach(ctx, d1)) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "root", Children: []json.TreeNode{{ @@ -298,7 +299,7 @@ func TestTree(t *testing.T) { d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, c1.Attach(ctx, d1)) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "root", Children: []json.TreeNode{ @@ -311,7 +312,7 @@ func TestTree(t *testing.T) { assert.NoError(t, c1.Sync(ctx)) assert.Equal(t, `

ab

cd

`, d1.Root().GetTree("t").ToXML()) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetTree("t").Style(3, 4, map[string]string{"bold": "true"}) return nil })) @@ -332,7 +333,7 @@ func TestTree(t *testing.T) { d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, c1.Attach(ctx, d1)) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "root", Children: []json.TreeNode{{ @@ -348,11 +349,11 @@ func TestTree(t *testing.T) { d2 := document.New(helper.TestDocKey(t)) assert.NoError(t, c2.Attach(ctx, d2)) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetTree("t").Edit(1, 1, &json.TreeNode{Type: "text", Value: "A"}) return nil })) - assert.NoError(t, d2.Update(func(root *json.Object) error { + assert.NoError(t, d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetTree("t").Edit(1, 1, &json.TreeNode{Type: "text", Value: "B"}) return nil })) @@ -368,7 +369,7 @@ func TestTree(t *testing.T) { d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, c1.Attach(ctx, d1)) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "root", Children: []json.TreeNode{{ @@ -384,11 +385,11 @@ func TestTree(t *testing.T) { d2 := document.New(helper.TestDocKey(t)) assert.NoError(t, c2.Attach(ctx, d2)) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetTree("t").Edit(2, 2, &json.TreeNode{Type: "text", Value: "A"}) return nil })) - assert.NoError(t, d2.Update(func(root *json.Object) error { + assert.NoError(t, d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetTree("t").Edit(2, 2, &json.TreeNode{Type: "text", Value: "B"}) return nil })) @@ -403,7 +404,7 @@ func TestTree(t *testing.T) { d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, c1.Attach(ctx, d1)) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "root", Children: []json.TreeNode{{ @@ -419,11 +420,11 @@ func TestTree(t *testing.T) { d2 := document.New(helper.TestDocKey(t)) assert.NoError(t, c2.Attach(ctx, d2)) - assert.NoError(t, d1.Update(func(root *json.Object) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetTree("t").Edit(3, 3, &json.TreeNode{Type: "text", Value: "A"}) return nil })) - assert.NoError(t, d2.Update(func(root *json.Object) error { + assert.NoError(t, d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { root.GetTree("t").Edit(3, 3, &json.TreeNode{Type: "text", Value: "B"}) return nil })) From beb4ad0d5903150719980306ccdd9864c2303ed2 Mon Sep 17 00:00:00 2001 From: Youngteac Hong Date: Fri, 7 Jul 2023 18:51:49 +0900 Subject: [PATCH 2/5] Store presence to snapshot --- api/converter/converter_test.go | 10 +- api/converter/from_bytes.go | 21 + api/converter/from_pb.go | 16 +- api/converter/to_bytes.go | 21 + api/converter/to_pb.go | 14 +- api/yorkie/v1/resources.pb.go | 760 +++++++++++++----- api/yorkie/v1/resources.proto | 8 + pkg/document/change/change.go | 10 +- pkg/document/change/context.go | 6 +- pkg/document/document.go | 18 +- pkg/document/document_test.go | 62 +- pkg/document/innerpresence/presence.go | 64 ++ pkg/document/internal_document.go | 30 +- pkg/document/presence/presence.go | 54 +- pkg/document/presenceproxy/proxy.go | 46 -- server/backend/database/change_info.go | 6 +- server/backend/database/memory/database.go | 2 +- server/backend/database/mongo/client.go | 2 +- .../backend/database/testcases/testcases.go | 8 +- server/packs/pushpull.go | 2 +- server/packs/serverpacks.go | 4 +- server/rpc/admin_server.go | 2 +- test/bench/document_bench_test.go | 61 +- test/bench/grpc_bench_test.go | 13 +- test/bench/text_editing_bench_test.go | 6 +- test/helper/helper.go | 6 +- test/integration/admin_test.go | 4 +- test/integration/array_test.go | 36 +- test/integration/auth_webhook_test.go | 6 +- test/integration/client_test.go | 16 +- test/integration/counter_test.go | 12 +- test/integration/document_test.go | 40 +- test/integration/gc_test.go | 26 +- test/integration/history_test.go | 8 +- test/integration/object_test.go | 26 +- test/integration/peer_awareness_test.go | 39 +- test/integration/primitive_test.go | 4 +- test/integration/retention_test.go | 13 +- test/integration/snapshot_test.go | 15 +- test/integration/text_test.go | 38 +- test/integration/tree_test.go | 40 +- 41 files changed, 1027 insertions(+), 548 deletions(-) create mode 100644 pkg/document/innerpresence/presence.go delete mode 100644 pkg/document/presenceproxy/proxy.go diff --git a/api/converter/converter_test.go b/api/converter/converter_test.go index 31da9e119..719c74dcc 100644 --- a/api/converter/converter_test.go +++ b/api/converter/converter_test.go @@ -17,7 +17,6 @@ package converter_test import ( - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "math" "testing" gotime "time" @@ -30,6 +29,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/crdt" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" "github.com/yorkie-team/yorkie/test/helper" ) @@ -42,14 +42,14 @@ func TestConverter(t *testing.T) { doc := document.New("d1") - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1").Edit(0, 0, "A") return nil }) assert.NoError(t, err) assert.Equal(t, `{"k1":[{"val":"A"}]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1").Edit(0, 0, "B") return nil }) @@ -67,7 +67,7 @@ func TestConverter(t *testing.T) { t.Run("snapshot test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { // an object and primitive types root.SetNewObject("k1"). SetNull("k1.0"). @@ -139,7 +139,7 @@ func TestConverter(t *testing.T) { t.Run("change pack test", func(t *testing.T) { d1 := document.New("d1") - err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := d1.Update(func(root *json.Object, p *presence.Presence) error { // an object and primitive types root.SetNewObject("k1"). SetBool("k1.1", true). diff --git a/api/converter/from_bytes.go b/api/converter/from_bytes.go index c82a2e3c8..27e49a188 100644 --- a/api/converter/from_bytes.go +++ b/api/converter/from_bytes.go @@ -24,9 +24,30 @@ import ( api "github.com/yorkie-team/yorkie/api/yorkie/v1" "github.com/yorkie-team/yorkie/pkg/document/crdt" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/document/time" ) +// BytesToSnapshot creates a Snapshot from the given byte array. +func BytesToSnapshot(snapshot []byte) (*crdt.Object, *innerpresence.Map, error) { + if snapshot == nil { + return crdt.NewObject(crdt.NewElementRHT(), time.InitialTicket), innerpresence.NewMap(), nil + } + + pbSnapshot := &api.Snapshot{} + if err := proto.Unmarshal(snapshot, pbSnapshot); err != nil { + return nil, nil, fmt.Errorf("unmarshal snapshot: %w", err) + } + + obj, err := fromJSONElement(pbSnapshot.GetRoot()) + if err != nil { + return nil, nil, err + } + + presenceMap := fromPresenceMap(pbSnapshot.GetPresenceMap()) + return obj.(*crdt.Object), presenceMap, nil +} + // BytesToObject creates an Object from the given byte array. func BytesToObject(snapshot []byte) (*crdt.Object, error) { if snapshot == nil { diff --git a/api/converter/from_pb.go b/api/converter/from_pb.go index 6cd896835..4a192fea6 100644 --- a/api/converter/from_pb.go +++ b/api/converter/from_pb.go @@ -18,7 +18,6 @@ package converter import ( "fmt" - "github.com/yorkie-team/yorkie/pkg/document/presence" protoTypes "github.com/gogo/protobuf/types" @@ -26,6 +25,7 @@ import ( api "github.com/yorkie-team/yorkie/api/yorkie/v1" "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/crdt" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/document/key" "github.com/yorkie-team/yorkie/pkg/document/operations" "github.com/yorkie-team/yorkie/pkg/document/time" @@ -192,7 +192,7 @@ func FromChanges(pbChanges []*api.Change) ([]*change.Change, error) { changeID, pbChange.Message, ops, - FromPresence(pbChange.Presence), + fromPresence(pbChange.Presence), )) } @@ -324,12 +324,20 @@ func FromOperations(pbOps []*api.Operation) ([]operations.Operation, error) { return ops, nil } -func FromPresence(pbPresence *api.Presence) *presence.InternalPresence { +func fromPresenceMap(pbPresenceMap map[string]*api.Presence) *innerpresence.Map { + presenceMap := innerpresence.NewMap() + for id, pbPresence := range pbPresenceMap { + presenceMap.Store(id, fromPresence(pbPresence)) + } + return presenceMap +} + +func fromPresence(pbPresence *api.Presence) *innerpresence.Presence { if pbPresence == nil { return nil } - p := presence.InternalPresence(pbPresence.GetData()) + p := innerpresence.Presence(pbPresence.GetData()) return &p } diff --git a/api/converter/to_bytes.go b/api/converter/to_bytes.go index f6b5b08e5..5049cd609 100644 --- a/api/converter/to_bytes.go +++ b/api/converter/to_bytes.go @@ -24,9 +24,30 @@ import ( api "github.com/yorkie-team/yorkie/api/yorkie/v1" "github.com/yorkie-team/yorkie/pkg/document/crdt" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/index" ) +// SnapshotToBytes converts the given document to byte array. +func SnapshotToBytes(obj *crdt.Object, presenceMap *innerpresence.Map) ([]byte, error) { + pbElem, err := toJSONElement(obj) + if err != nil { + return nil, err + } + + pbPresenceMap := ToPresenceMap(presenceMap) + + bytes, err := proto.Marshal(&api.Snapshot{ + Root: pbElem, + PresenceMap: pbPresenceMap, + }) + if err != nil { + return nil, fmt.Errorf("marshal Snapshot to bytes: %w", err) + } + + return bytes, nil +} + // ObjectToBytes converts the given object to byte array. func ObjectToBytes(obj *crdt.Object) ([]byte, error) { pbElem, err := toJSONElement(obj) diff --git a/api/converter/to_pb.go b/api/converter/to_pb.go index 10cbdef0f..9d929e289 100644 --- a/api/converter/to_pb.go +++ b/api/converter/to_pb.go @@ -18,7 +18,6 @@ package converter import ( "fmt" - "github.com/yorkie-team/yorkie/pkg/document/presence" "reflect" protoTypes "github.com/gogo/protobuf/types" @@ -27,6 +26,7 @@ import ( api "github.com/yorkie-team/yorkie/api/yorkie/v1" "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/crdt" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/document/operations" "github.com/yorkie-team/yorkie/pkg/document/time" "github.com/yorkie-team/yorkie/server/backend/sync" @@ -130,8 +130,18 @@ func ToClient(client types.Client) *api.Client { } } +// ToPresenceMap converts the given model to Protobuf format. +func ToPresenceMap(presenceMap *innerpresence.Map) map[string]*api.Presence { + pbPresences := make(map[string]*api.Presence) + presenceMap.Range(func(k, v interface{}) bool { + pbPresences[k.(string)] = ToPresence(v.(*innerpresence.Presence)) + return true + }) + return pbPresences +} + // ToPresence converts the given model to Protobuf format. -func ToPresence(p *presence.InternalPresence) *api.Presence { +func ToPresence(p *innerpresence.Presence) *api.Presence { if p == nil { return nil } diff --git a/api/yorkie/v1/resources.pb.go b/api/yorkie/v1/resources.pb.go index 5842b72a2..c466e0c11 100644 --- a/api/yorkie/v1/resources.pb.go +++ b/api/yorkie/v1/resources.pb.go @@ -115,6 +115,64 @@ func (DocEventType) EnumDescriptor() ([]byte, []int) { return fileDescriptor_36361b2f5d0f0896, []int{1} } +// /////////////////////////////////////// +// Messages for Snapshot // +// /////////////////////////////////////// +type Snapshot struct { + Root *JSONElement `protobuf:"bytes,1,opt,name=root,proto3" json:"root,omitempty"` + PresenceMap map[string]*Presence `protobuf:"bytes,2,rep,name=presence_map,json=presenceMap,proto3" json:"presence_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Snapshot) Reset() { *m = Snapshot{} } +func (m *Snapshot) String() string { return proto.CompactTextString(m) } +func (*Snapshot) ProtoMessage() {} +func (*Snapshot) Descriptor() ([]byte, []int) { + return fileDescriptor_36361b2f5d0f0896, []int{0} +} +func (m *Snapshot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Snapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Snapshot.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Snapshot) XXX_Merge(src proto.Message) { + xxx_messageInfo_Snapshot.Merge(m, src) +} +func (m *Snapshot) XXX_Size() int { + return m.Size() +} +func (m *Snapshot) XXX_DiscardUnknown() { + xxx_messageInfo_Snapshot.DiscardUnknown(m) +} + +var xxx_messageInfo_Snapshot proto.InternalMessageInfo + +func (m *Snapshot) GetRoot() *JSONElement { + if m != nil { + return m.Root + } + return nil +} + +func (m *Snapshot) GetPresenceMap() map[string]*Presence { + if m != nil { + return m.PresenceMap + } + return nil +} + // ChangePack is a message that contains all changes that occurred in a document. // It is used to synchronize changes between clients and servers. type ChangePack struct { @@ -133,7 +191,7 @@ func (m *ChangePack) Reset() { *m = ChangePack{} } func (m *ChangePack) String() string { return proto.CompactTextString(m) } func (*ChangePack) ProtoMessage() {} func (*ChangePack) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{0} + return fileDescriptor_36361b2f5d0f0896, []int{1} } func (m *ChangePack) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -218,7 +276,7 @@ func (m *Change) Reset() { *m = Change{} } func (m *Change) String() string { return proto.CompactTextString(m) } func (*Change) ProtoMessage() {} func (*Change) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{1} + return fileDescriptor_36361b2f5d0f0896, []int{2} } func (m *Change) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -289,7 +347,7 @@ func (m *ChangeID) Reset() { *m = ChangeID{} } func (m *ChangeID) String() string { return proto.CompactTextString(m) } func (*ChangeID) ProtoMessage() {} func (*ChangeID) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{2} + return fileDescriptor_36361b2f5d0f0896, []int{3} } func (m *ChangeID) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -369,7 +427,7 @@ func (m *Operation) Reset() { *m = Operation{} } func (m *Operation) String() string { return proto.CompactTextString(m) } func (*Operation) ProtoMessage() {} func (*Operation) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{3} + return fileDescriptor_36361b2f5d0f0896, []int{4} } func (m *Operation) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -553,7 +611,7 @@ func (m *Operation_Set) Reset() { *m = Operation_Set{} } func (m *Operation_Set) String() string { return proto.CompactTextString(m) } func (*Operation_Set) ProtoMessage() {} func (*Operation_Set) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{3, 0} + return fileDescriptor_36361b2f5d0f0896, []int{4, 0} } func (m *Operation_Set) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -624,7 +682,7 @@ func (m *Operation_Add) Reset() { *m = Operation_Add{} } func (m *Operation_Add) String() string { return proto.CompactTextString(m) } func (*Operation_Add) ProtoMessage() {} func (*Operation_Add) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{3, 1} + return fileDescriptor_36361b2f5d0f0896, []int{4, 1} } func (m *Operation_Add) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -695,7 +753,7 @@ func (m *Operation_Move) Reset() { *m = Operation_Move{} } func (m *Operation_Move) String() string { return proto.CompactTextString(m) } func (*Operation_Move) ProtoMessage() {} func (*Operation_Move) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{3, 2} + return fileDescriptor_36361b2f5d0f0896, []int{4, 2} } func (m *Operation_Move) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -765,7 +823,7 @@ func (m *Operation_Remove) Reset() { *m = Operation_Remove{} } func (m *Operation_Remove) String() string { return proto.CompactTextString(m) } func (*Operation_Remove) ProtoMessage() {} func (*Operation_Remove) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{3, 3} + return fileDescriptor_36361b2f5d0f0896, []int{4, 3} } func (m *Operation_Remove) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -832,7 +890,7 @@ func (m *Operation_Edit) Reset() { *m = Operation_Edit{} } func (m *Operation_Edit) String() string { return proto.CompactTextString(m) } func (*Operation_Edit) ProtoMessage() {} func (*Operation_Edit) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{3, 4} + return fileDescriptor_36361b2f5d0f0896, []int{4, 4} } func (m *Operation_Edit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -924,7 +982,7 @@ func (m *Operation_Select) Reset() { *m = Operation_Select{} } func (m *Operation_Select) String() string { return proto.CompactTextString(m) } func (*Operation_Select) ProtoMessage() {} func (*Operation_Select) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{3, 5} + return fileDescriptor_36361b2f5d0f0896, []int{4, 5} } func (m *Operation_Select) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -996,7 +1054,7 @@ func (m *Operation_Style) Reset() { *m = Operation_Style{} } func (m *Operation_Style) String() string { return proto.CompactTextString(m) } func (*Operation_Style) ProtoMessage() {} func (*Operation_Style) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{3, 6} + return fileDescriptor_36361b2f5d0f0896, []int{4, 6} } func (m *Operation_Style) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1073,7 +1131,7 @@ func (m *Operation_Increase) Reset() { *m = Operation_Increase{} } func (m *Operation_Increase) String() string { return proto.CompactTextString(m) } func (*Operation_Increase) ProtoMessage() {} func (*Operation_Increase) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{3, 7} + return fileDescriptor_36361b2f5d0f0896, []int{4, 7} } func (m *Operation_Increase) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1138,7 +1196,7 @@ func (m *Operation_TreeEdit) Reset() { *m = Operation_TreeEdit{} } func (m *Operation_TreeEdit) String() string { return proto.CompactTextString(m) } func (*Operation_TreeEdit) ProtoMessage() {} func (*Operation_TreeEdit) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{3, 8} + return fileDescriptor_36361b2f5d0f0896, []int{4, 8} } func (m *Operation_TreeEdit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1217,7 +1275,7 @@ func (m *Operation_TreeStyle) Reset() { *m = Operation_TreeStyle{} } func (m *Operation_TreeStyle) String() string { return proto.CompactTextString(m) } func (*Operation_TreeStyle) ProtoMessage() {} func (*Operation_TreeStyle) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{3, 9} + return fileDescriptor_36361b2f5d0f0896, []int{4, 9} } func (m *Operation_TreeStyle) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1296,7 +1354,7 @@ func (m *JSONElementSimple) Reset() { *m = JSONElementSimple{} } func (m *JSONElementSimple) String() string { return proto.CompactTextString(m) } func (*JSONElementSimple) ProtoMessage() {} func (*JSONElementSimple) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{4} + return fileDescriptor_36361b2f5d0f0896, []int{5} } func (m *JSONElementSimple) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1379,7 +1437,7 @@ func (m *JSONElement) Reset() { *m = JSONElement{} } func (m *JSONElement) String() string { return proto.CompactTextString(m) } func (*JSONElement) ProtoMessage() {} func (*JSONElement) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{5} + return fileDescriptor_36361b2f5d0f0896, []int{6} } func (m *JSONElement) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1515,7 +1573,7 @@ func (m *JSONElement_JSONObject) Reset() { *m = JSONElement_JSONObject{} func (m *JSONElement_JSONObject) String() string { return proto.CompactTextString(m) } func (*JSONElement_JSONObject) ProtoMessage() {} func (*JSONElement_JSONObject) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{5, 0} + return fileDescriptor_36361b2f5d0f0896, []int{6, 0} } func (m *JSONElement_JSONObject) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1586,7 +1644,7 @@ func (m *JSONElement_JSONArray) Reset() { *m = JSONElement_JSONArray{} } func (m *JSONElement_JSONArray) String() string { return proto.CompactTextString(m) } func (*JSONElement_JSONArray) ProtoMessage() {} func (*JSONElement_JSONArray) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{5, 1} + return fileDescriptor_36361b2f5d0f0896, []int{6, 1} } func (m *JSONElement_JSONArray) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1658,7 +1716,7 @@ func (m *JSONElement_Primitive) Reset() { *m = JSONElement_Primitive{} } func (m *JSONElement_Primitive) String() string { return proto.CompactTextString(m) } func (*JSONElement_Primitive) ProtoMessage() {} func (*JSONElement_Primitive) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{5, 2} + return fileDescriptor_36361b2f5d0f0896, []int{6, 2} } func (m *JSONElement_Primitive) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1736,7 +1794,7 @@ func (m *JSONElement_Text) Reset() { *m = JSONElement_Text{} } func (m *JSONElement_Text) String() string { return proto.CompactTextString(m) } func (*JSONElement_Text) ProtoMessage() {} func (*JSONElement_Text) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{5, 3} + return fileDescriptor_36361b2f5d0f0896, []int{6, 3} } func (m *JSONElement_Text) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1808,7 +1866,7 @@ func (m *JSONElement_Counter) Reset() { *m = JSONElement_Counter{} } func (m *JSONElement_Counter) String() string { return proto.CompactTextString(m) } func (*JSONElement_Counter) ProtoMessage() {} func (*JSONElement_Counter) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{5, 4} + return fileDescriptor_36361b2f5d0f0896, []int{6, 4} } func (m *JSONElement_Counter) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1886,7 +1944,7 @@ func (m *JSONElement_Tree) Reset() { *m = JSONElement_Tree{} } func (m *JSONElement_Tree) String() string { return proto.CompactTextString(m) } func (*JSONElement_Tree) ProtoMessage() {} func (*JSONElement_Tree) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{5, 5} + return fileDescriptor_36361b2f5d0f0896, []int{6, 5} } func (m *JSONElement_Tree) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1955,7 +2013,7 @@ func (m *RHTNode) Reset() { *m = RHTNode{} } func (m *RHTNode) String() string { return proto.CompactTextString(m) } func (*RHTNode) ProtoMessage() {} func (*RHTNode) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{6} + return fileDescriptor_36361b2f5d0f0896, []int{7} } func (m *RHTNode) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2010,7 +2068,7 @@ func (m *RGANode) Reset() { *m = RGANode{} } func (m *RGANode) String() string { return proto.CompactTextString(m) } func (*RGANode) ProtoMessage() {} func (*RGANode) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{7} + return fileDescriptor_36361b2f5d0f0896, []int{8} } func (m *RGANode) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2065,7 +2123,7 @@ func (m *NodeAttr) Reset() { *m = NodeAttr{} } func (m *NodeAttr) String() string { return proto.CompactTextString(m) } func (*NodeAttr) ProtoMessage() {} func (*NodeAttr) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{8} + return fileDescriptor_36361b2f5d0f0896, []int{9} } func (m *NodeAttr) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2123,7 +2181,7 @@ func (m *TextNode) Reset() { *m = TextNode{} } func (m *TextNode) String() string { return proto.CompactTextString(m) } func (*TextNode) ProtoMessage() {} func (*TextNode) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{9} + return fileDescriptor_36361b2f5d0f0896, []int{10} } func (m *TextNode) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2199,7 +2257,7 @@ func (m *TextNodeID) Reset() { *m = TextNodeID{} } func (m *TextNodeID) String() string { return proto.CompactTextString(m) } func (*TextNodeID) ProtoMessage() {} func (*TextNodeID) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{10} + return fileDescriptor_36361b2f5d0f0896, []int{11} } func (m *TextNodeID) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2259,7 +2317,7 @@ func (m *TreeNode) Reset() { *m = TreeNode{} } func (m *TreeNode) String() string { return proto.CompactTextString(m) } func (*TreeNode) ProtoMessage() {} func (*TreeNode) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{11} + return fileDescriptor_36361b2f5d0f0896, []int{12} } func (m *TreeNode) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2349,7 +2407,7 @@ func (m *TreePos) Reset() { *m = TreePos{} } func (m *TreePos) String() string { return proto.CompactTextString(m) } func (*TreePos) ProtoMessage() {} func (*TreePos) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{12} + return fileDescriptor_36361b2f5d0f0896, []int{13} } func (m *TreePos) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2405,7 +2463,7 @@ func (m *User) Reset() { *m = User{} } func (m *User) String() string { return proto.CompactTextString(m) } func (*User) ProtoMessage() {} func (*User) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{13} + return fileDescriptor_36361b2f5d0f0896, []int{14} } func (m *User) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2474,7 +2532,7 @@ func (m *Project) Reset() { *m = Project{} } func (m *Project) String() string { return proto.CompactTextString(m) } func (*Project) ProtoMessage() {} func (*Project) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{14} + return fileDescriptor_36361b2f5d0f0896, []int{15} } func (m *Project) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2580,7 +2638,7 @@ func (m *UpdatableProjectFields) Reset() { *m = UpdatableProjectFields{} func (m *UpdatableProjectFields) String() string { return proto.CompactTextString(m) } func (*UpdatableProjectFields) ProtoMessage() {} func (*UpdatableProjectFields) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{15} + return fileDescriptor_36361b2f5d0f0896, []int{16} } func (m *UpdatableProjectFields) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2652,7 +2710,7 @@ func (m *UpdatableProjectFields_AuthWebhookMethods) String() string { } func (*UpdatableProjectFields_AuthWebhookMethods) ProtoMessage() {} func (*UpdatableProjectFields_AuthWebhookMethods) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{15, 0} + return fileDescriptor_36361b2f5d0f0896, []int{16, 0} } func (m *UpdatableProjectFields_AuthWebhookMethods) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2704,7 +2762,7 @@ func (m *DocumentSummary) Reset() { *m = DocumentSummary{} } func (m *DocumentSummary) String() string { return proto.CompactTextString(m) } func (*DocumentSummary) ProtoMessage() {} func (*DocumentSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{16} + return fileDescriptor_36361b2f5d0f0896, []int{17} } func (m *DocumentSummary) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2786,7 +2844,7 @@ func (m *Presence) Reset() { *m = Presence{} } func (m *Presence) String() string { return proto.CompactTextString(m) } func (*Presence) ProtoMessage() {} func (*Presence) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{17} + return fileDescriptor_36361b2f5d0f0896, []int{18} } func (m *Presence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2834,7 +2892,7 @@ func (m *Client) Reset() { *m = Client{} } func (m *Client) String() string { return proto.CompactTextString(m) } func (*Client) ProtoMessage() {} func (*Client) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{18} + return fileDescriptor_36361b2f5d0f0896, []int{19} } func (m *Client) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2889,7 +2947,7 @@ func (m *Checkpoint) Reset() { *m = Checkpoint{} } func (m *Checkpoint) String() string { return proto.CompactTextString(m) } func (*Checkpoint) ProtoMessage() {} func (*Checkpoint) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{19} + return fileDescriptor_36361b2f5d0f0896, []int{20} } func (m *Checkpoint) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2945,7 +3003,7 @@ func (m *TextNodePos) Reset() { *m = TextNodePos{} } func (m *TextNodePos) String() string { return proto.CompactTextString(m) } func (*TextNodePos) ProtoMessage() {} func (*TextNodePos) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{20} + return fileDescriptor_36361b2f5d0f0896, []int{21} } func (m *TextNodePos) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3008,7 +3066,7 @@ func (m *TimeTicket) Reset() { *m = TimeTicket{} } func (m *TimeTicket) String() string { return proto.CompactTextString(m) } func (*TimeTicket) ProtoMessage() {} func (*TimeTicket) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{21} + return fileDescriptor_36361b2f5d0f0896, []int{22} } func (m *TimeTicket) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3071,7 +3129,7 @@ func (m *DocEvent) Reset() { *m = DocEvent{} } func (m *DocEvent) String() string { return proto.CompactTextString(m) } func (*DocEvent) ProtoMessage() {} func (*DocEvent) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{22} + return fileDescriptor_36361b2f5d0f0896, []int{23} } func (m *DocEvent) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3124,6 +3182,8 @@ func (m *DocEvent) GetDocumentId() string { func init() { proto.RegisterEnum("yorkie.v1.ValueType", ValueType_name, ValueType_value) proto.RegisterEnum("yorkie.v1.DocEventType", DocEventType_name, DocEventType_value) + proto.RegisterType((*Snapshot)(nil), "yorkie.v1.Snapshot") + proto.RegisterMapType((map[string]*Presence)(nil), "yorkie.v1.Snapshot.PresenceMapEntry") proto.RegisterType((*ChangePack)(nil), "yorkie.v1.ChangePack") proto.RegisterType((*Change)(nil), "yorkie.v1.Change") proto.RegisterType((*ChangeID)(nil), "yorkie.v1.ChangeID") @@ -3176,159 +3236,228 @@ func init() { func init() { proto.RegisterFile("yorkie/v1/resources.proto", fileDescriptor_36361b2f5d0f0896) } var fileDescriptor_36361b2f5d0f0896 = []byte{ - // 2424 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xcf, 0x8f, 0x1b, 0x49, - 0xf5, 0x9f, 0xb6, 0xdb, 0x3f, 0xfa, 0x79, 0x92, 0x71, 0x6a, 0x92, 0xac, 0xe3, 0x4d, 0x26, 0x13, - 0x67, 0xbf, 0xf9, 0x4e, 0x12, 0xf0, 0x24, 0x43, 0xc2, 0x8f, 0x0d, 0x0b, 0x78, 0xec, 0xde, 0xcc, - 0x64, 0x27, 0x9e, 0x51, 0xdb, 0x93, 0x90, 0x15, 0xa8, 0xd5, 0xd3, 0x5d, 0x89, 0x7b, 0xc7, 0xee, - 0xee, 0xed, 0x2e, 0x7b, 0xe3, 0x1b, 0x42, 0x20, 0xc1, 0x91, 0x1b, 0xff, 0x01, 0xe2, 0xc2, 0x7d, - 0xaf, 0x1c, 0x10, 0x1c, 0x10, 0x48, 0xac, 0xc4, 0x15, 0xc2, 0x01, 0x71, 0x44, 0x08, 0x6e, 0x48, - 0xa8, 0xaa, 0xba, 0xdb, 0xed, 0x76, 0xdb, 0x71, 0xcc, 0x68, 0x95, 0x88, 0x9b, 0xab, 0xea, 0xf3, - 0xaa, 0xde, 0xef, 0x7a, 0xd5, 0xcf, 0x70, 0x61, 0x68, 0xbb, 0xc7, 0x26, 0xde, 0x1c, 0xdc, 0xde, - 0x74, 0xb1, 0x67, 0xf7, 0x5d, 0x1d, 0x7b, 0x55, 0xc7, 0xb5, 0x89, 0x8d, 0x24, 0xbe, 0x54, 0x1d, - 0xdc, 0x2e, 0x5f, 0x7e, 0x66, 0xdb, 0xcf, 0xba, 0x78, 0x93, 0x2d, 0x1c, 0xf5, 0x9f, 0x6e, 0x12, - 0xb3, 0x87, 0x3d, 0xa2, 0xf5, 0x1c, 0x8e, 0x2d, 0xaf, 0xc5, 0x01, 0x9f, 0xb8, 0x9a, 0xe3, 0x60, - 0xd7, 0xdf, 0xab, 0xf2, 0x93, 0x14, 0x40, 0xbd, 0xa3, 0x59, 0xcf, 0xf0, 0x81, 0xa6, 0x1f, 0xa3, - 0x2b, 0xb0, 0x6c, 0xd8, 0x7a, 0xbf, 0x87, 0x2d, 0xa2, 0x1e, 0xe3, 0x61, 0x49, 0x58, 0x17, 0x36, - 0x24, 0xa5, 0x10, 0xcc, 0x7d, 0x80, 0x87, 0xe8, 0x2e, 0x80, 0xde, 0xc1, 0xfa, 0xb1, 0x63, 0x9b, - 0x16, 0x29, 0xa5, 0xd6, 0x85, 0x8d, 0xc2, 0xd6, 0xb9, 0x6a, 0xc8, 0x52, 0xb5, 0x1e, 0x2e, 0x2a, - 0x11, 0x20, 0x2a, 0x43, 0xde, 0xb3, 0x34, 0xc7, 0xeb, 0xd8, 0xa4, 0x94, 0x5e, 0x17, 0x36, 0x96, - 0x95, 0x70, 0x8c, 0x6e, 0x42, 0x4e, 0x67, 0x3c, 0x78, 0x25, 0x71, 0x3d, 0xbd, 0x51, 0xd8, 0x3a, - 0x33, 0xb6, 0x1f, 0x5d, 0x51, 0x02, 0x04, 0xaa, 0xc1, 0x99, 0x9e, 0x69, 0xa9, 0xde, 0xd0, 0xd2, - 0xb1, 0xa1, 0x12, 0x53, 0x3f, 0xc6, 0xa4, 0x94, 0x99, 0x60, 0xa3, 0x6d, 0xf6, 0x70, 0x9b, 0x2d, - 0x2a, 0x2b, 0x3d, 0xd3, 0x6a, 0x31, 0x38, 0x9f, 0x40, 0x97, 0x00, 0x4c, 0x4f, 0x75, 0x71, 0xcf, - 0x1e, 0x60, 0xa3, 0x94, 0x5d, 0x17, 0x36, 0xf2, 0x8a, 0x64, 0x7a, 0x0a, 0x9f, 0xa8, 0xfc, 0x42, - 0x80, 0x2c, 0x3f, 0x15, 0x5d, 0x85, 0x94, 0x69, 0x30, 0x2d, 0x14, 0xb6, 0x56, 0x27, 0x98, 0xda, - 0x6d, 0x28, 0x29, 0xd3, 0x40, 0x25, 0xc8, 0xf5, 0xb0, 0xe7, 0x69, 0xcf, 0x30, 0x53, 0x87, 0xa4, - 0x04, 0x43, 0x74, 0x07, 0xc0, 0x76, 0xb0, 0xab, 0x11, 0xd3, 0xb6, 0xbc, 0x52, 0x9a, 0xc9, 0x76, - 0x36, 0xb2, 0xcd, 0x7e, 0xb0, 0xa8, 0x44, 0x70, 0x68, 0x13, 0xf2, 0x8e, 0x8b, 0x3d, 0x6c, 0xe9, - 0xb8, 0x24, 0x4e, 0x1c, 0x7d, 0xe0, 0x2f, 0x29, 0x21, 0xa8, 0xf2, 0x43, 0x01, 0xf2, 0x01, 0x47, - 0x54, 0x38, 0xbd, 0x6b, 0x52, 0x03, 0x7a, 0xf8, 0x63, 0xc6, 0xfa, 0x29, 0x45, 0xe2, 0x33, 0x2d, - 0xfc, 0x31, 0xba, 0x02, 0xe0, 0x61, 0x77, 0x80, 0x5d, 0xb6, 0x4c, 0xf9, 0x4d, 0x6f, 0xa7, 0x6e, - 0x09, 0x8a, 0xc4, 0x67, 0x29, 0xe4, 0x22, 0xe4, 0xba, 0x5a, 0xcf, 0xb1, 0x5d, 0x6e, 0x29, 0xbe, - 0x1e, 0x4c, 0xa1, 0x0b, 0x90, 0xd7, 0x74, 0x62, 0xbb, 0xaa, 0x69, 0x30, 0xee, 0x96, 0x95, 0x1c, - 0x1b, 0xef, 0x1a, 0x95, 0xdf, 0x94, 0x41, 0x0a, 0x45, 0x42, 0x5f, 0x80, 0xb4, 0x87, 0x89, 0xaf, - 0xbc, 0x52, 0x92, 0xd4, 0xd5, 0x16, 0x26, 0x3b, 0x4b, 0x0a, 0x85, 0x51, 0xb4, 0x66, 0x18, 0xbe, - 0x3f, 0x25, 0xa3, 0x6b, 0x86, 0x41, 0xd1, 0x9a, 0x61, 0xa0, 0x4d, 0x10, 0xa9, 0xad, 0x18, 0x7f, - 0x85, 0xad, 0x0b, 0x89, 0xf0, 0x87, 0xf6, 0x00, 0xef, 0x2c, 0x29, 0x0c, 0x88, 0xee, 0x42, 0x96, - 0xdb, 0xdb, 0xd7, 0xe8, 0xdb, 0x89, 0x24, 0xdc, 0x03, 0x76, 0x96, 0x14, 0x1f, 0x4c, 0xcf, 0xc1, - 0x86, 0x19, 0xf8, 0x57, 0xf2, 0x39, 0xb2, 0x61, 0x52, 0x29, 0x18, 0x90, 0x9e, 0xe3, 0xe1, 0x2e, - 0xd6, 0x09, 0x73, 0xab, 0x69, 0xe7, 0xb4, 0x18, 0x84, 0x9e, 0xc3, 0xc1, 0x68, 0x0b, 0x32, 0x1e, - 0x19, 0x76, 0x71, 0x29, 0xc7, 0xa8, 0xca, 0xc9, 0x54, 0x14, 0xb1, 0xb3, 0xa4, 0x70, 0x28, 0xba, - 0x07, 0x79, 0xd3, 0xd2, 0x5d, 0xac, 0x79, 0xb8, 0x94, 0x67, 0x64, 0x97, 0x12, 0xc9, 0x76, 0x7d, - 0xd0, 0xce, 0x92, 0x12, 0x12, 0xa0, 0xaf, 0x83, 0x44, 0x5c, 0x8c, 0x55, 0x26, 0x9d, 0x34, 0x83, - 0xba, 0xed, 0x62, 0xec, 0x4b, 0x98, 0x27, 0xfe, 0x6f, 0xf4, 0x4d, 0x00, 0x46, 0xcd, 0x79, 0x06, - 0x46, 0xbe, 0x36, 0x95, 0x3c, 0xe0, 0x9b, 0x9d, 0xc8, 0x06, 0xe5, 0x5f, 0x09, 0x90, 0x6e, 0x61, - 0x42, 0x83, 0xd9, 0xd1, 0x5c, 0xea, 0xac, 0x94, 0x2f, 0x82, 0x0d, 0x55, 0x0b, 0x3c, 0x66, 0x5a, - 0x30, 0x73, 0x7c, 0x9d, 0xc3, 0x6b, 0x04, 0x15, 0x21, 0x4d, 0x33, 0x15, 0x8f, 0x3c, 0xfa, 0x93, - 0x2a, 0x73, 0xa0, 0x75, 0xfb, 0x81, 0x77, 0x5c, 0x8c, 0x6c, 0xf4, 0xa0, 0xb5, 0xdf, 0x94, 0xbb, - 0x98, 0xe6, 0xb2, 0x96, 0xd9, 0x73, 0xba, 0x58, 0xe1, 0x50, 0xf4, 0x65, 0x28, 0xe0, 0xe7, 0x58, - 0xef, 0xfb, 0x2c, 0x88, 0xb3, 0x58, 0x80, 0x00, 0x59, 0x23, 0xe5, 0x7f, 0x08, 0x90, 0xae, 0x19, - 0xc6, 0x49, 0x08, 0xf2, 0x1e, 0xac, 0x38, 0x2e, 0x1e, 0x44, 0x37, 0x48, 0xcd, 0xda, 0xe0, 0x14, - 0x45, 0x8f, 0xc8, 0x3f, 0x4f, 0xa9, 0xff, 0x25, 0x80, 0x48, 0xc3, 0xeb, 0x35, 0x10, 0xfb, 0x0e, - 0x40, 0x84, 0x32, 0x3d, 0x8b, 0x52, 0xd2, 0x43, 0xaa, 0x45, 0x05, 0xff, 0x54, 0x80, 0x2c, 0x4f, - 0x12, 0x27, 0x21, 0xfa, 0x38, 0xef, 0xa9, 0xc5, 0x78, 0x4f, 0xcf, 0xcb, 0xfb, 0x2f, 0x45, 0x10, - 0x59, 0xf4, 0x9e, 0x00, 0xe7, 0x37, 0x40, 0x7c, 0xea, 0xda, 0x3d, 0x9f, 0xe7, 0xf3, 0x51, 0x2a, - 0xfc, 0x9c, 0x34, 0x6d, 0x03, 0x1f, 0xd8, 0x9e, 0xc2, 0x30, 0xe8, 0x1a, 0xa4, 0x88, 0xed, 0xb3, - 0x39, 0x0d, 0x99, 0x22, 0x36, 0xea, 0xc0, 0x5b, 0x23, 0x7e, 0xd4, 0x9e, 0xe6, 0xa8, 0x47, 0x43, - 0x95, 0x5d, 0x2d, 0x7e, 0x55, 0xb0, 0x35, 0x35, 0xfd, 0x56, 0x43, 0xce, 0x1e, 0x6a, 0xce, 0xf6, - 0xb0, 0x46, 0x89, 0x64, 0x8b, 0xb8, 0x43, 0x65, 0x55, 0x9f, 0x5c, 0xa1, 0x17, 0xb6, 0x6e, 0x5b, - 0x04, 0x5b, 0x3c, 0xb1, 0x4b, 0x4a, 0x30, 0x8c, 0xeb, 0x36, 0x3b, 0xa7, 0x6e, 0xd1, 0x2e, 0x80, - 0x46, 0x88, 0x6b, 0x1e, 0xf5, 0x09, 0xf6, 0x4a, 0x39, 0xc6, 0xee, 0xf5, 0xe9, 0xec, 0xd6, 0x42, - 0x2c, 0xe7, 0x32, 0x42, 0x5c, 0xfe, 0x2e, 0x94, 0xa6, 0x49, 0x13, 0xe4, 0x3a, 0x61, 0x94, 0xeb, - 0x6e, 0x06, 0x51, 0x3f, 0xd3, 0x7b, 0x38, 0xe6, 0xdd, 0xd4, 0x57, 0x85, 0xf2, 0x7b, 0xb0, 0x12, - 0x3b, 0x3d, 0x61, 0xd7, 0xb3, 0xd1, 0x5d, 0xa5, 0x28, 0xf9, 0x1f, 0x05, 0xc8, 0xf2, 0xdb, 0xeb, - 0x75, 0x75, 0xa3, 0x45, 0x43, 0xfb, 0xcf, 0x29, 0xc8, 0xb0, 0xcb, 0xe9, 0x75, 0x15, 0xec, 0xc1, - 0x98, 0x8f, 0xf1, 0x90, 0xb8, 0x31, 0xbd, 0x50, 0x98, 0xe5, 0x64, 0x71, 0x25, 0x65, 0xe6, 0x55, - 0xd2, 0x7f, 0xe9, 0x3d, 0x9f, 0x0a, 0x90, 0x0f, 0xca, 0x91, 0x93, 0x50, 0xf3, 0xd6, 0xb8, 0xf7, - 0x2f, 0x72, 0xe7, 0xcd, 0x9d, 0x3e, 0xbf, 0x97, 0x82, 0x7c, 0x50, 0x0c, 0x9d, 0x04, 0xef, 0xd7, - 0xc6, 0x5c, 0x04, 0x45, 0xa9, 0x5c, 0x1c, 0x71, 0x8f, 0x4a, 0xc4, 0x3d, 0x92, 0x50, 0xd4, 0x35, - 0xbe, 0x38, 0x4a, 0x68, 0xdc, 0x2f, 0x56, 0x63, 0x40, 0xea, 0x47, 0x53, 0xb3, 0x5c, 0xe6, 0x15, - 0x42, 0x44, 0x0a, 0x0b, 0xba, 0xd7, 0x4d, 0x07, 0xcd, 0x84, 0xf0, 0xa8, 0xce, 0xae, 0x49, 0x5f, - 0xc3, 0x10, 0xd9, 0xce, 0x82, 0x78, 0x64, 0x1b, 0xc3, 0xca, 0xdf, 0x05, 0x38, 0x33, 0xe1, 0xc3, - 0xb1, 0x8a, 0x41, 0x98, 0xb3, 0x62, 0xb8, 0x05, 0x79, 0xf6, 0xb2, 0x7d, 0x69, 0x95, 0x91, 0x63, - 0x30, 0x5e, 0x99, 0xf8, 0xcf, 0xe3, 0x97, 0x57, 0x55, 0x3e, 0xb0, 0x46, 0xd0, 0x06, 0x88, 0x64, - 0xe8, 0xf0, 0x27, 0xd6, 0xe9, 0xb1, 0x87, 0xee, 0x23, 0x2a, 0x5f, 0x7b, 0xe8, 0x60, 0x85, 0x21, - 0x46, 0xf2, 0x67, 0xd8, 0x0b, 0x92, 0x0f, 0x2a, 0x3f, 0x3f, 0x05, 0x85, 0x88, 0xcc, 0xa8, 0x01, - 0x85, 0x8f, 0x3c, 0xdb, 0x52, 0xed, 0xa3, 0x8f, 0xe8, 0x8b, 0x8a, 0x8b, 0x7b, 0x25, 0x39, 0xc8, - 0xd9, 0xef, 0x7d, 0x06, 0xdc, 0x59, 0x52, 0x80, 0xd2, 0xf1, 0x11, 0xaa, 0x01, 0x1b, 0xa9, 0x9a, - 0xeb, 0x6a, 0x43, 0x5f, 0xfe, 0xf5, 0x19, 0x9b, 0xd4, 0x28, 0x8e, 0x3e, 0x57, 0x28, 0x15, 0x1b, - 0xa0, 0x6f, 0x81, 0xe4, 0xb8, 0x66, 0xcf, 0x24, 0x66, 0xf8, 0xe6, 0x9c, 0xb6, 0xc3, 0x41, 0x80, - 0xa3, 0x3b, 0x84, 0x44, 0xe8, 0x36, 0x88, 0x04, 0x3f, 0x0f, 0xdc, 0xe8, 0xed, 0x29, 0xc4, 0x34, - 0xe5, 0xd3, 0xa7, 0x24, 0x85, 0xa2, 0x77, 0x69, 0x50, 0xf7, 0x2d, 0x82, 0x5d, 0xbf, 0x0e, 0x59, - 0x9b, 0x42, 0x55, 0xe7, 0xa8, 0x9d, 0x25, 0x25, 0x20, 0x60, 0xc7, 0xb9, 0x38, 0x78, 0x4e, 0x4e, - 0x3d, 0xce, 0xc5, 0xec, 0x85, 0x4c, 0xa1, 0xe5, 0xcf, 0x04, 0x80, 0x91, 0x0e, 0xd1, 0x06, 0x64, - 0x2c, 0xdb, 0xc0, 0x5e, 0x49, 0x60, 0x91, 0x14, 0x8d, 0x3a, 0x65, 0xa7, 0xcd, 0xf2, 0x09, 0x07, - 0x2c, 0x58, 0xc5, 0x46, 0x7d, 0x32, 0xbd, 0x80, 0x4f, 0x8a, 0xf3, 0xf9, 0x64, 0xf9, 0x0f, 0x02, - 0x48, 0xa1, 0x55, 0x67, 0x4a, 0x75, 0xbf, 0xf6, 0xe6, 0x48, 0xf5, 0x37, 0x01, 0xa4, 0xd0, 0xd3, - 0xc2, 0xb8, 0x13, 0xe6, 0x8f, 0xbb, 0x54, 0x24, 0xee, 0x16, 0x7c, 0x43, 0x45, 0x65, 0x15, 0x17, - 0x90, 0x35, 0x33, 0xa7, 0xac, 0xbf, 0x13, 0x40, 0xa4, 0x81, 0x81, 0xae, 0x8f, 0x1b, 0x6f, 0x35, - 0xa1, 0x56, 0x7a, 0x33, 0xac, 0xf7, 0x57, 0x01, 0x72, 0x7e, 0xd0, 0xfe, 0x2f, 0xd8, 0xce, 0xc5, - 0x78, 0xa6, 0xed, 0x82, 0xfa, 0xe4, 0x4d, 0xb0, 0x5d, 0x78, 0x3f, 0x3f, 0x84, 0x9c, 0x9f, 0x07, - 0x13, 0xae, 0xf7, 0x5b, 0x90, 0xc3, 0x3c, 0xc7, 0x26, 0xbc, 0x00, 0x22, 0x19, 0x58, 0x09, 0x60, - 0x15, 0x1d, 0x72, 0x7e, 0x02, 0xa2, 0x45, 0x91, 0x45, 0xaf, 0x0a, 0x61, 0xa2, 0xdc, 0x09, 0x52, - 0x14, 0x5b, 0x5f, 0xe0, 0x90, 0x47, 0x90, 0xa7, 0xf4, 0xb4, 0x3c, 0x19, 0x79, 0x93, 0x10, 0xa9, - 0x40, 0xa8, 0x4e, 0xfa, 0x8e, 0x31, 0x9f, 0xee, 0x7d, 0x60, 0x8d, 0x54, 0x7e, 0x4b, 0x4b, 0x63, - 0x3f, 0x02, 0xd1, 0xff, 0x45, 0x3e, 0x99, 0x9f, 0x4b, 0x08, 0x51, 0xff, 0xa3, 0x79, 0x62, 0x05, - 0xb4, 0x60, 0xdd, 0x71, 0x17, 0x0a, 0xa6, 0xe5, 0xa9, 0xec, 0x33, 0x92, 0xff, 0x55, 0x7a, 0xea, - 0xd9, 0x92, 0x69, 0x79, 0x07, 0x2e, 0x1e, 0xec, 0x1a, 0xa8, 0x3e, 0x56, 0x31, 0x66, 0x98, 0x63, - 0x5e, 0x4d, 0xa0, 0x9a, 0xf9, 0x5c, 0x57, 0xe6, 0x29, 0xf7, 0xae, 0x8f, 0xbf, 0x53, 0xa2, 0xde, - 0x1f, 0x18, 0x24, 0x52, 0x03, 0x56, 0x3e, 0x04, 0x18, 0x71, 0xbc, 0x60, 0xcd, 0x77, 0x1e, 0xb2, - 0xf6, 0xd3, 0xa7, 0x1e, 0xe6, 0x56, 0xcc, 0x28, 0xfe, 0xa8, 0xf2, 0x4f, 0xff, 0x19, 0xc3, 0x6c, - 0xf5, 0x0e, 0xa4, 0x1d, 0xdb, 0x4b, 0xf0, 0xb4, 0xa0, 0xb0, 0xa6, 0xcb, 0x08, 0xf9, 0x29, 0x8a, - 0x5b, 0x2a, 0x96, 0x8c, 0xd2, 0xd3, 0xcd, 0x37, 0x67, 0x48, 0xa1, 0x3b, 0xb0, 0x1c, 0x9a, 0x8f, - 0xb2, 0x93, 0x99, 0xca, 0x0e, 0xf8, 0xc6, 0x3b, 0xb0, 0x3d, 0xca, 0x81, 0x81, 0x1d, 0xd2, 0x61, - 0xc5, 0x51, 0x46, 0xe1, 0x83, 0x98, 0x4d, 0x73, 0x93, 0x36, 0xf5, 0x45, 0xff, 0xdc, 0x6d, 0xfa, - 0x18, 0x72, 0xbe, 0x14, 0x27, 0x6c, 0xd0, 0x1e, 0x88, 0x87, 0x1e, 0x76, 0xd1, 0xe9, 0x30, 0xee, - 0x24, 0x16, 0x60, 0x65, 0xc8, 0xf7, 0x3d, 0xec, 0x5a, 0x5a, 0x2f, 0xb0, 0x5c, 0x38, 0x46, 0x5f, - 0x4b, 0xb8, 0x34, 0xca, 0x55, 0xde, 0x2a, 0xac, 0x06, 0xad, 0x42, 0xc6, 0x07, 0xeb, 0x25, 0x46, - 0xd8, 0xa8, 0xfc, 0x3b, 0x05, 0xb9, 0x03, 0xd7, 0x66, 0x35, 0x62, 0xfc, 0x48, 0x04, 0x62, 0xe4, - 0x38, 0xf6, 0x1b, 0x5d, 0x02, 0x70, 0xfa, 0x47, 0x5d, 0x53, 0x67, 0xfd, 0x44, 0xee, 0x2d, 0x12, - 0x9f, 0xf9, 0x00, 0x0f, 0xe9, 0xb2, 0x87, 0x75, 0x17, 0xf3, 0x76, 0xa3, 0xc8, 0x97, 0xf9, 0x0c, - 0x5d, 0xde, 0x80, 0xa2, 0xd6, 0x27, 0x1d, 0xf5, 0x13, 0x7c, 0xd4, 0xb1, 0xed, 0x63, 0xb5, 0xef, - 0x76, 0xfd, 0x4f, 0x76, 0xa7, 0xe9, 0xfc, 0x63, 0x3e, 0x7d, 0xe8, 0x76, 0xd1, 0x2d, 0x38, 0x3b, - 0x86, 0xec, 0x61, 0xd2, 0xb1, 0x0d, 0xaf, 0x94, 0x5d, 0x4f, 0x6f, 0x48, 0x0a, 0x8a, 0xa0, 0x1f, - 0xf2, 0x15, 0xf4, 0x0d, 0x78, 0xdb, 0x6f, 0x94, 0x19, 0x58, 0xd3, 0x89, 0x39, 0xd0, 0x08, 0x56, - 0x49, 0xc7, 0xc5, 0x5e, 0xc7, 0xee, 0x1a, 0xac, 0x74, 0x96, 0x94, 0x0b, 0x1c, 0xd2, 0x08, 0x11, - 0xed, 0x00, 0x10, 0x53, 0x62, 0xfe, 0x15, 0x94, 0x48, 0x49, 0x23, 0x69, 0x56, 0x7a, 0x39, 0xe9, - 0x28, 0xd7, 0xfe, 0x28, 0x0d, 0xe7, 0x0f, 0xe9, 0x48, 0x3b, 0xea, 0x62, 0xdf, 0x10, 0xef, 0x9b, - 0xb8, 0x6b, 0x78, 0xe8, 0x96, 0xaf, 0x7e, 0xc1, 0xff, 0x18, 0x12, 0xdf, 0xaf, 0x45, 0x5c, 0xd3, - 0x7a, 0xc6, 0xca, 0x0a, 0xdf, 0x38, 0xef, 0x27, 0xa8, 0x37, 0x35, 0x07, 0x75, 0x5c, 0xf9, 0x4f, - 0xa7, 0x28, 0x9f, 0x7b, 0xd6, 0x9d, 0x88, 0x6f, 0x27, 0xb3, 0x5e, 0xad, 0x4d, 0x98, 0x27, 0xd1, - 0x64, 0xdf, 0x99, 0x6d, 0x32, 0x71, 0x0e, 0xd6, 0xa7, 0x1b, 0xb4, 0x5c, 0x05, 0x34, 0xc9, 0x07, - 0xef, 0xee, 0x72, 0x71, 0x04, 0xe6, 0x4b, 0xc1, 0xb0, 0xf2, 0xfd, 0x14, 0xac, 0x34, 0xfc, 0xce, - 0x78, 0xab, 0xdf, 0xeb, 0x69, 0xee, 0x70, 0x22, 0x24, 0x26, 0xbb, 0x53, 0xf1, 0x46, 0xb8, 0x14, - 0x69, 0x84, 0x8f, 0xbb, 0x94, 0xf8, 0x2a, 0x2e, 0x75, 0x0f, 0x0a, 0x9a, 0xae, 0x63, 0xcf, 0x8b, - 0x16, 0x68, 0xb3, 0x68, 0x21, 0x80, 0x4f, 0xf8, 0x63, 0xf6, 0x55, 0xfc, 0x71, 0x00, 0xf9, 0xa0, - 0x23, 0x4d, 0x5f, 0x9d, 0xd4, 0xba, 0x7e, 0x8d, 0x77, 0x29, 0xa1, 0x69, 0x5d, 0x6d, 0x68, 0x44, - 0xe3, 0x09, 0x97, 0x41, 0xcb, 0x5f, 0x01, 0x29, 0x9c, 0x7a, 0x95, 0xef, 0x24, 0x95, 0x5d, 0xc8, - 0xd6, 0x99, 0x25, 0x23, 0x2a, 0x5f, 0x66, 0x2a, 0x8f, 0xb6, 0xcf, 0x53, 0xf3, 0xb4, 0xcf, 0x9b, - 0x00, 0xa3, 0x3f, 0x2d, 0xc4, 0x1a, 0xe4, 0x42, 0x52, 0x83, 0x7c, 0xbc, 0xc5, 0x9e, 0x8a, 0xb5, - 0xd8, 0x2b, 0x3f, 0x10, 0xa0, 0x10, 0xf9, 0x78, 0x7b, 0xb2, 0xf9, 0x1e, 0xfd, 0x3f, 0xac, 0xb8, - 0xb8, 0xab, 0xd1, 0x87, 0x9f, 0xea, 0x03, 0xd2, 0x0c, 0x70, 0x3a, 0x98, 0xde, 0xe7, 0x17, 0x83, - 0x0e, 0x30, 0xda, 0x39, 0xda, 0xd4, 0x17, 0x26, 0x9b, 0xfa, 0x17, 0x41, 0x32, 0x70, 0x97, 0xbe, - 0x27, 0xb1, 0x1b, 0x08, 0x14, 0x4e, 0x8c, 0xb5, 0xfc, 0xd3, 0xe3, 0x2d, 0xff, 0x1f, 0x0b, 0x90, - 0x6f, 0xd8, 0xba, 0x3c, 0xa0, 0x96, 0xb8, 0x39, 0xf6, 0x96, 0x79, 0x2b, 0x22, 0x62, 0x00, 0x89, - 0x3c, 0x67, 0x36, 0x81, 0x5f, 0x03, 0x5e, 0xc7, 0x3f, 0x32, 0xf6, 0xb7, 0x0f, 0xa6, 0x4e, 0x65, - 0x84, 0x41, 0x97, 0x21, 0xfc, 0x1f, 0x4a, 0xc0, 0x88, 0xa4, 0x40, 0x30, 0xb5, 0x6b, 0xdc, 0xf8, - 0x2c, 0x05, 0x52, 0xf8, 0x68, 0x42, 0xab, 0xb0, 0xf2, 0xa8, 0xb6, 0x77, 0x28, 0xab, 0xed, 0x27, - 0x07, 0xb2, 0xda, 0x3c, 0xdc, 0xdb, 0x2b, 0x2e, 0xa1, 0xf3, 0x80, 0x22, 0x93, 0xdb, 0xfb, 0xfb, - 0x7b, 0x72, 0xad, 0x59, 0x14, 0x62, 0xf3, 0xbb, 0xcd, 0xb6, 0x7c, 0x5f, 0x56, 0x8a, 0xa9, 0xd8, - 0x26, 0x7b, 0xfb, 0xcd, 0xfb, 0xc5, 0x34, 0x3a, 0x07, 0x67, 0x22, 0x93, 0x8d, 0xfd, 0xc3, 0xed, - 0x3d, 0xb9, 0x28, 0xc6, 0xa6, 0x5b, 0x6d, 0x65, 0xb7, 0x79, 0xbf, 0x98, 0x41, 0x67, 0xa1, 0x18, - 0x3d, 0xf2, 0x49, 0x5b, 0x6e, 0x15, 0xb3, 0xb1, 0x8d, 0x1b, 0xb5, 0xb6, 0x5c, 0xcc, 0xa1, 0x32, - 0x9c, 0x8f, 0x4c, 0xd2, 0x12, 0x5e, 0xdd, 0xdf, 0x7e, 0x20, 0xd7, 0xdb, 0xc5, 0x3c, 0xba, 0x00, - 0xe7, 0xe2, 0x6b, 0x35, 0x45, 0xa9, 0x3d, 0x29, 0x4a, 0xb1, 0xbd, 0xda, 0xf2, 0xb7, 0xdb, 0x45, - 0x88, 0xed, 0xe5, 0x4b, 0xa4, 0xd6, 0x9b, 0xed, 0x62, 0x01, 0xbd, 0x05, 0xab, 0x31, 0xa9, 0xd8, - 0xc2, 0x72, 0x7c, 0x27, 0x45, 0x96, 0x8b, 0xa7, 0x6e, 0xfc, 0x4c, 0x80, 0xe5, 0xa8, 0xfd, 0xd0, - 0x3b, 0xb0, 0xde, 0xd8, 0xaf, 0xab, 0xf2, 0x23, 0xb9, 0xd9, 0x0e, 0x74, 0x50, 0x3f, 0x7c, 0x28, - 0x37, 0xdb, 0x2d, 0xb5, 0xbe, 0x53, 0x6b, 0xde, 0x97, 0x1b, 0xc5, 0xa5, 0x99, 0xa8, 0xc7, 0xb5, - 0x76, 0x7d, 0x47, 0x6e, 0x14, 0x05, 0x74, 0x0d, 0x2a, 0x53, 0x51, 0x87, 0xcd, 0x00, 0x97, 0x42, - 0x57, 0xe1, 0x72, 0x0c, 0x77, 0xa0, 0xc8, 0x2d, 0xb9, 0x59, 0x97, 0xc3, 0x23, 0xd3, 0xdb, 0x37, - 0x7f, 0xfd, 0x62, 0x4d, 0xf8, 0xfd, 0x8b, 0x35, 0xe1, 0x4f, 0x2f, 0xd6, 0x84, 0x9f, 0xfe, 0x65, - 0x6d, 0x09, 0xce, 0x18, 0x78, 0x10, 0x38, 0x95, 0xe6, 0x98, 0xd5, 0xc1, 0xed, 0x03, 0xe1, 0x43, - 0xb1, 0x7a, 0x6f, 0x70, 0xfb, 0x28, 0xcb, 0xf2, 0xda, 0x97, 0xfe, 0x13, 0x00, 0x00, 0xff, 0xff, - 0x71, 0x73, 0x1f, 0x35, 0x69, 0x25, 0x00, 0x00, + // 2482 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xcd, 0x6f, 0xdb, 0xc8, + 0xd9, 0x37, 0x29, 0xea, 0x83, 0x8f, 0x9c, 0x58, 0x19, 0xe7, 0x43, 0x51, 0x12, 0xc7, 0x51, 0xf2, + 0xe6, 0x75, 0x92, 0x56, 0x4e, 0xdc, 0xa4, 0x1f, 0x9b, 0x6e, 0x5b, 0x59, 0xe6, 0xda, 0xce, 0xda, + 0xb2, 0x41, 0xc9, 0x49, 0xb3, 0x68, 0x41, 0xd0, 0xe4, 0x24, 0xe6, 0x5a, 0x12, 0xb9, 0x24, 0xa5, + 0x8d, 0x6e, 0x45, 0xd1, 0x02, 0xed, 0xb1, 0xb7, 0xfe, 0x07, 0x45, 0x2f, 0xbd, 0xef, 0xb5, 0x87, + 0xa2, 0x3d, 0x14, 0x2d, 0xd0, 0x00, 0xbd, 0xb6, 0xe9, 0xa1, 0xe8, 0xb1, 0x28, 0xda, 0x5b, 0x81, + 0x62, 0x66, 0x38, 0x14, 0x45, 0x51, 0x8a, 0xa2, 0x1a, 0x8b, 0x04, 0xbd, 0x69, 0x66, 0x7e, 0xcf, + 0xcc, 0xf3, 0x3d, 0xcf, 0xf0, 0x11, 0x5c, 0xec, 0xdb, 0xee, 0xb1, 0x85, 0x57, 0x7b, 0xf7, 0x56, + 0x5d, 0xec, 0xd9, 0x5d, 0xd7, 0xc0, 0x5e, 0xc5, 0x71, 0x6d, 0xdf, 0x46, 0x32, 0x5b, 0xaa, 0xf4, + 0xee, 0x95, 0xae, 0x3e, 0xb7, 0xed, 0xe7, 0x2d, 0xbc, 0x4a, 0x17, 0x0e, 0xbb, 0xcf, 0x56, 0x7d, + 0xab, 0x8d, 0x3d, 0x5f, 0x6f, 0x3b, 0x0c, 0x5b, 0x5a, 0x8a, 0x03, 0x3e, 0x75, 0x75, 0xc7, 0xc1, + 0x6e, 0xb0, 0x57, 0xf9, 0xa5, 0x00, 0xb9, 0x46, 0x47, 0x77, 0xbc, 0x23, 0xdb, 0x47, 0xb7, 0x41, + 0x72, 0x6d, 0xdb, 0x2f, 0x0a, 0xcb, 0xc2, 0x4a, 0x7e, 0xed, 0x7c, 0x25, 0x3c, 0xa7, 0xf2, 0xa8, + 0xb1, 0x57, 0x57, 0x5a, 0xb8, 0x8d, 0x3b, 0xbe, 0x4a, 0x31, 0x68, 0x13, 0xe6, 0x1d, 0x17, 0x7b, + 0xb8, 0x63, 0x60, 0xad, 0xad, 0x3b, 0x45, 0x71, 0x39, 0xb5, 0x92, 0x5f, 0xbb, 0x11, 0xa1, 0xe1, + 0xdb, 0x56, 0xf6, 0x03, 0xdc, 0xae, 0xee, 0x28, 0x1d, 0xdf, 0xed, 0xab, 0x79, 0x67, 0x30, 0x53, + 0x6a, 0x40, 0x21, 0x0e, 0x40, 0x05, 0x48, 0x1d, 0xe3, 0x3e, 0xe5, 0x43, 0x56, 0xc9, 0x4f, 0x74, + 0x0b, 0xd2, 0x3d, 0xbd, 0xd5, 0xc5, 0x45, 0x91, 0xf2, 0xb6, 0x18, 0x39, 0x87, 0x53, 0xab, 0x0c, + 0xf1, 0x9e, 0xf8, 0x55, 0xa1, 0xfc, 0x13, 0x11, 0xa0, 0x76, 0xa4, 0x77, 0x9e, 0xe3, 0x7d, 0xdd, + 0x38, 0x46, 0xd7, 0x60, 0xde, 0xb4, 0x8d, 0x2e, 0x61, 0x5f, 0x1b, 0x6c, 0x9c, 0xe7, 0x73, 0x1f, + 0xe2, 0x3e, 0x7a, 0x00, 0x60, 0x1c, 0x61, 0xe3, 0xd8, 0xb1, 0xad, 0x8e, 0x1f, 0x9c, 0x72, 0x2e, + 0x72, 0x4a, 0x2d, 0x5c, 0x54, 0x23, 0x40, 0x54, 0x82, 0x9c, 0x17, 0xc8, 0x59, 0x4c, 0x2d, 0x0b, + 0x2b, 0xf3, 0x6a, 0x38, 0x46, 0x77, 0x20, 0x6b, 0x50, 0x1e, 0xbc, 0xa2, 0x44, 0xb5, 0x73, 0x66, + 0x68, 0x3f, 0xb2, 0xa2, 0x72, 0x04, 0xaa, 0xc2, 0x99, 0xb6, 0xd5, 0xd1, 0xbc, 0x7e, 0xc7, 0xc0, + 0xa6, 0xe6, 0x5b, 0xc6, 0x31, 0xf6, 0x8b, 0xe9, 0x11, 0x36, 0x9a, 0x56, 0x1b, 0x37, 0xe9, 0xa2, + 0xba, 0xd0, 0xb6, 0x3a, 0x0d, 0x0a, 0x67, 0x13, 0xe8, 0x0a, 0x80, 0xe5, 0x69, 0x2e, 0x6e, 0xdb, + 0x3d, 0x6c, 0x16, 0x33, 0xcb, 0xc2, 0x4a, 0x4e, 0x95, 0x2d, 0x4f, 0x65, 0x13, 0xe5, 0x5f, 0x08, + 0x90, 0x61, 0xa7, 0xa2, 0xeb, 0x20, 0x5a, 0x66, 0x60, 0xe6, 0xc5, 0x11, 0xa6, 0xb6, 0x37, 0x54, + 0xd1, 0x32, 0x51, 0x11, 0xb2, 0x6d, 0xec, 0x79, 0xfa, 0x73, 0xa6, 0x74, 0x59, 0xe5, 0x43, 0x74, + 0x1f, 0xc0, 0x76, 0xb0, 0xab, 0xfb, 0x96, 0xdd, 0xf1, 0x8a, 0x29, 0x2a, 0xdb, 0xd9, 0xc8, 0x36, + 0x7b, 0x7c, 0x51, 0x8d, 0xe0, 0xd0, 0x2a, 0xe4, 0xb8, 0xdd, 0x8b, 0xd2, 0x78, 0x2b, 0x86, 0xa0, + 0xf2, 0x0f, 0x05, 0xc8, 0x71, 0x8e, 0x88, 0x70, 0x46, 0xcb, 0x22, 0x06, 0xf4, 0xf0, 0x27, 0x94, + 0xf5, 0x53, 0xaa, 0xcc, 0x66, 0x1a, 0xf8, 0x13, 0x74, 0x0d, 0xc0, 0xc3, 0x6e, 0x0f, 0xbb, 0x74, + 0x99, 0xf0, 0x9b, 0x5a, 0x17, 0xef, 0x0a, 0xaa, 0xcc, 0x66, 0x09, 0xe4, 0x32, 0x64, 0x5b, 0x7a, + 0xdb, 0xb1, 0x5d, 0x66, 0x29, 0xb6, 0xce, 0xa7, 0xd0, 0x45, 0xc8, 0xe9, 0x86, 0x6f, 0xbb, 0x9a, + 0x65, 0x52, 0xee, 0xe6, 0xd5, 0x2c, 0x1d, 0x6f, 0x9b, 0xe5, 0xdf, 0x94, 0x40, 0x0e, 0x45, 0x42, + 0x5f, 0x80, 0x94, 0x87, 0x79, 0x8c, 0x14, 0x93, 0xa4, 0xae, 0x34, 0xb0, 0xbf, 0x35, 0xa7, 0x12, + 0x18, 0x41, 0xeb, 0xa6, 0x19, 0xf8, 0x53, 0x32, 0xba, 0x6a, 0x9a, 0x04, 0xad, 0x9b, 0x26, 0x5a, + 0x05, 0x89, 0xd8, 0x8a, 0xf2, 0x97, 0x5f, 0xbb, 0x98, 0x08, 0xdf, 0xb5, 0x7b, 0x78, 0x6b, 0x4e, + 0xa5, 0x40, 0xf4, 0x00, 0x32, 0xcc, 0xde, 0x81, 0x46, 0x2f, 0x25, 0x92, 0x30, 0x0f, 0xd8, 0x9a, + 0x53, 0x03, 0x30, 0x39, 0x07, 0x9b, 0x16, 0xf7, 0xaf, 0xe4, 0x73, 0x14, 0xd3, 0x22, 0x52, 0x50, + 0x20, 0x39, 0xc7, 0xc3, 0x2d, 0x6c, 0xf8, 0xd4, 0xad, 0xc6, 0x9d, 0xd3, 0xa0, 0x10, 0x72, 0x0e, + 0x03, 0xa3, 0x35, 0x48, 0x7b, 0x7e, 0xbf, 0x85, 0x8b, 0x59, 0x4a, 0x55, 0x4a, 0xa6, 0x22, 0x88, + 0xad, 0x39, 0x95, 0x41, 0xd1, 0x43, 0xc8, 0x59, 0x1d, 0xc3, 0xc5, 0xba, 0x87, 0x8b, 0x39, 0x4a, + 0x76, 0x25, 0x91, 0x6c, 0x3b, 0x00, 0x6d, 0xcd, 0xa9, 0x21, 0x01, 0xfa, 0x3a, 0xc8, 0xbe, 0x8b, + 0xb1, 0x46, 0xa5, 0x93, 0x27, 0x50, 0x37, 0x5d, 0x8c, 0x03, 0x09, 0x73, 0x7e, 0xf0, 0x1b, 0x7d, + 0x13, 0x80, 0x52, 0x33, 0x9e, 0x81, 0x92, 0x2f, 0x8d, 0x25, 0xe7, 0x7c, 0xd3, 0x13, 0xe9, 0xa0, + 0xf4, 0x2b, 0x01, 0x52, 0x0d, 0xec, 0x93, 0x60, 0x76, 0x74, 0x97, 0x38, 0x2b, 0xe1, 0xcb, 0xc7, + 0xa6, 0xa6, 0x73, 0x8f, 0x19, 0x17, 0xcc, 0x0c, 0x5f, 0x63, 0xf0, 0xaa, 0xcf, 0x53, 0xa0, 0x38, + 0x48, 0x81, 0x6b, 0x3c, 0x05, 0x32, 0xef, 0xb8, 0x9c, 0x9c, 0x9e, 0x1b, 0x56, 0xdb, 0x69, 0xf1, + 0x5c, 0x88, 0xbe, 0x0c, 0x79, 0xfc, 0x02, 0x1b, 0xdd, 0x80, 0x05, 0x69, 0x12, 0x0b, 0xc0, 0x91, + 0x55, 0xbf, 0xf4, 0x0f, 0x01, 0x52, 0x55, 0xd3, 0x3c, 0x09, 0x41, 0xde, 0x87, 0x05, 0xc7, 0xc5, + 0xbd, 0xe8, 0x06, 0xe2, 0xa4, 0x0d, 0x4e, 0x11, 0xf4, 0x80, 0xfc, 0xf3, 0x94, 0xfa, 0x5f, 0x02, + 0x48, 0x24, 0xbc, 0xde, 0x02, 0xb1, 0xef, 0x03, 0x44, 0x28, 0x53, 0x93, 0x28, 0x65, 0x23, 0xa4, + 0x9a, 0x55, 0xf0, 0xcf, 0x04, 0xc8, 0xb0, 0x24, 0x71, 0x12, 0xa2, 0x0f, 0xf3, 0x2e, 0xce, 0xc6, + 0x7b, 0x6a, 0x5a, 0xde, 0x7f, 0x29, 0x81, 0x44, 0xa3, 0xf7, 0x04, 0x38, 0xbf, 0x0d, 0xd2, 0x33, + 0xd7, 0x6e, 0x07, 0x3c, 0x47, 0x0b, 0xa0, 0x26, 0x7e, 0xe1, 0xd7, 0x6d, 0x13, 0xef, 0xdb, 0x9e, + 0x4a, 0x31, 0xe8, 0x26, 0x88, 0xbe, 0x1d, 0xb0, 0x39, 0x0e, 0x29, 0xfa, 0x36, 0x3a, 0x82, 0x0b, + 0x03, 0x7e, 0x48, 0xa9, 0xa4, 0x1d, 0xf6, 0x35, 0x7a, 0xb5, 0x04, 0x55, 0xc1, 0xda, 0xd8, 0xf4, + 0x5b, 0x09, 0x39, 0xdb, 0xd5, 0x9d, 0xf5, 0x7e, 0x95, 0x10, 0xb1, 0x0a, 0x6a, 0xd1, 0x18, 0x5d, + 0x21, 0x17, 0xb6, 0x61, 0x77, 0x7c, 0xdc, 0x61, 0x89, 0x5d, 0x56, 0xf9, 0x30, 0xae, 0xdb, 0xcc, + 0x94, 0xba, 0x45, 0xdb, 0x00, 0xba, 0xef, 0xbb, 0xd6, 0x61, 0xd7, 0xc7, 0x5e, 0x31, 0x4b, 0xd9, + 0xbd, 0x35, 0x9e, 0xdd, 0x6a, 0x88, 0x65, 0x5c, 0x46, 0x88, 0x4b, 0xdf, 0x85, 0xe2, 0x38, 0x69, + 0x12, 0xca, 0xbd, 0x3b, 0xc3, 0xe5, 0xde, 0x18, 0x56, 0x07, 0x05, 0x5f, 0xe9, 0x7d, 0x58, 0x88, + 0x9d, 0x9e, 0xb0, 0xeb, 0xd9, 0xe8, 0xae, 0x72, 0x94, 0xfc, 0x8f, 0x02, 0x64, 0xd8, 0xed, 0xf5, + 0xb6, 0xba, 0xd1, 0xac, 0xa1, 0xfd, 0x67, 0x11, 0xd2, 0xf4, 0x72, 0x7a, 0x5b, 0x05, 0x7b, 0x34, + 0xe4, 0x63, 0x2c, 0x24, 0x6e, 0x8f, 0x2f, 0x14, 0x26, 0x39, 0x59, 0x5c, 0x49, 0xe9, 0x69, 0x95, + 0xf4, 0x5f, 0x7a, 0xcf, 0x67, 0x02, 0xe4, 0x78, 0x39, 0x72, 0x12, 0x6a, 0x5e, 0x1b, 0xf6, 0xfe, + 0x59, 0xee, 0xbc, 0xa9, 0xd3, 0xe7, 0xf7, 0x44, 0xc8, 0xf1, 0x62, 0xe8, 0x24, 0x78, 0xbf, 0x39, + 0xe4, 0x22, 0x28, 0x4a, 0xe5, 0xe2, 0x88, 0x7b, 0x94, 0x23, 0xee, 0x91, 0x84, 0x22, 0xae, 0xf1, + 0xc5, 0x41, 0x42, 0x63, 0x7e, 0xb1, 0x18, 0x03, 0x12, 0x3f, 0x1a, 0x9b, 0xe5, 0xd2, 0x6f, 0x10, + 0x22, 0x72, 0x58, 0xd0, 0xbd, 0x6d, 0x3a, 0xa8, 0x27, 0x84, 0x47, 0x65, 0x72, 0x4d, 0xfa, 0x16, + 0x86, 0xc8, 0x7a, 0x06, 0xa4, 0x43, 0xdb, 0xec, 0x97, 0xff, 0x2e, 0xc0, 0x99, 0x11, 0x1f, 0x8e, + 0x55, 0x0c, 0xc2, 0x94, 0x15, 0xc3, 0x5d, 0xc8, 0xd1, 0x97, 0xed, 0x6b, 0xab, 0x8c, 0x2c, 0x85, + 0xb1, 0xca, 0x24, 0x78, 0x1e, 0xbf, 0xbe, 0xaa, 0x0a, 0x80, 0x55, 0x1f, 0xad, 0x80, 0xe4, 0xf7, + 0x1d, 0xf6, 0xc4, 0x3a, 0x3d, 0xf4, 0xd0, 0x7d, 0x4c, 0xe4, 0x6b, 0xf6, 0x1d, 0xac, 0x52, 0xc4, + 0x40, 0xfe, 0x34, 0x7d, 0x41, 0xb2, 0x41, 0xf9, 0xe7, 0xa7, 0x20, 0x1f, 0x91, 0x19, 0x6d, 0x40, + 0xfe, 0x63, 0xcf, 0xee, 0x68, 0xf6, 0xe1, 0xc7, 0xe4, 0x45, 0xc5, 0xc4, 0xbd, 0x96, 0x1c, 0xe4, + 0xf4, 0xf7, 0x1e, 0x05, 0x6e, 0xcd, 0xa9, 0x40, 0xe8, 0xd8, 0x08, 0x55, 0x81, 0x8e, 0x34, 0xdd, + 0x75, 0xf5, 0x7e, 0x20, 0xff, 0xf2, 0x84, 0x4d, 0xaa, 0x04, 0x47, 0x9e, 0x2b, 0x84, 0x8a, 0x0e, + 0xd0, 0xb7, 0x40, 0x76, 0x5c, 0xab, 0x6d, 0xf9, 0x56, 0xf8, 0xe6, 0x1c, 0xb7, 0xc3, 0x3e, 0xc7, + 0x91, 0x1d, 0x42, 0x22, 0x74, 0x0f, 0x24, 0x1f, 0xbf, 0xe0, 0x6e, 0x74, 0x69, 0x0c, 0x31, 0x49, + 0xf9, 0xe4, 0x29, 0x49, 0xa0, 0xe8, 0x3d, 0x12, 0xd4, 0xdd, 0x8e, 0x8f, 0xdd, 0xa0, 0x0e, 0x59, + 0x1a, 0x43, 0x55, 0x63, 0xa8, 0xad, 0x39, 0x95, 0x13, 0xd0, 0xe3, 0x5c, 0xcc, 0x9f, 0x93, 0x63, + 0x8f, 0x73, 0x31, 0x7d, 0x21, 0x13, 0x68, 0xe9, 0xa5, 0x00, 0x30, 0xd0, 0x21, 0x5a, 0x81, 0x74, + 0xc7, 0x36, 0xb1, 0x57, 0x14, 0x68, 0x24, 0x45, 0xa3, 0x4e, 0xdd, 0x6a, 0xd2, 0x7c, 0xc2, 0x00, + 0x33, 0x56, 0xb1, 0x51, 0x9f, 0x4c, 0xcd, 0xe0, 0x93, 0xd2, 0x74, 0x3e, 0x59, 0xfa, 0x83, 0x00, + 0x72, 0x68, 0xd5, 0x89, 0x52, 0x6d, 0x56, 0xdf, 0x1d, 0xa9, 0xfe, 0x26, 0x80, 0x1c, 0x7a, 0x5a, + 0x18, 0x77, 0xc2, 0xf4, 0x71, 0x27, 0x46, 0xe2, 0x6e, 0xc6, 0x37, 0x54, 0x54, 0x56, 0x69, 0x06, + 0x59, 0xd3, 0x53, 0xca, 0xfa, 0x3b, 0x01, 0x24, 0x12, 0x18, 0xe8, 0xd6, 0xb0, 0xf1, 0x16, 0x13, + 0x6a, 0xa5, 0x77, 0xc3, 0x7a, 0x7f, 0x15, 0x20, 0x1b, 0x04, 0xed, 0xff, 0x82, 0xed, 0x5c, 0x8c, + 0x27, 0xda, 0x8e, 0xd7, 0x27, 0xef, 0x82, 0xed, 0xc2, 0xfb, 0x79, 0x17, 0xb2, 0x41, 0x1e, 0x4c, + 0xb8, 0xde, 0xef, 0x42, 0x16, 0xb3, 0x1c, 0x9b, 0xf0, 0x02, 0x88, 0xb6, 0x08, 0x38, 0xac, 0x6c, + 0x40, 0x36, 0x48, 0x40, 0xa4, 0x28, 0xea, 0x90, 0xab, 0x42, 0x18, 0x29, 0x77, 0x78, 0x8a, 0xa2, + 0xeb, 0x33, 0x1c, 0xf2, 0x18, 0x72, 0x84, 0x9e, 0x94, 0x27, 0x03, 0x6f, 0x12, 0x22, 0x15, 0x08, + 0xd1, 0x49, 0xd7, 0x31, 0xa7, 0xd3, 0x7d, 0x00, 0xac, 0xfa, 0xe5, 0xdf, 0x92, 0xd2, 0x38, 0x88, + 0x40, 0xf4, 0x7f, 0x91, 0x4f, 0xe6, 0xe7, 0x12, 0x42, 0x34, 0xf8, 0x68, 0x9e, 0x58, 0x01, 0xcd, + 0x58, 0x77, 0x3c, 0x80, 0xbc, 0xd5, 0xf1, 0x34, 0xfa, 0x19, 0x29, 0xf8, 0x2a, 0x3d, 0xf6, 0x6c, + 0xd9, 0xea, 0x78, 0xfb, 0x2e, 0xee, 0x6d, 0x9b, 0xa8, 0x36, 0x54, 0x31, 0xa6, 0xa9, 0x63, 0x5e, + 0x4f, 0xa0, 0x9a, 0xf8, 0x5c, 0x57, 0xa7, 0x29, 0xf7, 0x26, 0x34, 0x65, 0xb8, 0x41, 0xa2, 0x4d, + 0x99, 0x8f, 0x00, 0x06, 0x1c, 0xcf, 0x58, 0xf3, 0x9d, 0x87, 0x8c, 0xfd, 0xec, 0x99, 0x87, 0x99, + 0x15, 0xd3, 0x6a, 0x30, 0x2a, 0xff, 0x33, 0x78, 0xc6, 0x50, 0x5b, 0xdd, 0x80, 0x94, 0x63, 0x7b, + 0x09, 0x9e, 0xc6, 0x0b, 0x6b, 0xb2, 0x8c, 0x50, 0x90, 0xa2, 0x98, 0xa5, 0x62, 0xc9, 0x28, 0x35, + 0xde, 0x7c, 0x53, 0x86, 0x14, 0xba, 0x0f, 0xf3, 0xa1, 0xf9, 0x08, 0x3b, 0xe9, 0xb1, 0xec, 0x40, + 0x60, 0xbc, 0x7d, 0xdb, 0x23, 0x1c, 0x98, 0xd8, 0xf1, 0x8f, 0x68, 0x71, 0x94, 0x56, 0xd9, 0x20, + 0x66, 0xd3, 0xec, 0xa8, 0x4d, 0x03, 0xd1, 0x3f, 0x77, 0x9b, 0x3e, 0x81, 0x6c, 0x20, 0xc5, 0x09, + 0x1b, 0xb4, 0x0d, 0xd2, 0x81, 0x87, 0x5d, 0x74, 0x3a, 0x8c, 0x3b, 0x99, 0x06, 0x58, 0x09, 0x72, + 0x5d, 0x0f, 0xbb, 0x1d, 0xbd, 0xcd, 0x2d, 0x17, 0x8e, 0xd1, 0xd7, 0x12, 0x2e, 0x8d, 0x52, 0x85, + 0x75, 0x40, 0x2b, 0xbc, 0x03, 0x4a, 0xf9, 0xa0, 0x2d, 0xd2, 0x08, 0x1b, 0xe5, 0x7f, 0x8b, 0x90, + 0xdd, 0x77, 0x6d, 0x5a, 0x23, 0xc6, 0x8f, 0x44, 0x20, 0x45, 0x8e, 0xa3, 0xbf, 0xd1, 0x15, 0x00, + 0xa7, 0x7b, 0xd8, 0xb2, 0x0c, 0xda, 0x4f, 0x64, 0xde, 0x22, 0xb3, 0x99, 0x0f, 0x71, 0x9f, 0x2c, + 0x7b, 0xd8, 0x70, 0x31, 0x6b, 0x37, 0x4a, 0x6c, 0x99, 0xcd, 0x90, 0xe5, 0x15, 0x28, 0xe8, 0x5d, + 0xff, 0x48, 0xfb, 0x14, 0x1f, 0x1e, 0xd9, 0xf6, 0xb1, 0xd6, 0x75, 0x5b, 0xc1, 0x27, 0xbb, 0xd3, + 0x64, 0xfe, 0x09, 0x9b, 0x3e, 0x70, 0x5b, 0xe8, 0x2e, 0x9c, 0x1d, 0x42, 0xb6, 0xb1, 0x7f, 0x64, + 0x9b, 0x5e, 0x31, 0xb3, 0x9c, 0x5a, 0x91, 0x55, 0x14, 0x41, 0xef, 0xb2, 0x15, 0xf4, 0x0d, 0xb8, + 0x14, 0x34, 0xca, 0x4c, 0xac, 0x1b, 0xbe, 0xd5, 0xd3, 0x7d, 0xac, 0xf9, 0x47, 0x2e, 0xf6, 0x8e, + 0xec, 0x96, 0x49, 0x4b, 0x67, 0x59, 0xbd, 0xc8, 0x20, 0x1b, 0x21, 0xa2, 0xc9, 0x01, 0x31, 0x25, + 0xe6, 0xde, 0x40, 0x89, 0x84, 0x34, 0x92, 0x66, 0xe5, 0xd7, 0x93, 0x0e, 0x72, 0xed, 0x8f, 0x52, + 0x70, 0xfe, 0x80, 0x8c, 0xf4, 0xc3, 0x16, 0x0e, 0x0c, 0xf1, 0x81, 0x85, 0x5b, 0xa6, 0x87, 0xee, + 0x06, 0xea, 0x17, 0x82, 0x8f, 0x21, 0xf1, 0xfd, 0x1a, 0xbe, 0x6b, 0x75, 0x9e, 0xd3, 0xb2, 0x22, + 0x30, 0xce, 0x07, 0x09, 0xea, 0x15, 0xa7, 0xa0, 0x8e, 0x2b, 0xff, 0xd9, 0x18, 0xe5, 0x33, 0xcf, + 0xba, 0x1f, 0xf1, 0xed, 0x64, 0xd6, 0x2b, 0xd5, 0x11, 0xf3, 0x24, 0x9a, 0xec, 0x3b, 0x93, 0x4d, + 0x26, 0x4d, 0xc1, 0xfa, 0x78, 0x83, 0x96, 0x2a, 0x80, 0x46, 0xf9, 0x60, 0xdd, 0x5d, 0x26, 0x8e, + 0x40, 0x7d, 0x89, 0x0f, 0xcb, 0xdf, 0x17, 0x61, 0x61, 0x23, 0xe8, 0x8c, 0x37, 0xba, 0xed, 0xb6, + 0xee, 0xf6, 0x47, 0x42, 0x62, 0xb4, 0x3b, 0x15, 0x6f, 0x84, 0xcb, 0x91, 0x46, 0xf8, 0xb0, 0x4b, + 0x49, 0x6f, 0xe2, 0x52, 0x0f, 0x21, 0xaf, 0x1b, 0x06, 0xf6, 0xbc, 0x68, 0x81, 0x36, 0x89, 0x16, + 0x38, 0x7c, 0xc4, 0x1f, 0x33, 0x6f, 0xe2, 0x8f, 0x3d, 0xc8, 0xf1, 0x8e, 0x34, 0x79, 0x75, 0x12, + 0xeb, 0x06, 0x35, 0xde, 0x95, 0x84, 0xa6, 0x75, 0x65, 0x43, 0xf7, 0x75, 0x96, 0x70, 0x29, 0xb4, + 0xf4, 0x15, 0x90, 0xc3, 0xa9, 0x37, 0xf9, 0x4e, 0x52, 0xde, 0x86, 0x4c, 0x8d, 0x5a, 0x32, 0xa2, + 0xf2, 0x79, 0xaa, 0xf2, 0x68, 0xfb, 0x5c, 0x9c, 0xa6, 0x7d, 0x5e, 0x07, 0x18, 0xfc, 0x69, 0x21, + 0xd6, 0x20, 0x17, 0x92, 0x1a, 0xe4, 0xc3, 0x2d, 0x76, 0x31, 0xd6, 0x62, 0x2f, 0xff, 0x40, 0x80, + 0x7c, 0xe4, 0xe3, 0xed, 0xc9, 0xe6, 0x7b, 0xf4, 0xff, 0xb0, 0xe0, 0xe2, 0x96, 0x4e, 0x1e, 0x7e, + 0x5a, 0x00, 0x48, 0x51, 0xc0, 0x69, 0x3e, 0xbd, 0xc7, 0x2e, 0x06, 0x03, 0x60, 0xb0, 0x73, 0xb4, + 0xa9, 0x2f, 0x8c, 0x36, 0xf5, 0x2f, 0x83, 0x6c, 0xe2, 0x16, 0x79, 0x4f, 0x62, 0x97, 0x0b, 0x14, + 0x4e, 0x0c, 0xb5, 0xfc, 0x53, 0xc3, 0x2d, 0xff, 0x1f, 0x0b, 0x90, 0xdb, 0xb0, 0x0d, 0xa5, 0x47, + 0x2c, 0x71, 0x67, 0xe8, 0x2d, 0x73, 0x21, 0x22, 0x22, 0x87, 0x44, 0x9e, 0x33, 0xab, 0xc0, 0xae, + 0x01, 0xef, 0x28, 0x38, 0x32, 0xf6, 0xb7, 0x0f, 0xaa, 0x4e, 0x75, 0x80, 0x41, 0x57, 0x21, 0xfc, + 0x1f, 0x0a, 0x67, 0x44, 0x56, 0x81, 0x4f, 0x6d, 0x9b, 0xb7, 0x5f, 0x8a, 0x20, 0x87, 0x8f, 0x26, + 0xb4, 0x08, 0x0b, 0x8f, 0xab, 0x3b, 0x07, 0x8a, 0xd6, 0x7c, 0xba, 0xaf, 0x68, 0xf5, 0x83, 0x9d, + 0x9d, 0xc2, 0x1c, 0x3a, 0x0f, 0x28, 0x32, 0xb9, 0xbe, 0xb7, 0xb7, 0xa3, 0x54, 0xeb, 0x05, 0x21, + 0x36, 0xbf, 0x5d, 0x6f, 0x2a, 0x9b, 0x8a, 0x5a, 0x10, 0x63, 0x9b, 0xec, 0xec, 0xd5, 0x37, 0x0b, + 0x29, 0x74, 0x0e, 0xce, 0x44, 0x26, 0x37, 0xf6, 0x0e, 0xd6, 0x77, 0x94, 0x82, 0x14, 0x9b, 0x6e, + 0x34, 0xd5, 0xed, 0xfa, 0x66, 0x21, 0x8d, 0xce, 0x42, 0x21, 0x7a, 0xe4, 0xd3, 0xa6, 0xd2, 0x28, + 0x64, 0x62, 0x1b, 0x6f, 0x54, 0x9b, 0x4a, 0x21, 0x8b, 0x4a, 0x70, 0x3e, 0x32, 0x49, 0x4a, 0x78, + 0x6d, 0x6f, 0xfd, 0x91, 0x52, 0x6b, 0x16, 0x72, 0xe8, 0x22, 0x9c, 0x8b, 0xaf, 0x55, 0x55, 0xb5, + 0xfa, 0xb4, 0x20, 0xc7, 0xf6, 0x6a, 0x2a, 0xdf, 0x6e, 0x16, 0x20, 0xb6, 0x57, 0x20, 0x91, 0x56, + 0xab, 0x37, 0x0b, 0x79, 0x74, 0x01, 0x16, 0x63, 0x52, 0xd1, 0x85, 0xf9, 0xf8, 0x4e, 0xaa, 0xa2, + 0x14, 0x4e, 0xdd, 0xfe, 0x99, 0x00, 0xf3, 0x51, 0xfb, 0xa1, 0x1b, 0xb0, 0xbc, 0xb1, 0x57, 0xd3, + 0x94, 0xc7, 0x4a, 0xbd, 0xc9, 0x75, 0x50, 0x3b, 0xd8, 0x55, 0xea, 0xcd, 0x86, 0x56, 0xdb, 0xaa, + 0xd6, 0x37, 0x95, 0x8d, 0xc2, 0xdc, 0x44, 0xd4, 0x93, 0x6a, 0xb3, 0xb6, 0xa5, 0x6c, 0x14, 0x04, + 0x74, 0x13, 0xca, 0x63, 0x51, 0x07, 0x75, 0x8e, 0x13, 0xd1, 0x75, 0xb8, 0x1a, 0xc3, 0xed, 0xab, + 0x4a, 0x43, 0xa9, 0xd7, 0x94, 0xf0, 0xc8, 0xd4, 0xfa, 0x9d, 0x5f, 0xbf, 0x5a, 0x12, 0x7e, 0xff, + 0x6a, 0x49, 0xf8, 0xd3, 0xab, 0x25, 0xe1, 0xa7, 0x7f, 0x59, 0x9a, 0x83, 0x33, 0x26, 0xee, 0x71, + 0xa7, 0xd2, 0x1d, 0xab, 0xd2, 0xbb, 0xb7, 0x2f, 0x7c, 0x24, 0x55, 0x1e, 0xf6, 0xee, 0x1d, 0x66, + 0x68, 0x5e, 0xfb, 0xd2, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x1f, 0x28, 0x31, 0x40, 0x26, + 0x00, 0x00, +} + +func (m *Snapshot) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Snapshot) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Snapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.PresenceMap) > 0 { + for k := range m.PresenceMap { + v := m.PresenceMap[k] + baseI := i + if v != nil { + { + size, err := v.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintResources(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintResources(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintResources(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x12 + } + } + if m.Root != nil { + { + size, err := m.Root.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintResources(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } func (m *ChangePack) Marshal() (dAtA []byte, err error) { @@ -6376,6 +6505,35 @@ func encodeVarintResources(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } +func (m *Snapshot) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Root != nil { + l = m.Root.Size() + n += 1 + l + sovResources(uint64(l)) + } + if len(m.PresenceMap) > 0 { + for k, v := range m.PresenceMap { + _ = k + _ = v + l = 0 + if v != nil { + l = v.Size() + l += 1 + sovResources(uint64(l)) + } + mapEntrySize := 1 + len(k) + sovResources(uint64(len(k))) + l + n += mapEntrySize + 1 + sovResources(uint64(mapEntrySize)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *ChangePack) Size() (n int) { if m == nil { return 0 @@ -7696,6 +7854,222 @@ func sovResources(x uint64) (n int) { func sozResources(x uint64) (n int) { return sovResources(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (m *Snapshot) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Snapshot: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Snapshot: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Root", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Root == nil { + m.Root = &JSONElement{} + } + if err := m.Root.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PresenceMap", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PresenceMap == nil { + m.PresenceMap = make(map[string]*Presence) + } + var mapkey string + var mapvalue *Presence + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthResources + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthResources + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthResources + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLengthResources + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &Presence{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipResources(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.PresenceMap[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipResources(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *ChangePack) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/api/yorkie/v1/resources.proto b/api/yorkie/v1/resources.proto index 06da1fa4c..e78331389 100644 --- a/api/yorkie/v1/resources.proto +++ b/api/yorkie/v1/resources.proto @@ -25,6 +25,14 @@ option go_package = ".;v1"; option java_multiple_files = true; option java_package = "dev.yorkie.api.v1"; +///////////////////////////////////////// +// Messages for Snapshot // +///////////////////////////////////////// +message Snapshot { + JSONElement root = 1; + map presence_map = 2; +} + ///////////////////////////////////////// // Messages for ChangePack // ///////////////////////////////////////// diff --git a/pkg/document/change/change.go b/pkg/document/change/change.go index b23825295..378d5066c 100644 --- a/pkg/document/change/change.go +++ b/pkg/document/change/change.go @@ -20,8 +20,8 @@ package change import ( "github.com/yorkie-team/yorkie/pkg/document/crdt" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/document/operations" - "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" ) @@ -38,11 +38,11 @@ type Change struct { // presence represents the presence of the user who made the change. // TODO(hackerwins): Consider using changes instead of entire presence. - presence *presence.InternalPresence + presence *innerpresence.Presence } // New creates a new instance of Change. -func New(id ID, message string, operations []operations.Operation, p *presence.InternalPresence) *Change { +func New(id ID, message string, operations []operations.Operation, p *innerpresence.Presence) *Change { return &Change{ id: id, message: message, @@ -52,7 +52,7 @@ func New(id ID, message string, operations []operations.Operation, p *presence.I } // Execute applies this change to the given JSON root. -func (c *Change) Execute(root *crdt.Root, presenceMap *presence.Map) error { +func (c *Change) Execute(root *crdt.Root, presenceMap *innerpresence.Map) error { for _, op := range c.operations { if err := op.Execute(root); err != nil { return err @@ -105,6 +105,6 @@ func (c *Change) SetActor(actor *time.ActorID) { } // Presence returns the presence of this change. -func (c *Change) Presence() *presence.InternalPresence { +func (c *Change) Presence() *innerpresence.Presence { return c.presence } diff --git a/pkg/document/change/context.go b/pkg/document/change/context.go index 209d1e74f..0f4c7b4e1 100644 --- a/pkg/document/change/context.go +++ b/pkg/document/change/context.go @@ -18,8 +18,8 @@ package change import ( "github.com/yorkie-team/yorkie/pkg/document/crdt" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/document/operations" - "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" ) @@ -32,7 +32,7 @@ type Context struct { operations []operations.Operation delimiter uint32 root *crdt.Root - presence *presence.InternalPresence + presence *innerpresence.Presence } // NewContext creates a new instance of Context. @@ -91,6 +91,6 @@ func (c *Context) LastTimeTicket() *time.Ticket { } // SetPresence sets the presence of the user who made the change. -func (c *Context) SetPresence(presence presence.InternalPresence) { +func (c *Context) SetPresence(presence innerpresence.Presence) { c.presence = &presence } diff --git a/pkg/document/document.go b/pkg/document/document.go index 0add3ad9b..a7b873d10 100644 --- a/pkg/document/document.go +++ b/pkg/document/document.go @@ -19,13 +19,13 @@ package document import ( "fmt" - "github.com/yorkie-team/yorkie/pkg/document/presence" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/crdt" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/document/json" "github.com/yorkie-team/yorkie/pkg/document/key" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" ) @@ -54,7 +54,7 @@ func New(key key.Key) *Document { // Update executes the given updater to update this document. func (d *Document) Update( - updater func(root *json.Object, p *presenceproxy.Presence) error, + updater func(root *json.Object, p *presence.Presence) error, msgAndArgs ...interface{}, ) error { if d.doc.status == StatusRemoved { @@ -75,7 +75,7 @@ func (d *Document) Update( // from the document. if err := updater( json.NewObject(ctx, d.clone.Object()), - presenceproxy.New(ctx, d.doc.Presence()), + presence.New(ctx, d.doc.Presence()), ); err != nil { // drop clone because it is contaminated. d.clone = nil @@ -246,8 +246,14 @@ func (d *Document) ensureClone() error { } // PresenceMap returns the presence map of this document. -func (d *Document) PresenceMap() map[string]presence.InternalPresence { - return d.doc.PresenceMap() +func (d *Document) PresenceMap() map[string]innerpresence.Presence { + // TODO(hackerwins): We need to use client key instead of actor ID for exposing presence. + presenceMap := make(map[string]innerpresence.Presence) + d.doc.presenceMap.Range(func(key, value interface{}) bool { + presenceMap[key.(string)] = *value.(*innerpresence.Presence) + return true + }) + return presenceMap } func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { diff --git a/pkg/document/document_test.go b/pkg/document/document_test.go index 75d6be74f..dd3e62673 100644 --- a/pkg/document/document_test.go +++ b/pkg/document/document_test.go @@ -19,7 +19,6 @@ package document_test import ( "errors" "fmt" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" @@ -28,6 +27,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/crdt" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" ) @@ -55,7 +55,7 @@ func TestDocument(t *testing.T) { doc2 := document.New("d2") doc3 := document.New("d3") - err := doc1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") return nil }, "updates k1") @@ -72,7 +72,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, "{}", doc.Marshal()) assert.False(t, doc.HasLocalChanges()) - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") root.SetNewObject("k2").SetString("k4", "v4") root.SetNewArray("k3").AddString("v5", "v6") @@ -90,7 +90,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, "{}", doc.Marshal()) assert.False(t, doc.HasLocalChanges()) expected := `{"k1":"v1","k2":{"k4":"v4"},"k3":["v5","v6"]}` - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") root.SetNewObject("k2").SetString("k4", "v4") root.SetNewArray("k3").AddString("v5", "v6") @@ -101,7 +101,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, expected, doc.Marshal()) expected = `{"k1":"v1","k3":["v5","v6"]}` - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.Delete("k2") assert.Equal(t, expected, root.Marshal()) return nil @@ -112,7 +112,7 @@ func TestDocument(t *testing.T) { t.Run("object test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") assert.Equal(t, `{"k1":"v1"}`, root.Marshal()) root.SetString("k1", "v2") @@ -126,7 +126,7 @@ func TestDocument(t *testing.T) { t.Run("array test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k1").AddInteger(1).AddInteger(2).AddInteger(3) assert.Equal(t, 3, root.GetArray("k1").Len()) assert.Equal(t, `{"k1":[1,2,3]}`, root.Marshal()) @@ -163,7 +163,7 @@ func TestDocument(t *testing.T) { t.Run("delete elements of array test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("data").AddInteger(0).AddInteger(1).AddInteger(2) return nil }) @@ -171,7 +171,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, `{"data":[0,1,2]}`, doc.Marshal()) assert.Equal(t, 3, doc.Root().GetArray("data").Len()) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("data").Delete(0) return nil }) @@ -179,7 +179,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, `{"data":[1,2]}`, doc.Marshal()) assert.Equal(t, 2, doc.Root().GetArray("data").Len()) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("data").Delete(1) return nil }) @@ -187,7 +187,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, `{"data":[1]}`, doc.Marshal()) assert.Equal(t, 1, doc.Root().GetArray("data").Len()) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("data").Delete(0) return nil }) @@ -202,7 +202,7 @@ func TestDocument(t *testing.T) { // ---------- ins links -------- // | | | // [init] - [A] - [12] - [BC deleted] - [D] - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1"). Edit(0, 0, "ABCD"). Edit(1, 3, "12") @@ -212,7 +212,7 @@ func TestDocument(t *testing.T) { assert.NoError(t, err) assert.Equal(t, `{"k1":[{"val":"A"},{"val":"12"},{"val":"D"}]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") assert.Equal(t, `[0:0:00:0 {} ""][1:2:00:0 {} "A"][1:3:00:0 {} "12"]{1:2:00:1 {} "BC"}[1:2:00:3 {} "D"]`, @@ -241,7 +241,7 @@ func TestDocument(t *testing.T) { t.Run("text composition test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1"). Edit(0, 0, "ㅎ"). Edit(0, 1, "하"). @@ -259,7 +259,7 @@ func TestDocument(t *testing.T) { t.Run("rich text test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.SetNewText("k1") text.Edit(0, 0, "Hello world", nil) assert.Equal( @@ -272,7 +272,7 @@ func TestDocument(t *testing.T) { assert.NoError(t, err) assert.Equal(t, `{"k1":[{"val":"Hello world"}]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") text.Style(0, 5, map[string]string{"b": "1"}) assert.Equal(t, @@ -288,7 +288,7 @@ func TestDocument(t *testing.T) { doc.Marshal(), ) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") text.Style(0, 5, map[string]string{"b": "1"}) assert.Equal( @@ -312,7 +312,7 @@ func TestDocument(t *testing.T) { doc.Marshal(), ) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") text.Edit(5, 11, " Yorkie", nil) assert.Equal( @@ -330,7 +330,7 @@ func TestDocument(t *testing.T) { doc.Marshal(), ) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") text.Edit(5, 5, "\n", map[string]string{"list": "true"}) assert.Equal( @@ -359,7 +359,7 @@ func TestDocument(t *testing.T) { var double = 5.66 // integer type test - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewCounter("age", crdt.IntegerCnt, 5) age := root.GetCounter("age") @@ -376,7 +376,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, "128", doc.Root().GetCounter("age").Counter.Marshal()) // long type test - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewCounter("price", crdt.LongCnt, 9000000000000000000) price := root.GetCounter("price") println(price.ValueType()) @@ -392,7 +392,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, `{"age":128,"price":9000000000000000123}`, doc.Marshal()) // negative operator test - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { age := root.GetCounter("age") age.Increase(-5) age.Increase(-3.14) @@ -408,7 +408,7 @@ func TestDocument(t *testing.T) { // TODO: it should be modified to error check // when 'Remove panic from server code (#50)' is completed. - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { defer func() { r := recover() assert.NotNil(t, r) @@ -428,21 +428,21 @@ func TestDocument(t *testing.T) { t.Run("rollback test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k1").AddInteger(1, 2, 3) return nil }) assert.NoError(t, err) assert.Equal(t, `{"k1":[1,2,3]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("k1").AddInteger(4, 5) return errDummy }) assert.Equal(t, err, errDummy, "should returns the dummy error") assert.Equal(t, `{"k1":[1,2,3]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("k1").AddInteger(4, 5) return nil }) @@ -453,7 +453,7 @@ func TestDocument(t *testing.T) { t.Run("rollback test, primitive deepcopy", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewObject("k1"). SetInteger("k1.1", 1). SetInteger("k1.2", 2) @@ -462,7 +462,7 @@ func TestDocument(t *testing.T) { assert.NoError(t, err) assert.Equal(t, `{"k1":{"k1.1":1,"k1.2":2}}`, doc.Marshal()) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetObject("k1").Delete("k1.1") return errDummy }) @@ -473,7 +473,7 @@ func TestDocument(t *testing.T) { t.Run("text garbage collection test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("text") root.GetText("text").Edit(0, 0, "ABCD") root.GetText("text").Edit(0, 2, "12") @@ -495,7 +495,7 @@ func TestDocument(t *testing.T) { doc.Root().GetText("text").StructureAsString(), ) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("text").Edit(2, 4, "") return nil }) @@ -510,7 +510,7 @@ func TestDocument(t *testing.T) { t.Run("previously inserted elements in heap when running GC test", func(t *testing.T) { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetInteger("a", 1) root.SetInteger("a", 2) root.Delete("a") diff --git a/pkg/document/innerpresence/presence.go b/pkg/document/innerpresence/presence.go new file mode 100644 index 000000000..71b29a0f2 --- /dev/null +++ b/pkg/document/innerpresence/presence.go @@ -0,0 +1,64 @@ +/* + * Copyright 2023 The Yorkie Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package innerpresence provides the implementation of Presence. +// If the client is watching a document, the presence is shared with +// all other clients watching the same document. +package innerpresence + +import ( + "encoding/json" + "fmt" + "sync" +) + +// Map is a map of presence by clientID. +type Map = sync.Map // map[string]*innerpresence.Presence + +// NewMap creates a new instance of Map. +func NewMap() *Map { + return &sync.Map{} +} + +// Presence represents custom presence that can be defined by the client. +type Presence map[string]string + +// NewFromJSON creates a new instance of Presence from JSON. +func NewFromJSON(encodedJSON string) (*Presence, error) { + if encodedJSON == "" { + return nil, nil + } + + p := Presence{} + if err := json.Unmarshal([]byte(encodedJSON), &p); err != nil { + return nil, fmt.Errorf("unmarshal presence: %w", err) + } + + return &p, nil +} + +// NewPresence creates a new instance of Presence. +func NewPresence() *Presence { + data := make(map[string]string) + p := Presence(data) + return &p +} + +// Set sets the value of the given key. +func (p *Presence) Set(key string, value string) { + presence := *p + presence[key] = value +} diff --git a/pkg/document/internal_document.go b/pkg/document/internal_document.go index ab8caf019..e0bd4a996 100644 --- a/pkg/document/internal_document.go +++ b/pkg/document/internal_document.go @@ -18,11 +18,12 @@ package document import ( "errors" + "github.com/yorkie-team/yorkie/api/converter" "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/crdt" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/document/key" - "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" ) @@ -56,7 +57,7 @@ type InternalDocument struct { checkpoint change.Checkpoint changeID change.ID localChanges []*change.Change - presenceMap *presence.Map + presenceMap *innerpresence.Map } // NewInternalDocument creates a new instance of InternalDocument. @@ -70,7 +71,7 @@ func NewInternalDocument(k key.Key) *InternalDocument { root: crdt.NewRoot(root), checkpoint: change.InitialCheckpoint, changeID: change.InitialID, - presenceMap: presence.NewMap(), + presenceMap: innerpresence.NewMap(), } } @@ -81,7 +82,7 @@ func NewInternalDocumentFromSnapshot( lamport int64, snapshot []byte, ) (*InternalDocument, error) { - obj, err := converter.BytesToObject(snapshot) + obj, presenceMap, err := converter.BytesToSnapshot(snapshot) if err != nil { return nil, err } @@ -90,7 +91,7 @@ func NewInternalDocumentFromSnapshot( key: k, status: StatusDetached, root: crdt.NewRoot(obj), - presenceMap: presence.NewMap(), + presenceMap: presenceMap, checkpoint: change.InitialCheckpoint.NextServerSeq(serverSeq), changeID: change.InitialID.SyncLamport(lamport), }, nil @@ -208,12 +209,13 @@ func (d *InternalDocument) RootObject() *crdt.Object { } func (d *InternalDocument) applySnapshot(snapshot []byte, serverSeq int64) error { - rootObj, err := converter.BytesToObject(snapshot) + rootObj, presenceMap, err := converter.BytesToSnapshot(snapshot) if err != nil { return err } d.root = crdt.NewRoot(rootObj) + d.presenceMap = presenceMap d.changeID = d.changeID.SyncLamport(serverSeq) return nil @@ -232,18 +234,12 @@ func (d *InternalDocument) ApplyChanges(changes ...*change.Change) error { } // Presence returns the presence of the actor currently editing the document. -func (d *InternalDocument) Presence() *presence.InternalPresence { - value, _ := d.presenceMap.LoadOrStore(d.changeID.ActorID().String(), presence.NewInternalPresence()) - return value.(*presence.InternalPresence) +func (d *InternalDocument) Presence() *innerpresence.Presence { + value, _ := d.presenceMap.LoadOrStore(d.changeID.ActorID().String(), innerpresence.NewPresence()) + return value.(*innerpresence.Presence) } // PresenceMap returns the map of presences of the actors currently editing the document. -func (d *InternalDocument) PresenceMap() map[string]presence.InternalPresence { - // TODO(hackerwins): We need to use client key instead of actor ID for exposing presence. - presenceMap := make(map[string]presence.InternalPresence) - d.presenceMap.Range(func(key, value interface{}) bool { - presenceMap[key.(string)] = *value.(*presence.InternalPresence) - return true - }) - return presenceMap +func (d *InternalDocument) PresenceMap() *innerpresence.Map { + return d.presenceMap } diff --git a/pkg/document/presence/presence.go b/pkg/document/presence/presence.go index c15d54ef2..0f81894fd 100644 --- a/pkg/document/presence/presence.go +++ b/pkg/document/presence/presence.go @@ -14,51 +14,33 @@ * limitations under the License. */ -// Package presence provides the implementation of InternalPresence. -// If the client is watching a document, the presence is shared with -// all other clients watching the same document. +// Package presence provides the proxy for the innerpresence.Presence to be manipulated from the outside. +// TODO(hackerwins): Consider to remove this package. It is used to solve the problem of cyclic dependency +// between pkg/document/presence and pkg/document/change. package presence import ( - "encoding/json" - "fmt" - "sync" + "github.com/yorkie-team/yorkie/pkg/document/change" + presence2 "github.com/yorkie-team/yorkie/pkg/document/innerpresence" ) -// Map is a map of presence by clientID. -type Map = sync.Map // map[string]*presence.InternalPresence - -// NewMap creates a new instance of Map. -func NewMap() *Map { - return &sync.Map{} +// Presence represents a proxy for the Presence to be manipulated from the outside. +type Presence struct { + presence *presence2.Presence + context *change.Context } -// InternalPresence represents custom presence that can be defined by the client. -type InternalPresence map[string]string - -// NewFromJSON creates a new instance of InternalPresence from JSON. -func NewFromJSON(encodedJSON string) (*InternalPresence, error) { - if encodedJSON == "" { - return nil, nil - } - - p := InternalPresence{} - if err := json.Unmarshal([]byte(encodedJSON), &p); err != nil { - return nil, fmt.Errorf("unmarshal presence: %w", err) +// New creates a new instance of Presence. +func New(ctx *change.Context, presence *presence2.Presence) *Presence { + return &Presence{ + presence: presence, + context: ctx, } - - return &p, nil -} - -// NewInternalPresence creates a new instance of InternalPresence. -func NewInternalPresence() *InternalPresence { - data := make(map[string]string) - p := InternalPresence(data) - return &p } // Set sets the value of the given key. -func (p *InternalPresence) Set(key string, value string) { - presence := *p - presence[key] = value +func (p *Presence) Set(key string, value string) { + internalPresence := *p.presence + internalPresence.Set(key, value) + p.context.SetPresence(internalPresence) } diff --git a/pkg/document/presenceproxy/proxy.go b/pkg/document/presenceproxy/proxy.go deleted file mode 100644 index 8ef51f1eb..000000000 --- a/pkg/document/presenceproxy/proxy.go +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2023 The Yorkie Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Package presenceproxy provides the proxy for the InternalPresence to be manipulated from the outside. -// TODO(hackerwins): Consider to remove this package. It is used to solve the problem of cyclic dependency -// between pkg/document/presence and pkg/document/change. -package presenceproxy - -import ( - "github.com/yorkie-team/yorkie/pkg/document/change" - presence2 "github.com/yorkie-team/yorkie/pkg/document/presence" -) - -// Presence represents a proxy for the InternalPresence to be manipulated from the outside. -type Presence struct { - presence *presence2.InternalPresence - context *change.Context -} - -// New creates a new instance of Presence. -func New(ctx *change.Context, presence *presence2.InternalPresence) *Presence { - return &Presence{ - presence: presence, - context: ctx, - } -} - -// Set sets the value of the given key. -func (p *Presence) Set(key string, value string) { - internalPresence := *p.presence - internalPresence.Set(key, value) - p.context.SetPresence(internalPresence) -} diff --git a/server/backend/database/change_info.go b/server/backend/database/change_info.go index 625d1fb72..5a394d824 100644 --- a/server/backend/database/change_info.go +++ b/server/backend/database/change_info.go @@ -20,12 +20,12 @@ import ( "encoding/json" "errors" "fmt" - "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/api/converter" "github.com/yorkie-team/yorkie/api/types" api "github.com/yorkie-team/yorkie/api/yorkie/v1" "github.com/yorkie-team/yorkie/pkg/document/change" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/document/operations" "github.com/yorkie-team/yorkie/pkg/document/time" ) @@ -67,7 +67,7 @@ func EncodeOperations(operations []operations.Operation) ([][]byte, error) { } // EncodePresence encodes the given presence into string. -func EncodePresence(p *presence.InternalPresence) (string, error) { +func EncodePresence(p *innerpresence.Presence) (string, error) { if p == nil { return "", nil } @@ -103,7 +103,7 @@ func (i *ChangeInfo) ToChange() (*change.Change, error) { return nil, err } - p, err := presence.NewFromJSON(i.Presence) + p, err := innerpresence.NewFromJSON(i.Presence) if err != nil { return nil, err } diff --git a/server/backend/database/memory/database.go b/server/backend/database/memory/database.go index 380609134..11d5f9c14 100644 --- a/server/backend/database/memory/database.go +++ b/server/backend/database/memory/database.go @@ -947,7 +947,7 @@ func (d *DB) CreateSnapshotInfo( docID types.ID, doc *document.InternalDocument, ) error { - snapshot, err := converter.ObjectToBytes(doc.RootObject()) + snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.PresenceMap()) if err != nil { return err } diff --git a/server/backend/database/mongo/client.go b/server/backend/database/mongo/client.go index bedaa23ee..c00ec55fb 100644 --- a/server/backend/database/mongo/client.go +++ b/server/backend/database/mongo/client.go @@ -1031,7 +1031,7 @@ func (c *Client) CreateSnapshotInfo( if err != nil { return err } - snapshot, err := converter.ObjectToBytes(doc.RootObject()) + snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.PresenceMap()) if err != nil { return err } diff --git a/server/backend/database/testcases/testcases.go b/server/backend/database/testcases/testcases.go index 6621e8168..e1fcbca7a 100644 --- a/server/backend/database/testcases/testcases.go +++ b/server/backend/database/testcases/testcases.go @@ -21,7 +21,6 @@ package testcases import ( "context" "fmt" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "strconv" "testing" gotime "time" @@ -34,6 +33,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/json" "github.com/yorkie-team/yorkie/pkg/document/key" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" "github.com/yorkie-team/yorkie/server/backend/database" "github.com/yorkie-team/yorkie/test/helper" @@ -176,12 +176,12 @@ func RunFindChangesBetweenServerSeqsTest( actorID, _ := time.ActorIDFromBytes(bytesID) doc := document.New(key.Key(t.Name())) doc.SetActor(actorID) - assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("array") return nil })) for idx := 0; idx < 10; idx++ { - assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("array").AddInteger(idx) return nil })) @@ -221,7 +221,7 @@ func RunFindClosestSnapshotInfoTest(t *testing.T, db database.Database, projectI doc := document.New(key.Key(t.Name())) doc.SetActor(actorID) - assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("array") return nil })) diff --git a/server/packs/pushpull.go b/server/packs/pushpull.go index 9d30494be..18872916f 100644 --- a/server/packs/pushpull.go +++ b/server/packs/pushpull.go @@ -157,7 +157,7 @@ func pullSnapshot( } cpAfterPull := cpAfterPush.NextServerSeq(docInfo.ServerSeq) - snapshot, err := converter.ObjectToBytes(doc.RootObject()) + snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.PresenceMap()) if err != nil { return nil, err } diff --git a/server/packs/serverpacks.go b/server/packs/serverpacks.go index 95c531f97..92a3d8a7f 100644 --- a/server/packs/serverpacks.go +++ b/server/packs/serverpacks.go @@ -20,8 +20,8 @@ import ( "github.com/yorkie-team/yorkie/api/converter" api "github.com/yorkie-team/yorkie/api/yorkie/v1" "github.com/yorkie-team/yorkie/pkg/document/change" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/document/key" - "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" "github.com/yorkie-team/yorkie/server/backend/database" ) @@ -102,7 +102,7 @@ func (p *ServerPack) ToPBChangePack() (*api.ChangePack, error) { pbOps = append(pbOps, &pbOp) } - p, err := presence.NewFromJSON(info.Presence) + p, err := innerpresence.NewFromJSON(info.Presence) if err != nil { return nil, err } diff --git a/server/rpc/admin_server.go b/server/rpc/admin_server.go index 26d36a5f6..109cd47d3 100644 --- a/server/rpc/admin_server.go +++ b/server/rpc/admin_server.go @@ -254,7 +254,7 @@ func (s *adminServer) GetSnapshotMeta( return nil, err } - snapshot, err := converter.ObjectToBytes(doc.RootObject()) + snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.PresenceMap()) if err != nil { return nil, err } diff --git a/test/bench/document_bench_test.go b/test/bench/document_bench_test.go index 7248f657d..2f43393a0 100644 --- a/test/bench/document_bench_test.go +++ b/test/bench/document_bench_test.go @@ -29,6 +29,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/crdt" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" ) @@ -57,7 +58,7 @@ func BenchmarkDocument(b *testing.B) { doc2 := document.New("d2") doc3 := document.New("d3") - err := doc1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") return nil }, "updates k1") @@ -76,7 +77,7 @@ func BenchmarkDocument(b *testing.B) { assert.Equal(b, "{}", doc.Marshal()) assert.False(b, doc.HasLocalChanges()) - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") root.SetNewObject("k2").SetString("k4", "v4") root.SetNewArray("k3").AddString("v5", "v6") @@ -97,7 +98,7 @@ func BenchmarkDocument(b *testing.B) { assert.False(b, doc.HasLocalChanges()) expected := `{"k1":"v1","k2":{"k4":"v4"},"k3":["v5","v6"]}` - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") root.SetNewObject("k2").SetString("k4", "v4") root.SetNewArray("k3").AddString("v5", "v6") @@ -108,7 +109,7 @@ func BenchmarkDocument(b *testing.B) { assert.Equal(b, expected, doc.Marshal()) expected = `{"k1":"v1","k3":["v5","v6"]}` - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.Delete("k2") assert.Equal(b, expected, root.Marshal()) return nil @@ -121,7 +122,7 @@ func BenchmarkDocument(b *testing.B) { b.Run("object test", func(b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") assert.Equal(b, `{"k1":"v1"}`, root.Marshal()) root.SetString("k1", "v2") @@ -137,7 +138,7 @@ func BenchmarkDocument(b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k1").AddInteger(1).AddInteger(2).AddInteger(3) assert.Equal(b, 3, root.GetArray("k1").Len()) assert.Equal(b, `{"k1":[1,2,3]}`, root.Marshal()) @@ -180,7 +181,7 @@ func BenchmarkDocument(b *testing.B) { // ---------- ins links -------- // | | | // [init] - [A] - [12] - [BC deleted] - [D] - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1"). Edit(0, 0, "ABCD"). Edit(1, 3, "12") @@ -190,7 +191,7 @@ func BenchmarkDocument(b *testing.B) { assert.NoError(b, err) assert.Equal(b, `{"k1":[{"val":"A"},{"val":"12"},{"val":"D"}]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") assert.Equal(b, `[0:0:00:0 {} ""][1:2:00:0 {} "A"][1:3:00:0 {} "12"]{1:2:00:1 {} "BC"}[1:2:00:3 {} "D"]`, @@ -221,7 +222,7 @@ func BenchmarkDocument(b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1"). Edit(0, 0, "ㅎ"). Edit(0, 1, "하"). @@ -241,7 +242,7 @@ func BenchmarkDocument(b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.SetNewText("k1") text.Edit(0, 0, "Hello world", nil) assert.Equal( @@ -254,7 +255,7 @@ func BenchmarkDocument(b *testing.B) { assert.NoError(b, err) assert.Equal(b, `{"k1":[{"val":"Hello world"}]}`, doc.Marshal()) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") text.Style(0, 5, map[string]string{"b": "1"}) assert.Equal(b, @@ -270,7 +271,7 @@ func BenchmarkDocument(b *testing.B) { doc.Marshal(), ) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") text.Style(0, 5, map[string]string{"b": "1"}) assert.Equal( @@ -294,7 +295,7 @@ func BenchmarkDocument(b *testing.B) { doc.Marshal(), ) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") text.Edit(5, 11, " Yorkie", nil) assert.Equal( @@ -312,7 +313,7 @@ func BenchmarkDocument(b *testing.B) { doc.Marshal(), ) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") text.Edit(5, 5, "\n", map[string]string{"list": "true"}) assert.Equal( @@ -341,7 +342,7 @@ func BenchmarkDocument(b *testing.B) { var double = 5.66 // integer type test - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewCounter("age", crdt.IntegerCnt, 5) age := root.GetCounter("age") @@ -357,7 +358,7 @@ func BenchmarkDocument(b *testing.B) { assert.Equal(b, `{"age":128}`, doc.Marshal()) // long type test - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewCounter("price", crdt.LongCnt, 9000000000000000000) price := root.GetCounter("price") price.Increase(long) @@ -372,7 +373,7 @@ func BenchmarkDocument(b *testing.B) { assert.Equal(b, `{"age":128,"price":9000000000000000123}`, doc.Marshal()) // negative operator test - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { age := root.GetCounter("age") age.Increase(-5) age.Increase(-3.14) @@ -388,7 +389,7 @@ func BenchmarkDocument(b *testing.B) { // TODO: it should be modified to error check // when 'Remove panic from server code (#50)' is completed. - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { defer func() { r := recover() assert.NotNil(b, r) @@ -475,7 +476,7 @@ func benchmarkText(cnt int, b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.SetNewText("k1") for c := 0; c < cnt; c++ { text.Edit(c, c, "a") @@ -491,7 +492,7 @@ func benchmarkTextDeleteAll(cnt int, b *testing.B) { b.StopTimer() doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.SetNewText("k1") for c := 0; c < cnt; c++ { text.Edit(c, c, "a") @@ -501,7 +502,7 @@ func benchmarkTextDeleteAll(cnt int, b *testing.B) { assert.NoError(b, err) b.StartTimer() - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") text.Edit(0, cnt, "") return nil @@ -518,7 +519,7 @@ func benchmarkTextEditGC(cnt int, b *testing.B) { assert.Equal(b, "{}", doc.Marshal()) assert.False(b, doc.HasLocalChanges()) - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.SetNewText("k1") for i := 0; i < cnt; i++ { text.Edit(i, i, "a") @@ -527,7 +528,7 @@ func benchmarkTextEditGC(cnt int, b *testing.B) { }, "creates a text then appends a") assert.NoError(b, err) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") for i := 0; i < cnt; i++ { text.Edit(i, i+1, "b") @@ -549,14 +550,14 @@ func benchmarkTextSplitGC(cnt int, b *testing.B) { for i := 0; i < cnt; i++ { builder.WriteString("a") } - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.SetNewText("k2") text.Edit(0, 0, builder.String()) return nil }, "initial") assert.NoError(b, err) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k2") for i := 0; i < cnt; i++ { if i != cnt { @@ -576,7 +577,7 @@ func benchmarkArray(cnt int, b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { array := root.SetNewArray("k1") for c := 0; c < cnt; c++ { array.AddInteger(c) @@ -590,7 +591,7 @@ func benchmarkArray(cnt int, b *testing.B) { func benchmarkArrayGC(cnt int, b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("1") for i := 0; i < cnt; i++ { root.GetArray("1").AddInteger(i) @@ -600,7 +601,7 @@ func benchmarkArrayGC(cnt int, b *testing.B) { }, "creates an array then adds integers") assert.NoError(b, err) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.Delete("1") return nil }, "deletes the array") @@ -615,7 +616,7 @@ func benchmarkCounter(cnt int, b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { counter := root.SetNewCounter("k1", crdt.IntegerCnt, 0) for c := 0; c < cnt; c++ { counter.Increase(c) @@ -630,7 +631,7 @@ func benchmarkObject(cnt int, b *testing.B) { for i := 0; i < b.N; i++ { doc := document.New("d1") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { for c := 0; c < cnt; c++ { root.SetInteger("k1", c) } diff --git a/test/bench/grpc_bench_test.go b/test/bench/grpc_bench_test.go index eae516269..f6f47cd19 100644 --- a/test/bench/grpc_bench_test.go +++ b/test/bench/grpc_bench_test.go @@ -34,6 +34,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" "github.com/yorkie-team/yorkie/pkg/document/key" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/server" "github.com/yorkie-team/yorkie/server/backend/database" "github.com/yorkie-team/yorkie/server/logging" @@ -89,7 +90,7 @@ func benchmarkUpdateAndSync( key string, ) { for i := 0; i < cnt; i++ { - err := d.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := d.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText(key) text.Edit(0, 0, "c") return nil @@ -184,7 +185,7 @@ func BenchmarkRPC(b *testing.B) { for i := 0; i < b.N; i++ { testKey := "testKey" - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText(testKey) return nil }) @@ -205,7 +206,7 @@ func BenchmarkRPC(b *testing.B) { err := c1.Attach(ctx, d1) assert.NoError(b, err) testKey1 := "testKey1" - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText(testKey1) return nil }) @@ -215,7 +216,7 @@ func BenchmarkRPC(b *testing.B) { err = c2.Attach(ctx, d2) assert.NoError(b, err) testKey2 := "testKey2" - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText(testKey2) return nil }) @@ -270,13 +271,13 @@ func BenchmarkRPC(b *testing.B) { doc1 := document.New(key.Key(helper.TestDocKey(b))) doc2 := document.New(key.Key(helper.TestDocKey(b))) - err := doc1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc1.Update(func(root *json.Object, p *presence.Presence) error { text := root.SetNewText("k1") text.Edit(0, 0, builder.String()) return nil }) assert.NoError(b, err) - err = doc2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc2.Update(func(root *json.Object, p *presence.Presence) error { text := root.SetNewText("k1") text.Edit(0, 0, builder.String()) return nil diff --git a/test/bench/text_editing_bench_test.go b/test/bench/text_editing_bench_test.go index facbc6b6e..6b91a45aa 100644 --- a/test/bench/text_editing_bench_test.go +++ b/test/bench/text_editing_bench_test.go @@ -20,7 +20,6 @@ package bench import ( gojson "encoding/json" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "io" "os" "testing" @@ -29,6 +28,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" ) func BenchmarkTextEditing(b *testing.B) { @@ -42,7 +42,7 @@ func BenchmarkTextEditing(b *testing.B) { b.StartTimer() doc := document.New("d1") - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("text") return nil }) @@ -51,7 +51,7 @@ func BenchmarkTextEditing(b *testing.B) { cursor := int(edit[0].(float64)) mode := int(edit[1].(float64)) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("text") if mode == 0 { value := edit[2].(string) diff --git a/test/helper/helper.go b/test/helper/helper.go index 27456f873..706b5b14a 100644 --- a/test/helper/helper.go +++ b/test/helper/helper.go @@ -20,7 +20,6 @@ package helper import ( "context" "fmt" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "log" "strings" "testing" @@ -35,6 +34,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document/crdt" "github.com/yorkie-team/yorkie/pkg/document/json" "github.com/yorkie-team/yorkie/pkg/document/key" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" "github.com/yorkie-team/yorkie/pkg/index" "github.com/yorkie-team/yorkie/server" @@ -178,7 +178,7 @@ func ToDiagnostic(node *crdt.TreeNode) string { // BuildIndexTree builds an index tree from the given block node. func BuildIndexTree(node *json.TreeNode) *index.Tree[*crdt.TreeNode] { doc := document.New("test") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("test", node) return nil @@ -193,7 +193,7 @@ func BuildIndexTree(node *json.TreeNode) *index.Tree[*crdt.TreeNode] { // BuildTreeNode builds a crdt.TreeNode from the given tree node. func BuildTreeNode(node *json.TreeNode) *crdt.TreeNode { doc := document.New("test") - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("test", node) return nil diff --git a/test/integration/admin_test.go b/test/integration/admin_test.go index 714b2a467..89480aaa8 100644 --- a/test/integration/admin_test.go +++ b/test/integration/admin_test.go @@ -20,7 +20,6 @@ package integration import ( "context" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "io" "sync" "testing" @@ -33,6 +32,7 @@ import ( "github.com/yorkie-team/yorkie/client" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/test/helper" ) @@ -74,7 +74,7 @@ func TestAdmin(t *testing.T) { assert.Equal(t, document.StatusAttached, d1.Status()) // 03. client updates the document and sync to the server. - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") return nil })) diff --git a/test/integration/array_test.go b/test/integration/array_test.go index 2e729217c..90c992fcc 100644 --- a/test/integration/array_test.go +++ b/test/integration/array_test.go @@ -20,13 +20,13 @@ package integration import ( "context" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/test/helper" ) @@ -41,7 +41,7 @@ func TestArray(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k1"). AddString("v1"). AddNewArray().AddString("1", "2", "3") @@ -62,7 +62,7 @@ func TestArray(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k1").AddString("v1", "v2") return nil }, "add v1, v2 by c1") @@ -75,13 +75,13 @@ func TestArray(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("k1").Delete(1) return nil }, "delete v2 by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("k1").AddString("v3") return nil }, "add v3 by c2") @@ -96,7 +96,7 @@ func TestArray(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k1").AddString("v1") return nil }, "new array and add v1") @@ -108,14 +108,14 @@ func TestArray(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("k1").AddString("v2", "v3") root.GetArray("k1").Delete(1) return nil }, "add v2, v3 and delete v2 by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("k1").AddString("v4", "v5") return nil }, "add v4, v5 by c2") @@ -130,7 +130,7 @@ func TestArray(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k1").AddString("v1", "v2", "v3") return nil }, "new array and add v1 v2 v3") @@ -142,20 +142,20 @@ func TestArray(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("k1").Delete(1) return nil }, "delete v2") assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("k1").Delete(1) return nil }, "delete v2") assert.NoError(t, err) syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { assert.Equal(t, 2, root.GetArray("k1").Len()) return nil }, "check array length") @@ -169,7 +169,7 @@ func TestArray(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k1").AddInteger(0, 1, 2) assert.Equal(t, `{"k1":[0,1,2]}`, root.Marshal()) return nil @@ -182,7 +182,7 @@ func TestArray(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { prev := root.GetArray("k1").Get(0) elem := root.GetArray("k1").Get(2) root.GetArray("k1").MoveBefore(prev.CreatedAt(), elem.CreatedAt()) @@ -191,7 +191,7 @@ func TestArray(t *testing.T) { }, "move 2 before 0") assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { prev := root.GetArray("k1").Get(1) elem := root.GetArray("k1").Get(2) root.GetArray("k1").MoveBefore(prev.CreatedAt(), elem.CreatedAt()) @@ -207,7 +207,7 @@ func TestArray(t *testing.T) { ctx := context.Background() d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, c1.Attach(ctx, d1)) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k1").AddInteger(0, 1, 2) assert.Equal(t, `{"k1":[0,1,2]}`, root.Marshal()) return nil @@ -216,7 +216,7 @@ func TestArray(t *testing.T) { d2 := document.New(helper.TestDocKey(t)) assert.NoError(t, c2.Attach(ctx, d2)) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { next := root.GetArray("k1").Get(0) elem := root.GetArray("k1").Get(2) root.GetArray("k1").MoveBefore(next.CreatedAt(), elem.CreatedAt()) @@ -224,7 +224,7 @@ func TestArray(t *testing.T) { return nil })) - assert.NoError(t, d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d2.Update(func(root *json.Object, p *presence.Presence) error { next := root.GetArray("k1").Get(0) elem := root.GetArray("k1").Get(1) root.GetArray("k1").MoveBefore(next.CreatedAt(), elem.CreatedAt()) diff --git a/test/integration/auth_webhook_test.go b/test/integration/auth_webhook_test.go index 0ffcd01da..f5bb1aa5b 100644 --- a/test/integration/auth_webhook_test.go +++ b/test/integration/auth_webhook_test.go @@ -20,7 +20,6 @@ package integration import ( "context" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "net/http" "net/http/httptest" "testing" @@ -35,6 +34,7 @@ import ( "github.com/yorkie-team/yorkie/client" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/server" "github.com/yorkie-team/yorkie/test/helper" ) @@ -323,7 +323,7 @@ func TestAuthWebhook(t *testing.T) { // 01. multiple requests to update the document. for i := 0; i < 3; i++ { - assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewObject("k1") return nil })) @@ -333,7 +333,7 @@ func TestAuthWebhook(t *testing.T) { // 02. multiple requests to update the document after eviction by ttl. time.Sleep(authorizedTTL) for i := 0; i < 3; i++ { - assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewObject("k1") return nil })) diff --git a/test/integration/client_test.go b/test/integration/client_test.go index b55a3d27f..ae07fcb8a 100644 --- a/test/integration/client_test.go +++ b/test/integration/client_test.go @@ -20,7 +20,6 @@ package integration import ( "context" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" @@ -30,6 +29,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/crdt" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/test/helper" ) @@ -89,11 +89,11 @@ func TestClient(t *testing.T) { assert.NoError(t, c3.Attach(ctx, d3)) // 02. c1, c2 sync with push-pull mode. - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetInteger("c1", 0) return nil })) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetInteger("c2", 0) return nil })) @@ -105,11 +105,11 @@ func TestClient(t *testing.T) { // 03. c1 and c2 sync with push-only mode. So, the changes of c1 and c2 // are not reflected to each other. // But, c3 can get the changes of c1 and c2, because c3 sync with pull-pull mode. - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetInteger("c1", 1) return nil })) - assert.NoError(t, d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d2.Update(func(root *json.Object, p *presence.Presence) error { root.SetInteger("c2", 1) return nil })) @@ -140,7 +140,7 @@ func TestClient(t *testing.T) { // 02. cli update the document with creating a counter // and sync with push-pull mode: CP(0, 0) -> CP(1, 1) - assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewCounter("counter", crdt.IntegerCnt, 0) return nil })) @@ -150,7 +150,7 @@ func TestClient(t *testing.T) { // 03. cli update the document with increasing the counter(0 -> 1) // and sync with push-only mode: CP(1, 1) -> CP(2, 1) - assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetCounter("counter").Increase(1) return nil })) @@ -160,7 +160,7 @@ func TestClient(t *testing.T) { // 04. cli update the document with increasing the counter(1 -> 2) // and sync with push-pull mode. CP(2, 1) -> CP(3, 3) - assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetCounter("counter").Increase(1) return nil })) diff --git a/test/integration/counter_test.go b/test/integration/counter_test.go index 492d476dd..4971c88e5 100644 --- a/test/integration/counter_test.go +++ b/test/integration/counter_test.go @@ -20,7 +20,6 @@ package integration import ( "context" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "math" "testing" @@ -29,6 +28,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/crdt" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/test/helper" ) @@ -43,7 +43,7 @@ func TestCounter(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewCounter("age", crdt.LongCnt, 1). Increase(2). Increase(2.5). @@ -67,7 +67,7 @@ func TestCounter(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewCounter("age", crdt.IntegerCnt, 0) root.SetNewCounter("width", crdt.LongCnt, 0) root.SetNewCounter("height", crdt.LongCnt, 0) @@ -81,7 +81,7 @@ func TestCounter(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetCounter("age"). Increase(1). Increase(2). @@ -91,13 +91,13 @@ func TestCounter(t *testing.T) { }) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetCounter("width").Increase(math.MaxInt32 + 100).Increase(10) return nil }) assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetCounter("age").Increase(20) root.GetCounter("width").Increase(100).Increase(200) root.GetCounter("height").Increase(50) diff --git a/test/integration/document_test.go b/test/integration/document_test.go index 82c959c7a..f887bba86 100644 --- a/test/integration/document_test.go +++ b/test/integration/document_test.go @@ -20,7 +20,6 @@ package integration import ( "context" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "io" "sync" "testing" @@ -32,6 +31,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" "github.com/yorkie-team/yorkie/pkg/document/key" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/test/helper" ) @@ -43,7 +43,7 @@ func TestDocument(t *testing.T) { t.Run("attach/detach test", func(t *testing.T) { ctx := context.Background() doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") return nil }, "update k1 with v1") @@ -58,7 +58,7 @@ func TestDocument(t *testing.T) { assert.False(t, doc.IsAttached()) doc2 := document.New(helper.TestDocKey(t)) - err = doc2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc2.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v2") return nil }, "update k1 with v2") @@ -77,7 +77,7 @@ func TestDocument(t *testing.T) { // 01. create a document and attach it to c1 ctx := context.Background() doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") return nil }, "update k1 with v1") @@ -115,26 +115,26 @@ func TestDocument(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewObject("k1").SetNewArray("k1.1").AddString("1", "2") return nil }) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k2").AddString("1", "2", "3") return nil }) assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k1").AddString("4", "5") root.SetNewArray("k2").AddString("6", "7") return nil }) assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.Delete("k2") return nil }) @@ -183,7 +183,7 @@ func TestDocument(t *testing.T) { }() // 02. cli2 updates doc2. - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("key", "value") return nil }) @@ -201,7 +201,7 @@ func TestDocument(t *testing.T) { ctx := context.Background() d1 := document.New(helper.TestDocKey(t)) - err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k1").AddInteger(1, 2) return nil }) @@ -216,7 +216,7 @@ func TestDocument(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("k1").AddInteger(3) return nil }) @@ -225,7 +225,7 @@ func TestDocument(t *testing.T) { prevArray := d1.Root().Get("k1") assert.Nil(t, prevArray.RemovedAt()) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("k1") return nil }) @@ -258,7 +258,7 @@ func TestDocument(t *testing.T) { assert.Equal(t, document.StatusRemoved, d1.Status()) // 04. try to update a removed document. - assert.ErrorIs(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.ErrorIs(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") return nil }), document.ErrDocumentRemoved) @@ -272,7 +272,7 @@ func TestDocument(t *testing.T) { // 01. cli1 creates d1 and removes it. d1 := document.New(helper.TestDocKey(t)) - err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") return nil }) @@ -295,7 +295,7 @@ func TestDocument(t *testing.T) { // 01. cli1 creates d1 and cli2 syncs. d1 := document.New(helper.TestDocKey(t)) - err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") return nil }) @@ -307,7 +307,7 @@ func TestDocument(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) // 02. cli1 updates d1 and removes it. - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v2") return nil }) @@ -326,7 +326,7 @@ func TestDocument(t *testing.T) { // 01. cli1 creates d1 and cli2 syncs. d1 := document.New(helper.TestDocKey(t)) - err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") return nil }) @@ -348,7 +348,7 @@ func TestDocument(t *testing.T) { // 01. cli1 creates d1 and cli2 syncs. d1 := document.New(helper.TestDocKey(t)) - err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") return nil }) @@ -517,7 +517,7 @@ func TestDocumentWithProjects(t *testing.T) { expected = append(expected, watchResponsePair{ Type: client.DocumentsChanged, }) - assert.NoError(t, d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d2.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("key", "value") return nil })) @@ -529,7 +529,7 @@ func TestDocumentWithProjects(t *testing.T) { defer cancel3() _, err = c3.Watch(watch3Ctx, d3) assert.NoError(t, err) - assert.NoError(t, d3.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d3.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("key3", "value3") return nil })) diff --git a/test/integration/gc_test.go b/test/integration/gc_test.go index aa026201c..0942fa0f7 100644 --- a/test/integration/gc_test.go +++ b/test/integration/gc_test.go @@ -20,13 +20,13 @@ package integration import ( "context" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" "github.com/yorkie-team/yorkie/test/helper" ) @@ -46,7 +46,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetInteger("1", 1) root.SetNewArray("2").AddInteger(1, 2, 3) root.SetInteger("3", 3) @@ -64,7 +64,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Sync(ctx) assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.Delete("2") return nil }, "removes 2") @@ -113,7 +113,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("text"). Edit(0, 0, "Hello world") root.SetNewText("richText"). @@ -132,7 +132,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Sync(ctx) assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("text"). Edit(0, 1, "a"). Edit(1, 2, "b") @@ -178,7 +178,7 @@ func TestGarbageCollection(t *testing.T) { t.Run("garbage collection for tree type test", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -201,7 +201,7 @@ func TestGarbageCollection(t *testing.T) { }) assert.NoError(t, err) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetTree("t").EditByPath([]int{0, 0, 0}, []int{0, 0, 2}, &json.TreeNode{Type: "text", Value: "gh"}) assert.Equal(t, `

ghcd

`, root.GetTree("t").ToXML()) return nil @@ -213,7 +213,7 @@ func TestGarbageCollection(t *testing.T) { assert.Equal(t, doc.GarbageCollect(time.MaxTicket), 2) assert.Equal(t, doc.GarbageLen(), 0) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetTree("t").EditByPath([]int{0, 0, 0}, []int{0, 0, 2}, &json.TreeNode{Type: "text", Value: "cv"}) assert.Equal(t, `

cvcd

`, root.GetTree("t").ToXML()) return nil @@ -225,7 +225,7 @@ func TestGarbageCollection(t *testing.T) { assert.Equal(t, doc.GarbageCollect(time.MaxTicket), 1) assert.Equal(t, doc.GarbageLen(), 0) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetTree("t").EditByPath([]int{0}, []int{1}, &json.TreeNode{ Type: "p", Children: []json.TreeNode{{ Type: "tn", Children: []json.TreeNode{{ @@ -253,7 +253,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -282,7 +282,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Sync(ctx) assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetTree("t").EditByPath([]int{0, 0, 0}, []int{0, 0, 2}, &json.TreeNode{Type: "text", Value: "gh"}) return nil }) @@ -331,7 +331,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetInteger("1", 1) root.SetNewArray("2").AddInteger(1, 2, 3) root.SetInteger("3", 3) @@ -351,7 +351,7 @@ func TestGarbageCollection(t *testing.T) { err = c2.Sync(ctx) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.Delete("2") root.GetText("4").Edit(0, 1, "h") root.GetText("5").Edit(0, 1, "h", map[string]string{"b": "1"}) diff --git a/test/integration/history_test.go b/test/integration/history_test.go index a6059a06a..15b97e299 100644 --- a/test/integration/history_test.go +++ b/test/integration/history_test.go @@ -20,13 +20,13 @@ package integration import ( "context" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/test/helper" ) @@ -44,19 +44,19 @@ func TestHistory(t *testing.T) { assert.NoError(t, cli.Attach(ctx, d1)) defer func() { assert.NoError(t, cli.Detach(ctx, d1, false)) }() - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("todos") return nil }, "create todos")) assert.Equal(t, `{"todos":[]}`, d1.Marshal()) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("todos").AddString("buy coffee") return nil }, "buy coffee")) assert.Equal(t, `{"todos":["buy coffee"]}`, d1.Marshal()) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("todos").AddString("buy bread") return nil }, "buy bread")) diff --git a/test/integration/object_test.go b/test/integration/object_test.go index c05bfb933..da1e95275 100644 --- a/test/integration/object_test.go +++ b/test/integration/object_test.go @@ -20,13 +20,13 @@ package integration import ( "context" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/test/helper" ) @@ -46,7 +46,7 @@ func TestObject(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewObject("k1"). SetString("k1.1", "v1"). SetString("k1.2", "v2"). @@ -60,7 +60,7 @@ func TestObject(t *testing.T) { assert.NoError(t, err) syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.Delete("k1") root.GetObject("k2").Delete("k2.2") return nil @@ -75,7 +75,7 @@ func TestObject(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewObject("k1") return nil }, "set v1 by c1") @@ -88,7 +88,7 @@ func TestObject(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.Delete("k1") root.SetString("k1", "v1") return nil @@ -96,7 +96,7 @@ func TestObject(t *testing.T) { assert.NoError(t, err) assert.Equal(t, `{"k1":"v1"}`, d1.Marshal()) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.Delete("k1") root.SetString("k1", "v2") return nil @@ -117,12 +117,12 @@ func TestObject(t *testing.T) { assert.NoError(t, err) // 01. concurrent set on same key - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v1") return nil }, "set k1 by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k1", "v2") return nil }, "set k1 by c2") @@ -130,19 +130,19 @@ func TestObject(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) // 02. concurrent set between ancestor descendant - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewObject("k2") return nil }, "set k2 by c1") assert.NoError(t, err) syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k2", "v2") return nil }, "set k2 by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetObject("k2").SetNewObject("k2.1").SetString("k2.1.1", "v2") return nil }, "set k2.1.1 by c2") @@ -150,12 +150,12 @@ func TestObject(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) // 03. concurrent set between independent keys - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k3", "v3") return nil }, "set k3 by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("k4", "v4") return nil }, "set k4 by c2") diff --git a/test/integration/peer_awareness_test.go b/test/integration/peer_awareness_test.go index 7409d4221..3fcf29c14 100644 --- a/test/integration/peer_awareness_test.go +++ b/test/integration/peer_awareness_test.go @@ -22,8 +22,7 @@ import ( "context" gojson "encoding/json" "fmt" - "github.com/yorkie-team/yorkie/pkg/document/json" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" + "strconv" "sync" "testing" "time" @@ -33,7 +32,9 @@ import ( "github.com/yorkie-team/yorkie/api/types" "github.com/yorkie-team/yorkie/client" "github.com/yorkie-team/yorkie/pkg/document" + "github.com/yorkie-team/yorkie/pkg/document/json" "github.com/yorkie-team/yorkie/pkg/document/key" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/test/helper" ) @@ -212,7 +213,7 @@ func TestPeerAwareness(t *testing.T) { assert.Equal(t, expected, responsePairs) }) - t.Run("allows updatePresence to be called within update", func(t *testing.T) { + t.Run("update presence by calling Document.Update test", func(t *testing.T) { // 01. Create a document and attach it to the clients ctx := context.Background() d1 := document.New(helper.TestDocKey(t)) @@ -223,7 +224,7 @@ func TestPeerAwareness(t *testing.T) { defer func() { assert.NoError(t, c2.Detach(ctx, d2, false)) }() // 02. Update the root of the document and presence - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("key", "value") p.Set("updated", "true") return nil @@ -239,4 +240,34 @@ func TestPeerAwareness(t *testing.T) { assert.NoError(t, err) assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"true"}}`, c1.ID()), string(encoded)) }) + + t.Run("presence with snapshot test", func(t *testing.T) { + // 01. Create a document and attach it to the clients + ctx := context.Background() + d1 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c1.Attach(ctx, d1)) + defer func() { assert.NoError(t, c1.Detach(ctx, d1, false)) }() + d2 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c2.Attach(ctx, d2)) + defer func() { assert.NoError(t, c2.Detach(ctx, d2, false)) }() + + // 02. Update the root of the document and presence + for i := 0; i < int(helper.SnapshotThreshold); i++ { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { + root.SetString("key", "value") + p.Set("updated", strconv.Itoa(i)) + return nil + })) + } + encoded, err := gojson.Marshal(d1.PresenceMap()) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"9"}}`, c1.ID()), string(encoded)) + + // 03 Sync documents and check that the presence is updated on the other client + assert.NoError(t, c1.Sync(ctx)) + assert.NoError(t, c2.Sync(ctx)) + encoded, err = gojson.Marshal(d2.PresenceMap()) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"9"}}`, c1.ID()), string(encoded)) + }) } diff --git a/test/integration/primitive_test.go b/test/integration/primitive_test.go index 50ec623e6..747eb598a 100644 --- a/test/integration/primitive_test.go +++ b/test/integration/primitive_test.go @@ -20,7 +20,6 @@ package integration import ( "context" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "time" @@ -28,6 +27,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/test/helper" ) @@ -42,7 +42,7 @@ func TestPrimitive(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewObject("k1"). SetBool("k1.1", true). SetInteger("k1.2", 2147483647). diff --git a/test/integration/retention_test.go b/test/integration/retention_test.go index a7e335ca3..65120919f 100644 --- a/test/integration/retention_test.go +++ b/test/integration/retention_test.go @@ -33,6 +33,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/server" "github.com/yorkie-team/yorkie/server/backend/background" "github.com/yorkie-team/yorkie/server/backend/database/mongo" @@ -87,19 +88,19 @@ func TestRetention(t *testing.T) { assert.NoError(t, cli.Attach(ctx, doc)) defer func() { assert.NoError(t, cli.Detach(ctx, doc, false)) }() - assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("todos") return nil }, "create todos")) assert.Equal(t, `{"todos":[]}`, doc.Marshal()) - assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("todos").AddString("buy coffee") return nil }, "buy coffee")) assert.Equal(t, `{"todos":["buy coffee"]}`, doc.Marshal()) - assert.NoError(t, doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetArray("todos").AddString("buy bread") return nil }, "buy bread")) @@ -149,7 +150,7 @@ func TestRetention(t *testing.T) { assert.NoError(t, cli1.Attach(ctx, d1)) defer func() { assert.NoError(t, cli1.Detach(ctx, d1, false)) }() - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1") return nil }) @@ -167,7 +168,7 @@ func TestRetention(t *testing.T) { // Create 6 changes for _, edit := range edits { - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(edit.from, edit.to, edit.content) return nil }) @@ -226,7 +227,7 @@ func TestRetention(t *testing.T) { // Create 6 changes for _, edit := range edits { - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(edit.from, edit.to, edit.content) return nil }) diff --git a/test/integration/snapshot_test.go b/test/integration/snapshot_test.go index 710bb6349..7e3cc3a73 100644 --- a/test/integration/snapshot_test.go +++ b/test/integration/snapshot_test.go @@ -30,6 +30,7 @@ import ( "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/server/backend/background" "github.com/yorkie-team/yorkie/test/helper" ) @@ -65,7 +66,7 @@ func TestSnapshot(t *testing.T) { // 01. Update changes over snapshot threshold. for i := 0; i <= int(helper.SnapshotThreshold); i++ { - err := d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetInteger(fmt.Sprintf("%d", i), i) return nil }) @@ -75,7 +76,7 @@ func TestSnapshot(t *testing.T) { assert.NoError(t, err) // 02. Makes local changes then pull a snapshot from the server. - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("key", "value") return nil }) @@ -95,7 +96,7 @@ func TestSnapshot(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1") return nil }) @@ -116,7 +117,7 @@ func TestSnapshot(t *testing.T) { } for _, edit := range edits { - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(edit.from, edit.to, edit.content) return nil }) @@ -140,7 +141,7 @@ func TestSnapshot(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1") return nil }) @@ -153,14 +154,14 @@ func TestSnapshot(t *testing.T) { assert.NoError(t, err) for i := 0; i <= int(helper.SnapshotThreshold); i++ { - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(i, i, "x") return nil }) assert.NoError(t, err) } - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(0, 0, "o") return nil }) diff --git a/test/integration/text_test.go b/test/integration/text_test.go index a60e95658..3d1e0287f 100644 --- a/test/integration/text_test.go +++ b/test/integration/text_test.go @@ -20,13 +20,13 @@ package integration import ( "context" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/test/helper" ) @@ -41,7 +41,7 @@ func TestText(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1") return nil }, "set a new text by c1") @@ -53,13 +53,13 @@ func TestText(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(0, 0, "ABCD") return nil }, "edit 0,0 ABCD by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(0, 0, "1234") return nil }, "edit 0,0 1234 by c2") @@ -67,13 +67,13 @@ func TestText(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(2, 3, "XX") return nil }, "edit 2,3 XX by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(2, 3, "YY") return nil }, "edit 2,3 YY by c2") @@ -81,13 +81,13 @@ func TestText(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(4, 5, "ZZ") return nil }, "edit 4,5 ZZ by c1") assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(2, 3, "TT") return nil }, "edit 2,3 TT by c2") @@ -102,7 +102,7 @@ func TestText(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1").Edit(0, 0, "Hello world", nil) return nil }, `set a new text with "Hello world" by c1`) @@ -114,14 +114,14 @@ func TestText(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") text.Style(0, 1, map[string]string{"b": "1"}) return nil }, `set style b to "H" by c1`) assert.NoError(t, err) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { text := root.GetText("k1") text.Style(0, 5, map[string]string{"i": "1"}) return nil @@ -137,7 +137,7 @@ func TestText(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1") return nil }, "set a new text by c1") @@ -149,7 +149,7 @@ func TestText(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(0, 0, "123") root.GetText("k1").Edit(3, 3, "456") root.GetText("k1").Edit(6, 6, "789") @@ -160,14 +160,14 @@ func TestText(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) assert.Equal(t, `{"k1":[{"val":"123"},{"val":"456"},{"val":"789"}]}`, d2.Marshal()) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(1, 7, "") return nil }, "delete block by c1") assert.NoError(t, err) assert.Equal(t, `{"k1":[{"val":"1"},{"val":"89"}]}`, d1.Marshal()) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(2, 5, "") return nil }, "delete block by c2") @@ -183,7 +183,7 @@ func TestText(t *testing.T) { err := c1.Attach(ctx, d1) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1") return nil }, "set a new text by c1") @@ -195,7 +195,7 @@ func TestText(t *testing.T) { err = c2.Attach(ctx, d2) assert.NoError(t, err) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(0, 0, "0") root.GetText("k1").Edit(1, 1, "0") root.GetText("k1").Edit(2, 2, "0") @@ -206,7 +206,7 @@ func TestText(t *testing.T) { syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}}) assert.Equal(t, `{"k1":[{"val":"0"},{"val":"0"},{"val":"0"}]}`, d2.Marshal()) - err = d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(1, 2, "1") root.GetText("k1").Edit(1, 2, "1") root.GetText("k1").Edit(1, 2, "") @@ -215,7 +215,7 @@ func TestText(t *testing.T) { assert.NoError(t, err) assert.Equal(t, `{"k1":[{"val":"0"},{"val":"0"}]}`, d1.Marshal()) - err = d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetText("k1").Edit(0, 3, "") return nil }, "delete the range includes above new nodes") diff --git a/test/integration/tree_test.go b/test/integration/tree_test.go index 790f75578..cc5e07ed9 100644 --- a/test/integration/tree_test.go +++ b/test/integration/tree_test.go @@ -20,13 +20,13 @@ package integration import ( "context" - "github.com/yorkie-team/yorkie/pkg/document/presenceproxy" "testing" "github.com/stretchr/testify/assert" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/test/helper" ) @@ -37,7 +37,7 @@ func TestTree(t *testing.T) { t.Run("tree", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { // 01. Create a tree and insert a paragraph. root.SetNewTree("t").Edit(0, 0, &json.TreeNode{ Type: "p", @@ -93,7 +93,7 @@ func TestTree(t *testing.T) { t.Run("created from JSON test", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -120,7 +120,7 @@ func TestTree(t *testing.T) { t.Run("created from JSON with attributes test", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -140,7 +140,7 @@ func TestTree(t *testing.T) { t.Run("edit its content test", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -173,7 +173,7 @@ func TestTree(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "

ab

", doc.Root().GetTree("t").ToXML()) - err = doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err = doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -202,7 +202,7 @@ func TestTree(t *testing.T) { t.Run("edit its content with path", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "doc", Children: []json.TreeNode{{ @@ -242,7 +242,7 @@ func TestTree(t *testing.T) { t.Run("edit its content with attributes test", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) - err := doc.Update(func(root *json.Object, p *presenceproxy.Presence) error { + err := doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{Type: "doc"}) assert.Equal(t, "", root.GetTree("t").ToXML()) @@ -274,7 +274,7 @@ func TestTree(t *testing.T) { d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, c1.Attach(ctx, d1)) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "root", Children: []json.TreeNode{{ @@ -299,7 +299,7 @@ func TestTree(t *testing.T) { d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, c1.Attach(ctx, d1)) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "root", Children: []json.TreeNode{ @@ -312,7 +312,7 @@ func TestTree(t *testing.T) { assert.NoError(t, c1.Sync(ctx)) assert.Equal(t, `

ab

cd

`, d1.Root().GetTree("t").ToXML()) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetTree("t").Style(3, 4, map[string]string{"bold": "true"}) return nil })) @@ -333,7 +333,7 @@ func TestTree(t *testing.T) { d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, c1.Attach(ctx, d1)) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "root", Children: []json.TreeNode{{ @@ -349,11 +349,11 @@ func TestTree(t *testing.T) { d2 := document.New(helper.TestDocKey(t)) assert.NoError(t, c2.Attach(ctx, d2)) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetTree("t").Edit(1, 1, &json.TreeNode{Type: "text", Value: "A"}) return nil })) - assert.NoError(t, d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetTree("t").Edit(1, 1, &json.TreeNode{Type: "text", Value: "B"}) return nil })) @@ -369,7 +369,7 @@ func TestTree(t *testing.T) { d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, c1.Attach(ctx, d1)) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "root", Children: []json.TreeNode{{ @@ -385,11 +385,11 @@ func TestTree(t *testing.T) { d2 := document.New(helper.TestDocKey(t)) assert.NoError(t, c2.Attach(ctx, d2)) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetTree("t").Edit(2, 2, &json.TreeNode{Type: "text", Value: "A"}) return nil })) - assert.NoError(t, d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetTree("t").Edit(2, 2, &json.TreeNode{Type: "text", Value: "B"}) return nil })) @@ -404,7 +404,7 @@ func TestTree(t *testing.T) { d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, c1.Attach(ctx, d1)) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewTree("t", &json.TreeNode{ Type: "root", Children: []json.TreeNode{{ @@ -420,11 +420,11 @@ func TestTree(t *testing.T) { d2 := document.New(helper.TestDocKey(t)) assert.NoError(t, c2.Attach(ctx, d2)) - assert.NoError(t, d1.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.GetTree("t").Edit(3, 3, &json.TreeNode{Type: "text", Value: "A"}) return nil })) - assert.NoError(t, d2.Update(func(root *json.Object, p *presenceproxy.Presence) error { + assert.NoError(t, d2.Update(func(root *json.Object, p *presence.Presence) error { root.GetTree("t").Edit(3, 3, &json.TreeNode{Type: "text", Value: "B"}) return nil })) From 313ee1d07d36a1c78e1a198f6002f37b2cfbb4ef Mon Sep 17 00:00:00 2001 From: Yourim Cha Date: Mon, 10 Jul 2023 17:38:07 +0900 Subject: [PATCH 3/5] Add PresenceChange to change --- api/converter/from_pb.go | 19 +- api/converter/to_pb.go | 25 +- api/yorkie/v1/resources.pb.go | 729 +++++++++++++++------ api/yorkie/v1/resources.proto | 12 +- pkg/document/change/change.go | 28 +- pkg/document/change/context.go | 22 +- pkg/document/innerpresence/presence.go | 26 +- pkg/document/presence/presence.go | 13 +- server/backend/database/change_info.go | 26 +- server/backend/database/memory/database.go | 20 +- server/backend/database/mongo/client.go | 14 +- server/packs/serverpacks.go | 10 +- 12 files changed, 685 insertions(+), 259 deletions(-) diff --git a/api/converter/from_pb.go b/api/converter/from_pb.go index 4a192fea6..08f6465bd 100644 --- a/api/converter/from_pb.go +++ b/api/converter/from_pb.go @@ -192,7 +192,7 @@ func FromChanges(pbChanges []*api.Change) ([]*change.Change, error) { changeID, pbChange.Message, ops, - fromPresence(pbChange.Presence), + fromPresenceChange(pbChange.PresenceChange), )) } @@ -341,6 +341,23 @@ func fromPresence(pbPresence *api.Presence) *innerpresence.Presence { return &p } +func fromPresenceChange(pbPresenceChange *api.PresenceChange) *innerpresence.PresenceChange { + if pbPresenceChange == nil { + return nil + } + + var changeType innerpresence.PresenceChangeType + switch pbPresenceChange.Type { + case api.PresenceChange_CHANGE_TYPE_PUT: + changeType = innerpresence.Put + } + p := innerpresence.PresenceChange{ + ChangeType: changeType, + Presence: pbPresenceChange.Presence, + } + return &p +} + func fromSet(pbSet *api.Operation_Set) (*operations.Set, error) { parentCreatedAt, err := fromTimeTicket(pbSet.ParentCreatedAt) if err != nil { diff --git a/api/converter/to_pb.go b/api/converter/to_pb.go index 9d929e289..d2e7b7205 100644 --- a/api/converter/to_pb.go +++ b/api/converter/to_pb.go @@ -151,6 +151,23 @@ func ToPresence(p *innerpresence.Presence) *api.Presence { } } +// ToPresenceChange converts the given model to Protobuf format. +func ToPresenceChange(p *innerpresence.PresenceChange) *api.PresenceChange { + if p == nil { + return nil + } + + changeType := api.PresenceChange_CHANGE_TYPE_UNSPECIFIED + switch p.ChangeType { + case innerpresence.Put: + changeType = api.PresenceChange_CHANGE_TYPE_PUT + } + return &api.PresenceChange{ + Type: changeType, + Presence: p.Presence, + } +} + // ToPresenceInfo converts the given model to Protobuf format. func ToPresenceInfo(info types.PresenceInfo) *api.Presence { return &api.Presence{ @@ -296,10 +313,10 @@ func ToChanges(changes []*change.Change) ([]*api.Change, error) { } pbChanges = append(pbChanges, &api.Change{ - Id: ToChangeID(c.ID()), - Message: c.Message(), - Operations: pbOperations, - Presence: ToPresence(c.Presence()), + Id: ToChangeID(c.ID()), + Message: c.Message(), + Operations: pbOperations, + PresenceChange: ToPresenceChange(c.PresenceChange()), }) } diff --git a/api/yorkie/v1/resources.pb.go b/api/yorkie/v1/resources.pb.go index c466e0c11..5d76d1327 100644 --- a/api/yorkie/v1/resources.pb.go +++ b/api/yorkie/v1/resources.pb.go @@ -115,9 +115,37 @@ func (DocEventType) EnumDescriptor() ([]byte, []int) { return fileDescriptor_36361b2f5d0f0896, []int{1} } -// /////////////////////////////////////// +type PresenceChange_ChangeType int32 + +const ( + PresenceChange_CHANGE_TYPE_UNSPECIFIED PresenceChange_ChangeType = 0 + PresenceChange_CHANGE_TYPE_PUT PresenceChange_ChangeType = 1 + PresenceChange_CHANGE_TYPE_DELETE PresenceChange_ChangeType = 2 +) + +var PresenceChange_ChangeType_name = map[int32]string{ + 0: "CHANGE_TYPE_UNSPECIFIED", + 1: "CHANGE_TYPE_PUT", + 2: "CHANGE_TYPE_DELETE", +} + +var PresenceChange_ChangeType_value = map[string]int32{ + "CHANGE_TYPE_UNSPECIFIED": 0, + "CHANGE_TYPE_PUT": 1, + "CHANGE_TYPE_DELETE": 2, +} + +func (x PresenceChange_ChangeType) String() string { + return proto.EnumName(PresenceChange_ChangeType_name, int32(x)) +} + +func (PresenceChange_ChangeType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_36361b2f5d0f0896, []int{18, 0} +} + +///////////////////////////////////////// // Messages for Snapshot // -// /////////////////////////////////////// +///////////////////////////////////////// type Snapshot struct { Root *JSONElement `protobuf:"bytes,1,opt,name=root,proto3" json:"root,omitempty"` PresenceMap map[string]*Presence `protobuf:"bytes,2,rep,name=presence_map,json=presenceMap,proto3" json:"presence_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` @@ -263,13 +291,13 @@ func (m *ChangePack) GetIsRemoved() bool { } type Change struct { - Id *ChangeID `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` - Operations []*Operation `protobuf:"bytes,3,rep,name=operations,proto3" json:"operations,omitempty"` - Presence *Presence `protobuf:"bytes,4,opt,name=presence,proto3" json:"presence,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Id *ChangeID `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Operations []*Operation `protobuf:"bytes,3,rep,name=operations,proto3" json:"operations,omitempty"` + PresenceChange *PresenceChange `protobuf:"bytes,4,opt,name=presence_change,json=presenceChange,proto3" json:"presence_change,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Change) Reset() { *m = Change{} } @@ -326,9 +354,9 @@ func (m *Change) GetOperations() []*Operation { return nil } -func (m *Change) GetPresence() *Presence { +func (m *Change) GetPresenceChange() *PresenceChange { if m != nil { - return m.Presence + return m.PresenceChange } return nil } @@ -406,7 +434,6 @@ func (m *ChangeID) GetActorId() []byte { type Operation struct { // Types that are valid to be assigned to Body: - // // *Operation_Set_ // *Operation_Add_ // *Operation_Move_ @@ -1420,7 +1447,6 @@ func (m *JSONElementSimple) GetValue() []byte { type JSONElement struct { // Types that are valid to be assigned to Body: - // // *JSONElement_JsonObject // *JSONElement_JsonArray // *JSONElement_Primitive_ @@ -2833,6 +2859,61 @@ func (m *DocumentSummary) GetUpdatedAt() *types.Timestamp { return nil } +type PresenceChange struct { + Type PresenceChange_ChangeType `protobuf:"varint,1,opt,name=type,proto3,enum=yorkie.v1.PresenceChange_ChangeType" json:"type,omitempty"` + Presence map[string]string `protobuf:"bytes,2,rep,name=presence,proto3" json:"presence,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PresenceChange) Reset() { *m = PresenceChange{} } +func (m *PresenceChange) String() string { return proto.CompactTextString(m) } +func (*PresenceChange) ProtoMessage() {} +func (*PresenceChange) Descriptor() ([]byte, []int) { + return fileDescriptor_36361b2f5d0f0896, []int{18} +} +func (m *PresenceChange) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PresenceChange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PresenceChange.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PresenceChange) XXX_Merge(src proto.Message) { + xxx_messageInfo_PresenceChange.Merge(m, src) +} +func (m *PresenceChange) XXX_Size() int { + return m.Size() +} +func (m *PresenceChange) XXX_DiscardUnknown() { + xxx_messageInfo_PresenceChange.DiscardUnknown(m) +} + +var xxx_messageInfo_PresenceChange proto.InternalMessageInfo + +func (m *PresenceChange) GetType() PresenceChange_ChangeType { + if m != nil { + return m.Type + } + return PresenceChange_CHANGE_TYPE_UNSPECIFIED +} + +func (m *PresenceChange) GetPresence() map[string]string { + if m != nil { + return m.Presence + } + return nil +} + type Presence struct { Data map[string]string `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -2844,7 +2925,7 @@ func (m *Presence) Reset() { *m = Presence{} } func (m *Presence) String() string { return proto.CompactTextString(m) } func (*Presence) ProtoMessage() {} func (*Presence) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{18} + return fileDescriptor_36361b2f5d0f0896, []int{19} } func (m *Presence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2892,7 +2973,7 @@ func (m *Client) Reset() { *m = Client{} } func (m *Client) String() string { return proto.CompactTextString(m) } func (*Client) ProtoMessage() {} func (*Client) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{19} + return fileDescriptor_36361b2f5d0f0896, []int{20} } func (m *Client) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2947,7 +3028,7 @@ func (m *Checkpoint) Reset() { *m = Checkpoint{} } func (m *Checkpoint) String() string { return proto.CompactTextString(m) } func (*Checkpoint) ProtoMessage() {} func (*Checkpoint) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{20} + return fileDescriptor_36361b2f5d0f0896, []int{21} } func (m *Checkpoint) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3003,7 +3084,7 @@ func (m *TextNodePos) Reset() { *m = TextNodePos{} } func (m *TextNodePos) String() string { return proto.CompactTextString(m) } func (*TextNodePos) ProtoMessage() {} func (*TextNodePos) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{21} + return fileDescriptor_36361b2f5d0f0896, []int{22} } func (m *TextNodePos) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3066,7 +3147,7 @@ func (m *TimeTicket) Reset() { *m = TimeTicket{} } func (m *TimeTicket) String() string { return proto.CompactTextString(m) } func (*TimeTicket) ProtoMessage() {} func (*TimeTicket) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{22} + return fileDescriptor_36361b2f5d0f0896, []int{23} } func (m *TimeTicket) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3129,7 +3210,7 @@ func (m *DocEvent) Reset() { *m = DocEvent{} } func (m *DocEvent) String() string { return proto.CompactTextString(m) } func (*DocEvent) ProtoMessage() {} func (*DocEvent) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{23} + return fileDescriptor_36361b2f5d0f0896, []int{24} } func (m *DocEvent) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3182,6 +3263,7 @@ func (m *DocEvent) GetDocumentId() string { func init() { proto.RegisterEnum("yorkie.v1.ValueType", ValueType_name, ValueType_value) proto.RegisterEnum("yorkie.v1.DocEventType", DocEventType_name, DocEventType_value) + proto.RegisterEnum("yorkie.v1.PresenceChange_ChangeType", PresenceChange_ChangeType_name, PresenceChange_ChangeType_value) proto.RegisterType((*Snapshot)(nil), "yorkie.v1.Snapshot") proto.RegisterMapType((map[string]*Presence)(nil), "yorkie.v1.Snapshot.PresenceMapEntry") proto.RegisterType((*ChangePack)(nil), "yorkie.v1.ChangePack") @@ -3224,6 +3306,8 @@ func init() { proto.RegisterType((*UpdatableProjectFields)(nil), "yorkie.v1.UpdatableProjectFields") proto.RegisterType((*UpdatableProjectFields_AuthWebhookMethods)(nil), "yorkie.v1.UpdatableProjectFields.AuthWebhookMethods") proto.RegisterType((*DocumentSummary)(nil), "yorkie.v1.DocumentSummary") + proto.RegisterType((*PresenceChange)(nil), "yorkie.v1.PresenceChange") + proto.RegisterMapType((map[string]string)(nil), "yorkie.v1.PresenceChange.PresenceEntry") proto.RegisterType((*Presence)(nil), "yorkie.v1.Presence") proto.RegisterMapType((map[string]string)(nil), "yorkie.v1.Presence.DataEntry") proto.RegisterType((*Client)(nil), "yorkie.v1.Client") @@ -3236,163 +3320,169 @@ func init() { func init() { proto.RegisterFile("yorkie/v1/resources.proto", fileDescriptor_36361b2f5d0f0896) } var fileDescriptor_36361b2f5d0f0896 = []byte{ - // 2482 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xcd, 0x6f, 0xdb, 0xc8, - 0xd9, 0x37, 0x29, 0xea, 0x83, 0x8f, 0x9c, 0x58, 0x19, 0xe7, 0x43, 0x51, 0x12, 0xc7, 0x51, 0xf2, - 0xe6, 0x75, 0x92, 0x56, 0x4e, 0xdc, 0xa4, 0x1f, 0x9b, 0x6e, 0x5b, 0x59, 0xe6, 0xda, 0xce, 0xda, - 0xb2, 0x41, 0xc9, 0x49, 0xb3, 0x68, 0x41, 0xd0, 0xe4, 0x24, 0xe6, 0x5a, 0x12, 0xb9, 0x24, 0xa5, - 0x8d, 0x6e, 0x45, 0xd1, 0x02, 0xed, 0xb1, 0xb7, 0xfe, 0x07, 0x45, 0x2f, 0xbd, 0xef, 0xb5, 0x87, - 0xa2, 0x3d, 0x14, 0x2d, 0xd0, 0x00, 0xbd, 0xb6, 0xe9, 0xa1, 0xe8, 0xb1, 0x28, 0xda, 0x5b, 0x81, - 0x62, 0x66, 0x38, 0x14, 0x45, 0x51, 0x8a, 0xa2, 0x1a, 0x8b, 0x04, 0xbd, 0x69, 0x66, 0x7e, 0xcf, - 0xcc, 0xf3, 0x3d, 0xcf, 0xf0, 0x11, 0x5c, 0xec, 0xdb, 0xee, 0xb1, 0x85, 0x57, 0x7b, 0xf7, 0x56, - 0x5d, 0xec, 0xd9, 0x5d, 0xd7, 0xc0, 0x5e, 0xc5, 0x71, 0x6d, 0xdf, 0x46, 0x32, 0x5b, 0xaa, 0xf4, - 0xee, 0x95, 0xae, 0x3e, 0xb7, 0xed, 0xe7, 0x2d, 0xbc, 0x4a, 0x17, 0x0e, 0xbb, 0xcf, 0x56, 0x7d, - 0xab, 0x8d, 0x3d, 0x5f, 0x6f, 0x3b, 0x0c, 0x5b, 0x5a, 0x8a, 0x03, 0x3e, 0x75, 0x75, 0xc7, 0xc1, - 0x6e, 0xb0, 0x57, 0xf9, 0xa5, 0x00, 0xb9, 0x46, 0x47, 0x77, 0xbc, 0x23, 0xdb, 0x47, 0xb7, 0x41, - 0x72, 0x6d, 0xdb, 0x2f, 0x0a, 0xcb, 0xc2, 0x4a, 0x7e, 0xed, 0x7c, 0x25, 0x3c, 0xa7, 0xf2, 0xa8, - 0xb1, 0x57, 0x57, 0x5a, 0xb8, 0x8d, 0x3b, 0xbe, 0x4a, 0x31, 0x68, 0x13, 0xe6, 0x1d, 0x17, 0x7b, - 0xb8, 0x63, 0x60, 0xad, 0xad, 0x3b, 0x45, 0x71, 0x39, 0xb5, 0x92, 0x5f, 0xbb, 0x11, 0xa1, 0xe1, - 0xdb, 0x56, 0xf6, 0x03, 0xdc, 0xae, 0xee, 0x28, 0x1d, 0xdf, 0xed, 0xab, 0x79, 0x67, 0x30, 0x53, - 0x6a, 0x40, 0x21, 0x0e, 0x40, 0x05, 0x48, 0x1d, 0xe3, 0x3e, 0xe5, 0x43, 0x56, 0xc9, 0x4f, 0x74, - 0x0b, 0xd2, 0x3d, 0xbd, 0xd5, 0xc5, 0x45, 0x91, 0xf2, 0xb6, 0x18, 0x39, 0x87, 0x53, 0xab, 0x0c, - 0xf1, 0x9e, 0xf8, 0x55, 0xa1, 0xfc, 0x13, 0x11, 0xa0, 0x76, 0xa4, 0x77, 0x9e, 0xe3, 0x7d, 0xdd, - 0x38, 0x46, 0xd7, 0x60, 0xde, 0xb4, 0x8d, 0x2e, 0x61, 0x5f, 0x1b, 0x6c, 0x9c, 0xe7, 0x73, 0x1f, - 0xe2, 0x3e, 0x7a, 0x00, 0x60, 0x1c, 0x61, 0xe3, 0xd8, 0xb1, 0xad, 0x8e, 0x1f, 0x9c, 0x72, 0x2e, - 0x72, 0x4a, 0x2d, 0x5c, 0x54, 0x23, 0x40, 0x54, 0x82, 0x9c, 0x17, 0xc8, 0x59, 0x4c, 0x2d, 0x0b, - 0x2b, 0xf3, 0x6a, 0x38, 0x46, 0x77, 0x20, 0x6b, 0x50, 0x1e, 0xbc, 0xa2, 0x44, 0xb5, 0x73, 0x66, - 0x68, 0x3f, 0xb2, 0xa2, 0x72, 0x04, 0xaa, 0xc2, 0x99, 0xb6, 0xd5, 0xd1, 0xbc, 0x7e, 0xc7, 0xc0, - 0xa6, 0xe6, 0x5b, 0xc6, 0x31, 0xf6, 0x8b, 0xe9, 0x11, 0x36, 0x9a, 0x56, 0x1b, 0x37, 0xe9, 0xa2, - 0xba, 0xd0, 0xb6, 0x3a, 0x0d, 0x0a, 0x67, 0x13, 0xe8, 0x0a, 0x80, 0xe5, 0x69, 0x2e, 0x6e, 0xdb, - 0x3d, 0x6c, 0x16, 0x33, 0xcb, 0xc2, 0x4a, 0x4e, 0x95, 0x2d, 0x4f, 0x65, 0x13, 0xe5, 0x5f, 0x08, - 0x90, 0x61, 0xa7, 0xa2, 0xeb, 0x20, 0x5a, 0x66, 0x60, 0xe6, 0xc5, 0x11, 0xa6, 0xb6, 0x37, 0x54, - 0xd1, 0x32, 0x51, 0x11, 0xb2, 0x6d, 0xec, 0x79, 0xfa, 0x73, 0xa6, 0x74, 0x59, 0xe5, 0x43, 0x74, - 0x1f, 0xc0, 0x76, 0xb0, 0xab, 0xfb, 0x96, 0xdd, 0xf1, 0x8a, 0x29, 0x2a, 0xdb, 0xd9, 0xc8, 0x36, - 0x7b, 0x7c, 0x51, 0x8d, 0xe0, 0xd0, 0x2a, 0xe4, 0xb8, 0xdd, 0x8b, 0xd2, 0x78, 0x2b, 0x86, 0xa0, - 0xf2, 0x0f, 0x05, 0xc8, 0x71, 0x8e, 0x88, 0x70, 0x46, 0xcb, 0x22, 0x06, 0xf4, 0xf0, 0x27, 0x94, - 0xf5, 0x53, 0xaa, 0xcc, 0x66, 0x1a, 0xf8, 0x13, 0x74, 0x0d, 0xc0, 0xc3, 0x6e, 0x0f, 0xbb, 0x74, - 0x99, 0xf0, 0x9b, 0x5a, 0x17, 0xef, 0x0a, 0xaa, 0xcc, 0x66, 0x09, 0xe4, 0x32, 0x64, 0x5b, 0x7a, - 0xdb, 0xb1, 0x5d, 0x66, 0x29, 0xb6, 0xce, 0xa7, 0xd0, 0x45, 0xc8, 0xe9, 0x86, 0x6f, 0xbb, 0x9a, - 0x65, 0x52, 0xee, 0xe6, 0xd5, 0x2c, 0x1d, 0x6f, 0x9b, 0xe5, 0xdf, 0x94, 0x40, 0x0e, 0x45, 0x42, - 0x5f, 0x80, 0x94, 0x87, 0x79, 0x8c, 0x14, 0x93, 0xa4, 0xae, 0x34, 0xb0, 0xbf, 0x35, 0xa7, 0x12, - 0x18, 0x41, 0xeb, 0xa6, 0x19, 0xf8, 0x53, 0x32, 0xba, 0x6a, 0x9a, 0x04, 0xad, 0x9b, 0x26, 0x5a, - 0x05, 0x89, 0xd8, 0x8a, 0xf2, 0x97, 0x5f, 0xbb, 0x98, 0x08, 0xdf, 0xb5, 0x7b, 0x78, 0x6b, 0x4e, - 0xa5, 0x40, 0xf4, 0x00, 0x32, 0xcc, 0xde, 0x81, 0x46, 0x2f, 0x25, 0x92, 0x30, 0x0f, 0xd8, 0x9a, - 0x53, 0x03, 0x30, 0x39, 0x07, 0x9b, 0x16, 0xf7, 0xaf, 0xe4, 0x73, 0x14, 0xd3, 0x22, 0x52, 0x50, - 0x20, 0x39, 0xc7, 0xc3, 0x2d, 0x6c, 0xf8, 0xd4, 0xad, 0xc6, 0x9d, 0xd3, 0xa0, 0x10, 0x72, 0x0e, - 0x03, 0xa3, 0x35, 0x48, 0x7b, 0x7e, 0xbf, 0x85, 0x8b, 0x59, 0x4a, 0x55, 0x4a, 0xa6, 0x22, 0x88, - 0xad, 0x39, 0x95, 0x41, 0xd1, 0x43, 0xc8, 0x59, 0x1d, 0xc3, 0xc5, 0xba, 0x87, 0x8b, 0x39, 0x4a, - 0x76, 0x25, 0x91, 0x6c, 0x3b, 0x00, 0x6d, 0xcd, 0xa9, 0x21, 0x01, 0xfa, 0x3a, 0xc8, 0xbe, 0x8b, - 0xb1, 0x46, 0xa5, 0x93, 0x27, 0x50, 0x37, 0x5d, 0x8c, 0x03, 0x09, 0x73, 0x7e, 0xf0, 0x1b, 0x7d, - 0x13, 0x80, 0x52, 0x33, 0x9e, 0x81, 0x92, 0x2f, 0x8d, 0x25, 0xe7, 0x7c, 0xd3, 0x13, 0xe9, 0xa0, - 0xf4, 0x2b, 0x01, 0x52, 0x0d, 0xec, 0x93, 0x60, 0x76, 0x74, 0x97, 0x38, 0x2b, 0xe1, 0xcb, 0xc7, - 0xa6, 0xa6, 0x73, 0x8f, 0x19, 0x17, 0xcc, 0x0c, 0x5f, 0x63, 0xf0, 0xaa, 0xcf, 0x53, 0xa0, 0x38, - 0x48, 0x81, 0x6b, 0x3c, 0x05, 0x32, 0xef, 0xb8, 0x9c, 0x9c, 0x9e, 0x1b, 0x56, 0xdb, 0x69, 0xf1, - 0x5c, 0x88, 0xbe, 0x0c, 0x79, 0xfc, 0x02, 0x1b, 0xdd, 0x80, 0x05, 0x69, 0x12, 0x0b, 0xc0, 0x91, - 0x55, 0xbf, 0xf4, 0x0f, 0x01, 0x52, 0x55, 0xd3, 0x3c, 0x09, 0x41, 0xde, 0x87, 0x05, 0xc7, 0xc5, - 0xbd, 0xe8, 0x06, 0xe2, 0xa4, 0x0d, 0x4e, 0x11, 0xf4, 0x80, 0xfc, 0xf3, 0x94, 0xfa, 0x5f, 0x02, - 0x48, 0x24, 0xbc, 0xde, 0x02, 0xb1, 0xef, 0x03, 0x44, 0x28, 0x53, 0x93, 0x28, 0x65, 0x23, 0xa4, - 0x9a, 0x55, 0xf0, 0xcf, 0x04, 0xc8, 0xb0, 0x24, 0x71, 0x12, 0xa2, 0x0f, 0xf3, 0x2e, 0xce, 0xc6, - 0x7b, 0x6a, 0x5a, 0xde, 0x7f, 0x29, 0x81, 0x44, 0xa3, 0xf7, 0x04, 0x38, 0xbf, 0x0d, 0xd2, 0x33, - 0xd7, 0x6e, 0x07, 0x3c, 0x47, 0x0b, 0xa0, 0x26, 0x7e, 0xe1, 0xd7, 0x6d, 0x13, 0xef, 0xdb, 0x9e, - 0x4a, 0x31, 0xe8, 0x26, 0x88, 0xbe, 0x1d, 0xb0, 0x39, 0x0e, 0x29, 0xfa, 0x36, 0x3a, 0x82, 0x0b, - 0x03, 0x7e, 0x48, 0xa9, 0xa4, 0x1d, 0xf6, 0x35, 0x7a, 0xb5, 0x04, 0x55, 0xc1, 0xda, 0xd8, 0xf4, - 0x5b, 0x09, 0x39, 0xdb, 0xd5, 0x9d, 0xf5, 0x7e, 0x95, 0x10, 0xb1, 0x0a, 0x6a, 0xd1, 0x18, 0x5d, - 0x21, 0x17, 0xb6, 0x61, 0x77, 0x7c, 0xdc, 0x61, 0x89, 0x5d, 0x56, 0xf9, 0x30, 0xae, 0xdb, 0xcc, - 0x94, 0xba, 0x45, 0xdb, 0x00, 0xba, 0xef, 0xbb, 0xd6, 0x61, 0xd7, 0xc7, 0x5e, 0x31, 0x4b, 0xd9, - 0xbd, 0x35, 0x9e, 0xdd, 0x6a, 0x88, 0x65, 0x5c, 0x46, 0x88, 0x4b, 0xdf, 0x85, 0xe2, 0x38, 0x69, - 0x12, 0xca, 0xbd, 0x3b, 0xc3, 0xe5, 0xde, 0x18, 0x56, 0x07, 0x05, 0x5f, 0xe9, 0x7d, 0x58, 0x88, - 0x9d, 0x9e, 0xb0, 0xeb, 0xd9, 0xe8, 0xae, 0x72, 0x94, 0xfc, 0x8f, 0x02, 0x64, 0xd8, 0xed, 0xf5, - 0xb6, 0xba, 0xd1, 0xac, 0xa1, 0xfd, 0x67, 0x11, 0xd2, 0xf4, 0x72, 0x7a, 0x5b, 0x05, 0x7b, 0x34, - 0xe4, 0x63, 0x2c, 0x24, 0x6e, 0x8f, 0x2f, 0x14, 0x26, 0x39, 0x59, 0x5c, 0x49, 0xe9, 0x69, 0x95, - 0xf4, 0x5f, 0x7a, 0xcf, 0x67, 0x02, 0xe4, 0x78, 0x39, 0x72, 0x12, 0x6a, 0x5e, 0x1b, 0xf6, 0xfe, - 0x59, 0xee, 0xbc, 0xa9, 0xd3, 0xe7, 0xf7, 0x44, 0xc8, 0xf1, 0x62, 0xe8, 0x24, 0x78, 0xbf, 0x39, - 0xe4, 0x22, 0x28, 0x4a, 0xe5, 0xe2, 0x88, 0x7b, 0x94, 0x23, 0xee, 0x91, 0x84, 0x22, 0xae, 0xf1, - 0xc5, 0x41, 0x42, 0x63, 0x7e, 0xb1, 0x18, 0x03, 0x12, 0x3f, 0x1a, 0x9b, 0xe5, 0xd2, 0x6f, 0x10, - 0x22, 0x72, 0x58, 0xd0, 0xbd, 0x6d, 0x3a, 0xa8, 0x27, 0x84, 0x47, 0x65, 0x72, 0x4d, 0xfa, 0x16, - 0x86, 0xc8, 0x7a, 0x06, 0xa4, 0x43, 0xdb, 0xec, 0x97, 0xff, 0x2e, 0xc0, 0x99, 0x11, 0x1f, 0x8e, - 0x55, 0x0c, 0xc2, 0x94, 0x15, 0xc3, 0x5d, 0xc8, 0xd1, 0x97, 0xed, 0x6b, 0xab, 0x8c, 0x2c, 0x85, - 0xb1, 0xca, 0x24, 0x78, 0x1e, 0xbf, 0xbe, 0xaa, 0x0a, 0x80, 0x55, 0x1f, 0xad, 0x80, 0xe4, 0xf7, - 0x1d, 0xf6, 0xc4, 0x3a, 0x3d, 0xf4, 0xd0, 0x7d, 0x4c, 0xe4, 0x6b, 0xf6, 0x1d, 0xac, 0x52, 0xc4, - 0x40, 0xfe, 0x34, 0x7d, 0x41, 0xb2, 0x41, 0xf9, 0xe7, 0xa7, 0x20, 0x1f, 0x91, 0x19, 0x6d, 0x40, - 0xfe, 0x63, 0xcf, 0xee, 0x68, 0xf6, 0xe1, 0xc7, 0xe4, 0x45, 0xc5, 0xc4, 0xbd, 0x96, 0x1c, 0xe4, - 0xf4, 0xf7, 0x1e, 0x05, 0x6e, 0xcd, 0xa9, 0x40, 0xe8, 0xd8, 0x08, 0x55, 0x81, 0x8e, 0x34, 0xdd, - 0x75, 0xf5, 0x7e, 0x20, 0xff, 0xf2, 0x84, 0x4d, 0xaa, 0x04, 0x47, 0x9e, 0x2b, 0x84, 0x8a, 0x0e, - 0xd0, 0xb7, 0x40, 0x76, 0x5c, 0xab, 0x6d, 0xf9, 0x56, 0xf8, 0xe6, 0x1c, 0xb7, 0xc3, 0x3e, 0xc7, - 0x91, 0x1d, 0x42, 0x22, 0x74, 0x0f, 0x24, 0x1f, 0xbf, 0xe0, 0x6e, 0x74, 0x69, 0x0c, 0x31, 0x49, - 0xf9, 0xe4, 0x29, 0x49, 0xa0, 0xe8, 0x3d, 0x12, 0xd4, 0xdd, 0x8e, 0x8f, 0xdd, 0xa0, 0x0e, 0x59, - 0x1a, 0x43, 0x55, 0x63, 0xa8, 0xad, 0x39, 0x95, 0x13, 0xd0, 0xe3, 0x5c, 0xcc, 0x9f, 0x93, 0x63, - 0x8f, 0x73, 0x31, 0x7d, 0x21, 0x13, 0x68, 0xe9, 0xa5, 0x00, 0x30, 0xd0, 0x21, 0x5a, 0x81, 0x74, - 0xc7, 0x36, 0xb1, 0x57, 0x14, 0x68, 0x24, 0x45, 0xa3, 0x4e, 0xdd, 0x6a, 0xd2, 0x7c, 0xc2, 0x00, - 0x33, 0x56, 0xb1, 0x51, 0x9f, 0x4c, 0xcd, 0xe0, 0x93, 0xd2, 0x74, 0x3e, 0x59, 0xfa, 0x83, 0x00, - 0x72, 0x68, 0xd5, 0x89, 0x52, 0x6d, 0x56, 0xdf, 0x1d, 0xa9, 0xfe, 0x26, 0x80, 0x1c, 0x7a, 0x5a, - 0x18, 0x77, 0xc2, 0xf4, 0x71, 0x27, 0x46, 0xe2, 0x6e, 0xc6, 0x37, 0x54, 0x54, 0x56, 0x69, 0x06, - 0x59, 0xd3, 0x53, 0xca, 0xfa, 0x3b, 0x01, 0x24, 0x12, 0x18, 0xe8, 0xd6, 0xb0, 0xf1, 0x16, 0x13, - 0x6a, 0xa5, 0x77, 0xc3, 0x7a, 0x7f, 0x15, 0x20, 0x1b, 0x04, 0xed, 0xff, 0x82, 0xed, 0x5c, 0x8c, - 0x27, 0xda, 0x8e, 0xd7, 0x27, 0xef, 0x82, 0xed, 0xc2, 0xfb, 0x79, 0x17, 0xb2, 0x41, 0x1e, 0x4c, - 0xb8, 0xde, 0xef, 0x42, 0x16, 0xb3, 0x1c, 0x9b, 0xf0, 0x02, 0x88, 0xb6, 0x08, 0x38, 0xac, 0x6c, - 0x40, 0x36, 0x48, 0x40, 0xa4, 0x28, 0xea, 0x90, 0xab, 0x42, 0x18, 0x29, 0x77, 0x78, 0x8a, 0xa2, - 0xeb, 0x33, 0x1c, 0xf2, 0x18, 0x72, 0x84, 0x9e, 0x94, 0x27, 0x03, 0x6f, 0x12, 0x22, 0x15, 0x08, - 0xd1, 0x49, 0xd7, 0x31, 0xa7, 0xd3, 0x7d, 0x00, 0xac, 0xfa, 0xe5, 0xdf, 0x92, 0xd2, 0x38, 0x88, - 0x40, 0xf4, 0x7f, 0x91, 0x4f, 0xe6, 0xe7, 0x12, 0x42, 0x34, 0xf8, 0x68, 0x9e, 0x58, 0x01, 0xcd, - 0x58, 0x77, 0x3c, 0x80, 0xbc, 0xd5, 0xf1, 0x34, 0xfa, 0x19, 0x29, 0xf8, 0x2a, 0x3d, 0xf6, 0x6c, - 0xd9, 0xea, 0x78, 0xfb, 0x2e, 0xee, 0x6d, 0x9b, 0xa8, 0x36, 0x54, 0x31, 0xa6, 0xa9, 0x63, 0x5e, - 0x4f, 0xa0, 0x9a, 0xf8, 0x5c, 0x57, 0xa7, 0x29, 0xf7, 0x26, 0x34, 0x65, 0xb8, 0x41, 0xa2, 0x4d, - 0x99, 0x8f, 0x00, 0x06, 0x1c, 0xcf, 0x58, 0xf3, 0x9d, 0x87, 0x8c, 0xfd, 0xec, 0x99, 0x87, 0x99, - 0x15, 0xd3, 0x6a, 0x30, 0x2a, 0xff, 0x33, 0x78, 0xc6, 0x50, 0x5b, 0xdd, 0x80, 0x94, 0x63, 0x7b, - 0x09, 0x9e, 0xc6, 0x0b, 0x6b, 0xb2, 0x8c, 0x50, 0x90, 0xa2, 0x98, 0xa5, 0x62, 0xc9, 0x28, 0x35, - 0xde, 0x7c, 0x53, 0x86, 0x14, 0xba, 0x0f, 0xf3, 0xa1, 0xf9, 0x08, 0x3b, 0xe9, 0xb1, 0xec, 0x40, - 0x60, 0xbc, 0x7d, 0xdb, 0x23, 0x1c, 0x98, 0xd8, 0xf1, 0x8f, 0x68, 0x71, 0x94, 0x56, 0xd9, 0x20, - 0x66, 0xd3, 0xec, 0xa8, 0x4d, 0x03, 0xd1, 0x3f, 0x77, 0x9b, 0x3e, 0x81, 0x6c, 0x20, 0xc5, 0x09, - 0x1b, 0xb4, 0x0d, 0xd2, 0x81, 0x87, 0x5d, 0x74, 0x3a, 0x8c, 0x3b, 0x99, 0x06, 0x58, 0x09, 0x72, - 0x5d, 0x0f, 0xbb, 0x1d, 0xbd, 0xcd, 0x2d, 0x17, 0x8e, 0xd1, 0xd7, 0x12, 0x2e, 0x8d, 0x52, 0x85, - 0x75, 0x40, 0x2b, 0xbc, 0x03, 0x4a, 0xf9, 0xa0, 0x2d, 0xd2, 0x08, 0x1b, 0xe5, 0x7f, 0x8b, 0x90, - 0xdd, 0x77, 0x6d, 0x5a, 0x23, 0xc6, 0x8f, 0x44, 0x20, 0x45, 0x8e, 0xa3, 0xbf, 0xd1, 0x15, 0x00, - 0xa7, 0x7b, 0xd8, 0xb2, 0x0c, 0xda, 0x4f, 0x64, 0xde, 0x22, 0xb3, 0x99, 0x0f, 0x71, 0x9f, 0x2c, - 0x7b, 0xd8, 0x70, 0x31, 0x6b, 0x37, 0x4a, 0x6c, 0x99, 0xcd, 0x90, 0xe5, 0x15, 0x28, 0xe8, 0x5d, - 0xff, 0x48, 0xfb, 0x14, 0x1f, 0x1e, 0xd9, 0xf6, 0xb1, 0xd6, 0x75, 0x5b, 0xc1, 0x27, 0xbb, 0xd3, - 0x64, 0xfe, 0x09, 0x9b, 0x3e, 0x70, 0x5b, 0xe8, 0x2e, 0x9c, 0x1d, 0x42, 0xb6, 0xb1, 0x7f, 0x64, - 0x9b, 0x5e, 0x31, 0xb3, 0x9c, 0x5a, 0x91, 0x55, 0x14, 0x41, 0xef, 0xb2, 0x15, 0xf4, 0x0d, 0xb8, - 0x14, 0x34, 0xca, 0x4c, 0xac, 0x1b, 0xbe, 0xd5, 0xd3, 0x7d, 0xac, 0xf9, 0x47, 0x2e, 0xf6, 0x8e, - 0xec, 0x96, 0x49, 0x4b, 0x67, 0x59, 0xbd, 0xc8, 0x20, 0x1b, 0x21, 0xa2, 0xc9, 0x01, 0x31, 0x25, - 0xe6, 0xde, 0x40, 0x89, 0x84, 0x34, 0x92, 0x66, 0xe5, 0xd7, 0x93, 0x0e, 0x72, 0xed, 0x8f, 0x52, - 0x70, 0xfe, 0x80, 0x8c, 0xf4, 0xc3, 0x16, 0x0e, 0x0c, 0xf1, 0x81, 0x85, 0x5b, 0xa6, 0x87, 0xee, - 0x06, 0xea, 0x17, 0x82, 0x8f, 0x21, 0xf1, 0xfd, 0x1a, 0xbe, 0x6b, 0x75, 0x9e, 0xd3, 0xb2, 0x22, - 0x30, 0xce, 0x07, 0x09, 0xea, 0x15, 0xa7, 0xa0, 0x8e, 0x2b, 0xff, 0xd9, 0x18, 0xe5, 0x33, 0xcf, - 0xba, 0x1f, 0xf1, 0xed, 0x64, 0xd6, 0x2b, 0xd5, 0x11, 0xf3, 0x24, 0x9a, 0xec, 0x3b, 0x93, 0x4d, - 0x26, 0x4d, 0xc1, 0xfa, 0x78, 0x83, 0x96, 0x2a, 0x80, 0x46, 0xf9, 0x60, 0xdd, 0x5d, 0x26, 0x8e, - 0x40, 0x7d, 0x89, 0x0f, 0xcb, 0xdf, 0x17, 0x61, 0x61, 0x23, 0xe8, 0x8c, 0x37, 0xba, 0xed, 0xb6, - 0xee, 0xf6, 0x47, 0x42, 0x62, 0xb4, 0x3b, 0x15, 0x6f, 0x84, 0xcb, 0x91, 0x46, 0xf8, 0xb0, 0x4b, - 0x49, 0x6f, 0xe2, 0x52, 0x0f, 0x21, 0xaf, 0x1b, 0x06, 0xf6, 0xbc, 0x68, 0x81, 0x36, 0x89, 0x16, - 0x38, 0x7c, 0xc4, 0x1f, 0x33, 0x6f, 0xe2, 0x8f, 0x3d, 0xc8, 0xf1, 0x8e, 0x34, 0x79, 0x75, 0x12, - 0xeb, 0x06, 0x35, 0xde, 0x95, 0x84, 0xa6, 0x75, 0x65, 0x43, 0xf7, 0x75, 0x96, 0x70, 0x29, 0xb4, - 0xf4, 0x15, 0x90, 0xc3, 0xa9, 0x37, 0xf9, 0x4e, 0x52, 0xde, 0x86, 0x4c, 0x8d, 0x5a, 0x32, 0xa2, - 0xf2, 0x79, 0xaa, 0xf2, 0x68, 0xfb, 0x5c, 0x9c, 0xa6, 0x7d, 0x5e, 0x07, 0x18, 0xfc, 0x69, 0x21, - 0xd6, 0x20, 0x17, 0x92, 0x1a, 0xe4, 0xc3, 0x2d, 0x76, 0x31, 0xd6, 0x62, 0x2f, 0xff, 0x40, 0x80, - 0x7c, 0xe4, 0xe3, 0xed, 0xc9, 0xe6, 0x7b, 0xf4, 0xff, 0xb0, 0xe0, 0xe2, 0x96, 0x4e, 0x1e, 0x7e, - 0x5a, 0x00, 0x48, 0x51, 0xc0, 0x69, 0x3e, 0xbd, 0xc7, 0x2e, 0x06, 0x03, 0x60, 0xb0, 0x73, 0xb4, - 0xa9, 0x2f, 0x8c, 0x36, 0xf5, 0x2f, 0x83, 0x6c, 0xe2, 0x16, 0x79, 0x4f, 0x62, 0x97, 0x0b, 0x14, - 0x4e, 0x0c, 0xb5, 0xfc, 0x53, 0xc3, 0x2d, 0xff, 0x1f, 0x0b, 0x90, 0xdb, 0xb0, 0x0d, 0xa5, 0x47, - 0x2c, 0x71, 0x67, 0xe8, 0x2d, 0x73, 0x21, 0x22, 0x22, 0x87, 0x44, 0x9e, 0x33, 0xab, 0xc0, 0xae, - 0x01, 0xef, 0x28, 0x38, 0x32, 0xf6, 0xb7, 0x0f, 0xaa, 0x4e, 0x75, 0x80, 0x41, 0x57, 0x21, 0xfc, - 0x1f, 0x0a, 0x67, 0x44, 0x56, 0x81, 0x4f, 0x6d, 0x9b, 0xb7, 0x5f, 0x8a, 0x20, 0x87, 0x8f, 0x26, - 0xb4, 0x08, 0x0b, 0x8f, 0xab, 0x3b, 0x07, 0x8a, 0xd6, 0x7c, 0xba, 0xaf, 0x68, 0xf5, 0x83, 0x9d, - 0x9d, 0xc2, 0x1c, 0x3a, 0x0f, 0x28, 0x32, 0xb9, 0xbe, 0xb7, 0xb7, 0xa3, 0x54, 0xeb, 0x05, 0x21, - 0x36, 0xbf, 0x5d, 0x6f, 0x2a, 0x9b, 0x8a, 0x5a, 0x10, 0x63, 0x9b, 0xec, 0xec, 0xd5, 0x37, 0x0b, - 0x29, 0x74, 0x0e, 0xce, 0x44, 0x26, 0x37, 0xf6, 0x0e, 0xd6, 0x77, 0x94, 0x82, 0x14, 0x9b, 0x6e, - 0x34, 0xd5, 0xed, 0xfa, 0x66, 0x21, 0x8d, 0xce, 0x42, 0x21, 0x7a, 0xe4, 0xd3, 0xa6, 0xd2, 0x28, - 0x64, 0x62, 0x1b, 0x6f, 0x54, 0x9b, 0x4a, 0x21, 0x8b, 0x4a, 0x70, 0x3e, 0x32, 0x49, 0x4a, 0x78, - 0x6d, 0x6f, 0xfd, 0x91, 0x52, 0x6b, 0x16, 0x72, 0xe8, 0x22, 0x9c, 0x8b, 0xaf, 0x55, 0x55, 0xb5, - 0xfa, 0xb4, 0x20, 0xc7, 0xf6, 0x6a, 0x2a, 0xdf, 0x6e, 0x16, 0x20, 0xb6, 0x57, 0x20, 0x91, 0x56, - 0xab, 0x37, 0x0b, 0x79, 0x74, 0x01, 0x16, 0x63, 0x52, 0xd1, 0x85, 0xf9, 0xf8, 0x4e, 0xaa, 0xa2, - 0x14, 0x4e, 0xdd, 0xfe, 0x99, 0x00, 0xf3, 0x51, 0xfb, 0xa1, 0x1b, 0xb0, 0xbc, 0xb1, 0x57, 0xd3, - 0x94, 0xc7, 0x4a, 0xbd, 0xc9, 0x75, 0x50, 0x3b, 0xd8, 0x55, 0xea, 0xcd, 0x86, 0x56, 0xdb, 0xaa, - 0xd6, 0x37, 0x95, 0x8d, 0xc2, 0xdc, 0x44, 0xd4, 0x93, 0x6a, 0xb3, 0xb6, 0xa5, 0x6c, 0x14, 0x04, - 0x74, 0x13, 0xca, 0x63, 0x51, 0x07, 0x75, 0x8e, 0x13, 0xd1, 0x75, 0xb8, 0x1a, 0xc3, 0xed, 0xab, - 0x4a, 0x43, 0xa9, 0xd7, 0x94, 0xf0, 0xc8, 0xd4, 0xfa, 0x9d, 0x5f, 0xbf, 0x5a, 0x12, 0x7e, 0xff, - 0x6a, 0x49, 0xf8, 0xd3, 0xab, 0x25, 0xe1, 0xa7, 0x7f, 0x59, 0x9a, 0x83, 0x33, 0x26, 0xee, 0x71, - 0xa7, 0xd2, 0x1d, 0xab, 0xd2, 0xbb, 0xb7, 0x2f, 0x7c, 0x24, 0x55, 0x1e, 0xf6, 0xee, 0x1d, 0x66, - 0x68, 0x5e, 0xfb, 0xd2, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x1f, 0x28, 0x31, 0x40, 0x26, - 0x00, 0x00, + // 2584 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xdf, 0x6f, 0x1b, 0x49, + 0x1d, 0xcf, 0xae, 0x7f, 0xee, 0x37, 0x69, 0xe2, 0x4e, 0xfa, 0xc3, 0x75, 0xaf, 0xb9, 0x9c, 0xaf, + 0xdc, 0xe5, 0x5a, 0x70, 0xda, 0xd0, 0x83, 0xe3, 0xca, 0x01, 0x8e, 0xbd, 0x4d, 0xd2, 0x4b, 0x1c, + 0x6b, 0xed, 0xb4, 0xf4, 0x04, 0x5a, 0x6d, 0x76, 0xa7, 0xcd, 0x5e, 0x6c, 0xef, 0xde, 0xee, 0xda, + 0x57, 0xbf, 0x21, 0x04, 0x12, 0x3c, 0xf2, 0xc6, 0x1f, 0x80, 0x84, 0xf8, 0x13, 0xee, 0x11, 0x1e, + 0x10, 0x3c, 0x20, 0x90, 0xa8, 0xc4, 0x2b, 0x94, 0x07, 0xc4, 0x23, 0x42, 0xf0, 0x86, 0x84, 0xe6, + 0xc7, 0xae, 0xd7, 0xeb, 0x5d, 0xd7, 0x09, 0xd1, 0xa9, 0x15, 0x6f, 0x3b, 0x33, 0x9f, 0xef, 0xcc, + 0xf7, 0xf7, 0x7c, 0x67, 0x67, 0xe0, 0xca, 0xd0, 0x72, 0x8e, 0x4d, 0xbc, 0x3e, 0xb8, 0xbd, 0xee, + 0x60, 0xd7, 0xea, 0x3b, 0x3a, 0x76, 0x2b, 0xb6, 0x63, 0x79, 0x16, 0x92, 0xd8, 0x50, 0x65, 0x70, + 0xbb, 0xf4, 0xfa, 0x13, 0xcb, 0x7a, 0xd2, 0xc1, 0xeb, 0x74, 0xe0, 0xb0, 0xff, 0x78, 0xdd, 0x33, + 0xbb, 0xd8, 0xf5, 0xb4, 0xae, 0xcd, 0xb0, 0xa5, 0x95, 0x28, 0xe0, 0x53, 0x47, 0xb3, 0x6d, 0xec, + 0xf0, 0xb9, 0xca, 0xcf, 0x04, 0xc8, 0xb7, 0x7a, 0x9a, 0xed, 0x1e, 0x59, 0x1e, 0xba, 0x01, 0x69, + 0xc7, 0xb2, 0xbc, 0xa2, 0xb0, 0x2a, 0xac, 0xcd, 0x6f, 0x5c, 0xaa, 0x04, 0xeb, 0x54, 0xee, 0xb7, + 0xf6, 0x1b, 0x72, 0x07, 0x77, 0x71, 0xcf, 0x53, 0x28, 0x06, 0x6d, 0xc1, 0x82, 0xed, 0x60, 0x17, + 0xf7, 0x74, 0xac, 0x76, 0x35, 0xbb, 0x28, 0xae, 0xa6, 0xd6, 0xe6, 0x37, 0xae, 0x87, 0x68, 0xfc, + 0x69, 0x2b, 0x4d, 0x8e, 0xdb, 0xd3, 0x6c, 0xb9, 0xe7, 0x39, 0x43, 0x65, 0xde, 0x1e, 0xf5, 0x94, + 0x5a, 0x50, 0x88, 0x02, 0x50, 0x01, 0x52, 0xc7, 0x78, 0x48, 0xf9, 0x90, 0x14, 0xf2, 0x89, 0xde, + 0x81, 0xcc, 0x40, 0xeb, 0xf4, 0x71, 0x51, 0xa4, 0xbc, 0x2d, 0x87, 0xd6, 0xf1, 0xa9, 0x15, 0x86, + 0x78, 0x5f, 0x7c, 0x4f, 0x28, 0xff, 0x44, 0x04, 0xa8, 0x1d, 0x69, 0xbd, 0x27, 0xb8, 0xa9, 0xe9, + 0xc7, 0xe8, 0x0d, 0x58, 0x30, 0x2c, 0xbd, 0x4f, 0xd8, 0x57, 0x47, 0x13, 0xcf, 0xfb, 0x7d, 0x1f, + 0xe2, 0x21, 0x7a, 0x17, 0x40, 0x3f, 0xc2, 0xfa, 0xb1, 0x6d, 0x99, 0x3d, 0x8f, 0xaf, 0x72, 0x31, + 0xb4, 0x4a, 0x2d, 0x18, 0x54, 0x42, 0x40, 0x54, 0x82, 0xbc, 0xcb, 0xe5, 0x2c, 0xa6, 0x56, 0x85, + 0xb5, 0x05, 0x25, 0x68, 0xa3, 0x9b, 0x90, 0xd3, 0x29, 0x0f, 0x6e, 0x31, 0x4d, 0xb5, 0x73, 0x7e, + 0x6c, 0x3e, 0x32, 0xa2, 0xf8, 0x08, 0x54, 0x85, 0xf3, 0x5d, 0xb3, 0xa7, 0xba, 0xc3, 0x9e, 0x8e, + 0x0d, 0xd5, 0x33, 0xf5, 0x63, 0xec, 0x15, 0x33, 0x13, 0x6c, 0xb4, 0xcd, 0x2e, 0x6e, 0xd3, 0x41, + 0x65, 0xa9, 0x6b, 0xf6, 0x5a, 0x14, 0xce, 0x3a, 0xd0, 0x35, 0x00, 0xd3, 0x55, 0x1d, 0xdc, 0xb5, + 0x06, 0xd8, 0x28, 0x66, 0x57, 0x85, 0xb5, 0xbc, 0x22, 0x99, 0xae, 0xc2, 0x3a, 0xca, 0xbf, 0x14, + 0x20, 0xcb, 0x56, 0x45, 0x6f, 0x82, 0x68, 0x1a, 0xdc, 0xcc, 0xcb, 0x13, 0x4c, 0xed, 0xd4, 0x15, + 0xd1, 0x34, 0x50, 0x11, 0x72, 0x5d, 0xec, 0xba, 0xda, 0x13, 0xa6, 0x74, 0x49, 0xf1, 0x9b, 0xe8, + 0x0e, 0x80, 0x65, 0x63, 0x47, 0xf3, 0x4c, 0xab, 0xe7, 0x16, 0x53, 0x54, 0xb6, 0x0b, 0xa1, 0x69, + 0xf6, 0xfd, 0x41, 0x25, 0x84, 0x43, 0x9b, 0xb0, 0x14, 0x78, 0x0c, 0x93, 0xba, 0x98, 0xa6, 0x1c, + 0x5c, 0x89, 0x31, 0x26, 0x57, 0xcf, 0xa2, 0x3d, 0xd6, 0x2e, 0xff, 0x50, 0x80, 0xbc, 0xcf, 0x24, + 0x91, 0x57, 0xef, 0x98, 0xc4, 0xa6, 0x2e, 0xfe, 0x84, 0x4a, 0x73, 0x4e, 0x91, 0x58, 0x4f, 0x0b, + 0x7f, 0x82, 0xde, 0x00, 0x70, 0xb1, 0x33, 0xc0, 0x0e, 0x1d, 0x26, 0x22, 0xa4, 0x36, 0xc5, 0x5b, + 0x82, 0x22, 0xb1, 0x5e, 0x02, 0x79, 0x0d, 0x72, 0x1d, 0xad, 0x6b, 0x5b, 0x0e, 0x33, 0x1e, 0x1b, + 0xf7, 0xbb, 0xd0, 0x15, 0xc8, 0x6b, 0xba, 0x67, 0x39, 0xaa, 0x69, 0x50, 0x4e, 0x17, 0x94, 0x1c, + 0x6d, 0xef, 0x18, 0xe5, 0xdf, 0x96, 0x40, 0x0a, 0xa4, 0x44, 0x5f, 0x84, 0x94, 0x8b, 0xfd, 0xb0, + 0x29, 0xc6, 0x29, 0xa2, 0xd2, 0xc2, 0xde, 0xf6, 0x9c, 0x42, 0x60, 0x04, 0xad, 0x19, 0x06, 0x77, + 0xb1, 0x78, 0x74, 0xd5, 0x30, 0x08, 0x5a, 0x33, 0x0c, 0xb4, 0x0e, 0x69, 0x62, 0x3e, 0xca, 0xdf, + 0xb8, 0xaa, 0x46, 0xf0, 0x3d, 0x6b, 0x80, 0xb7, 0xe7, 0x14, 0x0a, 0x44, 0xef, 0x42, 0x96, 0xb9, + 0x00, 0xd7, 0xee, 0xd5, 0x58, 0x12, 0xe6, 0x14, 0xdb, 0x73, 0x0a, 0x07, 0x93, 0x75, 0xb0, 0x61, + 0xfa, 0x2e, 0x17, 0xbf, 0x8e, 0x6c, 0x98, 0x44, 0x0a, 0x0a, 0x24, 0xeb, 0xb8, 0xb8, 0x83, 0x75, + 0x8f, 0x7a, 0x5a, 0xd2, 0x3a, 0x2d, 0x0a, 0x21, 0xeb, 0x30, 0x30, 0xda, 0x80, 0x8c, 0xeb, 0x0d, + 0x3b, 0xb8, 0x98, 0xa3, 0x54, 0xa5, 0x78, 0x2a, 0x82, 0xd8, 0x9e, 0x53, 0x18, 0x14, 0xdd, 0x85, + 0xbc, 0xd9, 0xd3, 0x1d, 0xac, 0xb9, 0xb8, 0x98, 0xa7, 0x64, 0xd7, 0x62, 0xc9, 0x76, 0x38, 0x68, + 0x7b, 0x4e, 0x09, 0x08, 0xd0, 0xd7, 0x41, 0xf2, 0x1c, 0x8c, 0x55, 0x2a, 0x9d, 0x34, 0x85, 0xba, + 0xed, 0x60, 0xcc, 0x25, 0xcc, 0x7b, 0xfc, 0x1b, 0x7d, 0x13, 0x80, 0x52, 0x33, 0x9e, 0x81, 0x92, + 0xaf, 0x24, 0x92, 0xfb, 0x7c, 0xd3, 0x15, 0x69, 0xa3, 0xf4, 0x6b, 0x01, 0x52, 0x2d, 0xec, 0x91, + 0xf8, 0xb6, 0x35, 0x87, 0x38, 0x2b, 0xe1, 0xcb, 0xc3, 0x86, 0xaa, 0xf9, 0x1e, 0x93, 0x14, 0xdf, + 0x0c, 0x5f, 0x63, 0xf0, 0xaa, 0xe7, 0x67, 0x45, 0x71, 0x94, 0x15, 0x37, 0xfc, 0xac, 0xc8, 0xbc, + 0xe3, 0xb5, 0xf8, 0x8c, 0xdd, 0x32, 0xbb, 0x76, 0xc7, 0x4f, 0x8f, 0xe8, 0x2b, 0x30, 0x8f, 0x9f, + 0x62, 0xbd, 0xcf, 0x59, 0x48, 0x4f, 0x63, 0x01, 0x7c, 0x64, 0xd5, 0x2b, 0xfd, 0x53, 0x80, 0x54, + 0xd5, 0x30, 0xce, 0x42, 0x90, 0x0f, 0x68, 0x26, 0x18, 0x84, 0x27, 0x10, 0xa7, 0x4d, 0x70, 0x8e, + 0xa0, 0x47, 0xe4, 0x9f, 0xa7, 0xd4, 0xff, 0x16, 0x20, 0x4d, 0xc2, 0xeb, 0x25, 0x10, 0xfb, 0x0e, + 0x40, 0x88, 0x32, 0x35, 0x8d, 0x52, 0xd2, 0x03, 0xaa, 0xd3, 0x0a, 0xfe, 0x99, 0x00, 0x59, 0x96, + 0x24, 0xce, 0x42, 0xf4, 0x71, 0xde, 0xc5, 0xd3, 0xf1, 0x9e, 0x9a, 0x95, 0xf7, 0x5f, 0xa5, 0x21, + 0x4d, 0xa3, 0xf7, 0x0c, 0x38, 0xbf, 0x01, 0xe9, 0xc7, 0x8e, 0xd5, 0xe5, 0x3c, 0x87, 0x6b, 0xa2, + 0x36, 0x7e, 0xea, 0x35, 0x2c, 0x03, 0x37, 0x2d, 0x57, 0xa1, 0x18, 0xf4, 0x16, 0x88, 0x9e, 0xc5, + 0xd9, 0x4c, 0x42, 0x8a, 0x9e, 0x85, 0x8e, 0xe0, 0xf2, 0x88, 0x1f, 0x52, 0x3d, 0xa9, 0x87, 0x43, + 0x95, 0x6e, 0x2d, 0xbc, 0x50, 0xd8, 0x48, 0x4c, 0xbf, 0x95, 0x80, 0xb3, 0x3d, 0xcd, 0xde, 0x1c, + 0x56, 0x09, 0x11, 0x2b, 0xaa, 0x96, 0xf5, 0xc9, 0x11, 0xb2, 0x87, 0xeb, 0x56, 0xcf, 0xc3, 0x3d, + 0x96, 0xd8, 0x25, 0xc5, 0x6f, 0x46, 0x75, 0x9b, 0x9d, 0x51, 0xb7, 0x68, 0x07, 0x40, 0xf3, 0x3c, + 0xc7, 0x3c, 0xec, 0x7b, 0xd8, 0x2d, 0xe6, 0x28, 0xbb, 0xef, 0x24, 0xb3, 0x5b, 0x0d, 0xb0, 0x8c, + 0xcb, 0x10, 0x71, 0xe9, 0xbb, 0x50, 0x4c, 0x92, 0x26, 0xa6, 0x02, 0xbc, 0x39, 0x5e, 0x01, 0x26, + 0xb0, 0x3a, 0xaa, 0x01, 0x4b, 0x1f, 0xc0, 0x52, 0x64, 0xf5, 0x98, 0x59, 0x2f, 0x84, 0x67, 0x95, + 0xc2, 0xe4, 0x7f, 0x12, 0x20, 0xcb, 0x76, 0xaf, 0x97, 0xd5, 0x8d, 0x4e, 0x1b, 0xda, 0x7f, 0x11, + 0x21, 0x43, 0x37, 0xa7, 0x97, 0x55, 0xb0, 0xfb, 0x63, 0x3e, 0xc6, 0x42, 0xe2, 0x46, 0x72, 0xa1, + 0x30, 0xcd, 0xc9, 0xa2, 0x4a, 0xca, 0xcc, 0xaa, 0xa4, 0xff, 0xd1, 0x7b, 0x3e, 0x13, 0x20, 0xef, + 0x97, 0x23, 0x67, 0xa1, 0xe6, 0x8d, 0x71, 0xef, 0x3f, 0xcd, 0x9e, 0x37, 0x73, 0xfa, 0xfc, 0x9e, + 0x08, 0x79, 0xbf, 0x18, 0x3a, 0x0b, 0xde, 0xdf, 0x1a, 0x73, 0x11, 0x14, 0xa6, 0x72, 0x70, 0xc8, + 0x3d, 0xca, 0x21, 0xf7, 0x88, 0x43, 0x11, 0xd7, 0xf8, 0xd2, 0x28, 0xa1, 0x31, 0xbf, 0x58, 0x8e, + 0x00, 0x89, 0x1f, 0x25, 0x66, 0xb9, 0xcc, 0x09, 0x42, 0x44, 0x0a, 0x0a, 0xba, 0x97, 0x4d, 0x07, + 0x8d, 0x98, 0xf0, 0xa8, 0x4c, 0xaf, 0x49, 0x5f, 0xc2, 0x10, 0xd9, 0xcc, 0x42, 0xfa, 0xd0, 0x32, + 0x86, 0xe5, 0x7f, 0x08, 0x70, 0x7e, 0xc2, 0x87, 0x23, 0x15, 0x83, 0x30, 0x63, 0xc5, 0x70, 0x0b, + 0xf2, 0xf4, 0xb0, 0xfb, 0xc2, 0x2a, 0x23, 0x47, 0x61, 0xac, 0x32, 0xe1, 0x27, 0xe6, 0x17, 0x57, + 0x55, 0x1c, 0x58, 0xf5, 0xd0, 0x1a, 0xa4, 0xbd, 0xa1, 0xcd, 0x8e, 0x58, 0x8b, 0x63, 0x67, 0xdf, + 0x07, 0x44, 0xbe, 0xf6, 0xd0, 0xc6, 0x0a, 0x45, 0x8c, 0xe4, 0xcf, 0xd0, 0x13, 0x24, 0x6b, 0x94, + 0x7f, 0x71, 0x0e, 0xe6, 0x43, 0x32, 0xa3, 0x3a, 0xcc, 0x7f, 0xec, 0x5a, 0x3d, 0xd5, 0x3a, 0xfc, + 0x98, 0x9c, 0xa8, 0x98, 0xb8, 0x6f, 0xc4, 0x07, 0x39, 0xfd, 0xde, 0xa7, 0xc0, 0xed, 0x39, 0x05, + 0x08, 0x1d, 0x6b, 0xa1, 0x2a, 0xd0, 0x96, 0xaa, 0x39, 0x8e, 0x36, 0xe4, 0xf2, 0xaf, 0x4e, 0x99, + 0xa4, 0x4a, 0x70, 0xe4, 0xb8, 0x42, 0xa8, 0x68, 0x03, 0x7d, 0x0b, 0x24, 0xdb, 0x31, 0xbb, 0xa6, + 0x67, 0x06, 0x67, 0xce, 0xa4, 0x19, 0x9a, 0x3e, 0x8e, 0xcc, 0x10, 0x10, 0xa1, 0xdb, 0x90, 0xf6, + 0xf0, 0x53, 0xdf, 0x8d, 0xae, 0x26, 0x10, 0x93, 0x94, 0x4f, 0x8e, 0x92, 0x04, 0x8a, 0xde, 0x27, + 0x41, 0xdd, 0xef, 0x79, 0xd8, 0xe1, 0x75, 0xc8, 0x4a, 0x02, 0x55, 0x8d, 0xa1, 0xb6, 0xe7, 0x14, + 0x9f, 0x80, 0x2e, 0xe7, 0x60, 0xff, 0x38, 0x99, 0xb8, 0x9c, 0x83, 0xe9, 0x09, 0x99, 0x40, 0x4b, + 0xcf, 0x04, 0x80, 0x91, 0x0e, 0xd1, 0x1a, 0x64, 0x7a, 0x96, 0x81, 0xdd, 0xa2, 0x40, 0x23, 0x29, + 0x1c, 0x75, 0xca, 0x76, 0x9b, 0xe6, 0x13, 0x06, 0x38, 0x65, 0x15, 0x1b, 0xf6, 0xc9, 0xd4, 0x29, + 0x7c, 0x32, 0x3d, 0x9b, 0x4f, 0x96, 0xfe, 0x28, 0x80, 0x14, 0x58, 0x75, 0xaa, 0x54, 0x5b, 0xd5, + 0x57, 0x47, 0xaa, 0xbf, 0x0b, 0x20, 0x05, 0x9e, 0x16, 0xc4, 0x9d, 0x30, 0x7b, 0xdc, 0x89, 0xa1, + 0xb8, 0x3b, 0xe5, 0x19, 0x2a, 0x2c, 0x6b, 0xfa, 0x14, 0xb2, 0x66, 0x66, 0x94, 0xf5, 0xf7, 0x02, + 0xa4, 0x49, 0x60, 0xa0, 0x77, 0xc6, 0x8d, 0xb7, 0x1c, 0x53, 0x2b, 0xbd, 0x1a, 0xd6, 0xfb, 0x9b, + 0x00, 0x39, 0x1e, 0xb4, 0xff, 0x0f, 0xb6, 0x73, 0x30, 0x9e, 0x6a, 0x3b, 0xbf, 0x3e, 0x79, 0x15, + 0x6c, 0x17, 0xec, 0xcf, 0x7b, 0x90, 0xe3, 0x79, 0x30, 0x66, 0x7b, 0xbf, 0x05, 0x39, 0xcc, 0x72, + 0x6c, 0xcc, 0x09, 0x20, 0x7c, 0x6b, 0xe0, 0xc3, 0xca, 0x3a, 0xe4, 0x78, 0x02, 0x22, 0x45, 0x51, + 0x8f, 0x6c, 0x15, 0xc2, 0x44, 0xb9, 0xe3, 0xa7, 0x28, 0x3a, 0x7e, 0x8a, 0x45, 0x1e, 0x40, 0x9e, + 0xd0, 0x93, 0xf2, 0x64, 0xe4, 0x4d, 0x42, 0xa8, 0x02, 0x21, 0x3a, 0xe9, 0xdb, 0xc6, 0x6c, 0xba, + 0xe7, 0xc0, 0xaa, 0x57, 0xfe, 0x1d, 0x29, 0x8d, 0x79, 0x04, 0xa2, 0x2f, 0x84, 0xfe, 0xa2, 0x5f, + 0x8c, 0x09, 0x51, 0xfe, 0x1f, 0x3d, 0xb6, 0x02, 0x3a, 0x65, 0xdd, 0xf1, 0x2e, 0xcc, 0x9b, 0x3d, + 0x57, 0xa5, 0xbf, 0x91, 0xf8, 0x5f, 0xe9, 0xc4, 0xb5, 0x25, 0xb3, 0xe7, 0x36, 0x1d, 0x3c, 0xd8, + 0x31, 0x50, 0x6d, 0xac, 0x62, 0xcc, 0x50, 0xc7, 0x7c, 0x33, 0x86, 0x6a, 0xea, 0x71, 0x5d, 0x99, + 0xa5, 0xdc, 0x9b, 0x72, 0x4f, 0xe3, 0x1b, 0x24, 0x7c, 0x4f, 0xf3, 0x11, 0xc0, 0x88, 0xe3, 0x53, + 0xd6, 0x7c, 0x97, 0x20, 0x6b, 0x3d, 0x7e, 0xec, 0x62, 0x66, 0xc5, 0x8c, 0xc2, 0x5b, 0xe5, 0x7f, + 0xf1, 0x63, 0x0c, 0xb5, 0xd5, 0x75, 0x48, 0xd9, 0x96, 0x1b, 0xe3, 0x69, 0x7e, 0x61, 0x4d, 0x86, + 0x11, 0xe2, 0x29, 0x8a, 0x59, 0x2a, 0x92, 0x8c, 0x52, 0xc9, 0xe6, 0x9b, 0x31, 0xa4, 0xd0, 0x1d, + 0x58, 0x08, 0xcc, 0x47, 0xd8, 0xc9, 0x24, 0xb2, 0x03, 0xdc, 0x78, 0x4d, 0xcb, 0x25, 0x1c, 0x18, + 0xd8, 0xf6, 0x8e, 0x68, 0x71, 0x94, 0x51, 0x58, 0x23, 0x62, 0xd3, 0xdc, 0xa4, 0x4d, 0xb9, 0xe8, + 0x9f, 0xbb, 0x4d, 0x1f, 0x42, 0x8e, 0x4b, 0x71, 0xc6, 0x06, 0xed, 0x42, 0xfa, 0xc0, 0xc5, 0x0e, + 0x5a, 0x0c, 0xe2, 0x4e, 0xa2, 0x01, 0x56, 0x82, 0x7c, 0xdf, 0xc5, 0x4e, 0x4f, 0xeb, 0xfa, 0x96, + 0x0b, 0xda, 0xe8, 0x6b, 0x31, 0x9b, 0x46, 0xa9, 0xc2, 0x2e, 0x45, 0x2b, 0xfe, 0xa5, 0x28, 0xe5, + 0x83, 0xde, 0x9a, 0x86, 0xd8, 0x28, 0xff, 0x47, 0x84, 0x5c, 0xd3, 0xb1, 0x68, 0x8d, 0x18, 0x5d, + 0x12, 0x41, 0x3a, 0xb4, 0x1c, 0xfd, 0x46, 0xd7, 0x00, 0xec, 0xfe, 0x61, 0xc7, 0xd4, 0xe9, 0x15, + 0x23, 0xf3, 0x16, 0x89, 0xf5, 0x7c, 0x88, 0x87, 0x64, 0xd8, 0xc5, 0xba, 0x83, 0xd9, 0x0d, 0x64, + 0x9a, 0x0d, 0xb3, 0x1e, 0x32, 0xbc, 0x06, 0x05, 0xad, 0xef, 0x1d, 0xa9, 0x9f, 0xe2, 0xc3, 0x23, + 0xcb, 0x3a, 0x56, 0xfb, 0x4e, 0x87, 0xff, 0xb2, 0x5b, 0x24, 0xfd, 0x0f, 0x59, 0xf7, 0x81, 0xd3, + 0x41, 0xb7, 0xe0, 0xc2, 0x18, 0xb2, 0x8b, 0xbd, 0x23, 0xcb, 0x70, 0x8b, 0xd9, 0xd5, 0xd4, 0x9a, + 0xa4, 0xa0, 0x10, 0x7a, 0x8f, 0x8d, 0xa0, 0x6f, 0xc0, 0x55, 0x7e, 0x51, 0x66, 0x60, 0x4d, 0xf7, + 0xcc, 0x81, 0xe6, 0x61, 0xd5, 0x3b, 0x72, 0xb0, 0x7b, 0x64, 0x75, 0x0c, 0x5a, 0x3a, 0x4b, 0xca, + 0x15, 0x06, 0xa9, 0x07, 0x88, 0xb6, 0x0f, 0x88, 0x28, 0x31, 0x7f, 0x02, 0x25, 0x12, 0xd2, 0x50, + 0x9a, 0x95, 0x5e, 0x4c, 0x3a, 0xca, 0xb5, 0x3f, 0x4a, 0xc1, 0xa5, 0x03, 0xd2, 0xd2, 0x0e, 0x3b, + 0x98, 0x1b, 0xe2, 0x9e, 0x89, 0x3b, 0x86, 0x8b, 0x6e, 0x71, 0xf5, 0x0b, 0xfc, 0x67, 0x48, 0x74, + 0xbe, 0x96, 0xe7, 0x98, 0xbd, 0x27, 0xb4, 0xac, 0xe0, 0xc6, 0xb9, 0x17, 0xa3, 0x5e, 0x71, 0x06, + 0xea, 0xa8, 0xf2, 0x1f, 0x27, 0x28, 0x9f, 0x79, 0xd6, 0x9d, 0x90, 0x6f, 0xc7, 0xb3, 0x5e, 0xa9, + 0x4e, 0x98, 0x27, 0xd6, 0x64, 0xdf, 0x99, 0x6e, 0xb2, 0xf4, 0x0c, 0xac, 0x27, 0x1b, 0xb4, 0x54, + 0x01, 0x34, 0xc9, 0x07, 0xbb, 0xf0, 0x65, 0xe2, 0x08, 0xd4, 0x97, 0xfc, 0x66, 0xf9, 0xfb, 0x22, + 0x2c, 0xd5, 0xf9, 0x65, 0x79, 0xab, 0xdf, 0xed, 0x6a, 0xce, 0x70, 0x22, 0x24, 0x26, 0x6f, 0xa7, + 0xa2, 0x77, 0xe3, 0x52, 0xe8, 0x6e, 0x7c, 0xdc, 0xa5, 0xd2, 0x27, 0x71, 0xa9, 0xbb, 0x30, 0xaf, + 0xe9, 0x3a, 0x76, 0xdd, 0x70, 0x81, 0x36, 0x8d, 0x16, 0x7c, 0xf8, 0x84, 0x3f, 0x66, 0x4f, 0xe2, + 0x8f, 0x3f, 0x13, 0x61, 0x71, 0xfc, 0x7a, 0x1a, 0xbd, 0x37, 0x56, 0xd2, 0x5e, 0x4f, 0xbc, 0xc7, + 0xe6, 0x17, 0xeb, 0xa1, 0x12, 0xb7, 0x06, 0x79, 0xff, 0x6a, 0x9b, 0x3f, 0x9d, 0x78, 0x3b, 0x99, + 0xda, 0x6f, 0xb2, 0xfc, 0x1d, 0x10, 0x96, 0xee, 0xc2, 0xb9, 0xb1, 0xa1, 0x93, 0xfc, 0x7e, 0x29, + 0x3f, 0xf0, 0x5f, 0x48, 0x10, 0xae, 0xd0, 0x55, 0xb8, 0x5c, 0xdb, 0xae, 0x36, 0xb6, 0x64, 0xb5, + 0xfd, 0xa8, 0x29, 0xab, 0x07, 0x8d, 0x56, 0x53, 0xae, 0xed, 0xdc, 0xdb, 0x91, 0xeb, 0x85, 0x39, + 0xb4, 0x0c, 0x4b, 0xe1, 0xc1, 0xe6, 0x41, 0xbb, 0x20, 0xa0, 0x4b, 0x80, 0xc2, 0x9d, 0x75, 0x79, + 0x57, 0x6e, 0xcb, 0x05, 0xb1, 0x3c, 0x80, 0xbc, 0xcf, 0x14, 0x39, 0x9c, 0x93, 0x20, 0xe0, 0xa5, + 0xf0, 0xb5, 0x18, 0x09, 0x2b, 0x75, 0xcd, 0xd3, 0x98, 0x5c, 0x14, 0x5a, 0xfa, 0x2a, 0x48, 0x41, + 0xd7, 0x89, 0xe4, 0xd9, 0x81, 0x6c, 0x8d, 0x3a, 0x7c, 0xc8, 0x33, 0x17, 0xa8, 0x67, 0xae, 0x8f, + 0xe9, 0x3a, 0xf1, 0xf9, 0x48, 0x00, 0x2a, 0x37, 0x88, 0x6a, 0x82, 0x27, 0x1e, 0xe3, 0xef, 0x08, + 0x84, 0xb8, 0x77, 0x04, 0xe3, 0x2f, 0x11, 0xc4, 0xc8, 0x4b, 0x84, 0xf2, 0x0f, 0x04, 0x98, 0x0f, + 0xfd, 0xe3, 0x3e, 0xdb, 0x6d, 0x11, 0xbd, 0x0d, 0x4b, 0x0e, 0xee, 0x68, 0xe4, 0x7c, 0xac, 0x72, + 0x40, 0x8a, 0x02, 0x16, 0xfd, 0xee, 0x7d, 0xb6, 0x7f, 0xea, 0x00, 0xa3, 0x99, 0xc3, 0x6f, 0x1f, + 0x84, 0xc9, 0xb7, 0x0f, 0xaf, 0x81, 0x64, 0xe0, 0x0e, 0x39, 0x76, 0x63, 0xc7, 0x17, 0x28, 0xe8, + 0x18, 0x7b, 0x19, 0x91, 0x1a, 0x7f, 0x19, 0xf1, 0x63, 0x01, 0xf2, 0x75, 0x4b, 0x97, 0x07, 0xc4, + 0x12, 0x37, 0xc7, 0xe2, 0xe3, 0x72, 0x48, 0x44, 0x1f, 0x12, 0x0a, 0x89, 0x75, 0x60, 0xbb, 0xa5, + 0x7b, 0xc4, 0x97, 0x8c, 0x3c, 0x98, 0xa1, 0xea, 0x54, 0x46, 0x18, 0xf4, 0x3a, 0x04, 0x2f, 0x78, + 0x7c, 0x46, 0x24, 0x05, 0xfc, 0xae, 0x1d, 0xe3, 0xc6, 0x33, 0x11, 0xa4, 0xe0, 0x6c, 0x49, 0xbc, + 0xf8, 0x41, 0x75, 0xf7, 0x80, 0xfb, 0x6b, 0xe3, 0x60, 0x77, 0xb7, 0x30, 0x47, 0xbc, 0x38, 0xd4, + 0xb9, 0xb9, 0xbf, 0xbf, 0x2b, 0x57, 0x1b, 0xcc, 0xbb, 0x43, 0xfd, 0x3b, 0x8d, 0xb6, 0xbc, 0x25, + 0x2b, 0x05, 0x31, 0x32, 0xc9, 0xee, 0x7e, 0x63, 0xab, 0x90, 0x42, 0x17, 0xe1, 0x7c, 0xa8, 0xb3, + 0xbe, 0x7f, 0xb0, 0xb9, 0x2b, 0x17, 0xd2, 0x91, 0xee, 0x56, 0x5b, 0xd9, 0x69, 0x6c, 0x15, 0x32, + 0xe8, 0x02, 0x14, 0xc2, 0x4b, 0x3e, 0x6a, 0xcb, 0xad, 0x42, 0x36, 0x32, 0x71, 0xbd, 0xda, 0x96, + 0x0b, 0x39, 0x54, 0x82, 0x4b, 0xa1, 0x4e, 0x72, 0xd2, 0x51, 0xf7, 0x37, 0xef, 0xcb, 0xb5, 0x76, + 0x21, 0x8f, 0xae, 0xc0, 0xc5, 0xe8, 0x58, 0x55, 0x51, 0xaa, 0x8f, 0x0a, 0x52, 0x64, 0xae, 0xb6, + 0xfc, 0xed, 0x76, 0x01, 0x22, 0x73, 0x71, 0x89, 0xd4, 0x5a, 0xa3, 0x5d, 0x98, 0x47, 0x97, 0x61, + 0x39, 0x22, 0x15, 0x1d, 0x58, 0x88, 0xce, 0xa4, 0xc8, 0x72, 0xe1, 0xdc, 0x8d, 0x9f, 0x0b, 0xb0, + 0x10, 0xb6, 0x1f, 0xba, 0x0e, 0xab, 0xf5, 0xfd, 0x9a, 0x2a, 0x3f, 0x90, 0x1b, 0x6d, 0x5f, 0x07, + 0xb5, 0x83, 0x3d, 0xb9, 0xd1, 0x6e, 0xa9, 0x2c, 0x47, 0x90, 0x2c, 0x32, 0x0d, 0xf5, 0xb0, 0xda, + 0xae, 0x6d, 0xcb, 0xf5, 0x82, 0x80, 0xde, 0x82, 0x72, 0x22, 0xea, 0xa0, 0xe1, 0xe3, 0x44, 0xf4, + 0x26, 0xbc, 0x1e, 0xc1, 0x35, 0x15, 0xb9, 0x25, 0x37, 0x6a, 0x72, 0xb0, 0x64, 0x6a, 0xf3, 0xe6, + 0x6f, 0x9e, 0xaf, 0x08, 0x7f, 0x78, 0xbe, 0x22, 0xfc, 0xf9, 0xf9, 0x8a, 0xf0, 0xd3, 0xbf, 0xae, + 0xcc, 0xc1, 0x79, 0x03, 0x0f, 0x7c, 0xa7, 0xd2, 0x6c, 0xb3, 0x32, 0xb8, 0xdd, 0x14, 0x3e, 0x4a, + 0x57, 0xee, 0x0e, 0x6e, 0x1f, 0x66, 0x69, 0xfa, 0xff, 0xf2, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, + 0xa6, 0xd2, 0x34, 0x16, 0x7a, 0x27, 0x00, 0x00, } func (m *Snapshot) Marshal() (dAtA []byte, err error) { @@ -3573,9 +3663,9 @@ func (m *Change) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if m.Presence != nil { + if m.PresenceChange != nil { { - size, err := m.Presence.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.PresenceChange.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -6221,6 +6311,57 @@ func (m *DocumentSummary) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *PresenceChange) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PresenceChange) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PresenceChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Presence) > 0 { + for k := range m.Presence { + v := m.Presence[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintResources(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintResources(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintResources(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x12 + } + } + if m.Type != 0 { + i = encodeVarintResources(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *Presence) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -6591,8 +6732,8 @@ func (m *Change) Size() (n int) { n += 1 + l + sovResources(uint64(l)) } } - if m.Presence != nil { - l = m.Presence.Size() + if m.PresenceChange != nil { + l = m.PresenceChange.Size() n += 1 + l + sovResources(uint64(l)) } if m.XXX_unrecognized != nil { @@ -7723,6 +7864,29 @@ func (m *DocumentSummary) Size() (n int) { return n } +func (m *PresenceChange) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Type != 0 { + n += 1 + sovResources(uint64(m.Type)) + } + if len(m.Presence) > 0 { + for k, v := range m.Presence { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovResources(uint64(len(k))) + 1 + len(v) + sovResources(uint64(len(v))) + n += mapEntrySize + 1 + sovResources(uint64(mapEntrySize)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *Presence) Size() (n int) { if m == nil { return 0 @@ -8446,7 +8610,7 @@ func (m *Change) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Presence", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field PresenceChange", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -8473,10 +8637,10 @@ func (m *Change) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Presence == nil { - m.Presence = &Presence{} + if m.PresenceChange == nil { + m.PresenceChange = &PresenceChange{} } - if err := m.Presence.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.PresenceChange.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -15459,6 +15623,203 @@ func (m *DocumentSummary) Unmarshal(dAtA []byte) error { } return nil } +func (m *PresenceChange) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PresenceChange: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PresenceChange: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Type |= PresenceChange_ChangeType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Presence", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Presence == nil { + m.Presence = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthResources + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthResources + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthResources + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthResources + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipResources(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Presence[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipResources(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *Presence) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/api/yorkie/v1/resources.proto b/api/yorkie/v1/resources.proto index e78331389..5532ce518 100644 --- a/api/yorkie/v1/resources.proto +++ b/api/yorkie/v1/resources.proto @@ -52,7 +52,7 @@ message Change { ChangeID id = 1; string message = 2; repeated Operation operations = 3; - Presence presence = 4; + PresenceChange presence_change = 4; } message ChangeID { @@ -289,6 +289,16 @@ message DocumentSummary { google.protobuf.Timestamp updated_at = 6; } +message PresenceChange { + enum ChangeType { + CHANGE_TYPE_UNSPECIFIED = 0; + CHANGE_TYPE_PUT = 1; + CHANGE_TYPE_DELETE = 2; + } + ChangeType type = 1; + map presence = 2; +} + message Presence { map data = 1; } diff --git a/pkg/document/change/change.go b/pkg/document/change/change.go index 378d5066c..acdf947ef 100644 --- a/pkg/document/change/change.go +++ b/pkg/document/change/change.go @@ -36,18 +36,18 @@ type Change struct { // operations represent a series of user edits. operations []operations.Operation - // presence represents the presence of the user who made the change. - // TODO(hackerwins): Consider using changes instead of entire presence. - presence *innerpresence.Presence + // presenceChange represents the presenceChange of the user who made the change. + // TODO(hackerwins): Consider using changes instead of entire presenceChange. + presenceChange *innerpresence.PresenceChange } // New creates a new instance of Change. -func New(id ID, message string, operations []operations.Operation, p *innerpresence.Presence) *Change { +func New(id ID, message string, operations []operations.Operation, p *innerpresence.PresenceChange) *Change { return &Change{ - id: id, - message: message, - operations: operations, - presence: p, + id: id, + message: message, + operations: operations, + presenceChange: p, } } @@ -59,8 +59,10 @@ func (c *Change) Execute(root *crdt.Root, presenceMap *innerpresence.Map) error } } - if c.presence != nil { - presenceMap.Store(c.id.actorID.String(), c.presence) + if c.presenceChange != nil { + // TODO(hackerwins): For now, we only support PUT operation. We need to + // support other operations such as DELETE, PATCH. + presenceMap.Store(c.id.actorID.String(), &c.presenceChange.Presence) } return nil @@ -104,7 +106,7 @@ func (c *Change) SetActor(actor *time.ActorID) { } } -// Presence returns the presence of this change. -func (c *Change) Presence() *innerpresence.Presence { - return c.presence +// PresenceChange returns the presence change of this change. +func (c *Change) PresenceChange() *innerpresence.PresenceChange { + return c.presenceChange } diff --git a/pkg/document/change/context.go b/pkg/document/change/context.go index 0f4c7b4e1..c494644fb 100644 --- a/pkg/document/change/context.go +++ b/pkg/document/change/context.go @@ -27,12 +27,12 @@ import ( // Each time we add an operation, a new time ticket is issued. // Finally, returns a Change after the modification has been completed. type Context struct { - id ID - message string - operations []operations.Operation - delimiter uint32 - root *crdt.Root - presence *innerpresence.Presence + id ID + message string + operations []operations.Operation + delimiter uint32 + root *crdt.Root + presenceChange *innerpresence.PresenceChange } // NewContext creates a new instance of Context. @@ -51,12 +51,12 @@ func (c *Context) ID() ID { // ToChange creates a new change of this context. func (c *Context) ToChange() *Change { - return New(c.id, c.message, c.operations, c.presence) + return New(c.id, c.message, c.operations, c.presenceChange) } // HasChange returns whether this context has changes. func (c *Context) HasChange() bool { - return len(c.operations) > 0 || c.presence != nil + return len(c.operations) > 0 || c.presenceChange != nil } // IssueTimeTicket creates a time ticket to be used to create a new operation. @@ -90,7 +90,7 @@ func (c *Context) LastTimeTicket() *time.Ticket { return c.id.NewTimeTicket(c.delimiter) } -// SetPresence sets the presence of the user who made the change. -func (c *Context) SetPresence(presence innerpresence.Presence) { - c.presence = &presence +// SetPresenceChange sets the presence change of the user who made the change. +func (c *Context) SetPresenceChange(presenceChange innerpresence.PresenceChange) { + c.presenceChange = &presenceChange } diff --git a/pkg/document/innerpresence/presence.go b/pkg/document/innerpresence/presence.go index 71b29a0f2..3476d5d1a 100644 --- a/pkg/document/innerpresence/presence.go +++ b/pkg/document/innerpresence/presence.go @@ -33,23 +33,37 @@ func NewMap() *Map { return &sync.Map{} } -// Presence represents custom presence that can be defined by the client. -type Presence map[string]string +// PresenceChangeType represents the type of presence change. +type PresenceChangeType string -// NewFromJSON creates a new instance of Presence from JSON. -func NewFromJSON(encodedJSON string) (*Presence, error) { +const ( + // Put represents the presence is put. + Put PresenceChangeType = "put" +) + +// PresenceChange represents the change of presence. +type PresenceChange struct { + ChangeType PresenceChangeType + Presence Presence +} + +// NewChangeFromJSON creates a new instance of PresenceChange from JSON. +func NewChangeFromJSON(encodedJSON string) (*PresenceChange, error) { if encodedJSON == "" { return nil, nil } - p := Presence{} + p := PresenceChange{} if err := json.Unmarshal([]byte(encodedJSON), &p); err != nil { - return nil, fmt.Errorf("unmarshal presence: %w", err) + return nil, fmt.Errorf("unmarshal presence change: %w", err) } return &p, nil } +// Presence represents custom presence that can be defined by the client. +type Presence map[string]string + // NewPresence creates a new instance of Presence. func NewPresence() *Presence { data := make(map[string]string) diff --git a/pkg/document/presence/presence.go b/pkg/document/presence/presence.go index 0f81894fd..d03814995 100644 --- a/pkg/document/presence/presence.go +++ b/pkg/document/presence/presence.go @@ -21,17 +21,17 @@ package presence import ( "github.com/yorkie-team/yorkie/pkg/document/change" - presence2 "github.com/yorkie-team/yorkie/pkg/document/innerpresence" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" ) // Presence represents a proxy for the Presence to be manipulated from the outside. type Presence struct { - presence *presence2.Presence + presence *innerpresence.Presence context *change.Context } // New creates a new instance of Presence. -func New(ctx *change.Context, presence *presence2.Presence) *Presence { +func New(ctx *change.Context, presence *innerpresence.Presence) *Presence { return &Presence{ presence: presence, context: ctx, @@ -42,5 +42,10 @@ func New(ctx *change.Context, presence *presence2.Presence) *Presence { func (p *Presence) Set(key string, value string) { internalPresence := *p.presence internalPresence.Set(key, value) - p.context.SetPresence(internalPresence) + + // TODO(hackerwins): We should support partial update here. + p.context.SetPresenceChange(innerpresence.PresenceChange{ + ChangeType: "put", + Presence: internalPresence, + }) } diff --git a/server/backend/database/change_info.go b/server/backend/database/change_info.go index 5a394d824..bfbf76680 100644 --- a/server/backend/database/change_info.go +++ b/server/backend/database/change_info.go @@ -35,15 +35,15 @@ var ErrEncodeOperationFailed = errors.New("encode operations failed") // ChangeInfo is a structure representing information of a change. type ChangeInfo struct { - ID types.ID `bson:"_id"` - DocID types.ID `bson:"doc_id"` - ServerSeq int64 `bson:"server_seq"` - ClientSeq uint32 `bson:"client_seq"` - Lamport int64 `bson:"lamport"` - ActorID types.ID `bson:"actor_id"` - Message string `bson:"message"` - Operations [][]byte `bson:"operations"` - Presence string `bson:"presence"` + ID types.ID `bson:"_id"` + DocID types.ID `bson:"doc_id"` + ServerSeq int64 `bson:"server_seq"` + ClientSeq uint32 `bson:"client_seq"` + Lamport int64 `bson:"lamport"` + ActorID types.ID `bson:"actor_id"` + Message string `bson:"message"` + Operations [][]byte `bson:"operations"` + PresenceChange string `bson:"presence_change"` } // EncodeOperations encodes the given operations into bytes array. @@ -66,15 +66,15 @@ func EncodeOperations(operations []operations.Operation) ([][]byte, error) { return encodedOps, nil } -// EncodePresence encodes the given presence into string. -func EncodePresence(p *innerpresence.Presence) (string, error) { +// EncodePresenceChange encodes the given presence change into string. +func EncodePresenceChange(p *innerpresence.PresenceChange) (string, error) { if p == nil { return "", nil } bytes, err := json.Marshal(p) if err != nil { - return "", fmt.Errorf("marshal presence to bytes: %w", err) + return "", fmt.Errorf("marshal presence change to bytes: %w", err) } return string(bytes), nil @@ -103,7 +103,7 @@ func (i *ChangeInfo) ToChange() (*change.Change, error) { return nil, err } - p, err := innerpresence.NewFromJSON(i.Presence) + p, err := innerpresence.NewChangeFromJSON(i.PresenceChange) if err != nil { return nil, err } diff --git a/server/backend/database/memory/database.go b/server/backend/database/memory/database.go index 11d5f9c14..172847089 100644 --- a/server/backend/database/memory/database.go +++ b/server/backend/database/memory/database.go @@ -782,21 +782,21 @@ func (d *DB) CreateChangeInfos( if err != nil { return err } - encodedPresence, err := database.EncodePresence(cn.Presence()) + encodedPresence, err := database.EncodePresenceChange(cn.PresenceChange()) if err != nil { return err } if err := txn.Insert(tblChanges, &database.ChangeInfo{ - ID: newID(), - DocID: docInfo.ID, - ServerSeq: cn.ServerSeq(), - ActorID: types.ID(cn.ID().ActorID().String()), - ClientSeq: cn.ClientSeq(), - Lamport: cn.ID().Lamport(), - Message: cn.Message(), - Operations: encodedOperations, - Presence: encodedPresence, + ID: newID(), + DocID: docInfo.ID, + ServerSeq: cn.ServerSeq(), + ActorID: types.ID(cn.ID().ActorID().String()), + ClientSeq: cn.ClientSeq(), + Lamport: cn.ID().Lamport(), + Message: cn.Message(), + Operations: encodedOperations, + PresenceChange: encodedPresence, }); err != nil { return fmt.Errorf("create change: %w", err) } diff --git a/server/backend/database/mongo/client.go b/server/backend/database/mongo/client.go index c00ec55fb..132f2007a 100644 --- a/server/backend/database/mongo/client.go +++ b/server/backend/database/mongo/client.go @@ -864,7 +864,7 @@ func (c *Client) CreateChangeInfos( if err != nil { return err } - encodedPresence, err := database.EncodePresence(cn.Presence()) + encodedPresence, err := database.EncodePresenceChange(cn.PresenceChange()) if err != nil { return err } @@ -873,12 +873,12 @@ func (c *Client) CreateChangeInfos( "doc_id": encodedDocID, "server_seq": cn.ServerSeq(), }).SetUpdate(bson.M{"$set": bson.M{ - "actor_id": encodeActorID(cn.ID().ActorID()), - "client_seq": cn.ID().ClientSeq(), - "lamport": cn.ID().Lamport(), - "message": cn.Message(), - "operations": encodedOperations, - "presence": encodedPresence, + "actor_id": encodeActorID(cn.ID().ActorID()), + "client_seq": cn.ID().ClientSeq(), + "lamport": cn.ID().Lamport(), + "message": cn.Message(), + "operations": encodedOperations, + "presence_change": encodedPresence, }}).SetUpsert(true)) } diff --git a/server/packs/serverpacks.go b/server/packs/serverpacks.go index 92a3d8a7f..e36f75009 100644 --- a/server/packs/serverpacks.go +++ b/server/packs/serverpacks.go @@ -102,16 +102,16 @@ func (p *ServerPack) ToPBChangePack() (*api.ChangePack, error) { pbOps = append(pbOps, &pbOp) } - p, err := innerpresence.NewFromJSON(info.Presence) + p, err := innerpresence.NewChangeFromJSON(info.PresenceChange) if err != nil { return nil, err } pbChanges = append(pbChanges, &api.Change{ - Id: converter.ToChangeID(changeID), - Message: info.Message, - Operations: pbOps, - Presence: converter.ToPresence(p), + Id: converter.ToChangeID(changeID), + Message: info.Message, + Operations: pbOps, + PresenceChange: converter.ToPresenceChange(p), }) } From 68d78a14ea7c1a49f7c02548fd90d5828265128a Mon Sep 17 00:00:00 2001 From: Youngteac Hong Date: Fri, 14 Jul 2023 16:56:03 +0900 Subject: [PATCH 4/5] Remove client.UpdatePresence and pass PeersChangedEvent to Client (#578) Co-authored-by: Yourim Cha --- admin/client.go | 2 +- api/converter/converter_test.go | 35 +- api/converter/from_bytes.go | 4 +- api/converter/from_pb.go | 83 +- api/converter/to_bytes.go | 8 +- api/converter/to_pb.go | 47 +- api/types/client.go | 53 -- api/types/event.go | 3 - api/yorkie/v1/resources.pb.go | 834 +++++------------- api/yorkie/v1/resources.proto | 14 +- api/yorkie/v1/yorkie.pb.go | 586 ++---------- api/yorkie/v1/yorkie.proto | 12 +- client/client.go | 245 +++-- client/options.go | 60 +- pkg/document/change/change.go | 10 +- pkg/document/change/checkpoint.go | 2 +- pkg/document/document.go | 129 ++- pkg/document/innerpresence/presence.go | 104 ++- pkg/document/internal_document.go | 157 +++- pkg/document/presence/presence.go | 36 +- server/backend/database/memory/database.go | 2 +- server/backend/database/mongo/client.go | 2 +- server/backend/sync/coordinator.go | 11 +- server/backend/sync/memory/coordinator.go | 18 +- .../backend/sync/memory/coordinator_test.go | 4 +- server/backend/sync/memory/pubsub.go | 57 +- server/backend/sync/memory/pubsub_test.go | 8 +- server/backend/sync/pubsub.go | 19 +- server/clients/clients.go | 5 + server/packs/packs.go | 2 +- server/packs/pushpull.go | 2 +- server/rpc/admin_server.go | 4 +- server/rpc/server_test.go | 2 +- server/rpc/yorkie_server.go | 74 +- test/bench/grpc_bench_test.go | 2 +- test/helper/helper.go | 1 + test/integration/admin_test.go | 8 +- test/integration/agent_test.go | 3 +- test/integration/client_test.go | 14 +- test/integration/document_test.go | 65 +- test/integration/gc_test.go | 2 +- test/integration/history_test.go | 5 +- test/integration/main_test.go | 11 +- test/integration/peer_awareness_test.go | 273 ------ test/integration/presence_test.go | 485 ++++++++++ test/integration/retention_test.go | 6 +- 46 files changed, 1469 insertions(+), 2040 deletions(-) delete mode 100644 api/types/client.go delete mode 100644 test/integration/peer_awareness_test.go create mode 100644 test/integration/presence_test.go diff --git a/admin/client.go b/admin/client.go index d8616f94e..493e0600e 100644 --- a/admin/client.go +++ b/admin/client.go @@ -348,7 +348,7 @@ func (c *Client) ListChangeSummaries( } var summaries []*types.ChangeSummary for _, c := range changes { - if err := newDoc.ApplyChanges(c); err != nil { + if _, err := newDoc.ApplyChanges(c); err != nil { return nil, err } diff --git a/api/converter/converter_test.go b/api/converter/converter_test.go index 719c74dcc..c92b047bc 100644 --- a/api/converter/converter_test.go +++ b/api/converter/converter_test.go @@ -24,10 +24,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/yorkie-team/yorkie/api/converter" - "github.com/yorkie-team/yorkie/api/types" api "github.com/yorkie-team/yorkie/api/yorkie/v1" "github.com/yorkie-team/yorkie/pkg/document" "github.com/yorkie-team/yorkie/pkg/document/crdt" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/document/json" "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" @@ -64,7 +64,7 @@ func TestConverter(t *testing.T) { assert.Equal(t, `{"k1":[{"val":"B"}]}`, obj.Marshal()) }) - t.Run("snapshot test", func(t *testing.T) { + t.Run("root snapshot test", func(t *testing.T) { doc := document.New("d1") err := doc.Update(func(root *json.Object, p *presence.Presence) error { @@ -210,28 +210,6 @@ func TestConverter(t *testing.T) { assert.ErrorIs(t, err, converter.ErrCheckpointRequired) }) - t.Run("client test", func(t *testing.T) { - cli := types.Client{ - ID: time.InitialActorID, - PresenceInfo: types.PresenceInfo{ - Presence: types.Presence{"Name": "ClientName"}, - }, - } - - pbCli := converter.ToClient(cli) - decodedCli, err := converter.FromClient(pbCli) - assert.NoError(t, err) - assert.Equal(t, cli.ID.Bytes(), decodedCli.ID.Bytes()) - assert.Equal(t, cli.PresenceInfo, decodedCli.PresenceInfo) - - pbClients := converter.ToClients([]types.Client{cli}) - - decodedCli, err = converter.FromClient(pbClients[0]) - assert.NoError(t, err) - assert.Equal(t, cli.ID.Bytes(), decodedCli.ID.Bytes()) - assert.Equal(t, cli.PresenceInfo, decodedCli.PresenceInfo) - }) - t.Run("tree converting test", func(t *testing.T) { root := helper.BuildTreeNode(&json.TreeNode{ Type: "r", @@ -265,4 +243,13 @@ func TestConverter(t *testing.T) { assert.Equal(t, tree.ToXML(), clone.ToXML()) }) + + t.Run("empty presence converting test", func(t *testing.T) { + change, err := innerpresence.NewChangeFromJSON(`{"ChangeType":"put","Presence":{}}`) + assert.NoError(t, err) + + pbChange := converter.ToPresenceChange(change) + clone := converter.FromPresenceChange(pbChange) + assert.Equal(t, change, clone) + }) } diff --git a/api/converter/from_bytes.go b/api/converter/from_bytes.go index 27e49a188..6f0b6e8fa 100644 --- a/api/converter/from_bytes.go +++ b/api/converter/from_bytes.go @@ -44,8 +44,8 @@ func BytesToSnapshot(snapshot []byte) (*crdt.Object, *innerpresence.Map, error) return nil, nil, err } - presenceMap := fromPresenceMap(pbSnapshot.GetPresenceMap()) - return obj.(*crdt.Object), presenceMap, nil + presences := fromPresences(pbSnapshot.GetPresences()) + return obj.(*crdt.Object), presences, nil } // BytesToObject creates an Object from the given byte array. diff --git a/api/converter/from_pb.go b/api/converter/from_pb.go index 08f6465bd..7c31f144f 100644 --- a/api/converter/from_pb.go +++ b/api/converter/from_pb.go @@ -120,26 +120,6 @@ func FromDocumentSummary(pbSummary *api.DocumentSummary) (*types.DocumentSummary }, nil } -// FromClient converts the given Protobuf formats to model format. -func FromClient(pbClient *api.Client) (*types.Client, error) { - id, err := time.ActorIDFromBytes(pbClient.Id) - if err != nil { - return nil, err - } - - return &types.Client{ - ID: id, - PresenceInfo: FromPresenceInfo(pbClient.Presence), - }, nil -} - -// FromPresenceInfo converts the given Protobuf formats to model format. -func FromPresenceInfo(pbPresence *api.Presence) types.PresenceInfo { - return types.PresenceInfo{ - Presence: pbPresence.Data, - } -} - // FromChangePack converts the given Protobuf formats to model format. func FromChangePack(pbPack *api.ChangePack) (*change.Pack, error) { if pbPack == nil { @@ -192,7 +172,7 @@ func FromChanges(pbChanges []*api.Change) ([]*change.Change, error) { changeID, pbChange.Message, ops, - fromPresenceChange(pbChange.PresenceChange), + FromPresenceChange(pbChange.PresenceChange), )) } @@ -241,15 +221,13 @@ func FromEventType(pbDocEventType api.DocEventType) (types.DocEventType, error) return types.DocumentsWatchedEvent, nil case api.DocEventType_DOC_EVENT_TYPE_DOCUMENTS_UNWATCHED: return types.DocumentsUnwatchedEvent, nil - case api.DocEventType_DOC_EVENT_TYPE_PRESENCE_CHANGED: - return types.PresenceChangedEvent, nil } return "", fmt.Errorf("%v: %w", pbDocEventType, ErrUnsupportedEventType) } // FromDocEvent converts the given Protobuf formats to model format. func FromDocEvent(docEvent *api.DocEvent) (*sync.DocEvent, error) { - client, err := FromClient(docEvent.Publisher) + client, err := time.ActorIDFromBytes(docEvent.Publisher) if err != nil { return nil, err } @@ -259,32 +237,12 @@ func FromDocEvent(docEvent *api.DocEvent) (*sync.DocEvent, error) { return nil, err } - documentID, err := FromDocumentID(docEvent.DocumentId) - if err != nil { - return nil, err - } - return &sync.DocEvent{ - Type: eventType, - Publisher: *client, - DocumentID: documentID, + Type: eventType, + Publisher: client, }, nil } -// FromClients converts the given Protobuf formats to model format. -func FromClients(pbClients []*api.Client) ([]*types.Client, error) { - var clients []*types.Client - for _, pbClient := range pbClients { - client, err := FromClient(pbClient) - if err != nil { - return nil, err - } - clients = append(clients, client) - } - - return clients, nil -} - // FromOperations converts the given Protobuf formats to model format. func FromOperations(pbOps []*api.Operation) ([]operations.Operation, error) { var ops []operations.Operation @@ -324,24 +282,29 @@ func FromOperations(pbOps []*api.Operation) ([]operations.Operation, error) { return ops, nil } -func fromPresenceMap(pbPresenceMap map[string]*api.Presence) *innerpresence.Map { - presenceMap := innerpresence.NewMap() - for id, pbPresence := range pbPresenceMap { - presenceMap.Store(id, fromPresence(pbPresence)) +func fromPresences(pbPresences map[string]*api.Presence) *innerpresence.Map { + presences := innerpresence.NewMap() + for id, pbPresence := range pbPresences { + presences.Store(id, fromPresence(pbPresence)) } - return presenceMap + return presences } -func fromPresence(pbPresence *api.Presence) *innerpresence.Presence { +func fromPresence(pbPresence *api.Presence) innerpresence.Presence { if pbPresence == nil { return nil } - p := innerpresence.Presence(pbPresence.GetData()) - return &p + data := pbPresence.GetData() + if data == nil { + data = innerpresence.NewPresence() + } + + return data } -func fromPresenceChange(pbPresenceChange *api.PresenceChange) *innerpresence.PresenceChange { +// FromPresenceChange converts the given Protobuf formats to model format. +func FromPresenceChange(pbPresenceChange *api.PresenceChange) *innerpresence.PresenceChange { if pbPresenceChange == nil { return nil } @@ -350,11 +313,19 @@ func fromPresenceChange(pbPresenceChange *api.PresenceChange) *innerpresence.Pre switch pbPresenceChange.Type { case api.PresenceChange_CHANGE_TYPE_PUT: changeType = innerpresence.Put + case api.PresenceChange_CHANGE_TYPE_CLEAR: + changeType = innerpresence.Clear } + p := innerpresence.PresenceChange{ ChangeType: changeType, - Presence: pbPresenceChange.Presence, + Presence: pbPresenceChange.Presence.Data, } + + if p.ChangeType == innerpresence.Put && p.Presence == nil { + p.Presence = innerpresence.NewPresence() + } + return &p } diff --git a/api/converter/to_bytes.go b/api/converter/to_bytes.go index 5049cd609..6eeeaee05 100644 --- a/api/converter/to_bytes.go +++ b/api/converter/to_bytes.go @@ -29,17 +29,17 @@ import ( ) // SnapshotToBytes converts the given document to byte array. -func SnapshotToBytes(obj *crdt.Object, presenceMap *innerpresence.Map) ([]byte, error) { +func SnapshotToBytes(obj *crdt.Object, presences *innerpresence.Map) ([]byte, error) { pbElem, err := toJSONElement(obj) if err != nil { return nil, err } - pbPresenceMap := ToPresenceMap(presenceMap) + pbPresences := ToPresences(presences) bytes, err := proto.Marshal(&api.Snapshot{ - Root: pbElem, - PresenceMap: pbPresenceMap, + Root: pbElem, + Presences: pbPresences, }) if err != nil { return nil, fmt.Errorf("marshal Snapshot to bytes: %w", err) diff --git a/api/converter/to_pb.go b/api/converter/to_pb.go index d2e7b7205..6d9efa52e 100644 --- a/api/converter/to_pb.go +++ b/api/converter/to_pb.go @@ -122,32 +122,24 @@ func ToDocumentSummary(summary *types.DocumentSummary) (*api.DocumentSummary, er }, nil } -// ToClient converts the given model to Protobuf format. -func ToClient(client types.Client) *api.Client { - return &api.Client{ - Id: client.ID.Bytes(), - Presence: ToPresenceInfo(client.PresenceInfo), - } -} - -// ToPresenceMap converts the given model to Protobuf format. -func ToPresenceMap(presenceMap *innerpresence.Map) map[string]*api.Presence { +// ToPresences converts the given model to Protobuf format. +func ToPresences(presences *innerpresence.Map) map[string]*api.Presence { pbPresences := make(map[string]*api.Presence) - presenceMap.Range(func(k, v interface{}) bool { - pbPresences[k.(string)] = ToPresence(v.(*innerpresence.Presence)) + presences.Range(func(k string, v innerpresence.Presence) bool { + pbPresences[k] = ToPresence(v) return true }) return pbPresences } // ToPresence converts the given model to Protobuf format. -func ToPresence(p *innerpresence.Presence) *api.Presence { +func ToPresence(p innerpresence.Presence) *api.Presence { if p == nil { return nil } return &api.Presence{ - Data: *p, + Data: p, } } @@ -161,17 +153,12 @@ func ToPresenceChange(p *innerpresence.PresenceChange) *api.PresenceChange { switch p.ChangeType { case innerpresence.Put: changeType = api.PresenceChange_CHANGE_TYPE_PUT + case innerpresence.Clear: + changeType = api.PresenceChange_CHANGE_TYPE_CLEAR } return &api.PresenceChange{ Type: changeType, - Presence: p.Presence, - } -} - -// ToPresenceInfo converts the given model to Protobuf format. -func ToPresenceInfo(info types.PresenceInfo) *api.Presence { - return &api.Presence{ - Data: info.Presence, + Presence: &api.Presence{Data: p.Presence}, } } @@ -210,15 +197,6 @@ func ToChangeID(id change.ID) *api.ChangeID { } } -// ToClients converts the given model to Protobuf format. -func ToClients(clients []types.Client) []*api.Client { - var pbClients []*api.Client - for _, client := range clients { - pbClients = append(pbClients, ToClient(client)) - } - return pbClients -} - // ToDocEventType converts the given model format to Protobuf format. func ToDocEventType(eventType types.DocEventType) (api.DocEventType, error) { switch eventType { @@ -228,8 +206,6 @@ func ToDocEventType(eventType types.DocEventType) (api.DocEventType, error) { return api.DocEventType_DOC_EVENT_TYPE_DOCUMENTS_WATCHED, nil case types.DocumentsUnwatchedEvent: return api.DocEventType_DOC_EVENT_TYPE_DOCUMENTS_UNWATCHED, nil - case types.PresenceChangedEvent: - return api.DocEventType_DOC_EVENT_TYPE_PRESENCE_CHANGED, nil default: return 0, fmt.Errorf("%s: %w", eventType, ErrUnsupportedEventType) } @@ -243,9 +219,8 @@ func ToDocEvent(docEvent sync.DocEvent) (*api.DocEvent, error) { } return &api.DocEvent{ - Type: eventType, - Publisher: ToClient(docEvent.Publisher), - DocumentId: docEvent.DocumentID.String(), + Type: eventType, + Publisher: docEvent.Publisher.Bytes(), }, nil } diff --git a/api/types/client.go b/api/types/client.go deleted file mode 100644 index ff2a0fab8..000000000 --- a/api/types/client.go +++ /dev/null @@ -1,53 +0,0 @@ -package types - -import ( - "encoding/json" - "fmt" - - "github.com/yorkie-team/yorkie/pkg/document/time" -) - -// Client represents the Client that communicates with the Server. -type Client struct { - ID *time.ActorID - PresenceInfo PresenceInfo -} - -// NewClient creates a new Client from the given JSON. -func NewClient(encoded []byte) (*Client, error) { - cli := &Client{} - err := json.Unmarshal(encoded, cli) - if err != nil { - return nil, fmt.Errorf("unmarshal client: %w", err) - } - return cli, nil -} - -// Marshal serializes the Client to JSON. -func (c *Client) Marshal() (string, error) { - encoded, err := json.Marshal(c) - if err != nil { - return "", fmt.Errorf("marshal client: %w", err) - } - - return string(encoded), nil -} - -// Presence represents custom presence that can be defined in the client. -type Presence map[string]string - -// PresenceInfo is a presence information with logical clock. -type PresenceInfo struct { - Clock int32 - Presence Presence -} - -// Update updates the given presence information with the given clock. -func (i *PresenceInfo) Update(info PresenceInfo) bool { - if info.Clock > i.Clock { - i.Clock = info.Clock - i.Presence = info.Presence - return true - } - return false -} diff --git a/api/types/event.go b/api/types/event.go index 7472e6816..d04de35bc 100644 --- a/api/types/event.go +++ b/api/types/event.go @@ -15,7 +15,4 @@ const ( // DocumentsUnwatchedEvent is an event that occurs when documents are // unwatched by other clients. DocumentsUnwatchedEvent DocEventType = "documents-unwatched" - - // PresenceChangedEvent is an event indicating that presence is changed. - PresenceChangedEvent DocEventType = "presence-changed" ) diff --git a/api/yorkie/v1/resources.pb.go b/api/yorkie/v1/resources.pb.go index 5d76d1327..e736967f1 100644 --- a/api/yorkie/v1/resources.pb.go +++ b/api/yorkie/v1/resources.pb.go @@ -90,21 +90,18 @@ const ( DocEventType_DOC_EVENT_TYPE_DOCUMENTS_CHANGED DocEventType = 0 DocEventType_DOC_EVENT_TYPE_DOCUMENTS_WATCHED DocEventType = 1 DocEventType_DOC_EVENT_TYPE_DOCUMENTS_UNWATCHED DocEventType = 2 - DocEventType_DOC_EVENT_TYPE_PRESENCE_CHANGED DocEventType = 3 ) var DocEventType_name = map[int32]string{ 0: "DOC_EVENT_TYPE_DOCUMENTS_CHANGED", 1: "DOC_EVENT_TYPE_DOCUMENTS_WATCHED", 2: "DOC_EVENT_TYPE_DOCUMENTS_UNWATCHED", - 3: "DOC_EVENT_TYPE_PRESENCE_CHANGED", } var DocEventType_value = map[string]int32{ "DOC_EVENT_TYPE_DOCUMENTS_CHANGED": 0, "DOC_EVENT_TYPE_DOCUMENTS_WATCHED": 1, "DOC_EVENT_TYPE_DOCUMENTS_UNWATCHED": 2, - "DOC_EVENT_TYPE_PRESENCE_CHANGED": 3, } func (x DocEventType) String() string { @@ -121,18 +118,21 @@ const ( PresenceChange_CHANGE_TYPE_UNSPECIFIED PresenceChange_ChangeType = 0 PresenceChange_CHANGE_TYPE_PUT PresenceChange_ChangeType = 1 PresenceChange_CHANGE_TYPE_DELETE PresenceChange_ChangeType = 2 + PresenceChange_CHANGE_TYPE_CLEAR PresenceChange_ChangeType = 3 ) var PresenceChange_ChangeType_name = map[int32]string{ 0: "CHANGE_TYPE_UNSPECIFIED", 1: "CHANGE_TYPE_PUT", 2: "CHANGE_TYPE_DELETE", + 3: "CHANGE_TYPE_CLEAR", } var PresenceChange_ChangeType_value = map[string]int32{ "CHANGE_TYPE_UNSPECIFIED": 0, "CHANGE_TYPE_PUT": 1, "CHANGE_TYPE_DELETE": 2, + "CHANGE_TYPE_CLEAR": 3, } func (x PresenceChange_ChangeType) String() string { @@ -143,12 +143,12 @@ func (PresenceChange_ChangeType) EnumDescriptor() ([]byte, []int) { return fileDescriptor_36361b2f5d0f0896, []int{18, 0} } -///////////////////////////////////////// +// /////////////////////////////////////// // Messages for Snapshot // -///////////////////////////////////////// +// /////////////////////////////////////// type Snapshot struct { Root *JSONElement `protobuf:"bytes,1,opt,name=root,proto3" json:"root,omitempty"` - PresenceMap map[string]*Presence `protobuf:"bytes,2,rep,name=presence_map,json=presenceMap,proto3" json:"presence_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Presences map[string]*Presence `protobuf:"bytes,2,rep,name=presences,proto3" json:"presences,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -194,9 +194,9 @@ func (m *Snapshot) GetRoot() *JSONElement { return nil } -func (m *Snapshot) GetPresenceMap() map[string]*Presence { +func (m *Snapshot) GetPresences() map[string]*Presence { if m != nil { - return m.PresenceMap + return m.Presences } return nil } @@ -434,6 +434,7 @@ func (m *ChangeID) GetActorId() []byte { type Operation struct { // Types that are valid to be assigned to Body: + // // *Operation_Set_ // *Operation_Add_ // *Operation_Move_ @@ -1447,6 +1448,7 @@ func (m *JSONElementSimple) GetValue() []byte { type JSONElement struct { // Types that are valid to be assigned to Body: + // // *JSONElement_JsonObject // *JSONElement_JsonArray // *JSONElement_Primitive_ @@ -2861,7 +2863,7 @@ func (m *DocumentSummary) GetUpdatedAt() *types.Timestamp { type PresenceChange struct { Type PresenceChange_ChangeType `protobuf:"varint,1,opt,name=type,proto3,enum=yorkie.v1.PresenceChange_ChangeType" json:"type,omitempty"` - Presence map[string]string `protobuf:"bytes,2,rep,name=presence,proto3" json:"presence,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Presence *Presence `protobuf:"bytes,2,opt,name=presence,proto3" json:"presence,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2907,7 +2909,7 @@ func (m *PresenceChange) GetType() PresenceChange_ChangeType { return PresenceChange_CHANGE_TYPE_UNSPECIFIED } -func (m *PresenceChange) GetPresence() map[string]string { +func (m *PresenceChange) GetPresence() *Presence { if m != nil { return m.Presence } @@ -2961,61 +2963,6 @@ func (m *Presence) GetData() map[string]string { return nil } -type Client struct { - Id []byte `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Presence *Presence `protobuf:"bytes,2,opt,name=presence,proto3" json:"presence,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Client) Reset() { *m = Client{} } -func (m *Client) String() string { return proto.CompactTextString(m) } -func (*Client) ProtoMessage() {} -func (*Client) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{20} -} -func (m *Client) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *Client) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_Client.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *Client) XXX_Merge(src proto.Message) { - xxx_messageInfo_Client.Merge(m, src) -} -func (m *Client) XXX_Size() int { - return m.Size() -} -func (m *Client) XXX_DiscardUnknown() { - xxx_messageInfo_Client.DiscardUnknown(m) -} - -var xxx_messageInfo_Client proto.InternalMessageInfo - -func (m *Client) GetId() []byte { - if m != nil { - return m.Id - } - return nil -} - -func (m *Client) GetPresence() *Presence { - if m != nil { - return m.Presence - } - return nil -} - type Checkpoint struct { ServerSeq int64 `protobuf:"varint,1,opt,name=server_seq,json=serverSeq,proto3" json:"server_seq,omitempty"` ClientSeq uint32 `protobuf:"varint,2,opt,name=client_seq,json=clientSeq,proto3" json:"client_seq,omitempty"` @@ -3028,7 +2975,7 @@ func (m *Checkpoint) Reset() { *m = Checkpoint{} } func (m *Checkpoint) String() string { return proto.CompactTextString(m) } func (*Checkpoint) ProtoMessage() {} func (*Checkpoint) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{21} + return fileDescriptor_36361b2f5d0f0896, []int{20} } func (m *Checkpoint) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3084,7 +3031,7 @@ func (m *TextNodePos) Reset() { *m = TextNodePos{} } func (m *TextNodePos) String() string { return proto.CompactTextString(m) } func (*TextNodePos) ProtoMessage() {} func (*TextNodePos) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{22} + return fileDescriptor_36361b2f5d0f0896, []int{21} } func (m *TextNodePos) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3147,7 +3094,7 @@ func (m *TimeTicket) Reset() { *m = TimeTicket{} } func (m *TimeTicket) String() string { return proto.CompactTextString(m) } func (*TimeTicket) ProtoMessage() {} func (*TimeTicket) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{23} + return fileDescriptor_36361b2f5d0f0896, []int{22} } func (m *TimeTicket) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3199,8 +3146,7 @@ func (m *TimeTicket) GetActorId() []byte { type DocEvent struct { Type DocEventType `protobuf:"varint,1,opt,name=type,proto3,enum=yorkie.v1.DocEventType" json:"type,omitempty"` - Publisher *Client `protobuf:"bytes,2,opt,name=publisher,proto3" json:"publisher,omitempty"` - DocumentId string `protobuf:"bytes,3,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` + Publisher []byte `protobuf:"bytes,2,opt,name=publisher,proto3" json:"publisher,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -3210,7 +3156,7 @@ func (m *DocEvent) Reset() { *m = DocEvent{} } func (m *DocEvent) String() string { return proto.CompactTextString(m) } func (*DocEvent) ProtoMessage() {} func (*DocEvent) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{24} + return fileDescriptor_36361b2f5d0f0896, []int{23} } func (m *DocEvent) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3246,26 +3192,19 @@ func (m *DocEvent) GetType() DocEventType { return DocEventType_DOC_EVENT_TYPE_DOCUMENTS_CHANGED } -func (m *DocEvent) GetPublisher() *Client { +func (m *DocEvent) GetPublisher() []byte { if m != nil { return m.Publisher } return nil } -func (m *DocEvent) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - func init() { proto.RegisterEnum("yorkie.v1.ValueType", ValueType_name, ValueType_value) proto.RegisterEnum("yorkie.v1.DocEventType", DocEventType_name, DocEventType_value) proto.RegisterEnum("yorkie.v1.PresenceChange_ChangeType", PresenceChange_ChangeType_name, PresenceChange_ChangeType_value) proto.RegisterType((*Snapshot)(nil), "yorkie.v1.Snapshot") - proto.RegisterMapType((map[string]*Presence)(nil), "yorkie.v1.Snapshot.PresenceMapEntry") + proto.RegisterMapType((map[string]*Presence)(nil), "yorkie.v1.Snapshot.PresencesEntry") proto.RegisterType((*ChangePack)(nil), "yorkie.v1.ChangePack") proto.RegisterType((*Change)(nil), "yorkie.v1.Change") proto.RegisterType((*ChangeID)(nil), "yorkie.v1.ChangeID") @@ -3307,10 +3246,8 @@ func init() { proto.RegisterType((*UpdatableProjectFields_AuthWebhookMethods)(nil), "yorkie.v1.UpdatableProjectFields.AuthWebhookMethods") proto.RegisterType((*DocumentSummary)(nil), "yorkie.v1.DocumentSummary") proto.RegisterType((*PresenceChange)(nil), "yorkie.v1.PresenceChange") - proto.RegisterMapType((map[string]string)(nil), "yorkie.v1.PresenceChange.PresenceEntry") proto.RegisterType((*Presence)(nil), "yorkie.v1.Presence") proto.RegisterMapType((map[string]string)(nil), "yorkie.v1.Presence.DataEntry") - proto.RegisterType((*Client)(nil), "yorkie.v1.Client") proto.RegisterType((*Checkpoint)(nil), "yorkie.v1.Checkpoint") proto.RegisterType((*TextNodePos)(nil), "yorkie.v1.TextNodePos") proto.RegisterType((*TimeTicket)(nil), "yorkie.v1.TimeTicket") @@ -3320,169 +3257,165 @@ func init() { func init() { proto.RegisterFile("yorkie/v1/resources.proto", fileDescriptor_36361b2f5d0f0896) } var fileDescriptor_36361b2f5d0f0896 = []byte{ - // 2584 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xdf, 0x6f, 0x1b, 0x49, - 0x1d, 0xcf, 0xae, 0x7f, 0xee, 0x37, 0x69, 0xe2, 0x4e, 0xfa, 0xc3, 0x75, 0xaf, 0xb9, 0x9c, 0xaf, - 0xdc, 0xe5, 0x5a, 0x70, 0xda, 0xd0, 0x83, 0xe3, 0xca, 0x01, 0x8e, 0xbd, 0x4d, 0xd2, 0x4b, 0x1c, - 0x6b, 0xed, 0xb4, 0xf4, 0x04, 0x5a, 0x6d, 0x76, 0xa7, 0xcd, 0x5e, 0x6c, 0xef, 0xde, 0xee, 0xda, - 0x57, 0xbf, 0x21, 0x04, 0x12, 0x3c, 0xf2, 0xc6, 0x1f, 0x80, 0x84, 0xf8, 0x13, 0xee, 0x11, 0x1e, - 0x10, 0x3c, 0x20, 0x90, 0xa8, 0xc4, 0x2b, 0x94, 0x07, 0xc4, 0x23, 0x42, 0xf0, 0x86, 0x84, 0xe6, - 0xc7, 0xae, 0xd7, 0xeb, 0x5d, 0xd7, 0x09, 0xd1, 0xa9, 0x15, 0x6f, 0x3b, 0x33, 0x9f, 0xef, 0xcc, - 0xf7, 0xf7, 0x7c, 0x67, 0x67, 0xe0, 0xca, 0xd0, 0x72, 0x8e, 0x4d, 0xbc, 0x3e, 0xb8, 0xbd, 0xee, - 0x60, 0xd7, 0xea, 0x3b, 0x3a, 0x76, 0x2b, 0xb6, 0x63, 0x79, 0x16, 0x92, 0xd8, 0x50, 0x65, 0x70, - 0xbb, 0xf4, 0xfa, 0x13, 0xcb, 0x7a, 0xd2, 0xc1, 0xeb, 0x74, 0xe0, 0xb0, 0xff, 0x78, 0xdd, 0x33, - 0xbb, 0xd8, 0xf5, 0xb4, 0xae, 0xcd, 0xb0, 0xa5, 0x95, 0x28, 0xe0, 0x53, 0x47, 0xb3, 0x6d, 0xec, - 0xf0, 0xb9, 0xca, 0xcf, 0x04, 0xc8, 0xb7, 0x7a, 0x9a, 0xed, 0x1e, 0x59, 0x1e, 0xba, 0x01, 0x69, - 0xc7, 0xb2, 0xbc, 0xa2, 0xb0, 0x2a, 0xac, 0xcd, 0x6f, 0x5c, 0xaa, 0x04, 0xeb, 0x54, 0xee, 0xb7, - 0xf6, 0x1b, 0x72, 0x07, 0x77, 0x71, 0xcf, 0x53, 0x28, 0x06, 0x6d, 0xc1, 0x82, 0xed, 0x60, 0x17, - 0xf7, 0x74, 0xac, 0x76, 0x35, 0xbb, 0x28, 0xae, 0xa6, 0xd6, 0xe6, 0x37, 0xae, 0x87, 0x68, 0xfc, - 0x69, 0x2b, 0x4d, 0x8e, 0xdb, 0xd3, 0x6c, 0xb9, 0xe7, 0x39, 0x43, 0x65, 0xde, 0x1e, 0xf5, 0x94, - 0x5a, 0x50, 0x88, 0x02, 0x50, 0x01, 0x52, 0xc7, 0x78, 0x48, 0xf9, 0x90, 0x14, 0xf2, 0x89, 0xde, - 0x81, 0xcc, 0x40, 0xeb, 0xf4, 0x71, 0x51, 0xa4, 0xbc, 0x2d, 0x87, 0xd6, 0xf1, 0xa9, 0x15, 0x86, - 0x78, 0x5f, 0x7c, 0x4f, 0x28, 0xff, 0x44, 0x04, 0xa8, 0x1d, 0x69, 0xbd, 0x27, 0xb8, 0xa9, 0xe9, - 0xc7, 0xe8, 0x0d, 0x58, 0x30, 0x2c, 0xbd, 0x4f, 0xd8, 0x57, 0x47, 0x13, 0xcf, 0xfb, 0x7d, 0x1f, - 0xe2, 0x21, 0x7a, 0x17, 0x40, 0x3f, 0xc2, 0xfa, 0xb1, 0x6d, 0x99, 0x3d, 0x8f, 0xaf, 0x72, 0x31, - 0xb4, 0x4a, 0x2d, 0x18, 0x54, 0x42, 0x40, 0x54, 0x82, 0xbc, 0xcb, 0xe5, 0x2c, 0xa6, 0x56, 0x85, - 0xb5, 0x05, 0x25, 0x68, 0xa3, 0x9b, 0x90, 0xd3, 0x29, 0x0f, 0x6e, 0x31, 0x4d, 0xb5, 0x73, 0x7e, - 0x6c, 0x3e, 0x32, 0xa2, 0xf8, 0x08, 0x54, 0x85, 0xf3, 0x5d, 0xb3, 0xa7, 0xba, 0xc3, 0x9e, 0x8e, - 0x0d, 0xd5, 0x33, 0xf5, 0x63, 0xec, 0x15, 0x33, 0x13, 0x6c, 0xb4, 0xcd, 0x2e, 0x6e, 0xd3, 0x41, - 0x65, 0xa9, 0x6b, 0xf6, 0x5a, 0x14, 0xce, 0x3a, 0xd0, 0x35, 0x00, 0xd3, 0x55, 0x1d, 0xdc, 0xb5, - 0x06, 0xd8, 0x28, 0x66, 0x57, 0x85, 0xb5, 0xbc, 0x22, 0x99, 0xae, 0xc2, 0x3a, 0xca, 0xbf, 0x14, - 0x20, 0xcb, 0x56, 0x45, 0x6f, 0x82, 0x68, 0x1a, 0xdc, 0xcc, 0xcb, 0x13, 0x4c, 0xed, 0xd4, 0x15, - 0xd1, 0x34, 0x50, 0x11, 0x72, 0x5d, 0xec, 0xba, 0xda, 0x13, 0xa6, 0x74, 0x49, 0xf1, 0x9b, 0xe8, - 0x0e, 0x80, 0x65, 0x63, 0x47, 0xf3, 0x4c, 0xab, 0xe7, 0x16, 0x53, 0x54, 0xb6, 0x0b, 0xa1, 0x69, - 0xf6, 0xfd, 0x41, 0x25, 0x84, 0x43, 0x9b, 0xb0, 0x14, 0x78, 0x0c, 0x93, 0xba, 0x98, 0xa6, 0x1c, - 0x5c, 0x89, 0x31, 0x26, 0x57, 0xcf, 0xa2, 0x3d, 0xd6, 0x2e, 0xff, 0x50, 0x80, 0xbc, 0xcf, 0x24, - 0x91, 0x57, 0xef, 0x98, 0xc4, 0xa6, 0x2e, 0xfe, 0x84, 0x4a, 0x73, 0x4e, 0x91, 0x58, 0x4f, 0x0b, - 0x7f, 0x82, 0xde, 0x00, 0x70, 0xb1, 0x33, 0xc0, 0x0e, 0x1d, 0x26, 0x22, 0xa4, 0x36, 0xc5, 0x5b, - 0x82, 0x22, 0xb1, 0x5e, 0x02, 0x79, 0x0d, 0x72, 0x1d, 0xad, 0x6b, 0x5b, 0x0e, 0x33, 0x1e, 0x1b, - 0xf7, 0xbb, 0xd0, 0x15, 0xc8, 0x6b, 0xba, 0x67, 0x39, 0xaa, 0x69, 0x50, 0x4e, 0x17, 0x94, 0x1c, - 0x6d, 0xef, 0x18, 0xe5, 0xdf, 0x96, 0x40, 0x0a, 0xa4, 0x44, 0x5f, 0x84, 0x94, 0x8b, 0xfd, 0xb0, - 0x29, 0xc6, 0x29, 0xa2, 0xd2, 0xc2, 0xde, 0xf6, 0x9c, 0x42, 0x60, 0x04, 0xad, 0x19, 0x06, 0x77, - 0xb1, 0x78, 0x74, 0xd5, 0x30, 0x08, 0x5a, 0x33, 0x0c, 0xb4, 0x0e, 0x69, 0x62, 0x3e, 0xca, 0xdf, - 0xb8, 0xaa, 0x46, 0xf0, 0x3d, 0x6b, 0x80, 0xb7, 0xe7, 0x14, 0x0a, 0x44, 0xef, 0x42, 0x96, 0xb9, - 0x00, 0xd7, 0xee, 0xd5, 0x58, 0x12, 0xe6, 0x14, 0xdb, 0x73, 0x0a, 0x07, 0x93, 0x75, 0xb0, 0x61, - 0xfa, 0x2e, 0x17, 0xbf, 0x8e, 0x6c, 0x98, 0x44, 0x0a, 0x0a, 0x24, 0xeb, 0xb8, 0xb8, 0x83, 0x75, - 0x8f, 0x7a, 0x5a, 0xd2, 0x3a, 0x2d, 0x0a, 0x21, 0xeb, 0x30, 0x30, 0xda, 0x80, 0x8c, 0xeb, 0x0d, - 0x3b, 0xb8, 0x98, 0xa3, 0x54, 0xa5, 0x78, 0x2a, 0x82, 0xd8, 0x9e, 0x53, 0x18, 0x14, 0xdd, 0x85, - 0xbc, 0xd9, 0xd3, 0x1d, 0xac, 0xb9, 0xb8, 0x98, 0xa7, 0x64, 0xd7, 0x62, 0xc9, 0x76, 0x38, 0x68, - 0x7b, 0x4e, 0x09, 0x08, 0xd0, 0xd7, 0x41, 0xf2, 0x1c, 0x8c, 0x55, 0x2a, 0x9d, 0x34, 0x85, 0xba, - 0xed, 0x60, 0xcc, 0x25, 0xcc, 0x7b, 0xfc, 0x1b, 0x7d, 0x13, 0x80, 0x52, 0x33, 0x9e, 0x81, 0x92, - 0xaf, 0x24, 0x92, 0xfb, 0x7c, 0xd3, 0x15, 0x69, 0xa3, 0xf4, 0x6b, 0x01, 0x52, 0x2d, 0xec, 0x91, - 0xf8, 0xb6, 0x35, 0x87, 0x38, 0x2b, 0xe1, 0xcb, 0xc3, 0x86, 0xaa, 0xf9, 0x1e, 0x93, 0x14, 0xdf, - 0x0c, 0x5f, 0x63, 0xf0, 0xaa, 0xe7, 0x67, 0x45, 0x71, 0x94, 0x15, 0x37, 0xfc, 0xac, 0xc8, 0xbc, - 0xe3, 0xb5, 0xf8, 0x8c, 0xdd, 0x32, 0xbb, 0x76, 0xc7, 0x4f, 0x8f, 0xe8, 0x2b, 0x30, 0x8f, 0x9f, - 0x62, 0xbd, 0xcf, 0x59, 0x48, 0x4f, 0x63, 0x01, 0x7c, 0x64, 0xd5, 0x2b, 0xfd, 0x53, 0x80, 0x54, - 0xd5, 0x30, 0xce, 0x42, 0x90, 0x0f, 0x68, 0x26, 0x18, 0x84, 0x27, 0x10, 0xa7, 0x4d, 0x70, 0x8e, - 0xa0, 0x47, 0xe4, 0x9f, 0xa7, 0xd4, 0xff, 0x16, 0x20, 0x4d, 0xc2, 0xeb, 0x25, 0x10, 0xfb, 0x0e, - 0x40, 0x88, 0x32, 0x35, 0x8d, 0x52, 0xd2, 0x03, 0xaa, 0xd3, 0x0a, 0xfe, 0x99, 0x00, 0x59, 0x96, - 0x24, 0xce, 0x42, 0xf4, 0x71, 0xde, 0xc5, 0xd3, 0xf1, 0x9e, 0x9a, 0x95, 0xf7, 0x5f, 0xa5, 0x21, - 0x4d, 0xa3, 0xf7, 0x0c, 0x38, 0xbf, 0x01, 0xe9, 0xc7, 0x8e, 0xd5, 0xe5, 0x3c, 0x87, 0x6b, 0xa2, - 0x36, 0x7e, 0xea, 0x35, 0x2c, 0x03, 0x37, 0x2d, 0x57, 0xa1, 0x18, 0xf4, 0x16, 0x88, 0x9e, 0xc5, - 0xd9, 0x4c, 0x42, 0x8a, 0x9e, 0x85, 0x8e, 0xe0, 0xf2, 0x88, 0x1f, 0x52, 0x3d, 0xa9, 0x87, 0x43, - 0x95, 0x6e, 0x2d, 0xbc, 0x50, 0xd8, 0x48, 0x4c, 0xbf, 0x95, 0x80, 0xb3, 0x3d, 0xcd, 0xde, 0x1c, - 0x56, 0x09, 0x11, 0x2b, 0xaa, 0x96, 0xf5, 0xc9, 0x11, 0xb2, 0x87, 0xeb, 0x56, 0xcf, 0xc3, 0x3d, - 0x96, 0xd8, 0x25, 0xc5, 0x6f, 0x46, 0x75, 0x9b, 0x9d, 0x51, 0xb7, 0x68, 0x07, 0x40, 0xf3, 0x3c, - 0xc7, 0x3c, 0xec, 0x7b, 0xd8, 0x2d, 0xe6, 0x28, 0xbb, 0xef, 0x24, 0xb3, 0x5b, 0x0d, 0xb0, 0x8c, - 0xcb, 0x10, 0x71, 0xe9, 0xbb, 0x50, 0x4c, 0x92, 0x26, 0xa6, 0x02, 0xbc, 0x39, 0x5e, 0x01, 0x26, - 0xb0, 0x3a, 0xaa, 0x01, 0x4b, 0x1f, 0xc0, 0x52, 0x64, 0xf5, 0x98, 0x59, 0x2f, 0x84, 0x67, 0x95, - 0xc2, 0xe4, 0x7f, 0x12, 0x20, 0xcb, 0x76, 0xaf, 0x97, 0xd5, 0x8d, 0x4e, 0x1b, 0xda, 0x7f, 0x11, - 0x21, 0x43, 0x37, 0xa7, 0x97, 0x55, 0xb0, 0xfb, 0x63, 0x3e, 0xc6, 0x42, 0xe2, 0x46, 0x72, 0xa1, - 0x30, 0xcd, 0xc9, 0xa2, 0x4a, 0xca, 0xcc, 0xaa, 0xa4, 0xff, 0xd1, 0x7b, 0x3e, 0x13, 0x20, 0xef, - 0x97, 0x23, 0x67, 0xa1, 0xe6, 0x8d, 0x71, 0xef, 0x3f, 0xcd, 0x9e, 0x37, 0x73, 0xfa, 0xfc, 0x9e, - 0x08, 0x79, 0xbf, 0x18, 0x3a, 0x0b, 0xde, 0xdf, 0x1a, 0x73, 0x11, 0x14, 0xa6, 0x72, 0x70, 0xc8, - 0x3d, 0xca, 0x21, 0xf7, 0x88, 0x43, 0x11, 0xd7, 0xf8, 0xd2, 0x28, 0xa1, 0x31, 0xbf, 0x58, 0x8e, - 0x00, 0x89, 0x1f, 0x25, 0x66, 0xb9, 0xcc, 0x09, 0x42, 0x44, 0x0a, 0x0a, 0xba, 0x97, 0x4d, 0x07, - 0x8d, 0x98, 0xf0, 0xa8, 0x4c, 0xaf, 0x49, 0x5f, 0xc2, 0x10, 0xd9, 0xcc, 0x42, 0xfa, 0xd0, 0x32, - 0x86, 0xe5, 0x7f, 0x08, 0x70, 0x7e, 0xc2, 0x87, 0x23, 0x15, 0x83, 0x30, 0x63, 0xc5, 0x70, 0x0b, - 0xf2, 0xf4, 0xb0, 0xfb, 0xc2, 0x2a, 0x23, 0x47, 0x61, 0xac, 0x32, 0xe1, 0x27, 0xe6, 0x17, 0x57, - 0x55, 0x1c, 0x58, 0xf5, 0xd0, 0x1a, 0xa4, 0xbd, 0xa1, 0xcd, 0x8e, 0x58, 0x8b, 0x63, 0x67, 0xdf, - 0x07, 0x44, 0xbe, 0xf6, 0xd0, 0xc6, 0x0a, 0x45, 0x8c, 0xe4, 0xcf, 0xd0, 0x13, 0x24, 0x6b, 0x94, - 0x7f, 0x71, 0x0e, 0xe6, 0x43, 0x32, 0xa3, 0x3a, 0xcc, 0x7f, 0xec, 0x5a, 0x3d, 0xd5, 0x3a, 0xfc, - 0x98, 0x9c, 0xa8, 0x98, 0xb8, 0x6f, 0xc4, 0x07, 0x39, 0xfd, 0xde, 0xa7, 0xc0, 0xed, 0x39, 0x05, - 0x08, 0x1d, 0x6b, 0xa1, 0x2a, 0xd0, 0x96, 0xaa, 0x39, 0x8e, 0x36, 0xe4, 0xf2, 0xaf, 0x4e, 0x99, - 0xa4, 0x4a, 0x70, 0xe4, 0xb8, 0x42, 0xa8, 0x68, 0x03, 0x7d, 0x0b, 0x24, 0xdb, 0x31, 0xbb, 0xa6, - 0x67, 0x06, 0x67, 0xce, 0xa4, 0x19, 0x9a, 0x3e, 0x8e, 0xcc, 0x10, 0x10, 0xa1, 0xdb, 0x90, 0xf6, - 0xf0, 0x53, 0xdf, 0x8d, 0xae, 0x26, 0x10, 0x93, 0x94, 0x4f, 0x8e, 0x92, 0x04, 0x8a, 0xde, 0x27, - 0x41, 0xdd, 0xef, 0x79, 0xd8, 0xe1, 0x75, 0xc8, 0x4a, 0x02, 0x55, 0x8d, 0xa1, 0xb6, 0xe7, 0x14, - 0x9f, 0x80, 0x2e, 0xe7, 0x60, 0xff, 0x38, 0x99, 0xb8, 0x9c, 0x83, 0xe9, 0x09, 0x99, 0x40, 0x4b, - 0xcf, 0x04, 0x80, 0x91, 0x0e, 0xd1, 0x1a, 0x64, 0x7a, 0x96, 0x81, 0xdd, 0xa2, 0x40, 0x23, 0x29, - 0x1c, 0x75, 0xca, 0x76, 0x9b, 0xe6, 0x13, 0x06, 0x38, 0x65, 0x15, 0x1b, 0xf6, 0xc9, 0xd4, 0x29, - 0x7c, 0x32, 0x3d, 0x9b, 0x4f, 0x96, 0xfe, 0x28, 0x80, 0x14, 0x58, 0x75, 0xaa, 0x54, 0x5b, 0xd5, - 0x57, 0x47, 0xaa, 0xbf, 0x0b, 0x20, 0x05, 0x9e, 0x16, 0xc4, 0x9d, 0x30, 0x7b, 0xdc, 0x89, 0xa1, - 0xb8, 0x3b, 0xe5, 0x19, 0x2a, 0x2c, 0x6b, 0xfa, 0x14, 0xb2, 0x66, 0x66, 0x94, 0xf5, 0xf7, 0x02, - 0xa4, 0x49, 0x60, 0xa0, 0x77, 0xc6, 0x8d, 0xb7, 0x1c, 0x53, 0x2b, 0xbd, 0x1a, 0xd6, 0xfb, 0x9b, - 0x00, 0x39, 0x1e, 0xb4, 0xff, 0x0f, 0xb6, 0x73, 0x30, 0x9e, 0x6a, 0x3b, 0xbf, 0x3e, 0x79, 0x15, - 0x6c, 0x17, 0xec, 0xcf, 0x7b, 0x90, 0xe3, 0x79, 0x30, 0x66, 0x7b, 0xbf, 0x05, 0x39, 0xcc, 0x72, - 0x6c, 0xcc, 0x09, 0x20, 0x7c, 0x6b, 0xe0, 0xc3, 0xca, 0x3a, 0xe4, 0x78, 0x02, 0x22, 0x45, 0x51, - 0x8f, 0x6c, 0x15, 0xc2, 0x44, 0xb9, 0xe3, 0xa7, 0x28, 0x3a, 0x7e, 0x8a, 0x45, 0x1e, 0x40, 0x9e, - 0xd0, 0x93, 0xf2, 0x64, 0xe4, 0x4d, 0x42, 0xa8, 0x02, 0x21, 0x3a, 0xe9, 0xdb, 0xc6, 0x6c, 0xba, - 0xe7, 0xc0, 0xaa, 0x57, 0xfe, 0x1d, 0x29, 0x8d, 0x79, 0x04, 0xa2, 0x2f, 0x84, 0xfe, 0xa2, 0x5f, - 0x8c, 0x09, 0x51, 0xfe, 0x1f, 0x3d, 0xb6, 0x02, 0x3a, 0x65, 0xdd, 0xf1, 0x2e, 0xcc, 0x9b, 0x3d, - 0x57, 0xa5, 0xbf, 0x91, 0xf8, 0x5f, 0xe9, 0xc4, 0xb5, 0x25, 0xb3, 0xe7, 0x36, 0x1d, 0x3c, 0xd8, - 0x31, 0x50, 0x6d, 0xac, 0x62, 0xcc, 0x50, 0xc7, 0x7c, 0x33, 0x86, 0x6a, 0xea, 0x71, 0x5d, 0x99, - 0xa5, 0xdc, 0x9b, 0x72, 0x4f, 0xe3, 0x1b, 0x24, 0x7c, 0x4f, 0xf3, 0x11, 0xc0, 0x88, 0xe3, 0x53, - 0xd6, 0x7c, 0x97, 0x20, 0x6b, 0x3d, 0x7e, 0xec, 0x62, 0x66, 0xc5, 0x8c, 0xc2, 0x5b, 0xe5, 0x7f, - 0xf1, 0x63, 0x0c, 0xb5, 0xd5, 0x75, 0x48, 0xd9, 0x96, 0x1b, 0xe3, 0x69, 0x7e, 0x61, 0x4d, 0x86, - 0x11, 0xe2, 0x29, 0x8a, 0x59, 0x2a, 0x92, 0x8c, 0x52, 0xc9, 0xe6, 0x9b, 0x31, 0xa4, 0xd0, 0x1d, - 0x58, 0x08, 0xcc, 0x47, 0xd8, 0xc9, 0x24, 0xb2, 0x03, 0xdc, 0x78, 0x4d, 0xcb, 0x25, 0x1c, 0x18, - 0xd8, 0xf6, 0x8e, 0x68, 0x71, 0x94, 0x51, 0x58, 0x23, 0x62, 0xd3, 0xdc, 0xa4, 0x4d, 0xb9, 0xe8, - 0x9f, 0xbb, 0x4d, 0x1f, 0x42, 0x8e, 0x4b, 0x71, 0xc6, 0x06, 0xed, 0x42, 0xfa, 0xc0, 0xc5, 0x0e, - 0x5a, 0x0c, 0xe2, 0x4e, 0xa2, 0x01, 0x56, 0x82, 0x7c, 0xdf, 0xc5, 0x4e, 0x4f, 0xeb, 0xfa, 0x96, - 0x0b, 0xda, 0xe8, 0x6b, 0x31, 0x9b, 0x46, 0xa9, 0xc2, 0x2e, 0x45, 0x2b, 0xfe, 0xa5, 0x28, 0xe5, - 0x83, 0xde, 0x9a, 0x86, 0xd8, 0x28, 0xff, 0x47, 0x84, 0x5c, 0xd3, 0xb1, 0x68, 0x8d, 0x18, 0x5d, - 0x12, 0x41, 0x3a, 0xb4, 0x1c, 0xfd, 0x46, 0xd7, 0x00, 0xec, 0xfe, 0x61, 0xc7, 0xd4, 0xe9, 0x15, - 0x23, 0xf3, 0x16, 0x89, 0xf5, 0x7c, 0x88, 0x87, 0x64, 0xd8, 0xc5, 0xba, 0x83, 0xd9, 0x0d, 0x64, - 0x9a, 0x0d, 0xb3, 0x1e, 0x32, 0xbc, 0x06, 0x05, 0xad, 0xef, 0x1d, 0xa9, 0x9f, 0xe2, 0xc3, 0x23, - 0xcb, 0x3a, 0x56, 0xfb, 0x4e, 0x87, 0xff, 0xb2, 0x5b, 0x24, 0xfd, 0x0f, 0x59, 0xf7, 0x81, 0xd3, - 0x41, 0xb7, 0xe0, 0xc2, 0x18, 0xb2, 0x8b, 0xbd, 0x23, 0xcb, 0x70, 0x8b, 0xd9, 0xd5, 0xd4, 0x9a, - 0xa4, 0xa0, 0x10, 0x7a, 0x8f, 0x8d, 0xa0, 0x6f, 0xc0, 0x55, 0x7e, 0x51, 0x66, 0x60, 0x4d, 0xf7, - 0xcc, 0x81, 0xe6, 0x61, 0xd5, 0x3b, 0x72, 0xb0, 0x7b, 0x64, 0x75, 0x0c, 0x5a, 0x3a, 0x4b, 0xca, - 0x15, 0x06, 0xa9, 0x07, 0x88, 0xb6, 0x0f, 0x88, 0x28, 0x31, 0x7f, 0x02, 0x25, 0x12, 0xd2, 0x50, - 0x9a, 0x95, 0x5e, 0x4c, 0x3a, 0xca, 0xb5, 0x3f, 0x4a, 0xc1, 0xa5, 0x03, 0xd2, 0xd2, 0x0e, 0x3b, - 0x98, 0x1b, 0xe2, 0x9e, 0x89, 0x3b, 0x86, 0x8b, 0x6e, 0x71, 0xf5, 0x0b, 0xfc, 0x67, 0x48, 0x74, - 0xbe, 0x96, 0xe7, 0x98, 0xbd, 0x27, 0xb4, 0xac, 0xe0, 0xc6, 0xb9, 0x17, 0xa3, 0x5e, 0x71, 0x06, - 0xea, 0xa8, 0xf2, 0x1f, 0x27, 0x28, 0x9f, 0x79, 0xd6, 0x9d, 0x90, 0x6f, 0xc7, 0xb3, 0x5e, 0xa9, - 0x4e, 0x98, 0x27, 0xd6, 0x64, 0xdf, 0x99, 0x6e, 0xb2, 0xf4, 0x0c, 0xac, 0x27, 0x1b, 0xb4, 0x54, - 0x01, 0x34, 0xc9, 0x07, 0xbb, 0xf0, 0x65, 0xe2, 0x08, 0xd4, 0x97, 0xfc, 0x66, 0xf9, 0xfb, 0x22, - 0x2c, 0xd5, 0xf9, 0x65, 0x79, 0xab, 0xdf, 0xed, 0x6a, 0xce, 0x70, 0x22, 0x24, 0x26, 0x6f, 0xa7, - 0xa2, 0x77, 0xe3, 0x52, 0xe8, 0x6e, 0x7c, 0xdc, 0xa5, 0xd2, 0x27, 0x71, 0xa9, 0xbb, 0x30, 0xaf, - 0xe9, 0x3a, 0x76, 0xdd, 0x70, 0x81, 0x36, 0x8d, 0x16, 0x7c, 0xf8, 0x84, 0x3f, 0x66, 0x4f, 0xe2, - 0x8f, 0x3f, 0x13, 0x61, 0x71, 0xfc, 0x7a, 0x1a, 0xbd, 0x37, 0x56, 0xd2, 0x5e, 0x4f, 0xbc, 0xc7, - 0xe6, 0x17, 0xeb, 0xa1, 0x12, 0xb7, 0x06, 0x79, 0xff, 0x6a, 0x9b, 0x3f, 0x9d, 0x78, 0x3b, 0x99, - 0xda, 0x6f, 0xb2, 0xfc, 0x1d, 0x10, 0x96, 0xee, 0xc2, 0xb9, 0xb1, 0xa1, 0x93, 0xfc, 0x7e, 0x29, - 0x3f, 0xf0, 0x5f, 0x48, 0x10, 0xae, 0xd0, 0x55, 0xb8, 0x5c, 0xdb, 0xae, 0x36, 0xb6, 0x64, 0xb5, - 0xfd, 0xa8, 0x29, 0xab, 0x07, 0x8d, 0x56, 0x53, 0xae, 0xed, 0xdc, 0xdb, 0x91, 0xeb, 0x85, 0x39, - 0xb4, 0x0c, 0x4b, 0xe1, 0xc1, 0xe6, 0x41, 0xbb, 0x20, 0xa0, 0x4b, 0x80, 0xc2, 0x9d, 0x75, 0x79, - 0x57, 0x6e, 0xcb, 0x05, 0xb1, 0x3c, 0x80, 0xbc, 0xcf, 0x14, 0x39, 0x9c, 0x93, 0x20, 0xe0, 0xa5, - 0xf0, 0xb5, 0x18, 0x09, 0x2b, 0x75, 0xcd, 0xd3, 0x98, 0x5c, 0x14, 0x5a, 0xfa, 0x2a, 0x48, 0x41, - 0xd7, 0x89, 0xe4, 0xd9, 0x81, 0x6c, 0x8d, 0x3a, 0x7c, 0xc8, 0x33, 0x17, 0xa8, 0x67, 0xae, 0x8f, - 0xe9, 0x3a, 0xf1, 0xf9, 0x48, 0x00, 0x2a, 0x37, 0x88, 0x6a, 0x82, 0x27, 0x1e, 0xe3, 0xef, 0x08, - 0x84, 0xb8, 0x77, 0x04, 0xe3, 0x2f, 0x11, 0xc4, 0xc8, 0x4b, 0x84, 0xf2, 0x0f, 0x04, 0x98, 0x0f, - 0xfd, 0xe3, 0x3e, 0xdb, 0x6d, 0x11, 0xbd, 0x0d, 0x4b, 0x0e, 0xee, 0x68, 0xe4, 0x7c, 0xac, 0x72, - 0x40, 0x8a, 0x02, 0x16, 0xfd, 0xee, 0x7d, 0xb6, 0x7f, 0xea, 0x00, 0xa3, 0x99, 0xc3, 0x6f, 0x1f, - 0x84, 0xc9, 0xb7, 0x0f, 0xaf, 0x81, 0x64, 0xe0, 0x0e, 0x39, 0x76, 0x63, 0xc7, 0x17, 0x28, 0xe8, - 0x18, 0x7b, 0x19, 0x91, 0x1a, 0x7f, 0x19, 0xf1, 0x63, 0x01, 0xf2, 0x75, 0x4b, 0x97, 0x07, 0xc4, - 0x12, 0x37, 0xc7, 0xe2, 0xe3, 0x72, 0x48, 0x44, 0x1f, 0x12, 0x0a, 0x89, 0x75, 0x60, 0xbb, 0xa5, - 0x7b, 0xc4, 0x97, 0x8c, 0x3c, 0x98, 0xa1, 0xea, 0x54, 0x46, 0x18, 0xf4, 0x3a, 0x04, 0x2f, 0x78, - 0x7c, 0x46, 0x24, 0x05, 0xfc, 0xae, 0x1d, 0xe3, 0xc6, 0x33, 0x11, 0xa4, 0xe0, 0x6c, 0x49, 0xbc, - 0xf8, 0x41, 0x75, 0xf7, 0x80, 0xfb, 0x6b, 0xe3, 0x60, 0x77, 0xb7, 0x30, 0x47, 0xbc, 0x38, 0xd4, - 0xb9, 0xb9, 0xbf, 0xbf, 0x2b, 0x57, 0x1b, 0xcc, 0xbb, 0x43, 0xfd, 0x3b, 0x8d, 0xb6, 0xbc, 0x25, - 0x2b, 0x05, 0x31, 0x32, 0xc9, 0xee, 0x7e, 0x63, 0xab, 0x90, 0x42, 0x17, 0xe1, 0x7c, 0xa8, 0xb3, - 0xbe, 0x7f, 0xb0, 0xb9, 0x2b, 0x17, 0xd2, 0x91, 0xee, 0x56, 0x5b, 0xd9, 0x69, 0x6c, 0x15, 0x32, - 0xe8, 0x02, 0x14, 0xc2, 0x4b, 0x3e, 0x6a, 0xcb, 0xad, 0x42, 0x36, 0x32, 0x71, 0xbd, 0xda, 0x96, - 0x0b, 0x39, 0x54, 0x82, 0x4b, 0xa1, 0x4e, 0x72, 0xd2, 0x51, 0xf7, 0x37, 0xef, 0xcb, 0xb5, 0x76, - 0x21, 0x8f, 0xae, 0xc0, 0xc5, 0xe8, 0x58, 0x55, 0x51, 0xaa, 0x8f, 0x0a, 0x52, 0x64, 0xae, 0xb6, - 0xfc, 0xed, 0x76, 0x01, 0x22, 0x73, 0x71, 0x89, 0xd4, 0x5a, 0xa3, 0x5d, 0x98, 0x47, 0x97, 0x61, - 0x39, 0x22, 0x15, 0x1d, 0x58, 0x88, 0xce, 0xa4, 0xc8, 0x72, 0xe1, 0xdc, 0x8d, 0x9f, 0x0b, 0xb0, - 0x10, 0xb6, 0x1f, 0xba, 0x0e, 0xab, 0xf5, 0xfd, 0x9a, 0x2a, 0x3f, 0x90, 0x1b, 0x6d, 0x5f, 0x07, - 0xb5, 0x83, 0x3d, 0xb9, 0xd1, 0x6e, 0xa9, 0x2c, 0x47, 0x90, 0x2c, 0x32, 0x0d, 0xf5, 0xb0, 0xda, - 0xae, 0x6d, 0xcb, 0xf5, 0x82, 0x80, 0xde, 0x82, 0x72, 0x22, 0xea, 0xa0, 0xe1, 0xe3, 0x44, 0xf4, - 0x26, 0xbc, 0x1e, 0xc1, 0x35, 0x15, 0xb9, 0x25, 0x37, 0x6a, 0x72, 0xb0, 0x64, 0x6a, 0xf3, 0xe6, - 0x6f, 0x9e, 0xaf, 0x08, 0x7f, 0x78, 0xbe, 0x22, 0xfc, 0xf9, 0xf9, 0x8a, 0xf0, 0xd3, 0xbf, 0xae, - 0xcc, 0xc1, 0x79, 0x03, 0x0f, 0x7c, 0xa7, 0xd2, 0x6c, 0xb3, 0x32, 0xb8, 0xdd, 0x14, 0x3e, 0x4a, - 0x57, 0xee, 0x0e, 0x6e, 0x1f, 0x66, 0x69, 0xfa, 0xff, 0xf2, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, - 0xa6, 0xd2, 0x34, 0x16, 0x7a, 0x27, 0x00, 0x00, + // 2523 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xdf, 0x6f, 0x1b, 0x59, + 0xf5, 0xcf, 0x8c, 0x7f, 0xce, 0x49, 0x9a, 0xb8, 0x37, 0xfd, 0xe1, 0xba, 0x6d, 0xbe, 0x59, 0x6f, + 0xbf, 0x25, 0x6d, 0xc1, 0x69, 0x43, 0x0b, 0xcb, 0x96, 0x05, 0x1c, 0x7b, 0xda, 0xa4, 0x9b, 0x3a, + 0x61, 0xec, 0xb4, 0x74, 0x05, 0x1a, 0x4d, 0x66, 0x6e, 0x9b, 0xd9, 0xd8, 0x1e, 0xef, 0xcc, 0xd8, + 0x5b, 0xbf, 0xa1, 0x15, 0x48, 0xbc, 0xf2, 0xc6, 0xbf, 0xc0, 0x9f, 0xb0, 0x8f, 0xf0, 0x80, 0x40, + 0x08, 0x81, 0xc4, 0x4a, 0xbc, 0x42, 0x79, 0x40, 0xf0, 0x86, 0x10, 0xbc, 0x21, 0xa1, 0xfb, 0x6b, + 0x7c, 0x6d, 0x8f, 0x5d, 0x37, 0x44, 0xab, 0x56, 0xbc, 0xf9, 0xde, 0xfb, 0x39, 0xf7, 0x9e, 0x73, + 0xcf, 0xe7, 0x9c, 0x7b, 0xae, 0xef, 0xc0, 0x85, 0xbe, 0xe7, 0x1f, 0xb9, 0x78, 0xbd, 0x77, 0x6b, + 0xdd, 0xc7, 0x81, 0xd7, 0xf5, 0x6d, 0x1c, 0x94, 0x3a, 0xbe, 0x17, 0x7a, 0x48, 0x63, 0x43, 0xa5, + 0xde, 0xad, 0xc2, 0xff, 0x3d, 0xf3, 0xbc, 0x67, 0x4d, 0xbc, 0x4e, 0x07, 0x0e, 0xba, 0x4f, 0xd7, + 0x43, 0xb7, 0x85, 0x83, 0xd0, 0x6a, 0x75, 0x18, 0xb6, 0xb0, 0x32, 0x0a, 0xf8, 0xd8, 0xb7, 0x3a, + 0x1d, 0xec, 0xf3, 0xb9, 0x8a, 0xbf, 0x56, 0x20, 0x5b, 0x6f, 0x5b, 0x9d, 0xe0, 0xd0, 0x0b, 0xd1, + 0x75, 0x48, 0xfa, 0x9e, 0x17, 0xe6, 0x95, 0x55, 0x65, 0x6d, 0x7e, 0xe3, 0x5c, 0x29, 0x5a, 0xa7, + 0xf4, 0xa0, 0xbe, 0x5b, 0xd3, 0x9b, 0xb8, 0x85, 0xdb, 0xa1, 0x41, 0x31, 0xe8, 0x5b, 0xa0, 0x75, + 0x7c, 0x1c, 0xe0, 0xb6, 0x8d, 0x83, 0xbc, 0xba, 0x9a, 0x58, 0x9b, 0xdf, 0x28, 0x4a, 0x02, 0x62, + 0xce, 0xd2, 0x9e, 0x00, 0xe9, 0xed, 0xd0, 0xef, 0x1b, 0x03, 0xa1, 0xc2, 0xb7, 0x61, 0x71, 0x78, + 0x10, 0xe5, 0x20, 0x71, 0x84, 0xfb, 0x74, 0x79, 0xcd, 0x20, 0x3f, 0xd1, 0x35, 0x48, 0xf5, 0xac, + 0x66, 0x17, 0xe7, 0x55, 0xaa, 0xd2, 0xb2, 0xb4, 0x82, 0x90, 0x35, 0x18, 0xe2, 0x5d, 0xf5, 0x1d, + 0xa5, 0xf8, 0x63, 0x15, 0xa0, 0x72, 0x68, 0xb5, 0x9f, 0xe1, 0x3d, 0xcb, 0x3e, 0x42, 0x6f, 0xc1, + 0x82, 0xe3, 0xd9, 0x5d, 0xa2, 0xb5, 0x39, 0x98, 0x78, 0x5e, 0xf4, 0xbd, 0x8f, 0xfb, 0xe8, 0x0e, + 0x80, 0x7d, 0x88, 0xed, 0xa3, 0x8e, 0xe7, 0xb6, 0x43, 0xbe, 0xca, 0x59, 0x69, 0x95, 0x4a, 0x34, + 0x68, 0x48, 0x40, 0x54, 0x80, 0x6c, 0xc0, 0x2d, 0xcc, 0x27, 0x56, 0x95, 0xb5, 0x05, 0x23, 0x6a, + 0xa3, 0x1b, 0x90, 0xb1, 0xa9, 0x0e, 0x41, 0x3e, 0x49, 0xf7, 0xe5, 0xf4, 0xd0, 0x7c, 0x64, 0xc4, + 0x10, 0x08, 0x54, 0x86, 0xd3, 0x2d, 0xb7, 0x6d, 0x06, 0xfd, 0xb6, 0x8d, 0x1d, 0x33, 0x74, 0xed, + 0x23, 0x1c, 0xe6, 0x53, 0x63, 0x6a, 0x34, 0xdc, 0x16, 0x6e, 0xd0, 0x41, 0x63, 0xa9, 0xe5, 0xb6, + 0xeb, 0x14, 0xce, 0x3a, 0xd0, 0x65, 0x00, 0x37, 0x30, 0x7d, 0xdc, 0xf2, 0x7a, 0xd8, 0xc9, 0xa7, + 0x57, 0x95, 0xb5, 0xac, 0xa1, 0xb9, 0x81, 0xc1, 0x3a, 0x8a, 0x3f, 0x53, 0x20, 0xcd, 0x56, 0x45, + 0x6f, 0x83, 0xea, 0x3a, 0xdc, 0xbb, 0xcb, 0x63, 0x4a, 0x6d, 0x57, 0x0d, 0xd5, 0x75, 0x50, 0x1e, + 0x32, 0x2d, 0x1c, 0x04, 0xd6, 0x33, 0xb6, 0xe9, 0x9a, 0x21, 0x9a, 0xe8, 0x36, 0x80, 0xd7, 0xc1, + 0xbe, 0x15, 0xba, 0x5e, 0x3b, 0xc8, 0x27, 0xa8, 0x6d, 0x67, 0xa4, 0x69, 0x76, 0xc5, 0xa0, 0x21, + 0xe1, 0xd0, 0x26, 0x2c, 0x09, 0x9f, 0x9b, 0xcc, 0xea, 0x7c, 0x92, 0x6a, 0x70, 0x21, 0xc6, 0x99, + 0x7c, 0x7b, 0x16, 0x3b, 0x43, 0xed, 0xe2, 0x0f, 0x15, 0xc8, 0x0a, 0x25, 0x89, 0xbd, 0x76, 0xd3, + 0x25, 0x3e, 0x0d, 0xf0, 0x47, 0xd4, 0x9a, 0x53, 0x86, 0xc6, 0x7a, 0xea, 0xf8, 0x23, 0xf4, 0x16, + 0x40, 0x80, 0xfd, 0x1e, 0xf6, 0xe9, 0x30, 0x31, 0x21, 0xb1, 0xa9, 0xde, 0x54, 0x0c, 0x8d, 0xf5, + 0x12, 0xc8, 0x25, 0xc8, 0x34, 0xad, 0x56, 0xc7, 0xf3, 0x99, 0xf3, 0xd8, 0xb8, 0xe8, 0x42, 0x17, + 0x20, 0x6b, 0xd9, 0xa1, 0xe7, 0x9b, 0xae, 0x43, 0x35, 0x5d, 0x30, 0x32, 0xb4, 0xbd, 0xed, 0x14, + 0x7f, 0x55, 0x00, 0x2d, 0xb2, 0x12, 0x7d, 0x11, 0x12, 0x01, 0x16, 0xd1, 0x92, 0x8f, 0xdb, 0x88, + 0x52, 0x1d, 0x87, 0x5b, 0x73, 0x06, 0x81, 0x11, 0xb4, 0xe5, 0x38, 0x9c, 0x62, 0xf1, 0xe8, 0xb2, + 0xe3, 0x10, 0xb4, 0xe5, 0x38, 0x68, 0x1d, 0x92, 0xc4, 0x7d, 0x54, 0xbf, 0xe1, 0xad, 0x1a, 0xc0, + 0x1f, 0x7a, 0x3d, 0xbc, 0x35, 0x67, 0x50, 0x20, 0xba, 0x03, 0x69, 0x46, 0x01, 0xbe, 0xbb, 0x17, + 0x63, 0x45, 0x18, 0x29, 0xb6, 0xe6, 0x0c, 0x0e, 0x26, 0xeb, 0x60, 0xc7, 0x15, 0x94, 0x8b, 0x5f, + 0x47, 0x77, 0x5c, 0x62, 0x05, 0x05, 0x92, 0x75, 0x02, 0xdc, 0xc4, 0x76, 0x48, 0x99, 0x36, 0x69, + 0x9d, 0x3a, 0x85, 0x90, 0x75, 0x18, 0x18, 0x6d, 0x40, 0x2a, 0x08, 0xfb, 0x4d, 0x9c, 0xcf, 0x50, + 0xa9, 0x42, 0xbc, 0x14, 0x41, 0x6c, 0xcd, 0x19, 0x0c, 0x8a, 0xee, 0x42, 0xd6, 0x6d, 0xdb, 0x3e, + 0xb6, 0x02, 0x9c, 0xcf, 0x52, 0xb1, 0xcb, 0xb1, 0x62, 0xdb, 0x1c, 0xb4, 0x35, 0x67, 0x44, 0x02, + 0xe8, 0xeb, 0xa0, 0x85, 0x3e, 0xc6, 0x26, 0xb5, 0x4e, 0x9b, 0x22, 0xdd, 0xf0, 0x31, 0xe6, 0x16, + 0x66, 0x43, 0xfe, 0x1b, 0x7d, 0x13, 0x80, 0x4a, 0x33, 0x9d, 0x81, 0x8a, 0xaf, 0x4c, 0x14, 0x17, + 0x7a, 0xd3, 0x15, 0x69, 0xa3, 0xf0, 0x0b, 0x05, 0x12, 0x75, 0x1c, 0x92, 0xf8, 0xee, 0x58, 0x3e, + 0x21, 0x2b, 0xd1, 0x2b, 0xc4, 0x8e, 0x69, 0x09, 0xc6, 0x4c, 0x8a, 0x6f, 0x86, 0xaf, 0x30, 0x78, + 0x39, 0x14, 0x59, 0x51, 0x1d, 0x64, 0xc5, 0x0d, 0x91, 0x15, 0x19, 0x3b, 0x2e, 0xc5, 0x27, 0xea, + 0xba, 0xdb, 0xea, 0x34, 0x45, 0x7a, 0x44, 0x5f, 0x81, 0x79, 0xfc, 0x1c, 0xdb, 0x5d, 0xae, 0x42, + 0x72, 0x9a, 0x0a, 0x20, 0x90, 0xe5, 0xb0, 0xf0, 0x0f, 0x05, 0x12, 0x65, 0xc7, 0x39, 0x09, 0x43, + 0xde, 0xa3, 0x99, 0xa0, 0x27, 0x4f, 0xa0, 0x4e, 0x9b, 0xe0, 0x14, 0x41, 0x0f, 0xc4, 0x3f, 0x4f, + 0xab, 0xff, 0xa5, 0x40, 0x92, 0x84, 0xd7, 0x6b, 0x60, 0xf6, 0x6d, 0x00, 0x49, 0x32, 0x31, 0x4d, + 0x52, 0xb3, 0x23, 0xa9, 0xe3, 0x1a, 0xfe, 0xa9, 0x02, 0x69, 0x96, 0x24, 0x4e, 0xc2, 0xf4, 0x61, + 0xdd, 0xd5, 0xe3, 0xe9, 0x9e, 0x98, 0x55, 0xf7, 0x9f, 0x27, 0x21, 0x49, 0xa3, 0xf7, 0x04, 0x34, + 0xbf, 0x0e, 0xc9, 0xa7, 0xbe, 0xd7, 0xe2, 0x3a, 0xcb, 0xa5, 0x50, 0x03, 0x3f, 0x0f, 0x6b, 0x9e, + 0x83, 0xf7, 0xbc, 0xc0, 0xa0, 0x18, 0x74, 0x15, 0xd4, 0xd0, 0xe3, 0x6a, 0x4e, 0x42, 0xaa, 0xa1, + 0x87, 0x0e, 0xe1, 0xfc, 0x40, 0x1f, 0xb3, 0x65, 0x75, 0xcc, 0x83, 0xbe, 0x49, 0x8f, 0x16, 0x5e, + 0x28, 0x6c, 0x4c, 0x4c, 0xbf, 0xa5, 0x48, 0xb3, 0x87, 0x56, 0x67, 0xb3, 0x5f, 0x26, 0x42, 0xac, + 0xa0, 0x5a, 0xb6, 0xc7, 0x47, 0xc8, 0x19, 0x6e, 0x7b, 0xed, 0x10, 0xb7, 0x59, 0x62, 0xd7, 0x0c, + 0xd1, 0x1c, 0xdd, 0xdb, 0xf4, 0x8c, 0x7b, 0x8b, 0xb6, 0x01, 0xac, 0x30, 0xf4, 0xdd, 0x83, 0x6e, + 0x88, 0x83, 0x7c, 0x86, 0xaa, 0x7b, 0x6d, 0xb2, 0xba, 0xe5, 0x08, 0xcb, 0xb4, 0x94, 0x84, 0x0b, + 0xdf, 0x83, 0xfc, 0x24, 0x6b, 0x62, 0x2a, 0xc0, 0x1b, 0xc3, 0x15, 0xe0, 0x04, 0x55, 0x07, 0x35, + 0x60, 0xe1, 0x3d, 0x58, 0x1a, 0x59, 0x3d, 0x66, 0xd6, 0x33, 0xf2, 0xac, 0x9a, 0x2c, 0xfe, 0x07, + 0x05, 0xd2, 0xec, 0xf4, 0x7a, 0x5d, 0x69, 0x74, 0xdc, 0xd0, 0xfe, 0x93, 0x0a, 0x29, 0x7a, 0x38, + 0xbd, 0xae, 0x86, 0x3d, 0x18, 0xe2, 0x18, 0x0b, 0x89, 0xeb, 0x93, 0x0b, 0x85, 0x69, 0x24, 0x1b, + 0xdd, 0xa4, 0xd4, 0xac, 0x9b, 0xf4, 0x5f, 0xb2, 0xe7, 0x53, 0x05, 0xb2, 0xa2, 0x1c, 0x39, 0x89, + 0x6d, 0xde, 0x18, 0x66, 0xff, 0x71, 0xce, 0xbc, 0x99, 0xd3, 0xe7, 0xf7, 0x55, 0xc8, 0x8a, 0x62, + 0xe8, 0x24, 0x74, 0xbf, 0x3a, 0x44, 0x11, 0x24, 0x4b, 0xf9, 0x58, 0xa2, 0x47, 0x51, 0xa2, 0x47, + 0x1c, 0x8a, 0x50, 0xe3, 0x4b, 0x83, 0x84, 0xc6, 0x78, 0xb1, 0x3c, 0x02, 0x24, 0x3c, 0x9a, 0x98, + 0xe5, 0x52, 0xaf, 0x10, 0x22, 0x5a, 0x54, 0xd0, 0xbd, 0x6e, 0x7b, 0x50, 0x8b, 0x09, 0x8f, 0xd2, + 0xf4, 0x9a, 0xf4, 0x35, 0x0c, 0x91, 0xcd, 0x34, 0x24, 0x0f, 0x3c, 0xa7, 0x5f, 0xfc, 0xbb, 0x02, + 0xa7, 0xc7, 0x38, 0x3c, 0x52, 0x31, 0x28, 0x33, 0x56, 0x0c, 0x37, 0x21, 0x4b, 0x2f, 0xbb, 0x2f, + 0xad, 0x32, 0x32, 0x14, 0xc6, 0x2a, 0x13, 0x7e, 0x63, 0x7e, 0x79, 0x55, 0xc5, 0x81, 0xe5, 0x10, + 0xad, 0x41, 0x32, 0xec, 0x77, 0xd8, 0x15, 0x6b, 0x71, 0xe8, 0xee, 0xfb, 0x88, 0xd8, 0xd7, 0xe8, + 0x77, 0xb0, 0x41, 0x11, 0x03, 0xfb, 0x53, 0xf4, 0x06, 0xc9, 0x1a, 0xc5, 0x9f, 0x9e, 0x82, 0x79, + 0xc9, 0x66, 0x54, 0x85, 0xf9, 0x0f, 0x03, 0xaf, 0x6d, 0x7a, 0x07, 0x1f, 0x92, 0x1b, 0x15, 0x33, + 0xf7, 0xad, 0xf8, 0x20, 0xa7, 0xbf, 0x77, 0x29, 0x70, 0x6b, 0xce, 0x00, 0x22, 0xc7, 0x5a, 0xa8, + 0x0c, 0xb4, 0x65, 0x5a, 0xbe, 0x6f, 0xf5, 0xb9, 0xfd, 0xab, 0x53, 0x26, 0x29, 0x13, 0x1c, 0xb9, + 0xae, 0x10, 0x29, 0xda, 0x60, 0xff, 0xe6, 0xb8, 0x2d, 0x37, 0x74, 0xa3, 0x3b, 0xe7, 0xa4, 0x19, + 0xf6, 0x04, 0x8e, 0xcc, 0x10, 0x09, 0xa1, 0x5b, 0x90, 0x0c, 0xf1, 0x73, 0x41, 0xa3, 0x8b, 0x13, + 0x84, 0x49, 0xca, 0x27, 0x57, 0x49, 0x02, 0x45, 0xef, 0x92, 0xa0, 0xee, 0xb6, 0x43, 0xec, 0xf3, + 0x3a, 0x64, 0x65, 0x82, 0x54, 0x85, 0xa1, 0xb6, 0xe6, 0x0c, 0x21, 0x40, 0x97, 0xf3, 0xb1, 0xb8, + 0x4e, 0x4e, 0x5c, 0xce, 0xc7, 0xf4, 0x86, 0x4c, 0xa0, 0x85, 0xcf, 0x14, 0x80, 0xc1, 0x1e, 0xa2, + 0x35, 0x48, 0xb5, 0x3d, 0x07, 0x07, 0x79, 0x85, 0x46, 0x92, 0x1c, 0x75, 0xc6, 0x56, 0x83, 0xe6, + 0x13, 0x06, 0x38, 0x66, 0x15, 0x2b, 0x73, 0x32, 0x71, 0x0c, 0x4e, 0x26, 0x67, 0xe3, 0x64, 0xe1, + 0xf7, 0x0a, 0x68, 0x91, 0x57, 0xa7, 0x5a, 0x75, 0xbf, 0xfc, 0xe6, 0x58, 0xf5, 0x57, 0x05, 0xb4, + 0x88, 0x69, 0x51, 0xdc, 0x29, 0xb3, 0xc7, 0x9d, 0x2a, 0xc5, 0xdd, 0x31, 0xef, 0x50, 0xb2, 0xad, + 0xc9, 0x63, 0xd8, 0x9a, 0x9a, 0xd1, 0xd6, 0xdf, 0x2a, 0x90, 0x24, 0x81, 0x81, 0xae, 0x0d, 0x3b, + 0x6f, 0x39, 0xa6, 0x56, 0x7a, 0x33, 0xbc, 0xf7, 0x17, 0x05, 0x32, 0x3c, 0x68, 0xff, 0x17, 0x7c, + 0xe7, 0x63, 0x3c, 0xd5, 0x77, 0xa2, 0x3e, 0x79, 0x13, 0x7c, 0x17, 0x9d, 0xcf, 0x0f, 0x21, 0xc3, + 0xf3, 0x60, 0xcc, 0xf1, 0x7e, 0x13, 0x32, 0x98, 0xe5, 0xd8, 0x98, 0x1b, 0x80, 0xfc, 0x58, 0x20, + 0x60, 0x45, 0x1b, 0x32, 0x3c, 0x01, 0x91, 0xa2, 0xa8, 0x4d, 0x8e, 0x0a, 0x65, 0xac, 0xdc, 0x11, + 0x29, 0x8a, 0x8e, 0x1f, 0x63, 0x91, 0x47, 0x90, 0x25, 0xf2, 0xa4, 0x3c, 0x19, 0xb0, 0x49, 0x91, + 0x2a, 0x10, 0xb2, 0x27, 0xdd, 0x8e, 0x33, 0xdb, 0xde, 0x73, 0x60, 0x39, 0x2c, 0xfe, 0x86, 0x94, + 0xc6, 0x3c, 0x02, 0xd1, 0xff, 0x4b, 0xff, 0xa2, 0x9f, 0x8d, 0x09, 0x51, 0xfe, 0x3f, 0x7a, 0x6c, + 0x05, 0x74, 0xcc, 0xba, 0xe3, 0x0e, 0xcc, 0xbb, 0xed, 0xc0, 0xa4, 0x7f, 0x23, 0xf1, 0x7f, 0xa5, + 0x27, 0xae, 0xad, 0xb9, 0xed, 0x60, 0xcf, 0xc7, 0xbd, 0x6d, 0x07, 0x55, 0x86, 0x2a, 0xc6, 0x14, + 0x25, 0xe6, 0xdb, 0x31, 0x52, 0x53, 0xaf, 0xeb, 0xc6, 0x2c, 0xe5, 0xde, 0x94, 0x77, 0x1a, 0xe1, + 0x10, 0xf9, 0x9d, 0xe6, 0x03, 0x80, 0x81, 0xc6, 0xc7, 0xac, 0xf9, 0xce, 0x41, 0xda, 0x7b, 0xfa, + 0x34, 0xc0, 0xcc, 0x8b, 0x29, 0x83, 0xb7, 0x8a, 0xff, 0xe4, 0xd7, 0x18, 0xea, 0xab, 0x2b, 0x90, + 0xe8, 0x78, 0x41, 0x0c, 0xd3, 0x44, 0x61, 0x4d, 0x86, 0x11, 0xe2, 0x29, 0x8a, 0x79, 0x6a, 0x24, + 0x19, 0x25, 0x26, 0xbb, 0x6f, 0xc6, 0x90, 0x42, 0xb7, 0x61, 0x21, 0x72, 0x1f, 0x51, 0x27, 0x35, + 0x51, 0x1d, 0xe0, 0xce, 0xdb, 0xf3, 0x02, 0xa2, 0x81, 0x83, 0x3b, 0xe1, 0x21, 0x2d, 0x8e, 0x52, + 0x06, 0x6b, 0x8c, 0xf8, 0x34, 0x33, 0xee, 0x53, 0x6e, 0xfa, 0xe7, 0xee, 0xd3, 0xc7, 0x90, 0xe1, + 0x56, 0x9c, 0xb0, 0x43, 0x5b, 0x90, 0xdc, 0x0f, 0xb0, 0x8f, 0x16, 0xa3, 0xb8, 0xd3, 0x68, 0x80, + 0x15, 0x20, 0xdb, 0x0d, 0xb0, 0xdf, 0xb6, 0x5a, 0xc2, 0x73, 0x51, 0x1b, 0x7d, 0x2d, 0xe6, 0xd0, + 0x28, 0x94, 0xd8, 0x5b, 0x68, 0x49, 0xbc, 0x85, 0x52, 0x3d, 0xe8, 0x63, 0xa9, 0xa4, 0x46, 0xf1, + 0xdf, 0x2a, 0x64, 0xf6, 0x7c, 0x8f, 0xd6, 0x88, 0xa3, 0x4b, 0x22, 0x48, 0x4a, 0xcb, 0xd1, 0xdf, + 0xe8, 0x32, 0x40, 0xa7, 0x7b, 0xd0, 0x74, 0x6d, 0xfa, 0xc4, 0xc8, 0xd8, 0xa2, 0xb1, 0x9e, 0xf7, + 0x71, 0x9f, 0x0c, 0x07, 0xd8, 0xf6, 0x31, 0x7b, 0x81, 0x4c, 0xb2, 0x61, 0xd6, 0x43, 0x86, 0xd7, + 0x20, 0x67, 0x75, 0xc3, 0x43, 0xf3, 0x63, 0x7c, 0x70, 0xe8, 0x79, 0x47, 0x66, 0xd7, 0x6f, 0xf2, + 0xbf, 0xec, 0x16, 0x49, 0xff, 0x63, 0xd6, 0xbd, 0xef, 0x37, 0xd1, 0x4d, 0x38, 0x33, 0x84, 0x6c, + 0xe1, 0xf0, 0xd0, 0x73, 0x82, 0x7c, 0x7a, 0x35, 0xb1, 0xa6, 0x19, 0x48, 0x42, 0x3f, 0x64, 0x23, + 0xe8, 0x1b, 0x70, 0x91, 0x3f, 0x94, 0x39, 0xd8, 0xb2, 0x43, 0xb7, 0x67, 0x85, 0xd8, 0x0c, 0x0f, + 0x7d, 0x1c, 0x1c, 0x7a, 0x4d, 0x87, 0x96, 0xce, 0x9a, 0x71, 0x81, 0x41, 0xaa, 0x11, 0xa2, 0x21, + 0x00, 0x23, 0x9b, 0x98, 0x7d, 0x85, 0x4d, 0x24, 0xa2, 0x52, 0x9a, 0xd5, 0x5e, 0x2e, 0x3a, 0xc8, + 0xb5, 0x3f, 0x4a, 0xc0, 0xb9, 0x7d, 0xd2, 0xb2, 0x0e, 0x9a, 0x98, 0x3b, 0xe2, 0x9e, 0x8b, 0x9b, + 0x4e, 0x80, 0x6e, 0xf2, 0xed, 0x57, 0xf8, 0x9f, 0x21, 0xa3, 0xf3, 0xd5, 0x43, 0xdf, 0x6d, 0x3f, + 0xa3, 0x65, 0x05, 0x77, 0xce, 0xbd, 0x98, 0xed, 0x55, 0x67, 0x90, 0x1e, 0xdd, 0xfc, 0xa7, 0x13, + 0x36, 0x9f, 0x31, 0xeb, 0xb6, 0xc4, 0xed, 0x78, 0xd5, 0x4b, 0xe5, 0x31, 0xf7, 0xc4, 0xba, 0xec, + 0xbb, 0xd3, 0x5d, 0x96, 0x9c, 0x41, 0xf5, 0xc9, 0x0e, 0x2d, 0x94, 0x00, 0x8d, 0xeb, 0xc1, 0x1e, + 0x7c, 0x99, 0x39, 0x0a, 0xe5, 0x92, 0x68, 0x16, 0x3f, 0x51, 0x61, 0xa9, 0xca, 0x1f, 0xcb, 0xeb, + 0xdd, 0x56, 0xcb, 0xf2, 0xfb, 0x63, 0x21, 0x31, 0xfe, 0x3a, 0x35, 0xfa, 0x36, 0xae, 0x49, 0x6f, + 0xe3, 0xc3, 0x94, 0x4a, 0xbe, 0x0a, 0xa5, 0xee, 0xc2, 0xbc, 0x65, 0xdb, 0x38, 0x08, 0xe4, 0x02, + 0x6d, 0x9a, 0x2c, 0x08, 0xf8, 0x18, 0x1f, 0xd3, 0xaf, 0xc2, 0xc7, 0xbf, 0x29, 0x83, 0xef, 0x14, + 0xf8, 0x3b, 0xfa, 0x3b, 0x43, 0x25, 0xed, 0x95, 0x89, 0xef, 0xd8, 0xfc, 0x61, 0x5d, 0x2a, 0x71, + 0xd7, 0x21, 0x2b, 0x9e, 0xb6, 0xa7, 0x7d, 0xd2, 0x10, 0x81, 0x8a, 0x2d, 0xf1, 0x41, 0x03, 0x99, + 0x04, 0x5d, 0x84, 0xf3, 0x95, 0xad, 0x72, 0xed, 0xbe, 0x6e, 0x36, 0x9e, 0xec, 0xe9, 0xe6, 0x7e, + 0xad, 0xbe, 0xa7, 0x57, 0xb6, 0xef, 0x6d, 0xeb, 0xd5, 0xdc, 0x1c, 0x5a, 0x86, 0x25, 0x79, 0x70, + 0x6f, 0xbf, 0x91, 0x53, 0xd0, 0x39, 0x40, 0x72, 0x67, 0x55, 0xdf, 0xd1, 0x1b, 0x7a, 0x4e, 0x45, + 0x67, 0xe1, 0xb4, 0xdc, 0x5f, 0xd9, 0xd1, 0xcb, 0x46, 0x2e, 0x51, 0xec, 0x41, 0x56, 0x28, 0x41, + 0xae, 0xd8, 0x84, 0xca, 0xbc, 0xa0, 0xbd, 0x1c, 0xa3, 0x67, 0xa9, 0x6a, 0x85, 0x16, 0x3b, 0x5d, + 0x28, 0xb4, 0xf0, 0x55, 0xd0, 0xa2, 0xae, 0x57, 0xf9, 0x53, 0xa8, 0x58, 0x23, 0x66, 0x46, 0x5f, + 0x57, 0x0c, 0x3f, 0xe1, 0x2b, 0x71, 0x4f, 0xf8, 0xc3, 0x1f, 0x01, 0xa8, 0x23, 0x1f, 0x01, 0x14, + 0x7f, 0xa0, 0xc0, 0xbc, 0xf4, 0xf7, 0xf2, 0xc9, 0x9e, 0x48, 0xe8, 0x0b, 0xb0, 0xe4, 0xe3, 0xa6, + 0x45, 0xae, 0xa6, 0x26, 0x07, 0x24, 0x28, 0x60, 0x51, 0x74, 0xef, 0xb2, 0xa3, 0xcb, 0x06, 0x18, + 0xcc, 0x2c, 0x7f, 0x76, 0xa0, 0x8c, 0x7f, 0x76, 0x70, 0x09, 0x34, 0x07, 0x37, 0xc9, 0x8d, 0x17, + 0xfb, 0xc2, 0xa0, 0xa8, 0x63, 0xe8, 0xa3, 0x84, 0xc4, 0xf0, 0x47, 0x09, 0xfb, 0x90, 0xad, 0x7a, + 0xb6, 0xde, 0xc3, 0xed, 0x10, 0xdd, 0x18, 0x62, 0xe6, 0x79, 0xc9, 0x42, 0x01, 0x91, 0xc8, 0x78, + 0x09, 0xd8, 0x39, 0x15, 0x1c, 0xf2, 0x15, 0x17, 0x8c, 0x41, 0xc7, 0xf5, 0xcf, 0x54, 0xd0, 0xa2, + 0x1b, 0x1a, 0x21, 0xd7, 0xa3, 0xf2, 0xce, 0x3e, 0xa7, 0x4b, 0x6d, 0x7f, 0x67, 0x27, 0x37, 0x47, + 0xc8, 0x25, 0x75, 0x6e, 0xee, 0xee, 0xee, 0xe8, 0xe5, 0x1a, 0x23, 0x9d, 0xd4, 0xbf, 0x5d, 0x6b, + 0xe8, 0xf7, 0x75, 0x23, 0xa7, 0x8e, 0x4c, 0xb2, 0xb3, 0x5b, 0xbb, 0x9f, 0x4b, 0x10, 0x26, 0x4a, + 0x9d, 0xd5, 0xdd, 0xfd, 0xcd, 0x1d, 0x3d, 0x97, 0x1c, 0xe9, 0xae, 0x37, 0x8c, 0xed, 0xda, 0xfd, + 0x5c, 0x0a, 0x9d, 0x81, 0x9c, 0xbc, 0xe4, 0x93, 0x86, 0x5e, 0xcf, 0xa5, 0x47, 0x26, 0xae, 0x96, + 0x1b, 0x7a, 0x2e, 0x83, 0x0a, 0x70, 0x4e, 0xea, 0x24, 0xf7, 0x05, 0x73, 0x77, 0xf3, 0x81, 0x5e, + 0x69, 0xe4, 0xb2, 0xe8, 0x02, 0x9c, 0x1d, 0x1d, 0x2b, 0x1b, 0x46, 0xf9, 0x49, 0x4e, 0x1b, 0x99, + 0xab, 0xa1, 0x7f, 0xa7, 0x91, 0x83, 0x91, 0xb9, 0xb8, 0x45, 0x66, 0xa5, 0xd6, 0xc8, 0xcd, 0xa3, + 0xf3, 0xb0, 0x3c, 0x62, 0x15, 0x1d, 0x58, 0x18, 0x9d, 0xc9, 0xd0, 0xf5, 0xdc, 0xa9, 0xeb, 0x9f, + 0x28, 0xb0, 0x20, 0xfb, 0x02, 0x5d, 0x81, 0xd5, 0xea, 0x6e, 0xc5, 0xd4, 0x1f, 0xe9, 0xb5, 0x86, + 0xd8, 0x83, 0xca, 0xfe, 0x43, 0xbd, 0xd6, 0xa8, 0x9b, 0x2c, 0x44, 0x49, 0x70, 0x4f, 0x43, 0x3d, + 0x2e, 0x37, 0x2a, 0x5b, 0x7a, 0x35, 0xa7, 0xa0, 0xab, 0x50, 0x9c, 0x88, 0xda, 0xaf, 0x09, 0x9c, + 0xba, 0x79, 0xe3, 0x97, 0x2f, 0x56, 0x94, 0xdf, 0xbd, 0x58, 0x51, 0xfe, 0xf8, 0x62, 0x45, 0xf9, + 0xc9, 0x9f, 0x57, 0xe6, 0xe0, 0xb4, 0x83, 0x7b, 0x82, 0x2d, 0x56, 0xc7, 0x2d, 0xf5, 0x6e, 0xed, + 0x29, 0x1f, 0x24, 0x4b, 0x77, 0x7b, 0xb7, 0x0e, 0xd2, 0x34, 0x3f, 0x7e, 0xf9, 0x3f, 0x01, 0x00, + 0x00, 0xff, 0xff, 0xf1, 0x10, 0x5f, 0xc6, 0x92, 0x26, 0x00, 0x00, } func (m *Snapshot) Marshal() (dAtA []byte, err error) { @@ -3509,9 +3442,9 @@ func (m *Snapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.PresenceMap) > 0 { - for k := range m.PresenceMap { - v := m.PresenceMap[k] + if len(m.Presences) > 0 { + for k := range m.Presences { + v := m.Presences[k] baseI := i if v != nil { { @@ -6335,24 +6268,17 @@ func (m *PresenceChange) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Presence) > 0 { - for k := range m.Presence { - v := m.Presence[k] - baseI := i - i -= len(v) - copy(dAtA[i:], v) - i = encodeVarintResources(dAtA, i, uint64(len(v))) - i-- - dAtA[i] = 0x12 - i -= len(k) - copy(dAtA[i:], k) - i = encodeVarintResources(dAtA, i, uint64(len(k))) - i-- - dAtA[i] = 0xa - i = encodeVarintResources(dAtA, i, uint64(baseI-i)) - i-- - dAtA[i] = 0x12 + if m.Presence != nil { + { + size, err := m.Presence.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintResources(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x12 } if m.Type != 0 { i = encodeVarintResources(dAtA, i, uint64(m.Type)) @@ -6408,52 +6334,6 @@ func (m *Presence) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *Client) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *Client) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *Client) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if m.Presence != nil { - { - size, err := m.Presence.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintResources(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if len(m.Id) > 0 { - i -= len(m.Id) - copy(dAtA[i:], m.Id) - i = encodeVarintResources(dAtA, i, uint64(len(m.Id))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - func (m *Checkpoint) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -6608,22 +6488,10 @@ func (m *DocEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.DocumentId) > 0 { - i -= len(m.DocumentId) - copy(dAtA[i:], m.DocumentId) - i = encodeVarintResources(dAtA, i, uint64(len(m.DocumentId))) - i-- - dAtA[i] = 0x1a - } - if m.Publisher != nil { - { - size, err := m.Publisher.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintResources(dAtA, i, uint64(size)) - } + if len(m.Publisher) > 0 { + i -= len(m.Publisher) + copy(dAtA[i:], m.Publisher) + i = encodeVarintResources(dAtA, i, uint64(len(m.Publisher))) i-- dAtA[i] = 0x12 } @@ -6656,8 +6524,8 @@ func (m *Snapshot) Size() (n int) { l = m.Root.Size() n += 1 + l + sovResources(uint64(l)) } - if len(m.PresenceMap) > 0 { - for k, v := range m.PresenceMap { + if len(m.Presences) > 0 { + for k, v := range m.Presences { _ = k _ = v l = 0 @@ -7873,13 +7741,9 @@ func (m *PresenceChange) Size() (n int) { if m.Type != 0 { n += 1 + sovResources(uint64(m.Type)) } - if len(m.Presence) > 0 { - for k, v := range m.Presence { - _ = k - _ = v - mapEntrySize := 1 + len(k) + sovResources(uint64(len(k))) + 1 + len(v) + sovResources(uint64(len(v))) - n += mapEntrySize + 1 + sovResources(uint64(mapEntrySize)) - } + if m.Presence != nil { + l = m.Presence.Size() + n += 1 + l + sovResources(uint64(l)) } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) @@ -7907,26 +7771,6 @@ func (m *Presence) Size() (n int) { return n } -func (m *Client) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Id) - if l > 0 { - n += 1 + l + sovResources(uint64(l)) - } - if m.Presence != nil { - l = m.Presence.Size() - n += 1 + l + sovResources(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - func (m *Checkpoint) Size() (n int) { if m == nil { return 0 @@ -7998,11 +7842,7 @@ func (m *DocEvent) Size() (n int) { if m.Type != 0 { n += 1 + sovResources(uint64(m.Type)) } - if m.Publisher != nil { - l = m.Publisher.Size() - n += 1 + l + sovResources(uint64(l)) - } - l = len(m.DocumentId) + l = len(m.Publisher) if l > 0 { n += 1 + l + sovResources(uint64(l)) } @@ -8085,7 +7925,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PresenceMap", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Presences", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -8112,8 +7952,8 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.PresenceMap == nil { - m.PresenceMap = make(map[string]*Presence) + if m.Presences == nil { + m.Presences = make(map[string]*Presence) } var mapkey string var mapvalue *Presence @@ -8210,7 +8050,7 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error { iNdEx += skippy } } - m.PresenceMap[mapkey] = mapvalue + m.Presences[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex @@ -15701,102 +15541,11 @@ func (m *PresenceChange) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } if m.Presence == nil { - m.Presence = make(map[string]string) + m.Presence = &Presence{} } - var mapkey string - var mapvalue string - for iNdEx < postIndex { - entryPreIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowResources - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - if fieldNum == 1 { - var stringLenmapkey uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowResources - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapkey |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthResources - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey < 0 { - return ErrInvalidLengthResources - } - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey - } else if fieldNum == 2 { - var stringLenmapvalue uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowResources - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLenmapvalue |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLenmapvalue := int(stringLenmapvalue) - if intStringLenmapvalue < 0 { - return ErrInvalidLengthResources - } - postStringIndexmapvalue := iNdEx + intStringLenmapvalue - if postStringIndexmapvalue < 0 { - return ErrInvalidLengthResources - } - if postStringIndexmapvalue > l { - return io.ErrUnexpectedEOF - } - mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) - iNdEx = postStringIndexmapvalue - } else { - iNdEx = entryPreIndex - skippy, err := skipResources(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthResources - } - if (iNdEx + skippy) > postIndex { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } + if err := m.Presence.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err } - m.Presence[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex @@ -15998,127 +15747,6 @@ func (m *Presence) Unmarshal(dAtA []byte) error { } return nil } -func (m *Client) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowResources - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: Client: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: Client: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowResources - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthResources - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthResources - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Id = append(m.Id[:0], dAtA[iNdEx:postIndex]...) - if m.Id == nil { - m.Id = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Presence", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowResources - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthResources - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthResources - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Presence == nil { - m.Presence = &Presence{} - } - if err := m.Presence.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipResources(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthResources - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *Checkpoint) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -16508,7 +16136,7 @@ func (m *DocEvent) Unmarshal(dAtA []byte) error { if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Publisher", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowResources @@ -16518,59 +16146,25 @@ func (m *DocEvent) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthResources } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthResources } if postIndex > l { return io.ErrUnexpectedEOF } + m.Publisher = append(m.Publisher[:0], dAtA[iNdEx:postIndex]...) if m.Publisher == nil { - m.Publisher = &Client{} - } - if err := m.Publisher.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DocumentId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowResources - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthResources - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthResources - } - if postIndex > l { - return io.ErrUnexpectedEOF + m.Publisher = []byte{} } - m.DocumentId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex diff --git a/api/yorkie/v1/resources.proto b/api/yorkie/v1/resources.proto index 5532ce518..53e23d110 100644 --- a/api/yorkie/v1/resources.proto +++ b/api/yorkie/v1/resources.proto @@ -30,7 +30,7 @@ option java_package = "dev.yorkie.api.v1"; ///////////////////////////////////////// message Snapshot { JSONElement root = 1; - map presence_map = 2; + map presences = 2; } ///////////////////////////////////////// @@ -294,20 +294,16 @@ message PresenceChange { CHANGE_TYPE_UNSPECIFIED = 0; CHANGE_TYPE_PUT = 1; CHANGE_TYPE_DELETE = 2; + CHANGE_TYPE_CLEAR = 3; } ChangeType type = 1; - map presence = 2; + Presence presence = 2; } message Presence { map data = 1; } -message Client { - bytes id = 1; - Presence presence = 2; -} - message Checkpoint { int64 server_seq = 1 [jstype = JS_STRING]; uint32 client_seq = 2; @@ -346,11 +342,9 @@ enum DocEventType { DOC_EVENT_TYPE_DOCUMENTS_CHANGED = 0; DOC_EVENT_TYPE_DOCUMENTS_WATCHED = 1; DOC_EVENT_TYPE_DOCUMENTS_UNWATCHED = 2; - DOC_EVENT_TYPE_PRESENCE_CHANGED = 3; } message DocEvent { DocEventType type = 1; - Client publisher = 2; - string document_id = 3; + bytes publisher = 2; } diff --git a/api/yorkie/v1/yorkie.pb.go b/api/yorkie/v1/yorkie.pb.go index 543a706b5..0bd73bedd 100644 --- a/api/yorkie/v1/yorkie.pb.go +++ b/api/yorkie/v1/yorkie.pb.go @@ -467,7 +467,7 @@ func (m *DetachDocumentResponse) GetChangePack() *ChangePack { } type WatchDocumentRequest struct { - Client *Client `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"` + ClientId []byte `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` DocumentId string `protobuf:"bytes,2,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -507,9 +507,9 @@ func (m *WatchDocumentRequest) XXX_DiscardUnknown() { var xxx_messageInfo_WatchDocumentRequest proto.InternalMessageInfo -func (m *WatchDocumentRequest) GetClient() *Client { +func (m *WatchDocumentRequest) GetClientId() []byte { if m != nil { - return m.Client + return m.ClientId } return nil } @@ -610,10 +610,10 @@ func (*WatchDocumentResponse) XXX_OneofWrappers() []interface{} { } type WatchDocumentResponse_Initialization struct { - Peers []*Client `protobuf:"bytes,1,rep,name=peers,proto3" json:"peers,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + ClientIds [][]byte `protobuf:"bytes,1,rep,name=client_ids,json=clientIds,proto3" json:"client_ids,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *WatchDocumentResponse_Initialization) Reset() { *m = WatchDocumentResponse_Initialization{} } @@ -649,9 +649,9 @@ func (m *WatchDocumentResponse_Initialization) XXX_DiscardUnknown() { var xxx_messageInfo_WatchDocumentResponse_Initialization proto.InternalMessageInfo -func (m *WatchDocumentResponse_Initialization) GetPeers() []*Client { +func (m *WatchDocumentResponse_Initialization) GetClientIds() [][]byte { if m != nil { - return m.Peers + return m.ClientIds } return nil } @@ -900,100 +900,6 @@ func (m *PushPullChangesResponse) GetChangePack() *ChangePack { return nil } -type UpdatePresenceRequest struct { - Client *Client `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"` - DocumentId string `protobuf:"bytes,2,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdatePresenceRequest) Reset() { *m = UpdatePresenceRequest{} } -func (m *UpdatePresenceRequest) String() string { return proto.CompactTextString(m) } -func (*UpdatePresenceRequest) ProtoMessage() {} -func (*UpdatePresenceRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_40070c858814ab24, []int{14} -} -func (m *UpdatePresenceRequest) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *UpdatePresenceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_UpdatePresenceRequest.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *UpdatePresenceRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdatePresenceRequest.Merge(m, src) -} -func (m *UpdatePresenceRequest) XXX_Size() int { - return m.Size() -} -func (m *UpdatePresenceRequest) XXX_DiscardUnknown() { - xxx_messageInfo_UpdatePresenceRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdatePresenceRequest proto.InternalMessageInfo - -func (m *UpdatePresenceRequest) GetClient() *Client { - if m != nil { - return m.Client - } - return nil -} - -func (m *UpdatePresenceRequest) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -type UpdatePresenceResponse struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdatePresenceResponse) Reset() { *m = UpdatePresenceResponse{} } -func (m *UpdatePresenceResponse) String() string { return proto.CompactTextString(m) } -func (*UpdatePresenceResponse) ProtoMessage() {} -func (*UpdatePresenceResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_40070c858814ab24, []int{15} -} -func (m *UpdatePresenceResponse) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *UpdatePresenceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_UpdatePresenceResponse.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *UpdatePresenceResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdatePresenceResponse.Merge(m, src) -} -func (m *UpdatePresenceResponse) XXX_Size() int { - return m.Size() -} -func (m *UpdatePresenceResponse) XXX_DiscardUnknown() { - xxx_messageInfo_UpdatePresenceResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdatePresenceResponse proto.InternalMessageInfo - func init() { proto.RegisterType((*ActivateClientRequest)(nil), "yorkie.v1.ActivateClientRequest") proto.RegisterType((*ActivateClientResponse)(nil), "yorkie.v1.ActivateClientResponse") @@ -1010,59 +916,54 @@ func init() { proto.RegisterType((*RemoveDocumentResponse)(nil), "yorkie.v1.RemoveDocumentResponse") proto.RegisterType((*PushPullChangesRequest)(nil), "yorkie.v1.PushPullChangesRequest") proto.RegisterType((*PushPullChangesResponse)(nil), "yorkie.v1.PushPullChangesResponse") - proto.RegisterType((*UpdatePresenceRequest)(nil), "yorkie.v1.UpdatePresenceRequest") - proto.RegisterType((*UpdatePresenceResponse)(nil), "yorkie.v1.UpdatePresenceResponse") } func init() { proto.RegisterFile("yorkie/v1/yorkie.proto", fileDescriptor_40070c858814ab24) } var fileDescriptor_40070c858814ab24 = []byte{ - // 712 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0xdd, 0x6e, 0xd3, 0x4c, - 0x10, 0xcd, 0x7e, 0xfd, 0x51, 0x33, 0xf9, 0x1a, 0xe8, 0x16, 0xbb, 0x21, 0x15, 0x69, 0x30, 0x17, - 0x14, 0x55, 0x4a, 0x49, 0x2b, 0x15, 0x21, 0xae, 0xda, 0x06, 0xa9, 0x15, 0x12, 0x44, 0xe6, 0xa7, - 0x6a, 0x25, 0x64, 0xb9, 0xf6, 0x94, 0x58, 0x71, 0xbd, 0xc6, 0x76, 0x2c, 0x85, 0x77, 0x80, 0x6b, - 0xde, 0x81, 0xb7, 0xe0, 0x8a, 0x4b, 0xde, 0x00, 0x14, 0x5e, 0x04, 0xc5, 0xeb, 0x86, 0xac, 0xb3, - 0xa4, 0xa1, 0x52, 0x04, 0x77, 0xf6, 0xee, 0x9c, 0x33, 0x67, 0x8e, 0x77, 0x76, 0x0c, 0x6a, 0x97, - 0x05, 0x6d, 0x07, 0x37, 0xe3, 0xfa, 0x26, 0x7f, 0xaa, 0xf9, 0x01, 0x8b, 0x18, 0xcd, 0xa7, 0x6f, - 0x71, 0xbd, 0x7c, 0xf3, 0x57, 0x48, 0x80, 0x21, 0xeb, 0x04, 0x16, 0x86, 0x3c, 0x4a, 0xdb, 0x01, - 0x65, 0xd7, 0x8a, 0x9c, 0xd8, 0x8c, 0x70, 0xdf, 0x75, 0xd0, 0x8b, 0x74, 0x7c, 0xdb, 0xc1, 0x30, - 0xa2, 0xb7, 0x00, 0xac, 0x64, 0xc1, 0x68, 0x63, 0xb7, 0x44, 0xaa, 0x64, 0x3d, 0xaf, 0xe7, 0xf9, - 0xca, 0x13, 0xec, 0x6a, 0x2f, 0x40, 0xcd, 0xe2, 0x42, 0x9f, 0x79, 0x21, 0x5e, 0x02, 0xa4, 0xab, - 0x90, 0xbe, 0x18, 0x8e, 0x5d, 0xfa, 0xaf, 0x4a, 0xd6, 0xff, 0xd7, 0x17, 0xf8, 0xc2, 0xa1, 0xad, - 0xed, 0xc0, 0x4a, 0x03, 0x4d, 0xa9, 0x1e, 0x01, 0x47, 0x32, 0xb8, 0x07, 0x50, 0x1a, 0xc5, 0xa5, - 0x7a, 0xc6, 0x02, 0x5d, 0x50, 0x76, 0xa3, 0xc8, 0xb4, 0x5a, 0x0d, 0x66, 0x75, 0xce, 0x27, 0x4c, - 0x47, 0x77, 0xa0, 0x60, 0xb5, 0x4c, 0xef, 0x0d, 0x1a, 0xbe, 0x69, 0xb5, 0x93, 0x2a, 0x0a, 0x5b, - 0x4a, 0x6d, 0x60, 0x78, 0x6d, 0x3f, 0xd9, 0x6d, 0x9a, 0x56, 0x5b, 0x07, 0x6b, 0xf0, 0xac, 0x7d, - 0x20, 0xa0, 0x66, 0xd3, 0x4d, 0xa0, 0x92, 0xae, 0x41, 0xc1, 0x4e, 0x01, 0x17, 0xae, 0xe5, 0x75, - 0xb8, 0x58, 0x1a, 0x15, 0x34, 0x33, 0xa9, 0xa0, 0xcf, 0x04, 0x94, 0x06, 0xfe, 0x71, 0xfd, 0xd3, - 0xd2, 0x43, 0xb7, 0x41, 0x0d, 0xf0, 0x9c, 0xc5, 0x68, 0x38, 0x67, 0x86, 0xc7, 0x22, 0xc3, 0x4c, - 0xdc, 0x42, 0xbb, 0x34, 0x5b, 0x25, 0xeb, 0x0b, 0xfa, 0x32, 0xdf, 0x3d, 0x3c, 0x7b, 0xca, 0xa2, - 0xdd, 0x74, 0x4b, 0x63, 0xa0, 0x66, 0x6b, 0x98, 0xec, 0x28, 0x5e, 0xf5, 0x33, 0x9e, 0xc2, 0x8d, - 0x23, 0x33, 0x1a, 0xf5, 0xec, 0x1e, 0xcc, 0x73, 0xf2, 0x24, 0x55, 0x61, 0x6b, 0x69, 0x98, 0x8a, - 0x1f, 0xca, 0x34, 0xe0, 0x52, 0x07, 0xb5, 0x1e, 0x01, 0x25, 0x93, 0x24, 0x2d, 0xea, 0x18, 0x8a, - 0x8e, 0xe7, 0x44, 0x8e, 0xe9, 0x3a, 0xef, 0xcc, 0xc8, 0x61, 0x5e, 0x9a, 0x6d, 0x73, 0x28, 0x9b, - 0x14, 0x59, 0x3b, 0x14, 0x60, 0x07, 0x39, 0x3d, 0x43, 0x44, 0x37, 0x60, 0x0e, 0xe3, 0xbe, 0x7e, - 0x6e, 0xc5, 0xf2, 0x10, 0x63, 0x83, 0x59, 0x8f, 0xfb, 0x5b, 0x07, 0x39, 0x9d, 0xc7, 0x94, 0x1f, - 0x42, 0x51, 0x24, 0xa4, 0x77, 0x61, 0xce, 0x47, 0x0c, 0xc2, 0x12, 0xa9, 0xce, 0xc8, 0xcb, 0xe7, - 0xfb, 0x7b, 0xf3, 0x30, 0x7b, 0xca, 0xec, 0xae, 0xf6, 0x9e, 0x80, 0xa2, 0x27, 0x5f, 0xf4, 0x9f, - 0x38, 0x7e, 0xfd, 0x93, 0x94, 0x95, 0x33, 0xdd, 0x93, 0xf4, 0x89, 0x80, 0xda, 0xec, 0x84, 0xad, - 0x66, 0xc7, 0x75, 0x79, 0x48, 0xf8, 0x77, 0x1b, 0x70, 0x15, 0xf2, 0x7e, 0x27, 0x6c, 0x19, 0xcc, - 0x73, 0xbb, 0x69, 0xcf, 0x2d, 0xf4, 0x17, 0x9e, 0x79, 0x6e, 0x57, 0xf3, 0x60, 0x65, 0x44, 0xec, - 0x24, 0xd7, 0xd7, 0x55, 0xdd, 0xb1, 0x40, 0x79, 0xe9, 0xdb, 0x66, 0x84, 0xcd, 0x00, 0x43, 0xf4, - 0x2c, 0x9c, 0x46, 0xa3, 0x95, 0x40, 0xcd, 0x26, 0xe1, 0x35, 0x6d, 0x7d, 0x9b, 0x83, 0xc5, 0xe3, - 0x84, 0xf7, 0x39, 0x06, 0xb1, 0x63, 0x21, 0x3d, 0x82, 0xa2, 0x38, 0xf4, 0x68, 0x75, 0x28, 0xb3, - 0x74, 0x8e, 0x96, 0x6f, 0x8f, 0x89, 0xe0, 0x89, 0xb4, 0x1c, 0x7d, 0x0d, 0xd7, 0xb3, 0xf3, 0x8b, - 0x6a, 0xc3, 0xdd, 0x27, 0x1f, 0x8a, 0xe5, 0x3b, 0x63, 0x63, 0x06, 0xf4, 0x47, 0x50, 0x14, 0x6b, - 0x14, 0x74, 0x4b, 0x3d, 0x16, 0x74, 0xcb, 0x0d, 0xe2, 0xc4, 0xe2, 0x3c, 0x13, 0x0d, 0x91, 0x4d, - 0x56, 0xd1, 0x10, 0xe9, 0x30, 0xe4, 0xc4, 0xe2, 0x9d, 0x2e, 0x10, 0x4b, 0x47, 0x96, 0x40, 0x2c, - 0x1f, 0x08, 0x9c, 0x58, 0x6c, 0x71, 0x81, 0x58, 0x7a, 0x19, 0x09, 0xc4, 0xf2, 0xfb, 0x41, 0xcb, - 0xd1, 0x13, 0xb8, 0x96, 0x69, 0x0e, 0x3a, 0x8c, 0x93, 0x77, 0x79, 0x59, 0x1b, 0x17, 0x32, 0xe0, - 0x7e, 0x05, 0x8b, 0xc2, 0x8d, 0x4e, 0xd7, 0x7e, 0x7f, 0xd7, 0x73, 0xde, 0xea, 0x65, 0xc3, 0x40, - 0xcb, 0xdd, 0x27, 0x7b, 0x1b, 0x5f, 0x7a, 0x15, 0xf2, 0xb5, 0x57, 0x21, 0xdf, 0x7b, 0x15, 0xf2, - 0xf1, 0x47, 0x25, 0x07, 0x4b, 0x36, 0xc6, 0x17, 0x50, 0xd3, 0x77, 0x6a, 0x71, 0xbd, 0x49, 0x4e, - 0x66, 0x6b, 0x8f, 0xe2, 0xfa, 0xe9, 0x7c, 0xf2, 0xc3, 0xb8, 0xfd, 0x33, 0x00, 0x00, 0xff, 0xff, - 0xe0, 0x36, 0xe4, 0xdd, 0x70, 0x0a, 0x00, 0x00, + // 661 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0xcd, 0x6e, 0xd3, 0x40, + 0x10, 0xce, 0xd2, 0x1f, 0x35, 0x93, 0xb6, 0xc0, 0x96, 0xa4, 0x21, 0x15, 0xa9, 0x59, 0x2e, 0x91, + 0x2a, 0x39, 0xa4, 0x95, 0xca, 0x81, 0x53, 0xdb, 0x20, 0x35, 0x42, 0x82, 0xc8, 0x54, 0x54, 0xad, + 0x84, 0x2c, 0xd7, 0xde, 0x92, 0x55, 0x5c, 0x6f, 0xb0, 0xd7, 0x96, 0xcc, 0x03, 0x70, 0x83, 0x33, + 0xef, 0xc0, 0x5b, 0x70, 0xe2, 0xc8, 0x91, 0x23, 0x2a, 0x2f, 0x82, 0x62, 0x3b, 0xc1, 0xeb, 0x9a, + 0x34, 0x54, 0x54, 0x70, 0xb3, 0x67, 0xe7, 0xfb, 0xfc, 0xcd, 0xec, 0xfc, 0x18, 0x2a, 0x21, 0x77, + 0xfb, 0x8c, 0x36, 0x83, 0x56, 0x33, 0x7e, 0x52, 0x07, 0x2e, 0x17, 0x1c, 0x17, 0x93, 0xb7, 0xa0, + 0x55, 0xbb, 0xfb, 0xcb, 0xc5, 0xa5, 0x1e, 0xf7, 0x5d, 0x93, 0x7a, 0xb1, 0x17, 0xd9, 0x86, 0xf2, + 0x8e, 0x29, 0x58, 0x60, 0x08, 0xba, 0x67, 0x33, 0xea, 0x08, 0x8d, 0xbe, 0xf1, 0xa9, 0x27, 0xf0, + 0x3d, 0x00, 0x33, 0x32, 0xe8, 0x7d, 0x1a, 0x56, 0x91, 0x82, 0x1a, 0x45, 0xad, 0x18, 0x5b, 0x9e, + 0xd2, 0x90, 0x1c, 0x40, 0x25, 0x8b, 0xf3, 0x06, 0xdc, 0xf1, 0xe8, 0x25, 0x40, 0xbc, 0x06, 0xc9, + 0x8b, 0xce, 0xac, 0xea, 0x0d, 0x05, 0x35, 0x16, 0xb5, 0x85, 0xd8, 0xd0, 0xb1, 0xc8, 0x36, 0xac, + 0xb6, 0xa9, 0x91, 0xab, 0x47, 0xc2, 0xa1, 0x0c, 0xee, 0x11, 0x54, 0x2f, 0xe2, 0x12, 0x3d, 0x13, + 0x81, 0x36, 0x94, 0x77, 0x84, 0x30, 0xcc, 0x5e, 0x9b, 0x9b, 0xfe, 0xd9, 0x94, 0x9f, 0xc3, 0xdb, + 0x50, 0x32, 0x7b, 0x86, 0xf3, 0x9a, 0xea, 0x03, 0xc3, 0xec, 0x47, 0x51, 0x94, 0x36, 0xcb, 0xea, + 0x38, 0xe1, 0xea, 0x5e, 0x74, 0xda, 0x35, 0xcc, 0xbe, 0x06, 0xe6, 0xf8, 0x99, 0x7c, 0x40, 0x50, + 0xc9, 0x7e, 0x6e, 0x0a, 0x95, 0x78, 0x1d, 0x4a, 0x56, 0x02, 0x18, 0x65, 0xad, 0xa8, 0xc1, 0xc8, + 0x74, 0x51, 0xd0, 0xcc, 0xb4, 0x82, 0x3e, 0x23, 0x28, 0xb7, 0xe9, 0x1f, 0xc7, 0x7f, 0x5d, 0x7a, + 0xf0, 0x16, 0x54, 0x5c, 0x7a, 0xc6, 0x03, 0xaa, 0xb3, 0x53, 0xdd, 0xe1, 0x42, 0x37, 0xa2, 0x6c, + 0x51, 0xab, 0x3a, 0xab, 0xa0, 0xc6, 0x82, 0xb6, 0x12, 0x9f, 0x76, 0x4e, 0x9f, 0x71, 0xb1, 0x93, + 0x1c, 0x11, 0x0e, 0x95, 0x6c, 0x0c, 0xd3, 0x95, 0xe2, 0x55, 0xaf, 0xf1, 0x00, 0xee, 0x1c, 0x1a, + 0xe2, 0x2f, 0xe7, 0x8c, 0x7c, 0x43, 0x50, 0xce, 0xd0, 0x26, 0x61, 0x1c, 0xc1, 0x32, 0x73, 0x98, + 0x60, 0x86, 0xcd, 0xde, 0x1a, 0x82, 0x71, 0x27, 0x22, 0x2f, 0x6d, 0x36, 0x53, 0x52, 0x73, 0x91, + 0x6a, 0x47, 0x82, 0xed, 0x17, 0xb4, 0x0c, 0x11, 0xde, 0x80, 0x39, 0x1a, 0x50, 0x47, 0x24, 0xc1, + 0xaf, 0xa4, 0x18, 0xdb, 0xdc, 0x7c, 0x32, 0x3c, 0xda, 0x2f, 0x68, 0xb1, 0x4f, 0xad, 0x09, 0xcb, + 0x32, 0x61, 0x2a, 0xc1, 0xcc, 0xf2, 0xaa, 0x48, 0x99, 0x69, 0x2c, 0x8e, 0x12, 0xdc, 0xb1, 0xbc, + 0xdd, 0x79, 0x98, 0x3d, 0xe1, 0x56, 0x48, 0xde, 0x23, 0x28, 0x6b, 0xd1, 0xcd, 0xfd, 0x17, 0x65, + 0x36, 0xac, 0x98, 0xac, 0x9c, 0xeb, 0xad, 0x98, 0x4f, 0x08, 0x2a, 0x5d, 0xdf, 0xeb, 0x75, 0x7d, + 0xdb, 0x8e, 0x5d, 0xbc, 0x7f, 0xdb, 0x68, 0x6b, 0x50, 0x1c, 0xf8, 0x5e, 0x4f, 0xe7, 0x8e, 0x1d, + 0x26, 0xbd, 0xb5, 0x30, 0x34, 0x3c, 0x77, 0xec, 0x90, 0x38, 0xb0, 0x7a, 0x41, 0xec, 0x34, 0x63, + 0xea, 0x8a, 0xd9, 0xd9, 0x7c, 0x37, 0x07, 0x4b, 0x47, 0x91, 0xd3, 0x0b, 0xea, 0x06, 0xcc, 0xa4, + 0xf8, 0x10, 0x96, 0xe5, 0xed, 0x82, 0x95, 0x14, 0x4d, 0xee, 0xc2, 0xaa, 0xdd, 0x9f, 0xe0, 0x11, + 0xab, 0x27, 0x05, 0xfc, 0x0a, 0x6e, 0x65, 0x17, 0x05, 0x26, 0xe9, 0xa2, 0xcf, 0xdf, 0x3e, 0xb5, + 0x07, 0x13, 0x7d, 0xc6, 0xf4, 0x43, 0xdd, 0xd2, 0x7c, 0x97, 0x75, 0xe7, 0x6d, 0x1a, 0x59, 0x77, + 0xee, 0x72, 0x88, 0x89, 0xe5, 0x19, 0x27, 0x11, 0xe7, 0x8e, 0x70, 0x89, 0x38, 0x7f, 0x40, 0xc6, + 0xc4, 0x72, 0x2b, 0x48, 0xc4, 0xb9, 0x4d, 0x2b, 0x11, 0xe7, 0xf7, 0x11, 0x29, 0xe0, 0x63, 0xb8, + 0x99, 0x29, 0x22, 0x9c, 0xc6, 0xe5, 0x77, 0x43, 0x8d, 0x4c, 0x72, 0x19, 0x73, 0xbf, 0x84, 0x25, + 0x69, 0xde, 0xe1, 0xf5, 0xdf, 0x4f, 0xc2, 0x98, 0x57, 0xb9, 0x6c, 0x54, 0x92, 0xc2, 0x43, 0xb4, + 0xbb, 0xf1, 0xe5, 0xbc, 0x8e, 0xbe, 0x9e, 0xd7, 0xd1, 0xf7, 0xf3, 0x3a, 0xfa, 0xf8, 0xa3, 0x5e, + 0x80, 0xdb, 0x16, 0x0d, 0x46, 0x50, 0x63, 0xc0, 0xd4, 0xa0, 0xd5, 0x45, 0xc7, 0xb3, 0xea, 0xe3, + 0xa0, 0x75, 0x32, 0x1f, 0xfd, 0x40, 0x6d, 0xfd, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x78, 0xaa, 0xfe, + 0x50, 0x80, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1079,7 +980,6 @@ const _ = grpc.SupportPackageIsVersion4 type YorkieServiceClient interface { ActivateClient(ctx context.Context, in *ActivateClientRequest, opts ...grpc.CallOption) (*ActivateClientResponse, error) DeactivateClient(ctx context.Context, in *DeactivateClientRequest, opts ...grpc.CallOption) (*DeactivateClientResponse, error) - UpdatePresence(ctx context.Context, in *UpdatePresenceRequest, opts ...grpc.CallOption) (*UpdatePresenceResponse, error) AttachDocument(ctx context.Context, in *AttachDocumentRequest, opts ...grpc.CallOption) (*AttachDocumentResponse, error) DetachDocument(ctx context.Context, in *DetachDocumentRequest, opts ...grpc.CallOption) (*DetachDocumentResponse, error) RemoveDocument(ctx context.Context, in *RemoveDocumentRequest, opts ...grpc.CallOption) (*RemoveDocumentResponse, error) @@ -1113,15 +1013,6 @@ func (c *yorkieServiceClient) DeactivateClient(ctx context.Context, in *Deactiva return out, nil } -func (c *yorkieServiceClient) UpdatePresence(ctx context.Context, in *UpdatePresenceRequest, opts ...grpc.CallOption) (*UpdatePresenceResponse, error) { - out := new(UpdatePresenceResponse) - err := c.cc.Invoke(ctx, "/yorkie.v1.YorkieService/UpdatePresence", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *yorkieServiceClient) AttachDocument(ctx context.Context, in *AttachDocumentRequest, opts ...grpc.CallOption) (*AttachDocumentResponse, error) { out := new(AttachDocumentResponse) err := c.cc.Invoke(ctx, "/yorkie.v1.YorkieService/AttachDocument", in, out, opts...) @@ -1194,7 +1085,6 @@ func (x *yorkieServiceWatchDocumentClient) Recv() (*WatchDocumentResponse, error type YorkieServiceServer interface { ActivateClient(context.Context, *ActivateClientRequest) (*ActivateClientResponse, error) DeactivateClient(context.Context, *DeactivateClientRequest) (*DeactivateClientResponse, error) - UpdatePresence(context.Context, *UpdatePresenceRequest) (*UpdatePresenceResponse, error) AttachDocument(context.Context, *AttachDocumentRequest) (*AttachDocumentResponse, error) DetachDocument(context.Context, *DetachDocumentRequest) (*DetachDocumentResponse, error) RemoveDocument(context.Context, *RemoveDocumentRequest) (*RemoveDocumentResponse, error) @@ -1212,9 +1102,6 @@ func (*UnimplementedYorkieServiceServer) ActivateClient(ctx context.Context, req func (*UnimplementedYorkieServiceServer) DeactivateClient(ctx context.Context, req *DeactivateClientRequest) (*DeactivateClientResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DeactivateClient not implemented") } -func (*UnimplementedYorkieServiceServer) UpdatePresence(ctx context.Context, req *UpdatePresenceRequest) (*UpdatePresenceResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdatePresence not implemented") -} func (*UnimplementedYorkieServiceServer) AttachDocument(ctx context.Context, req *AttachDocumentRequest) (*AttachDocumentResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AttachDocument not implemented") } @@ -1271,24 +1158,6 @@ func _YorkieService_DeactivateClient_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } -func _YorkieService_UpdatePresence_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdatePresenceRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(YorkieServiceServer).UpdatePresence(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/yorkie.v1.YorkieService/UpdatePresence", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(YorkieServiceServer).UpdatePresence(ctx, req.(*UpdatePresenceRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _YorkieService_AttachDocument_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AttachDocumentRequest) if err := dec(in); err != nil { @@ -1394,10 +1263,6 @@ var _YorkieService_serviceDesc = grpc.ServiceDesc{ MethodName: "DeactivateClient", Handler: _YorkieService_DeactivateClient_Handler, }, - { - MethodName: "UpdatePresence", - Handler: _YorkieService_UpdatePresence_Handler, - }, { MethodName: "AttachDocument", Handler: _YorkieService_AttachDocument_Handler, @@ -1807,15 +1672,10 @@ func (m *WatchDocumentRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x12 } - if m.Client != nil { - { - size, err := m.Client.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintYorkie(dAtA, i, uint64(size)) - } + if len(m.ClientId) > 0 { + i -= len(m.ClientId) + copy(dAtA[i:], m.ClientId) + i = encodeVarintYorkie(dAtA, i, uint64(len(m.ClientId))) i-- dAtA[i] = 0xa } @@ -1924,16 +1784,11 @@ func (m *WatchDocumentResponse_Initialization) MarshalToSizedBuffer(dAtA []byte) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Peers) > 0 { - for iNdEx := len(m.Peers) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.Peers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintYorkie(dAtA, i, uint64(size)) - } + if len(m.ClientIds) > 0 { + for iNdEx := len(m.ClientIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ClientIds[iNdEx]) + copy(dAtA[i:], m.ClientIds[iNdEx]) + i = encodeVarintYorkie(dAtA, i, uint64(len(m.ClientIds[iNdEx]))) i-- dAtA[i] = 0xa } @@ -2149,79 +2004,6 @@ func (m *PushPullChangesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } -func (m *UpdatePresenceRequest) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *UpdatePresenceRequest) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *UpdatePresenceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if len(m.DocumentId) > 0 { - i -= len(m.DocumentId) - copy(dAtA[i:], m.DocumentId) - i = encodeVarintYorkie(dAtA, i, uint64(len(m.DocumentId))) - i-- - dAtA[i] = 0x12 - } - if m.Client != nil { - { - size, err := m.Client.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintYorkie(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - -func (m *UpdatePresenceResponse) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *UpdatePresenceResponse) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *UpdatePresenceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - return len(dAtA) - i, nil -} - func encodeVarintYorkie(dAtA []byte, offset int, v uint64) int { offset -= sovYorkie(v) base := offset @@ -2398,8 +2180,8 @@ func (m *WatchDocumentRequest) Size() (n int) { } var l int _ = l - if m.Client != nil { - l = m.Client.Size() + l = len(m.ClientId) + if l > 0 { n += 1 + l + sovYorkie(uint64(l)) } l = len(m.DocumentId) @@ -2457,9 +2239,9 @@ func (m *WatchDocumentResponse_Initialization) Size() (n int) { } var l int _ = l - if len(m.Peers) > 0 { - for _, e := range m.Peers { - l = e.Size() + if len(m.ClientIds) > 0 { + for _, b := range m.ClientIds { + l = len(b) n += 1 + l + sovYorkie(uint64(l)) } } @@ -2560,38 +2342,6 @@ func (m *PushPullChangesResponse) Size() (n int) { return n } -func (m *UpdatePresenceRequest) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Client != nil { - l = m.Client.Size() - n += 1 + l + sovYorkie(uint64(l)) - } - l = len(m.DocumentId) - if l > 0 { - n += 1 + l + sovYorkie(uint64(l)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - -func (m *UpdatePresenceResponse) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - func sovYorkie(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -3565,9 +3315,9 @@ func (m *WatchDocumentRequest) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Client", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowYorkie @@ -3577,26 +3327,24 @@ func (m *WatchDocumentRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthYorkie } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthYorkie } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Client == nil { - m.Client = &Client{} - } - if err := m.Client.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + m.ClientId = append(m.ClientId[:0], dAtA[iNdEx:postIndex]...) + if m.ClientId == nil { + m.ClientId = []byte{} } iNdEx = postIndex case 2: @@ -3805,9 +3553,9 @@ func (m *WatchDocumentResponse_Initialization) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Peers", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ClientIds", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowYorkie @@ -3817,25 +3565,23 @@ func (m *WatchDocumentResponse_Initialization) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthYorkie } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthYorkie } if postIndex > l { return io.ErrUnexpectedEOF } - m.Peers = append(m.Peers, &Client{}) - if err := m.Peers[len(m.Peers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } + m.ClientIds = append(m.ClientIds, make([]byte, postIndex-iNdEx)) + copy(m.ClientIds[len(m.ClientIds)-1], dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -4425,176 +4171,6 @@ func (m *PushPullChangesResponse) Unmarshal(dAtA []byte) error { } return nil } -func (m *UpdatePresenceRequest) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowYorkie - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: UpdatePresenceRequest: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: UpdatePresenceRequest: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Client", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowYorkie - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthYorkie - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthYorkie - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Client == nil { - m.Client = &Client{} - } - if err := m.Client.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field DocumentId", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowYorkie - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthYorkie - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthYorkie - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.DocumentId = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipYorkie(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthYorkie - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *UpdatePresenceResponse) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowYorkie - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: UpdatePresenceResponse: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: UpdatePresenceResponse: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - default: - iNdEx = preIndex - skippy, err := skipYorkie(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthYorkie - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func skipYorkie(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/api/yorkie/v1/yorkie.proto b/api/yorkie/v1/yorkie.proto index 0acf21d99..b4f0eda6a 100644 --- a/api/yorkie/v1/yorkie.proto +++ b/api/yorkie/v1/yorkie.proto @@ -28,7 +28,6 @@ option java_package = "dev.yorkie.api.v1"; service YorkieService { rpc ActivateClient (ActivateClientRequest) returns (ActivateClientResponse) {} rpc DeactivateClient (DeactivateClientRequest) returns (DeactivateClientResponse) {} - rpc UpdatePresence (UpdatePresenceRequest) returns (UpdatePresenceResponse) {} rpc AttachDocument (AttachDocumentRequest) returns (AttachDocumentResponse) {} rpc DetachDocument (DetachDocumentRequest) returns (DetachDocumentResponse) {} @@ -79,13 +78,13 @@ message DetachDocumentResponse { } message WatchDocumentRequest { - Client client = 1; + bytes client_id = 1; string document_id = 2; } message WatchDocumentResponse { message Initialization { - repeated Client peers = 1; + repeated bytes client_ids = 1; } oneof body { @@ -116,10 +115,3 @@ message PushPullChangesResponse { bytes client_id = 1; ChangePack change_pack = 2; } - -message UpdatePresenceRequest { - Client client = 1; - string document_id = 2; -} - -message UpdatePresenceResponse {} diff --git a/client/client.go b/client/client.go index 8f81d5e61..655433967 100644 --- a/client/client.go +++ b/client/client.go @@ -35,7 +35,10 @@ import ( "github.com/yorkie-team/yorkie/api/types" api "github.com/yorkie-team/yorkie/api/yorkie/v1" "github.com/yorkie-team/yorkie/pkg/document" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" + "github.com/yorkie-team/yorkie/pkg/document/json" "github.com/yorkie-team/yorkie/pkg/document/key" + "github.com/yorkie-team/yorkie/pkg/document/presence" "github.com/yorkie-team/yorkie/pkg/document/time" ) @@ -64,26 +67,10 @@ var ( ErrUnsupportedWatchResponseType = errors.New("unsupported watch response type") ) -// SyncOption is an option for sync. It contains the key of the document to -// sync and the sync mode. -type SyncOption struct { - key key.Key - mode types.SyncMode -} - -// WithPushOnly returns a SyncOption with the sync mode set to PushOnly. -func (o SyncOption) WithPushOnly() SyncOption { - return SyncOption{ - key: o.key, - mode: types.SyncModePushOnly, - } -} - -// Attachment represents the document attached and peers. +// Attachment represents the document attached. type Attachment struct { doc *document.Document docID types.ID - peers map[string]types.PresenceInfo } // Client is a normal client that can communicate with the server. @@ -96,11 +83,10 @@ type Client struct { dialOptions []grpc.DialOption logger *zap.Logger - id *time.ActorID - key string - presenceInfo types.PresenceInfo - status status - attachments map[key.Key]*Attachment + id *time.ActorID + key string + status status + attachments map[key.Key]*Attachment } // WatchResponseType is type of watch response. @@ -108,16 +94,17 @@ type WatchResponseType string // The values below are types of WatchResponseType. const ( - DocumentsChanged WatchResponseType = "documents-changed" - PeersChanged WatchResponseType = "peers-changed" + DocumentChanged WatchResponseType = "document-changed" + DocumentWatched WatchResponseType = "document-watched" + DocumentUnwatched WatchResponseType = "document-unwatched" + PresenceChanged WatchResponseType = "presence-changed" ) // WatchResponse is a structure representing response of Watch. type WatchResponse struct { - Type WatchResponseType - Key key.Key - PeersMapByDoc map[key.Key]map[string]types.Presence - Err error + Type WatchResponseType + Presences map[string]innerpresence.Presence + Err error } // New creates an instance of Client. @@ -132,11 +119,6 @@ func New(opts ...Option) (*Client, error) { k = xid.New().String() } - presence := types.Presence{} - if options.Presence != nil { - presence = options.Presence - } - var dialOptions []grpc.DialOption transportCreds := grpc.WithTransportCredentials(insecure.NewCredentials()) @@ -171,10 +153,9 @@ func New(opts ...Option) (*Client, error) { options: options, logger: logger, - key: k, - presenceInfo: types.PresenceInfo{Presence: presence}, - status: deactivated, - attachments: make(map[key.Key]*Attachment), + key: k, + status: deactivated, + attachments: make(map[key.Key]*Attachment), }, nil } @@ -264,7 +245,7 @@ func (c *Client) Deactivate(ctx context.Context) error { // Attach attaches the given document to this client. It tells the server that // this client will synchronize the given document. -func (c *Client) Attach(ctx context.Context, doc *document.Document) error { +func (c *Client) Attach(ctx context.Context, doc *document.Document, options ...AttachOption) error { if c.status != activated { return ErrClientNotActivated } @@ -273,8 +254,20 @@ func (c *Client) Attach(ctx context.Context, doc *document.Document) error { return ErrDocumentNotDetached } + opts := &AttachOptions{} + for _, opt := range options { + opt(opts) + } + doc.SetActor(c.id) + if err := doc.Update(func(root *json.Object, p *presence.Presence) error { + p.Initialize(opts.Presence) + return nil + }); err != nil { + return err + } + pbChangePack, err := converter.ToChangePack(doc.CreateChangePack()) if err != nil { return err @@ -315,7 +308,6 @@ func (c *Client) Attach(ctx context.Context, doc *document.Document) error { c.attachments[doc.Key()] = &Attachment{ doc: doc, docID: types.ID(res.DocumentId), - peers: make(map[string]types.PresenceInfo), } return nil @@ -327,16 +319,28 @@ func (c *Client) Attach(ctx context.Context, doc *document.Document) error { // To collect garbage things like CRDT tombstones left on the document, all the // changes should be applied to other replicas before GC time. For this, if the // document is no longer used by this client, it should be detached. -func (c *Client) Detach(ctx context.Context, doc *document.Document, removeIfNotAttached bool) error { +func (c *Client) Detach(ctx context.Context, doc *document.Document, options ...DetachOption) error { if c.status != activated { return ErrClientNotActivated } + opts := &DetachOptions{} + for _, opt := range options { + opt(opts) + } + attachment, ok := c.attachments[doc.Key()] if !ok { return ErrDocumentNotAttached } + if err := doc.Update(func(root *json.Object, p *presence.Presence) error { + p.Clear() + return nil + }); err != nil { + return err + } + pbChangePack, err := converter.ToChangePack(doc.CreateChangePack()) if err != nil { return err @@ -348,7 +352,7 @@ func (c *Client) Detach(ctx context.Context, doc *document.Document, removeIfNot ClientId: c.id.Bytes(), DocumentId: attachment.docID.String(), ChangePack: pbChangePack, - RemoveIfNotAttached: removeIfNotAttached, + RemoveIfNotAttached: opts.removeIfNotAttached, }, ) if err != nil { @@ -371,18 +375,10 @@ func (c *Client) Detach(ctx context.Context, doc *document.Document, removeIfNot return nil } -// WithDocKey creates a SyncOption with the given document key. -func WithDocKey(k key.Key) SyncOption { - return SyncOption{ - key: k, - mode: types.SyncModePushPull, - } -} - // Sync pushes local changes of the attached documents to the server and // receives changes of the remote replica from the server then apply them to // local documents. -func (c *Client) Sync(ctx context.Context, options ...SyncOption) error { +func (c *Client) Sync(ctx context.Context, options ...SyncOptions) error { if len(options) == 0 { for _, attachment := range c.attachments { options = append(options, WithDocKey(attachment.doc.Key())) @@ -416,10 +412,7 @@ func (c *Client) Watch( stream, err := c.client.WatchDocument( withShardKey(ctx, c.options.APIKey, doc.Key().String()), &api.WatchDocumentRequest{ - Client: converter.ToClient(types.Client{ - ID: c.id, - PresenceInfo: c.presenceInfo, - }), + ClientId: c.id.Bytes(), DocumentId: attachment.docID.String(), }, ) @@ -430,16 +423,16 @@ func (c *Client) Watch( handleResponse := func(pbResp *api.WatchDocumentResponse) (*WatchResponse, error) { switch resp := pbResp.Body.(type) { case *api.WatchDocumentResponse_Initialization_: - clients, err := converter.FromClients(resp.Initialization.Peers) - if err != nil { - return nil, err - } - - attachment := c.attachments[doc.Key()] - for _, cli := range clients { - attachment.peers[cli.ID.String()] = cli.PresenceInfo + var clientIDs []string + for _, clientID := range resp.Initialization.ClientIds { + id, err := time.ActorIDFromBytes(clientID) + if err != nil { + return nil, err + } + clientIDs = append(clientIDs, id.String()) } + doc.SetOnlineClientSet(clientIDs...) return nil, nil case *api.WatchDocumentResponse_Event: eventType, err := converter.FromEventType(resp.Event.Type) @@ -447,37 +440,35 @@ func (c *Client) Watch( return nil, err } - docKey, err := c.findDocKey(resp.Event.DocumentId) + cli, err := time.ActorIDFromBytes(resp.Event.Publisher) if err != nil { return nil, err } switch eventType { case types.DocumentsChangedEvent: - return &WatchResponse{ - Type: DocumentsChanged, - Key: docKey, - }, nil - case types.DocumentsWatchedEvent, types.DocumentsUnwatchedEvent, types.PresenceChangedEvent: - cli, err := converter.FromClient(resp.Event.Publisher) - if err != nil { - return nil, err + return &WatchResponse{Type: DocumentChanged}, nil + case types.DocumentsWatchedEvent: + doc.AddOnlineClient(cli.String()) + if doc.OnlinePresence(cli.String()) == nil { + return nil, nil } - attachment := c.attachments[docKey] - if eventType == types.DocumentsWatchedEvent || - eventType == types.PresenceChangedEvent { - if info, ok := attachment.peers[cli.ID.String()]; ok { - cli.PresenceInfo.Update(info) - } - attachment.peers[cli.ID.String()] = cli.PresenceInfo - } else { - delete(attachment.peers, cli.ID.String()) - } + return &WatchResponse{ + Type: DocumentWatched, + Presences: map[string]innerpresence.Presence{ + cli.String(): doc.OnlinePresence(cli.String()), + }, + }, nil + case types.DocumentsUnwatchedEvent: + p := doc.OnlinePresence(cli.String()) + doc.RemoveOnlineClient(cli.String()) return &WatchResponse{ - Type: PeersChanged, - PeersMapByDoc: c.PeersMapByDoc(), + Type: DocumentUnwatched, + Presences: map[string]innerpresence.Presence{ + cli.String(): p, + }, }, nil } } @@ -506,10 +497,38 @@ func (c *Client) Watch( close(rch) return } + if resp == nil { + continue + } + rch <- *resp } }() + // TODO(hackerwins): We need to revise the implementation of the watch + // event handling. Currently, we are using the same channel for both + // document events and watch events. This is not ideal because the + // client cannot distinguish between document events and watch events. + // We'll expose only document events and watch events will be handled + // internally. + + // TODO(hackerwins): We should ensure that the goroutine is closed when + // the stream is closed. + go func() { + for { + select { + case e := <-doc.Events(): + t := PresenceChanged + if e.Type == document.WatchedEvent { + t = DocumentWatched + } + rch <- WatchResponse{Type: t, Presences: e.Presences} + case <-ctx.Done(): + return + } + } + }() + return rch, nil } @@ -523,41 +542,6 @@ func (c *Client) findDocKey(docID string) (key.Key, error) { return "", ErrDocumentNotAttached } -// UpdatePresence updates the presence of this client. -func (c *Client) UpdatePresence(ctx context.Context, k, v string) error { - if c.status != activated { - return ErrClientNotActivated - } - - c.presenceInfo.Presence[k] = v - c.presenceInfo.Clock++ - - if len(c.attachments) == 0 { - return nil - } - - // TODO(hackerwins): We temporarily use Unary Call to update presence, - // because grpc-web can't handle Bi-Directional streaming for now. - // After grpc-web supports bi-directional streaming, we can remove the - // following. - // TODO(hackerwins): We will move Presence from client-level to document-level. - for _, attachment := range c.attachments { - if _, err := c.client.UpdatePresence( - withShardKey(ctx, c.options.APIKey, attachment.doc.Key().String()), - &api.UpdatePresenceRequest{ - Client: converter.ToClient(types.Client{ - ID: c.id, - PresenceInfo: c.presenceInfo, - }), - DocumentId: attachment.docID.String(), - }); err != nil { - return err - } - } - - return nil -} - // ID returns the ID of this client. func (c *Client) ID() *time.ActorID { return c.id @@ -568,36 +552,13 @@ func (c *Client) Key() string { return c.key } -// Presence returns the presence data of this client. -func (c *Client) Presence() types.Presence { - presence := make(types.Presence) - for k, v := range c.presenceInfo.Presence { - presence[k] = v - } - - return presence -} - -// PeersMapByDoc returns the peersMap. -func (c *Client) PeersMapByDoc() map[key.Key]map[string]types.Presence { - peersMapByDoc := make(map[key.Key]map[string]types.Presence) - for docKey, attachment := range c.attachments { - peers := make(map[string]types.Presence) - for id, info := range attachment.peers { - peers[id] = info.Presence - } - peersMapByDoc[docKey] = peers - } - return peersMapByDoc -} - // IsActive returns whether this client is active or not. func (c *Client) IsActive() bool { return c.status == activated } // pushPullChanges pushes the changes of the document to the server and pulls the changes from the server. -func (c *Client) pushPullChanges(ctx context.Context, opt SyncOption) error { +func (c *Client) pushPullChanges(ctx context.Context, opt SyncOptions) error { if c.status != activated { return ErrClientNotActivated } diff --git a/client/options.go b/client/options.go index ba84db28b..82c50f1a3 100644 --- a/client/options.go +++ b/client/options.go @@ -20,6 +20,8 @@ import ( "go.uber.org/zap" "github.com/yorkie-team/yorkie/api/types" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" + "github.com/yorkie-team/yorkie/pkg/document/key" ) // Option configures Options. @@ -30,9 +32,6 @@ type Options struct { // Key is the key of the client. It is used to identify the client. Key string - // Presence is the presence of the client. - Presence types.Presence - // APIKey is the API key of the client. APIKey string @@ -57,11 +56,6 @@ func WithKey(key string) Option { return func(o *Options) { o.Key = key } } -// WithPresence configures the presence of the client. -func WithPresence(presence types.Presence) Option { - return func(o *Options) { o.Presence = presence } -} - // WithAPIKey configures the API key of the client. func WithAPIKey(apiKey string) Option { return func(o *Options) { o.APIKey = apiKey } @@ -91,3 +85,53 @@ func WithLogger(logger *zap.Logger) Option { func WithMaxRecvMsgSize(maxRecvMsgSize int) Option { return func(o *Options) { o.MaxCallRecvMsgSize = maxRecvMsgSize } } + +// AttachOption configures AttachOptions. +type AttachOption func(*AttachOptions) + +// AttachOptions configures how we set up the document. +type AttachOptions struct { + // Presence is the presence of the client. + Presence innerpresence.Presence +} + +// WithPresence configures the presence of the client. +func WithPresence(presence innerpresence.Presence) AttachOption { + return func(o *AttachOptions) { o.Presence = presence } +} + +// DetachOption configures DetachOptions. +type DetachOption func(*DetachOptions) + +// DetachOptions configures how we set up the document. +type DetachOptions struct { + removeIfNotAttached bool +} + +// WithRemoveIfNotAttached configures the removeIfNotAttached of the document. +func WithRemoveIfNotAttached() DetachOption { + return func(o *DetachOptions) { o.removeIfNotAttached = true } +} + +// SyncOptions is an option for sync. It contains the key of the document to +// sync and the sync mode. +type SyncOptions struct { + key key.Key + mode types.SyncMode +} + +// WithDocKey creates a SyncOptions with the given document key. +func WithDocKey(k key.Key) SyncOptions { + return SyncOptions{ + key: k, + mode: types.SyncModePushPull, + } +} + +// WithPushOnly returns a SyncOptions with the sync mode set to PushOnly. +func (o SyncOptions) WithPushOnly() SyncOptions { + return SyncOptions{ + key: o.key, + mode: types.SyncModePushOnly, + } +} diff --git a/pkg/document/change/change.go b/pkg/document/change/change.go index acdf947ef..88a638ba1 100644 --- a/pkg/document/change/change.go +++ b/pkg/document/change/change.go @@ -52,7 +52,7 @@ func New(id ID, message string, operations []operations.Operation, p *innerprese } // Execute applies this change to the given JSON root. -func (c *Change) Execute(root *crdt.Root, presenceMap *innerpresence.Map) error { +func (c *Change) Execute(root *crdt.Root, presences *innerpresence.Map) error { for _, op := range c.operations { if err := op.Execute(root); err != nil { return err @@ -60,9 +60,11 @@ func (c *Change) Execute(root *crdt.Root, presenceMap *innerpresence.Map) error } if c.presenceChange != nil { - // TODO(hackerwins): For now, we only support PUT operation. We need to - // support other operations such as DELETE, PATCH. - presenceMap.Store(c.id.actorID.String(), &c.presenceChange.Presence) + if c.presenceChange.ChangeType == innerpresence.Clear { + presences.Delete(c.id.actorID.String()) + } else { + presences.Store(c.id.actorID.String(), c.presenceChange.Presence) + } } return nil diff --git a/pkg/document/change/checkpoint.go b/pkg/document/change/checkpoint.go index 0be20a7ed..8967b9c43 100644 --- a/pkg/document/change/checkpoint.go +++ b/pkg/document/change/checkpoint.go @@ -115,7 +115,7 @@ func (cp Checkpoint) Forward(other Checkpoint) Checkpoint { } // Equals returns whether the given checkpoint is equal to this checkpoint or not. -func (cp *Checkpoint) Equals(other Checkpoint) bool { +func (cp Checkpoint) Equals(other Checkpoint) bool { return cp.ServerSeq == other.ServerSeq && cp.ClientSeq == other.ClientSeq } diff --git a/pkg/document/document.go b/pkg/document/document.go index a7b873d10..985dd2f6d 100644 --- a/pkg/document/document.go +++ b/pkg/document/document.go @@ -29,6 +29,25 @@ import ( "github.com/yorkie-team/yorkie/pkg/document/time" ) +// DocEvent represents the event that occurred in the document. +type DocEvent struct { + Type DocEventType + Presences map[string]innerpresence.Presence +} + +// DocEventType represents the type of the event that occurred in the document. +type DocEventType string + +const ( + // WatchedEvent means that the client has established a connection with the server, + // enabling real-time synchronization. + WatchedEvent DocEventType = "watched" + + // PresenceChangedEvent means that the presences of the clients who are editing + // the document have changed. + PresenceChangedEvent DocEventType = "presence-changed" +) + // Document represents a document accessible to the user. // // How document works: @@ -40,15 +59,23 @@ type Document struct { // doc is the original data of the actual document. doc *InternalDocument - // clone is a copy of `doc` to be exposed to the user and is used to - // protect `doc`. - clone *crdt.Root + // cloneRoot is a copy of `doc.root` to be exposed to the user and is used to + // protect `doc.root`. + cloneRoot *crdt.Root + + // clonePresences is a copy of `doc.presences` to be exposed to the user and + // is used to protect `doc.presences`. + clonePresences *innerpresence.Map + + // events is the channel to send events that occurred in the document. + events chan DocEvent } // New creates a new instance of Document. func New(key key.Key) *Document { return &Document{ - doc: NewInternalDocument(key), + doc: NewInternalDocument(key), + events: make(chan DocEvent, 1), } } @@ -68,23 +95,22 @@ func (d *Document) Update( ctx := change.NewContext( d.doc.changeID.Next(), messageFromMsgAndArgs(msgAndArgs...), - d.clone, + d.cloneRoot, ) - // TODO(hackerwins): We should use the presence of the client that cloned - // from the document. if err := updater( - json.NewObject(ctx, d.clone.Object()), - presence.New(ctx, d.doc.Presence()), + json.NewObject(ctx, d.cloneRoot.Object()), + presence.New(ctx, d.clonePresences.LoadOrStore(d.ActorID().String(), innerpresence.NewPresence())), ); err != nil { - // drop clone because it is contaminated. - d.clone = nil + // drop cloneRoot because it is contaminated. + d.cloneRoot = nil + d.clonePresences = nil return err } if ctx.HasChange() { c := ctx.ToChange() - if err := c.Execute(d.doc.root, d.doc.presenceMap); err != nil { + if err := c.Execute(d.doc.root, d.doc.presences); err != nil { return err } @@ -97,9 +123,10 @@ func (d *Document) Update( // ApplyChangePack applies the given change pack into this document. func (d *Document) ApplyChangePack(pack *change.Pack) error { - // 01. Apply remote changes to both the clone and the document. + // 01. Apply remote changes to both the cloneRoot and the document. if len(pack.Snapshot) > 0 { - d.clone = nil + d.cloneRoot = nil + d.clonePresences = nil if err := d.doc.applySnapshot(pack.Snapshot, pack.Checkpoint.ServerSeq); err != nil { return err } @@ -109,14 +136,19 @@ func (d *Document) ApplyChangePack(pack *change.Pack) error { } for _, c := range pack.Changes { - if err := c.Execute(d.clone, d.doc.presenceMap); err != nil { + if err := c.Execute(d.cloneRoot, d.clonePresences); err != nil { return err } } - if err := d.doc.ApplyChanges(pack.Changes...); err != nil { + events, err := d.doc.ApplyChanges(pack.Changes...) + if err != nil { return err } + + for _, e := range events { + d.events <- e + } } // 02. Remove local changes applied to server. @@ -209,14 +241,14 @@ func (d *Document) Root() *json.Object { panic(err) } - ctx := change.NewContext(d.doc.changeID.Next(), "", d.clone) - return json.NewObject(ctx, d.clone.Object()) + ctx := change.NewContext(d.doc.changeID.Next(), "", d.cloneRoot) + return json.NewObject(ctx, d.cloneRoot.Object()) } // GarbageCollect purge elements that were removed before the given time. func (d *Document) GarbageCollect(ticket *time.Ticket) int { - if d.clone != nil { - if _, err := d.clone.GarbageCollect(ticket); err != nil { + if d.cloneRoot != nil { + if _, err := d.cloneRoot.GarbageCollect(ticket); err != nil { panic(err) } } @@ -235,25 +267,66 @@ func (d *Document) GarbageLen() int { } func (d *Document) ensureClone() error { - if d.clone == nil { + if d.cloneRoot == nil { copiedDoc, err := d.doc.root.DeepCopy() if err != nil { return err } - d.clone = copiedDoc + d.cloneRoot = copiedDoc } + + if d.clonePresences == nil { + d.clonePresences = d.doc.presences.DeepCopy() + } + return nil } -// PresenceMap returns the presence map of this document. -func (d *Document) PresenceMap() map[string]innerpresence.Presence { +// Presences returns the presence map of this document. +func (d *Document) Presences() map[string]innerpresence.Presence { // TODO(hackerwins): We need to use client key instead of actor ID for exposing presence. - presenceMap := make(map[string]innerpresence.Presence) - d.doc.presenceMap.Range(func(key, value interface{}) bool { - presenceMap[key.(string)] = *value.(*innerpresence.Presence) + presences := make(map[string]innerpresence.Presence) + d.doc.presences.Range(func(key string, value innerpresence.Presence) bool { + presences[key] = value return true }) - return presenceMap + return presences +} + +// Presence returns the presence of the given client. +func (d *Document) Presence(clientID string) innerpresence.Presence { + return d.doc.Presence(clientID) +} + +// MyPresence returns the presence of the actor. +func (d *Document) MyPresence() innerpresence.Presence { + return d.doc.MyPresence() +} + +// SetOnlineClientSet sets the online client set. +func (d *Document) SetOnlineClientSet(clientIDs ...string) { + d.doc.SetOnlineClientSet(clientIDs...) +} + +// AddOnlineClient adds the given client to the online client set. +func (d *Document) AddOnlineClient(clientID string) { + d.doc.AddOnlineClient(clientID) +} + +// RemoveOnlineClient removes the given client from the online client set. +func (d *Document) RemoveOnlineClient(clientID string) { + d.doc.RemoveOnlineClient(clientID) +} + +// OnlinePresence returns the presence of the given client. If the client is not +// online, it returns nil. +func (d *Document) OnlinePresence(clientID string) innerpresence.Presence { + return d.doc.OnlinePresence(clientID) +} + +// Events returns the events of this document. +func (d *Document) Events() <-chan DocEvent { + return d.events } func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { diff --git a/pkg/document/innerpresence/presence.go b/pkg/document/innerpresence/presence.go index 3476d5d1a..7376d0f02 100644 --- a/pkg/document/innerpresence/presence.go +++ b/pkg/document/innerpresence/presence.go @@ -25,12 +25,66 @@ import ( "sync" ) -// Map is a map of presence by clientID. -type Map = sync.Map // map[string]*innerpresence.Presence +// Map is a map of Presence. It is wrapper of sync.Map to use type-safe. +type Map struct { + presences sync.Map +} // NewMap creates a new instance of Map. func NewMap() *Map { - return &sync.Map{} + return &Map{presences: sync.Map{}} +} + +// Store stores the given presence to the map. +func (m *Map) Store(clientID string, presence Presence) { + m.presences.Store(clientID, presence) +} + +// Range calls f sequentially for each key and value present in the map. +func (m *Map) Range(f func(clientID string, presence Presence) bool) { + m.presences.Range(func(key, value interface{}) bool { + clientID := key.(string) + presence := value.(Presence) + return f(clientID, presence) + }) +} + +// Load returns the presence for the given clientID. +func (m *Map) Load(clientID string) (Presence, bool) { + presence, ok := m.presences.Load(clientID) + if !ok { + return nil, false + } + + return presence.(Presence), true +} + +// LoadOrStore returns the existing presence if exists. +// Otherwise, it stores and returns the given presence. +func (m *Map) LoadOrStore(clientID string, presence Presence) Presence { + actual, _ := m.presences.LoadOrStore(clientID, presence) + return actual.(Presence) +} + +// Has returns whether the given clientID exists. +func (m *Map) Has(clientID string) bool { + _, ok := m.presences.Load(clientID) + return ok +} + +// Delete deletes the presence for the given clientID. +func (m *Map) Delete(clientID string) { + m.presences.Delete(clientID) +} + +// DeepCopy copies itself deeply. +func (m *Map) DeepCopy() *Map { + copied := NewMap() + m.Range(func(clientID string, presence Presence) bool { + copied.Store(clientID, presence.DeepCopy()) + return true + }) + return copied } // PresenceChangeType represents the type of presence change. @@ -39,40 +93,56 @@ type PresenceChangeType string const ( // Put represents the presence is put. Put PresenceChangeType = "put" + + // Clear represents the presence is cleared. + Clear PresenceChangeType = "clear" ) // PresenceChange represents the change of presence. type PresenceChange struct { - ChangeType PresenceChangeType - Presence Presence + ChangeType PresenceChangeType `json:"changeType"` + Presence Presence `json:"presence"` } // NewChangeFromJSON creates a new instance of PresenceChange from JSON. -func NewChangeFromJSON(encodedJSON string) (*PresenceChange, error) { - if encodedJSON == "" { +func NewChangeFromJSON(encodedChange string) (*PresenceChange, error) { + if encodedChange == "" { return nil, nil } - p := PresenceChange{} - if err := json.Unmarshal([]byte(encodedJSON), &p); err != nil { + p := &PresenceChange{} + if err := json.Unmarshal([]byte(encodedChange), p); err != nil { return nil, fmt.Errorf("unmarshal presence change: %w", err) } - return &p, nil + return p, nil } // Presence represents custom presence that can be defined by the client. type Presence map[string]string // NewPresence creates a new instance of Presence. -func NewPresence() *Presence { - data := make(map[string]string) - p := Presence(data) - return &p +func NewPresence() Presence { + return make(map[string]string) } // Set sets the value of the given key. -func (p *Presence) Set(key string, value string) { - presence := *p - presence[key] = value +func (p Presence) Set(key string, value string) { + p[key] = value +} + +// Clear clears the presence. +func (p Presence) Clear() { + for k := range p { + delete(p, k) + } +} + +// DeepCopy copies itself deeply. +func (p Presence) DeepCopy() Presence { + clone := make(map[string]string) + for k, v := range p { + clone[k] = v + } + return clone } diff --git a/pkg/document/internal_document.go b/pkg/document/internal_document.go index e0bd4a996..e4ce0d8fc 100644 --- a/pkg/document/internal_document.go +++ b/pkg/document/internal_document.go @@ -18,6 +18,7 @@ package document import ( "errors" + gosync "sync" "github.com/yorkie-team/yorkie/api/converter" "github.com/yorkie-team/yorkie/pkg/document/change" @@ -49,15 +50,41 @@ var ( ErrDocumentRemoved = errors.New("document is removed") ) -// InternalDocument represents a document in MongoDB and contains logical clocks. +// InternalDocument is a document that is used internally. It is not directly +// exposed to the user. type InternalDocument struct { - key key.Key - status StatusType - root *crdt.Root - checkpoint change.Checkpoint - changeID change.ID + // key is the key of the document. It is used as the key of the document in + // user's perspective. + key key.Key + + // status is the status of the document. It is used to check whether the + // document is attached to the client or detached or removed. + status StatusType + + // checkpoint is the checkpoint of the document. It is used to determine + // what changes should be sent and what changes should be received. + checkpoint change.Checkpoint + + // changeID is the ID of the last change. It is used to create a new change. + // It contains logical clock information like the lamport timestamp, actorID + // and checkpoint information. + changeID change.ID + + // root is the root of the document. It is used to store JSON-like data in + // CRDT manner. + root *crdt.Root + + // presences is the map of the presence. It is used to store the presence + // of the actors who are attaching this document. + presences *innerpresence.Map + + // onlineClients is the set of the client who is editing this document in + // online. + onlineClients *gosync.Map + + // localChanges is the list of the changes that are not yet sent to the + // server. localChanges []*change.Change - presenceMap *innerpresence.Map } // NewInternalDocument creates a new instance of InternalDocument. @@ -66,12 +93,13 @@ func NewInternalDocument(k key.Key) *InternalDocument { // TODO(hackerwins): We need to initialize the presence of the actor who edited the document. return &InternalDocument{ - key: k, - status: StatusDetached, - root: crdt.NewRoot(root), - checkpoint: change.InitialCheckpoint, - changeID: change.InitialID, - presenceMap: innerpresence.NewMap(), + key: k, + status: StatusDetached, + root: crdt.NewRoot(root), + checkpoint: change.InitialCheckpoint, + changeID: change.InitialID, + presences: innerpresence.NewMap(), + onlineClients: &gosync.Map{}, } } @@ -82,18 +110,19 @@ func NewInternalDocumentFromSnapshot( lamport int64, snapshot []byte, ) (*InternalDocument, error) { - obj, presenceMap, err := converter.BytesToSnapshot(snapshot) + obj, presences, err := converter.BytesToSnapshot(snapshot) if err != nil { return nil, err } return &InternalDocument{ - key: k, - status: StatusDetached, - root: crdt.NewRoot(obj), - presenceMap: presenceMap, - checkpoint: change.InitialCheckpoint.NextServerSeq(serverSeq), - changeID: change.InitialID.SyncLamport(lamport), + key: k, + status: StatusDetached, + root: crdt.NewRoot(obj), + presences: presences, + onlineClients: &gosync.Map{}, + checkpoint: change.InitialCheckpoint.NextServerSeq(serverSeq), + changeID: change.InitialID.SyncLamport(lamport), }, nil } @@ -114,13 +143,13 @@ func (d *InternalDocument) HasLocalChanges() bool { // ApplyChangePack applies the given change pack into this document. func (d *InternalDocument) ApplyChangePack(pack *change.Pack) error { - // 01. Apply remote changes to both the clone and the document. + // 01. Apply remote changes to both the cloneRoot and the document. if len(pack.Snapshot) > 0 { if err := d.applySnapshot(pack.Snapshot, pack.Checkpoint.ServerSeq); err != nil { return err } } else { - if err := d.ApplyChanges(pack.Changes...); err != nil { + if _, err := d.ApplyChanges(pack.Changes...); err != nil { return err } } @@ -209,37 +238,95 @@ func (d *InternalDocument) RootObject() *crdt.Object { } func (d *InternalDocument) applySnapshot(snapshot []byte, serverSeq int64) error { - rootObj, presenceMap, err := converter.BytesToSnapshot(snapshot) + rootObj, presences, err := converter.BytesToSnapshot(snapshot) if err != nil { return err } d.root = crdt.NewRoot(rootObj) - d.presenceMap = presenceMap + d.presences = presences d.changeID = d.changeID.SyncLamport(serverSeq) return nil } // ApplyChanges applies remote changes to the document. -func (d *InternalDocument) ApplyChanges(changes ...*change.Change) error { +func (d *InternalDocument) ApplyChanges(changes ...*change.Change) ([]DocEvent, error) { + var events []DocEvent for _, c := range changes { - if err := c.Execute(d.root, d.presenceMap); err != nil { - return err + if c.PresenceChange() != nil { + clientID := c.ID().ActorID().String() + if _, ok := d.onlineClients.Load(clientID); ok { + event := DocEvent{ + Type: PresenceChangedEvent, + Presences: map[string]innerpresence.Presence{ + clientID: c.PresenceChange().Presence, + }, + } + + if !d.presences.Has(clientID) { + event.Type = WatchedEvent + } + events = append(events, event) + } + } + + if err := c.Execute(d.root, d.presences); err != nil { + return nil, err } + d.changeID = d.changeID.SyncLamport(c.ID().Lamport()) } - return nil + return events, nil +} + +// MyPresence returns the presence of the actor currently editing the document. +func (d *InternalDocument) MyPresence() innerpresence.Presence { + p := d.presences.LoadOrStore(d.changeID.ActorID().String(), innerpresence.NewPresence()) + return p.DeepCopy() +} + +// Presences returns the map of presences of the actors currently editing the document. +func (d *InternalDocument) Presences() *innerpresence.Map { + return d.presences +} + +// OnlinePresence returns the presence of the given client. If the client is not +// online, it returns nil. +func (d *InternalDocument) OnlinePresence(clientID string) innerpresence.Presence { + if _, ok := d.onlineClients.Load(clientID); !ok { + return nil + } + + presence, _ := d.presences.Load(clientID) + return presence +} + +// Presence returns the presence of the given client. +func (d *InternalDocument) Presence(clientID string) innerpresence.Presence { + presence, _ := d.presences.Load(clientID) + return presence +} + +// SetOnlineClientSet sets the online client set. +func (d *InternalDocument) SetOnlineClientSet(ids ...string) { + d.onlineClients.Range(func(key, value interface{}) bool { + d.onlineClients.Delete(key) + return true + }) + + for _, id := range ids { + d.onlineClients.Store(id, true) + } } -// Presence returns the presence of the actor currently editing the document. -func (d *InternalDocument) Presence() *innerpresence.Presence { - value, _ := d.presenceMap.LoadOrStore(d.changeID.ActorID().String(), innerpresence.NewPresence()) - return value.(*innerpresence.Presence) +// AddOnlineClient adds the given client to the online client set. +func (d *InternalDocument) AddOnlineClient(clientID string) { + d.onlineClients.Store(clientID, true) } -// PresenceMap returns the map of presences of the actors currently editing the document. -func (d *InternalDocument) PresenceMap() *innerpresence.Map { - return d.presenceMap +// RemoveOnlineClient removes the given client from the online client set. +func (d *InternalDocument) RemoveOnlineClient(clientID string) { + d.onlineClients.Delete(clientID) } diff --git a/pkg/document/presence/presence.go b/pkg/document/presence/presence.go index d03814995..f9a72fd2b 100644 --- a/pkg/document/presence/presence.go +++ b/pkg/document/presence/presence.go @@ -26,26 +26,48 @@ import ( // Presence represents a proxy for the Presence to be manipulated from the outside. type Presence struct { - presence *innerpresence.Presence + presence innerpresence.Presence context *change.Context } // New creates a new instance of Presence. -func New(ctx *change.Context, presence *innerpresence.Presence) *Presence { +func New(ctx *change.Context, presence innerpresence.Presence) *Presence { return &Presence{ presence: presence, context: ctx, } } +// Initialize initializes the presence. +func (p *Presence) Initialize(presence innerpresence.Presence) { + p.presence = presence + if p.presence == nil { + p.presence = innerpresence.NewPresence() + } + + p.context.SetPresenceChange(innerpresence.PresenceChange{ + ChangeType: innerpresence.Put, + Presence: p.presence, + }) +} + // Set sets the value of the given key. func (p *Presence) Set(key string, value string) { - internalPresence := *p.presence - internalPresence.Set(key, value) + innerPresence := p.presence + innerPresence.Set(key, value) + + p.context.SetPresenceChange(innerpresence.PresenceChange{ + ChangeType: innerpresence.Put, + Presence: innerPresence, + }) +} + +// Clear clears the value of the given key. +func (p *Presence) Clear() { + innerPresence := p.presence + innerPresence.Clear() - // TODO(hackerwins): We should support partial update here. p.context.SetPresenceChange(innerpresence.PresenceChange{ - ChangeType: "put", - Presence: internalPresence, + ChangeType: innerpresence.Clear, }) } diff --git a/server/backend/database/memory/database.go b/server/backend/database/memory/database.go index 172847089..93882c313 100644 --- a/server/backend/database/memory/database.go +++ b/server/backend/database/memory/database.go @@ -947,7 +947,7 @@ func (d *DB) CreateSnapshotInfo( docID types.ID, doc *document.InternalDocument, ) error { - snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.PresenceMap()) + snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.Presences()) if err != nil { return err } diff --git a/server/backend/database/mongo/client.go b/server/backend/database/mongo/client.go index 132f2007a..bdcdc460a 100644 --- a/server/backend/database/mongo/client.go +++ b/server/backend/database/mongo/client.go @@ -1031,7 +1031,7 @@ func (c *Client) CreateSnapshotInfo( if err != nil { return err } - snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.PresenceMap()) + snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.Presences()) if err != nil { return err } diff --git a/server/backend/sync/coordinator.go b/server/backend/sync/coordinator.go index ff70e1006..deab2105c 100644 --- a/server/backend/sync/coordinator.go +++ b/server/backend/sync/coordinator.go @@ -40,9 +40,9 @@ type Coordinator interface { // Subscribe subscribes to the given documents. Subscribe( ctx context.Context, - subscriber types.Client, + subscriber *time.ActorID, documentID types.ID, - ) (*Subscription, []types.Client, error) + ) (*Subscription, []*time.ActorID, error) // Unsubscribe unsubscribes from the given documents. Unsubscribe( @@ -57,13 +57,6 @@ type Coordinator interface { // PublishToLocal publishes the given event. PublishToLocal(ctx context.Context, publisherID *time.ActorID, event DocEvent) - // UpdatePresence updates the presence of the given client. - UpdatePresence( - ctx context.Context, - publisher *types.Client, - documentID types.ID, - ) error - // Members returns the members of this cluster. Members() map[string]*ServerInfo diff --git a/server/backend/sync/memory/coordinator.go b/server/backend/sync/memory/coordinator.go index 9b08a1cc1..57b6ca909 100644 --- a/server/backend/sync/memory/coordinator.go +++ b/server/backend/sync/memory/coordinator.go @@ -57,16 +57,16 @@ func (c *Coordinator) NewLocker( // Subscribe subscribes to the given documents. func (c *Coordinator) Subscribe( ctx context.Context, - subscriber types.Client, + subscriber *time.ActorID, documentID types.ID, -) (*sync.Subscription, []types.Client, error) { +) (*sync.Subscription, []*time.ActorID, error) { sub, err := c.pubSub.Subscribe(ctx, subscriber, documentID) if err != nil { return nil, nil, err } - peers := c.pubSub.GetPeers(documentID) - return sub, peers, nil + ids := c.pubSub.ClientIDs(documentID) + return sub, ids, nil } // Unsubscribe unsubscribes the given documents. @@ -97,16 +97,6 @@ func (c *Coordinator) PublishToLocal( c.pubSub.Publish(ctx, publisherID, event) } -// UpdatePresence updates the presence of the given client. -func (c *Coordinator) UpdatePresence( - _ context.Context, - publisher *types.Client, - documentID types.ID, -) error { - c.pubSub.UpdatePresence(publisher, documentID) - return nil -} - // Members returns the members of this cluster. func (c *Coordinator) Members() map[string]*sync.ServerInfo { members := make(map[string]*sync.ServerInfo) diff --git a/server/backend/sync/memory/coordinator_test.go b/server/backend/sync/memory/coordinator_test.go index a0ae2594d..02e2e5ce4 100644 --- a/server/backend/sync/memory/coordinator_test.go +++ b/server/backend/sync/memory/coordinator_test.go @@ -37,9 +37,9 @@ func TestCoordinator(t *testing.T) { id, err := time.ActorIDFromBytes([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byte(i)}) assert.NoError(t, err) - _, peers, err := coordinator.Subscribe(ctx, types.Client{ID: id}, docID) + _, clientIDs, err := coordinator.Subscribe(ctx, id, docID) assert.NoError(t, err) - assert.Len(t, peers, i+1) + assert.Len(t, clientIDs, i+1) } }) } diff --git a/server/backend/sync/memory/pubsub.go b/server/backend/sync/memory/pubsub.go index 733a15eec..a01703784 100644 --- a/server/backend/sync/memory/pubsub.go +++ b/server/backend/sync/memory/pubsub.go @@ -65,31 +65,29 @@ func (s *subscriptions) Len() int { // PubSub is the memory implementation of PubSub, used for single server. type PubSub struct { - subscriptionsMapMu *gosync.RWMutex - subscriptionMapBySubscriber map[string]*sync.Subscription - subscriptionsMapByDocID map[types.ID]*subscriptions + subscriptionsMapMu *gosync.RWMutex + subscriptionsMapByDocID map[types.ID]*subscriptions } // NewPubSub creates an instance of PubSub. func NewPubSub() *PubSub { return &PubSub{ - subscriptionsMapMu: &gosync.RWMutex{}, - subscriptionMapBySubscriber: make(map[string]*sync.Subscription), - subscriptionsMapByDocID: make(map[types.ID]*subscriptions), + subscriptionsMapMu: &gosync.RWMutex{}, + subscriptionsMapByDocID: make(map[types.ID]*subscriptions), } } // Subscribe subscribes to the given document keys. func (m *PubSub) Subscribe( ctx context.Context, - subscriber types.Client, + subscriber *time.ActorID, documentID types.ID, ) (*sync.Subscription, error) { if logging.Enabled(zap.DebugLevel) { logging.From(ctx).Debugf( `Subscribe(%s,%s) Start`, documentID.String(), - subscriber.ID.String(), + subscriber.String(), ) } @@ -97,8 +95,6 @@ func (m *PubSub) Subscribe( defer m.subscriptionsMapMu.Unlock() sub := sync.NewSubscription(subscriber) - m.subscriptionMapBySubscriber[sub.SubscriberID()] = sub - if _, ok := m.subscriptionsMapByDocID[documentID]; !ok { m.subscriptionsMapByDocID[documentID] = newSubscriptions() } @@ -108,7 +104,7 @@ func (m *PubSub) Subscribe( logging.From(ctx).Debugf( `Subscribe(%s,%s) End`, documentID.String(), - subscriber.ID.String(), + subscriber.String(), ) } return sub, nil @@ -127,14 +123,12 @@ func (m *PubSub) Unsubscribe( logging.From(ctx).Debugf( `Unsubscribe(%s,%s) Start`, documentID, - sub.SubscriberID(), + sub.Subscriber().String(), ) } sub.Close() - delete(m.subscriptionMapBySubscriber, sub.SubscriberID()) - if subs, ok := m.subscriptionsMapByDocID[documentID]; ok { subs.Delete(sub.ID()) @@ -147,7 +141,7 @@ func (m *PubSub) Unsubscribe( logging.From(ctx).Debugf( `Unsubscribe(%s,%s) End`, documentID, - sub.SubscriberID(), + sub.Subscriber().String(), ) } } @@ -168,7 +162,7 @@ func (m *PubSub) Publish( if subs, ok := m.subscriptionsMapByDocID[documentID]; ok { for _, sub := range subs.Map() { - if sub.Subscriber().ID.Compare(publisherID) == 0 { + if sub.Subscriber().Compare(publisherID) == 0 { continue } @@ -178,7 +172,7 @@ func (m *PubSub) Publish( event.Type, documentID.String(), publisherID.String(), - sub.SubscriberID(), + sub.Subscriber().String(), ) } @@ -191,7 +185,7 @@ func (m *PubSub) Publish( `Publish(%s,%s) to %s timeout`, documentID.String(), publisherID.String(), - sub.SubscriberID(), + sub.Subscriber().String(), ) } } @@ -201,31 +195,14 @@ func (m *PubSub) Publish( } } -// GetPeers returns the peers of the given document. -func (m *PubSub) GetPeers(documentID types.ID) []types.Client { +// ClientIDs returns the clients of the given document. +func (m *PubSub) ClientIDs(documentID types.ID) []*time.ActorID { m.subscriptionsMapMu.RLock() defer m.subscriptionsMapMu.RUnlock() - var peers []types.Client + var ids []*time.ActorID for _, sub := range m.subscriptionsMapByDocID[documentID].Map() { - peers = append(peers, sub.Subscriber()) - } - return peers -} - -// UpdatePresence updates the presence of the given client. -func (m *PubSub) UpdatePresence( - publisher *types.Client, - documentID types.ID, -) *sync.Subscription { - m.subscriptionsMapMu.Lock() - defer m.subscriptionsMapMu.Unlock() - - sub, ok := m.subscriptionMapBySubscriber[publisher.ID.String()] - if !ok { - return nil + ids = append(ids, sub.Subscriber()) } - - sub.UpdatePresence(publisher.PresenceInfo) - return sub + return ids } diff --git a/server/backend/sync/memory/pubsub_test.go b/server/backend/sync/memory/pubsub_test.go index 686991ca5..138ec680c 100644 --- a/server/backend/sync/memory/pubsub_test.go +++ b/server/backend/sync/memory/pubsub_test.go @@ -34,21 +34,19 @@ func TestPubSub(t *testing.T) { assert.NoError(t, err) idB, err := time.ActorIDFromBytes([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) assert.NoError(t, err) - actorA := types.Client{ID: idA} - actorB := types.Client{ID: idB} t.Run("publish subscribe test", func(t *testing.T) { pubSub := memory.NewPubSub() id := types.ID(t.Name() + "id") docEvent := sync.DocEvent{ Type: types.DocumentsWatchedEvent, - Publisher: actorB, + Publisher: idB, DocumentID: id, } ctx := context.Background() // subscribe the documents by actorA - subA, err := pubSub.Subscribe(ctx, actorA, id) + subA, err := pubSub.Subscribe(ctx, idA, id) assert.NoError(t, err) defer func() { pubSub.Unsubscribe(ctx, id, subA) @@ -63,7 +61,7 @@ func TestPubSub(t *testing.T) { }() // publish the event to the documents by actorB - pubSub.Publish(ctx, actorB.ID, docEvent) + pubSub.Publish(ctx, idB, docEvent) wg.Wait() }) } diff --git a/server/backend/sync/pubsub.go b/server/backend/sync/pubsub.go index f66e33000..80ce74164 100644 --- a/server/backend/sync/pubsub.go +++ b/server/backend/sync/pubsub.go @@ -20,18 +20,19 @@ import ( "github.com/rs/xid" "github.com/yorkie-team/yorkie/api/types" + "github.com/yorkie-team/yorkie/pkg/document/time" ) // Subscription represents a subscription of a subscriber to documents. type Subscription struct { id string - subscriber types.Client + subscriber *time.ActorID closed bool events chan DocEvent } // NewSubscription creates a new instance of Subscription. -func NewSubscription(subscriber types.Client) *Subscription { +func NewSubscription(subscriber *time.ActorID) *Subscription { return &Subscription{ id: xid.New().String(), subscriber: subscriber, @@ -47,7 +48,7 @@ func (s *Subscription) ID() string { // DocEvent represents events that occur related to the document. type DocEvent struct { Type types.DocEventType - Publisher types.Client + Publisher *time.ActorID DocumentID types.ID } @@ -57,20 +58,10 @@ func (s *Subscription) Events() chan DocEvent { } // Subscriber returns the subscriber of this subscription. -func (s *Subscription) Subscriber() types.Client { +func (s *Subscription) Subscriber() *time.ActorID { return s.subscriber } -// SubscriberID returns string representation of the subscriber. -func (s *Subscription) SubscriberID() string { - return s.subscriber.ID.String() -} - -// UpdatePresence updates the presence of the subscriber. -func (s *Subscription) UpdatePresence(info types.PresenceInfo) { - s.subscriber.PresenceInfo.Update(info) -} - // Close closes all resources of this Subscription. func (s *Subscription) Close() { if s.closed { diff --git a/server/clients/clients.go b/server/clients/clients.go index ab6c6691c..1803c7f17 100644 --- a/server/clients/clients.go +++ b/server/clients/clients.go @@ -73,6 +73,11 @@ func Deactivate( return nil, err } + // TODO(hackerwins): We need to remove the presence of the client from the document. + // Be careful that housekeeping is executed by the leader. And documents are sharded + // by the servers in the cluster. So, we need to consider the case where the leader is + // not the same as the server that handles the document. + if err := db.UpdateSyncedSeq( ctx, clientInfo, diff --git a/server/packs/packs.go b/server/packs/packs.go index f27910ab6..159abf1e5 100644 --- a/server/packs/packs.go +++ b/server/packs/packs.go @@ -133,7 +133,7 @@ func PushPull( publisherID, sync.DocEvent{ Type: types.DocumentsChangedEvent, - Publisher: types.Client{ID: publisherID}, + Publisher: publisherID, DocumentID: docInfo.ID, }, ) diff --git a/server/packs/pushpull.go b/server/packs/pushpull.go index 18872916f..2eb0b95ae 100644 --- a/server/packs/pushpull.go +++ b/server/packs/pushpull.go @@ -157,7 +157,7 @@ func pullSnapshot( } cpAfterPull := cpAfterPush.NextServerSeq(docInfo.ServerSeq) - snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.PresenceMap()) + snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.Presences()) if err != nil { return nil, err } diff --git a/server/rpc/admin_server.go b/server/rpc/admin_server.go index ea519c386..3487b1ee9 100644 --- a/server/rpc/admin_server.go +++ b/server/rpc/admin_server.go @@ -254,7 +254,7 @@ func (s *adminServer) GetSnapshotMeta( return nil, err } - snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.PresenceMap()) + snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.Presences()) if err != nil { return nil, err } @@ -376,7 +376,7 @@ func (s *adminServer) RemoveDocumentByAdmin( publisherID, sync.DocEvent{ Type: types.DocumentsChangedEvent, - Publisher: types.Client{ID: publisherID}, + Publisher: publisherID, DocumentID: docInfo.ID, }, ) diff --git a/server/rpc/server_test.go b/server/rpc/server_test.go index 36de756d4..e883d0e91 100644 --- a/server/rpc/server_test.go +++ b/server/rpc/server_test.go @@ -672,7 +672,7 @@ func TestSDKRPCServerBackend(t *testing.T) { watchResp, err := testClient.WatchDocument( context.Background(), &api.WatchDocumentRequest{ - Client: &api.Client{Id: activateResp.ClientId, Presence: &api.Presence{}}, + ClientId: activateResp.ClientId, DocumentId: resPack.DocumentId, }, ) diff --git a/server/rpc/yorkie_server.go b/server/rpc/yorkie_server.go index ab1f46c07..5bf8f918d 100644 --- a/server/rpc/yorkie_server.go +++ b/server/rpc/yorkie_server.go @@ -236,11 +236,15 @@ func (s *yorkieServer) DetachDocument( return nil, err } - if err := clientInfo.RemoveDocument(docInfo.ID); err != nil { - return nil, err - } if req.RemoveIfNotAttached && !isAttached { pack.IsRemoved = true + if err := clientInfo.RemoveDocument(docInfo.ID); err != nil { + return nil, err + } + } else { + if err := clientInfo.DetachDocument(docInfo.ID); err != nil { + return nil, err + } } pulled, err := packs.PushPull(ctx, s.backend, project, clientInfo, docInfo, pack, types.SyncModePushPull) @@ -344,7 +348,7 @@ func (s *yorkieServer) WatchDocument( req *api.WatchDocumentRequest, stream api.YorkieService_WatchDocumentServer, ) error { - cli, err := converter.FromClient(req.Client) + clientID, err := time.ActorIDFromBytes(req.ClientId) if err != nil { return err } @@ -371,13 +375,13 @@ func (s *yorkieServer) WatchDocument( } project := projects.From(stream.Context()) - if _, err = clients.FindClientInfo(stream.Context(), s.backend.DB, project, cli.ID); err != nil { + if _, err = clients.FindClientInfo(stream.Context(), s.backend.DB, project, clientID); err != nil { return err } locker, err := s.backend.Coordinator.NewLocker( stream.Context(), - sync.NewKey(fmt.Sprintf("watchdoc-%s-%s", cli.ID.String(), docID)), + sync.NewKey(fmt.Sprintf("watchdoc-%s-%s", clientID.String(), docID)), ) if err != nil { return err @@ -391,7 +395,7 @@ func (s *yorkieServer) WatchDocument( } }() - subscription, peersMap, err := s.watchDoc(stream.Context(), *cli, docID) + subscription, clientIDs, err := s.watchDoc(stream.Context(), clientID, docID) if err != nil { logging.From(stream.Context()).Error(err) return err @@ -400,10 +404,14 @@ func (s *yorkieServer) WatchDocument( s.unwatchDoc(subscription, docID) }() + var pbClientIDs [][]byte + for _, id := range clientIDs { + pbClientIDs = append(pbClientIDs, id.Bytes()) + } if err := stream.Send(&api.WatchDocumentResponse{ Body: &api.WatchDocumentResponse_Initialization_{ Initialization: &api.WatchDocumentResponse_Initialization{ - Peers: converter.ToClients(peersMap), + ClientIds: pbClientIDs, }, }, }); err != nil { @@ -425,9 +433,8 @@ func (s *yorkieServer) WatchDocument( if err := stream.Send(&api.WatchDocumentResponse{ Body: &api.WatchDocumentResponse_Event{ Event: &api.DocEvent{ - Type: eventType, - Publisher: converter.ToClient(event.Publisher), - DocumentId: event.DocumentID.String(), + Type: eventType, + Publisher: event.Publisher.Bytes(), }, }, }); err != nil { @@ -508,45 +515,12 @@ func (s *yorkieServer) RemoveDocument( }, nil } -// UpdatePresence updates the presence of the given client. -func (s *yorkieServer) UpdatePresence( - ctx context.Context, - req *api.UpdatePresenceRequest, -) (*api.UpdatePresenceResponse, error) { - cli, err := converter.FromClient(req.Client) - if err != nil { - return nil, err - } - documentID, err := converter.FromDocumentID(req.DocumentId) - if err != nil { - return nil, err - } - - project := projects.From(ctx) - _, err = documents.FindDocInfo(ctx, s.backend, project, documentID) - if err != nil { - return nil, err - } - - if err = s.backend.Coordinator.UpdatePresence(ctx, cli, documentID); err != nil { - return nil, err - } - - s.backend.Coordinator.Publish(ctx, cli.ID, sync.DocEvent{ - Type: types.PresenceChangedEvent, - Publisher: *cli, - DocumentID: documentID, - }) - - return &api.UpdatePresenceResponse{}, nil -} - func (s *yorkieServer) watchDoc( ctx context.Context, - client types.Client, + clientID *time.ActorID, documentID types.ID, -) (*sync.Subscription, []types.Client, error) { - subscription, peers, err := s.backend.Coordinator.Subscribe(ctx, client, documentID) +) (*sync.Subscription, []*time.ActorID, error) { + subscription, clientIDs, err := s.backend.Coordinator.Subscribe(ctx, clientID, documentID) if err != nil { logging.From(ctx).Error(err) return nil, nil, err @@ -554,7 +528,7 @@ func (s *yorkieServer) watchDoc( s.backend.Coordinator.Publish( ctx, - subscription.Subscriber().ID, + subscription.Subscriber(), sync.DocEvent{ Type: types.DocumentsWatchedEvent, Publisher: subscription.Subscriber(), @@ -562,7 +536,7 @@ func (s *yorkieServer) watchDoc( }, ) - return subscription, peers, nil + return subscription, clientIDs, nil } func (s *yorkieServer) unwatchDoc( @@ -573,7 +547,7 @@ func (s *yorkieServer) unwatchDoc( _ = s.backend.Coordinator.Unsubscribe(ctx, documentID, subscription) s.backend.Coordinator.Publish( ctx, - subscription.Subscriber().ID, + subscription.Subscriber(), sync.DocEvent{ Type: types.DocumentsUnwatchedEvent, Publisher: subscription.Subscriber(), diff --git a/test/bench/grpc_bench_test.go b/test/bench/grpc_bench_test.go index f6f47cd19..2f8b91db5 100644 --- a/test/bench/grpc_bench_test.go +++ b/test/bench/grpc_bench_test.go @@ -141,7 +141,7 @@ func watchDoc( } assert.NoError(b, resp.Err) - if resp.Type == client.DocumentsChanged { + if resp.Type == client.DocumentChanged { err := cli.Sync(ctx, client.WithDocKey(resp.Key)) assert.NoError(b, err) } diff --git a/test/helper/helper.go b/test/helper/helper.go index 706b5b14a..ba6c2da1f 100644 --- a/test/helper/helper.go +++ b/test/helper/helper.go @@ -231,6 +231,7 @@ func TestConfig() *server.Config { AdminTokenDuration: server.DefaultAdminTokenDuration.String(), UseDefaultProject: true, ClientDeactivateThreshold: server.DefaultClientDeactivateThreshold, + SnapshotInterval: 10, SnapshotThreshold: SnapshotThreshold, SnapshotWithPurgingChanges: SnapshotWithPurgingChanges, AuthWebhookMaxWaitInterval: AuthWebhookMaxWaitInterval.String(), diff --git a/test/integration/admin_test.go b/test/integration/admin_test.go index 89480aaa8..cc3873baa 100644 --- a/test/integration/admin_test.go +++ b/test/integration/admin_test.go @@ -105,8 +105,8 @@ func TestAdmin(t *testing.T) { } assert.NoError(t, resp.Err) - if resp.Type == client.DocumentsChanged { - err := c1.Sync(ctx, client.WithDocKey(resp.Key)) + if resp.Type == client.DocumentChanged { + err := c1.Sync(ctx, client.WithDocKey(d1.Key())) assert.NoError(t, err) return } @@ -119,7 +119,7 @@ func TestAdmin(t *testing.T) { // 03. wait for watching document changed event. wg.Wait() - assert.Equal(t, d1.Status(), document.StatusRemoved) + assert.Equal(t, document.StatusRemoved, d1.Status()) }) t.Run("document removal without force test", func(t *testing.T) { @@ -144,7 +144,7 @@ func TestAdmin(t *testing.T) { assert.Equal(t, document.StatusAttached, doc.Status()) // 03. remove document that is detached by the client. - assert.NoError(t, cli.Detach(ctx, doc, false)) + assert.NoError(t, cli.Detach(ctx, doc)) err = adminCli.RemoveDocument(ctx, "default", doc.Key().String(), false) assert.NoError(t, err) assert.Equal(t, document.StatusDetached, doc.Status()) diff --git a/test/integration/agent_test.go b/test/integration/agent_test.go index 5b0138451..ebe9bfedc 100644 --- a/test/integration/agent_test.go +++ b/test/integration/agent_test.go @@ -58,8 +58,7 @@ func TestServer(t *testing.T) { return case wr := <-wrch: if wr.Err == io.EOF || status.Code(wr.Err) == codes.Canceled { - peers := wr.PeersMapByDoc[doc.Key()] - assert.Len(t, peers, 0) + assert.Len(t, wr.Presences, 0) wg.Done() return } diff --git a/test/integration/client_test.go b/test/integration/client_test.go index ae07fcb8a..e37fe4413 100644 --- a/test/integration/client_test.go +++ b/test/integration/client_test.go @@ -139,27 +139,27 @@ func TestClient(t *testing.T) { assert.NoError(t, cli.Attach(ctx, doc)) // 02. cli update the document with creating a counter - // and sync with push-pull mode: CP(0, 0) -> CP(1, 1) + // and sync with push-pull mode: CP(1, 1) -> CP(2, 2) assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewCounter("counter", crdt.IntegerCnt, 0) return nil })) - assert.Equal(t, change.Checkpoint{ClientSeq: 0, ServerSeq: 0}, doc.Checkpoint()) + assert.Equal(t, change.Checkpoint{ClientSeq: 1, ServerSeq: 1}, doc.Checkpoint()) assert.NoError(t, cli.Sync(ctx, client.WithDocKey(doc.Key()))) - assert.Equal(t, doc.Checkpoint(), change.Checkpoint{ClientSeq: 1, ServerSeq: 1}) + assert.Equal(t, doc.Checkpoint(), change.Checkpoint{ClientSeq: 2, ServerSeq: 2}) // 03. cli update the document with increasing the counter(0 -> 1) - // and sync with push-only mode: CP(1, 1) -> CP(2, 1) + // and sync with push-only mode: CP(2, 2) -> CP(3, 2) assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetCounter("counter").Increase(1) return nil })) assert.Len(t, doc.CreateChangePack().Changes, 1) assert.NoError(t, cli.Sync(ctx, client.WithDocKey(doc.Key()).WithPushOnly())) - assert.Equal(t, doc.Checkpoint(), change.Checkpoint{ClientSeq: 2, ServerSeq: 1}) + assert.Equal(t, doc.Checkpoint(), change.Checkpoint{ClientSeq: 3, ServerSeq: 2}) // 04. cli update the document with increasing the counter(1 -> 2) - // and sync with push-pull mode. CP(2, 1) -> CP(3, 3) + // and sync with push-pull mode. CP(3, 2) -> CP(4, 4) assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.GetCounter("counter").Increase(1) return nil @@ -169,7 +169,7 @@ func TestClient(t *testing.T) { // so the ChangePack of the request only has the increase(1 -> 2). assert.Len(t, doc.CreateChangePack().Changes, 1) assert.NoError(t, cli.Sync(ctx, client.WithDocKey(doc.Key()))) - assert.Equal(t, doc.Checkpoint(), change.Checkpoint{ClientSeq: 3, ServerSeq: 3}) + assert.Equal(t, doc.Checkpoint(), change.Checkpoint{ClientSeq: 4, ServerSeq: 4}) assert.Equal(t, "2", doc.Root().GetCounter("counter").Marshal()) }) } diff --git a/test/integration/document_test.go b/test/integration/document_test.go index 086dde787..2a5981c3d 100644 --- a/test/integration/document_test.go +++ b/test/integration/document_test.go @@ -26,9 +26,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/yorkie-team/yorkie/api/types" "github.com/yorkie-team/yorkie/client" "github.com/yorkie-team/yorkie/pkg/document" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/pkg/document/json" "github.com/yorkie-team/yorkie/pkg/document/key" "github.com/yorkie-team/yorkie/pkg/document/presence" @@ -53,7 +53,7 @@ func TestDocument(t *testing.T) { assert.NoError(t, err) assert.True(t, doc.IsAttached()) - err = c1.Detach(ctx, doc, false) + err = c1.Detach(ctx, doc) assert.NoError(t, err) assert.False(t, doc.IsAttached()) @@ -88,7 +88,7 @@ func TestDocument(t *testing.T) { assert.True(t, doc.IsAttached()) // 02. detach with removeIfNotAttached option false - err = c1.Detach(ctx, doc, false) + err = c1.Detach(ctx, doc) assert.NoError(t, err) assert.False(t, doc.IsAttached()) assert.Equal(t, doc.Status(), document.StatusDetached) @@ -99,7 +99,7 @@ func TestDocument(t *testing.T) { assert.True(t, doc.IsAttached()) // 04. detach with removeIfNotAttached option true - err = c1.Detach(ctx, doc, true) + err = c1.Detach(ctx, doc, client.WithRemoveIfNotAttached()) assert.NoError(t, err) assert.False(t, doc.IsAttached()) assert.Equal(t, doc.Status(), document.StatusRemoved) @@ -174,8 +174,8 @@ func TestDocument(t *testing.T) { } assert.NoError(t, resp.Err) - if resp.Type == client.DocumentsChanged { - err := c1.Sync(ctx, client.WithDocKey(resp.Key)) + if resp.Type == client.DocumentChanged { + err := c1.Sync(ctx, client.WithDocKey(d1.Key())) assert.NoError(t, err) return } @@ -338,7 +338,7 @@ func TestDocument(t *testing.T) { // 02. cli1 removes d1 and cli2 detaches d2. assert.NoError(t, c1.Remove(ctx, d1)) - assert.NoError(t, c2.Detach(ctx, d2, false)) + assert.NoError(t, c2.Detach(ctx, d2)) assert.Equal(t, d1.Status(), document.StatusRemoved) assert.Equal(t, d2.Status(), document.StatusRemoved) }) @@ -383,7 +383,7 @@ func TestDocument(t *testing.T) { // 01. abnormal behavior on detached state d1 := document.New(helper.TestDocKey(t)) - assert.ErrorIs(t, cli.Detach(ctx, d1, false), client.ErrDocumentNotAttached) + assert.ErrorIs(t, cli.Detach(ctx, d1), client.ErrDocumentNotAttached) assert.ErrorIs(t, cli.Sync(ctx, client.WithDocKey(d1.Key())), client.ErrDocumentNotAttached) assert.ErrorIs(t, cli.Remove(ctx, d1), client.ErrDocumentNotAttached) @@ -395,7 +395,7 @@ func TestDocument(t *testing.T) { assert.NoError(t, cli.Remove(ctx, d1)) assert.ErrorIs(t, cli.Remove(ctx, d1), client.ErrDocumentNotAttached) assert.ErrorIs(t, cli.Sync(ctx, client.WithDocKey(d1.Key())), client.ErrDocumentNotAttached) - assert.ErrorIs(t, cli.Detach(ctx, d1, false), client.ErrDocumentNotAttached) + assert.ErrorIs(t, cli.Detach(ctx, d1), client.ErrDocumentNotAttached) }) t.Run("removed document removal with watching test", func(t *testing.T) { @@ -470,7 +470,6 @@ func TestDocumentWithProjects(t *testing.T) { go func() { defer wg.Done() - for { resp := <-rch if resp.Err == io.EOF { @@ -479,62 +478,62 @@ func TestDocumentWithProjects(t *testing.T) { } assert.NoError(t, resp.Err) - if resp.Type == client.DocumentsChanged { - err := c1.Sync(ctx, client.WithDocKey(resp.Key)) + if resp.Type == client.DocumentChanged { + err := c1.Sync(ctx, client.WithDocKey(d1.Key())) assert.NoError(t, err) + } else { responsePairs = append(responsePairs, watchResponsePair{ - Type: resp.Type, + Type: resp.Type, + Presences: resp.Presences, }) - return } - - if resp.Type == client.PeersChanged { - peers := resp.PeersMapByDoc[d1.Key()] - responsePairs = append(responsePairs, watchResponsePair{ - Type: resp.Type, - Peers: peers, - }) + if len(responsePairs) == 2 { + return } } }() - // c2 watches the same document, so c1 receives a peers changed event. + // c2 watches the same document, so c1 receives a document watched event. expected = append(expected, watchResponsePair{ - Type: client.PeersChanged, - Peers: map[string]types.Presence{ - c1.ID().String(): nil, - c2.ID().String(): nil, + Type: client.DocumentWatched, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): {}, }, }) d2 := document.New(helper.TestDocKey(t)) assert.NoError(t, c2.Attach(ctx, d2)) watch2Ctx, cancel2 := context.WithCancel(ctx) - defer cancel2() _, err = c2.Watch(watch2Ctx, d2) assert.NoError(t, err) // c2 updates the document, so c1 receives a documents changed event. - expected = append(expected, watchResponsePair{ - Type: client.DocumentsChanged, - }) assert.NoError(t, d2.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("key", "value") return nil })) + assert.NoError(t, c2.Sync(ctx)) // d3 is in another project, so c1 and c2 should not receive events. d3 := document.New(helper.TestDocKey(t)) assert.NoError(t, c3.Attach(ctx, d3)) watch3Ctx, cancel3 := context.WithCancel(ctx) - defer cancel3() _, err = c3.Watch(watch3Ctx, d3) assert.NoError(t, err) assert.NoError(t, d3.Update(func(root *json.Object, p *presence.Presence) error { root.SetString("key3", "value3") return nil })) - assert.NoError(t, c2.Sync(ctx)) + assert.NoError(t, c3.Sync(ctx)) + // c2 unwatch the document, so c1 receives a document unwatched event. + expected = append(expected, watchResponsePair{ + Type: client.DocumentUnwatched, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): {}, + }, + }) + cancel3() + cancel2() wg.Wait() assert.Equal(t, expected, responsePairs) @@ -550,7 +549,7 @@ func TestDocumentWithProjects(t *testing.T) { t.Run("includeSnapshot test", func(t *testing.T) { d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, cli.Attach(ctx, d1)) - defer func() { assert.NoError(t, cli.Detach(ctx, d1, false)) }() + defer func() { assert.NoError(t, cli.Detach(ctx, d1)) }() assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("testArray") diff --git a/test/integration/gc_test.go b/test/integration/gc_test.go index 0942fa0f7..907973820 100644 --- a/test/integration/gc_test.go +++ b/test/integration/gc_test.go @@ -367,7 +367,7 @@ func TestGarbageCollection(t *testing.T) { assert.Equal(t, 6, d1.GarbageLen()) assert.Equal(t, 0, d2.GarbageLen()) - err = c2.Detach(ctx, d2, false) + err = c2.Detach(ctx, d2) assert.NoError(t, err) // (2, 1) -> (2, 2): syncedseqs:(1, x) diff --git a/test/integration/history_test.go b/test/integration/history_test.go index 15b97e299..24134ab06 100644 --- a/test/integration/history_test.go +++ b/test/integration/history_test.go @@ -42,7 +42,7 @@ func TestHistory(t *testing.T) { ctx := context.Background() d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, cli.Attach(ctx, d1)) - defer func() { assert.NoError(t, cli.Detach(ctx, d1, false)) }() + defer func() { assert.NoError(t, cli.Detach(ctx, d1)) }() assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("todos") @@ -65,7 +65,8 @@ func TestHistory(t *testing.T) { changes, err := adminCli.ListChangeSummaries(ctx, "default", d1.Key(), 0, 0, true) assert.NoError(t, err) - assert.Len(t, changes, 3) + // NOTE(chacha912): When attaching, a change is made to set the initial presence. + assert.Len(t, changes, 4) assert.Equal(t, "create todos", changes[2].Message) assert.Equal(t, "buy coffee", changes[1].Message) diff --git a/test/integration/main_test.go b/test/integration/main_test.go index e217e6e7d..fc49bb486 100644 --- a/test/integration/main_test.go +++ b/test/integration/main_test.go @@ -28,9 +28,9 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "github.com/yorkie-team/yorkie/api/types" "github.com/yorkie-team/yorkie/client" "github.com/yorkie-team/yorkie/pkg/document" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" "github.com/yorkie-team/yorkie/server" "github.com/yorkie-team/yorkie/server/logging" "github.com/yorkie-team/yorkie/test/helper" @@ -42,8 +42,8 @@ type clientAndDocPair struct { } type watchResponsePair struct { - Type client.WatchResponseType - Peers map[string]types.Presence + Type client.WatchResponseType + Presences map[string]innerpresence.Presence } var defaultServer *server.Yorkie @@ -106,10 +106,7 @@ func clientConn() (*grpc.ClientConn, error) { // activeClients creates and activates the given number of clients. func activeClients(t *testing.T, n int) (clients []*client.Client) { for i := 0; i < n; i++ { - c, err := client.Dial( - defaultServer.RPCAddr(), - client.WithPresence(types.Presence{"name": fmt.Sprintf("name-%d", i)}), - ) + c, err := client.Dial(defaultServer.RPCAddr()) assert.NoError(t, err) assert.NoError(t, c.Activate(context.Background())) diff --git a/test/integration/peer_awareness_test.go b/test/integration/peer_awareness_test.go deleted file mode 100644 index 3fcf29c14..000000000 --- a/test/integration/peer_awareness_test.go +++ /dev/null @@ -1,273 +0,0 @@ -//go:build integration - -/* - * Copyright 2021 The Yorkie Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package integration - -import ( - "context" - gojson "encoding/json" - "fmt" - "strconv" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - - "github.com/yorkie-team/yorkie/api/types" - "github.com/yorkie-team/yorkie/client" - "github.com/yorkie-team/yorkie/pkg/document" - "github.com/yorkie-team/yorkie/pkg/document/json" - "github.com/yorkie-team/yorkie/pkg/document/key" - "github.com/yorkie-team/yorkie/pkg/document/presence" - "github.com/yorkie-team/yorkie/test/helper" -) - -func TestPeerAwareness(t *testing.T) { - clients := activeClients(t, 2) - c1, c2 := clients[0], clients[1] - defer deactivateAndCloseClients(t, clients) - - t.Run("WatchStarted and PeersChanged event test", func(t *testing.T) { - ctx := context.Background() - d1 := document.New(helper.TestDocKey(t)) - d2 := document.New(helper.TestDocKey(t)) - assert.NoError(t, c1.Attach(ctx, d1)) - defer func() { assert.NoError(t, c1.Detach(ctx, d1, false)) }() - assert.NoError(t, c2.Attach(ctx, d2)) - defer func() { assert.NoError(t, c2.Detach(ctx, d2, false)) }() - - var expected []watchResponsePair - var responsePairs []watchResponsePair - wgEvents := sync.WaitGroup{} - wgEvents.Add(1) - - // starting to watch a document - watch1Ctx, cancel1 := context.WithCancel(ctx) - defer cancel1() - wrch, err := c1.Watch(watch1Ctx, d1) - assert.NoError(t, err) - go func() { - defer func() { - wgEvents.Done() - }() - for { - select { - case <-time.After(time.Second): - assert.Fail(t, "timeout") - return - case wr := <-wrch: - if wr.Err != nil { - assert.Fail(t, "unexpected stream closing", wr.Err) - return - } - - if wr.Type == client.PeersChanged { - peers := wr.PeersMapByDoc[d1.Key()] - responsePairs = append(responsePairs, watchResponsePair{ - Type: wr.Type, - Peers: peers, - }) - - if len(peers) == 1 { - return - } - } - } - } - }() - - // 01. PeersChanged is triggered when another client watches the document - expected = append(expected, watchResponsePair{ - Type: client.PeersChanged, - Peers: map[string]types.Presence{ - c1.ID().String(): c1.Presence(), - c2.ID().String(): c2.Presence(), - }, - }) - watch2Ctx, cancel2 := context.WithCancel(ctx) - _, err = c2.Watch(watch2Ctx, d2) - assert.NoError(t, err) - - // 02. PeersChanged is triggered when another client updates its presence - assert.NoError(t, c2.UpdatePresence(ctx, "updated", "true")) - expected = append(expected, watchResponsePair{ - Type: client.PeersChanged, - Peers: map[string]types.Presence{ - c1.ID().String(): c1.Presence(), - c2.ID().String(): c2.Presence(), - }, - }) - - // 03. PeersChanged is triggered when another client closes the watch - expected = append(expected, watchResponsePair{ - Type: client.PeersChanged, - Peers: map[string]types.Presence{ - c1.ID().String(): c1.Presence(), - }, - }) - cancel2() - - wgEvents.Wait() - - assert.Equal(t, expected, responsePairs) - }) - - t.Run("Watch multiple documents test", func(t *testing.T) { - ctx := context.Background() - - d1 := document.New(helper.TestDocKey(t)) - d2 := document.New(helper.TestDocKey(t)) - d3 := document.New(key.Key(helper.TestDocKey(t) + "2")) - assert.NoError(t, c1.Attach(ctx, d1)) - defer func() { assert.NoError(t, c1.Detach(ctx, d1, false)) }() - - var expected []watchResponsePair - var responsePairs []watchResponsePair - wgEvents := sync.WaitGroup{} - wgEvents.Add(1) - - // starting to watch a document - watch1Ctx, cancel1 := context.WithCancel(ctx) - defer cancel1() - wrch, err := c1.Watch(watch1Ctx, d1) - assert.NoError(t, err) - go func() { - defer func() { - wgEvents.Done() - }() - for { - select { - case <-time.After(time.Second): - assert.Fail(t, "timeout") - return - case wr := <-wrch: - if wr.Err != nil { - assert.Fail(t, "unexpected stream closing", wr.Err) - return - } - - if wr.Type == client.PeersChanged { - peers := wr.PeersMapByDoc[d1.Key()] - responsePairs = append(responsePairs, watchResponsePair{ - Type: wr.Type, - Peers: peers, - }) - - if len(peers) == 1 { - return - } - } - } - } - }() - - // 01. PeersChanged is triggered when another client watches the document - expected = append(expected, watchResponsePair{ - Type: client.PeersChanged, - Peers: map[string]types.Presence{ - c1.ID().String(): c1.Presence(), - c2.ID().String(): c2.Presence(), - }, - }) - assert.NoError(t, c2.Attach(ctx, d2)) - defer func() { assert.NoError(t, c2.Detach(ctx, d2, false)) }() - assert.NoError(t, c2.Attach(ctx, d3)) - defer func() { assert.NoError(t, c2.Detach(ctx, d3, false)) }() - - watch2Ctx, cancel2 := context.WithCancel(ctx) - _, err = c2.Watch(watch2Ctx, d2) - assert.NoError(t, err) - - watch3Ctx, cancel3 := context.WithCancel(ctx) - _, err = c2.Watch(watch3Ctx, d3) - assert.NoError(t, err) - - // 02. PeersChanged is triggered when another client closes the watch - expected = append(expected, watchResponsePair{ - Type: client.PeersChanged, - Peers: map[string]types.Presence{ - c1.ID().String(): c1.Presence(), - }, - }) - cancel2() - cancel3() - - wgEvents.Wait() - - assert.Equal(t, expected, responsePairs) - }) - - t.Run("update presence by calling Document.Update test", func(t *testing.T) { - // 01. Create a document and attach it to the clients - ctx := context.Background() - d1 := document.New(helper.TestDocKey(t)) - assert.NoError(t, c1.Attach(ctx, d1)) - defer func() { assert.NoError(t, c1.Detach(ctx, d1, false)) }() - d2 := document.New(helper.TestDocKey(t)) - assert.NoError(t, c2.Attach(ctx, d2)) - defer func() { assert.NoError(t, c2.Detach(ctx, d2, false)) }() - - // 02. Update the root of the document and presence - assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { - root.SetString("key", "value") - p.Set("updated", "true") - return nil - })) - encoded, err := gojson.Marshal(d1.PresenceMap()) - assert.NoError(t, err) - assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"true"}}`, c1.ID()), string(encoded)) - - // 03 Sync documents and check that the presence is updated on the other client - assert.NoError(t, c1.Sync(ctx)) - assert.NoError(t, c2.Sync(ctx)) - encoded, err = gojson.Marshal(d2.PresenceMap()) - assert.NoError(t, err) - assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"true"}}`, c1.ID()), string(encoded)) - }) - - t.Run("presence with snapshot test", func(t *testing.T) { - // 01. Create a document and attach it to the clients - ctx := context.Background() - d1 := document.New(helper.TestDocKey(t)) - assert.NoError(t, c1.Attach(ctx, d1)) - defer func() { assert.NoError(t, c1.Detach(ctx, d1, false)) }() - d2 := document.New(helper.TestDocKey(t)) - assert.NoError(t, c2.Attach(ctx, d2)) - defer func() { assert.NoError(t, c2.Detach(ctx, d2, false)) }() - - // 02. Update the root of the document and presence - for i := 0; i < int(helper.SnapshotThreshold); i++ { - assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { - root.SetString("key", "value") - p.Set("updated", strconv.Itoa(i)) - return nil - })) - } - encoded, err := gojson.Marshal(d1.PresenceMap()) - assert.NoError(t, err) - assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"9"}}`, c1.ID()), string(encoded)) - - // 03 Sync documents and check that the presence is updated on the other client - assert.NoError(t, c1.Sync(ctx)) - assert.NoError(t, c2.Sync(ctx)) - encoded, err = gojson.Marshal(d2.PresenceMap()) - assert.NoError(t, err) - assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"9"}}`, c1.ID()), string(encoded)) - }) -} diff --git a/test/integration/presence_test.go b/test/integration/presence_test.go new file mode 100644 index 000000000..1ff65809c --- /dev/null +++ b/test/integration/presence_test.go @@ -0,0 +1,485 @@ +//go:build integration + +/* + * Copyright 2021 The Yorkie Authors. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package integration + +import ( + "context" + gojson "encoding/json" + "fmt" + "strconv" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/yorkie-team/yorkie/client" + "github.com/yorkie-team/yorkie/pkg/document" + "github.com/yorkie-team/yorkie/pkg/document/innerpresence" + "github.com/yorkie-team/yorkie/pkg/document/json" + "github.com/yorkie-team/yorkie/pkg/document/presence" + "github.com/yorkie-team/yorkie/test/helper" +) + +func TestPresence(t *testing.T) { + clients := activeClients(t, 2) + c1, c2 := clients[0], clients[1] + defer deactivateAndCloseClients(t, clients) + + t.Run("update presence by calling Document.Update test", func(t *testing.T) { + // 01. Create a document and attach it to the clients + ctx := context.Background() + d1 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c1.Attach(ctx, d1)) + defer func() { assert.NoError(t, c1.Detach(ctx, d1)) }() + d2 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c2.Attach(ctx, d2)) + defer func() { assert.NoError(t, c2.Detach(ctx, d2)) }() + + // 02. Update the root of the document and presence + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { + root.SetString("key", "value") + p.Set("updated", "true") + return nil + })) + encoded, err := gojson.Marshal(d1.Presences()) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"true"}}`, c1.ID()), string(encoded)) + + // 03 Sync documents and check that the presence is updated on the other client + assert.NoError(t, c1.Sync(ctx)) + assert.NoError(t, c2.Sync(ctx)) + encoded, err = gojson.Marshal(d2.Presences()) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"true"},"%s":{}}`, c1.ID(), c2.ID()), string(encoded)) + }) + + t.Run("presence with snapshot test", func(t *testing.T) { + // 01. Create a document and attach it to the clients + ctx := context.Background() + d1 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c1.Attach(ctx, d1)) + defer func() { assert.NoError(t, c1.Detach(ctx, d1)) }() + d2 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c2.Attach(ctx, d2)) + defer func() { assert.NoError(t, c2.Detach(ctx, d2)) }() + + // 02. Update the root of the document and presence + for i := 0; i < int(helper.SnapshotThreshold); i++ { + assert.NoError(t, d1.Update(func(root *json.Object, p *presence.Presence) error { + root.SetString("key", "value") + p.Set("updated", strconv.Itoa(i)) + return nil + })) + } + encoded, err := gojson.Marshal(d1.Presences()) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"9"}}`, c1.ID()), string(encoded)) + + // 03 Sync documents and check that the presence is updated on the other client + assert.NoError(t, c1.Sync(ctx)) + assert.NoError(t, c2.Sync(ctx)) + encoded, err = gojson.Marshal(d2.Presences()) + assert.NoError(t, err) + assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"9"},"%s":{}}`, c1.ID(), c2.ID()), string(encoded)) + }) + + t.Run("presence with attach and detach test", func(t *testing.T) { + // 01. Create a document and attach it to the clients + ctx := context.Background() + d1 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c1.Attach(ctx, d1, client.WithPresence(innerpresence.Presence{"key": c1.Key()}))) + d2 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c2.Attach(ctx, d2, client.WithPresence(innerpresence.Presence{"key": c2.Key()}))) + defer func() { assert.NoError(t, c2.Detach(ctx, d2)) }() + + // 02. Check that the presence is updated on the other client. + assert.NoError(t, c1.Sync(ctx)) + assert.Equal(t, innerpresence.Presence{"key": c1.Key()}, d1.MyPresence()) + assert.Equal(t, innerpresence.Presence{"key": c2.Key()}, d1.Presence(c2.ID().String())) + assert.Equal(t, innerpresence.Presence{"key": c2.Key()}, d2.MyPresence()) + assert.Equal(t, innerpresence.Presence{"key": c1.Key()}, d2.Presence(c1.ID().String())) + + // 03. The first client detaches the document and check that the presence is updated on the other client. + assert.NoError(t, c1.Detach(ctx, d1)) + assert.NoError(t, c2.Sync(ctx)) + assert.Equal(t, innerpresence.Presence{"key": c2.Key()}, d2.MyPresence()) + assert.Nil(t, d2.Presence(c1.ID().String())) + }) + + t.Run("presence-related events test", func(t *testing.T) { + // 01. Create two clients and documents and attach them. + ctx := context.Background() + d1 := document.New(helper.TestDocKey(t)) + d2 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c1.Attach(ctx, d1)) + defer func() { assert.NoError(t, c1.Detach(ctx, d1)) }() + assert.NoError(t, c2.Attach(ctx, d2)) + defer func() { assert.NoError(t, c2.Detach(ctx, d2)) }() + + // 02. Watch the first client's document. + var expected []watchResponsePair + var responsePairs []watchResponsePair + wgEvents := sync.WaitGroup{} + wgEvents.Add(1) + + watch1Ctx, cancel1 := context.WithCancel(ctx) + defer cancel1() + wrch, err := c1.Watch(watch1Ctx, d1) + assert.NoError(t, err) + go func() { + defer func() { + wgEvents.Done() + }() + for { + select { + case <-time.After(time.Second): + assert.Fail(t, "timeout") + return + case wr := <-wrch: + if wr.Err != nil { + assert.Fail(t, "unexpected stream closing", wr.Err) + return + } + if wr.Type != client.DocumentChanged { + responsePairs = append(responsePairs, watchResponsePair{ + Type: wr.Type, + Presences: wr.Presences, + }) + } + if len(responsePairs) == 3 { + return + } + } + } + }() + + // 03. Watch the second client's document. + expected = append(expected, watchResponsePair{ + Type: client.DocumentWatched, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): {}, + }, + }) + watch2Ctx, cancel2 := context.WithCancel(ctx) + _, err = c2.Watch(watch2Ctx, d2) + assert.NoError(t, err) + + // 04. Update the second client's presence. + err = d2.Update(func(root *json.Object, p *presence.Presence) error { + p.Set("updated", "true") + return nil + }) + assert.NoError(t, err) + expected = append(expected, watchResponsePair{ + Type: client.PresenceChanged, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): d2.MyPresence(), + }, + }) + assert.NoError(t, c2.Sync(ctx, client.WithDocKey(helper.TestDocKey(t)))) + assert.NoError(t, c1.Sync(ctx, client.WithDocKey(helper.TestDocKey(t)))) + + // 05. Unwatch the second client's document. + expected = append(expected, watchResponsePair{ + Type: client.DocumentUnwatched, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): d2.MyPresence(), + }, + }) + cancel2() + + wgEvents.Wait() + assert.Equal(t, expected, responsePairs) + }) + + t.Run("unwatch after detach events test", func(t *testing.T) { + // 01. Create two clients and documents and attach them. + ctx := context.Background() + d1 := document.New(helper.TestDocKey(t)) + d2 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c1.Attach(ctx, d1)) + defer func() { assert.NoError(t, c1.Detach(ctx, d1)) }() + assert.NoError(t, c2.Attach(ctx, d2)) + + // 02. Watch the first client's document. + var expected []watchResponsePair + var responsePairs []watchResponsePair + wgEvents := sync.WaitGroup{} + wgEvents.Add(1) + + watch1Ctx, cancel1 := context.WithCancel(ctx) + defer cancel1() + wrch, err := c1.Watch(watch1Ctx, d1) + assert.NoError(t, err) + go func() { + defer func() { + wgEvents.Done() + }() + for { + select { + case <-time.After(10 * time.Second): + assert.Fail(t, "timeout") + return + case wr := <-wrch: + if wr.Err != nil { + assert.Fail(t, "unexpected stream closing", wr.Err) + return + } + if wr.Type != client.DocumentChanged { + responsePairs = append(responsePairs, watchResponsePair{ + Type: wr.Type, + Presences: wr.Presences, + }) + } + + if len(responsePairs) == 4 { + return + } + } + } + }() + + // 03. Watch the second client's document. + expected = append(expected, watchResponsePair{ + Type: client.DocumentWatched, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): {}, + }, + }) + watch2Ctx, cancel2 := context.WithCancel(ctx) + defer cancel2() + _, err = c2.Watch(watch2Ctx, d2) + assert.NoError(t, err) + + // 04. Update the second client's presence. + err = d2.Update(func(root *json.Object, p *presence.Presence) error { + p.Set("updated", "true") + return nil + }) + assert.NoError(t, err) + expected = append(expected, watchResponsePair{ + Type: client.PresenceChanged, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): d2.MyPresence(), + }, + }) + assert.NoError(t, c2.Sync(ctx, client.WithDocKey(helper.TestDocKey(t)))) + assert.NoError(t, c1.Sync(ctx, client.WithDocKey(helper.TestDocKey(t)))) + + // 05. Unwatch the second client's document. + expected = append(expected, watchResponsePair{ + Type: client.PresenceChanged, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): nil, + }, + }) + assert.NoError(t, c2.Detach(ctx, d2)) + assert.NoError(t, c1.Sync(ctx, client.WithDocKey(helper.TestDocKey(t)))) + + expected = append(expected, watchResponsePair{ + Type: client.DocumentUnwatched, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): nil, + }, + }) + cancel2() + + wgEvents.Wait() + assert.Equal(t, expected, responsePairs) + }) + + t.Run("detach after unwatch events test", func(t *testing.T) { + // 01. Create two clients and documents and attach them. + ctx := context.Background() + d1 := document.New(helper.TestDocKey(t)) + d2 := document.New(helper.TestDocKey(t)) + assert.NoError(t, c1.Attach(ctx, d1)) + defer func() { assert.NoError(t, c1.Detach(ctx, d1)) }() + assert.NoError(t, c2.Attach(ctx, d2)) + + // 02. Watch the first client's document. + var expected []watchResponsePair + var responsePairs []watchResponsePair + wgEvents := sync.WaitGroup{} + wgEvents.Add(1) + + watch1Ctx, cancel1 := context.WithCancel(ctx) + defer cancel1() + wrch, err := c1.Watch(watch1Ctx, d1) + assert.NoError(t, err) + go func() { + defer func() { + wgEvents.Done() + }() + for { + select { + case <-time.After(10 * time.Second): + assert.Fail(t, "timeout") + return + case wr := <-wrch: + if wr.Err != nil { + assert.Fail(t, "unexpected stream closing", wr.Err) + return + } + if wr.Type != client.DocumentChanged { + responsePairs = append(responsePairs, watchResponsePair{ + Type: wr.Type, + Presences: wr.Presences, + }) + } + + if len(responsePairs) == 3 { + return + } + } + } + }() + + // 03. Watch the second client's document. + expected = append(expected, watchResponsePair{ + Type: client.DocumentWatched, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): {}, + }, + }) + watch2Ctx, cancel2 := context.WithCancel(ctx) + defer cancel2() + _, err = c2.Watch(watch2Ctx, d2) + assert.NoError(t, err) + + // 04. Update the second client's presence. + err = d2.Update(func(root *json.Object, p *presence.Presence) error { + p.Set("updated", "true") + return nil + }) + assert.NoError(t, err) + expected = append(expected, watchResponsePair{ + Type: client.PresenceChanged, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): d2.MyPresence(), + }, + }) + assert.NoError(t, c2.Sync(ctx, client.WithDocKey(helper.TestDocKey(t)))) + assert.NoError(t, c1.Sync(ctx, client.WithDocKey(helper.TestDocKey(t)))) + + // 05. Unwatch the second client's document. + expected = append(expected, watchResponsePair{ + Type: client.DocumentUnwatched, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): d2.MyPresence(), + }, + }) + cancel2() + + assert.NoError(t, c2.Detach(ctx, d2)) + assert.NoError(t, c1.Sync(ctx, client.WithDocKey(helper.TestDocKey(t)))) + + wgEvents.Wait() + assert.Equal(t, expected, responsePairs) + }) + + t.Run("watching multiple documents test", func(t *testing.T) { + ctx := context.Background() + + // 01. Two clients attach two documents with the same key, and watch them. + // But the second client also attaches another document. + d1 := document.New(helper.TestDocKey(t)) + d2 := document.New(helper.TestDocKey(t)) + d3 := document.New(helper.TestDocKey(t) + "2") + assert.NoError(t, c1.Attach(ctx, d1)) + defer func() { assert.NoError(t, c1.Detach(ctx, d1)) }() + + // 02. Watch the first client's document. + var expected []watchResponsePair + var responsePairs []watchResponsePair + wgEvents := sync.WaitGroup{} + wgEvents.Add(1) + + watch1Ctx, cancel1 := context.WithCancel(ctx) + defer cancel1() + wrch, err := c1.Watch(watch1Ctx, d1) + assert.NoError(t, err) + go func() { + defer func() { + wgEvents.Done() + }() + for { + select { + case <-time.After(time.Second): + assert.Fail(t, "timeout") + return + case wr := <-wrch: + if wr.Err != nil { + assert.Fail(t, "unexpected stream closing", wr.Err) + return + } + + if wr.Type != client.DocumentChanged { + responsePairs = append(responsePairs, watchResponsePair{ + Type: wr.Type, + Presences: wr.Presences, + }) + } + + if len(responsePairs) == 2 { + return + } + } + } + }() + + // 03. The second client attaches a document with the same key as the first client's document + // and another document with a different key. + assert.NoError(t, c2.Attach(ctx, d2)) + defer func() { assert.NoError(t, c2.Detach(ctx, d2)) }() + assert.NoError(t, c2.Attach(ctx, d3)) + defer func() { assert.NoError(t, c2.Detach(ctx, d3)) }() + + // 04. The second client watches the documents attached by itself. + expected = append(expected, watchResponsePair{ + Type: client.DocumentWatched, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): d2.MyPresence(), + }, + }) + watch2Ctx, cancel2 := context.WithCancel(ctx) + _, err = c2.Watch(watch2Ctx, d2) + assert.NoError(t, err) + assert.NoError(t, c1.Sync(ctx, client.WithDocKey(helper.TestDocKey(t)))) + + watch3Ctx, cancel3 := context.WithCancel(ctx) + _, err = c2.Watch(watch3Ctx, d3) + assert.NoError(t, err) + + // 05. The second client unwatch the documents attached by itself. + expected = append(expected, watchResponsePair{ + Type: client.DocumentUnwatched, + Presences: map[string]innerpresence.Presence{ + c2.ID().String(): {}, + }, + }) + cancel2() + cancel3() + + wgEvents.Wait() + + assert.Equal(t, expected, responsePairs) + }) +} diff --git a/test/integration/retention_test.go b/test/integration/retention_test.go index 65120919f..e879a3695 100644 --- a/test/integration/retention_test.go +++ b/test/integration/retention_test.go @@ -86,7 +86,7 @@ func TestRetention(t *testing.T) { doc := document.New(helper.TestDocKey(t)) assert.NoError(t, cli.Attach(ctx, doc)) - defer func() { assert.NoError(t, cli.Detach(ctx, doc, false)) }() + defer func() { assert.NoError(t, cli.Detach(ctx, doc)) }() assert.NoError(t, doc.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewArray("todos") @@ -148,7 +148,7 @@ func TestRetention(t *testing.T) { ctx := context.Background() d1 := document.New(helper.TestDocKey(t)) assert.NoError(t, cli1.Attach(ctx, d1)) - defer func() { assert.NoError(t, cli1.Detach(ctx, d1, false)) }() + defer func() { assert.NoError(t, cli1.Detach(ctx, d1)) }() err = d1.Update(func(root *json.Object, p *presence.Presence) error { root.SetNewText("k1") @@ -223,7 +223,7 @@ func TestRetention(t *testing.T) { d2 := document.New(helper.TestDocKey(t)) assert.NoError(t, cli2.Attach(ctx, d2)) - defer func() { assert.NoError(t, cli2.Detach(ctx, d2, false)) }() + defer func() { assert.NoError(t, cli2.Detach(ctx, d2)) }() // Create 6 changes for _, edit := range edits { From 890d835db8c7708fce1ed344cfbd13258d7ff61d Mon Sep 17 00:00:00 2001 From: JOOHOJANG <46807540+JOOHOJANG@users.noreply.github.com> Date: Thu, 13 Jul 2023 19:22:22 +0900 Subject: [PATCH 5/5] Allow users to pass multi nodes when calling Tree.edit (#579) * Allow users to pass multi nodes when calling Tree.edit * Add nodes validation to Tree.edit --- api/converter/from_pb.go | 26 +- api/converter/to_bytes.go | 20 + api/converter/to_pb.go | 2 +- api/yorkie/v1/resources.pb.go | 573 ++++++++++++++++++--------- api/yorkie/v1/resources.proto | 6 +- pkg/document/crdt/tree.go | 52 ++- pkg/document/crdt/tree_test.go | 146 ++++--- pkg/document/json/tree.go | 195 +++++---- pkg/document/operations/tree_edit.go | 33 +- test/integration/tree_test.go | 46 +++ 10 files changed, 750 insertions(+), 349 deletions(-) diff --git a/api/converter/from_pb.go b/api/converter/from_pb.go index 7c31f144f..2386ba766 100644 --- a/api/converter/from_pb.go +++ b/api/converter/from_pb.go @@ -547,7 +547,7 @@ func fromTreeEdit(pbTreeEdit *api.Operation_TreeEdit) (*operations.TreeEdit, err return nil, err } - node, err := FromTreeNodes(pbTreeEdit.Content) + nodes, err := FromTreeNodesWhenEdit(pbTreeEdit.Contents) if err != nil { return nil, err } @@ -556,7 +556,7 @@ func fromTreeEdit(pbTreeEdit *api.Operation_TreeEdit) (*operations.TreeEdit, err parentCreatedAt, from, to, - node, + nodes, executedAt, ), nil } @@ -653,6 +653,28 @@ func FromTreeNodes(pbNodes []*api.TreeNode) (*crdt.TreeNode, error) { return crdt.NewTree(root, nil).Root(), nil } +// FromTreeNodesWhenEdit converts protobuf tree nodes to array of crdt.TreeNode. +// in each element in array, the last node in slice is the root node, because the slice is in post-order. +func FromTreeNodesWhenEdit(pbNodes []*api.TreeNodes) ([]*crdt.TreeNode, error) { + if len(pbNodes) == 0 { + return nil, nil + } + + var treeNodes []*crdt.TreeNode + + for _, pbNode := range pbNodes { + treeNode, err := FromTreeNodes(pbNode.Content) + + if err != nil { + return nil, err + } + + treeNodes = append(treeNodes, treeNode) + } + + return treeNodes, nil +} + func fromTreeNode(pbNode *api.TreeNode) (*crdt.TreeNode, error) { pos, err := fromTreePos(pbNode.Pos) if err != nil { diff --git a/api/converter/to_bytes.go b/api/converter/to_bytes.go index 6eeeaee05..29fbd54b5 100644 --- a/api/converter/to_bytes.go +++ b/api/converter/to_bytes.go @@ -261,6 +261,26 @@ func ToTreeNodes(treeNode *crdt.TreeNode) []*api.TreeNode { return pbTreeNodes } +// ToTreeNodesWhenEdit converts a TreeNodes to a slice of two-dimensional array of TreeNodes in post-order traversal. +func ToTreeNodesWhenEdit(treeNodes []*crdt.TreeNode) []*api.TreeNodes { + pbTreeNodes := make([]*api.TreeNodes, len(treeNodes)) + + if len(treeNodes) == 0 { + return pbTreeNodes + } + + for i, treeNode := range treeNodes { + var pbTreeNode []*api.TreeNode + + pbTreeNode = append(pbTreeNode, ToTreeNodes(treeNode)[:]...) + + pbTreeNodes[i] = &api.TreeNodes{} + pbTreeNodes[i].Content = append(pbTreeNodes[i].Content, pbTreeNode[:]...) + } + + return pbTreeNodes +} + func toTreeNode(treeNode *crdt.TreeNode, depth int) *api.TreeNode { var attrs map[string]*api.NodeAttr if treeNode.Attrs != nil { diff --git a/api/converter/to_pb.go b/api/converter/to_pb.go index 6d9efa52e..ebaec09e8 100644 --- a/api/converter/to_pb.go +++ b/api/converter/to_pb.go @@ -409,7 +409,7 @@ func toTreeEdit(e *operations.TreeEdit) (*api.Operation_TreeEdit_, error) { ParentCreatedAt: ToTimeTicket(e.ParentCreatedAt()), From: toTreePos(e.FromPos()), To: toTreePos(e.ToPos()), - Content: ToTreeNodes(e.Content()), + Contents: ToTreeNodesWhenEdit(e.Contents()), ExecutedAt: ToTimeTicket(e.ExecutedAt()), }, }, nil diff --git a/api/yorkie/v1/resources.pb.go b/api/yorkie/v1/resources.pb.go index e736967f1..3e8c98cc3 100644 --- a/api/yorkie/v1/resources.pb.go +++ b/api/yorkie/v1/resources.pb.go @@ -140,7 +140,7 @@ func (x PresenceChange_ChangeType) String() string { } func (PresenceChange_ChangeType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{18, 0} + return fileDescriptor_36361b2f5d0f0896, []int{19, 0} } // /////////////////////////////////////// @@ -1210,14 +1210,14 @@ func (m *Operation_Increase) GetExecutedAt() *TimeTicket { } type Operation_TreeEdit struct { - ParentCreatedAt *TimeTicket `protobuf:"bytes,1,opt,name=parent_created_at,json=parentCreatedAt,proto3" json:"parent_created_at,omitempty"` - From *TreePos `protobuf:"bytes,2,opt,name=from,proto3" json:"from,omitempty"` - To *TreePos `protobuf:"bytes,3,opt,name=to,proto3" json:"to,omitempty"` - Content []*TreeNode `protobuf:"bytes,4,rep,name=content,proto3" json:"content,omitempty"` - ExecutedAt *TimeTicket `protobuf:"bytes,5,opt,name=executed_at,json=executedAt,proto3" json:"executed_at,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + ParentCreatedAt *TimeTicket `protobuf:"bytes,1,opt,name=parent_created_at,json=parentCreatedAt,proto3" json:"parent_created_at,omitempty"` + From *TreePos `protobuf:"bytes,2,opt,name=from,proto3" json:"from,omitempty"` + To *TreePos `protobuf:"bytes,3,opt,name=to,proto3" json:"to,omitempty"` + Contents []*TreeNodes `protobuf:"bytes,4,rep,name=contents,proto3" json:"contents,omitempty"` + ExecutedAt *TimeTicket `protobuf:"bytes,5,opt,name=executed_at,json=executedAt,proto3" json:"executed_at,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Operation_TreeEdit) Reset() { *m = Operation_TreeEdit{} } @@ -1274,9 +1274,9 @@ func (m *Operation_TreeEdit) GetTo() *TreePos { return nil } -func (m *Operation_TreeEdit) GetContent() []*TreeNode { +func (m *Operation_TreeEdit) GetContents() []*TreeNodes { if m != nil { - return m.Content + return m.Contents } return nil } @@ -2423,6 +2423,53 @@ func (m *TreeNode) GetAttributes() map[string]*NodeAttr { return nil } +type TreeNodes struct { + Content []*TreeNode `protobuf:"bytes,1,rep,name=content,proto3" json:"content,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TreeNodes) Reset() { *m = TreeNodes{} } +func (m *TreeNodes) String() string { return proto.CompactTextString(m) } +func (*TreeNodes) ProtoMessage() {} +func (*TreeNodes) Descriptor() ([]byte, []int) { + return fileDescriptor_36361b2f5d0f0896, []int{13} +} +func (m *TreeNodes) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TreeNodes) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TreeNodes.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TreeNodes) XXX_Merge(src proto.Message) { + xxx_messageInfo_TreeNodes.Merge(m, src) +} +func (m *TreeNodes) XXX_Size() int { + return m.Size() +} +func (m *TreeNodes) XXX_DiscardUnknown() { + xxx_messageInfo_TreeNodes.DiscardUnknown(m) +} + +var xxx_messageInfo_TreeNodes proto.InternalMessageInfo + +func (m *TreeNodes) GetContent() []*TreeNode { + if m != nil { + return m.Content + } + return nil +} + type TreePos struct { CreatedAt *TimeTicket `protobuf:"bytes,1,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` Offset int32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` @@ -2435,7 +2482,7 @@ func (m *TreePos) Reset() { *m = TreePos{} } func (m *TreePos) String() string { return proto.CompactTextString(m) } func (*TreePos) ProtoMessage() {} func (*TreePos) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{13} + return fileDescriptor_36361b2f5d0f0896, []int{14} } func (m *TreePos) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2491,7 +2538,7 @@ func (m *User) Reset() { *m = User{} } func (m *User) String() string { return proto.CompactTextString(m) } func (*User) ProtoMessage() {} func (*User) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{14} + return fileDescriptor_36361b2f5d0f0896, []int{15} } func (m *User) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2560,7 +2607,7 @@ func (m *Project) Reset() { *m = Project{} } func (m *Project) String() string { return proto.CompactTextString(m) } func (*Project) ProtoMessage() {} func (*Project) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{15} + return fileDescriptor_36361b2f5d0f0896, []int{16} } func (m *Project) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2666,7 +2713,7 @@ func (m *UpdatableProjectFields) Reset() { *m = UpdatableProjectFields{} func (m *UpdatableProjectFields) String() string { return proto.CompactTextString(m) } func (*UpdatableProjectFields) ProtoMessage() {} func (*UpdatableProjectFields) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{16} + return fileDescriptor_36361b2f5d0f0896, []int{17} } func (m *UpdatableProjectFields) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2738,7 +2785,7 @@ func (m *UpdatableProjectFields_AuthWebhookMethods) String() string { } func (*UpdatableProjectFields_AuthWebhookMethods) ProtoMessage() {} func (*UpdatableProjectFields_AuthWebhookMethods) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{16, 0} + return fileDescriptor_36361b2f5d0f0896, []int{17, 0} } func (m *UpdatableProjectFields_AuthWebhookMethods) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2790,7 +2837,7 @@ func (m *DocumentSummary) Reset() { *m = DocumentSummary{} } func (m *DocumentSummary) String() string { return proto.CompactTextString(m) } func (*DocumentSummary) ProtoMessage() {} func (*DocumentSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{17} + return fileDescriptor_36361b2f5d0f0896, []int{18} } func (m *DocumentSummary) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2873,7 +2920,7 @@ func (m *PresenceChange) Reset() { *m = PresenceChange{} } func (m *PresenceChange) String() string { return proto.CompactTextString(m) } func (*PresenceChange) ProtoMessage() {} func (*PresenceChange) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{18} + return fileDescriptor_36361b2f5d0f0896, []int{19} } func (m *PresenceChange) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2927,7 +2974,7 @@ func (m *Presence) Reset() { *m = Presence{} } func (m *Presence) String() string { return proto.CompactTextString(m) } func (*Presence) ProtoMessage() {} func (*Presence) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{19} + return fileDescriptor_36361b2f5d0f0896, []int{20} } func (m *Presence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2975,7 +3022,7 @@ func (m *Checkpoint) Reset() { *m = Checkpoint{} } func (m *Checkpoint) String() string { return proto.CompactTextString(m) } func (*Checkpoint) ProtoMessage() {} func (*Checkpoint) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{20} + return fileDescriptor_36361b2f5d0f0896, []int{21} } func (m *Checkpoint) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3031,7 +3078,7 @@ func (m *TextNodePos) Reset() { *m = TextNodePos{} } func (m *TextNodePos) String() string { return proto.CompactTextString(m) } func (*TextNodePos) ProtoMessage() {} func (*TextNodePos) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{21} + return fileDescriptor_36361b2f5d0f0896, []int{22} } func (m *TextNodePos) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3094,7 +3141,7 @@ func (m *TimeTicket) Reset() { *m = TimeTicket{} } func (m *TimeTicket) String() string { return proto.CompactTextString(m) } func (*TimeTicket) ProtoMessage() {} func (*TimeTicket) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{22} + return fileDescriptor_36361b2f5d0f0896, []int{23} } func (m *TimeTicket) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3156,7 +3203,7 @@ func (m *DocEvent) Reset() { *m = DocEvent{} } func (m *DocEvent) String() string { return proto.CompactTextString(m) } func (*DocEvent) ProtoMessage() {} func (*DocEvent) Descriptor() ([]byte, []int) { - return fileDescriptor_36361b2f5d0f0896, []int{23} + return fileDescriptor_36361b2f5d0f0896, []int{24} } func (m *DocEvent) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3239,6 +3286,7 @@ func init() { proto.RegisterType((*TextNodeID)(nil), "yorkie.v1.TextNodeID") proto.RegisterType((*TreeNode)(nil), "yorkie.v1.TreeNode") proto.RegisterMapType((map[string]*NodeAttr)(nil), "yorkie.v1.TreeNode.AttributesEntry") + proto.RegisterType((*TreeNodes)(nil), "yorkie.v1.TreeNodes") proto.RegisterType((*TreePos)(nil), "yorkie.v1.TreePos") proto.RegisterType((*User)(nil), "yorkie.v1.User") proto.RegisterType((*Project)(nil), "yorkie.v1.Project") @@ -3257,165 +3305,166 @@ func init() { func init() { proto.RegisterFile("yorkie/v1/resources.proto", fileDescriptor_36361b2f5d0f0896) } var fileDescriptor_36361b2f5d0f0896 = []byte{ - // 2523 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xdf, 0x6f, 0x1b, 0x59, - 0xf5, 0xcf, 0x8c, 0x7f, 0xce, 0x49, 0x9a, 0xb8, 0x37, 0xfd, 0xe1, 0xba, 0x6d, 0xbe, 0x59, 0x6f, - 0xbf, 0x25, 0x6d, 0xc1, 0x69, 0x43, 0x0b, 0xcb, 0x96, 0x05, 0x1c, 0x7b, 0xda, 0xa4, 0x9b, 0x3a, - 0x61, 0xec, 0xb4, 0x74, 0x05, 0x1a, 0x4d, 0x66, 0x6e, 0x9b, 0xd9, 0xd8, 0x1e, 0xef, 0xcc, 0xd8, - 0x5b, 0xbf, 0xa1, 0x15, 0x48, 0xbc, 0xf2, 0xc6, 0xbf, 0xc0, 0x9f, 0xb0, 0x8f, 0xf0, 0x80, 0x40, - 0x08, 0x81, 0xc4, 0x4a, 0xbc, 0x42, 0x79, 0x40, 0xf0, 0x86, 0x10, 0xbc, 0x21, 0xa1, 0xfb, 0x6b, - 0x7c, 0x6d, 0x8f, 0x5d, 0x37, 0x44, 0xab, 0x56, 0xbc, 0xf9, 0xde, 0xfb, 0x39, 0xf7, 0x9e, 0x73, - 0xcf, 0xe7, 0x9c, 0x7b, 0xae, 0xef, 0xc0, 0x85, 0xbe, 0xe7, 0x1f, 0xb9, 0x78, 0xbd, 0x77, 0x6b, - 0xdd, 0xc7, 0x81, 0xd7, 0xf5, 0x6d, 0x1c, 0x94, 0x3a, 0xbe, 0x17, 0x7a, 0x48, 0x63, 0x43, 0xa5, - 0xde, 0xad, 0xc2, 0xff, 0x3d, 0xf3, 0xbc, 0x67, 0x4d, 0xbc, 0x4e, 0x07, 0x0e, 0xba, 0x4f, 0xd7, - 0x43, 0xb7, 0x85, 0x83, 0xd0, 0x6a, 0x75, 0x18, 0xb6, 0xb0, 0x32, 0x0a, 0xf8, 0xd8, 0xb7, 0x3a, - 0x1d, 0xec, 0xf3, 0xb9, 0x8a, 0xbf, 0x56, 0x20, 0x5b, 0x6f, 0x5b, 0x9d, 0xe0, 0xd0, 0x0b, 0xd1, - 0x75, 0x48, 0xfa, 0x9e, 0x17, 0xe6, 0x95, 0x55, 0x65, 0x6d, 0x7e, 0xe3, 0x5c, 0x29, 0x5a, 0xa7, - 0xf4, 0xa0, 0xbe, 0x5b, 0xd3, 0x9b, 0xb8, 0x85, 0xdb, 0xa1, 0x41, 0x31, 0xe8, 0x5b, 0xa0, 0x75, - 0x7c, 0x1c, 0xe0, 0xb6, 0x8d, 0x83, 0xbc, 0xba, 0x9a, 0x58, 0x9b, 0xdf, 0x28, 0x4a, 0x02, 0x62, - 0xce, 0xd2, 0x9e, 0x00, 0xe9, 0xed, 0xd0, 0xef, 0x1b, 0x03, 0xa1, 0xc2, 0xb7, 0x61, 0x71, 0x78, - 0x10, 0xe5, 0x20, 0x71, 0x84, 0xfb, 0x74, 0x79, 0xcd, 0x20, 0x3f, 0xd1, 0x35, 0x48, 0xf5, 0xac, - 0x66, 0x17, 0xe7, 0x55, 0xaa, 0xd2, 0xb2, 0xb4, 0x82, 0x90, 0x35, 0x18, 0xe2, 0x5d, 0xf5, 0x1d, - 0xa5, 0xf8, 0x63, 0x15, 0xa0, 0x72, 0x68, 0xb5, 0x9f, 0xe1, 0x3d, 0xcb, 0x3e, 0x42, 0x6f, 0xc1, - 0x82, 0xe3, 0xd9, 0x5d, 0xa2, 0xb5, 0x39, 0x98, 0x78, 0x5e, 0xf4, 0xbd, 0x8f, 0xfb, 0xe8, 0x0e, - 0x80, 0x7d, 0x88, 0xed, 0xa3, 0x8e, 0xe7, 0xb6, 0x43, 0xbe, 0xca, 0x59, 0x69, 0x95, 0x4a, 0x34, - 0x68, 0x48, 0x40, 0x54, 0x80, 0x6c, 0xc0, 0x2d, 0xcc, 0x27, 0x56, 0x95, 0xb5, 0x05, 0x23, 0x6a, - 0xa3, 0x1b, 0x90, 0xb1, 0xa9, 0x0e, 0x41, 0x3e, 0x49, 0xf7, 0xe5, 0xf4, 0xd0, 0x7c, 0x64, 0xc4, - 0x10, 0x08, 0x54, 0x86, 0xd3, 0x2d, 0xb7, 0x6d, 0x06, 0xfd, 0xb6, 0x8d, 0x1d, 0x33, 0x74, 0xed, - 0x23, 0x1c, 0xe6, 0x53, 0x63, 0x6a, 0x34, 0xdc, 0x16, 0x6e, 0xd0, 0x41, 0x63, 0xa9, 0xe5, 0xb6, - 0xeb, 0x14, 0xce, 0x3a, 0xd0, 0x65, 0x00, 0x37, 0x30, 0x7d, 0xdc, 0xf2, 0x7a, 0xd8, 0xc9, 0xa7, - 0x57, 0x95, 0xb5, 0xac, 0xa1, 0xb9, 0x81, 0xc1, 0x3a, 0x8a, 0x3f, 0x53, 0x20, 0xcd, 0x56, 0x45, - 0x6f, 0x83, 0xea, 0x3a, 0xdc, 0xbb, 0xcb, 0x63, 0x4a, 0x6d, 0x57, 0x0d, 0xd5, 0x75, 0x50, 0x1e, - 0x32, 0x2d, 0x1c, 0x04, 0xd6, 0x33, 0xb6, 0xe9, 0x9a, 0x21, 0x9a, 0xe8, 0x36, 0x80, 0xd7, 0xc1, - 0xbe, 0x15, 0xba, 0x5e, 0x3b, 0xc8, 0x27, 0xa8, 0x6d, 0x67, 0xa4, 0x69, 0x76, 0xc5, 0xa0, 0x21, - 0xe1, 0xd0, 0x26, 0x2c, 0x09, 0x9f, 0x9b, 0xcc, 0xea, 0x7c, 0x92, 0x6a, 0x70, 0x21, 0xc6, 0x99, - 0x7c, 0x7b, 0x16, 0x3b, 0x43, 0xed, 0xe2, 0x0f, 0x15, 0xc8, 0x0a, 0x25, 0x89, 0xbd, 0x76, 0xd3, - 0x25, 0x3e, 0x0d, 0xf0, 0x47, 0xd4, 0x9a, 0x53, 0x86, 0xc6, 0x7a, 0xea, 0xf8, 0x23, 0xf4, 0x16, - 0x40, 0x80, 0xfd, 0x1e, 0xf6, 0xe9, 0x30, 0x31, 0x21, 0xb1, 0xa9, 0xde, 0x54, 0x0c, 0x8d, 0xf5, - 0x12, 0xc8, 0x25, 0xc8, 0x34, 0xad, 0x56, 0xc7, 0xf3, 0x99, 0xf3, 0xd8, 0xb8, 0xe8, 0x42, 0x17, - 0x20, 0x6b, 0xd9, 0xa1, 0xe7, 0x9b, 0xae, 0x43, 0x35, 0x5d, 0x30, 0x32, 0xb4, 0xbd, 0xed, 0x14, - 0x7f, 0x55, 0x00, 0x2d, 0xb2, 0x12, 0x7d, 0x11, 0x12, 0x01, 0x16, 0xd1, 0x92, 0x8f, 0xdb, 0x88, - 0x52, 0x1d, 0x87, 0x5b, 0x73, 0x06, 0x81, 0x11, 0xb4, 0xe5, 0x38, 0x9c, 0x62, 0xf1, 0xe8, 0xb2, - 0xe3, 0x10, 0xb4, 0xe5, 0x38, 0x68, 0x1d, 0x92, 0xc4, 0x7d, 0x54, 0xbf, 0xe1, 0xad, 0x1a, 0xc0, - 0x1f, 0x7a, 0x3d, 0xbc, 0x35, 0x67, 0x50, 0x20, 0xba, 0x03, 0x69, 0x46, 0x01, 0xbe, 0xbb, 0x17, - 0x63, 0x45, 0x18, 0x29, 0xb6, 0xe6, 0x0c, 0x0e, 0x26, 0xeb, 0x60, 0xc7, 0x15, 0x94, 0x8b, 0x5f, - 0x47, 0x77, 0x5c, 0x62, 0x05, 0x05, 0x92, 0x75, 0x02, 0xdc, 0xc4, 0x76, 0x48, 0x99, 0x36, 0x69, - 0x9d, 0x3a, 0x85, 0x90, 0x75, 0x18, 0x18, 0x6d, 0x40, 0x2a, 0x08, 0xfb, 0x4d, 0x9c, 0xcf, 0x50, - 0xa9, 0x42, 0xbc, 0x14, 0x41, 0x6c, 0xcd, 0x19, 0x0c, 0x8a, 0xee, 0x42, 0xd6, 0x6d, 0xdb, 0x3e, - 0xb6, 0x02, 0x9c, 0xcf, 0x52, 0xb1, 0xcb, 0xb1, 0x62, 0xdb, 0x1c, 0xb4, 0x35, 0x67, 0x44, 0x02, - 0xe8, 0xeb, 0xa0, 0x85, 0x3e, 0xc6, 0x26, 0xb5, 0x4e, 0x9b, 0x22, 0xdd, 0xf0, 0x31, 0xe6, 0x16, - 0x66, 0x43, 0xfe, 0x1b, 0x7d, 0x13, 0x80, 0x4a, 0x33, 0x9d, 0x81, 0x8a, 0xaf, 0x4c, 0x14, 0x17, - 0x7a, 0xd3, 0x15, 0x69, 0xa3, 0xf0, 0x0b, 0x05, 0x12, 0x75, 0x1c, 0x92, 0xf8, 0xee, 0x58, 0x3e, - 0x21, 0x2b, 0xd1, 0x2b, 0xc4, 0x8e, 0x69, 0x09, 0xc6, 0x4c, 0x8a, 0x6f, 0x86, 0xaf, 0x30, 0x78, - 0x39, 0x14, 0x59, 0x51, 0x1d, 0x64, 0xc5, 0x0d, 0x91, 0x15, 0x19, 0x3b, 0x2e, 0xc5, 0x27, 0xea, - 0xba, 0xdb, 0xea, 0x34, 0x45, 0x7a, 0x44, 0x5f, 0x81, 0x79, 0xfc, 0x1c, 0xdb, 0x5d, 0xae, 0x42, - 0x72, 0x9a, 0x0a, 0x20, 0x90, 0xe5, 0xb0, 0xf0, 0x0f, 0x05, 0x12, 0x65, 0xc7, 0x39, 0x09, 0x43, - 0xde, 0xa3, 0x99, 0xa0, 0x27, 0x4f, 0xa0, 0x4e, 0x9b, 0xe0, 0x14, 0x41, 0x0f, 0xc4, 0x3f, 0x4f, - 0xab, 0xff, 0xa5, 0x40, 0x92, 0x84, 0xd7, 0x6b, 0x60, 0xf6, 0x6d, 0x00, 0x49, 0x32, 0x31, 0x4d, - 0x52, 0xb3, 0x23, 0xa9, 0xe3, 0x1a, 0xfe, 0xa9, 0x02, 0x69, 0x96, 0x24, 0x4e, 0xc2, 0xf4, 0x61, - 0xdd, 0xd5, 0xe3, 0xe9, 0x9e, 0x98, 0x55, 0xf7, 0x9f, 0x27, 0x21, 0x49, 0xa3, 0xf7, 0x04, 0x34, - 0xbf, 0x0e, 0xc9, 0xa7, 0xbe, 0xd7, 0xe2, 0x3a, 0xcb, 0xa5, 0x50, 0x03, 0x3f, 0x0f, 0x6b, 0x9e, - 0x83, 0xf7, 0xbc, 0xc0, 0xa0, 0x18, 0x74, 0x15, 0xd4, 0xd0, 0xe3, 0x6a, 0x4e, 0x42, 0xaa, 0xa1, - 0x87, 0x0e, 0xe1, 0xfc, 0x40, 0x1f, 0xb3, 0x65, 0x75, 0xcc, 0x83, 0xbe, 0x49, 0x8f, 0x16, 0x5e, - 0x28, 0x6c, 0x4c, 0x4c, 0xbf, 0xa5, 0x48, 0xb3, 0x87, 0x56, 0x67, 0xb3, 0x5f, 0x26, 0x42, 0xac, - 0xa0, 0x5a, 0xb6, 0xc7, 0x47, 0xc8, 0x19, 0x6e, 0x7b, 0xed, 0x10, 0xb7, 0x59, 0x62, 0xd7, 0x0c, - 0xd1, 0x1c, 0xdd, 0xdb, 0xf4, 0x8c, 0x7b, 0x8b, 0xb6, 0x01, 0xac, 0x30, 0xf4, 0xdd, 0x83, 0x6e, - 0x88, 0x83, 0x7c, 0x86, 0xaa, 0x7b, 0x6d, 0xb2, 0xba, 0xe5, 0x08, 0xcb, 0xb4, 0x94, 0x84, 0x0b, - 0xdf, 0x83, 0xfc, 0x24, 0x6b, 0x62, 0x2a, 0xc0, 0x1b, 0xc3, 0x15, 0xe0, 0x04, 0x55, 0x07, 0x35, - 0x60, 0xe1, 0x3d, 0x58, 0x1a, 0x59, 0x3d, 0x66, 0xd6, 0x33, 0xf2, 0xac, 0x9a, 0x2c, 0xfe, 0x07, - 0x05, 0xd2, 0xec, 0xf4, 0x7a, 0x5d, 0x69, 0x74, 0xdc, 0xd0, 0xfe, 0x93, 0x0a, 0x29, 0x7a, 0x38, - 0xbd, 0xae, 0x86, 0x3d, 0x18, 0xe2, 0x18, 0x0b, 0x89, 0xeb, 0x93, 0x0b, 0x85, 0x69, 0x24, 0x1b, - 0xdd, 0xa4, 0xd4, 0xac, 0x9b, 0xf4, 0x5f, 0xb2, 0xe7, 0x53, 0x05, 0xb2, 0xa2, 0x1c, 0x39, 0x89, - 0x6d, 0xde, 0x18, 0x66, 0xff, 0x71, 0xce, 0xbc, 0x99, 0xd3, 0xe7, 0xf7, 0x55, 0xc8, 0x8a, 0x62, - 0xe8, 0x24, 0x74, 0xbf, 0x3a, 0x44, 0x11, 0x24, 0x4b, 0xf9, 0x58, 0xa2, 0x47, 0x51, 0xa2, 0x47, - 0x1c, 0x8a, 0x50, 0xe3, 0x4b, 0x83, 0x84, 0xc6, 0x78, 0xb1, 0x3c, 0x02, 0x24, 0x3c, 0x9a, 0x98, - 0xe5, 0x52, 0xaf, 0x10, 0x22, 0x5a, 0x54, 0xd0, 0xbd, 0x6e, 0x7b, 0x50, 0x8b, 0x09, 0x8f, 0xd2, - 0xf4, 0x9a, 0xf4, 0x35, 0x0c, 0x91, 0xcd, 0x34, 0x24, 0x0f, 0x3c, 0xa7, 0x5f, 0xfc, 0xbb, 0x02, - 0xa7, 0xc7, 0x38, 0x3c, 0x52, 0x31, 0x28, 0x33, 0x56, 0x0c, 0x37, 0x21, 0x4b, 0x2f, 0xbb, 0x2f, - 0xad, 0x32, 0x32, 0x14, 0xc6, 0x2a, 0x13, 0x7e, 0x63, 0x7e, 0x79, 0x55, 0xc5, 0x81, 0xe5, 0x10, - 0xad, 0x41, 0x32, 0xec, 0x77, 0xd8, 0x15, 0x6b, 0x71, 0xe8, 0xee, 0xfb, 0x88, 0xd8, 0xd7, 0xe8, - 0x77, 0xb0, 0x41, 0x11, 0x03, 0xfb, 0x53, 0xf4, 0x06, 0xc9, 0x1a, 0xc5, 0x9f, 0x9e, 0x82, 0x79, - 0xc9, 0x66, 0x54, 0x85, 0xf9, 0x0f, 0x03, 0xaf, 0x6d, 0x7a, 0x07, 0x1f, 0x92, 0x1b, 0x15, 0x33, - 0xf7, 0xad, 0xf8, 0x20, 0xa7, 0xbf, 0x77, 0x29, 0x70, 0x6b, 0xce, 0x00, 0x22, 0xc7, 0x5a, 0xa8, - 0x0c, 0xb4, 0x65, 0x5a, 0xbe, 0x6f, 0xf5, 0xb9, 0xfd, 0xab, 0x53, 0x26, 0x29, 0x13, 0x1c, 0xb9, - 0xae, 0x10, 0x29, 0xda, 0x60, 0xff, 0xe6, 0xb8, 0x2d, 0x37, 0x74, 0xa3, 0x3b, 0xe7, 0xa4, 0x19, - 0xf6, 0x04, 0x8e, 0xcc, 0x10, 0x09, 0xa1, 0x5b, 0x90, 0x0c, 0xf1, 0x73, 0x41, 0xa3, 0x8b, 0x13, - 0x84, 0x49, 0xca, 0x27, 0x57, 0x49, 0x02, 0x45, 0xef, 0x92, 0xa0, 0xee, 0xb6, 0x43, 0xec, 0xf3, - 0x3a, 0x64, 0x65, 0x82, 0x54, 0x85, 0xa1, 0xb6, 0xe6, 0x0c, 0x21, 0x40, 0x97, 0xf3, 0xb1, 0xb8, - 0x4e, 0x4e, 0x5c, 0xce, 0xc7, 0xf4, 0x86, 0x4c, 0xa0, 0x85, 0xcf, 0x14, 0x80, 0xc1, 0x1e, 0xa2, - 0x35, 0x48, 0xb5, 0x3d, 0x07, 0x07, 0x79, 0x85, 0x46, 0x92, 0x1c, 0x75, 0xc6, 0x56, 0x83, 0xe6, - 0x13, 0x06, 0x38, 0x66, 0x15, 0x2b, 0x73, 0x32, 0x71, 0x0c, 0x4e, 0x26, 0x67, 0xe3, 0x64, 0xe1, - 0xf7, 0x0a, 0x68, 0x91, 0x57, 0xa7, 0x5a, 0x75, 0xbf, 0xfc, 0xe6, 0x58, 0xf5, 0x57, 0x05, 0xb4, - 0x88, 0x69, 0x51, 0xdc, 0x29, 0xb3, 0xc7, 0x9d, 0x2a, 0xc5, 0xdd, 0x31, 0xef, 0x50, 0xb2, 0xad, - 0xc9, 0x63, 0xd8, 0x9a, 0x9a, 0xd1, 0xd6, 0xdf, 0x2a, 0x90, 0x24, 0x81, 0x81, 0xae, 0x0d, 0x3b, - 0x6f, 0x39, 0xa6, 0x56, 0x7a, 0x33, 0xbc, 0xf7, 0x17, 0x05, 0x32, 0x3c, 0x68, 0xff, 0x17, 0x7c, - 0xe7, 0x63, 0x3c, 0xd5, 0x77, 0xa2, 0x3e, 0x79, 0x13, 0x7c, 0x17, 0x9d, 0xcf, 0x0f, 0x21, 0xc3, - 0xf3, 0x60, 0xcc, 0xf1, 0x7e, 0x13, 0x32, 0x98, 0xe5, 0xd8, 0x98, 0x1b, 0x80, 0xfc, 0x58, 0x20, - 0x60, 0x45, 0x1b, 0x32, 0x3c, 0x01, 0x91, 0xa2, 0xa8, 0x4d, 0x8e, 0x0a, 0x65, 0xac, 0xdc, 0x11, - 0x29, 0x8a, 0x8e, 0x1f, 0x63, 0x91, 0x47, 0x90, 0x25, 0xf2, 0xa4, 0x3c, 0x19, 0xb0, 0x49, 0x91, - 0x2a, 0x10, 0xb2, 0x27, 0xdd, 0x8e, 0x33, 0xdb, 0xde, 0x73, 0x60, 0x39, 0x2c, 0xfe, 0x86, 0x94, - 0xc6, 0x3c, 0x02, 0xd1, 0xff, 0x4b, 0xff, 0xa2, 0x9f, 0x8d, 0x09, 0x51, 0xfe, 0x3f, 0x7a, 0x6c, - 0x05, 0x74, 0xcc, 0xba, 0xe3, 0x0e, 0xcc, 0xbb, 0xed, 0xc0, 0xa4, 0x7f, 0x23, 0xf1, 0x7f, 0xa5, - 0x27, 0xae, 0xad, 0xb9, 0xed, 0x60, 0xcf, 0xc7, 0xbd, 0x6d, 0x07, 0x55, 0x86, 0x2a, 0xc6, 0x14, - 0x25, 0xe6, 0xdb, 0x31, 0x52, 0x53, 0xaf, 0xeb, 0xc6, 0x2c, 0xe5, 0xde, 0x94, 0x77, 0x1a, 0xe1, - 0x10, 0xf9, 0x9d, 0xe6, 0x03, 0x80, 0x81, 0xc6, 0xc7, 0xac, 0xf9, 0xce, 0x41, 0xda, 0x7b, 0xfa, - 0x34, 0xc0, 0xcc, 0x8b, 0x29, 0x83, 0xb7, 0x8a, 0xff, 0xe4, 0xd7, 0x18, 0xea, 0xab, 0x2b, 0x90, - 0xe8, 0x78, 0x41, 0x0c, 0xd3, 0x44, 0x61, 0x4d, 0x86, 0x11, 0xe2, 0x29, 0x8a, 0x79, 0x6a, 0x24, - 0x19, 0x25, 0x26, 0xbb, 0x6f, 0xc6, 0x90, 0x42, 0xb7, 0x61, 0x21, 0x72, 0x1f, 0x51, 0x27, 0x35, - 0x51, 0x1d, 0xe0, 0xce, 0xdb, 0xf3, 0x02, 0xa2, 0x81, 0x83, 0x3b, 0xe1, 0x21, 0x2d, 0x8e, 0x52, - 0x06, 0x6b, 0x8c, 0xf8, 0x34, 0x33, 0xee, 0x53, 0x6e, 0xfa, 0xe7, 0xee, 0xd3, 0xc7, 0x90, 0xe1, - 0x56, 0x9c, 0xb0, 0x43, 0x5b, 0x90, 0xdc, 0x0f, 0xb0, 0x8f, 0x16, 0xa3, 0xb8, 0xd3, 0x68, 0x80, - 0x15, 0x20, 0xdb, 0x0d, 0xb0, 0xdf, 0xb6, 0x5a, 0xc2, 0x73, 0x51, 0x1b, 0x7d, 0x2d, 0xe6, 0xd0, - 0x28, 0x94, 0xd8, 0x5b, 0x68, 0x49, 0xbc, 0x85, 0x52, 0x3d, 0xe8, 0x63, 0xa9, 0xa4, 0x46, 0xf1, - 0xdf, 0x2a, 0x64, 0xf6, 0x7c, 0x8f, 0xd6, 0x88, 0xa3, 0x4b, 0x22, 0x48, 0x4a, 0xcb, 0xd1, 0xdf, - 0xe8, 0x32, 0x40, 0xa7, 0x7b, 0xd0, 0x74, 0x6d, 0xfa, 0xc4, 0xc8, 0xd8, 0xa2, 0xb1, 0x9e, 0xf7, - 0x71, 0x9f, 0x0c, 0x07, 0xd8, 0xf6, 0x31, 0x7b, 0x81, 0x4c, 0xb2, 0x61, 0xd6, 0x43, 0x86, 0xd7, - 0x20, 0x67, 0x75, 0xc3, 0x43, 0xf3, 0x63, 0x7c, 0x70, 0xe8, 0x79, 0x47, 0x66, 0xd7, 0x6f, 0xf2, - 0xbf, 0xec, 0x16, 0x49, 0xff, 0x63, 0xd6, 0xbd, 0xef, 0x37, 0xd1, 0x4d, 0x38, 0x33, 0x84, 0x6c, - 0xe1, 0xf0, 0xd0, 0x73, 0x82, 0x7c, 0x7a, 0x35, 0xb1, 0xa6, 0x19, 0x48, 0x42, 0x3f, 0x64, 0x23, - 0xe8, 0x1b, 0x70, 0x91, 0x3f, 0x94, 0x39, 0xd8, 0xb2, 0x43, 0xb7, 0x67, 0x85, 0xd8, 0x0c, 0x0f, - 0x7d, 0x1c, 0x1c, 0x7a, 0x4d, 0x87, 0x96, 0xce, 0x9a, 0x71, 0x81, 0x41, 0xaa, 0x11, 0xa2, 0x21, - 0x00, 0x23, 0x9b, 0x98, 0x7d, 0x85, 0x4d, 0x24, 0xa2, 0x52, 0x9a, 0xd5, 0x5e, 0x2e, 0x3a, 0xc8, - 0xb5, 0x3f, 0x4a, 0xc0, 0xb9, 0x7d, 0xd2, 0xb2, 0x0e, 0x9a, 0x98, 0x3b, 0xe2, 0x9e, 0x8b, 0x9b, - 0x4e, 0x80, 0x6e, 0xf2, 0xed, 0x57, 0xf8, 0x9f, 0x21, 0xa3, 0xf3, 0xd5, 0x43, 0xdf, 0x6d, 0x3f, - 0xa3, 0x65, 0x05, 0x77, 0xce, 0xbd, 0x98, 0xed, 0x55, 0x67, 0x90, 0x1e, 0xdd, 0xfc, 0xa7, 0x13, - 0x36, 0x9f, 0x31, 0xeb, 0xb6, 0xc4, 0xed, 0x78, 0xd5, 0x4b, 0xe5, 0x31, 0xf7, 0xc4, 0xba, 0xec, - 0xbb, 0xd3, 0x5d, 0x96, 0x9c, 0x41, 0xf5, 0xc9, 0x0e, 0x2d, 0x94, 0x00, 0x8d, 0xeb, 0xc1, 0x1e, - 0x7c, 0x99, 0x39, 0x0a, 0xe5, 0x92, 0x68, 0x16, 0x3f, 0x51, 0x61, 0xa9, 0xca, 0x1f, 0xcb, 0xeb, - 0xdd, 0x56, 0xcb, 0xf2, 0xfb, 0x63, 0x21, 0x31, 0xfe, 0x3a, 0x35, 0xfa, 0x36, 0xae, 0x49, 0x6f, - 0xe3, 0xc3, 0x94, 0x4a, 0xbe, 0x0a, 0xa5, 0xee, 0xc2, 0xbc, 0x65, 0xdb, 0x38, 0x08, 0xe4, 0x02, - 0x6d, 0x9a, 0x2c, 0x08, 0xf8, 0x18, 0x1f, 0xd3, 0xaf, 0xc2, 0xc7, 0xbf, 0x29, 0x83, 0xef, 0x14, - 0xf8, 0x3b, 0xfa, 0x3b, 0x43, 0x25, 0xed, 0x95, 0x89, 0xef, 0xd8, 0xfc, 0x61, 0x5d, 0x2a, 0x71, - 0xd7, 0x21, 0x2b, 0x9e, 0xb6, 0xa7, 0x7d, 0xd2, 0x10, 0x81, 0x8a, 0x2d, 0xf1, 0x41, 0x03, 0x99, - 0x04, 0x5d, 0x84, 0xf3, 0x95, 0xad, 0x72, 0xed, 0xbe, 0x6e, 0x36, 0x9e, 0xec, 0xe9, 0xe6, 0x7e, - 0xad, 0xbe, 0xa7, 0x57, 0xb6, 0xef, 0x6d, 0xeb, 0xd5, 0xdc, 0x1c, 0x5a, 0x86, 0x25, 0x79, 0x70, - 0x6f, 0xbf, 0x91, 0x53, 0xd0, 0x39, 0x40, 0x72, 0x67, 0x55, 0xdf, 0xd1, 0x1b, 0x7a, 0x4e, 0x45, - 0x67, 0xe1, 0xb4, 0xdc, 0x5f, 0xd9, 0xd1, 0xcb, 0x46, 0x2e, 0x51, 0xec, 0x41, 0x56, 0x28, 0x41, - 0xae, 0xd8, 0x84, 0xca, 0xbc, 0xa0, 0xbd, 0x1c, 0xa3, 0x67, 0xa9, 0x6a, 0x85, 0x16, 0x3b, 0x5d, - 0x28, 0xb4, 0xf0, 0x55, 0xd0, 0xa2, 0xae, 0x57, 0xf9, 0x53, 0xa8, 0x58, 0x23, 0x66, 0x46, 0x5f, - 0x57, 0x0c, 0x3f, 0xe1, 0x2b, 0x71, 0x4f, 0xf8, 0xc3, 0x1f, 0x01, 0xa8, 0x23, 0x1f, 0x01, 0x14, - 0x7f, 0xa0, 0xc0, 0xbc, 0xf4, 0xf7, 0xf2, 0xc9, 0x9e, 0x48, 0xe8, 0x0b, 0xb0, 0xe4, 0xe3, 0xa6, - 0x45, 0xae, 0xa6, 0x26, 0x07, 0x24, 0x28, 0x60, 0x51, 0x74, 0xef, 0xb2, 0xa3, 0xcb, 0x06, 0x18, - 0xcc, 0x2c, 0x7f, 0x76, 0xa0, 0x8c, 0x7f, 0x76, 0x70, 0x09, 0x34, 0x07, 0x37, 0xc9, 0x8d, 0x17, - 0xfb, 0xc2, 0xa0, 0xa8, 0x63, 0xe8, 0xa3, 0x84, 0xc4, 0xf0, 0x47, 0x09, 0xfb, 0x90, 0xad, 0x7a, - 0xb6, 0xde, 0xc3, 0xed, 0x10, 0xdd, 0x18, 0x62, 0xe6, 0x79, 0xc9, 0x42, 0x01, 0x91, 0xc8, 0x78, - 0x09, 0xd8, 0x39, 0x15, 0x1c, 0xf2, 0x15, 0x17, 0x8c, 0x41, 0xc7, 0xf5, 0xcf, 0x54, 0xd0, 0xa2, - 0x1b, 0x1a, 0x21, 0xd7, 0xa3, 0xf2, 0xce, 0x3e, 0xa7, 0x4b, 0x6d, 0x7f, 0x67, 0x27, 0x37, 0x47, - 0xc8, 0x25, 0x75, 0x6e, 0xee, 0xee, 0xee, 0xe8, 0xe5, 0x1a, 0x23, 0x9d, 0xd4, 0xbf, 0x5d, 0x6b, - 0xe8, 0xf7, 0x75, 0x23, 0xa7, 0x8e, 0x4c, 0xb2, 0xb3, 0x5b, 0xbb, 0x9f, 0x4b, 0x10, 0x26, 0x4a, - 0x9d, 0xd5, 0xdd, 0xfd, 0xcd, 0x1d, 0x3d, 0x97, 0x1c, 0xe9, 0xae, 0x37, 0x8c, 0xed, 0xda, 0xfd, - 0x5c, 0x0a, 0x9d, 0x81, 0x9c, 0xbc, 0xe4, 0x93, 0x86, 0x5e, 0xcf, 0xa5, 0x47, 0x26, 0xae, 0x96, - 0x1b, 0x7a, 0x2e, 0x83, 0x0a, 0x70, 0x4e, 0xea, 0x24, 0xf7, 0x05, 0x73, 0x77, 0xf3, 0x81, 0x5e, - 0x69, 0xe4, 0xb2, 0xe8, 0x02, 0x9c, 0x1d, 0x1d, 0x2b, 0x1b, 0x46, 0xf9, 0x49, 0x4e, 0x1b, 0x99, - 0xab, 0xa1, 0x7f, 0xa7, 0x91, 0x83, 0x91, 0xb9, 0xb8, 0x45, 0x66, 0xa5, 0xd6, 0xc8, 0xcd, 0xa3, - 0xf3, 0xb0, 0x3c, 0x62, 0x15, 0x1d, 0x58, 0x18, 0x9d, 0xc9, 0xd0, 0xf5, 0xdc, 0xa9, 0xeb, 0x9f, - 0x28, 0xb0, 0x20, 0xfb, 0x02, 0x5d, 0x81, 0xd5, 0xea, 0x6e, 0xc5, 0xd4, 0x1f, 0xe9, 0xb5, 0x86, - 0xd8, 0x83, 0xca, 0xfe, 0x43, 0xbd, 0xd6, 0xa8, 0x9b, 0x2c, 0x44, 0x49, 0x70, 0x4f, 0x43, 0x3d, - 0x2e, 0x37, 0x2a, 0x5b, 0x7a, 0x35, 0xa7, 0xa0, 0xab, 0x50, 0x9c, 0x88, 0xda, 0xaf, 0x09, 0x9c, - 0xba, 0x79, 0xe3, 0x97, 0x2f, 0x56, 0x94, 0xdf, 0xbd, 0x58, 0x51, 0xfe, 0xf8, 0x62, 0x45, 0xf9, - 0xc9, 0x9f, 0x57, 0xe6, 0xe0, 0xb4, 0x83, 0x7b, 0x82, 0x2d, 0x56, 0xc7, 0x2d, 0xf5, 0x6e, 0xed, - 0x29, 0x1f, 0x24, 0x4b, 0x77, 0x7b, 0xb7, 0x0e, 0xd2, 0x34, 0x3f, 0x7e, 0xf9, 0x3f, 0x01, 0x00, - 0x00, 0xff, 0xff, 0xf1, 0x10, 0x5f, 0xc6, 0x92, 0x26, 0x00, 0x00, + // 2541 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xdd, 0x73, 0xdb, 0x58, + 0x15, 0x8f, 0xe4, 0x4f, 0x9d, 0xa4, 0x89, 0x7b, 0xd3, 0x0f, 0xd7, 0x6d, 0x43, 0xd6, 0x5b, 0x4a, + 0xda, 0x82, 0x93, 0x86, 0x16, 0x96, 0x96, 0x05, 0x1c, 0x5b, 0x6d, 0xd2, 0x4d, 0x9d, 0x20, 0x3b, + 0x2d, 0xdd, 0x81, 0xd1, 0x28, 0xd2, 0x6d, 0xa3, 0x8d, 0x6d, 0x79, 0x25, 0xd9, 0x5b, 0xbf, 0x2e, + 0x30, 0xc3, 0x2b, 0x6f, 0xfc, 0x0b, 0xfc, 0x09, 0xfb, 0x08, 0x0f, 0x0c, 0x33, 0xc0, 0xc0, 0x0c, + 0x3b, 0xc3, 0x2b, 0x5b, 0x1e, 0x18, 0x78, 0x63, 0x18, 0x78, 0x63, 0x86, 0xb9, 0x1f, 0x92, 0xaf, + 0x65, 0xd9, 0x75, 0x4d, 0x66, 0xa7, 0x1d, 0xde, 0x74, 0xef, 0xfd, 0x9d, 0x7b, 0xcf, 0xb9, 0xe7, + 0x77, 0x8e, 0xce, 0xd5, 0x15, 0x5c, 0xe8, 0x3b, 0xee, 0xb1, 0x8d, 0xd7, 0x7b, 0x37, 0xd7, 0x5d, + 0xec, 0x39, 0x5d, 0xd7, 0xc4, 0x5e, 0xa9, 0xe3, 0x3a, 0xbe, 0x83, 0x14, 0x36, 0x54, 0xea, 0xdd, + 0x2c, 0x7c, 0xe1, 0x99, 0xe3, 0x3c, 0x6b, 0xe2, 0x75, 0x3a, 0x70, 0xd8, 0x7d, 0xba, 0xee, 0xdb, + 0x2d, 0xec, 0xf9, 0x46, 0xab, 0xc3, 0xb0, 0x85, 0x95, 0x28, 0xe0, 0x23, 0xd7, 0xe8, 0x74, 0xb0, + 0xcb, 0xe7, 0x2a, 0xfe, 0x46, 0x82, 0x6c, 0xbd, 0x6d, 0x74, 0xbc, 0x23, 0xc7, 0x47, 0xd7, 0x21, + 0xe9, 0x3a, 0x8e, 0x9f, 0x97, 0x56, 0xa5, 0xb5, 0xf9, 0xcd, 0x73, 0xa5, 0x70, 0x9d, 0xd2, 0x83, + 0xfa, 0x5e, 0x4d, 0x6d, 0xe2, 0x16, 0x6e, 0xfb, 0x1a, 0xc5, 0xa0, 0xef, 0x80, 0xd2, 0x71, 0xb1, + 0x87, 0xdb, 0x26, 0xf6, 0xf2, 0xf2, 0x6a, 0x62, 0x6d, 0x7e, 0xb3, 0x28, 0x08, 0x04, 0x73, 0x96, + 0xf6, 0x03, 0x90, 0xda, 0xf6, 0xdd, 0xbe, 0x36, 0x10, 0x2a, 0x7c, 0x17, 0x16, 0x87, 0x07, 0x51, + 0x0e, 0x12, 0xc7, 0xb8, 0x4f, 0x97, 0x57, 0x34, 0xf2, 0x88, 0xae, 0x41, 0xaa, 0x67, 0x34, 0xbb, + 0x38, 0x2f, 0x53, 0x95, 0x96, 0x85, 0x15, 0x02, 0x59, 0x8d, 0x21, 0xee, 0xc8, 0xef, 0x48, 0xc5, + 0x9f, 0xca, 0x00, 0x95, 0x23, 0xa3, 0xfd, 0x0c, 0xef, 0x1b, 0xe6, 0x31, 0x7a, 0x0b, 0x16, 0x2c, + 0xc7, 0xec, 0x12, 0xad, 0xf5, 0xc1, 0xc4, 0xf3, 0x41, 0xdf, 0x7b, 0xb8, 0x8f, 0x6e, 0x03, 0x98, + 0x47, 0xd8, 0x3c, 0xee, 0x38, 0x76, 0xdb, 0xe7, 0xab, 0x9c, 0x15, 0x56, 0xa9, 0x84, 0x83, 0x9a, + 0x00, 0x44, 0x05, 0xc8, 0x7a, 0xdc, 0xc2, 0x7c, 0x62, 0x55, 0x5a, 0x5b, 0xd0, 0xc2, 0x36, 0xba, + 0x01, 0x19, 0x93, 0xea, 0xe0, 0xe5, 0x93, 0x74, 0x5f, 0x4e, 0x0f, 0xcd, 0x47, 0x46, 0xb4, 0x00, + 0x81, 0xca, 0x70, 0xba, 0x65, 0xb7, 0x75, 0xaf, 0xdf, 0x36, 0xb1, 0xa5, 0xfb, 0xb6, 0x79, 0x8c, + 0xfd, 0x7c, 0x6a, 0x44, 0x8d, 0x86, 0xdd, 0xc2, 0x0d, 0x3a, 0xa8, 0x2d, 0xb5, 0xec, 0x76, 0x9d, + 0xc2, 0x59, 0x07, 0xba, 0x0c, 0x60, 0x7b, 0xba, 0x8b, 0x5b, 0x4e, 0x0f, 0x5b, 0xf9, 0xf4, 0xaa, + 0xb4, 0x96, 0xd5, 0x14, 0xdb, 0xd3, 0x58, 0x47, 0xf1, 0x17, 0x12, 0xa4, 0xd9, 0xaa, 0xe8, 0x6d, + 0x90, 0x6d, 0x8b, 0x7b, 0x77, 0x79, 0x44, 0xa9, 0x9d, 0xaa, 0x26, 0xdb, 0x16, 0xca, 0x43, 0xa6, + 0x85, 0x3d, 0xcf, 0x78, 0xc6, 0x36, 0x5d, 0xd1, 0x82, 0x26, 0xba, 0x05, 0xe0, 0x74, 0xb0, 0x6b, + 0xf8, 0xb6, 0xd3, 0xf6, 0xf2, 0x09, 0x6a, 0xdb, 0x19, 0x61, 0x9a, 0xbd, 0x60, 0x50, 0x13, 0x70, + 0x68, 0x0b, 0x96, 0x02, 0x9f, 0xeb, 0xcc, 0xea, 0x7c, 0x92, 0x6a, 0x70, 0x21, 0xc6, 0x99, 0x7c, + 0x7b, 0x16, 0x3b, 0x43, 0xed, 0xe2, 0x8f, 0x25, 0xc8, 0x06, 0x4a, 0x12, 0x7b, 0xcd, 0xa6, 0x4d, + 0x7c, 0xea, 0xe1, 0x0f, 0xa9, 0x35, 0xa7, 0x34, 0x85, 0xf5, 0xd4, 0xf1, 0x87, 0xe8, 0x2d, 0x00, + 0x0f, 0xbb, 0x3d, 0xec, 0xd2, 0x61, 0x62, 0x42, 0x62, 0x4b, 0xde, 0x90, 0x34, 0x85, 0xf5, 0x12, + 0xc8, 0x25, 0xc8, 0x34, 0x8d, 0x56, 0xc7, 0x71, 0x99, 0xf3, 0xd8, 0x78, 0xd0, 0x85, 0x2e, 0x40, + 0xd6, 0x30, 0x7d, 0xc7, 0xd5, 0x6d, 0x8b, 0x6a, 0xba, 0xa0, 0x65, 0x68, 0x7b, 0xc7, 0x2a, 0xfe, + 0xb6, 0x00, 0x4a, 0x68, 0x25, 0xfa, 0x32, 0x24, 0x3c, 0x1c, 0x44, 0x4b, 0x3e, 0x6e, 0x23, 0x4a, + 0x75, 0xec, 0x6f, 0xcf, 0x69, 0x04, 0x46, 0xd0, 0x86, 0x65, 0x71, 0x8a, 0xc5, 0xa3, 0xcb, 0x96, + 0x45, 0xd0, 0x86, 0x65, 0xa1, 0x75, 0x48, 0x12, 0xf7, 0x51, 0xfd, 0x86, 0xb7, 0x6a, 0x00, 0x7f, + 0xe8, 0xf4, 0xf0, 0xf6, 0x9c, 0x46, 0x81, 0xe8, 0x36, 0xa4, 0x19, 0x05, 0xf8, 0xee, 0x5e, 0x8c, + 0x15, 0x61, 0xa4, 0xd8, 0x9e, 0xd3, 0x38, 0x98, 0xac, 0x83, 0x2d, 0x3b, 0xa0, 0x5c, 0xfc, 0x3a, + 0xaa, 0x65, 0x13, 0x2b, 0x28, 0x90, 0xac, 0xe3, 0xe1, 0x26, 0x36, 0x7d, 0xca, 0xb4, 0x71, 0xeb, + 0xd4, 0x29, 0x84, 0xac, 0xc3, 0xc0, 0x68, 0x13, 0x52, 0x9e, 0xdf, 0x6f, 0xe2, 0x7c, 0x86, 0x4a, + 0x15, 0xe2, 0xa5, 0x08, 0x62, 0x7b, 0x4e, 0x63, 0x50, 0x74, 0x17, 0xb2, 0x76, 0xdb, 0x74, 0xb1, + 0xe1, 0xe1, 0x7c, 0x96, 0x8a, 0x5d, 0x8e, 0x15, 0xdb, 0xe1, 0xa0, 0xed, 0x39, 0x2d, 0x14, 0x40, + 0xdf, 0x04, 0xc5, 0x77, 0x31, 0xd6, 0xa9, 0x75, 0xca, 0x04, 0xe9, 0x86, 0x8b, 0x31, 0xb7, 0x30, + 0xeb, 0xf3, 0x67, 0xf4, 0x6d, 0x00, 0x2a, 0xcd, 0x74, 0x06, 0x2a, 0xbe, 0x32, 0x56, 0x3c, 0xd0, + 0x9b, 0xae, 0x48, 0x1b, 0x85, 0x5f, 0x49, 0x90, 0xa8, 0x63, 0x9f, 0xc4, 0x77, 0xc7, 0x70, 0x09, + 0x59, 0x89, 0x5e, 0x3e, 0xb6, 0x74, 0x23, 0x60, 0xcc, 0xb8, 0xf8, 0x66, 0xf8, 0x0a, 0x83, 0x97, + 0xfd, 0x20, 0x2b, 0xca, 0x83, 0xac, 0xb8, 0x19, 0x64, 0x45, 0xc6, 0x8e, 0x4b, 0xf1, 0x89, 0xba, + 0x6e, 0xb7, 0x3a, 0xcd, 0x20, 0x3d, 0xa2, 0xaf, 0xc1, 0x3c, 0x7e, 0x8e, 0xcd, 0x2e, 0x57, 0x21, + 0x39, 0x49, 0x05, 0x08, 0x90, 0x65, 0xbf, 0xf0, 0x4f, 0x09, 0x12, 0x65, 0xcb, 0x3a, 0x09, 0x43, + 0xde, 0xa5, 0x99, 0xa0, 0x27, 0x4e, 0x20, 0x4f, 0x9a, 0xe0, 0x14, 0x41, 0x0f, 0xc4, 0x3f, 0x4f, + 0xab, 0xff, 0x2d, 0x41, 0x92, 0x84, 0xd7, 0x6b, 0x60, 0xf6, 0x2d, 0x00, 0x41, 0x32, 0x31, 0x49, + 0x52, 0x31, 0x43, 0xa9, 0x59, 0x0d, 0xff, 0x44, 0x82, 0x34, 0x4b, 0x12, 0x27, 0x61, 0xfa, 0xb0, + 0xee, 0xf2, 0x6c, 0xba, 0x27, 0xa6, 0xd5, 0xfd, 0x97, 0x49, 0x48, 0xd2, 0xe8, 0x3d, 0x01, 0xcd, + 0xaf, 0x43, 0xf2, 0xa9, 0xeb, 0xb4, 0xb8, 0xce, 0x62, 0x29, 0xd4, 0xc0, 0xcf, 0xfd, 0x9a, 0x63, + 0xe1, 0x7d, 0xc7, 0xd3, 0x28, 0x06, 0x5d, 0x05, 0xd9, 0x77, 0xb8, 0x9a, 0xe3, 0x90, 0xb2, 0xef, + 0xa0, 0x23, 0x38, 0x3f, 0xd0, 0x47, 0x6f, 0x19, 0x1d, 0xfd, 0xb0, 0xaf, 0xd3, 0x57, 0x0b, 0x2f, + 0x14, 0x36, 0xc7, 0xa6, 0xdf, 0x52, 0xa8, 0xd9, 0x43, 0xa3, 0xb3, 0xd5, 0x2f, 0x13, 0x21, 0x56, + 0x50, 0x2d, 0x9b, 0xa3, 0x23, 0xe4, 0x1d, 0x6e, 0x3a, 0x6d, 0x1f, 0xb7, 0x59, 0x62, 0x57, 0xb4, + 0xa0, 0x19, 0xdd, 0xdb, 0xf4, 0x94, 0x7b, 0x8b, 0x76, 0x00, 0x0c, 0xdf, 0x77, 0xed, 0xc3, 0xae, + 0x8f, 0xbd, 0x7c, 0x86, 0xaa, 0x7b, 0x6d, 0xbc, 0xba, 0xe5, 0x10, 0xcb, 0xb4, 0x14, 0x84, 0x0b, + 0x3f, 0x80, 0xfc, 0x38, 0x6b, 0x62, 0x2a, 0xc0, 0x1b, 0xc3, 0x15, 0xe0, 0x18, 0x55, 0x07, 0x35, + 0x60, 0xe1, 0x5d, 0x58, 0x8a, 0xac, 0x1e, 0x33, 0xeb, 0x19, 0x71, 0x56, 0x45, 0x14, 0xff, 0x93, + 0x04, 0x69, 0xf6, 0xf6, 0x7a, 0x5d, 0x69, 0x34, 0x6b, 0x68, 0x7f, 0x26, 0x43, 0x8a, 0xbe, 0x9c, + 0x5e, 0x57, 0xc3, 0x1e, 0x0c, 0x71, 0x8c, 0x85, 0xc4, 0xf5, 0xf1, 0x85, 0xc2, 0x24, 0x92, 0x45, + 0x37, 0x29, 0x35, 0xed, 0x26, 0xfd, 0x8f, 0xec, 0xf9, 0x44, 0x82, 0x6c, 0x50, 0x8e, 0x9c, 0xc4, + 0x36, 0x6f, 0x0e, 0xb3, 0x7f, 0x96, 0x77, 0xde, 0xd4, 0xe9, 0xf3, 0x87, 0x32, 0x64, 0x83, 0x62, + 0xe8, 0x24, 0x74, 0xbf, 0x3a, 0x44, 0x11, 0x24, 0x4a, 0xb9, 0x58, 0xa0, 0x47, 0x51, 0xa0, 0x47, + 0x1c, 0x8a, 0x50, 0x63, 0x03, 0xb2, 0x3c, 0x83, 0x05, 0xc4, 0x38, 0x13, 0x41, 0x12, 0x22, 0x79, + 0x5a, 0x88, 0x9a, 0x99, 0x00, 0x9f, 0xc9, 0xa0, 0x84, 0x35, 0xdd, 0xeb, 0xb6, 0x0d, 0xb5, 0x98, + 0x08, 0x29, 0x4d, 0x2e, 0x4b, 0x5f, 0xc3, 0x28, 0xd9, 0x4a, 0x43, 0xf2, 0xd0, 0xb1, 0xfa, 0xc5, + 0x7f, 0x48, 0x70, 0x7a, 0x84, 0xc6, 0x91, 0xa2, 0x41, 0x9a, 0xb2, 0x68, 0xd8, 0x80, 0x2c, 0x3d, + 0xef, 0xbe, 0xb4, 0xd0, 0xc8, 0x50, 0x18, 0x2b, 0x4e, 0xf8, 0xa1, 0xf9, 0xe5, 0x85, 0x15, 0x07, + 0x96, 0x7d, 0xb4, 0x06, 0x49, 0xbf, 0xdf, 0x61, 0xa7, 0xac, 0xc5, 0x21, 0x16, 0x3e, 0x22, 0xf6, + 0x35, 0xfa, 0x1d, 0xac, 0x51, 0xc4, 0xc0, 0xfe, 0x14, 0x3d, 0x44, 0xb2, 0x46, 0xf1, 0xe7, 0xa7, + 0x60, 0x5e, 0xb0, 0x19, 0x55, 0x61, 0xfe, 0x03, 0xcf, 0x69, 0xeb, 0xce, 0xe1, 0x07, 0xe4, 0x50, + 0xc5, 0xcc, 0x7d, 0x2b, 0x3e, 0xce, 0xe9, 0xf3, 0x1e, 0x05, 0x6e, 0xcf, 0x69, 0x40, 0xe4, 0x58, + 0x0b, 0x95, 0x81, 0xb6, 0x74, 0xc3, 0x75, 0x8d, 0x3e, 0xb7, 0x7f, 0x75, 0xc2, 0x24, 0x65, 0x82, + 0x23, 0x27, 0x16, 0x22, 0x45, 0x1b, 0xec, 0x83, 0x8e, 0xdd, 0xb2, 0x7d, 0x3b, 0x3c, 0x76, 0x8e, + 0x9b, 0x61, 0x3f, 0xc0, 0x91, 0x19, 0x42, 0x21, 0x74, 0x13, 0x92, 0x3e, 0x7e, 0x1e, 0xd0, 0xe8, + 0xe2, 0x18, 0x61, 0x92, 0xf5, 0xc9, 0x69, 0x92, 0x40, 0xd1, 0x1d, 0x52, 0xa8, 0x74, 0xdb, 0x3e, + 0x76, 0x79, 0x29, 0xb2, 0x32, 0x46, 0xaa, 0xc2, 0x50, 0xdb, 0x73, 0x5a, 0x20, 0x40, 0x97, 0x73, + 0x71, 0x70, 0xa2, 0x1c, 0xbb, 0x9c, 0x8b, 0xe9, 0x21, 0x99, 0x40, 0x0b, 0x9f, 0x4a, 0x00, 0x83, + 0x3d, 0x44, 0x6b, 0x90, 0x6a, 0x93, 0xb4, 0x91, 0x97, 0x68, 0x24, 0x89, 0x51, 0xa7, 0x6d, 0x37, + 0x48, 0x46, 0xd1, 0x18, 0x60, 0xc6, 0x42, 0x56, 0xe4, 0x64, 0x62, 0x06, 0x4e, 0x26, 0xa7, 0xe3, + 0x64, 0xe1, 0x8f, 0x12, 0x28, 0xa1, 0x57, 0x27, 0x5a, 0x75, 0xbf, 0xfc, 0xe6, 0x58, 0xf5, 0x37, + 0x09, 0x94, 0x90, 0x69, 0x61, 0xdc, 0x49, 0xd3, 0xc7, 0x9d, 0x2c, 0xc4, 0xdd, 0x8c, 0xc7, 0x28, + 0xd1, 0xd6, 0xe4, 0x0c, 0xb6, 0xa6, 0xa6, 0xb4, 0xf5, 0xf7, 0x12, 0x24, 0x49, 0x60, 0xa0, 0x6b, + 0xc3, 0xce, 0x5b, 0x8e, 0x29, 0x97, 0xde, 0x0c, 0xef, 0xfd, 0x55, 0x82, 0x0c, 0x0f, 0xda, 0xff, + 0x07, 0xdf, 0xb9, 0x18, 0x4f, 0xf4, 0x1d, 0xaf, 0x50, 0xde, 0x08, 0xdf, 0x85, 0xef, 0xe7, 0x87, + 0x90, 0xe1, 0x79, 0x30, 0xe6, 0xf5, 0xbe, 0x01, 0x19, 0xcc, 0x72, 0x6c, 0xcc, 0x21, 0x40, 0xbc, + 0x2f, 0x08, 0x60, 0x45, 0x13, 0x32, 0x3c, 0x01, 0x91, 0xa2, 0xa8, 0x4d, 0x5e, 0x15, 0xd2, 0x48, + 0xb9, 0x13, 0xa4, 0x28, 0x3a, 0x3e, 0xc3, 0x22, 0x8f, 0x20, 0x4b, 0xe4, 0x49, 0x79, 0x32, 0x60, + 0x93, 0x24, 0x54, 0x20, 0x64, 0x4f, 0xba, 0x1d, 0x6b, 0xba, 0xbd, 0xe7, 0xc0, 0xb2, 0x5f, 0xfc, + 0x1d, 0xa9, 0x8e, 0x79, 0x04, 0xa2, 0x2f, 0x0a, 0x1f, 0xd2, 0xcf, 0xc6, 0x84, 0x28, 0xff, 0x94, + 0x1e, 0x5b, 0x01, 0xcd, 0x58, 0x77, 0xdc, 0x86, 0x79, 0xbb, 0xed, 0xe9, 0xf4, 0x4b, 0x12, 0xff, + 0x30, 0x3d, 0x76, 0x6d, 0xc5, 0x6e, 0x7b, 0xfb, 0x2e, 0xee, 0xed, 0x58, 0xa8, 0x32, 0x54, 0x31, + 0xa6, 0x28, 0x31, 0xdf, 0x8e, 0x91, 0x9a, 0x78, 0x62, 0xd7, 0xa6, 0x29, 0xf7, 0x26, 0x5c, 0xd5, + 0x04, 0x0e, 0x11, 0xaf, 0x6a, 0xde, 0x07, 0x18, 0x68, 0x3c, 0x63, 0xcd, 0x77, 0x0e, 0xd2, 0xce, + 0xd3, 0xa7, 0x1e, 0x66, 0x5e, 0x4c, 0x69, 0xbc, 0x55, 0xfc, 0x17, 0x3f, 0xc9, 0x50, 0x5f, 0x5d, + 0x81, 0x44, 0xc7, 0xf1, 0x62, 0x98, 0x16, 0x14, 0xd6, 0x64, 0x18, 0x21, 0x9e, 0xa2, 0x98, 0xa7, + 0x22, 0xc9, 0x28, 0x31, 0xde, 0x7d, 0x53, 0x86, 0x14, 0xba, 0x05, 0x0b, 0xa1, 0xfb, 0x88, 0x3a, + 0xa9, 0xb1, 0xea, 0x00, 0x77, 0xde, 0xbe, 0xe3, 0x11, 0x0d, 0x2c, 0xdc, 0xf1, 0x8f, 0x68, 0x71, + 0x94, 0xd2, 0x58, 0x23, 0xe2, 0xd3, 0xcc, 0xa8, 0x4f, 0xb9, 0xe9, 0x9f, 0xbb, 0x4f, 0xef, 0xb0, + 0xa3, 0x13, 0x3d, 0x8a, 0xa1, 0xaf, 0x0c, 0xbe, 0x41, 0x4d, 0xc8, 0x87, 0x01, 0xa6, 0xf8, 0x18, + 0x32, 0x7c, 0x07, 0x4e, 0x98, 0x0c, 0x2d, 0x48, 0x1e, 0x78, 0xd8, 0x45, 0x8b, 0x61, 0xcc, 0x2a, + 0x34, 0x38, 0x0b, 0x90, 0xed, 0x7a, 0xd8, 0x6d, 0x1b, 0xad, 0xc0, 0xeb, 0x61, 0x1b, 0x7d, 0x23, + 0xe6, 0x85, 0x53, 0x28, 0xb1, 0xab, 0xd4, 0x52, 0x70, 0x95, 0x4a, 0xf5, 0xa0, 0x77, 0xad, 0x82, + 0x1a, 0xc5, 0xff, 0xc8, 0x90, 0xd9, 0x77, 0x1d, 0x5a, 0x5f, 0x46, 0x97, 0x44, 0x90, 0x14, 0x96, + 0xa3, 0xcf, 0xe8, 0x32, 0x40, 0xa7, 0x7b, 0xd8, 0xb4, 0x4d, 0x7a, 0x43, 0xc9, 0x98, 0xa6, 0xb0, + 0x9e, 0xf7, 0x70, 0x9f, 0x0c, 0x7b, 0xd8, 0x74, 0x31, 0xbb, 0xc0, 0x4c, 0xb2, 0x61, 0xd6, 0x43, + 0x86, 0xd7, 0x20, 0x67, 0x74, 0xfd, 0x23, 0xfd, 0x23, 0x7c, 0x78, 0xe4, 0x38, 0xc7, 0x7a, 0xd7, + 0x6d, 0xf2, 0x2f, 0x7e, 0x8b, 0xa4, 0xff, 0x31, 0xeb, 0x3e, 0x70, 0x9b, 0x68, 0x03, 0xce, 0x0c, + 0x21, 0x5b, 0xd8, 0x3f, 0x72, 0x2c, 0x2f, 0x9f, 0x5e, 0x4d, 0xac, 0x29, 0x1a, 0x12, 0xd0, 0x0f, + 0xd9, 0x08, 0xfa, 0x16, 0x5c, 0xe4, 0xf7, 0x6c, 0x16, 0x36, 0x4c, 0xdf, 0xee, 0x19, 0x3e, 0xd6, + 0xfd, 0x23, 0x17, 0x7b, 0x47, 0x4e, 0xd3, 0xa2, 0x65, 0xb7, 0xa2, 0x5d, 0x60, 0x90, 0x6a, 0x88, + 0x68, 0x04, 0x80, 0xc8, 0x26, 0x66, 0x5f, 0x61, 0x13, 0x89, 0xa8, 0x90, 0xa2, 0x95, 0x97, 0x8b, + 0x0e, 0xf2, 0xf4, 0x4f, 0x12, 0x70, 0xee, 0x80, 0xb4, 0x8c, 0xc3, 0x26, 0xe6, 0x8e, 0xb8, 0x67, + 0xe3, 0xa6, 0xe5, 0xa1, 0x0d, 0xbe, 0xfd, 0x12, 0xff, 0x96, 0x12, 0x9d, 0xaf, 0xee, 0xbb, 0x76, + 0xfb, 0x19, 0x2d, 0x49, 0xb8, 0x73, 0xee, 0xc5, 0x6c, 0xaf, 0x3c, 0x85, 0x74, 0x74, 0xf3, 0x9f, + 0x8e, 0xd9, 0x7c, 0xc6, 0xac, 0x5b, 0x02, 0xb7, 0xe3, 0x55, 0x2f, 0x95, 0x47, 0xdc, 0x13, 0xeb, + 0xb2, 0xef, 0x4f, 0x76, 0x59, 0x72, 0x0a, 0xd5, 0xc7, 0x3b, 0xb4, 0x50, 0x02, 0x34, 0xaa, 0x07, + 0xbb, 0x2f, 0x66, 0xe6, 0x48, 0x94, 0x4b, 0x41, 0xb3, 0xf8, 0xb1, 0x0c, 0x4b, 0x55, 0x7e, 0xd7, + 0x5e, 0xef, 0xb6, 0x5a, 0x86, 0xdb, 0x1f, 0x09, 0x89, 0xd1, 0xcb, 0xad, 0xe8, 0xd5, 0xba, 0x22, + 0x5c, 0xad, 0x0f, 0x53, 0x2a, 0xf9, 0x2a, 0x94, 0xba, 0x0b, 0xf3, 0x86, 0x69, 0x62, 0xcf, 0x13, + 0x8b, 0xbb, 0x49, 0xb2, 0x10, 0xc0, 0x47, 0xf8, 0x98, 0x7e, 0x15, 0x3e, 0xfe, 0x5d, 0x1a, 0xfc, + 0xe6, 0xc0, 0xaf, 0xe1, 0xdf, 0x19, 0x2a, 0x87, 0xaf, 0x8c, 0xbd, 0x06, 0xe7, 0xf7, 0xf2, 0x42, + 0x79, 0xbc, 0x0e, 0xd9, 0xe0, 0x66, 0x7c, 0xd2, 0x1f, 0x11, 0x21, 0xa8, 0xd8, 0x0a, 0xfe, 0x87, + 0x20, 0x93, 0xa0, 0x8b, 0x70, 0xbe, 0xb2, 0x5d, 0xae, 0xdd, 0x57, 0xf5, 0xc6, 0x93, 0x7d, 0x55, + 0x3f, 0xa8, 0xd5, 0xf7, 0xd5, 0xca, 0xce, 0xbd, 0x1d, 0xb5, 0x9a, 0x9b, 0x43, 0xcb, 0xb0, 0x24, + 0x0e, 0xee, 0x1f, 0x34, 0x72, 0x12, 0x3a, 0x07, 0x48, 0xec, 0xac, 0xaa, 0xbb, 0x6a, 0x43, 0xcd, + 0xc9, 0xe8, 0x2c, 0x9c, 0x16, 0xfb, 0x2b, 0xbb, 0x6a, 0x59, 0xcb, 0x25, 0x8a, 0x3d, 0xc8, 0x06, + 0x4a, 0x90, 0xe3, 0x39, 0xa1, 0x32, 0x4f, 0xfe, 0x97, 0x63, 0xf4, 0x2c, 0x55, 0x0d, 0xdf, 0x60, + 0x6f, 0x26, 0x0a, 0x2d, 0x7c, 0x1d, 0x94, 0xb0, 0xeb, 0x55, 0x3e, 0x28, 0x15, 0x6b, 0xc4, 0xcc, + 0xf0, 0xe7, 0x8c, 0xe1, 0x3f, 0x00, 0xa4, 0xb8, 0x3f, 0x00, 0x86, 0xff, 0x21, 0x90, 0x23, 0xff, + 0x10, 0x14, 0x7f, 0x24, 0xc1, 0xbc, 0xf0, 0x75, 0xfa, 0x64, 0xdf, 0x48, 0xe8, 0x4b, 0xb0, 0xe4, + 0xe2, 0xa6, 0x41, 0x8e, 0xb5, 0x3a, 0x07, 0x24, 0x28, 0x60, 0x31, 0xe8, 0xde, 0x63, 0xaf, 0x2e, + 0x13, 0x60, 0x30, 0xb3, 0xf8, 0xd7, 0x82, 0x34, 0xfa, 0xd7, 0xc2, 0x25, 0x50, 0x2c, 0xdc, 0x24, + 0xa7, 0x65, 0xec, 0x06, 0x06, 0x85, 0x1d, 0x43, 0xff, 0x34, 0x24, 0x86, 0xff, 0x69, 0x38, 0x80, + 0x6c, 0xd5, 0x31, 0xd5, 0x1e, 0x6e, 0xfb, 0xe8, 0xc6, 0x10, 0x33, 0xcf, 0x0b, 0x16, 0x06, 0x10, + 0x81, 0x8c, 0x97, 0x80, 0xbd, 0xa7, 0xbc, 0x23, 0xbe, 0xe2, 0x82, 0x36, 0xe8, 0xb8, 0xfe, 0xa9, + 0x0c, 0x4a, 0x78, 0xba, 0x23, 0xe4, 0x7a, 0x54, 0xde, 0x3d, 0xe0, 0x74, 0xa9, 0x1d, 0xec, 0xee, + 0xe6, 0xe6, 0x08, 0xb9, 0x84, 0xce, 0xad, 0xbd, 0xbd, 0x5d, 0xb5, 0x5c, 0x63, 0xa4, 0x13, 0xfa, + 0x77, 0x6a, 0x0d, 0xf5, 0xbe, 0xaa, 0xe5, 0xe4, 0xc8, 0x24, 0xbb, 0x7b, 0xb5, 0xfb, 0xb9, 0x04, + 0x61, 0xa2, 0xd0, 0x59, 0xdd, 0x3b, 0xd8, 0xda, 0x55, 0x73, 0xc9, 0x48, 0x77, 0xbd, 0xa1, 0xed, + 0xd4, 0xee, 0xe7, 0x52, 0xe8, 0x0c, 0xe4, 0xc4, 0x25, 0x9f, 0x34, 0xd4, 0x7a, 0x2e, 0x1d, 0x99, + 0xb8, 0x5a, 0x6e, 0xa8, 0xb9, 0x0c, 0x2a, 0xc0, 0x39, 0xa1, 0x93, 0x9c, 0x35, 0xf4, 0xbd, 0xad, + 0x07, 0x6a, 0xa5, 0x91, 0xcb, 0xa2, 0x0b, 0x70, 0x36, 0x3a, 0x56, 0xd6, 0xb4, 0xf2, 0x93, 0x9c, + 0x12, 0x99, 0xab, 0xa1, 0x7e, 0xaf, 0x91, 0x83, 0xc8, 0x5c, 0xdc, 0x22, 0xbd, 0x52, 0x6b, 0xe4, + 0xe6, 0xd1, 0x79, 0x58, 0x8e, 0x58, 0x45, 0x07, 0x16, 0xa2, 0x33, 0x69, 0xaa, 0x9a, 0x3b, 0x75, + 0xfd, 0x63, 0x09, 0x16, 0x44, 0x5f, 0xa0, 0x2b, 0xb0, 0x5a, 0xdd, 0xab, 0xe8, 0xea, 0x23, 0xb5, + 0xd6, 0x08, 0xf6, 0xa0, 0x72, 0xf0, 0x50, 0xad, 0x35, 0xea, 0x3a, 0x0b, 0x51, 0x12, 0xdc, 0x93, + 0x50, 0x8f, 0xcb, 0x8d, 0xca, 0xb6, 0x5a, 0xcd, 0x49, 0xe8, 0x2a, 0x14, 0xc7, 0xa2, 0x0e, 0x6a, + 0x01, 0x4e, 0xde, 0xba, 0xf1, 0xeb, 0x17, 0x2b, 0xd2, 0x1f, 0x5e, 0xac, 0x48, 0x7f, 0x7e, 0xb1, + 0x22, 0xfd, 0xec, 0x2f, 0x2b, 0x73, 0x70, 0xda, 0xc2, 0xbd, 0x80, 0x2d, 0x46, 0xc7, 0x2e, 0xf5, + 0x6e, 0xee, 0x4b, 0xef, 0x27, 0x4b, 0x77, 0x7b, 0x37, 0x0f, 0xd3, 0x34, 0x3f, 0x7e, 0xf5, 0xbf, + 0x01, 0x00, 0x00, 0xff, 0xff, 0xfc, 0xa8, 0x34, 0x19, 0xd1, 0x26, 0x00, 0x00, } func (m *Snapshot) Marshal() (dAtA []byte, err error) { @@ -4617,10 +4666,10 @@ func (m *Operation_TreeEdit) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x2a } - if len(m.Content) > 0 { - for iNdEx := len(m.Content) - 1; iNdEx >= 0; iNdEx-- { + if len(m.Contents) > 0 { + for iNdEx := len(m.Contents) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.Content[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.Contents[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -5850,6 +5899,47 @@ func (m *TreeNode) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *TreeNodes) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TreeNodes) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TreeNodes) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Content) > 0 { + for iNdEx := len(m.Content) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Content[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintResources(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *TreePos) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -7037,8 +7127,8 @@ func (m *Operation_TreeEdit) Size() (n int) { l = m.To.Size() n += 1 + l + sovResources(uint64(l)) } - if len(m.Content) > 0 { - for _, e := range m.Content { + if len(m.Contents) > 0 { + for _, e := range m.Contents { l = e.Size() n += 1 + l + sovResources(uint64(l)) } @@ -7557,6 +7647,24 @@ func (m *TreeNode) Size() (n int) { return n } +func (m *TreeNodes) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Content) > 0 { + for _, e := range m.Content { + l = e.Size() + n += 1 + l + sovResources(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *TreePos) Size() (n int) { if m == nil { return 0 @@ -11087,7 +11195,7 @@ func (m *Operation_TreeEdit) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Contents", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -11114,8 +11222,8 @@ func (m *Operation_TreeEdit) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Content = append(m.Content, &TreeNode{}) - if err := m.Content[len(m.Content)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Contents = append(m.Contents, &TreeNodes{}) + if err := m.Contents[len(m.Contents)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -14326,6 +14434,91 @@ func (m *TreeNode) Unmarshal(dAtA []byte) error { } return nil } +func (m *TreeNodes) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TreeNodes: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TreeNodes: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Content = append(m.Content, &TreeNode{}) + if err := m.Content[len(m.Content)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipResources(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *TreePos) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/api/yorkie/v1/resources.proto b/api/yorkie/v1/resources.proto index 53e23d110..46d879aad 100644 --- a/api/yorkie/v1/resources.proto +++ b/api/yorkie/v1/resources.proto @@ -117,7 +117,7 @@ message Operation { TimeTicket parent_created_at = 1; TreePos from = 2; TreePos to = 3; - repeated TreeNode content = 4; + repeated TreeNodes contents = 4; TimeTicket executed_at = 5; } message TreeStyle { @@ -242,6 +242,10 @@ message TreeNode { map attributes = 7; } +message TreeNodes { + repeated TreeNode content = 1; +} + message TreePos { TimeTicket created_at = 1; int32 offset = 2; diff --git a/pkg/document/crdt/tree.go b/pkg/document/crdt/tree.go index 7559cb5b7..4cb98de2d 100644 --- a/pkg/document/crdt/tree.go +++ b/pkg/document/crdt/tree.go @@ -473,7 +473,7 @@ func (t *Tree) ToXML() string { // EditByIndex edits the given range with the given value. // This method uses indexes instead of a pair of TreePos for testing. -func (t *Tree) EditByIndex(start, end int, content *TreeNode, editedAt *time.Ticket) error { +func (t *Tree) EditByIndex(start, end int, contents []*TreeNode, editedAt *time.Ticket) error { fromPos, err := t.FindPos(start) if err != nil { return err @@ -483,7 +483,7 @@ func (t *Tree) EditByIndex(start, end int, content *TreeNode, editedAt *time.Tic return err } - return t.Edit(fromPos, toPos, content, editedAt) + return t.Edit(fromPos, toPos, contents, editedAt) } // FindPos finds the position of the given index in the tree. @@ -501,7 +501,7 @@ func (t *Tree) FindPos(offset int) (*TreePos, error) { // Edit edits the tree with the given range and content. // If the content is undefined, the range will be removed. -func (t *Tree) Edit(from, to *TreePos, content *TreeNode, editedAt *time.Ticket) error { +func (t *Tree) Edit(from, to *TreePos, contents []*TreeNode, editedAt *time.Ticket) error { // 01. split text nodes at the given range if needed. toPos, toRight, err := t.findTreePosWithSplitText(to, editedAt) if err != nil { @@ -567,29 +567,39 @@ func (t *Tree) Edit(from, to *TreePos, content *TreeNode, editedAt *time.Ticket) } // 03. insert the given node at the given position. - if content != nil { - // 03-1. insert the content nodes to the list. + if len(contents) != 0 { + previous := fromRight.Prev - index.TraverseNode(content.IndexTreeNode, func(node *index.Node[*TreeNode], depth int) { - t.InsertAfter(previous, node.Value) - previous = node.Value - }) - - // 03-2. insert the content nodes to the tree. - if fromPos.Node.IsText() { - if fromPos.Offset == 0 { - if err := fromPos.Node.Parent.InsertBefore(content.IndexTreeNode, fromPos.Node); err != nil { - return err + offset := fromPos.Offset + node := fromPos.Node + + for _, content := range contents { + // 03-1. insert the content nodes to the list. + index.TraverseNode(content.IndexTreeNode, func(node *index.Node[*TreeNode], depth int) { + t.InsertAfter(previous, node.Value) + previous = node.Value + }) + + // 03-2. insert the content nodes to the tree. + if node.IsText() { + // if `contents` is consist of text nodes, then there'll be only one element in `contents` + // thus, there's no need to update fromPos + if fromPos.Offset == 0 { + if err := node.Parent.InsertBefore(content.IndexTreeNode, node); err != nil { + return err + } + } else { + if err := node.Parent.InsertAfter(content.IndexTreeNode, node); err != nil { + return err + } } } else { - if err := fromPos.Node.Parent.InsertAfter(content.IndexTreeNode, fromPos.Node); err != nil { + target := node + if err := target.InsertAt(content.IndexTreeNode, offset+1); err != nil { return err } - } - } else { - target := fromPos.Node - if err := target.InsertAt(content.IndexTreeNode, fromPos.Offset+1); err != nil { - return err + + offset++ } } } diff --git a/pkg/document/crdt/tree_test.go b/pkg/document/crdt/tree_test.go index 621671fc5..a7e028f53 100644 --- a/pkg/document/crdt/tree_test.go +++ b/pkg/document/crdt/tree_test.go @@ -105,7 +105,8 @@ func TestTree(t *testing.T) { // 1 //

- err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err := tree.EditByIndex(0, 0, + []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

", tree.ToXML()) helper.ListEqual(t, tree, []string{"p", "r"}) @@ -115,7 +116,7 @@ func TestTree(t *testing.T) { //

h e l l o

err = tree.EditByIndex( 1, 1, - crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "hello"), + []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "hello")}, helper.IssueTime(ctx), ) assert.NoError(t, err) @@ -128,7 +129,7 @@ func TestTree(t *testing.T) { p := crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil) err = p.InsertAt(crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "world"), 0) assert.NoError(t, err) - err = tree.EditByIndex(7, 7, p, helper.IssueTime(ctx)) + err = tree.EditByIndex(7, 7, []*crdt.TreeNode{p}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

hello

world

", tree.ToXML()) helper.ListEqual(t, tree, []string{"text.hello", "p", "text.world", "p", "r"}) @@ -138,7 +139,7 @@ func TestTree(t *testing.T) { //

h e l l o !

w o r l d

err = tree.EditByIndex( 6, 6, - crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "!"), + []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "!")}, helper.IssueTime(ctx), ) assert.NoError(t, err) @@ -173,7 +174,7 @@ func TestTree(t *testing.T) { //

h e l l o ~ !

w o r l d

err = tree.EditByIndex( 6, 6, - crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "~"), + []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "~")}, helper.IssueTime(ctx), ) assert.NoError(t, err) @@ -187,14 +188,19 @@ func TestTree(t *testing.T) { //

a b

c d

ctx := helper.TextChangeContext(helper.TestRoot()) - tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx)) - err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), + "root", nil), helper.IssueTime(ctx)) + err := tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx)) + err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "ab")}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(4, 4, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx)) + err = tree.EditByIndex(5, 5, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "cd")}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

ab

cd

", tree.ToXML()) helper.ListEqual(t, tree, []string{"text.ab", "p", "text.cd", "p", "root"}) @@ -225,13 +231,17 @@ func TestTree(t *testing.T) { ctx := helper.TextChangeContext(helper.TestRoot()) tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx)) - err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err := tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx)) + err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "ab")}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(4, 4, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx)) + err = tree.EditByIndex(5, 5, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "cd")}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

ab

cd

", tree.ToXML()) helper.ListEqual(t, tree, []string{"text.ab", "p", "text.cd", "p", "root"}) @@ -251,7 +261,8 @@ func TestTree(t *testing.T) { assert.Equal(t, 1, structure.Children[0].Children[1].Size) // 03. insert a new text node at the start of the first paragraph. - err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "@"), helper.IssueTime(ctx)) + err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "@")}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

@ad

", tree.ToXML()) }) @@ -262,13 +273,17 @@ func TestTree(t *testing.T) { //

a b

ctx := helper.TextChangeContext(helper.TestRoot()) tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx)) - err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err := tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "b", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(2, 2, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "i", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx)) + err = tree.EditByIndex(3, 3, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "ab")}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

ab

", tree.ToXML()) err = tree.EditByIndex(5, 6, nil, helper.IssueTime(ctx)) @@ -277,13 +292,17 @@ func TestTree(t *testing.T) { // 02. Edit between two element nodes in same hierarchy. tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx)) - err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "b", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(2, 2, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "i", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx)) + err = tree.EditByIndex(3, 3, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "ab")}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

ab

", tree.ToXML()) err = tree.EditByIndex(6, 7, nil, helper.IssueTime(ctx)) @@ -292,13 +311,17 @@ func TestTree(t *testing.T) { // 03. Edit between text and element node in same hierarchy. tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx)) - err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "b", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(2, 2, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "i", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx)) + err = tree.EditByIndex(3, 3, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "ab")}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

ab

", tree.ToXML()) err = tree.EditByIndex(4, 6, nil, helper.IssueTime(ctx)) @@ -307,13 +330,17 @@ func TestTree(t *testing.T) { // 04. Edit between text and element node in same hierarchy. tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx)) - err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "b", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(2, 2, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "i", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx)) + err = tree.EditByIndex(3, 3, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "ab")}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

ab

", tree.ToXML()) err = tree.EditByIndex(5, 7, nil, helper.IssueTime(ctx)) @@ -322,13 +349,17 @@ func TestTree(t *testing.T) { // 05. Edit between text and element node in same hierarchy. tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx)) - err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "b", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(2, 2, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "i", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx)) + err = tree.EditByIndex(3, 3, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "ab")}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

ab

", tree.ToXML()) err = tree.EditByIndex(4, 7, nil, helper.IssueTime(ctx)) @@ -337,13 +368,17 @@ func TestTree(t *testing.T) { // 06. Edit between text and element node in same hierarchy. tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx)) - err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "b", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(2, 2, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "i", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx)) + err = tree.EditByIndex(3, 3, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "ab")}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

ab

", tree.ToXML()) err = tree.EditByIndex(3, 7, nil, helper.IssueTime(ctx)) @@ -352,19 +387,26 @@ func TestTree(t *testing.T) { // 07. Edit between text and element node in same hierarchy. tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx)) - err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx)) + err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "ab")}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(4, 4, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(5, 5, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "b", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(6, 6, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx)) + err = tree.EditByIndex(6, 6, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "cd")}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(10, 10, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(10, 10, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(11, 11, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ef"), helper.IssueTime(ctx)) + err = tree.EditByIndex(11, 11, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "ef")}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

ab

cd

ef

", tree.ToXML()) err = tree.EditByIndex(9, 10, nil, helper.IssueTime(ctx)) @@ -376,13 +418,17 @@ func TestTree(t *testing.T) { // 01. style attributes to an element node. ctx := helper.TextChangeContext(helper.TestRoot()) tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx)) - err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err := tree.EditByIndex(0, 0, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx)) + err = tree.EditByIndex(1, 1, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "ab")}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx)) + err = tree.EditByIndex(4, 4, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "p", nil)}, helper.IssueTime(ctx)) assert.NoError(t, err) - err = tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx)) + err = tree.EditByIndex(5, 5, []*crdt.TreeNode{crdt.NewTreeNode(helper.IssuePos(ctx), + "text", nil, "cd")}, helper.IssueTime(ctx)) assert.NoError(t, err) assert.Equal(t, "

ab

cd

", tree.ToXML()) diff --git a/pkg/document/json/tree.go b/pkg/document/json/tree.go index dc7710bc6..78e33b228 100644 --- a/pkg/document/json/tree.go +++ b/pkg/document/json/tree.go @@ -17,6 +17,8 @@ package json import ( + "errors" + "github.com/yorkie-team/yorkie/pkg/document/change" "github.com/yorkie-team/yorkie/pkg/document/crdt" "github.com/yorkie-team/yorkie/pkg/document/operations" @@ -29,6 +31,19 @@ const ( DefaultRootNodeType = "root" ) +var ( + // ErrEmptyTextNode is returned when there's empty string value in text node + ErrEmptyTextNode = errors.New("text node cannot have empty value") + // ErrMixedNodeType is returned when there're element node and text node inside contents array + ErrMixedNodeType = errors.New("element node and text node cannot be passed together") + // ErrIndexBoundary is returned when from index is bigger than to index + ErrIndexBoundary = errors.New("from should be less than or equal to to") + // ErrPathLenDiff is returned when two paths have different length + ErrPathLenDiff = errors.New("both paths should have same length") + // ErrEmptyPath is returned when there's empty path + ErrEmptyPath = errors.New("path should not be empty") +) + // TreeNode is a node of Tree. type TreeNode struct { // Type is the type of this node. It is used to distinguish between text @@ -62,30 +77,47 @@ func NewTree(ctx *change.Context, tree *crdt.Tree) *Tree { } } -// Edit edits this tree with the given node. -func (t *Tree) Edit(fromIdx, toIdx int, content *TreeNode) bool { - if fromIdx > toIdx { - panic("from should be less than or equal to to") +// validateTextNode make sure that text node have non-empty string value +func validateTextNode(treeNode TreeNode) error { + if len(treeNode.Value) == 0 { + return ErrEmptyTextNode } - ticket := t.context.IssueTimeTicket() - var node *crdt.TreeNode - if content != nil { - var attributes *crdt.RHT - if content.Attributes != nil { - attributes = crdt.NewRHT() - for key, val := range content.Attributes { - attributes.Set(key, val, ticket) + return nil +} + +// validateTextNode make sure that treeNodes consist of only one of the two types (text or element) +func validateTreeNodes(treeNodes []*TreeNode) error { + firstTreeNodeType := treeNodes[0].Type + + if firstTreeNodeType == index.DefaultTextType { + for _, treeNode := range treeNodes { + if treeNode.Type != index.DefaultTextType { + return ErrMixedNodeType + } + + if err := validateTextNode(*treeNode); err != nil { + return err } } - node = crdt.NewTreeNode(crdt.NewTreePos(ticket, 0), content.Type, attributes, content.Value) - for _, child := range content.Children { - if err := buildDescendants(t.context, child, node); err != nil { - panic(err) + + } else { + for _, treeNode := range treeNodes { + if treeNode.Type == index.DefaultTextType { + return ErrMixedNodeType } } } + return nil +} + +// Edit edits this tree with the given nodes. +func (t *Tree) Edit(fromIdx, toIdx int, contents ...*TreeNode) bool { + if fromIdx > toIdx { + panic(ErrIndexBoundary) + } + fromPos, err := t.Tree.FindPos(fromIdx) if err != nil { panic(err) @@ -95,32 +127,7 @@ func (t *Tree) Edit(fromIdx, toIdx int, content *TreeNode) bool { panic(err) } - var clone *crdt.TreeNode - if node != nil { - clone, err = node.DeepCopy() - if err != nil { - panic(err) - } - } - - ticket = t.context.LastTimeTicket() - if err = t.Tree.Edit(fromPos, toPos, clone, ticket); err != nil { - panic(err) - } - - t.context.Push(operations.NewTreeEdit( - t.CreatedAt(), - fromPos, - toPos, - node, - ticket, - )) - - if fromPos.CreatedAt.Compare(toPos.CreatedAt) != 0 || fromPos.Offset != toPos.Offset { - t.context.RegisterElementHasRemovedNodes(t.Tree) - } - - return true + return t.edit(fromPos, toPos, contents) } // Len returns the length of this tree. @@ -128,46 +135,65 @@ func (t *Tree) Len() int { return t.IndexTree.Root().Len() } -// EditByPath edits this tree with the given path and node. -func (t *Tree) EditByPath(fromPath []int, toPath []int, content *TreeNode) bool { +// edit edits the tree with the given nodes +func (t *Tree) edit(fromPos, toPos *crdt.TreePos, contents []*TreeNode) bool { ticket := t.context.IssueTimeTicket() - var node *crdt.TreeNode - if content != nil { - var attributes *crdt.RHT - if content.Attributes != nil { - attributes = crdt.NewRHT() - for key, val := range content.Attributes { - attributes.Set(key, val, ticket) - } + var nodes []*crdt.TreeNode + + if len(contents) != 0 && contents[0] != nil { + if err := validateTreeNodes(contents); err != nil { + panic(err) } - node = crdt.NewTreeNode(crdt.NewTreePos(ticket, 0), content.Type, attributes, content.Value) - for _, child := range content.Children { - if err := buildDescendants(t.context, child, node); err != nil { - panic(err) + + if contents[0].Type == index.DefaultTextType { + value := "" + + for _, content := range contents { + value += content.Value + } + + nodes = append(nodes, crdt.NewTreeNode(crdt.NewTreePos(ticket, 0), index.DefaultTextType, nil, value)) + } else { + for _, content := range contents { + var attributes *crdt.RHT + if content.Attributes != nil { + attributes = crdt.NewRHT() + for key, val := range content.Attributes { + attributes.Set(key, val, ticket) + } + } + var node *crdt.TreeNode + + node = crdt.NewTreeNode(crdt.NewTreePos(ticket, 0), content.Type, attributes, content.Value) + + for _, child := range content.Children { + if err := buildDescendants(t.context, child, node); err != nil { + panic(err) + } + } + + nodes = append(nodes, node) } } } - fromPos, err := t.Tree.PathToPos(fromPath) - if err != nil { - panic(err) - } - toPos, err := t.Tree.PathToPos(toPath) - if err != nil { - panic(err) - } + var clones []*crdt.TreeNode + if len(nodes) != 0 { + for _, node := range nodes { + var clone *crdt.TreeNode - var clone *crdt.TreeNode - if node != nil { - clone, err = node.DeepCopy() - if err != nil { - panic(err) + clone, err := node.DeepCopy() + if err != nil { + panic(err) + } + + clones = append(clones, clone) } } ticket = t.context.LastTimeTicket() - if err = t.Tree.Edit(fromPos, toPos, clone, ticket); err != nil { + if err := t.Tree.Edit(fromPos, toPos, clones, ticket); err != nil { panic(err) } @@ -175,7 +201,7 @@ func (t *Tree) EditByPath(fromPath []int, toPath []int, content *TreeNode) bool t.CreatedAt(), fromPos, toPos, - node, + nodes, ticket, )) @@ -186,6 +212,28 @@ func (t *Tree) EditByPath(fromPath []int, toPath []int, content *TreeNode) bool return true } +// EditByPath edits this tree with the given path and nodes. +func (t *Tree) EditByPath(fromPath []int, toPath []int, contents ...*TreeNode) bool { + if len(fromPath) != len(toPath) { + panic(ErrPathLenDiff) + } + + if len(fromPath) == 0 || len(toPath) == 0 { + panic(ErrEmptyPath) + } + + fromPos, err := t.Tree.PathToPos(fromPath) + if err != nil { + panic(err) + } + toPos, err := t.Tree.PathToPos(toPath) + if err != nil { + panic(err) + } + + return t.edit(fromPos, toPos, contents) +} + // Style sets the attributes to the elements of the given range. func (t *Tree) Style(fromIdx, toIdx int, attributes map[string]string) bool { if fromIdx > toIdx { @@ -237,6 +285,11 @@ func buildRoot(ctx *change.Context, node *TreeNode, createdAt *time.Ticket) *crd // buildDescendants converts the given node to a CRDT-based tree node. func buildDescendants(ctx *change.Context, n TreeNode, parent *crdt.TreeNode) error { if n.Type == index.DefaultTextType { + + if err := validateTextNode(n); err != nil { + return err + } + treeNode := crdt.NewTreeNode(crdt.NewTreePos(ctx.IssueTimeTicket(), 0), n.Type, nil, n.Value) return parent.Append(treeNode) } diff --git a/pkg/document/operations/tree_edit.go b/pkg/document/operations/tree_edit.go index aeb4e8c6d..845c02451 100644 --- a/pkg/document/operations/tree_edit.go +++ b/pkg/document/operations/tree_edit.go @@ -33,8 +33,8 @@ type TreeEdit struct { // toPos represents the end point of the editing range. to *crdt.TreePos - // content is the content of tree added when editing. - content *crdt.TreeNode + // contents is the content of tree added when editing. + contents []*crdt.TreeNode // executedAt is the time the operation was executed. executedAt *time.Ticket @@ -45,14 +45,14 @@ func NewTreeEdit( parentCreatedAt *time.Ticket, from *crdt.TreePos, to *crdt.TreePos, - content *crdt.TreeNode, + contents []*crdt.TreeNode, executedAt *time.Ticket, ) *TreeEdit { return &TreeEdit{ parentCreatedAt: parentCreatedAt, from: from, to: to, - content: content, + contents: contents, executedAt: executedAt, } } @@ -63,15 +63,22 @@ func (e *TreeEdit) Execute(root *crdt.Root) error { switch obj := parent.(type) { case *crdt.Tree: - var content *crdt.TreeNode + var contents []*crdt.TreeNode var err error - if e.Content() != nil { - content, err = e.Content().DeepCopy() - if err != nil { - return err + if len(e.Contents()) != 0 { + for _, content := range e.Contents() { + var clone *crdt.TreeNode + + clone, err = content.DeepCopy() + if err != nil { + return err + } + + contents = append(contents, clone) } + } - if err = obj.Edit(e.from, e.to, content, e.executedAt); err != nil { + if err = obj.Edit(e.from, e.to, contents, e.executedAt); err != nil { return err } @@ -110,7 +117,7 @@ func (e *TreeEdit) ParentCreatedAt() *time.Ticket { return e.parentCreatedAt } -// Content returns the content of Edit. -func (e *TreeEdit) Content() *crdt.TreeNode { - return e.content +// Contents returns the content of Edit. +func (e *TreeEdit) Contents() []*crdt.TreeNode { + return e.contents } diff --git a/test/integration/tree_test.go b/test/integration/tree_test.go index cc5e07ed9..2d6d36edb 100644 --- a/test/integration/tree_test.go +++ b/test/integration/tree_test.go @@ -240,6 +240,52 @@ func TestTree(t *testing.T) { assert.NoError(t, err) }) + t.Run("edit its content when multi tree nodes passed", func(t *testing.T) { + doc := document.New(helper.TestDocKey(t)) + err := doc.Update(func(root *json.Object, p *presence.Presence) error { + root.SetNewTree("t", &json.TreeNode{ + Type: "doc", + Children: []json.TreeNode{{ + Type: "tc", + Children: []json.TreeNode{{ + Type: "p", Children: []json.TreeNode{{ + Type: "tn", Children: []json.TreeNode{{ + Type: "text", Value: "ab", + }}, + }}, + }}, + }}, + }) + assert.Equal(t, "

ab

", root.GetTree("t").ToXML()) + + root.GetTree("t").EditByPath([]int{0, 0, 0, 1}, []int{0, 0, 0, 1}, &json.TreeNode{ + Type: "text", + Value: "X"}, &json.TreeNode{ + Type: "text", + Value: "X", + }) + assert.Equal(t, "

aXXb

", root.GetTree("t").ToXML()) + + root.GetTree("t").EditByPath([]int{0, 1}, []int{0, 1}, &json.TreeNode{ + Type: "p", + Children: []json.TreeNode{{Type: "tn", Children: []json.TreeNode{{Type: "text", Value: "te"}, {Type: "text", Value: "st"}}}}}, + &json.TreeNode{ + Type: "p", + Children: []json.TreeNode{{Type: "tn", Children: []json.TreeNode{{Type: "text", Value: "te"}, {Type: "text", Value: "xt"}}}}, + }) + assert.Equal(t, "

aXXb

test

text

", root.GetTree("t").ToXML()) + + root.GetTree("t").EditByPath([]int{0, 3}, []int{0, 3}, &json.TreeNode{ + Type: "p", + Children: []json.TreeNode{{Type: "tn", Children: []json.TreeNode{{Type: "text", Value: "te"}, {Type: "text", Value: "st"}}}}}, + &json.TreeNode{Type: "tn", Children: []json.TreeNode{{Type: "text", Value: "te"}, {Type: "text", Value: "xt"}}}) + assert.Equal(t, "

aXXb

test

text

test

text
", root.GetTree("t").ToXML()) + + return nil + }) + assert.NoError(t, err) + }) + t.Run("edit its content with attributes test", func(t *testing.T) { doc := document.New(helper.TestDocKey(t)) err := doc.Update(func(root *json.Object, p *presence.Presence) error {