diff --git a/bindings/follow.go b/bindings/follow.go index 6267a34125388a681827437d7a1e1cea1aa1c8dd..199990bd69ee51e2b377d501ec537399dca89eab 100644 --- a/bindings/follow.go +++ b/bindings/follow.go @@ -10,6 +10,7 @@ package bindings import ( "encoding/json" "fmt" + "gitlab.com/elixxir/client/cmix/message" "time" "github.com/pkg/errors" @@ -168,3 +169,50 @@ func (c *Cmix) RegisterClientErrorCallback(clientError ClientError) { } }() } + +// TrackServicesCallback is the callback for Cmix.TrackServices. +// This will pass to the user a JSON-marshalled list of backend services. +// If there was an error retrieving or marshalling the service list, +// there is an error for the second parameter which will be non-null. +// +// Example JSON: +// +// [ +// { +// "Id": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD", +// "Services": [ +// { +// "Identifier": null, +// "Tag": "test", +// "Metadata": null +// } +// ] +// }, +// { +// "Id": "AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD", +// "Services": [ +// { +// "Identifier": null, +// "Tag": "test", +// "Metadata": null +// } +// ] +// }, +//] +type TrackServicesCallback interface { + Callback(marshalData []byte, err error) +} + +// TrackServices will return via a callback the list of services the +// backend keeps track of, which is formally referred to as a +// [message.ServiceList]. This may be passed into other bindings call which +// may need context on the available services for this client. +// +// Parameters: +// - cb - A TrackServicesCallback, which will be passed the marshalled +// message.ServiceList. +func (c *Cmix) TrackServices(cb TrackServicesCallback) { + c.api.GetCmix().TrackServices(func(list message.ServiceList) { + cb.Callback(json.Marshal(list)) + }) +} diff --git a/bindings/notifications.go b/bindings/notifications.go index bbc79ca79c028ceac33a5ba1e972119f904aa4ba..e028ae0ce7a2186a2afd67873e668e7109a33022 100644 --- a/bindings/notifications.go +++ b/bindings/notifications.go @@ -7,67 +7,141 @@ package bindings -// FIXME: This is the old NotificationsForMe code that needs to be fixed -/* -type NotificationForMeReport struct { - ForMe bool - Type string +import ( + "encoding/json" + "gitlab.com/elixxir/client/cmix/message" + "gitlab.com/elixxir/primitives/notifications" +) + +// NotificationReports is a list of NotificationReport's. This will be returned +// via GetNotificationsReport as a JSON marshalled byte data. +// +// Example JSON: +// +// [ +// { +// "ForMe": true, +// "Type": "e2e", +// "Source": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD" +// }, +// { +// "ForMe": true, +// "Type": "e2e", +// "Source": "AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD" +// }, +// { +// "ForMe": true, +// "Type": "e2e", +// "Source": "AAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD" +// } +//] +type NotificationReports []NotificationReport + +// NotificationReport is the bindings' representation for notifications for +// this user. +// +// Example NotificationReport JSON: +// +// { +// "ForMe": true, +// "Type": "e2e", +// "Source": "dGVzdGVyMTIzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +//} +// +// Given the Type, the Source value will have specific contextual meanings. +// Below is a table that will define the contextual meaning of the Source field +// given all possible Type fields. +// +// TYPE | SOURCE | DESCRIPTION +// ________________________________________________________________________________________ +// "default" | recipient user ID | A message with no association. +// "request" | sender user ID | A channel request has been received, from Source. +// "reset" | sender user ID | A channel reset has been received. +// "confirm" | sender user ID | A channel request has been accepted. +// "silent" | sender user ID | A message where the user should not be notified. +// "e2e" | sender user ID | A reception of an E2E message. +// "group" | group ID | A reception of a group chat message. +// "endFT" | sender user ID | The last message sent confirming end of file transfer. +// "groupRQ" | sender user ID | A request from Source to join a group chat. +// todo iterate over this docstring, ensure descriptions/sources are +// still accurate (they are from the old implementation +type NotificationReport struct { + // ForMe determines whether this value is for the user. If it is + // false, this report may be ignored. + ForMe bool + // Type is the type of notification. The list can be seen + Type string + // Source is the source of the notification. Source []byte } -type ManyNotificationForMeReport struct { - Many []*NotificationForMeReport -} +// GetNotificationsReport parses the received notification data to determine which +// notifications are for this user. // This returns the JSON-marshalled +// NotificationReports. +// +// Parameters: +// - e2eID - e2e object ID in the tracker +// - notificationCSV - the notification data received from the +// notifications' server. +// - marshalledServices - the JSON-marshalled list of services the backend +// keeps track of. Refer to Cmix.TrackServices for information about this. +// +// Returns: +// - []byte - A JSON marshalled NotificationReports. Some NotificationReport's +// within in this structure may have their NotificationReport.ForMe +// set to false. These may be ignored. +func GetNotificationsReport(e2eId int, notificationCSV string, + marshalledServices []byte) ([]byte, error) { + // Retrieve user + user, err := e2eTrackerSingleton.get(e2eId) + if err != nil { + return nil, err + } -// NotificationsForMe Check if a notification received is for me -// It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller, -// a Type, and a source. These are as follows: -// TYPE SOURCE DESCRIPTION -// "default" recipient user ID A message with no association -// "request" sender user ID A channel request has been received -// "reset" sender user ID A channel reset has been received -// "confirm" sender user ID A channel request has been accepted -// "silent" sender user ID A message which should not be notified on -// "e2e" sender user ID reception of an E2E message -// "group" group ID reception of a group chat message -// "endFT" sender user ID Last message sent confirming end of file transfer -// "groupRQ" sender user ID Request from sender to join a group chat -func NotificationsForMe(notifCSV, preimages string) (*ManyNotificationForMeReport, error) { - // Handle deserialization of preimages - var preimageList []edge.Preimage - if err := json.Unmarshal([]byte(preimages), &preimageList); err != nil { - return nil, errors.WithMessagef(err, "Failed to unmarshal the " + - "preimages list, cannot check if notification is for me") + serviceList := message.ServiceList{} + err = json.Unmarshal(marshalledServices, &serviceList) + if err != nil { + return nil, err } - list, err := notifications.DecodeNotificationsCSV(notifCSV) + // Retrieve the services for this user + services := serviceList[*user.api.GetReceptionIdentity().ID] + + // Decode notifications' server data + notificationList, err := notifications.DecodeNotificationsCSV(notificationCSV) if err != nil { return nil, err } - notifList := make([]*NotificationForMeReport, len(list)) + // Construct a report list + reportList := make([]*NotificationReport, len(notificationList)) - for i, notifData := range list { - notifList[i] = &NotificationForMeReport{ - ForMe: false, - Type: "", - Source: nil, - } - // check if any preimages match with the passed in data - for _, preimage := range preimageList { - if fingerprint.CheckIdentityFpFromMessageHash(notifData.IdentityFP, notifData.MessageHash, preimage.Data) { - notifList[i] = &NotificationForMeReport{ + // Iterate over data provided by server + for i := range notificationList { + notifData := notificationList[i] + + // Iterate over all services + for j := range services { + // Pull data from services and from notification data + service := services[j] + messageHash := notifData.MessageHash + hash := service.HashFromMessageHash(notifData.MessageHash) + + // Check if this notification data is recognized by + // this service, ie "ForMe" + if service.ForMeFromMessageHash(messageHash, hash) { + // Fill report list with service data + reportList[i] = &NotificationReport{ ForMe: true, - Type: preimage.Type, - Source: preimage.Source, + Type: service.Tag, + Source: service.Identifier, } - break } } } - return &ManyNotificationForMeReport{notifList}, nil -}*/ + return json.Marshal(reportList) +} // RegisterForNotifications allows a client to register for push notifications. // The token is a firebase messaging token. diff --git a/bindings/notifications_test.go b/bindings/notifications_test.go new file mode 100644 index 0000000000000000000000000000000000000000..390062f08456961cb3e18eb84675e64f18e867b8 --- /dev/null +++ b/bindings/notifications_test.go @@ -0,0 +1,28 @@ +package bindings + +import ( + "encoding/json" + "fmt" + "gitlab.com/elixxir/client/e2e/ratchet" + "gitlab.com/xx_network/primitives/id" + "testing" +) + +func TestNotificationReport(t *testing.T) { + reports := []NotificationReport{} + + for i := 0; i < 3; i++ { + nr := NotificationReport{ + ForMe: true, + Type: ratchet.E2e, + Source: id.NewIdFromUInt(uint64(i), id.User, t).Bytes(), + } + + reports = append(reports, nr) + } + + nrs := NotificationReports(reports) + + marshal, _ := json.Marshal(nrs) + fmt.Printf("%s\n", marshal) +} diff --git a/cmix/message/serviceTracker.go b/cmix/message/serviceTracker.go index 616f6dfda392ba4cb65c8d02179bbae493fe26ec..e3313a4de659d935b26d6e4477c1e5056a558497 100644 --- a/cmix/message/serviceTracker.go +++ b/cmix/message/serviceTracker.go @@ -26,7 +26,6 @@ func (sm *ServicesManager) triggerServiceTracking() { if len(sm.trackers) == 0 { return } - services := make(ServiceList) for uid, tmap := range sm.tmap { tList := make([]Service, 0, len(tmap)) diff --git a/cmix/message/serviceTracker_test.go b/cmix/message/serviceTracker_test.go index c7b06b46a853bcc23e267830c9e2cda925385b3a..a1a487762c271bde32476a07652cfd4264b311bf 100644 --- a/cmix/message/serviceTracker_test.go +++ b/cmix/message/serviceTracker_test.go @@ -13,7 +13,7 @@ import ( func TestServiceList_Marshal_UnmarshalJSON(t *testing.T) { var sl ServiceList = make(map[id.ID][]Service) - numServices := 10 + numServices := 3 testString := "test" for i := 0; i < numServices; i++ { uid := id.NewIdFromUInt(uint64(i), id.User, t) @@ -24,6 +24,8 @@ func TestServiceList_Marshal_UnmarshalJSON(t *testing.T) { t.Errorf(err.Error()) } + t.Logf("%s", jsonResult) + sl = make(map[id.ID][]Service) err = sl.UnmarshalJSON(jsonResult) if err != nil {