MM-15422: Adds new parameters for retrieving pages of channels. (#10903)

* MM-15422: Adds new parameters for retrieving pages of channels and searching channels.

* MM-15422: Appends excluded channel names with defaults. Adds separate struct for data-tier option parameter.
This commit is contained in:
Martin Kraft
2019-05-24 11:28:42 -04:00
committed by GitHub
parent 124b371312
commit e8af4872c6
11 changed files with 256 additions and 91 deletions

View File

@@ -503,7 +503,12 @@ func getAllChannels(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
channels, err := c.App.GetAllChannels(c.Params.Page, c.Params.PerPage, false)
opts := model.ChannelSearchOpts{
NotAssociatedToGroup: c.Params.NotAssociatedToGroup,
ExcludeDefaultChannels: c.Params.ExcludeDefaultChannels,
}
channels, err := c.App.GetAllChannels(c.Params.Page, c.Params.PerPage, opts)
if err != nil {
c.Err = err
return
@@ -727,9 +732,13 @@ func searchAllChannels(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
includeDeleted := r.URL.Query().Get("include_deleted") == "true"
opts := model.ChannelSearchOpts{
NotAssociatedToGroup: props.NotAssociatedToGroup,
ExcludeDefaultChannels: props.ExcludeDefaultChannels,
IncludeDeleted: r.URL.Query().Get("include_deleted") == "true",
}
channels, err := c.App.SearchAllChannels(props.Term, includeDeleted)
channels, err := c.App.SearchAllChannels(props.Term, opts)
if err != nil {
c.Err = err
return

View File

@@ -16,23 +16,52 @@ import (
"github.com/mattermost/mattermost-server/utils"
)
func (a *App) CreateDefaultChannels(teamId string) ([]*model.Channel, *model.AppError) {
townSquare := &model.Channel{DisplayName: utils.T("api.channel.create_default_channels.town_square"), Name: "town-square", Type: model.CHANNEL_OPEN, TeamId: teamId}
if _, err := a.CreateChannel(townSquare, false); err != nil {
return nil, err
// CreateDefaultChannels creates channels in the given team for each channel returned by (*App).DefaultChannelNames.
//
func (a *App) CreateDefaultChannels(teamID string) ([]*model.Channel, *model.AppError) {
displayNames := map[string]string{
"town-square": utils.T("api.channel.create_default_channels.town_square"),
"off-topic": utils.T("api.channel.create_default_channels.off_topic"),
}
offTopic := &model.Channel{DisplayName: utils.T("api.channel.create_default_channels.off_topic"), Name: "off-topic", Type: model.CHANNEL_OPEN, TeamId: teamId}
if _, err := a.CreateChannel(offTopic, false); err != nil {
return nil, err
channels := []*model.Channel{}
defaultChannelNames := a.DefaultChannelNames()
for _, name := range defaultChannelNames {
displayName := utils.TDefault(displayNames[name], name)
channel := &model.Channel{DisplayName: displayName, Name: name, Type: model.CHANNEL_OPEN, TeamId: teamID}
if _, err := a.CreateChannel(channel, false); err != nil {
return nil, err
}
channels = append(channels, channel)
}
channels := []*model.Channel{townSquare, offTopic}
return channels, nil
}
// DefaultChannelNames returns the list of system-wide default channel names.
//
// By default the list will be (not necessarily in this order):
// ['town-square', 'off-topic']
// However, if TeamSettings.ExperimentalDefaultChannels contains a list of channels then that list will replace
// 'off-topic' and be included in the return results in addition to 'town-square'. For example:
// ['town-square', 'game-of-thrones', 'wow']
//
func (a *App) DefaultChannelNames() []string {
names := []string{"town-square"}
if len(a.Config().TeamSettings.ExperimentalDefaultChannels) == 0 {
names = append(names, "off-topic")
} else {
seenChannels := map[string]bool{}
for _, channelName := range a.Config().TeamSettings.ExperimentalDefaultChannels {
if !seenChannels[channelName] {
names = append(names, channelName)
seenChannels[channelName] = true
}
}
}
return names
}
func (a *App) JoinDefaultChannels(teamId string, user *model.User, shouldBeAdmin bool, userRequestorId string) *model.AppError {
var requestor *model.User
if userRequestorId != "" {
@@ -43,22 +72,8 @@ func (a *App) JoinDefaultChannels(teamId string, user *model.User, shouldBeAdmin
}
}
defaultChannelList := []string{"town-square"}
if len(a.Config().TeamSettings.ExperimentalDefaultChannels) == 0 {
defaultChannelList = append(defaultChannelList, "off-topic")
} else {
seenChannels := map[string]bool{}
for _, channelName := range a.Config().TeamSettings.ExperimentalDefaultChannels {
if !seenChannels[channelName] {
defaultChannelList = append(defaultChannelList, channelName)
seenChannels[channelName] = true
}
}
}
var err *model.AppError
for _, channelName := range defaultChannelList {
for _, channelName := range a.DefaultChannelNames() {
if result := <-a.Srv.Store.Channel().GetByName(teamId, channelName, true); result.Err != nil {
err = result.Err
} else {
@@ -1230,8 +1245,16 @@ func (a *App) GetChannelsForUser(teamId string, userId string, includeDeleted bo
return result.Data.(*model.ChannelList), nil
}
func (a *App) GetAllChannels(page, perPage int, includeDeleted bool) (*model.ChannelListWithTeamData, *model.AppError) {
result := <-a.Srv.Store.Channel().GetAllChannels(page*perPage, perPage, includeDeleted)
func (a *App) GetAllChannels(page, perPage int, opts model.ChannelSearchOpts) (*model.ChannelListWithTeamData, *model.AppError) {
if opts.ExcludeDefaultChannels {
opts.ExcludeChannelNames = a.DefaultChannelNames()
}
storeOpts := store.ChannelSearchOpts{
ExcludeChannelNames: opts.ExcludeChannelNames,
NotAssociatedToGroup: opts.NotAssociatedToGroup,
IncludeDeleted: opts.IncludeDeleted,
}
result := <-a.Srv.Store.Channel().GetAllChannels(page*perPage, perPage, storeOpts)
if result.Err != nil {
return nil, result.Err
}
@@ -1784,8 +1807,17 @@ func (a *App) AutocompleteChannelsForSearch(teamId string, userId string, term s
return result.Data.(*model.ChannelList), nil
}
func (a *App) SearchAllChannels(term string, includeDeleted bool) (*model.ChannelListWithTeamData, *model.AppError) {
result := <-a.Srv.Store.Channel().SearchAllChannels(term, *a.Config().TeamSettings.ExperimentalViewArchivedChannels && includeDeleted)
func (a *App) SearchAllChannels(term string, opts model.ChannelSearchOpts) (*model.ChannelListWithTeamData, *model.AppError) {
opts.IncludeDeleted = *a.Config().TeamSettings.ExperimentalViewArchivedChannels && opts.IncludeDeleted
if opts.ExcludeDefaultChannels {
opts.ExcludeChannelNames = a.DefaultChannelNames()
}
storeOpts := store.ChannelSearchOpts{
ExcludeChannelNames: opts.ExcludeChannelNames,
NotAssociatedToGroup: opts.NotAssociatedToGroup,
IncludeDeleted: opts.IncludeDeleted,
}
result := <-a.Srv.Store.Channel().SearchAllChannels(term, storeOpts)
if result.Err != nil {
return nil, result.Err
}

View File

@@ -895,3 +895,20 @@ func TestUpdateChannelMemberRolesChangingGuest(t *testing.T) {
}
})
}
func TestDefaultChannelNames(t *testing.T) {
th := Setup(t)
defer th.TearDown()
actual := th.App.DefaultChannelNames()
expect := []string{"town-square", "off-topic"}
require.ElementsMatch(t, expect, actual)
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.TeamSettings.ExperimentalDefaultChannels = []string{"foo", "bar"}
})
actual = th.App.DefaultChannelNames()
expect = []string{"town-square", "foo", "bar"}
require.ElementsMatch(t, expect, actual)
}

View File

@@ -5274,6 +5274,10 @@
"id": "store.select_error",
"translation": "select error"
},
{
"id": "store.sql.build_query.app_error",
"translation": "failed to build query"
},
{
"id": "store.sql.convert_string_array",
"translation": "FromDb: Unable to convert StringArray to *string"

View File

@@ -80,6 +80,20 @@ type DirectChannelForExport struct {
Members *[]string
}
// ChannelSearchOpts contains options for searching channels.
//
// NotAssociatedToGroup will exclude channels that have associated, active GroupChannels records.
// ExcludeDefaultChannels will exclude the configured default channels (ex 'town-square' and 'off-topic').
// IncludeDeleted will include channel records where DeleteAt != 0.
// ExcludeChannelNames will exclude channels from the results by name.
//
type ChannelSearchOpts struct {
NotAssociatedToGroup string
ExcludeDefaultChannels bool
IncludeDeleted bool
ExcludeChannelNames []string
}
func (o *Channel) DeepCopy() *Channel {
copy := *o
if copy.SchemeId != nil {

View File

@@ -11,7 +11,9 @@ import (
const CHANNEL_SEARCH_DEFAULT_LIMIT = 50
type ChannelSearch struct {
Term string `json:"term"`
Term string `json:"term"`
ExcludeDefaultChannels bool `json:"exclude_default_channels"`
NotAssociatedToGroup string `json:"not_associated_to_group"`
}
// ToJson convert a Channel to a json string

View File

@@ -949,17 +949,37 @@ func (s SqlChannelStore) GetChannels(teamId string, userId string, includeDelete
})
}
func (s SqlChannelStore) GetAllChannels(offset int, limit int, includeDeleted bool) store.StoreChannel {
func (s SqlChannelStore) GetAllChannels(offset int, limit int, opts store.ChannelSearchOpts) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
deleteFilter := "AND c.DeleteAt = 0"
if includeDeleted {
deleteFilter = ""
query := s.getQueryBuilder().
Select("c.*, Teams.DisplayName AS TeamDisplayName, Teams.Name AS TeamName, Teams.UpdateAt AS TeamUpdateAt").
From("Channels AS c").
Join("Teams ON Teams.Id = c.TeamId").
Where(sq.Eq{"c.Type": []string{model.CHANNEL_PRIVATE, model.CHANNEL_OPEN}}).
OrderBy("c.DisplayName, Teams.DisplayName").
Limit(uint64(limit)).
Offset(uint64(offset))
if !opts.IncludeDeleted {
query = query.Where(sq.Eq{"c.DeleteAt": int(0)})
}
query := "SELECT c.*, Teams.DisplayName AS TeamDisplayName, Teams.Name AS TeamName, Teams.UpdateAt as TeamUpdateAt FROM Channels AS c JOIN Teams ON Teams.Id = c.TeamId WHERE (c.Type = 'P' OR c.Type = 'O') " + deleteFilter + " ORDER BY c.DisplayName, Teams.DisplayName LIMIT :Limit OFFSET :Offset"
if len(opts.NotAssociatedToGroup) > 0 {
query = query.Where("c.Id NOT IN (SELECT ChannelId FROM GroupChannels WHERE GroupChannels.GroupId = ? AND GroupChannels.DeleteAt = 0)", opts.NotAssociatedToGroup)
}
if len(opts.ExcludeChannelNames) > 0 {
query = query.Where(fmt.Sprintf("c.Name NOT IN ('%s')", strings.Join(opts.ExcludeChannelNames, "', '")))
}
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetAllChannels", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
data := &model.ChannelListWithTeamData{}
_, err := s.GetReplica().Select(data, query, map[string]interface{}{"Limit": limit, "Offset": offset})
_, err = s.GetReplica().Select(data, queryString, args...)
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetAllChannels", "store.sql_channel.get_all_channels.get.app_error", nil, err.Error(), http.StatusInternalServerError)
@@ -2120,29 +2140,45 @@ func (s SqlChannelStore) SearchInTeam(teamId string, term string, includeDeleted
})
}
func (s SqlChannelStore) SearchAllChannels(term string, includeDeleted bool) store.StoreChannel {
func (s SqlChannelStore) SearchAllChannels(term string, opts store.ChannelSearchOpts) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
parameters := map[string]interface{}{}
deleteFilter := "AND c.DeleteAt = 0"
if includeDeleted {
deleteFilter = ""
query := s.getQueryBuilder().
Select("c.*, t.DisplayName AS TeamDisplayName, t.Name AS TeamName, t.UpdateAt as TeamUpdateAt").
From("Channels AS c").
Join("Teams AS t ON t.Id = c.TeamId").
Where(sq.Eq{"c.Type": []string{model.CHANNEL_PRIVATE, model.CHANNEL_OPEN}}).
OrderBy("c.DisplayName, t.DisplayName").
Limit(uint64(100))
if !opts.IncludeDeleted {
query = query.Where(sq.Eq{"c.DeleteAt": int(0)})
}
searchQuery := `SELECT c.*, t.DisplayName AS TeamDisplayName, t.Name AS TeamName, t.UpdateAt as TeamUpdateAt FROM Channels AS c JOIN Teams AS t ON t.Id = c.TeamId WHERE (c.Type = 'P' OR c.Type = 'O') ` + deleteFilter + ` SEARCH_CLAUSE ORDER BY c.DisplayName, t.DisplayName LIMIT 100`
likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose")
if likeTerm == "" {
// If the likeTerm is empty after preparing, then don't bother searching.
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
} else {
parameters["LikeTerm"] = likeTerm
if len(likeTerm) > 0 {
likeClause = strings.ReplaceAll(likeClause, ":LikeTerm", "'"+likeTerm+"'")
fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose")
parameters["FulltextTerm"] = fulltextTerm
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "AND ("+likeClause+" OR "+fulltextClause+")", 1)
fulltextClause = strings.ReplaceAll(fulltextClause, ":FulltextTerm", "'"+fulltextTerm+"'")
query = query.Where("(" + likeClause + " OR " + fulltextClause + ")")
}
if len(opts.ExcludeChannelNames) > 0 {
query = query.Where(fmt.Sprintf("c.Name NOT IN ('%s')", strings.Join(opts.ExcludeChannelNames, "', '")))
}
if len(opts.NotAssociatedToGroup) > 0 {
query = query.Where("c.Id NOT IN (SELECT ChannelId FROM GroupChannels WHERE GroupChannels.GroupId = ? AND GroupChannels.DeleteAt = 0)", opts.NotAssociatedToGroup)
}
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.SearchAllChannels", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
var channels model.ChannelListWithTeamData
if _, err := s.GetReplica().Select(&channels, searchQuery, parameters); err != nil {
if _, err := s.GetReplica().Select(&channels, queryString, args...); err != nil {
result.Err = model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
}
@@ -2501,7 +2537,7 @@ func (s SqlChannelStore) GetAllChannelsForExportAfter(limit int, afterId string)
Id
LIMIT :Limit`,
map[string]interface{}{"AfterId": afterId, "Limit": limit}); err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetAllChannelsForExportAfter", "store.sql_channel.get_all.app_error", nil, err.Error(), http.StatusInternalServerError)
result.Err = model.NewAppError("SqlChannelStore.GetAllChannelsForExportAfter", "store.sql_channel.get_all.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
@@ -2561,12 +2597,12 @@ func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId s
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
result.Err = model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
if _, err = s.GetReplica().Select(&directChannelsForExport, queryString, args...); err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
result.Err = model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
@@ -2585,13 +2621,13 @@ func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId s
queryString, args, err = query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
result.Err = model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
var channelMembers []*model.ChannelMemberForExport
if _, err := s.GetReplica().Select(&channelMembers, queryString, args...); err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
result.Err = model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
}
// Populate each channel with its members

View File

@@ -148,7 +148,7 @@ type ChannelStore interface {
GetDeletedByName(team_id string, name string) StoreChannel
GetDeleted(team_id string, offset int, limit int) StoreChannel
GetChannels(teamId string, userId string, includeDeleted bool) StoreChannel
GetAllChannels(page, perPage int, includeDeleted bool) StoreChannel
GetAllChannels(page, perPage int, opts ChannelSearchOpts) StoreChannel
GetMoreChannels(teamId string, userId string, offset int, limit int) StoreChannel
GetPublicChannelsForTeam(teamId string, offset int, limit int) StoreChannel
GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) StoreChannel
@@ -182,7 +182,7 @@ type ChannelStore interface {
GetMembersForUserWithPagination(teamId, userId string, page, perPage int) StoreChannel
AutocompleteInTeam(teamId string, term string, includeDeleted bool) StoreChannel
AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) StoreChannel
SearchAllChannels(term string, includeDeleted bool) StoreChannel
SearchAllChannels(term string, opts ChannelSearchOpts) StoreChannel
SearchInTeam(teamId string, term string, includeDeleted bool) StoreChannel
SearchMore(userId string, teamId string, term string) StoreChannel
GetMembersByIds(channelId string, userIds []string) StoreChannel
@@ -607,3 +607,15 @@ type LinkMetadataStore interface {
Save(linkMetadata *model.LinkMetadata) StoreChannel
Get(url string, timestamp int64) StoreChannel
}
// ChannelSearchOpts contains options for searching channels.
//
// NotAssociatedToGroup will exclude channels that have associated, active GroupChannels records.
// IncludeDeleted will include channel records where DeleteAt != 0.
// ExcludeChannelNames will exclude channels from the results by name.
//
type ChannelSearchOpts struct {
NotAssociatedToGroup string
IncludeDeleted bool
ExcludeChannelNames []string
}

View File

@@ -23,9 +23,9 @@ type SqlSupplier interface {
}
func cleanupChannels(t *testing.T, ss store.Store) {
result := <-ss.Channel().GetAllChannels(0, 100000, true)
result := <-ss.Channel().GetAllChannels(0, 100000, store.ChannelSearchOpts{IncludeDeleted: true})
if result.Err != nil {
t.Fatal("error cleaning all channels")
t.Fatalf("error cleaning all channels: %v", result.Err)
}
list := result.Data.(*model.ChannelListWithTeamData)
for _, channel := range *list {
@@ -1073,6 +1073,15 @@ func testChannelStoreGetAllChannels(t *testing.T, ss store.Store, s SqlSupplier)
c1.Type = model.CHANNEL_OPEN
store.Must(ss.Channel().Save(&c1, -1))
group := &model.Group{
Name: model.NewId(),
DisplayName: model.NewId(),
Source: model.GroupSourceLdap,
RemoteId: model.NewId(),
}
store.Must(ss.Group().Create(group))
store.Must(ss.Group().CreateGroupSyncable(model.NewGroupChannel(group.Id, c1.Id, true)))
c2 := model.Channel{}
c2.TeamId = t1.Id
c2.DisplayName = "Channel2" + model.NewId()
@@ -1103,7 +1112,7 @@ func testChannelStoreGetAllChannels(t *testing.T, ss store.Store, s SqlSupplier)
c5.Type = model.CHANNEL_GROUP
store.Must(ss.Channel().Save(&c5, -1))
cresult := <-ss.Channel().GetAllChannels(0, 10, false)
cresult := <-ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{})
list := cresult.Data.(*model.ChannelListWithTeamData)
assert.Len(t, *list, 2)
assert.Equal(t, (*list)[0].Id, c1.Id)
@@ -1111,7 +1120,7 @@ func testChannelStoreGetAllChannels(t *testing.T, ss store.Store, s SqlSupplier)
assert.Equal(t, (*list)[1].Id, c3.Id)
assert.Equal(t, (*list)[1].TeamDisplayName, "Name2")
cresult = <-ss.Channel().GetAllChannels(0, 10, true)
cresult = <-ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{IncludeDeleted: true})
list = cresult.Data.(*model.ChannelListWithTeamData)
assert.Len(t, *list, 3)
assert.Equal(t, (*list)[0].Id, c1.Id)
@@ -1119,12 +1128,22 @@ func testChannelStoreGetAllChannels(t *testing.T, ss store.Store, s SqlSupplier)
assert.Equal(t, (*list)[1].Id, c2.Id)
assert.Equal(t, (*list)[2].Id, c3.Id)
cresult = <-ss.Channel().GetAllChannels(0, 1, true)
cresult = <-ss.Channel().GetAllChannels(0, 1, store.ChannelSearchOpts{IncludeDeleted: true})
list = cresult.Data.(*model.ChannelListWithTeamData)
assert.Len(t, *list, 1)
assert.Equal(t, (*list)[0].Id, c1.Id)
assert.Equal(t, (*list)[0].TeamDisplayName, "Name")
// Not associated to group
cresult = <-ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{NotAssociatedToGroup: group.Id})
list = cresult.Data.(*model.ChannelListWithTeamData)
assert.Len(t, *list, 1)
// Exclude channel names
cresult = <-ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{ExcludeChannelNames: []string{c1.Name}})
list = cresult.Data.(*model.ChannelListWithTeamData)
assert.Len(t, *list, 1)
// Manually truncate Channels table until testlib can handle cleanups
s.GetMaster().Exec("TRUNCATE Channels")
}
@@ -2325,6 +2344,15 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
}
store.Must(ss.Channel().Save(&o7, -1))
group := &model.Group{
Name: model.NewId(),
DisplayName: model.NewId(),
Source: model.GroupSourceLdap,
RemoteId: model.NewId(),
}
store.Must(ss.Group().Create(group))
store.Must(ss.Group().CreateGroupSyncable(model.NewGroupChannel(group.Id, o7.Id, true)))
o8 := model.Channel{
TeamId: t1.Id,
DisplayName: "Off-Limit",
@@ -2381,28 +2409,31 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
testCases := []struct {
Description string
Term string
IncludeDeleted bool
Opts store.ChannelSearchOpts
ExpectedResults *model.ChannelList
}{
{"ChannelA", "ChannelA", false, &model.ChannelList{&o1, &o2, &o3}},
{"ChannelA, include deleted", "ChannelA", true, &model.ChannelList{&o1, &o2, &o3, &o13}},
{"empty string", "", false, &model.ChannelList{&o1, &o2, &o3, &o4, &o5, &o12, &o11, &o8, &o7, &o6, &o10, &o9}},
{"no matches", "blargh", false, &model.ChannelList{}},
{"prefix", "off-", false, &model.ChannelList{&o8, &o7, &o6}},
{"full match with dash", "off-topic", false, &model.ChannelList{&o6}},
{"town square", "town square", false, &model.ChannelList{&o9}},
{"the in name", "the", false, &model.ChannelList{&o10}},
{"Mobile", "Mobile", false, &model.ChannelList{&o11}},
{"search purpose", "now searchable", false, &model.ChannelList{&o12}},
{"pipe ignored", "town square |", false, &model.ChannelList{&o9}},
{"ChannelA", "ChannelA", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o1, &o2, &o3}},
{"ChannelA, include deleted", "ChannelA", store.ChannelSearchOpts{IncludeDeleted: true}, &model.ChannelList{&o1, &o2, &o3, &o13}},
{"empty string", "", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o1, &o2, &o3, &o4, &o5, &o12, &o11, &o8, &o7, &o6, &o10, &o9}},
{"no matches", "blargh", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{}},
{"prefix", "off-", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o8, &o7, &o6}},
{"full match with dash", "off-topic", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o6}},
{"town square", "town square", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o9}},
{"the in name", "the", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o10}},
{"Mobile", "Mobile", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o11}},
{"search purpose", "now searchable", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o12}},
{"pipe ignored", "town square |", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o9}},
{"exclude defaults search 'off'", "off-", store.ChannelSearchOpts{IncludeDeleted: false, ExcludeChannelNames: []string{"off-topic"}}, &model.ChannelList{&o8, &o7}},
{"exclude defaults search 'town'", "town", store.ChannelSearchOpts{IncludeDeleted: false, ExcludeChannelNames: []string{"town-square"}}, &model.ChannelList{}},
{"exclude by group association", "off", store.ChannelSearchOpts{IncludeDeleted: false, NotAssociatedToGroup: group.Id}, &model.ChannelList{&o8, &o6}},
}
for _, testCase := range testCases {
t.Run(testCase.Description, func(t *testing.T) {
result := <-ss.Channel().SearchAllChannels(testCase.Term, testCase.IncludeDeleted)
result := <-ss.Channel().SearchAllChannels(testCase.Term, testCase.Opts)
require.Nil(t, result.Err)
channels := result.Data.(*model.ChannelListWithTeamData)
require.Equal(t, len(*channels), len(*testCase.ExpectedResults))
require.Equal(t, len(*testCase.ExpectedResults), len(*channels))
for i, expected := range *testCase.ExpectedResults {
require.Equal(t, (*channels)[i].Id, expected.Id)
}

View File

@@ -212,13 +212,13 @@ func (_m *ChannelStore) GetAllChannelMembersNotifyPropsForChannel(channelId stri
return r0
}
// GetAllChannels provides a mock function with given fields: page, perPage, includeDeleted
func (_m *ChannelStore) GetAllChannels(page int, perPage int, includeDeleted bool) store.StoreChannel {
ret := _m.Called(page, perPage, includeDeleted)
// GetAllChannels provides a mock function with given fields: page, perPage, opts
func (_m *ChannelStore) GetAllChannels(page int, perPage int, opts store.ChannelSearchOpts) store.StoreChannel {
ret := _m.Called(page, perPage, opts)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(int, int, bool) store.StoreChannel); ok {
r0 = rf(page, perPage, includeDeleted)
if rf, ok := ret.Get(0).(func(int, int, store.ChannelSearchOpts) store.StoreChannel); ok {
r0 = rf(page, perPage, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
@@ -1003,13 +1003,13 @@ func (_m *ChannelStore) SaveMember(member *model.ChannelMember) store.StoreChann
return r0
}
// SearchAllChannels provides a mock function with given fields: term, includeDeleted
func (_m *ChannelStore) SearchAllChannels(term string, includeDeleted bool) store.StoreChannel {
ret := _m.Called(term, includeDeleted)
// SearchAllChannels provides a mock function with given fields: term, opts
func (_m *ChannelStore) SearchAllChannels(term string, opts store.ChannelSearchOpts) store.StoreChannel {
ret := _m.Called(term, opts)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, bool) store.StoreChannel); ok {
r0 = rf(term, includeDeleted)
if rf, ok := ret.Get(0).(func(string, store.ChannelSearchOpts) store.StoreChannel); ok {
r0 = rf(term, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)

View File

@@ -66,6 +66,8 @@ type Params struct {
NotAssociatedToChannel string
Paginate *bool
IncludeMemberCount bool
NotAssociatedToGroup string
ExcludeDefaultChannels bool
}
func ParamsFromRequest(r *http.Request) *Params {
@@ -260,5 +262,11 @@ func ParamsFromRequest(r *http.Request) *Params {
params.IncludeMemberCount = val
}
params.NotAssociatedToGroup = query.Get("not_associated_to_group")
if val, err := strconv.ParseBool(query.Get("exclude_default_channels")); err == nil {
params.ExcludeDefaultChannels = val
}
return params
}