Этот коммит содержится в:
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 return
} }
changedProduct, err := c.App.Cloud().GetCloudProduct(userId, changedSub.ProductID) product, err := c.App.Cloud().GetCloudProduct(c.AppContext.Session().UserId, subscriptionChange.ProductID)
if err != nil { if err != nil || product == nil {
c.Err = model.NewAppError("Api4.changeSubscription", "api.cloud.app_error", nil, "", http.StatusInternalServerError).Wrap(err) c.Logger.Error("Error finding the new cloud product", mlog.Err(err))
} }
starterSku := fmt.Sprintf("%s-%s", model.SubscriptionFamilyCloud, model.ProductSkuStarter) starterSku := fmt.Sprintf("%s-%s", model.SubscriptionFamilyCloud, model.ProductSkuStarter)
if changedProduct.SKU != starterSku { if product.SKU != starterSku {
w.Write(json) w.Write(json)
return 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 // 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. // 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)) 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 return
} }
case model.EventTypeSendUpgradeConfirmationEmail: 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 c.Err = nErr
return 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 != "" { if c.AppContext.Session().UserId != "" {
c.App.Srv().Platform().HubRegister(wc) c.App.Srv().Platform().HubRegister(wc)
} }

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

@@ -1038,7 +1038,7 @@ type AppIface interface {
SendPasswordReset(email string, siteURL string) (bool, *model.AppError) SendPasswordReset(email string, siteURL string) (bool, *model.AppError)
SendPaymentFailedEmail(failedPayment *model.FailedPayment) *model.AppError SendPaymentFailedEmail(failedPayment *model.FailedPayment) *model.AppError
SendTestPushNotification(deviceID string) string SendTestPushNotification(deviceID string) string
SendUpgradeConfirmationEmail() *model.AppError SendUpgradeConfirmationEmail(isYearly bool) *model.AppError
ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string)
SessionHasPermissionTo(session model.Session, permission *model.Permission) bool SessionHasPermissionTo(session model.Session, permission *model.Permission) bool
SessionHasPermissionToAny(session model.Session, permissions []*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 { if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() { a.Srv().Go(func() {
pluginContext := pluginContext(c) pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.ChannelHasBeenCreated(pluginContext, sc) hooks.ChannelHasBeenCreated(pluginContext, sc)
return true return true
}, plugin.ChannelHasBeenCreatedID) }, plugin.ChannelHasBeenCreatedID)
@@ -432,7 +432,7 @@ func (a *App) handleCreationEvent(c request.CTX, userID, otherUserID string, cha
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil { if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() { a.Srv().Go(func() {
pluginContext := pluginContext(c) pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.ChannelHasBeenCreated(pluginContext, channel) hooks.ChannelHasBeenCreated(pluginContext, channel)
return true return true
}, plugin.ChannelHasBeenCreatedID) }, plugin.ChannelHasBeenCreatedID)
@@ -1602,7 +1602,7 @@ func (a *App) AddChannelMember(c request.CTX, userID string, channel *model.Chan
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil { if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() { a.Srv().Go(func() {
pluginContext := pluginContext(c) pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasJoinedChannel(pluginContext, cm, userRequestor) hooks.UserHasJoinedChannel(pluginContext, cm, userRequestor)
return true return true
}, plugin.UserHasJoinedChannelID) }, plugin.UserHasJoinedChannelID)
@@ -2180,7 +2180,7 @@ func (a *App) JoinChannel(c request.CTX, channel *model.Channel, userID string)
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil { if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() { a.Srv().Go(func() {
pluginContext := pluginContext(c) pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasJoinedChannel(pluginContext, cm, nil) hooks.UserHasJoinedChannel(pluginContext, cm, nil)
return true return true
}, plugin.UserHasJoinedChannelID) }, plugin.UserHasJoinedChannelID)
@@ -2492,7 +2492,7 @@ func (a *App) removeUserFromChannel(c request.CTX, userIDToRemove string, remove
a.Srv().Go(func() { a.Srv().Go(func() {
pluginContext := pluginContext(c) pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasLeftChannel(pluginContext, cm, actorUser) hooks.UserHasLeftChannel(pluginContext, cm, actorUser)
return true return true
}, plugin.UserHasLeftChannelID) }, plugin.UserHasLeftChannelID)

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

@@ -322,5 +322,33 @@ func (s *hooksService) RegisterHooks(productID string, hooks any) error {
return errors.New("could not find plugins environment") 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()) 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() sysAdmins, e := a.getSysAdminsEmailRecipients()
if e != nil { if e != nil {
return e return e
@@ -200,7 +200,7 @@ func (a *App) SendUpgradeConfirmationEmail() *model.AppError {
name = admin.Username 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 { if err != nil {
a.Log().Error("Error sending trial ended email to", mlog.String("email", admin.Email), mlog.Err(err)) a.Log().Error("Error sending trial ended email to", mlog.String("email", admin.Email), mlog.Err(err))
countNotOks++ countNotOks++

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

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

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

@@ -233,13 +233,13 @@ func (es *Service) SendWelcomeEmail(userID string, email string, verified bool,
return nil 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) T := i18n.GetUserTranslations(locale)
subject := T("api.templates.cloud_upgrade_confirmation.subject") subject := T("api.templates.cloud_upgrade_confirmation.subject")
data := es.NewEmailTemplateData(locale) data := es.NewEmailTemplateData(locale)
data.Props["Title"] = T("api.templates.cloud_upgrade_confirmation.title") 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["SiteURL"] = siteURL
data.Props["ButtonURL"] = siteURL data.Props["ButtonURL"] = siteURL
data.Props["Button"] = T("api.templates.cloud_welcome_email.button") 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["QuestionInfo"] = T("api.templates.questions_footer.info")
data.Props["SupportEmail"] = *es.config().SupportSettings.SupportEmail 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) body, err := es.templatesContainer.RenderToString("cloud_upgrade_confirmation", data)
if err != nil { if err != nil {
return err return err

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

@@ -258,7 +258,7 @@ func TestSendCloudUpgradedEmail(t *testing.T) {
emailTo := "testclouduser@example.com" emailTo := "testclouduser@example.com"
emailToUsername := strings.Split(emailTo, "@")[0] emailToUsername := strings.Split(emailTo, "@")[0]
t.Run("SendCloudUpgradedEmail", func(t *testing.T) { t.Run("SendCloudMonthlyUpgradedEmail", func(t *testing.T) {
verifyMailbox := func(t *testing.T) { verifyMailbox := func(t *testing.T) {
t.Helper() t.Helper()
@@ -278,10 +278,44 @@ func TestSendCloudUpgradedEmail(t *testing.T) {
require.NoError(t, err, "Could not get message from mailbox") 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, "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, "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) 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) require.NoError(t, err)
verifyMailbox(t) verifyMailbox(t)

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

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

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

@@ -129,7 +129,7 @@ type ServiceInterface interface {
SendVerifyEmail(userEmail, locale, siteURL, token, redirect string) error SendVerifyEmail(userEmail, locale, siteURL, token, redirect string) error
SendSignInChangeEmail(email, method, locale, siteURL string) error SendSignInChangeEmail(email, method, locale, siteURL string) error
SendWelcomeEmail(userID string, email string, verified bool, disableWelcomeEmail bool, locale, siteURL, redirect 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 SendCloudWelcomeEmail(userEmail, locale, teamInviteID, workSpaceName, dns, siteURL string) error
SendPasswordChangeEmail(email, method, locale, siteURL string) error SendPasswordChangeEmail(email, method, locale, siteURL string) error
SendUserAccessTokenAddedEmail(email, 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 { if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
var rejectionError *model.AppError var rejectionError *model.AppError
pluginContext := pluginContext(c) pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
var newBytes bytes.Buffer var newBytes bytes.Buffer
replacementInfo, rejectionReason := hooks.FileWillBeUploaded(pluginContext, info, bytes.NewReader(data), &newBytes) replacementInfo, rejectionReason := hooks.FileWillBeUploaded(pluginContext, info, bytes.NewReader(data), &newBytes)
if rejectionReason != "" { 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 { if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
var rejectionReason string var rejectionReason string
pluginContext := pluginContext(c) pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
rejectionReason = hooks.UserWillLogIn(pluginContext, user) rejectionReason = hooks.UserWillLogIn(pluginContext, user)
return rejectionReason == "" return rejectionReason == ""
}, plugin.UserWillLogInID) }, 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 { if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() { a.Srv().Go(func() {
pluginContext := pluginContext(c) pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasLoggedIn(pluginContext, user) hooks.UserHasLoggedIn(pluginContext, user)
return true return true
}, plugin.UserHasLoggedInID) }, plugin.UserHasLoggedInID)

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

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

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

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

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

@@ -95,7 +95,12 @@ type PlatformService struct {
additionalClusterHandlers map[model.ClusterEvent]einterfaces.ClusterMessageHandler additionalClusterHandlers map[model.ClusterEvent]einterfaces.ClusterMessageHandler
sharedChannelService SharedChannelServiceIFace 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. // New creates a new PlatformService.
@@ -426,17 +431,17 @@ func (ps *PlatformService) SetSharedChannelService(s SharedChannelServiceIFace)
ps.sharedChannelService = s ps.sharedChannelService = s
} }
func (ps *PlatformService) SetPluginsEnvironment(env *plugin.Environment) { func (ps *PlatformService) SetPluginsEnvironment(runner HookRunner) {
ps.pluginEnv = env ps.pluginEnv = runner
} }
// GetPluginStatuses meant to be used by cluster implementation // GetPluginStatuses meant to be used by cluster implementation
func (ps *PlatformService) GetPluginStatuses() (model.PluginStatuses, *model.AppError) { 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) 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 { if err != nil {
return nil, model.NewAppError("GetPluginStatuses", "app.plugin.get_statuses.app_error", nil, "", http.StatusInternalServerError).Wrap(err) 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 // It contains all the necessary state to manage sending/receiving data to/from
// a websocket. // a websocket.
type WebConn struct { type WebConn struct {
sessionExpiresAt int64 // This should stay at the top for 64-bit alignment of 64-bit words accessed atomically sessionExpiresAt int64 // This should stay at the top for 64-bit alignment of 64-bit words accessed atomically
Platform *PlatformService Platform *PlatformService
Suite SuiteIFace Suite SuiteIFace
PluginsEnvironment func() *plugin.Environment HookRunner HookRunner
WebSocket *websocket.Conn WebSocket *websocket.Conn
T i18n.TranslateFunc T i18n.TranslateFunc
Locale string Locale string
Sequence int64 Sequence int64
UserId string UserId string
allChannelMembers map[string]string allChannelMembers map[string]string
lastAllChannelMembersTime int64 lastAllChannelMembersTime int64
@@ -162,7 +162,7 @@ func (ps *PlatformService) PopulateWebConnConfig(s *model.Session, cfg *WebConnC
} }
// NewWebConn returns a new WebConn instance. // 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 != "" { if cfg.Session.UserId != "" {
ps.Go(func() { ps.Go(func() {
suite.SetStatusOnline(cfg.Session.UserId, false) suite.SetStatusOnline(cfg.Session.UserId, false)
@@ -200,7 +200,7 @@ func (ps *PlatformService) NewWebConn(cfg *WebConnConfig, suite SuiteIFace, envF
wc := &WebConn{ wc := &WebConn{
Platform: ps, Platform: ps,
Suite: suite, Suite: suite,
PluginsEnvironment: envFn, HookRunner: runner,
send: cfg.activeQueue, send: cfg.activeQueue,
deadQueue: cfg.deadQueue, deadQueue: cfg.deadQueue,
deadQueuePointer: cfg.deadQueuePointer, deadQueuePointer: cfg.deadQueuePointer,
@@ -222,14 +222,12 @@ func (ps *PlatformService) NewWebConn(cfg *WebConnConfig, suite SuiteIFace, envF
wc.SetSessionExpiresAt(cfg.Session.ExpiresAt) wc.SetSessionExpiresAt(cfg.Session.ExpiresAt)
wc.SetConnectionID(cfg.ConnectionID) wc.SetConnectionID(cfg.ConnectionID)
if pluginsEnvironment := wc.PluginsEnvironment(); pluginsEnvironment != nil { wc.Platform.Go(func() {
wc.Platform.Go(func() { wc.HookRunner.RunMultiHook(func(hooks plugin.Hooks) bool {
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { hooks.OnWebSocketConnect(wc.GetConnectionID(), wc.UserId)
hooks.OnWebSocketConnect(wc.GetConnectionID(), wc.UserId) return true
return true }, plugin.OnWebSocketConnectID)
}, plugin.OnWebSocketConnectID) })
})
}
return wc return wc
} }
@@ -238,12 +236,10 @@ func (wc *WebConn) pluginPostedConsumer(wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
for msg := range wc.pluginPosted { for msg := range wc.pluginPosted {
if pluginsEnvironment := wc.PluginsEnvironment(); pluginsEnvironment != nil { wc.HookRunner.RunMultiHook(func(hooks plugin.Hooks) bool {
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { hooks.WebSocketMessageHasBeenPosted(msg.connectionID, msg.userID, msg.req)
hooks.WebSocketMessageHasBeenPosted(msg.connectionID, msg.userID, msg.req) return true
return true }, plugin.WebSocketMessageHasBeenPostedID)
}, plugin.WebSocketMessageHasBeenPostedID)
}
} }
} }
@@ -328,14 +324,12 @@ func (wc *WebConn) Pump() {
wc.Platform.HubUnregister(wc) wc.Platform.HubUnregister(wc)
close(wc.pumpFinished) close(wc.pumpFinished)
if pluginsEnvironment := wc.PluginsEnvironment(); pluginsEnvironment != nil { wc.Platform.Go(func() {
wc.Platform.Go(func() { wc.HookRunner.RunMultiHook(func(hooks plugin.Hooks) bool {
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { hooks.OnWebSocketDisconnect(wc.GetConnectionID(), wc.UserId)
hooks.OnWebSocketDisconnect(wc.GetConnectionID(), wc.UserId) return true
return true }, plugin.OnWebSocketDisconnectID)
}, plugin.OnWebSocketDisconnectID) })
})
}
} }
func (wc *WebConn) readPump() { func (wc *WebConn) readPump() {

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

@@ -5,6 +5,7 @@ package platform
import ( import (
"bytes" "bytes"
"errors"
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@@ -18,13 +19,27 @@ import (
"github.com/mattermost/mattermost-server/v6/plugin" "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) { func TestWebConnAddDeadQueue(t *testing.T) {
th := Setup(t) th := Setup(t)
defer th.TearDown() defer th.TearDown()
wc := th.Service.NewWebConn(&WebConnConfig{ wc := th.Service.NewWebConn(&WebConnConfig{
WebSocket: &websocket.Conn{}, WebSocket: &websocket.Conn{},
}, th.Suite, func() *plugin.Environment { return nil }) }, th.Suite, &hookRunner{})
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
msg := &model.WebSocketEvent{} msg := &model.WebSocketEvent{}
@@ -53,7 +68,7 @@ func TestWebConnIsInDeadQueue(t *testing.T) {
wc := th.Service.NewWebConn(&WebConnConfig{ wc := th.Service.NewWebConn(&WebConnConfig{
WebSocket: &websocket.Conn{}, WebSocket: &websocket.Conn{},
}, th.Suite, func() *plugin.Environment { return nil }) }, th.Suite, &hookRunner{})
var i int var i int
for ; i < 2; i++ { for ; i < 2; i++ {
@@ -114,7 +129,7 @@ func TestWebConnClearDeadQueue(t *testing.T) {
wc := th.Service.NewWebConn(&WebConnConfig{ wc := th.Service.NewWebConn(&WebConnConfig{
WebSocket: &websocket.Conn{}, WebSocket: &websocket.Conn{},
}, th.Suite, func() *plugin.Environment { return nil }) }, th.Suite, &hookRunner{})
var i int var i int
for ; i < 2; i++ { for ; i < 2; i++ {
@@ -140,7 +155,7 @@ func TestWebConnDrainDeadQueue(t *testing.T) {
cfg := &WebConnConfig{ cfg := &WebConnConfig{
WebSocket: c, 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) { t.Run("Empty Queue", func(t *testing.T) {

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

@@ -17,7 +17,6 @@ import (
platform_mocks "github.com/mattermost/mattermost-server/v6/app/platform/mocks" platform_mocks "github.com/mattermost/mattermost-server/v6/app/platform/mocks"
"github.com/mattermost/mattermost-server/v6/model" "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/shared/i18n"
"github.com/mattermost/mattermost-server/v6/store/storetest/mocks" "github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
"github.com/mattermost/mattermost-server/v6/testlib" "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(), TFunc: i18n.IdentityTfunc(),
Locale: "en", 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) th.Service.HubRegister(wc)
go wc.Pump() go wc.Pump()
return wc return wc

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

@@ -93,7 +93,7 @@ func (ch *Channels) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment
defer ch.pluginsLock.Unlock() defer ch.pluginsLock.Unlock()
ch.pluginsEnvironment = pluginsEnvironment ch.pluginsEnvironment = pluginsEnvironment
ch.srv.Platform().SetPluginsEnvironment(pluginsEnvironment) ch.srv.Platform().SetPluginsEnvironment(ch)
} }
func (ch *Channels) syncPluginsActiveState() { 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) { func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir string) {
// Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment. // Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
defer func() { defer func() {
ch.srv.Platform().SetPluginsEnvironment(ch.pluginsEnvironment) ch.srv.Platform().SetPluginsEnvironment(ch)
}() }()
ch.pluginsLock.RLock() ch.pluginsLock.RLock()
@@ -279,7 +279,7 @@ func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir s
ch.syncPluginsActiveState() ch.syncPluginsActiveState()
} }
if pluginsEnvironment := ch.GetPluginsEnvironment(); pluginsEnvironment != nil { 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 { if err := hooks.OnConfigurationChange(); err != nil {
ch.srv.Log().Error("Plugin OnConfigurationChange hook failed", mlog.Err(err)) 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)) require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
hookCalled := false 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) n, _ := hooks.RunDataRetention(0, 0)
// Ensure return it correct // Ensure return it correct
assert.Equal(t, int64(100), n) assert.Equal(t, int64(100), n)
@@ -1278,7 +1278,7 @@ func TestHookOnSendDailyTelemetry(t *testing.T) {
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID)) require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
hookCalled := false hookCalled := false
th.App.GetPluginsEnvironment().RunMultiPluginHook(func(hooks plugin.Hooks) bool { th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.OnSendDailyTelemetry() hooks.OnSendDailyTelemetry()
hookCalled = true hookCalled = true
@@ -1322,7 +1322,7 @@ func TestHookOnCloudLimitsUpdated(t *testing.T) {
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID)) require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
hookCalled := false hookCalled := false
th.App.GetPluginsEnvironment().RunMultiPluginHook(func(hooks plugin.Hooks) bool { th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.OnCloudLimitsUpdated(nil) hooks.OnCloudLimitsUpdated(nil)
hookCalled = true hookCalled = true

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

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

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

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

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

@@ -161,6 +161,8 @@ type Server struct {
tracer *tracing.Tracer tracer *tracing.Tracer
products map[string]Product products map[string]Product
hooksManager *product.HooksManager
} }
func (s *Server) Store() store.Store { 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") return nil, errors.Wrapf(err, "unable to create teams service")
} }
s.hooksManager = product.NewHooksManager(s.GetMetrics())
// ensure app implements `product.UserService` // ensure app implements `product.UserService`
var _ product.UserService = (*App)(nil) 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() { a.Srv().Go(func() {
pluginContext := pluginContext(c) pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasJoinedTeam(pluginContext, teamMember, actor) hooks.UserHasJoinedTeam(pluginContext, teamMember, actor)
return true return true
}, plugin.UserHasJoinedTeamID) }, plugin.UserHasJoinedTeamID)
@@ -1228,7 +1228,7 @@ func (a *App) postProcessTeamMemberLeave(c request.CTX, teamMember *model.TeamMe
a.Srv().Go(func() { a.Srv().Go(func() {
pluginContext := pluginContext(c) pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasLeftTeam(pluginContext, teamMember, actor) hooks.UserHasLeftTeam(pluginContext, teamMember, actor)
return true return true
}, plugin.UserHasLeftTeamID) }, plugin.UserHasLeftTeamID)

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

@@ -67,7 +67,7 @@ func (a *App) runPluginsHook(c *request.Context, info *model.FileInfo, file io.R
var rejErr *model.AppError var rejErr *model.AppError
var once sync.Once var once sync.Once
pluginContext := pluginContext(c) pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
once.Do(func() { once.Do(func() {
hookHasRunCh <- struct{}{} 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 { if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
a.Srv().Go(func() { a.Srv().Go(func() {
pluginContext := pluginContext(c) pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasBeenCreated(pluginContext, ruser) hooks.UserHasBeenCreated(pluginContext, ruser)
return true return true
}, plugin.UserHasBeenCreatedID) }, plugin.UserHasBeenCreatedID)

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

@@ -16,5 +16,5 @@ func (a *App) PopulateWebConnConfig(s *model.Session, cfg *platform.WebConnConfi
// NewWebConn returns a new WebConn instance. // NewWebConn returns a new WebConn instance.
func (a *App) NewWebConn(cfg *platform.WebConnConfig) *platform.WebConn { 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" "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}}" "translation": "Your {{.WorkspaceName}} workspace has now been upgraded. You'll be billed from {{.Date}}"
}, },
{ {
"id": "api.templates.cloud_upgrade_confirmation.title", "id": "api.templates.cloud_upgrade_confirmation_yearly.subtitle",
"translation": "You are now upgraded!" "translation": "Your {{.WorkspaceName}} workspace has now been upgraded."
}, },
{ {
"id": "api.templates.cloud_welcome_email.add_apps_info", "id": "api.templates.cloud_welcome_email.add_apps_info",
@@ -3239,6 +3243,10 @@
"id": "api.templates.cloud_welcome_email.title", "id": "api.templates.cloud_welcome_email.title",
"translation": "Your workspace is ready to go!" "translation": "Your workspace is ready to go!"
}, },
{
"id": "api.templates.cloud_welcome_email.yearly_plan_button",
"translation": "View your invoice"
},
{ {
"id": "api.templates.copyright", "id": "api.templates.copyright",
"translation": "© 2021 Mattermost, Inc. 530 Lytton Avenue, Second floor, Palo Alto, CA, 94301" "translation": "© 2021 Mattermost, Inc. 530 Lytton Avenue, Second floor, Palo Alto, CA, 94301"

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

@@ -50,7 +50,6 @@ type PrepackagedPlugin struct {
// of active plugins. // of active plugins.
type Environment struct { type Environment struct {
registeredPlugins sync.Map registeredPlugins sync.Map
registeredProducts sync.Map
pluginHealthCheckJob *PluginHealthCheckJob pluginHealthCheckJob *PluginHealthCheckJob
logger *mlog.Logger logger *mlog.Logger
metrics einterfaces.MetricsInterface metrics einterfaces.MetricsInterface
@@ -326,26 +325,6 @@ func (env *Environment) Activate(id string) (manifest *model.Manifest, activated
return pluginInfo.Manifest, true, nil 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) { func (env *Environment) RemovePlugin(id string) {
if _, ok := env.registeredPlugins.Load(id); ok { if _, ok := env.registeredPlugins.Load(id); ok {
env.registeredPlugins.Delete(id) 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) return nil, fmt.Errorf("plugin not found: %v", id)
} }
@@ -533,24 +506,6 @@ func (env *Environment) RunMultiPluginHook(hookRunnerFunc func(hooks Hooks) bool
return result 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 { if env.metrics != nil {
elapsedTime := float64(time.Since(startTime)) / float64(time.Second) elapsedTime := float64(time.Since(startTime)) / float64(time.Second)
env.metrics.ObservePluginMultiHookDuration(elapsedTime) env.metrics.ObservePluginMultiHookDuration(elapsedTime)

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

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

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

@@ -7,13 +7,13 @@ import (
"net/http" "net/http"
) )
type registeredProduct struct { type RegisteredProduct struct {
productID string ProductID string
adapter Hooks Adapter Hooks
} }
func (rp *registeredProduct) Implements(hookId int) bool { func (rp *RegisteredProduct) Implements(hookId int) bool {
adapter, ok := rp.adapter.(*hooksAdapter) adapter, ok := rp.Adapter.(*HooksAdapter)
if !ok { if !ok {
return false return false
} }
@@ -23,19 +23,19 @@ func (rp *registeredProduct) Implements(hookId int) bool {
} }
// Implemented method is overridden intentionally to prevent calling it from outside. // 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 return nil, nil
} }
// OnActivate is overridden intentionally as product should not call it. // OnActivate is overridden intentionally as product should not call it.
func (a *hooksAdapter) OnActivate() error { func (a *HooksAdapter) OnActivate() error {
return nil return nil
} }
// OnDeactivate is overridden intentionally as product should not call it. // OnDeactivate is overridden intentionally as product should not call it.
func (a *hooksAdapter) OnDeactivate() error { func (a *HooksAdapter) OnDeactivate() error {
return nil return nil
} }
// ServeHTTP is overridden intentionally as product should not call it. // 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) GetTopicMetadataByIds(c *Context, topicType string, topicIds []string) (map[string]*model.TopicMetadata, error)
} }
type hooksAdapter struct { type HooksAdapter struct {
implemented map[int]struct{} implemented map[int]struct{}
productHooks any productHooks any
} }
func newAdapter(productHooks any) (*hooksAdapter, error) { func NewAdapter(productHooks any) (*HooksAdapter, error) {
a := &hooksAdapter{ a := &HooksAdapter{
implemented: make(map[int]struct{}), implemented: make(map[int]struct{}),
productHooks: productHooks, productHooks: productHooks,
} }
@@ -433,7 +433,7 @@ func newAdapter(productHooks any) (*hooksAdapter, error) {
return a, nil return a, nil
} }
func (a *hooksAdapter) OnConfigurationChange() error { func (a *HooksAdapter) OnConfigurationChange() error {
if _, ok := a.implemented[OnConfigurationChangeID]; !ok { if _, ok := a.implemented[OnConfigurationChangeID]; !ok {
panic("product hooks must implement OnConfigurationChange") 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 { if _, ok := a.implemented[ExecuteCommandID]; !ok {
panic("product hooks must implement ExecuteCommand") 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 { if _, ok := a.implemented[UserHasBeenCreatedID]; !ok {
panic("product hooks must implement UserHasBeenCreated") 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 { if _, ok := a.implemented[UserWillLogInID]; !ok {
panic("product hooks must implement UserWillLogIn") 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 { if _, ok := a.implemented[UserHasLoggedInID]; !ok {
panic("product hooks must implement UserHasLoggedIn") 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 { if _, ok := a.implemented[MessageWillBePostedID]; !ok {
panic("product hooks must implement MessageWillBePosted") 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 { if _, ok := a.implemented[MessageWillBeUpdatedID]; !ok {
panic("product hooks must implement MessageWillBeUpdated") 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 { if _, ok := a.implemented[MessageHasBeenPostedID]; !ok {
panic("product hooks must implement MessageHasBeenPosted") 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 { if _, ok := a.implemented[MessageHasBeenUpdatedID]; !ok {
panic("product hooks must implement MessageHasBeenUpdated") 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 { if _, ok := a.implemented[ChannelHasBeenCreatedID]; !ok {
panic("product hooks must implement ChannelHasBeenCreated") 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 { if _, ok := a.implemented[UserHasJoinedChannelID]; !ok {
panic("product hooks must implement UserHasJoinedChannel") 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 { if _, ok := a.implemented[UserHasLeftChannelID]; !ok {
panic("product hooks must implement UserHasLeftChannel") 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 { if _, ok := a.implemented[UserHasJoinedTeamID]; !ok {
panic("product hooks must implement UserHasJoinedTeam") 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 { if _, ok := a.implemented[UserHasLeftTeamID]; !ok {
panic("product hooks must implement UserHasLeftTeam") 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 { if _, ok := a.implemented[FileWillBeUploadedID]; !ok {
panic("product hooks must implement FileWillBeUploaded") 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 { if _, ok := a.implemented[ReactionHasBeenAddedID]; !ok {
panic("product hooks must implement ReactionHasBeenAdded") 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 { if _, ok := a.implemented[ReactionHasBeenRemovedID]; !ok {
panic("product hooks must implement ReactionHasBeenRemoved") 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 { if _, ok := a.implemented[OnPluginClusterEventID]; !ok {
panic("product hooks must implement OnPluginClusterEvent") 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 { if _, ok := a.implemented[OnWebSocketConnectID]; !ok {
panic("product hooks must implement OnWebSocketConnect") 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 { if _, ok := a.implemented[OnWebSocketDisconnectID]; !ok {
panic("product hooks must implement OnWebSocketDisconnect") 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 { if _, ok := a.implemented[WebSocketMessageHasBeenPostedID]; !ok {
panic("product hooks must implement WebSocketMessageHasBeenPosted") 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 { if _, ok := a.implemented[RunDataRetentionID]; !ok {
panic("product hooks must implement RunDataRetention") 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 { if _, ok := a.implemented[OnInstallID]; !ok {
panic("product hooks must implement OnInstall") 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 { if _, ok := a.implemented[OnSendDailyTelemetryID]; !ok {
panic("product hooks must implement OnSendDailyTelemetry") 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 { if _, ok := a.implemented[OnCloudLimitsUpdatedID]; !ok {
panic("product hooks must implement OnCloudLimitsUpdated") 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 { if _, ok := a.implemented[UserHasPermissionToCollectionID]; !ok {
panic("product hooks must implement UserHasPermissionToCollection") 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 { if _, ok := a.implemented[GetAllCollectionIDsForUserID]; !ok {
panic("product hooks must implement GetAllCollectionIDsForUser") 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 { if _, ok := a.implemented[GetAllUserIdsForCollectionID]; !ok {
panic("product hooks must implement GetAllUserIdsForCollection") 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 { if _, ok := a.implemented[GetTopicRedirectID]; !ok {
panic("product hooks must implement GetTopicRedirect") 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 { if _, ok := a.implemented[GetCollectionMetadataByIdsID]; !ok {
panic("product hooks must implement GetCollectionMetadataByIds") 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 { if _, ok := a.implemented[GetTopicMetadataByIdsID]; !ok {
panic("product hooks must implement GetTopicMetadataByIds") 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
}