diff --git a/net/server/internal/api_clearCardNotes.go b/net/server/internal/api_clearCardNotes.go new file mode 100644 index 00000000..be80e8f8 --- /dev/null +++ b/net/server/internal/api_clearCardNotes.go @@ -0,0 +1,61 @@ +package databag + +import ( + "errors" + "net/http" + "gorm.io/gorm" + "github.com/gorilla/mux" + "databag/internal/store" +) + +func ClearCardNotes(w http.ResponseWriter, r *http.Request) { + + account, code, err := BearerAppToken(r, false); + if err != nil { + ErrResponse(w, code, err) + return + } + + // scan parameters + params := mux.Vars(r) + cardId := params["cardId"] + + // load referenced card + var slot store.CardSlot + if err := store.DB.Preload("Card.Groups").Where("account_id = ? AND card_slot_id = ?", account.ID, cardId).First(&slot).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + ErrResponse(w, http.StatusInternalServerError, err) + } else { + ErrResponse(w, http.StatusNotFound, err) + } + return + } + if slot.Card == nil { + ErrResponse(w, http.StatusNotFound, errors.New("card has been deleted")) + return + } + + // save and update contact revision + err = store.DB.Transaction(func(tx *gorm.DB) error { + slot.Card.Notes = "" + slot.Card.DetailRevision += 1 + if res := tx.Save(&slot.Card).Error; res != nil { + return res + } + if res := tx.Model(&slot).Update("revision", account.CardRevision + 1).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 { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + + SetStatus(account) + WriteResponse(w, getCardDetailModel(&slot)); +} + diff --git a/net/server/internal/api_contact.go b/net/server/internal/api_contact.go index f935a8f4..06708f96 100644 --- a/net/server/internal/api_contact.go +++ b/net/server/internal/api_contact.go @@ -18,11 +18,6 @@ func ClearCardGroup(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } -func ClearCardNotes(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusOK) -} - func GetCloseMessage(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.WriteHeader(http.StatusOK) @@ -33,11 +28,6 @@ func RemoveCard(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } -func SetCardNotes(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusOK) -} - func SetCloseMessage(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.WriteHeader(http.StatusOK) diff --git a/net/server/internal/api_getCards.go b/net/server/internal/api_getCards.go index 51e237ef..797a572c 100644 --- a/net/server/internal/api_getCards.go +++ b/net/server/internal/api_getCards.go @@ -16,7 +16,7 @@ func GetCards(w http.ResponseWriter, r *http.Request) { return } - card := r.FormValue("cardRevision") + card := r.FormValue("revision") if card != "" { cardRevisionSet = true if cardRevision, err = strconv.ParseInt(card, 10, 64); err != nil { @@ -28,7 +28,7 @@ func GetCards(w http.ResponseWriter, r *http.Request) { var response []*Card if cardRevisionSet { var slots []store.CardSlot - if err := store.DB.Where("account_id = ? AND revision > ?", account.ID, cardRevision).Find(&slots).Error; err != nil { + if err := store.DB.Preload("Card").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_setCardNotes.go b/net/server/internal/api_setCardNotes.go new file mode 100644 index 00000000..43b3b666 --- /dev/null +++ b/net/server/internal/api_setCardNotes.go @@ -0,0 +1,67 @@ +package databag + +import ( + "errors" + "net/http" + "gorm.io/gorm" + "github.com/gorilla/mux" + "databag/internal/store" +) + +func SetCardNotes(w http.ResponseWriter, r *http.Request) { + + account, code, err := BearerAppToken(r, false); + if err != nil { + ErrResponse(w, code, err) + return + } + + // scan parameters + params := mux.Vars(r) + cardId := params["cardId"] + + var notes string + if err := ParseRequest(r, w, ¬es); err != nil { + ErrResponse(w, http.StatusBadRequest, err) + return + } + + // load referenced card + var slot store.CardSlot + if err := store.DB.Preload("Card.Groups").Where("account_id = ? AND card_slot_id = ?", account.ID, cardId).First(&slot).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + ErrResponse(w, http.StatusInternalServerError, err) + } else { + ErrResponse(w, http.StatusNotFound, err) + } + return + } + if slot.Card == nil { + ErrResponse(w, http.StatusNotFound, errors.New("card has been deleted")) + return + } + + // save and update contact revision + err = store.DB.Transaction(func(tx *gorm.DB) error { + slot.Card.Notes = notes + slot.Card.DetailRevision += 1 + if res := tx.Save(&slot.Card).Error; res != nil { + return res + } + if res := tx.Model(&slot).Update("revision", account.CardRevision + 1).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 { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + + SetStatus(account) + WriteResponse(w, getCardDetailModel(&slot)); +} + diff --git a/net/server/internal/ucContactSync_test.go b/net/server/internal/ucContactSync_test.go index e628ced7..c0739f69 100644 --- a/net/server/internal/ucContactSync_test.go +++ b/net/server/internal/ucContactSync_test.go @@ -16,7 +16,11 @@ func TestContactSync(t *testing.T) { var data []byte var hdr map[string][]string var res error - var cards []Card + var cards *[]Card + var cardRevision string + var detailRevision int64 + var detail CardDetail + var rev *Revision // setup testing group set, err := AddTestGroup("contactsync") @@ -45,11 +49,36 @@ func TestContactSync(t *testing.T) { assert.Zero(t, bytes.Compare(img, data)) // get full card list + cards = &[]Card{} assert.NoError(t, ApiTestMsg(GetCards, "GET", "/contact/cards", - nil, nil, APP_TOKENAPP, set.B.Token, &cards, &hdr)) + nil, nil, APP_TOKENAPP, set.B.Token, cards, &hdr)) + cardRevision = hdr["Card-Revision"][0] + cards = &[]Card{} + assert.NoError(t, ApiTestMsg(GetCards, "GET", "/contact/cards?revision=" + cardRevision, + nil, nil, APP_TOKENAPP, set.B.Token, cards, &hdr)) + cardRevision = hdr["Card-Revision"][0] + assert.Equal(t, 0, len(*cards)) - PrintMsg(set.B.A.CardId) - PrintMsg(hdr) - PrintMsg(cards) + // set card notes + GetTestRevision(set.B.Revisions) + assert.NoError(t, ApiTestMsg(SetCardNotes, "PUT", "/conact/cards/{cardId}/notes", + ¶m, "CardA notes", APP_TOKENAPP, set.B.Token, &detail, nil)) + rev = GetTestRevision(set.B.Revisions) + cards = &[]Card{} + assert.NoError(t, ApiTestMsg(GetCards, "GET", "/contact/cards?revision=" + cardRevision, + nil, nil, APP_TOKENAPP, set.B.Token, cards, &hdr)) + assert.Equal(t, 1, len(*cards)) + detailRevision = (*cards)[0].Data.DetailRevision + + // clear card notes + GetTestRevision(set.B.Revisions) + assert.NoError(t, ApiTestMsg(ClearCardNotes, "DELETE", "/conact/cards/{cardId}/notes", + ¶m, nil, APP_TOKENAPP, set.B.Token, &detail, nil)) + assert.NotEqual(t, rev.Card, GetTestRevision(set.B.Revisions).Card) + cards = &[]Card{} + assert.NoError(t, ApiTestMsg(GetCards, "GET", "/contact/cards?revision=" + cardRevision, + nil, nil, APP_TOKENAPP, set.B.Token, cards, &hdr)) + assert.Equal(t, 1, len(*cards)) + assert.NotEqual(t, detailRevision, (*cards)[0].Data.DetailRevision) }