From 6bdd2fc03395cf5f429d05c6d0fa25d88de3b56a Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Thu, 17 Feb 2022 00:30:33 -0800 Subject: [PATCH] testing channel sharing --- net/server/internal/api_clearArticleGroup.go | 1 - net/server/internal/api_clearChannelCard.go | 93 +++++++++++ net/server/internal/api_clearChannelGroup.go | 87 ++++++++++ net/server/internal/api_content.go | 20 --- net/server/internal/api_getChannels.go | 4 +- net/server/internal/api_setArticleSubject.go | 7 +- net/server/internal/api_setChannelCard.go | 17 +- net/server/internal/api_setChannelGroup.go | 87 ++++++++++ net/server/internal/api_setChannelSubject.go | 83 ++++++++++ net/server/internal/models.go | 4 +- net/server/internal/ucTopicShare_test.go | 159 ++++++++++++++++++- 11 files changed, 522 insertions(+), 40 deletions(-) create mode 100644 net/server/internal/api_clearChannelCard.go create mode 100644 net/server/internal/api_clearChannelGroup.go create mode 100644 net/server/internal/api_setChannelGroup.go create mode 100644 net/server/internal/api_setChannelSubject.go diff --git a/net/server/internal/api_clearArticleGroup.go b/net/server/internal/api_clearArticleGroup.go index 8d114829..854fd7a1 100644 --- a/net/server/internal/api_clearArticleGroup.go +++ b/net/server/internal/api_clearArticleGroup.go @@ -74,7 +74,6 @@ func ClearArticleGroup(w http.ResponseWriter, r *http.Request) { for _, card := range groupSlot.Group.Cards { SetContactArticleNotification(account, &card) } - WriteResponse(w, getArticleModel(&articleSlot, true, true)); } diff --git a/net/server/internal/api_clearChannelCard.go b/net/server/internal/api_clearChannelCard.go new file mode 100644 index 00000000..241f6be2 --- /dev/null +++ b/net/server/internal/api_clearChannelCard.go @@ -0,0 +1,93 @@ +package databag + +import ( + "errors" + "net/http" + "gorm.io/gorm" + "github.com/gorilla/mux" + "databag/internal/store" +) + +func ClearChannelCard(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) + channelId := params["channelId"] + cardId := params["cardId"] + + // load referenced channel + var channelSlot store.ChannelSlot + if err := store.DB.Preload("Channel.Cards.CardSlot").Preload("Channel.Groups.GroupSlot").Preload("Channel.Groups.Cards").Where("account_id = ? AND channel_slot_id = ?", account.ID, channelId).First(&channelSlot).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + ErrResponse(w, http.StatusInternalServerError, err) + } else { + ErrResponse(w, http.StatusNotFound, err) + } + return + } + if channelSlot.Channel == nil { + ErrResponse(w, http.StatusNotFound, errors.New("channel has been deleted")) + return + } + + // load referenced card + var cardSlot store.CardSlot + if err := store.DB.Preload("Card.CardSlot").Where("account_id = ? AND card_slot_id = ?", account.ID, cardId).First(&cardSlot).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + ErrResponse(w, http.StatusInternalServerError, err) + } else { + ErrResponse(w, http.StatusNotFound, err) + } + return + } + if cardSlot.Card == nil { + ErrResponse(w, http.StatusNotFound, errors.New("card has been deleted")) + return + } + + // determine contact list + cards := make(map[string]store.Card) + for _, card := range channelSlot.Channel.Cards { + cards[card.Guid] = card + } + for _, group := range channelSlot.Channel.Groups { + for _, card := range group.Cards { + cards[card.Guid] = card + } + } + + // save and update contact revision + err = store.DB.Transaction(func(tx *gorm.DB) error { + if res := tx.Model(&channelSlot.Channel).Association("Cards").Delete(cardSlot.Card); res != nil { + return res + } + if res := tx.Model(&channelSlot.Channel).Update("detail_revision", account.ChannelRevision + 1).Error; res != nil { + return res + } + if res := tx.Model(&channelSlot).Update("revision", account.ChannelRevision + 1).Error; res != nil { + return res + } + if res := tx.Model(&account).Update("channel_revision", account.ChannelRevision + 1).Error; res != nil { + return res + } + return nil + }) + if err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + + // notify contacts of content change + SetStatus(account) + for _, card := range cards { + SetContactChannelNotification(account, &card) + } + WriteResponse(w, getChannelModel(&channelSlot, true, true)); +} + diff --git a/net/server/internal/api_clearChannelGroup.go b/net/server/internal/api_clearChannelGroup.go new file mode 100644 index 00000000..176e37b6 --- /dev/null +++ b/net/server/internal/api_clearChannelGroup.go @@ -0,0 +1,87 @@ +package databag + +import ( + "errors" + "net/http" + "gorm.io/gorm" + "github.com/gorilla/mux" + "databag/internal/store" +) + +func ClearChannelGroup(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) + channelId := params["channelId"] + groupId := params["groupId"] + + // load referenced channel + var channelSlot store.ChannelSlot + if err := store.DB.Preload("Channel.Cards.CardSlot").Preload("Channel.Groups.GroupSlot").Preload("Channel.Groups.Cards").Where("account_id = ? AND channel_slot_id = ?", account.ID, channelId).First(&channelSlot).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + ErrResponse(w, http.StatusInternalServerError, err) + } else { + ErrResponse(w, http.StatusNotFound, err) + } + return + } + if channelSlot.Channel == nil { + ErrResponse(w, http.StatusNotFound, errors.New("referenced empty channel")) + return + } + + // load referenced group + var groupSlot store.GroupSlot + if err := store.DB.Preload("Group.Cards").Preload("Group.GroupSlot").Where("account_id = ? AND group_slot_id = ?", account.ID, groupId).First(&groupSlot).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + ErrResponse(w, http.StatusInternalServerError, err) + } else { + ErrResponse(w, http.StatusNotFound, err) + } + return + } + if groupSlot.Group == nil { + ErrResponse(w, http.StatusNotFound, errors.New("referenced empty group")) + return + } + + // determine contact list + cards := make(map[string]store.Card) + for _, group := range channelSlot.Channel.Groups { + for _, card := range group.Cards { + cards[card.Guid] = card + } + } + + // save and update contact revision + err = store.DB.Transaction(func(tx *gorm.DB) error { + if res := tx.Model(&channelSlot.Channel).Association("Groups").Delete(groupSlot.Group); res != nil { + return res + } + if res := tx.Model(&channelSlot).Update("revision", account.ChannelRevision + 1).Error; res != nil { + return res + } + if res := tx.Model(&account).Update("channel_revision", account.ChannelRevision + 1).Error; res != nil { + return res + } + return nil + }) + if err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + + // notify contacts of content change + SetStatus(account) + for _, card := range cards { + SetContactChannelNotification(account, &card) + } + WriteResponse(w, getChannelModel(&channelSlot, true, true)); +} + diff --git a/net/server/internal/api_content.go b/net/server/internal/api_content.go index ca7e0bea..357bae20 100644 --- a/net/server/internal/api_content.go +++ b/net/server/internal/api_content.go @@ -28,16 +28,6 @@ func AddChannelTopicTag(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } -func ClearChannelCard(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusOK) -} - -func ClearChannelGroup(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusOK) -} - func GetChannelAsset(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.WriteHeader(http.StatusOK) @@ -113,16 +103,6 @@ func SetChannelConfirmed(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } -func SetChannelGroup(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusOK) -} - -func SetChannelSubject(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusOK) -} - func SetChannelTopicSubject(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_getChannels.go b/net/server/internal/api_getChannels.go index 6c7aa67b..d5d91563 100644 --- a/net/server/internal/api_getChannels.go +++ b/net/server/internal/api_getChannels.go @@ -60,12 +60,12 @@ func GetChannels(w http.ResponseWriter, r *http.Request) { var slots []store.ChannelSlot if channelRevisionSet { - if err := store.DB.Preload("Channel.Cards").Preload("Channel.Groups").Where("account_id = ? AND revision > ?", account.ID, channelRevision).Find(&slots).Error; err != nil { + if err := store.DB.Preload("Channel").Where("account_id = ? AND revision > ?", account.ID, channelRevision).Find(&slots).Error; err != nil { ErrResponse(w, http.StatusInternalServerError, err) return } } else { - if err := store.DB.Preload("Channel.Cards").Preload("Channel.Groups").Where("account_id = ? AND channel_id != 0", account.ID).Find(&slots).Error; err != nil { + if err := store.DB.Preload("Channel.Cards.CardSlot").Preload("Channel.Groups.GroupSlot").Where("account_id = ? AND channel_id != 0", account.ID).Find(&slots).Error; err != nil { ErrResponse(w, http.StatusInternalServerError, err) return } diff --git a/net/server/internal/api_setArticleSubject.go b/net/server/internal/api_setArticleSubject.go index 77b6c2dd..b76def40 100644 --- a/net/server/internal/api_setArticleSubject.go +++ b/net/server/internal/api_setArticleSubject.go @@ -42,10 +42,10 @@ func SetArticleSubject(w http.ResponseWriter, r *http.Request) { } // determine affected contact list - cards := make(map[string]*store.Card) + cards := make(map[string]store.Card) for _, group := range slot.Article.Groups { for _, card := range group.Cards { - cards[card.Guid] = &card + cards[card.Guid] = card } } @@ -73,9 +73,8 @@ func SetArticleSubject(w http.ResponseWriter, r *http.Request) { // notify contacts of content change SetStatus(account) for _, card := range cards { - SetContactArticleNotification(account, card) + SetContactArticleNotification(account, &card) } - WriteResponse(w, getArticleModel(&slot, true, true)); } diff --git a/net/server/internal/api_setChannelCard.go b/net/server/internal/api_setChannelCard.go index c38032a3..cdb10e96 100644 --- a/net/server/internal/api_setChannelCard.go +++ b/net/server/internal/api_setChannelCard.go @@ -23,7 +23,7 @@ func SetChannelCard(w http.ResponseWriter, r *http.Request) { // load referenced channel var channelSlot store.ChannelSlot - if err := store.DB.Preload("Channel").Where("account_id = ? AND channel_slot_id = ?", account.ID, channelId).First(&channelSlot).Error; err != nil { + if err := store.DB.Preload("Channel.Cards.CardSlot").Preload("Channel.Groups.GroupSlot").Preload("Channel.Groups.Cards").Where("account_id = ? AND channel_slot_id = ?", account.ID, channelId).First(&channelSlot).Error; err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { ErrResponse(w, http.StatusInternalServerError, err) } else { @@ -51,6 +51,18 @@ func SetChannelCard(w http.ResponseWriter, r *http.Request) { return } + // determine contact list + cards := make(map[string]store.Card) + for _, card := range channelSlot.Channel.Cards { + cards[card.Guid] = card + } + for _, group := range channelSlot.Channel.Groups { + for _, card := range group.Cards { + cards[card.Guid] = card + } + } + cards[cardSlot.Card.Guid] = *cardSlot.Card + // save and update contact revision err = store.DB.Transaction(func(tx *gorm.DB) error { if res := tx.Model(&channelSlot.Channel).Association("Cards").Append(cardSlot.Card); res != nil { @@ -74,10 +86,9 @@ func SetChannelCard(w http.ResponseWriter, r *http.Request) { // notify contacts of content change SetStatus(account) - for _, card := range channelSlot.Channel.Cards { + for _, card := range cards { SetContactChannelNotification(account, &card) } - WriteResponse(w, getChannelModel(&channelSlot, true, true)); } diff --git a/net/server/internal/api_setChannelGroup.go b/net/server/internal/api_setChannelGroup.go new file mode 100644 index 00000000..d4034b85 --- /dev/null +++ b/net/server/internal/api_setChannelGroup.go @@ -0,0 +1,87 @@ +package databag + +import ( + "errors" + "net/http" + "gorm.io/gorm" + "github.com/gorilla/mux" + "databag/internal/store" +) + +func SetChannelGroup(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) + channelId := params["channelId"] + groupId := params["groupId"] + + // load referenced channel + var channelSlot store.ChannelSlot + if err := store.DB.Preload("Channel.Cards.CardSlot").Preload("Channel.Groups.GroupSlot").Preload("Channel.Groups.Cards").Where("account_id = ? AND channel_slot_id = ?", account.ID, channelId).First(&channelSlot).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + ErrResponse(w, http.StatusInternalServerError, err) + } else { + ErrResponse(w, http.StatusNotFound, err) + } + return + } + if channelSlot.Channel == nil { + ErrResponse(w, http.StatusNotFound, errors.New("channel has been deleted")) + return + } + + // load referenced group + var groupSlot store.GroupSlot + if err := store.DB.Preload("Group.Cards").Preload("Group.GroupSlot").Where("account_id = ? AND group_slot_id = ?", account.ID, groupId).First(&groupSlot).Error; err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + ErrResponse(w, http.StatusInternalServerError, err) + } else { + ErrResponse(w, http.StatusNotFound, err) + } + return + } + if groupSlot.Group == nil { + ErrResponse(w, http.StatusNotFound, errors.New("group has been deleted")) + return + } + + // save and update contact revision + err = store.DB.Transaction(func(tx *gorm.DB) error { + if res := tx.Model(&channelSlot.Channel).Association("Groups").Append(groupSlot.Group); res != nil { + return res + } + if res := tx.Model(&channelSlot).Update("revision", account.ChannelRevision + 1).Error; res != nil { + return res + } + if res := tx.Model(&account).Update("channel_revision", account.ChannelRevision + 1).Error; res != nil { + return res + } + return nil + }) + if err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + + // determine contact list + cards := make(map[string]store.Card) + for _, group := range channelSlot.Channel.Groups { + for _, card := range group.Cards { + cards[card.Guid] = card + } + } + + // notify contacts of content change + SetStatus(account) + for _, card := range cards { + SetContactChannelNotification(account, &card) + } + WriteResponse(w, getChannelModel(&channelSlot, true, true)); +} + diff --git a/net/server/internal/api_setChannelSubject.go b/net/server/internal/api_setChannelSubject.go new file mode 100644 index 00000000..136a157e --- /dev/null +++ b/net/server/internal/api_setChannelSubject.go @@ -0,0 +1,83 @@ +package databag + +import ( + "errors" + "net/http" + "gorm.io/gorm" + "github.com/gorilla/mux" + "databag/internal/store" +) + +func SetChannelSubject(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) + channelId := params["channelId"] + + var subject Subject + if err := ParseRequest(r, w, &subject); err != nil { + ErrResponse(w, http.StatusBadRequest, err) + return + } + + // load referenced channel + var slot store.ChannelSlot + if err := store.DB.Preload("Channel.Cards").Preload("Channel.Groups.Cards").Where("account_id = ? AND channel_slot_id = ?", account.ID, channelId).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.Channel == nil { + ErrResponse(w, http.StatusNotFound, errors.New("channel has been deleted")) + return + } + + // determine affected contact list + cards := make(map[string]store.Card) + for _, card := range slot.Channel.Cards { + cards[card.Guid] = card + } + for _, group := range slot.Channel.Groups { + for _, card := range group.Cards { + cards[card.Guid] = card + } + } + + // save and update contact revision + err = store.DB.Transaction(func(tx *gorm.DB) error { + if res := tx.Model(&slot.Channel).Update("data", subject.Data).Error; res != nil { + return res + } + if res := tx.Model(&slot.Channel).Update("data_type", subject.DataType).Error; res != nil { + return res + } + if res := tx.Model(&slot).Update("revision", account.ChannelRevision + 1).Error; res != nil { + return res + } + if res := tx.Model(&account).Update("channel_revision", account.ChannelRevision + 1).Error; res != nil { + return res + } + return nil + }) + if err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + + // notify contacts of content change + SetStatus(account) + for _, card := range cards { + SetContactChannelNotification(account, &card) + } + WriteResponse(w, getChannelModel(&slot, true, true)); +} + diff --git a/net/server/internal/models.go b/net/server/internal/models.go index 6047195f..95480c27 100644 --- a/net/server/internal/models.go +++ b/net/server/internal/models.go @@ -173,9 +173,9 @@ type ChannelDetail struct { Groups *IdList `json:"groups,omitempty"` - Cards *IdList `json:"groups,omitempty"` + Cards *IdList `json:"cards,omitempty"` - Members []string `json:"cards"` + Members []string `json:"members"` } type Claim struct { diff --git a/net/server/internal/ucTopicShare_test.go b/net/server/internal/ucTopicShare_test.go index ecf311bf..8f8c489e 100644 --- a/net/server/internal/ucTopicShare_test.go +++ b/net/server/internal/ucTopicShare_test.go @@ -2,6 +2,7 @@ package databag import ( "testing" + "strconv" "github.com/stretchr/testify/assert" ) @@ -9,12 +10,20 @@ func TestTopicShare(t *testing.T) { var subject *Subject var channel *Channel var channels *[]Channel + var cards *[]Card aRevision := make(map[string][]string) - bRevision := make(map[string][]string) - cRevision := make(map[string][]string) + bCardRevision := make(map[string][]string) + bChannelRevision := make(map[string][]string) + cCardRevision := make(map[string][]string) + cChannelRevision := make(map[string][]string) var revision string params := make(map[string]string) var detailRevision int64 + var rev *Revision + var aRev *Revision + var cRev *Revision + var bRev *Revision + var r int64 // setup testing group set, err := AddTestGroup("topicshare") @@ -35,11 +44,11 @@ func TestTopicShare(t *testing.T) { detailRevision = (*channels)[0].Data.DetailRevision channels = &[]Channel{} assert.NoError(t, ApiTestMsg(GetChannels, "GET", "/content/channels", - nil, nil, APP_TOKENCONTACT, set.B.A.Token, channels, &bRevision)) + nil, nil, APP_TOKENCONTACT, set.B.A.Token, channels, &bChannelRevision)) assert.Equal(t, 0, len(*channels)) channels = &[]Channel{} assert.NoError(t, ApiTestMsg(GetChannels, "GET", "/content/channels", - nil, nil, APP_TOKENCONTACT, set.C.A.Token, channels, &cRevision)) + nil, nil, APP_TOKENCONTACT, set.C.A.Token, channels, &cChannelRevision)) assert.Equal(t, 0, len(*channels)) // assign channel to B @@ -57,15 +66,15 @@ func TestTopicShare(t *testing.T) { assert.NotNil(t, (*channels)[0].Data) assert.NotEqual(t, detailRevision, (*channels)[0].Data.DetailRevision) channels = &[]Channel{} - revision = "?channelRevision=" + bRevision["Channel-Revision"][0] + "&viewRevision=" + bRevision["View-Revision"][0] + revision = "?channelRevision=" + bChannelRevision["Channel-Revision"][0] + "&viewRevision=" + bChannelRevision["View-Revision"][0] assert.NoError(t, ApiTestMsg(GetChannels, "GET", "/content/channels" + revision, - nil, nil, APP_TOKENCONTACT, set.B.A.Token, channels, &bRevision)) + nil, nil, APP_TOKENCONTACT, set.B.A.Token, channels, &bChannelRevision)) assert.Equal(t, 1, len(*channels)) assert.NotNil(t, (*channels)[0].Data) channels = &[]Channel{} - revision = "?channelRevision=" + cRevision["Channel-Revision"][0] + "&viewRevision=" + cRevision["View-Revision"][0] + revision = "?channelRevision=" + cChannelRevision["Channel-Revision"][0] + "&viewRevision=" + cChannelRevision["View-Revision"][0] assert.NoError(t, ApiTestMsg(GetChannels, "GET", "/content/channels" + revision, - nil, nil, APP_TOKENCONTACT, set.C.A.Token, channels, &cRevision)) + nil, nil, APP_TOKENCONTACT, set.C.A.Token, channels, &cChannelRevision)) assert.Equal(t, 1, len(*channels)) assert.Nil(t, (*channels)[0].Data) @@ -76,6 +85,140 @@ func TestTopicShare(t *testing.T) { assert.Equal(t, "channeldatatype", channel.Data.ChannelDetail.DataType) assert.Equal(t, 1, len(channel.Data.ChannelDetail.Members)) assert.Equal(t, set.B.Guid, channel.Data.ChannelDetail.Members[0]) + assert.Nil(t, channel.Data.ChannelDetail.Groups) + assert.Nil(t, channel.Data.ChannelDetail.Cards) + + // get revision + aRev = GetTestRevision(set.A.Revisions) + bRev = GetTestRevision(set.B.Revisions) + cRev = GetTestRevision(set.C.Revisions) + cards = &[]Card{} + assert.NoError(t, ApiTestMsg(GetCards, "GET", "/contact/cards", + nil, nil, APP_TOKENAPP, set.B.Token, cards, &bCardRevision)) + channels = &[]Channel{} + assert.NoError(t, ApiTestMsg(GetChannels, "GET", "/content/channels", + nil, nil, APP_TOKENCONTACT, set.B.A.Token, channels, &bChannelRevision)) + cards = &[]Card{} + assert.NoError(t, ApiTestMsg(GetCards, "GET", "/contact/cards", + nil, nil, APP_TOKENAPP, set.C.Token, cards, &cCardRevision)) + channels = &[]Channel{} + assert.NoError(t, ApiTestMsg(GetChannels, "GET", "/content/channels", + nil, nil, APP_TOKENCONTACT, set.C.A.Token, channels, &cChannelRevision)) + + // assign channel to C + params["channelId"] = channel.Id + params["cardId"] = set.A.C.CardId + assert.NoError(t, ApiTestMsg(SetChannelCard, "PUT", "/content/channels/{channelId}/cards/{cardId}", + ¶ms, nil, APP_TOKENAPP, set.A.Token, nil, nil)) + + // check revision change + rev = GetTestRevision(set.A.Revisions) + assert.NotEqual(t, rev.Channel, aRev.Channel) + aRev = rev + rev = GetTestRevision(set.B.Revisions) + assert.NotEqual(t, rev.Card, bRev.Card) + cards = &[]Card{} + assert.NoError(t, ApiTestMsg(GetCards, "GET", "/contact/cards?revision=" + bCardRevision["Card-Revision"][0], + nil, nil, APP_TOKENAPP, set.B.Token, cards, &bCardRevision)) + assert.Equal(t, 1, len(*cards)) + r, _ = strconv.ParseInt(bChannelRevision["Channel-Revision"][0], 10, 64) + assert.NotEqual(t, r, (*cards)[0].Data.NotifiedChannel) + r, _ = strconv.ParseInt(bChannelRevision["View-Revision"][0], 10, 64) + assert.Equal(t, r, (*cards)[0].Data.NotifiedView) + bRev = rev + + rev = GetTestRevision(set.C.Revisions) + assert.NotEqual(t, rev.Card, cRev.Card) + cards = &[]Card{} + assert.NoError(t, ApiTestMsg(GetCards, "GET", "/contact/cards?revision=" + cCardRevision["Card-Revision"][0], + nil, nil, APP_TOKENAPP, set.C.Token, cards, &cCardRevision)) + assert.Equal(t, 1, len(*cards)) + r, _ = strconv.ParseInt(cChannelRevision["Channel-Revision"][0], 10, 64) + assert.NotEqual(t, r, (*cards)[0].Data.NotifiedChannel) + r, _ = strconv.ParseInt(cChannelRevision["View-Revision"][0], 10, 64) + assert.Equal(t, r, (*cards)[0].Data.NotifiedView) + cRev = rev + + channels = &[]Channel{} + revision = "?channelRevision=" + bChannelRevision["Channel-Revision"][0] + "&viewRevision=" + bChannelRevision["View-Revision"][0] + assert.NoError(t, ApiTestMsg(GetChannels, "GET", "/content/channels" + revision, + nil, nil, APP_TOKENCONTACT, set.B.A.Token, channels, &bChannelRevision)) + assert.Equal(t, 1, len(*channels)) + channels = &[]Channel{} + revision = "?channelRevision=" + cChannelRevision["Channel-Revision"][0] + "&viewRevision=" + cChannelRevision["View-Revision"][0] + assert.NoError(t, ApiTestMsg(GetChannels, "GET", "/content/channels" + revision, + nil, nil, APP_TOKENCONTACT, set.C.A.Token, channels, &cChannelRevision)) + assert.Equal(t, 1, len(*channels)) + + // get discovered channel + channel = &Channel{} + assert.NoError(t, ApiTestMsg(GetChannel, "GET", "/content/channels/{channelId}", + ¶ms, nil, APP_TOKENCONTACT, set.C.A.Token, channel, nil)) + assert.Equal(t, "channeldatatype", channel.Data.ChannelDetail.DataType) + assert.Equal(t, 2, len(channel.Data.ChannelDetail.Members)) + assert.Nil(t, channel.Data.ChannelDetail.Groups) + assert.Nil(t, channel.Data.ChannelDetail.Cards) + + // reset notification + GetTestRevision(set.B.Revisions) + GetTestRevision(set.C.Revisions) + + // remove channel from C + params["channelId"] = channel.Id + params["cardId"] = set.A.C.CardId + assert.NoError(t, ApiTestMsg(ClearChannelCard, "DELETE", "/content/channels/{channelId}/cards/{cardId}", + ¶ms, nil, APP_TOKENAPP, set.A.Token, nil, nil)) + + // check channel view from C + assert.NotNil(t, GetTestRevision(set.B.Revisions)) + assert.NotNil(t, GetTestRevision(set.C.Revisions)) + revision = "?channelRevision=" + cChannelRevision["Channel-Revision"][0] + "&viewRevision=" + cChannelRevision["View-Revision"][0] + assert.NoError(t, ApiTestMsg(GetChannels, "GET", "/content/channels" + revision, + nil, nil, APP_TOKENCONTACT, set.C.A.Token, channels, &cChannelRevision)) + assert.Equal(t, 1, len(*channels)) + assert.Nil(t, (*channels)[0].Data) + + // update attribute + image := "iVBORw0KGgoAAAANSUhEUgAAAaQAAAGkCAIAAADxLsZiAAAFzElEQVR4nOzWUY3jMBhG0e0qSEqoaIqiaEIoGAxh3gZAldid3nMI+JOiXP3bGOMfwLf7v3oAwAxiBySIHZAgdkCC2AEJYgckiB2QIHZAgtgBCWIHJIgdkCB2QILYAQliBySIHZAgdkCC2AEJYgckiB2QIHZAgtgBCWIHJIgdkCB2QILYAQliBySIHZAgdkCC2AEJYgckiB2QIHZAgtgBCWIHJGzTXnrtx7S3pnk+7qsnnMk3+ny+0dtcdkCC2AEJYgckiB2QIHZAgtgBCWIHJIgdkCB2QILYAQliBySIHZAgdkCC2AEJYgckiB2QIHZAgtgBCWIHJIgdkCB2QILYAQliBySIHZAgdkCC2AEJYgckiB2QIHZAgtgBCWIHJIgdkCB2QILYAQliBySIHZAgdkCC2AEJYgckiB2QIHZAgtgBCWIHJIgdkCB2QILYAQliBySIHZAgdkCC2AEJYgckiB2QIHZAgtgBCWIHJIgdkCB2QILYAQliBySIHZAgdkCC2AEJYgckiB2QIHZAgtgBCWIHJIgdkCB2QILYAQnbtJeej/u0t+Bb+Y/e5rIDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSbmOM1RsALueyAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyAhG31gD/stR+rJ5zv+bivnnAm34hfLjsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBhWz2Az/Laj9UT4BIuOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgITbGGP1BoDLueyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7IAEsQMSxA5IEDsgQeyABLEDEsQOSBA7IEHsgASxAxLEDkgQOyBB7ICEnwAAAP//DQ4epwV6rzkAAAAASUVORK5CYII=" + data := "{ \"nested\" : { \"image\" : \"" + image + "\" } }" + subject = &Subject{ Data: data, DataType: "nestedimage" } + channel = &Channel{} + assert.NoError(t, ApiTestMsg(SetChannelSubject, "PUT", "/content/channels/{channelId}/subject", + ¶ms, subject, APP_TOKENAPP, set.A.Token, channel, nil)) + + // check notifications + assert.NotNil(t, GetTestRevision(set.B.Revisions)) + assert.Nil(t, GetTestRevision(set.C.Revisions)) + + // add C group to channel + params["groupId"] = set.A.C.GroupId + channel = &Channel{} + assert.NoError(t, ApiTestMsg(SetChannelGroup, "PUT", "/content/channels/{channelId}/groups/{groupId}", + ¶ms, nil, APP_TOKENAPP, set.A.Token, channel, nil)) + assert.Equal(t, 1, len(channel.Data.ChannelDetail.Cards.Ids)) + assert.Equal(t, 1, len(channel.Data.ChannelDetail.Groups.Ids)) + + // reset notification + GetTestRevision(set.B.Revisions) + GetTestRevision(set.C.Revisions) + + // remove channel from B + params["channelId"] = channel.Id + params["cardId"] = set.A.B.CardId + assert.NoError(t, ApiTestMsg(ClearChannelCard, "DELETE", "/content/channels/{channelId}/cards/{cardId}", + ¶ms, nil, APP_TOKENAPP, set.A.Token, channel, nil)) + + // check notifications + assert.NotNil(t, GetTestRevision(set.B.Revisions)) + assert.NotNil(t, GetTestRevision(set.C.Revisions)) + + // remove C group from channel + params["groupId"] = set.A.C.GroupId + channel = &Channel{} + assert.NoError(t, ApiTestMsg(ClearChannelGroup, "DELETE", "/content/channels/{channelId}/groups/{groupId}", + ¶ms, nil, APP_TOKENAPP, set.A.Token, channel, nil)) + assert.Equal(t, 0, len(channel.Data.ChannelDetail.Cards.Ids)) + assert.Equal(t, 0, len(channel.Data.ChannelDetail.Groups.Ids)) }