Skip to content

Commit ed34468

Browse files
authored
[MM-25990] Add filters to search all channels (mattermost#15009)
* MM-25990 Add filters to search all channels endpoint and clean up tests * Add deleted filter * Remove redundant private public query
1 parent aad940e commit ed34468

File tree

8 files changed

+275
-148
lines changed

8 files changed

+275
-148
lines changed

api4/channel.go

+13-7
Original file line numberDiff line numberDiff line change
@@ -999,12 +999,20 @@ func searchAllChannels(c *Context, w http.ResponseWriter, r *http.Request) {
999999
return
10001000
}
10011001
includeDeleted, _ := strconv.ParseBool(r.URL.Query().Get("include_deleted"))
1002+
includeDeleted = includeDeleted || props.IncludeDeleted
1003+
10021004
opts := model.ChannelSearchOpts{
1003-
NotAssociatedToGroup: props.NotAssociatedToGroup,
1004-
ExcludeDefaultChannels: props.ExcludeDefaultChannels,
1005-
IncludeDeleted: includeDeleted,
1006-
Page: props.Page,
1007-
PerPage: props.PerPage,
1005+
NotAssociatedToGroup: props.NotAssociatedToGroup,
1006+
ExcludeDefaultChannels: props.ExcludeDefaultChannels,
1007+
TeamIds: props.TeamIds,
1008+
GroupConstrained: props.GroupConstrained,
1009+
ExcludeGroupConstrained: props.ExcludeGroupConstrained,
1010+
Public: props.Public,
1011+
Private: props.Private,
1012+
IncludeDeleted: includeDeleted,
1013+
Deleted: props.Deleted,
1014+
Page: props.Page,
1015+
PerPage: props.PerPage,
10081016
}
10091017

10101018
channels, totalCount, appErr := c.App.SearchAllChannels(props.Term, opts)
@@ -1014,9 +1022,7 @@ func searchAllChannels(c *Context, w http.ResponseWriter, r *http.Request) {
10141022
}
10151023

10161024
// Don't fill in channels props, since unused by client and potentially expensive.
1017-
10181025
var payload []byte
1019-
10201026
if props.Page != nil && props.PerPage != nil {
10211027
data := model.ChannelsWithCount{Channels: channels, TotalCount: totalCount}
10221028
payload = data.ToJson()

api4/channel_test.go

+150-93
Original file line numberDiff line numberDiff line change
@@ -1227,109 +1227,166 @@ func TestSearchAllChannels(t *testing.T) {
12271227
defer th.TearDown()
12281228
Client := th.Client
12291229

1230-
channel := &model.Channel{
1231-
DisplayName: "FOOBARDISPLAYNAME",
1230+
openChannel, chanErr := th.SystemAdminClient.CreateChannel(&model.Channel{
1231+
DisplayName: "SearchAllChannels-FOOBARDISPLAYNAME",
12321232
Name: "whatever",
12331233
Type: model.CHANNEL_OPEN,
12341234
TeamId: th.BasicTeam.Id,
1235-
}
1236-
1237-
// Testing Mixed Case (Ensure we get results for partial word searches)
1238-
1239-
// Search by using display name
1240-
foobarchannel, err := th.SystemAdminClient.CreateChannel(channel)
1241-
CheckNoError(t, err)
1242-
1243-
search := &model.ChannelSearch{Term: "bardisplay"}
1244-
1245-
channels, resp := th.SystemAdminClient.SearchAllChannels(search)
1246-
CheckNoError(t, resp)
1247-
1248-
assert.Len(t, *channels, 1)
1249-
assert.Equal(t, foobarchannel.Id, (*channels)[0].Id)
1250-
1251-
search = &model.ChannelSearch{Term: "foobar"}
1252-
1253-
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
1254-
CheckNoError(t, resp)
1255-
1256-
assert.Len(t, *channels, 1)
1257-
assert.Equal(t, foobarchannel.Id, (*channels)[0].Id)
1258-
1259-
search = &model.ChannelSearch{Term: "displayname"}
1260-
1261-
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
1262-
CheckNoError(t, resp)
1263-
1264-
assert.Len(t, *channels, 1)
1265-
assert.Equal(t, foobarchannel.Id, (*channels)[0].Id)
1266-
1267-
// Search by using Name
1268-
search = &model.ChannelSearch{Term: "what"}
1269-
1270-
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
1271-
CheckNoError(t, resp)
1272-
1273-
assert.Len(t, *channels, 1)
1274-
assert.Equal(t, foobarchannel.Id, (*channels)[0].Id)
1275-
1276-
search = &model.ChannelSearch{Term: "ever"}
1277-
1278-
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
1279-
CheckNoError(t, resp)
1280-
1281-
// Seach by partial word search and testing case sensitivty
1282-
assert.Len(t, *channels, 1)
1283-
assert.Equal(t, foobarchannel.Id, (*channels)[0].Id)
1284-
1285-
search = &model.ChannelSearch{Term: th.BasicChannel.Name[2:14]}
1286-
1287-
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
1288-
CheckNoError(t, resp)
1289-
1290-
assert.Len(t, *channels, 1)
1291-
assert.Equal(t, th.BasicChannel.Id, (*channels)[0].Id)
1292-
1293-
search = &model.ChannelSearch{Term: strings.ToUpper(th.BasicChannel.Name)}
1294-
1295-
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
1296-
CheckNoError(t, resp)
1297-
1298-
assert.Len(t, *channels, 1)
1299-
assert.Equal(t, th.BasicChannel.Id, (*channels)[0].Id)
1300-
1301-
search = &model.ChannelSearch{Term: th.BasicChannel.Name[0:2] + strings.ToUpper(th.BasicChannel.Name[2:5]) + th.BasicChannel.Name[5:]}
1302-
1303-
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
1304-
CheckNoError(t, resp)
1305-
1306-
assert.Len(t, *channels, 1)
1307-
assert.Equal(t, th.BasicChannel.Id, (*channels)[0].Id)
1308-
1309-
// Testing Non-Mixed Case test cases
1310-
search = &model.ChannelSearch{Term: th.BasicChannel.Name}
1235+
})
1236+
CheckNoError(t, chanErr)
13111237

1312-
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
1313-
CheckNoError(t, resp)
1238+
privateChannel, privErr := th.SystemAdminClient.CreateChannel(&model.Channel{
1239+
DisplayName: "SearchAllChannels-private1",
1240+
Name: "private1",
1241+
Type: model.CHANNEL_PRIVATE,
1242+
TeamId: th.BasicTeam.Id,
1243+
})
1244+
CheckNoError(t, privErr)
13141245

1315-
assert.Len(t, *channels, 1)
1316-
assert.Equal(t, th.BasicChannel.Id, (*channels)[0].Id)
1246+
team := th.CreateTeam()
1247+
groupConstrainedChannel, groupErr := th.SystemAdminClient.CreateChannel(&model.Channel{
1248+
DisplayName: "SearchAllChannels-groupConstrained-1",
1249+
Name: "groupconstrained1",
1250+
Type: model.CHANNEL_PRIVATE,
1251+
GroupConstrained: model.NewBool(true),
1252+
TeamId: team.Id,
1253+
})
1254+
CheckNoError(t, groupErr)
13171255

1318-
search.Term = th.BasicPrivateChannel.Name
1319-
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
1320-
CheckNoError(t, resp)
1256+
th.App.UpdateConfig(func(cfg *model.Config) {
1257+
*cfg.TeamSettings.ExperimentalViewArchivedChannels = true
1258+
})
13211259

1322-
assert.Len(t, *channels, 1)
1323-
assert.Equal(t, th.BasicPrivateChannel.Id, (*channels)[0].Id)
1260+
testCases := []struct {
1261+
Description string
1262+
Search *model.ChannelSearch
1263+
ExpectedChannelIds []string
1264+
}{
1265+
{
1266+
"Middle of word search",
1267+
&model.ChannelSearch{Term: "bardisplay"},
1268+
[]string{openChannel.Id},
1269+
},
1270+
{
1271+
"Prefix search",
1272+
&model.ChannelSearch{Term: "SearchAllChannels-foobar"},
1273+
[]string{openChannel.Id},
1274+
},
1275+
{
1276+
"Suffix search",
1277+
&model.ChannelSearch{Term: "displayname"},
1278+
[]string{openChannel.Id},
1279+
},
1280+
{
1281+
"Name search",
1282+
&model.ChannelSearch{Term: "what"},
1283+
[]string{openChannel.Id},
1284+
},
1285+
{
1286+
"Name suffix search",
1287+
&model.ChannelSearch{Term: "ever"},
1288+
[]string{openChannel.Id},
1289+
},
1290+
{
1291+
"Basic channel name middle of word search",
1292+
&model.ChannelSearch{Term: th.BasicChannel.Name[2:14]},
1293+
[]string{th.BasicChannel.Id},
1294+
},
1295+
{
1296+
"Upper case search",
1297+
&model.ChannelSearch{Term: strings.ToUpper(th.BasicChannel.Name)},
1298+
[]string{th.BasicChannel.Id},
1299+
},
1300+
{
1301+
"Mixed case search",
1302+
&model.ChannelSearch{Term: th.BasicChannel.Name[0:2] + strings.ToUpper(th.BasicChannel.Name[2:5]) + th.BasicChannel.Name[5:]},
1303+
[]string{th.BasicChannel.Id},
1304+
},
1305+
{
1306+
"Non mixed case search",
1307+
&model.ChannelSearch{Term: th.BasicChannel.Name},
1308+
[]string{th.BasicChannel.Id},
1309+
},
1310+
{
1311+
"Search private channel name",
1312+
&model.ChannelSearch{Term: th.BasicPrivateChannel.Name},
1313+
[]string{th.BasicPrivateChannel.Id},
1314+
},
1315+
{
1316+
"Search with private channel filter",
1317+
&model.ChannelSearch{Private: true},
1318+
[]string{th.BasicPrivateChannel.Id, th.BasicPrivateChannel2.Id, privateChannel.Id, groupConstrainedChannel.Id},
1319+
},
1320+
{
1321+
"Search with public channel filter",
1322+
&model.ChannelSearch{Term: "SearchAllChannels", Public: true},
1323+
[]string{openChannel.Id},
1324+
},
1325+
{
1326+
"Search with private channel filter",
1327+
&model.ChannelSearch{Term: "SearchAllChannels", Private: true},
1328+
[]string{privateChannel.Id, groupConstrainedChannel.Id},
1329+
},
1330+
{
1331+
"Search with teamIds channel filter",
1332+
&model.ChannelSearch{Term: "SearchAllChannels", TeamIds: []string{th.BasicTeam.Id}},
1333+
[]string{openChannel.Id, privateChannel.Id},
1334+
},
1335+
{
1336+
"Search with deleted without IncludeDeleted filter",
1337+
&model.ChannelSearch{Term: th.BasicDeletedChannel.Name},
1338+
[]string{},
1339+
},
1340+
{
1341+
"Search with deleted IncludeDeleted filter",
1342+
&model.ChannelSearch{Term: th.BasicDeletedChannel.Name, IncludeDeleted: true},
1343+
[]string{th.BasicDeletedChannel.Id},
1344+
},
1345+
{
1346+
"Search with deleted IncludeDeleted filter",
1347+
&model.ChannelSearch{Term: th.BasicDeletedChannel.Name, IncludeDeleted: true},
1348+
[]string{th.BasicDeletedChannel.Id},
1349+
},
1350+
{
1351+
"Search with deleted Deleted filter and empty term",
1352+
&model.ChannelSearch{Term: "", Deleted: true},
1353+
[]string{th.BasicDeletedChannel.Id},
1354+
},
1355+
{
1356+
"Search for group constrained",
1357+
&model.ChannelSearch{Term: "SearchAllChannels", GroupConstrained: true},
1358+
[]string{groupConstrainedChannel.Id},
1359+
},
1360+
{
1361+
"Search for group constrained and public",
1362+
&model.ChannelSearch{Term: "SearchAllChannels", GroupConstrained: true, Public: true},
1363+
[]string{},
1364+
},
1365+
{
1366+
"Search for exclude group constrained",
1367+
&model.ChannelSearch{Term: "SearchAllChannels", ExcludeGroupConstrained: true},
1368+
[]string{openChannel.Id, privateChannel.Id},
1369+
},
1370+
}
1371+
for _, testCase := range testCases {
1372+
t.Run(testCase.Description, func(t *testing.T) {
1373+
channels, resp := th.SystemAdminClient.SearchAllChannels(testCase.Search)
1374+
CheckNoError(t, resp)
1375+
assert.Equal(t, len(testCase.ExpectedChannelIds), len(*channels))
1376+
actualChannelIds := []string{}
1377+
for _, channelWithTeamData := range *channels {
1378+
actualChannelIds = append(actualChannelIds, channelWithTeamData.Channel.Id)
1379+
}
1380+
assert.ElementsMatch(t, testCase.ExpectedChannelIds, actualChannelIds)
1381+
})
1382+
}
13241383

1325-
search.Term = ""
1326-
channels, resp = th.SystemAdminClient.SearchAllChannels(search)
1384+
// Searching with no terms returns all default channels
1385+
allChannels, resp := th.SystemAdminClient.SearchAllChannels(&model.ChannelSearch{Term: ""})
13271386
CheckNoError(t, resp)
1328-
// At least, all the not-deleted channels created during the InitBasic
1329-
assert.True(t, len(*channels) >= 3)
1387+
assert.True(t, len(*allChannels) >= 3)
13301388

1331-
search.Term = th.BasicChannel.Name
1332-
_, resp = Client.SearchAllChannels(search)
1389+
_, resp = Client.SearchAllChannels(&model.ChannelSearch{Term: ""})
13331390
CheckForbiddenStatus(t, resp)
13341391
}
13351392

app/channel.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -2192,11 +2192,17 @@ func (a *App) SearchAllChannels(term string, opts model.ChannelSearchOpts) (*mod
21922192
opts.ExcludeChannelNames = a.DefaultChannelNames()
21932193
}
21942194
storeOpts := store.ChannelSearchOpts{
2195-
ExcludeChannelNames: opts.ExcludeChannelNames,
2196-
NotAssociatedToGroup: opts.NotAssociatedToGroup,
2197-
IncludeDeleted: opts.IncludeDeleted,
2198-
Page: opts.Page,
2199-
PerPage: opts.PerPage,
2195+
ExcludeChannelNames: opts.ExcludeChannelNames,
2196+
NotAssociatedToGroup: opts.NotAssociatedToGroup,
2197+
IncludeDeleted: opts.IncludeDeleted,
2198+
Deleted: opts.Deleted,
2199+
TeamIds: opts.TeamIds,
2200+
GroupConstrained: opts.GroupConstrained,
2201+
ExcludeGroupConstrained: opts.ExcludeGroupConstrained,
2202+
Public: opts.Public,
2203+
Private: opts.Private,
2204+
Page: opts.Page,
2205+
PerPage: opts.PerPage,
22002206
}
22012207

22022208
term = strings.TrimSpace(term)

model/channel.go

+12-6
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,18 @@ type ChannelModeratedRolesPatch struct {
120120
// PerPage number of results per page, if paginated.
121121
//
122122
type ChannelSearchOpts struct {
123-
NotAssociatedToGroup string
124-
ExcludeDefaultChannels bool
125-
IncludeDeleted bool
126-
ExcludeChannelNames []string
127-
Page *int
128-
PerPage *int
123+
NotAssociatedToGroup string
124+
ExcludeDefaultChannels bool
125+
IncludeDeleted bool
126+
Deleted bool
127+
ExcludeChannelNames []string
128+
TeamIds []string
129+
GroupConstrained bool
130+
ExcludeGroupConstrained bool
131+
Public bool
132+
Private bool
133+
Page *int
134+
PerPage *int
129135
}
130136

131137
type ChannelMemberCountByGroup struct {

model/channel_search.go

+12-5
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,18 @@ import (
1111
const CHANNEL_SEARCH_DEFAULT_LIMIT = 50
1212

1313
type ChannelSearch struct {
14-
Term string `json:"term"`
15-
ExcludeDefaultChannels bool `json:"exclude_default_channels"`
16-
NotAssociatedToGroup string `json:"not_associated_to_group"`
17-
Page *int `json:"page,omitempty"`
18-
PerPage *int `json:"per_page,omitempty"`
14+
Term string `json:"term"`
15+
ExcludeDefaultChannels bool `json:"exclude_default_channels"`
16+
NotAssociatedToGroup string `json:"not_associated_to_group"`
17+
TeamIds []string `json:"team_ids"`
18+
GroupConstrained bool `json:"group_constrained"`
19+
ExcludeGroupConstrained bool `json:"exclude_group_constrained"`
20+
Public bool `json:"public"`
21+
Private bool `json:"private"`
22+
IncludeDeleted bool `json:"include_deleted"`
23+
Deleted bool `json:"deleted"`
24+
Page *int `json:"page,omitempty"`
25+
PerPage *int `json:"per_page,omitempty"`
1926
}
2027

2128
// ToJson convert a Channel to a json string

0 commit comments

Comments
 (0)