[MM-53990] Support a global retention time of less than 1 day (#25196)

* adding new MessageRetentionHours config

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
Ben Cooke
2024-01-09 14:10:22 -05:00
committed by GitHub
parent 82b8d4dc07
commit bb88b92b4c
18 changed files with 376 additions and 47 deletions

View File

@@ -122,9 +122,9 @@ func GenerateClientConfig(c *model.Config, telemetryID string, license *model.Li
props["AllowCustomThemes"] = "true"
props["AllowedThemes"] = ""
props["DataRetentionEnableMessageDeletion"] = "false"
props["DataRetentionMessageRetentionDays"] = "0"
props["DataRetentionMessageRetentionHours"] = "0"
props["DataRetentionEnableFileDeletion"] = "false"
props["DataRetentionFileRetentionDays"] = "0"
props["DataRetentionFileRetentionHours"] = "0"
props["CustomUrlSchemes"] = strings.Join(c.DisplaySettings.CustomURLSchemes, ",")
props["MaxMarkdownNodes"] = strconv.FormatInt(int64(*c.DisplaySettings.MaxMarkdownNodes), 10)
@@ -204,9 +204,9 @@ func GenerateClientConfig(c *model.Config, telemetryID string, license *model.Li
if *license.Features.DataRetention {
props["DataRetentionEnableMessageDeletion"] = strconv.FormatBool(*c.DataRetentionSettings.EnableMessageDeletion)
props["DataRetentionMessageRetentionDays"] = strconv.FormatInt(int64(*c.DataRetentionSettings.MessageRetentionDays), 10)
props["DataRetentionMessageRetentionHours"] = strconv.FormatInt(int64(c.DataRetentionSettings.GetMessageRetentionHours()), 10)
props["DataRetentionEnableFileDeletion"] = strconv.FormatBool(*c.DataRetentionSettings.EnableFileDeletion)
props["DataRetentionFileRetentionDays"] = strconv.FormatInt(int64(*c.DataRetentionSettings.FileRetentionDays), 10)
props["DataRetentionFileRetentionHours"] = strconv.FormatInt(int64(c.DataRetentionSettings.GetFileRetentionHours()), 10)
}
if license.HasSharedChannels() {

View File

@@ -8742,13 +8742,37 @@
"id": "model.config.is_valid.data_retention.deletion_job_start_time.app_error",
"translation": "Data retention job start time must be a 24-hour time stamp in the form HH:MM."
},
{
"id": "model.config.is_valid.data_retention.file_retention_both_zero.app_error",
"translation": "File retention days and file retention hours cannot both be 0."
},
{
"id": "model.config.is_valid.data_retention.file_retention_days_too_low.app_error",
"translation": "File retention must be one day or longer."
"translation": "File retention days cannot be less than 0."
},
{
"id": "model.config.is_valid.data_retention.file_retention_hours_too_low.app_error",
"translation": "File retention hours cannot be less than 0"
},
{
"id": "model.config.is_valid.data_retention.file_retention_misconfiguration.app_error",
"translation": "File retention days and file retention hours cannot both be greater than 0."
},
{
"id": "model.config.is_valid.data_retention.message_retention_both_zero.app_error",
"translation": "Message retention days and message retention hours cannot both be 0."
},
{
"id": "model.config.is_valid.data_retention.message_retention_days_too_low.app_error",
"translation": "Message retention must be one day or longer."
"translation": "Message retention days cannot be less than 0."
},
{
"id": "model.config.is_valid.data_retention.message_retention_hours_too_low.app_error",
"translation": "Message retention hours cannot be less than 0"
},
{
"id": "model.config.is_valid.data_retention.message_retention_misconfiguration.app_error",
"translation": "Message retention days and message retention hours cannot both be greater than 0."
},
{
"id": "model.config.is_valid.directory.app_error",

View File

@@ -829,7 +829,9 @@ func (ts *TelemetryService) trackConfig() {
"enable_message_deletion": *cfg.DataRetentionSettings.EnableMessageDeletion,
"enable_file_deletion": *cfg.DataRetentionSettings.EnableFileDeletion,
"message_retention_days": *cfg.DataRetentionSettings.MessageRetentionDays,
"message_retention_hours": *cfg.DataRetentionSettings.MessageRetentionHours,
"file_retention_days": *cfg.DataRetentionSettings.FileRetentionDays,
"file_retention_hours": *cfg.DataRetentionSettings.FileRetentionHours,
"deletion_job_start_time": *cfg.DataRetentionSettings.DeletionJobStartTime,
"batch_size": *cfg.DataRetentionSettings.BatchSize,
"time_between_batches": *cfg.DataRetentionSettings.TimeBetweenBatchesMilliseconds,

View File

@@ -207,7 +207,9 @@ const (
BleveSettingsDefaultBatchSize = 10000
DataRetentionSettingsDefaultMessageRetentionDays = 365
DataRetentionSettingsDefaultMessageRetentionHours = 0
DataRetentionSettingsDefaultFileRetentionDays = 365
DataRetentionSettingsDefaultFileRetentionHours = 0
DataRetentionSettingsDefaultBoardsRetentionDays = 365
DataRetentionSettingsDefaultDeletionJobStartTime = "02:00"
DataRetentionSettingsDefaultBatchSize = 3000
@@ -2911,8 +2913,10 @@ type DataRetentionSettings struct {
EnableMessageDeletion *bool `access:"compliance_data_retention_policy"`
EnableFileDeletion *bool `access:"compliance_data_retention_policy"`
EnableBoardsDeletion *bool `access:"compliance_data_retention_policy"`
MessageRetentionDays *int `access:"compliance_data_retention_policy"`
FileRetentionDays *int `access:"compliance_data_retention_policy"`
MessageRetentionDays *int `access:"compliance_data_retention_policy"` // Deprecated: use `MessageRetentionHours`
MessageRetentionHours *int `access:"compliance_data_retention_policy"`
FileRetentionDays *int `access:"compliance_data_retention_policy"` // Deprecated: use `FileRetentionHours`
FileRetentionHours *int `access:"compliance_data_retention_policy"`
BoardsRetentionDays *int `access:"compliance_data_retention_policy"`
DeletionJobStartTime *string `access:"compliance_data_retention_policy"`
BatchSize *int `access:"compliance_data_retention_policy"`
@@ -2937,10 +2941,18 @@ func (s *DataRetentionSettings) SetDefaults() {
s.MessageRetentionDays = NewInt(DataRetentionSettingsDefaultMessageRetentionDays)
}
if s.MessageRetentionHours == nil {
s.MessageRetentionHours = NewInt(DataRetentionSettingsDefaultMessageRetentionHours)
}
if s.FileRetentionDays == nil {
s.FileRetentionDays = NewInt(DataRetentionSettingsDefaultFileRetentionDays)
}
if s.FileRetentionHours == nil {
s.FileRetentionHours = NewInt(DataRetentionSettingsDefaultFileRetentionHours)
}
if s.BoardsRetentionDays == nil {
s.BoardsRetentionDays = NewInt(DataRetentionSettingsDefaultBoardsRetentionDays)
}
@@ -2961,6 +2973,30 @@ func (s *DataRetentionSettings) SetDefaults() {
}
}
// GetMessageRetentionHours returns the message retention time as an int.
// MessageRetentionHours takes precedence over the deprecated MessageRetentionDays.
func (s *DataRetentionSettings) GetMessageRetentionHours() int {
if s.MessageRetentionHours != nil && *s.MessageRetentionHours > 0 {
return *s.MessageRetentionHours
}
if s.MessageRetentionDays != nil && *s.MessageRetentionDays > 0 {
return *s.MessageRetentionDays * 24
}
return DataRetentionSettingsDefaultMessageRetentionDays * 24
}
// GetFileRetentionHours returns the message retention time as an int.
// FileRetentionHours takes precedence over the deprecated FileRetentionDays.
func (s *DataRetentionSettings) GetFileRetentionHours() int {
if s.FileRetentionHours != nil && *s.FileRetentionHours > 0 {
return *s.FileRetentionHours
}
if s.FileRetentionDays != nil && *s.FileRetentionDays > 0 {
return *s.FileRetentionDays * 24
}
return DataRetentionSettingsDefaultFileRetentionDays * 24
}
type JobSettings struct {
RunJobs *bool `access:"write_restrictable,cloud_restrictable"` // telemetry: none
RunScheduler *bool `access:"write_restrictable,cloud_restrictable"` // telemetry: none
@@ -4141,14 +4177,38 @@ func (bs *BleveSettings) isValid() *AppError {
}
func (s *DataRetentionSettings) isValid() *AppError {
if *s.MessageRetentionDays <= 0 {
if s.MessageRetentionDays == nil || *s.MessageRetentionDays < 0 {
return NewAppError("Config.IsValid", "model.config.is_valid.data_retention.message_retention_days_too_low.app_error", nil, "", http.StatusBadRequest)
}
if *s.FileRetentionDays <= 0 {
if s.MessageRetentionHours == nil || *s.MessageRetentionHours < 0 {
return NewAppError("Config.IsValid", "model.config.is_valid.data_retention.message_retention_hours_too_low.app_error", nil, "", http.StatusBadRequest)
}
if s.FileRetentionDays == nil || *s.FileRetentionDays < 0 {
return NewAppError("Config.IsValid", "model.config.is_valid.data_retention.file_retention_days_too_low.app_error", nil, "", http.StatusBadRequest)
}
if s.FileRetentionHours == nil || *s.FileRetentionHours < 0 {
return NewAppError("Config.IsValid", "model.config.is_valid.data_retention.file_retention_hours_too_low.app_error", nil, "", http.StatusBadRequest)
}
if *s.MessageRetentionDays > 0 && *s.MessageRetentionHours > 0 {
return NewAppError("Config.IsValid", "model.config.is_valid.data_retention.message_retention_misconfiguration.app_error", nil, "", http.StatusBadRequest)
}
if *s.FileRetentionDays > 0 && *s.FileRetentionHours > 0 {
return NewAppError("Config.IsValid", "model.config.is_valid.data_retention.file_retention_misconfiguration.app_error", nil, "", http.StatusBadRequest)
}
if *s.MessageRetentionDays == 0 && *s.MessageRetentionHours == 0 {
return NewAppError("Config.IsValid", "model.config.is_valid.data_retention.message_retention_both_zero.app_error", nil, "", http.StatusBadRequest)
}
if *s.FileRetentionDays == 0 && *s.FileRetentionHours == 0 {
return NewAppError("Config.IsValid", "model.config.is_valid.data_retention.file_retention_both_zero.app_error", nil, "", http.StatusBadRequest)
}
if _, err := time.Parse("15:04", *s.DeletionJobStartTime); err != nil {
return NewAppError("Config.IsValid", "model.config.is_valid.data_retention.deletion_job_start_time.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}

View File

@@ -1647,3 +1647,105 @@ func TestConfigDefaultCallsPluginState(t *testing.T) {
assert.False(t, c1.PluginSettings.PluginStates["com.mattermost.calls"].Enable)
})
}
func TestConfigGetMessageRetentionHours(t *testing.T) {
tests := []struct {
name string
config Config
value int
}{
{
name: "should return MessageRetentionDays config value in hours by default",
config: Config{},
value: 8760,
},
{
name: "should return MessageRetentionHours config value",
config: Config{
DataRetentionSettings: DataRetentionSettings{
MessageRetentionHours: NewInt(48),
},
},
value: 48,
},
{
name: "should return MessageRetentionHours config value",
config: Config{
DataRetentionSettings: DataRetentionSettings{
MessageRetentionDays: NewInt(50),
MessageRetentionHours: NewInt(48),
},
},
value: 48,
},
{
name: "should return MessageRetentionDays config value in hours",
config: Config{
DataRetentionSettings: DataRetentionSettings{
MessageRetentionDays: NewInt(50),
MessageRetentionHours: NewInt(0),
},
},
value: 1200,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
test.config.SetDefaults()
require.Equal(t, test.value, test.config.DataRetentionSettings.GetMessageRetentionHours())
})
}
}
func TestConfigGetFileRetentionHours(t *testing.T) {
tests := []struct {
name string
config Config
value int
}{
{
name: "should return FileRetentionDays config value in hours by default",
config: Config{},
value: 8760,
},
{
name: "should return FileRetentionHours config value",
config: Config{
DataRetentionSettings: DataRetentionSettings{
FileRetentionHours: NewInt(48),
},
},
value: 48,
},
{
name: "should return FileRetentionHours config value",
config: Config{
DataRetentionSettings: DataRetentionSettings{
FileRetentionDays: NewInt(50),
FileRetentionHours: NewInt(48),
},
},
value: 48,
},
{
name: "should return FileRetentionDays config value in hours",
config: Config{
DataRetentionSettings: DataRetentionSettings{
FileRetentionDays: NewInt(50),
FileRetentionHours: NewInt(0),
},
},
value: 1200,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
test.config.SetDefaults()
require.Equal(t, test.value, test.config.DataRetentionSettings.GetFileRetentionHours())
})
}
}