diff --git a/pkg/model/roster/roster.pb.go b/pkg/model/roster/roster.pb.go index e1e6831ab..498484284 100644 --- a/pkg/model/roster/roster.pb.go +++ b/pkg/model/roster/roster.pb.go @@ -124,6 +124,54 @@ func (x *Item) GetGroups() []string { return nil } +// Items represent a set of roster items. +type Items struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Items []*Item `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` +} + +func (x *Items) Reset() { + *x = Items{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_model_v1_roster_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Items) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Items) ProtoMessage() {} + +func (x *Items) ProtoReflect() protoreflect.Message { + mi := &file_proto_model_v1_roster_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Items.ProtoReflect.Descriptor instead. +func (*Items) Descriptor() ([]byte, []int) { + return file_proto_model_v1_roster_proto_rawDescGZIP(), []int{1} +} + +func (x *Items) GetItems() []*Item { + if x != nil { + return x.Items + } + return nil +} + // Notification represents a roster subscription pending notification. type Notification struct { state protoimpl.MessageState @@ -138,7 +186,7 @@ type Notification struct { func (x *Notification) Reset() { *x = Notification{} if protoimpl.UnsafeEnabled { - mi := &file_proto_model_v1_roster_proto_msgTypes[1] + mi := &file_proto_model_v1_roster_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -151,7 +199,7 @@ func (x *Notification) String() string { func (*Notification) ProtoMessage() {} func (x *Notification) ProtoReflect() protoreflect.Message { - mi := &file_proto_model_v1_roster_proto_msgTypes[1] + mi := &file_proto_model_v1_roster_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -164,7 +212,7 @@ func (x *Notification) ProtoReflect() protoreflect.Message { // Deprecated: Use Notification.ProtoReflect.Descriptor instead. func (*Notification) Descriptor() ([]byte, []int) { - return file_proto_model_v1_roster_proto_rawDescGZIP(), []int{1} + return file_proto_model_v1_roster_proto_rawDescGZIP(), []int{2} } func (x *Notification) GetContact() string { @@ -188,6 +236,102 @@ func (x *Notification) GetPresence() *stravaganza.PBElement { return nil } +// Notifications represents a set of roster notifications. +type Notifications struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Notifications []*Notification `protobuf:"bytes,1,rep,name=notifications,proto3" json:"notifications,omitempty"` +} + +func (x *Notifications) Reset() { + *x = Notifications{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_model_v1_roster_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Notifications) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Notifications) ProtoMessage() {} + +func (x *Notifications) ProtoReflect() protoreflect.Message { + mi := &file_proto_model_v1_roster_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Notifications.ProtoReflect.Descriptor instead. +func (*Notifications) Descriptor() ([]byte, []int) { + return file_proto_model_v1_roster_proto_rawDescGZIP(), []int{3} +} + +func (x *Notifications) GetNotifications() []*Notification { + if x != nil { + return x.Notifications + } + return nil +} + +// Groups represents a set of roster groups. +type Groups struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Groups []string `protobuf:"bytes,1,rep,name=groups,proto3" json:"groups,omitempty"` +} + +func (x *Groups) Reset() { + *x = Groups{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_model_v1_roster_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Groups) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Groups) ProtoMessage() {} + +func (x *Groups) ProtoReflect() protoreflect.Message { + mi := &file_proto_model_v1_roster_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Groups.ProtoReflect.Descriptor instead. +func (*Groups) Descriptor() ([]byte, []int) { + return file_proto_model_v1_roster_proto_rawDescGZIP(), []int{4} +} + +func (x *Groups) GetGroups() []string { + if x != nil { + return x.Groups + } + return nil +} + var File_proto_model_v1_roster_proto protoreflect.FileDescriptor var file_proto_model_v1_roster_proto_rawDesc = []byte{ @@ -206,17 +350,28 @@ var file_proto_model_v1_roster_proto_rawDesc = []byte{ 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x73, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0x6e, 0x0a, - 0x0c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, - 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6a, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6a, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x08, 0x70, 0x72, 0x65, - 0x73, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x74, - 0x72, 0x61, 0x76, 0x61, 0x67, 0x61, 0x6e, 0x7a, 0x61, 0x2e, 0x50, 0x42, 0x45, 0x6c, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x08, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x1f, 0x5a, - 0x1d, 0x70, 0x6b, 0x67, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2f, 0x72, 0x6f, 0x73, 0x74, 0x65, - 0x72, 0x2f, 0x3b, 0x72, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0x34, 0x0a, + 0x05, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2b, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x72, 0x6f, + 0x73, 0x74, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, + 0x65, 0x6d, 0x73, 0x22, 0x6e, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x10, 0x0a, + 0x03, 0x6a, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6a, 0x69, 0x64, 0x12, + 0x32, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x76, 0x61, 0x67, 0x61, 0x6e, 0x7a, 0x61, 0x2e, + 0x50, 0x42, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x70, 0x72, 0x65, 0x73, 0x65, + 0x6e, 0x63, 0x65, 0x22, 0x54, 0x0a, 0x0d, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x6f, + 0x64, 0x65, 0x6c, 0x2e, 0x72, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x20, 0x0a, 0x06, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x42, 0x1f, 0x5a, 0x1d, 0x70, + 0x6b, 0x67, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2f, 0x72, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x2f, + 0x3b, 0x72, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -231,19 +386,24 @@ func file_proto_model_v1_roster_proto_rawDescGZIP() []byte { return file_proto_model_v1_roster_proto_rawDescData } -var file_proto_model_v1_roster_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_proto_model_v1_roster_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_proto_model_v1_roster_proto_goTypes = []interface{}{ (*Item)(nil), // 0: model.roster.v1.Item - (*Notification)(nil), // 1: model.roster.v1.Notification - (*stravaganza.PBElement)(nil), // 2: stravaganza.PBElement + (*Items)(nil), // 1: model.roster.v1.Items + (*Notification)(nil), // 2: model.roster.v1.Notification + (*Notifications)(nil), // 3: model.roster.v1.Notifications + (*Groups)(nil), // 4: model.roster.v1.Groups + (*stravaganza.PBElement)(nil), // 5: stravaganza.PBElement } var file_proto_model_v1_roster_proto_depIdxs = []int32{ - 2, // 0: model.roster.v1.Notification.presence:type_name -> stravaganza.PBElement - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 0, // 0: model.roster.v1.Items.items:type_name -> model.roster.v1.Item + 5, // 1: model.roster.v1.Notification.presence:type_name -> stravaganza.PBElement + 2, // 2: model.roster.v1.Notifications.notifications:type_name -> model.roster.v1.Notification + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_proto_model_v1_roster_proto_init() } @@ -265,6 +425,18 @@ func file_proto_model_v1_roster_proto_init() { } } file_proto_model_v1_roster_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Items); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_model_v1_roster_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Notification); i { case 0: return &v.state @@ -276,6 +448,30 @@ func file_proto_model_v1_roster_proto_init() { return nil } } + file_proto_model_v1_roster_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Notifications); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_model_v1_roster_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Groups); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -283,7 +479,7 @@ func file_proto_model_v1_roster_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_model_v1_roster_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/router/router.go b/pkg/router/router.go index 9b57d60a4..b0b971344 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -17,12 +17,11 @@ package router import ( "context" - c2smodel "github.com/ortuman/jackal/pkg/model/c2s" - "github.com/jackal-xmpp/stravaganza/v2" streamerror "github.com/jackal-xmpp/stravaganza/v2/errors/stream" "github.com/jackal-xmpp/stravaganza/v2/jid" "github.com/ortuman/jackal/pkg/host" + c2smodel "github.com/ortuman/jackal/pkg/model/c2s" "github.com/ortuman/jackal/pkg/router/stream" ) @@ -33,10 +32,10 @@ type Router interface { // (https://xmpp.org/rfcs/rfc3921.html#rules) Route(ctx context.Context, stanza stravaganza.Stanza) (targets []jid.JID, err error) - // C2SRouter returns the underlying C2S router. + // C2S returns the underlying C2S router. C2S() C2SRouter - // S2SRouter returns the underlying S2S router. + // S2S returns the underlying S2S router. S2S() S2SRouter // Start starts global router subsystem. diff --git a/pkg/storage/cached/blocklist.go b/pkg/storage/cached/blocklist.go index 3ab0019d9..907cabaaf 100644 --- a/pkg/storage/cached/blocklist.go +++ b/pkg/storage/cached/blocklist.go @@ -55,9 +55,9 @@ type cachedBlockListRep struct { func (c *cachedBlockListRep) UpsertBlockListItem(ctx context.Context, item *blocklistmodel.Item) error { op := updateOp{ - c: c.c, - namespace: blockListNS(item.Username), - invalidKeys: []string{blockListItems}, + c: c.c, + namespace: blockListNS(item.Username), + invalidateKeys: []string{blockListItems}, updateFn: func(ctx context.Context) error { return c.rep.UpsertBlockListItem(ctx, item) }, @@ -67,9 +67,9 @@ func (c *cachedBlockListRep) UpsertBlockListItem(ctx context.Context, item *bloc func (c *cachedBlockListRep) DeleteBlockListItem(ctx context.Context, item *blocklistmodel.Item) error { op := updateOp{ - c: c.c, - namespace: blockListNS(item.Username), - invalidKeys: []string{blockListItems}, + c: c.c, + namespace: blockListNS(item.Username), + invalidateKeys: []string{blockListItems}, updateFn: func(ctx context.Context) error { return c.rep.DeleteBlockListItem(ctx, item) }, diff --git a/pkg/storage/cached/cached.go b/pkg/storage/cached/cached.go index 8adbd73d5..1f01be5c1 100644 --- a/pkg/storage/cached/cached.go +++ b/pkg/storage/cached/cached.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ type Cache interface { // Put stores a value into the cache store. Put(ctx context.Context, ns, key string, val []byte) error - // Del removes k value from the cache store. + // Del removes keys values from the cache store. Del(ctx context.Context, ns string, keys ...string) error // DelNS removes all keys contained under a given namespace from the cache store. @@ -89,7 +89,7 @@ func New(cfg Config, rep repository.Repository, logger kitlog.Logger) (repositor Capabilities: &cachedCapsRep{c: c, rep: rep}, Private: &cachedPrivateRep{c: c, rep: rep}, BlockList: &cachedBlockListRep{c: c, rep: rep}, - Roster: rep, + Roster: &cachedRosterRep{c: c, rep: rep}, VCard: &cachedVCardRep{c: c, rep: rep}, Offline: rep, Locker: rep, diff --git a/pkg/storage/cached/capabilities.go b/pkg/storage/cached/capabilities.go index a9477c8e8..b29721169 100644 --- a/pkg/storage/cached/capabilities.go +++ b/pkg/storage/cached/capabilities.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -53,9 +53,9 @@ type cachedCapsRep struct { func (c *cachedCapsRep) UpsertCapabilities(ctx context.Context, caps *capsmodel.Capabilities) error { op := updateOp{ - c: c.c, - namespace: capsNS(caps.Node, caps.Ver), - invalidKeys: []string{capsKey}, + c: c.c, + namespace: capsNS(caps.Node, caps.Ver), + invalidateKeys: []string{capsKey}, updateFn: func(ctx context.Context) error { return c.rep.UpsertCapabilities(ctx, caps) }, diff --git a/pkg/storage/cached/capabilities_test.go b/pkg/storage/cached/capabilities_test.go index e3f9b5d5a..f539ff72f 100644 --- a/pkg/storage/cached/capabilities_test.go +++ b/pkg/storage/cached/capabilities_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/storage/cached/interface.go b/pkg/storage/cached/interface.go index 1101db817..1222beef4 100644 --- a/pkg/storage/cached/interface.go +++ b/pkg/storage/cached/interface.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/storage/cached/last.go b/pkg/storage/cached/last.go index 08b1b46b2..5470d855d 100644 --- a/pkg/storage/cached/last.go +++ b/pkg/storage/cached/last.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -53,9 +53,9 @@ type cachedLastRep struct { func (c *cachedLastRep) UpsertLast(ctx context.Context, last *lastmodel.Last) error { op := updateOp{ - c: c.c, - namespace: lastNS(last.Username), - invalidKeys: []string{lastKey}, + c: c.c, + namespace: lastNS(last.Username), + invalidateKeys: []string{lastKey}, updateFn: func(ctx context.Context) error { return c.rep.UpsertLast(ctx, last) }, @@ -85,9 +85,9 @@ func (c *cachedLastRep) FetchLast(ctx context.Context, username string) (*lastmo func (c *cachedLastRep) DeleteLast(ctx context.Context, username string) error { op := updateOp{ - c: c.c, - namespace: lastNS(username), - invalidKeys: []string{lastKey}, + c: c.c, + namespace: lastNS(username), + invalidateKeys: []string{lastKey}, updateFn: func(ctx context.Context) error { return c.rep.DeleteLast(ctx, username) }, diff --git a/pkg/storage/cached/last_test.go b/pkg/storage/cached/last_test.go index eb467e832..543486386 100644 --- a/pkg/storage/cached/last_test.go +++ b/pkg/storage/cached/last_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/storage/cached/op.go b/pkg/storage/cached/op.go index 111cba3ab..ea01749f5 100644 --- a/pkg/storage/cached/op.go +++ b/pkg/storage/cached/op.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -43,16 +43,16 @@ func (op existsOp) do(ctx context.Context) (bool, error) { } type updateOp struct { - c Cache - namespace string - invalidKeys []string - updateFn func(context.Context) error + c Cache + namespace string + invalidateKeys []string + updateFn func(context.Context) error } func (op updateOp) do(ctx context.Context) error { switch { - case len(op.invalidKeys) > 0: - if err := op.c.Del(ctx, op.namespace, op.invalidKeys...); err != nil { + case len(op.invalidateKeys) > 0: + if err := op.c.Del(ctx, op.namespace, op.invalidateKeys...); err != nil { return err } diff --git a/pkg/storage/cached/op_test.go b/pkg/storage/cached/op_test.go index 6225b59c6..3bc66ae76 100644 --- a/pkg/storage/cached/op_test.go +++ b/pkg/storage/cached/op_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -63,7 +63,7 @@ func TestCachedRepository_UpdateOp(t *testing.T) { } // when - op := updateOp{c: cacheMock, invalidKeys: []string{"k0"}, updateFn: updateFn} + op := updateOp{c: cacheMock, invalidateKeys: []string{"k0"}, updateFn: updateFn} _ = op.do(context.Background()) // then diff --git a/pkg/storage/cached/private.go b/pkg/storage/cached/private.go index 502509e2a..3a37a9ee2 100644 --- a/pkg/storage/cached/private.go +++ b/pkg/storage/cached/private.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -72,9 +72,9 @@ func (c *cachedPrivateRep) FetchPrivate(ctx context.Context, namespace, username func (c *cachedPrivateRep) UpsertPrivate(ctx context.Context, private stravaganza.Element, namespace, username string) error { op := updateOp{ - c: c.c, - namespace: privateNS(username), - invalidKeys: []string{namespace}, + c: c.c, + namespace: privateNS(username), + invalidateKeys: []string{namespace}, updateFn: func(ctx context.Context) error { return c.rep.UpsertPrivate(ctx, private, namespace, username) }, diff --git a/pkg/storage/cached/private_test.go b/pkg/storage/cached/private_test.go index 649daac05..2af99114a 100644 --- a/pkg/storage/cached/private_test.go +++ b/pkg/storage/cached/private_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/storage/cached/roster.go b/pkg/storage/cached/roster.go new file mode 100644 index 000000000..a1ba3d93f --- /dev/null +++ b/pkg/storage/cached/roster.go @@ -0,0 +1,310 @@ +// Copyright 2022 The jackal Authors +// +// 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 cachedrepository + +import ( + "context" + "fmt" + "strings" + + "github.com/gogo/protobuf/types" + "github.com/golang/protobuf/proto" + rostermodel "github.com/ortuman/jackal/pkg/model/roster" + "github.com/ortuman/jackal/pkg/storage/repository" +) + +const ( + rosterVersionKey = "ver" + rosterItemsKey = "items" + rosterGroupsKey = "groups" +) + +type rosterVersionCodec struct { + val *types.Int64Value +} + +func (c *rosterVersionCodec) encode(i interface{}) ([]byte, error) { + val := &types.Int64Value{ + Value: int64(i.(int)), + } + return proto.Marshal(val) +} + +func (c *rosterVersionCodec) decode(b []byte) error { + var v types.Int64Value + if err := proto.Unmarshal(b, &v); err != nil { + return err + } + c.val = &v + return nil +} + +func (c *rosterVersionCodec) value() interface{} { + return int(c.val.Value) +} + +type rosterGroupsCodec struct { + val *rostermodel.Groups +} + +func (c *rosterGroupsCodec) encode(i interface{}) ([]byte, error) { + val := &rostermodel.Groups{ + Groups: i.([]string), + } + return proto.Marshal(val) +} + +func (c *rosterGroupsCodec) decode(b []byte) error { + var gr rostermodel.Groups + if err := proto.Unmarshal(b, &gr); err != nil { + return err + } + c.val = &gr + return nil +} + +func (c *rosterGroupsCodec) value() interface{} { + return c.val.Groups +} + +type rosterItemCodec struct { + val *rostermodel.Item +} + +func (c *rosterItemCodec) encode(i interface{}) ([]byte, error) { + return proto.Marshal(i.(*rostermodel.Item)) +} + +func (c *rosterItemCodec) decode(b []byte) error { + var itm rostermodel.Item + if err := proto.Unmarshal(b, &itm); err != nil { + return err + } + c.val = &itm + return nil +} + +func (c *rosterItemCodec) value() interface{} { + return c.val.Groups +} + +type rosterItemsCodec struct { + val *rostermodel.Items +} + +func (c *rosterItemsCodec) encode(i interface{}) ([]byte, error) { + items := rostermodel.Items{ + Items: i.([]*rostermodel.Item), + } + return proto.Marshal(&items) +} + +func (c *rosterItemsCodec) decode(b []byte) error { + var items rostermodel.Items + if err := proto.Unmarshal(b, &items); err != nil { + return err + } + c.val = &items + return nil +} + +func (c *rosterItemsCodec) value() interface{} { + return c.val.Items +} + +type cachedRosterRep struct { + c Cache + rep repository.Roster +} + +func (c *cachedRosterRep) TouchRosterVersion(ctx context.Context, username string) (int, error) { + var ver int + var err error + + op := updateOp{ + c: c.c, + namespace: rosterItemsNS(username), + invalidateKeys: []string{rosterVersionKey}, + updateFn: func(ctx context.Context) error { + ver, err = c.rep.TouchRosterVersion(ctx, username) + return err + }, + } + if err := op.do(ctx); err != nil { + return 0, err + } + return ver, nil +} + +func (c *cachedRosterRep) FetchRosterVersion(ctx context.Context, username string) (int, error) { + op := fetchOp{ + c: c.c, + namespace: rosterItemsNS(username), + key: rosterVersionKey, + codec: &rosterVersionCodec{}, + missFn: func(ctx context.Context) (interface{}, error) { + return c.rep.FetchRosterVersion(ctx, username) + }, + } + v, err := op.do(ctx) + switch { + case err != nil: + return 0, err + case v != nil: + return v.(int), nil + } + return 0, nil +} + +func (c *cachedRosterRep) UpsertRosterItem(ctx context.Context, ri *rostermodel.Item) error { + op := updateOp{ + c: c.c, + namespace: rosterItemsNS(ri.Username), + updateFn: func(ctx context.Context) error { + return c.rep.UpsertRosterItem(ctx, ri) + }, + } + return op.do(ctx) +} + +func (c *cachedRosterRep) DeleteRosterItem(ctx context.Context, username, jid string) error { + op := updateOp{ + c: c.c, + namespace: rosterItemsNS(username), + updateFn: func(ctx context.Context) error { + return c.rep.DeleteRosterItem(ctx, username, jid) + }, + } + return op.do(ctx) +} + +func (c *cachedRosterRep) DeleteRosterItems(ctx context.Context, username string) error { + op := updateOp{ + c: c.c, + namespace: rosterItemsNS(username), + updateFn: func(ctx context.Context) error { + return c.rep.DeleteRosterItems(ctx, username) + }, + } + return op.do(ctx) +} + +func (c *cachedRosterRep) FetchRosterItems(ctx context.Context, username string) ([]*rostermodel.Item, error) { + op := fetchOp{ + c: c.c, + namespace: rosterItemsNS(username), + key: rosterItemsKey, + codec: &rosterItemsCodec{}, + missFn: func(ctx context.Context) (interface{}, error) { + return c.rep.FetchRosterItems(ctx, username) + }, + } + v, err := op.do(ctx) + switch { + case err != nil: + return nil, err + case v != nil: + return v.([]*rostermodel.Item), nil + } + return nil, nil +} + +func (c *cachedRosterRep) FetchRosterItemsInGroups(ctx context.Context, username string, groups []string) ([]*rostermodel.Item, error) { + op := fetchOp{ + c: c.c, + namespace: rosterItemsNS(username), + key: rosterGroupsSliceKey(groups), + codec: &rosterItemsCodec{}, + missFn: func(ctx context.Context) (interface{}, error) { + return c.rep.FetchRosterItemsInGroups(ctx, username, groups) + }, + } + v, err := op.do(ctx) + switch { + case err != nil: + return nil, err + case v != nil: + return v.([]*rostermodel.Item), nil + } + return nil, nil +} + +func (c *cachedRosterRep) FetchRosterItem(ctx context.Context, username, jid string) (*rostermodel.Item, error) { + op := fetchOp{ + c: c.c, + namespace: rosterItemsNS(username), + key: jid, + codec: &rosterItemCodec{}, + missFn: func(ctx context.Context) (interface{}, error) { + return c.rep.FetchRosterItem(ctx, username, jid) + }, + } + v, err := op.do(ctx) + switch { + case err != nil: + return nil, err + case v != nil: + return v.(*rostermodel.Item), nil + } + return nil, nil +} + +func (c *cachedRosterRep) UpsertRosterNotification(ctx context.Context, rn *rostermodel.Notification) error { + return nil +} + +func (c *cachedRosterRep) DeleteRosterNotification(ctx context.Context, contact, jid string) error { + return nil +} + +func (c *cachedRosterRep) DeleteRosterNotifications(ctx context.Context, contact string) error { + return nil +} + +func (c *cachedRosterRep) FetchRosterNotification(ctx context.Context, contact string, jid string) (*rostermodel.Notification, error) { + return nil, nil +} + +func (c *cachedRosterRep) FetchRosterNotifications(ctx context.Context, contact string) ([]*rostermodel.Notification, error) { + return nil, nil +} + +func (c *cachedRosterRep) FetchRosterGroups(ctx context.Context, username string) ([]string, error) { + op := fetchOp{ + c: c.c, + namespace: rosterItemsNS(username), + key: rosterGroupsKey, + codec: &rosterGroupsCodec{}, + missFn: func(ctx context.Context) (interface{}, error) { + return c.rep.FetchRosterGroups(ctx, username) + }, + } + v, err := op.do(ctx) + switch { + case err != nil: + return nil, err + case v != nil: + return v.([]string), nil + } + return nil, nil +} + +func rosterItemsNS(username string) string { + return fmt.Sprintf("ros:items:%s", username) +} + +func rosterGroupsSliceKey(groups []string) string { + return fmt.Sprintf("groups:%s", strings.Join(groups, "|")) +} diff --git a/pkg/storage/cached/roster_test.go b/pkg/storage/cached/roster_test.go new file mode 100644 index 000000000..f2606d371 --- /dev/null +++ b/pkg/storage/cached/roster_test.go @@ -0,0 +1,90 @@ +// Copyright 2022 The jackal Authors +// +// 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 cachedrepository + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCachedRosterRep_TouchVersion(t *testing.T) { + // given + var cacheNS, cacheKey string + + cacheMock := &cacheMock{} + cacheMock.DelFunc = func(ctx context.Context, ns string, keys ...string) error { + cacheNS = ns + cacheKey = keys[0] + return nil + } + + repMock := &repositoryMock{} + repMock.TouchRosterVersionFunc = func(ctx context.Context, username string) (int, error) { + return 5, nil + } + + // when + rep := cachedRosterRep{ + c: cacheMock, + rep: repMock, + } + ver, err := rep.TouchRosterVersion(context.Background(), "u1") + + // then + require.NoError(t, err) + require.Equal(t, rosterItemsNS("u1"), cacheNS) + require.Equal(t, rosterVersionKey, cacheKey) + require.Equal(t, 5, ver) + require.Len(t, repMock.TouchRosterVersionCalls(), 1) +} + +func TestCachedUserRep_FetchRosterVersion(t *testing.T) { + // given + var cacheNS, cacheKey string + + cacheMock := &cacheMock{} + cacheMock.GetFunc = func(ctx context.Context, ns, k string) ([]byte, error) { + cacheNS = ns + cacheKey = k + return nil, nil + } + cacheMock.PutFunc = func(ctx context.Context, ns, k string, val []byte) error { + return nil + } + + repMock := &repositoryMock{} + repMock.FetchRosterVersionFunc = func(ctx context.Context, username string) (int, error) { + return 5, nil + } + + // when + rep := cachedRosterRep{ + c: cacheMock, + rep: repMock, + } + ver, err := rep.FetchRosterVersion(context.Background(), "u1") + + // then + require.NoError(t, err) + require.Equal(t, 5, ver) + + require.Equal(t, rosterItemsNS("u1"), cacheNS) + require.Equal(t, rosterVersionKey, cacheKey) + require.Len(t, cacheMock.GetCalls(), 1) + require.Len(t, cacheMock.PutCalls(), 1) + require.Len(t, repMock.FetchRosterVersionCalls(), 1) +} diff --git a/pkg/storage/cached/tx.go b/pkg/storage/cached/tx.go index fe143a938..5c2084a01 100644 --- a/pkg/storage/cached/tx.go +++ b/pkg/storage/cached/tx.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ func newCacheTx(c Cache, tx repository.Transaction) *cachedTx { Capabilities: &cachedCapsRep{c: c, rep: tx}, Private: &cachedPrivateRep{c: c, rep: tx}, BlockList: &cachedBlockListRep{c: c, rep: tx}, - Roster: tx, + Roster: &cachedRosterRep{c: c, rep: tx}, VCard: &cachedVCardRep{c: c, rep: tx}, Offline: tx, Locker: tx, diff --git a/pkg/storage/cached/user.go b/pkg/storage/cached/user.go index 1ba9f5d2c..93fdf4e60 100644 --- a/pkg/storage/cached/user.go +++ b/pkg/storage/cached/user.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -53,9 +53,9 @@ type cachedUserRep struct { func (c *cachedUserRep) UpsertUser(ctx context.Context, user *usermodel.User) error { op := updateOp{ - c: c.c, - namespace: userNS(user.Username), - invalidKeys: []string{userKey}, + c: c.c, + namespace: userNS(user.Username), + invalidateKeys: []string{userKey}, updateFn: func(ctx context.Context) error { return c.rep.UpsertUser(ctx, user) }, @@ -65,9 +65,9 @@ func (c *cachedUserRep) UpsertUser(ctx context.Context, user *usermodel.User) er func (c *cachedUserRep) DeleteUser(ctx context.Context, username string) error { op := updateOp{ - c: c.c, - namespace: userNS(username), - invalidKeys: []string{userKey}, + c: c.c, + namespace: userNS(username), + invalidateKeys: []string{userKey}, updateFn: func(ctx context.Context) error { return c.rep.DeleteUser(ctx, username) }, diff --git a/pkg/storage/cached/user_test.go b/pkg/storage/cached/user_test.go index 1de0fa614..7ec0482f5 100644 --- a/pkg/storage/cached/user_test.go +++ b/pkg/storage/cached/user_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 The jackal Authors +// Copyright 2022 The jackal Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/storage/cached/vcard.go b/pkg/storage/cached/vcard.go index d8ce8f076..8d67f584b 100644 --- a/pkg/storage/cached/vcard.go +++ b/pkg/storage/cached/vcard.go @@ -54,9 +54,9 @@ type cachedVCardRep struct { func (c *cachedVCardRep) UpsertVCard(ctx context.Context, vCard stravaganza.Element, username string) error { op := updateOp{ - c: c.c, - namespace: vCardNS(username), - invalidKeys: []string{vCardKey}, + c: c.c, + namespace: vCardNS(username), + invalidateKeys: []string{vCardKey}, updateFn: func(ctx context.Context) error { return c.rep.UpsertVCard(ctx, vCard, username) }, @@ -86,9 +86,9 @@ func (c *cachedVCardRep) FetchVCard(ctx context.Context, username string) (strav func (c *cachedVCardRep) DeleteVCard(ctx context.Context, username string) error { op := updateOp{ - c: c.c, - namespace: vCardNS(username), - invalidKeys: []string{vCardKey}, + c: c.c, + namespace: vCardNS(username), + invalidateKeys: []string{vCardKey}, updateFn: func(ctx context.Context) error { return c.rep.DeleteVCard(ctx, username) }, diff --git a/pkg/storage/pgsql/roster.go b/pkg/storage/pgsql/roster.go index 32c995c2b..2e9061d84 100644 --- a/pkg/storage/pgsql/roster.go +++ b/pkg/storage/pgsql/roster.go @@ -18,10 +18,9 @@ import ( "context" "database/sql" - "github.com/golang/protobuf/proto" - sq "github.com/Masterminds/squirrel" kitlog "github.com/go-kit/log" + "github.com/golang/protobuf/proto" "github.com/jackal-xmpp/stravaganza/v2" "github.com/lib/pq" rostermodel "github.com/ortuman/jackal/pkg/model/roster" diff --git a/proto/model/v1/roster.proto b/proto/model/v1/roster.proto index c7d9c2157..945673158 100644 --- a/proto/model/v1/roster.proto +++ b/proto/model/v1/roster.proto @@ -30,9 +30,24 @@ message Item { repeated string groups = 6; } +// Items represent a set of roster items. +message Items { + repeated Item items = 1; +} + // Notification represents a roster subscription pending notification. message Notification { string contact = 1; string jid = 2; stravaganza.PBElement presence = 3; } + +// Notifications represents a set of roster notifications. +message Notifications { + repeated Notification notifications = 1; +} + +// Groups represents a set of roster groups. +message Groups { + repeated string groups = 1; +}