From 7f419ea091c221a90cb09a0dfa12c69138160c81 Mon Sep 17 00:00:00 2001 From: Javier Aguirre Date: Wed, 23 Nov 2022 10:42:23 +0100 Subject: [PATCH] [MM-48542] Removing integration limits (#21282) * Removing integration limits * Remove freemium limit test * Remove test assertion regarding cloud limits * Remove GetIntegrationsUsage * Removing integrations usage notifications * This shouldn't be removed * Removing client call and websocket event * Remove old translations Co-authored-by: Mattermod --- api4/config.go | 12 +-- api4/config_test.go | 108 ------------------------- api4/usage.go | 29 ------- api4/usage_test.go | 25 ------ app/app_iface.go | 4 - app/integrations.go | 113 --------------------------- app/integrations_test.go | 74 ------------------ app/opentracing/opentracing_layer.go | 44 ----------- app/plugin.go | 23 ------ app/plugin_install.go | 16 ---- app/plugin_test.go | 11 --- app/usage.go | 31 -------- i18n/de.json | 4 - i18n/en.json | 4 - i18n/en_AU.json | 4 - i18n/es.json | 4 - i18n/fr.json | 4 - i18n/hu.json | 4 - i18n/it.json | 4 - i18n/ja.json | 4 - i18n/nl.json | 4 - i18n/pl.json | 4 - i18n/sv.json | 4 - i18n/tr.json | 4 - i18n/zh-CN.json | 4 - model/client4.go | 13 --- model/usage.go | 4 - model/websocket_message.go | 1 - 28 files changed, 1 insertion(+), 559 deletions(-) delete mode 100644 app/integrations.go delete mode 100644 app/integrations_test.go diff --git a/api4/config.go b/api4/config.go index d2f2019c36..5f620e14d5 100644 --- a/api4/config.go +++ b/api4/config.go @@ -157,12 +157,7 @@ func updateConfig(c *Context, w http.ResponseWriter, r *http.Request) { } if cfg.PluginSettings.PluginStates[model.PluginIdFocalboard].Enable && cfg.FeatureFlags.BoardsProduct { - c.Err = model.NewAppError("EnablePlugin", "app.plugin.product_mode.app_error", map[string]any{"Name": model.PluginIdFocalboard}, "", http.StatusBadRequest) - return - } - - if appErr := c.App.CheckFreemiumLimitsForConfigSave(appCfg, cfg); appErr != nil { - c.Err = appErr + c.Err = model.NewAppError("EnablePlugin", "app.plugin.product_mode.app_error", map[string]any{"Name": model.PluginIdFocalboard}, "", http.StatusInternalServerError) return } @@ -304,11 +299,6 @@ func patchConfig(c *Context, w http.ResponseWriter, r *http.Request) { } } - if appErr := c.App.CheckFreemiumLimitsForConfigSave(appCfg, cfg); appErr != nil { - c.Err = appErr - return - } - // There are some settings that cannot be changed in a cloud env if c.App.Channels().License().IsCloud() { if cfg.ComplianceSettings.Directory != nil && *appCfg.ComplianceSettings.Directory != *cfg.ComplianceSettings.Directory { diff --git a/api4/config_test.go b/api4/config_test.go index 2af4fe7bac..3db4082e49 100644 --- a/api4/config_test.go +++ b/api4/config_test.go @@ -17,9 +17,7 @@ import ( "github.com/mattermost/mattermost-server/v6/app" "github.com/mattermost/mattermost-server/v6/config" - "github.com/mattermost/mattermost-server/v6/einterfaces/mocks" "github.com/mattermost/mattermost-server/v6/model" - "github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock" ) func TestGetConfig(t *testing.T) { @@ -249,59 +247,6 @@ func TestUpdateConfig(t *testing.T) { assert.Equal(t, newURL, *cfg2.PluginSettings.MarketplaceURL) }) - t.Run("Should not be able to save config if the new config exceeds Freemium limits", func(t *testing.T) { - th.App.Srv().SetLicense(model.NewTestLicense("cloud")) - defer th.App.Srv().RemoveLicense() - - cloud := &mocks.CloudInterface{} - cloudImpl := th.App.Srv().Cloud - defer func() { - th.App.Srv().Cloud = cloudImpl - }() - th.App.Srv().Cloud = cloud - - cloud.Mock.On("GetCloudLimits", mock.Anything).Return(&model.ProductLimits{ - Integrations: &model.IntegrationsLimits{ - Enabled: model.NewInt(0), - }, - }, nil).Once() - - // Exceed freemium limit. Should throw error. - cfg1 := th.App.Config().Clone() - cfg1.PluginSettings.PluginStates["new-plugin"] = &model.PluginState{Enable: true} - _, _, err1 := th.SystemAdminClient.UpdateConfig(cfg1) - require.Error(t, err1) - - // No attempt to enable a plugin. Should not throw error. - cfg1 = th.App.Config().Clone() - cfg1.PluginSettings.PluginStates["new-plugin"] = &model.PluginState{Enable: false} - _, _, err1 = th.SystemAdminClient.UpdateConfig(cfg1) - require.NoError(t, err1) - - cloud.Mock.On("GetCloudLimits", mock.Anything).Return(&model.ProductLimits{ - Integrations: &model.IntegrationsLimits{ - Enabled: model.NewInt(1), - }, - }, nil).Twice() - - // Exceed freemium limit while enabling more than one plugin. Should throw error. - cfg1 = th.App.Config().Clone() - cfg1.PluginSettings.PluginStates["new-plugin"] = &model.PluginState{Enable: true} - cfg1.PluginSettings.PluginStates["new-plugin2"] = &model.PluginState{Enable: true} - _, _, err1 = th.SystemAdminClient.PatchConfig(cfg1) - require.Error(t, err1) - - // Match freemium limit. Should not throw error. - cfg1 = th.App.Config().Clone() - cfg1.PluginSettings.PluginStates["new-plugin"] = &model.PluginState{Enable: true} - _, _, err1 = th.SystemAdminClient.UpdateConfig(cfg1) - require.NoError(t, err1) - - // Save same config with same plugin enabled. Should not throw error. - _, _, err1 = th.SystemAdminClient.UpdateConfig(cfg1) - require.NoError(t, err1) - }) - t.Run("Should not be able to modify ComplianceSettings.Directory in cloud", func(t *testing.T) { th.App.Srv().SetLicense(model.NewTestLicense("cloud")) defer th.App.Srv().RemoveLicense() @@ -847,59 +792,6 @@ func TestPatchConfig(t *testing.T) { assert.Equal(t, newURL, *cfg.PluginSettings.MarketplaceURL) }) - t.Run("Should not be able to save config if the new config exceeds Freemium limits", func(t *testing.T) { - th.App.Srv().SetLicense(model.NewTestLicense("cloud")) - defer th.App.Srv().RemoveLicense() - - cloud := &mocks.CloudInterface{} - cloudImpl := th.App.Srv().Cloud - defer func() { - th.App.Srv().Cloud = cloudImpl - }() - th.App.Srv().Cloud = cloud - - cloud.Mock.On("GetCloudLimits", mock.Anything).Return(&model.ProductLimits{ - Integrations: &model.IntegrationsLimits{ - Enabled: model.NewInt(0), - }, - }, nil).Once() - - // Exceed freemium limit. Should throw error. - cfg1 := th.App.Config().Clone() - cfg1.PluginSettings.PluginStates["new-plugin"] = &model.PluginState{Enable: true} - _, _, err1 := th.SystemAdminClient.PatchConfig(cfg1) - require.Error(t, err1) - - // No attempt to enable a plugin. Should not throw error. - cfg1 = th.App.Config().Clone() - cfg1.PluginSettings.PluginStates["new-plugin"] = &model.PluginState{Enable: false} - _, _, err1 = th.SystemAdminClient.PatchConfig(cfg1) - require.NoError(t, err1) - - cloud.Mock.On("GetCloudLimits", mock.Anything).Return(&model.ProductLimits{ - Integrations: &model.IntegrationsLimits{ - Enabled: model.NewInt(1), - }, - }, nil).Twice() - - // Exceed freemium limit while enabling more than one plugin. Should throw error. - cfg1 = th.App.Config().Clone() - cfg1.PluginSettings.PluginStates["new-plugin"] = &model.PluginState{Enable: true} - cfg1.PluginSettings.PluginStates["new-plugin2"] = &model.PluginState{Enable: true} - _, _, err1 = th.SystemAdminClient.PatchConfig(cfg1) - require.Error(t, err1) - - // Match freemium limit. Should not throw error. - cfg1 = th.App.Config().Clone() - cfg1.PluginSettings.PluginStates["new-plugin"] = &model.PluginState{Enable: true} - _, _, err1 = th.SystemAdminClient.PatchConfig(cfg1) - require.NoError(t, err1) - - // Save same config with same plugin enabled. Should not throw error. - _, _, err1 = th.SystemAdminClient.PatchConfig(cfg1) - require.NoError(t, err1) - }) - t.Run("System Admin should not be able to clear Site URL", func(t *testing.T) { cfg, _, err := th.SystemAdminClient.GetConfig() require.NoError(t, err) diff --git a/api4/usage.go b/api4/usage.go index 3822972ba4..542c89ffa8 100644 --- a/api4/usage.go +++ b/api4/usage.go @@ -18,8 +18,6 @@ func (api *API) InitUsage() { api.BaseRoutes.Usage.Handle("/storage", api.APISessionRequired(getStorageUsage)).Methods("GET") // GET /api/v4/usage/teams api.BaseRoutes.Usage.Handle("/teams", api.APISessionRequired(getTeamsUsage)).Methods("GET") - // GET /api/v4/usage/integrations - api.BaseRoutes.Usage.Handle("/integrations", api.APISessionRequired(getIntegrationsUsage)).Methods("GET") } func getPostsUsage(c *Context, w http.ResponseWriter, r *http.Request) { @@ -74,30 +72,3 @@ func getTeamsUsage(c *Context, w http.ResponseWriter, r *http.Request) { w.Write(json) } - -func getIntegrationsUsage(c *Context, w http.ResponseWriter, r *http.Request) { - if !*c.App.Config().PluginSettings.Enable { - json, err := json.Marshal(&model.IntegrationsUsage{}) - if err != nil { - c.Err = model.NewAppError("Api4.getIntegrationsUsage", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err) - return - } - - w.Write(json) - return - } - - usage, appErr := c.App.GetIntegrationsUsage() - if appErr != nil { - c.Err = appErr - return - } - - json, err := json.Marshal(usage) - if err != nil { - c.Err = model.NewAppError("Api4.getIntegrationsUsage", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err) - return - } - - w.Write(json) -} diff --git a/api4/usage_test.go b/api4/usage_test.go index 41d96ae75c..9171614cc8 100644 --- a/api4/usage_test.go +++ b/api4/usage_test.go @@ -91,28 +91,3 @@ func TestGetTeamsUsage(t *testing.T) { assert.Equal(t, int64(3), usage.Active) }) } - -func TestGetIntegrationsUsage(t *testing.T) { - t.Run("unauthenticated users can not access", func(t *testing.T) { - th := Setup(t) - defer th.TearDown() - - th.Client.Logout() - - usage, r, err := th.Client.GetIntegrationsUsage() - assert.Error(t, err) - assert.Nil(t, usage) - assert.Equal(t, http.StatusUnauthorized, r.StatusCode) - }) - - t.Run("good request returns response", func(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - - usage, r, err := th.Client.GetIntegrationsUsage() - assert.NoError(t, err) - assert.Equal(t, http.StatusOK, r.StatusCode) - assert.NotNil(t, usage) - assert.Equal(t, 0, usage.Enabled) - }) -} diff --git a/app/app_iface.go b/app/app_iface.go index ab22b9cbf9..a96681d93c 100644 --- a/app/app_iface.go +++ b/app/app_iface.go @@ -69,8 +69,6 @@ type AppIface interface { // If includeRemovedMembers is true, then channel members who left or were removed from the channel will // be included; otherwise, they will be excluded. ChannelMembersToAdd(since int64, channelID *string, includeRemovedMembers bool) ([]*model.UserChannelIDPair, *model.AppError) - // CheckFreemiumLimitsForConfigSave returns an error if the configuration being saved violates a cloud plan's limits - CheckFreemiumLimitsForConfigSave(oldConfig, newConfig *model.Config) *model.AppError // CheckProviderAttributes returns the empty string if the patch can be applied without // overriding attributes set by the user's login provider; otherwise, the name of the offending // field is returned. @@ -184,8 +182,6 @@ type AppIface interface { GetFilteredUsersStats(options *model.UserCountOptions) (*model.UsersStats, *model.AppError) // GetGroupsByTeam returns the paged list and the total count of group associated to the given team. GetGroupsByTeam(teamID string, opts model.GroupSearchOpts) ([]*model.GroupWithSchemeAdmin, int, *model.AppError) - // GetIntegrationsUsage returns usage information on enabled integrations - GetIntegrationsUsage() (*model.IntegrationsUsage, *model.AppError) // GetKnownUsers returns the list of user ids of users with any direct // relationship with a user. That means any user sharing any channel, including // direct and group channels. diff --git a/app/integrations.go b/app/integrations.go deleted file mode 100644 index 0a810cbf0b..0000000000 --- a/app/integrations.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package app - -import ( - "net/http" - "sort" - "strings" - - "github.com/mattermost/mattermost-server/v6/model" - "github.com/mattermost/mattermost-server/v6/shared/mlog" -) - -func (a *App) checkIntegrationLimitsForConfigSave(oldConfig, newConfig *model.Config) *model.AppError { - pluginIds := []string{} - for pluginId, newState := range newConfig.PluginSettings.PluginStates { - oldState, ok := oldConfig.PluginSettings.PluginStates[pluginId] - if newState.Enable && !(ok && oldState.Enable) { - pluginIds = append(pluginIds, pluginId) - } - } - - if len(pluginIds) > 0 { - return a.checkIfIntegrationsMeetFreemiumLimits(pluginIds) - } - - return nil -} - -func (ch *Channels) getInstalledIntegrations() ([]*model.InstalledIntegration, *model.AppError) { - out := []*model.InstalledIntegration{} - - pluginsEnvironment := ch.GetPluginsEnvironment() - if pluginsEnvironment == nil { - return out, nil - } - - plugins, err := pluginsEnvironment.Available() - if err != nil { - return nil, model.NewAppError("getInstalledIntegrations", "app.plugin.sync.read_local_folder.app_error", nil, "", 0).Wrap(err) - } - - pluginStates := ch.cfgSvc.Config().PluginSettings.PluginStates - for _, p := range plugins { - if _, ok := model.InstalledIntegrationsIgnoredPlugins[p.Manifest.Id]; !ok { - enabled := false - if state, ok := pluginStates[p.Manifest.Id]; ok { - enabled = state.Enable - } - - integration := &model.InstalledIntegration{ - Type: "plugin", - ID: p.Manifest.Id, - Name: p.Manifest.Name, - Version: p.Manifest.Version, - Enabled: enabled, - } - - out = append(out, integration) - } - } - - // Sort result alphabetically, by display name. - sort.SliceStable(out, func(i, j int) bool { - return strings.ToLower(out[i].Name) < strings.ToLower(out[j].Name) - }) - - return out, nil -} - -func (a *App) checkIfIntegrationsMeetFreemiumLimits(originalPluginIds []string) *model.AppError { - if !a.License().IsCloud() { - return nil - } - - pluginIds := map[string]bool{} - for _, pluginId := range originalPluginIds { - if _, ok := model.InstalledIntegrationsIgnoredPlugins[pluginId]; !ok { - pluginIds[pluginId] = true - } - } - - limits, err := a.Cloud().GetCloudLimits("") - if err != nil { - a.Log().Error("Error fetching cloud limits for enabled integrations", mlog.Err(err)) - return nil - } - - if limits == nil || limits.Integrations == nil || limits.Integrations.Enabled == nil { - return nil - } - - installed, appErr := a.ch.getInstalledIntegrations() - if appErr != nil { - a.Log().Error("Failed to get installed integrations to check cloud limit", mlog.Err(appErr)) - return nil - } - - enableCount := len(pluginIds) - for _, integration := range installed { - if _, ok := pluginIds[integration.ID]; !ok && integration.Enabled { - enableCount++ - } - } - - limit := *limits.Integrations.Enabled - if enableCount > limit { - return model.NewAppError("checkIfIntegrationMeetsFreemiumLimits", "app.install_integration.reached_max_limit.error", map[string]any{"NumIntegrations": limit}, "", http.StatusBadRequest) - } - - return nil -} diff --git a/app/integrations_test.go b/app/integrations_test.go deleted file mode 100644 index 3a3b6b6c6a..0000000000 --- a/app/integrations_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package app - -import ( - "testing" - - "github.com/mattermost/mattermost-server/v6/model" - "github.com/stretchr/testify/require" -) - -func TestGetIntegrationsUsage(t *testing.T) { - th := Setup(t).InitBasic() - defer th.TearDown() - - samplePluginCode := ` - package main - - import ( - "github.com/mattermost/mattermost-server/v6/plugin" - ) - - type MyPlugin struct { - plugin.MattermostPlugin - } - - func main() { - plugin.ClientMain(&MyPlugin{}) - } - ` - - setupMultiPluginAPITest(t, - []string{samplePluginCode, samplePluginCode, samplePluginCode, samplePluginCode, samplePluginCode, samplePluginCode, samplePluginCode}, []string{ - `{"id": "otherplugin", "name": "Other Plugin", "version": "1.2.0", "server": {"executable": "backend.exe"}}`, - `{"id": "mattermost-autolink", "name": "Autolink", "version": "1.2.0", "server": {"executable": "backend.exe"}}`, - `{"id": "playbooks", "name": "Playbooks", "version": "1.2.0", "server": {"executable": "backend.exe"}}`, - `{"id": "focalboard", "name": "Mattermost Boards", "version": "1.2.0", "server": {"executable": "backend.exe"}}`, - `{"id": "com.mattermost.calls", "name": "Calls", "version": "1.2.0", "server": {"executable": "backend.exe"}}`, - `{"id": "com.mattermost.nps", "name": "User Satisfaction Surveys", "version": "1.2.0", "server": {"executable": "backend.exe"}}`, - `{"id": "com.mattermost.apps", "server": {"executable": "backend.exe"}}`, - }, []string{"otherplugin", "mattermost-autolink", "playbooks", "focalboard", "com.mattermost.calls", "com.mattermost.nps", "com.mattermost.apps"}, - true, th.App, th.Context) - - integrations, appErr := th.App.ch.getInstalledIntegrations() - require.Nil(t, appErr) - - expected := []*model.InstalledIntegration{ - { - Type: "plugin", - ID: "mattermost-autolink", - Name: "Autolink", - Version: "1.2.0", - Enabled: true, - }, - { - Type: "plugin", - ID: "otherplugin", - Name: "Other Plugin", - Version: "1.2.0", - Enabled: true, - }, - } - require.Equal(t, expected, integrations) - - usage, appErr := th.App.GetIntegrationsUsage() - require.Nil(t, appErr) - - // 2 enabled integrations - expectedUsage := &model.IntegrationsUsage{ - Enabled: 2, - } - require.Equal(t, expectedUsage, usage) -} diff --git a/app/opentracing/opentracing_layer.go b/app/opentracing/opentracing_layer.go index b40c1921aa..51a5daba92 100644 --- a/app/opentracing/opentracing_layer.go +++ b/app/opentracing/opentracing_layer.go @@ -1200,28 +1200,6 @@ func (a *OpenTracingAppLayer) CheckForClientSideCert(r *http.Request) (string, s return resultVar0, resultVar1, resultVar2 } -func (a *OpenTracingAppLayer) CheckFreemiumLimitsForConfigSave(oldConfig *model.Config, newConfig *model.Config) *model.AppError { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CheckFreemiumLimitsForConfigSave") - - a.ctx = newCtx - a.app.Srv().Store().SetContext(newCtx) - defer func() { - a.app.Srv().Store().SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - resultVar0 := a.app.CheckFreemiumLimitsForConfigSave(oldConfig, newConfig) - - if resultVar0 != nil { - span.LogFields(spanlog.Error(resultVar0)) - ext.Error.Set(span, true) - } - - return resultVar0 -} - func (a *OpenTracingAppLayer) CheckIntegrity() <-chan model.IntegrityCheckResult { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CheckIntegrity") @@ -6630,28 +6608,6 @@ func (a *OpenTracingAppLayer) GetIncomingWebhooksPageByUser(userID string, page return resultVar0, resultVar1 } -func (a *OpenTracingAppLayer) GetIntegrationsUsage() (*model.IntegrationsUsage, *model.AppError) { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetIntegrationsUsage") - - a.ctx = newCtx - a.app.Srv().Store().SetContext(newCtx) - defer func() { - a.app.Srv().Store().SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - resultVar0, resultVar1 := a.app.GetIntegrationsUsage() - - if resultVar1 != nil { - span.LogFields(spanlog.Error(resultVar1)) - ext.Error.Set(span, true) - } - - return resultVar0, resultVar1 -} - func (a *OpenTracingAppLayer) GetJob(id string) (*model.Job, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetJob") diff --git a/app/plugin.go b/app/plugin.go index b9042330bf..b48503de5d 100644 --- a/app/plugin.go +++ b/app/plugin.go @@ -200,10 +200,6 @@ func (ch *Channels) syncPluginsActiveState() { if err := ch.notifyPluginStatusesChanged(); err != nil { mlog.Warn("failed to notify plugin status changed", mlog.Err(err)) } - - if err := ch.notifyIntegrationsUsageChanged(); err != nil { - mlog.Warn("Failed to notify integrations usage changed", mlog.Err(err)) - } } func (a *App) NewPluginAPI(c *request.Context, manifest *model.Manifest) plugin.API { @@ -422,11 +418,6 @@ func (a *App) GetActivePluginManifests() ([]*model.Manifest, *model.AppError) { // activation if inactive anywhere in the cluster. // Notifies cluster peers through config change. func (a *App) EnablePlugin(id string) *model.AppError { - appErr := a.checkIfIntegrationsMeetFreemiumLimits([]string{id}) - if appErr != nil { - return appErr - } - return a.ch.enablePlugin(id) } @@ -537,20 +528,6 @@ func (ch *Channels) disablePlugin(id string) *model.AppError { return nil } -func (ch *Channels) notifyIntegrationsUsageChanged() *model.AppError { - usage, appErr := ch.getIntegrationsUsage() - if appErr != nil { - return appErr - } - - message := model.NewWebSocketEvent(model.WebsocketEventIntegrationsUsageChanged, "", "", "", nil, "") - message.Add("usage", usage) - message.GetBroadcast().ContainsSensitiveData = true - ch.Publish(message) - - return nil -} - func (a *App) GetPlugins() (*model.PluginsResponse, *model.AppError) { pluginsEnvironment := a.GetPluginsEnvironment() if pluginsEnvironment == nil { diff --git a/app/plugin_install.go b/app/plugin_install.go index fa076df3c4..431c3abebe 100644 --- a/app/plugin_install.go +++ b/app/plugin_install.go @@ -102,10 +102,6 @@ func (ch *Channels) installPluginFromData(data model.PluginEventData) { if err := ch.notifyPluginStatusesChanged(); err != nil { mlog.Error("Failed to notify plugin status changed", mlog.Err(err)) } - - if err := ch.notifyIntegrationsUsageChanged(); err != nil { - mlog.Warn("Failed to notify integrations usage changed", mlog.Err(err)) - } } func (ch *Channels) removePluginFromData(data model.PluginEventData) { @@ -118,10 +114,6 @@ func (ch *Channels) removePluginFromData(data model.PluginEventData) { if err := ch.notifyPluginStatusesChanged(); err != nil { mlog.Warn("failed to notify plugin status changed", mlog.Err(err)) } - - if err := ch.notifyIntegrationsUsageChanged(); err != nil { - mlog.Warn("Failed to notify integrations usage changed", mlog.Err(err)) - } } // InstallPluginWithSignature verifies and installs plugin. @@ -177,10 +169,6 @@ func (ch *Channels) installPlugin(pluginFile, signature io.ReadSeeker, installat mlog.Warn("Failed to notify plugin status changed", mlog.Err(err)) } - if err := ch.notifyIntegrationsUsageChanged(); err != nil { - mlog.Warn("Failed to notify integrations usage changed", mlog.Err(err)) - } - return manifest, nil } @@ -455,10 +443,6 @@ func (ch *Channels) RemovePlugin(id string) *model.AppError { mlog.Warn("Failed to notify plugin status changed", mlog.Err(err)) } - if err := ch.notifyIntegrationsUsageChanged(); err != nil { - mlog.Warn("Failed to notify integrations usage changed", mlog.Err(err)) - } - return nil } diff --git a/app/plugin_test.go b/app/plugin_test.go index fcc719c891..57802c67ac 100644 --- a/app/plugin_test.go +++ b/app/plugin_test.go @@ -1057,17 +1057,6 @@ func TestEnablePluginWithCloudLimits(t *testing.T) { appErr = th.App.EnablePlugin("testplugin") checkNoError(t, appErr) - appErr = th.App.EnablePlugin("testplugin2") - checkError(t, appErr) - require.Equal(t, "app.install_integration.reached_max_limit.error", appErr.Id) - - th.App.Srv().RemoveLicense() - appErr = th.App.EnablePlugin("testplugin2") - checkNoError(t, appErr) - th.App.Srv().SetLicense(model.NewTestLicense("cloud")) - appErr = th.App.EnablePlugin("testplugin2") - checkError(t, appErr) - // Let enable succeed if a CWS error occurs cloud = &mocks.CloudInterface{} th.App.Srv().Cloud = cloud diff --git a/app/usage.go b/app/usage.go index 3abf9bdf64..43c71e004b 100644 --- a/app/usage.go +++ b/app/usage.go @@ -10,37 +10,6 @@ import ( "github.com/mattermost/mattermost-server/v6/utils" ) -// CheckFreemiumLimitsForConfigSave returns an error if the configuration being saved violates a cloud plan's limits -func (a *App) CheckFreemiumLimitsForConfigSave(oldConfig, newConfig *model.Config) *model.AppError { - appErr := a.checkIntegrationLimitsForConfigSave(oldConfig, newConfig) - if appErr != nil { - return appErr - } - - return nil -} - -// GetIntegrationsUsage returns usage information on enabled integrations -func (a *App) GetIntegrationsUsage() (*model.IntegrationsUsage, *model.AppError) { - return a.ch.getIntegrationsUsage() -} - -func (ch *Channels) getIntegrationsUsage() (*model.IntegrationsUsage, *model.AppError) { - installed, appErr := ch.getInstalledIntegrations() - if appErr != nil { - return nil, appErr - } - - var count = 0 - for _, i := range installed { - if i.Enabled { - count++ - } - } - - return &model.IntegrationsUsage{Enabled: count}, nil -} - // GetPostsUsage returns the total posts count rounded down to the most // significant digit func (a *App) GetPostsUsage() (int64, *model.AppError) { diff --git a/i18n/de.json b/i18n/de.json index 4c409f0669..01f8c07fc7 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -9202,10 +9202,6 @@ "id": "app.recent_searches.app_error", "translation": "Fehler beim Holen der letzten Suchen" }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": "Du hast das Maximum von {{.NumIntegrations}} aktivierten Integrationen erreicht. Um Integrationen ohne Beschränkungen zu installieren, upgrade auf ein bezahltes Abonnements." - }, { "id": "app.teams.analytics_teams_count.app_error", "translation": "Kann Team-Zähler nicht abfragen" diff --git a/i18n/en.json b/i18n/en.json index 067f77b4fe..a9612782c4 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -5619,10 +5619,6 @@ "id": "app.insights.feature_disabled", "translation": "Insights feature is disabled." }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": "You've reached the max limit of {{.NumIntegrations}} enabled integrations. To install unlimited integrations, upgrade to one of our paid plans." - }, { "id": "app.job.download_export_results_not_enabled", "translation": "DownloadExportResults in config.json is false. Please set this to true to download the results of this job." diff --git a/i18n/en_AU.json b/i18n/en_AU.json index 64ea23c44d..dc7bbc6e2a 100644 --- a/i18n/en_AU.json +++ b/i18n/en_AU.json @@ -9198,10 +9198,6 @@ "id": "api.file.cloud_upload.app_error", "translation": "Uploading via mmctl to a Cloud instance is not supported. Please check the documentation here: https://docs.mattermost.com/manage/cloud-data-export.html." }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": "You've reached the limit of {{.NumIntegrations}} enabled integrations. To install unlimited integrations, upgrade to one of the paid plans." - }, { "id": "app.usage.get_storage_usage.app_error", "translation": "Failed to get storage usage." diff --git a/i18n/es.json b/i18n/es.json index 65593d7226..f2c4f8e106 100644 --- a/i18n/es.json +++ b/i18n/es.json @@ -9151,10 +9151,6 @@ "id": "api.templates.invite_team_and_channels_body.title", "translation": " " }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": "Has alcanzado el límite máximo de {{.NumIntegrations}} integraciones activas. Para instalar integraciones ilimitadas, actualiza a uno de unos planes de pago." - }, { "id": "model.channel.is_valid.1_or_more.app_error", "translation": "El Nombre debe tener 1 o más caracteres alfanuméricos en minúsculas." diff --git a/i18n/fr.json b/i18n/fr.json index c94978c9da..73d49de59b 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -8747,10 +8747,6 @@ "id": "app.job.get_all_jobs_by_type_and_status.app_error", "translation": "Impossible d'obtenir tous les travaux par type et statuts." }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": "Vous avez atteint la limite maximale de {{.NumIntegrations}} d'intégrations activées. Pour installer un nombre illimité d'intégrations, effectuez une mise à niveau vers l'un de nos plans payants." - }, { "id": "app.insights.feature_disabled", "translation": "La fonctionnalité des aperçus est désactivée." diff --git a/i18n/hu.json b/i18n/hu.json index 930d7fd5d3..1a316e0355 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -9203,10 +9203,6 @@ "id": "app.teams.analytics_teams_count.app_error", "translation": "Nem kérdezhető le a csapatok száma" }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": "Elérte az engedélyezett integrációk maximális számát {{.NumIntegrations}}. Korlátlan számú integráció telepítéséhez frissítsen valamelyik fizetős csomagunkra." - }, { "id": "app.post.analytics_teams_count.app_error", "translation": "Nem kérdezhető le a csapat használtság" diff --git a/i18n/it.json b/i18n/it.json index f9c6ae0911..c4e839c010 100644 --- a/i18n/it.json +++ b/i18n/it.json @@ -8383,10 +8383,6 @@ "id": "app.notification.body.group.title", "translation": " " }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": " " - }, { "id": "app.user.missing_account.const", "translation": " " diff --git a/i18n/ja.json b/i18n/ja.json index ff427d87e3..648a82e5b0 100644 --- a/i18n/ja.json +++ b/i18n/ja.json @@ -9195,10 +9195,6 @@ "id": "api.cloud.subscription.update_error", "translation": "ウェブフックからサブスクリプションを更新する際にエラーが発生しました。" }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": "有効な統合機能数の上限 {{.NumIntegrations}} に達しました。無制限に統合機能をインストールするには、いずれかの有料プランにアップグレードしてください。" - }, { "id": "model.config.is_valid.image_decoder_concurrency.app_error", "translation": "デコーダーの並列数 {{.Value}} は不正です。正の数または-1であるべきです。" diff --git a/i18n/nl.json b/i18n/nl.json index dedae87b78..994d088bcd 100644 --- a/i18n/nl.json +++ b/i18n/nl.json @@ -9218,10 +9218,6 @@ "id": "app.teams.analytics_teams_count.app_error", "translation": "Niet gelukt om het aan aantal teams op te halen" }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": "Je bereikte de maximumlimiet van {{.NumIntegrations}} ingeschakelde integraties. Om onbeperkte integraties te installeren, upgrade naar een van onze betaalde plannen." - }, { "id": "api.cloud.teams_limit_reached.restore", "translation": "Kan het team niet herstellen omdat de teamlimiet bereikt is" diff --git a/i18n/pl.json b/i18n/pl.json index 64c2419ae0..922951e724 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -9203,10 +9203,6 @@ "id": "app.recent_searches.app_error", "translation": "Błąd pobierania ostatnich wyszukiwań" }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": "Osiągnąłeś maksymalny limit {{.NumIntegrations}} włączonych integracji. Aby zainstalować nieograniczoną liczbę integracji, uaktualnij do jednego z naszych płatnych planów." - }, { "id": "app.teams.analytics_teams_count.app_error", "translation": "Nie można uzyskać liczby zespołów" diff --git a/i18n/sv.json b/i18n/sv.json index 04d40500d7..a1c59374bc 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -9202,10 +9202,6 @@ "id": "api.file.cloud_upload.app_error", "translation": "Uppladdning via mmctl till en molninstans stöds inte. Se dokumentationen här: https://docs.mattermost.com/manage/cloud-data-export.html." }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": "Du har nått maxgränsen {{.NumIntegrations}} aktiva integrationer. Om du vill installera obegränsat antal integrationer kan du uppgradera till en av våra betal-abonnemang." - }, { "id": "app.usage.get_storage_usage.app_error", "translation": "Det gick inte att få fram lagringsvolym." diff --git a/i18n/tr.json b/i18n/tr.json index 680520e2fd..f9e472bfed 100644 --- a/i18n/tr.json +++ b/i18n/tr.json @@ -9202,10 +9202,6 @@ "id": "api.file.cloud_upload.app_error", "translation": "Bir Bulut kopyasına mmctl ile yükleme desteklenmiyor. Lütfen şu makaleye bakın: https://docs.mattermost.com/manage/cloud-data-export.html." }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": "Kullanabileceğiniz en fazla {{.NumIntegrations}} bütünleştirme sınırına ulaştınız. Sınırsız bütünleştirme için ücretli tarifelerimizden birine geçin." - }, { "id": "app.teams.analytics_teams_count.app_error", "translation": "Takım sayısı alınamadı" diff --git a/i18n/zh-CN.json b/i18n/zh-CN.json index 9d3b1ef5fe..724dd25d78 100644 --- a/i18n/zh-CN.json +++ b/i18n/zh-CN.json @@ -9231,10 +9231,6 @@ "id": "app.post_reminder_dm", "translation": "您好,这是您关于此消息的提醒 @{{.Username}}: {{.SiteURL}}/{{.TeamName}}/pl/{{.PostId}}" }, - { - "id": "app.install_integration.reached_max_limit.error", - "translation": "您已达到启用 {{.NumIntegrations}} 集成的最大限制。 要安装无限集成,请升级到我们的付费计划之一。" - }, { "id": "app.usage.get_storage_usage.app_error", "translation": "无法获取存储使用情况。" diff --git a/model/client4.go b/model/client4.go index d195bec920..47a1ce366b 100644 --- a/model/client4.go +++ b/model/client4.go @@ -8434,19 +8434,6 @@ func (c *Client4) GetTeamsUsage() (*TeamsUsage, *Response, error) { return usage, BuildResponse(r), err } -// GetIntegrationsUsage returns usage information on integrations, including the count of enabled integrations -func (c *Client4) GetIntegrationsUsage() (*IntegrationsUsage, *Response, error) { - r, err := c.DoAPIGet(c.usageRoute()+"/integrations", "") - if err != nil { - return nil, BuildResponse(r), err - } - defer closeBody(r) - - var usage *IntegrationsUsage - err = json.NewDecoder(r.Body).Decode(&usage) - return usage, BuildResponse(r), err -} - func (c *Client4) GetNewTeamMembersSince(teamID string, timeRange string, page int, perPage int) (*NewTeamMembersList, *Response, error) { query := fmt.Sprintf("?time_range=%v&page=%v&per_page=%v", timeRange, page, perPage) r, err := c.DoAPIGet(c.teamRoute(teamID)+"/top/team_members"+query, "") diff --git a/model/usage.go b/model/usage.go index 6ee0c4383f..413ee2b8e6 100644 --- a/model/usage.go +++ b/model/usage.go @@ -16,10 +16,6 @@ type TeamsUsage struct { CloudArchived int64 `json:"cloud_archived"` } -type IntegrationsUsage struct { - Enabled int `json:"enabled"` -} - var InstalledIntegrationsIgnoredPlugins = map[string]struct{}{ PluginIdPlaybooks: {}, PluginIdFocalboard: {}, diff --git a/model/websocket_message.go b/model/websocket_message.go index 41c802dbfc..9cd3892453 100644 --- a/model/websocket_message.go +++ b/model/websocket_message.go @@ -76,7 +76,6 @@ const ( WebsocketEventThreadFollowChanged = "thread_follow_changed" WebsocketEventThreadReadChanged = "thread_read_changed" WebsocketFirstAdminVisitMarketplaceStatusReceived = "first_admin_visit_marketplace_status_received" - WebsocketEventIntegrationsUsageChanged = "integrations_usage_changed" ) type WebSocketMessage interface {