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

View File

@ -2,13 +2,48 @@ package databag
import ( import (
"net/http" "net/http"
"gorm.io/gorm"
"databag/internal/store"
) )
func SetContentRevision(w http.ResponseWriter, r *http.Request) { 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 return nil
} }

View File

@ -2,14 +2,49 @@ package databag
import ( import (
"net/http" "net/http"
"gorm.io/gorm"
"databag/internal/store"
) )
func SetProfileRevision(w http.ResponseWriter, r *http.Request) { 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 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 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) { func BasicCredentials(r *http.Request) (string, []byte, error) {
var username string var username string

View File

@ -1,6 +1,7 @@
package databag package databag
import ( import (
"errors"
"gorm.io/gorm" "gorm.io/gorm"
"databag/internal/store" "databag/internal/store"
) )
@ -43,12 +44,24 @@ func SendNotifications() {
} }
func SendLocalNotification(notification *store.Notification) { 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 notification.Module == APP_MODULEPROFILE {
if err := NotifyProfileRevision(notification.Token, notification.Revision); err != nil { if err := NotifyProfileRevision(&card, notification.Revision); err != nil {
ErrMsg(err) ErrMsg(err)
} }
} else if notification.Module == APP_MODULECONTENT { } 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) ErrMsg(err)
} }
} else { } else {

View File

@ -2,30 +2,65 @@ package databag
import ( import (
"testing" "testing"
"encoding/json"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
) )
func TestProfileNotification(t *testing.T) { func TestProfileNotification(t *testing.T) {
var views []CardView
var revision Revision
var data []byte
// start notifcation thread // start notifcation thread
go SendNotifications() go SendNotifications()
// connect contacts
access := AddTestContacts(t, "profilenotification", 2); access := AddTestContacts(t, "profilenotification", 2);
contact := ConnectTestContacts(t, access[0], access[1]) ConnectTestContacts(t, access[0], access[1])
PrintMsg(access) // get views list of cards
PrintMsg(contact) 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 // get views list of cards
r, w, _ = NewRequest("GET", "/contact/cards/view", nil)
// check B profile incremented 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 // stop notification thread
ExitNotifications() ExitNotifications()