Этот коммит содержится в:
Conor Macpherson
2022-12-05 15:07:03 -05:00
родитель 352eedf7c1 43e26ccda2
Коммит 6c43daa218
34 изменённых файлов: 342 добавлений и 193 удалений

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

@@ -139,20 +139,22 @@ func changeSubscription(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
changedProduct, err := c.App.Cloud().GetCloudProduct(userId, changedSub.ProductID)
if err != nil {
c.Err = model.NewAppError("Api4.changeSubscription", "api.cloud.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
product, err := c.App.Cloud().GetCloudProduct(c.AppContext.Session().UserId, subscriptionChange.ProductID)
if err != nil || product == nil {
c.Logger.Error("Error finding the new cloud product", mlog.Err(err))
}
starterSku := fmt.Sprintf("%s-%s", model.SubscriptionFamilyCloud, model.ProductSkuStarter)
if changedProduct.SKU != starterSku {
if product.SKU != starterSku {
w.Write(json)
return
}
isYearly := product.IsYearly()
// Log failures for purchase confirmation email, but don't show an error to the user so as not to confuse them
// At this point, the upgrade is complete.
if appErr := c.App.SendUpgradeConfirmationEmail(); appErr != nil {
if appErr := c.App.SendUpgradeConfirmationEmail(isYearly); appErr != nil {
c.Logger.Error("Error sending purchase confirmation email", mlog.Err(appErr))
}
@@ -650,7 +652,26 @@ func handleCWSWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
case model.EventTypeSendUpgradeConfirmationEmail:
if nErr := c.App.SendUpgradeConfirmationEmail(); nErr != nil {
// isYearly determines whether to send the yearly or monthly Upgrade email
isYearly := false
if event.Subscription != nil && event.CloudWorkspaceOwner != nil {
user, appErr := c.App.GetUserByUsername(event.CloudWorkspaceOwner.UserName)
if appErr != nil {
c.Err = model.NewAppError("Api4.handleCWSWebhook", appErr.Id, nil, appErr.Error(), appErr.StatusCode)
return
}
// Get the current cloud product to determine whether it's a monthly or yearly product
product, err := c.App.Cloud().GetCloudProduct(user.Id, event.Subscription.ProductID)
if err != nil {
c.Err = model.NewAppError("Api4.handleCWSWebhook", "api.cloud.request_error", nil, err.Error(), http.StatusInternalServerError)
return
}
isYearly = product.IsYearly()
}
if nErr := c.App.SendUpgradeConfirmationEmail(isYearly); nErr != nil {
c.Err = nErr
return
}

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

@@ -61,7 +61,7 @@ func connectWebSocket(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
wc := c.App.Srv().Platform().NewWebConn(cfg, c.App, c.App.Srv().Channels().GetPluginsEnvironment)
wc := c.App.Srv().Platform().NewWebConn(cfg, c.App, c.App.Srv().Channels())
if c.AppContext.Session().UserId != "" {
c.App.Srv().Platform().HubRegister(wc)
}

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

@@ -1038,7 +1038,7 @@ type AppIface interface {
SendPasswordReset(email string, siteURL string) (bool, *model.AppError)
SendPaymentFailedEmail(failedPayment *model.FailedPayment) *model.AppError
SendTestPushNotification(deviceID string) string
SendUpgradeConfirmationEmail() *model.AppError
SendUpgradeConfirmationEmail(isYearly bool) *model.AppError
ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string)
SessionHasPermissionTo(session model.Session, permission *model.Permission) bool
SessionHasPermissionToAny(session model.Session, permissions []*model.Permission) bool

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

@@ -346,7 +346,7 @@ func (a *App) CreateChannel(c request.CTX, channel *model.Channel, addMember boo
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.ChannelHasBeenCreated(pluginContext, sc)
return true
}, plugin.ChannelHasBeenCreatedID)
@@ -432,7 +432,7 @@ func (a *App) handleCreationEvent(c request.CTX, userID, otherUserID string, cha
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.ChannelHasBeenCreated(pluginContext, channel)
return true
}, plugin.ChannelHasBeenCreatedID)
@@ -1602,7 +1602,7 @@ func (a *App) AddChannelMember(c request.CTX, userID string, channel *model.Chan
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasJoinedChannel(pluginContext, cm, userRequestor)
return true
}, plugin.UserHasJoinedChannelID)
@@ -2180,7 +2180,7 @@ func (a *App) JoinChannel(c request.CTX, channel *model.Channel, userID string)
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasJoinedChannel(pluginContext, cm, nil)
return true
}, plugin.UserHasJoinedChannelID)
@@ -2492,7 +2492,7 @@ func (a *App) removeUserFromChannel(c request.CTX, userIDToRemove string, remove
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasLeftChannel(pluginContext, cm, actorUser)
return true
}, plugin.UserHasLeftChannelID)

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

@@ -322,5 +322,33 @@ func (s *hooksService) RegisterHooks(productID string, hooks any) error {
return errors.New("could not find plugins environment")
}
return s.ch.pluginsEnvironment.AddProduct(productID, hooks)
return s.ch.srv.hooksManager.AddProduct(productID, hooks)
}
func (ch *Channels) RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks) bool, hookId int) {
if env := ch.pluginsEnvironment; env != nil {
env.RunMultiPluginHook(hookRunnerFunc, hookId)
}
// run hook for the products
ch.srv.hooksManager.RunMultiHook(hookRunnerFunc, hookId)
}
func (ch *Channels) HooksForPluginOrProduct(id string) (plugin.Hooks, error) {
var hooks plugin.Hooks
if env := ch.pluginsEnvironment; env != nil {
// we intentionally ignore the error here, because the id can be a product id
// we are going to check if we have the hooks or not
hooks, _ = env.HooksForPlugin(id)
if hooks != nil {
return hooks, nil
}
}
hooks = ch.srv.hooksManager.HooksForProduct(id)
if hooks != nil {
return hooks, nil
}
return nil, fmt.Errorf("could not find hooks for id %s", id)
}

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

@@ -174,7 +174,7 @@ func getNextBillingDateString() string {
return fmt.Sprintf("%s %d, %d", t.Month(), t.Day(), t.Year())
}
func (a *App) SendUpgradeConfirmationEmail() *model.AppError {
func (a *App) SendUpgradeConfirmationEmail(isYearly bool) *model.AppError {
sysAdmins, e := a.getSysAdminsEmailRecipients()
if e != nil {
return e
@@ -200,7 +200,7 @@ func (a *App) SendUpgradeConfirmationEmail() *model.AppError {
name = admin.Username
}
err := a.Srv().EmailService.SendCloudUpgradeConfirmationEmail(admin.Email, name, billingDate, admin.Locale, *a.Config().ServiceSettings.SiteURL, subscription.GetWorkSpaceNameFromDNS())
err := a.Srv().EmailService.SendCloudUpgradeConfirmationEmail(admin.Email, name, billingDate, admin.Locale, *a.Config().ServiceSettings.SiteURL, subscription.GetWorkSpaceNameFromDNS(), isYearly)
if err != nil {
a.Log().Error("Error sending trial ended email to", mlog.String("email", admin.Email), mlog.Err(err))
countNotOks++

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

@@ -28,10 +28,6 @@ func (s *Server) clusterRemovePluginHandler(msg *model.ClusterMessage) {
}
func (s *Server) clusterPluginEventHandler(msg *model.ClusterMessage) {
env := s.Channels().GetPluginsEnvironment()
if env == nil {
return
}
if msg.Props == nil {
mlog.Warn("ClusterMessage.Props for plugin event should not be nil")
return
@@ -48,7 +44,12 @@ func (s *Server) clusterPluginEventHandler(msg *model.ClusterMessage) {
return
}
hooks, err := env.HooksForPlugin(pluginID)
channels, ok := s.products["channels"].(*Channels)
if !ok {
return
}
hooks, err := channels.HooksForPluginOrProduct(pluginID)
if err != nil {
mlog.Warn("Getting hooks for plugin failed", mlog.String("plugin_id", pluginID), mlog.Err(err))
return

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

@@ -233,13 +233,13 @@ func (es *Service) SendWelcomeEmail(userID string, email string, verified bool,
return nil
}
func (es *Service) SendCloudUpgradeConfirmationEmail(userEmail, name, date, locale, siteURL, workspaceName string) error {
func (es *Service) SendCloudUpgradeConfirmationEmail(userEmail, name, date, locale, siteURL, workspaceName string, isYearly bool) error {
T := i18n.GetUserTranslations(locale)
subject := T("api.templates.cloud_upgrade_confirmation.subject")
data := es.NewEmailTemplateData(locale)
data.Props["Title"] = T("api.templates.cloud_upgrade_confirmation.title")
data.Props["SubTitle"] = T("api.templates.cloud_upgrade_confirmation.subtitle", map[string]any{"WorkspaceName": workspaceName, "Date": date})
data.Props["SubTitle"] = T("api.templates.cloud_upgrade_confirmation_monthly.subtitle", map[string]any{"WorkspaceName": workspaceName, "Date": date})
data.Props["SiteURL"] = siteURL
data.Props["ButtonURL"] = siteURL
data.Props["Button"] = T("api.templates.cloud_welcome_email.button")
@@ -247,6 +247,12 @@ func (es *Service) SendCloudUpgradeConfirmationEmail(userEmail, name, date, loca
data.Props["QuestionInfo"] = T("api.templates.questions_footer.info")
data.Props["SupportEmail"] = *es.config().SupportSettings.SupportEmail
if isYearly {
data.Props["SubTitle"] = T("api.templates.cloud_upgrade_confirmation_yearly.subtitle", map[string]any{"WorkspaceName": workspaceName})
data.Props["ButtonURL"] = siteURL + "/admin_console/billing/billing_history"
data.Props["Button"] = T("api.templates.cloud_welcome_email.yearly_plan_button")
}
body, err := es.templatesContainer.RenderToString("cloud_upgrade_confirmation", data)
if err != nil {
return err

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

@@ -258,7 +258,7 @@ func TestSendCloudUpgradedEmail(t *testing.T) {
emailTo := "testclouduser@example.com"
emailToUsername := strings.Split(emailTo, "@")[0]
t.Run("SendCloudUpgradedEmail", func(t *testing.T) {
t.Run("SendCloudMonthlyUpgradedEmail", func(t *testing.T) {
verifyMailbox := func(t *testing.T) {
t.Helper()
@@ -278,10 +278,44 @@ func TestSendCloudUpgradedEmail(t *testing.T) {
require.NoError(t, err, "Could not get message from mailbox")
require.Contains(t, resultsEmail.Body.Text, "You are now upgraded!", "Wrong received message %s", resultsEmail.Body.Text)
require.Contains(t, resultsEmail.Body.Text, "SomeName workspace has now been upgraded", "Wrong received message %s", resultsEmail.Body.Text)
require.Contains(t, resultsEmail.Body.Text, "You'll be billed from", "Wrong received message %s", resultsEmail.Body.Text)
require.Contains(t, resultsEmail.Body.Text, "Open Mattermost", "Wrong received message %s", resultsEmail.Body.Text)
}
mail.DeleteMailBox(emailTo)
err := th.service.SendCloudUpgradeConfirmationEmail(emailTo, emailToUsername, "June 23, 2200", th.BasicUser.Locale, "https://example.com", "SomeName")
// Send Update to Monthly Plan email
err := th.service.SendCloudUpgradeConfirmationEmail(emailTo, emailToUsername, "June 23, 2200", th.BasicUser.Locale, "https://example.com", "SomeName", false)
require.NoError(t, err)
verifyMailbox(t)
})
t.Run("SendCloudYearlyUpgradedEmail", func(t *testing.T) {
verifyMailbox := func(t *testing.T) {
t.Helper()
var resultsMailbox mail.JSONMessageHeaderInbucket
err2 := mail.RetryInbucket(5, func() error {
var err error
resultsMailbox, err = mail.GetMailBox(emailTo)
return err
})
if err2 != nil {
t.Skipf("No email was received, maybe due load on the server: %v", err2)
}
require.Len(t, resultsMailbox, 1)
require.Contains(t, resultsMailbox[0].To[0], emailTo, "Wrong To: recipient")
resultsEmail, err := mail.GetMessageFromMailbox(emailTo, resultsMailbox[0].ID)
require.NoError(t, err, "Could not get message from mailbox")
require.Contains(t, resultsEmail.Body.Text, "You are now upgraded!", "Wrong received message %s", resultsEmail.Body.Text)
require.Contains(t, resultsEmail.Body.Text, "SomeName workspace has now been upgraded", "Wrong received message %s", resultsEmail.Body.Text)
require.Contains(t, resultsEmail.Body.Text, "View your invoice", "Wrong received message %s", resultsEmail.Body.Text)
}
mail.DeleteMailBox(emailTo)
// Send Update to Monthly Plan email
err := th.service.SendCloudUpgradeConfirmationEmail(emailTo, emailToUsername, "June 23, 2200", th.BasicUser.Locale, "https://example.com", "SomeName", true)
require.NoError(t, err)
verifyMailbox(t)

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

@@ -125,13 +125,13 @@ func (_m *ServiceInterface) SendChangeUsernameEmail(newUsername string, _a1 stri
return r0
}
// SendCloudUpgradeConfirmationEmail provides a mock function with given fields: userEmail, name, trialEndDate, locale, siteURL, workspaceName
func (_m *ServiceInterface) SendCloudUpgradeConfirmationEmail(userEmail string, name string, trialEndDate string, locale string, siteURL string, workspaceName string) error {
ret := _m.Called(userEmail, name, trialEndDate, locale, siteURL, workspaceName)
// SendCloudUpgradeConfirmationEmail provides a mock function with given fields: userEmail, name, trialEndDate, locale, siteURL, workspaceName, isYearly
func (_m *ServiceInterface) SendCloudUpgradeConfirmationEmail(userEmail string, name string, trialEndDate string, locale string, siteURL string, workspaceName string, isYearly bool) error {
ret := _m.Called(userEmail, name, trialEndDate, locale, siteURL, workspaceName, isYearly)
var r0 error
if rf, ok := ret.Get(0).(func(string, string, string, string, string, string) error); ok {
r0 = rf(userEmail, name, trialEndDate, locale, siteURL, workspaceName)
if rf, ok := ret.Get(0).(func(string, string, string, string, string, string, bool) error); ok {
r0 = rf(userEmail, name, trialEndDate, locale, siteURL, workspaceName, isYearly)
} else {
r0 = ret.Error(0)
}

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

@@ -129,7 +129,7 @@ type ServiceInterface interface {
SendVerifyEmail(userEmail, locale, siteURL, token, redirect string) error
SendSignInChangeEmail(email, method, locale, siteURL string) error
SendWelcomeEmail(userID string, email string, verified bool, disableWelcomeEmail bool, locale, siteURL, redirect string) error
SendCloudUpgradeConfirmationEmail(userEmail, name, trialEndDate, locale, siteURL, workspaceName string) error
SendCloudUpgradeConfirmationEmail(userEmail, name, trialEndDate, locale, siteURL, workspaceName string, isYearly bool) error
SendCloudWelcomeEmail(userEmail, locale, teamInviteID, workSpaceName, dns, siteURL string) error
SendPasswordChangeEmail(email, method, locale, siteURL string) error
SendUserAccessTokenAddedEmail(email, locale, siteURL string) error

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

@@ -898,7 +898,7 @@ func (a *App) DoUploadFileExpectModification(c request.CTX, now time.Time, rawTe
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
var rejectionError *model.AppError
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
var newBytes bytes.Buffer
replacementInfo, rejectionReason := hooks.FileWillBeUploaded(pluginContext, info, bytes.NewReader(data), &newBytes)
if rejectionReason != "" {

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

@@ -160,7 +160,7 @@ func (a *App) DoLogin(c *request.Context, w http.ResponseWriter, r *http.Request
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
var rejectionReason string
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
rejectionReason = hooks.UserWillLogIn(pluginContext, user)
return rejectionReason == ""
}, plugin.UserWillLogInID)
@@ -229,7 +229,7 @@ func (a *App) DoLogin(c *request.Context, w http.ResponseWriter, r *http.Request
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasLoggedIn(pluginContext, user)
return true
}, plugin.UserHasLoggedInID)

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

@@ -53,7 +53,7 @@ func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnbo
return
}
hooks, err := pluginsEnvironment.HooksForPlugin(id)
hooks, err := a.ch.HooksForPluginOrProduct(id)
if err != nil {
mlog.Warn("Getting hooks for plugin failed", mlog.String("plugin_id", id), mlog.Err(err))
return

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

@@ -15546,7 +15546,7 @@ func (a *OpenTracingAppLayer) SendTestPushNotification(deviceID string) string {
return resultVar0
}
func (a *OpenTracingAppLayer) SendUpgradeConfirmationEmail() *model.AppError {
func (a *OpenTracingAppLayer) SendUpgradeConfirmationEmail(isYearly bool) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SendUpgradeConfirmationEmail")
@@ -15558,7 +15558,7 @@ func (a *OpenTracingAppLayer) SendUpgradeConfirmationEmail() *model.AppError {
}()
defer span.Finish()
resultVar0 := a.app.SendUpgradeConfirmationEmail()
resultVar0 := a.app.SendUpgradeConfirmationEmail(isYearly)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))

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

@@ -95,7 +95,12 @@ type PlatformService struct {
additionalClusterHandlers map[model.ClusterEvent]einterfaces.ClusterMessageHandler
sharedChannelService SharedChannelServiceIFace
pluginEnv *plugin.Environment
pluginEnv HookRunner
}
type HookRunner interface {
RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks) bool, hookId int)
GetPluginsEnvironment() *plugin.Environment
}
// New creates a new PlatformService.
@@ -426,17 +431,17 @@ func (ps *PlatformService) SetSharedChannelService(s SharedChannelServiceIFace)
ps.sharedChannelService = s
}
func (ps *PlatformService) SetPluginsEnvironment(env *plugin.Environment) {
ps.pluginEnv = env
func (ps *PlatformService) SetPluginsEnvironment(runner HookRunner) {
ps.pluginEnv = runner
}
// GetPluginStatuses meant to be used by cluster implementation
func (ps *PlatformService) GetPluginStatuses() (model.PluginStatuses, *model.AppError) {
if ps.pluginEnv == nil {
if ps.pluginEnv == nil || ps.pluginEnv.GetPluginsEnvironment() == nil {
return nil, model.NewAppError("GetPluginStatuses", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
}
pluginStatuses, err := ps.pluginEnv.Statuses()
pluginStatuses, err := ps.pluginEnv.GetPluginsEnvironment().Statuses()
if err != nil {
return nil, model.NewAppError("GetPluginStatuses", "app.plugin.get_statuses.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}

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

@@ -72,15 +72,15 @@ type WebConnConfig struct {
// It contains all the necessary state to manage sending/receiving data to/from
// a websocket.
type WebConn struct {
sessionExpiresAt int64 // This should stay at the top for 64-bit alignment of 64-bit words accessed atomically
Platform *PlatformService
Suite SuiteIFace
PluginsEnvironment func() *plugin.Environment
WebSocket *websocket.Conn
T i18n.TranslateFunc
Locale string
Sequence int64
UserId string
sessionExpiresAt int64 // This should stay at the top for 64-bit alignment of 64-bit words accessed atomically
Platform *PlatformService
Suite SuiteIFace
HookRunner HookRunner
WebSocket *websocket.Conn
T i18n.TranslateFunc
Locale string
Sequence int64
UserId string
allChannelMembers map[string]string
lastAllChannelMembersTime int64
@@ -162,7 +162,7 @@ func (ps *PlatformService) PopulateWebConnConfig(s *model.Session, cfg *WebConnC
}
// NewWebConn returns a new WebConn instance.
func (ps *PlatformService) NewWebConn(cfg *WebConnConfig, suite SuiteIFace, envFn func() *plugin.Environment) *WebConn {
func (ps *PlatformService) NewWebConn(cfg *WebConnConfig, suite SuiteIFace, runner HookRunner) *WebConn {
if cfg.Session.UserId != "" {
ps.Go(func() {
suite.SetStatusOnline(cfg.Session.UserId, false)
@@ -200,7 +200,7 @@ func (ps *PlatformService) NewWebConn(cfg *WebConnConfig, suite SuiteIFace, envF
wc := &WebConn{
Platform: ps,
Suite: suite,
PluginsEnvironment: envFn,
HookRunner: runner,
send: cfg.activeQueue,
deadQueue: cfg.deadQueue,
deadQueuePointer: cfg.deadQueuePointer,
@@ -222,14 +222,12 @@ func (ps *PlatformService) NewWebConn(cfg *WebConnConfig, suite SuiteIFace, envF
wc.SetSessionExpiresAt(cfg.Session.ExpiresAt)
wc.SetConnectionID(cfg.ConnectionID)
if pluginsEnvironment := wc.PluginsEnvironment(); pluginsEnvironment != nil {
wc.Platform.Go(func() {
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
hooks.OnWebSocketConnect(wc.GetConnectionID(), wc.UserId)
return true
}, plugin.OnWebSocketConnectID)
})
}
wc.Platform.Go(func() {
wc.HookRunner.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.OnWebSocketConnect(wc.GetConnectionID(), wc.UserId)
return true
}, plugin.OnWebSocketConnectID)
})
return wc
}
@@ -238,12 +236,10 @@ func (wc *WebConn) pluginPostedConsumer(wg *sync.WaitGroup) {
defer wg.Done()
for msg := range wc.pluginPosted {
if pluginsEnvironment := wc.PluginsEnvironment(); pluginsEnvironment != nil {
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
hooks.WebSocketMessageHasBeenPosted(msg.connectionID, msg.userID, msg.req)
return true
}, plugin.WebSocketMessageHasBeenPostedID)
}
wc.HookRunner.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.WebSocketMessageHasBeenPosted(msg.connectionID, msg.userID, msg.req)
return true
}, plugin.WebSocketMessageHasBeenPostedID)
}
}
@@ -328,14 +324,12 @@ func (wc *WebConn) Pump() {
wc.Platform.HubUnregister(wc)
close(wc.pumpFinished)
if pluginsEnvironment := wc.PluginsEnvironment(); pluginsEnvironment != nil {
wc.Platform.Go(func() {
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
hooks.OnWebSocketDisconnect(wc.GetConnectionID(), wc.UserId)
return true
}, plugin.OnWebSocketDisconnectID)
})
}
wc.Platform.Go(func() {
wc.HookRunner.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.OnWebSocketDisconnect(wc.GetConnectionID(), wc.UserId)
return true
}, plugin.OnWebSocketDisconnectID)
})
}
func (wc *WebConn) readPump() {

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

@@ -5,6 +5,7 @@ package platform
import (
"bytes"
"errors"
"net"
"net/http"
"net/http/httptest"
@@ -18,13 +19,27 @@ import (
"github.com/mattermost/mattermost-server/v6/plugin"
)
type hookRunner struct {
}
func (h *hookRunner) RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks) bool, hookId int) {
}
func (h *hookRunner) HooksForPlugin(id string) (plugin.Hooks, error) {
return nil, errors.New("not implemented")
}
func (h *hookRunner) GetPluginsEnvironment() *plugin.Environment {
return nil
}
func TestWebConnAddDeadQueue(t *testing.T) {
th := Setup(t)
defer th.TearDown()
wc := th.Service.NewWebConn(&WebConnConfig{
WebSocket: &websocket.Conn{},
}, th.Suite, func() *plugin.Environment { return nil })
}, th.Suite, &hookRunner{})
for i := 0; i < 2; i++ {
msg := &model.WebSocketEvent{}
@@ -53,7 +68,7 @@ func TestWebConnIsInDeadQueue(t *testing.T) {
wc := th.Service.NewWebConn(&WebConnConfig{
WebSocket: &websocket.Conn{},
}, th.Suite, func() *plugin.Environment { return nil })
}, th.Suite, &hookRunner{})
var i int
for ; i < 2; i++ {
@@ -114,7 +129,7 @@ func TestWebConnClearDeadQueue(t *testing.T) {
wc := th.Service.NewWebConn(&WebConnConfig{
WebSocket: &websocket.Conn{},
}, th.Suite, func() *plugin.Environment { return nil })
}, th.Suite, &hookRunner{})
var i int
for ; i < 2; i++ {
@@ -140,7 +155,7 @@ func TestWebConnDrainDeadQueue(t *testing.T) {
cfg := &WebConnConfig{
WebSocket: c,
}
return th.Service.NewWebConn(cfg, th.Suite, func() *plugin.Environment { return nil })
return th.Service.NewWebConn(cfg, th.Suite, &hookRunner{})
}
t.Run("Empty Queue", func(t *testing.T) {

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

@@ -17,7 +17,6 @@ import (
platform_mocks "github.com/mattermost/mattermost-server/v6/app/platform/mocks"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin"
"github.com/mattermost/mattermost-server/v6/shared/i18n"
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
"github.com/mattermost/mattermost-server/v6/testlib"
@@ -50,7 +49,7 @@ func registerDummyWebConn(t *testing.T, th *TestHelper, addr net.Addr, session *
TFunc: i18n.IdentityTfunc(),
Locale: "en",
}
wc := th.Service.NewWebConn(cfg, th.Suite, func() *plugin.Environment { return nil })
wc := th.Service.NewWebConn(cfg, th.Suite, &hookRunner{})
th.Service.HubRegister(wc)
go wc.Pump()
return wc

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

@@ -93,7 +93,7 @@ func (ch *Channels) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment
defer ch.pluginsLock.Unlock()
ch.pluginsEnvironment = pluginsEnvironment
ch.srv.Platform().SetPluginsEnvironment(pluginsEnvironment)
ch.srv.Platform().SetPluginsEnvironment(ch)
}
func (ch *Channels) syncPluginsActiveState() {
@@ -213,7 +213,7 @@ func (a *App) InitPlugins(c *request.Context, pluginDir, webappPluginDir string)
func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir string) {
// Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
defer func() {
ch.srv.Platform().SetPluginsEnvironment(ch.pluginsEnvironment)
ch.srv.Platform().SetPluginsEnvironment(ch)
}()
ch.pluginsLock.RLock()
@@ -279,7 +279,7 @@ func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir s
ch.syncPluginsActiveState()
}
if pluginsEnvironment := ch.GetPluginsEnvironment(); pluginsEnvironment != nil {
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
ch.RunMultiHook(func(hooks plugin.Hooks) bool {
if err := hooks.OnConfigurationChange(); err != nil {
ch.srv.Log().Error("Plugin OnConfigurationChange hook failed", mlog.Err(err))
}

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

@@ -1234,7 +1234,7 @@ func TestHookRunDataRetention(t *testing.T) {
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
hookCalled := false
th.App.GetPluginsEnvironment().RunMultiPluginHook(func(hooks plugin.Hooks) bool {
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
n, _ := hooks.RunDataRetention(0, 0)
// Ensure return it correct
assert.Equal(t, int64(100), n)
@@ -1278,7 +1278,7 @@ func TestHookOnSendDailyTelemetry(t *testing.T) {
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
hookCalled := false
th.App.GetPluginsEnvironment().RunMultiPluginHook(func(hooks plugin.Hooks) bool {
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.OnSendDailyTelemetry()
hookCalled = true
@@ -1322,7 +1322,7 @@ func TestHookOnCloudLimitsUpdated(t *testing.T) {
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
hookCalled := false
th.App.GetPluginsEnvironment().RunMultiPluginHook(func(hooks plugin.Hooks) bool {
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.OnCloudLimitsUpdated(nil)
hookCalled = true

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

@@ -270,7 +270,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
}
var rejectionError *model.AppError
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
replacementPost, rejectionReason := hooks.MessageWillBePosted(pluginContext, post.ForPlugin())
if rejectionReason != "" {
id := "Post rejected by plugin. " + rejectionReason
@@ -332,7 +332,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
pluginPost := rpost.ForPlugin()
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.MessageHasBeenPosted(pluginContext, pluginPost)
return true
}, plugin.MessageHasBeenPostedID)
@@ -661,7 +661,7 @@ func (a *App) UpdatePost(c *request.Context, post *model.Post, safeUpdate bool)
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
var rejectionReason string
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
newPost, rejectionReason = hooks.MessageWillBeUpdated(pluginContext, newPost.ForPlugin(), oldPost.ForPlugin())
return post != nil
}, plugin.MessageWillBeUpdatedID)
@@ -689,7 +689,7 @@ func (a *App) UpdatePost(c *request.Context, post *model.Post, safeUpdate bool)
pluginNewPost := newPost.ForPlugin()
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.MessageHasBeenUpdated(pluginContext, pluginNewPost, pluginOldPost)
return true
}, plugin.MessageHasBeenUpdatedID)

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

@@ -46,7 +46,7 @@ func (a *App) SaveReactionForPost(c *request.Context, reaction *model.Reaction)
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.ReactionHasBeenAdded(pluginContext, reaction)
return true
}, plugin.ReactionHasBeenAddedID)
@@ -145,7 +145,7 @@ func (a *App) DeleteReactionForPost(c *request.Context, reaction *model.Reaction
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.ReactionHasBeenRemoved(pluginContext, reaction)
return true
}, plugin.ReactionHasBeenRemovedID)

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

@@ -161,6 +161,8 @@ type Server struct {
tracer *tracing.Tracer
products map[string]Product
hooksManager *product.HooksManager
}
func (s *Server) Store() store.Store {
@@ -255,6 +257,8 @@ func NewServer(options ...Option) (*Server, error) {
return nil, errors.Wrapf(err, "unable to create teams service")
}
s.hooksManager = product.NewHooksManager(s.GetMetrics())
// ensure app implements `product.UserService`
var _ product.UserService = (*App)(nil)

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

@@ -854,7 +854,7 @@ func (a *App) JoinUserToTeam(c request.CTX, team *model.Team, user *model.User,
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasJoinedTeam(pluginContext, teamMember, actor)
return true
}, plugin.UserHasJoinedTeamID)
@@ -1228,7 +1228,7 @@ func (a *App) postProcessTeamMemberLeave(c request.CTX, teamMember *model.TeamMe
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasLeftTeam(pluginContext, teamMember, actor)
return true
}, plugin.UserHasLeftTeamID)

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

@@ -67,7 +67,7 @@ func (a *App) runPluginsHook(c *request.Context, info *model.FileInfo, file io.R
var rejErr *model.AppError
var once sync.Once
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
once.Do(func() {
hookHasRunCh <- struct{}{}
})

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

@@ -311,7 +311,7 @@ func (a *App) createUserOrGuest(c request.CTX, user *model.User, guest bool) (*m
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasBeenCreated(pluginContext, ruser)
return true
}, plugin.UserHasBeenCreatedID)

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

@@ -16,5 +16,5 @@ func (a *App) PopulateWebConnConfig(s *model.Session, cfg *platform.WebConnConfi
// NewWebConn returns a new WebConn instance.
func (a *App) NewWebConn(cfg *platform.WebConnConfig) *platform.WebConn {
return a.Srv().Platform().NewWebConn(cfg, a, a.ch.GetPluginsEnvironment)
return a.Srv().Platform().NewWebConn(cfg, a, a.ch)
}

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

@@ -3164,12 +3164,16 @@
"translation": "Mattermost Upgrade Confirmation"
},
{
"id": "api.templates.cloud_upgrade_confirmation.subtitle",
"id": "api.templates.cloud_upgrade_confirmation.title",
"translation": "You are now upgraded!"
},
{
"id": "api.templates.cloud_upgrade_confirmation_monthly.subtitle",
"translation": "Your {{.WorkspaceName}} workspace has now been upgraded. You'll be billed from {{.Date}}"
},
{
"id": "api.templates.cloud_upgrade_confirmation.title",
"translation": "You are now upgraded!"
"id": "api.templates.cloud_upgrade_confirmation_yearly.subtitle",
"translation": "Your {{.WorkspaceName}} workspace has now been upgraded."
},
{
"id": "api.templates.cloud_welcome_email.add_apps_info",
@@ -3239,6 +3243,10 @@
"id": "api.templates.cloud_welcome_email.title",
"translation": "Your workspace is ready to go!"
},
{
"id": "api.templates.cloud_welcome_email.yearly_plan_button",
"translation": "View your invoice"
},
{
"id": "api.templates.copyright",
"translation": "© 2021 Mattermost, Inc. 530 Lytton Avenue, Second floor, Palo Alto, CA, 94301"

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

@@ -50,7 +50,6 @@ type PrepackagedPlugin struct {
// of active plugins.
type Environment struct {
registeredPlugins sync.Map
registeredProducts sync.Map
pluginHealthCheckJob *PluginHealthCheckJob
logger *mlog.Logger
metrics einterfaces.MetricsInterface
@@ -326,26 +325,6 @@ func (env *Environment) Activate(id string) (manifest *model.Manifest, activated
return pluginInfo.Manifest, true, nil
}
func (env *Environment) AddProduct(productID string, hooks any) error {
prod, err := newAdapter(hooks)
if err != nil {
return err
}
rp := &registeredProduct{
productID: productID,
adapter: prod,
}
env.registeredProducts.Store(productID, rp)
return nil
}
func (env *Environment) RemoveProduct(productID string) {
env.registeredProducts.Delete(productID)
}
func (env *Environment) RemovePlugin(id string) {
if _, ok := env.registeredPlugins.Load(id); ok {
env.registeredPlugins.Delete(id)
@@ -499,12 +478,6 @@ func (env *Environment) HooksForPlugin(id string) (Hooks, error) {
}
}
if p, ok := env.registeredProducts.Load(id); ok {
rp := p.(*registeredProduct)
return rp.adapter, nil
}
return nil, fmt.Errorf("plugin not found: %v", id)
}
@@ -533,24 +506,6 @@ func (env *Environment) RunMultiPluginHook(hookRunnerFunc func(hooks Hooks) bool
return result
})
env.registeredProducts.Range(func(key, value any) bool {
rp := value.(*registeredProduct)
if !rp.Implements(hookId) {
return true
}
hookStartTime := time.Now()
result := hookRunnerFunc(rp.adapter)
if env.metrics != nil {
elapsedTime := float64(time.Since(hookStartTime)) / float64(time.Second)
env.metrics.ObservePluginMultiHookIterationDuration(rp.productID, elapsedTime)
}
return result
})
if env.metrics != nil {
elapsedTime := float64(time.Since(startTime)) / float64(time.Second)
env.metrics.ObservePluginMultiHookDuration(elapsedTime)

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

@@ -399,13 +399,13 @@ type {{.Name}}IFace interface {
{{end}}
type hooksAdapter struct {
type HooksAdapter struct {
implemented map[int]struct{}
productHooks any
}
func newAdapter(productHooks any) (*hooksAdapter, error) {
a := &hooksAdapter{
func NewAdapter(productHooks any) (*HooksAdapter, error) {
a := &HooksAdapter{
implemented: make(map[int]struct{}),
productHooks: productHooks,
}
@@ -427,7 +427,7 @@ func newAdapter(productHooks any) (*hooksAdapter, error) {
}
{{range .HooksMethods}}
func (a *hooksAdapter) {{.Name}}{{funcStyle .Params}} {{funcStyle .Return}} {
func (a *HooksAdapter) {{.Name}}{{funcStyle .Params}} {{funcStyle .Return}} {
if _, ok := a.implemented[{{.Name}}ID]; !ok {
panic("product hooks must implement {{.Name}}")
}

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

@@ -7,13 +7,13 @@ import (
"net/http"
)
type registeredProduct struct {
productID string
adapter Hooks
type RegisteredProduct struct {
ProductID string
Adapter Hooks
}
func (rp *registeredProduct) Implements(hookId int) bool {
adapter, ok := rp.adapter.(*hooksAdapter)
func (rp *RegisteredProduct) Implements(hookId int) bool {
adapter, ok := rp.Adapter.(*HooksAdapter)
if !ok {
return false
}
@@ -23,19 +23,19 @@ func (rp *registeredProduct) Implements(hookId int) bool {
}
// Implemented method is overridden intentionally to prevent calling it from outside.
func (a *hooksAdapter) Implemented() ([]string, error) {
func (a *HooksAdapter) Implemented() ([]string, error) {
return nil, nil
}
// OnActivate is overridden intentionally as product should not call it.
func (a *hooksAdapter) OnActivate() error {
func (a *HooksAdapter) OnActivate() error {
return nil
}
// OnDeactivate is overridden intentionally as product should not call it.
func (a *hooksAdapter) OnDeactivate() error {
func (a *HooksAdapter) OnDeactivate() error {
return nil
}
// ServeHTTP is overridden intentionally as product should not call it.
func (a *hooksAdapter) ServeHTTP(c *Context, w http.ResponseWriter, r *http.Request) {}
func (a *HooksAdapter) ServeHTTP(c *Context, w http.ResponseWriter, r *http.Request) {}

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

@@ -138,13 +138,13 @@ type GetTopicMetadataByIdsIFace interface {
GetTopicMetadataByIds(c *Context, topicType string, topicIds []string) (map[string]*model.TopicMetadata, error)
}
type hooksAdapter struct {
type HooksAdapter struct {
implemented map[int]struct{}
productHooks any
}
func newAdapter(productHooks any) (*hooksAdapter, error) {
a := &hooksAdapter{
func NewAdapter(productHooks any) (*HooksAdapter, error) {
a := &HooksAdapter{
implemented: make(map[int]struct{}),
productHooks: productHooks,
}
@@ -433,7 +433,7 @@ func newAdapter(productHooks any) (*hooksAdapter, error) {
return a, nil
}
func (a *hooksAdapter) OnConfigurationChange() error {
func (a *HooksAdapter) OnConfigurationChange() error {
if _, ok := a.implemented[OnConfigurationChangeID]; !ok {
panic("product hooks must implement OnConfigurationChange")
}
@@ -442,7 +442,7 @@ func (a *hooksAdapter) OnConfigurationChange() error {
}
func (a *hooksAdapter) ExecuteCommand(c *Context, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
func (a *HooksAdapter) ExecuteCommand(c *Context, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
if _, ok := a.implemented[ExecuteCommandID]; !ok {
panic("product hooks must implement ExecuteCommand")
}
@@ -451,7 +451,7 @@ func (a *hooksAdapter) ExecuteCommand(c *Context, args *model.CommandArgs) (*mod
}
func (a *hooksAdapter) UserHasBeenCreated(c *Context, user *model.User) {
func (a *HooksAdapter) UserHasBeenCreated(c *Context, user *model.User) {
if _, ok := a.implemented[UserHasBeenCreatedID]; !ok {
panic("product hooks must implement UserHasBeenCreated")
}
@@ -460,7 +460,7 @@ func (a *hooksAdapter) UserHasBeenCreated(c *Context, user *model.User) {
}
func (a *hooksAdapter) UserWillLogIn(c *Context, user *model.User) string {
func (a *HooksAdapter) UserWillLogIn(c *Context, user *model.User) string {
if _, ok := a.implemented[UserWillLogInID]; !ok {
panic("product hooks must implement UserWillLogIn")
}
@@ -469,7 +469,7 @@ func (a *hooksAdapter) UserWillLogIn(c *Context, user *model.User) string {
}
func (a *hooksAdapter) UserHasLoggedIn(c *Context, user *model.User) {
func (a *HooksAdapter) UserHasLoggedIn(c *Context, user *model.User) {
if _, ok := a.implemented[UserHasLoggedInID]; !ok {
panic("product hooks must implement UserHasLoggedIn")
}
@@ -478,7 +478,7 @@ func (a *hooksAdapter) UserHasLoggedIn(c *Context, user *model.User) {
}
func (a *hooksAdapter) MessageWillBePosted(c *Context, post *model.Post) (*model.Post, string) {
func (a *HooksAdapter) MessageWillBePosted(c *Context, post *model.Post) (*model.Post, string) {
if _, ok := a.implemented[MessageWillBePostedID]; !ok {
panic("product hooks must implement MessageWillBePosted")
}
@@ -487,7 +487,7 @@ func (a *hooksAdapter) MessageWillBePosted(c *Context, post *model.Post) (*model
}
func (a *hooksAdapter) MessageWillBeUpdated(c *Context, newPost, oldPost *model.Post) (*model.Post, string) {
func (a *HooksAdapter) MessageWillBeUpdated(c *Context, newPost, oldPost *model.Post) (*model.Post, string) {
if _, ok := a.implemented[MessageWillBeUpdatedID]; !ok {
panic("product hooks must implement MessageWillBeUpdated")
}
@@ -496,7 +496,7 @@ func (a *hooksAdapter) MessageWillBeUpdated(c *Context, newPost, oldPost *model.
}
func (a *hooksAdapter) MessageHasBeenPosted(c *Context, post *model.Post) {
func (a *HooksAdapter) MessageHasBeenPosted(c *Context, post *model.Post) {
if _, ok := a.implemented[MessageHasBeenPostedID]; !ok {
panic("product hooks must implement MessageHasBeenPosted")
}
@@ -505,7 +505,7 @@ func (a *hooksAdapter) MessageHasBeenPosted(c *Context, post *model.Post) {
}
func (a *hooksAdapter) MessageHasBeenUpdated(c *Context, newPost, oldPost *model.Post) {
func (a *HooksAdapter) MessageHasBeenUpdated(c *Context, newPost, oldPost *model.Post) {
if _, ok := a.implemented[MessageHasBeenUpdatedID]; !ok {
panic("product hooks must implement MessageHasBeenUpdated")
}
@@ -514,7 +514,7 @@ func (a *hooksAdapter) MessageHasBeenUpdated(c *Context, newPost, oldPost *model
}
func (a *hooksAdapter) ChannelHasBeenCreated(c *Context, channel *model.Channel) {
func (a *HooksAdapter) ChannelHasBeenCreated(c *Context, channel *model.Channel) {
if _, ok := a.implemented[ChannelHasBeenCreatedID]; !ok {
panic("product hooks must implement ChannelHasBeenCreated")
}
@@ -523,7 +523,7 @@ func (a *hooksAdapter) ChannelHasBeenCreated(c *Context, channel *model.Channel)
}
func (a *hooksAdapter) UserHasJoinedChannel(c *Context, channelMember *model.ChannelMember, actor *model.User) {
func (a *HooksAdapter) UserHasJoinedChannel(c *Context, channelMember *model.ChannelMember, actor *model.User) {
if _, ok := a.implemented[UserHasJoinedChannelID]; !ok {
panic("product hooks must implement UserHasJoinedChannel")
}
@@ -532,7 +532,7 @@ func (a *hooksAdapter) UserHasJoinedChannel(c *Context, channelMember *model.Cha
}
func (a *hooksAdapter) UserHasLeftChannel(c *Context, channelMember *model.ChannelMember, actor *model.User) {
func (a *HooksAdapter) UserHasLeftChannel(c *Context, channelMember *model.ChannelMember, actor *model.User) {
if _, ok := a.implemented[UserHasLeftChannelID]; !ok {
panic("product hooks must implement UserHasLeftChannel")
}
@@ -541,7 +541,7 @@ func (a *hooksAdapter) UserHasLeftChannel(c *Context, channelMember *model.Chann
}
func (a *hooksAdapter) UserHasJoinedTeam(c *Context, teamMember *model.TeamMember, actor *model.User) {
func (a *HooksAdapter) UserHasJoinedTeam(c *Context, teamMember *model.TeamMember, actor *model.User) {
if _, ok := a.implemented[UserHasJoinedTeamID]; !ok {
panic("product hooks must implement UserHasJoinedTeam")
}
@@ -550,7 +550,7 @@ func (a *hooksAdapter) UserHasJoinedTeam(c *Context, teamMember *model.TeamMembe
}
func (a *hooksAdapter) UserHasLeftTeam(c *Context, teamMember *model.TeamMember, actor *model.User) {
func (a *HooksAdapter) UserHasLeftTeam(c *Context, teamMember *model.TeamMember, actor *model.User) {
if _, ok := a.implemented[UserHasLeftTeamID]; !ok {
panic("product hooks must implement UserHasLeftTeam")
}
@@ -559,7 +559,7 @@ func (a *hooksAdapter) UserHasLeftTeam(c *Context, teamMember *model.TeamMember,
}
func (a *hooksAdapter) FileWillBeUploaded(c *Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) {
func (a *HooksAdapter) FileWillBeUploaded(c *Context, info *model.FileInfo, file io.Reader, output io.Writer) (*model.FileInfo, string) {
if _, ok := a.implemented[FileWillBeUploadedID]; !ok {
panic("product hooks must implement FileWillBeUploaded")
}
@@ -568,7 +568,7 @@ func (a *hooksAdapter) FileWillBeUploaded(c *Context, info *model.FileInfo, file
}
func (a *hooksAdapter) ReactionHasBeenAdded(c *Context, reaction *model.Reaction) {
func (a *HooksAdapter) ReactionHasBeenAdded(c *Context, reaction *model.Reaction) {
if _, ok := a.implemented[ReactionHasBeenAddedID]; !ok {
panic("product hooks must implement ReactionHasBeenAdded")
}
@@ -577,7 +577,7 @@ func (a *hooksAdapter) ReactionHasBeenAdded(c *Context, reaction *model.Reaction
}
func (a *hooksAdapter) ReactionHasBeenRemoved(c *Context, reaction *model.Reaction) {
func (a *HooksAdapter) ReactionHasBeenRemoved(c *Context, reaction *model.Reaction) {
if _, ok := a.implemented[ReactionHasBeenRemovedID]; !ok {
panic("product hooks must implement ReactionHasBeenRemoved")
}
@@ -586,7 +586,7 @@ func (a *hooksAdapter) ReactionHasBeenRemoved(c *Context, reaction *model.Reacti
}
func (a *hooksAdapter) OnPluginClusterEvent(c *Context, ev model.PluginClusterEvent) {
func (a *HooksAdapter) OnPluginClusterEvent(c *Context, ev model.PluginClusterEvent) {
if _, ok := a.implemented[OnPluginClusterEventID]; !ok {
panic("product hooks must implement OnPluginClusterEvent")
}
@@ -595,7 +595,7 @@ func (a *hooksAdapter) OnPluginClusterEvent(c *Context, ev model.PluginClusterEv
}
func (a *hooksAdapter) OnWebSocketConnect(webConnID, userID string) {
func (a *HooksAdapter) OnWebSocketConnect(webConnID, userID string) {
if _, ok := a.implemented[OnWebSocketConnectID]; !ok {
panic("product hooks must implement OnWebSocketConnect")
}
@@ -604,7 +604,7 @@ func (a *hooksAdapter) OnWebSocketConnect(webConnID, userID string) {
}
func (a *hooksAdapter) OnWebSocketDisconnect(webConnID, userID string) {
func (a *HooksAdapter) OnWebSocketDisconnect(webConnID, userID string) {
if _, ok := a.implemented[OnWebSocketDisconnectID]; !ok {
panic("product hooks must implement OnWebSocketDisconnect")
}
@@ -613,7 +613,7 @@ func (a *hooksAdapter) OnWebSocketDisconnect(webConnID, userID string) {
}
func (a *hooksAdapter) WebSocketMessageHasBeenPosted(webConnID, userID string, req *model.WebSocketRequest) {
func (a *HooksAdapter) WebSocketMessageHasBeenPosted(webConnID, userID string, req *model.WebSocketRequest) {
if _, ok := a.implemented[WebSocketMessageHasBeenPostedID]; !ok {
panic("product hooks must implement WebSocketMessageHasBeenPosted")
}
@@ -622,7 +622,7 @@ func (a *hooksAdapter) WebSocketMessageHasBeenPosted(webConnID, userID string, r
}
func (a *hooksAdapter) RunDataRetention(nowTime, batchSize int64) (int64, error) {
func (a *HooksAdapter) RunDataRetention(nowTime, batchSize int64) (int64, error) {
if _, ok := a.implemented[RunDataRetentionID]; !ok {
panic("product hooks must implement RunDataRetention")
}
@@ -631,7 +631,7 @@ func (a *hooksAdapter) RunDataRetention(nowTime, batchSize int64) (int64, error)
}
func (a *hooksAdapter) OnInstall(c *Context, event model.OnInstallEvent) error {
func (a *HooksAdapter) OnInstall(c *Context, event model.OnInstallEvent) error {
if _, ok := a.implemented[OnInstallID]; !ok {
panic("product hooks must implement OnInstall")
}
@@ -640,7 +640,7 @@ func (a *hooksAdapter) OnInstall(c *Context, event model.OnInstallEvent) error {
}
func (a *hooksAdapter) OnSendDailyTelemetry() {
func (a *HooksAdapter) OnSendDailyTelemetry() {
if _, ok := a.implemented[OnSendDailyTelemetryID]; !ok {
panic("product hooks must implement OnSendDailyTelemetry")
}
@@ -649,7 +649,7 @@ func (a *hooksAdapter) OnSendDailyTelemetry() {
}
func (a *hooksAdapter) OnCloudLimitsUpdated(limits *model.ProductLimits) {
func (a *HooksAdapter) OnCloudLimitsUpdated(limits *model.ProductLimits) {
if _, ok := a.implemented[OnCloudLimitsUpdatedID]; !ok {
panic("product hooks must implement OnCloudLimitsUpdated")
}
@@ -658,7 +658,7 @@ func (a *hooksAdapter) OnCloudLimitsUpdated(limits *model.ProductLimits) {
}
func (a *hooksAdapter) UserHasPermissionToCollection(c *Context, userID string, collectionType, collectionId string, permission *model.Permission) (bool, error) {
func (a *HooksAdapter) UserHasPermissionToCollection(c *Context, userID string, collectionType, collectionId string, permission *model.Permission) (bool, error) {
if _, ok := a.implemented[UserHasPermissionToCollectionID]; !ok {
panic("product hooks must implement UserHasPermissionToCollection")
}
@@ -667,7 +667,7 @@ func (a *hooksAdapter) UserHasPermissionToCollection(c *Context, userID string,
}
func (a *hooksAdapter) GetAllCollectionIDsForUser(c *Context, userID, collectionType string) ([]string, error) {
func (a *HooksAdapter) GetAllCollectionIDsForUser(c *Context, userID, collectionType string) ([]string, error) {
if _, ok := a.implemented[GetAllCollectionIDsForUserID]; !ok {
panic("product hooks must implement GetAllCollectionIDsForUser")
}
@@ -676,7 +676,7 @@ func (a *hooksAdapter) GetAllCollectionIDsForUser(c *Context, userID, collection
}
func (a *hooksAdapter) GetAllUserIdsForCollection(c *Context, collectionType, collectionID string) ([]string, error) {
func (a *HooksAdapter) GetAllUserIdsForCollection(c *Context, collectionType, collectionID string) ([]string, error) {
if _, ok := a.implemented[GetAllUserIdsForCollectionID]; !ok {
panic("product hooks must implement GetAllUserIdsForCollection")
}
@@ -685,7 +685,7 @@ func (a *hooksAdapter) GetAllUserIdsForCollection(c *Context, collectionType, co
}
func (a *hooksAdapter) GetTopicRedirect(c *Context, topicType, topicID string) (string, error) {
func (a *HooksAdapter) GetTopicRedirect(c *Context, topicType, topicID string) (string, error) {
if _, ok := a.implemented[GetTopicRedirectID]; !ok {
panic("product hooks must implement GetTopicRedirect")
}
@@ -694,7 +694,7 @@ func (a *hooksAdapter) GetTopicRedirect(c *Context, topicType, topicID string) (
}
func (a *hooksAdapter) GetCollectionMetadataByIds(c *Context, collectionType string, collectionIds []string) (map[string]*model.CollectionMetadata, error) {
func (a *HooksAdapter) GetCollectionMetadataByIds(c *Context, collectionType string, collectionIds []string) (map[string]*model.CollectionMetadata, error) {
if _, ok := a.implemented[GetCollectionMetadataByIdsID]; !ok {
panic("product hooks must implement GetCollectionMetadataByIds")
}
@@ -703,7 +703,7 @@ func (a *hooksAdapter) GetCollectionMetadataByIds(c *Context, collectionType str
}
func (a *hooksAdapter) GetTopicMetadataByIds(c *Context, topicType string, topicIds []string) (map[string]*model.TopicMetadata, error) {
func (a *HooksAdapter) GetTopicMetadataByIds(c *Context, topicType string, topicIds []string) (map[string]*model.TopicMetadata, error) {
if _, ok := a.implemented[GetTopicMetadataByIdsID]; !ok {
panic("product hooks must implement GetTopicMetadataByIds")
}

79
product/hooks.go Обычный файл
Просмотреть файл

@@ -0,0 +1,79 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package product
import (
"sync"
"time"
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/plugin"
)
type HooksManager struct {
registeredProducts sync.Map
metrics einterfaces.MetricsInterface
}
func NewHooksManager(metrics einterfaces.MetricsInterface) *HooksManager {
return &HooksManager{
metrics: metrics,
}
}
func (m *HooksManager) AddProduct(productID string, hooks any) error {
prod, err := plugin.NewAdapter(hooks)
if err != nil {
return err
}
rp := &plugin.RegisteredProduct{
ProductID: productID,
Adapter: prod,
}
m.registeredProducts.Store(productID, rp)
return nil
}
func (m *HooksManager) RemoveProduct(productID string) {
m.registeredProducts.Delete(productID)
}
func (m *HooksManager) RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks) bool, hookId int) {
startTime := time.Now()
m.registeredProducts.Range(func(key, value any) bool {
rp := value.(*plugin.RegisteredProduct)
if !rp.Implements(hookId) {
return true
}
hookStartTime := time.Now()
result := hookRunnerFunc(rp.Adapter)
if m.metrics != nil {
elapsedTime := float64(time.Since(hookStartTime)) / float64(time.Second)
m.metrics.ObservePluginMultiHookIterationDuration(rp.ProductID, elapsedTime)
}
return result
})
if m.metrics != nil {
elapsedTime := float64(time.Since(startTime)) / float64(time.Second)
m.metrics.ObservePluginMultiHookDuration(elapsedTime)
}
}
func (m *HooksManager) HooksForProduct(id string) plugin.Hooks {
if value, ok := m.registeredProducts.Load(id); ok {
rp := value.(*plugin.RegisteredProduct)
return rp.Adapter
}
return nil
}