diff --git a/doc/api.oa3 b/doc/api.oa3 index 17d9012c..c5d493fc 100644 --- a/doc/api.oa3 +++ b/doc/api.oa3 @@ -835,6 +835,13 @@ paths: operationId: get-groups security: - bearerAuth: [] + parameters: + - name: groupRevision + in: query + description: only return updated groups since specified revision + required: false + schema: + type: string responses: '200': description: successful operation @@ -1524,6 +1531,30 @@ paths: type: integer format: int64 + /contact/label/revision: + put: + tags: + - contact + description: Set label revision for contact. This is intend to be invoked automatically anytime a contact updates their label or sharing. Access granted to contact tokens. + operationId: set-label-revision + security: + - bearerAuth: [] + responses: + '200': + description: revision set + '401': + description: not authorized + '410': + description: account disabled + '500': + description: internal server error + requestBody: + content: + application/json: + schema: + type: integer + format: int64 + /contact/view/revision: put: tags: @@ -2404,6 +2435,19 @@ paths: operationId: get-labels security: - bearerAuth: [] + parameters: + - name: viewRevision + in: query + description: only return updated if view matches + required: false + schema: + type: string + - name: labelRevision + in: query + description: only return updated if view matches + required: false + schema: + type: string responses: '200': description: successful operation @@ -4068,30 +4112,21 @@ components: node: type: string - CardData: - type: object - required: - - status - properties: - revision: - type: integer - format: int64 - status: - type: string - enum: [ pending, confirmed, requested, connecting, connected ] - notes: - type: string - token: - type: string - groups: - type: array - items: - type: string - Card: type: object required: - cardId + - cardData + properties: + cardId: + type: string + cardData: + $ref: '#/components/schemas/CardData' + + CardData: + type: object + required: + - status - cardProfile - cardData - notifiedProfile @@ -4110,9 +4145,23 @@ components: notifiedContent: type: integer format: int64 + notifiedLabel: + type: integer + format: int64 notifiedView: type: integer format: int64 + status: + type: string + enum: [ pending, confirmed, requested, connecting, connected ] + notes: + type: string + token: + type: string + groups: + type: array + items: + type: string Asset: type: object @@ -4326,37 +4375,25 @@ components: type: integer format: int64 - ArticleView: - type: object - required: - - articleId - - revision - - tagRevision - properties: - articleId: - type: string - revision: - type: integer - format: int64 - tagRevision: - type: integer - format: int64 - Group: type: object required: - groupId - - revision + - groupData + properties: + groupId: + type: string + groupData: + $ref: '#/components/schemas/GroupData' + + GroupData: + type: object + required: - dataType - data - created - updated properties: - groupId: - type: string - revision: - type: integer - format: int64 dataType: type: string data: @@ -4372,18 +4409,22 @@ components: type: object required: - labelId - - labelRevision - - type + - labelData + properties: + labelId: + type: string + labelData: + $ref: '#/components/schemas/LabelData' + + LabelData: + type: object + required: + - dataType - data - created - modified properties: - labelId: - type: string - labelRevision: - type: integer - format: int64 - type: + dataType: type: string data: type: string @@ -4544,4 +4585,3 @@ components: scheme: bearer - diff --git a/net/server/internal/api_getCards.go b/net/server/internal/api_getCards.go index b76c099d..f3cce601 100644 --- a/net/server/internal/api_getCards.go +++ b/net/server/internal/api_getCards.go @@ -1,11 +1,22 @@ package databag import ( + "strconv" "net/http" "databag/internal/store" ) func GetCards(w http.ResponseWriter, r *http.Request) { + var res error + var cardRevision int64 + + card := r.FormValue("cardRevision") + if card != "" { + if cardRevision, res = strconv.ParseInt(card, 10, 64); res != nil { + ErrResponse(w, http.StatusBadRequest, res) + return + } + } account, code, err := BearerAppToken(r, false); if err != nil { @@ -14,7 +25,7 @@ func GetCards(w http.ResponseWriter, r *http.Request) { } var slots []store.CardSlot - if err := store.DB.Preload("Card.Groups.GroupSlot").Where("account_id = ?", account.ID).Find(&slots).Error; err != nil { + if err := store.DB.Preload("Card.Groups.GroupSlot").Where("account_id = ? AND revision > ?", account.ID, cardRevision).Find(&slots).Error; err != nil { ErrResponse(w, http.StatusInternalServerError, err) return } diff --git a/net/server/internal/api_getGroups.go b/net/server/internal/api_getGroups.go index f8d79aa7..2c740a12 100644 --- a/net/server/internal/api_getGroups.go +++ b/net/server/internal/api_getGroups.go @@ -1,11 +1,22 @@ package databag import ( + "strconv" "net/http" "databag/internal/store" ) func GetGroups(w http.ResponseWriter, r *http.Request) { + var res error + var groupRevision int64 + + group := r.FormValue("groupRevision") + if group != "" { + if groupRevision, res = strconv.ParseInt(group, 10, 64); res != nil { + ErrResponse(w, http.StatusBadRequest, res) + return + } + } account, code, err := BearerAppToken(r, false) if err != nil { @@ -14,7 +25,7 @@ func GetGroups(w http.ResponseWriter, r *http.Request) { } var slots []store.GroupSlot - if err := store.DB.Where("account_id = ?", account.ID).Find(&slots).Error; err != nil { + if err := store.DB.Preload("Group").Where("account_id = ? AND revision > ?", account.ID, groupRevision).Find(&slots).Error; err != nil { ErrResponse(w, http.StatusInternalServerError, err) return } diff --git a/net/server/internal/api_setLabelRevision.go b/net/server/internal/api_setLabelRevision.go new file mode 100644 index 00000000..9ab0a76b --- /dev/null +++ b/net/server/internal/api_setLabelRevision.go @@ -0,0 +1,49 @@ +package databag + +import ( + "net/http" + "gorm.io/gorm" + "databag/internal/store" +) + +func SetLabelRevision(w http.ResponseWriter, r *http.Request) { + + 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 NotifyLabelRevision(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("notified_label", 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 +} + diff --git a/net/server/internal/appValues.go b/net/server/internal/appValues.go index dd401afd..8a144856 100644 --- a/net/server/internal/appValues.go +++ b/net/server/internal/appValues.go @@ -22,6 +22,7 @@ const APP_CARDCONNECTING = "connecting" const APP_CARDCONNECTED = "connected" const APP_NOTIFYPROFILE = "profile" const APP_NOTIFYCONTENT = "content" +const APP_NOTIFYLABEL = "label" const APP_NOTIFYVIEW = "view" const APP_TOKENAPP = "app" const APP_TOKENCONTACT = "contact" diff --git a/net/server/internal/notify.go b/net/server/internal/notify.go index 2dee2e9d..8ce49179 100644 --- a/net/server/internal/notify.go +++ b/net/server/internal/notify.go @@ -64,6 +64,10 @@ func SendLocalNotification(notification *store.Notification) { if err := NotifyContentRevision(&card, notification.Revision); err != nil { ErrMsg(err) } + } else if notification.Module == APP_NOTIFYLABEL { + if err := NotifyLabelRevision(&card, notification.Revision); err != nil { + ErrMsg(err) + } } else if notification.Module == APP_NOTIFYVIEW { if err := NotifyViewRevision(&card, notification.Revision); err != nil { ErrMsg(err) @@ -220,4 +224,59 @@ func SetContactViewNotification(account *store.Account, card *store.Card) { } +// notify all cards of label change +// account.Label incremented by adding, updating, removing a label & setting or clearning group from article +func SetLabelNotification(account *store.Account) { + + // select all connected cards + var cards []store.Card + if err := store.DB.Where("account_id = ? AND status = ?", account.Guid, APP_CARDCONNECTED).Find(&cards).Error; err != nil { + ErrMsg(err) + return + } + + // add new notification for each card + err := store.DB.Transaction(func(tx *gorm.DB) error { + for _, card := range cards { + notification := &store.Notification{ + Node: card.Node, + Module: APP_NOTIFYLABEL, + Token: card.OutToken, + Revision: account.LabelRevision, + } + if err := tx.Save(notification).Error; err != nil { + return err + } + notify <- notification + } + return nil + }) + if err != nil { + ErrMsg(err) + } +} + +// notify single card of label change +// card.Label incremented by adding or removing group from label +func SetContactLabelNotification(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_NOTIFYLABEL, + Token: card.OutToken, + Revision: account.LabelRevision, + } + + if res := store.DB.Save(notification).Error; res != nil { + ErrMsg(res) + } else { + notify <- notification + } +} + diff --git a/net/server/internal/routers.go b/net/server/internal/routers.go index 566f6f1d..4c03138e 100644 --- a/net/server/internal/routers.go +++ b/net/server/internal/routers.go @@ -390,6 +390,13 @@ var routes = Routes{ SetContentRevision, }, + Route{ + "SetLabelRevision", + strings.ToUpper("Put"), + "/contact/label/revision", + SetLabelRevision, + }, + Route{ "SetOpenMessage", strings.ToUpper("Put"), diff --git a/net/server/internal/store/schema.go b/net/server/internal/store/schema.go index c970db85..a6914523 100644 --- a/net/server/internal/store/schema.go +++ b/net/server/internal/store/schema.go @@ -185,6 +185,7 @@ type Card struct { ViewRevision int64 `gorm:"not null"` NotifiedView int64 NotifiedContent int64 + NotifiedLabel int64 NotifiedProfile int64 Account Account `gorm:"references:Guid"` Groups []Group `gorm:"many2many:card_groups"` diff --git a/net/server/internal/ucAddArticle_test.go b/net/server/internal/ucAddArticle_test.go index c6fbede4..11dacf25 100644 --- a/net/server/internal/ucAddArticle_test.go +++ b/net/server/internal/ucAddArticle_test.go @@ -17,8 +17,6 @@ func TestAddArticle(t *testing.T) { set, err = AddTestGroup("addarticle") assert.NoError(t, err) -PrintMsg(set) - // initial revision rev = GetTestRevision(set.B.Revisions)