added filtering of articles

This commit is contained in:
Roland Osborne 2022-02-01 23:39:25 -08:00
parent b1e0a17506
commit a6ea2a7be1
11 changed files with 206 additions and 35 deletions

View File

@ -45,13 +45,14 @@ func AddArticle(w http.ResponseWriter, r *http.Request) {
Status: APP_ARTICLEUNCONFIRMED, Status: APP_ARTICLEUNCONFIRMED,
TagUpdated: time.Now().Unix(), TagUpdated: time.Now().Unix(),
TagRevision: 1, TagRevision: 1,
TagCount: 0,
Labels: append(groups, labels...), Labels: append(groups, labels...),
}; };
if res := store.DB.Save(articleData).Error; res != nil { if res := store.DB.Save(articleData).Error; res != nil {
return res; 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) { if errors.Is(res, gorm.ErrRecordNotFound) {
article = &store.Article{ article = &store.Article{
ArticleId: uuid.New().String(), 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 { if ret := store.DB.Preload("ArticleData.Labels.Groups").Where("id = ?", article.ID).First(article).Error; ret != nil {
return ret; return ret;
} }
if ret := tx.Model(&account).Update("content_revision", account.ContentRevision + 1).Error; ret != nil {
return ret
}
return nil return nil
}) })
if err != nil { if err != nil {
@ -82,7 +86,7 @@ func AddArticle(w http.ResponseWriter, r *http.Request) {
SetContentNotification(account) SetContentNotification(account)
SetStatus(account) SetStatus(account)
WriteResponse(w, getArticleModel(article, 0)) WriteResponse(w, getArticleModel(article, false, true))
} }

View File

@ -51,11 +51,6 @@ func AddGroup(w http.ResponseWriter, r *http.Request) {
return res 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 { if res := tx.Model(&account).Update("group_revision", account.GroupRevision + 1).Error; res != nil {
return res return res
} }

View File

@ -88,21 +88,11 @@ func GetArticleTags(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) 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) { func GetLabels(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK) 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) { func RemoveArticleAsset(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)

View File

@ -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
}

View File

@ -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)
}

View File

@ -139,7 +139,7 @@ func BearerContactToken(r *http.Request) (*store.Card, int, error) {
// find token record // find token record
var card store.Card 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) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, http.StatusNotFound, err return nil, http.StatusNotFound, err
} else { } else {

View File

@ -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{ return &Article{
ArticleId: article.ArticleId, ArticleId: article.ArticleId,
Revision: article.Revision, Revision: article.Revision,
@ -62,8 +62,10 @@ func getArticleModel(article *store.Article, tagCount int32) *Article {
var groups []string; var groups []string;
var labels []string; var labels []string;
for _, label := range article.ArticleData.Labels { for _, label := range article.ArticleData.Labels {
if label.Direct && len(label.Groups) > 0 { if label.Direct {
groups = append(groups, label.Groups[0].GroupId) if !contact && len(label.Groups) > 0 {
groups = append(groups, label.Groups[0].GroupId)
}
} else { } else {
labels = append(labels, label.LabelId) labels = append(labels, label.LabelId)
} }
@ -78,7 +80,7 @@ func getArticleModel(article *store.Article, tagCount int32) *Article {
Status: article.ArticleData.Status, Status: article.ArticleData.Status,
Labels: labels, Labels: labels,
Groups: groups, Groups: groups,
TagCount: tagCount, TagCount: article.ArticleData.TagCount,
Created: article.ArticleData.Created, Created: article.ArticleData.Created,
Updated: article.ArticleData.Updated, Updated: article.ArticleData.Updated,
TagUpdated: article.ArticleData.TagUpdated, TagUpdated: article.ArticleData.TagUpdated,

View File

@ -106,6 +106,7 @@ type Group struct {
Data string Data string
Created int64 `gorm:"autoCreateTime"` Created int64 `gorm:"autoCreateTime"`
Updated int64 `gorm:"autoUpdateTime"` Updated int64 `gorm:"autoUpdateTime"`
Cards []Card `gorm:"many2many:card_groups"`
Label Label //reference to label for direct assignment to articles Label Label //reference to label for direct assignment to articles
Account Account Account Account
} }
@ -148,8 +149,8 @@ type Card struct {
NotifiedView int64 NotifiedView int64
NotifiedContent int64 NotifiedContent int64
NotifiedProfile int64 NotifiedProfile int64
Groups []Group `gorm:"many2many:card_groups;"`
Account Account `gorm:"references:Guid"` Account Account `gorm:"references:Guid"`
Groups []Group `gorm:"many2many:card_groups"`
} }
type Asset struct { type Asset struct {
@ -172,7 +173,7 @@ type Article struct {
ArticleId string `gorm:"not null;index:article,unique"` ArticleId string `gorm:"not null;index:article,unique"`
AccountID uint `gorm:"not null;index:article,unique"` AccountID uint `gorm:"not null;index:article,unique"`
Revision int64 `gorm:"not null"` Revision int64 `gorm:"not null"`
ArticleDataID uint ArticleDataID uint `gorm:"not null;default:0"`
ArticleData *ArticleData ArticleData *ArticleData
Account Account Account Account
} }
@ -187,6 +188,7 @@ type ArticleData struct {
Created int64 `gorm:"autoCreateTime"` Created int64 `gorm:"autoCreateTime"`
Updated int64 `gorm:"autoUpdateTime"` Updated int64 `gorm:"autoUpdateTime"`
TagUpdated int64 `gorm:"not null"` TagUpdated int64 `gorm:"not null"`
TagCount int32 `gorm:"not null"`
TagRevision int64 `gorm:"not null"` TagRevision int64 `gorm:"not null"`
Labels []Label `gorm:"many2many:article_labels;"` Labels []Label `gorm:"many2many:article_labels;"`
} }

View File

@ -57,6 +57,7 @@ func SendEndpointTest(
name string, name string,
params *map[string]string, params *map[string]string,
body interface{}, body interface{},
tokenType string,
token string, token string,
response interface{}, response interface{},
) (err error) { ) (err error) {
@ -71,7 +72,7 @@ func SendEndpointTest(
r = mux.SetURLVars(r, *params) r = mux.SetURLVars(r, *params)
} }
if token != "" { if token != "" {
r.Header.Add("TokenType", APP_TOKENAPP) r.Header.Add("TokenType", tokenType)
SetBearerAuth(r, token) SetBearerAuth(r, token)
} }
endpoint(w, r) 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 { if g.A.B.Token, err = GetCardToken(g.A.Token, g.A.B.CardId); err != nil {
return g, err return g, err
} }
if g.B.A.Token, err = GetCardToken(g.B.Token, g.B.A.CardId); err != nil { if g.B.A.Token, err = GetCardToken(g.B.Token, g.B.A.CardId); err != nil {
return g, err return g, err
} }

View File

@ -10,24 +10,42 @@ func TestAddArticle(t *testing.T) {
var err error var err error
var rev *Revision var rev *Revision
var article Article var article Article
var articles *[]Article
var articleAccess *ArticleAccess
// setup testing group // setup testing group
set, err = AddTestGroup("addarticle") set, err = AddTestGroup("addarticle")
assert.NoError(t, err) assert.NoError(t, err)
// initial revision // initial revision
rev = GetTestRevision(set.A.Revisions) rev = GetTestRevision(set.B.Revisions)
// create article // create article
articleAccess := &ArticleAccess{ Groups: []string{set.A.B.GroupId} } articleAccess = &ArticleAccess{ Groups: []string{set.A.B.GroupId} }
assert.NoError(t, SendEndpointTest(AddArticle, "POST", "/content/articles", nil, articleAccess, set.A.Token, &article)) assert.NoError(t, SendEndpointTest(AddArticle, "POST", "/content/articles", nil, articleAccess, APP_TOKENAPP, set.A.Token, &article))
PrintMsg(article);
// check revisions assert.NoError(t, SendEndpointTest(AddArticle, "POST", "/content/articles", nil, articleAccess, APP_TOKENAPP, set.A.Token, &article))
rev = GetTestRevision(set.A.Revisions)
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 // view article
assert.NotEqual(t, GetTestRevision(set.B.Revisions).Card, rev)
PrintMsg(rev)
} }

View File

@ -138,8 +138,6 @@ func TestGroupContact(t *testing.T) {
vars["groupId"] = group.GroupId vars["groupId"] = group.GroupId
r = mux.SetURLVars(r, vars) r = mux.SetURLVars(r, vars)
SetBearerAuth(r, a) SetBearerAuth(r, a)
PrintMsg(group.GroupId)
PrintMsg("REMOVED")
RemoveGroup(w, r) RemoveGroup(w, r)
assert.NoError(t, ReadResponse(w, &group)) assert.NoError(t, ReadResponse(w, &group))