ezbookkeeping/pkg/api/user_app_cloud_settings.go

235 lines
9.5 KiB
Go

package api
import (
"encoding/json"
"time"
"github.com/mayswind/ezbookkeeping/pkg/core"
"github.com/mayswind/ezbookkeeping/pkg/errs"
"github.com/mayswind/ezbookkeeping/pkg/log"
"github.com/mayswind/ezbookkeeping/pkg/models"
"github.com/mayswind/ezbookkeeping/pkg/services"
"github.com/mayswind/ezbookkeeping/pkg/utils"
)
// UserApplicationCloudSettingsApi represents user application cloud settings api
type UserApplicationCloudSettingsApi struct {
userAppCloudSettings *services.UserApplicationCloudSettingsService
users *services.UserService
}
// Initialize a user application cloud settings api singleton instance
var (
UserApplicationCloudSettings = &UserApplicationCloudSettingsApi{
userAppCloudSettings: services.UserApplicationCloudSettings,
users: services.Users,
}
)
// ApplicationSettingsGetHandler returns application cloud settings of current user
func (a *UserApplicationCloudSettingsApi) ApplicationSettingsGetHandler(c *core.WebContext) (any, *errs.Error) {
uid := c.GetCurrentUid()
userApplicationCloudSettings, err := a.userAppCloudSettings.GetUserApplicationCloudSettingsByUid(c, uid)
if err != nil {
log.Errorf(c, "[user_app_cloud_settings.ApplicationSettingsGetHandler] failed to get latest user application cloud settings for user \"uid:%d\", because %s", uid, err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
if userApplicationCloudSettings == nil {
return false, nil
}
applicationCloudSettingSlice := userApplicationCloudSettings.Settings
if len(applicationCloudSettingSlice) < 1 {
return false, nil
}
return applicationCloudSettingSlice, nil
}
// ApplicationSettingsUpdateHandler updates user application cloud settings by request parameters for current user
func (a *UserApplicationCloudSettingsApi) ApplicationSettingsUpdateHandler(c *core.WebContext) (any, *errs.Error) {
var userAppCloudSettingUpdateReq models.UserApplicationCloudSettingsUpdateRequest
err := c.ShouldBindJSON(&userAppCloudSettingUpdateReq)
if err != nil {
log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] parse request failed, because %s", err.Error())
return false, errs.NewIncompleteOrIncorrectSubmissionError(err)
}
uid := c.GetCurrentUid()
user, err := a.users.GetUserById(c, uid)
if err != nil {
if !errs.IsCustomError(err) {
log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] failed to get user for user \"uid:%d\", because %s", uid, err.Error())
}
return false, errs.ErrUserNotFound
}
if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_SYNC_APPLICATION_SETTINGS) {
return false, errs.ErrNotPermittedToPerformThisAction
}
var userApplicationCloudSettings *models.UserApplicationCloudSetting
// Retry up to 3 times
for i := 0; i < 3; i++ {
userApplicationCloudSettings, err = a.userAppCloudSettings.GetUserApplicationCloudSettingsByUid(c, uid)
if err != nil {
log.Errorf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] failed to get latest user application cloud settings for user \"uid:%d\" (try count %d), because %s", uid, i+1, err.Error())
return false, errs.Or(err, errs.ErrOperationFailed)
}
oldApplicationCloudSettingsMap := make(map[string]models.ApplicationCloudSetting)
lastUpdateTime := int64(0)
if userApplicationCloudSettings != nil {
for _, setting := range userApplicationCloudSettings.Settings {
oldApplicationCloudSettingsMap[setting.SettingKey] = setting
}
lastUpdateTime = userApplicationCloudSettings.UpdatedUnixTime
}
// Check if the full update settings are the same as the existing settings
if userAppCloudSettingUpdateReq.FullUpdate {
if len(userAppCloudSettingUpdateReq.Settings) == len(oldApplicationCloudSettingsMap) {
needUpdate := false
for _, setting := range userAppCloudSettingUpdateReq.Settings {
oldSetting, exists := oldApplicationCloudSettingsMap[setting.SettingKey]
if !exists || oldSetting.SettingValue != setting.SettingValue {
needUpdate = true
break
}
}
if !needUpdate {
return false, errs.ErrNothingWillBeUpdated
}
}
} else { // Check if the partial update settings are the same as the existing settings or the settings to update are not set to sync
needUpdate := true
for _, setting := range userAppCloudSettingUpdateReq.Settings {
cloudSetting, exists := oldApplicationCloudSettingsMap[setting.SettingKey]
if !exists {
needUpdate = false
log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" is not set to sync (try count %d)", setting.SettingKey, i+1)
} else if cloudSetting.SettingValue == setting.SettingValue {
needUpdate = false
log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" value \"%s\" is not changed, no need to update (try count %d)", setting.SettingKey, setting.SettingValue, i+1)
}
}
if !needUpdate {
log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] no user application cloud settings need to update for user \"uid:%d\" (try count %d)", uid, i+1)
return true, nil
}
}
newApplicationCloudSettingsMap := make(map[string]models.ApplicationCloudSetting)
var newApplicationCloudSettingSlice models.ApplicationCloudSettingSlice
if userAppCloudSettingUpdateReq.FullUpdate {
log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user \"uid:%d\" application cloud settings force update, will overwrite all existing settings (try count %d)", uid, i+1)
} else {
if len(oldApplicationCloudSettingsMap) > 0 {
log.Infof(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user \"uid:%d\" application cloud settings exists, try to merge it with request settings (try count %d)", uid, i+1)
newApplicationCloudSettingsMap = oldApplicationCloudSettingsMap
}
}
for _, setting := range userAppCloudSettingUpdateReq.Settings {
newApplicationCloudSettingsMap[setting.SettingKey] = setting
}
for settingKey, setting := range newApplicationCloudSettingsMap {
settingType, exists := models.ALL_ALLOWED_CLOUD_SYNC_APP_SETTING_KEY_TYPES[settingKey]
if !exists {
log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" is not supported to sync (try count %d)", settingKey, i+1)
continue
}
if settingType == models.USER_APPLICATION_CLOUD_SETTING_TYPE_STRING {
// Do Nothing
} else if settingType == models.USER_APPLICATION_CLOUD_SETTING_TYPE_NUMBER {
_, err := utils.StringToFloat64(setting.SettingValue)
if err != nil {
log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" has invalid number value \"%s\" (try count %d)", settingKey, setting.SettingValue, i+1)
continue
}
} else if settingType == models.USER_APPLICATION_CLOUD_SETTING_TYPE_BOOLEAN {
if setting.SettingValue != "true" && setting.SettingValue != "false" {
log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" has invalid boolean value \"%s\" (try count %d)", settingKey, setting.SettingValue, i+1)
continue
}
} else if settingType == models.USER_APPLICATION_CLOUD_SETTING_TYPE_STRING_BOOLEAN_MAP {
var settingValueMap map[string]bool
err := json.Unmarshal([]byte(setting.SettingValue), &settingValueMap)
if err != nil {
log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" has invalid map value \"%s\" (try count %d), because %s", settingKey, setting.SettingValue, i+1, err.Error())
continue
}
} else {
log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] user application cloud setting key \"%s\" has unknown type \"%s\" (try count %d)", settingKey, settingType, i+1)
continue
}
newApplicationCloudSettingSlice = append(newApplicationCloudSettingSlice, setting)
}
err = a.userAppCloudSettings.UpdateUserApplicationCloudSettings(c, uid, newApplicationCloudSettingSlice, userAppCloudSettingUpdateReq.FullUpdate, lastUpdateTime)
if err == nil {
break
}
time.Sleep(100 * time.Millisecond) // Wait for 100 milliseconds before retrying
}
if err != nil {
log.Errorf(c, "[user_app_cloud_settings.ApplicationSettingsUpdateHandler] failed to update user application cloud settings for user \"uid:%d\", because %s", uid, err.Error())
return false, errs.Or(err, errs.ErrOperationFailed)
}
return true, nil
}
// ApplicationSettingsDisableHandler disabled user application cloud settings by request parameters for current user
func (a *UserApplicationCloudSettingsApi) ApplicationSettingsDisableHandler(c *core.WebContext) (any, *errs.Error) {
uid := c.GetCurrentUid()
user, err := a.users.GetUserById(c, uid)
if err != nil {
if !errs.IsCustomError(err) {
log.Warnf(c, "[user_app_cloud_settings.ApplicationSettingsDisableHandler] failed to get user for user \"uid:%d\", because %s", uid, err.Error())
}
return false, errs.ErrUserNotFound
}
if user.FeatureRestriction.Contains(core.USER_FEATURE_RESTRICTION_TYPE_SYNC_APPLICATION_SETTINGS) {
return false, errs.ErrNotPermittedToPerformThisAction
}
err = a.userAppCloudSettings.ClearUserApplicationCloudSettings(c, uid)
if err != nil {
log.Errorf(c, "[user_app_cloud_settings.ApplicationSettingsDisableHandler] failed to clear user application cloud settings for user \"uid:%d\", because %s", uid, err.Error())
return false, errs.Or(err, errs.ErrOperationFailed)
}
return true, nil
}