// todo - refactor this part func (f *Controller) MessageListDeleted(cml *models.ChannelMessageList) error { c, err := models.Cache.Channel.ById(cml.ChannelId) if err != nil { return err } // Since we are making hard deletes, we no longer need to check the // message existency cm := models.NewChannelMessage() cm.Id = cml.MessageId cp := models.NewChannelParticipant() cp.AccountId = c.CreatorId cue := &channelUpdatedEvent{ Controller: f, Channel: c, ParentChannelMessage: cm, ChannelParticipant: cp, EventType: channelUpdatedEventMessageRemovedFromChannel, } if err := cue.notifyAllParticipants(); err != nil { return err } // f.sendNotification(cp.AccountId, ChannelUpdateEventName, cue) if err := f.sendChannelEvent(cml, cm, MessageRemovedEventName); err != nil { return err } return nil }
func FetchPostCount(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) { query := request.GetQuery(u) query = context.OverrideQuery(query) accountId, err := request.GetId(u) if err != nil { return response.NewBadRequest(err) } c, err := models.Cache.Channel.ByGroupName(query.GroupName) if err != nil { return response.NewBadRequest(err) } // fetch user post count in koding channel q := request.NewQuery() q.AccountId = accountId q.Type = models.ChannelMessage_TYPE_POST q.GroupChannelId = c.Id cm := models.NewChannelMessage() count, err := cm.FetchTotalMessageCount(q) if err != nil { return response.NewBadRequest(err) } res := new(models.CountResponse) res.TotalCount = count return response.NewOK(res) }
func (mwc *Controller) GrantMessageAccess() { mwc.log.Notice("Granting public access for messages") c := models.NewChannelMessage() query := bongo.B.DB. Model(c). Table(c.BongoName()). Select("api.channel_message.token") rows, err := query.Rows() defer rows.Close() if err != nil { panic(err) } if rows == nil { return } var token string for rows.Next() { rows.Scan(&token) channel := realtimemodels.UpdateInstanceMessage{ Token: token, } err := mwc.pubnub.GrantPublicAccess(realtimemodels.NewMessageUpdateChannel(channel)) if err != nil { mwc.log.Error("Could not grant public access to token %s: %s", token, err) } } }
func CreatePostWithBody(channelId, accountId int64, body string) (*models.ChannelMessage, error) { cm := models.NewChannelMessage() cm.Body = body cm.AccountId = accountId return createPostRequest(channelId, cm, http.Header{}) }
func getMessageByUrl(u *url.URL) (*models.ChannelMessage, error) { // TODO // fmt.Println(` // -------> // ADD SECURTY CHECK FOR VISIBILTY OF THE MESSAGE // FOR THE REQUESTER // ------->"`, // ) id, err := request.GetURIInt64(u, "id") if err != nil { return nil, err } // get url query params q := request.GetQuery(u) query := &bongo.Query{ Selector: map[string]interface{}{ "id": id, }, Pagination: *bongo.NewPagination(1, 0), } cm := models.NewChannelMessage() // add exempt info query.AddScope(models.RemoveTrollContent(cm, q.ShowExempt)) if err := cm.One(query); err != nil { return nil, err } return cm, nil }
func ListPosts(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) { query := request.GetQuery(u) query = context.OverrideQuery(query) buildMessageQuery := query.Clone() accountId, err := request.GetId(u) if err != nil { return response.NewBadRequest(err) } c, err := models.Cache.Channel.ByGroupName(query.GroupName) if err != nil { return response.NewBadRequest(err) } // fetch only channel messages query.Type = models.ChannelMessage_TYPE_POST query.AccountId = accountId cm := models.NewChannelMessage() messages, err := cm.FetchMessagesByChannelId(c.Id, query) if err != nil { return response.NewBadRequest(err) } buildMessageQuery.Limit = 3 return response.HandleResultAndError( cm.BuildMessages(buildMessageQuery, messages), ) }
func GetPost(id int64, token string) (*models.ChannelMessage, error) { url := fmt.Sprintf("/message/%d", id) cm := models.NewChannelMessage() cmI, err := sendModelWithAuth("GET", url, cm, token) if err != nil { return nil, err } return cmI.(*models.ChannelMessage), nil }
func checkThrottle(channelId, requesterId int64) error { c, err := models.Cache.Channel.ById(channelId) if err != nil { return err } if c.TypeConstant != models.Channel_TYPE_GROUP { return nil } cm := models.NewChannelMessage() conf := config.MustGet() // if oit is defaul treturn early if conf.Limits.PostThrottleDuration == "" { return nil } // if throttle count is zero, it meands it is not set if conf.Limits.PostThrottleCount == 0 { return nil } dur, err := time.ParseDuration(conf.Limits.PostThrottleDuration) if err != nil { return err } // subtrack duration from current time prevTime := time.Now().UTC().Truncate(dur) // count sends positional parameters, no need to sanitize input count, err := bongo.B.Count( cm, "initial_channel_id = ? and "+ "account_id = ? and "+ "created_at > ?", channelId, requesterId, prevTime.Format(time.RFC3339Nano), ) if err != nil { return err } if count > conf.Limits.PostThrottleCount { return fmt.Errorf("reached to throttle, current post count %d for user %d", count, requesterId) } return nil }
func decorateContainers(containers []*models.ChannelMessageContainer, messages []models.ChannelMessage, accountId int64) { log := runner.MustGetLogger() var err error for i, message := range messages { d := models.NewChannelMessage() *d = message containers[i], err = d.BuildEmptyMessageContainer() if err != nil { log.Error("Could not create message container for message %d: %s", containers[i].Message.Id, err) continue } } }
func addRepliesToMessage(channelId, accountId, messageId int64) { // add replies to the message rply1 := models.NewChannelMessage() rply1.AccountId = accountId rply1.InitialChannelId = channelId rply1.TypeConstant = models.ChannelMessage_TYPE_REPLY rply1.Body = "hello reply all from test" So(rply1.Create(), ShouldBeNil) mr := models.NewMessageReply() mr.ReplyId = rply1.Id mr.MessageId = messageId So(mr.Create(), ShouldBeNil) }
func FetchMessagesByIds(messageIds []int64, err error) ([]models.ChannelMessage, error) { if err != nil { return make([]models.ChannelMessage, 0), err } if len(messageIds) == 0 { return make([]models.ChannelMessage, 0), nil } channelMessages, err := models.NewChannelMessage().FetchByIds(messageIds) if err != nil { return nil, err } return channelMessages, nil }
func CreatePostWithBodyAndAuth(channelId int64, body, token string) (*models.ChannelMessage, error) { url := fmt.Sprintf("/channel/%d/message", channelId) cm := models.NewChannelMessage() cm.Body = body res, err := marshallAndSendRequestWithAuth("POST", url, cm, token) if err != nil { return nil, err } container := models.NewChannelMessageContainer() err = json.Unmarshal(res, container) if err != nil { return nil, err } return container.Message, nil }
func (m *Mail) persistReply(accountId int64) error { mId, err := m.getIdsFromMailboxHash() if err != nil { return err } messageId := mId cm, err := socialapimodels.Cache.Message.ById(messageId) if err != nil { return err } c, err := channelPermission(cm.InitialChannelId, accountId) if err != nil { return err } // create reply reply := socialapimodels.NewChannelMessage() reply.Body = m.StrippedTextReply // set the body reply.TypeConstant = socialapimodels.ChannelMessage_TYPE_REPLY if cm.TypeConstant == socialapimodels.ChannelMessage_TYPE_PRIVATE_MESSAGE { reply.TypeConstant = socialapimodels.ChannelMessage_TYPE_PRIVATE_MESSAGE } if c.TypeConstant == socialapimodels.Channel_TYPE_COLLABORATION { reply.TypeConstant = socialapimodels.ChannelMessage_TYPE_PRIVATE_MESSAGE } reply.InitialChannelId = cm.InitialChannelId reply.AccountId = accountId if err := reply.Create(); err != nil { return err } if _, err = cm.AddReply(reply); err != nil { return err } return nil }
func CheckOwnership(u *url.URL, h http.Header) (int, http.Header, interface{}, error) { accountId, err := request.GetURIInt64(u, "id") if err != nil { return response.NewBadRequest(err) } query := request.GetQuery(u) ownershipResponse := func(err error) (int, http.Header, interface{}, error) { var success bool switch err { case bongo.RecordNotFound: success = false case nil: success = true default: return response.NewBadRequest(err) } return response.NewOK(map[string]bool{"success": success}) } switch query.Type { case "channel": channel := models.NewChannel() err = channel.One(&bongo.Query{ Selector: map[string]interface{}{ "id": query.ObjectId, "creator_id": accountId, }, }) case "channel-message": channelMessage := models.NewChannelMessage() err = channelMessage.One(&bongo.Query{ Selector: map[string]interface{}{ "id": query.ObjectId, "account_id": accountId, }, }) } return ownershipResponse(err) }
func migrateChannels(r *runner.Runner, handler *algoliaconnector.Controller) error { count := 0 for b := 0; ; b++ { var messages []models.ChannelMessage err := models.NewChannelMessage().Some(&messages, &bongo.Query{ Pagination: bongo.Pagination{ Limit: 100, Skip: b * 100, }, Sort: map[string]string{ "created_at": "DESC", }, }) if err != nil { return err } for _, message := range messages { cmls, err := message.GetChannelMessageLists() if err != nil { return err } for _, cml := range cmls { count++ r.Log.Info("[%d]: currently migrating channel.Id: %d message.Id: %d", count, cml.ChannelId, cml.MessageId) if err := handler.MessageListSaved(&cml); err != nil { return err } } } if len(messages) < 100 { return nil } } }
func GetBySlug(u *url.URL, h http.Header, _ interface{}, ctx *models.Context) (int, http.Header, interface{}, error) { q := request.GetQuery(u) if q.Slug == "" { return response.NewBadRequest(errors.New("slug is not set")) } cm := models.NewChannelMessage() if err := cm.BySlug(q); err != nil { if err == bongo.RecordNotFound { return response.NewNotFound() } return response.NewBadRequest(err) } ch := models.NewChannel() if err := ch.ById(cm.InitialChannelId); err != nil { return response.NewBadRequest(err) } query := ctx.OverrideQuery(q) // check if user can open canOpen, err := ch.CanOpen(query.AccountId) if err != nil { return response.NewBadRequest(err) } if !canOpen { return response.NewBadRequest(models.ErrCannotOpenChannel) } cmc := models.NewChannelMessageContainer() if err := cmc.Fetch(cm.Id, query); err != nil { return response.NewBadRequest(err) } return response.HandleResultAndError(cmc, cmc.Err) }
func Delete(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) { parentId, err := request.GetURIInt64(u, "id") if err != nil { return response.NewBadRequest(err) } if parentId == 0 { // todo add proper logging return response.NewBadRequest(err) } replyId, err := request.GetURIInt64(u, "replyId") if err != nil { return response.NewBadRequest(err) } if replyId == 0 { // todo add proper logging return response.NewBadRequest(err) } // first delete the connection between message and the reply mr := models.NewMessageReply() mr.MessageId = parentId mr.ReplyId = replyId if err := mr.Delete(); err != nil { return response.NewBadRequest(err) } // then delete the message itself reply := models.NewChannelMessage() reply.Id = replyId if err := reply.Delete(); err != nil { return response.NewBadRequest(err) } // yes it is deleted but not removed completely from our system return response.NewDeleted() }
func (m *Mail) persistPost(accountId int64) error { cid, err := m.getIdsFromMailboxHash() if err != nil { return err } channelId := cid c, err := channelPermission(channelId, accountId) if err != nil { return err } cm := socialapimodels.NewChannelMessage() cm.Body = m.TextBody // set the body cm.TypeConstant = socialapimodels.ChannelMessage_TYPE_POST if c.TypeConstant == socialapimodels.Channel_TYPE_PRIVATE_MESSAGE { cm.TypeConstant = socialapimodels.ChannelMessage_TYPE_PRIVATE_MESSAGE } if c.TypeConstant == socialapimodels.Channel_TYPE_COLLABORATION { cm.TypeConstant = socialapimodels.ChannelMessage_TYPE_PRIVATE_MESSAGE } cm.InitialChannelId = channelId cm.AccountId = accountId if err := cm.Create(); err != nil { return err } _, err = c.EnsureMessage(cm, true) if err != nil { return err } return nil }
func (f *Controller) MessageListSaved(listing *models.ChannelMessageList) error { message := models.NewChannelMessage() if err := message.ById(listing.MessageId); err != nil { return err } if !message.SearchIndexable() { return nil } objectID := strconv.FormatInt(message.Id, 10) channelId := strconv.FormatInt(listing.ChannelId, 10) // if message is doesnt exist on algolia it will be created and tag will be // added, if it is already created before tag will be added return f.partialUpdate(IndexMessages, map[string]interface{}{ "objectID": objectID, "body": message.Body, "_tags": map[string]interface{}{ "_operation": "AddUnique", "value": channelId, }, }) }
// this is a TEMP function just for @usirin func TempList(u *url.URL, h http.Header, _ interface{}, context *models.Context) (int, http.Header, interface{}, error) { channelId, err := request.GetURIInt64(u, "id") if err != nil { return response.NewBadRequest(err) } query := request.GetQuery(u) query = context.OverrideQuery(query) c, err := models.Cache.Channel.ById(channelId) if err != nil { return response.NewBadRequest(err) } // if channel is exempt and user should see the // content, return not found err if !query.ShowExempt { query.ShowExempt = context.Client.Account.IsTroll } if c.MetaBits.Is(models.Troll) && !query.ShowExempt { return response.NewNotFound() } // check if channel is accessible by the requester canOpen, err := c.CanOpen(query.AccountId) if err != nil { return response.NewBadRequest(err) } if !canOpen { return response.NewAccessDenied( fmt.Errorf( "account (%d) tried to retrieve the unattended private channel (%d)", query.AccountId, c.Id, )) } bq := &bongo.Query{ Selector: map[string]interface{}{ "initial_channel_id": c.Id, }, Pagination: *bongo.NewPagination(query.Limit, query.Skip), Pluck: "id", } bq.AddScope(models.SortedByCreatedAt) bq.AddScope(models.RemoveTrollContent(c, query.ShowExempt)) bq.AddScope(models.ExcludeFields(query.Exclude)) bq.AddScope(models.TillTo(query.From)) bqq := bongo.B.BuildQuery(models.NewChannelMessage(), bq) var messages []int64 if err := bongo.CheckErr( bqq.Pluck(bq.Pluck, &messages), ); err != nil { return response.NewBadRequest(err) } // get the messages in regarding channel cmcs, err := models. NewChannelMessageList(). PopulateChannelMessages( messages, query, ) if err != nil { return response.NewBadRequest(err) } // reduce replies replyIds := make([]int64, 0) for i := range cmcs { cmc := cmcs[i] if cmc.Message.TypeConstant == models.ChannelMessage_TYPE_REPLY { replyIds = append(replyIds, cmc.Message.Id) } } if len(replyIds) == 0 { return response.NewOK(cmcs) } // select replies var mrs []models.MessageReply gerr := bongo.B. BuildQuery(models.NewMessageReply(), &bongo.Query{}). Where("reply_id in (?)", replyIds). Find(&mrs) if err := bongo.CheckErr(gerr); err != nil { return response.NewBadRequest(err) } // set their parent ids for j, mr := range mrs { for i := range cmcs { if mr.ReplyId == cmcs[i].Message.Id { cmcs[i].ParentID = mrs[j].MessageId } } } // send response return response.NewOK(cmcs) }
func CreatePostWithHeader(channelId int64, header http.Header, token string) (*models.ChannelMessage, error) { cm := models.NewChannelMessage() cm.Body = "Text1Text2" return createPostRequestWithAuth(channelId, cm, header, token) }
func TestChannelParticipantOperations(t *testing.T) { tests.WithRunner(t, func(r *runner.Runner) { Convey("while testing channel participants", t, func() { Convey("First Create Users and initiate conversation", func() { ownerAccount, groupChannel, groupName := models.CreateRandomGroupDataWithChecks() ownerSes, err := modelhelper.FetchOrCreateSession(ownerAccount.Nick, groupName) So(err, ShouldBeNil) So(ownerSes, ShouldNotBeNil) secondAccount, err := models.CreateAccountInBothDbs() tests.ResultedWithNoErrorCheck(secondAccount, err) _, err = groupChannel.AddParticipant(secondAccount.Id) So(err, ShouldBeNil) thirdAccount, err := models.CreateAccountInBothDbs() tests.ResultedWithNoErrorCheck(thirdAccount, err) _, err = groupChannel.AddParticipant(thirdAccount.Id) So(err, ShouldBeNil) forthAccount, err := models.CreateAccountInBothDbs() tests.ResultedWithNoErrorCheck(forthAccount, err) _, err = groupChannel.AddParticipant(forthAccount.Id) So(err, ShouldBeNil) devrim, err := models.CreateAccountInBothDbsWithNick("devrim") tests.ResultedWithNoErrorCheck(devrim, err) _, err = groupChannel.AddParticipant(devrim.Id) So(err, ShouldBeNil) ses, err := modelhelper.FetchOrCreateSession(ownerAccount.Nick, groupName) tests.ResultedWithNoErrorCheck(ses, err) secondSes, err := modelhelper.FetchOrCreateSession(secondAccount.Nick, groupName) tests.ResultedWithNoErrorCheck(secondSes, err) pmr := models.ChannelRequest{} pmr.AccountId = ownerAccount.Id pmr.Body = "new conversation" pmr.GroupName = groupName pmr.Recipients = []string{"devrim"} channelContainer, err := rest.SendPrivateChannelRequest(pmr, ownerSes.ClientId) So(err, ShouldBeNil) So(channelContainer, ShouldNotBeNil) Convey("First user should be able to add second and third users to conversation", func() { _, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerSes.ClientId, secondAccount.Id, thirdAccount.Id) So(err, ShouldBeNil) participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerSes.ClientId) So(err, ShouldBeNil) So(participants, ShouldNotBeNil) // it is four because first user is "devrim" here So(len(participants), ShouldEqual, 4) Convey("First user should not be able to re-add second participant", func() { _, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerSes.ClientId, secondAccount.Id) So(err, ShouldBeNil) participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerSes.ClientId) So(err, ShouldBeNil) So(participants, ShouldNotBeNil) So(len(participants), ShouldEqual, 4) }) Convey("Second user should be able to leave conversation", func() { // token of account -> secondAccount _, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, secondSes.ClientId, secondAccount.Id) So(err, ShouldBeNil) participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerSes.ClientId) So(err, ShouldBeNil) So(participants, ShouldNotBeNil) So(len(participants), ShouldEqual, 3) Convey("A user who is not participant of a conversation should not be able to add another user to the conversation", func() { _, err = rest.AddChannelParticipant(channelContainer.Channel.Id, secondSes.ClientId, forthAccount.Id) So(err, ShouldNotBeNil) participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerSes.ClientId) So(err, ShouldBeNil) So(participants, ShouldNotBeNil) So(len(participants), ShouldEqual, 3) }) }) Convey("Channel owner should be able to kick another conversation participant", func() { _, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, ownerSes.ClientId, secondAccount.Id) So(err, ShouldBeNil) participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerSes.ClientId) So(err, ShouldBeNil) So(participants, ShouldNotBeNil) So(len(participants), ShouldEqual, 3) }) Convey("when a user is blocked", func() { _, err = rest.BlockChannelParticipant(channelContainer.Channel.Id, ownerSes.ClientId, secondAccount.Id) So(err, ShouldBeNil) Convey("it should not be in channel participant list", func() { participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerSes.ClientId) So(err, ShouldBeNil) So(participants, ShouldNotBeNil) So(len(participants), ShouldEqual, 3) }) Convey("should not be able to add it back", func() { _, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerSes.ClientId, secondAccount.Id) So(err, ShouldNotBeNil) }) Convey("should be able to unblock", func() { _, err = rest.UnblockChannelParticipant(channelContainer.Channel.Id, ownerSes.ClientId, secondAccount.Id) So(err, ShouldBeNil) Convey("it should not be in channel participant list still", func() { participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerSes.ClientId) So(err, ShouldBeNil) So(participants, ShouldNotBeNil) So(len(participants), ShouldEqual, 3) }) Convey("when we add the same user as participant", func() { _, err = rest.AddChannelParticipant(channelContainer.Channel.Id, ownerSes.ClientId, secondAccount.Id, thirdAccount.Id) So(err, ShouldBeNil) Convey("it should be in channel participant list", func() { participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerSes.ClientId) So(err, ShouldBeNil) So(participants, ShouldNotBeNil) So(len(participants), ShouldEqual, 4) }) }) }) }) Convey("Second user should not be able to kick another conversation participant", func() { _, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, secondSes.ClientId, thirdAccount.Id) So(err, ShouldNotBeNil) }) }) Convey("First user should be able to invite second user", func() { _, err = rest.InviteChannelParticipant(channelContainer.Channel.Id, ownerSes.ClientId, secondAccount.Id) So(err, ShouldBeNil) participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerSes.ClientId) So(err, ShouldBeNil) So(participants, ShouldNotBeNil) // it is four because first user is "devrim" here So(len(participants), ShouldEqual, 2) Convey("Second user should be able to reject invitation", func() { ses, err := modelhelper.FetchOrCreateSession(secondAccount.Nick, groupName) So(err, ShouldBeNil) So(ses, ShouldNotBeNil) err = rest.RejectInvitation(channelContainer.Channel.Id, ses.ClientId) So(err, ShouldBeNil) participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerSes.ClientId) So(err, ShouldBeNil) So(participants, ShouldNotBeNil) So(len(participants), ShouldEqual, 2) }) Convey("Second user should be able to accept invitation", func() { ses, err := modelhelper.FetchOrCreateSession(secondAccount.Nick, groupName) So(err, ShouldBeNil) So(ses, ShouldNotBeNil) err = rest.AcceptInvitation(channelContainer.Channel.Id, ses.ClientId) So(err, ShouldBeNil) participants, err := rest.ListChannelParticipants(channelContainer.Channel.Id, ownerSes.ClientId) So(err, ShouldBeNil) So(participants, ShouldNotBeNil) So(len(participants), ShouldEqual, 3) }) }) // TODO Until we find a better way for handling async stuff, this test is skipped. Instead of sleep, we should use some // timeouts for testing these kind of stuff. SkipConvey("All private messages must be deleted when all participant users leave the channel", func() { account := models.NewAccount() err = account.ByNick("devrim") So(err, ShouldBeNil) _, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, ses.ClientId, account.Id) So(err, ShouldBeNil) _, err = rest.DeleteChannelParticipant(channelContainer.Channel.Id, ownerSes.ClientId, ownerAccount.Id) So(err, ShouldBeNil) time.Sleep(1 * time.Second) testChannel := models.NewChannel() err := testChannel.ById(channelContainer.Channel.Id) So(err, ShouldEqual, bongo.RecordNotFound) testChannelList := models.NewChannelMessageList() err = bongo.B.Unscoped().Where("channel_id = ?", channelContainer.Channel.Id).Find(testChannelList).Error So(err, ShouldEqual, bongo.RecordNotFound) testMessage := models.NewChannelMessage() err = bongo.B.Unscoped().Where("initial_channel_id = ?", channelContainer.Channel.Id).Find(testMessage).Error So(err, ShouldEqual, bongo.RecordNotFound) }) Convey("Users should not be able to add/remove users to/from bot channels", func() { ownerAccount, _, groupName := models.CreateRandomGroupDataWithChecks() participant := models.NewAccount() participant.OldId = AccountOldId.Hex() participant, err = rest.CreateAccount(participant) So(err, ShouldBeNil) So(participant, ShouldNotBeNil) ses, err := modelhelper.FetchOrCreateSession(ownerAccount.Nick, groupName) So(err, ShouldBeNil) ch, err := rest.CreateChannelByGroupNameAndType(ownerAccount.Id, groupName, models.Channel_TYPE_BOT, ses.ClientId) So(err, ShouldBeNil) So(ch, ShouldNotBeNil) // account is -> ownerAccount.Id _, err = rest.AddChannelParticipant(ch.Id, ses.ClientId, participant.Id) So(strings.Contains(err.Error(), "can not add participants for bot channel"), ShouldBeTrue) }) Convey("Users should be able to add/remove users to/from topic channels", func() { ownerAccount, _, groupName := models.CreateRandomGroupDataWithChecks() participant := models.NewAccount() participant.OldId = AccountOldId.Hex() participant, err = rest.CreateAccount(participant) So(err, ShouldBeNil) So(participant, ShouldNotBeNil) ses, err := modelhelper.FetchOrCreateSession(ownerAccount.Nick, groupName) So(err, ShouldBeNil) ch, err := rest.CreateChannelByGroupNameAndType(ownerAccount.Id, groupName, models.Channel_TYPE_TOPIC, ses.ClientId) So(err, ShouldBeNil) So(ch, ShouldNotBeNil) // account is -> ownerAccount.Id _, err = rest.AddChannelParticipant(ch.Id, ses.ClientId, participant.Id) So(err, ShouldBeNil) Convey("adding same user again should success", func() { _, err = rest.AddChannelParticipant(ch.Id, ses.ClientId, participant.Id) So(err, ShouldBeNil) }) _, err = rest.DeleteChannelParticipant(ch.Id, ses.ClientId, participant.Id) So(err, ShouldBeNil) Convey("removing same user again should success", func() { _, err = rest.DeleteChannelParticipant(ch.Id, ses.ClientId, participant.Id) So(err, ShouldBeNil) }) }) Convey("while removing users from group channels", func() { ownerAccount, _, groupName := models.CreateRandomGroupDataWithChecks() participant := models.NewAccount() participant.OldId = AccountOldId.Hex() participant, err = rest.CreateAccount(participant) So(err, ShouldBeNil) So(participant, ShouldNotBeNil) participant2 := models.NewAccount() participant2.OldId = AccountOldId.Hex() participant2, err = rest.CreateAccount(participant2) So(err, ShouldBeNil) So(participant2, ShouldNotBeNil) ownerSes, err := modelhelper.FetchOrCreateSession(ownerAccount.Nick, groupName) So(err, ShouldBeNil) ses, err := modelhelper.FetchOrCreateSession(participant.Nick, groupName) So(err, ShouldBeNil) ch, err := rest.CreateChannelByGroupNameAndType(ownerAccount.Id, groupName, models.Channel_TYPE_GROUP, ownerSes.ClientId) So(err, ShouldBeNil) So(ch, ShouldNotBeNil) // ownerSes session is admin's session data _, err = rest.AddChannelParticipant(ch.Id, ownerSes.ClientId, participant.Id) So(err, ShouldBeNil) _, err = rest.AddChannelParticipant(ch.Id, ownerSes.ClientId, participant2.Id) So(err, ShouldBeNil) Convey("owner should be able to remove user from group channel", func() { // ownerSes session is admin's session data _, err = rest.DeleteChannelParticipant(ch.Id, ownerSes.ClientId, participant2.Id) So(err, ShouldBeNil) }) Convey("nonOwner should not be able to remove user from group channel", func() { // ses session is participant's session data _, err = rest.DeleteChannelParticipant(ch.Id, ses.ClientId, participant2.Id) So(err, ShouldNotBeNil) }) }) }) }) }) }
func TestChannelUpdatedCalculateUnreadItemCount(t *testing.T) { r := runner.New("test") if err := r.Init(); err != nil { t.Fatalf("couldnt start bongo %s", err.Error()) } defer r.Close() config.MustRead(r.Conf.Path) groupName := models.RandomGroupName() Convey("while testing unread count", t, func() { Convey("channel should be set", func() { cue := &channelUpdatedEvent{} unreadCount, err := cue.calculateUnreadItemCount() So(err, ShouldNotBeNil) So(err, ShouldEqual, models.ErrChannelIsNotSet) So(unreadCount, ShouldEqual, 0) }) Convey("unread count for group channel can not be calculated", func() { c := models.NewChannel() c.TypeConstant = models.Channel_TYPE_GROUP cue := &channelUpdatedEvent{ Channel: c, } unreadCount, err := cue.calculateUnreadItemCount() So(err, ShouldNotBeNil) So(err.Error(), ShouldContainSubstring, "not supported channel type for unread count calculation") So(unreadCount, ShouldEqual, 0) }) Convey("unread count for following feed channel can not be calculated", func() { c := models.NewChannel() c.TypeConstant = models.Channel_TYPE_FOLLOWINGFEED cue := &channelUpdatedEvent{ Channel: c, } unreadCount, err := cue.calculateUnreadItemCount() So(err, ShouldNotBeNil) So(err.Error(), ShouldContainSubstring, "not supported channel type for unread count calculation") So(unreadCount, ShouldEqual, 0) }) Convey("unread count for followers feed channel can not be calculated", func() { c := models.NewChannel() c.TypeConstant = models.Channel_TYPE_FOLLOWERS cue := &channelUpdatedEvent{ Channel: c, } unreadCount, err := cue.calculateUnreadItemCount() So(err, ShouldNotBeNil) So(err.Error(), ShouldContainSubstring, "not supported channel type for unread count calculation") So(unreadCount, ShouldEqual, 0) }) Convey("unread count for default channel can not be calculated", func() { c := models.NewChannel() c.TypeConstant = models.Channel_TYPE_DEFAULT cue := &channelUpdatedEvent{ Channel: c, } unreadCount, err := cue.calculateUnreadItemCount() So(err, ShouldNotBeNil) So(err.Error(), ShouldContainSubstring, "not supported channel type for unread count calculation") So(unreadCount, ShouldEqual, 0) }) Convey("channel participant should be set", func() { c := models.NewChannel() c.TypeConstant = models.Channel_TYPE_TOPIC cue := &channelUpdatedEvent{ Channel: c, } unreadCount, err := cue.calculateUnreadItemCount() So(err, ShouldNotBeNil) So(err, ShouldEqual, models.ErrChannelParticipantIsNotSet) So(unreadCount, ShouldEqual, 0) }) SkipConvey("pinned message's unread count could be calculated", func() { // create an account account, err := createAccount() So(err, ShouldBeNil) So(account, ShouldNotBeNil) // create their pinned activity channel c, err := models.EnsurePinnedActivityChannel(account.Id, groupName) So(err, ShouldBeNil) So(c, ShouldNotBeNil) // fetch participant cp, err := c.FetchParticipant(account.Id) So(err, ShouldBeNil) So(cp, ShouldNotBeNil) // create message cm := models.NewChannelMessage() cm.AccountId = account.Id // this cahnnel should be group channel cm.InitialChannelId = c.Id cm.Body = "hello all from test" So(cm.Create(), ShouldBeNil) // add message to the list cml, err := c.AddMessage(cm) So(err, ShouldBeNil) So(cml, ShouldNotBeNil) cue := &channelUpdatedEvent{ Channel: c, ChannelParticipant: cp, ParentChannelMessage: cm, } unreadCount, err := cue.calculateUnreadItemCount() So(err, ShouldBeNil) So(unreadCount, ShouldEqual, 0) // add replies to the message addRepliesToMessage(c.Id, account.Id, cm.Id) addRepliesToMessage(c.Id, account.Id, cm.Id) cue = &channelUpdatedEvent{ Channel: c, ChannelParticipant: cp, ParentChannelMessage: cm, } unreadCount, err = cue.calculateUnreadItemCount() So(err, ShouldBeNil) So(unreadCount, ShouldEqual, 2) // glance message cml.Glance() //after glancing the message, unread count should be zero cue = &channelUpdatedEvent{ Channel: c, ChannelParticipant: cp, ParentChannelMessage: cm, } unreadCount, err = cue.calculateUnreadItemCount() So(err, ShouldBeNil) So(unreadCount, ShouldEqual, 0) }) Convey("private message's unread count could be calculated", func() { // create an account account, err := createAccount() So(err, ShouldBeNil) So(account, ShouldNotBeNil) // create private message channel c := models.NewPrivateMessageChannel(account.Id, groupName) So(c.Create(), ShouldBeNil) // add participant into channel cp, err := c.AddParticipant(account.Id) So(err, ShouldBeNil) So(cp, ShouldNotBeNil) // create message cm := models.NewChannelMessage() cm.AccountId = account.Id cm.InitialChannelId = c.Id cm.Body = "hello all from test" So(cm.Create(), ShouldBeNil) // add message to the list // but message is already added into channel cml, err := c.EnsureMessage(cm, false) So(err, ShouldBeNil) So(cml, ShouldNotBeNil) cue := &channelUpdatedEvent{ Channel: c, ChannelParticipant: cp, ParentChannelMessage: cm, } // calculate unread count unreadCount, err := cue.calculateUnreadItemCount() So(err, ShouldBeNil) So(unreadCount, ShouldEqual, 1) cp.LastSeenAt = time.Now().UTC() So(cp.Update(), ShouldBeNil) // calculate unread count unreadCount, err = cue.calculateUnreadItemCount() So(err, ShouldBeNil) So(unreadCount, ShouldEqual, 0) }) Convey("collaboration channel's unread count could be calculated", func() { // create an account account, err := createAccount() So(err, ShouldBeNil) So(account, ShouldNotBeNil) // create private message channel c := models.NewCollaborationChannel(account.Id, groupName) So(c.Create(), ShouldBeNil) // add participant into channel cp, err := c.AddParticipant(account.Id) So(err, ShouldBeNil) So(cp, ShouldNotBeNil) // create message cm := models.NewChannelMessage() cm.AccountId = account.Id cm.InitialChannelId = c.Id cm.Body = "hello all from test" So(cm.Create(), ShouldBeNil) // add message to the list cml, err := c.EnsureMessage(cm, false) So(err, ShouldBeNil) So(cml, ShouldNotBeNil) cue := &channelUpdatedEvent{ Channel: c, ChannelParticipant: cp, ParentChannelMessage: cm, } // calculate unread count unreadCount, err := cue.calculateUnreadItemCount() So(err, ShouldBeNil) So(unreadCount, ShouldEqual, 1) cp.LastSeenAt = time.Now().UTC() So(cp.Update(), ShouldBeNil) // calculate unread count unreadCount, err = cue.calculateUnreadItemCount() So(err, ShouldBeNil) So(unreadCount, ShouldEqual, 0) }) Convey("topic channel's unread count could be calculated", func() { // create an account account, err := createAccount() So(err, ShouldBeNil) So(account, ShouldNotBeNil) // create private message channel c, err := createTypedChannel(account.Id, groupName, models.Channel_TYPE_TOPIC) So(err, ShouldBeNil) So(c.Create(), ShouldBeNil) // add participant into channel cp, err := c.AddParticipant(account.Id) So(err, ShouldBeNil) So(cp, ShouldNotBeNil) // create message cm := models.NewChannelMessage() cm.AccountId = account.Id cm.InitialChannelId = c.Id cm.Body = "hello all from test" So(cm.Create(), ShouldBeNil) // add message to the list cml, err := c.AddMessage(cm) So(err, ShouldBeNil) So(cml, ShouldNotBeNil) cue := &channelUpdatedEvent{ Channel: c, ChannelParticipant: cp, ParentChannelMessage: cm, } // calculate unread count unreadCount, err := cue.calculateUnreadItemCount() So(err, ShouldBeNil) So(unreadCount, ShouldEqual, 1) cp.LastSeenAt = time.Now().UTC() So(cp.Update(), ShouldBeNil) // calculate unread count unreadCount, err = cue.calculateUnreadItemCount() So(err, ShouldBeNil) So(unreadCount, ShouldEqual, 0) }) Convey("announcement channel's unread count could be calculated", func() { // create an account account, err := createAccount() So(err, ShouldBeNil) So(account, ShouldNotBeNil) // create private message channel c, err := createTypedChannel(account.Id, groupName, models.Channel_TYPE_ANNOUNCEMENT) So(err, ShouldBeNil) So(c.Create(), ShouldBeNil) // add participant into channel cp, err := c.AddParticipant(account.Id) So(err, ShouldBeNil) So(cp, ShouldNotBeNil) // create message cm := models.NewChannelMessage() cm.AccountId = account.Id cm.InitialChannelId = c.Id cm.Body = "hello all from test" So(cm.Create(), ShouldBeNil) // add message to the list cml, err := c.EnsureMessage(cm, false) So(err, ShouldBeNil) So(cml, ShouldNotBeNil) cue := &channelUpdatedEvent{ Channel: c, ChannelParticipant: cp, ParentChannelMessage: cm, } // calculate unread count unreadCount, err := cue.calculateUnreadItemCount() So(err, ShouldBeNil) So(unreadCount, ShouldEqual, 1) cp.LastSeenAt = time.Now().UTC() So(cp.Update(), ShouldBeNil) // calculate unread count unreadCount, err = cue.calculateUnreadItemCount() So(err, ShouldBeNil) So(unreadCount, ShouldEqual, 0) }) }) }
func Delete(u *url.URL, h http.Header, _ interface{}, c *models.Context) (int, http.Header, interface{}, error) { if !c.IsLoggedIn() { return response.NewAccessDenied(models.ErrNotLoggedIn) } id, err := request.GetURIInt64(u, "id") if err != nil { return response.NewBadRequest(err) } if id == 0 { return response.NewBadRequest(models.ErrMessageIdIsNotSet) } cm := models.NewChannelMessage() cm.Id = id if err := cm.ById(id); err != nil { if err == bongo.RecordNotFound { return response.NewNotFound() } return response.NewBadRequest(err) } // Add isAdmin checking // is user is admin, then can delete another user's message if cm.AccountId != c.Client.Account.Id { isAdmin, err := modelhelper.IsAdmin(c.Client.Account.Nick, c.GroupName) if err != nil { return response.NewBadRequest(err) } if !isAdmin { return response.NewBadRequest(models.ErrAccessDenied) } } // if this is a reply no need to delete it's replies if cm.TypeConstant == models.ChannelMessage_TYPE_REPLY { mr := models.NewMessageReply() mr.ReplyId = id parent, err := mr.FetchParent() if err != nil { return response.NewBadRequest(err) } // delete the message here err = cm.DeleteMessageAndDependencies(false) // then invalidate the cache of the parent message bongo.B.AddToCache(parent) } else { err = cm.DeleteMessageAndDependencies(true) } if err != nil { return response.NewBadRequest(err) } // yes it is deleted but not removed completely from our system return response.NewDeleted() }