From 013c495a97a3229f49f8fcbd40d67eecf011216e Mon Sep 17 00:00:00 2001 From: Ibrahim Serdar Acikgoz Date: Wed, 17 Feb 2021 12:38:52 +0300 Subject: [PATCH] [MM-31674] app/product_notices: implement notices for ext. dependencies (#16871) * app/product_notices: implement notices for ext. dependencies * remove a log line Co-authored-by: Mattermod --- app/product_notices.go | 36 +++++++++++++++++- app/product_notices_test.go | 74 +++++++++++++++++++++++++++++++++++++ model/product_notices.go | 30 +++++++++------ 3 files changed, 127 insertions(+), 13 deletions(-) diff --git a/app/product_notices.go b/app/product_notices.go index ebe63a0d81..10dbaa8022 100644 --- a/app/product_notices.go +++ b/app/product_notices.go @@ -32,6 +32,8 @@ var noticesCache = utils.RequestCache{} var cachedPostCount int64 var cachedUserCount int64 +var cachedDBMSVersion string + // previously fetched notices var cachedNotices model.ProductNotices var rcStripRegexp = regexp.MustCompile(`(.*?)(-rc\d+)(.*?)`) @@ -52,7 +54,8 @@ func cleanupVersion(originalVersion string) string { func noticeMatchesConditions(config *model.Config, preferences store.PreferenceStore, userID string, client model.NoticeClientType, clientVersion string, postCount int64, userCount int64, isSystemAdmin bool, - isTeamAdmin bool, isCloud bool, sku string, notice *model.ProductNotice) (bool, error) { + isTeamAdmin bool, isCloud bool, sku, dbName, dbVer string, + notice *model.ProductNotice) (bool, error) { cnd := notice.Conditions // check client type @@ -144,6 +147,27 @@ func noticeMatchesConditions(config *model.Config, preferences store.PreferenceS } } + if cnd.DeprecatingDependency != nil { + extDepVersion, err := semver.NewVersion(cnd.DeprecatingDependency.MinimumVersion) + if err != nil { + return false, errors.Wrapf(err, "Cannot parse external dependency version %s", cnd.DeprecatingDependency.MinimumVersion) + } + + switch cnd.DeprecatingDependency.Name { + case model.DATABASE_DRIVER_MYSQL, model.DATABASE_DRIVER_POSTGRES: + if dbName != cnd.DeprecatingDependency.Name { + return false, nil + } + serverDBMSVersion, err := semver.NewVersion(dbVer) + if err != nil { + return false, errors.Wrapf(err, "Cannot parse DBMS version %s", dbVer) + } + return extDepVersion.GreaterThan(serverDBMSVersion), nil + default: + return false, nil + } + } + // check if our server config matches the notice for k, v := range cnd.ServerConfig { if !validateConfigEntry(config, k, v) { @@ -225,6 +249,7 @@ func (a *App) GetProductNotices(userID, teamID string, client model.NoticeClient sku := a.Srv().ClientLicense()["SkuShortName"] isCloud := a.Srv().License() != nil && *a.Srv().License().Features.Cloud + dbName := *a.Srv().Config().SqlSettings.DriverName filteredNotices := make([]model.NoticeMessage, 0) @@ -262,6 +287,8 @@ func (a *App) GetProductNotices(userID, teamID string, client model.NoticeClient isTeamAdmin, isCloud, sku, + dbName, + cachedDBMSVersion, &cachedNotices[noticeIndex]) if err != nil { return nil, model.NewAppError("GetProductNotices", "api.system.update_notices.validating_failed", nil, err.Error(), http.StatusBadRequest) @@ -316,6 +343,13 @@ func (a *App) UpdateProductNotices() *model.AppError { mlog.Warn("Failed to fetch user count", mlog.String("error", err.Error())) } + cachedDBMSVersion, err = a.Srv().Store.GetDbVersion(false) + if err != nil { + mlog.Warn("Failed to get DBMS version", mlog.String("error", err.Error())) + } + + cachedDBMSVersion = strings.Split(cachedDBMSVersion, " ")[0] // get rid of trailing strings attached to the version + data, err := utils.GetUrlWithCache(url, ¬icesCache, skip) if err != nil { return model.NewAppError("UpdateProductNotices", "api.system.update_notices.fetch_failed", nil, err.Error(), http.StatusBadRequest) diff --git a/app/product_notices_test.go b/app/product_notices_test.go index 1fd3da2b98..705fd42362 100644 --- a/app/product_notices_test.go +++ b/app/product_notices_test.go @@ -60,6 +60,8 @@ func TestNoticeValidation(t *testing.T) { systemAdmin bool serverVersion string notice *model.ProductNotice + dbmsName string + dbmsVer string } messages := map[string]model.NoticeMessageInternal{ "en": { @@ -538,6 +540,76 @@ func TestNoticeValidation(t *testing.T) { wantErr: false, wantOk: true, }, + { + name: "notice with depreacting an external dependency", + args: args{ + dbmsName: "mysql", + dbmsVer: "5.6", + notice: &model.ProductNotice{ + Conditions: model.Conditions{ + DeprecatingDependency: &model.ExternalDependency{ + Name: "mysql", + MinimumVersion: "5.7", + }, + }, + }, + }, + wantErr: false, + wantOk: true, + }, + { + name: "notice with depreacting an external dependency, on a future version", + args: args{ + dbmsName: "mysql", + dbmsVer: "5.6", + serverVersion: "5.32", + notice: &model.ProductNotice{ + Conditions: model.Conditions{ + ServerVersion: []string{">=v5.33"}, + DeprecatingDependency: &model.ExternalDependency{ + Name: "mysql", + MinimumVersion: "5.7", + }, + }, + }, + }, + wantErr: false, + wantOk: false, + }, + { + name: "notice on a deprecating dependency, server is all good", + args: args{ + dbmsName: "postgres", + dbmsVer: "10", + notice: &model.ProductNotice{ + Conditions: model.Conditions{ + DeprecatingDependency: &model.ExternalDependency{ + Name: "postgres", + MinimumVersion: "10", + }, + }, + }, + }, + wantErr: false, + wantOk: false, + }, + { + name: "notice on a deprecating dependency, server has different dbms", + args: args{ + dbmsName: "mysql", + dbmsVer: "5.7", + notice: &model.ProductNotice{ + Conditions: model.Conditions{ + DeprecatingDependency: &model.ExternalDependency{ + Name: "postgres", + MinimumVersion: "10", + }, + }, + }, + }, + wantErr: false, + wantOk: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -561,6 +633,8 @@ func TestNoticeValidation(t *testing.T) { tt.args.teamAdmin, tt.args.cloud, tt.args.sku, + tt.args.dbmsName, + tt.args.dbmsVer, tt.args.notice, ); (err != nil) != tt.wantErr { t.Errorf("noticeMatchesConditions() error = %v, wantErr %v", err, tt.wantErr) diff --git a/model/product_notices.go b/model/product_notices.go index fae8ccb3ce..455ae47568 100644 --- a/model/product_notices.go +++ b/model/product_notices.go @@ -40,18 +40,19 @@ func (n *ProductNotice) TeamAdminOnly() bool { } type Conditions struct { - Audience *NoticeAudience `json:"audience,omitempty"` - ClientType *NoticeClientType `json:"clientType,omitempty"` // Only show the notice on specific clients. Defaults to 'all' - DesktopVersion []string `json:"desktopVersion,omitempty"` // What desktop client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["= 2020-03-01T00:00:00Z" - show after specified date; "< 2020-03-01T00:00:00Z" - show before the specified date; "> 2020-03-01T00:00:00Z <= 2020-04-01T00:00:00Z" - show only between the specified dates - InstanceType *NoticeInstanceType `json:"instanceType,omitempty"` - MobileVersion []string `json:"mobileVersion,omitempty"` // What mobile client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["=1.2.3 < ~2.4.x"]; Example: ["=1.2.3 < ~2.4.x"]; Example: ["= 2020-03-01T00:00:00Z" - show after specified date; "< 2020-03-01T00:00:00Z" - show before the specified date; "> 2020-03-01T00:00:00Z <= 2020-04-01T00:00:00Z" - show only between the specified dates + InstanceType *NoticeInstanceType `json:"instanceType,omitempty"` + MobileVersion []string `json:"mobileVersion,omitempty"` // What mobile client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["=1.2.3 < ~2.4.x"]; Example: ["