From a6ea2a7be1218229613a82a8c8023ef9526eff19 Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Tue, 1 Feb 2022 23:39:25 -0800 Subject: [PATCH] added filtering of articles --- net/server/internal/api_addArticle.go | 8 +- net/server/internal/api_addGroup.go | 5 - net/server/internal/api_content.go | 10 -- net/server/internal/api_getArticles.go | 104 +++++++++++++++++++++ net/server/internal/api_removeArticle.go | 56 +++++++++++ net/server/internal/authUtil.go | 2 +- net/server/internal/modelUtil.go | 12 ++- net/server/internal/store/schema.go | 6 +- net/server/internal/testUtil.go | 4 +- net/server/internal/ucAddArticle_test.go | 32 +++++-- net/server/internal/ucGroupContact_test.go | 2 - 11 files changed, 206 insertions(+), 35 deletions(-) create mode 100644 net/server/internal/api_getArticles.go create mode 100644 net/server/internal/api_removeArticle.go diff --git a/net/server/internal/api_addArticle.go b/net/server/internal/api_addArticle.go index 7a9d35b2..a7d67513 100644 --- a/net/server/internal/api_addArticle.go +++ b/net/server/internal/api_addArticle.go @@ -45,13 +45,14 @@ func AddArticle(w http.ResponseWriter, r *http.Request) { Status: APP_ARTICLEUNCONFIRMED, TagUpdated: time.Now().Unix(), TagRevision: 1, + TagCount: 0, Labels: append(groups, labels...), }; if res := store.DB.Save(articleData).Error; res != nil { return res; } - if res := store.DB.Where("article_data_id is null AND account_id = ?", account.ID).First(&article).Error; res != nil { + if res := store.DB.Where("article_data_id = 0 AND account_id = ?", account.ID).First(&article).Error; res != nil { if errors.Is(res, gorm.ErrRecordNotFound) { article = &store.Article{ ArticleId: uuid.New().String(), @@ -73,6 +74,9 @@ func AddArticle(w http.ResponseWriter, r *http.Request) { if ret := store.DB.Preload("ArticleData.Labels.Groups").Where("id = ?", article.ID).First(article).Error; ret != nil { return ret; } + if ret := tx.Model(&account).Update("content_revision", account.ContentRevision + 1).Error; ret != nil { + return ret + } return nil }) if err != nil { @@ -82,7 +86,7 @@ func AddArticle(w http.ResponseWriter, r *http.Request) { SetContentNotification(account) SetStatus(account) - WriteResponse(w, getArticleModel(article, 0)) + WriteResponse(w, getArticleModel(article, false, true)) } diff --git a/net/server/internal/api_addGroup.go b/net/server/internal/api_addGroup.go index 98319a08..3749f25a 100644 --- a/net/server/internal/api_addGroup.go +++ b/net/server/internal/api_addGroup.go @@ -51,11 +51,6 @@ func AddGroup(w http.ResponseWriter, r *http.Request) { return res } -PrintMsg("ADDED") -PrintMsg(group.GroupId) -PrintMsg(label.LabelId) -PrintMsg("***") - if res := tx.Model(&account).Update("group_revision", account.GroupRevision + 1).Error; res != nil { return res } diff --git a/net/server/internal/api_content.go b/net/server/internal/api_content.go index 7aee985a..a68015f6 100644 --- a/net/server/internal/api_content.go +++ b/net/server/internal/api_content.go @@ -88,21 +88,11 @@ func GetArticleTags(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } -func GetArticles(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusOK) -} - func GetLabels(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.WriteHeader(http.StatusOK) } -func RemoveArticle(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusOK) -} - func RemoveArticleAsset(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_getArticles.go b/net/server/internal/api_getArticles.go new file mode 100644 index 00000000..91d53ee0 --- /dev/null +++ b/net/server/internal/api_getArticles.go @@ -0,0 +1,104 @@ +package databag + +import ( + "errors" + "strconv" + "net/http" + "databag/internal/store" +) + +func GetArticles(w http.ResponseWriter, r *http.Request) { + var res error + var viewRevision int64 + var contentRevision int64 + + view := r.FormValue("viewRevision") + if view != "" { + if viewRevision, res = strconv.ParseInt(view, 10, 64); res != nil { + ErrResponse(w, http.StatusBadRequest, res) + return + } + } + content := r.FormValue("contentRevision") + if content != "" { + if contentRevision, res = strconv.ParseInt(content, 10, 64); res != nil { + ErrResponse(w, http.StatusBadRequest, res) + return + } + } + + tokenType := r.Header.Get("TokenType") + var response []*Article + if tokenType == APP_TOKENAPP { + + account, code, err := BearerAppToken(r, false) + if err != nil { + ErrResponse(w, code, err) + return + } + + var articles []store.Article + if err := getAccountArticles(account, contentRevision, &articles); err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + + for _, article := range articles { + response = append(response, getArticleModel(&article, false, true)) + } + } else if tokenType == APP_TOKENCONTACT { + + card, code, err := BearerContactToken(r) + if err != nil { + ErrResponse(w, code, err) + return + } + + if viewRevision != card.ViewRevision + card.Account.ViewRevision { + contentRevision = 0 + } + + var articles []store.Article + if err := getContactArticles(card, contentRevision, &articles); err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + + for _, article := range articles { + response = append(response, getArticleModel(&article, true, isShared(&article, card.Guid))) + } + } else { + ErrResponse(w, http.StatusBadRequest, errors.New("invalid token type")) + } + + WriteResponse(w, response) +} + +// better if this filtering was done in gorm or sql +func isShared(article *store.Article, guid string) bool { + if article.ArticleData == nil { + return false + } + for _, label := range article.ArticleData.Labels { + for _, group := range label.Groups { + for _, card := range group.Cards { + if card.Guid == guid { + return true + } + } + } + } + return false +} + +func getAccountArticles(account *store.Account, revision int64, articles *[]store.Article) error { + return store.DB.Preload("ArticleData.Labels.Groups").Where("account_id = ? AND revision > ?", account.ID, revision).Find(articles).Error +} + +func getContactArticles(card *store.Card, revision int64, articles *[]store.Article) error { + return store.DB.Preload("ArticleData.Labels.Groups.Cards").Where("account_id = ? AND revision > ?", card.Account.ID, revision).Find(articles).Error +} + + + + diff --git a/net/server/internal/api_removeArticle.go b/net/server/internal/api_removeArticle.go new file mode 100644 index 00000000..495f88fc --- /dev/null +++ b/net/server/internal/api_removeArticle.go @@ -0,0 +1,56 @@ +package databag + +import ( + "errors" + "net/http" + "gorm.io/gorm" + "github.com/gorilla/mux" + "databag/internal/store" +) + +func RemoveArticle(w http.ResponseWriter, r *http.Request) { + + account, code, err := BearerAppToken(r, false) + if err != nil { + ErrResponse(w, code, err) + return + } + + params := mux.Vars(r) + articleId := params["articleId"] + + err = store.DB.Transaction(func(tx *gorm.DB) error { + var article store.Article + if res := store.DB.Preload("ArticleData").Where("account_id = ? AND article_id = ?", account.ID, articleId).First(&article).Error; res != nil { + return res + } + if article.ArticleData == nil { + return nil + } + if res := tx.Delete(article.ArticleData).Error; res != nil { + return res + } + article.ArticleDataID = 0 + article.ArticleData = nil + if res := tx.Save(&article).Error; res != nil { + return res + } + if res := tx.Model(&account).Update("content_revision", account.ContentRevision).Error; res != nil { + return res + } + return nil + }) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + ErrResponse(w, http.StatusNotFound, err) + } else { + ErrResponse(w, http.StatusInternalServerError, err) + } + return + } + + SetContentNotification(account) + SetStatus(account) + WriteResponse(w, nil) +} + diff --git a/net/server/internal/authUtil.go b/net/server/internal/authUtil.go index e87c5ec0..90c2b404 100644 --- a/net/server/internal/authUtil.go +++ b/net/server/internal/authUtil.go @@ -139,7 +139,7 @@ func BearerContactToken(r *http.Request) (*store.Card, int, error) { // find token record var card store.Card - if err := store.DB.Preload("Account").Where("account_id = ? AND InToken = ?", target, access).First(&card).Error; err != nil { + if err := store.DB.Preload("Account").Where("account_id = ? AND in_token = ?", target, access).First(&card).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, http.StatusNotFound, err } else { diff --git a/net/server/internal/modelUtil.go b/net/server/internal/modelUtil.go index 61922506..cf566f9b 100644 --- a/net/server/internal/modelUtil.go +++ b/net/server/internal/modelUtil.go @@ -49,9 +49,9 @@ func getGroupModel(group *store.Group) *Group { } } -func getArticleModel(article *store.Article, tagCount int32) *Article { +func getArticleModel(article *store.Article, contact bool, shared bool) *Article { - if article.ArticleData == nil { + if !shared || article.ArticleData == nil { return &Article{ ArticleId: article.ArticleId, Revision: article.Revision, @@ -62,8 +62,10 @@ func getArticleModel(article *store.Article, tagCount int32) *Article { var groups []string; var labels []string; for _, label := range article.ArticleData.Labels { - if label.Direct && len(label.Groups) > 0 { - groups = append(groups, label.Groups[0].GroupId) + if label.Direct { + if !contact && len(label.Groups) > 0 { + groups = append(groups, label.Groups[0].GroupId) + } } else { labels = append(labels, label.LabelId) } @@ -78,7 +80,7 @@ func getArticleModel(article *store.Article, tagCount int32) *Article { Status: article.ArticleData.Status, Labels: labels, Groups: groups, - TagCount: tagCount, + TagCount: article.ArticleData.TagCount, Created: article.ArticleData.Created, Updated: article.ArticleData.Updated, TagUpdated: article.ArticleData.TagUpdated, diff --git a/net/server/internal/store/schema.go b/net/server/internal/store/schema.go index 4db1569f..9f351703 100644 --- a/net/server/internal/store/schema.go +++ b/net/server/internal/store/schema.go @@ -106,6 +106,7 @@ type Group struct { Data string Created int64 `gorm:"autoCreateTime"` Updated int64 `gorm:"autoUpdateTime"` + Cards []Card `gorm:"many2many:card_groups"` Label Label //reference to label for direct assignment to articles Account Account } @@ -148,8 +149,8 @@ type Card struct { NotifiedView int64 NotifiedContent int64 NotifiedProfile int64 - Groups []Group `gorm:"many2many:card_groups;"` Account Account `gorm:"references:Guid"` + Groups []Group `gorm:"many2many:card_groups"` } type Asset struct { @@ -172,7 +173,7 @@ type Article struct { ArticleId string `gorm:"not null;index:article,unique"` AccountID uint `gorm:"not null;index:article,unique"` Revision int64 `gorm:"not null"` - ArticleDataID uint + ArticleDataID uint `gorm:"not null;default:0"` ArticleData *ArticleData Account Account } @@ -187,6 +188,7 @@ type ArticleData struct { Created int64 `gorm:"autoCreateTime"` Updated int64 `gorm:"autoUpdateTime"` TagUpdated int64 `gorm:"not null"` + TagCount int32 `gorm:"not null"` TagRevision int64 `gorm:"not null"` Labels []Label `gorm:"many2many:article_labels;"` } diff --git a/net/server/internal/testUtil.go b/net/server/internal/testUtil.go index 9b1975d0..325372c5 100644 --- a/net/server/internal/testUtil.go +++ b/net/server/internal/testUtil.go @@ -57,6 +57,7 @@ func SendEndpointTest( name string, params *map[string]string, body interface{}, + tokenType string, token string, response interface{}, ) (err error) { @@ -71,7 +72,7 @@ func SendEndpointTest( r = mux.SetURLVars(r, *params) } if token != "" { - r.Header.Add("TokenType", APP_TOKENAPP) + r.Header.Add("TokenType", tokenType) SetBearerAuth(r, token) } endpoint(w, r) @@ -205,6 +206,7 @@ func AddTestGroup(prefix string) (*TestGroup, error) { if g.A.B.Token, err = GetCardToken(g.A.Token, g.A.B.CardId); err != nil { return g, err } + if g.B.A.Token, err = GetCardToken(g.B.Token, g.B.A.CardId); err != nil { return g, err } diff --git a/net/server/internal/ucAddArticle_test.go b/net/server/internal/ucAddArticle_test.go index e89326f8..11dacf25 100644 --- a/net/server/internal/ucAddArticle_test.go +++ b/net/server/internal/ucAddArticle_test.go @@ -10,24 +10,42 @@ func TestAddArticle(t *testing.T) { var err error var rev *Revision var article Article + var articles *[]Article + var articleAccess *ArticleAccess // setup testing group set, err = AddTestGroup("addarticle") assert.NoError(t, err) // initial revision - rev = GetTestRevision(set.A.Revisions) + rev = GetTestRevision(set.B.Revisions) // create article - articleAccess := &ArticleAccess{ Groups: []string{set.A.B.GroupId} } - assert.NoError(t, SendEndpointTest(AddArticle, "POST", "/content/articles", nil, articleAccess, set.A.Token, &article)) - PrintMsg(article); + articleAccess = &ArticleAccess{ Groups: []string{set.A.B.GroupId} } + assert.NoError(t, SendEndpointTest(AddArticle, "POST", "/content/articles", nil, articleAccess, APP_TOKENAPP, set.A.Token, &article)) - // check revisions - rev = GetTestRevision(set.A.Revisions) + assert.NoError(t, SendEndpointTest(AddArticle, "POST", "/content/articles", nil, articleAccess, APP_TOKENAPP, set.A.Token, &article)) + assert.NoError(t, SendEndpointTest(RemoveArticle, "DELETE", "/content/articls/" + article.ArticleId, &map[string]string{"articleId": article.ArticleId }, nil, APP_TOKENAPP, set.A.Token, nil)) + + articles = &[]Article{} + assert.NoError(t, SendEndpointTest(GetArticles, "GET", "/content/articles", nil, nil, APP_TOKENAPP, set.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) + + 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) + + 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) // view article + assert.NotEqual(t, GetTestRevision(set.B.Revisions).Card, rev) - PrintMsg(rev) } diff --git a/net/server/internal/ucGroupContact_test.go b/net/server/internal/ucGroupContact_test.go index 8b787e7f..07fae335 100644 --- a/net/server/internal/ucGroupContact_test.go +++ b/net/server/internal/ucGroupContact_test.go @@ -138,8 +138,6 @@ func TestGroupContact(t *testing.T) { vars["groupId"] = group.GroupId r = mux.SetURLVars(r, vars) SetBearerAuth(r, a) -PrintMsg(group.GroupId) -PrintMsg("REMOVED") RemoveGroup(w, r) assert.NoError(t, ReadResponse(w, &group))