Merge branch 'master' into mpa-playbooks
Этот коммит содержится в:
@@ -699,6 +699,7 @@ type AppIface interface {
|
||||
GetOnboarding() (*model.System, *model.AppError)
|
||||
GetOpenGraphMetadata(requestURL string) ([]byte, error)
|
||||
GetOrCreateDirectChannel(c request.CTX, userID, otherUserID string, channelOptions ...model.ChannelOption) (*model.Channel, *model.AppError)
|
||||
GetOrCreateTrueUpReviewStatus() (*model.TrueUpReviewStatus, *model.AppError)
|
||||
GetOutgoingWebhook(hookID string) (*model.OutgoingWebhook, *model.AppError)
|
||||
GetOutgoingWebhooksForChannelPageByUser(channelID string, userID string, page, perPage int) ([]*model.OutgoingWebhook, *model.AppError)
|
||||
GetOutgoingWebhooksForTeamPage(teamID string, page, perPage int) ([]*model.OutgoingWebhook, *model.AppError)
|
||||
@@ -811,6 +812,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)
|
||||
GetTrueUpProfile() (map[string]any, error)
|
||||
GetUploadSession(c request.CTX, uploadId string) (*model.UploadSession, *model.AppError)
|
||||
GetUploadSessionsForUser(userID string) ([]*model.UploadSession, *model.AppError)
|
||||
GetUser(userID string) (*model.User, *model.AppError)
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/request"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/plugin"
|
||||
@@ -1178,6 +1179,15 @@ func (a *App) PatchChannelModerationsForChannel(c request.CTX, channel *model.Ch
|
||||
|
||||
cErr := a.forEachChannelMember(c, channel.Id, func(channelMember model.ChannelMember) error {
|
||||
a.Srv().Store().Channel().InvalidateAllChannelMembersForUser(channelMember.UserId)
|
||||
|
||||
evt := model.NewWebSocketEvent(model.WebsocketEventChannelMemberUpdated, "", "", channelMember.UserId, nil, "")
|
||||
memberJSON, jsonErr := json.Marshal(channelMember)
|
||||
if jsonErr != nil {
|
||||
return jsonErr
|
||||
}
|
||||
evt.Add("channelMember", string(memberJSON))
|
||||
a.Publish(evt)
|
||||
|
||||
return nil
|
||||
})
|
||||
if cErr != nil {
|
||||
|
||||
@@ -1113,7 +1113,7 @@ func (es *Service) SendDelinquencyEmail30(email, locale, siteURL, planName strin
|
||||
data.Props["SupportEmail"] = *es.config().SupportSettings.SupportEmail
|
||||
data.Props["Button"] = T("api.templates.delinquency_30.button")
|
||||
data.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
data.Props["BulletListItems"] = []string{T("api.templates.delinquency_30.bullet.message_history"), T("api.templates.delinquency_30.bullet.files"), T("api.templates.delinquency_30.bullet.cards"), T("api.templates.delinquency_30.bullet.plugins")}
|
||||
data.Props["BulletListItems"] = []string{T("api.templates.delinquency_30.bullet.message_history"), T("api.templates.delinquency_30.bullet.files")}
|
||||
data.Props["LimitsDocs"] = T("api.templates.delinquency_30.limits_documentation")
|
||||
data.Props["Footer"] = T("api.templates.copyright")
|
||||
|
||||
|
||||
@@ -315,7 +315,7 @@ func (es *Service) sendBatchedEmailNotification(userID string, notifications []*
|
||||
MessageURL: MessageURL,
|
||||
ShowChannelIcon: showChannelIcon,
|
||||
OtherChannelMembersCount: otherChannelMembersCount,
|
||||
MessageAttachments: ProcessMessageAttachments(notification.post),
|
||||
MessageAttachments: ProcessMessageAttachments(notification.post, siteURL),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,14 +60,14 @@ func (es *Service) GetMessageForNotification(post *model.Post, translateFunc i18
|
||||
return translateFunc("api.post.get_message_for_notification.files_sent", len(filenames), props)
|
||||
}
|
||||
|
||||
func ProcessMessageAttachments(post *model.Post) []*EmailMessageAttachment {
|
||||
func ProcessMessageAttachments(post *model.Post, siteURL string) []*EmailMessageAttachment {
|
||||
emailMessageAttachments := []*EmailMessageAttachment{}
|
||||
|
||||
for _, messageAttachment := range post.Attachments() {
|
||||
emailMessageAttachment := &EmailMessageAttachment{
|
||||
SlackAttachment: *messageAttachment,
|
||||
Pretext: prepareTextForEmail(messageAttachment.Pretext),
|
||||
Text: prepareTextForEmail(messageAttachment.Text),
|
||||
Pretext: prepareTextForEmail(messageAttachment.Pretext, siteURL),
|
||||
Text: prepareTextForEmail(messageAttachment.Text, siteURL),
|
||||
}
|
||||
|
||||
stripedTitle, err := utils.StripMarkdown(emailMessageAttachment.Title)
|
||||
@@ -92,7 +92,7 @@ func ProcessMessageAttachments(post *model.Post) []*EmailMessageAttachment {
|
||||
}
|
||||
|
||||
if stringValue, ok := field.Value.(string); ok {
|
||||
field.Value = prepareTextForEmail(stringValue)
|
||||
field.Value = prepareTextForEmail(stringValue, siteURL)
|
||||
}
|
||||
|
||||
if !field.Short {
|
||||
@@ -124,9 +124,9 @@ func ProcessMessageAttachments(post *model.Post) []*EmailMessageAttachment {
|
||||
return emailMessageAttachments
|
||||
}
|
||||
|
||||
func prepareTextForEmail(text string) template.HTML {
|
||||
func prepareTextForEmail(text, siteURL string) template.HTML {
|
||||
escapedText := html.EscapeString(text)
|
||||
markdownText, err := utils.MarkdownToHTML(escapedText)
|
||||
markdownText, err := utils.MarkdownToHTML(escapedText, siteURL)
|
||||
if err != nil {
|
||||
mlog.Warn("Encountered error while converting markdown to HTML", mlog.Err(err))
|
||||
return template.HTML(text)
|
||||
|
||||
@@ -63,10 +63,10 @@ func TestProcessMessageAttachments(t *testing.T) {
|
||||
|
||||
model.ParseSlackAttachment(post, messageAttachments)
|
||||
|
||||
processedAttachcmentsPost := ProcessMessageAttachments(post)
|
||||
require.NotNil(t, processedAttachcmentsPost)
|
||||
require.Len(t, processedAttachcmentsPost, 2)
|
||||
require.Equal(t, processedAttachcmentsPost[0].Color, "#FF0000")
|
||||
require.Equal(t, processedAttachcmentsPost[0].FieldRows[0].Cells[0].Title, "message attachment 1 field 1 title")
|
||||
require.Equal(t, processedAttachcmentsPost[1].Color, "#FF0000")
|
||||
processedAttachmentsPost := ProcessMessageAttachments(post, "https://example.com")
|
||||
require.NotNil(t, processedAttachmentsPost)
|
||||
require.Len(t, processedAttachmentsPost, 2)
|
||||
require.Equal(t, processedAttachmentsPost[0].Color, "#FF0000")
|
||||
require.Equal(t, processedAttachmentsPost[0].FieldRows[0].Cells[0].Title, "message attachment 1 field 1 title")
|
||||
require.Equal(t, processedAttachmentsPost[1].Color, "#FF0000")
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ func (a *App) getNotificationEmailBody(c request.CTX, recipient *model.User, pos
|
||||
if emailNotificationContentsType == model.EmailNotificationContentsFull {
|
||||
postMessage := a.GetMessageForNotification(post, translateFunc)
|
||||
postMessage = html.EscapeString(postMessage)
|
||||
mdPostMessage, mdErr := utils.MarkdownToHTML(postMessage)
|
||||
mdPostMessage, mdErr := utils.MarkdownToHTML(postMessage, a.GetSiteURL())
|
||||
if mdErr != nil {
|
||||
mlog.Warn("Encountered error while converting markdown to HTML", mlog.Err(mdErr))
|
||||
mdPostMessage = postMessage
|
||||
@@ -247,7 +247,7 @@ func (a *App) getNotificationEmailBody(c request.CTX, recipient *model.User, pos
|
||||
}
|
||||
pData.Message = template.HTML(normalizedPostMessage)
|
||||
pData.Time = translateFunc("app.notification.body.dm.time", messageTime)
|
||||
pData.MessageAttachments = email.ProcessMessageAttachments(post)
|
||||
pData.MessageAttachments = email.ProcessMessageAttachments(post, a.GetSiteURL())
|
||||
}
|
||||
|
||||
data := a.Srv().EmailService.NewEmailTemplateData(recipient.Locale)
|
||||
|
||||
@@ -7584,6 +7584,28 @@ func (a *OpenTracingAppLayer) GetOrCreateDirectChannel(c request.CTX, userID str
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetOrCreateTrueUpReviewStatus() (*model.TrueUpReviewStatus, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetOrCreateTrueUpReviewStatus")
|
||||
|
||||
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.GetOrCreateTrueUpReviewStatus()
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetOutgoingWebhook(hookID string) (*model.OutgoingWebhook, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetOutgoingWebhook")
|
||||
@@ -10328,6 +10350,28 @@ func (a *OpenTracingAppLayer) GetTotalUsersStats(viewRestrictions *model.ViewUse
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetTrueUpProfile() (map[string]any, error) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetTrueUpProfile")
|
||||
|
||||
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.GetTrueUpProfile()
|
||||
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) GetUploadSession(c request.CTX, uploadId string) (*model.UploadSession, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetUploadSession")
|
||||
|
||||
@@ -320,21 +320,12 @@ func (ps *PlatformService) LimitedClientConfig() map[string]string {
|
||||
}
|
||||
|
||||
func (ps *PlatformService) IsFirstUserAccount() bool {
|
||||
cachedSessions, err := ps.sessionCache.Len()
|
||||
count, err := ps.Store.User().Count(model.UserCountOptions{IncludeDeleted: true})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if cachedSessions == 0 {
|
||||
count, err := ps.Store.User().Count(model.UserCountOptions{IncludeDeleted: true})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if count <= 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return count <= 0
|
||||
}
|
||||
|
||||
func (ps *PlatformService) MaxPostSize() int {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/einterfaces/mocks"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
smocks "github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
||||
)
|
||||
|
||||
func TestConfigListener(t *testing.T) {
|
||||
@@ -98,3 +100,47 @@ func TestConfigSave(t *testing.T) {
|
||||
metricsMock.AssertNumberOfCalls(t, "Register", 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsFirstUserAccount(t *testing.T) {
|
||||
th := SetupWithStoreMock(t)
|
||||
defer th.TearDown()
|
||||
storeMock := th.Service.Store.(*smocks.Store)
|
||||
userStoreMock := &smocks.UserStore{}
|
||||
storeMock.On("User").Return(userStoreMock)
|
||||
|
||||
type test struct {
|
||||
name string
|
||||
count int64
|
||||
err error
|
||||
result bool
|
||||
}
|
||||
|
||||
tests := []test{
|
||||
{"success no users", 0, nil, true},
|
||||
{"success one user", 1, nil, false},
|
||||
{"success multiple users", 42, nil, false},
|
||||
{"success negative users", -100, nil, true},
|
||||
{"failed request", 0, errors.New("error"), false},
|
||||
}
|
||||
|
||||
for _, te := range tests {
|
||||
t.Run(te.name, func(t *testing.T) {
|
||||
*userStoreMock = smocks.UserStore{}
|
||||
|
||||
userStoreMock.On("Count", model.UserCountOptions{IncludeDeleted: true}).Return(te.count, te.err)
|
||||
require.Equal(t, te.result, th.Service.IsFirstUserAccount())
|
||||
})
|
||||
}
|
||||
|
||||
// create a session, this should not affect IsFirstUserAccount
|
||||
th.Service.sessionCache.Set("mock_session", 1)
|
||||
|
||||
for _, te := range tests {
|
||||
t.Run(te.name+" with session", func(t *testing.T) {
|
||||
*userStoreMock = smocks.UserStore{}
|
||||
|
||||
userStoreMock.On("Count", model.UserCountOptions{IncludeDeleted: true}).Return(te.count, te.err)
|
||||
require.Equal(t, te.result, th.Service.IsFirstUserAccount())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ func handleInvitation(ps *PlatformService, syncService SharedChannelServiceIFace
|
||||
return err
|
||||
}
|
||||
|
||||
if participant == nil {
|
||||
if participant == nil || participant.RemoteId == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock"
|
||||
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -68,4 +70,40 @@ func TestServerSyncSharedChannelHandler(t *testing.T) {
|
||||
require.Len(t, mockService.channelNotifications, 1)
|
||||
assert.Equal(t, channel.Id, mockService.channelNotifications[0])
|
||||
})
|
||||
|
||||
t.Run("sync service doesn't panic when no RemoteId", func(t *testing.T) {
|
||||
th := SetupWithStoreMock(t)
|
||||
defer th.TearDown()
|
||||
|
||||
mockStore := th.Service.Store.(*mocks.Store)
|
||||
|
||||
mockChannelStore := &mocks.ChannelStore{}
|
||||
mockChannelStore.On("Get", "channelID", true).Return(&model.Channel{
|
||||
Id: "channelID",
|
||||
Shared: model.NewBool(true),
|
||||
}, nil)
|
||||
|
||||
mockUserStore := &mocks.UserStore{}
|
||||
mockUserStore.On("Get", mock.Anything, "creator").Return(&model.User{}, nil)
|
||||
// Not setting RemoteId here causes the panic.
|
||||
mockUserStore.On("Get", mock.Anything, "teammate").Return(&model.User{}, nil)
|
||||
|
||||
mockRemoteClusterStore := &mocks.RemoteClusterStore{}
|
||||
mockRemoteClusterStore.On("Get", mock.Anything).Return(&model.RemoteCluster{}, nil)
|
||||
|
||||
mockStore.On("Channel").Return(mockChannelStore)
|
||||
mockStore.On("User").Return(mockUserStore)
|
||||
mockStore.On("RemoteCluster").Return(mockRemoteClusterStore)
|
||||
|
||||
mockService := NewMockSharedChannelService(nil)
|
||||
mockService.active = true
|
||||
th.Service.SetSharedChannelService(mockService)
|
||||
|
||||
require.NotPanics(t, func() {
|
||||
websocketEvent := model.NewWebSocketEvent(model.WebsocketEventDirectAdded, "teamID", "channelID", "userID", nil, "")
|
||||
websocketEvent = websocketEvent.SetData(map[string]any{"creator_id": "creator", "teammate_id": "teammate"})
|
||||
th.Service.SharedChannelSyncHandler(websocketEvent)
|
||||
assert.Empty(t, mockService.channelNotifications)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -19,11 +18,9 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/request"
|
||||
"github.com/mattermost/mattermost-server/v6/einterfaces/mocks"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/plugin"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
@@ -1007,65 +1004,6 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEnablePluginWithCloudLimits(t *testing.T) {
|
||||
th := Setup(t)
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.Srv().SetLicense(model.NewTestLicense("cloud"))
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.Enable = true
|
||||
*cfg.PluginSettings.RequirePluginSignature = false
|
||||
cfg.PluginSettings.PluginStates["testplugin"] = &model.PluginState{Enable: false}
|
||||
cfg.PluginSettings.PluginStates["testplugin2"] = &model.PluginState{Enable: false}
|
||||
})
|
||||
|
||||
cloud := &mocks.CloudInterface{}
|
||||
cloud.Mock.On("GetCloudLimits", mock.Anything).Return(&model.ProductLimits{
|
||||
Integrations: &model.IntegrationsLimits{
|
||||
Enabled: model.NewInt(1),
|
||||
},
|
||||
}, nil)
|
||||
|
||||
cloudImpl := th.App.Srv().Cloud
|
||||
defer func() {
|
||||
th.App.Srv().Cloud = cloudImpl
|
||||
}()
|
||||
th.App.Srv().Cloud = cloud
|
||||
|
||||
env := th.App.GetPluginsEnvironment()
|
||||
require.NotNil(t, env)
|
||||
|
||||
path, _ := fileutils.FindDir("tests")
|
||||
fileReader, err := os.Open(filepath.Join(path, "testplugin.tar.gz"))
|
||||
require.NoError(t, err)
|
||||
defer fileReader.Close()
|
||||
|
||||
_, appErr := th.App.WriteFile(fileReader, getBundleStorePath("testplugin"))
|
||||
checkNoError(t, appErr)
|
||||
|
||||
fileReader, err = os.Open(filepath.Join(path, "testplugin2.tar.gz"))
|
||||
require.NoError(t, err)
|
||||
defer fileReader.Close()
|
||||
|
||||
_, appErr = th.App.WriteFile(fileReader, getBundleStorePath("testplugin2"))
|
||||
checkNoError(t, appErr)
|
||||
|
||||
appErr = th.App.SyncPlugins()
|
||||
checkNoError(t, appErr)
|
||||
|
||||
appErr = th.App.EnablePlugin("testplugin")
|
||||
checkNoError(t, appErr)
|
||||
|
||||
// Let enable succeed if a CWS error occurs
|
||||
cloud = &mocks.CloudInterface{}
|
||||
th.App.Srv().Cloud = cloud
|
||||
cloud.Mock.On("GetCloudLimits", mock.Anything).Return(nil, errors.New("error getting limits"))
|
||||
|
||||
appErr = th.App.EnablePlugin("testplugin2")
|
||||
checkNoError(t, appErr)
|
||||
}
|
||||
|
||||
func TestGetPluginStateOverride(t *testing.T) {
|
||||
th := Setup(t)
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -33,8 +33,7 @@ func (w *preferencesServiceWrapper) DeletePreferencesForUser(userID string, pref
|
||||
}
|
||||
|
||||
func (a *App) GetPreferencesForUser(userID string) (model.Preferences, *model.AppError) {
|
||||
limit := *a.Config().ServiceSettings.ExperimentalMaxUserPreferences
|
||||
preferences, err := a.Srv().Store().Preference().GetAll(userID, limit)
|
||||
preferences, err := a.Srv().Store().Preference().GetAll(userID)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetPreferencesForUser", "app.preference.get_all.app_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
|
||||
168
app/true_up.go
Обычный файл
168
app/true_up.go
Обычный файл
@@ -0,0 +1,168 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/services/telemetry"
|
||||
"github.com/mattermost/mattermost-server/v6/store"
|
||||
"github.com/mattermost/mattermost-server/v6/utils"
|
||||
)
|
||||
|
||||
func pluginActivated(pluginStates map[string]*model.PluginState, pluginId string) bool {
|
||||
state, ok := pluginStates[pluginId]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return state.Enable
|
||||
}
|
||||
|
||||
func (a *App) getMarketplacePlugins() ([]string, error) {
|
||||
ts := a.Srv().telemetryService
|
||||
config := a.Srv().Config()
|
||||
|
||||
marketplacePlugins, err := ts.GetAllMarketplacePlugins(model.PluginSettingsDefaultMarketplaceURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
activePlugins := []string{}
|
||||
for _, p := range marketplacePlugins {
|
||||
id := p.Manifest.Id
|
||||
if pluginActivated(config.PluginSettings.PluginStates, id) {
|
||||
activePlugins = append(activePlugins, id)
|
||||
}
|
||||
}
|
||||
|
||||
return activePlugins, nil
|
||||
}
|
||||
|
||||
func (a *App) getTrueUpProfile() (*model.TrueUpReviewProfile, error) {
|
||||
|
||||
license := a.Channels().License()
|
||||
if license == nil {
|
||||
return nil, model.NewAppError("requestTrueUpReview", "api.license.true_up_review.license_required", nil, "Could not get the total active users count", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// Customer Info & Usage Analytics
|
||||
activeUserCount, err := a.Srv().Store().Status().GetTotalActiveUsersCount()
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("requestTrueUpReview", "api.license.true_up_review.user_count_fail", nil, "Could not get the total active users count", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// Webhook, calls, boards, and playbook counts
|
||||
incomingWebhookCount, err := a.Srv().Store().Webhook().AnalyticsIncomingCount("")
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("requestTrueUpReview", "api.license.true_up_review.webhook_in_count_fail", nil, "Could not get the total incoming webhook count", http.StatusInternalServerError)
|
||||
}
|
||||
outgoingWebhookCount, err := a.Srv().Store().Webhook().AnalyticsOutgoingCount("")
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("requestTrueUpReview", "api.license.true_up_review.webhook_out_count_fail", nil, "Could not get the total outgoing webhook count", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// Plugin Data
|
||||
trueUpReviewPlugins := model.TrueUpReviewPlugins{
|
||||
PluginNames: []string{},
|
||||
}
|
||||
|
||||
if plugins, err := a.getMarketplacePlugins(); err == nil {
|
||||
trueUpReviewPlugins.PluginNames = plugins
|
||||
trueUpReviewPlugins.TotalPlugins = len(plugins)
|
||||
}
|
||||
|
||||
// Authentication Features
|
||||
config := a.Config()
|
||||
mfaUsed := config.ServiceSettings.EnforceMultifactorAuthentication
|
||||
ldapUsed := config.LdapSettings.Enable
|
||||
samlUsed := config.SamlSettings.Enable
|
||||
openIdUsed := config.OpenIdSettings.Enable
|
||||
guestAccessAllowed := config.GuestAccountsSettings.Enable
|
||||
|
||||
authFeatures := map[string]*bool{
|
||||
model.TrueUpReviewAuthFeaturesMfa: mfaUsed,
|
||||
model.TrueUpReviewAuthFeaturesADLdap: ldapUsed,
|
||||
model.TrueUpReviewAuthFeaturesSaml: samlUsed,
|
||||
model.TrueUpReviewAuthFeatureOpenId: openIdUsed,
|
||||
model.TrueUpReviewAuthFeatureGuestAccess: guestAccessAllowed,
|
||||
}
|
||||
|
||||
authFeatureList := []string{}
|
||||
for feature, used := range authFeatures {
|
||||
if used != nil && *used {
|
||||
authFeatureList = append(authFeatureList, feature)
|
||||
}
|
||||
}
|
||||
|
||||
reviewProfile := model.TrueUpReviewProfile{
|
||||
ServerId: a.TelemetryId(),
|
||||
ServerVersion: model.CurrentVersion,
|
||||
ServerInstallationType: os.Getenv(telemetry.EnvVarInstallType),
|
||||
LicenseId: license.Id,
|
||||
LicensedSeats: *license.Features.Users,
|
||||
LicensePlan: license.SkuName,
|
||||
CustomerName: license.Customer.Name,
|
||||
ActiveUsers: activeUserCount,
|
||||
TotalIncomingWebhooks: incomingWebhookCount,
|
||||
TotalOutgoingWebhooks: outgoingWebhookCount,
|
||||
Plugins: trueUpReviewPlugins,
|
||||
AuthenticationFeatures: authFeatureList,
|
||||
}
|
||||
|
||||
return &reviewProfile, nil
|
||||
|
||||
}
|
||||
|
||||
func (a *App) GetTrueUpProfile() (map[string]any, error) {
|
||||
profile, err := a.getTrueUpProfile()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
profileJson, err := json.Marshal(profile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
telemetryProperties := map[string]any{}
|
||||
|
||||
json.Unmarshal(profileJson, &telemetryProperties)
|
||||
delete(telemetryProperties, "plugins")
|
||||
plugins := profile.Plugins.ToMap()
|
||||
for key, pluginValue := range plugins {
|
||||
telemetryProperties[key] = pluginValue
|
||||
}
|
||||
|
||||
delete(telemetryProperties, "authentication_features")
|
||||
telemetryProperties["authentication_features"] = strings.Join(profile.AuthenticationFeatures, ",")
|
||||
|
||||
return telemetryProperties, nil
|
||||
}
|
||||
|
||||
func (a *App) GetOrCreateTrueUpReviewStatus() (*model.TrueUpReviewStatus, *model.AppError) {
|
||||
nextDueDate := utils.GetNextTrueUpReviewDueDate(time.Now())
|
||||
status, err := a.Srv().Store().TrueUpReview().GetTrueUpReviewStatus(nextDueDate.UnixMilli())
|
||||
if err != nil {
|
||||
var nfErr *store.ErrNotFound
|
||||
switch {
|
||||
case errors.As(err, &nfErr):
|
||||
a.Log().Warn("Could not find true up review status")
|
||||
default:
|
||||
return nil, model.NewAppError("requestTrueUpReview", "api.license.true_up_review.get_status_error", nil, "Could not get true up status records", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
status, err = a.Srv().Store().TrueUpReview().CreateTrueUpReviewStatusRecord(&model.TrueUpReviewStatus{DueDate: nextDueDate.UnixMilli(), Completed: false})
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("requestTrueUpReview", "api.license.true_up_review.create_error", nil, "Could not create true up status record", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
Ссылка в новой задаче
Block a user