From 26f4f2d4588324f8b2804cce90372e43a254aa60 Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Thu, 3 Mar 2022 00:46:32 -0800 Subject: [PATCH] testing channel remove --- net/server/internal/api_addChannelTopicTag.go | 1 + net/server/internal/api_content.go | 26 ----- net/server/internal/api_removeChannel.go | 102 ++++++++++++++++++ net/server/internal/api_removeChannelTopic.go | 101 +++++++++++++++++ net/server/internal/store/schema.go | 2 + net/server/internal/ucTopicShare_test.go | 18 ++++ 6 files changed, 224 insertions(+), 26 deletions(-) delete mode 100644 net/server/internal/api_content.go create mode 100644 net/server/internal/api_removeChannel.go create mode 100644 net/server/internal/api_removeChannelTopic.go diff --git a/net/server/internal/api_addChannelTopicTag.go b/net/server/internal/api_addChannelTopicTag.go index afb69a1d..8de28081 100644 --- a/net/server/internal/api_addChannelTopicTag.go +++ b/net/server/internal/api_addChannelTopicTag.go @@ -51,6 +51,7 @@ func AddChannelTopicTag(w http.ResponseWriter, r *http.Request) { tagSlot.TagSlotId = uuid.New().String() tagSlot.AccountID = act.ID tagSlot.Revision = act.ChannelRevision + 1 + tagSlot.ChannelID = channelSlot.Channel.ID tagSlot.TopicID = topicSlot.Topic.ID if res := tx.Save(tagSlot).Error; res != nil { return res diff --git a/net/server/internal/api_content.go b/net/server/internal/api_content.go deleted file mode 100644 index 8b3c1c8f..00000000 --- a/net/server/internal/api_content.go +++ /dev/null @@ -1,26 +0,0 @@ -/* - * DataBag - * - * DataBag provides storage for decentralized identity based self-hosting apps. It is intended to support sharing of personal data and hosting group conversations. - * - * API version: 0.0.1 - * Contact: roland.osborne@gmail.com - * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) - */ -package databag - -import ( - "net/http" -) - -func RemoveChannel(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=UTF-8") - w.WriteHeader(http.StatusOK) -} - -func RemoveChannelTopic(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_removeChannel.go b/net/server/internal/api_removeChannel.go new file mode 100644 index 00000000..fdee4f8b --- /dev/null +++ b/net/server/internal/api_removeChannel.go @@ -0,0 +1,102 @@ +package databag + +import ( + "errors" + "net/http" + "gorm.io/gorm" + "github.com/gorilla/mux" + "databag/internal/store" +) + +func RemoveChannel(w http.ResponseWriter, r *http.Request) { + + // scan parameters + params := mux.Vars(r) + channelId := params["channelId"] + + // validate contact access + account, code, err := BearerAppToken(r, false) + if err != nil { + ErrResponse(w, code, err) + return + } + + // load 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.StatusNotFound, err) + } else { + ErrResponse(w, http.StatusInternalServerError, err) + } + return + } + if slot.Channel == nil { + ErrResponse(w, http.StatusNotFound, errors.New("referenced empty channel")) + 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 + } + } + + err = store.DB.Transaction(func(tx *gorm.DB) error { + if res := tx.Model(&slot.Channel).Association("Groups").Clear(); res != nil { + return res + } + slot.Channel.Groups = []store.Group{} + if res := tx.Model(&slot.Channel).Association("Cards").Clear(); res != nil { + return res + } + slot.Channel.Cards = []store.Card{} + if res := tx.Where("channel_id = ?", slot.Channel.ID).Delete(&store.Tag{}).Error; res != nil { + return res + } + if res := tx.Where("channel_id = ?", slot.Channel.ID).Delete(&store.TagSlot{}).Error; res != nil { + return res + } + if res := tx.Where("channel_id = ?", slot.Channel.ID).Delete(&store.Asset{}).Error; res != nil { + return res + } + if res := tx.Where("channel_id = ?", slot.Channel.ID).Delete(&store.Topic{}).Error; res != nil { + return res + } + if res := tx.Where("channel_id = ?", slot.Channel.ID).Delete(&store.TopicSlot{}).Error; res != nil { + return res + } + slot.Channel.Topics = []store.Topic{} + if res := tx.Delete(&slot.Channel).Error; res != nil { + return res + } + slot.Channel = nil + 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 + } + + // cleanup file assets + go garbageCollect(account) + + SetStatus(account) + for _, card := range cards { + SetContactChannelNotification(account, &card) + } + WriteResponse(w, nil) +} + + diff --git a/net/server/internal/api_removeChannelTopic.go b/net/server/internal/api_removeChannelTopic.go new file mode 100644 index 00000000..1942a401 --- /dev/null +++ b/net/server/internal/api_removeChannelTopic.go @@ -0,0 +1,101 @@ +package databag + +import ( + "errors" + "net/http" + "gorm.io/gorm" + "github.com/gorilla/mux" + "databag/internal/store" +) + +func RemoveChannelTopic(w http.ResponseWriter, r *http.Request) { + + // scan parameters + params := mux.Vars(r) + channelId := params["channelId"] + topicId := params["topicId"] + + channelSlot, guid, err, code := getChannelSlot(r, true) + if err != nil { + ErrResponse(w, code, err) + return + } + act := &channelSlot.Account + + // load topic + var topicSlot store.TopicSlot + if ret := store.DB.Preload("Topic.Channel.ChannelSlot").Where("account_id = ? AND topic_slot_id = ?", act.ID, topicId).First(&topicSlot).Error; ret != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + ErrResponse(w, http.StatusNotFound, ret) + } else { + ErrResponse(w, http.StatusInternalServerError, ret) + } + } + if topicSlot.Topic == nil { + ErrResponse(w, http.StatusNotFound, errors.New("referenced empty topic")) + return + } + if topicSlot.Topic.Channel.ChannelSlot.ChannelSlotId != channelId { + ErrResponse(w, http.StatusNotFound, errors.New("channel topic not found")) + return + } + + // check permission + if topicSlot.Topic.Guid != guid { + ErrResponse(w, http.StatusUnauthorized, errors.New("not creator of topic")) + return + } + + err = store.DB.Transaction(func(tx *gorm.DB) error { + + if res := tx.Where("topic_id = ?", topicSlot.Topic.ID).Delete(&store.Tag{}).Error; res != nil { + return res + } + if res := tx.Where("topic_id = ?", topicSlot.Topic.ID).Delete(&store.TagSlot{}).Error; res != nil { + return res + } + if res := tx.Where("topic_id = ?", topicSlot.Topic.ID).Delete(&store.Asset{}).Error; res != nil { + return res + } + if res := tx.Delete(&topicSlot.Topic).Error; res != nil { + return res + } + topicSlot.Topic = nil + if res := tx.Model(&topicSlot).Update("revision", act.ChannelRevision + 1).Error; res != nil { + return res + } + if res := tx.Model(&channelSlot).Update("revision", act.ChannelRevision + 1).Error; res != nil { + return res + } + if res := tx.Model(act).Update("channel_revision", act.ChannelRevision + 1).Error; res != nil { + return res + } + return nil + }) + if err != nil { + ErrResponse(w, http.StatusInternalServerError, err) + return + } + + // cleanup file assets + go garbageCollect(act) + + // determine affected 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 + } + } + + SetStatus(act) + for _, card := range cards { + SetContactChannelNotification(act, &card) + } + WriteResponse(w, nil) +} + + diff --git a/net/server/internal/store/schema.go b/net/server/internal/store/schema.go index 255fd551..76c1a216 100644 --- a/net/server/internal/store/schema.go +++ b/net/server/internal/store/schema.go @@ -261,10 +261,12 @@ type TagSlot struct { ID uint TagSlotId string `gorm:"not null;index:tagslot,unique"` AccountID uint `gorm:"not null;index:tagslot,unique"` + ChannelID uint `gorm:"not null"` TopicID uint `gorm:"not null;index:tagtopic"` Revision int64 `gorm:"not null"` Tag *Tag Account Account + Channel *Channel Topic *Topic } diff --git a/net/server/internal/ucTopicShare_test.go b/net/server/internal/ucTopicShare_test.go index 269fa4e4..bc43986d 100644 --- a/net/server/internal/ucTopicShare_test.go +++ b/net/server/internal/ucTopicShare_test.go @@ -150,5 +150,23 @@ func TestTopicShare(t *testing.T) { assert.NoError(t, ApiTestMsg(GetChannelTopicAssets, "GET", "/content/channels/{channelId}/topics/{topicId}", ¶ms, nil, APP_TOKENCONTACT, set.C.A.Token, &assets, nil)) assert.Equal(t, 0, len(assets)) + + // add asset to topic + assets = []Asset{} + params["topicId"] = topic.Id + transforms, err = json.Marshal([]string{ "copy;photo" }) + assert.NoError(t, err) + assert.NoError(t, ApiTestUpload(AddChannelTopicAsset, "POST", "/content/channels/{channelId}/topics/{topicId}/assets?transforms=" + url.QueryEscape(string(transforms)), + ¶ms, img, APP_TOKENCONTACT, set.C.A.Token, &assets, nil)) + + // add a tag to topic + tag = Tag{} + subject = &Subject{ DataType: "tagdatatype", Data: "subjectfromA" } + assert.NoError(t, ApiTestMsg(AddChannelTopicTag, "POST", "/content/channels/{channelId}/topics/{topicId}", + ¶ms, subject, APP_TOKENAPP, set.A.Token, &tag, nil)) + + // remove channel + assert.NoError(t, ApiTestMsg(RemoveChannel, "DELETE", "/content/channels/{channelId}", + ¶ms, nil, APP_TOKENAPP, set.A.Token, nil, nil)) }