[MM-23017] Add check for groups to SendNotifications (#14039)
* MM-23017 Check group mentions as part of notification logic * Add nil groups to existing test cases * MM-23017 Add tests for insertGroupMention and addGroupMention * MM-23017 Add tests for getExplicitMentions that have groups * Add tests for group store GetMemberUsersNotInChannel * MM-23017 Add tests for AllowGroupMentions * MM-23017 Fix error message name * MM-23017 Swap Checks to Name * MM-23017 Code review fixes * Rename var and fix allowGroupMentions test * MM-23017 Use GetMemberUsersInTeam inside of insertGroupMentions * MM-23017 use group mentions permission * Actually call GetMemberUsersInTeam * Remove unnecessary new line * Uncomment filter allow reference * MM-23017 Fix group channel notifications * Update store layer * MM-23017 Improve test coverage for group channels * Trigger CI * Trigger CI
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
29fae242e1
Коммит
e88ba85d60
@@ -36,6 +36,22 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
|
|||||||
close(cmnchan)
|
close(cmnchan)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
var gchan chan store.StoreResult
|
||||||
|
if a.allowGroupMentions(post) {
|
||||||
|
gchan = make(chan store.StoreResult, 1)
|
||||||
|
go func() {
|
||||||
|
groups, err := a.Srv().Store.Group().GetGroups(0, 0, model.GroupSearchOpts{FilterAllowReference: true})
|
||||||
|
groupsMap := make(map[string]*model.Group)
|
||||||
|
if err == nil {
|
||||||
|
for _, group := range groups {
|
||||||
|
groupsMap[group.Name] = group
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gchan <- store.StoreResult{Data: groupsMap, Err: err}
|
||||||
|
close(gchan)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
var fchan chan store.StoreResult
|
var fchan chan store.StoreResult
|
||||||
if len(post.FileIds) != 0 {
|
if len(post.FileIds) != 0 {
|
||||||
fchan = make(chan store.StoreResult, 1)
|
fchan = make(chan store.StoreResult, 1)
|
||||||
@@ -58,6 +74,15 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
|
|||||||
}
|
}
|
||||||
channelMemberNotifyPropsMap := result.Data.(map[string]model.StringMap)
|
channelMemberNotifyPropsMap := result.Data.(map[string]model.StringMap)
|
||||||
|
|
||||||
|
groups := make(map[string]*model.Group)
|
||||||
|
if gchan != nil {
|
||||||
|
result = <-gchan
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
groups = result.Data.(map[string]*model.Group)
|
||||||
|
}
|
||||||
|
|
||||||
mentions := &ExplicitMentions{}
|
mentions := &ExplicitMentions{}
|
||||||
allActivityPushUserIds := []string{}
|
allActivityPushUserIds := []string{}
|
||||||
|
|
||||||
@@ -76,7 +101,7 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
|
|||||||
allowChannelMentions := a.allowChannelMentions(post, len(profileMap))
|
allowChannelMentions := a.allowChannelMentions(post, len(profileMap))
|
||||||
keywords := a.getMentionKeywordsInChannel(profileMap, allowChannelMentions, channelMemberNotifyPropsMap)
|
keywords := a.getMentionKeywordsInChannel(profileMap, allowChannelMentions, channelMemberNotifyPropsMap)
|
||||||
|
|
||||||
mentions = getExplicitMentions(post, keywords)
|
mentions = getExplicitMentions(post, keywords, groups)
|
||||||
|
|
||||||
// Add an implicit mention when a user is added to a channel
|
// Add an implicit mention when a user is added to a channel
|
||||||
// even if the user has set 'username mentions' to false in account settings.
|
// even if the user has set 'username mentions' to false in account settings.
|
||||||
@@ -87,6 +112,18 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Iterate through all groups that were mentioned and insert group members into the list of mentions or potential mentions
|
||||||
|
for _, group := range mentions.GroupMentions {
|
||||||
|
anyUsersMentionedByGroup, err := a.insertGroupMentions(group, channel, profileMap, mentions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !anyUsersMentionedByGroup {
|
||||||
|
a.sendNoUsersNotifiedByGroupInChannel(sender, post, channel, group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get users that have comment thread mentions enabled
|
// get users that have comment thread mentions enabled
|
||||||
if len(post.RootId) > 0 && parentPostList != nil {
|
if len(post.RootId) > 0 && parentPostList != nil {
|
||||||
for _, threadPost := range parentPostList.Posts {
|
for _, threadPost := range parentPostList.Posts {
|
||||||
@@ -373,6 +410,18 @@ func (a *App) userAllowsEmail(user *model.User, channelMemberNotificationProps m
|
|||||||
return userAllowsEmails && emailNotificationsAllowedForStatus && user.DeleteAt == 0 && !autoResponderRelated
|
return userAllowsEmails && emailNotificationsAllowedForStatus && user.DeleteAt == 0 && !autoResponderRelated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) sendNoUsersNotifiedByGroupInChannel(sender *model.User, post *model.Post, channel *model.Channel, group *model.Group) {
|
||||||
|
T := utils.GetUserTranslations(sender.Locale)
|
||||||
|
ephemeralPost := &model.Post{
|
||||||
|
UserId: sender.Id,
|
||||||
|
RootId: post.RootId,
|
||||||
|
ParentId: post.ParentId,
|
||||||
|
ChannelId: channel.Id,
|
||||||
|
Message: T("api.post.check_for_out_of_channel_group_users.message.none", model.StringInterface{"GroupDisplayName": group.DisplayName}),
|
||||||
|
}
|
||||||
|
a.SendEphemeralPost(post.UserId, ephemeralPost)
|
||||||
|
}
|
||||||
|
|
||||||
// sendOutOfChannelMentions sends an ephemeral post to the sender of a post if any of the given potential mentions
|
// sendOutOfChannelMentions sends an ephemeral post to the sender of a post if any of the given potential mentions
|
||||||
// are outside of the post's channel. Returns whether or not an ephemeral post was sent.
|
// are outside of the post's channel. Returns whether or not an ephemeral post was sent.
|
||||||
func (a *App) sendOutOfChannelMentions(sender *model.User, post *model.Post, channel *model.Channel, potentialMentions []string) (bool, error) {
|
func (a *App) sendOutOfChannelMentions(sender *model.User, post *model.Post, channel *model.Channel, potentialMentions []string) (bool, error) {
|
||||||
@@ -520,6 +569,9 @@ type ExplicitMentions struct {
|
|||||||
// Mentions contains the ID of each user that was mentioned and how they were mentioned.
|
// Mentions contains the ID of each user that was mentioned and how they were mentioned.
|
||||||
Mentions map[string]MentionType
|
Mentions map[string]MentionType
|
||||||
|
|
||||||
|
// Contains a map of groups that were mentioned
|
||||||
|
GroupMentions map[string]*model.Group
|
||||||
|
|
||||||
// OtherPotentialMentions contains a list of strings that looked like mentions, but didn't have
|
// OtherPotentialMentions contains a list of strings that looked like mentions, but didn't have
|
||||||
// a corresponding keyword.
|
// a corresponding keyword.
|
||||||
OtherPotentialMentions []string
|
OtherPotentialMentions []string
|
||||||
@@ -556,6 +608,9 @@ const (
|
|||||||
|
|
||||||
// The post contains an at-mention for the user
|
// The post contains an at-mention for the user
|
||||||
KeywordMention
|
KeywordMention
|
||||||
|
|
||||||
|
// The post contains a group mention for the user
|
||||||
|
GroupMention
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *ExplicitMentions) addMention(userId string, mentionType MentionType) {
|
func (m *ExplicitMentions) addMention(userId string, mentionType MentionType) {
|
||||||
@@ -570,6 +625,32 @@ func (m *ExplicitMentions) addMention(userId string, mentionType MentionType) {
|
|||||||
m.Mentions[userId] = mentionType
|
m.Mentions[userId] = mentionType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *ExplicitMentions) addGroupMention(word string, groups map[string]*model.Group) bool {
|
||||||
|
if strings.HasPrefix(word, "@") {
|
||||||
|
word = word[1:]
|
||||||
|
} else {
|
||||||
|
// Only allow group mentions when mentioned directly with @group-name
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
group, groupFound := groups[word]
|
||||||
|
if !groupFound {
|
||||||
|
group = groups[strings.ToLower(word)]
|
||||||
|
}
|
||||||
|
|
||||||
|
if group == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.GroupMentions == nil {
|
||||||
|
m.GroupMentions = make(map[string]*model.Group)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.GroupMentions[group.Name] = group
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (m *ExplicitMentions) addMentions(userIds []string, mentionType MentionType) {
|
func (m *ExplicitMentions) addMentions(userIds []string, mentionType MentionType) {
|
||||||
for _, userId := range userIds {
|
for _, userId := range userIds {
|
||||||
m.addMention(userId, mentionType)
|
m.addMention(userId, mentionType)
|
||||||
@@ -582,7 +663,7 @@ func (m *ExplicitMentions) removeMention(userId string) {
|
|||||||
|
|
||||||
// Given a message and a map mapping mention keywords to the users who use them, returns a map of mentioned
|
// Given a message and a map mapping mention keywords to the users who use them, returns a map of mentioned
|
||||||
// users and a slice of potential mention users not in the channel and whether or not @here was mentioned.
|
// users and a slice of potential mention users not in the channel and whether or not @here was mentioned.
|
||||||
func getExplicitMentions(post *model.Post, keywords map[string][]string) *ExplicitMentions {
|
func getExplicitMentions(post *model.Post, keywords map[string][]string, groups map[string]*model.Group) *ExplicitMentions {
|
||||||
ret := &ExplicitMentions{}
|
ret := &ExplicitMentions{}
|
||||||
|
|
||||||
buf := ""
|
buf := ""
|
||||||
@@ -591,7 +672,7 @@ func getExplicitMentions(post *model.Post, keywords map[string][]string) *Explic
|
|||||||
markdown.Inspect(message, func(node interface{}) bool {
|
markdown.Inspect(message, func(node interface{}) bool {
|
||||||
text, ok := node.(*markdown.Text)
|
text, ok := node.(*markdown.Text)
|
||||||
if !ok {
|
if !ok {
|
||||||
ret.processText(buf, keywords)
|
ret.processText(buf, keywords, groups)
|
||||||
buf = ""
|
buf = ""
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -599,7 +680,7 @@ func getExplicitMentions(post *model.Post, keywords map[string][]string) *Explic
|
|||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ret.processText(buf, keywords)
|
ret.processText(buf, keywords, groups)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
@@ -639,6 +720,19 @@ func (a *App) allowChannelMentions(post *model.Post, numProfiles int) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allowGroupMentions returns whether or not the group mentions are allowed for the given post.
|
||||||
|
func (a *App) allowGroupMentions(post *model.Post) bool {
|
||||||
|
if !a.HasPermissionToChannel(post.UserId, post.ChannelId, model.PERMISSION_USE_GROUP_MENTIONS) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if post.Type == model.POST_HEADER_CHANGE || post.Type == model.POST_PURPOSE_CHANGE {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Given a map of user IDs to profiles, returns a list of mention
|
// Given a map of user IDs to profiles, returns a list of mention
|
||||||
// keywords for all users in the channel.
|
// keywords for all users in the channel.
|
||||||
func (a *App) getMentionKeywordsInChannel(profiles map[string]*model.User, allowChannelMentions bool, channelMemberNotifyPropsMap map[string]model.StringMap) map[string][]string {
|
func (a *App) getMentionKeywordsInChannel(profiles map[string]*model.User, allowChannelMentions bool, channelMemberNotifyPropsMap map[string]model.StringMap) map[string][]string {
|
||||||
@@ -657,6 +751,49 @@ func (a *App) getMentionKeywordsInChannel(profiles map[string]*model.User, allow
|
|||||||
return keywords
|
return keywords
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// insertGroupMentions adds group members in the channel to Mentions, adds group members not in the channel to OtherPotentialMentions
|
||||||
|
// returns false if no group members present in the team that the channel belongs to
|
||||||
|
func (a *App) insertGroupMentions(group *model.Group, channel *model.Channel, profileMap map[string]*model.User, mentions *ExplicitMentions) (bool, *model.AppError) {
|
||||||
|
var err *model.AppError
|
||||||
|
var groupMembers []*model.User
|
||||||
|
outOfChannelGroupMembers := []*model.User{}
|
||||||
|
isGroupOrDirect := channel.IsGroupOrDirect()
|
||||||
|
|
||||||
|
if isGroupOrDirect {
|
||||||
|
groupMembers, err = a.Srv().Store.Group().GetMemberUsers(group.Id)
|
||||||
|
} else {
|
||||||
|
groupMembers, err = a.Srv().Store.Group().GetMemberUsersInTeam(group.Id, channel.TeamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if mentions.Mentions == nil {
|
||||||
|
mentions.Mentions = make(map[string]MentionType)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, member := range groupMembers {
|
||||||
|
if _, ok := profileMap[member.Id]; ok {
|
||||||
|
mentions.Mentions[member.Id] = GroupMention
|
||||||
|
} else {
|
||||||
|
outOfChannelGroupMembers = append(outOfChannelGroupMembers, member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
potentialGroupMembersMentioned := []string{}
|
||||||
|
for _, user := range outOfChannelGroupMembers {
|
||||||
|
potentialGroupMembersMentioned = append(potentialGroupMembersMentioned, user.Username)
|
||||||
|
}
|
||||||
|
if mentions.OtherPotentialMentions == nil {
|
||||||
|
mentions.OtherPotentialMentions = potentialGroupMembersMentioned
|
||||||
|
} else {
|
||||||
|
mentions.OtherPotentialMentions = append(mentions.OtherPotentialMentions, potentialGroupMembersMentioned...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return isGroupOrDirect || len(groupMembers) > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// addMentionKeywordsForUser adds the mention keywords for a given user to the given keyword map. Returns the provided keyword map.
|
// addMentionKeywordsForUser adds the mention keywords for a given user to the given keyword map. Returns the provided keyword map.
|
||||||
func addMentionKeywordsForUser(keywords map[string][]string, profile *model.User, channelNotifyProps map[string]string, status *model.Status, allowChannelMentions bool) map[string][]string {
|
func addMentionKeywordsForUser(keywords map[string][]string, profile *model.User, channelNotifyProps map[string]string, status *model.Status, allowChannelMentions bool) map[string][]string {
|
||||||
userMention := "@" + strings.ToLower(profile.Username)
|
userMention := "@" + strings.ToLower(profile.Username)
|
||||||
@@ -742,7 +879,7 @@ func (n *PostNotification) GetSenderName(userNameFormat string, overridesAllowed
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checkForMention checks if there is a mention to a specific user or to the keywords here / channel / all
|
// checkForMention checks if there is a mention to a specific user or to the keywords here / channel / all
|
||||||
func (m *ExplicitMentions) checkForMention(word string, keywords map[string][]string) bool {
|
func (m *ExplicitMentions) checkForMention(word string, keywords map[string][]string, groups map[string]*model.Group) bool {
|
||||||
var mentionType MentionType
|
var mentionType MentionType
|
||||||
|
|
||||||
switch strings.ToLower(word) {
|
switch strings.ToLower(word) {
|
||||||
@@ -759,6 +896,8 @@ func (m *ExplicitMentions) checkForMention(word string, keywords map[string][]st
|
|||||||
mentionType = KeywordMention
|
mentionType = KeywordMention
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.addGroupMention(word, groups)
|
||||||
|
|
||||||
if ids, match := keywords[strings.ToLower(word)]; match {
|
if ids, match := keywords[strings.ToLower(word)]; match {
|
||||||
m.addMentions(ids, mentionType)
|
m.addMentions(ids, mentionType)
|
||||||
return true
|
return true
|
||||||
@@ -795,7 +934,7 @@ func isKeywordMultibyte(keywords map[string][]string, word string) ([]string, bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Processes text to filter mentioned users and other potential mentions
|
// Processes text to filter mentioned users and other potential mentions
|
||||||
func (m *ExplicitMentions) processText(text string, keywords map[string][]string) {
|
func (m *ExplicitMentions) processText(text string, keywords map[string][]string, groups map[string]*model.Group) {
|
||||||
systemMentions := map[string]bool{"@here": true, "@channel": true, "@all": true}
|
systemMentions := map[string]bool{"@here": true, "@channel": true, "@all": true}
|
||||||
|
|
||||||
for _, word := range strings.FieldsFunc(text, func(c rune) bool {
|
for _, word := range strings.FieldsFunc(text, func(c rune) bool {
|
||||||
@@ -809,16 +948,17 @@ func (m *ExplicitMentions) processText(text string, keywords map[string][]string
|
|||||||
|
|
||||||
word = strings.TrimLeft(word, ":.-_")
|
word = strings.TrimLeft(word, ":.-_")
|
||||||
|
|
||||||
if m.checkForMention(word, keywords) {
|
if m.checkForMention(word, keywords, groups) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
foundWithoutSuffix := false
|
foundWithoutSuffix := false
|
||||||
wordWithoutSuffix := word
|
wordWithoutSuffix := word
|
||||||
|
|
||||||
for len(wordWithoutSuffix) > 0 && strings.LastIndexAny(wordWithoutSuffix, ".-:_") == (len(wordWithoutSuffix)-1) {
|
for len(wordWithoutSuffix) > 0 && strings.LastIndexAny(wordWithoutSuffix, ".-:_") == (len(wordWithoutSuffix)-1) {
|
||||||
wordWithoutSuffix = wordWithoutSuffix[0 : len(wordWithoutSuffix)-1]
|
wordWithoutSuffix = wordWithoutSuffix[0 : len(wordWithoutSuffix)-1]
|
||||||
|
|
||||||
if m.checkForMention(wordWithoutSuffix, keywords) {
|
if m.checkForMention(wordWithoutSuffix, keywords, groups) {
|
||||||
foundWithoutSuffix = true
|
foundWithoutSuffix = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -844,7 +984,7 @@ func (m *ExplicitMentions) processText(text string, keywords map[string][]string
|
|||||||
})
|
})
|
||||||
|
|
||||||
for _, splitWord := range splitWords {
|
for _, splitWord := range splitWords {
|
||||||
if m.checkForMention(splitWord, keywords) {
|
if m.checkForMention(splitWord, keywords, groups) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, ok := systemMentions[splitWord]; !ok && strings.HasPrefix(splitWord, "@") {
|
if _, ok := systemMentions[splitWord]; !ok && strings.HasPrefix(splitWord, "@") {
|
||||||
|
|||||||
@@ -304,6 +304,7 @@ func TestGetExplicitMentions(t *testing.T) {
|
|||||||
Message string
|
Message string
|
||||||
Attachments []*model.SlackAttachment
|
Attachments []*model.SlackAttachment
|
||||||
Keywords map[string][]string
|
Keywords map[string][]string
|
||||||
|
Groups map[string]*model.Group
|
||||||
Expected *ExplicitMentions
|
Expected *ExplicitMentions
|
||||||
}{
|
}{
|
||||||
"Nobody": {
|
"Nobody": {
|
||||||
@@ -799,6 +800,54 @@ func TestGetExplicitMentions(t *testing.T) {
|
|||||||
OtherPotentialMentions: []string{"other-one", "other", "other-two"},
|
OtherPotentialMentions: []string{"other-one", "other", "other-two"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"No groups": {
|
||||||
|
Message: "@nothing",
|
||||||
|
Groups: map[string]*model.Group{},
|
||||||
|
Expected: &ExplicitMentions{
|
||||||
|
Mentions: nil,
|
||||||
|
OtherPotentialMentions: []string{"nothing"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"No matching groups": {
|
||||||
|
Message: "@nothing",
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}},
|
||||||
|
Expected: &ExplicitMentions{
|
||||||
|
Mentions: nil,
|
||||||
|
GroupMentions: nil,
|
||||||
|
OtherPotentialMentions: []string{"nothing"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"matching group with no @": {
|
||||||
|
Message: "engineering",
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}},
|
||||||
|
Expected: &ExplicitMentions{
|
||||||
|
Mentions: nil,
|
||||||
|
GroupMentions: nil,
|
||||||
|
OtherPotentialMentions: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"matching group with preceeding @": {
|
||||||
|
Message: "@engineering",
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}},
|
||||||
|
Expected: &ExplicitMentions{
|
||||||
|
Mentions: nil,
|
||||||
|
GroupMentions: map[string]*model.Group{
|
||||||
|
"engineering": {Name: "engineering"},
|
||||||
|
},
|
||||||
|
OtherPotentialMentions: []string{"engineering"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"matching upper case group with preceeding @": {
|
||||||
|
Message: "@Engineering",
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}},
|
||||||
|
Expected: &ExplicitMentions{
|
||||||
|
Mentions: nil,
|
||||||
|
GroupMentions: map[string]*model.Group{
|
||||||
|
"engineering": {Name: "engineering"},
|
||||||
|
},
|
||||||
|
OtherPotentialMentions: []string{"Engineering"},
|
||||||
|
},
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
post := &model.Post{
|
post := &model.Post{
|
||||||
@@ -808,7 +857,7 @@ func TestGetExplicitMentions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
m := getExplicitMentions(post, tc.Keywords)
|
m := getExplicitMentions(post, tc.Keywords, tc.Groups)
|
||||||
|
|
||||||
assert.EqualValues(t, tc.Expected, m)
|
assert.EqualValues(t, tc.Expected, m)
|
||||||
})
|
})
|
||||||
@@ -861,7 +910,7 @@ func TestGetExplicitMentionsAtHere(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for message, shouldMention := range cases {
|
for message, shouldMention := range cases {
|
||||||
post := &model.Post{Message: message}
|
post := &model.Post{Message: message}
|
||||||
m := getExplicitMentions(post, nil)
|
m := getExplicitMentions(post, nil, nil)
|
||||||
require.False(t, m.HereMentioned && !shouldMention, "shouldn't have mentioned @here with \"%v\"")
|
require.False(t, m.HereMentioned && !shouldMention, "shouldn't have mentioned @here with \"%v\"")
|
||||||
require.False(t, !m.HereMentioned && shouldMention, "should've mentioned @here with \"%v\"")
|
require.False(t, !m.HereMentioned && shouldMention, "should've mentioned @here with \"%v\"")
|
||||||
}
|
}
|
||||||
@@ -869,7 +918,7 @@ func TestGetExplicitMentionsAtHere(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Mention @here and someone", func(t *testing.T) {
|
t.Run("Mention @here and someone", func(t *testing.T) {
|
||||||
id := model.NewId()
|
id := model.NewId()
|
||||||
m := getExplicitMentions(&model.Post{Message: "@here @user @potential"}, map[string][]string{"@user": {id}})
|
m := getExplicitMentions(&model.Post{Message: "@here @user @potential"}, map[string][]string{"@user": {id}}, nil)
|
||||||
require.True(t, m.HereMentioned, "should've mentioned @here with \"@here @user\"")
|
require.True(t, m.HereMentioned, "should've mentioned @here with \"@here @user\"")
|
||||||
require.Len(t, m.Mentions, 1)
|
require.Len(t, m.Mentions, 1)
|
||||||
require.Equal(t, KeywordMention, m.Mentions[id], "should've mentioned @user with \"@here @user\"")
|
require.Equal(t, KeywordMention, m.Mentions[id], "should've mentioned @user with \"@here @user\"")
|
||||||
@@ -879,11 +928,10 @@ func TestGetExplicitMentionsAtHere(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("Username ending with period", func(t *testing.T) {
|
t.Run("Username ending with period", func(t *testing.T) {
|
||||||
id := model.NewId()
|
id := model.NewId()
|
||||||
m := getExplicitMentions(&model.Post{Message: "@potential. test"}, map[string][]string{"@user": {id}})
|
m := getExplicitMentions(&model.Post{Message: "@potential. test"}, map[string][]string{"@user": {id}}, nil)
|
||||||
require.Equal(t, len(m.OtherPotentialMentions), 1, "should've potential mentions for @potential")
|
require.Equal(t, len(m.OtherPotentialMentions), 1, "should've potential mentions for @potential")
|
||||||
assert.Equal(t, "potential", m.OtherPotentialMentions[0])
|
assert.Equal(t, "potential", m.OtherPotentialMentions[0])
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAllowChannelMentions(t *testing.T) {
|
func TestAllowChannelMentions(t *testing.T) {
|
||||||
@@ -924,6 +972,41 @@ func TestAllowChannelMentions(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAllowGroupMentions(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
post := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id}
|
||||||
|
|
||||||
|
t.Run("should return true for a regular post with few channel members", func(t *testing.T) {
|
||||||
|
allowGroupMentions := th.App.allowGroupMentions(post)
|
||||||
|
assert.True(t, allowGroupMentions)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should return false for a channel header post", func(t *testing.T) {
|
||||||
|
headerChangePost := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id, Type: model.POST_HEADER_CHANGE}
|
||||||
|
allowGroupMentions := th.App.allowGroupMentions(headerChangePost)
|
||||||
|
assert.False(t, allowGroupMentions)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should return false for a channel purpose post", func(t *testing.T) {
|
||||||
|
purposeChangePost := &model.Post{ChannelId: th.BasicChannel.Id, UserId: th.BasicUser.Id, Type: model.POST_PURPOSE_CHANGE}
|
||||||
|
allowGroupMentions := th.App.allowGroupMentions(purposeChangePost)
|
||||||
|
assert.False(t, allowGroupMentions)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should return false for a post where the post user does not have USE_GROUP_MENTIONS permission", func(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
th.AddPermissionToRole(model.PERMISSION_USE_GROUP_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID)
|
||||||
|
th.AddPermissionToRole(model.PERMISSION_USE_GROUP_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID)
|
||||||
|
}()
|
||||||
|
th.RemovePermissionFromRole(model.PERMISSION_USE_GROUP_MENTIONS.Id, model.CHANNEL_USER_ROLE_ID)
|
||||||
|
th.RemovePermissionFromRole(model.PERMISSION_USE_GROUP_MENTIONS.Id, model.CHANNEL_ADMIN_ROLE_ID)
|
||||||
|
allowGroupMentions := th.App.allowGroupMentions(post)
|
||||||
|
assert.False(t, allowGroupMentions)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetMentionKeywords(t *testing.T) {
|
func TestGetMentionKeywords(t *testing.T) {
|
||||||
th := Setup(t)
|
th := Setup(t)
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
@@ -1669,6 +1752,7 @@ func TestIsKeywordMultibyte(t *testing.T) {
|
|||||||
Message string
|
Message string
|
||||||
Attachments []*model.SlackAttachment
|
Attachments []*model.SlackAttachment
|
||||||
Keywords map[string][]string
|
Keywords map[string][]string
|
||||||
|
Groups map[string]*model.Group
|
||||||
Expected *ExplicitMentions
|
Expected *ExplicitMentions
|
||||||
}{
|
}{
|
||||||
"MultibyteCharacter": {
|
"MultibyteCharacter": {
|
||||||
@@ -1760,10 +1844,7 @@ func TestIsKeywordMultibyte(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
m := getExplicitMentions(post, tc.Keywords)
|
m := getExplicitMentions(post, tc.Keywords, tc.Groups)
|
||||||
// if tc.Expected.MentionedUserIds == nil {
|
|
||||||
// tc.Expected.MentionedUserIds = make(map[string]bool)
|
|
||||||
// }
|
|
||||||
assert.EqualValues(t, tc.Expected, m)
|
assert.EqualValues(t, tc.Expected, m)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1913,23 +1994,71 @@ func TestCheckForMentionUsers(t *testing.T) {
|
|||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
|
||||||
e := &ExplicitMentions{}
|
e := &ExplicitMentions{}
|
||||||
e.checkForMention(tc.Word, tc.Keywords)
|
e.checkForMention(tc.Word, tc.Keywords, nil)
|
||||||
|
|
||||||
assert.EqualValues(t, tc.Expected, e)
|
assert.EqualValues(t, tc.Expected, e)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAddGroupMention(t *testing.T) {
|
||||||
|
for name, tc := range map[string]struct {
|
||||||
|
Word string
|
||||||
|
Groups map[string]*model.Group
|
||||||
|
Expected bool
|
||||||
|
}{
|
||||||
|
"No groups": {
|
||||||
|
Word: "nothing",
|
||||||
|
Groups: map[string]*model.Group{},
|
||||||
|
Expected: false,
|
||||||
|
},
|
||||||
|
"No matching groups": {
|
||||||
|
Word: "nothing",
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
|
Expected: false,
|
||||||
|
},
|
||||||
|
"matching group with no @": {
|
||||||
|
Word: "engineering",
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
|
Expected: false,
|
||||||
|
},
|
||||||
|
"matching group with preceeding @": {
|
||||||
|
Word: "@engineering",
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
|
Expected: true,
|
||||||
|
},
|
||||||
|
"matching upper case group with preceeding @": {
|
||||||
|
Word: "@Engineering",
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
|
Expected: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
e := &ExplicitMentions{}
|
||||||
|
groupFound := e.addGroupMention(tc.Word, tc.Groups)
|
||||||
|
|
||||||
|
if groupFound {
|
||||||
|
require.Equal(t, len(e.GroupMentions), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, tc.Expected, groupFound)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestProcessText(t *testing.T) {
|
func TestProcessText(t *testing.T) {
|
||||||
id1 := model.NewId()
|
id1 := model.NewId()
|
||||||
|
|
||||||
for name, tc := range map[string]struct {
|
for name, tc := range map[string]struct {
|
||||||
Text string
|
Text string
|
||||||
Keywords map[string][]string
|
Keywords map[string][]string
|
||||||
|
Groups map[string]*model.Group
|
||||||
Expected *ExplicitMentions
|
Expected *ExplicitMentions
|
||||||
}{
|
}{
|
||||||
"Mention user in text": {
|
"Mention user in text": {
|
||||||
Text: "hello user @user1",
|
Text: "hello user @user1",
|
||||||
Keywords: map[string][]string{"@user1": {id1}},
|
Keywords: map[string][]string{"@user1": {id1}},
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
Expected: &ExplicitMentions{
|
Expected: &ExplicitMentions{
|
||||||
Mentions: map[string]MentionType{
|
Mentions: map[string]MentionType{
|
||||||
id1: KeywordMention,
|
id1: KeywordMention,
|
||||||
@@ -1939,6 +2068,7 @@ func TestProcessText(t *testing.T) {
|
|||||||
"Mention user after ending a sentence with full stop": {
|
"Mention user after ending a sentence with full stop": {
|
||||||
Text: "hello user.@user1",
|
Text: "hello user.@user1",
|
||||||
Keywords: map[string][]string{"@user1": {id1}},
|
Keywords: map[string][]string{"@user1": {id1}},
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
Expected: &ExplicitMentions{
|
Expected: &ExplicitMentions{
|
||||||
Mentions: map[string]MentionType{
|
Mentions: map[string]MentionType{
|
||||||
id1: KeywordMention,
|
id1: KeywordMention,
|
||||||
@@ -1957,6 +2087,7 @@ func TestProcessText(t *testing.T) {
|
|||||||
"Mention user after colon": {
|
"Mention user after colon": {
|
||||||
Text: "hello user:@user1",
|
Text: "hello user:@user1",
|
||||||
Keywords: map[string][]string{"@user1": {id1}},
|
Keywords: map[string][]string{"@user1": {id1}},
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
Expected: &ExplicitMentions{
|
Expected: &ExplicitMentions{
|
||||||
Mentions: map[string]MentionType{
|
Mentions: map[string]MentionType{
|
||||||
id1: KeywordMention,
|
id1: KeywordMention,
|
||||||
@@ -1966,6 +2097,7 @@ func TestProcessText(t *testing.T) {
|
|||||||
"Mention here after colon": {
|
"Mention here after colon": {
|
||||||
Text: "hello all:@here",
|
Text: "hello all:@here",
|
||||||
Keywords: map[string][]string{},
|
Keywords: map[string][]string{},
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
Expected: &ExplicitMentions{
|
Expected: &ExplicitMentions{
|
||||||
HereMentioned: true,
|
HereMentioned: true,
|
||||||
},
|
},
|
||||||
@@ -1973,6 +2105,7 @@ func TestProcessText(t *testing.T) {
|
|||||||
"Mention all after hyphen": {
|
"Mention all after hyphen": {
|
||||||
Text: "hello all-@all",
|
Text: "hello all-@all",
|
||||||
Keywords: map[string][]string{},
|
Keywords: map[string][]string{},
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
Expected: &ExplicitMentions{
|
Expected: &ExplicitMentions{
|
||||||
AllMentioned: true,
|
AllMentioned: true,
|
||||||
},
|
},
|
||||||
@@ -1980,6 +2113,7 @@ func TestProcessText(t *testing.T) {
|
|||||||
"Mention channel after full stop": {
|
"Mention channel after full stop": {
|
||||||
Text: "hello channel.@channel",
|
Text: "hello channel.@channel",
|
||||||
Keywords: map[string][]string{},
|
Keywords: map[string][]string{},
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
Expected: &ExplicitMentions{
|
Expected: &ExplicitMentions{
|
||||||
ChannelMentioned: true,
|
ChannelMentioned: true,
|
||||||
},
|
},
|
||||||
@@ -1987,6 +2121,7 @@ func TestProcessText(t *testing.T) {
|
|||||||
"Mention other pontential users or system calls": {
|
"Mention other pontential users or system calls": {
|
||||||
Text: "hello @potentialuser and @otherpotentialuser",
|
Text: "hello @potentialuser and @otherpotentialuser",
|
||||||
Keywords: map[string][]string{},
|
Keywords: map[string][]string{},
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
Expected: &ExplicitMentions{
|
Expected: &ExplicitMentions{
|
||||||
OtherPotentialMentions: []string{"potentialuser", "otherpotentialuser"},
|
OtherPotentialMentions: []string{"potentialuser", "otherpotentialuser"},
|
||||||
},
|
},
|
||||||
@@ -1994,6 +2129,7 @@ func TestProcessText(t *testing.T) {
|
|||||||
"Mention a real user and another potential user": {
|
"Mention a real user and another potential user": {
|
||||||
Text: "@user1, you can use @systembot to get help",
|
Text: "@user1, you can use @systembot to get help",
|
||||||
Keywords: map[string][]string{"@user1": {id1}},
|
Keywords: map[string][]string{"@user1": {id1}},
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
Expected: &ExplicitMentions{
|
Expected: &ExplicitMentions{
|
||||||
Mentions: map[string]MentionType{
|
Mentions: map[string]MentionType{
|
||||||
id1: KeywordMention,
|
id1: KeywordMention,
|
||||||
@@ -2001,10 +2137,31 @@ func TestProcessText(t *testing.T) {
|
|||||||
OtherPotentialMentions: []string{"systembot"},
|
OtherPotentialMentions: []string{"systembot"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"Mention a group": {
|
||||||
|
Text: "@engineering",
|
||||||
|
Keywords: map[string][]string{"@user1": {id1}},
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
|
Expected: &ExplicitMentions{
|
||||||
|
GroupMentions: map[string]*model.Group{"engineering": {Name: "engineering"}},
|
||||||
|
OtherPotentialMentions: []string{"engineering"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Mention a real user and another potential user and a group": {
|
||||||
|
Text: "@engineering @user1, you can use @systembot to get help from",
|
||||||
|
Keywords: map[string][]string{"@user1": {id1}},
|
||||||
|
Groups: map[string]*model.Group{"engineering": {Name: "engineering"}, "developers": {Name: "developers"}},
|
||||||
|
Expected: &ExplicitMentions{
|
||||||
|
Mentions: map[string]MentionType{
|
||||||
|
id1: KeywordMention,
|
||||||
|
},
|
||||||
|
GroupMentions: map[string]*model.Group{"engineering": {Name: "engineering"}},
|
||||||
|
OtherPotentialMentions: []string{"engineering", "systembot"},
|
||||||
|
},
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
e := &ExplicitMentions{}
|
e := &ExplicitMentions{}
|
||||||
e.processText(tc.Text, tc.Keywords)
|
e.processText(tc.Text, tc.Keywords, tc.Groups)
|
||||||
|
|
||||||
assert.EqualValues(t, tc.Expected, e)
|
assert.EqualValues(t, tc.Expected, e)
|
||||||
})
|
})
|
||||||
@@ -2130,3 +2287,105 @@ func TestUserAllowsEmail(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInsertGroupMentions(t *testing.T) {
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
team := th.BasicTeam
|
||||||
|
channel := th.BasicChannel
|
||||||
|
group := th.CreateGroup()
|
||||||
|
group.DisplayName = "engineering"
|
||||||
|
group.Name = "engineering"
|
||||||
|
group, err := th.App.UpdateGroup(group)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
groupChannelMember := th.CreateUser()
|
||||||
|
th.LinkUserToTeam(groupChannelMember, team)
|
||||||
|
th.App.AddUserToChannel(groupChannelMember, channel)
|
||||||
|
_, err = th.App.UpsertGroupMember(group.Id, groupChannelMember.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
nonGroupChannelMember := th.CreateUser()
|
||||||
|
th.LinkUserToTeam(nonGroupChannelMember, team)
|
||||||
|
th.App.AddUserToChannel(nonGroupChannelMember, channel)
|
||||||
|
|
||||||
|
nonChannelGroupMember := th.CreateUser()
|
||||||
|
th.LinkUserToTeam(nonChannelGroupMember, team)
|
||||||
|
_, err = th.App.UpsertGroupMember(group.Id, nonChannelGroupMember.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
groupWithNoMembers := th.CreateGroup()
|
||||||
|
groupWithNoMembers.DisplayName = "marketing"
|
||||||
|
groupWithNoMembers.Name = "marketing"
|
||||||
|
groupWithNoMembers, err = th.App.UpdateGroup(groupWithNoMembers)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
profileMap := map[string]*model.User{groupChannelMember.Id: groupChannelMember, nonGroupChannelMember.Id: nonGroupChannelMember}
|
||||||
|
|
||||||
|
t.Run("should add expected mentions for users part of the mentioned group", func(t *testing.T) {
|
||||||
|
mentions := &ExplicitMentions{}
|
||||||
|
usersMentioned, err := th.App.insertGroupMentions(group, channel, profileMap, mentions)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, usersMentioned, true)
|
||||||
|
|
||||||
|
// Ensure group member that is also a channel member is added to the mentions list.
|
||||||
|
require.Equal(t, len(mentions.Mentions), 1)
|
||||||
|
_, found := mentions.Mentions[groupChannelMember.Id]
|
||||||
|
require.Equal(t, found, true)
|
||||||
|
|
||||||
|
// Ensure group member that is not a channel member is added to the other potential mentions list.
|
||||||
|
require.Equal(t, len(mentions.OtherPotentialMentions), 1)
|
||||||
|
require.Equal(t, mentions.OtherPotentialMentions[0], nonChannelGroupMember.Username)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should add no expected or potential mentions if the group has no users ", func(t *testing.T) {
|
||||||
|
mentions := &ExplicitMentions{}
|
||||||
|
usersMentioned, err := th.App.insertGroupMentions(groupWithNoMembers, channel, profileMap, mentions)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, usersMentioned, false)
|
||||||
|
|
||||||
|
// Ensure no mentions are added for a group with no users
|
||||||
|
require.Equal(t, len(mentions.Mentions), 0)
|
||||||
|
require.Equal(t, len(mentions.OtherPotentialMentions), 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should keep existing mentions", func(t *testing.T) {
|
||||||
|
mentions := &ExplicitMentions{}
|
||||||
|
th.App.insertGroupMentions(group, channel, profileMap, mentions)
|
||||||
|
th.App.insertGroupMentions(groupWithNoMembers, channel, profileMap, mentions)
|
||||||
|
|
||||||
|
// Ensure mentions from group are kept after running with groupWithNoMembers
|
||||||
|
require.Equal(t, len(mentions.Mentions), 1)
|
||||||
|
require.Equal(t, len(mentions.OtherPotentialMentions), 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should return true if no members mentioned while in group or direct message channel", func(t *testing.T) {
|
||||||
|
mentions := &ExplicitMentions{}
|
||||||
|
emptyProfileMap := make(map[string]*model.User)
|
||||||
|
|
||||||
|
groupChannel := &model.Channel{Type: model.CHANNEL_GROUP}
|
||||||
|
usersMentioned, _ := th.App.insertGroupMentions(group, groupChannel, emptyProfileMap, mentions)
|
||||||
|
// Ensure group channel with no group members mentioned always returns true
|
||||||
|
require.Equal(t, usersMentioned, true)
|
||||||
|
require.Equal(t, len(mentions.Mentions), 0)
|
||||||
|
|
||||||
|
directChannel := &model.Channel{Type: model.CHANNEL_DIRECT}
|
||||||
|
usersMentioned, _ = th.App.insertGroupMentions(group, directChannel, emptyProfileMap, mentions)
|
||||||
|
// Ensure direct channel with no group members mentioned always returns true
|
||||||
|
require.Equal(t, usersMentioned, true)
|
||||||
|
require.Equal(t, len(mentions.Mentions), 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should add mentions for members while in group channel", func(t *testing.T) {
|
||||||
|
groupChannel, err := th.App.CreateGroupChannel([]string{groupChannelMember.Id, nonGroupChannelMember.Id, th.BasicUser.Id}, groupChannelMember.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
mentions := &ExplicitMentions{}
|
||||||
|
th.App.insertGroupMentions(group, groupChannel, profileMap, mentions)
|
||||||
|
|
||||||
|
require.Equal(t, len(mentions.Mentions), 1)
|
||||||
|
_, found := mentions.Mentions[groupChannelMember.Id]
|
||||||
|
require.Equal(t, found, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -1230,7 +1230,7 @@ func isPostMention(user *model.User, post *model.Post, keywords map[string][]str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for keyword mentions
|
// Check for keyword mentions
|
||||||
mentions := getExplicitMentions(post, keywords)
|
mentions := getExplicitMentions(post, keywords, make(map[string]*model.Group))
|
||||||
if _, ok := mentions.Mentions[user.Id]; ok {
|
if _, ok := mentions.Mentions[user.Id]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1600,6 +1600,10 @@
|
|||||||
"id": "api.plugin.verify_plugin.app_error",
|
"id": "api.plugin.verify_plugin.app_error",
|
||||||
"translation": "Unable to verify plugin signature."
|
"translation": "Unable to verify plugin signature."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "api.post.check_for_out_of_channel_group_users.message.none",
|
||||||
|
"translation": "@{{.GroupDisplayName}} has no members on this team"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "api.post.check_for_out_of_channel_groups_mentions.message.multiple",
|
"id": "api.post.check_for_out_of_channel_groups_mentions.message.multiple",
|
||||||
"translation": "@{{.Usernames}} and @{{.LastUsername}} did not get notified by this mention because they are not in the channel. They cannot be added to the channel because they are not a member of the linked groups. To add them to this channel, they must be added to the linked groups."
|
"translation": "@{{.Usernames}} and @{{.LastUsername}} did not get notified by this mention because they are not in the channel. They cannot be added to the channel because they are not a member of the linked groups. To add them to this channel, they must be added to the linked groups."
|
||||||
|
|||||||
@@ -3337,6 +3337,42 @@ func (s *OpenTracingLayerGroupStore) GetMemberUsers(groupID string) ([]*model.Us
|
|||||||
return resultVar0, resultVar1
|
return resultVar0, resultVar1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *OpenTracingLayerGroupStore) GetMemberUsersInTeam(groupID string, teamID string) ([]*model.User, *model.AppError) {
|
||||||
|
origCtx := s.Root.Store.Context()
|
||||||
|
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "GroupStore.GetMemberUsersInTeam")
|
||||||
|
s.Root.Store.SetContext(newCtx)
|
||||||
|
defer func() {
|
||||||
|
s.Root.Store.SetContext(origCtx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer span.Finish()
|
||||||
|
resultVar0, resultVar1 := s.GroupStore.GetMemberUsersInTeam(groupID, teamID)
|
||||||
|
if resultVar1 != nil {
|
||||||
|
span.LogFields(spanlog.Error(resultVar1))
|
||||||
|
ext.Error.Set(span, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultVar0, resultVar1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OpenTracingLayerGroupStore) GetMemberUsersNotInChannel(groupID string, channelID string) ([]*model.User, *model.AppError) {
|
||||||
|
origCtx := s.Root.Store.Context()
|
||||||
|
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "GroupStore.GetMemberUsersNotInChannel")
|
||||||
|
s.Root.Store.SetContext(newCtx)
|
||||||
|
defer func() {
|
||||||
|
s.Root.Store.SetContext(origCtx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer span.Finish()
|
||||||
|
resultVar0, resultVar1 := s.GroupStore.GetMemberUsersNotInChannel(groupID, channelID)
|
||||||
|
if resultVar1 != nil {
|
||||||
|
span.LogFields(spanlog.Error(resultVar1))
|
||||||
|
ext.Error.Set(span, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultVar0, resultVar1
|
||||||
|
}
|
||||||
|
|
||||||
func (s *OpenTracingLayerGroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, *model.AppError) {
|
func (s *OpenTracingLayerGroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, *model.AppError) {
|
||||||
origCtx := s.Root.Store.Context()
|
origCtx := s.Root.Store.Context()
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "GroupStore.GetMemberUsersPage")
|
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "GroupStore.GetMemberUsersPage")
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ func (s *SqlGroupStore) GetMemberUsers(groupID string) ([]*model.User, *model.Ap
|
|||||||
AND GroupId = :GroupId`
|
AND GroupId = :GroupId`
|
||||||
|
|
||||||
if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID}); err != nil {
|
if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID}); err != nil {
|
||||||
return nil, model.NewAppError("SqlGroupStore.GroupGetAllBySource", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
return nil, model.NewAppError("SqlGroupStore.GetMemberUsers", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
return groupMembers, nil
|
return groupMembers, nil
|
||||||
@@ -312,6 +312,70 @@ func (s *SqlGroupStore) GetMemberCount(groupID string) (int64, *model.AppError)
|
|||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SqlGroupStore) GetMemberUsersInTeam(groupID string, teamID string) ([]*model.User, *model.AppError) {
|
||||||
|
var groupMembers []*model.User
|
||||||
|
|
||||||
|
query := `
|
||||||
|
SELECT
|
||||||
|
Users.*
|
||||||
|
FROM
|
||||||
|
GroupMembers
|
||||||
|
JOIN Users ON Users.Id = GroupMembers.UserId
|
||||||
|
WHERE
|
||||||
|
GroupId = :GroupId
|
||||||
|
AND GroupMembers.UserId IN (
|
||||||
|
SELECT TeamMembers.UserId
|
||||||
|
FROM TeamMembers
|
||||||
|
JOIN Teams ON Teams.Id = :TeamId
|
||||||
|
WHERE TeamMembers.TeamId = Teams.Id
|
||||||
|
AND TeamMembers.DeleteAt = 0
|
||||||
|
)
|
||||||
|
AND GroupMembers.DeleteAt = 0
|
||||||
|
AND Users.DeleteAt = 0
|
||||||
|
`
|
||||||
|
|
||||||
|
if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID, "TeamId": teamID}); err != nil {
|
||||||
|
return nil, model.NewAppError("SqlGroupStore.GetMemberUsersInTeam", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupMembers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SqlGroupStore) GetMemberUsersNotInChannel(groupID string, channelID string) ([]*model.User, *model.AppError) {
|
||||||
|
var groupMembers []*model.User
|
||||||
|
|
||||||
|
query := `
|
||||||
|
SELECT
|
||||||
|
Users.*
|
||||||
|
FROM
|
||||||
|
GroupMembers
|
||||||
|
JOIN Users ON Users.Id = GroupMembers.UserId
|
||||||
|
WHERE
|
||||||
|
GroupId = :GroupId
|
||||||
|
AND GroupMembers.UserId NOT IN (
|
||||||
|
SELECT ChannelMembers.UserId
|
||||||
|
FROM ChannelMembers
|
||||||
|
WHERE ChannelMembers.ChannelId = :ChannelId
|
||||||
|
)
|
||||||
|
AND GroupMembers.UserId IN (
|
||||||
|
SELECT TeamMembers.UserId
|
||||||
|
FROM TeamMembers
|
||||||
|
JOIN Channels ON Channels.Id = :ChannelId
|
||||||
|
JOIN Teams ON Teams.Id = Channels.TeamId
|
||||||
|
WHERE TeamMembers.TeamId = Teams.Id
|
||||||
|
AND TeamMembers.DeleteAt = 0
|
||||||
|
)
|
||||||
|
AND GroupMembers.DeleteAt = 0
|
||||||
|
AND Users.DeleteAt = 0
|
||||||
|
`
|
||||||
|
|
||||||
|
if _, err := s.GetReplica().Select(&groupMembers, query, map[string]interface{}{"GroupId": groupID, "ChannelId": channelID}); err != nil {
|
||||||
|
return nil, model.NewAppError("SqlGroupStore.GetMemberUsersNotInChannel", "store.select_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupMembers, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SqlGroupStore) UpsertMember(groupID string, userID string) (*model.GroupMember, *model.AppError) {
|
func (s *SqlGroupStore) UpsertMember(groupID string, userID string) (*model.GroupMember, *model.AppError) {
|
||||||
member := &model.GroupMember{
|
member := &model.GroupMember{
|
||||||
GroupId: groupID,
|
GroupId: groupID,
|
||||||
@@ -1081,10 +1145,14 @@ func (s *SqlGroupStore) GetGroups(page, perPage int, opts model.GroupSearchOpts)
|
|||||||
groupsQuery = groupsQuery.
|
groupsQuery = groupsQuery.
|
||||||
From("UserGroups g").
|
From("UserGroups g").
|
||||||
Where("g.DeleteAt = 0").
|
Where("g.DeleteAt = 0").
|
||||||
Limit(uint64(perPage)).
|
|
||||||
Offset(uint64(page * perPage)).
|
|
||||||
OrderBy("g.DisplayName")
|
OrderBy("g.DisplayName")
|
||||||
|
|
||||||
|
if perPage != 0 {
|
||||||
|
groupsQuery = groupsQuery.
|
||||||
|
Limit(uint64(perPage)).
|
||||||
|
Offset(uint64(page * perPage))
|
||||||
|
}
|
||||||
|
|
||||||
if opts.FilterAllowReference {
|
if opts.FilterAllowReference {
|
||||||
groupsQuery = groupsQuery.Where("g.AllowReference = true")
|
groupsQuery = groupsQuery.Where("g.AllowReference = true")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -629,6 +629,10 @@ type GroupStore interface {
|
|||||||
GetMemberUsers(groupID string) ([]*model.User, *model.AppError)
|
GetMemberUsers(groupID string) ([]*model.User, *model.AppError)
|
||||||
GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, *model.AppError)
|
GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, *model.AppError)
|
||||||
GetMemberCount(groupID string) (int64, *model.AppError)
|
GetMemberCount(groupID string) (int64, *model.AppError)
|
||||||
|
|
||||||
|
GetMemberUsersInTeam(groupID string, teamID string) ([]*model.User, *model.AppError)
|
||||||
|
GetMemberUsersNotInChannel(groupID string, channelID string) ([]*model.User, *model.AppError)
|
||||||
|
|
||||||
UpsertMember(groupID string, userID string) (*model.GroupMember, *model.AppError)
|
UpsertMember(groupID string, userID string) (*model.GroupMember, *model.AppError)
|
||||||
DeleteMember(groupID string, userID string) (*model.GroupMember, *model.AppError)
|
DeleteMember(groupID string, userID string) (*model.GroupMember, *model.AppError)
|
||||||
PermanentDeleteMembersByUser(userId string) *model.AppError
|
PermanentDeleteMembersByUser(userId string) *model.AppError
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ func TestGroupStore(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
t.Run("GetMemberUsers", func(t *testing.T) { testGroupGetMemberUsers(t, ss) })
|
t.Run("GetMemberUsers", func(t *testing.T) { testGroupGetMemberUsers(t, ss) })
|
||||||
t.Run("GetMemberUsersPage", func(t *testing.T) { testGroupGetMemberUsersPage(t, ss) })
|
t.Run("GetMemberUsersPage", func(t *testing.T) { testGroupGetMemberUsersPage(t, ss) })
|
||||||
|
|
||||||
|
t.Run("GetMemberUsersInTeam", func(t *testing.T) { testGroupGetMemberUsersInTeam(t, ss) })
|
||||||
|
t.Run("GetMemberUsersNotInChannel", func(t *testing.T) { testGroupGetMemberUsersNotInChannel(t, ss) })
|
||||||
|
|
||||||
t.Run("UpsertMember", func(t *testing.T) { testUpsertMember(t, ss) })
|
t.Run("UpsertMember", func(t *testing.T) { testUpsertMember(t, ss) })
|
||||||
t.Run("DeleteMember", func(t *testing.T) { testGroupDeleteMember(t, ss) })
|
t.Run("DeleteMember", func(t *testing.T) { testGroupDeleteMember(t, ss) })
|
||||||
t.Run("PermanentDeleteMembersByUser", func(t *testing.T) { testGroupPermanentDeleteMembersByUser(t, ss) })
|
t.Run("PermanentDeleteMembersByUser", func(t *testing.T) { testGroupPermanentDeleteMembersByUser(t, ss) })
|
||||||
@@ -682,6 +686,231 @@ func testGroupGetMemberUsersPage(t *testing.T, ss store.Store) {
|
|||||||
require.Equal(t, 2, len(groupMembers))
|
require.Equal(t, 2, len(groupMembers))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testGroupGetMemberUsersInTeam(t *testing.T, ss store.Store) {
|
||||||
|
// Save a team
|
||||||
|
team := &model.Team{
|
||||||
|
DisplayName: "Name",
|
||||||
|
Description: "Some description",
|
||||||
|
CompanyName: "Some company name",
|
||||||
|
Name: "z-z-" + model.NewId() + "a",
|
||||||
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
||||||
|
Type: model.TEAM_OPEN,
|
||||||
|
}
|
||||||
|
team, err := ss.Team().Save(team)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// Save a group
|
||||||
|
g1 := &model.Group{
|
||||||
|
Name: model.NewId(),
|
||||||
|
DisplayName: model.NewId(),
|
||||||
|
Description: model.NewId(),
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
}
|
||||||
|
group, err := ss.Group().Create(g1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
u1 := &model.User{
|
||||||
|
Email: MakeEmail(),
|
||||||
|
Username: model.NewId(),
|
||||||
|
}
|
||||||
|
user1, err := ss.User().Save(u1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = ss.Group().UpsertMember(group.Id, user1.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
u2 := &model.User{
|
||||||
|
Email: MakeEmail(),
|
||||||
|
Username: model.NewId(),
|
||||||
|
}
|
||||||
|
user2, err := ss.User().Save(u2)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = ss.Group().UpsertMember(group.Id, user2.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
u3 := &model.User{
|
||||||
|
Email: MakeEmail(),
|
||||||
|
Username: model.NewId(),
|
||||||
|
}
|
||||||
|
user3, err := ss.User().Save(u3)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = ss.Group().UpsertMember(group.Id, user3.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// returns no members when team does not exist
|
||||||
|
groupMembers, err := ss.Group().GetMemberUsersInTeam(group.Id, "non-existant-channel-id")
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 0, len(groupMembers))
|
||||||
|
|
||||||
|
// returns no members when group has no members in the team
|
||||||
|
groupMembers, err = ss.Group().GetMemberUsersInTeam(group.Id, team.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 0, len(groupMembers))
|
||||||
|
|
||||||
|
m1 := &model.TeamMember{TeamId: team.Id, UserId: user1.Id}
|
||||||
|
_, err = ss.Team().SaveMember(m1, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// returns single member in team
|
||||||
|
groupMembers, err = ss.Group().GetMemberUsersInTeam(group.Id, team.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 1, len(groupMembers))
|
||||||
|
|
||||||
|
m2 := &model.TeamMember{TeamId: team.Id, UserId: user2.Id}
|
||||||
|
m3 := &model.TeamMember{TeamId: team.Id, UserId: user3.Id}
|
||||||
|
_, err = ss.Team().SaveMember(m2, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = ss.Team().SaveMember(m3, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// returns all members when all members are in team
|
||||||
|
groupMembers, err = ss.Group().GetMemberUsersInTeam(group.Id, team.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 3, len(groupMembers))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGroupGetMemberUsersNotInChannel(t *testing.T, ss store.Store) {
|
||||||
|
// Save a team
|
||||||
|
team := &model.Team{
|
||||||
|
DisplayName: "Name",
|
||||||
|
Description: "Some description",
|
||||||
|
CompanyName: "Some company name",
|
||||||
|
Name: "z-z-" + model.NewId() + "a",
|
||||||
|
Email: "success+" + model.NewId() + "@simulator.amazonses.com",
|
||||||
|
Type: model.TEAM_OPEN,
|
||||||
|
}
|
||||||
|
team, err := ss.Team().Save(team)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// Save a group
|
||||||
|
g1 := &model.Group{
|
||||||
|
Name: model.NewId(),
|
||||||
|
DisplayName: model.NewId(),
|
||||||
|
Description: model.NewId(),
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
}
|
||||||
|
group, err := ss.Group().Create(g1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
u1 := &model.User{
|
||||||
|
Email: MakeEmail(),
|
||||||
|
Username: model.NewId(),
|
||||||
|
}
|
||||||
|
user1, err := ss.User().Save(u1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = ss.Group().UpsertMember(group.Id, user1.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
u2 := &model.User{
|
||||||
|
Email: MakeEmail(),
|
||||||
|
Username: model.NewId(),
|
||||||
|
}
|
||||||
|
user2, err := ss.User().Save(u2)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = ss.Group().UpsertMember(group.Id, user2.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
u3 := &model.User{
|
||||||
|
Email: MakeEmail(),
|
||||||
|
Username: model.NewId(),
|
||||||
|
}
|
||||||
|
user3, err := ss.User().Save(u3)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = ss.Group().UpsertMember(group.Id, user3.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// Create Channel
|
||||||
|
channel := &model.Channel{
|
||||||
|
TeamId: team.Id,
|
||||||
|
DisplayName: "Channel",
|
||||||
|
Name: model.NewId(),
|
||||||
|
Type: model.CHANNEL_OPEN, // Query does not look at type so this shouldn't matter.
|
||||||
|
}
|
||||||
|
channel, err = ss.Channel().Save(channel, 9999)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// returns no members when channel does not exist
|
||||||
|
groupMembers, err := ss.Group().GetMemberUsersNotInChannel(group.Id, "non-existant-channel-id")
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 0, len(groupMembers))
|
||||||
|
|
||||||
|
// returns no members when group has no members in the team that the channel belongs to
|
||||||
|
groupMembers, err = ss.Group().GetMemberUsersNotInChannel(group.Id, channel.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 0, len(groupMembers))
|
||||||
|
|
||||||
|
m1 := &model.TeamMember{TeamId: team.Id, UserId: user1.Id}
|
||||||
|
_, err = ss.Team().SaveMember(m1, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// returns single member in team and not in channel
|
||||||
|
groupMembers, err = ss.Group().GetMemberUsersNotInChannel(group.Id, channel.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 1, len(groupMembers))
|
||||||
|
|
||||||
|
m2 := &model.TeamMember{TeamId: team.Id, UserId: user2.Id}
|
||||||
|
m3 := &model.TeamMember{TeamId: team.Id, UserId: user3.Id}
|
||||||
|
_, err = ss.Team().SaveMember(m2, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = ss.Team().SaveMember(m3, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// returns all members when all members are in team and not in channel
|
||||||
|
groupMembers, err = ss.Group().GetMemberUsersNotInChannel(group.Id, channel.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 3, len(groupMembers))
|
||||||
|
|
||||||
|
cm1 := &model.ChannelMember{
|
||||||
|
ChannelId: channel.Id,
|
||||||
|
UserId: user1.Id,
|
||||||
|
SchemeGuest: false,
|
||||||
|
SchemeUser: true,
|
||||||
|
SchemeAdmin: false,
|
||||||
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||||
|
}
|
||||||
|
_, err = ss.Channel().SaveMember(cm1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// returns both members not yet added to channel
|
||||||
|
groupMembers, err = ss.Group().GetMemberUsersNotInChannel(group.Id, channel.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 2, len(groupMembers))
|
||||||
|
|
||||||
|
cm2 := &model.ChannelMember{
|
||||||
|
ChannelId: channel.Id,
|
||||||
|
UserId: user2.Id,
|
||||||
|
SchemeGuest: false,
|
||||||
|
SchemeUser: true,
|
||||||
|
SchemeAdmin: false,
|
||||||
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||||
|
}
|
||||||
|
cm3 := &model.ChannelMember{
|
||||||
|
ChannelId: channel.Id,
|
||||||
|
UserId: user3.Id,
|
||||||
|
SchemeGuest: false,
|
||||||
|
SchemeUser: true,
|
||||||
|
SchemeAdmin: false,
|
||||||
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ss.Channel().SaveMember(cm2)
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = ss.Channel().SaveMember(cm3)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
// returns none when all members have been added to team and channel
|
||||||
|
groupMembers, err = ss.Group().GetMemberUsersNotInChannel(group.Id, channel.Id)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, 0, len(groupMembers))
|
||||||
|
}
|
||||||
|
|
||||||
func testUpsertMember(t *testing.T, ss store.Store) {
|
func testUpsertMember(t *testing.T, ss store.Store) {
|
||||||
// Create group
|
// Create group
|
||||||
g1 := &model.Group{
|
g1 := &model.Group{
|
||||||
|
|||||||
@@ -702,6 +702,56 @@ func (_m *GroupStore) GetMemberUsers(groupID string) ([]*model.User, *model.AppE
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMemberUsersInTeam provides a mock function with given fields: groupID, teamID
|
||||||
|
func (_m *GroupStore) GetMemberUsersInTeam(groupID string, teamID string) ([]*model.User, *model.AppError) {
|
||||||
|
ret := _m.Called(groupID, teamID)
|
||||||
|
|
||||||
|
var r0 []*model.User
|
||||||
|
if rf, ok := ret.Get(0).(func(string, string) []*model.User); ok {
|
||||||
|
r0 = rf(groupID, teamID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*model.User)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 *model.AppError
|
||||||
|
if rf, ok := ret.Get(1).(func(string, string) *model.AppError); ok {
|
||||||
|
r1 = rf(groupID, teamID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(1) != nil {
|
||||||
|
r1 = ret.Get(1).(*model.AppError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMemberUsersNotInChannel provides a mock function with given fields: groupID, channelID
|
||||||
|
func (_m *GroupStore) GetMemberUsersNotInChannel(groupID string, channelID string) ([]*model.User, *model.AppError) {
|
||||||
|
ret := _m.Called(groupID, channelID)
|
||||||
|
|
||||||
|
var r0 []*model.User
|
||||||
|
if rf, ok := ret.Get(0).(func(string, string) []*model.User); ok {
|
||||||
|
r0 = rf(groupID, channelID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*model.User)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 *model.AppError
|
||||||
|
if rf, ok := ret.Get(1).(func(string, string) *model.AppError); ok {
|
||||||
|
r1 = rf(groupID, channelID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(1) != nil {
|
||||||
|
r1 = ret.Get(1).(*model.AppError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// GetMemberUsersPage provides a mock function with given fields: groupID, page, perPage
|
// GetMemberUsersPage provides a mock function with given fields: groupID, page, perPage
|
||||||
func (_m *GroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, *model.AppError) {
|
func (_m *GroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, *model.AppError) {
|
||||||
ret := _m.Called(groupID, page, perPage)
|
ret := _m.Called(groupID, page, perPage)
|
||||||
|
|||||||
@@ -3054,6 +3054,38 @@ func (s *TimerLayerGroupStore) GetMemberUsers(groupID string) ([]*model.User, *m
|
|||||||
return resultVar0, resultVar1
|
return resultVar0, resultVar1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TimerLayerGroupStore) GetMemberUsersInTeam(groupID string, teamID string) ([]*model.User, *model.AppError) {
|
||||||
|
start := timemodule.Now()
|
||||||
|
|
||||||
|
resultVar0, resultVar1 := s.GroupStore.GetMemberUsersInTeam(groupID, teamID)
|
||||||
|
|
||||||
|
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
|
||||||
|
if s.Root.Metrics != nil {
|
||||||
|
success := "false"
|
||||||
|
if resultVar1 == nil {
|
||||||
|
success = "true"
|
||||||
|
}
|
||||||
|
s.Root.Metrics.ObserveStoreMethodDuration("GroupStore.GetMemberUsersInTeam", success, elapsed)
|
||||||
|
}
|
||||||
|
return resultVar0, resultVar1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TimerLayerGroupStore) GetMemberUsersNotInChannel(groupID string, channelID string) ([]*model.User, *model.AppError) {
|
||||||
|
start := timemodule.Now()
|
||||||
|
|
||||||
|
resultVar0, resultVar1 := s.GroupStore.GetMemberUsersNotInChannel(groupID, channelID)
|
||||||
|
|
||||||
|
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
|
||||||
|
if s.Root.Metrics != nil {
|
||||||
|
success := "false"
|
||||||
|
if resultVar1 == nil {
|
||||||
|
success = "true"
|
||||||
|
}
|
||||||
|
s.Root.Metrics.ObserveStoreMethodDuration("GroupStore.GetMemberUsersNotInChannel", success, elapsed)
|
||||||
|
}
|
||||||
|
return resultVar0, resultVar1
|
||||||
|
}
|
||||||
|
|
||||||
func (s *TimerLayerGroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, *model.AppError) {
|
func (s *TimerLayerGroupStore) GetMemberUsersPage(groupID string, page int, perPage int) ([]*model.User, *model.AppError) {
|
||||||
start := timemodule.Now()
|
start := timemodule.Now()
|
||||||
|
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user