mirror of
https://github.com/balzack/databag.git
synced 2025-02-11 19:19:16 +00:00
update notified revisions on card status update
This commit is contained in:
parent
6dfab23234
commit
7f625cd9bc
@ -33,7 +33,7 @@ func AddCard(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
slot := &store.CardSlot{}
|
||||
var card store.Card
|
||||
if err := store.DB.Preload("Card.Groups").Where("account_id = ? AND guid = ?", account.ID, guid).First(&card).Error; err != nil {
|
||||
if err := store.DB.Preload("CardSlot").Preload("Groups").Where("account_id = ? AND guid = ?", account.Guid, guid).First(&card).Error; err != nil {
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
ErrResponse(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
@ -110,7 +110,6 @@ func AddCard(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
if identity.Revision > card.ProfileRevision {
|
||||
|
||||
// update card data
|
||||
@ -122,35 +121,33 @@ func AddCard(w http.ResponseWriter, r *http.Request) {
|
||||
card.Version = identity.Version
|
||||
card.Node = identity.Node
|
||||
card.ProfileRevision = identity.Revision
|
||||
|
||||
// save contact card
|
||||
err = store.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if res := tx.Save(&card).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
if res := store.DB.Where("account_id = ? AND card_id = ?", account.ID, card.ID).First(&slot).Error; res != nil {
|
||||
if !errors.Is(res, gorm.ErrRecordNotFound) {
|
||||
return nil
|
||||
}
|
||||
slot = &store.CardSlot{
|
||||
CardSlotId: uuid.New().String(),
|
||||
AccountID: account.ID,
|
||||
Revision: account.CardRevision + 1,
|
||||
CardID: card.ID,
|
||||
Card: &card,
|
||||
}
|
||||
} else {
|
||||
slot.Revision = account.CardRevision + 1
|
||||
}
|
||||
if res := tx.Preload("Card").Save(slot).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
if res := tx.Model(&account).Update("card_revision", account.CardRevision + 1).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// save contact card
|
||||
err = store.DB.Transaction(func(tx *gorm.DB) error {
|
||||
slot = &card.CardSlot
|
||||
if res := tx.Save(&card).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
if slot == nil {
|
||||
slot = &store.CardSlot{
|
||||
CardSlotId: uuid.New().String(),
|
||||
AccountID: account.ID,
|
||||
Revision: account.CardRevision + 1,
|
||||
CardID: card.ID,
|
||||
Card: &card,
|
||||
}
|
||||
} else {
|
||||
slot.Revision = account.CardRevision + 1
|
||||
}
|
||||
if res := tx.Preload("Card").Save(slot).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
if res := tx.Model(&account).Update("card_revision", account.CardRevision + 1).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -11,6 +11,7 @@ func GetArticles(w http.ResponseWriter, r *http.Request) {
|
||||
var res error
|
||||
var viewRevision int64
|
||||
var contentRevision int64
|
||||
var revisionSet bool
|
||||
|
||||
view := r.FormValue("viewRevision")
|
||||
if view != "" {
|
||||
@ -25,6 +26,7 @@ func GetArticles(w http.ResponseWriter, r *http.Request) {
|
||||
ErrResponse(w, http.StatusBadRequest, res)
|
||||
return
|
||||
}
|
||||
revisionSet = true
|
||||
}
|
||||
|
||||
tokenType := r.Header.Get("TokenType")
|
||||
@ -55,7 +57,12 @@ func GetArticles(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if viewRevision != card.ViewRevision + card.Account.ViewRevision {
|
||||
contentRevision = 0
|
||||
if revisionSet {
|
||||
ErrResponse(w, http.StatusGone, errors.New("article view unavailable"))
|
||||
return
|
||||
} else {
|
||||
w.Header().Set("View-Revision", strconv.FormatInt(card.ViewRevision + card.Account.ViewRevision, 10))
|
||||
}
|
||||
}
|
||||
|
||||
var articles []store.ArticleSlot
|
||||
@ -65,7 +72,11 @@ func GetArticles(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
for _, article := range articles {
|
||||
response = append(response, getArticleModel(&article, true, isShared(&article, card.Guid)))
|
||||
if isShared(&article, card.Guid) {
|
||||
response = append(response, getArticleModel(&article, true, true))
|
||||
} else if revisionSet {
|
||||
response = append(response, getArticleModel(&article, true, false))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ErrResponse(w, http.StatusBadRequest, errors.New("invalid token type"))
|
||||
|
@ -42,6 +42,8 @@ func GetOpenMessage(w http.ResponseWriter, r *http.Request) {
|
||||
Token: slot.Card.InToken,
|
||||
ContentRevision: account.ContentRevision + account.ViewRevision + slot.Card.ViewRevision,
|
||||
ProfileRevision: account.ProfileRevision,
|
||||
ViewRevision: account.ViewRevision + slot.Card.ViewRevision,
|
||||
LabelRevision: account.LabelRevision,
|
||||
Handle: account.Username,
|
||||
Name: detail.Name,
|
||||
Description: detail.Description,
|
||||
|
@ -2,6 +2,7 @@ package databag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"net/http"
|
||||
"encoding/hex"
|
||||
"gorm.io/gorm"
|
||||
@ -11,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func SetCardStatus(w http.ResponseWriter, r *http.Request) {
|
||||
var res error
|
||||
|
||||
account, code, err := BearerAppToken(r, false);
|
||||
if err != nil {
|
||||
@ -23,6 +25,40 @@ func SetCardStatus(w http.ResponseWriter, r *http.Request) {
|
||||
cardId := params["cardId"]
|
||||
token := r.FormValue("token")
|
||||
|
||||
// scan revisions
|
||||
var viewRevision int64
|
||||
view := r.FormValue("viewRevision")
|
||||
if view != "" {
|
||||
if viewRevision, res = strconv.ParseInt(view, 10, 64); res != nil {
|
||||
ErrResponse(w, http.StatusBadRequest, res)
|
||||
return
|
||||
}
|
||||
}
|
||||
var contentRevision int64
|
||||
content := r.FormValue("contentRevision")
|
||||
if content != "" {
|
||||
if contentRevision, res = strconv.ParseInt(content, 10, 64); res != nil {
|
||||
ErrResponse(w, http.StatusBadRequest, res)
|
||||
return
|
||||
}
|
||||
}
|
||||
var labelRevision int64
|
||||
label := r.FormValue("labelRevision")
|
||||
if label != "" {
|
||||
if labelRevision, res = strconv.ParseInt(label, 10, 64); res != nil {
|
||||
ErrResponse(w, http.StatusBadRequest, res)
|
||||
return
|
||||
}
|
||||
}
|
||||
var profileRevision int64
|
||||
profile := r.FormValue("profileRevision")
|
||||
if profile != "" {
|
||||
if profileRevision, res = strconv.ParseInt(profile, 10, 64); res != nil {
|
||||
ErrResponse(w, http.StatusBadRequest, res)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var status string
|
||||
if err := ParseRequest(r, w, &status); err != nil {
|
||||
ErrResponse(w, http.StatusBadRequest, err)
|
||||
@ -68,6 +104,10 @@ func SetCardStatus(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
slot.Card.Status = status
|
||||
slot.Card.NotifiedView = viewRevision
|
||||
slot.Card.NotifiedContent = contentRevision
|
||||
slot.Card.NotifiedLabel = labelRevision
|
||||
slot.Card.NotifiedProfile = profileRevision
|
||||
|
||||
// save and update contact revision
|
||||
err = store.DB.Transaction(func(tx *gorm.DB) error {
|
||||
|
@ -69,6 +69,7 @@ func SetOpenMessage(w http.ResponseWriter, r *http.Request) {
|
||||
card.NotifiedProfile = connect.ProfileRevision
|
||||
card.NotifiedContent = connect.ContentRevision
|
||||
card.NotifiedView = connect.ViewRevision
|
||||
card.NotifiedLabel = connect.LabelRevision
|
||||
card.OutToken = connect.Token
|
||||
card.InToken = hex.EncodeToString(data)
|
||||
card.AccountID = account.Guid
|
||||
@ -140,6 +141,9 @@ func SetOpenMessage(w http.ResponseWriter, r *http.Request) {
|
||||
if connect.ViewRevision > card.NotifiedView {
|
||||
card.NotifiedView = connect.ViewRevision
|
||||
}
|
||||
if connect.LabelRevision > card.NotifiedLabel {
|
||||
card.NotifiedLabel = connect.LabelRevision
|
||||
}
|
||||
if connect.ProfileRevision > card.NotifiedProfile {
|
||||
card.NotifiedProfile = connect.ProfileRevision
|
||||
}
|
||||
@ -187,7 +191,15 @@ func SetOpenMessage(w http.ResponseWriter, r *http.Request) {
|
||||
status := &ContactStatus{
|
||||
Token: slot.Card.InToken,
|
||||
Status: slot.Card.Status,
|
||||
ViewRevision: slot.Card.ViewRevision + account.ViewRevision,
|
||||
LabelRevision: account.LabelRevision,
|
||||
ProfileRevision: account.ProfileRevision,
|
||||
ContentRevision: account.ContentRevision,
|
||||
}
|
||||
//SetContactProfileNotification(&account, slot.Card)
|
||||
//SetContactContentNotification(&account, slot.Card)
|
||||
//SetContactViewNotification(&account, slot.Card)
|
||||
//SetContactLabelNotification(&account, slot.Card)
|
||||
SetStatus(&account)
|
||||
WriteResponse(w, &status)
|
||||
}
|
||||
|
@ -159,6 +159,9 @@ func BearerContactToken(r *http.Request, detail bool) (*store.Card, int, error)
|
||||
if card.Account.Disabled {
|
||||
return nil, http.StatusGone, errors.New("account is inactive")
|
||||
}
|
||||
if card.Status != APP_CARDCONNECTING && card.Status != APP_CARDCONNECTED {
|
||||
return nil, http.StatusUnauthorized, errors.New("invalid connection state")
|
||||
}
|
||||
|
||||
return &card, http.StatusOK, nil
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ func WriteResponse(w http.ResponseWriter, v interface{}) {
|
||||
log.Printf("%s:%d %s", strings.TrimPrefix(file, p), line, err.Error())
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
} else {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(body);
|
||||
}
|
||||
}
|
||||
|
@ -261,6 +261,10 @@ type Tunnel struct {
|
||||
type ContactStatus struct {
|
||||
Token string `json:"token,omitempty"`
|
||||
Status string `json:"status"`
|
||||
ViewRevision int64 `json:"viewRevision"`
|
||||
LabelRevision int64 `json:"labelRevision"`
|
||||
ContentRevision int64 `json:"contentRevision"`
|
||||
ProfileRevision int64 `json:"profileRevision,omitempty"`
|
||||
}
|
||||
|
||||
type DataMessage struct {
|
||||
@ -293,6 +297,7 @@ type Connect struct {
|
||||
Contact string `json:"contact"`
|
||||
Token string `json:"token"`
|
||||
ViewRevision int64 `json:"viewRevision"`
|
||||
LabelRevision int64 `json:"labelRevision"`
|
||||
ContentRevision int64 `json:"contentRevision"`
|
||||
ProfileRevision int64 `json:"profileRevision,omitempty"`
|
||||
Handle string `json:"handle,omitempty"`
|
||||
|
@ -112,6 +112,29 @@ func SetProfileNotification(account *store.Account) {
|
||||
}
|
||||
}
|
||||
|
||||
// notify single card of profile revision
|
||||
func SetContactProfileNotification(account *store.Account, card *store.Card) {
|
||||
|
||||
if card.Status != APP_CARDCONNECTED {
|
||||
return
|
||||
}
|
||||
|
||||
// add new notification for card
|
||||
notification := &store.Notification{
|
||||
Node: card.Node,
|
||||
Module: APP_NOTIFYPROFILE,
|
||||
Token: card.OutToken,
|
||||
Revision: account.ProfileRevision,
|
||||
}
|
||||
|
||||
if res := store.DB.Save(notification).Error; res != nil {
|
||||
ErrMsg(res)
|
||||
} else {
|
||||
notify <- notification
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// notify all cards of content change
|
||||
// account.Content incremented by adding, updating, removing article & setting or clearning group or label from article
|
||||
func SetContentNotification(account *store.Account) {
|
||||
|
@ -175,14 +175,14 @@ type Card struct {
|
||||
Version string `gorm:"not null"`
|
||||
Node string `gorm:"not null"`
|
||||
ProfileRevision int64 `gorm:"not null"`
|
||||
DetailRevision int64 `gorm:"not null"`
|
||||
DetailRevision int64 `gorm:"not null;default:1"`
|
||||
Status string `gorm:"not null"`
|
||||
InToken string `gorm:"not null;index:cardguid,unique"`
|
||||
OutToken string
|
||||
Notes string
|
||||
Created int64 `gorm:"autoCreateTime"`
|
||||
Updated int64 `gorm:"autoUpdateTime"`
|
||||
ViewRevision int64 `gorm:"not null"`
|
||||
ViewRevision int64 `gorm:"not null;default:1"`
|
||||
NotifiedView int64
|
||||
NotifiedContent int64
|
||||
NotifiedLabel int64
|
||||
|
@ -3,6 +3,7 @@ package databag
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"strconv"
|
||||
"time"
|
||||
"net/url"
|
||||
"net/http"
|
||||
@ -383,7 +384,11 @@ func OpenTestCard(account string, cardId string) (err error) {
|
||||
|
||||
// update status if connected
|
||||
if contactStatus.Status == APP_CARDCONNECTED {
|
||||
if r, w, err = NewRequest("PUT", "/contact/cards/{cardId}/status?token=" + contactStatus.Token, APP_CARDCONNECTED); err != nil {
|
||||
view := "viewRevision=" + strconv.FormatInt(contactStatus.ViewRevision, 10)
|
||||
content := "contentRevision=" + strconv.FormatInt(contactStatus.ContentRevision, 10)
|
||||
label := "labelRevision=" + strconv.FormatInt(contactStatus.LabelRevision, 10)
|
||||
profile := "profileRevision=" + strconv.FormatInt(contactStatus.ProfileRevision, 10)
|
||||
if r, w, err = NewRequest("PUT", "/contact/cards/{cardId}/status?token=" + contactStatus.Token + "&" + view + "&" + content + "&" + label + "&" + profile, APP_CARDCONNECTED); err != nil {
|
||||
return
|
||||
}
|
||||
r = mux.SetURLVars(r, vars)
|
||||
|
@ -2,6 +2,7 @@ package databag
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"strconv"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -9,9 +10,11 @@ func TestAddArticle(t *testing.T) {
|
||||
var set *TestGroup
|
||||
var err error
|
||||
var rev *Revision
|
||||
var ver *Revision
|
||||
var article Article
|
||||
var articles *[]Article
|
||||
var articleAccess *ArticleAccess
|
||||
var cards []Card
|
||||
|
||||
// setup testing group
|
||||
set, err = AddTestGroup("addarticle")
|
||||
@ -28,6 +31,12 @@ func TestAddArticle(t *testing.T) {
|
||||
|
||||
assert.NoError(t, SendEndpointTest(RemoveArticle, "DELETE", "/content/articls/" + article.ArticleId, &map[string]string{"articleId": article.ArticleId }, nil, APP_TOKENAPP, set.A.Token, nil))
|
||||
|
||||
ver = GetTestRevision(set.B.Revisions)
|
||||
assert.NoError(t, SendEndpointTest(GetCards, "GET", "/contact/cards?cardRevision=" + strconv.FormatInt(rev.Card, 10), nil, nil, APP_TOKENAPP, set.B.Token, &cards))
|
||||
assert.NotEqual(t, ver.Card, rev.Card)
|
||||
assert.Equal(t, 1, len(cards))
|
||||
rev = ver
|
||||
|
||||
articles = &[]Article{}
|
||||
assert.NoError(t, SendEndpointTest(GetArticles, "GET", "/content/articles", nil, nil, APP_TOKENAPP, set.A.Token, articles))
|
||||
assert.Equal(t, 2, len(*articles))
|
||||
@ -36,16 +45,26 @@ func TestAddArticle(t *testing.T) {
|
||||
|
||||
articles = &[]Article{}
|
||||
assert.NoError(t, SendEndpointTest(GetArticles, "GET", "/content/articles", nil, nil, APP_TOKENCONTACT, set.B.A.Token, articles))
|
||||
assert.Equal(t, 2, len(*articles))
|
||||
assert.True(t, (*articles)[0].ArticleData != nil || (*articles)[1].ArticleData != nil)
|
||||
assert.True(t, (*articles)[0].ArticleData == nil || (*articles)[1].ArticleData == nil)
|
||||
assert.Equal(t, 1, len(*articles))
|
||||
assert.True(t, (*articles)[0].ArticleData != nil)
|
||||
|
||||
articles = &[]Article{}
|
||||
assert.NoError(t, SendEndpointTest(GetArticles, "GET", "/content/articles", nil, nil, APP_TOKENCONTACT, set.C.A.Token, articles))
|
||||
assert.Equal(t, 2, len(*articles))
|
||||
assert.True(t, (*articles)[0].ArticleData == nil && (*articles)[1].ArticleData == nil)
|
||||
assert.Equal(t, 0, len(*articles))
|
||||
|
||||
// view article
|
||||
assert.NotEqual(t, GetTestRevision(set.B.Revisions).Card, rev)
|
||||
r, w, ret := NewRequest("GET", "/content/articles", nil)
|
||||
assert.NoError(t, ret)
|
||||
r.Header.Add("TokenType", APP_TOKENCONTACT)
|
||||
SetBearerAuth(r, set.B.A.Token)
|
||||
GetArticles(w, r)
|
||||
resp := w.Result()
|
||||
var view int64
|
||||
view, err = strconv.ParseInt(resp.Header["View-Revision"][0], 10, 64)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, view, cards[0].CardData.NotifiedView)
|
||||
|
||||
articles = &[]Article{}
|
||||
assert.NoError(t, SendEndpointTest(GetArticles, "GET", "/content/articles?contentRevision=0&viewRevision=" + strconv.FormatInt(cards[0].CardData.NotifiedView, 10), nil, nil, APP_TOKENCONTACT, set.B.A.Token, articles))
|
||||
assert.Equal(t, 2, len(*articles))
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user