[MM-25990] Add filters to search all channels (#15009)
* MM-25990 Add filters to search all channels endpoint and clean up tests * Add deleted filter * Remove redundant private public query
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
aad940e104
Коммит
ed34468996
@@ -999,12 +999,20 @@ func searchAllChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
includeDeleted, _ := strconv.ParseBool(r.URL.Query().Get("include_deleted"))
|
includeDeleted, _ := strconv.ParseBool(r.URL.Query().Get("include_deleted"))
|
||||||
|
includeDeleted = includeDeleted || props.IncludeDeleted
|
||||||
|
|
||||||
opts := model.ChannelSearchOpts{
|
opts := model.ChannelSearchOpts{
|
||||||
NotAssociatedToGroup: props.NotAssociatedToGroup,
|
NotAssociatedToGroup: props.NotAssociatedToGroup,
|
||||||
ExcludeDefaultChannels: props.ExcludeDefaultChannels,
|
ExcludeDefaultChannels: props.ExcludeDefaultChannels,
|
||||||
IncludeDeleted: includeDeleted,
|
TeamIds: props.TeamIds,
|
||||||
Page: props.Page,
|
GroupConstrained: props.GroupConstrained,
|
||||||
PerPage: props.PerPage,
|
ExcludeGroupConstrained: props.ExcludeGroupConstrained,
|
||||||
|
Public: props.Public,
|
||||||
|
Private: props.Private,
|
||||||
|
IncludeDeleted: includeDeleted,
|
||||||
|
Deleted: props.Deleted,
|
||||||
|
Page: props.Page,
|
||||||
|
PerPage: props.PerPage,
|
||||||
}
|
}
|
||||||
|
|
||||||
channels, totalCount, appErr := c.App.SearchAllChannels(props.Term, opts)
|
channels, totalCount, appErr := c.App.SearchAllChannels(props.Term, opts)
|
||||||
@@ -1014,9 +1022,7 @@ func searchAllChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Don't fill in channels props, since unused by client and potentially expensive.
|
// Don't fill in channels props, since unused by client and potentially expensive.
|
||||||
|
|
||||||
var payload []byte
|
var payload []byte
|
||||||
|
|
||||||
if props.Page != nil && props.PerPage != nil {
|
if props.Page != nil && props.PerPage != nil {
|
||||||
data := model.ChannelsWithCount{Channels: channels, TotalCount: totalCount}
|
data := model.ChannelsWithCount{Channels: channels, TotalCount: totalCount}
|
||||||
payload = data.ToJson()
|
payload = data.ToJson()
|
||||||
|
|||||||
@@ -1227,109 +1227,166 @@ func TestSearchAllChannels(t *testing.T) {
|
|||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
Client := th.Client
|
Client := th.Client
|
||||||
|
|
||||||
channel := &model.Channel{
|
openChannel, chanErr := th.SystemAdminClient.CreateChannel(&model.Channel{
|
||||||
DisplayName: "FOOBARDISPLAYNAME",
|
DisplayName: "SearchAllChannels-FOOBARDISPLAYNAME",
|
||||||
Name: "whatever",
|
Name: "whatever",
|
||||||
Type: model.CHANNEL_OPEN,
|
Type: model.CHANNEL_OPEN,
|
||||||
TeamId: th.BasicTeam.Id,
|
TeamId: th.BasicTeam.Id,
|
||||||
|
})
|
||||||
|
CheckNoError(t, chanErr)
|
||||||
|
|
||||||
|
privateChannel, privErr := th.SystemAdminClient.CreateChannel(&model.Channel{
|
||||||
|
DisplayName: "SearchAllChannels-private1",
|
||||||
|
Name: "private1",
|
||||||
|
Type: model.CHANNEL_PRIVATE,
|
||||||
|
TeamId: th.BasicTeam.Id,
|
||||||
|
})
|
||||||
|
CheckNoError(t, privErr)
|
||||||
|
|
||||||
|
team := th.CreateTeam()
|
||||||
|
groupConstrainedChannel, groupErr := th.SystemAdminClient.CreateChannel(&model.Channel{
|
||||||
|
DisplayName: "SearchAllChannels-groupConstrained-1",
|
||||||
|
Name: "groupconstrained1",
|
||||||
|
Type: model.CHANNEL_PRIVATE,
|
||||||
|
GroupConstrained: model.NewBool(true),
|
||||||
|
TeamId: team.Id,
|
||||||
|
})
|
||||||
|
CheckNoError(t, groupErr)
|
||||||
|
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
*cfg.TeamSettings.ExperimentalViewArchivedChannels = true
|
||||||
|
})
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
Description string
|
||||||
|
Search *model.ChannelSearch
|
||||||
|
ExpectedChannelIds []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Middle of word search",
|
||||||
|
&model.ChannelSearch{Term: "bardisplay"},
|
||||||
|
[]string{openChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Prefix search",
|
||||||
|
&model.ChannelSearch{Term: "SearchAllChannels-foobar"},
|
||||||
|
[]string{openChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Suffix search",
|
||||||
|
&model.ChannelSearch{Term: "displayname"},
|
||||||
|
[]string{openChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name search",
|
||||||
|
&model.ChannelSearch{Term: "what"},
|
||||||
|
[]string{openChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name suffix search",
|
||||||
|
&model.ChannelSearch{Term: "ever"},
|
||||||
|
[]string{openChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Basic channel name middle of word search",
|
||||||
|
&model.ChannelSearch{Term: th.BasicChannel.Name[2:14]},
|
||||||
|
[]string{th.BasicChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Upper case search",
|
||||||
|
&model.ChannelSearch{Term: strings.ToUpper(th.BasicChannel.Name)},
|
||||||
|
[]string{th.BasicChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Mixed case search",
|
||||||
|
&model.ChannelSearch{Term: th.BasicChannel.Name[0:2] + strings.ToUpper(th.BasicChannel.Name[2:5]) + th.BasicChannel.Name[5:]},
|
||||||
|
[]string{th.BasicChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Non mixed case search",
|
||||||
|
&model.ChannelSearch{Term: th.BasicChannel.Name},
|
||||||
|
[]string{th.BasicChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Search private channel name",
|
||||||
|
&model.ChannelSearch{Term: th.BasicPrivateChannel.Name},
|
||||||
|
[]string{th.BasicPrivateChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Search with private channel filter",
|
||||||
|
&model.ChannelSearch{Private: true},
|
||||||
|
[]string{th.BasicPrivateChannel.Id, th.BasicPrivateChannel2.Id, privateChannel.Id, groupConstrainedChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Search with public channel filter",
|
||||||
|
&model.ChannelSearch{Term: "SearchAllChannels", Public: true},
|
||||||
|
[]string{openChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Search with private channel filter",
|
||||||
|
&model.ChannelSearch{Term: "SearchAllChannels", Private: true},
|
||||||
|
[]string{privateChannel.Id, groupConstrainedChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Search with teamIds channel filter",
|
||||||
|
&model.ChannelSearch{Term: "SearchAllChannels", TeamIds: []string{th.BasicTeam.Id}},
|
||||||
|
[]string{openChannel.Id, privateChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Search with deleted without IncludeDeleted filter",
|
||||||
|
&model.ChannelSearch{Term: th.BasicDeletedChannel.Name},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Search with deleted IncludeDeleted filter",
|
||||||
|
&model.ChannelSearch{Term: th.BasicDeletedChannel.Name, IncludeDeleted: true},
|
||||||
|
[]string{th.BasicDeletedChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Search with deleted IncludeDeleted filter",
|
||||||
|
&model.ChannelSearch{Term: th.BasicDeletedChannel.Name, IncludeDeleted: true},
|
||||||
|
[]string{th.BasicDeletedChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Search with deleted Deleted filter and empty term",
|
||||||
|
&model.ChannelSearch{Term: "", Deleted: true},
|
||||||
|
[]string{th.BasicDeletedChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Search for group constrained",
|
||||||
|
&model.ChannelSearch{Term: "SearchAllChannels", GroupConstrained: true},
|
||||||
|
[]string{groupConstrainedChannel.Id},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Search for group constrained and public",
|
||||||
|
&model.ChannelSearch{Term: "SearchAllChannels", GroupConstrained: true, Public: true},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Search for exclude group constrained",
|
||||||
|
&model.ChannelSearch{Term: "SearchAllChannels", ExcludeGroupConstrained: true},
|
||||||
|
[]string{openChannel.Id, privateChannel.Id},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.Description, func(t *testing.T) {
|
||||||
|
channels, resp := th.SystemAdminClient.SearchAllChannels(testCase.Search)
|
||||||
|
CheckNoError(t, resp)
|
||||||
|
assert.Equal(t, len(testCase.ExpectedChannelIds), len(*channels))
|
||||||
|
actualChannelIds := []string{}
|
||||||
|
for _, channelWithTeamData := range *channels {
|
||||||
|
actualChannelIds = append(actualChannelIds, channelWithTeamData.Channel.Id)
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, testCase.ExpectedChannelIds, actualChannelIds)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Testing Mixed Case (Ensure we get results for partial word searches)
|
// Searching with no terms returns all default channels
|
||||||
|
allChannels, resp := th.SystemAdminClient.SearchAllChannels(&model.ChannelSearch{Term: ""})
|
||||||
// Search by using display name
|
|
||||||
foobarchannel, err := th.SystemAdminClient.CreateChannel(channel)
|
|
||||||
CheckNoError(t, err)
|
|
||||||
|
|
||||||
search := &model.ChannelSearch{Term: "bardisplay"}
|
|
||||||
|
|
||||||
channels, resp := th.SystemAdminClient.SearchAllChannels(search)
|
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
assert.True(t, len(*allChannels) >= 3)
|
||||||
|
|
||||||
assert.Len(t, *channels, 1)
|
_, resp = Client.SearchAllChannels(&model.ChannelSearch{Term: ""})
|
||||||
assert.Equal(t, foobarchannel.Id, (*channels)[0].Id)
|
|
||||||
|
|
||||||
search = &model.ChannelSearch{Term: "foobar"}
|
|
||||||
|
|
||||||
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
assert.Len(t, *channels, 1)
|
|
||||||
assert.Equal(t, foobarchannel.Id, (*channels)[0].Id)
|
|
||||||
|
|
||||||
search = &model.ChannelSearch{Term: "displayname"}
|
|
||||||
|
|
||||||
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
assert.Len(t, *channels, 1)
|
|
||||||
assert.Equal(t, foobarchannel.Id, (*channels)[0].Id)
|
|
||||||
|
|
||||||
// Search by using Name
|
|
||||||
search = &model.ChannelSearch{Term: "what"}
|
|
||||||
|
|
||||||
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
assert.Len(t, *channels, 1)
|
|
||||||
assert.Equal(t, foobarchannel.Id, (*channels)[0].Id)
|
|
||||||
|
|
||||||
search = &model.ChannelSearch{Term: "ever"}
|
|
||||||
|
|
||||||
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
// Seach by partial word search and testing case sensitivty
|
|
||||||
assert.Len(t, *channels, 1)
|
|
||||||
assert.Equal(t, foobarchannel.Id, (*channels)[0].Id)
|
|
||||||
|
|
||||||
search = &model.ChannelSearch{Term: th.BasicChannel.Name[2:14]}
|
|
||||||
|
|
||||||
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
assert.Len(t, *channels, 1)
|
|
||||||
assert.Equal(t, th.BasicChannel.Id, (*channels)[0].Id)
|
|
||||||
|
|
||||||
search = &model.ChannelSearch{Term: strings.ToUpper(th.BasicChannel.Name)}
|
|
||||||
|
|
||||||
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
assert.Len(t, *channels, 1)
|
|
||||||
assert.Equal(t, th.BasicChannel.Id, (*channels)[0].Id)
|
|
||||||
|
|
||||||
search = &model.ChannelSearch{Term: th.BasicChannel.Name[0:2] + strings.ToUpper(th.BasicChannel.Name[2:5]) + th.BasicChannel.Name[5:]}
|
|
||||||
|
|
||||||
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
assert.Len(t, *channels, 1)
|
|
||||||
assert.Equal(t, th.BasicChannel.Id, (*channels)[0].Id)
|
|
||||||
|
|
||||||
// Testing Non-Mixed Case test cases
|
|
||||||
search = &model.ChannelSearch{Term: th.BasicChannel.Name}
|
|
||||||
|
|
||||||
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
assert.Len(t, *channels, 1)
|
|
||||||
assert.Equal(t, th.BasicChannel.Id, (*channels)[0].Id)
|
|
||||||
|
|
||||||
search.Term = th.BasicPrivateChannel.Name
|
|
||||||
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
|
|
||||||
assert.Len(t, *channels, 1)
|
|
||||||
assert.Equal(t, th.BasicPrivateChannel.Id, (*channels)[0].Id)
|
|
||||||
|
|
||||||
search.Term = ""
|
|
||||||
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
|
|
||||||
CheckNoError(t, resp)
|
|
||||||
// At least, all the not-deleted channels created during the InitBasic
|
|
||||||
assert.True(t, len(*channels) >= 3)
|
|
||||||
|
|
||||||
search.Term = th.BasicChannel.Name
|
|
||||||
_, resp = Client.SearchAllChannels(search)
|
|
||||||
CheckForbiddenStatus(t, resp)
|
CheckForbiddenStatus(t, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2192,11 +2192,17 @@ func (a *App) SearchAllChannels(term string, opts model.ChannelSearchOpts) (*mod
|
|||||||
opts.ExcludeChannelNames = a.DefaultChannelNames()
|
opts.ExcludeChannelNames = a.DefaultChannelNames()
|
||||||
}
|
}
|
||||||
storeOpts := store.ChannelSearchOpts{
|
storeOpts := store.ChannelSearchOpts{
|
||||||
ExcludeChannelNames: opts.ExcludeChannelNames,
|
ExcludeChannelNames: opts.ExcludeChannelNames,
|
||||||
NotAssociatedToGroup: opts.NotAssociatedToGroup,
|
NotAssociatedToGroup: opts.NotAssociatedToGroup,
|
||||||
IncludeDeleted: opts.IncludeDeleted,
|
IncludeDeleted: opts.IncludeDeleted,
|
||||||
Page: opts.Page,
|
Deleted: opts.Deleted,
|
||||||
PerPage: opts.PerPage,
|
TeamIds: opts.TeamIds,
|
||||||
|
GroupConstrained: opts.GroupConstrained,
|
||||||
|
ExcludeGroupConstrained: opts.ExcludeGroupConstrained,
|
||||||
|
Public: opts.Public,
|
||||||
|
Private: opts.Private,
|
||||||
|
Page: opts.Page,
|
||||||
|
PerPage: opts.PerPage,
|
||||||
}
|
}
|
||||||
|
|
||||||
term = strings.TrimSpace(term)
|
term = strings.TrimSpace(term)
|
||||||
|
|||||||
@@ -120,12 +120,18 @@ type ChannelModeratedRolesPatch struct {
|
|||||||
// PerPage number of results per page, if paginated.
|
// PerPage number of results per page, if paginated.
|
||||||
//
|
//
|
||||||
type ChannelSearchOpts struct {
|
type ChannelSearchOpts struct {
|
||||||
NotAssociatedToGroup string
|
NotAssociatedToGroup string
|
||||||
ExcludeDefaultChannels bool
|
ExcludeDefaultChannels bool
|
||||||
IncludeDeleted bool
|
IncludeDeleted bool
|
||||||
ExcludeChannelNames []string
|
Deleted bool
|
||||||
Page *int
|
ExcludeChannelNames []string
|
||||||
PerPage *int
|
TeamIds []string
|
||||||
|
GroupConstrained bool
|
||||||
|
ExcludeGroupConstrained bool
|
||||||
|
Public bool
|
||||||
|
Private bool
|
||||||
|
Page *int
|
||||||
|
PerPage *int
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelMemberCountByGroup struct {
|
type ChannelMemberCountByGroup struct {
|
||||||
|
|||||||
@@ -11,11 +11,18 @@ import (
|
|||||||
const CHANNEL_SEARCH_DEFAULT_LIMIT = 50
|
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"`
|
ExcludeDefaultChannels bool `json:"exclude_default_channels"`
|
||||||
NotAssociatedToGroup string `json:"not_associated_to_group"`
|
NotAssociatedToGroup string `json:"not_associated_to_group"`
|
||||||
Page *int `json:"page,omitempty"`
|
TeamIds []string `json:"team_ids"`
|
||||||
PerPage *int `json:"per_page,omitempty"`
|
GroupConstrained bool `json:"group_constrained"`
|
||||||
|
ExcludeGroupConstrained bool `json:"exclude_group_constrained"`
|
||||||
|
Public bool `json:"public"`
|
||||||
|
Private bool `json:"private"`
|
||||||
|
IncludeDeleted bool `json:"include_deleted"`
|
||||||
|
Deleted bool `json:"deleted"`
|
||||||
|
Page *int `json:"page,omitempty"`
|
||||||
|
PerPage *int `json:"per_page,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToJson convert a Channel to a json string
|
// ToJson convert a Channel to a json string
|
||||||
|
|||||||
@@ -2816,8 +2816,7 @@ func (s SqlChannelStore) channelSearchQuery(term string, opts store.ChannelSearc
|
|||||||
query := s.getQueryBuilder().
|
query := s.getQueryBuilder().
|
||||||
Select(selectStr).
|
Select(selectStr).
|
||||||
From("Channels AS c").
|
From("Channels AS c").
|
||||||
Join("Teams AS t ON t.Id = c.TeamId").
|
Join("Teams AS t ON t.Id = c.TeamId")
|
||||||
Where(sq.Eq{"c.Type": []string{model.CHANNEL_PRIVATE, model.CHANNEL_OPEN}})
|
|
||||||
|
|
||||||
// don't bother ordering or limiting if we're just getting the count
|
// don't bother ordering or limiting if we're just getting the count
|
||||||
if !countQuery {
|
if !countQuery {
|
||||||
@@ -2825,8 +2824,9 @@ func (s SqlChannelStore) channelSearchQuery(term string, opts store.ChannelSearc
|
|||||||
OrderBy("c.DisplayName, t.DisplayName").
|
OrderBy("c.DisplayName, t.DisplayName").
|
||||||
Limit(uint64(limit))
|
Limit(uint64(limit))
|
||||||
}
|
}
|
||||||
|
if opts.Deleted {
|
||||||
if !opts.IncludeDeleted {
|
query = query.Where(sq.NotEq{"c.DeleteAt": int(0)})
|
||||||
|
} else if !opts.IncludeDeleted {
|
||||||
query = query.Where(sq.Eq{"c.DeleteAt": int(0)})
|
query = query.Where(sq.Eq{"c.DeleteAt": int(0)})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2854,6 +2854,30 @@ func (s SqlChannelStore) channelSearchQuery(term string, opts store.ChannelSearc
|
|||||||
query = query.Where("c.Id NOT IN (SELECT ChannelId FROM GroupChannels WHERE GroupChannels.GroupId = ? AND GroupChannels.DeleteAt = 0)", opts.NotAssociatedToGroup)
|
query = query.Where("c.Id NOT IN (SELECT ChannelId FROM GroupChannels WHERE GroupChannels.GroupId = ? AND GroupChannels.DeleteAt = 0)", opts.NotAssociatedToGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(opts.TeamIds) > 0 {
|
||||||
|
query = query.Where(sq.Eq{"c.TeamId": opts.TeamIds})
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.GroupConstrained {
|
||||||
|
query = query.Where(sq.Eq{"c.GroupConstrained": true})
|
||||||
|
} else if opts.ExcludeGroupConstrained {
|
||||||
|
query = query.Where(sq.Or{
|
||||||
|
sq.NotEq{"c.GroupConstrained": true},
|
||||||
|
sq.Eq{"c.GroupConstrained": nil},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Public && !opts.Private {
|
||||||
|
query = query.Where(sq.Eq{"c.Type": model.CHANNEL_OPEN})
|
||||||
|
} else if opts.Private && !opts.Public {
|
||||||
|
query = query.Where(sq.Eq{"c.Type": model.CHANNEL_PRIVATE})
|
||||||
|
} else {
|
||||||
|
query = query.Where(sq.Or{
|
||||||
|
sq.Eq{"c.Type": model.CHANNEL_OPEN},
|
||||||
|
sq.Eq{"c.Type": model.CHANNEL_PRIVATE},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -750,11 +750,17 @@ type LinkMetadataStore interface {
|
|||||||
// PerPage number of results per page, if paginated.
|
// PerPage number of results per page, if paginated.
|
||||||
//
|
//
|
||||||
type ChannelSearchOpts struct {
|
type ChannelSearchOpts struct {
|
||||||
NotAssociatedToGroup string
|
NotAssociatedToGroup string
|
||||||
IncludeDeleted bool
|
IncludeDeleted bool
|
||||||
ExcludeChannelNames []string
|
Deleted bool
|
||||||
Page *int
|
ExcludeChannelNames []string
|
||||||
PerPage *int
|
TeamIds []string
|
||||||
|
GroupConstrained bool
|
||||||
|
ExcludeGroupConstrained bool
|
||||||
|
Public bool
|
||||||
|
Private bool
|
||||||
|
Page *int
|
||||||
|
PerPage *int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ChannelSearchOpts) IsPaginated() bool {
|
func (c *ChannelSearchOpts) IsPaginated() bool {
|
||||||
|
|||||||
@@ -5208,7 +5208,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o1 := model.Channel{
|
o1 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "ChannelA",
|
DisplayName: "A1 ChannelA",
|
||||||
Name: "zz" + model.NewId() + "b",
|
Name: "zz" + model.NewId() + "b",
|
||||||
Type: model.CHANNEL_OPEN,
|
Type: model.CHANNEL_OPEN,
|
||||||
}
|
}
|
||||||
@@ -5217,7 +5217,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o2 := model.Channel{
|
o2 := model.Channel{
|
||||||
TeamId: t2.Id,
|
TeamId: t2.Id,
|
||||||
DisplayName: "ChannelA",
|
DisplayName: "A2 ChannelA",
|
||||||
Name: "zz" + model.NewId() + "b",
|
Name: "zz" + model.NewId() + "b",
|
||||||
Type: model.CHANNEL_OPEN,
|
Type: model.CHANNEL_OPEN,
|
||||||
}
|
}
|
||||||
@@ -5250,7 +5250,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o3 := model.Channel{
|
o3 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "ChannelA (alternate)",
|
DisplayName: "A3 ChannelA (alternate)",
|
||||||
Name: "zz" + model.NewId() + "b",
|
Name: "zz" + model.NewId() + "b",
|
||||||
Type: model.CHANNEL_OPEN,
|
Type: model.CHANNEL_OPEN,
|
||||||
}
|
}
|
||||||
@@ -5259,7 +5259,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o4 := model.Channel{
|
o4 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "ChannelB",
|
DisplayName: "A4 ChannelB",
|
||||||
Name: "zz" + model.NewId() + "b",
|
Name: "zz" + model.NewId() + "b",
|
||||||
Type: model.CHANNEL_PRIVATE,
|
Type: model.CHANNEL_PRIVATE,
|
||||||
}
|
}
|
||||||
@@ -5267,17 +5267,18 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
require.Nil(t, nErr)
|
require.Nil(t, nErr)
|
||||||
|
|
||||||
o5 := model.Channel{
|
o5 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "ChannelC",
|
DisplayName: "A5 ChannelC",
|
||||||
Name: "zz" + model.NewId() + "b",
|
Name: "zz" + model.NewId() + "b",
|
||||||
Type: model.CHANNEL_PRIVATE,
|
Type: model.CHANNEL_PRIVATE,
|
||||||
|
GroupConstrained: model.NewBool(true),
|
||||||
}
|
}
|
||||||
_, nErr = ss.Channel().Save(&o5, -1)
|
_, nErr = ss.Channel().Save(&o5, -1)
|
||||||
require.Nil(t, nErr)
|
require.Nil(t, nErr)
|
||||||
|
|
||||||
o6 := model.Channel{
|
o6 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "Off-Topic",
|
DisplayName: "A6 Off-Topic",
|
||||||
Name: "off-topic",
|
Name: "off-topic",
|
||||||
Type: model.CHANNEL_OPEN,
|
Type: model.CHANNEL_OPEN,
|
||||||
}
|
}
|
||||||
@@ -5286,7 +5287,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o7 := model.Channel{
|
o7 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "Off-Set",
|
DisplayName: "A7 Off-Set",
|
||||||
Name: "off-set",
|
Name: "off-set",
|
||||||
Type: model.CHANNEL_OPEN,
|
Type: model.CHANNEL_OPEN,
|
||||||
}
|
}
|
||||||
@@ -5307,7 +5308,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o8 := model.Channel{
|
o8 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "Off-Limit",
|
DisplayName: "A8 Off-Limit",
|
||||||
Name: "off-limit",
|
Name: "off-limit",
|
||||||
Type: model.CHANNEL_PRIVATE,
|
Type: model.CHANNEL_PRIVATE,
|
||||||
}
|
}
|
||||||
@@ -5316,7 +5317,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o9 := model.Channel{
|
o9 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "Town Square",
|
DisplayName: "A9 Town Square",
|
||||||
Name: "town-square",
|
Name: "town-square",
|
||||||
Type: model.CHANNEL_OPEN,
|
Type: model.CHANNEL_OPEN,
|
||||||
}
|
}
|
||||||
@@ -5325,7 +5326,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o10 := model.Channel{
|
o10 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "The",
|
DisplayName: "B10 The",
|
||||||
Name: "the",
|
Name: "the",
|
||||||
Type: model.CHANNEL_OPEN,
|
Type: model.CHANNEL_OPEN,
|
||||||
}
|
}
|
||||||
@@ -5334,7 +5335,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o11 := model.Channel{
|
o11 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "Native Mobile Apps",
|
DisplayName: "B11 Native Mobile Apps",
|
||||||
Name: "native-mobile-apps",
|
Name: "native-mobile-apps",
|
||||||
Type: model.CHANNEL_OPEN,
|
Type: model.CHANNEL_OPEN,
|
||||||
}
|
}
|
||||||
@@ -5343,7 +5344,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o12 := model.Channel{
|
o12 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "ChannelZ",
|
DisplayName: "B12 ChannelZ",
|
||||||
Purpose: "This can now be searchable!",
|
Purpose: "This can now be searchable!",
|
||||||
Name: "with-purpose",
|
Name: "with-purpose",
|
||||||
Type: model.CHANNEL_OPEN,
|
Type: model.CHANNEL_OPEN,
|
||||||
@@ -5353,7 +5354,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o13 := model.Channel{
|
o13 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "ChannelA (deleted)",
|
DisplayName: "B13 ChannelA (deleted)",
|
||||||
Name: model.NewId(),
|
Name: model.NewId(),
|
||||||
Type: model.CHANNEL_OPEN,
|
Type: model.CHANNEL_OPEN,
|
||||||
}
|
}
|
||||||
@@ -5367,7 +5368,7 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
|
|
||||||
o14 := model.Channel{
|
o14 := model.Channel{
|
||||||
TeamId: t2.Id,
|
TeamId: t2.Id,
|
||||||
DisplayName: "FOOBARDISPLAYNAME",
|
DisplayName: "B14 FOOBARDISPLAYNAME",
|
||||||
Name: "whatever",
|
Name: "whatever",
|
||||||
Type: model.CHANNEL_OPEN,
|
Type: model.CHANNEL_OPEN,
|
||||||
}
|
}
|
||||||
@@ -5387,20 +5388,34 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
{"Search FooBar by name2", "ever", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o14}, 1},
|
{"Search FooBar by name2", "ever", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o14}, 1},
|
||||||
{"ChannelA", "ChannelA", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o1, &o2, &o3}, 0},
|
{"ChannelA", "ChannelA", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o1, &o2, &o3}, 0},
|
||||||
{"ChannelA, include deleted", "ChannelA", store.ChannelSearchOpts{IncludeDeleted: true}, &model.ChannelList{&o1, &o2, &o3, &o13}, 0},
|
{"ChannelA, include deleted", "ChannelA", store.ChannelSearchOpts{IncludeDeleted: true}, &model.ChannelList{&o1, &o2, &o3, &o13}, 0},
|
||||||
{"empty string", "", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o1, &o2, &o3, &o4, &o5, &o12, &o14, &o11, &o8, &o7, &o6, &o10, &o9}, 0},
|
{"empty string", "", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o1, &o2, &o3, &o4, &o5, &o6, &o7, &o8, &o9, &o10, &o11, &o12, &o14}, 0},
|
||||||
{"no matches", "blargh", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{}, 0},
|
{"no matches", "blargh", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{}, 0},
|
||||||
{"prefix", "off-", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o8, &o7, &o6}, 0},
|
{"prefix", "off-", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o6, &o7, &o8}, 0},
|
||||||
{"full match with dash", "off-topic", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o6}, 0},
|
{"full match with dash", "off-topic", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o6}, 0},
|
||||||
{"town square", "town square", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o9}, 0},
|
{"town square", "town square", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o9}, 0},
|
||||||
{"the in name", "the", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o10}, 0},
|
{"the in name", "the", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o10}, 0},
|
||||||
{"Mobile", "Mobile", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o11}, 0},
|
{"Mobile", "Mobile", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o11}, 0},
|
||||||
{"search purpose", "now searchable", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o12}, 0},
|
{"search purpose", "now searchable", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o12}, 0},
|
||||||
{"pipe ignored", "town square |", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o9}, 0},
|
{"pipe ignored", "town square |", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o9}, 0},
|
||||||
{"exclude defaults search 'off'", "off-", store.ChannelSearchOpts{IncludeDeleted: false, ExcludeChannelNames: []string{"off-topic"}}, &model.ChannelList{&o8, &o7}, 0},
|
{"exclude defaults search 'off'", "off-", store.ChannelSearchOpts{IncludeDeleted: false, ExcludeChannelNames: []string{"off-topic"}}, &model.ChannelList{&o7, &o8}, 0},
|
||||||
{"exclude defaults search 'town'", "town", store.ChannelSearchOpts{IncludeDeleted: false, ExcludeChannelNames: []string{"town-square"}}, &model.ChannelList{}, 0},
|
{"exclude defaults search 'town'", "town", store.ChannelSearchOpts{IncludeDeleted: false, ExcludeChannelNames: []string{"town-square"}}, &model.ChannelList{}, 0},
|
||||||
{"exclude by group association", "off-", store.ChannelSearchOpts{IncludeDeleted: false, NotAssociatedToGroup: group.Id}, &model.ChannelList{&o8, &o6}, 0},
|
{"exclude by group association", "off-", store.ChannelSearchOpts{IncludeDeleted: false, NotAssociatedToGroup: group.Id}, &model.ChannelList{&o6, &o8}, 0},
|
||||||
{"paginate includes count", "off-", store.ChannelSearchOpts{IncludeDeleted: false, PerPage: model.NewInt(100)}, &model.ChannelList{&o8, &o7, &o6}, 3},
|
{"paginate includes count", "off-", store.ChannelSearchOpts{IncludeDeleted: false, PerPage: model.NewInt(100)}, &model.ChannelList{&o6, &o7, &o8}, 3},
|
||||||
{"paginate, page 2 correct entries and count", "off-", store.ChannelSearchOpts{IncludeDeleted: false, PerPage: model.NewInt(2), Page: model.NewInt(1)}, &model.ChannelList{&o6}, 3},
|
{"paginate, page 2 correct entries and count", "off-", store.ChannelSearchOpts{IncludeDeleted: false, PerPage: model.NewInt(2), Page: model.NewInt(1)}, &model.ChannelList{&o8}, 3},
|
||||||
|
{"Filter private", "", store.ChannelSearchOpts{IncludeDeleted: false, Private: true}, &model.ChannelList{&o4, &o5, &o8}, 3},
|
||||||
|
{"Filter public", "", store.ChannelSearchOpts{IncludeDeleted: false, Public: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{&o1, &o2, &o3, &o6, &o7}, 10},
|
||||||
|
{"Filter public and private", "", store.ChannelSearchOpts{IncludeDeleted: false, Public: true, Private: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{&o1, &o2, &o3, &o4, &o5}, 13},
|
||||||
|
{"Filter public and private and include deleted", "", store.ChannelSearchOpts{IncludeDeleted: true, Public: true, Private: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{&o1, &o2, &o3, &o4, &o5}, 14},
|
||||||
|
{"Filter group constrained", "", store.ChannelSearchOpts{IncludeDeleted: false, GroupConstrained: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{&o5}, 1},
|
||||||
|
{"Filter exclude group constrained and include deleted", "", store.ChannelSearchOpts{IncludeDeleted: true, ExcludeGroupConstrained: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{&o1, &o2, &o3, &o4, &o6}, 13},
|
||||||
|
{"Filter private and exclude group constrained", "", store.ChannelSearchOpts{IncludeDeleted: false, ExcludeGroupConstrained: true, Private: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{&o4, &o8}, 2},
|
||||||
|
{"Filter team 2", "", store.ChannelSearchOpts{IncludeDeleted: false, TeamIds: []string{t2.Id}, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{&o2, &o14}, 2},
|
||||||
|
{"Filter team 2, private", "", store.ChannelSearchOpts{IncludeDeleted: false, TeamIds: []string{t2.Id}, Private: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{}, 0},
|
||||||
|
{"Filter team 1 and team 2, private", "", store.ChannelSearchOpts{IncludeDeleted: false, TeamIds: []string{t1.Id, t2.Id}, Private: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{&o4, &o5, &o8}, 3},
|
||||||
|
{"Filter team 1 and team 2, public and private", "", store.ChannelSearchOpts{IncludeDeleted: false, TeamIds: []string{t1.Id, t2.Id}, Public: true, Private: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{&o1, &o2, &o3, &o4, &o5}, 13},
|
||||||
|
{"Filter team 1 and team 2, public and private and group constrained", "", store.ChannelSearchOpts{IncludeDeleted: false, TeamIds: []string{t1.Id, t2.Id}, Public: true, Private: true, GroupConstrained: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{&o5}, 1},
|
||||||
|
{"Filter team 1 and team 2, public and private and exclude group constrained", "", store.ChannelSearchOpts{IncludeDeleted: false, TeamIds: []string{t1.Id, t2.Id}, Public: true, Private: true, ExcludeGroupConstrained: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{&o1, &o2, &o3, &o4, &o6}, 12},
|
||||||
|
{"Filter deleted returns only deleted channels", "", store.ChannelSearchOpts{Deleted: true, Page: model.NewInt(0), PerPage: model.NewInt(5)}, &model.ChannelList{&o13}, 1},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user