added profile notification

This commit is contained in:
Roland Osborne 2022-01-21 23:00:47 -08:00
parent a2354704fc
commit bc0921c721
6 changed files with 166 additions and 24 deletions

View File

@ -48,12 +48,12 @@ func SetCardStatus(w http.ResponseWriter, r *http.Request) {
}
// update card
card.Status = status
card.DataRevision += 1
if token != "" {
card.OutToken = token
}
if status == APP_CARDCONNECTING {
if card.Status != APP_CARDCONNECTING && card.Status != APP_CARDCONNECTED {
data, err := securerandom.Bytes(32)
if err != nil {
ErrResponse(w, http.StatusInternalServerError, err)
@ -61,6 +61,8 @@ func SetCardStatus(w http.ResponseWriter, r *http.Request) {
}
card.InToken = hex.EncodeToString(data)
}
}
card.Status = status
// save and update contact revision
err = store.DB.Transaction(func(tx *gorm.DB) error {

View File

@ -2,13 +2,48 @@ package databag
import (
"net/http"
"gorm.io/gorm"
"databag/internal/store"
)
func SetContentRevision(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
card, code, err := BearerContactToken(r)
if err != nil {
ErrResponse(w, code, err)
return
}
var revision int64
if err := ParseRequest(r, w, &revision); err != nil {
ErrResponse(w, http.StatusBadRequest, err)
return
}
if err := NotifyProfileRevision(card, revision); err != nil {
ErrResponse(w, http.StatusInternalServerError, err)
return
}
WriteResponse(w, nil)
}
func NotifyContentRevision(token string, revision int64) error {
func NotifyContentRevision(card *store.Card, revision int64) error {
act := &card.Account
err := store.DB.Transaction(func(tx *gorm.DB) error {
if res := tx.Model(card).Where("id = ?", card.ID).Update("remote_content", revision).Error; res != nil {
return res
}
if res := tx.Model(act).Where("id = ?", act.ID).Update("card_revision", act.CardRevision+1).Error; res != nil {
return res
}
return nil
})
if err != nil {
return err
}
SetStatus(act)
return nil
}

View File

@ -2,14 +2,49 @@ package databag
import (
"net/http"
"gorm.io/gorm"
"databag/internal/store"
)
func SetProfileRevision(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
card, code, err := BearerContactToken(r)
if err != nil {
ErrResponse(w, code, err)
return
}
var revision int64
if err := ParseRequest(r, w, &revision); err != nil {
ErrResponse(w, http.StatusBadRequest, err)
return
}
if err := NotifyProfileRevision(card, revision); err != nil {
ErrResponse(w, http.StatusInternalServerError, err)
return
}
WriteResponse(w, nil)
}
func NotifyProfileRevision(token string, revision int64) error {
func NotifyProfileRevision(card *store.Card, revision int64) error {
card.RemoteProfile = revision
act := &card.Account
err := store.DB.Transaction(func(tx *gorm.DB) error {
if res := tx.Model(card).Where("id = ?", card.ID).Update("remote_profile", revision).Error; res != nil {
return res
}
if res := tx.Model(act).Where("id = ?", act.ID).Update("card_revision", act.CardRevision+1).Error; res != nil {
return res
}
return nil
})
if err != nil {
return err
}
SetStatus(act)
return nil
}

View File

@ -114,6 +114,28 @@ func BearerAppToken(r *http.Request, detail bool) (*store.Account, int, error) {
return &app.Account, http.StatusOK, nil
}
func BearerContactToken(r *http.Request) (*store.Card, int, error) {
// parse bearer authentication
auth := r.Header.Get("Authorization")
token := strings.TrimSpace(strings.TrimPrefix(auth, "Bearer"))
// find token record
var card store.Card
if err := store.DB.Preload("Account").Where("InToken = ?", token).First(&card).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, http.StatusNotFound, err
} else {
return nil, http.StatusInternalServerError, err
}
}
if card.Account.Disabled {
return nil, http.StatusGone, errors.New("account is inactive")
}
return &card, http.StatusOK, nil
}
func BasicCredentials(r *http.Request) (string, []byte, error) {
var username string

View File

@ -1,6 +1,7 @@
package databag
import (
"errors"
"gorm.io/gorm"
"databag/internal/store"
)
@ -43,12 +44,24 @@ func SendNotifications() {
}
func SendLocalNotification(notification *store.Notification) {
// pull reference account
var card store.Card
if err := store.DB.Preload("Account").Where("in_token = ?", notification.Token).First(&card).Error; err != nil {
ErrMsg(err)
return
}
if card.Account.Disabled {
ErrMsg(errors.New("account is inactive"))
return
}
if notification.Module == APP_MODULEPROFILE {
if err := NotifyProfileRevision(notification.Token, notification.Revision); err != nil {
if err := NotifyProfileRevision(&card, notification.Revision); err != nil {
ErrMsg(err)
}
} else if notification.Module == APP_MODULECONTENT {
if err := NotifyContentRevision(notification.Token, notification.Revision); err != nil {
if err := NotifyContentRevision(&card, notification.Revision); err != nil {
ErrMsg(err)
}
} else {

View File

@ -2,30 +2,65 @@ package databag
import (
"testing"
"encoding/json"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
)
func TestProfileNotification(t *testing.T) {
var views []CardView
var revision Revision
var data []byte
// start notifcation thread
go SendNotifications()
// connect contacts
access := AddTestContacts(t, "profilenotification", 2);
contact := ConnectTestContacts(t, access[0], access[1])
ConnectTestContacts(t, access[0], access[1])
PrintMsg(access)
PrintMsg(contact)
// get views list of cards
r, w, _ := NewRequest("GET", "/contact/cards/view", nil)
SetBearerAuth(r, access[0])
GetCardView(w, r)
assert.NoError(t, ReadResponse(w, &views))
assert.Equal(t, len(views), 1)
profileRevision := views[0].RemoteProfile
// connect revision websocket for A
// app connects websocket
ws := getTestWebsocket()
announce := Announce{ AppToken: access[0] }
data, _ = json.Marshal(&announce)
ws.WriteMessage(websocket.TextMessage, data)
// get profile revision of B
// receive revision
_, data, _ = ws.ReadMessage()
assert.NoError(t, json.Unmarshal(data, &revision))
cardRevision := revision.Card
// update profile of B
// update B profile
profileData := ProfileData{
Name: "Namer",
Location: "San Francisco",
Description: "databaggerr",
};
r, w, _ = NewRequest("PUT", "/profile/data", &profileData)
SetBearerAuth(r, access[1])
SetProfile(w, r)
assert.NoError(t, ReadResponse(w, nil))
// read revision message
// receive revision
_, data, _ = ws.ReadMessage()
assert.NoError(t, json.Unmarshal(data, &revision))
assert.NotEqual(t, cardRevision, revision.Card)
// check card increment
// check B profile incremented
// get views list of cards
r, w, _ = NewRequest("GET", "/contact/cards/view", nil)
SetBearerAuth(r, access[0])
GetCardView(w, r)
assert.NoError(t, ReadResponse(w, &views))
assert.Equal(t, len(views), 1)
assert.NotEqual(t, profileRevision, views[0].RemoteProfile)
// stop notification thread
ExitNotifications()