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.
Этот коммит содержится в:
Martin Kraft
2019-05-24 11:28:42 -04:00
коммит произвёл GitHub
родитель 124b371312
Коммит e8af4872c6
11 изменённых файлов: 256 добавлений и 91 удалений

Просмотреть файл

@@ -503,7 +503,12 @@ func getAllChannels(c *Context, w http.ResponseWriter, r *http.Request) {
return 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 { if err != nil {
c.Err = err c.Err = err
return return
@@ -727,9 +732,13 @@ func searchAllChannels(c *Context, w http.ResponseWriter, r *http.Request) {
return 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 { if err != nil {
c.Err = err c.Err = err
return return

Просмотреть файл

@@ -16,23 +16,52 @@ import (
"github.com/mattermost/mattermost-server/utils" "github.com/mattermost/mattermost-server/utils"
) )
func (a *App) CreateDefaultChannels(teamId string) ([]*model.Channel, *model.AppError) { // CreateDefaultChannels creates channels in the given team for each channel returned by (*App).DefaultChannelNames.
townSquare := &model.Channel{DisplayName: utils.T("api.channel.create_default_channels.town_square"), Name: "town-square", Type: model.CHANNEL_OPEN, TeamId: teamId} //
func (a *App) CreateDefaultChannels(teamID string) ([]*model.Channel, *model.AppError) {
if _, err := a.CreateChannel(townSquare, false); err != nil { 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"),
}
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 return nil, err
} }
channels = append(channels, channel)
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{townSquare, offTopic}
return channels, nil 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 { func (a *App) JoinDefaultChannels(teamId string, user *model.User, shouldBeAdmin bool, userRequestorId string) *model.AppError {
var requestor *model.User var requestor *model.User
if userRequestorId != "" { 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 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 { if result := <-a.Srv.Store.Channel().GetByName(teamId, channelName, true); result.Err != nil {
err = result.Err err = result.Err
} else { } else {
@@ -1230,8 +1245,16 @@ func (a *App) GetChannelsForUser(teamId string, userId string, includeDeleted bo
return result.Data.(*model.ChannelList), nil return result.Data.(*model.ChannelList), nil
} }
func (a *App) GetAllChannels(page, perPage int, includeDeleted bool) (*model.ChannelListWithTeamData, *model.AppError) { func (a *App) GetAllChannels(page, perPage int, opts model.ChannelSearchOpts) (*model.ChannelListWithTeamData, *model.AppError) {
result := <-a.Srv.Store.Channel().GetAllChannels(page*perPage, perPage, 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().GetAllChannels(page*perPage, perPage, storeOpts)
if result.Err != nil { if result.Err != nil {
return nil, result.Err return nil, result.Err
} }
@@ -1784,8 +1807,17 @@ func (a *App) AutocompleteChannelsForSearch(teamId string, userId string, term s
return result.Data.(*model.ChannelList), nil return result.Data.(*model.ChannelList), nil
} }
func (a *App) SearchAllChannels(term string, includeDeleted bool) (*model.ChannelListWithTeamData, *model.AppError) { func (a *App) SearchAllChannels(term string, opts model.ChannelSearchOpts) (*model.ChannelListWithTeamData, *model.AppError) {
result := <-a.Srv.Store.Channel().SearchAllChannels(term, *a.Config().TeamSettings.ExperimentalViewArchivedChannels && includeDeleted) 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 { if result.Err != nil {
return nil, result.Err return nil, result.Err
} }

Просмотреть файл

@@ -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)
}

Просмотреть файл

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

Просмотреть файл

@@ -80,6 +80,20 @@ type DirectChannelForExport struct {
Members *[]string 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 { func (o *Channel) DeepCopy() *Channel {
copy := *o copy := *o
if copy.SchemeId != nil { if copy.SchemeId != nil {

Просмотреть файл

@@ -12,6 +12,8 @@ const CHANNEL_SEARCH_DEFAULT_LIMIT = 50
type ChannelSearch struct { 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 // ToJson convert a Channel to a json string

Просмотреть файл

@@ -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) { return store.Do(func(result *store.StoreResult) {
deleteFilter := "AND c.DeleteAt = 0" query := s.getQueryBuilder().
if includeDeleted { Select("c.*, Teams.DisplayName AS TeamDisplayName, Teams.Name AS TeamName, Teams.UpdateAt AS TeamUpdateAt").
deleteFilter = "" 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{} 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 { if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetAllChannels", "store.sql_channel.get_all_channels.get.app_error", nil, err.Error(), http.StatusInternalServerError) 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) { return store.Do(func(result *store.StoreResult) {
parameters := map[string]interface{}{} query := s.getQueryBuilder().
deleteFilter := "AND c.DeleteAt = 0" Select("c.*, t.DisplayName AS TeamDisplayName, t.Name AS TeamName, t.UpdateAt as TeamUpdateAt").
if includeDeleted { From("Channels AS c").
deleteFilter = "" 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") likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose")
if likeTerm == "" { if len(likeTerm) > 0 {
// If the likeTerm is empty after preparing, then don't bother searching. likeClause = strings.ReplaceAll(likeClause, ":LikeTerm", "'"+likeTerm+"'")
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
} else {
parameters["LikeTerm"] = likeTerm
fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose") fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose")
parameters["FulltextTerm"] = fulltextTerm fulltextClause = strings.ReplaceAll(fulltextClause, ":FulltextTerm", "'"+fulltextTerm+"'")
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "AND ("+likeClause+" OR "+fulltextClause+")", 1) 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 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) 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 Id
LIMIT :Limit`, LIMIT :Limit`,
map[string]interface{}{"AfterId": afterId, "Limit": limit}); err != nil { 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 return
} }
@@ -2561,12 +2597,12 @@ func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId s
queryString, args, err := query.ToSql() queryString, args, err := query.ToSql()
if err != nil { 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 return
} }
if _, err = s.GetReplica().Select(&directChannelsForExport, queryString, args...); err != nil { 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 return
} }
@@ -2585,13 +2621,13 @@ func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId s
queryString, args, err = query.ToSql() queryString, args, err = query.ToSql()
if err != nil { 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 return
} }
var channelMembers []*model.ChannelMemberForExport var channelMembers []*model.ChannelMemberForExport
if _, err := s.GetReplica().Select(&channelMembers, queryString, args...); err != nil { 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 // Populate each channel with its members

Просмотреть файл

@@ -148,7 +148,7 @@ type ChannelStore interface {
GetDeletedByName(team_id string, name string) StoreChannel GetDeletedByName(team_id string, name string) StoreChannel
GetDeleted(team_id string, offset int, limit int) StoreChannel GetDeleted(team_id string, offset int, limit int) StoreChannel
GetChannels(teamId string, userId string, includeDeleted bool) 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 GetMoreChannels(teamId string, userId string, offset int, limit int) StoreChannel
GetPublicChannelsForTeam(teamId string, offset int, limit int) StoreChannel GetPublicChannelsForTeam(teamId string, offset int, limit int) StoreChannel
GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) StoreChannel GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) StoreChannel
@@ -182,7 +182,7 @@ type ChannelStore interface {
GetMembersForUserWithPagination(teamId, userId string, page, perPage int) StoreChannel GetMembersForUserWithPagination(teamId, userId string, page, perPage int) StoreChannel
AutocompleteInTeam(teamId string, term string, includeDeleted bool) StoreChannel AutocompleteInTeam(teamId string, term string, includeDeleted bool) StoreChannel
AutocompleteInTeamForSearch(teamId string, userId 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 SearchInTeam(teamId string, term string, includeDeleted bool) StoreChannel
SearchMore(userId string, teamId string, term string) StoreChannel SearchMore(userId string, teamId string, term string) StoreChannel
GetMembersByIds(channelId string, userIds []string) StoreChannel GetMembersByIds(channelId string, userIds []string) StoreChannel
@@ -607,3 +607,15 @@ type LinkMetadataStore interface {
Save(linkMetadata *model.LinkMetadata) StoreChannel Save(linkMetadata *model.LinkMetadata) StoreChannel
Get(url string, timestamp int64) 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
}

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@@ -66,6 +66,8 @@ type Params struct {
NotAssociatedToChannel string NotAssociatedToChannel string
Paginate *bool Paginate *bool
IncludeMemberCount bool IncludeMemberCount bool
NotAssociatedToGroup string
ExcludeDefaultChannels bool
} }
func ParamsFromRequest(r *http.Request) *Params { func ParamsFromRequest(r *http.Request) *Params {
@@ -260,5 +262,11 @@ func ParamsFromRequest(r *http.Request) *Params {
params.IncludeMemberCount = val 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 return params
} }