[MM-18946] [MM-26721] [MM-6842] Cross team search+private channel autocomplete (#18468)

* Show private channels in autocomplete

This is supported in all Engines:
MySQL, Postgres, Bleve, Elasticsearch.

https://mattermost.atlassian.net/browse/MM-18496

```release-note
Private channels will now appear in channel autocomplete.
If you are using Bleve or ElasticSearch, you will have to reindex
the channels again to populate them with the new attributes.
```

A large chunk of this work has been based on the earlier
effort at https://github.com/mattermost/mattermost-server/pull/17804.

Full credit goes to https://github.com/arvinDarmawan.

* Add comment

```release-note
NONE
```

* Adding more tests

```release-note
NONE
```

* fix more tests

```release-note
NONE
```

* tmp

```release-note
NONE
```

* more fixes

```release-note
NONE
```

* add tests

```release-note
NONE
```

* Add review comments from previous PR

```release-note
NONE
```

* Add API to return all channels from all team

```release-note
NONE
```

* Added support for bleve and ES

```release-note
NONE
```

* Streaming response for GetAllChannels

```release-note
NONE
```

* Fix tests

```release-note
NONE
```

* Trigger CI
```release-note
NONE
```

* fix tests

```release-note
NONE
```

* Addressing review comments

```release-note
NONE
```

* Fix lint

```release-note
NONE
```

* Removing flaky test

```release-note
NONE
```

* Address comments

```release-note
NONE
```

* Trigger CI
```release-note
NONE
```

* Added /users/<userid>/channel_members endpoint

```release-note
NONE
```

* Minor edit

```release-note
NONE
```

* Improve embedding

```release-note
NONE
```

* Fix lint error

```release-note
NONE
```

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Agniva De Sarker
2021-10-26 11:30:59 +05:30
коммит произвёл GitHub
родитель 971af6935c
Коммит e24f22745e
30 изменённых файлов: 1907 добавлений и 276 удалений

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

@@ -16,7 +16,12 @@ var searchChannelStoreTests = []searchTest{
{
Name: "Should be able to autocomplete a channel by name",
Fn: testAutocompleteChannelByName,
Tags: []string{EngineAll},
Tags: []string{EngineMySql, EngineElasticSearch, EngineBleve},
},
{
Name: "Should be able to autocomplete a channel by name (Postgres)",
Fn: testAutocompleteChannelByNamePostgres,
Tags: []string{EnginePostgres},
},
{
Name: "Should be able to autocomplete a channel by display name",
@@ -26,7 +31,12 @@ var searchChannelStoreTests = []searchTest{
{
Name: "Should be able to autocomplete a channel by a part of its name when has parts splitted by - character",
Fn: testAutocompleteChannelByNameSplittedWithDashChar,
Tags: []string{EngineAll},
Tags: []string{EngineMySql, EngineElasticSearch, EngineBleve},
},
{
Name: "Should be able to autocomplete a channel by a part of its name when has parts splitted by - character (Postgres)",
Fn: testAutocompleteChannelByNameSplittedWithDashCharPostgres,
Tags: []string{EnginePostgres},
},
{
Name: "Should be able to autocomplete a channel by a part of its name when has parts splitted by _ character",
@@ -46,12 +56,12 @@ var searchChannelStoreTests = []searchTest{
{
Name: "Should be able to autocomplete channels in a case insensitive manner",
Fn: testSearchChannelsInCaseInsensitiveManner,
Tags: []string{EngineAll},
Tags: []string{EngineMySql, EngineElasticSearch, EngineBleve},
},
{
Name: "Should autocomplete only returning public channels",
Fn: testSearchOnlyPublicChannels,
Tags: []string{EngineAll},
Name: "Should be able to autocomplete channels in a case insensitive manner (Postgres)",
Fn: testSearchChannelsInCaseInsensitiveMannerPostgres,
Tags: []string{EnginePostgres},
},
{
Name: "Should support to autocomplete having a hyphen as the last character",
@@ -76,94 +86,148 @@ func TestSearchChannelStore(t *testing.T, s store.Store, testEngine *SearchTestE
}
func testAutocompleteChannelByName(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "Channel Alternate", "Channel Alternate", model.ChannelTypeOpen, false)
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "Channel Alternate", "Channel Alternate", model.ChannelTypeOpen, th.User, false)
require.NoError(t, err)
defer th.deleteChannel(alternate)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, "channel-a", false)
private, err := th.createChannel(th.Team.Id, "channel-altprivate", "Channel AltPrivate", "Channel Private", model.ChannelTypePrivate, th.User, false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, alternate.Id}, res)
defer th.deleteChannel(private)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "channel-a", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, alternate.Id, private.Id}, res)
res2, err := th.Store.Channel().Autocomplete(th.User.Id, "channel-a", false)
require.NoError(t, err)
th.checkChannelIdsMatchWithTeamData(t, []string{th.ChannelBasic.Id, alternate.Id, private.Id, th.ChannelAnotherTeam.Id}, res2)
}
func testAutocompleteChannelByNamePostgres(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "Channel Alternate", "Channel Alternate", model.ChannelTypeOpen, th.User, false)
require.NoError(t, err)
defer th.deleteChannel(alternate)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "channel-a", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, th.ChannelPrivate.Id, alternate.Id}, res)
}
func testAutocompleteChannelByDisplayName(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "ChannelAlternate", "", model.ChannelTypeOpen, false)
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "ChannelAlternate", "", model.ChannelTypeOpen, th.User, false)
require.NoError(t, err)
defer th.deleteChannel(alternate)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, "ChannelA", false)
private, err := th.createChannel(th.Team.Id, "channel-altprivate", "ChannelAltPrivate", "Channel Private", model.ChannelTypePrivate, th.User, false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, alternate.Id}, res)
defer th.deleteChannel(private)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "ChannelA", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, alternate.Id, private.Id}, res)
res2, err := th.Store.Channel().Autocomplete(th.User.Id, "ChannelA", false)
require.NoError(t, err)
th.checkChannelIdsMatchWithTeamData(t, []string{th.ChannelBasic.Id, alternate.Id, private.Id, th.ChannelAnotherTeam.Id}, res2)
}
func testAutocompleteChannelByNameSplittedWithDashChar(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "ChannelAlternate", "", model.ChannelTypeOpen, false)
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "ChannelAlternate", "", model.ChannelTypeOpen, th.User, false)
require.NoError(t, err)
defer th.deleteChannel(alternate)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, "channel-a", false)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "channel-a", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, alternate.Id}, res)
}
func testAutocompleteChannelByNameSplittedWithUnderscoreChar(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel_alternate", "ChannelAlternate", "", model.ChannelTypeOpen, false)
func testAutocompleteChannelByNameSplittedWithDashCharPostgres(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "ChannelAlternate", "", model.ChannelTypeOpen, th.User, false)
require.NoError(t, err)
defer th.deleteChannel(alternate)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, "channel_a", false)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "channel-a", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, th.ChannelPrivate.Id, alternate.Id}, res)
}
func testAutocompleteChannelByNameSplittedWithUnderscoreChar(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel_alternate", "ChannelAlternate", "", model.ChannelTypeOpen, th.User, false)
require.NoError(t, err)
defer th.deleteChannel(alternate)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "channel_a", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{alternate.Id}, res)
res2, err := th.Store.Channel().Autocomplete(th.User.Id, "channel_a", false)
require.NoError(t, err)
th.checkChannelIdsMatchWithTeamData(t, []string{alternate.Id}, res2)
}
func testAutocompleteChannelByDisplayNameSplittedByWhitespaces(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "Channel Alternate", "", model.ChannelTypeOpen, false)
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "Channel Alternate", "", model.ChannelTypeOpen, th.User, false)
require.NoError(t, err)
defer th.deleteChannel(alternate)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, "Channel A", false)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "Channel A", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{alternate.Id}, res)
}
func testAutocompleteAllChannelsIfTermIsEmpty(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "Channel Alternate", "", model.ChannelTypeOpen, false)
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "Channel Alternate", "", model.ChannelTypeOpen, th.User, false)
require.NoError(t, err)
other, err := th.createChannel(th.Team.Id, "other-channel", "Other Channel", "", model.ChannelTypeOpen, false)
other, err := th.createChannel(th.Team.Id, "other-channel", "Other Channel", "", model.ChannelTypeOpen, th.User, false)
require.NoError(t, err)
defer th.deleteChannel(alternate)
defer th.deleteChannel(other)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, "", false)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, alternate.Id, other.Id}, res)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, th.ChannelPrivate.Id, alternate.Id, other.Id}, res)
}
func testSearchChannelsInCaseInsensitiveManner(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "ChannelAlternate", "", model.ChannelTypeOpen, false)
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "ChannelAlternate", "", model.ChannelTypeOpen, th.User, false)
require.NoError(t, err)
defer th.deleteChannel(alternate)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, "channela", false)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "channela", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, alternate.Id}, res)
res, err = th.Store.Channel().AutocompleteInTeam(th.Team.Id, "ChAnNeL-a", false)
res, err = th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "ChAnNeL-a", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, alternate.Id}, res)
res2, err := th.Store.Channel().Autocomplete(th.User.Id, "channela", false)
require.NoError(t, err)
th.checkChannelIdsMatchWithTeamData(t, []string{th.ChannelAnotherTeam.Id, th.ChannelBasic.Id, alternate.Id}, res2)
res2, err = th.Store.Channel().Autocomplete(th.User.Id, "ChAnNeL-a", false)
require.NoError(t, err)
th.checkChannelIdsMatchWithTeamData(t, []string{th.ChannelAnotherTeam.Id, th.ChannelBasic.Id, alternate.Id}, res2)
}
func testSearchOnlyPublicChannels(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "ChannelAlternate", "", model.ChannelTypePrivate, false)
func testSearchChannelsInCaseInsensitiveMannerPostgres(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "ChannelAlternate", "", model.ChannelTypeOpen, th.User, false)
require.NoError(t, err)
defer th.deleteChannel(alternate)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, "channel-a", false)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "channela", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id}, res)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, alternate.Id}, res)
res, err = th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "ChAnNeL-a", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, th.ChannelPrivate.Id, alternate.Id}, res)
}
func testSearchShouldSupportHavingHyphenAsLastCharacter(t *testing.T, th *SearchTestHelper) {
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "ChannelAlternate", "", model.ChannelTypeOpen, false)
alternate, err := th.createChannel(th.Team.Id, "channel-alternate", "ChannelAlternate", "", model.ChannelTypeOpen, th.User, false)
require.NoError(t, err)
defer th.deleteChannel(alternate)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, "channel-", false)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "channel-", false)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, alternate.Id}, res)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, th.ChannelPrivate.Id, alternate.Id}, res)
res2, err := th.Store.Channel().Autocomplete(th.User.Id, "channel-", false)
require.NoError(t, err)
th.checkChannelIdsMatchWithTeamData(t, []string{th.ChannelAnotherTeam.Id, th.ChannelBasic.Id, th.ChannelPrivate.Id, alternate.Id}, res2)
}
func testSearchShouldSupportAutocompleteWithArchivedChannels(t *testing.T, th *SearchTestHelper) {
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, "channel-", true)
res, err := th.Store.Channel().AutocompleteInTeam(th.Team.Id, th.User.Id, "channel-", true)
require.NoError(t, err)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, th.ChannelDeleted.Id}, res)
th.checkChannelIdsMatch(t, []string{th.ChannelBasic.Id, th.ChannelPrivate.Id, th.ChannelDeleted.Id}, res)
}

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

@@ -60,19 +60,19 @@ func (th *SearchTestHelper) SetupBasicFixtures() error {
}
// Create channels
channelBasic, err := th.createChannel(team.Id, "channel-a", "ChannelA", "", model.ChannelTypeOpen, false)
channelBasic, err := th.createChannel(team.Id, "channel-a", "ChannelA", "", model.ChannelTypeOpen, nil, false)
if err != nil {
return err
}
channelPrivate, err := th.createChannel(team.Id, "channel-private", "ChannelPrivate", "", model.ChannelTypePrivate, false)
channelPrivate, err := th.createChannel(team.Id, "channel-private", "ChannelPrivate", "", model.ChannelTypePrivate, nil, false)
if err != nil {
return err
}
channelDeleted, err := th.createChannel(team.Id, "channel-deleted", "ChannelA (deleted)", "", model.ChannelTypeOpen, true)
channelDeleted, err := th.createChannel(team.Id, "channel-deleted", "ChannelA (deleted)", "", model.ChannelTypeOpen, nil, true)
if err != nil {
return err
}
channelAnotherTeam, err := th.createChannel(anotherTeam.Id, "channel-a", "ChannelA", "", model.ChannelTypeOpen, false)
channelAnotherTeam, err := th.createChannel(anotherTeam.Id, "channel-a", "ChannelA", "", model.ChannelTypeOpen, nil, false)
if err != nil {
return err
}
@@ -239,7 +239,7 @@ func (th *SearchTestHelper) deleteBot(botID string) error {
return nil
}
func (th *SearchTestHelper) createChannel(teamID, name, displayName, purpose string, channelType model.ChannelType, deleted bool) (*model.Channel, error) {
func (th *SearchTestHelper) createChannel(teamID, name, displayName, purpose string, channelType model.ChannelType, user *model.User, deleted bool) (*model.Channel, error) {
channel, err := th.Store.Channel().Save(&model.Channel{
TeamId: teamID,
DisplayName: displayName,
@@ -251,6 +251,13 @@ func (th *SearchTestHelper) createChannel(teamID, name, displayName, purpose str
return nil, err
}
if user != nil {
err = th.addUserToChannels(user, []string{channel.Id})
if err != nil {
return nil, err
}
}
if deleted {
err := th.Store.Channel().Delete(channel.Id, model.GetMillis())
if err != nil {
@@ -474,6 +481,15 @@ func (th *SearchTestHelper) checkChannelIdsMatch(t *testing.T, expected []string
require.ElementsMatch(t, expected, channelIds)
}
func (th *SearchTestHelper) checkChannelIdsMatchWithTeamData(t *testing.T, expected []string, results model.ChannelListWithTeamData) {
t.Helper()
channelIds := make([]string, len(results))
for i, channel := range results {
channelIds[i] = channel.Id
}
require.ElementsMatch(t, expected, channelIds)
}
type ByChannelDisplayName model.ChannelList
func (s ByChannelDisplayName) Len() int { return len(s) }