snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 00:29:55 +05:00
commit 240db4e2a7
15309 changed files with 2173406 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
with-expecter: false
packages:
github.com/mattermost/mattermost/server/v8/platform/services/searchengine:
config:
all: true
dir: "platform/services/searchengine/mocks"
filename: "{{.InterfaceName}}.go"
mockname: "{{.InterfaceName}}"
outpkg: mocks
note: "Regenerate this file using `make searchengine-mocks`."

View File

@@ -0,0 +1,75 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package searchengine
import (
"context"
"time"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/request"
)
type SearchEngineInterface interface {
// Start initializes the engine connection. Implementations must set the
// initial health state (e.g. healthy) before returning, because the broker
// may call IsHealthy() immediately after Start() returns.
Start(ctx context.Context) *model.AppError
// Stop tears down the engine connection. Implementations must clear the
// health flag (i.e. set unhealthy) during Stop so that the broker does not
// route queries to a stopped engine.
Stop() *model.AppError
HealthCheck(rctx request.CTX) *model.AppError
GetFullVersion() string
GetVersion() int
GetPlugins() []string
UpdateConfig(cfg *model.Config)
GetName() string
// IsEnabled returns a boolean indicating whether the engine is enabled in the settings
IsEnabled() bool
IsActive() bool
// IsHealthy reports whether the engine is reachable. The initial value is
// set by the engine itself during Start() and cleared during Stop(). After
// startup, only the watcher drives transitions by calling SetHealthy(false)
// on the first health-check failure and SetHealthy(true) on success,
// allowing the broker to skip unhealthy engines immediately instead of
// waiting for full stop/restart.
IsHealthy() bool
// SetHealthy is called by the watcher to update the engine's health status
// based on health-check results. Implementations should not call this
// themselves — Start() and Stop() set the initial/final state directly.
SetHealthy(healthy bool)
IsIndexingEnabled() bool
IsSearchEnabled() bool
IsAutocompletionEnabled() bool
IsIndexingSync() bool
IndexPost(post *model.Post, teamId string, channelType string) *model.AppError
SearchPosts(channels model.ChannelList, searchParams []*model.SearchParams, page, perPage int) ([]string, model.PostSearchMatches, *model.AppError)
DeletePost(post *model.Post) *model.AppError
DeleteChannelPosts(rctx request.CTX, channelID string) *model.AppError
UpdatePostsChannelTypeByChannelId(rctx request.CTX, channelID string, channelType string) *model.AppError
BackfillPostsChannelType(rctx request.CTX, channelIDs []string, channelType string) *model.AppError
DeleteUserPosts(rctx request.CTX, userID string) *model.AppError
// IndexChannel indexes a given channel. The userIDs are only populated
// for private channels.
IndexChannel(rctx request.CTX, channel *model.Channel, userIDs, teamMemberIDs []string) *model.AppError
SyncBulkIndexChannels(rctx request.CTX, channels []*model.Channel, getUserIDsForChannel func(channel *model.Channel) ([]string, error), teamMemberIDs []string) *model.AppError
SearchChannels(teamId, userID, term string, isGuest, includeDeleted bool) ([]string, *model.AppError)
DeleteChannel(channel *model.Channel) *model.AppError
IndexUser(rctx request.CTX, user *model.User, teamsIds, channelsIds []string) *model.AppError
SearchUsersInChannel(teamId, channelId string, restrictedToChannels []string, term string, options *model.UserSearchOptions) ([]string, []string, *model.AppError)
SearchUsersInTeam(teamId string, restrictedToChannels []string, term string, options *model.UserSearchOptions) ([]string, *model.AppError)
DeleteUser(user *model.User) *model.AppError
IndexFile(file *model.FileInfo, channelId string) *model.AppError
SearchFiles(channels model.ChannelList, searchParams []*model.SearchParams, page, perPage int) ([]string, *model.AppError)
DeleteFile(fileID string) *model.AppError
DeletePostFiles(rctx request.CTX, postID string) *model.AppError
DeleteUserFiles(rctx request.CTX, userID string) *model.AppError
DeleteFilesBatch(rctx request.CTX, endTime, limit int64) *model.AppError
TestConfig(rctx request.CTX, cfg *model.Config) *model.AppError
PurgeIndexes(rctx request.CTX) *model.AppError
PurgeIndexList(rctx request.CTX, indexes []string) *model.AppError
RefreshIndexes(rctx request.CTX) *model.AppError
DataRetentionDeleteIndexes(rctx request.CTX, cutoff time.Time) *model.AppError
}

View File

@@ -0,0 +1,902 @@
// Code generated by mockery v2.53.4. DO NOT EDIT.
// Regenerate this file using `make searchengine-mocks`.
package mocks
import (
context "context"
model "github.com/mattermost/mattermost/server/public/model"
request "github.com/mattermost/mattermost/server/public/shared/request"
mock "github.com/stretchr/testify/mock"
time "time"
)
// SearchEngineInterface is an autogenerated mock type for the SearchEngineInterface type
type SearchEngineInterface struct {
mock.Mock
}
// BackfillPostsChannelType provides a mock function with given fields: rctx, channelIDs, channelType
func (_m *SearchEngineInterface) BackfillPostsChannelType(rctx request.CTX, channelIDs []string, channelType string) *model.AppError {
ret := _m.Called(rctx, channelIDs, channelType)
if len(ret) == 0 {
panic("no return value specified for BackfillPostsChannelType")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, []string, string) *model.AppError); ok {
r0 = rf(rctx, channelIDs, channelType)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// DataRetentionDeleteIndexes provides a mock function with given fields: rctx, cutoff
func (_m *SearchEngineInterface) DataRetentionDeleteIndexes(rctx request.CTX, cutoff time.Time) *model.AppError {
ret := _m.Called(rctx, cutoff)
if len(ret) == 0 {
panic("no return value specified for DataRetentionDeleteIndexes")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, time.Time) *model.AppError); ok {
r0 = rf(rctx, cutoff)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// DeleteChannel provides a mock function with given fields: channel
func (_m *SearchEngineInterface) DeleteChannel(channel *model.Channel) *model.AppError {
ret := _m.Called(channel)
if len(ret) == 0 {
panic("no return value specified for DeleteChannel")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(*model.Channel) *model.AppError); ok {
r0 = rf(channel)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// DeleteChannelPosts provides a mock function with given fields: rctx, channelID
func (_m *SearchEngineInterface) DeleteChannelPosts(rctx request.CTX, channelID string) *model.AppError {
ret := _m.Called(rctx, channelID)
if len(ret) == 0 {
panic("no return value specified for DeleteChannelPosts")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, string) *model.AppError); ok {
r0 = rf(rctx, channelID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// DeleteFile provides a mock function with given fields: fileID
func (_m *SearchEngineInterface) DeleteFile(fileID string) *model.AppError {
ret := _m.Called(fileID)
if len(ret) == 0 {
panic("no return value specified for DeleteFile")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(string) *model.AppError); ok {
r0 = rf(fileID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// DeleteFilesBatch provides a mock function with given fields: rctx, endTime, limit
func (_m *SearchEngineInterface) DeleteFilesBatch(rctx request.CTX, endTime int64, limit int64) *model.AppError {
ret := _m.Called(rctx, endTime, limit)
if len(ret) == 0 {
panic("no return value specified for DeleteFilesBatch")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, int64, int64) *model.AppError); ok {
r0 = rf(rctx, endTime, limit)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// DeletePost provides a mock function with given fields: post
func (_m *SearchEngineInterface) DeletePost(post *model.Post) *model.AppError {
ret := _m.Called(post)
if len(ret) == 0 {
panic("no return value specified for DeletePost")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(*model.Post) *model.AppError); ok {
r0 = rf(post)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// DeletePostFiles provides a mock function with given fields: rctx, postID
func (_m *SearchEngineInterface) DeletePostFiles(rctx request.CTX, postID string) *model.AppError {
ret := _m.Called(rctx, postID)
if len(ret) == 0 {
panic("no return value specified for DeletePostFiles")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, string) *model.AppError); ok {
r0 = rf(rctx, postID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// DeleteUser provides a mock function with given fields: user
func (_m *SearchEngineInterface) DeleteUser(user *model.User) *model.AppError {
ret := _m.Called(user)
if len(ret) == 0 {
panic("no return value specified for DeleteUser")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(*model.User) *model.AppError); ok {
r0 = rf(user)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// DeleteUserFiles provides a mock function with given fields: rctx, userID
func (_m *SearchEngineInterface) DeleteUserFiles(rctx request.CTX, userID string) *model.AppError {
ret := _m.Called(rctx, userID)
if len(ret) == 0 {
panic("no return value specified for DeleteUserFiles")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, string) *model.AppError); ok {
r0 = rf(rctx, userID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// DeleteUserPosts provides a mock function with given fields: rctx, userID
func (_m *SearchEngineInterface) DeleteUserPosts(rctx request.CTX, userID string) *model.AppError {
ret := _m.Called(rctx, userID)
if len(ret) == 0 {
panic("no return value specified for DeleteUserPosts")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, string) *model.AppError); ok {
r0 = rf(rctx, userID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// GetFullVersion provides a mock function with no fields
func (_m *SearchEngineInterface) GetFullVersion() string {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetFullVersion")
}
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// GetName provides a mock function with no fields
func (_m *SearchEngineInterface) GetName() string {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetName")
}
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// GetPlugins provides a mock function with no fields
func (_m *SearchEngineInterface) GetPlugins() []string {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetPlugins")
}
var r0 []string
if rf, ok := ret.Get(0).(func() []string); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
return r0
}
// GetVersion provides a mock function with no fields
func (_m *SearchEngineInterface) GetVersion() int {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetVersion")
}
var r0 int
if rf, ok := ret.Get(0).(func() int); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(int)
}
return r0
}
// HealthCheck provides a mock function with given fields: rctx
func (_m *SearchEngineInterface) HealthCheck(rctx request.CTX) *model.AppError {
ret := _m.Called(rctx)
if len(ret) == 0 {
panic("no return value specified for HealthCheck")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX) *model.AppError); ok {
r0 = rf(rctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// IndexChannel provides a mock function with given fields: rctx, channel, userIDs, teamMemberIDs
func (_m *SearchEngineInterface) IndexChannel(rctx request.CTX, channel *model.Channel, userIDs []string, teamMemberIDs []string) *model.AppError {
ret := _m.Called(rctx, channel, userIDs, teamMemberIDs)
if len(ret) == 0 {
panic("no return value specified for IndexChannel")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, *model.Channel, []string, []string) *model.AppError); ok {
r0 = rf(rctx, channel, userIDs, teamMemberIDs)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// IndexFile provides a mock function with given fields: file, channelId
func (_m *SearchEngineInterface) IndexFile(file *model.FileInfo, channelId string) *model.AppError {
ret := _m.Called(file, channelId)
if len(ret) == 0 {
panic("no return value specified for IndexFile")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(*model.FileInfo, string) *model.AppError); ok {
r0 = rf(file, channelId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// IndexPost provides a mock function with given fields: post, teamId, channelType
func (_m *SearchEngineInterface) IndexPost(post *model.Post, teamId string, channelType string) *model.AppError {
ret := _m.Called(post, teamId, channelType)
if len(ret) == 0 {
panic("no return value specified for IndexPost")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(*model.Post, string, string) *model.AppError); ok {
r0 = rf(post, teamId, channelType)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// IndexUser provides a mock function with given fields: rctx, user, teamsIds, channelsIds
func (_m *SearchEngineInterface) IndexUser(rctx request.CTX, user *model.User, teamsIds []string, channelsIds []string) *model.AppError {
ret := _m.Called(rctx, user, teamsIds, channelsIds)
if len(ret) == 0 {
panic("no return value specified for IndexUser")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, *model.User, []string, []string) *model.AppError); ok {
r0 = rf(rctx, user, teamsIds, channelsIds)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// IsActive provides a mock function with no fields
func (_m *SearchEngineInterface) IsActive() bool {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for IsActive")
}
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// IsAutocompletionEnabled provides a mock function with no fields
func (_m *SearchEngineInterface) IsAutocompletionEnabled() bool {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for IsAutocompletionEnabled")
}
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// IsEnabled provides a mock function with no fields
func (_m *SearchEngineInterface) IsEnabled() bool {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for IsEnabled")
}
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// IsHealthy provides a mock function with no fields
func (_m *SearchEngineInterface) IsHealthy() bool {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for IsHealthy")
}
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// IsIndexingEnabled provides a mock function with no fields
func (_m *SearchEngineInterface) IsIndexingEnabled() bool {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for IsIndexingEnabled")
}
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// IsIndexingSync provides a mock function with no fields
func (_m *SearchEngineInterface) IsIndexingSync() bool {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for IsIndexingSync")
}
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// IsSearchEnabled provides a mock function with no fields
func (_m *SearchEngineInterface) IsSearchEnabled() bool {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for IsSearchEnabled")
}
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// PurgeIndexList provides a mock function with given fields: rctx, indexes
func (_m *SearchEngineInterface) PurgeIndexList(rctx request.CTX, indexes []string) *model.AppError {
ret := _m.Called(rctx, indexes)
if len(ret) == 0 {
panic("no return value specified for PurgeIndexList")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, []string) *model.AppError); ok {
r0 = rf(rctx, indexes)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// PurgeIndexes provides a mock function with given fields: rctx
func (_m *SearchEngineInterface) PurgeIndexes(rctx request.CTX) *model.AppError {
ret := _m.Called(rctx)
if len(ret) == 0 {
panic("no return value specified for PurgeIndexes")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX) *model.AppError); ok {
r0 = rf(rctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// RefreshIndexes provides a mock function with given fields: rctx
func (_m *SearchEngineInterface) RefreshIndexes(rctx request.CTX) *model.AppError {
ret := _m.Called(rctx)
if len(ret) == 0 {
panic("no return value specified for RefreshIndexes")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX) *model.AppError); ok {
r0 = rf(rctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// SearchChannels provides a mock function with given fields: teamId, userID, term, isGuest, includeDeleted
func (_m *SearchEngineInterface) SearchChannels(teamId string, userID string, term string, isGuest bool, includeDeleted bool) ([]string, *model.AppError) {
ret := _m.Called(teamId, userID, term, isGuest, includeDeleted)
if len(ret) == 0 {
panic("no return value specified for SearchChannels")
}
var r0 []string
var r1 *model.AppError
if rf, ok := ret.Get(0).(func(string, string, string, bool, bool) ([]string, *model.AppError)); ok {
return rf(teamId, userID, term, isGuest, includeDeleted)
}
if rf, ok := ret.Get(0).(func(string, string, string, bool, bool) []string); ok {
r0 = rf(teamId, userID, term, isGuest, includeDeleted)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
if rf, ok := ret.Get(1).(func(string, string, string, bool, bool) *model.AppError); ok {
r1 = rf(teamId, userID, term, isGuest, includeDeleted)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// SearchFiles provides a mock function with given fields: channels, searchParams, page, perPage
func (_m *SearchEngineInterface) SearchFiles(channels model.ChannelList, searchParams []*model.SearchParams, page int, perPage int) ([]string, *model.AppError) {
ret := _m.Called(channels, searchParams, page, perPage)
if len(ret) == 0 {
panic("no return value specified for SearchFiles")
}
var r0 []string
var r1 *model.AppError
if rf, ok := ret.Get(0).(func(model.ChannelList, []*model.SearchParams, int, int) ([]string, *model.AppError)); ok {
return rf(channels, searchParams, page, perPage)
}
if rf, ok := ret.Get(0).(func(model.ChannelList, []*model.SearchParams, int, int) []string); ok {
r0 = rf(channels, searchParams, page, perPage)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
if rf, ok := ret.Get(1).(func(model.ChannelList, []*model.SearchParams, int, int) *model.AppError); ok {
r1 = rf(channels, searchParams, page, perPage)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// SearchPosts provides a mock function with given fields: channels, searchParams, page, perPage
func (_m *SearchEngineInterface) SearchPosts(channels model.ChannelList, searchParams []*model.SearchParams, page int, perPage int) ([]string, model.PostSearchMatches, *model.AppError) {
ret := _m.Called(channels, searchParams, page, perPage)
if len(ret) == 0 {
panic("no return value specified for SearchPosts")
}
var r0 []string
var r1 model.PostSearchMatches
var r2 *model.AppError
if rf, ok := ret.Get(0).(func(model.ChannelList, []*model.SearchParams, int, int) ([]string, model.PostSearchMatches, *model.AppError)); ok {
return rf(channels, searchParams, page, perPage)
}
if rf, ok := ret.Get(0).(func(model.ChannelList, []*model.SearchParams, int, int) []string); ok {
r0 = rf(channels, searchParams, page, perPage)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
if rf, ok := ret.Get(1).(func(model.ChannelList, []*model.SearchParams, int, int) model.PostSearchMatches); ok {
r1 = rf(channels, searchParams, page, perPage)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(model.PostSearchMatches)
}
}
if rf, ok := ret.Get(2).(func(model.ChannelList, []*model.SearchParams, int, int) *model.AppError); ok {
r2 = rf(channels, searchParams, page, perPage)
} else {
if ret.Get(2) != nil {
r2 = ret.Get(2).(*model.AppError)
}
}
return r0, r1, r2
}
// SearchUsersInChannel provides a mock function with given fields: teamId, channelId, restrictedToChannels, term, options
func (_m *SearchEngineInterface) SearchUsersInChannel(teamId string, channelId string, restrictedToChannels []string, term string, options *model.UserSearchOptions) ([]string, []string, *model.AppError) {
ret := _m.Called(teamId, channelId, restrictedToChannels, term, options)
if len(ret) == 0 {
panic("no return value specified for SearchUsersInChannel")
}
var r0 []string
var r1 []string
var r2 *model.AppError
if rf, ok := ret.Get(0).(func(string, string, []string, string, *model.UserSearchOptions) ([]string, []string, *model.AppError)); ok {
return rf(teamId, channelId, restrictedToChannels, term, options)
}
if rf, ok := ret.Get(0).(func(string, string, []string, string, *model.UserSearchOptions) []string); ok {
r0 = rf(teamId, channelId, restrictedToChannels, term, options)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
if rf, ok := ret.Get(1).(func(string, string, []string, string, *model.UserSearchOptions) []string); ok {
r1 = rf(teamId, channelId, restrictedToChannels, term, options)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).([]string)
}
}
if rf, ok := ret.Get(2).(func(string, string, []string, string, *model.UserSearchOptions) *model.AppError); ok {
r2 = rf(teamId, channelId, restrictedToChannels, term, options)
} else {
if ret.Get(2) != nil {
r2 = ret.Get(2).(*model.AppError)
}
}
return r0, r1, r2
}
// SearchUsersInTeam provides a mock function with given fields: teamId, restrictedToChannels, term, options
func (_m *SearchEngineInterface) SearchUsersInTeam(teamId string, restrictedToChannels []string, term string, options *model.UserSearchOptions) ([]string, *model.AppError) {
ret := _m.Called(teamId, restrictedToChannels, term, options)
if len(ret) == 0 {
panic("no return value specified for SearchUsersInTeam")
}
var r0 []string
var r1 *model.AppError
if rf, ok := ret.Get(0).(func(string, []string, string, *model.UserSearchOptions) ([]string, *model.AppError)); ok {
return rf(teamId, restrictedToChannels, term, options)
}
if rf, ok := ret.Get(0).(func(string, []string, string, *model.UserSearchOptions) []string); ok {
r0 = rf(teamId, restrictedToChannels, term, options)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
if rf, ok := ret.Get(1).(func(string, []string, string, *model.UserSearchOptions) *model.AppError); ok {
r1 = rf(teamId, restrictedToChannels, term, options)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// SetHealthy provides a mock function with given fields: healthy
func (_m *SearchEngineInterface) SetHealthy(healthy bool) {
_m.Called(healthy)
}
// Start provides a mock function with given fields: ctx
func (_m *SearchEngineInterface) Start(ctx context.Context) *model.AppError {
ret := _m.Called(ctx)
if len(ret) == 0 {
panic("no return value specified for Start")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(context.Context) *model.AppError); ok {
r0 = rf(ctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// Stop provides a mock function with no fields
func (_m *SearchEngineInterface) Stop() *model.AppError {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for Stop")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func() *model.AppError); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// SyncBulkIndexChannels provides a mock function with given fields: rctx, channels, getUserIDsForChannel, teamMemberIDs
func (_m *SearchEngineInterface) SyncBulkIndexChannels(rctx request.CTX, channels []*model.Channel, getUserIDsForChannel func(*model.Channel) ([]string, error), teamMemberIDs []string) *model.AppError {
ret := _m.Called(rctx, channels, getUserIDsForChannel, teamMemberIDs)
if len(ret) == 0 {
panic("no return value specified for SyncBulkIndexChannels")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, []*model.Channel, func(*model.Channel) ([]string, error), []string) *model.AppError); ok {
r0 = rf(rctx, channels, getUserIDsForChannel, teamMemberIDs)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// TestConfig provides a mock function with given fields: rctx, cfg
func (_m *SearchEngineInterface) TestConfig(rctx request.CTX, cfg *model.Config) *model.AppError {
ret := _m.Called(rctx, cfg)
if len(ret) == 0 {
panic("no return value specified for TestConfig")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, *model.Config) *model.AppError); ok {
r0 = rf(rctx, cfg)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// UpdateConfig provides a mock function with given fields: cfg
func (_m *SearchEngineInterface) UpdateConfig(cfg *model.Config) {
_m.Called(cfg)
}
// UpdatePostsChannelTypeByChannelId provides a mock function with given fields: rctx, channelID, channelType
func (_m *SearchEngineInterface) UpdatePostsChannelTypeByChannelId(rctx request.CTX, channelID string, channelType string) *model.AppError {
ret := _m.Called(rctx, channelID, channelType)
if len(ret) == 0 {
panic("no return value specified for UpdatePostsChannelTypeByChannelId")
}
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(request.CTX, string, string) *model.AppError); ok {
r0 = rf(rctx, channelID, channelType)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// NewSearchEngineInterface creates a new instance of SearchEngineInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewSearchEngineInterface(t interface {
mock.TestingT
Cleanup(func())
}) *SearchEngineInterface {
mock := &SearchEngineInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package searchengine
import (
"github.com/mattermost/mattermost/server/public/model"
)
func NewBroker(cfg *model.Config) *Broker {
return &Broker{
cfg: cfg,
}
}
func (seb *Broker) RegisterElasticsearchEngine(es SearchEngineInterface) {
seb.ElasticsearchEngine = es
}
type Broker struct {
cfg *model.Config
ElasticsearchEngine SearchEngineInterface
}
func (seb *Broker) UpdateConfig(cfg *model.Config) *model.AppError {
seb.cfg = cfg
if seb.ElasticsearchEngine != nil {
seb.ElasticsearchEngine.UpdateConfig(cfg)
}
return nil
}
func (seb *Broker) GetActiveEngines() []SearchEngineInterface {
engines := []SearchEngineInterface{}
if seb.ElasticsearchEngine != nil && seb.ElasticsearchEngine.IsActive() && seb.ElasticsearchEngine.IsHealthy() {
engines = append(engines, seb.ElasticsearchEngine)
}
return engines
}
func (seb *Broker) ActiveEngine() string {
activeEngines := seb.GetActiveEngines()
if len(activeEngines) > 0 {
return activeEngines[0].GetName()
}
if *seb.cfg.SqlSettings.DisableDatabaseSearch {
return "none"
}
return "database"
}

View File

@@ -0,0 +1,73 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package searchengine
import (
"testing"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/platform/services/searchengine/mocks"
"github.com/stretchr/testify/assert"
)
func TestActiveEngine(t *testing.T) {
newBroker := func(disableDatabaseSearch bool) *Broker {
cfg := &model.Config{}
cfg.SetDefaults()
cfg.SqlSettings.DisableDatabaseSearch = new(disableDatabaseSearch)
return NewBroker(cfg)
}
getESEngine := func(isActive bool, isHealthy bool) SearchEngineInterface {
esMock := &mocks.SearchEngineInterface{}
esMock.On("IsActive").Return(isActive)
esMock.On("IsHealthy").Return(isHealthy)
esMock.On("GetName").Return("elasticsearch")
return esMock
}
t.Run("default to database", func(t *testing.T) {
b := newBroker(false)
assert.Equal(t, "database", b.ActiveEngine())
})
t.Run("no active engine when DisableDatabaseSearch", func(t *testing.T) {
// Disable database search
b := newBroker(true)
assert.Equal(t, "none", b.ActiveEngine())
})
t.Run("switches to elasticsearch when active and healthy", func(t *testing.T) {
b := newBroker(false)
// Use an active, healthy ES engine
b.ElasticsearchEngine = getESEngine(true, true)
assert.Equal(t, "elasticsearch", b.ActiveEngine())
})
t.Run("active engines", func(t *testing.T) {
for _, isActive := range []bool{true, false} {
for _, isHealthy := range []bool{true, false} {
t.Run("(in)active/(un)healthy engines", func(t *testing.T) {
b := newBroker(false)
b.ElasticsearchEngine = getESEngine(isActive, isHealthy)
engines := b.GetActiveEngines()
// Default to database, since DisableDatabaseSearch is false
if isActive && isHealthy {
assert.Equal(t, "elasticsearch", b.ActiveEngine())
assert.Equal(t, []SearchEngineInterface{b.ElasticsearchEngine}, engines, "active healthy engine should appear in active engines")
} else {
assert.Equal(t, "database", b.ActiveEngine())
assert.Empty(t, engines, "inactive or unhealthy engines should not appear in active engines")
}
})
}
}
})
}

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package searchengine
import (
"regexp"
"strings"
"github.com/mattermost/mattermost/server/v8/channels/utils"
)
var EmailRegex = regexp.MustCompile(`^[^\s"]+@[^\s"]+$`)
func GetSuggestionInputsSplitBy(term, splitStr string) []string {
splitTerm := strings.Split(strings.ToLower(term), splitStr)
var initialSuggestionList []string
for i := range splitTerm {
initialSuggestionList = append(initialSuggestionList, strings.Join(splitTerm[i:], splitStr))
}
suggestionList := []string{}
// If splitStr is not an empty space, we create a suggestion with it at the beginning
if splitStr == " " {
suggestionList = initialSuggestionList
} else {
for i, suggestion := range initialSuggestionList {
if i == 0 {
suggestionList = append(suggestionList, suggestion)
} else {
suggestionList = append(suggestionList, splitStr+suggestion, suggestion)
}
}
}
return suggestionList
}
func GetSuggestionInputsSplitByMultiple(term string, splitStrs []string) []string {
suggestionList := []string{}
for _, splitStr := range splitStrs {
suggestionList = append(suggestionList, GetSuggestionInputsSplitBy(term, splitStr)...)
}
return utils.RemoveDuplicatesFromStringArray(suggestionList)
}

View File

@@ -0,0 +1,57 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package searchengine
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestElasticsearchGetSuggestionsSplitBy(t *testing.T) {
testCases := []struct {
Name string
Term string
SplitStr string ``
Expected []string
}{
{
Name: "Single string",
Term: "string",
SplitStr: " ",
Expected: []string{"string"},
},
{
Name: "String with spaces",
Term: "String with spaces",
SplitStr: " ",
Expected: []string{"string with spaces", "with spaces", "spaces"},
},
{
Name: "Username split by a dot",
Term: "name.surname",
SplitStr: ".",
Expected: []string{"name.surname", ".surname", "surname"},
},
{
Name: "String split by several dashes",
Term: "one-two-three",
SplitStr: "-",
Expected: []string{"one-two-three", "-two-three", "two-three", "-three", "three"},
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
res := GetSuggestionInputsSplitBy(tc.Term, tc.SplitStr)
assert.ElementsMatch(t, res, tc.Expected)
})
}
}
func TestElasticsearchGetSuggestionsSplitByMultiple(t *testing.T) {
r1 := GetSuggestionInputsSplitByMultiple("String with user.name", []string{" ", "."})
expectedR1 := []string{"string with user.name", "with user.name", "user.name", ".name", "name"}
assert.ElementsMatch(t, r1, expectedR1)
}