business: add privacy-aware user projection
(cherry picked from commit a636192ef22ee69d792b0ca7db1c6be963be9cb2)
This commit is contained in:
parent
14d220c971
commit
8ff3343ae0
29 changed files with 2239 additions and 112 deletions
|
|
@ -10,9 +10,12 @@ import (
|
|||
|
||||
// Service 提供消息历史、搜索与已读业务。
|
||||
type Service struct {
|
||||
messages store.MessageStore
|
||||
dialogs store.DialogStore
|
||||
contacts store.ContactStore
|
||||
messages store.MessageStore
|
||||
dialogs store.DialogStore
|
||||
contacts store.ContactStore
|
||||
photos userprojection.ProfilePhotoProvider
|
||||
privacy userprojection.PrivacyEvaluator
|
||||
projector *userprojection.Projector
|
||||
}
|
||||
|
||||
// Option adjusts optional message service dependencies.
|
||||
|
|
@ -23,12 +26,27 @@ func WithContactStore(c store.ContactStore) Option {
|
|||
return func(s *Service) { s.contacts = c }
|
||||
}
|
||||
|
||||
// WithPhotoProvider enables current profile photo enrichment for message users.
|
||||
func WithPhotoProvider(p userprojection.ProfilePhotoProvider) Option {
|
||||
return func(s *Service) { s.photos = p }
|
||||
}
|
||||
|
||||
// WithPrivacyEvaluator enables viewer-specific privacy projection for message users.
|
||||
func WithPrivacyEvaluator(p userprojection.PrivacyEvaluator) Option {
|
||||
return func(s *Service) { s.privacy = p }
|
||||
}
|
||||
|
||||
// NewService 创建 messages 服务。
|
||||
func NewService(messages store.MessageStore, dialogs store.DialogStore, opts ...Option) *Service {
|
||||
s := &Service{messages: messages, dialogs: dialogs}
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
s.projector = userprojection.New(
|
||||
userprojection.WithContactStore(s.contacts),
|
||||
userprojection.WithPhotoProvider(s.photos),
|
||||
userprojection.WithPrivacyEvaluator(s.privacy),
|
||||
)
|
||||
return s
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +220,10 @@ func (s *Service) list(ctx context.Context, userID int64, filter domain.MessageF
|
|||
}
|
||||
|
||||
func (s *Service) projectMessageUsers(ctx context.Context, userID int64, list domain.MessageList) (domain.MessageList, error) {
|
||||
users, err := userprojection.ForViewer(ctx, s.contacts, userID, list.Users)
|
||||
if s == nil || s.projector == nil {
|
||||
return list, nil
|
||||
}
|
||||
users, err := s.projector.ForViewer(ctx, userID, list.Users)
|
||||
if err != nil {
|
||||
return domain.MessageList{}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,10 @@ func TestServiceProjectsMessageUsersForViewerContacts(t *testing.T) {
|
|||
{ID: strangerID, AccessHash: 33, Phone: "15550000003", FirstName: "Stranger"},
|
||||
},
|
||||
}}
|
||||
svc := NewService(store, nil, WithContactStore(contacts))
|
||||
svc := NewService(store, nil, WithContactStore(contacts), WithPhotoProvider(messageProfilePhotos{
|
||||
friendID: {PhotoID: 9101, DCID: 2, Stripped: []byte{5, 6}},
|
||||
strangerID: {PhotoID: 9102, DCID: 4},
|
||||
}))
|
||||
|
||||
list, err := svc.GetHistory(ctx, ownerID, domain.MessageFilter{Limit: 10})
|
||||
if err != nil {
|
||||
|
|
@ -39,10 +42,16 @@ func TestServiceProjectsMessageUsersForViewerContacts(t *testing.T) {
|
|||
if !friend.Contact || friend.FirstName != "Remark" || friend.LastName != "Friend" || friend.Phone != "15550000002" {
|
||||
t.Fatalf("friend projection = %+v, want contact remark and phone", friend)
|
||||
}
|
||||
if friend.PhotoID != 9101 || friend.PhotoDCID != 2 || string(friend.PhotoStripped) != string([]byte{5, 6}) {
|
||||
t.Fatalf("friend photo = id %d dc %d stripped %v, want 9101/2/[5 6]", friend.PhotoID, friend.PhotoDCID, friend.PhotoStripped)
|
||||
}
|
||||
stranger := findUser(t, list.Users, strangerID)
|
||||
if stranger.Contact || stranger.Phone != "" || stranger.FirstName != "Stranger" {
|
||||
t.Fatalf("stranger projection = %+v, want non-contact with hidden phone", stranger)
|
||||
}
|
||||
if stranger.PhotoID != 9102 || stranger.PhotoDCID != 4 {
|
||||
t.Fatalf("stranger photo = id %d dc %d, want 9102/4", stranger.PhotoID, stranger.PhotoDCID)
|
||||
}
|
||||
self := findUser(t, list.Users, ownerID)
|
||||
if self.Phone != "15550000001" {
|
||||
t.Fatalf("self phone = %q, want preserved", self.Phone)
|
||||
|
|
@ -64,6 +73,18 @@ type projectionMessageStore struct {
|
|||
list domain.MessageList
|
||||
}
|
||||
|
||||
type messageProfilePhotos map[int64]domain.ProfilePhotoRef
|
||||
|
||||
func (p messageProfilePhotos) CurrentProfilePhotos(_ context.Context, _ domain.PeerType, ids []int64) (map[int64]domain.ProfilePhotoRef, error) {
|
||||
out := make(map[int64]domain.ProfilePhotoRef, len(ids))
|
||||
for _, id := range ids {
|
||||
if ref, ok := p[id]; ok {
|
||||
out[id] = ref
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s projectionMessageStore) Create(context.Context, domain.Message) (domain.Message, error) {
|
||||
return domain.Message{}, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue