Skip to content
Snippets Groups Projects
Commit 57b7e3b8 authored by Josh Brooks's avatar Josh Brooks
Browse files

Merge branch 'XX-4082/Notifications' into 'release'

Implement Notifications for bindings

See merge request !360
parents 9a44f798 d8abdf19
No related branches found
No related tags found
2 merge requests!510Release,!360Implement Notifications for bindings
...@@ -10,6 +10,7 @@ package bindings ...@@ -10,6 +10,7 @@ package bindings
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"gitlab.com/elixxir/client/cmix/message"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
...@@ -168,3 +169,50 @@ func (c *Cmix) RegisterClientErrorCallback(clientError ClientError) { ...@@ -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))
})
}
...@@ -7,67 +7,141 @@ ...@@ -7,67 +7,141 @@
package bindings package bindings
// FIXME: This is the old NotificationsForMe code that needs to be fixed import (
/* "encoding/json"
type NotificationForMeReport struct { "gitlab.com/elixxir/client/cmix/message"
ForMe bool "gitlab.com/elixxir/primitives/notifications"
Type string )
// 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 Source []byte
} }
type ManyNotificationForMeReport struct { // GetNotificationsReport parses the received notification data to determine which
Many []*NotificationForMeReport // 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 serviceList := message.ServiceList{}
// It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller, err = json.Unmarshal(marshalledServices, &serviceList)
// a Type, and a source. These are as follows: if err != nil {
// TYPE SOURCE DESCRIPTION return nil, err
// "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")
} }
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 { if err != nil {
return nil, err return nil, err
} }
notifList := make([]*NotificationForMeReport, len(list)) // Construct a report list
reportList := make([]*NotificationReport, len(notificationList))
for i, notifData := range list { // Iterate over data provided by server
notifList[i] = &NotificationForMeReport{ for i := range notificationList {
ForMe: false, notifData := notificationList[i]
Type: "",
Source: nil, // Iterate over all services
} for j := range services {
// check if any preimages match with the passed in data // Pull data from services and from notification data
for _, preimage := range preimageList { service := services[j]
if fingerprint.CheckIdentityFpFromMessageHash(notifData.IdentityFP, notifData.MessageHash, preimage.Data) { messageHash := notifData.MessageHash
notifList[i] = &NotificationForMeReport{ 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, ForMe: true,
Type: preimage.Type, Type: service.Tag,
Source: preimage.Source, Source: service.Identifier,
} }
break
} }
} }
} }
return &ManyNotificationForMeReport{notifList}, nil return json.Marshal(reportList)
}*/ }
// RegisterForNotifications allows a client to register for push notifications. // RegisterForNotifications allows a client to register for push notifications.
// The token is a firebase messaging token. // The token is a firebase messaging token.
......
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)
}
...@@ -26,7 +26,6 @@ func (sm *ServicesManager) triggerServiceTracking() { ...@@ -26,7 +26,6 @@ func (sm *ServicesManager) triggerServiceTracking() {
if len(sm.trackers) == 0 { if len(sm.trackers) == 0 {
return return
} }
services := make(ServiceList) services := make(ServiceList)
for uid, tmap := range sm.tmap { for uid, tmap := range sm.tmap {
tList := make([]Service, 0, len(tmap)) tList := make([]Service, 0, len(tmap))
......
...@@ -13,7 +13,7 @@ import ( ...@@ -13,7 +13,7 @@ import (
func TestServiceList_Marshal_UnmarshalJSON(t *testing.T) { func TestServiceList_Marshal_UnmarshalJSON(t *testing.T) {
var sl ServiceList = make(map[id.ID][]Service) var sl ServiceList = make(map[id.ID][]Service)
numServices := 10 numServices := 3
testString := "test" testString := "test"
for i := 0; i < numServices; i++ { for i := 0; i < numServices; i++ {
uid := id.NewIdFromUInt(uint64(i), id.User, t) uid := id.NewIdFromUInt(uint64(i), id.User, t)
...@@ -24,6 +24,8 @@ func TestServiceList_Marshal_UnmarshalJSON(t *testing.T) { ...@@ -24,6 +24,8 @@ func TestServiceList_Marshal_UnmarshalJSON(t *testing.T) {
t.Errorf(err.Error()) t.Errorf(err.Error())
} }
t.Logf("%s", jsonResult)
sl = make(map[id.ID][]Service) sl = make(map[id.ID][]Service)
err = sl.UnmarshalJSON(jsonResult) err = sl.UnmarshalJSON(jsonResult)
if err != nil { if err != nil {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment