Merge branch 'master' into mpa-playbooks

Этот коммит содержится в:
Giorgi Bochorishvili
2022-12-22 14:04:38 +04:00
родитель df1865001e c252704140
Коммит ee0390db1d
94 изменённых файлов: 2948 добавлений и 477 удалений

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

@@ -24,6 +24,7 @@ import (
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin"
"github.com/mattermost/mattermost-server/v6/product"
"github.com/mattermost/mattermost-server/v6/services/httpservice"
"github.com/mattermost/mattermost-server/v6/services/imageproxy"
"github.com/mattermost/mattermost-server/v6/services/remotecluster"
@@ -651,6 +652,7 @@ type AppIface interface {
GetGroupMemberCount(groupID string, viewRestrictions *model.ViewUsersRestrictions) (int64, *model.AppError)
GetGroupMemberUsers(groupID string) ([]*model.User, *model.AppError)
GetGroupMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, int, *model.AppError)
GetGroupMemberUsersSortedPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions, teammateNameDisplay string) ([]*model.User, int, *model.AppError)
GetGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, *model.AppError)
GetGroupSyncables(groupID string, syncableType model.GroupSyncableType) ([]*model.GroupSyncable, *model.AppError)
GetGroups(page, perPage int, opts model.GroupSearchOpts, viewRestrictions *model.ViewUsersRestrictions) ([]*model.Group, *model.AppError)
@@ -809,7 +811,7 @@ type AppIface interface {
GetTopReactionsForUserSince(userID string, teamID string, opts *model.InsightsOpts) (*model.TopReactionList, *model.AppError)
GetTopThreadsForTeamSince(c request.CTX, teamID, userID string, opts *model.InsightsOpts) (*model.TopThreadList, *model.AppError)
GetTopThreadsForUserSince(c request.CTX, teamID, userID string, opts *model.InsightsOpts) (*model.TopThreadList, *model.AppError)
GetUploadSession(uploadId string) (*model.UploadSession, *model.AppError)
GetUploadSession(c request.CTX, uploadId string) (*model.UploadSession, *model.AppError)
GetUploadSessionsForUser(userID string) ([]*model.UploadSession, *model.AppError)
GetUser(userID string) (*model.User, *model.AppError)
GetUserAccessToken(tokenID string, sanitize bool) (*model.UserAccessToken, *model.AppError)
@@ -868,6 +870,7 @@ type AppIface interface {
HasPermissionToTeam(askingUserId string, teamID string, permission *model.Permission) bool
HasPermissionToUser(askingUserId string, userID string) bool
HasSharedChannel(channelID string) (bool, error)
HooksManager() *product.HooksManager
ImageProxy() *imageproxy.ImageProxy
ImageProxyAdder() func(string) string
ImageProxyRemover() (f func(string) string)
@@ -916,6 +919,7 @@ type AppIface interface {
Notification() einterfaces.NotificationInterface
NotificationsLog() *mlog.Logger
NotifyAndSetWarnMetricAck(warnMetricId string, sender *model.User, forceAck bool, isBot bool) *model.AppError
NotifySelfHostedSignupProgress(progress string, userId string)
NotifySharedChannelUserUpdate(user *model.User)
OpenInteractiveDialog(request model.OpenDialogRequest) *model.AppError
OriginChecker() func(*http.Request) bool
@@ -1147,7 +1151,7 @@ type AppIface interface {
UpdateUserAuth(userID string, userAuth *model.UserAuth) (*model.UserAuth, *model.AppError)
UpdateUserRoles(c request.CTX, userID string, newRoles string, sendWebSocketEvent bool) (*model.User, *model.AppError)
UpdateUserRolesWithUser(c request.CTX, user *model.User, newRoles string, sendWebSocketEvent bool) (*model.User, *model.AppError)
UploadData(c *request.Context, us *model.UploadSession, rd io.Reader) (*model.FileInfo, *model.AppError)
UploadData(c request.CTX, us *model.UploadSession, rd io.Reader) (*model.FileInfo, *model.AppError)
UploadEmojiImage(c request.CTX, id string, imageData *multipart.FileHeader) *model.AppError
UpsertDraft(c *request.Context, draft *model.Draft, connectionID string) (*model.Draft, *model.AppError)
UpsertGroupMember(groupID string, userID string) (*model.GroupMember, *model.AppError)
@@ -1160,4 +1164,5 @@ type AppIface interface {
VerifyUserEmail(userID, email string) *model.AppError
ViewChannel(c request.CTX, view *model.ChannelView, userID string, currentSessionId string, collapsedThreadsSupported bool) (map[string]int64, *model.AppError)
WriteFile(fr io.Reader, path string) (int64, *model.AppError)
WriteFileContext(ctx context.Context, fr io.Reader, path string) (int64, *model.AppError)
}

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

@@ -168,6 +168,7 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) {
model.PermissionCreateCustomGroup.Id,
model.PermissionEditCustomGroup.Id,
model.PermissionDeleteCustomGroup.Id,
model.PermissionRestoreCustomGroup.Id,
model.PermissionManageCustomGroupMembers.Id,
},
"system_post_all": {
@@ -228,6 +229,7 @@ func TestDoEmojisPermissionsMigration(t *testing.T) {
model.PermissionEditCustomGroup.Id,
model.PermissionDeleteCustomGroup.Id,
model.PermissionManageCustomGroupMembers.Id,
model.PermissionRestoreCustomGroup.Id,
model.PermissionListPublicTeams.Id,
model.PermissionJoinPublicTeams.Id,
model.PermissionCreateDirectChannel.Id,

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

@@ -23,6 +23,8 @@ import (
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
const ServerKey product.ServiceKey = "server"
// licenseSvc is added to act as a starting point for future integrated products.
// It has the same signature and functionality with the license related APIs of the plugin-api.
type licenseSvc interface {
@@ -86,19 +88,24 @@ type Channels struct {
}
func init() {
RegisterProduct("channels", ProductManifest{
Initializer: func(s *Server, services map[ServiceKey]any) (Product, error) {
return NewChannels(s, services)
product.RegisterProduct("channels", product.Manifest{
Initializer: func(services map[product.ServiceKey]any) (product.Product, error) {
return NewChannels(services)
},
Dependencies: map[ServiceKey]struct{}{
ConfigKey: {},
LicenseKey: {},
FilestoreKey: {},
Dependencies: map[product.ServiceKey]struct{}{
ServerKey: {},
product.ConfigKey: {},
product.LicenseKey: {},
product.FilestoreKey: {},
},
})
}
func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
func NewChannels(services map[product.ServiceKey]any) (*Channels, error) {
s, ok := services[ServerKey].(*Server)
if !ok {
return nil, errors.New("server not passed")
}
ch := &Channels{
srv: s,
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log()),
@@ -112,10 +119,10 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
// 2. Add the field to *Channels
// 3. Add the service key to the slice.
// 4. Add a new case in the switch statement.
requiredServices := []ServiceKey{
ConfigKey,
LicenseKey,
FilestoreKey,
requiredServices := []product.ServiceKey{
product.ConfigKey,
product.LicenseKey,
product.FilestoreKey,
}
for _, svcKey := range requiredServices {
svc, ok := services[svcKey]
@@ -124,19 +131,19 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
}
switch svcKey {
// Keep adding more services here
case ConfigKey:
case product.ConfigKey:
cfgSvc, ok := svc.(product.ConfigService)
if !ok {
return nil, errors.New("Config service did not satisfy ConfigSvc interface")
}
ch.cfgSvc = cfgSvc
case FilestoreKey:
case product.FilestoreKey:
filestore, ok := svc.(filestore.FileBackend)
if !ok {
return nil, errors.New("Filestore service did not satisfy FileBackend interface")
}
ch.filestore = filestore
case LicenseKey:
case product.LicenseKey:
svc, ok := svc.(licenseSvc)
if !ok {
return nil, errors.New("License service did not satisfy licenseSvc interface")
@@ -198,7 +205,7 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
}
ch.routerSvc = newRouterService()
services[RouterKey] = ch.routerSvc
services[product.RouterKey] = ch.routerSvc
// Setup routes.
pluginsRoute := ch.srv.Router.PathPrefix("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter()
@@ -206,29 +213,29 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
pluginsRoute.HandleFunc("/public/{public_file:.*}", ch.ServePluginPublicRequest)
pluginsRoute.HandleFunc("/{anything:.*}", ch.ServePluginRequest)
services[PostKey] = &postServiceWrapper{
services[product.PostKey] = &postServiceWrapper{
app: &App{ch: ch},
}
services[PermissionsKey] = &permissionsServiceWrapper{
services[product.PermissionsKey] = &permissionsServiceWrapper{
app: &App{ch: ch},
}
services[TeamKey] = &teamServiceWrapper{
services[product.TeamKey] = &teamServiceWrapper{
app: &App{ch: ch},
}
services[BotKey] = &botServiceWrapper{
services[product.BotKey] = &botServiceWrapper{
app: &App{ch: ch},
}
services[HooksKey] = &hooksService{
services[product.HooksKey] = &hooksService{
ch: ch,
}
services[UserKey] = &App{ch: ch}
services[product.UserKey] = &App{ch: ch}
services[PreferencesKey] = &preferencesServiceWrapper{
services[product.PreferencesKey] = &preferencesServiceWrapper{
app: &App{ch: ch},
}
@@ -310,6 +317,10 @@ func (ch *Channels) RequestTrialLicense(requesterID string, users int, termsAcce
receiveEmailsAccepted)
}
func (a *App) HooksManager() *product.HooksManager {
return a.Srv().hooksManager
}
// Ensure hooksService implements `product.HooksService`
var _ product.HooksService = (*hooksService)(nil)
@@ -318,10 +329,6 @@ type hooksService struct {
}
func (s *hooksService) RegisterHooks(productID string, hooks any) error {
if s.ch.pluginsEnvironment == nil {
return errors.New("could not find plugins environment")
}
return s.ch.srv.hooksManager.AddProduct(productID, hooks)
}

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

@@ -185,7 +185,7 @@ func TestExportAllUsers(t *testing.T) {
defer th2.TearDown()
err, i := th2.App.BulkImport(th2.Context, &b, nil, false, 5)
assert.Nil(t, err)
assert.Equal(t, 0, i)
assert.EqualValues(t, 0, i)
users1, err := th1.App.GetUsersFromProfiles(&model.UserGetOptions{
Page: 0,
@@ -323,7 +323,7 @@ func TestExportDMChannelToSelf(t *testing.T) {
// import the exported channel
err, i := th2.App.BulkImport(th2.Context, &b, nil, false, 5)
assert.Nil(t, err)
assert.Equal(t, 0, i)
assert.EqualValues(t, 0, i)
channels, nErr = th2.App.Srv().Store().Channel().GetAllDirectChannelsForExportAfter(1000, "00000000")
require.NoError(t, nErr)

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

@@ -161,6 +161,10 @@ func (a *App) MoveFile(oldPath, newPath string) *model.AppError {
return nil
}
func (a *App) WriteFileContext(ctx context.Context, fr io.Reader, path string) (int64, *model.AppError) {
return a.Srv().writeFileContext(ctx, fr, path)
}
func (a *App) WriteFile(fr io.Reader, path string) (int64, *model.AppError) {
return a.Srv().writeFile(fr, path)
}
@@ -173,6 +177,30 @@ func (s *Server) writeFile(fr io.Reader, path string) (int64, *model.AppError) {
return result, nil
}
func (s *Server) writeFileContext(ctx context.Context, fr io.Reader, path string) (int64, *model.AppError) {
type ContextWriter interface {
WriteFileContext(context.Context, io.Reader, string) (int64, error)
}
var (
fileBackend = s.FileBackend()
written int64
err error
)
// Check if we can provide a custom context, otherwise just use the default method.
if cw, ok := fileBackend.(ContextWriter); ok {
written, err = cw.WriteFileContext(ctx, fr, path)
} else {
written, err = fileBackend.WriteFile(fr, path)
}
if err != nil {
return written, model.NewAppError("WriteFile", "api.file.write_file.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return written, nil
}
func (a *App) AppendFile(fr io.Reader, path string) (int64, *model.AppError) {
result, nErr := a.FileBackend().AppendFile(fr, path)
if nErr != nil {
@@ -796,6 +824,7 @@ func (t *UploadFileTask) postprocessImage(file io.Reader) {
_, aerr := t.writeFile(r, path)
if aerr != nil {
mlog.Error("Unable to upload", mlog.String("path", path), mlog.Err(aerr))
r.CloseWithError(aerr) // always returns nil
return
}
}

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

@@ -250,8 +250,8 @@ func (a *App) GetGroupMemberUsers(groupID string) ([]*model.User, *model.AppErro
return users, nil
}
func (a *App) GetGroupMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, int, *model.AppError) {
members, err := a.Srv().Store().Group().GetMemberUsersPage(groupID, page, perPage, viewRestrictions)
func (a *App) GetGroupMemberUsersSortedPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions, teammateNameDisplay string) ([]*model.User, int, *model.AppError) {
members, err := a.Srv().Store().Group().GetMemberUsersSortedPage(groupID, page, perPage, viewRestrictions, teammateNameDisplay)
if err != nil {
return nil, 0, model.NewAppError("GetGroupMemberUsersPage", "app.select_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
@@ -263,6 +263,10 @@ func (a *App) GetGroupMemberUsersPage(groupID string, page int, perPage int, vie
return a.sanitizeProfiles(members, false), int(count), nil
}
func (a *App) GetGroupMemberUsersPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, int, *model.AppError) {
return a.GetGroupMemberUsersSortedPage(groupID, page, perPage, viewRestrictions, model.ShowUsername)
}
func (a *App) GetUsersNotInGroupPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions) ([]*model.User, *model.AppError) {
members, err := a.Srv().Store().Group().GetNonMemberUsersPage(groupID, page, perPage, viewRestrictions)
if err != nil {

21
app/hosted_customer.go Обычный файл
Просмотреть файл

@@ -0,0 +1,21 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"github.com/mattermost/mattermost-server/v6/model"
)
func (a *App) NotifySelfHostedSignupProgress(progress string, userId string) {
// this is an event only the relevant admin should receive.
// If there is no progress, there is nothing to report.
// If there is no userId, we do not want to mistakenly broadcast to all users.
if progress == "" || userId == "" {
return
}
message := model.NewWebSocketEvent(model.WebsocketEventHostedCustomerSignupProgressUpdated, "", "", userId, nil, "")
message.Add("progress", progress)
a.Srv().Platform().Publish(message)
}

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

@@ -6,7 +6,6 @@ package app
import (
"archive/zip"
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
@@ -26,6 +25,7 @@ type ReactionImportData = imports.ReactionImportData // part of the app interfac
const (
importMultiplePostsThreshold = 1000
maxScanTokenSize = 16 * 1024 * 1024 // Need to set a higher limit than default because some customers cross the limit. See MM-22314
statusUpdateAfterLines = 8192
)
func stopOnError(c request.CTX, err imports.LineImportWorkerError) bool {
@@ -41,7 +41,7 @@ func stopOnError(c request.CTX, err imports.LineImportWorkerError) bool {
}
}
func processAttachmentPaths(files *[]imports.AttachmentImportData, basePath string, filesMap map[string]*zip.File) error {
func processAttachmentPaths(c request.CTX, files *[]imports.AttachmentImportData, basePath string, filesMap map[string]*zip.File) error {
if files == nil {
return nil
}
@@ -61,20 +61,20 @@ func processAttachmentPaths(files *[]imports.AttachmentImportData, basePath stri
return nil
}
func processAttachments(line *imports.LineImportData, basePath string, filesMap map[string]*zip.File) error {
func processAttachments(c request.CTX, line *imports.LineImportData, basePath string, filesMap map[string]*zip.File) error {
var ok bool
switch line.Type {
case "post", "direct_post":
var replies []imports.ReplyImportData
if line.Type == "direct_post" {
if err := processAttachmentPaths(line.DirectPost.Attachments, basePath, filesMap); err != nil {
if err := processAttachmentPaths(c, line.DirectPost.Attachments, basePath, filesMap); err != nil {
return err
}
if line.DirectPost.Replies != nil {
replies = *line.DirectPost.Replies
}
} else {
if err := processAttachmentPaths(line.Post.Attachments, basePath, filesMap); err != nil {
if err := processAttachmentPaths(c, line.Post.Attachments, basePath, filesMap); err != nil {
return err
}
if line.Post.Replies != nil {
@@ -82,7 +82,7 @@ func processAttachments(line *imports.LineImportData, basePath string, filesMap
}
}
for _, reply := range replies {
if err := processAttachmentPaths(reply.Attachments, basePath, filesMap); err != nil {
if err := processAttachmentPaths(c, reply.Attachments, basePath, filesMap); err != nil {
return err
}
}
@@ -112,6 +112,15 @@ func processAttachments(line *imports.LineImportData, basePath string, filesMap
}
func (a *App) bulkImportWorker(c request.CTX, dryRun bool, wg *sync.WaitGroup, lines <-chan imports.LineImportWorkerData, errors chan<- imports.LineImportWorkerError) {
workerID := model.NewId()
processedLines := uint64(0)
c.Logger().Info("Started new bulk import worker", mlog.String("bulk_import_worker_id", workerID))
defer func() {
wg.Done()
c.Logger().Info("Bulk import worker finished", mlog.String("bulk_import_worker_id", workerID), mlog.Uint64("processed_lines", processedLines))
}()
postLines := []imports.LineImportWorkerData{}
directPostLines := []imports.LineImportWorkerData{}
for line := range lines {
@@ -143,6 +152,11 @@ func (a *App) bulkImportWorker(c request.CTX, dryRun bool, wg *sync.WaitGroup, l
errors <- imports.LineImportWorkerError{Error: err, LineNumber: line.LineNumber}
}
}
processedLines++
if processedLines%statusUpdateAfterLines == 0 {
c.Logger().Info("Worker progress", mlog.String("bulk_import_worker_id", workerID), mlog.Uint64("processed_lines", processedLines))
}
}
if len(postLines) > 0 {
@@ -155,7 +169,6 @@ func (a *App) bulkImportWorker(c request.CTX, dryRun bool, wg *sync.WaitGroup, l
errors <- imports.LineImportWorkerError{Error: err, LineNumber: errLine}
}
}
wg.Done()
}
func (a *App) BulkImport(c *request.Context, jsonlReader io.Reader, attachmentsReader *zip.Reader, dryRun bool, workers int) (*model.AppError, int) {
@@ -194,15 +207,17 @@ func (a *App) bulkImport(c request.CTX, jsonlReader io.Reader, attachmentsReader
}
for scanner.Scan() {
decoder := json.NewDecoder(bytes.NewReader(scanner.Bytes()))
lineNumber++
if lineNumber%statusUpdateAfterLines == 0 {
c.Logger().Info("Reader progress", mlog.Int("processed_lines", lineNumber))
}
var line imports.LineImportData
if err := decoder.Decode(&line); err != nil {
if err := json.Unmarshal(scanner.Bytes(), &line); err != nil {
return model.NewAppError("BulkImport", "app.import.bulk_import.json_decode.error", nil, "", http.StatusBadRequest).Wrap(err), lineNumber
}
if err := processAttachments(&line, importPath, attachedFiles); err != nil {
if err := processAttachments(c, &line, importPath, attachedFiles); err != nil {
c.Logger().Warn("Error while processing import attachments. Objects might be broken.", mlog.Err(err))
}
@@ -222,6 +237,12 @@ func (a *App) bulkImport(c request.CTX, jsonlReader io.Reader, attachmentsReader
if line.Type != lastLineType {
// Only clear the worker queue if is not the first data entry
if lineNumber != 2 {
c.Logger().Info(
"Finished parsing segment, waiting for workers to finish",
mlog.String("old_segment", lastLineType),
mlog.String("new_segment", line.Type),
)
// Changing type. Clear out the worker queue before continuing.
close(linesChan)
wg.Wait()
@@ -235,6 +256,13 @@ func (a *App) bulkImport(c request.CTX, jsonlReader io.Reader, attachmentsReader
}
}
c.Logger().Info(
"Starting workers for new segment",
mlog.String("old_segment", lastLineType),
mlog.String("new_segment", line.Type),
mlog.Int("workers", workers),
)
// Set up the workers and channel for this type.
lastLineType = line.Type
linesChan = make(chan imports.LineImportWorkerData, workers)
@@ -290,7 +318,7 @@ func (a *App) importLine(c request.CTX, line imports.LineImportData, dryRun bool
if line.Scheme == nil {
return model.NewAppError("BulkImport", "app.import.import_line.null_scheme.error", nil, "", http.StatusBadRequest)
}
return a.importScheme(line.Scheme, dryRun)
return a.importScheme(c, line.Scheme, dryRun)
case line.Type == "team":
if line.Team == nil {
return model.NewAppError("BulkImport", "app.import.import_line.null_team.error", nil, "", http.StatusBadRequest)
@@ -315,7 +343,7 @@ func (a *App) importLine(c request.CTX, line imports.LineImportData, dryRun bool
if line.Emoji == nil {
return model.NewAppError("BulkImport", "app.import.import_line.null_emoji.error", nil, "", http.StatusBadRequest)
}
return a.importEmoji(line.Emoji, dryRun)
return a.importEmoji(c, line.Emoji, dryRun)
default:
return model.NewAppError("BulkImport", "app.import.import_line.unknown_line_type.error", map[string]any{"Type": line.Type}, "", http.StatusBadRequest)
}

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

@@ -15,6 +15,7 @@ import (
"path"
"strings"
"github.com/mattermost/logr/v2"
"github.com/mattermost/mattermost-server/v6/app/imports"
"github.com/mattermost/mattermost-server/v6/app/request"
"github.com/mattermost/mattermost-server/v6/app/teams"
@@ -25,13 +26,16 @@ import (
"github.com/mattermost/mattermost-server/v6/utils"
)
//
// -- Bulk Import Functions --
// These functions import data directly into the database. Security and permission checks are bypassed but validity is
// still enforced.
//
func (a *App) importScheme(c request.CTX, data *imports.SchemeImportData, dryRun bool) *model.AppError {
var fields []logr.Field
if data != nil && data.Name != nil {
fields = append(fields, mlog.String("schema_name", *data.Name))
}
c.Logger().Info("Validating schema", fields...)
func (a *App) importScheme(data *imports.SchemeImportData, dryRun bool) *model.AppError {
if err := imports.ValidateSchemeImportData(data); err != nil {
return err
}
@@ -41,6 +45,8 @@ func (a *App) importScheme(data *imports.SchemeImportData, dryRun bool) *model.A
return nil
}
c.Logger().Info("Importing schema", fields...)
scheme, err := a.GetSchemeByName(*data.Name)
if err != nil {
scheme = new(model.Scheme)
@@ -68,12 +74,12 @@ func (a *App) importScheme(data *imports.SchemeImportData, dryRun bool) *model.A
if scheme.Scope == model.SchemeScopeTeam {
data.DefaultTeamAdminRole.Name = &scheme.DefaultTeamAdminRole
if err := a.importRole(data.DefaultTeamAdminRole, dryRun, true); err != nil {
if err := a.importRole(c, data.DefaultTeamAdminRole, dryRun, true); err != nil {
return err
}
data.DefaultTeamUserRole.Name = &scheme.DefaultTeamUserRole
if err := a.importRole(data.DefaultTeamUserRole, dryRun, true); err != nil {
if err := a.importRole(c, data.DefaultTeamUserRole, dryRun, true); err != nil {
return err
}
@@ -83,19 +89,19 @@ func (a *App) importScheme(data *imports.SchemeImportData, dryRun bool) *model.A
}
}
data.DefaultTeamGuestRole.Name = &scheme.DefaultTeamGuestRole
if err := a.importRole(data.DefaultTeamGuestRole, dryRun, true); err != nil {
if err := a.importRole(c, data.DefaultTeamGuestRole, dryRun, true); err != nil {
return err
}
}
if scheme.Scope == model.SchemeScopeTeam || scheme.Scope == model.SchemeScopeChannel {
data.DefaultChannelAdminRole.Name = &scheme.DefaultChannelAdminRole
if err := a.importRole(data.DefaultChannelAdminRole, dryRun, true); err != nil {
if err := a.importRole(c, data.DefaultChannelAdminRole, dryRun, true); err != nil {
return err
}
data.DefaultChannelUserRole.Name = &scheme.DefaultChannelUserRole
if err := a.importRole(data.DefaultChannelUserRole, dryRun, true); err != nil {
if err := a.importRole(c, data.DefaultChannelUserRole, dryRun, true); err != nil {
return err
}
@@ -105,7 +111,7 @@ func (a *App) importScheme(data *imports.SchemeImportData, dryRun bool) *model.A
}
}
data.DefaultChannelGuestRole.Name = &scheme.DefaultChannelGuestRole
if err := a.importRole(data.DefaultChannelGuestRole, dryRun, true); err != nil {
if err := a.importRole(c, data.DefaultChannelGuestRole, dryRun, true); err != nil {
return err
}
}
@@ -113,8 +119,15 @@ func (a *App) importScheme(data *imports.SchemeImportData, dryRun bool) *model.A
return nil
}
func (a *App) importRole(data *imports.RoleImportData, dryRun bool, isSchemeRole bool) *model.AppError {
func (a *App) importRole(c request.CTX, data *imports.RoleImportData, dryRun bool, isSchemeRole bool) *model.AppError {
var fields []logr.Field
if data != nil && data.Name != nil {
fields = append(fields, mlog.String("role_name", *data.Name))
}
if !isSchemeRole {
c.Logger().Info("Validating role", fields...)
if err := imports.ValidateRoleImportData(data); err != nil {
return err
}
@@ -125,6 +138,8 @@ func (a *App) importRole(data *imports.RoleImportData, dryRun bool, isSchemeRole
return nil
}
c.Logger().Info("Importing role", fields...)
role, err := a.GetRoleByName(context.Background(), *data.Name)
if err != nil {
role = new(model.Role)
@@ -160,6 +175,12 @@ func (a *App) importRole(data *imports.RoleImportData, dryRun bool, isSchemeRole
}
func (a *App) importTeam(c request.CTX, data *imports.TeamImportData, dryRun bool) *model.AppError {
var fields []logr.Field
if data != nil && data.Name != nil {
fields = append(fields, mlog.String("team_name", *data.Name))
}
c.Logger().Info("Validating team", fields...)
if err := imports.ValidateTeamImportData(data); err != nil {
return err
}
@@ -169,6 +190,8 @@ func (a *App) importTeam(c request.CTX, data *imports.TeamImportData, dryRun boo
return nil
}
c.Logger().Info("Importing team", fields...)
var team *model.Team
team, err := a.Srv().Store().Team().GetByName(*data.Name)
@@ -228,6 +251,12 @@ func (a *App) importTeam(c request.CTX, data *imports.TeamImportData, dryRun boo
}
func (a *App) importChannel(c request.CTX, data *imports.ChannelImportData, dryRun bool) *model.AppError {
var fields []logr.Field
if data != nil && data.Name != nil {
fields = append(fields, mlog.String("channel_name", *data.Name))
}
c.Logger().Info("Validating channel", fields...)
if err := imports.ValidateChannelImportData(data); err != nil {
return err
}
@@ -237,6 +266,8 @@ func (a *App) importChannel(c request.CTX, data *imports.ChannelImportData, dryR
return nil
}
c.Logger().Info("Importing channel", fields...)
team, err := a.Srv().Store().Team().GetByName(*data.Team)
if err != nil {
return model.NewAppError("BulkImport", "app.import.import_channel.team_not_found.error", map[string]any{"TeamName": *data.Team}, "", http.StatusBadRequest).Wrap(err)
@@ -293,6 +324,12 @@ func (a *App) importChannel(c request.CTX, data *imports.ChannelImportData, dryR
}
func (a *App) importUser(c request.CTX, data *imports.UserImportData, dryRun bool) *model.AppError {
var fields []logr.Field
if data != nil && data.Username != nil {
fields = append(fields, mlog.String("user_name", *data.Username))
}
c.Logger().Info("Validating user", fields...)
if err := imports.ValidateUserImportData(data); err != nil {
return err
}
@@ -302,6 +339,8 @@ func (a *App) importUser(c request.CTX, data *imports.UserImportData, dryRun boo
return nil
}
c.Logger().Info("Importing user", fields...)
// We want to avoid database writes if nothing has changed.
hasUserChanged := false
hasNotifyPropsChanged := false
@@ -1214,6 +1253,8 @@ func (a *App) importAttachment(c request.CTX, data *imports.AttachmentImportData
defer zipFile.Close()
name = data.Data.Name
file = zipFile.(io.Reader)
c.Logger().Info("Preparing file upload from ZIP", mlog.String("file_name", name), mlog.Uint64("file_size", data.Data.UncompressedSize64))
} else {
realFile, err := os.Open(*data.Path)
if err != nil {
@@ -1222,6 +1263,12 @@ func (a *App) importAttachment(c request.CTX, data *imports.AttachmentImportData
defer realFile.Close()
name = realFile.Name()
file = realFile
fields := []logr.Field{mlog.String("file_name", name)}
if info, err := realFile.Stat(); err != nil {
fields = append(fields, mlog.Int64("file_size", info.Size()))
}
c.Logger().Info("Preparing file upload from file system", fields...)
}
timestamp := utils.TimeFromMillis(post.CreateAt)
@@ -1241,7 +1288,8 @@ func (a *App) importAttachment(c request.CTX, data *imports.AttachmentImportData
if oldFile.Name != path.Base(name) || oldFile.Size != int64(len(fileData)) {
continue
}
// check md5
// check sha1
newHash := sha1.Sum(fileData)
oldFileData, err := a.getFileIgnoreCloudLimit(oldFile.Id)
if err != nil {
@@ -1260,7 +1308,7 @@ func (a *App) importAttachment(c request.CTX, data *imports.AttachmentImportData
fileInfo, appErr := a.DoUploadFile(c, timestamp, teamID, post.ChannelId, post.UserId, name, fileData)
if appErr != nil {
mlog.Error("Failed to upload file:", mlog.Err(appErr))
mlog.Error("Failed to upload file", mlog.Err(appErr), mlog.String("file_name", name))
return nil, appErr
}
@@ -1358,6 +1406,8 @@ func (a *App) importMultiplePostLines(c request.CTX, lines []imports.LineImportW
return 0, nil
}
c.Logger().Info("Validating post lines", mlog.Int("count", len(lines)), mlog.Int("first_line", lines[0].LineNumber))
for _, line := range lines {
if err := imports.ValidatePostImportData(line.Post, a.MaxPostSize()); err != nil {
return line.LineNumber, err
@@ -1369,6 +1419,8 @@ func (a *App) importMultiplePostLines(c request.CTX, lines []imports.LineImportW
return 0, nil
}
c.Logger().Info("Importing post lines", mlog.Int("count", len(lines)), mlog.Int("first_line", lines[0].LineNumber))
usernames := []string{}
teamNames := make([]string, len(lines))
postsData := make([]*imports.PostImportData, len(lines))
@@ -1855,7 +1907,13 @@ func (a *App) importMultipleDirectPostLines(c request.CTX, lines []imports.LineI
return 0, nil
}
func (a *App) importEmoji(data *imports.EmojiImportData, dryRun bool) *model.AppError {
func (a *App) importEmoji(c request.CTX, data *imports.EmojiImportData, dryRun bool) *model.AppError {
var fields []logr.Field
if data != nil && data.Name != nil {
fields = append(fields, mlog.String("emoji_name", *data.Name))
}
c.Logger().Info("Validating emoji", fields...)
aerr := imports.ValidateEmojiImportData(data)
if aerr != nil {
if aerr.Id == "model.emoji.system_emoji_name.app_error" {
@@ -1870,6 +1928,8 @@ func (a *App) importEmoji(data *imports.EmojiImportData, dryRun bool) *model.App
return nil
}
c.Logger().Info("Importing emoji", fields...)
var emoji *model.Emoji
emoji, err := a.Srv().Store().Emoji().GetByName(context.Background(), *data.Name, true)

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

@@ -65,7 +65,7 @@ func TestImportImportScheme(t *testing.T) {
Description: ptrStr("description"),
}
err := th.App.importScheme(&data, true)
err := th.App.importScheme(th.Context, &data, true)
require.NotNil(t, err, "Should have failed to import.")
_, nErr := th.App.Srv().Store().Scheme().GetByName(*data.Name)
@@ -74,7 +74,7 @@ func TestImportImportScheme(t *testing.T) {
// Try importing a valid scheme in dryRun mode.
data.DisplayName = ptrStr("display name")
err = th.App.importScheme(&data, true)
err = th.App.importScheme(th.Context, &data, true)
require.Nil(t, err, "Should have succeeded.")
_, nErr = th.App.Srv().Store().Scheme().GetByName(*data.Name)
@@ -83,7 +83,7 @@ func TestImportImportScheme(t *testing.T) {
// Try importing an invalid scheme.
data.DisplayName = nil
err = th.App.importScheme(&data, false)
err = th.App.importScheme(th.Context, &data, false)
require.NotNil(t, err, "Should have failed to import.")
_, nErr = th.App.Srv().Store().Scheme().GetByName(*data.Name)
@@ -92,7 +92,7 @@ func TestImportImportScheme(t *testing.T) {
// Try importing a valid scheme with all params set.
data.DisplayName = ptrStr("display name")
err = th.App.importScheme(&data, false)
err = th.App.importScheme(th.Context, &data, false)
require.Nil(t, err, "Should have succeeded.")
scheme, nErr := th.App.Srv().Store().Scheme().GetByName(*data.Name)
@@ -149,7 +149,7 @@ func TestImportImportScheme(t *testing.T) {
data.DisplayName = ptrStr("new display name")
data.Description = ptrStr("new description")
err = th.App.importScheme(&data, false)
err = th.App.importScheme(th.Context, &data, false)
require.Nil(t, err, "Should have succeeded: %v", err)
scheme, nErr = th.App.Srv().Store().Scheme().GetByName(*data.Name)
@@ -205,7 +205,7 @@ func TestImportImportScheme(t *testing.T) {
// Try changing the scope of the scheme and reimporting.
data.Scope = ptrStr("channel")
err = th.App.importScheme(&data, false)
err = th.App.importScheme(th.Context, &data, false)
require.NotNil(t, err, "Should have failed to import.")
scheme, nErr = th.App.Srv().Store().Scheme().GetByName(*data.Name)
@@ -252,7 +252,7 @@ func TestImportImportSchemeWithoutGuestRoles(t *testing.T) {
Description: ptrStr("description"),
}
err := th.App.importScheme(&data, true)
err := th.App.importScheme(th.Context, &data, true)
require.NotNil(t, err, "Should have failed to import.")
_, nErr := th.App.Srv().Store().Scheme().GetByName(*data.Name)
@@ -261,7 +261,7 @@ func TestImportImportSchemeWithoutGuestRoles(t *testing.T) {
// Try importing a valid scheme in dryRun mode.
data.DisplayName = ptrStr("display name")
err = th.App.importScheme(&data, true)
err = th.App.importScheme(th.Context, &data, true)
require.Nil(t, err, "Should have succeeded.")
_, nErr = th.App.Srv().Store().Scheme().GetByName(*data.Name)
@@ -270,7 +270,7 @@ func TestImportImportSchemeWithoutGuestRoles(t *testing.T) {
// Try importing an invalid scheme.
data.DisplayName = nil
err = th.App.importScheme(&data, false)
err = th.App.importScheme(th.Context, &data, false)
require.NotNil(t, err, "Should have failed to import.")
_, nErr = th.App.Srv().Store().Scheme().GetByName(*data.Name)
@@ -279,7 +279,7 @@ func TestImportImportSchemeWithoutGuestRoles(t *testing.T) {
// Try importing a valid scheme with all params set.
data.DisplayName = ptrStr("display name")
err = th.App.importScheme(&data, false)
err = th.App.importScheme(th.Context, &data, false)
require.Nil(t, err, "Should have succeeded.")
scheme, nErr := th.App.Srv().Store().Scheme().GetByName(*data.Name)
@@ -336,7 +336,7 @@ func TestImportImportSchemeWithoutGuestRoles(t *testing.T) {
data.DisplayName = ptrStr("new display name")
data.Description = ptrStr("new description")
err = th.App.importScheme(&data, false)
err = th.App.importScheme(th.Context, &data, false)
require.Nil(t, err, "Should have succeeded: %v", err)
scheme, nErr = th.App.Srv().Store().Scheme().GetByName(*data.Name)
@@ -392,7 +392,7 @@ func TestImportImportSchemeWithoutGuestRoles(t *testing.T) {
// Try changing the scope of the scheme and reimporting.
data.Scope = ptrStr("channel")
err = th.App.importScheme(&data, false)
err = th.App.importScheme(th.Context, &data, false)
require.NotNil(t, err, "Should have failed to import.")
scheme, nErr = th.App.Srv().Store().Scheme().GetByName(*data.Name)
@@ -414,7 +414,7 @@ func TestImportImportRole(t *testing.T) {
Name: &rid1,
}
err := th.App.importRole(&data, true, false)
err := th.App.importRole(th.Context, &data, true, false)
require.NotNil(t, err, "Should have failed to import.")
_, nErr := th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
@@ -423,7 +423,7 @@ func TestImportImportRole(t *testing.T) {
// Try importing the valid role in dryRun mode.
data.DisplayName = ptrStr("display name")
err = th.App.importRole(&data, true, false)
err = th.App.importRole(th.Context, &data, true, false)
require.Nil(t, err, "Should have succeeded.")
_, nErr = th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
@@ -432,7 +432,7 @@ func TestImportImportRole(t *testing.T) {
// Try importing an invalid role.
data.DisplayName = nil
err = th.App.importRole(&data, false, false)
err = th.App.importRole(th.Context, &data, false, false)
require.NotNil(t, err, "Should have failed to import.")
_, nErr = th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
@@ -443,7 +443,7 @@ func TestImportImportRole(t *testing.T) {
data.Description = ptrStr("description")
data.Permissions = &[]string{"invite_user", "add_user_to_team"}
err = th.App.importRole(&data, false, false)
err = th.App.importRole(th.Context, &data, false, false)
require.Nil(t, err, "Should have succeeded.")
role, nErr := th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
@@ -461,7 +461,7 @@ func TestImportImportRole(t *testing.T) {
data.Description = ptrStr("description")
data.Permissions = &[]string{"use_slash_commands"}
err = th.App.importRole(&data, false, true)
err = th.App.importRole(th.Context, &data, false, true)
require.Nil(t, err, "Should have succeeded. %v", err)
role, nErr = th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
@@ -480,7 +480,7 @@ func TestImportImportRole(t *testing.T) {
DisplayName: ptrStr("new display name again"),
}
err = th.App.importRole(&data2, false, false)
err = th.App.importRole(th.Context, &data2, false, false)
require.Nil(t, err, "Should have succeeded.")
role, nErr = th.App.Srv().Store().Role().GetByName(context.Background(), rid1)
@@ -1384,7 +1384,7 @@ func TestImportImportUser(t *testing.T) {
Description: ptrStr("description"),
}
appErr = th.App.importScheme(teamSchemeData, false)
appErr = th.App.importScheme(th.Context, teamSchemeData, false)
assert.Nil(t, appErr)
teamScheme, nErr := th.App.Srv().Store().Scheme().GetByName(*teamSchemeData.Name)
@@ -4151,7 +4151,7 @@ func TestImportImportEmoji(t *testing.T) {
testImage := filepath.Join(testsDir, "test.png")
data := imports.EmojiImportData{Name: ptrStr(model.NewId())}
appErr := th.App.importEmoji(&data, true)
appErr := th.App.importEmoji(th.Context, &data, true)
assert.NotNil(t, appErr, "Invalid emoji should have failed dry run")
emoji, nErr := th.App.Srv().Store().Emoji().GetByName(context.Background(), *data.Name, true)
@@ -4159,35 +4159,35 @@ func TestImportImportEmoji(t *testing.T) {
assert.Error(t, nErr)
data.Image = ptrStr(testImage)
appErr = th.App.importEmoji(&data, true)
appErr = th.App.importEmoji(th.Context, &data, true)
assert.Nil(t, appErr, "Valid emoji should have passed dry run")
data = imports.EmojiImportData{Name: ptrStr(model.NewId())}
appErr = th.App.importEmoji(&data, false)
appErr = th.App.importEmoji(th.Context, &data, false)
assert.NotNil(t, appErr, "Invalid emoji should have failed apply mode")
data.Image = ptrStr("non-existent-file")
appErr = th.App.importEmoji(&data, false)
appErr = th.App.importEmoji(th.Context, &data, false)
assert.NotNil(t, appErr, "Emoji with bad image file should have failed apply mode")
data.Image = ptrStr(testImage)
appErr = th.App.importEmoji(&data, false)
appErr = th.App.importEmoji(th.Context, &data, false)
assert.Nil(t, appErr, "Valid emoji should have succeeded apply mode")
emoji, nErr = th.App.Srv().Store().Emoji().GetByName(context.Background(), *data.Name, true)
assert.NotNil(t, emoji, "Emoji should have been imported")
assert.NoError(t, nErr, "Emoji should have been imported without any error")
appErr = th.App.importEmoji(&data, false)
appErr = th.App.importEmoji(th.Context, &data, false)
assert.Nil(t, appErr, "Second run should have succeeded apply mode")
data = imports.EmojiImportData{Name: ptrStr("smiley"), Image: ptrStr(testImage)}
appErr = th.App.importEmoji(&data, false)
appErr = th.App.importEmoji(th.Context, &data, false)
assert.Nil(t, appErr, "System emoji should not fail")
largeImage := filepath.Join(testsDir, "large_image_file.jpg")
data = imports.EmojiImportData{Name: ptrStr(model.NewId()), Image: ptrStr(largeImage)}
appErr = th.App.importEmoji(&data, false)
appErr = th.App.importEmoji(th.Context, &data, false)
require.NotNil(t, appErr)
require.ErrorIs(t, appErr.Unwrap(), utils.SizeLimitExceeded)
}

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

@@ -17,7 +17,9 @@ import (
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v6/app/imports"
"github.com/mattermost/mattermost-server/v6/app/request"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
"github.com/mattermost/mattermost-server/v6/utils"
"github.com/mattermost/mattermost-server/v6/utils/fileutils"
)
@@ -238,7 +240,7 @@ func TestImportBulkImport(t *testing.T) {
{"type": "user", "user": {"username": "` + username + `", "email": "` + username + `@example.com", "teams": [{"name": "` + teamName + `","theme": "` + teamTheme1 + `", "channels": [{"name": "` + channelName + `"}]}]}}
{"type": "post", "post": {"team": "` + teamName + `", "channel": "` + channelName + `", "user": "` + username + `", "message": "Hello World", "create_at": 123456789012, "attachments":[{"path": "` + testImage + `"}], "props":{"attachments":[{"id":0,"fallback":"[February 4th, 2020 2:46 PM] author: fallback","color":"D0D0D0","pretext":"","author_name":"author","author_link":"","title":"","title_link":"","text":"this post has props","fields":null,"image_url":"","thumb_url":"","footer":"Posted in #general","footer_icon":"","ts":"1580823992.000100"}]}}}
{"type": "direct_channel", "direct_channel": {"members": ["` + username + `", "` + username + `"]}}
{"type": "direct_post", "direct_post": {"channel_members": ["` + username + `", "` + username + `"], "user": "` + username + `", "message": "Hello Direct Channel to myself", "create_at": 123456789014, "props":{"attachments":[{"id":0,"fallback":"[February 4th, 2020 2:46 PM] author: fallback","color":"D0D0D0","pretext":"","author_name":"author","author_link":"","title":"","title_link":"","text":"this post has props","fields":null,"image_url":"","thumb_url":"","footer":"Posted in #general","footer_icon":"","ts":"1580823992.000100"}]}}}}`
{"type": "direct_post", "direct_post": {"channel_members": ["` + username + `", "` + username + `"], "user": "` + username + `", "message": "Hello Direct Channel to myself", "create_at": 123456789014, "props":{"attachments":[{"id":0,"fallback":"[February 4th, 2020 2:46 PM] author: fallback","color":"D0D0D0","pretext":"","author_name":"author","author_link":"","title":"","title_link":"","text":"this post has props","fields":null,"image_url":"","thumb_url":"","footer":"Posted in #general","footer_icon":"","ts":"1580823992.000100"}]}}}`
err, line := th.App.BulkImport(th.Context, strings.NewReader(data6), nil, false, 2)
require.Nil(t, err, "BulkImport should have succeeded")
@@ -285,6 +287,9 @@ func AssertFileIdsInPost(files []*model.FileInfo, th *TestHelper, t *testing.T)
}
func TestProcessAttachments(t *testing.T) {
logger, _ := mlog.NewLogger()
c := request.EmptyContext(logger)
genAttachments := func() *[]imports.AttachmentImportData {
return &[]imports.AttachmentImportData{
{
@@ -333,10 +338,11 @@ func TestProcessAttachments(t *testing.T) {
Path: model.NewString("somedir/file.jpg"),
},
}
err := processAttachments(&line, "", nil)
err := processAttachments(c, &line, "", nil)
require.NoError(t, err)
require.Equal(t, expected, line.Post.Attachments)
err = processAttachments(&line2, "", nil)
err = processAttachments(c, &line2, "", nil)
require.NoError(t, err)
require.Equal(t, expected, line2.DirectPost.Attachments)
})
@@ -352,27 +358,27 @@ func TestProcessAttachments(t *testing.T) {
}
t.Run("post attachments", func(t *testing.T) {
err := processAttachments(&line, "/tmp", nil)
err := processAttachments(c, &line, "/tmp", nil)
require.NoError(t, err)
require.Equal(t, expected, line.Post.Attachments)
})
t.Run("direct post attachments", func(t *testing.T) {
err := processAttachments(&line2, "/tmp", nil)
err := processAttachments(c, &line2, "/tmp", nil)
require.NoError(t, err)
require.Equal(t, expected, line2.DirectPost.Attachments)
})
t.Run("profile image", func(t *testing.T) {
expected := "/tmp/profile.jpg"
err := processAttachments(&userLine, "/tmp", nil)
err := processAttachments(c, &userLine, "/tmp", nil)
require.NoError(t, err)
require.Equal(t, expected, *userLine.User.ProfileImage)
})
t.Run("emoji", func(t *testing.T) {
expected := "/tmp/emoji.png"
err := processAttachments(&emojiLine, "/tmp", nil)
err := processAttachments(c, &emojiLine, "/tmp", nil)
require.NoError(t, err)
require.Equal(t, expected, *emojiLine.Emoji.Image)
})
@@ -383,11 +389,11 @@ func TestProcessAttachments(t *testing.T) {
filesMap := map[string]*zip.File{
"/tmp/file.jpg": nil,
}
err := processAttachments(&line, "", filesMap)
err := processAttachments(c, &line, "", filesMap)
require.Error(t, err)
filesMap["/tmp/somedir/file.jpg"] = nil
err = processAttachments(&line, "", filesMap)
err = processAttachments(c, &line, "", filesMap)
require.NoError(t, err)
})
@@ -395,11 +401,11 @@ func TestProcessAttachments(t *testing.T) {
filesMap := map[string]*zip.File{
"/tmp/file.jpg": nil,
}
err := processAttachments(&line2, "", filesMap)
err := processAttachments(c, &line2, "", filesMap)
require.Error(t, err)
filesMap["/tmp/somedir/file.jpg"] = nil
err = processAttachments(&line2, "", filesMap)
err = processAttachments(c, &line2, "", filesMap)
require.NoError(t, err)
})
@@ -407,11 +413,11 @@ func TestProcessAttachments(t *testing.T) {
filesMap := map[string]*zip.File{
"/tmp/file.jpg": nil,
}
err := processAttachments(&userLine, "", filesMap)
err := processAttachments(c, &userLine, "", filesMap)
require.Error(t, err)
filesMap["/tmp/profile.jpg"] = nil
err = processAttachments(&userLine, "", filesMap)
err = processAttachments(c, &userLine, "", filesMap)
require.NoError(t, err)
})
@@ -419,11 +425,11 @@ func TestProcessAttachments(t *testing.T) {
filesMap := map[string]*zip.File{
"/tmp/file.jpg": nil,
}
err := processAttachments(&emojiLine, "", filesMap)
err := processAttachments(c, &emojiLine, "", filesMap)
require.Error(t, err)
filesMap["/tmp/emoji.png"] = nil
err = processAttachments(&emojiLine, "", filesMap)
err = processAttachments(c, &emojiLine, "", filesMap)
require.NoError(t, err)
})
})

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

@@ -32,8 +32,8 @@ type licenseWrapper struct {
srv *Server
}
func (w *licenseWrapper) Name() ServiceKey {
return LicenseKey
func (w *licenseWrapper) Name() product.ServiceKey {
return product.LicenseKey
}
func (w *licenseWrapper) GetLicense() *model.License {

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

@@ -20,6 +20,7 @@ import (
"github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/product"
fmocks "github.com/mattermost/mattermost-server/v6/shared/filestore/mocks"
"github.com/mattermost/mattermost-server/v6/shared/i18n"
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
@@ -1440,7 +1441,7 @@ func TestPushNotificationRace(t *testing.T) {
Return(&model.Preference{Value: "test"}, nil)
mockStore.On("Preference").Return(&mockPreferenceStore)
s := &Server{
products: make(map[string]Product),
products: make(map[string]product.Product),
Router: mux.NewRouter(),
}
var err error
@@ -1449,12 +1450,13 @@ func TestPushNotificationRace(t *testing.T) {
}, platform.SetFileStore(&fmocks.FileBackend{}))
s.SetStore(mockStore)
require.NoError(t, err)
serviceMap := map[ServiceKey]any{
ConfigKey: s.platform,
LicenseKey: &licenseWrapper{s},
FilestoreKey: s.FileBackend(),
serviceMap := map[product.ServiceKey]any{
ServerKey: s,
product.ConfigKey: s.platform,
product.LicenseKey: &licenseWrapper{s},
product.FilestoreKey: s.FileBackend(),
}
ch, err := NewChannels(s, serviceMap)
ch, err := NewChannels(serviceMap)
require.NoError(t, err)
s.products["channels"] = ch

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

@@ -25,6 +25,7 @@ import (
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin"
"github.com/mattermost/mattermost-server/v6/product"
"github.com/mattermost/mattermost-server/v6/services/httpservice"
"github.com/mattermost/mattermost-server/v6/services/imageproxy"
"github.com/mattermost/mattermost-server/v6/services/remotecluster"
@@ -6437,6 +6438,28 @@ func (a *OpenTracingAppLayer) GetGroupMemberUsersPage(groupID string, page int,
return resultVar0, resultVar1, resultVar2
}
func (a *OpenTracingAppLayer) GetGroupMemberUsersSortedPage(groupID string, page int, perPage int, viewRestrictions *model.ViewUsersRestrictions, teammateNameDisplay string) ([]*model.User, int, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetGroupMemberUsersSortedPage")
a.ctx = newCtx
a.app.Srv().Store().SetContext(newCtx)
defer func() {
a.app.Srv().Store().SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1, resultVar2 := a.app.GetGroupMemberUsersSortedPage(groupID, page, perPage, viewRestrictions, teammateNameDisplay)
if resultVar2 != nil {
span.LogFields(spanlog.Error(resultVar2))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1, resultVar2
}
func (a *OpenTracingAppLayer) GetGroupSyncable(groupID string, syncableID string, syncableType model.GroupSyncableType) (*model.GroupSyncable, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetGroupSyncable")
@@ -10305,7 +10328,7 @@ func (a *OpenTracingAppLayer) GetTotalUsersStats(viewRestrictions *model.ViewUse
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetUploadSession(uploadId string) (*model.UploadSession, *model.AppError) {
func (a *OpenTracingAppLayer) GetUploadSession(c request.CTX, uploadId string) (*model.UploadSession, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetUploadSession")
@@ -10317,7 +10340,7 @@ func (a *OpenTracingAppLayer) GetUploadSession(uploadId string) (*model.UploadSe
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetUploadSession(uploadId)
resultVar0, resultVar1 := a.app.GetUploadSession(c, uploadId)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
@@ -11559,6 +11582,23 @@ func (a *OpenTracingAppLayer) HasSharedChannel(channelID string) (bool, error) {
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) HooksManager() *product.HooksManager {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.HooksManager")
a.ctx = newCtx
a.app.Srv().Store().SetContext(newCtx)
defer func() {
a.app.Srv().Store().SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.HooksManager()
return resultVar0
}
func (a *OpenTracingAppLayer) HubRegister(webConn *platform.WebConn) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.HubRegister")
@@ -12632,6 +12672,21 @@ func (a *OpenTracingAppLayer) NotifyAndSetWarnMetricAck(warnMetricId string, sen
return resultVar0
}
func (a *OpenTracingAppLayer) NotifySelfHostedSignupProgress(progress string, userId string) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.NotifySelfHostedSignupProgress")
a.ctx = newCtx
a.app.Srv().Store().SetContext(newCtx)
defer func() {
a.app.Srv().Store().SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
a.app.NotifySelfHostedSignupProgress(progress, userId)
}
func (a *OpenTracingAppLayer) NotifySessionsExpired() error {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.NotifySessionsExpired")
@@ -18135,7 +18190,7 @@ func (a *OpenTracingAppLayer) UpdateWebConnUserActivity(session model.Session, a
a.app.UpdateWebConnUserActivity(session, activityAt)
}
func (a *OpenTracingAppLayer) UploadData(c *request.Context, us *model.UploadSession, rd io.Reader) (*model.FileInfo, *model.AppError) {
func (a *OpenTracingAppLayer) UploadData(c request.CTX, us *model.UploadSession, rd io.Reader) (*model.FileInfo, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UploadData")
@@ -18499,6 +18554,28 @@ func (a *OpenTracingAppLayer) WriteFile(fr io.Reader, path string) (int64, *mode
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) WriteFileContext(ctx context.Context, fr io.Reader, path string) (int64, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.WriteFileContext")
a.ctx = newCtx
a.app.Srv().Store().SetContext(newCtx)
defer func() {
a.app.Srv().Store().SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.WriteFileContext(ctx, fr, path)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func NewOpenTracingAppLayer(childApp app.AppIface, ctx context.Context) *OpenTracingAppLayer {
newApp := OpenTracingAppLayer{
app: childApp,

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

@@ -994,6 +994,30 @@ func (a *App) getAddCustomUserGroupsPermissions() (permissionsMap, error) {
return t, nil
}
func (a *App) getAddCustomUserGroupsPermissionRestore() (permissionsMap, error) {
t := []permissionTransformation{}
customGroupPermissions := []string{
model.PermissionRestoreCustomGroup.Id,
}
t = append(t, permissionTransformation{
On: isExactRole(model.SystemUserRoleId),
Add: customGroupPermissions,
})
t = append(t, permissionTransformation{
On: isExactRole(model.SystemAdminRoleId),
Add: customGroupPermissions,
})
t = append(t, permissionTransformation{
On: isExactRole(model.SystemCustomGroupAdminRoleId),
Add: customGroupPermissions,
})
return t, nil
}
func (a *App) getAddPlaybooksPermissions() (permissionsMap, error) {
transformations := []permissionTransformation{}
@@ -1110,6 +1134,7 @@ func (s *Server) doPermissionsMigrations() error {
{Key: model.MigrationKeyAddCustomUserGroupsPermissions, Migration: a.getAddCustomUserGroupsPermissions},
{Key: model.MigrationKeyAddPlayboosksManageRolesPermissions, Migration: a.getPlaybooksPermissionsAddManageRoles},
{Key: model.MigrationKeyAddProductsBoardsPermissions, Migration: a.getProductsBoardsPermissions},
{Key: model.MigrationKeyAddCustomUserGroupsPermissionRestore, Migration: a.getAddCustomUserGroupsPermissionRestore},
}
roles, err := s.Store().Role().GetAll()

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

@@ -1255,7 +1255,9 @@ func (api *PluginAPI) UploadData(us *model.UploadSession, rd io.Reader) (*model.
}
func (api *PluginAPI) GetUploadSession(uploadID string) (*model.UploadSession, error) {
fi, err := api.app.GetUploadSession(uploadID)
// We want to fetch from master DB to avoid a potential read-after-write on the plugin side.
api.ctx.SetContext(WithMaster(api.ctx.Context()))
fi, err := api.app.GetUploadSession(api.ctx, uploadID)
if err != nil {
return nil, err
}

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

@@ -8,6 +8,7 @@ import (
"database/sql"
"database/sql/driver"
"sync"
"time"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin"
@@ -44,7 +45,10 @@ func (d *DriverImpl) Conn(isMaster bool) (string, error) {
if !isMaster {
dbFunc = d.s.Platform().Store.GetInternalReplicaDB
}
conn, err := dbFunc().Conn(context.Background())
timeout := time.Duration(*d.s.Config().SqlSettings.QueryTimeout) * time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
conn, err := dbFunc().Conn(ctx)
if err != nil {
return "", err
}

21
app/plugin_db_driver_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,21 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestConnCreateTimeout(t *testing.T) {
th := Setup(t)
defer th.TearDown()
*th.App.Config().SqlSettings.QueryTimeout = 0
d := NewDriverImpl(th.Server)
_, err := d.Conn(true)
require.Error(t, err)
}

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

@@ -6,31 +6,20 @@ package app
import (
"fmt"
"strings"
"github.com/mattermost/mattermost-server/v6/product"
)
type Product interface {
Start() error
Stop() error
}
type ProductManifest struct {
Initializer func(*Server, map[ServiceKey]any) (Product, error)
Dependencies map[ServiceKey]struct{}
}
var products = make(map[string]ProductManifest)
func RegisterProduct(name string, m ProductManifest) {
products[name] = m
}
func (s *Server) initializeProducts(
productMap map[string]ProductManifest,
serviceMap map[ServiceKey]any,
productMap map[string]product.Manifest,
serviceMap map[product.ServiceKey]any,
) error {
// create a product map to consume
pmap := make(map[string]struct{})
for name := range productMap {
if !s.shouldStart(name) {
continue
}
pmap[name] = struct{}{}
}
@@ -57,7 +46,7 @@ func (s *Server) initializeProducts(
// some products can register themselves/their services
initializer := manifest.Initializer
prod, err := initializer(s, serviceMap)
prod, err := initializer(serviceMap)
if err != nil {
return fmt.Errorf("error initializing product %q: %w", product, err)
}
@@ -78,3 +67,11 @@ func (s *Server) initializeProducts(
return nil
}
func (s *Server) shouldStart(product string) bool {
if !s.Config().FeatureFlags.BoardsProduct && product == "boards" {
return false
}
return true
}

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

@@ -6,6 +6,9 @@ package app
import (
"testing"
"github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/product"
"github.com/stretchr/testify/require"
)
@@ -16,7 +19,7 @@ const (
type productA struct{}
func newProductA(s *Server, m map[ServiceKey]any) (Product, error) {
func newProductA(m map[product.ServiceKey]any) (product.Product, error) {
m[testSrvKey1] = nil
return &productA{}, nil
}
@@ -26,7 +29,7 @@ func (p *productA) Stop() error { return nil }
type productB struct{}
func newProductB(s *Server, m map[ServiceKey]any) (Product, error) {
func newProductB(m map[product.ServiceKey]any) (product.Product, error) {
m[testSrvKey2] = nil
return &productB{}, nil
}
@@ -35,74 +38,80 @@ func (p *productB) Start() error { return nil }
func (p *productB) Stop() error { return nil }
func TestInitializeProducts(t *testing.T) {
ps, err := platform.New(platform.ServiceConfig{ConfigStore: config.NewTestMemoryStore()})
require.NoError(t, err)
t.Run("2 products and no circular dependency", func(t *testing.T) {
serviceMap := map[ServiceKey]any{
ConfigKey: nil,
LicenseKey: nil,
FilestoreKey: nil,
ClusterKey: nil,
serviceMap := map[product.ServiceKey]any{
product.ConfigKey: nil,
product.LicenseKey: nil,
product.FilestoreKey: nil,
product.ClusterKey: nil,
}
products := map[string]ProductManifest{
products := map[string]product.Manifest{
"productA": {
Initializer: newProductA,
Dependencies: map[ServiceKey]struct{}{
ConfigKey: {},
LicenseKey: {},
FilestoreKey: {},
ClusterKey: {},
Dependencies: map[product.ServiceKey]struct{}{
product.ConfigKey: {},
product.LicenseKey: {},
product.FilestoreKey: {},
product.ClusterKey: {},
},
},
"productB": {
Initializer: newProductB,
Dependencies: map[ServiceKey]struct{}{
ConfigKey: {},
testSrvKey1: {},
FilestoreKey: {},
ClusterKey: {},
Dependencies: map[product.ServiceKey]struct{}{
product.ConfigKey: {},
testSrvKey1: {},
product.FilestoreKey: {},
product.ClusterKey: {},
},
},
}
server := &Server{
products: make(map[string]Product),
products: make(map[string]product.Product),
platform: ps,
}
err := server.initializeProducts(products, serviceMap)
err = server.initializeProducts(products, serviceMap)
require.NoError(t, err)
require.Len(t, server.products, 2)
})
t.Run("2 products and circular dependency", func(t *testing.T) {
serviceMap := map[ServiceKey]any{
ConfigKey: nil,
LicenseKey: nil,
FilestoreKey: nil,
ClusterKey: nil,
serviceMap := map[product.ServiceKey]any{
product.ConfigKey: nil,
product.LicenseKey: nil,
product.FilestoreKey: nil,
product.ClusterKey: nil,
}
products := map[string]ProductManifest{
products := map[string]product.Manifest{
"productA": {
Initializer: newProductA,
Dependencies: map[ServiceKey]struct{}{
ConfigKey: {},
LicenseKey: {},
FilestoreKey: {},
ClusterKey: {},
testSrvKey2: {},
Dependencies: map[product.ServiceKey]struct{}{
product.ConfigKey: {},
product.LicenseKey: {},
product.FilestoreKey: {},
product.ClusterKey: {},
testSrvKey2: {},
},
},
"productB": {
Initializer: newProductB,
Dependencies: map[ServiceKey]struct{}{
ConfigKey: {},
testSrvKey1: {},
FilestoreKey: {},
ClusterKey: {},
Dependencies: map[product.ServiceKey]struct{}{
product.ConfigKey: {},
testSrvKey1: {},
product.FilestoreKey: {},
product.ClusterKey: {},
},
},
}
server := &Server{
products: make(map[string]Product),
products: make(map[string]product.Product),
platform: ps,
}
err := server.initializeProducts(products, serviceMap)
@@ -110,19 +119,19 @@ func TestInitializeProducts(t *testing.T) {
})
t.Run("2 products and one w/o any dependency", func(t *testing.T) {
serviceMap := map[ServiceKey]any{
ConfigKey: nil,
LicenseKey: nil,
FilestoreKey: nil,
ClusterKey: nil,
serviceMap := map[product.ServiceKey]any{
product.ConfigKey: nil,
product.LicenseKey: nil,
product.FilestoreKey: nil,
product.ClusterKey: nil,
}
products := map[string]ProductManifest{
products := map[string]product.Manifest{
"productA": {
Initializer: newProductA,
Dependencies: map[ServiceKey]struct{}{
ConfigKey: {},
LicenseKey: {},
Dependencies: map[product.ServiceKey]struct{}{
product.ConfigKey: {},
product.LicenseKey: {},
},
},
"productB": {
@@ -130,11 +139,32 @@ func TestInitializeProducts(t *testing.T) {
},
}
server := &Server{
products: make(map[string]Product),
products: make(map[string]product.Product),
platform: ps,
}
err := server.initializeProducts(products, serviceMap)
require.NoError(t, err)
require.Len(t, server.products, 2)
})
t.Run("boards product to be blocked", func(t *testing.T) {
products := map[string]product.Manifest{
"productA": {
Initializer: newProductA,
},
"boards": {
Initializer: newProductB,
},
}
server := &Server{
products: make(map[string]product.Product),
platform: ps,
}
err := server.initializeProducts(products, map[product.ServiceKey]any{})
require.NoError(t, err)
require.Len(t, server.products, 1)
})
}

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

@@ -75,30 +75,6 @@ import (
// declaring this as var to allow overriding in tests
var SentryDSN = "placeholder_sentry_dsn"
type ServiceKey string
const (
ChannelKey ServiceKey = "channel"
ConfigKey ServiceKey = "config"
LicenseKey ServiceKey = "license"
FilestoreKey ServiceKey = "filestore"
FileInfoStoreKey ServiceKey = "fileinfostore"
ClusterKey ServiceKey = "cluster"
CloudKey ServiceKey = "cloud"
PostKey ServiceKey = "post"
TeamKey ServiceKey = "team"
UserKey ServiceKey = "user"
PermissionsKey ServiceKey = "permissions"
RouterKey ServiceKey = "router"
BotKey ServiceKey = "bot"
LogKey ServiceKey = "log"
HooksKey ServiceKey = "hooks"
KVStoreKey ServiceKey = "kvstore"
StoreKey ServiceKey = "storekey"
SystemKey ServiceKey = "systemkey"
PreferencesKey ServiceKey = "preferenceskey"
)
type Server struct {
// RootRouter is the starting point for all HTTP requests to the server.
RootRouter *mux.Router
@@ -160,7 +136,7 @@ type Server struct {
tracer *tracing.Tracer
products map[string]Product
products map[string]product.Product
hooksManager *product.HooksManager
}
@@ -187,7 +163,7 @@ func NewServer(options ...Option) (*Server, error) {
RootRouter: rootRouter,
LocalRouter: localRouter,
timezones: timezones.New(),
products: make(map[string]Product),
products: make(map[string]product.Product),
}
for _, option := range options {
@@ -263,24 +239,25 @@ func NewServer(options ...Option) (*Server, error) {
var _ product.UserService = (*App)(nil)
app := New(ServerConnector(s.Channels()))
serviceMap := map[ServiceKey]any{
ChannelKey: &channelsWrapper{srv: s, app: app},
ConfigKey: s.platform,
LicenseKey: s.licenseWrapper,
FilestoreKey: s.platform.FileBackend(),
FileInfoStoreKey: &fileInfoWrapper{srv: s},
ClusterKey: s.platform,
UserKey: New(ServerConnector(s.Channels())),
LogKey: s.platform.Log(),
CloudKey: &cloudWrapper{cloud: s.Cloud},
KVStoreKey: s.platform,
StoreKey: store.NewStoreServiceAdapter(s.Store()),
SystemKey: &systemServiceAdapter{server: s},
serviceMap := map[product.ServiceKey]any{
ServerKey: s,
product.ChannelKey: &channelsWrapper{srv: s, app: app},
product.ConfigKey: s.platform,
product.LicenseKey: s.licenseWrapper,
product.FilestoreKey: s.platform.FileBackend(),
product.FileInfoStoreKey: &fileInfoWrapper{srv: s},
product.ClusterKey: s.platform,
product.UserKey: New(ServerConnector(s.Channels())),
product.LogKey: s.platform.Log(),
product.CloudKey: &cloudWrapper{cloud: s.Cloud},
product.KVStoreKey: s.platform,
product.StoreKey: store.NewStoreServiceAdapter(s.Store()),
product.SystemKey: &systemServiceAdapter{server: s},
}
// Step 4: Initialize products.
// Depends on s.httpService.
err = s.initializeProducts(products, serviceMap)
err = s.initializeProducts(product.GetProducts(), serviceMap)
if err != nil {
return nil, errors.Wrap(err, "failed to initialize products")
}

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

@@ -48,7 +48,7 @@ func (a *App) genFileInfoFromReader(name string, file io.ReadSeeker, size int64)
return info, nil
}
func (a *App) runPluginsHook(c *request.Context, info *model.FileInfo, file io.Reader) *model.AppError {
func (a *App) runPluginsHook(c request.CTX, info *model.FileInfo, file io.Reader) *model.AppError {
filePath := info.Path
// using a pipe to avoid loading the whole file content in memory.
r, w := io.Pipe()
@@ -93,6 +93,7 @@ func (a *App) runPluginsHook(c *request.Context, info *model.FileInfo, file io.R
if fileErr := a.RemoveFile(tmpPath); fileErr != nil {
mlog.Warn("Failed to remove file", mlog.Err(fileErr))
}
r.CloseWithError(err) // always returns nil
return err
}
@@ -154,8 +155,8 @@ func (a *App) CreateUploadSession(c request.CTX, us *model.UploadSession) (*mode
return us, nil
}
func (a *App) GetUploadSession(uploadId string) (*model.UploadSession, *model.AppError) {
us, err := a.Srv().Store().UploadSession().Get(uploadId)
func (a *App) GetUploadSession(c request.CTX, uploadId string) (*model.UploadSession, *model.AppError) {
us, err := a.Srv().Store().UploadSession().Get(c.Context(), uploadId)
if err != nil {
var nfErr *store.ErrNotFound
switch {
@@ -179,7 +180,7 @@ func (a *App) GetUploadSessionsForUser(userID string) ([]*model.UploadSession, *
return uss, nil
}
func (a *App) UploadData(c *request.Context, us *model.UploadSession, rd io.Reader) (*model.FileInfo, *model.AppError) {
func (a *App) UploadData(c request.CTX, us *model.UploadSession, rd io.Reader) (*model.FileInfo, *model.AppError) {
// prevent more than one caller to upload data at the same time for a given upload session.
// This is to avoid possible inconsistencies.
a.ch.uploadLockMapMut.Lock()
@@ -202,7 +203,8 @@ func (a *App) UploadData(c *request.Context, us *model.UploadSession, rd io.Read
}()
// fetch the session from store to check for inconsistencies.
if storedSession, err := a.GetUploadSession(us.Id); err != nil {
c.SetContext(WithMaster(c.Context()))
if storedSession, err := a.GetUploadSession(c, us.Id); err != nil {
return nil, err
} else if us.FileOffset != storedSession.FileOffset {
return nil, model.NewAppError("UploadData", "app.upload.upload_data.concurrent.app_error",

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

@@ -1877,7 +1877,6 @@ func TestSendSubscriptionHistoryEvent(t *testing.T) {
CreateAt: 1000000000,
Seats: 10,
DNS: "some.dns.server",
IsPaidTier: "false",
}
subscriptionHistory := &model.SubscriptionHistory{